物流需求分析&代码实现

206 阅读5分钟

客户数据指标开发

物流字典码表数据类型定义

为了后续使用方便且易于维护,根据物流字典表的数据类型定义成枚举工具类,物流字典表的数据如下:

来自:tbl_codes表

nametype
注册渠道1
揽件状态2
派件状态3
快递员状态4
地址类型5
网点状态6
员工状态7
是否保价8
运输工具类型9
运输工具状态10
仓库类型11
是否租赁12
货架状态13
回执单状态14
出入库类型15
客户类型16
下单终端类型17
下单渠道类型18

客户主题

需求分析

指标计算公式备注
总客户数下单总人数1人1天多次下单
今日新增客户数今日注册下单总人数
留存用户总数用户最后下单日期距今天数小于等于180
留存率(超过180天未下单表示已流失,否则表示留存)用户最后下单日期距今天数小于等于180
活跃用户数(近10天内有发件的客户表示活跃用户)用户最后下单日期距今天数小于等于10
月度新用户数用户当月下单总人数
沉睡用户数(3个月~6个月之间的用户表示已沉睡)用户最后下单日期距今天数:3个月 ~ 6 个月
流失用户数(9个月未下单表示已流失)用户最后下单日期距今天数 大于270天
客单数每个用户下单数
客单价每个用户单价
平均客单数总单数除以下单人数

数据字典

字段名别名字段描述
tbl_customeridid客户ID
tbl_customernamename客户姓名
tbl_customerteltel客户电话
tbl_customermobilemobile客户手机
tbl_customeremailemail客户邮箱
tbl_customertypetype客户类型ID
tbl_codescodeDesctype_name客户类型名称
tbl_customerisownregis_own_reg是否自行注册
tbl_customerregdtregdt注册时间
tbl_customerregchannelidreg_channel_id注册渠道ID
tbl_customerstatestate客户状态ID
tbl_customercdtcdt创建时间
tbl_customerudtudt修改时间
tbl_customerlastlogindtlast_login_dt最后登录时间
tbl_customerremarkremark备注
tbl_consumer_sender_infocdtfirst_cdt首次下单时间
tbl_consumer_sender_infocdtlast_cdt尾次下单时间
tbl_express_packagebillCountbillCount下单总数
tbl_express_packagetotalAmounttotalAmount累计下单金额
tbl_customeryyyyMMdd(cdt)day创建时间年月日格式

实现步骤:

实现步骤:

| 在dwd目录下创建 CustomerDWD 单例对象,继承自AbstractOfflineApp特质

| 初始化环境的参数,创建SparkSession对象

| 获取客户表(tbl_customer)数据,并缓存数据

| 判断是否是首次运行,如果是首次运行的话,则全量装载数据(含历史数据)

| 获取客户寄件信息表(tbl_consumer_sender_info)数据,并缓存数据

| 获取客户包裹表(tbl_express_package)数据,并缓存数据

| 获取物流字典码表(tbl_codes)数据,并缓存数据

| 根据以下方式拉宽仓库车辆明细数据

| 根据客户id,在客户表中获取客户数据

| 根据包裹id,在包裹表中获取包裹数据

| 根据客户类型id,在物流字典码表中获取客户类型名称数据

| 创建客户明细宽表(若存在则不创建)

| 将客户明细宽表数据写入到数据表中

| 删除缓存数据

代码

package cn. manor.logistics.offline.dws

import cn. manor.logistics.common.OfflineTableDefine import cn. manor.logistics.offline.AbstractOfflineApp import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Row}
import org.apache.spark.sql.functions.{current_date, date_format, date_sub, sum, trunc} import org.apache.spark.sql.types.{DoubleType, LongType, StringType, StructType}



/**
*客户主题指标开发:
*从表加载客户详情宽表数据,按照具体业务进行统计分析,最后结果存储数据库中DWS层。
*/
object CustomerDWS extends AbstractOfflineApp{
/**
*对数据集DataFrame按照业务需求编码,由子类复杂实现
*
*@param dataframe 数据集,表示加载事实表的数据
*@retur| 处理以后数据集
*/
override def process(dataframe: DataFrame): DataFrame = {
// 导入隐式转换
import dataframe.sparkSession.implicits._

/**
* TODO: 针对客户主题数据宽表来说,每天都是全量更新的,计算指标时加载全量数据进行计算的
*/
// 指标一:总客户数
val customerCount: Long = dataframe.count()
// 指标二:今日新增客户数(注册时间为今天)
val additionCustomerCount: Long = dataframe
.where(
date_format($"regdt", "yyyy-MM-dd") === date_sub(current_date(), 1)
)
.count()
// 指标三:留存数(超过180天未下单表示已流失,否则表示留存) 和留存率
val preserveCustomerCount: Long = dataframe
.where($"last_sender_cdt" <= date_sub(current_date(), 180))
.count()
val preserveRate: Double = preserveCustomerCount / customerCount.toDouble
// 指标四:活跃用户数(近10天内有发件的客户表示活跃用户) val activeCustomerCount: Long = dataframe
.where($"last_sender_cdt" <= date_sub(current_date(), 10))
.count()
// 指标五:月度新用户数
val monthOfNewCustomerCount: Long = dataframe
.where(
$"regDt".between(
trunc($"regDt", "mm"), date_format(current_date(), "yyyy-MM-dd")
)
)
.count()
// 指标六:沉睡用户数(3个月~6个月之间的用户表示已沉睡) val sleepCustomerCount = dataframe
.where(
"last_sender_cdt >= date_sub(now(), 90) and last_sender_cdt <= date_sub(now(), 180)"
)
.count()
// 指标七:流失用户数(9个月未下单表示已流失) val loseCustomerCount: Long = dataframe
.where("last_sender_cdt >= date_sub(now(), 270)")



.count()
// 指标八:客单价、客单数、平均客单数
val customerBillDF: DataFrame = dataframe.where("first_sender_id is not null")
// 客单数
val customerBillCount: Long = customerBillDF.count()
// 客单价 = 总金额/总单数
val customerBillAggDF: DataFrame = customerBillDF.agg( sum($"totalCount").as("sumCount"), // 总单数sum($"totalAmount").as("sumAmount") // 总金额
)
val billAggRow: Row = customerBillAggDF.first()
// 客单价 = 总金额/总单数
val customerAvgAmount: Double=billAggRow.getAs[Double]("sumAmount")/billAggRow.getAs[Long]("sumCount")
// 平均客单数
val avgCustomerBillCount: Double = billAggRow.getAs[Long]("sumCount") / customerBillCount.toDouble

// TODO: 需要将计算所有指标结果提取出来,并且组合到Row对象中
val dayValue: String = dataframe
.select(
date_format(current_date(), "yyyyMMdd").cast(StringType)
)
.limit(1).first().getAs[String](0) val aggRow: Row = Row(
dayValue, // customerCount, // additionCustomerCount, // preserveRate, // activeCustomerCount, //
monthOfNewCustomerCount, // sleepCustomerCount, // loseCustomerCount, // customerBillCount, // customerAvgAmount, // avgCustomerBillCount
)

// 第一步、将列表转换为RDD
val rowsRDD: RDD[Row] = spark.sparkContext.parallelize(Seq(aggRow))
// 第二步、自定义Schema信息
val aggSchema: StructType = new StructType()
.add("id", StringType, nullable = false)
.add("customerCount", LongType, nullable = true)
.add("additionCustomerCount", LongType, nullable = true)
.add("preserveRate", DoubleType, nullable = true)
.add("activeCustomerCount", LongType, nullable = true)
.add("monthOfNewCustomerCount", LongType, nullable = true)
.add("sleepCustomerCount", LongType, nullable = true)
.add("loseCustomerCount", LongType, nullable = true)
.add("customerBillCount", LongType, nullable = true)
.add("customerAvgAmount", DoubleType, nullable = true)
.add("avgCustomerBillCount", DoubleType, nullable = true)
// 第三步、调用SparkSession中createDataFrame方法,组合RowsRDD和Schema为DataFrame
val aggDF: DataFrame = spark.createDataFrame(rowsRDD, aggSchema)

// 返回聚合数据
aggDF
}

def main(args: Array[String]): Unit = {
// 调用模板方法,进行数据加载、计算和保存
execute(
this.getClass, OfflineTableDefine.CUSTOMER_DETAIL, // OfflineTableDefine.CUSTOMER_SUMMERY, isLoadFullData = true //
)
}
}

快递单指标开发

数据字典

字段名别名字段描述
tbl_express_billidid快递单id
tbl_express_billexpressNumberexpress_number快递单编号
tbl_express_billcidcid客户ID
tbl_customernamecname客户名字
tbl_addressdetailAddrcaddress详细地址
tbl_express_billeideid员工ID
tbl_couriernameename快递员姓名
tbl_dotiddot_id网点ID
tbl_dotdotNamedot_name网点名称
tbl_companycompanyNamecompany_name公司名字
tbl_express_billorderChannelIdorder_channel_id下单渠道ID
tbl_codeschannelTypeNameorder_channel_name下单渠道名称
tbl_express_billorderDtorder_dt下单时间
tbl_express_billorderTerminalTypeorder_terminal_type下单设备类型ID
tbl_codesorderTypeNameorder_terminal_type_name下单设备类型名称
tbl_express_billorderTerminalOsTypeorder_terminal_os_type下单设备操作系统ID
tbl_express_billreserveDtreserve_dt预约取件时间
tbl_express_billisCollectPackageTimeoutis_collect_package_timeout是否取件超时
tbl_express_billtimeoutDttimeout_dt超时时间
tbl_customertypetype客户类别id
tbl_express_billcdtcdt创建时间
tbl_express_billudtudt修改时间
tbl_express_billremarkremark备注
tbl_express_billyyyyMMdd(cdt)day创建时间 年月日格式

实现步骤:

| 在dws目录下创建 ExpressBillDWS 单例对象,继承自BasicOfflineApp特质

| 初始化环境的参数,创建SparkSession对象

| 根据指定的日期获取拉宽后的快递单宽表(tbl_express_bill_detail)增量数据,并缓存数据

| 判断是否是首次运行,如果是首次运行的话,则全量装载数据(含历史数据)

| 指标计算

| 计算总快递单数

| 各类客户快递单数

| 各类客户最大快递单数

| 各类客户最小快递单数

| 各类客户平均快递单数

| 各网点快递单数

| 各网点最大快递单数

| 各网点最小快递单数

| 各网点平均快递单数

| 各渠道快递单数

| 各渠道最大快递单数

| 各渠道最小快递单数

| 各渠道平均快递单数

| 各终端快递单数

| 各终端最大快递单数

| 各终端最小快递单数

| 各终端平均快递单数

| 获取当前时间yyyyMMddHH

| 构建要持久化的指标数据(需要判断计算的指标是否有值,若没有需要赋值默认值)

| 通过StructType构建指定Schema

| 创建快递单指标数据表(若存在则不创建)

| 持久化指标数据到表

代码

package cn. manor.logistics.offline.dws

import cn. manor.logistics.common.{Configuration, OfflineTableDefine, SparkUtils} import cn. manor.logistics.offline.BasicOfflineApp
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.functions._
import org.apache.spark.sql.types.{DoubleType, LongType, StringType, StructType} import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
import org.apache.spark.storage.StorageLevel import scala.collection.mutable.ListBuffer
/**
*快递单主题开发:
*加载Kudu中快递单宽表:dwd_tbl_express_bill_detail 数据,按照业务进行指标统计
*/
object ExpressBillDWS extends BasicOfflineApp{

/**
*数据处理,依据指标进行分析
*/
override def process(dataframe: DataFrame): DataFrame = {
// 导入隐式转换
val spark: SparkSessio| = dataframe.sparkSessio| import spark.implicits._

// 由于多个指标分析,宽表数据被使用多次,需要缓存dataframe.persist(StorageLevel.MEMORY_AND_DISK) if(dataframe.isEmpty){
logWarning(s"Kudu数据库DWD层[${OfflineTableDefine.EXPRESS_BILL_DETAIL}]表,没有加载到数据,请确认有数据在执

行	")

}


System.exit(-1)


// TODO: 假设每次加载宽表的数据都是全量的,离线分析按照每天聚合计算,将数据划分为不同天,针对每天的数据进行聚合计算
// 定义集合列表,可变的列表,将每天计算指标数据Row存储
val rowList: ListBuffer[Row] = ListBuffer[Row]() dataframe.select($"day").distinct().collect().foreach{dayRow =>
// 获取具体某天的值
val day: String = dayRow.getAs[String](0) // 1210, 1211, 1212

// 按照day进行过滤获取每天的数据,进行指标的计算
val expressBillDetailDF: Dataset[Row] = dataframe.filter($"day" === day)

// 按照业务指标计算
// 指标一:计算总快递单数
val expressBillTotalDF: DataFrame = expressBillDetailDF.agg(count($"id").as("total"))




// 指标二:各类客户快递单数,最大、最小、平均
val expressBillTypeTotalDF: DataFrame = expressBillDetailDF.groupBy($"type").count() val expressBillTypeTotalAggDF: DataFrame = expressBillTypeTotalDF.agg(
max($"count").as("maxTypeTotal"), // min($"count").as("minTypeTotal"), // round(avg($"count").as("avgTypeTotal"), 0) //
)

// 各网点快递单数指标三:,最大、最小、平均
val expressBillDotTotalDF: DataFrame = expressBillDetailDF.groupBy($"dot_id").count() val expressBillDotTotalAggDF: DataFrame = expressBillDotTotalDF.agg(
max($"count").as("maxDotTotal"), // min($"count").as("minDotTotal"), // round(avg($"count").as("avgDotTotal"), 0) //
)

// 指标四:各渠道快递单数,最大、最小、平均
val expressBillChannelTotalDF: DataFrame = expressBillDetailDF.groupBy($"order_channel_id").count() val expressBillChannelTotalAggDF: DataFrame = expressBillChannelTotalDF.agg(
max($"count").as("maxChannelTotal"), // min($"count").as("minChannelTotal"), // round(avg($"count").as("avgChannelTotal"), 0) //
)

// 指标五:各终端快递单数,最大、最小、平均
val expressBillTerminalTotalDF: DataFrame = expressBillDetailDF.groupBy($"order_terminal_type").count() val expressBillTerminalTotalAggDF: DataFrame = expressBillTerminalTotalDF.agg(
max($"count").as("maxTerminalTotal"), // min($"count").as("minTerminalTotal"), // round(avg($"count").as("avgTerminalTotal"), 0) //
)

// TODO: 需要将计算所有指标结果提取出来,并且组合到Row对象中
/*
Row对象创建方式2种:
-1. Row(v1, v2, v3,	)
-2. Row.fromSeq(Seq(v1, v2, v3,	))
*/
val aggRow: Row = Row.fromSeq( dayRow.toSeq ++
expressBillTotalDF.first().toSeq ++ expressBillTypeTotalAggDF.first().toSeq ++ expressBillDotTotalAggDF.first().toSeq ++ expressBillChannelTotalAggDF.first().toSeq ++ expressBillTerminalTotalAggDF.first().toSeq
}

/**

)
// 将每天聚合计算结果加入列表中
rowList += aggRow



*问题:如何将列表中数据转换为DataFrame
*采用并行化方向将列表(Seq类型对象)转换为RDD
*RDD转换为DataFrame: 反射RDD[CaseClass] 和 自定义Schema-> RDD[Row]和Schema
*/
// 第一步、将列表转换为RDD
val rowsRDD: RDD[Row] = spark.sparkContext.parallelize(rowList.toList) // 将可变集合对象转换为不可变的
// 第二步、自定义Schema信息
val aggSchema: StructType = new StructType()
.add("id", StringType, nullable = false) // 针对每天数据进行聚合得到一个结果,设置day为结果表中id
.add("total", LongType, nullable = true)
.add("maxTypeTotal", LongType, nullable = true)
.add("minTypeTotal", LongType, nullable = true)
.add("avgTypeTotal", DoubleType, nullable = true)
.add("maxDotTotal", LongType, nullable = true)
.add("minDotTotal", LongType, nullable = true)
.add("avgDotTotal", DoubleType, nullable = true)
.add("maxChannelTotal", LongType, nullable = true)
.add("minChannelTotal", LongType, nullable = true)
.add("avgChannelTotal", DoubleType, nullable = true)
.add("maxTerminalTotal", LongType, nullable = true)
.add("minTerminalTotal", LongType, nullable = true)
.add("avgTerminalTotal", DoubleType, nullable = true)

// 第三步、调用SparkSession中createDataFrame方法,组合RowsRDD和Schema为DataFrame val aggDF: DataFrame = spark.createDataFrame(rowsRDD, aggSchema)

// 返回计算指标结果数据
aggDF
}

// SparkSQL 应用程序入口
def main(args: Array[String]): Unit = {
// step1. 构建SparkSession实例对象,传递SparkConf对象
val spark: SparkSessio| = SparkUtils.createSparkSession( SparkUtils.autoSettingEnv(SparkUtils.sparkConf()), this.getClass
)
import spark.implicits._ spark.sparkContext.setLogLevel(Configuration.LOG_OFF)

// step2. 加载快递单宽表数据
val expressBillDetailDF: DataFrame = load(
spark, OfflineTableDefine.EXPRESS_BILL_DETAIL, isLoadFullData = Configuration.IS_FIRST_RUNNABLE
)
expressBillDetailDF.show(10, truncate = false)

// step3. 按照业务指标开发
val expressBillSummaryDF: DataFrame = process(expressBillDetailDF) expressBillSummaryDF.show(10, truncate = false)

// step4. 保存分析指标结果数据
save(expressBillSummaryDF, OfflineTableDefine.EXPRESS_BILL_SUMMARY)

// step5. 应用结束,关闭资源
spark.stop()
}
}

车辆指标开发

数据字典

字段名别名字段描述
tbl_transport_toolidid运输工具ID
tbl_transport_toolbrandbrand运输工具品牌
tbl_transport_toolmodelmodel运输工具型号
tbl_transport_tooltypetype运输工具类型
tbl_codescodeDesc/ttTypeNametype_name车辆类型描述
tbl_transport_toolgivenLoadgiven_load额定载重
tbl_transport_toolloadCnUnitload_cn_unit中文载重单位
tbl_transport_toolloadEnUnitload_en_unit英文载重单位
tbl_transport_toolbuyDtbuy_dt购买时间
tbl_transport_toollicensePlatelicense_plate牌照
tbl_transport_toolstatestate运输工具状态
tbl_codescodeDesc/ttStateNamestate_name运输工具状态描述
tbl_transport_toolcdtcdt创建时间
tbl_transport_tooludtudt修改时间
tbl_transport_toolremarkremark备注
tbl_dotiddot_id网点id
tbl_dotdotNumberdot_number网点编号
tbl_dotdotNamedot_name网点名称
tbl_dotdotAddrdot_addr网点地址
tbl_dotdotGisAddrdot_gis_addr网点GIS地址
tbl_dotdotTeldot_tel网点电话
tbl_dotmanageAreaIdmanage_area_id网点管理辖区ID
tbl_dotmanageAreaGismanage_area_gis网点管理辖区地理围栏
tbl_companyidcompany_id公司ID
tbl_companycompanyNamecompany_name公司名称
tbl_companycityIdcity_id城市ID
tbl_companycompanyNumbercompany_number公司编号
tbl_companycompanyAddrcompany_addr公司地址
tbl_companycompanyAddrGiscompany_addr_gis公司gis地址
tbl_companycompanyTelcompany_tel公司电话
tbl_companyisSubCompanyis_sub_company母公司ID
tbl_transport_toolyyyyMMdd(cdt)day创建时间,年月日格式

实现步骤:实现步骤:

l 在dws目录下创建 TransportToolWarehouseDWS 单例对象,继承自AbstractOfflineApp特质

l 初始化环境的参数,创建SparkSession对象

l 根据指定的日期获取拉宽后的车辆主题宽表(ttbl_warehouse_transport_tool_detail)增量数据,并缓存数据

l 判断是否是首次运行,如果是首次运行的话,则全量装载数据(含历史数据)

l 指标计算

l 构建要持久化的指标数据(需要判断计算的指标是否有值,若没有需要赋值默认值)

l 通过StructType构建指定Schema

| 各网点发车次数

| 各网点最大发车次数

| 各网点最小发车次数

| 各网点平均发车次数

| 各区域发车次数

| 各区域最大发车次数

| 各区域最小发车次数

| 各区域平均发车次数

| 各公司发车次数

| 各公司最大发车次数

| 各公司最小发车次数

| 各公司平均发车次数

| 获取当前时间yyyyMMddHH

l 创建车辆主题指标数据表(若存在则不创建)

l 持久化指标数据到表

代码

package cn. manor.logistics.offline.dws

import cn. manor.logistics.common.{Configuration, OfflineTableDefine} import cn. manor.logistics.offline.AbstractOfflineApp
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, Row}
import org.apache.spark.sql.functions.{avg, max, min, round, sum}
import org.apache.spark.sql.types.{DoubleType, LongType, StringType, StructType} import scala.collection.mutable.ListBuffer
/**
*仓库车辆相关指标聚合统计:
*从Kudu表加载仓库车辆详细宽表数据,按照不同维度进行分组,计算相关指标。
*/
object TransportToolWarehouseDWS extends AbstractOfflineApp{
/**
*对数据集DataFrame按照业务需求编码,由子类复杂实现
*
*@param dataframe 数据集,表示加载事实表的数据
*@retur| 处理以后数据集
*/
override def process(dataframe: DataFrame): DataFrame = {
// 导入隐式转换
import dataframe.sparkSession.implicits._

// dataframe 表示从Kudu表加载宽表数据:tbl_warehouse_transport_tool_detail val rowList: ListBuffer[Row] = ListBuffer[Row]() dataframe.select($"day").collect().foreach{dayRow =>
// 获取具体日期day
val day: String = dayRow.getAs[String](0)
// 依据day过滤出对应宽表数据
val ttWsDetailDF: Dataset[Row] = dataframe.filter($"day" === day)

// 指标计算
// 指标一:各网点发车次数及最大、最小和平均
val ttWsTotalDF: DataFrame = ttWsDetailDF.groupBy($"ws_id").count() val ttWsTotalAggDF: DataFrame = ttWsTotalDF.agg(
sum($"count").as("sumDotTotal"), // max($"count").as("maxDotTotal"), // min($"count").as("minDotTotal"), // round(avg($"count"), 0).as("avgDotTotal") //
)
// 指标二:各区域发车次数及最大、最小和平均
val ttCityTotalDF: DataFrame = ttWsDetailDF.groupBy($"city_id").count() val ttCityTotalAggDF: DataFrame = ttCityTotalDF.agg(
sum($"count").as("sumCityTotal"), // max($"count").as("maxCityTotal"), // min($"count").as("minCityTotal"), // round(avg($"count"), 0).as("avgCityTotal") //
)



// 指标三:各公司发车次数及最大、最小和平均
val ttCompanyTotalDF: DataFrame = ttWsDetailDF.groupBy($"company_id").count() val ttCompanyTotalAggDF: DataFrame = ttCompanyTotalDF.agg(
sum($"count").as("sumCompanyTotal"), // max($"count").as("maxCompanyTotal"), // min($"count").as("minCompanyTotal"), // round(avg($"count"), 0).as("avgCompanyTotal") //
)

// TODO: 需要将计算所有指标结果提取出来,并且组合到Row对象中
val aggRow: Row = Row.fromSeq( dayRow.toSeq ++ //
ttWsTotalAggDF.first().toSeq ++ // ttCityTotalAggDF.first().toSeq ++ // ttCompanyTotalAggDF.first().toSeq //
)
// 将每天聚合计算结果加入列表中
rowList += aggRow
}

// 第一步、将列表转换为RDD
val rowsRDD: RDD[Row] = spark.sparkContext.parallelize(rowList.toList) // 将可变集合对象转换为不可变的
// 第二步、自定义Schema信息
val aggSchema: StructType = new StructType()
.add("id", StringType, nullable = false) // 针对每天数据进行聚合得到一个结果,设置day为结果表中id
.add("sumWsTotal", LongType, nullable = true)
.add("maxWsTotal", LongType, nullable = true)
.add("minWsTotal", LongType, nullable = true)
.add("avgWsTotal", DoubleType, nullable = true)
.add("sumCityTotal", LongType, nullable = true)
.add("maxCityTotal", LongType, nullable = true)
.add("minCityTotal", LongType, nullable = true)
.add("avgCityTotal", DoubleType, nullable = true)
.add("sumCompanyTotal", LongType, nullable = true)
.add("maxCompanyTotal", LongType, nullable = true)
.add("minCompanyTotal", LongType, nullable = true)
.add("avgCompanyTotal", DoubleType, nullable = true)

// 第三步、调用SparkSession中createDataFrame方法,组合RowsRDD和Schema为DataFrame val aggDF: DataFrame = spark.createDataFrame(rowsRDD, aggSchema)

// 返回聚合数据
aggDF
}

def main(args: Array[String]): Unit = {
// 调用execute方法,传递Kudu表名称及是否第一次运行
execute(
this.getClass, OfflineTableDefine.WAREHOUSE_TRANSPORT_TOOL_DETAIL, // OfflineTableDefine.WAREHOUSE_TRANSPORT_TOOL_SUMMARY, // isLoadFullData = Configuration.IS_FIRST_RUNNABLE //
)
}
}