如何设计投放系统系列----灵活的字段映射补全机制

简介:

引言

我们知道搭建系统跟投放系统是两个紧密关联的系统,搭建产出的是页面的结构,投放产出的是页面的数据。搭建产出的页面包含各式各样的模块,这些模块包含的字段也没有太多规律可言,那么投放系统怎么为这些模块补全数据呢?

在回答这个问题之前,我们先尝试解决一些简单的业务 case

几个案例

案例一

有一个商品模块,字段包括:商品的标题、商品图片、购买链接、商品价格、商品描述,想要投放某个选品集的商品,请问投放系统应该怎么设计以补全这些字段信息?

拿到这个需求,最直观的解决方案就是,直接去商品库取选品集对应的商品列表,把商品库里的字段塞到对应的模块字段上。

// 商品选品集
var goodSet = [1,2,3,4,5];

// 从商品库获取对应的商品实体信息
var entityMap = goodService.fetch(goodSet);


var output = [];
for (var i = 0, l = goodSet.length; i < l; ++i) {
  var goodId = goodSet[i];
  var entity = entityMap[goodId];
  if (!entity) {
    continue;
  }
  // 字段映射
  output.push({
    '商品名称': entity['goodName'],
    '商品图片': entity['goodImg'],
    '商品链接': entity['goodLink'],
    '商品价格': entity['goodPrice']
    ...
  });
}
return output;

案例二

有一个店铺模块,字段包括:店铺的名称、店铺的照片、店铺链接,想要投放某个选品集的店铺列表,请问投放系统应该怎么设计以补全这些字段信息?

这个需求跟上面一个类似,依然是最直观的解决方案,直接去店铺库取选品集对应的店铺列表,把店铺库里的字段塞到对应的模块字段上。

// 店铺选品集
var shopSet = [1,2,3,4,5];

// 从店铺库获取对应的店铺实体信息
var entityMap = shopService.fetch(shopSet);


var output = [];
for (var i = 0, l = shopSet.length; i < l; ++i) {
  var shopId = shopSet[i];
  var entity = entityMap[shopId];
  if (!entity) {
    continue;
  }
  // 字段映射
  output.push({
    '店铺名称': entity['shopName'],
    '店铺图片': entity['shopImg'],
    '店铺链接': entity['shopLink']
  });
}
return output;

案例三

同样一个商品模块,字段包括:商品的标题、商品图片、购买链接、商品价格、优惠价格,想要投放某个选品集的商品,请问投放系统应该怎么设计以补全这些字段信息?

这个 case 我们发现单纯的从商品库取不到优惠价格信息,必须去另外一个服务获取商品的优惠价格。

对应的伪代码为:

// 商品选品集
var goodSet = [1,2,3,4,5];

// 从商品库获取对应的商品实体信息
var entityMap = goodService.fetch(goodSet);


var output = [];
for (var i = 0, l = goodSet.length; i < l; ++i) {
  var goodId = goodSet[i];
  var entity = entityMap[goodId];
  if (!entity) {
    continue;
  }
  // 字段映射
  output.push({
    '商品名称': entity['goodName'],
    '商品图片': entity['goodImg'],
    '商品链接': entity['goodLink'],
    '商品价格': entity['goodPrice'],
    // 去优惠券服务获取商品的优惠价格
    '优惠价格': couponService.fetch(goodId).price
  });
}
return output;

到了这里,我们发现模块只要一变,代码就得跟着变,有没有办法能模块变化,代码不变呢?

终极解决方案

相信聪明的你已经可以看出,随着需求的变化,我们的代码变化的都是字段的补全来源以及字段的映射关系,不变的是整个代码的流程。如果我们可以把这些变化的部分做成可配置的,似乎代码就不需要变动了。

我们试着写了这样一个接口

function get(id, field);

这个接口只需要传实体的 id,以及需要返回的字段名,就可以返回对应的值。

每个字段的具体补全逻辑都是 get 的具体实现,我们把实现做成可配置的形式

商品数据源配置
{
    'goodName': {adapter: 'goodService', param: ['id', 'goodName']},
    'goodPrice': {adapter: 'goodService', param: ['id', 'goodPrice']}
    'couponPrice': {adapter: 'couponService', param: ['id', 'couponPrice']}
}
店铺数据源配置
{
    'shopName': {adapter: 'shopService', param: ['id', 'shopName']},
    'shopImg': {adapter: 'shopService', param: ['id', 'shopImg']}
}

接着我们再添加一些配置,配置的是模块素材字段跟数据源中的字段ID的映射关系

商品字段映射
模块素材字段编码 => 数据源字段 ID
{
  '商品名称': 'goodName',
  '商品图片': 'goodImg',
  '商品链接': 'goodLink',
  '商品价格': 'goodPrice',
  '优惠价格': 'couponPrice'
}

店铺字段映射
{
  '店铺名称': 'shopName',
  '店铺图片': 'shopImg',
  '店铺链接': 'shopLink'
}

最后我们再修改下伪代码:


function get(id, field) {
    var param = configService.getDatastoreConfig(field).param;
    return adapterFactory.get(field).apply(param);
}


// 选品集
var entitySet = [1,2,3,4,5];
// 实体类型
var entityType = model.entityType;
// 获取字段映射配置
var fieldMappingConfig = configService.getFieldMappingConfig(entityType);

var output = [];
for (var i = 0, l = entitySet.length; i < l; ++i) {
  var entityId = entitySet[i];

  var entity = {id: entityId};
  // 遍历模块的字段列表
  for (var j = 0; j < model.fields.length; ++j) {
    // 模块字段编码
    var field = model.fields[j];
    // 补全该字段的值,get 会用数据源配置的类和参数补全该字段的值
    entity[field] = get(entityId, fieldMappingConfig[field]);
  }

  output.push(entity);
}
return output;

上面其实也是我们 UTCP 系统目前的设计思路。

get

get 接口对应的就是 AbstractEntity.get 方法。

字段映射配置

image.png

数据源配置

image.png

目录
相关文章
|
6月前
|
存储 人工智能 Java
【程序设计】做一个发送系统邮件的功能,如何设计数据表? 转至元数据结尾
重构系统邮件发送旨在实现统一的邮件发送功能,通过公共API提供服务。设计包括两个核心数据表:`mail` 表用于存储邮件基本信息,如邮件ID、业务类型、发送者、标题、内容、附件信息、发送状态和时间戳;`mail_receiver` 表记录邮件接收者信息,包括邮件ID、接收者邮箱、接收者类型、发送状态和重试次数。为了优化查询性能,建议创建`Email_Content`表,将`content`和`attach_file`从`Email`表中分离,以减少主表大小。这有助于提高主表的查询速度,并通过定期的数据结转策略,确保热表只存储最近的数据。
119 0
|
7月前
|
编译器 芯片
字扩展与位扩展
字扩展与位扩展
106 0
文本,好看的设计------我独自升级,六芒星技能表,可以用来判断是否在能力值之内的事情,使用六芒星可以显示能力之内,能力之外的事情,用以判断
文本,好看的设计------我独自升级,六芒星技能表,可以用来判断是否在能力值之内的事情,使用六芒星可以显示能力之内,能力之外的事情,用以判断
文本,好看的设计------我独自升级,六芒星技能表,可以用来判断是否在能力值之内的事情,使用六芒星可以显示能力之内,能力之外的事情,用以判断
文本,提升编程能力的方法-----代码库,能够完全显示代码,能够容纳一个完整的代码文件,左侧能够呈现代码
文本,提升编程能力的方法-----代码库,能够完全显示代码,能够容纳一个完整的代码文件,左侧能够呈现代码
|
5月前
|
存储 前端开发 Java
若依修改----数据字典,可以用于维护系统中常见的静态数据,为什么不写死,用字典维护?数据字典的好处是一个地方编写数据,在多个地方,复用他,静态选项这里填完,换其他,用户性别这里的男女,就转成而来字典
若依修改----数据字典,可以用于维护系统中常见的静态数据,为什么不写死,用字典维护?数据字典的好处是一个地方编写数据,在多个地方,复用他,静态选项这里填完,换其他,用户性别这里的男女,就转成而来字典
|
4月前
|
存储 开发框架 前端开发
EAV模型(实体-属性-值)的设计和低代码的处理方案(3)-- 实体属性定义及前端列表展示和数据录入处理
EAV模型(实体-属性-值)的设计和低代码的处理方案(3)-- 实体属性定义及前端列表展示和数据录入处理
|
7月前
|
存储 自然语言处理
平台设计-代码字段与标签
在平台里描述对象的属性可以使用代码和标签
聊天框(番外篇)—如何实现@功能的整体删除
上一篇文章中,我们已经初步实现了聊天输入框,但其@功能是不完善的,例如无法整体删除、无法获取除用户名以外的数据(假设用户名不是唯一的)。有问题就要想办法解决,在网上百度了一圈后,倒是有一些收获。本文就着重解决@的整体删除以及获取额外数据。
1116 0
聊天框(番外篇)—如何实现@功能的整体删除
|
移动开发
微信h5扫码接口范例:多个扫码框支持的办法,通过引入一个参数来区分及使用localStorage保证之前扫到的数据不丢失
微信h5扫码接口范例:多个扫码框支持的办法,通过引入一个参数来区分及使用localStorage保证之前扫到的数据不丢失
117 0
|
SQL Java 关系型数据库
从系统报表页面导出20w条数据到本地只用了4秒,我是如何做到的
最近有个学弟找到我,跟我描述了以下场景: 他们公司内部管理系统上有很多报表,报表数据都有分页显示,浏览的时候速度还可以。但是每个报表在导出时间窗口稍微大一点的数据时,就异常缓慢,有时候多人一起导出时还会出现堆溢出。 他知道是因为数据全部加载到jvm内存导致的堆溢出。所以只能对时间窗口做了限制。以避免因导出过数据过大而引起的堆溢出。最终拍脑袋定下个限制为:导出的数据时间窗口不能超过1个月。