7.16 小商场对接拼团结算部分,支付成功更新组队信息,组队成功触发回调=》模拟发货

This commit is contained in:
zhangsan 2025-07-16 14:01:48 +08:00
parent 89d4b04e9c
commit d8643194ee
27 changed files with 385 additions and 80 deletions

View File

@ -1,50 +0,0 @@
/*
Navicat Premium Data Transfer
Source Server : group_buy_local
Source Server Type : MySQL
Source Server Version : 80042
Source Host : localhost:13306
Source Schema : pay-mall
Target Server Type : MySQL
Target Server Version : 80042
File Encoding : 65001
Date: 15/07/2025 14:36:57
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for pay_order
-- ----------------------------
DROP TABLE IF EXISTS `pay_order`;
CREATE TABLE `pay_order` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户ID',
`product_id` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品ID',
`product_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品名称',
`order_id` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '订单ID',
`order_time` datetime NOT NULL COMMENT '下单时间',
`total_amount` decimal(8, 2) UNSIGNED NULL DEFAULT NULL COMMENT '订单金额',
`status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '订单状态create-创建完成、pay_wait-等待支付、pay_success-支付成功、deal_done-交易完成、close-订单关单',
`pay_url` varchar(2014) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '支付信息',
`pay_time` datetime NULL DEFAULT NULL COMMENT '支付时间',
`market_type` tinyint(1) NULL DEFAULT NULL COMMENT '营销类型0无营销、1拼团营销',
`market_deduction_amount` decimal(8, 2) NULL DEFAULT NULL COMMENT '营销金额;优惠金额',
`pay_amount` decimal(8, 2) NOT NULL COMMENT '支付金额',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uq_order_id`(`order_id` ASC) USING BTREE,
INDEX `idx_user_id_product_id`(`user_id` ASC, `product_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of pay_order
-- ----------------------------
INSERT INTO `pay_order` VALUES (18, 'smile02', '9890001', 'MyBatisBook', '855240590150', '2025-07-15 06:35:57', 100.00, 'PAY_WAIT', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=qPjHpyXTrmdn%2BGc4H4tqQ0i8MakJ%2BmNNAASwHRwizg0KEQ0YksmQyub3q4IzVOUjkRGj1HuxUdIgKLfJg9h%2F%2B%2FRuVXRa4DNCMw1dUmFXhH2sThvzbRGPAUaR2Nb2B0TAr77X6aDu%2FzWxhLHCkRf2mTaAGoKhuhySf6gxZeJp7Izwu9kka4wlzD1M08KHPWzmrszyJbcH%2BwJsrPHe%2B82%2B9GkJw0pi10nVX9BBPp6ss17zlYgbk8Sg4EWAIEaNZ46RxUDPznPhHNM%2FXY2awBHfNTqbY17eY%2Bu8Tl9nVnKpNXIBiKgG3SlMA0FEi1%2FTjNMkiQPfYr9YcCGwzQWdPJwQxQ%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top&notify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2&timestamp=2025-07-15+14%3A35%3A57&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{&quot;out_trade_no&quot;:&quot;855240590150&quot;,&quot;total_amount&quot;:80.00,&quot;subject&quot;:&quot;MyBatisBook&quot;,&quot;product_code&quot;:&quot;FAST_INSTANT_TRADE_PAY&quot;}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', NULL, 1, 20.00, 80.00, '2025-07-15 14:35:56', '2025-07-15 14:35:57');
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -0,0 +1,52 @@
/*
Navicat Premium Data Transfer
Source Server : group_buy_local
Source Server Type : MySQL
Source Server Version : 80042
Source Host : localhost:13306
Source Schema : pay-mall
Target Server Type : MySQL
Target Server Version : 80042
File Encoding : 65001
Date: 16/07/2025 13:59:35
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for pay_order
-- ----------------------------
DROP TABLE IF EXISTS `pay_order`;
CREATE TABLE `pay_order` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户ID',
`product_id` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品ID',
`product_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品名称',
`order_id` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '订单ID',
`order_time` datetime NOT NULL COMMENT '下单时间',
`total_amount` decimal(8, 2) UNSIGNED NULL DEFAULT NULL COMMENT '订单金额',
`status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '订单状态create-创建完成、pay_wait-等待支付、pay_success-支付成功、deal_done-交易完成、close-订单关单',
`pay_url` varchar(2014) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '支付信息',
`pay_time` datetime NULL DEFAULT NULL COMMENT '支付时间',
`market_type` tinyint(1) NULL DEFAULT NULL COMMENT '营销类型0无营销、1拼团营销',
`market_deduction_amount` decimal(8, 2) NULL DEFAULT NULL COMMENT '营销金额;优惠金额',
`pay_amount` decimal(8, 2) NOT NULL COMMENT '支付金额',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uq_order_id`(`order_id` ASC) USING BTREE,
INDEX `idx_user_id_product_id`(`user_id` ASC, `product_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of pay_order
-- ----------------------------
INSERT INTO `pay_order` VALUES (31, 'smile01', '9890001', 'MyBatisBook', '376456387082', '2025-07-16 13:47:35', 100.00, 'DEAL_DONE', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=QiS6Ss9WVG45w4G85WPEw8D7TLbahtqfJQ8M%2BtPGI4FGP0njFMLDamSWT%2B5H67mZlEJjO%2F03a0kZ%2Fs4eg2bsX2ZAn8hzFyUPNHIbAfNikjnlWPxVBT1ageB3zmc95xP9r%2BtGRnuPqryxSs1FzKQjqbQJJRWXxdQE%2FEHjq5bc6zTODbiDMk%2B8hhy104FlM%2BPiT4SKKOfAgtHI2rFruW%2B6rlIHdxsI5roHvq650uDQ0Ir%2BqTZ46FP%2Fv2RpNfmOp1pR8%2BJ4NAZlti8wng0AzWVkl7xUdsgBQ%2BD9Bja57Lt8pZC1SI0%2BrV0HOU0H%2F8AkuVvdCzAPgwUrzaPkYexrsH6a2g%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top&notify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2&timestamp=2025-07-16+13%3A47%3A35&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{&quot;out_trade_no&quot;:&quot;376456387082&quot;,&quot;total_amount&quot;:80.00,&quot;subject&quot;:&quot;MyBatisBook&quot;,&quot;product_code&quot;:&quot;FAST_INSTANT_TRADE_PAY&quot;}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', '2025-07-16 13:50:29', 1, 20.00, 80.00, '2025-07-16 13:47:35', '2025-07-16 13:58:39');
INSERT INTO `pay_order` VALUES (32, 'smile02', '9890001', 'MyBatisBook', '503529040337', '2025-07-16 13:55:51', 100.00, 'DEAL_DONE', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=l1%2FB5WhTnlHDBZGCmFD62mNUQZSGPISW0ny%2F%2Btxju2Mx0m8Ju%2B80cy43YPNGb5Law80EaByVO9O90S5yJ5qIa4%2FMWNHdcrpoVpdaoiuSwa8FvHAZYA65KM6nB7fe1ESy4Q3xOVYi9TS7TFhg5feSbrCe%2F48jfs0A7GxFzZuioQPXb8fFlTpQhm0w4bsUwYf5YfNKxUUNQn%2BK%2FMT2QMBsEceCT0%2FJn5OM2Yl1IufMvdkMsZVQLdN5%2BxsDP%2BabPTxmV%2BGB0gshWT9mHlfRONqxEbqkug99CmqxQKy9GEnKDZFCdkSknAI3ukS14cflpJB84Vq4Pb1m5ZA8AyCbcUUlYQ%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top&notify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2&timestamp=2025-07-16+13%3A55%3A51&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{&quot;out_trade_no&quot;:&quot;503529040337&quot;,&quot;total_amount&quot;:80.00,&quot;subject&quot;:&quot;MyBatisBook&quot;,&quot;product_code&quot;:&quot;FAST_INSTANT_TRADE_PAY&quot;}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', '2025-07-16 13:56:47', 1, 20.00, 80.00, '2025-07-16 13:55:50', '2025-07-16 13:58:39');
INSERT INTO `pay_order` VALUES (33, 'smile03', '9890001', 'MyBatisBook', '274640446882', '2025-07-16 13:57:47', 100.00, 'DEAL_DONE', '<form name=\"punchout_form\" method=\"post\" action=\"https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=aeJrx00JaoiPo%2Bd%2BelwPekikVqqcT0UjTQluUAWGtxia0Zp9BuzIljU%2BOrQtBj9vhV9cfh1qFxh4%2BGqpTyJN0ZTTPoQa1U4tT1UE2be5JKcV%2BKFtXfpSL%2BFHx7oPLoqXUnhksF5Q8h82n680LVt%2FFGfwo4mBKuFzJ%2FX1libjCzFwdcIHvosESbClSHb5hmNSyWSmyh4UZra9wvaofUjZvrE2%2FhEYIK01Sl9cTXFvmnCHaGLdKgBPUYdx5zVQryKuV%2BfZgDw9s7kEjPommlx3s0XK0tFb8dmitYc%2FdFvVDjDTJ8YE4PuFDedG2qtmOgJ%2BteXfrUHjWwfsMLTuor9elA%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top&notify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2&timestamp=2025-07-16+13%3A57%3A47&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{&quot;out_trade_no&quot;:&quot;274640446882&quot;,&quot;total_amount&quot;:80.00,&quot;subject&quot;:&quot;MyBatisBook&quot;,&quot;product_code&quot;:&quot;FAST_INSTANT_TRADE_PAY&quot;}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>', '2025-07-16 13:58:39', 1, 20.00, 80.00, '2025-07-16 13:57:47', '2025-07-16 13:58:40');
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -1,5 +1,5 @@
<form name="punchout_form" method="post" action="https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=fVfjda%2FIPa2CS680R%2Fqn3%2BvyNzSd7eVavXWa5IXG6bvxmwhmo1PKDMRDjYBNFHm4ppgcKaUohl7Wl1bfhSta1LK44%2FjMWqiuRYm3gpFkj1gt4ZKeuXYmQO7lcvdTyDF9U2xA%2BydKEWmuJVKJ%2BRRV5xEwCZ2ZICq%2BeF442IFmqpjrjNUlsswlNM%2BfzFJUaDggBXaKKfr0B8h%2BQ0lEfPA%2FfahBLoMG9V3CcxDPE0CGtL6YUacHzFY4niWZM7Lf2e0r5giXHKiAQ%2Fkx4vHbh4N7yG%2B%2BAj2OlDdVSolMOG3Vs3leElRttAWRJLTUxwR3GSHdQScr8kOEnEaCc9TiVGCNmg%3D%3D&return_url=https%3A%2F%2Fgaga.plus&notify_url=http%3A%2F%2Fxfg-studio.natapp1.cc%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000132689924&sign_type=RSA2&timestamp=2024-10-04+09%3A44%3A12&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json">
<input type="hidden" name="biz_content" value="{&quot;out_trade_no&quot;:&quot;26674145122785&quot;,&quot;total_amount&quot;:&quot;1.68&quot;,&quot;subject&quot;:&quot;测试商品&quot;,&quot;product_code&quot;:&quot;FAST_INSTANT_TRADE_PAY&quot;}">
<form name="punchout_form" method="post" action="https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=utf-8&method=alipay.trade.page.pay&sign=aeJrx00JaoiPo%2Bd%2BelwPekikVqqcT0UjTQluUAWGtxia0Zp9BuzIljU%2BOrQtBj9vhV9cfh1qFxh4%2BGqpTyJN0ZTTPoQa1U4tT1UE2be5JKcV%2BKFtXfpSL%2BFHx7oPLoqXUnhksF5Q8h82n680LVt%2FFGfwo4mBKuFzJ%2FX1libjCzFwdcIHvosESbClSHb5hmNSyWSmyh4UZra9wvaofUjZvrE2%2FhEYIK01Sl9cTXFvmnCHaGLdKgBPUYdx5zVQryKuV%2BfZgDw9s7kEjPommlx3s0XK0tFb8dmitYc%2FdFvVDjDTJ8YE4PuFDedG2qtmOgJ%2BteXfrUHjWwfsMLTuor9elA%3D%3D&return_url=https%3A%2F%2Fblog.bitday.top&notify_url=https%3A%2F%2Fpay.bitday.top%2Fapi%2Fv1%2Falipay%2Falipay_notify_url&version=1.0&app_id=9021000150645052&sign_type=RSA2&timestamp=2025-07-16+13%3A57%3A47&alipay_sdk=alipay-sdk-java-4.38.157.ALL&format=json">
<input type="hidden" name="biz_content" value="{&quot;out_trade_no&quot;:&quot;274640446882&quot;,&quot;total_amount&quot;:80.00,&quot;subject&quot;:&quot;MyBatisBook&quot;,&quot;product_code&quot;:&quot;FAST_INSTANT_TRADE_PAY&quot;}">
<input type="submit" value="立即支付" style="display:none" >
</form>
<script>document.forms[0].submit();</script>

View File

@ -1,10 +1,18 @@
package edu.whut.api;
import edu.whut.api.dto.CreatePayRequestDTO;
import edu.whut.api.dto.NotifyRequestDTO;
import edu.whut.api.response.Response;
public interface IPayService {
Response<String> createPayOrder(CreatePayRequestDTO createPayRequestDTO);
/**
* 拼团结算回调
*
* @param requestDTO 请求对象
* @return 返参success 成功
*/
String groupBuyNotify(NotifyRequestDTO requestDTO);
}

View File

@ -0,0 +1,18 @@
package edu.whut.api.dto;
import lombok.Data;
import java.util.List;
/**
* 回调请求对象
*/
@Data
public class NotifyRequestDTO {
/** 组队ID */
private String teamId;
/** 外部单号 */
private List<String> outTradeNoList;
}

View File

@ -11,7 +11,7 @@ app:
# SC 渠道配置 - 拼团对接渠道值、回调通知
group-buy-market:
api-url: http://127.0.0.1:8091
notify-url: http://127.0.0.1:8091/api/v1/test/group_buy_notify
notify-url: http://127.0.0.1:8092/api/v1/alipay/group_buy_notify
source: s01
chanel: c01
@ -31,7 +31,7 @@ spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:13306/pay-mall?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true
url: jdbc:mysql://127.0.0.1:13306/pay-mall?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&useSSL=true
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
pool-name: Retail_HikariCP

View File

@ -66,4 +66,23 @@
LIMIT 10
</select>
<update id="changeOrderMarketSettlement" parameterType="java.util.List">
update pay_order set status = 'MARKET', update_time = now()
where order_id in
<foreach item="orderId" collection="outTradeNoList" open="(" separator="," close=")">
#{orderId}
</foreach>
</update>
<select id="queryOrderByOrderId" parameterType="java.lang.String" resultMap="dataMap">
select user_id, product_id, product_name, order_id, order_time, total_amount, status, pay_url, market_type, market_deduction_amount, pay_amount
from pay_order
where order_id = #{orderId}
</select>
<update id="changeOrderDealDone" parameterType="java.lang.String">
update pay_order set status = 'DEAL_DONE', update_time = now()
where order_id = #{orderId}
</update>
</mapper>

View File

@ -1,19 +1,40 @@
package edu.whut.test;
import com.alibaba.fastjson.JSON;
import com.google.common.eventbus.EventBus;
import edu.whut.domain.order.adapter.event.PaySuccessMessageEvent;
import edu.whut.types.event.BaseEvent;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.concurrent.CountDownLatch;
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApiTest {
@Resource
private EventBus eventBus;
@Resource
private PaySuccessMessageEvent paySuccessMessageEvent;
@Test
public void test() {
public void test() throws InterruptedException {
BaseEvent.EventMessage<PaySuccessMessageEvent.PaySuccessMessage> paySuccessMessageEventMessage = paySuccessMessageEvent.buildEventMessage(
PaySuccessMessageEvent.PaySuccessMessage.builder()
.tradeNo("1100000111")
.build());
eventBus.post(JSON.toJSONString(paySuccessMessageEventMessage.getData()));
log.info("测试完成");
new CountDownLatch(1).await();
}
}

View File

@ -24,9 +24,9 @@ public class OrderServiceTest {
@Test
public void test_createOrder() throws Exception {
ShopCartEntity shopCartEntity = new ShopCartEntity();
shopCartEntity.setUserId("smile02");
shopCartEntity.setUserId("smile03");
shopCartEntity.setProductId("9890001");
shopCartEntity.setTeamId(null);
shopCartEntity.setTeamId("71752028");
shopCartEntity.setActivityId(100123L);
shopCartEntity.setMarketTypeVO(MarketTypeVO.GROUP_BUY_MARKET);

View File

@ -0,0 +1,10 @@
package edu.whut.domain.goods.adapter.repository;
/**
* 结算仓储
*/
public interface IGoodsRepository {
void changeOrderDealDone(String orderId);
}

View File

@ -0,0 +1,22 @@
package edu.whut.domain.goods.service;
import edu.whut.domain.goods.adapter.repository.IGoodsRepository;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 结算服务
*/
@Service
public class GoodsService implements IGoodsService {
@Resource
private IGoodsRepository repository;
@Override
public void changeOrderDealDone(String orderId) {
repository.changeOrderDealDone(orderId);
}
}

View File

@ -0,0 +1,10 @@
package edu.whut.domain.goods.service;
/**
* 结算服务
*/
public interface IGoodsService {
void changeOrderDealDone(String tradeNo);
}

View File

@ -4,10 +4,13 @@ package edu.whut.domain.order.adapter.port;
import edu.whut.domain.order.model.entity.MarketPayDiscountEntity;
import edu.whut.domain.order.model.entity.ProductEntity;
import java.util.Date;
public interface IProductPort {
ProductEntity queryProductByProductId(String productId);
MarketPayDiscountEntity lockMarketPayOrder(String userId, String teamId, Long activityId, String productId, String orderId);
void settlementMarketPayOrder(String userId, String orderId, Date orderTime);
}

View File

@ -5,6 +5,7 @@ import edu.whut.domain.order.model.entity.OrderEntity;
import edu.whut.domain.order.model.entity.PayOrderEntity;
import edu.whut.domain.order.model.entity.ShopCartEntity;
import java.util.Date;
import java.util.List;
public interface IOrderRepository {
@ -14,11 +15,17 @@ public interface IOrderRepository {
void updateOrderPayInfo(PayOrderEntity payOrderEntity);
void changeOrderPaySuccess(String orderId);
void changeOrderPaySuccess(String orderId, Date payTime);
void changeMarketOrderPaySuccess(String orderId);
List<String> queryNoPayNotifyOrder();
List<String> queryTimeoutCloseOrderList();
boolean changeOrderClose(String orderId);
void changeOrderMarketSettlement(List<String> outTradeNoList);
OrderEntity queryOrderByOrderId(String orderId);
}

View File

@ -14,6 +14,8 @@ import java.util.Date;
@NoArgsConstructor
public class OrderEntity {
// 用户ID
private String userId;
private String productId;
private String productName;
private String orderId;

View File

@ -12,7 +12,8 @@ public enum OrderStatusVO {
PAY_SUCCESS("PAY_SUCCESS", "支付成功 - 接收到支付回调消息"),
DEAL_DONE("DEAL_DONE", "交易完成 - 商品发货完成"),
CLOSE("CLOSE", "超时关单 - 超市未支付"),
;
MARKET("MARKET", "营销结算 - 拼团组队完成"),
;
private final String code;
private final String desc;

View File

@ -4,13 +4,14 @@ package edu.whut.domain.order.service;
import edu.whut.domain.order.model.entity.PayOrderEntity;
import edu.whut.domain.order.model.entity.ShopCartEntity;
import java.util.Date;
import java.util.List;
public interface IOrderService {
PayOrderEntity createOrder(ShopCartEntity shopCartEntity) throws Exception;
void changeOrderPaySuccess(String orderId);
void changeOrderPaySuccess(String orderId, Date orderTime);
List<String> queryNoPayNotifyOrder();
@ -18,4 +19,6 @@ public interface IOrderService {
boolean changeOrderClose(String orderId);
void changeOrderMarketSettlement(List<String> outTradeNoList);
}

View File

@ -7,6 +7,7 @@ import edu.whut.domain.order.adapter.port.IProductPort;
import edu.whut.domain.order.adapter.repository.IOrderRepository;
import edu.whut.domain.order.model.aggregate.CreateOrderAggregate;
import edu.whut.domain.order.model.entity.MarketPayDiscountEntity;
import edu.whut.domain.order.model.entity.OrderEntity;
import edu.whut.domain.order.model.entity.PayOrderEntity;
import edu.whut.domain.order.model.valobj.MarketTypeVO;
import edu.whut.domain.order.model.valobj.OrderStatusVO;
@ -16,6 +17,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@Slf4j
@ -89,8 +91,20 @@ public class OrderService extends AbstractOrderService{
* 标记订单为支付成功并发布支付成功事件
*/
@Override
public void changeOrderPaySuccess(String orderId) {
repository.changeOrderPaySuccess(orderId);
public void changeOrderPaySuccess(String orderId, Date payTime) {
OrderEntity orderEntity = repository.queryOrderByOrderId(orderId);
if (null == orderEntity) return;
//走拼团的流程
if (MarketTypeVO.GROUP_BUY_MARKET.getCode().equals(orderEntity.getMarketType())) {
repository.changeMarketOrderPaySuccess(orderId);
// 发起拼团系统的营销结算这个过程可以是http/rpc直接调用也可以发一个商城交易支付完成的mq消息之后拼团系统自己接收做结算
port.settlementMarketPayOrder(orderEntity.getUserId(), orderId, payTime);
} else {
//不走拼团的流程
repository.changeOrderPaySuccess(orderId, payTime);
}
}
/**
@ -116,4 +130,9 @@ public class OrderService extends AbstractOrderService{
public boolean changeOrderClose(String orderId) {
return repository.changeOrderClose(orderId);
}
@Override
public void changeOrderMarketSettlement(List<String> outTradeNoList) {
repository.changeOrderMarketSettlement(outTradeNoList);
}
}

View File

@ -3,6 +3,8 @@ package edu.whut.infrastructure.adapter.port;
import com.alibaba.fastjson.JSON;
import edu.whut.api.dto.LockMarketPayOrderRequestDTO;
import edu.whut.api.dto.LockMarketPayOrderResponseDTO;
import edu.whut.api.dto.SettlementMarketPayOrderRequestDTO;
import edu.whut.api.dto.SettlementMarketPayOrderResponseDTO;
import edu.whut.api.response.Response;
import edu.whut.domain.order.adapter.port.IProductPort;
import edu.whut.domain.order.model.entity.MarketPayDiscountEntity;
@ -17,6 +19,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import retrofit2.Call;
import java.util.Date;
@Component
@RequiredArgsConstructor
@Slf4j
@ -94,4 +98,33 @@ public class ProductPort implements IProductPort {
}
}
@Override
public void settlementMarketPayOrder(String userId, String orderId, Date orderTime) {
SettlementMarketPayOrderRequestDTO requestDTO = new SettlementMarketPayOrderRequestDTO();
requestDTO.setSource(source);
requestDTO.setChannel(chanel);
requestDTO.setUserId(userId);
requestDTO.setOutTradeNo(orderId);
requestDTO.setOutTradeTime(orderTime);
try {
Call<Response<SettlementMarketPayOrderResponseDTO>> call = groupBuyMarketService.settlementMarketPayOrder(requestDTO);
// 获取结果
Response<SettlementMarketPayOrderResponseDTO> response = call.execute().body();
log.info("营销结算{} requestDTO:{} responseDTO:{}", userId, JSON.toJSONString(requestDTO), JSON.toJSONString(response));
if (null == response) return;
// 异常判断
if (!"0000".equals(response.getCode())) {
log.info("营销结算返回code={}, info={}", response.getCode(), response.getInfo());
throw new AppException(response.getCode(), response.getInfo());
}
} catch (Exception e) {
log.error("营销结算失败 {}", userId, e);
}
}
}

View File

@ -0,0 +1,22 @@
package edu.whut.infrastructure.adapter.repository;
import edu.whut.domain.goods.adapter.repository.IGoodsRepository;
import edu.whut.infrastructure.dao.IOrderDao;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
/**
* 结算仓储服务
*/
@Repository
public class GoodsRepository implements IGoodsRepository {
@Resource
private IOrderDao orderDao;
@Override
public void changeOrderDealDone(String orderId) {
orderDao.changeOrderDealDone(orderId);
}
}

View File

@ -1,5 +1,6 @@
package edu.whut.infrastructure.adapter.repository;
import com.alibaba.fastjson.JSON;
import com.google.common.eventbus.EventBus;
import edu.whut.domain.order.adapter.event.PaySuccessMessageEvent;
import edu.whut.domain.order.adapter.repository.IOrderRepository;
@ -16,8 +17,8 @@ import edu.whut.types.event.BaseEvent;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@Repository
@ -108,20 +109,33 @@ public class OrderRepository implements IOrderRepository {
* 标记订单为支付成功并发布支付成功事件
*/
@Override
public void changeOrderPaySuccess(String orderId) {
public void changeOrderPaySuccess(String orderId, Date payTime) {
// 更新状态为 PAY_SUCCESS 并记录支付时间
PayOrder payOrderReq = new PayOrder();
payOrderReq.setOrderId(orderId);
payOrderReq.setStatus(OrderStatusVO.PAY_SUCCESS.getCode());
payOrderReq.setPayTime(payTime);
orderDao.changeOrderPaySuccess(payOrderReq);
// 构建并发布支付成功消息给 EventBus
// 构建事件消息
BaseEvent.EventMessage<PaySuccessMessageEvent.PaySuccessMessage> evtMsg = paySuccessMessageEvent.buildEventMessage(
PaySuccessMessageEvent.PaySuccessMessage.builder()
.tradeNo(orderId)
.tradeNo(orderId) // 将订单号作为消息内容
.build()
);
eventBus.post(evtMsg.getData());
PaySuccessMessageEvent.PaySuccessMessage paySuccessMessage = evtMsg.getData();
eventBus.post(JSON.toJSONString(paySuccessMessage)); //发布到 EventBus
}
/**
* 标记拼团订单为支付成功仅更新状态不发布事件
*/
@Override
public void changeMarketOrderPaySuccess(String orderId) {
PayOrder payOrderReq = new PayOrder();
payOrderReq.setOrderId(orderId);
payOrderReq.setStatus(OrderStatusVO.PAY_SUCCESS.getCode());
orderDao.changeOrderPaySuccess(payOrderReq);
}
/**
@ -148,4 +162,44 @@ public class OrderRepository implements IOrderRepository {
public boolean changeOrderClose(String orderId) {
return orderDao.changeOrderClose(orderId);
}
@Override
public void changeOrderMarketSettlement(List<String> outTradeNoList) {
// 更新拼团结算状态
orderDao.changeOrderMarketSettlement(outTradeNoList);
// 循环成功发送消息 - 一般在公司的场景里还会有job任务扫描超时没有结算的订单查询订单状态查询对方服务端的接口会被限制一次查询多少频次多少
outTradeNoList.forEach(outTradeNo -> {
BaseEvent.EventMessage<PaySuccessMessageEvent.PaySuccessMessage> paySuccessMessageEventMessage = paySuccessMessageEvent.buildEventMessage(
PaySuccessMessageEvent.PaySuccessMessage.builder()
.tradeNo(outTradeNo)
.build());
PaySuccessMessageEvent.PaySuccessMessage paySuccessMessage = paySuccessMessageEventMessage.getData();
eventBus.post(JSON.toJSONString(paySuccessMessage));
});
}
/**
* 根据订单id查询订单
*/
@Override
public OrderEntity queryOrderByOrderId(String orderId) {
PayOrder payOrder = orderDao.queryOrderByOrderId(orderId);
if (null == orderId) return null;
return OrderEntity.builder()
.userId(payOrder.getUserId())
.productId(payOrder.getProductId())
.productName(payOrder.getProductName())
.orderId(payOrder.getOrderId())
.orderTime(payOrder.getOrderTime())
.totalAmount(payOrder.getTotalAmount())
.payUrl(payOrder.getPayUrl())
.marketType(payOrder.getMarketType())
.marketDeductionAmount(payOrder.getMarketDeductionAmount())
.payAmount(payOrder.getPayAmount())
.build();
}
}

View File

@ -1,6 +1,7 @@
package edu.whut.infrastructure.dao;
import edu.whut.infrastructure.dao.po.PayOrder;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -21,5 +22,10 @@ public interface IOrderDao {
boolean changeOrderClose(String orderId);
void changeOrderMarketSettlement(@Param("outTradeNoList") List<String> outTradeNoList);
PayOrder queryOrderByOrderId(String orderId);
void changeOrderDealDone(String orderId);
}

View File

@ -2,6 +2,8 @@ package edu.whut.infrastructure.gateway;
import edu.whut.api.dto.LockMarketPayOrderRequestDTO;
import edu.whut.api.dto.LockMarketPayOrderResponseDTO;
import edu.whut.api.dto.SettlementMarketPayOrderRequestDTO;
import edu.whut.api.dto.SettlementMarketPayOrderResponseDTO;
import edu.whut.api.response.Response;
import retrofit2.Call;
import retrofit2.http.Body;
@ -21,4 +23,14 @@ public interface IGroupBuyMarketService {
@POST("api/v1/gbm/trade/lock_market_pay_order")
Call<Response<LockMarketPayOrderResponseDTO>> lockMarketPayOrder(@Body LockMarketPayOrderRequestDTO requestDTO);
/**
* 营销结算
*
* @param requestDTO 结算商品信息
* @return 结算结果信息
*/
@POST("api/v1/gbm/trade/settlement_market_pay_order")
Call<Response<SettlementMarketPayOrderResponseDTO>> settlementMarketPayOrder(@Body SettlementMarketPayOrderRequestDTO requestDTO);
}

View File

@ -1,8 +1,10 @@
package edu.whut.trigger.http;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import edu.whut.api.IPayService;
import edu.whut.api.dto.CreatePayRequestDTO;
import edu.whut.api.dto.NotifyRequestDTO;
import edu.whut.api.response.Response;
import edu.whut.domain.order.model.entity.PayOrderEntity;
import edu.whut.domain.order.model.entity.ShopCartEntity;
@ -14,6 +16,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
@ -67,8 +71,28 @@ public class AliPayController implements IPayService {
}
}
/**
* 拼团完成目标人数已达成后的回调地址由group_buy_market调用
*/
@PostMapping("/group_buy_notify")
@Override
public String groupBuyNotify(@RequestBody NotifyRequestDTO requestDTO) {
log.info("拼团回调,组队完成,结算开始 {}", JSON.toJSONString(requestDTO));
try {
// 营销结算
orderService.changeOrderMarketSettlement(requestDTO.getOutTradeNoList());
return "success";
} catch (Exception e) {
log.info("拼团回调,组队完成,结算失败 {}", JSON.toJSONString(requestDTO));
return "error";
}
}
/**
* 用户支付成功后的回调地址
*/
@PostMapping("/alipay_notify_url")
public String payNotify(HttpServletRequest request) throws AlipayApiException {
public String payNotify(HttpServletRequest request) throws AlipayApiException, ParseException {
log.info("支付回调,消息接收 {}", request.getParameter("trade_status"));
if (!request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
@ -104,7 +128,8 @@ public class AliPayController implements IPayService {
log.info("支付回调,买家付款金额: {}", params.get("buyer_pay_amount"));
log.info("支付回调,支付回调,更新订单 {}", tradeNo);
orderService.changeOrderPaySuccess(tradeNo);
// 后续处理更新订单状态...进入下一阶段如发货
orderService.changeOrderPaySuccess(tradeNo, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(params.get("gmt_payment")));
return "success";
}

View File

@ -25,7 +25,7 @@ public class NoPayNotifyOrderJob {
private final AlipayClient alipayClient;
/**
* 3秒执行一次扫描超过1分钟未收到回调的待支付订单
* 3 秒执行一次扫描超过1分钟未收到回调的待支付订单
*/
@Scheduled(cron = "0/3 * * * * ?")
public void exec() {
@ -46,12 +46,7 @@ public class NoPayNotifyOrderJob {
String code = alipayTradeQueryResponse.getCode();
// 如果支付宝返回业务成功码(10000)则更新订单为支付成功
if ("10000".equals(code)) {
orderService.changeOrderPaySuccess(orderId);
}else {
log.warn("订单[{}]支付宝查询未成功:{}/{}",
orderId,
alipayTradeQueryResponse.getSubCode(),
alipayTradeQueryResponse.getSubMsg());
orderService.changeOrderPaySuccess(orderId, alipayTradeQueryResponse.getSendPayDate());
}
}
} catch (Exception e) {

View File

@ -20,7 +20,7 @@ public class TimeoutCloseOrderJob {
/**
* 每10分钟执行一次扫描超时未支付订单
*/
@Scheduled(cron = "0 0/10 * * * ?")
@Scheduled(cron = "0 0/30 * * * ?")
public void exec() {
try {
log.info("任务超时30分钟订单关闭");
@ -35,7 +35,7 @@ public class TimeoutCloseOrderJob {
log.info("定时任务超时30分钟订单关闭 orderId: {} status{}", orderId, status);
}
} catch (Exception e) {
log.error("定时任务,超时15分钟订单关闭失败", e);
log.error("定时任务,超时30分钟订单关闭失败", e);
}
}

View File

@ -1,6 +1,10 @@
package edu.whut.trigger.listener;
import com.alibaba.fastjson.JSON;
import com.google.common.eventbus.Subscribe;
import edu.whut.domain.goods.service.IGoodsService;
import edu.whut.domain.order.adapter.event.PaySuccessMessageEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@ -9,11 +13,20 @@ import org.springframework.stereotype.Component;
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class OrderPaySuccessListener {
private final IGoodsService goodsService;
@Subscribe
public void handleEvent(String paySuccessMessage) {
log.info("收到支付成功消息,可以做接下来的事情,如;发货、充值、开户员、返利 {}", paySuccessMessage);
public void handleEvent(String paySuccessMessageJson) {
log.info("收到支付成功消息 {}", paySuccessMessageJson);
PaySuccessMessageEvent.PaySuccessMessage paySuccessMessage = JSON.parseObject(paySuccessMessageJson, PaySuccessMessageEvent.PaySuccessMessage.class);
log.info("模拟发货(如;发货、充值、开户员、返利),单号:{}", paySuccessMessage.getTradeNo());
// 变更订单状态 - 发货完成&结算
goodsService.changeOrderDealDone(paySuccessMessage.getTradeNo());
}
}