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

简介:

引言

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

在回答这个问题之前,我们先尝试解决一些简单的业务 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

目录
相关文章
|
5月前
|
存储 人工智能 Java
【程序设计】做一个发送系统邮件的功能,如何设计数据表? 转至元数据结尾
重构系统邮件发送旨在实现统一的邮件发送功能,通过公共API提供服务。设计包括两个核心数据表:`mail` 表用于存储邮件基本信息,如邮件ID、业务类型、发送者、标题、内容、附件信息、发送状态和时间戳;`mail_receiver` 表记录邮件接收者信息,包括邮件ID、接收者邮箱、接收者类型、发送状态和重试次数。为了优化查询性能,建议创建`Email_Content`表,将`content`和`attach_file`从`Email`表中分离,以减少主表大小。这有助于提高主表的查询速度,并通过定期的数据结转策略,确保热表只存储最近的数据。
97 0
|
6月前
|
编译器 芯片
字扩展与位扩展
字扩展与位扩展
95 0
文本,好看的设计------我独自升级,六芒星技能表,可以用来判断是否在能力值之内的事情,使用六芒星可以显示能力之内,能力之外的事情,用以判断
文本,好看的设计------我独自升级,六芒星技能表,可以用来判断是否在能力值之内的事情,使用六芒星可以显示能力之内,能力之外的事情,用以判断
文本,好看的设计------我独自升级,六芒星技能表,可以用来判断是否在能力值之内的事情,使用六芒星可以显示能力之内,能力之外的事情,用以判断
|
3月前
|
开发框架 前端开发 JavaScript
在Winform应用中增加通用的业务编码规则生成
在Winform应用中增加通用的业务编码规则生成
|
4月前
|
存储 前端开发 Java
若依修改----数据字典,可以用于维护系统中常见的静态数据,为什么不写死,用字典维护?数据字典的好处是一个地方编写数据,在多个地方,复用他,静态选项这里填完,换其他,用户性别这里的男女,就转成而来字典
若依修改----数据字典,可以用于维护系统中常见的静态数据,为什么不写死,用字典维护?数据字典的好处是一个地方编写数据,在多个地方,复用他,静态选项这里填完,换其他,用户性别这里的男女,就转成而来字典
|
6月前
|
存储 自然语言处理
平台设计-代码字段与标签
在平台里描述对象的属性可以使用代码和标签
游戏对接广告看视频系统开发详细规则/方案逻辑/步骤逻辑/规则玩法/源码程序
Advertising location and display method: According to the characteristics of the game interface and scene, choose the appropriate advertising location and display method to ensure that the advertisement naturally integrates into the game and does not affect the player&#39;s game experience.
|
存储 XML SQL
浅谈扩展字段设计
浅谈扩展字段设计
414 0
|
前端开发 区块链
合成游戏看广告视频盒子系统开发方案逻辑/详细案例/功能设计/需求步骤/规则项目/源码说明
在Solidity中,与外部合约交互可以通过调用函数来完成。这些函数可以是在Solidity合约中定义的函数,也可以是在外部合约中定义的函数。调用外部合约函数需要知道合约的地址和函数的签名。
|
SQL Java 关系型数据库
从系统报表页面导出20w条数据到本地只用了4秒,我是如何做到的
最近有个学弟找到我,跟我描述了以下场景: 他们公司内部管理系统上有很多报表,报表数据都有分页显示,浏览的时候速度还可以。但是每个报表在导出时间窗口稍微大一点的数据时,就异常缓慢,有时候多人一起导出时还会出现堆溢出。 他知道是因为数据全部加载到jvm内存导致的堆溢出。所以只能对时间窗口做了限制。以避免因导出过数据过大而引起的堆溢出。最终拍脑袋定下个限制为:导出的数据时间窗口不能超过1个月。