淘客返利app数据中台设计:打通多平台数据的统一数据服务架构

淘客返利app数据中台设计:打通多平台数据的统一数据服务架构

大家好,我是省赚客APP研发者阿可!省赚客APP(juwatech.cn)需对接淘宝联盟、京东联盟、拼多多开放平台、抖音精选联盟等十余个外部数据源,每日处理超 500 万条订单、商品与佣金记录。早期各业务线独立拉取数据,导致口径不一、重复计算、存储冗余。为构建"一份数据、统一服务"的能力,我们搭建了基于 Flink + Iceberg + Doris 的数据中台,实现从原始数据接入、标准化建模到统一 API 服务的全链路闭环。本文结合核心 Java 代码与架构设计,详解多平台数据融合的关键实践。

数据接入层:多源异构数据统一采集

通过 Flink CDC 与 HTTP 轮询混合模式采集原始数据:

java 复制代码
// 淘宝联盟订单拉取任务(定时调度)
@Scheduled(fixedDelay = 300_000) // 5分钟一次
public void pullTaobaoOrders() {
    String nextPageToken = offsetStore.get("taobao_order_cursor");
    TaobaoOrderResponse response = taobaoApiClient.queryOrders(
        LocalDateTime.now().minusHours(24),
        LocalDateTime.now(),
        nextPageToken
    );

    for (TaobaoOrder order : response.getData()) {
        // 转换为统一格式
        UnifiedOrder unified = TaobaoOrderConverter.toUnified(order);
        kafkaProducer.send("raw.orders", JsonUtil.toJson(unified));
    }

    if (response.hasNext()) {
        offsetStore.set("taobao_order_cursor", response.getNextCursor());
    }
}

所有原始事件写入 Kafka raw.orders Topic,供下游消费。

消费 Kafka 数据,进行字段对齐、状态补全、去重:

java 复制代码
public class OrderStandardizationJob {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.enableCheckpointing(60_000);

        DataStream<String> rawStream = env.addSource(
            new FlinkKafkaConsumer<>("raw.orders", new SimpleStringSchema(), kafkaProps)
        );

        DataStream<UnifiedOrder> standardized = rawStream
            .map(json -> JsonUtil.fromJson(json, UnifiedOrder.class))
            .keyBy(order -> order.getPlatform() + "_" + order.getOuterOrderId())
            .process(new DeduplicationProcessFunction(Time.minutes(10)));

        // 写入 Iceberg 表
        standardized.sinkTo(
            IcebergSink.forRowData()
                .tableLoader(TableLoader.fromHadoopTable("hdfs://warehouse/iceberg/db/orders"))
                .build()
        );

        env.execute("Order Standardization Job");
    }
}

去重逻辑基于订单 ID + 平台组合,在 10 分钟窗口内保留第一条:

java 复制代码
public class DeduplicationProcessFunction extends KeyedProcessFunction<String, UnifiedOrder, UnifiedOrder> {
    private ValueState<Boolean> seen;

    @Override
    public void open(Configuration parameters) {
        seen = getRuntimeContext().getState(new ValueStateDescriptor<>("seen", Boolean.class));
    }

    @Override
    public void processElement(UnifiedOrder order, Context ctx, Collector<UnifiedOrder> out) throws Exception {
        if (seen.value() == null) {
            seen.update(true);
            out.collect(order);
            ctx.timerService().registerEventTimeTimer(ctx.timestamp() + 600_000); // 10分钟后清理
        }
    }

    @Override
    public void onTimer(long timestamp, OnTimerContext ctx, Collector<UnifiedOrder> out) {
        seen.clear();
    }
}

统一数据模型与 Iceberg 分层存储

采用分层建模:

  • ODS 层 :原始快照(Iceberg 表 ods.orders_raw);
  • DWD 层 :清洗后明细(dwd.order_detail),含统一字段如 user_id, commission_amount, order_status
  • DWS 层 :聚合指标(dws.user_daily_summary),按用户+日期汇总。

DWD 表结构示例:

sql 复制代码
CREATE TABLE dwd.order_detail (
  order_id STRING,
  user_id BIGINT,
  platform STRING,
  item_title STRING,
  price DECIMAL(12,2),
  commission_rate DECIMAL(5,4),
  commission_amount DECIMAL(12,2),
  order_time TIMESTAMP,
  status STRING -- VALID / INVALID / SETTLED
) PARTITIONED BY (days(order_time));

Doris 构建统一查询服务

将 DWD/DWS 表同步至 Apache Doris,提供毫秒级 OLAP 查询:

bash 复制代码
# 使用 Flink Doris Connector 实时写入
stream.addSink(DorisSink.builder()
    .setDorisReadOptions(DorisReadOptions.builder().build())
    .setDorisExecutionOptions(DorisExecutionOptions.builder()
        .setLabelPrefix("order_detail_sync")
        .setStreamLoadProp(streamLoadProps)
        .build())
    .setSerializer(JsonDebeziumDeserializationSchema.builder()
        .setDatabase("dwd")
        .setTable("order_detail")
        .build())
    .build());

Java 服务通过 JDBC 查询 Doris 提供统一 API:

java 复制代码
@RestController
@RequestMapping("/api/data")
public class UnifiedDataService {

    @Autowired
    private JdbcTemplate dorisTemplate;

    @GetMapping("/user/commission")
    public List<UserCommissionStat> getUserCommission(@RequestParam Long userId,
                                                      @RequestParam LocalDate startDate,
                                                      @RequestParam LocalDate endDate) {
        return dorisTemplate.query(
            "SELECT platform, SUM(commission_amount) AS total, COUNT(*) AS order_count " +
            "FROM dwd.order_detail " +
            "WHERE user_id = ? AND order_time >= ? AND order_time < ? AND status = 'VALID' " +
            "GROUP BY platform",
            (rs, rowNum) -> new UserCommissionStat(
                rs.getString("platform"),
                rs.getBigDecimal("total"),
                rs.getInt("order_count")
            ),
            userId, startDate.atStartOfDay(), endDate.plusDays(1).atStartOfDay()
        );
    }
}

元数据管理与血缘追踪

自研元数据平台记录字段映射关系,例如:

平台字段(淘宝) 统一字段 类型
tk_status status STRING
pub_share_pre_fee commission_amount DECIMAL

确保数据可解释、可追溯。

本文著作权归聚娃科技省赚客app开发者团队,转载请注明出处!

相关推荐
要做一个小太阳3 小时前
华为Atlas 900 A3 SuperPoD 超节点网络架构
运维·服务器·网络·华为·架构
vx-bot5556664 小时前
企业微信接口在混合云环境下的集成架构与网络互联方案企业微信接口在混合云环境下的集成架构与网络互联方案
网络·架构·企业微信
JMchen12316 小时前
现代Android图像处理管道:从CameraX到OpenGL的60fps实时滤镜架构
android·图像处理·架构·kotlin·android studio·opengl·camerax
Jing_jing_X19 小时前
CPU 架构:x86、x64、ARM 到底是什么?为什么程序不能通用?
arm开发·架构·cpu
qq_1777673721 小时前
React Native鸿蒙跨平台自定义复选框组件,通过样式数组实现选中/未选中状态的样式切换,使用链式调用替代样式数组,实现状态驱动的样式变化
javascript·react native·react.js·架构·ecmascript·harmonyos·媒体
小程故事多_801 天前
深度搜索Agent架构全解析:从入门到进阶,解锁复杂问题求解密码
人工智能·架构·aigc
●VON1 天前
React Native for OpenHarmony:项目目录结构与跨平台构建流程详解
javascript·学习·react native·react.js·架构·跨平台·von
Gary董1 天前
高并发的微服务架构如何设计
微服务·云原生·架构
ujainu1 天前
Flutter + OpenHarmony 实战:《圆环跳跃》——完整游戏架构与视觉优化
flutter·游戏·架构·openharmony
爬山算法1 天前
Hibernate(74)如何在CQRS架构中使用Hibernate?
java·架构·hibernate