页面搭建系统是一个帮助运营快速搭建多样化的活动落地页的系统,包含运营搭建页面的后台系统和前台落地页呈现系统;为满足运营大小促搭建页面的多样化需求和提升运营搭建活动页的效率,于12月份需要在现有的系统下新增 50 + 模块;
随着模块不断增加,系统以来的服务越来越多(商品、品牌、优惠券、促销、推荐等等),现有各个模块相互耦合,扩展性不强,开发成本较高;为提升开发效率,快速迭代,需要重构梳理模块扩展这部分逻辑
模块扩展性业务框架重新抽象和整理了页面搭建前台页面获取数据的整个流程,按照业务分为业务聚合层、数据适配层和模块业务转换层;各层职责清晰、模块快速迭代、依赖第三方服务共用、性能调优方便
活动落地页请求流程:
活动落地页 :
主要列出服务注册中心和模块注册中心部分类结构
需要新增一个品牌领券,运营录入原始素材:品牌id、优惠券方案id、自定义品牌名称
先创建业务处理逻辑的类解析模块素材:暴露需要调用外部服务的数据标识(商品id、品牌id、落地页url、优惠券id、店铺等等);通过注解 @ModuleConvert 标识是哪个模块(品牌墙、品牌领券等等)的业务逻辑
@Component
// 通过注解 @ModuleConvert 标识是哪个模块的业务逻辑
@ModuleConvert(moduleMark = BrandCoupon.class)
public class BrandCouponModule extends AbstractIdModulesExecutor {
@Override
public Content convert(IdModuleParseDto arg) throws Exception {
if (arg == null || StringUtils.isEmpty(arg.getContent()) || arg.getData() == null) {
return null;
}
// 业务聚合层批量获取完数据后,把各个模块需要的数据分发到各个模块
Map<ModuleType, ModulesVo> voMap = arg.getData();
// 取出品牌信息
ModulesVo vo = voMap.get(ModuleType.BRAND);
// 取出优惠券数据
ModulesVo svo = voMap.get(ModuleType.COUPON);
if (vo == null) {
return null;
}
Content content = new Content();
// 品牌信息
BrandVo brandVo = (BrandVo) vo;
// 素材信息
BrandCoupon brandCoupon = JSON.parseObject(arg.getContent(), BrandCoupon.class);
// ============= 一些该模块的业务逻辑,一些不展示的过滤逻辑和参数转换逻辑 ======
// ============= 完成参数拼装,返回给前台 ======
content.setData(JSON.toJSONString(brandVo, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullNumberAsZero));
return content;
}
/**
* 暴露数据标识,分析发现品牌领券模块需要解析品牌id和优惠券方案id
* 分类暴露品牌id和优惠券id,方便业务聚合层收集id批量从数据适配层获取数据
*/
@Override
public Map<ModuleType, Key> getId(String arg) {
if (StringUtils.isEmpty(arg)) {
return null;
}
BrandCoupon brandCoupon = JSON.parseObject(arg, BrandCoupon.class);
if (brandCoupon != null) {
Map<ModuleType, Key> map = Maps.newHashMap();
// 暴露品牌id
map.put(ModuleType.BRAND, new Key(null, brandCoupon.getBrandId()));
// 暴露优惠券方案id
map.put(ModuleType.COUPON, new Key(null, brandCoupon.getSchemeId()));
return map;
}
return null;
}
}
找下需要的不同类型的数据标识调用服务是否已经存在:此处假设品牌服务已经对接,但是优惠券服务还未对接,那么需要编写一个批量调用优惠券服务获取优惠券数据的类对接优惠券服务
@Component
/**
* 调用优惠券服务批量获取优惠券信息
* 通过注解 @DataProvider 标识是什么类型 {ModuleType} 的服务数据
* 新增一个 CouponVo (extends ModulesVo)封装需要的参数
*/
@DataProvider(moduleType = ModuleType.COUPON,
threadPool = ThreadPoolType.COUPONS)
public class FetchCouponsExecutor extends AbstractFetchDataExecutor<CouponVo> {
@Autowired
private CouponService couponService;
private final Log LOGGER = LogFactory.getLog(getClass());
@Override
public Map<Long, CouponVo> executeGetData(List<Long> schemaIds) {
if (CollectionUtils.isEmpty(schemaIds)) {
LOGGER.info("betch get CouponVo with empty schemaIds:{}");
return null;
}
// 批量获取优惠券数据
return couponService.batchGetCouponsVo(schemaIds);
}
每个服务隔离的配置,可动态修改
disconfig key 后缀 | 默认值 | 类型 | 配置功能说明 |
---|---|---|---|
page_size | 20 | Integer | 批量接口单次调用第三方 dubbo 接口最大值 |
cache_switch | false | Boolean | 是否开启缓存 |
min_expire_time | 20 | Integer | 最小缓存时间 |
max_expire_time | 30 | Integer | 最大缓存时间 |
disconfig 前缀 :标识服务的枚举类 ModuleType 的 value,如下:优惠券类 COUPON 的 前缀为 coupon
public enum ModuleType {
/**
* {@link FetchBrandsExecutor}
* 调用品牌接口获取品牌基本信息{@link BrandVo}
* 包含:品牌基本信息
*/
BRAND("brand", "品牌类", RecModuleTypeEnum.BRAND.getValue()),
/**
* {@link FetchAlbumsExecutor}
* 专辑基本信息{@link AlbumVo}
*/
ALBUM("album", "专辑类", RecModuleTypeEnum.ALBUM.getValue()),
/**
* {@link FetchCouponsExecutor}
* 获取优惠券信息{@link CouponVo}
*/
COUPON("coupon", "优惠券", RecModuleTypeEnum.COUPON.getValue()),
/**
* {@link FetchPromotionGoodsExecutor}
* 拉取促销_商品数据:{@link PromotionGoodsVo}
* 通用模块:促销类型:秒杀、黑卡专享活动
*/
PROMOTION_GOODS("promotionGoods", "促销商品类", RecModuleTypeEnum.INVALID.getValue()),
}
以上述例子为例 :
网易云新用户大礼包:https://www.163yun.com/gift
本文来自网易实践者社区,经作者柯博文授权发布。