7.15 拼团锁单和小商场对接

This commit is contained in:
zhangsan 2025-07-15 14:38:02 +08:00
parent fbaa6b06dc
commit 89d4b04e9c
25 changed files with 408 additions and 60 deletions

View File

@ -14,6 +14,7 @@ services:
- "13306:3306"
volumes:
- ./mysql/sql:/docker-entrypoint-initdb.d
- ./mysql/my.cnf:/etc/mysql/conf.d/mysql.cnf:ro
healthcheck:
test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
interval: 5s

View File

@ -14,6 +14,7 @@ services:
- "13306:3306"
volumes:
- ./mysql/sql:/docker-entrypoint-initdb.d
- ./mysql/my.cnf:/etc/mysql/conf.d/mysql.cnf:ro
healthcheck:
test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
interval: 5s

24
docs/dev-ops/mysql/my.cnf Normal file
View File

@ -0,0 +1,24 @@
[client]
port = 3306
default-character-set = utf8mb4
[mysqld]
user = mysql
port = 3306
sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
default-storage-engine = InnoDB
default-authentication-plugin = mysql_native_password
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect = 'SET NAMES utf8mb4'
slow_query_log
#long_query_time = 3
slow-query-log-file = /var/log/mysql/mysql.slow.log
log-error = /var/log/mysql/mysql.error.log
default-time-zone = '+8:00'
[mysql]
default-character-set = utf8mb4

View File

@ -11,7 +11,7 @@
Target Server Version : 80042
File Encoding : 65001
Date: 13/07/2025 14:21:55
Date: 15/07/2025 14:36:57
*/
SET NAMES utf8mb4;
@ -32,17 +32,19 @@ CREATE TABLE `pay_order` (
`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 = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
) 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 (5, 'smile01', '10001', '测试商品', '51403944017404', '2025-07-12 05:49:16', 1.68, '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=Af8UHbR23xcDThup0mv3NVZLlkMAcQ3tjmqGNBpwYqJ1Xn1ytqt42NWBsbDxKHT3rEo3h3dmd60wJ9N0LTmjElq%2B9HGFukp7KtxOiZRlbxip9x9yl4mCjQTUunf6l7iMsUAAtBUh%2FiVJOsI7Uy4Jy8Fg7fqjRAjc06fKcsynU5wb3xtOhr9f8uQIpe7ykxLg3Uc6YyR9GYdTcceVRu15BoSur6xz4S%2FMhRTOqc8XbWrqBGhvWMk%2BV6FHbx04AHYg7HHc0%2FTndoIHFSX9c1h7Jysxbyxp3q40GMfrnpX53VkqcPpQBbLA8f%2BWO8MnzbnxobeKcXAR11gxSbl4FcEWng%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-13+14%3A21%3A33&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;51403944017404&quot;,&quot;total_amount&quot;:&quot;1.68&quot;,&quot;subject&quot;:&quot;测试商品&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, '2025-07-12 13:49:15', '2025-07-13 14:21:33');
INSERT INTO `pay_order` VALUES (6, '10001', '10001', '测试商品', '66665553128265', '2025-07-13 06:19:11', 1.68, '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=lQ1WTro6vDXTQet%2Bd%2BJ7TWIIWXF%2B5SWg0r53joEBUesQL12UtgHrWa%2BafAwjkJWxH9zPqwAvFuXr1ZFfmMnqz1PTsRXo4ACTAJC0Se2uFSYAU1HK8Cyk4jHh9OwEjW1MQXdWT1l52KjLuZOBeJAlUvVPyAfxQnBx0HF2XKHEfFLFBgeRufAp%2BEzCM%2BoX8l0G%2FkQdskCRhICLMh2tFj2T4i4sgsSwuJQDV%2Fs0EOwpDpDKV3Z9xPEzkI2zZM6ZB8jCfTa734H8%2BC9IWZmKX2njnNj89g4h9RVuLd92l0veNn%2FJmrxTz296eCWJl6yU5PLSB0pX0IiSnUoHiySFnrswaA%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-13+14%3A19%3A11&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;66665553128265&quot;,&quot;total_amount&quot;:&quot;1.68&quot;,&quot;subject&quot;:&quot;测试商品&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, '2025-07-13 14:19:11', '2025-07-13 14:21:21');
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

@ -9,5 +9,11 @@ public class CreatePayRequestDTO {
private String userId;
// 产品编号
private String productId;
// 拼团队伍 - 队伍ID
private String teamId;
// 活动ID来自于页面调用拼团试算后获得的活动ID信息
private Long activityId;
// 营销类型 - 0无营销
private Integer marketType = 0;
}

View File

@ -1,7 +1,9 @@
package edu.whut.config;
import edu.whut.infrastructure.gateway.IGroupBuyMarketService;
import edu.whut.infrastructure.gateway.IWeixinApiService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import retrofit2.Retrofit;
@ -14,25 +16,29 @@ public class Retrofit2Config {
// 微信开放平台的基础 URL后续所有接口都会在这个前缀下拼接路径
private static final String BASE_URL = "https://api.weixin.qq.com/";
/**
* 创建一个 Retrofit 对象并注册为 Spring Bean
* - baseUrl设置所有请求的公共前缀
* - addConverterFactory添加 Jackson 转换器用于 JSON <-> Java 对象的自动映射
*/
@Bean
public Retrofit retrofit() {
return new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(JacksonConverterFactory.create()).build();
}
@Value("${app.config.group-buy-market.api-url}")
private String groupBuyMarketApiUrl;
/**
* 通过 Retrofit 动态生成 IWeixinApiService 接口的实现
* 并注册为 Spring 容器中的 Bean方便业务层直接注入使用
*/
@Bean
public IWeixinApiService weixinApiService(Retrofit retrofit) {
public IWeixinApiService weixinApiService() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(JacksonConverterFactory.create()).build();
return retrofit.create(IWeixinApiService.class);
}
@Bean
public IGroupBuyMarketService groupBuyMarketService() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(groupBuyMarketApiUrl)
.addConverterFactory(JacksonConverterFactory.create()).build();
return retrofit.create(IGroupBuyMarketService.class);
}
}

View File

@ -1,6 +1,20 @@
server:
port: 8092
# 应用配置
app:
config:
# 版本,方便通过接口版本升级
api-version: v1
# 跨域,开发阶段可以设置为 * 不限制
cross-origin: '*'
# 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
source: s01
chanel: c01
# 线程池配置
thread:
pool:

View File

@ -13,15 +13,18 @@
<result column="status" property="status"/>
<result column="pay_url" property="payUrl"/>
<result column="pay_time" property="payTime"/>
<result column="market_type" property="marketType"/>
<result column="market_deduction_amount" property="marketDeductionAmount"/>
<result column="pay_amount" property="payAmount"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<insert id="insert" parameterType="edu.whut.infrastructure.dao.po.PayOrder">
insert into pay_order(user_id, product_id, product_name, order_id, order_time,
total_amount, status, create_time, update_time)
total_amount, status, market_type, market_deduction_amount, pay_amount, create_time, update_time)
values(#{userId}, #{productId}, #{productName}, #{orderId}, #{orderTime},
#{totalAmount}, #{status}, now(), now())
#{totalAmount}, #{status}, #{marketType}, #{marketDeductionAmount}, #{payAmount}, now(), now())
</insert>
<select id="queryUnPayOrder" parameterType="edu.whut.infrastructure.dao.po.PayOrder" resultMap="dataMap">
@ -33,7 +36,9 @@
</select>
<update id="updateOrderPayInfo" parameterType="edu.whut.infrastructure.dao.po.PayOrder">
update pay_order set pay_url = #{payUrl}, status = #{status}, update_time = now()
update pay_order set pay_url = #{payUrl}, status = #{status},
market_type = #{marketType}, market_deduction_amount = #{marketDeductionAmount}, pay_amount = #{payAmount},
update_time = now()
where order_id = #{orderId}
</update>

View File

@ -2,6 +2,7 @@ package edu.whut.test.domain;
import com.alibaba.fastjson.JSON;
import edu.whut.domain.order.model.entity.PayOrderEntity;
import edu.whut.domain.order.model.entity.ShopCartEntity;
import edu.whut.domain.order.model.valobj.MarketTypeVO;
import edu.whut.domain.order.service.IOrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -15,16 +16,20 @@ import javax.annotation.Resource;
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
@RequiredArgsConstructor
public class OrderServiceTest {
private final IOrderService orderService;
@Resource // 或者 @Autowired
private IOrderService orderService;
@Test
public void test_createOrder() throws Exception {
ShopCartEntity shopCartEntity = new ShopCartEntity();
shopCartEntity.setUserId("smile01");
shopCartEntity.setProductId("10001");
shopCartEntity.setUserId("smile02");
shopCartEntity.setProductId("9890001");
shopCartEntity.setTeamId(null);
shopCartEntity.setActivityId(100123L);
shopCartEntity.setMarketTypeVO(MarketTypeVO.GROUP_BUY_MARKET);
PayOrderEntity payOrderEntity = orderService.createOrder(shopCartEntity);
log.info("请求参数:{}", JSON.toJSONString(shopCartEntity));
log.info("测试结果:{}", JSON.toJSONString(payOrderEntity));

View File

@ -1,9 +1,13 @@
package edu.whut.domain.order.adapter.port;
import edu.whut.domain.order.model.entity.MarketPayDiscountEntity;
import edu.whut.domain.order.model.entity.ProductEntity;
public interface IProductPort {
ProductEntity queryProductByProductId(String productId);
MarketPayDiscountEntity lockMarketPayOrder(String userId, String teamId, Long activityId, String productId, String orderId);
}

View File

@ -22,13 +22,14 @@ public class CreateOrderAggregate {
private OrderEntity orderEntity;
public static OrderEntity buildOrderEntity(String productId, String productName){
public static OrderEntity buildOrderEntity(String productId, String productName, Integer marketType){
return OrderEntity.builder()
.productId(productId)
.productName(productName)
.orderId(RandomStringUtils.randomNumeric(14))
.orderId(RandomStringUtils.randomNumeric(12))
.orderTime(new Date())
.orderStatusVO(OrderStatusVO.CREATE)
.marketType(marketType)
.build();
}

View File

@ -0,0 +1,26 @@
package edu.whut.domain.order.model.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* 营销支付优惠
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MarketPayDiscountEntity {
/** 原始价格 */
private BigDecimal originalPrice;
/** 折扣金额 */
private BigDecimal deductionPrice;
/** 支付金额 */
private BigDecimal payPrice;
}

View File

@ -21,5 +21,11 @@ public class OrderEntity {
private BigDecimal totalAmount;
private OrderStatusVO orderStatusVO;
private String payUrl;
// 营销类型0无营销1拼团营销
private Integer marketType;
// 营销金额优惠金额
private BigDecimal marketDeductionAmount;
// 支付金额
private BigDecimal payAmount;
}

View File

@ -5,6 +5,8 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Data
@Builder
@AllArgsConstructor
@ -16,4 +18,11 @@ public class PayOrderEntity {
private String payUrl;
private OrderStatusVO orderStatus;
// 营销类型0无营销1拼团营销
private Integer marketType;
// 营销金额优惠金额
private BigDecimal marketDeductionAmount;
// 支付金额
private BigDecimal payAmount;
}

View File

@ -1,5 +1,6 @@
package edu.whut.domain.order.model.entity;
import edu.whut.domain.order.model.valobj.MarketTypeVO;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -11,8 +12,19 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
public class ShopCartEntity {
// 用户ID
private String userId;
// 商品ID
private String productId;
// 拼团组队ID可为空为空的时则为用户首次创建拼团
private String teamId;
// 活动ID来自于页面调用拼团试算后获得的活动ID信息
private Long activityId;
// 营销类型无营销拼团营销
private MarketTypeVO marketTypeVO;
}

View File

@ -0,0 +1,32 @@
package edu.whut.domain.order.model.valobj;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 营销类型
*/
@Getter
@AllArgsConstructor
public enum MarketTypeVO {
NO_MARKET(0, "无营销"),
GROUP_BUY_MARKET(1, "拼团营销"),
;
private final Integer code;
private final String desc;
public static MarketTypeVO valueOf(Integer code) {
switch (code) {
case 0:
return NO_MARKET;
case 1:
return GROUP_BUY_MARKET;
}
throw new RuntimeException("err code not exist!");
}
}

View File

@ -4,10 +4,8 @@ import com.alipay.api.AlipayApiException;
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.OrderEntity;
import edu.whut.domain.order.model.entity.PayOrderEntity;
import edu.whut.domain.order.model.entity.ProductEntity;
import edu.whut.domain.order.model.entity.ShopCartEntity;
import edu.whut.domain.order.model.entity.*;
import edu.whut.domain.order.model.valobj.MarketTypeVO;
import edu.whut.domain.order.model.valobj.OrderStatusVO;
import lombok.extern.slf4j.Slf4j;
@ -32,44 +30,86 @@ public abstract class AbstractOrderService implements IOrderService {
// 1. 查询当前用户是否存在掉单和未支付订单
OrderEntity unpaidOrderEntity = repository.queryUnPayOrder(shopCartEntity);
// 如果已有订单正在等待支付直接复用
if (null != unpaidOrderEntity && OrderStatusVO.PAY_WAIT.equals(unpaidOrderEntity.getOrderStatusVO())) {
log.info("创建订单-存在已存在未支付订单。userId:{} productId:{} orderId:{}", shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getOrderId());
// 如果已有未支付订单且状态为支付等待则直接复用
if (unpaidOrderEntity != null && OrderStatusVO.PAY_WAIT.equals(unpaidOrderEntity.getOrderStatusVO())) {
log.info("创建订单-存在已存在未支付订单。userId:{} productId:{} orderId:{}",
shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getOrderId());
return PayOrderEntity.builder()
.orderId(unpaidOrderEntity.getOrderId())
.payUrl(unpaidOrderEntity.getPayUrl())
.build();
} else if (null != unpaidOrderEntity && OrderStatusVO.CREATE.equals(unpaidOrderEntity.getOrderStatusVO())) {
log.info("创建订单-存在,存在未创建'支付单'订单,创建支付单开始 userId:{} productId:{} orderId:{}", shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getOrderId());
PayOrderEntity payOrderEntity = doPrepayOrder(shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getProductName(), unpaidOrderEntity.getOrderId(), unpaidOrderEntity.getTotalAmount());
// 如果已有订单仅创建了记录但未生成支付单则生成支付单
} else if (unpaidOrderEntity != null && OrderStatusVO.CREATE.equals(unpaidOrderEntity.getOrderStatusVO())) {
log.info("创建订单-存在,存在未创建支付单订单,创建支付单开始 userId:{} productId:{} orderId:{}",
shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getOrderId());
Integer marketType = unpaidOrderEntity.getMarketType();
BigDecimal marketDeductionAmount = unpaidOrderEntity.getMarketDeductionAmount();
PayOrderEntity payOrderEntity;
if (MarketTypeVO.GROUP_BUY_MARKET.getCode().equals(marketType) && marketDeductionAmount == null) {
MarketPayDiscountEntity discount = lockMarketPayOrder(
shopCartEntity.getUserId(), shopCartEntity.getTeamId(),
shopCartEntity.getActivityId(), shopCartEntity.getProductId(),
unpaidOrderEntity.getOrderId());
payOrderEntity = doPrepayOrder(
shopCartEntity.getUserId(), shopCartEntity.getProductId(),
unpaidOrderEntity.getProductName(), unpaidOrderEntity.getOrderId(),
unpaidOrderEntity.getTotalAmount(), discount);
} else if (MarketTypeVO.GROUP_BUY_MARKET.getCode().equals(marketType)) {
payOrderEntity = doPrepayOrder(
shopCartEntity.getUserId(), shopCartEntity.getProductId(),
unpaidOrderEntity.getProductName(), unpaidOrderEntity.getOrderId(),
unpaidOrderEntity.getPayAmount());
} else {
payOrderEntity = doPrepayOrder(
shopCartEntity.getUserId(), shopCartEntity.getProductId(),
unpaidOrderEntity.getProductName(), unpaidOrderEntity.getOrderId(),
unpaidOrderEntity.getTotalAmount());
}
return PayOrderEntity.builder()
.orderId(payOrderEntity.getOrderId())
.payUrl(payOrderEntity.getPayUrl())
.build();
}
// 2. 调用产品服务查询商品详细信息
// 2. 查询商品信息
ProductEntity productEntity = port.queryProductByProductId(shopCartEntity.getProductId());
// 3. 构建基础 OrderEntity orderId时间等
OrderEntity orderEntity = CreateOrderAggregate.buildOrderEntity(productEntity.getProductId(), productEntity.getProductName());
// 4. 组装聚合根聚合中包含用户商品和待持久化的订单实体
// 3. 构建新的订单实体并保存
OrderEntity orderEntity = CreateOrderAggregate.buildOrderEntity(
productEntity.getProductId(),
productEntity.getProductName(),
shopCartEntity.getMarketTypeVO().getCode());
CreateOrderAggregate orderAggregate = CreateOrderAggregate.builder()
.userId(shopCartEntity.getUserId())
.productEntity(productEntity)
.orderEntity(orderEntity)
.build();
// 5. 交由子类实现保存订单聚合
this.doSaveOrder(orderAggregate);
PayOrderEntity payOrderEntity = doPrepayOrder(shopCartEntity.getUserId(), productEntity.getProductId(), productEntity.getProductName(), orderEntity.getOrderId(), productEntity.getPrice());
log.info("创建订单-完成生成支付单。userId: {} orderId: {} payUrl: {}", shopCartEntity.getUserId(), orderEntity.getOrderId(), payOrderEntity.getPayUrl());
// 4. 如果是拼团发起营销锁单
MarketPayDiscountEntity marketPayDiscountEntity = null;
if (MarketTypeVO.GROUP_BUY_MARKET.equals(shopCartEntity.getMarketTypeVO())) {
marketPayDiscountEntity = this.lockMarketPayOrder(
shopCartEntity.getUserId(), shopCartEntity.getTeamId(),
shopCartEntity.getActivityId(), shopCartEntity.getProductId(),
orderEntity.getOrderId());
}
// 5. 创建支付订单
PayOrderEntity payOrderEntity = doPrepayOrder(
shopCartEntity.getUserId(),
productEntity.getProductId(),
productEntity.getProductName(),
orderEntity.getOrderId(),
productEntity.getPrice(),
marketPayDiscountEntity);
log.info("创建订单-完成生成支付单。userId: {} orderId: {} payUrl: {}",
shopCartEntity.getUserId(), orderEntity.getOrderId(), payOrderEntity.getPayUrl());
// 6. 返回支付实体
return PayOrderEntity.builder()
.orderId(orderEntity.getOrderId())
.payUrl(payOrderEntity.getPayUrl())
@ -81,5 +121,10 @@ public abstract class AbstractOrderService implements IOrderService {
*/
protected abstract void doSaveOrder(CreateOrderAggregate orderAggregate);
protected abstract MarketPayDiscountEntity lockMarketPayOrder(String userId, String teamId, Long activityId, String productId, String orderId);
protected abstract PayOrderEntity doPrepayOrder(String userId, String productId, String productName, String orderId, BigDecimal totalAmount) throws AlipayApiException;
protected abstract PayOrderEntity doPrepayOrder(String userId, String productId, String productName, String orderId, BigDecimal totalAmount, MarketPayDiscountEntity marketPayDiscountEntity) throws AlipayApiException;
}

View File

@ -6,7 +6,9 @@ import com.alipay.api.request.AlipayTradePagePayRequest;
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.PayOrderEntity;
import edu.whut.domain.order.model.valobj.MarketTypeVO;
import edu.whut.domain.order.model.valobj.OrderStatusVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
@ -37,18 +39,31 @@ public class OrderService extends AbstractOrderService{
repository.doSaveOrder(orderAggregate);
}
@Override
protected MarketPayDiscountEntity lockMarketPayOrder(String userId, String teamId, Long activityId, String productId, String orderId) {
return port.lockMarketPayOrder(userId, teamId, activityId, productId, orderId);
}
/**
* 预支付订单
*/
@Override
protected PayOrderEntity doPrepayOrder(String userId, String productId, String productName, String orderId, BigDecimal totalAmount) throws AlipayApiException {
return doPrepayOrder(userId, productId, productName, orderId, totalAmount, null);
}
@Override
protected PayOrderEntity doPrepayOrder(String userId, String productId, String productName, String orderId, BigDecimal totalAmount, MarketPayDiscountEntity marketPayDiscountEntity) throws AlipayApiException {
// 支付金额
BigDecimal payAmount = null == marketPayDiscountEntity ? totalAmount : marketPayDiscountEntity.getPayPrice();
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setNotifyUrl(notifyUrl);
request.setReturnUrl(returnUrl);
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", orderId);
bizContent.put("total_amount", totalAmount.toString());
bizContent.put("total_amount", payAmount);
bizContent.put("subject", productName);
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
request.setBizContent(bizContent.toString());
@ -58,9 +73,13 @@ public class OrderService extends AbstractOrderService{
PayOrderEntity payOrderEntity = new PayOrderEntity();
payOrderEntity.setOrderId(orderId);
payOrderEntity.setPayUrl(form);
//等待支付
payOrderEntity.setOrderStatus(OrderStatusVO.PAY_WAIT);
// 营销信息
payOrderEntity.setMarketType(null == marketPayDiscountEntity ? MarketTypeVO.NO_MARKET.getCode() : MarketTypeVO.GROUP_BUY_MARKET.getCode());
payOrderEntity.setMarketDeductionAmount(null == marketPayDiscountEntity ? BigDecimal.ZERO : marketPayDiscountEntity.getDeductionPrice());
payOrderEntity.setPayAmount(payAmount);
repository.updateOrderPayInfo(payOrderEntity);
return payOrderEntity;

View File

@ -30,6 +30,11 @@
<groupId>com.squareup.retrofit2</groupId>
<artifactId>adapter-rxjava2</artifactId>
</dependency>
<dependency>
<groupId>edu.whut</groupId>
<artifactId>group-buying-sys-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 系统模块 -->
<dependency>
<groupId>edu.whut</groupId>

View File

@ -1,29 +1,97 @@
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.response.Response;
import edu.whut.domain.order.adapter.port.IProductPort;
import edu.whut.domain.order.model.entity.MarketPayDiscountEntity;
import edu.whut.domain.order.model.entity.ProductEntity;
import edu.whut.infrastructure.gateway.IGroupBuyMarketService;
import edu.whut.infrastructure.gateway.ProductRPC;
import edu.whut.infrastructure.gateway.dto.ProductDTO;
import edu.whut.types.exception.AppException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import retrofit2.Call;
@Component
@RequiredArgsConstructor
@Slf4j
public class ProductPort implements IProductPort {
@Value("${app.config.group-buy-market.source}")
private String source;
@Value("${app.config.group-buy-market.chanel}")
private String chanel;
@Value("${app.config.group-buy-market.notify-url}")
private String notifyUrl;
private final IGroupBuyMarketService groupBuyMarketService;
private final ProductRPC productRPC;
public ProductPort(ProductRPC productRPC) {
this.productRPC = productRPC;
}
/**
* 根据产品ID查询基础商品信息
*/
@Override
public ProductEntity queryProductByProductId(String productId) {
ProductDTO productDTO = productRPC.queryProductByProductId(productId);
// 调用远程产品服务获取 DTO
ProductDTO dto = productRPC.queryProductByProductId(productId);
// 转换为领域实体并返回
return ProductEntity.builder()
.productId(productDTO.getProductId())
.productName(productDTO.getProductName())
.productDesc(productDTO.getProductDesc())
.price(productDTO.getPrice())
.productId(dto.getProductId())
.productName(dto.getProductName())
.productDesc(dto.getProductDesc())
.price(dto.getPrice())
.build();
}
/**
* 发起营销锁单请求获取拼团优惠信息
*/
@Override
public MarketPayDiscountEntity lockMarketPayOrder(String userId, String teamId, Long activityId, String productId, String orderId) {
// 请求参数
LockMarketPayOrderRequestDTO requestDTO = new LockMarketPayOrderRequestDTO();
requestDTO.setUserId(userId);
requestDTO.setTeamId(teamId);
requestDTO.setGoodsId(productId);
requestDTO.setActivityId(activityId);
requestDTO.setSource(source);
requestDTO.setChannel(chanel);
requestDTO.setOutTradeNo(orderId);
requestDTO.setNotifyUrl(notifyUrl);
try {
// 发起 HTTP 请求执行营销锁单
Call<Response<LockMarketPayOrderResponseDTO>> call = groupBuyMarketService.lockMarketPayOrder(requestDTO);
// 获取结果
Response<LockMarketPayOrderResponseDTO> response = call.execute().body();
log.info("营销锁单{} requestDTO:{} responseDTO:{}", userId, JSON.toJSONString(requestDTO), JSON.toJSONString(response));
if (null == response) return null;
// 异常判断
if (!"0000".equals(response.getCode())){
throw new AppException(response.getCode(), response.getInfo());
}
LockMarketPayOrderResponseDTO responseDTO = response.getData();
// 获取拼团优惠
return MarketPayDiscountEntity.builder()
.originalPrice(responseDTO.getOriginalPrice())
.deductionPrice(responseDTO.getDeductionPrice())
.payPrice(responseDTO.getPayPrice())
.build();
} catch (Exception e) {
log.error("营销锁单失败{}", userId, e);
return null;
}
}
}

View File

@ -8,6 +8,7 @@ import edu.whut.domain.order.model.entity.OrderEntity;
import edu.whut.domain.order.model.entity.PayOrderEntity;
import edu.whut.domain.order.model.entity.ProductEntity;
import edu.whut.domain.order.model.entity.ShopCartEntity;
import edu.whut.domain.order.model.valobj.MarketTypeVO;
import edu.whut.domain.order.model.valobj.OrderStatusVO;
import edu.whut.infrastructure.dao.IOrderDao;
import edu.whut.infrastructure.dao.po.PayOrder;
@ -16,6 +17,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
@Repository
@ -46,6 +48,10 @@ public class OrderRepository implements IOrderRepository {
order.setOrderTime(orderEntity.getOrderTime());
order.setTotalAmount(productEntity.getPrice());
order.setStatus(orderEntity.getOrderStatusVO().getCode());
order.setMarketType(MarketTypeVO.NO_MARKET.getCode());
order.setMarketDeductionAmount(BigDecimal.ZERO);
order.setPayAmount(productEntity.getPrice());
order.setMarketType(orderEntity.getMarketType());
// 插入数据库
orderDao.insert(order);
@ -74,6 +80,9 @@ public class OrderRepository implements IOrderRepository {
.orderTime(order.getOrderTime())
.totalAmount(order.getTotalAmount())
.payUrl(order.getPayUrl())
.marketType(order.getMarketType())
.marketDeductionAmount(order.getMarketDeductionAmount())
.payAmount(order.getPayAmount())
.build();
}
@ -88,6 +97,9 @@ public class OrderRepository implements IOrderRepository {
.orderId(payOrderEntity.getOrderId())
.status(payOrderEntity.getOrderStatus().getCode())
.payUrl(payOrderEntity.getPayUrl())
.marketType(payOrderEntity.getMarketType())
.marketDeductionAmount(payOrderEntity.getMarketDeductionAmount())
.payAmount(payOrderEntity.getPayAmount())
.build();
orderDao.updateOrderPayInfo(payOrderReq);
}

View File

@ -14,17 +14,35 @@ import java.util.Date;
@NoArgsConstructor
public class PayOrder {
// 自增ID
private Long id;
// 用户ID
private String userId;
// 商品ID
private String productId;
// 商品名称
private String productName;
// 订单ID
private String orderId;
// 下单时间
private Date orderTime;
// 订单金额
private BigDecimal totalAmount;
// 订单状态create-创建完成pay_wait-等待支付pay_success-支付成功deal_done-交易完成close-订单关单
private String status;
// 支付信息
private String payUrl;
// 支付时间
private Date payTime;
// 营销类型0无营销1拼团营销
private Integer marketType;
// 营销金额优惠金额
private BigDecimal marketDeductionAmount;
// 支付金额
private BigDecimal payAmount;
// 创建时间
private Date createTime;
// 更新时间
private Date updateTime;
}

View File

@ -0,0 +1,24 @@
package edu.whut.infrastructure.gateway;
import edu.whut.api.dto.LockMarketPayOrderRequestDTO;
import edu.whut.api.dto.LockMarketPayOrderResponseDTO;
import edu.whut.api.response.Response;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
/**
* 拼团营销
*/
public interface IGroupBuyMarketService {
/**
* 营销锁单
*
* @param requestDTO 锁单商品信息
* @return 锁单结果信息
*/
@POST("api/v1/gbm/trade/lock_market_pay_order")
Call<Response<LockMarketPayOrderResponseDTO>> lockMarketPayOrder(@Body LockMarketPayOrderRequestDTO requestDTO);
}

View File

@ -14,9 +14,9 @@ public class ProductRPC {
public ProductDTO queryProductByProductId(String productId){
ProductDTO productVO = new ProductDTO();
productVO.setProductId(productId);
productVO.setProductName("测试商品");
productVO.setProductDesc("这是一个测试商品");
productVO.setPrice(new BigDecimal("1.68"));
productVO.setProductName("MyBatisBook");
productVO.setProductDesc("MyBatisBook");
productVO.setPrice(new BigDecimal("100.00"));
return productVO;
}

View File

@ -6,14 +6,13 @@ import edu.whut.api.dto.CreatePayRequestDTO;
import edu.whut.api.response.Response;
import edu.whut.domain.order.model.entity.PayOrderEntity;
import edu.whut.domain.order.model.entity.ShopCartEntity;
import edu.whut.domain.order.model.valobj.MarketTypeVO;
import edu.whut.domain.order.service.IOrderService;
import edu.whut.types.common.Constants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@ -43,10 +42,14 @@ public class AliPayController implements IPayService {
log.info("商品下单根据商品ID创建支付单开始 userId:{} productId:{}", createPayRequestDTO.getUserId(), createPayRequestDTO.getUserId());
String userId = createPayRequestDTO.getUserId();
String productId = createPayRequestDTO.getProductId();
String teamId = createPayRequestDTO.getTeamId();
Integer marketType = createPayRequestDTO.getMarketType();
// 下单
PayOrderEntity payOrderEntity = orderService.createOrder(ShopCartEntity.builder()
.userId(userId)
.productId(productId)
.teamId(teamId)
.marketTypeVO(MarketTypeVO.valueOf(marketType))
.build());
log.info("商品下单根据商品ID创建支付单完成 userId:{} productId:{} orderId:{}", userId, productId, payOrderEntity.getOrderId());