一、用--仓

用户数据仓库
赚跟多人的钱,用户分析,运营
当前赚了多少钱,业务报表,财务

一、项目背景

1、项目概述

根据业务系统中采集的用户行为日志数据,实现:

  • 1)数据采集平台
  • 2)数据仓库搭建,实现对平台层用户维度分析。
  • 3)数据仓库的调度

分析平台层用户维度,对用户活跃、新增、留存、沉默、流失、回流等8个主题的分析。

  • 0)活跃用户:
  • 1)新增用户:今天活跃表 leftjoin 新增表(历史新增),活跃表存在但是新增表没有,证明是新增的
  • 2)留存用户:今天活跃表 leftjoin 上周一新增用户, 两个表都存在的用户为留存用户。
    留存率: 留存用户数/上周的新增用户数。
  • 3)回流用户(本周的老用户上周没活跃,本周活跃的,上周没活跃,但上周以前活跃了 ):本周活跃-本周新增-上周活跃,- 为leftjoin
  • 4)沉默用户:按照设备id,对日活跃表分组,登录次数为1,且是在一周前登录。
  • 5)流失用户:按照设备id,对日活跃表分组,且七天内没有登录过。
  • 6)最近连续3周活跃用户数:按照设备id,对周活进行分组,统计次数等于3次。
  • 7)最近七天内,连续3天活跃用户数?
    • 1)查询出最近7天的活跃用户,并对用户活跃,日期进行排名
    • 2)计算用户活跃日期,及排名之间的差值
    • 3)对同用户及差值分组,统计差值个数
    • 4)将差值相同个数大于等于3的数据取出,然后去重,即为连续3天及以上活跃的用户

等差数列,
先将每个人都登录日期排名,
每个登录日期减去自己对应的排名值,
如果差值相同,说明日期是连续的。
因此,对差值进行分组,count值为3,说明这7天里有3天日期连续,符合,得到该用户。

2、hive分层

1)ods层
外部分区表,按照日期分区,LZO压缩
HDFS数据映射 ,两张表,启动日志表、用户行为表

2)dwd层

启动日志表,

  • get_json_object,将ods中json每个字段解析出来
  • insert into select
  • 一个json,一行数据

事件日志基础表,

  • 事件日志json包括,公共字段cm,value是一个json;还包括一个事件字段et,value是一个json数组包含多个事件。。
  • 需要自定义hive解析函数,是因为,这个事件日志不是一个标准日志,事件日志json前面有一个服务器事件加竖线,因此不能像启动日志那样直接通过get_json_object来解析字段,
  • udf,一进一出,像get_json_object一样,把公共字段解析出来
  • udtf,一进多出,解析具体字段,输入一个事件日志,输出多个事件名与json,key为事件名,value为他的json,主要是在公共字段的基础上,增加了 event_name, event_json两个字段。
  • 再使用lateral view侧写 + udtf,将一行多个事件数据拆成多行单独的事件数据
  • 一个json,多行数据

事件明显表,
就是在基础表中(各种事件都在一张表),拿到自己的事件及其json解析后字段。

3)dws层:

  • 通过dwd层的表来实现各种维度的统计:
  • 用户活跃维度:统计当日、当周、当月活动的每个设备明细 。
  • 方式:统计活跃设备明细:启动日志表:每个设备启动一次都会有一个启动日志表

4)ads层:
dws层 ==> 一些指标的汇总到ads层 ==> 使用脚本进行控制

5)调度时
shell文件,使用hive -e sql 执行特定sql
还有hive -f 文件

6)数据可视化:
可以通过sqoop将hive当中ads层的数据,导出到mysql做报表展示
直接连hive进行可是化展示吗不行的,hive查询比较慢

3、其他

1)数据源

日志分为两大类:

  • 1、app启动日志,每启动一次app,都会打印一条启动日志
  • 2、用户行为日志:基础数据,用户操作数据

启动日志和用户行为日志,都在一个日志文件中,日志名字按天区分
1)启动日志

{"action":"1","ar":"MX","ba":"Huawei","detail":"542",
"en":"start","entry":"3","extend1":"","g":"5666N0XK@gmail.com","hw":"1080*1920","l":"en","la":"19.5","ln":"-78.6","loading_time":"10","md":"Huawei-18",
"mid":"0","nw":"3G","open_ad_type":"2","os":"8.2.6","sr":"B","sv":"V2.5.4","t":"1660178510462",
"uid":"0","vc":"0","vn":"1.3.5"}

共有25个字段,

关键字段:
状态 action=1,可以算成前台活跃;
en = start,日志类型为启动日志;

2)用户行为日志

公共字段:基本所有安卓手机都包含的字段
业务字段:埋点上报的字段,有具体的业务类型
下面就是一个示例,表示业务字段的上传。

1574774962420|{"ap":"app",
"cm":{"ln":"-51.5","sv":"V2.5.7","os":"8.1.0","g":"Z0BN6732@gmail.com","mid":"995"...},
"et":[{"ett":"1574758387950","en":"display","kv":{"goodsid":"239","action":"1","extend1":"1","place":"5","category":"41"}},{"ett":"1574688967010","en":"newsdetail","kv":{"entry":"3","goodsid":"240","news_staytime":"3","loading_time":"12","action":"4","showtype":"5","category":"49","type1":"542"}}]}
  • ① 公共字段:k:cm,value:json形式:包含客户端基本信息,如设备标识mid、用户标识uid、区域ar,表示那个用户。

  • ② 业务字段: k:et,value:jsonArray形式,包含多个具体事件json,每个具体的事件json,具有不同的字段,对于hive中不同的原始表

    • 事件k: 1)进入产品详情(loading)、2)商品点击(display)、3)评论(comment)、收藏(favorites)、4)点赞(praise)、5)商品页面详情(newsdetail)、6)点击广告(ad)

用户操作数据的一条数据包含两部分:
1、基本公共字段:cm对应的都是安卓手机公共字段
2、用户的行为事件字段:商品列表页,商品点击,商品详情,广告,消息通知
活跃,评论,收藏,点赞,错误日志

3)数据来源

用户行为数据:pc端使用的是JS埋点日志数据;移动端使用的是SDK内嵌的
业务数据(数据库的数据)

一部分是系统生成的样例数据,数据不够,又通过javabean造数据

模拟一个java程序去生成日志数据 => 使用flume进行采集 node01和node02都去采集 => 汇总到node03 => 通过node03的flume将数据保存到hdfs上面去了

2)有mid,不用uid

mid,设备id,因为我们系统通过公众号、视频号等一些手段,进行推广,可能一些用户没有进行注册,因此通过mid来标识,具体的是业务方面的同事处理,bs浏览器端是了浏览器的标识,微信小程序端是设备标识

数仓还有其他维度的开发

  • 平台层的用户维度,统计平台中用户的数量
  • 商品维度,
  • 订单维度,
  • 用户层,统计某个用户的点赞收藏评论,等用户画像。

开启hive本地模式以加快hive查询速度,yarn分配资源是很慢的
set hive.exec.mode.local.auto=true;

1、用户行为数据仓
使用flume采集node1、2日志数据 => node3的flume汇总,保存到hdfs
=> 创建hive表映射ods层,自定义hive的函数udf,udtf,解析启动日志以及事件的日志
=> 到了dwd层
=> dws层 1变11张表
=> ads层 统计各种指标结果

使用shell脚本来实现程序的自动化的运行

2、业务数据仓库如何实现:
sqoop导入数据到hdfs
=> ods层创建表映射hdfs的数据
=> dwd层进行数据的汇总(商品和分类表进行了汇总)
=> dws层 主要就是针对数据进行聚合操作 ==> ads层做数据报表

数据可视化:可以通过sqoop将hive当中ads层的数据,导出到mysql做报表展示

二、flume数据采集

flume作用

  • 1)采集日志数据到hdfs
    • node01与node02都生成了日志,日志数据会实时追加,通过flume中Taildir Source实时监测采集日志数据,使用file类型的Channel,把数据缓存在磁盘中。
  • 2)日志类型区分,将启动日志与用户行为日志 分开,存在hdfs不同文件夹。
    • 自定义类型区分拦截器 ,实现数据的区分,以及粗略的ETL的过滤,过滤脏数据
  • 3)数据校验,校验日志格式是否为json ,

1、flume参数配置

node01与node02:收集日志

  • source:tailDir source 支持事务处理
  • channel: fileChannel(实际工作当中使用比较多)
  • sink:avro sink

node03:汇总node01与node02数据,上传到hdfs,使用lzo的压缩方式了

  • source:avro source
  • channel: fileChannel
  • sink: hdfsSink

在这里插入图片描述

1)在node01和node02主机上
/kkb/install/flume-1.6.0-cdh5.14.2/myconf目录下
创建flume-client.conf文件

#flume-client.conf
a1.sources = r1
a1.sinks = k1
a1.channels = c1

# 配置source
a1.sources.r1.type = taildir
a1.sources.r1.positionFile = /kkb/bigdata/index/log_position.json
a1.sources.r1.filegroups = f1
a1.sources.r1.filegroups.f1 = /kkb/bigdata/weblog/app.+
a1.sources.r1.fileHeader = true
a1.sources.r1.channels = c1

#interceptor
a1.sources.r1.interceptors = i1 i2
a1.sources.r1.interceptors.i1.type = com.bigdata.flume.interceptor.LogETLInterceptor Builder
a1.sources.r1.interceptors.i2.type =com.bigdata.flume.interceptor.LogTypeInterceptor$Builder

#配置channel
a1.channels.c1.type = file
#检查点文件目录
a1.channels.c1.checkpointDir=/kkb/bigdata/flume_checkpoint
#缓存数据文件夹
a1.channels.c1.dataDirs=/kkb/bigdata/flume_data

#通道支持事务的最大大小
a1.channels.c1.transactionCapacity=1000000
#添加或者删除一个event的超时时间,单位为秒,默认是3
a1.channels.c1.keep-alive=60
#添加event,最多保存多少个event,默认是100
a1.channels.c1.capacity=1000000
#配置sink
a1.sinks.k1.channel = c1
a1.sinks.k1.type = avro
#node3
a1.sinks.k1.hostname = 192.168.52.120
a1.sinks.k1.port = 4141

在文件配置如下内容
注意:
com.bigdata.flume.interceptor.LogETLInterceptorcom.bigdata.flume.interceptor.LogTypeInterceptor是自定义的拦截器的全类名。
需要根据用户自定义的拦截器做相应修改。
2)在node03主机上
/kkb/install/flume-1.6.0-cdh5.14.2/myconf目录下
创建flume-hdfs.conf文件

#Name the components on this agent
a1.sources = r1
a1.sinks = k1
a1.channels = c1

#配置source
a1.sources.r1.type = avro
a1.sources.r1.bind = 192.168.52.120
a1.sources.r1.port = 4141
a1.sources.r1.channels = c1

#配置channel
a1.channels.c1.type = file
#检查点文件目录
a1.channels.c1.checkpointDir=/kkb/bigdata/flume_checkpoint
#缓存数据文件夹
a1.channels.c1.dataDirs=/kkb/bigdata/flume_data

#配置sink
a1.sinks.k1.channel = c1
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = hdfs://node01:8020/origin_data/gmall/log/%{topic}/%Y-%m-%d

a1.sinks.k1.hdfs.filePrefix = logevent-
a1.sinks.k1.hdfs.round = true
a1.sinks.k1.hdfs.roundValue = 10
a1.sinks.k1.hdfs.roundUnit = second

#不要产生大量小文件
a1.sinks.k1.hdfs.rollInterval = 10
a1.sinks.k1.hdfs.rollSize = 134217728
a1.sinks.k1.hdfs.rollCount =1000
a1.sinks.k1.hdfs.useLocalTimeStamp = true
a1.sinks.k1.hdfs.minBlockReplicas=1
a1.sinks.k1.hdfs.fileType = CompressedStream
a1.sinks.k1.hdfs.codeC = lzop

2、flume拦截器

实现Interceptor接口,重写intercept(Event event)方法,对event对象进行相关处理与返回;

拦截器打包之后,由于依赖包在flume的lib目录下面已经存在了,故只需要单独包,不需要将依赖的包上传。打包之后要放入三台服务器Flume的lib文件夹下面;

java编写flume的拦截器代码,maven打包上传到服务器,配置node01与node02的flume-client.conf时,source里添加拦截器的类型为刚刚打包的拦截器类型。

1)日志类型区分拦截器

主要用于,将启动日志和事件日志区分开来
在这里插入图片描述

2)ETL拦截器

主要用于过来时间戳不合法,,和json数据不完整的日志
在这里插入图片描述

3、flume启动与停止

/bin/flume-ng agent 
-n a1 agent名称
-c /kkb/myconf 置文件所在的目录
-f /kkb/myconf/flume-hdfs.conf  配置文件的路径名称
-Dflume.root.logger=info,console  定key=value键值对---启动的日志输出级别

ps -ef | grep flume | grep -v grep |awk '{print \$2}' | xargs kill

三、hive分层分析

1、ODS,原始数据层

2个表,存放原始数据,保持原貌不做处理

1)ods_start_log 启动日志表

只有一个字段 line(保存着json),按照日期dt分区,表的格式:lzo
在这里插入图片描述
现在根据启动日志表,进行每日/周/月用户活跃、新增、留存、沉默、流失、回流,6个主题的分析。
在这里插入图片描述

2)ods_event_log 事件日志表(格式同启动日志表)

只有一个字段 line ,按照日期dt 分区,表的格式:lzo

在这里插入图片描述
在这里插入图片描述

2、DWD,明细数据层:

10个表,对ods层数据清洗(去除空值,脏数据,超过极限范围的数据),

1)dwd_start_log 启动表,

  • 关键字段:mid_id,user_id,dt(分区字段,按照日期分区) (其实这是启动表和事件表的公共字段);
  • 从ods_start_log中的line,用get_json_object(line,’$.mid’) mid_id的方式获取字段。

2)dwd_base_event_log 事件日志基础明细表:

  • 公共字段:mid_id、user_id、dt(分区字段),event_name、event_json、server_time
  • 来源:
    ①从 ods_event_log的line 中用 UDF 获取 公共字段 和 server_time,
    ②用UDTF 获取 event_name , event_json。

1)dwd_start_log 启动表,

  • 关键字段:mid_id,user_id,dt(分区字段,按照日期分区) (其实这是启动表和事件表的公共字段);
  • 从ods_start_log中的line,用get_json_object(line,’$.mid’) mid_id的方式获取字段。

在这里插入图片描述

2)dwd_base_event_log 事件日志基础明细表:

  • 公共字段:mid_id、user_id、dt(分区字段),event_name、event_json、server_time
  • 来源:
    ①从 ods_event_log的line 中用 UDF 获取 公共字段 和 server_time,
    ②用UDTF 获取 event_name , event_json。
    在这里插入图片描述
    在这里插入图片描述

3)自定义UDF/UDTF

为什么要自定义UDF/UDTF,
因为自定义函数,可以自己埋点Log打印日志,出错或者数据异常,方便调试。

自定义UDF函数(解析公共字段,一进一出)
自定义UDTF函数(解析具体事件字段,一进多出)
  • 自定义UDF:继承UDF,重写evaluate方法

  • 自定义UDTF:继承自GenericUDTF,重写3个方法:initialize(自定义输出的列名和类型),process(将结果返回forward(result)),close

自定义UDF:继承UDF,重写evaluate方法
在这里插入图片描述
自定义UDTF:继承自GenericUDTF,
在这里插入图片描述

4)其他的具体事件明细表

在这里插入图片描述
8个具体事件表:
来源:
从刚才事件基础明显表中,直接获取公共字段和server_time,
从刚才事件基础明显表event_json中,获取特有字段,where event_name = “display”

从一张事件基础明细表,一共可以获得8张具体事件明细表
dwd_display_log 商品点击表
dwd_newsdetail_log 商品详情页表
dwd_loading_log 商品列表页表
dwd_ad_log 广告表
dwd_comment_log 评论表
dwd_favorites_log 收藏表
dwd_praise_log 点赞表
dwd_error_log 错误日志表

用于后续,用户画像分析,精准推销

3、DWS,服务数据层,轻度聚合;

1)设备活跃

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2)新增设备

在这里插入图片描述
在这里插入图片描述

3)设备留存

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2)新增设备

3)设备留存

4)沉默设备

使用日活明细表dws_uv_detail_day作为DWS层数据

5)本周回流用户

使用日活明细表dws_uv_detail_day作为DWS层数据

6)本周流失用户

使用日活明细表dws_uv_detail_day作为DWS层数据

7)最近连续三周活跃用户

使用活明细表dws_uv_detail_wk作为DWS层数据

8)最近7天内连续3天活跃用户

使用日活明细表dws_uv_detail_day作为DWS层数据

4、ADS,应用数据层,具体需求

1)设备活跃

在这里插入图片描述

2)新增设备

在这里插入图片描述

3)设备留存

在这里插入图片描述
在这里插入图片描述

4)沉默设备

在这里插入图片描述

5)本周回流用户

在这里插入图片描述

6)本周流失用户

在这里插入图片描述

7)最近连续三周活跃用户

在这里插入图片描述

8)最近7天内连续3天活跃用户

在这里插入图片描述

四、shell脚本调度

如:流失客户用户,
1)在node03 的/home/hadoop/bin目录下创建脚本
[hadoop@node03 bin]$ vim ads_wastage_log.sh
在这里插入图片描述

2)增加脚本执行权限
[hadoop@node03 bin]$ chmod 777 ads_continuity_wk_log.sh

3)脚本使用
[hadoop@node03 bin]$ ./ads_continuity_wk_log.sh 2019-02-20

4)查询结果
hive (gmall)> select * from gmall.ads_continuity_wk_count;

5)脚本执行时间
企业开发中一般在每周一凌晨30分~1点

五、业务分析数仓

1、数据源:

业务库mysql中8张表:用户表、订单表、商品表、商品一二三级表、订单流水表、支付流水表。通过sqoop定时导入

在这里插入图片描述
实体表用户表、商品表,指一个现实存在的业务对象,全量同步;
维度表商品一二三级表,业务对象的解释表,全量同步;
事务型事实表订单流水表、支付流水表,指随着业务发生不断产生,一旦发生不会再变化的数据。 每日增量;
周期性事实表订单表,指随着业务发生不断产生,会随着业务周期性的推进而变化的数据。 拉链表模型。

拉链表是一种数据模型, 所谓拉链,就是记录历史。记录一个事物从开始,一直到当前状态的所有变化的信息。拉链表可以避免按每一天存储所有记录造成的海量存储问题,同时也是处理缓慢变化数据(SCD2)的一种常见方式。

拉链表通常会增加三个技术字段“开始日期starttime、结束日期endtime、状态标识mark”。通过主键(PK)与历史数据进行对比,判断当前数据与历史数据是否发生变化,如果发生变化或者新增则进行相应的开链、闭链操作。

2、sqoop数据采集

1)在/home/hadoop/bin目录下创建脚本sqoop_import.sh

[hadoop@node03 bin]$ vim sqoop_import.sh

在脚本中填写如下内容

#!/bin/bash

db_date=$2
echo $db_date
db_name=gmall

import_data() {
/kkb/install/sqoop-1.4.6-cdh5.14.2/bin/sqoop import \
--connect jdbc:mysql://node03:3306/$db_name?useSSL=false \
--username root \
--password 123456 \
--target-dir /origin_data/$db_name/db/$1/$db_date \
--delete-target-dir \
--num-mappers 1 \
--fields-terminated-by "\t" \
--query "$2"' and $CONDITIONS;'
}

import_sku_info(){
  import_data "sku_info" "select 
id, spu_id, price, sku_name, sku_desc, weight, tm_id,
category3_id, create_time
  from sku_info where 1=1"
}

 

case $1 in   
  "sku_info")
     import_sku_info
;;   
   "all")
   import_sku_info
   
;;
esac

2)增加脚本执行权限

[hadoop@node03 bin]$ chmod 777 sqoop_import.sh

3)执行脚本导入数据

[hadoop@node03 bin]$ ./sqoop_import.sh all 2019-02-10

4)在SQLyog中生成2019年2月11日数据

CALL init_data('2019-02-11',1000,200,300,TRUE);

5)执行脚本导入数据

[hadoop@node03 bin]$ ./sqoop_import.sh all 2019-02-11

3、hive分层

1)ods层

(八张表,表名,字段跟mysql完全相同),从origin_data把数据导入到ods层,表名在原表名前加ods_。

hive (gmall)>
drop table if exists gmall.ods_order_info;
create external table gmall.ods_order_info (
 `id` string COMMENT '订单编号',
 `total_amount` decimal(10,2) COMMENT '订单金额',
 `order_status` string COMMENT '订单状态',
 `user_id` string COMMENT '用户id',
 `payment_way` string COMMENT '支付方式',
 `out_trade_no` string COMMENT '支付流水号',
 `create_time` string COMMENT '创建时间',
 `operate_time` string COMMENT '操作时间'
) COMMENT '订单表'
PARTITIONED BY (`dt` string)
row format delimited fields terminated by '\t'
location '/warehouse/gmall/ods/ods_order_info/'
;

2) dwd层

对ODS层数据进行判空过滤。对商品分类表进行维度退化(降维)。创建商品表,增加分类,宽表。其他数据跟ods层一模一样;
1)维度退化要付出什么代价?
如果被退化的维度,还有其他业务表使用,退化后处理起来就麻烦些。
2)想想在实际业务中还有那些维度表可以退化
城市的三级分类(省、市、县)等
在这里插入图片描述

3)DWS层之用户行为宽表

为什么要建宽表
把每个用户单日的行为聚合起来组成一张多列宽表,以便之后关联用户维度信息后进行,不同角度的统计分析。
在这里插入图片描述

在这里插入图片描述

4)ADS层

需求一:GMV成交总额

在这里插入图片描述
在这里插入图片描述

需求二:用户转化率

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

需求三:品牌复购率

在这里插入图片描述

六、业务术语

1.用户
用户以设备为判断标准,在移动统计中,每个独立设备认为是一个独立用户。Android系统根据IMEI号,IOS系统根据OpenUDID来标识一个独立用户,每部手机一个用户。

2.新增用户
首次联网使用应用的用户。如果一个用户首次打开某APP,那这个用户定义为新增用户;卸载再安装的设备,不会被算作一次新增。新增用户包括日新增用户、周新增用户、月新增用户。

3.活跃用户
打开应用的用户即为活跃用户,不考虑用户的使用情况。每天一台设备打开多次会被计为一个活跃用户。

4.周(月)活跃用户
某个自然周(月)内启动过应用的用户,该周(月)内的多次启动只记一个活跃用户。

5.月活跃率
月活跃用户与截止到该月累计的用户总和之间的比例。

6.沉默用户
用户仅在安装当天(次日)启动一次,后续时间无再启动行为。该指标可以反映新增用户质量和用户与APP的匹配程度。

7.版本分布
不同版本的周内各天新增用户数,活跃用户数和启动次数。利于判断APP各个版本之间的优劣和用户行为习惯。

8.本周回流用户
上周未启动过应用,本周启动了应用的用户。

9.连续n周活跃用户
连续n周,每周至少启动一次。

10.忠诚用户
连续活跃5周以上的用户

11.连续活跃用户
连续2周及以上活跃的用户

12.近期流失用户
连续n(2<= n <= 4)周没有启动应用的用户。(第n+1周没有启动过)

13.留存用户
某段时间内的新增用户,经过一段时间后,仍然使用应用的被认作是留存用户;这部分用户占当时新增用户的比例即是留存率。
例如,5月份新增用户200,这200人在6月份启动过应用的有100人,7月份启动过应用的有80人,8月份启动过应用的有50人;则5月份新增用户一个月后的留存率是50%,二个月后的留存率是40%,三个月后的留存率是25%。

14.用户新鲜度
每天启动应用的新老用户比例,即新增用户数占活跃用户数的比例。

15.单次使用时长
每次启动使用的时间长度。

16.日使用时长
累计一天内的使用时间长度。

17.启动次数计算标准
IOS平台应用退到后台就算一次独立的启动;Android平台我们规定,两次启动之间的间隔小于30秒,被计算一次启动。用户在使用过程中,若因收发短信或接电话等退出应用30秒又再次返回应用中,那这两次行为应该是延续而非独立的,所以可以被算作一次使用行为,即一次启动。业内大多使用30秒这个标准,但用户还是可以自定义此时间间隔。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值