diff --git a/docs/dev-ops/docker-compose-environment-aliyun.yml b/docs/dev-ops/docker-compose-environment-aliyun.yml
index 87bdbe0..65c1595 100644
--- a/docs/dev-ops/docker-compose-environment-aliyun.yml
+++ b/docs/dev-ops/docker-compose-environment-aliyun.yml
@@ -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
diff --git a/docs/dev-ops/docker-compose-environment.yml b/docs/dev-ops/docker-compose-environment.yml
index 2a270b8..82ff19d 100644
--- a/docs/dev-ops/docker-compose-environment.yml
+++ b/docs/dev-ops/docker-compose-environment.yml
@@ -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
diff --git a/docs/dev-ops/mysql/my.cnf b/docs/dev-ops/mysql/my.cnf
new file mode 100644
index 0000000..0768a14
--- /dev/null
+++ b/docs/dev-ops/mysql/my.cnf
@@ -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
\ No newline at end of file
diff --git a/docs/dev-ops/mysql/sql/0713paymall.sql b/docs/dev-ops/mysql/sql/0713paymall.sql
index 4ef4bc0..a7b24d6 100644
--- a/docs/dev-ops/mysql/sql/0713paymall.sql
+++ b/docs/dev-ops/mysql/sql/0713paymall.sql
@@ -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', '
\n', 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', '\n', 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', '\n', NULL, 1, 20.00, 80.00, '2025-07-15 14:35:56', '2025-07-15 14:35:57');
SET FOREIGN_KEY_CHECKS = 1;
diff --git a/pay-mall-api/src/main/java/edu/whut/api/dto/CreatePayRequestDTO.java b/pay-mall-api/src/main/java/edu/whut/api/dto/CreatePayRequestDTO.java
index 3473ab6..15c3394 100644
--- a/pay-mall-api/src/main/java/edu/whut/api/dto/CreatePayRequestDTO.java
+++ b/pay-mall-api/src/main/java/edu/whut/api/dto/CreatePayRequestDTO.java
@@ -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;
}
diff --git a/pay-mall-app/src/main/java/edu/whut/config/Retrofit2Config.java b/pay-mall-app/src/main/java/edu/whut/config/Retrofit2Config.java
index 7fe223c..cc8f328 100644
--- a/pay-mall-app/src/main/java/edu/whut/config/Retrofit2Config.java
+++ b/pay-mall-app/src/main/java/edu/whut/config/Retrofit2Config.java
@@ -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);
+ }
+
}
diff --git a/pay-mall-app/src/main/resources/application-dev.yml b/pay-mall-app/src/main/resources/application-dev.yml
index 617cb4d..7675090 100644
--- a/pay-mall-app/src/main/resources/application-dev.yml
+++ b/pay-mall-app/src/main/resources/application-dev.yml
@@ -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:
diff --git a/pay-mall-app/src/main/resources/mybatis/mapper/pay_order_mapper.xml b/pay-mall-app/src/main/resources/mybatis/mapper/pay_order_mapper.xml
index a0ef541..9042ba3 100644
--- a/pay-mall-app/src/main/resources/mybatis/mapper/pay_order_mapper.xml
+++ b/pay-mall-app/src/main/resources/mybatis/mapper/pay_order_mapper.xml
@@ -13,15 +13,18 @@
+
+
+
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())
- 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}
diff --git a/pay-mall-app/src/test/java/edu/whut/test/domain/OrderServiceTest.java b/pay-mall-app/src/test/java/edu/whut/test/domain/OrderServiceTest.java
index 45d147c..063d2ce 100644
--- a/pay-mall-app/src/test/java/edu/whut/test/domain/OrderServiceTest.java
+++ b/pay-mall-app/src/test/java/edu/whut/test/domain/OrderServiceTest.java
@@ -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));
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/port/IProductPort.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/port/IProductPort.java
index 33bc1a3..3fd0e55 100644
--- a/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/port/IProductPort.java
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/adapter/port/IProductPort.java
@@ -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);
+
+
}
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/model/aggregate/CreateOrderAggregate.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/aggregate/CreateOrderAggregate.java
index e34120a..0535bcd 100644
--- a/pay-mall-domain/src/main/java/edu/whut/domain/order/model/aggregate/CreateOrderAggregate.java
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/aggregate/CreateOrderAggregate.java
@@ -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();
}
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/MarketPayDiscountEntity.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/MarketPayDiscountEntity.java
new file mode 100644
index 0000000..da118f3
--- /dev/null
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/MarketPayDiscountEntity.java
@@ -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;
+
+}
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/OrderEntity.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/OrderEntity.java
index bb3fb2c..4ecdfcb 100644
--- a/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/OrderEntity.java
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/OrderEntity.java
@@ -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;
}
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/PayOrderEntity.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/PayOrderEntity.java
index ded0f1a..7a9246d 100644
--- a/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/PayOrderEntity.java
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/PayOrderEntity.java
@@ -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;
+
}
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/ShopCartEntity.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/ShopCartEntity.java
index 6dd7f1a..fbdd3ab 100644
--- a/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/ShopCartEntity.java
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/entity/ShopCartEntity.java
@@ -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;
+
}
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/model/valobj/MarketTypeVO.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/valobj/MarketTypeVO.java
new file mode 100644
index 0000000..0e9ab1e
--- /dev/null
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/model/valobj/MarketTypeVO.java
@@ -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!");
+ }
+
+}
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/service/AbstractOrderService.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/service/AbstractOrderService.java
index 78ae7ec..0d38bad 100644
--- a/pay-mall-domain/src/main/java/edu/whut/domain/order/service/AbstractOrderService.java
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/service/AbstractOrderService.java
@@ -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;
+
}
diff --git a/pay-mall-domain/src/main/java/edu/whut/domain/order/service/OrderService.java b/pay-mall-domain/src/main/java/edu/whut/domain/order/service/OrderService.java
index 584a055..6335b3b 100644
--- a/pay-mall-domain/src/main/java/edu/whut/domain/order/service/OrderService.java
+++ b/pay-mall-domain/src/main/java/edu/whut/domain/order/service/OrderService.java
@@ -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;
diff --git a/pay-mall-infrastructure/pom.xml b/pay-mall-infrastructure/pom.xml
index 51919a5..293f11c 100644
--- a/pay-mall-infrastructure/pom.xml
+++ b/pay-mall-infrastructure/pom.xml
@@ -30,6 +30,11 @@
com.squareup.retrofit2
adapter-rxjava2
+
+ edu.whut
+ group-buying-sys-api
+ 1.0-SNAPSHOT
+
edu.whut
diff --git a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/port/ProductPort.java b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/port/ProductPort.java
index ac6b61e..960e23a 100644
--- a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/port/ProductPort.java
+++ b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/port/ProductPort.java
@@ -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> call = groupBuyMarketService.lockMarketPayOrder(requestDTO);
+
+ // 获取结果
+ Response 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;
+ }
+ }
+
}
diff --git a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/repository/OrderRepository.java b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/repository/OrderRepository.java
index 3fb327b..5db7252 100644
--- a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/repository/OrderRepository.java
+++ b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/adapter/repository/OrderRepository.java
@@ -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);
}
diff --git a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/dao/po/PayOrder.java b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/dao/po/PayOrder.java
index 0d0002a..0dd7021 100644
--- a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/dao/po/PayOrder.java
+++ b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/dao/po/PayOrder.java
@@ -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;
}
diff --git a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/gateway/IGroupBuyMarketService.java b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/gateway/IGroupBuyMarketService.java
new file mode 100644
index 0000000..24f4388
--- /dev/null
+++ b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/gateway/IGroupBuyMarketService.java
@@ -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> lockMarketPayOrder(@Body LockMarketPayOrderRequestDTO requestDTO);
+
+}
diff --git a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/gateway/ProductRPC.java b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/gateway/ProductRPC.java
index 069a43e..ac1b7ad 100644
--- a/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/gateway/ProductRPC.java
+++ b/pay-mall-infrastructure/src/main/java/edu/whut/infrastructure/gateway/ProductRPC.java
@@ -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;
}
diff --git a/pay-mall-trigger/src/main/java/edu/whut/trigger/http/AliPayController.java b/pay-mall-trigger/src/main/java/edu/whut/trigger/http/AliPayController.java
index 62bff4f..347cdd1 100644
--- a/pay-mall-trigger/src/main/java/edu/whut/trigger/http/AliPayController.java
+++ b/pay-mall-trigger/src/main/java/edu/whut/trigger/http/AliPayController.java
@@ -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());