【用户行为归因分析项目】- 【企业级项目开发第四站】模拟三类用户行为数据上传到Hadoop

gitee代码仓地址:DataWareHouse: UserBehaviorAttributionAnalysis​​​​​​

一、主方法-应用起点

ProductionOdsDataToHdfs

功能:

读取hive表中的设备信息和应用信息,从全量的设备id和应用id中随机获取

作为用户行为数据的关键信息,然后根据获取到的应用信息和设备信息生成三类用户行为数据并生成文件上传到hdfs中

主要调用功能实现类ProductOdsData

Scala 复制代码
package com.dw.application

import com.dw.service.ProductOdsData

object ProductionOdsDataToHdfs {
  //生产数据
  //五类数据需要生产,其中安装激活卸载数据是每日生产,应用信息和设备信息,在本次项目中设计为不更新


  private val productOdsData = new ProductOdsData

  def main(args: Array[String]): Unit = {

    //获取dim_device_info中的device_id,获取dim_app_info中的app_id,模拟用户行为数据
    val cnt = 180
    val deviceIds: Seq[String] = productOdsData.getDeviceId(cnt)
    val appIds: Seq[String] = productOdsData.getAppId
    //安装数据
    println("####################安装数据准备##########################")
    productOdsData.userInstallData(deviceIds, appIds, cnt)
    //激活数据
    println("####################激活数据准备##########################")

    productOdsData.userActivateData(deviceIds, appIds, cnt)
    //卸载
    println("####################卸载数据准备##########################")

    productOdsData.userUninstallData(deviceIds, appIds, cnt)
  }
}

二、功能实现类ProductOdsData

包含方法:

getDeviceId :从hive表获取所有的设备id

getAppId:从hive表获取所有的应用id

userInstallData :获取安装的配置数据,再调用dataToFile生成用户安装行为数据

userActivateData 获取激活的配置数据,再调用dataToFile生成用户激活行为数据

userUninstallData 获取卸载的配置数据,再调用dataToFile生成用户卸载行为数据

dataToFile 根据输入的设备id和应用id,分别生成三种用户数据,并生成文件上传到hdfs的row层下的对应目录

Scala 复制代码
package com.dw.service

import com.dw.common.utils.ConfigUtil
import com.dw.dao.{HdfsFileWriter, HiveSqlExecute}
import com.dw.util.DateUtils

import java.time.LocalDate
import java.time.format.DateTimeFormatter
import scala.collection.mutable.ArrayBuffer
import scala.util.Random

class ProductOdsData {

//  private val dwdHiveTableName = ConfigUtil.getHiveTableName.getConfig("dwd")
//  private val dwsHiveTableName = ConfigUtil.getHiveTableName.getConfig("dws")
//  private val adsHiveTableName = ConfigUtil.getMysqlTableName.getConfig("ads")
  //加载相关hive表明和hdfs路径名
  private val dimHiveTableName = ConfigUtil.getHiveTableName.getConfig("dim")
  private val odsFilePath = ConfigUtil.getHdfsFilePath.getConfig("hdfs_file_path")
  private val odsFilePrefix = ConfigUtil.getHdfsFilePath.getConfig("hdfs_file_Prefix")

  private val hiveSqlExecute = new HiveSqlExecute
  private val dateRadom = new DateUtils
  private val hdfsFileWriter = new HdfsFileWriter

  /**
   * 获取备id
   *
   * @param dataCnt 需要获取的id数,默认0
   * @return Seq[String]
   */
  def getDeviceId(dataCnt: Int = 0): Seq[String] = {
    val TABLE_NAME = dimHiveTableName.getString("dim_device_info")
    hiveSqlExecute.selectOpt(s"select device_id from ${TABLE_NAME} limit ${dataCnt}").map(
      rows => rows("device_id")
    )
  }

  /**
   * 获取全部的应用id
   *
   * @return Seq[String]
   */
  def getAppId: Seq[String] = {
    val TABLE_NAME = dimHiveTableName.getString("dim_app_info")
    hiveSqlExecute.selectOpt(s"select app_id from ${TABLE_NAME}").map(
      rows => rows("app_id")
    )
  }

  /**
   * 生成用户安装行为信息数据,并生成文件,上传到hdfs上的row层
   *
   * @param deviceIds 获取到的设备id序列
   * @param appIds    获取到的引用id序列
   * @param cnt       需要生成的数据条数
   * @return Seq[String]
   */
  def userInstallData(deviceIds: Seq[String], appIds: Seq[String], cnt: Int): Unit = {
    // 1. 前置校验(避免空值/非法参数)
    if (deviceIds.isEmpty || appIds.isEmpty) {
      throw new IllegalArgumentException("deviceIds和appIds不能为空!")
    }
    val filePath = odsFilePath.getString("user_install_path")
    val filePrefix = odsFilePrefix.getString("user_install_file_Prefix")

    dataToFile(deviceIds, appIds, cnt, filePath, filePrefix)
  }

  /**
   * 生成用户激活行为信息数据,并生成文件,上传到hdfs上的row层
   *
   * @param deviceIds 获取到的设备id序列
   * @param appIds    获取到的引用id序列
   * @param cnt       需要生成的数据条数
   * @return Seq[String]
   */
  def userActivateData(deviceIds: Seq[String], appIds: Seq[String], cnt: Int): Unit = {
    // 1. 前置校验(避免空值/非法参数)
    if (deviceIds.isEmpty || appIds.isEmpty) {
      throw new IllegalArgumentException("deviceIds和appIds不能为空!")
    }
    val filePath = odsFilePath.getString("user_activate_path")
    val datePrefix = odsFilePrefix.getString("user_activate_file_Prefix")
    dataToFile(deviceIds, appIds, cnt, filePath, datePrefix)
  }

  /**
   * 生成用户卸载行为信息数据,并生成文件,上传到hdfs上的row层
   *
   * @param deviceIds 获取到的设备id序列
   * @param appIds    获取到的引用id序列
   * @param cnt       需要生成的数据条数
   * @return Seq[String]
   */
  def userUninstallData(deviceIds: Seq[String], appIds: Seq[String], cnt: Int): Unit = {
    // 1. 前置校验(避免空值/非法参数)
    if (deviceIds.isEmpty || appIds.isEmpty) {
      throw new IllegalArgumentException("deviceIds和appIds不能为空!")
    }
    val filePath = odsFilePath.getString("user_uninstall_path")
    val datePrefix = odsFilePrefix.getString("user_uninstall_file_Prefix")
    dataToFile(deviceIds, appIds, cnt, filePath, datePrefix)
  }

  /**
   * 将生成的数据加载成文件,文件命名为ODS_USER_INSTALL_20251231.000
   * 按照每天数据生成的日期作为文件名一部分
   * 每一百条数据为一个文件,文件序号递增,三位数不足前面补0
   *
   * @param deviceIds  获取到的设备id序列
   * @param appIds     获取到的引用id序列
   * @param cnt        需要生成的数据条数
   * @param filePath   hdfs上对应业务的row层目录
   * @param filePrefix 需要上传的文件前缀(eg:ODS_USER_INSTALL_20251231)
   * @return Seq[String]
   */
  private def dataToFile(deviceIds: Seq[String], appIds: Seq[String], cnt: Int, filePath: String, filePrefix: String): Unit = {
    val today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))
    val datePrefix = filePrefix + today
    //检查目录是否存在
    if (hdfsFileWriter.existsHdfsDir(filePath)) {
      if (hdfsFileWriter.createHdfsDir(filePath + "/" + today)) { //创建今天的目录,如果存在就不会再次创建了
        var currentGroup = ArrayBuffer[List[String]]() // 存储当前分组的100条数据
        (1 to cnt).foreach(i => {
          val groupNum = (i - 1) / 100
          val fileNameSuffix = f"$groupNum%03d" //文件名后缀000-999
          val fullFileName = s"${datePrefix}.${fileNameSuffix}"
          if (filePrefix == odsFilePrefix.getString("user_install_file_Prefix")) {
            currentGroup += List(
              deviceIds(Random.nextInt(deviceIds.length)), //随机获取一个设备id
              appIds(Random.nextInt(appIds.length)), //随机获取一个应用id
              dateRadom.getSpecifiedDayInLastYear(), //数据加载时间,一天前的任意时间,格式: yyyy-MM-dd HH:mm:ss
              fullFileName //文件名
            )
          } else {
            currentGroup += List(
              deviceIds(Random.nextInt(deviceIds.length)), //随机获取一个设备id
              appIds(Random.nextInt(appIds.length)), //随机获取一个应用id
              appIds(Random.nextInt(appIds.length)), //随机获取一个应用id,安装数据多一个安装来源渠道
              dateRadom.getSpecifiedDayInLastYear(), //数据加载时间,一天前的任意时间,格式: yyyy-MM-dd HH:mm:ss
              fullFileName //文件名
            )
          }
          //若当前分组满100条则封组,重置当前分组
          if (currentGroup.size == 100 || i == cnt) { // 满100条 或 到最后一条时封组
            hdfsFileWriter.writeToCsvFile(currentGroup.toList, fullFileName, "|") //生成文件
            currentGroup = ArrayBuffer.empty // 重置当前分组
          }
        }
        )
      }
      hdfsFileWriter.putBatchFiles(datePrefix, filePath)
    }
  }

}

至此我们项目架构中的数据生产层已经完成

三、功能验证

1、DIM层设备、应用数据验证

通过日志验证

通过结果验证

2、用户行为数据验证

报错

hdfs建目录

通过日志验证

通过结果验证

相关推荐
数据与后端架构提升之路1 小时前
Seata 全景拆解:AT、TCC、Saga 该怎么选?告别“一把梭”的架构误区
分布式·架构
Elastic 中国社区官方博客3 小时前
Elasticsearch:上下文工程 vs. 提示词工程
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
2501_933329553 小时前
Infoseek舆情系统:企业级数字公关AI中台技术解析
大数据·数据挖掘
2501_933670794 小时前
2026高职大数据与财务管理专业证书报考条件
大数据
weilaikeqi11114 小时前
2026年房产中介怎么选房源管理系统?
大数据
Hello.Reader4 小时前
Flink Standalone 本地一键起集群、Session/Application 两种模式、HA 高可用与排障清单
大数据·flink
月初,4 小时前
Git 常用操作大全(超详细教程)一文教会你完全使用Git
大数据·git·elasticsearch
清 晨5 小时前
TikTok Shop 跨境卖家最新合规与增长应对:从“内容冲量”升级为“商品与履约可控”
大数据·人工智能·跨境电商·tiktok·营销策略
3分钟秒懂大数据5 小时前
实时数仓实战篇一:长周期去重指标建设
大数据·数据仓库·面试·性能优化·flink
蓝眸少年CY6 小时前
什么是Hadoop
大数据·hadoop·分布式