6.22 试算模型抽象模板设计以及多线程数据异步加载

This commit is contained in:
zhangsan 2025-06-22 22:05:07 +08:00
parent c288e5e92e
commit c81b97ece7
40 changed files with 1002 additions and 53 deletions

View File

@ -1,29 +1,8 @@
# ************************************************************
# Sequel Ace SQL dump
# 版本号: 20050
#
# https://sequel-ace.com/
# https://github.com/Sequel-Ace/Sequel-Ace
#
# 主机: 127.0.0.1 (MySQL 5.6.39)
# 数据库: group_buy_market
# 生成时间: 2024-12-07 03:48:52 +0000
# ************************************************************
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
SET NAMES utf8mb4;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE='NO_AUTO_VALUE_ON_ZERO', SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE database if NOT EXISTS `group_buying_sys` default character set utf8mb4 collate utf8mb4_0900_ai_ci;
use `group_buying_sys`;
# 转储表 group_buy_activity
# ------------------------------------------------------------
DROP TABLE IF EXISTS `group_buy_activity`;
@ -55,15 +34,11 @@ LOCK TABLES `group_buy_activity` WRITE;
INSERT INTO `group_buy_activity` (`id`, `activity_id`, `activity_name`, `source`, `channel`, `goods_id`, `discount_id`, `group_type`, `take_limit_count`, `target`, `valid_time`, `status`, `start_time`, `end_time`, `tag_id`, `tag_scope`, `create_time`, `update_time`)
VALUES
(1,100123,'测试活动','s01','c01','9890001','25120207',0,1,1,15,0,'2024-12-07 10:19:40','2024-12-07 10:19:40','1','1','2024-12-07 10:19:40','2024-12-07 11:47:27');
(1,100123,'测试活动','s01','c01','9890001','25120207',0,1,1,15,0,'2025-06-19 10:19:40','2025-06-19 10:19:40','1','1','2025-06-19 10:19:40','2025-06-19 11:47:27');
/*!40000 ALTER TABLE `group_buy_activity` ENABLE KEYS */;
UNLOCK TABLES;
# 转储表 group_buy_discount
# ------------------------------------------------------------
DROP TABLE IF EXISTS `group_buy_discount`;
CREATE TABLE `group_buy_discount` (
@ -82,20 +57,35 @@ CREATE TABLE `group_buy_discount` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
LOCK TABLES `group_buy_discount` WRITE;
/*!40000 ALTER TABLE `group_buy_discount` DISABLE KEYS */;
INSERT INTO `group_buy_discount` (`id`, `discount_id`, `discount_name`, `discount_desc`, `discount_type`, `market_plan`, `market_expr`, `tag_id`, `create_time`, `update_time`)
VALUES
(1,25120207,'测试优惠','测试优惠',0,'ZJ','20',NULL,'2024-12-07 10:20:15','2024-12-07 10:20:15');
(1,25120207,'测试优惠','测试优惠',0,'ZJ','20',NULL,'2025-06-19 10:20:15','2025-06-19 10:20:15');
/*!40000 ALTER TABLE `group_buy_discount` ENABLE KEYS */;
UNLOCK TABLES;
DROP TABLE IF EXISTS `sku`;
CREATE TABLE `sku` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`source` varchar(8) NOT NULL COMMENT '渠道',
`channel` varchar(8) NOT NULL COMMENT '来源',
`goods_id` varchar(16) NOT NULL COMMENT '商品ID',
`goods_name` varchar(128) NOT NULL COMMENT '商品名称',
`original_price` decimal(10,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`),
UNIQUE KEY `uq_goods_id` (`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品信息';
LOCK TABLES `sku` WRITE;
INSERT INTO `sku` (`id`, `source`, `channel`, `goods_id`, `goods_name`, `original_price`, `create_time`, `update_time`)
VALUES
(1,'s01','c01','9890001','《手写MyBatis渐进式源码实践》',100.00,'2025-06-22 11:10:06','2025-06-22 11:10:06');
UNLOCK TABLES;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

View File

@ -0,0 +1,70 @@
SET NAMES utf8mb4;
CREATE database if NOT EXISTS `group_buying_sys` default character set utf8mb4 collate utf8mb4_0900_ai_ci;
use `group_buying_sys`;
# 转储表 group_buy_activity
# ------------------------------------------------------------
DROP TABLE IF EXISTS `group_buy_activity`;
CREATE TABLE `group_buy_activity` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增',
`activity_id` bigint(8) NOT NULL COMMENT '活动ID',
`activity_name` varchar(128) NOT NULL COMMENT '活动名称',
`source` varchar(8) NOT NULL COMMENT '来源',
`channel` varchar(8) NOT NULL COMMENT '渠道',
`goods_id` varchar(12) NOT NULL COMMENT '商品ID',
`discount_id` varchar(8) NOT NULL COMMENT '折扣ID',
`group_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '拼团方式0自动成团、1达成目标拼团',
`take_limit_count` int(4) NOT NULL DEFAULT '1' COMMENT '拼团次数限制',
`target` int(5) NOT NULL DEFAULT '1' COMMENT '拼团目标',
`valid_time` int(4) NOT NULL DEFAULT '15' COMMENT '拼团时长(分钟)',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '活动状态0创建、1生效、2过期、3废弃',
`start_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '活动开始时间',
`end_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '活动结束时间',
`tag_id` varchar(8) DEFAULT NULL COMMENT '人群标签规则标识',
`tag_scope` varchar(4) DEFAULT NULL COMMENT '人群标签规则范围多选1可见限制、2参与限制',
`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`),
UNIQUE KEY `uq_activity_id` (`activity_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='拼团活动';
LOCK TABLES `group_buy_activity` WRITE;
/*!40000 ALTER TABLE `group_buy_activity` DISABLE KEYS */;
INSERT INTO `group_buy_activity` (`id`, `activity_id`, `activity_name`, `source`, `channel`, `goods_id`, `discount_id`, `group_type`, `take_limit_count`, `target`, `valid_time`, `status`, `start_time`, `end_time`, `tag_id`, `tag_scope`, `create_time`, `update_time`)
VALUES
(1,100123,'测试活动','s01','c01','9890001','25120207',0,1,1,15,0,'2025-06-19 10:19:40','2025-06-19 10:19:40','1','1','2025-06-19 10:19:40','2025-06-19 11:47:27');
UNLOCK TABLES;
# 转储表 group_buy_discount
# ------------------------------------------------------------
DROP TABLE IF EXISTS `group_buy_discount`;
CREATE TABLE `group_buy_discount` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`discount_id` int(8) NOT NULL COMMENT '折扣ID',
`discount_name` varchar(64) NOT NULL COMMENT '折扣标题',
`discount_desc` varchar(256) NOT NULL COMMENT '折扣描述',
`discount_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '折扣类型0:base、1:tag',
`market_plan` varchar(4) NOT NULL DEFAULT 'ZJ' COMMENT '营销优惠计划ZJ:直减、MJ:满减、N元购',
`market_expr` varchar(32) NOT NULL COMMENT '营销优惠表达式',
`tag_id` varchar(8) DEFAULT 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`),
UNIQUE KEY `uq_discount_id` (`discount_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
LOCK TABLES `group_buy_discount` WRITE;
INSERT INTO `group_buy_discount` (`id`, `discount_id`, `discount_name`, `discount_desc`, `discount_type`, `market_plan`, `market_expr`, `tag_id`, `create_time`, `update_time`)
VALUES
(1,25120207,'测试优惠','测试优惠',0,'ZJ','20',NULL,'2025-06-19 10:20:15','2025-06-19 10:20:15');
UNLOCK TABLES;

View File

@ -27,4 +27,15 @@
select * from group_buy_activity
</select>
<select id="queryValidGroupBuyActivity" parameterType="edu.whut.infrastructure.dao.po.GroupBuyActivity" resultMap="dataMap">
select
activity_id, activity_name, source, channel, goods_id,
discount_id, group_type, take_limit_count, target, valid_time,
status, start_time, end_time, tag_id, tag_scope
from group_buy_activity
where source = #{source} and channel = #{channel}
order by id desc
limit 1
</select>
</mapper>

View File

@ -19,4 +19,10 @@
select * from group_buy_discount
</select>
<select id="queryGroupBuyActivityDiscountByDiscountId" parameterType="java.lang.String" resultMap="dataMap">
select discount_id, discount_name, discount_desc, discount_type, market_plan, market_expr, tag_id
from group_buy_discount
where discount_id = #{discountId}
</select>
</mapper>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="edu.whut.infrastructure.dao.ISkuDao">
<resultMap id="dataMap" type="edu.whut.infrastructure.dao.po.Sku">
<id column="id" property="id"/>
<result column="source" property="source"/>
<result column="channel" property="channel"/>
<result column="goods_id" property="goodsId"/>
<result column="goods_name" property="goodsName"/>
<result column="original_price" property="originalPrice"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<select id="querySkuByGoodsId" parameterType="java.lang.String" resultMap="dataMap">
select source, channel, goods_id, goods_name, original_price
from sku
where goods_id = #{goodsId}
</select>
</mapper>

View File

@ -0,0 +1,39 @@
package edu.whut.test.activity;
import com.alibaba.fastjson.JSON;
import edu.whut.domain.activity.model.entity.MarketProductEntity;
import edu.whut.domain.activity.model.entity.TrialBalanceEntity;
import edu.whut.domain.activity.service.IIndexGroupBuyMarketService;
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;
/**
* 首页营销服务接口测试
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class IIndexGroupBuyMarketServiceTest {
@Resource
private IIndexGroupBuyMarketService indexGroupBuyMarketService;
@Test
public void test_indexMarketTrial() throws Exception {
MarketProductEntity marketProductEntity = new MarketProductEntity();
marketProductEntity.setUserId("xiaofuge");
marketProductEntity.setSource("s01");
marketProductEntity.setChannel("c01");
marketProductEntity.setGoodsId("9890001");
TrialBalanceEntity trialBalanceEntity = indexGroupBuyMarketService.indexMarketTrial(marketProductEntity);
log.info("请求参数:{}", JSON.toJSONString(marketProductEntity));
log.info("返回结果:{}", JSON.toJSONString(trialBalanceEntity));
}
}

View File

@ -1,4 +1,4 @@
/**
* 外部接口适配器层当需要调用外部接口时则创建出这一层并定义接口之后由基础设施层的 adapter 层具体实现
*/
package edu.whut.domain.xxx.adapter;
package edu.whut.domain.activity.adapter;

View File

@ -1,4 +1,4 @@
/**
* 外部接口适配器层当需要调用外部接口时则创建出这一层并定义接口之后由基础设施层的 adapter 层具体实现
*/
package edu.whut.domain.xxx.adapter.port;
package edu.whut.domain.activity.adapter.port;

View File

@ -0,0 +1,14 @@
package edu.whut.domain.activity.adapter.repository;
import edu.whut.domain.activity.model.valobj.GroupBuyActivityDiscountVO;
import edu.whut.domain.activity.model.valobj.SkuVO;
/**
* @description 活动仓储
*/
public interface IActivityRepository {
GroupBuyActivityDiscountVO queryGroupBuyActivityDiscountVO(String source, String channel);
SkuVO querySkuByGoodsId(String goodsId);
}

View File

@ -2,4 +2,4 @@
* 仓储服务
* 1. 定义仓储接口之后由基础设施层做具体实现
*/
package edu.whut.domain.xxx.adapter.repository;
package edu.whut.domain.activity.adapter.repository;

View File

@ -4,4 +4,4 @@
* 2. 聚合是聚合的对象和提供基础处理对象的方法但不建议在聚合中引入仓储和接口来做过大的逻辑而这些复杂的操作应该放到service中处理
* 3. 对象名称 XxxAggregate
*/
package edu.whut.domain.xxx.model.aggregate;
package edu.whut.domain.activity.model.aggregate;

View File

@ -0,0 +1,26 @@
package edu.whut.domain.activity.model.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 营销商品实体信息通过这样一个信息获取商品优惠信息
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MarketProductEntity {
/** 用户ID */
private String userId;
/** 商品ID */
private String goodsId;
/** 渠道 */
private String source;
/** 来源 */
private String channel;
}

View File

@ -0,0 +1,39 @@
package edu.whut.domain.activity.model.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.Date;
/**
* 试算结果实体对象给用户展示拼团可获得的优惠信息
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TrialBalanceEntity {
/** 商品ID */
private String goodsId;
/** 商品名称 */
private String goodsName;
/** 原始价格 */
private BigDecimal originalPrice;
/** 折扣价格 */
private BigDecimal deductionPrice;
/** 拼团目标数量 */
private Integer targetCount;
/** 拼团开始时间 */
private Date startTime;
/** 拼团结束时间 */
private Date endTime;
/** 是否可见拼团 */
private Boolean isVisible;
/** 是否可参与进团 */
private Boolean isEnable;
}

View File

@ -0,0 +1,116 @@
package edu.whut.domain.activity.model.valobj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* 拼团活动营销配置值对象
*/
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class GroupBuyActivityDiscountVO {
/**
* 活动ID
*/
private Long activityId;
/**
* 活动名称
*/
private String activityName;
/**
* 来源
*/
private String source;
/**
* 渠道
*/
private String channel;
/**
* 商品ID
*/
private String goodsId;
/**
* 折扣配置
*/
private GroupBuyDiscount groupBuyDiscount;
/**
* 拼团方式0自动成团1达成目标拼团
*/
private Integer groupType;
/**
* 拼团次数限制
*/
private Integer takeLimitCount;
/**
* 拼团目标
*/
private Integer target;
/**
* 拼团时长分钟
*/
private Integer validTime;
/**
* 活动状态0创建1生效2过期3废弃
*/
private Integer status;
/**
* 活动开始时间
*/
private Date startTime;
/**
* 活动结束时间
*/
private Date endTime;
/**
* 人群标签规则标识
*/
private String tagId;
/**
* 人群标签规则范围
*/
private String tagScope;
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class GroupBuyDiscount {
/**
* 折扣标题
*/
private String discountName;
/**
* 折扣描述
*/
private String discountDesc;
/**
* 折扣类型0:base1:tag
*/
private Byte discountType;
/**
* 营销优惠计划ZJ:直减MJ:满减N元购
*/
private String marketPlan;
/**
* 营销优惠表达式
*/
private String marketExpr;
/**
* 人群标签特定优惠限定
*/
private String tagId;
}
}

View File

@ -0,0 +1,26 @@
package edu.whut.domain.activity.model.valobj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* 商品信息
*/
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SkuVO {
/** 商品ID */
private String goodsId;
/** 商品名称 */
private String goodsName;
/** 原始价格 */
private BigDecimal originalPrice;
}

View File

@ -0,0 +1,13 @@
package edu.whut.domain.activity.service;
import edu.whut.domain.activity.model.entity.MarketProductEntity;
import edu.whut.domain.activity.model.entity.TrialBalanceEntity;
/**
* 首页营销服务接口
*/
public interface IIndexGroupBuyMarketService {
TrialBalanceEntity indexMarketTrial(MarketProductEntity marketProductEntity) throws Exception;
}

View File

@ -0,0 +1,29 @@
package edu.whut.domain.activity.service;
import edu.whut.domain.activity.model.entity.MarketProductEntity;
import edu.whut.domain.activity.model.entity.TrialBalanceEntity;
import edu.whut.domain.activity.service.trial.factory.DefaultActivityStrategyFactory;
import edu.whut.types.framework.tree.StrategyHandler;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 首页营销服务
*/
@Service
public class IndexGroupBuyMarketServiceImpl implements IIndexGroupBuyMarketService {
@Resource
private DefaultActivityStrategyFactory defaultActivityStrategyFactory;
@Override
public TrialBalanceEntity indexMarketTrial(MarketProductEntity marketProductEntity) throws Exception {
StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> strategyHandler = defaultActivityStrategyFactory.strategyHandler();
TrialBalanceEntity trialBalanceEntity = strategyHandler.apply(marketProductEntity, new DefaultActivityStrategyFactory.DynamicContext());
return trialBalanceEntity;
}
}

View File

@ -0,0 +1 @@
package edu.whut.domain.activity.service;

View File

@ -0,0 +1,27 @@
package edu.whut.domain.activity.service.trial;
import edu.whut.domain.activity.adapter.repository.IActivityRepository;
import edu.whut.domain.activity.model.entity.MarketProductEntity;
import edu.whut.domain.activity.model.entity.TrialBalanceEntity;
import edu.whut.domain.activity.service.trial.factory.DefaultActivityStrategyFactory;
import edu.whut.types.framework.tree.AbstractMultiThreadStrategyRouter;
import javax.annotation.Resource;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
/**
* 抽象的拼团营销支撑类
*/
public abstract class AbstractGroupBuyMarketSupport<MarketProductEntity, DynamicContext, TrialBalanceEntity> extends AbstractMultiThreadStrategyRouter<edu.whut.domain.activity.model.entity.MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, edu.whut.domain.activity.model.entity.TrialBalanceEntity> {
protected long timeout = 500;
@Resource
protected IActivityRepository repository;
@Override
protected void multiThread(edu.whut.domain.activity.model.entity.MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws ExecutionException, InterruptedException, TimeoutException {
// 缺省的方法
}
}

View File

@ -0,0 +1,41 @@
package edu.whut.domain.activity.service.trial.factory;
import edu.whut.domain.activity.model.entity.MarketProductEntity;
import edu.whut.domain.activity.model.entity.TrialBalanceEntity;
import edu.whut.domain.activity.model.valobj.GroupBuyActivityDiscountVO;
import edu.whut.domain.activity.model.valobj.SkuVO;
import edu.whut.domain.activity.service.trial.node.RootNode;
import edu.whut.types.framework.tree.StrategyHandler;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Service;
/**
* 活动策略工厂
*/
@Service
public class DefaultActivityStrategyFactory {
private final RootNode rootNode;
public DefaultActivityStrategyFactory(RootNode rootNode) {
this.rootNode = rootNode;
}
public StrategyHandler<MarketProductEntity, DynamicContext, TrialBalanceEntity> strategyHandler() {
return rootNode;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class DynamicContext {
// 拼团活动营销配置值对象
private GroupBuyActivityDiscountVO groupBuyActivityDiscountVO;
// 商品信息
private SkuVO skuVO;
}
}

View File

@ -0,0 +1,48 @@
package edu.whut.domain.activity.service.trial.node;
import com.alibaba.fastjson.JSON;
import edu.whut.domain.activity.model.entity.MarketProductEntity;
import edu.whut.domain.activity.model.entity.TrialBalanceEntity;
import edu.whut.domain.activity.model.valobj.GroupBuyActivityDiscountVO;
import edu.whut.domain.activity.model.valobj.SkuVO;
import edu.whut.domain.activity.service.trial.AbstractGroupBuyMarketSupport;
import edu.whut.domain.activity.service.trial.factory.DefaultActivityStrategyFactory;
import edu.whut.types.framework.tree.StrategyHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
/**
* 结束节点
*/
@Slf4j
@Service
public class EndNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {
@Override
public TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
log.info("拼团商品查询试算服务-EndNode userId:{} requestParameter:{}", requestParameter.getUserId(), JSON.toJSONString(requestParameter));
GroupBuyActivityDiscountVO groupBuyActivityDiscountVO = dynamicContext.getGroupBuyActivityDiscountVO();
SkuVO skuVO = dynamicContext.getSkuVO();
// 返回空结果
return TrialBalanceEntity.builder()
.goodsId(skuVO.getGoodsId())
.goodsName(skuVO.getGoodsName())
.originalPrice(skuVO.getOriginalPrice())
.deductionPrice(new BigDecimal("0.00"))
.targetCount(groupBuyActivityDiscountVO.getTarget())
.startTime(groupBuyActivityDiscountVO.getStartTime())
.endTime(groupBuyActivityDiscountVO.getEndTime())
.isVisible(false)
.isEnable(false)
.build();
}
@Override
public StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
return defaultStrategyHandler;
}
}

View File

@ -0,0 +1,63 @@
package edu.whut.domain.activity.service.trial.node;
import com.alibaba.fastjson.JSON;
import edu.whut.domain.activity.model.entity.MarketProductEntity;
import edu.whut.domain.activity.model.entity.TrialBalanceEntity;
import edu.whut.domain.activity.model.valobj.GroupBuyActivityDiscountVO;
import edu.whut.domain.activity.model.valobj.SkuVO;
import edu.whut.domain.activity.service.trial.AbstractGroupBuyMarketSupport;
import edu.whut.domain.activity.service.trial.factory.DefaultActivityStrategyFactory;
import edu.whut.domain.activity.service.trial.thread.QueryGroupBuyActivityDiscountVOThreadTask;
import edu.whut.domain.activity.service.trial.thread.QuerySkuVOFromDBThreadTask;
import edu.whut.types.framework.tree.StrategyHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.*;
/**
* 营销优惠节点
*/
@Slf4j
@Service
public class MarketNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {
@Resource
private ThreadPoolExecutor threadPoolExecutor;
@Resource
private EndNode endNode;
@Override
protected void multiThread(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws ExecutionException, InterruptedException, TimeoutException {
// 异步查询活动配置
QueryGroupBuyActivityDiscountVOThreadTask queryGroupBuyActivityDiscountVOThreadTask = new QueryGroupBuyActivityDiscountVOThreadTask(requestParameter.getSource(), requestParameter.getChannel(), repository);
FutureTask<GroupBuyActivityDiscountVO> groupBuyActivityDiscountVOFutureTask = new FutureTask<>(queryGroupBuyActivityDiscountVOThreadTask);
threadPoolExecutor.execute(groupBuyActivityDiscountVOFutureTask);
// 异步查询商品信息 - 在实际生产中商品有同步库或者调用接口查询这里暂时使用DB方式查询
QuerySkuVOFromDBThreadTask querySkuVOFromDBThreadTask = new QuerySkuVOFromDBThreadTask(requestParameter.getGoodsId(), repository);
FutureTask<SkuVO> skuVOFutureTask = new FutureTask<>(querySkuVOFromDBThreadTask);
threadPoolExecutor.execute(skuVOFutureTask);
// 写入上下文 - 对于一些复杂场景获取数据的操作有时候会在下N个节点获取这样前置查询数据可以提高接口响应效率
dynamicContext.setGroupBuyActivityDiscountVO(groupBuyActivityDiscountVOFutureTask.get(timeout, TimeUnit.MINUTES));
dynamicContext.setSkuVO(skuVOFutureTask.get(timeout, TimeUnit.MINUTES));
log.info("拼团商品查询试算服务-MarketNode userId:{} 异步线程加载数据「GroupBuyActivityDiscountVO、SkuVO」完成", requestParameter.getUserId());
}
@Override
public TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
log.info("拼团商品查询试算服务-MarketNode userId:{} requestParameter:{}", requestParameter.getUserId(), JSON.toJSONString(requestParameter));
// todo xfg 拼团优惠试算
return router(requestParameter, dynamicContext);
}
@Override
public StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
return endNode;
}
}

View File

@ -0,0 +1,43 @@
package edu.whut.domain.activity.service.trial.node;
import com.alibaba.fastjson.JSON;
import edu.whut.domain.activity.model.entity.MarketProductEntity;
import edu.whut.domain.activity.model.entity.TrialBalanceEntity;
import edu.whut.domain.activity.service.trial.AbstractGroupBuyMarketSupport;
import edu.whut.domain.activity.service.trial.factory.DefaultActivityStrategyFactory;
import edu.whut.types.enums.ResponseCode;
import edu.whut.types.exception.AppException;
import edu.whut.types.framework.tree.StrategyHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 根节点
*/
@Slf4j
@Service
public class RootNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {
@Resource
private SwitchNode switchNode;
@Override
protected TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
log.info("拼团商品查询试算服务-RootNode userId:{} requestParameter:{}", requestParameter.getUserId(), JSON.toJSONString(requestParameter));
// 参数判断
if (StringUtils.isBlank(requestParameter.getUserId()) || StringUtils.isBlank(requestParameter.getGoodsId()) ||
StringUtils.isBlank(requestParameter.getSource()) || StringUtils.isBlank(requestParameter.getChannel())) {
throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
}
return router(requestParameter, dynamicContext);
}
@Override
public StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
return switchNode;
}
}

View File

@ -0,0 +1,31 @@
package edu.whut.domain.activity.service.trial.node;
import edu.whut.domain.activity.model.entity.MarketProductEntity;
import edu.whut.domain.activity.model.entity.TrialBalanceEntity;
import edu.whut.domain.activity.service.trial.AbstractGroupBuyMarketSupport;
import edu.whut.domain.activity.service.trial.factory.DefaultActivityStrategyFactory;
import edu.whut.types.framework.tree.StrategyHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 开关节点
*/
@Slf4j
@Service
public class SwitchNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {
@Resource
private MarketNode marketNode;
@Override
public TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
return router(requestParameter, dynamicContext);
}
@Override
public StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
return marketNode;
}
}

View File

@ -0,0 +1,39 @@
package edu.whut.domain.activity.service.trial.thread;
import edu.whut.domain.activity.adapter.repository.IActivityRepository;
import edu.whut.domain.activity.model.valobj.GroupBuyActivityDiscountVO;
import java.util.concurrent.Callable;
/**
* 查询营销配置任务
*/
public class QueryGroupBuyActivityDiscountVOThreadTask implements Callable<GroupBuyActivityDiscountVO> {
/**
* 来源
*/
private final String source;
/**
* 渠道
*/
private final String channel;
/**
* 活动仓储
*/
private final IActivityRepository activityRepository;
public QueryGroupBuyActivityDiscountVOThreadTask(String source, String channel, IActivityRepository activityRepository) {
this.source = source;
this.channel = channel;
this.activityRepository = activityRepository;
}
@Override
public GroupBuyActivityDiscountVO call() throws Exception {
return activityRepository.queryGroupBuyActivityDiscountVO(source, channel);
}
}

View File

@ -0,0 +1,27 @@
package edu.whut.domain.activity.service.trial.thread;
import edu.whut.domain.activity.adapter.repository.IActivityRepository;
import edu.whut.domain.activity.model.valobj.SkuVO;
import java.util.concurrent.Callable;
/**
* 查询商品信息任务
*/
public class QuerySkuVOFromDBThreadTask implements Callable<SkuVO> {
private final String goodsId;
private final IActivityRepository activityRepository;
public QuerySkuVOFromDBThreadTask(String goodsId, IActivityRepository activityRepository) {
this.goodsId = goodsId;
this.activityRepository = activityRepository;
}
@Override
public SkuVO call() throws Exception {
return activityRepository.querySkuByGoodsId(goodsId);
}
}

View File

@ -1,7 +0,0 @@
/**
* 实体对象
* 1. 一般和数据库持久化对象1v1的关系但因各自开发系统的不同也有1vn的可能
* 2. 如果是老系统改造那么旧的库表冗余了太多的字段可能会有nv1的情况
* 3. 对象名称 XxxEntity
*/
package edu.whut.domain.xxx.model.entity;

View File

@ -1,6 +0,0 @@
/**
* 值对象
* 1. 用于描述对象属性的值如一个库表中有json后者一个字段多个属性信息的枚举对象
* 2. 对象名称如XxxVO
*/
package edu.whut.domain.xxx.model.valobj;

View File

@ -1 +0,0 @@
package edu.whut.domain.xxx.service;

View File

@ -0,0 +1,79 @@
package edu.whut.infrastructure.adapter.repository;
import edu.whut.domain.activity.adapter.repository.IActivityRepository;
import edu.whut.domain.activity.model.valobj.GroupBuyActivityDiscountVO;
import edu.whut.domain.activity.model.valobj.SkuVO;
import edu.whut.infrastructure.dao.IGroupBuyActivityDao;
import edu.whut.infrastructure.dao.IGroupBuyDiscountDao;
import edu.whut.infrastructure.dao.ISkuDao;
import edu.whut.infrastructure.dao.po.GroupBuyActivity;
import edu.whut.infrastructure.dao.po.GroupBuyDiscount;
import edu.whut.infrastructure.dao.po.Sku;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
/**
* 活动仓储
*/
@Repository
public class ActivityRepository implements IActivityRepository {
@Resource
private IGroupBuyActivityDao groupBuyActivityDao;
@Resource
private IGroupBuyDiscountDao groupBuyDiscountDao;
@Resource
private ISkuDao skuDao;
@Override
public GroupBuyActivityDiscountVO queryGroupBuyActivityDiscountVO(String source, String channel) {
// 根据SC渠道值查询配置中最新的1个有效的活动
GroupBuyActivity groupBuyActivityReq = new GroupBuyActivity();
groupBuyActivityReq.setSource(source);
groupBuyActivityReq.setChannel(channel);
GroupBuyActivity groupBuyActivityRes = groupBuyActivityDao.queryValidGroupBuyActivity(groupBuyActivityReq);
String discountId = groupBuyActivityRes.getDiscountId();
GroupBuyDiscount groupBuyDiscountRes = groupBuyDiscountDao.queryGroupBuyActivityDiscountByDiscountId(discountId);
GroupBuyActivityDiscountVO.GroupBuyDiscount groupBuyDiscount = GroupBuyActivityDiscountVO.GroupBuyDiscount.builder()
.discountName(groupBuyDiscountRes.getDiscountName())
.discountDesc(groupBuyDiscountRes.getDiscountDesc())
.discountType(groupBuyDiscountRes.getDiscountType())
.marketPlan(groupBuyDiscountRes.getMarketPlan())
.marketExpr(groupBuyDiscountRes.getMarketExpr())
.tagId(groupBuyDiscountRes.getTagId())
.build();
return GroupBuyActivityDiscountVO.builder()
.activityId(groupBuyActivityRes.getActivityId())
.activityName(groupBuyActivityRes.getActivityName())
.source(groupBuyActivityRes.getSource())
.channel(groupBuyActivityRes.getChannel())
.goodsId(groupBuyActivityRes.getGoodsId())
.groupBuyDiscount(groupBuyDiscount)
.groupType(groupBuyActivityRes.getGroupType())
.takeLimitCount(groupBuyActivityRes.getTakeLimitCount())
.target(groupBuyActivityRes.getTarget())
.validTime(groupBuyActivityRes.getValidTime())
.status(groupBuyActivityRes.getStatus())
.startTime(groupBuyActivityRes.getStartTime())
.endTime(groupBuyActivityRes.getEndTime())
.tagId(groupBuyActivityRes.getTagId())
.tagScope(groupBuyActivityRes.getTagScope())
.build();
}
@Override
public SkuVO querySkuByGoodsId(String goodsId) {
Sku sku = skuDao.querySkuByGoodsId(goodsId);
return SkuVO.builder()
.goodsId(sku.getGoodsId())
.goodsName(sku.getGoodsName())
.originalPrice(sku.getOriginalPrice())
.build();
}
}

View File

@ -11,4 +11,6 @@ import java.util.List;
public interface IGroupBuyActivityDao {
List<GroupBuyActivity> queryGroupBuyActivityList();
GroupBuyActivity queryValidGroupBuyActivity(GroupBuyActivity groupBuyActivityReq);
}

View File

@ -11,4 +11,6 @@ import java.util.List;
public interface IGroupBuyDiscountDao {
List<GroupBuyDiscount> queryGroupBuyDiscountList();
GroupBuyDiscount queryGroupBuyActivityDiscountByDiscountId(String discountId);
}

View File

@ -0,0 +1,13 @@
package edu.whut.infrastructure.dao;
import edu.whut.infrastructure.dao.po.Sku;
import org.apache.ibatis.annotations.Mapper;
/**
* 商品查询
*/
@Mapper
public interface ISkuDao {
Sku querySkuByGoodsId(String goodsId);
}

View File

@ -0,0 +1,37 @@
package edu.whut.infrastructure.dao.po;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.Date;
/**
* 商品信息
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Sku {
/** 自增 */
private Long id;
/** 来源 */
private String source;
/** 渠道 */
private String channel;
/** 商品ID */
private String goodsId;
/** 商品名称 */
private String goodsName;
/** 原始价格 */
private BigDecimal originalPrice;
/** 创建时间 */
private Date createTime;
/** 更新时间 */
private Date updateTime;
}

View File

@ -0,0 +1,6 @@
/**
* @description 通用设计模板责任链
* @author Fuzhengwei bugstack.cn @小傅哥
* @create 2024-12-14 12:04
*/
package edu.whut.types.framework.link;

View File

@ -0,0 +1,42 @@
package edu.whut.types.framework.tree;
import lombok.Getter;
import lombok.Setter;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
/**
* 异步资源加载策略
*/
public abstract class AbstractMultiThreadStrategyRouter<T, D, R> implements StrategyMapper<T, D, R>, StrategyHandler<T, D, R> {
@Getter
@Setter
protected StrategyHandler<T, D, R> defaultStrategyHandler = StrategyHandler.DEFAULT;
public R router(T requestParameter, D dynamicContext) throws Exception {
StrategyHandler<T, D, R> strategyHandler = get(requestParameter, dynamicContext);
if(null != strategyHandler) return strategyHandler.apply(requestParameter, dynamicContext);
return defaultStrategyHandler.apply(requestParameter, dynamicContext);
}
@Override
public R apply(T requestParameter, D dynamicContext) throws Exception {
// 异步加载数据
multiThread(requestParameter, dynamicContext);
// 业务流程受理
return doApply(requestParameter, dynamicContext);
}
/**
* 异步加载数据
*/
protected abstract void multiThread(T requestParameter, D dynamicContext) throws ExecutionException, InterruptedException, TimeoutException;
/**
* 业务流程受理
*/
protected abstract R doApply(T requestParameter, D dynamicContext) throws Exception;
}

View File

@ -0,0 +1,21 @@
package edu.whut.types.framework.tree;
import lombok.Getter;
import lombok.Setter;
/**
* 策略路由抽象类
*/
public abstract class AbstractStrategyRouter<T, D, R> implements StrategyMapper<T, D, R>, StrategyHandler<T, D, R> {
@Getter
@Setter
protected StrategyHandler<T, D, R> defaultStrategyHandler = StrategyHandler.DEFAULT;
public R router(T requestParameter, D dynamicContext) throws Exception {
StrategyHandler<T, D, R> strategyHandler = get(requestParameter, dynamicContext);
if(null != strategyHandler) return strategyHandler.apply(requestParameter, dynamicContext);
return defaultStrategyHandler.apply(requestParameter, dynamicContext);
}
}

View File

@ -0,0 +1,15 @@
package edu.whut.types.framework.tree;
/**
* 受理策略处理
* T 入参类型
* D 上下文参数
* R 返参类型
*/
public interface StrategyHandler<T, D, R> {
StrategyHandler DEFAULT = (T, D) -> null;
R apply(T requestParameter, D dynamicContext) throws Exception;
}

View File

@ -0,0 +1,21 @@
package edu.whut.types.framework.tree;
/**
* 策略映射器
* T 入参类型
* D 上下文参数
* R 返参类型
*/
public interface StrategyMapper<T, D, R> {
/**
* 获取待执行策略
*
* @param requestParameter 入参
* @param dynamicContext 上下文
* @return 返参
* @throws Exception 异常
*/
StrategyHandler<T, D, R> get(T requestParameter, D dynamicContext) throws Exception;
}

View File

@ -0,0 +1,4 @@
/**
* 通用设计模板规则树
*/
package edu.whut.types.framework.tree;