热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数仓项目之滴滴出行

项目背景本次的案例将某出行打车的日志数据来进行数据分析,例如:我们需要统计某一天订单量是多少、预约订单与非预约订单的占比是多少、不同时段订单占比等。通过对这些数据进行分析,了解到

项目背景

本次的案例将某出行打车的日志数据来进行数据分析,例如:我们需要统计某一天订单量是多少、预约订单与非预约订单的占比是多少、不同时段订单占比等。通过对这些数据进行分析,了解到不同区域、不同时段运营情况。


架构方案

1、基于Hadoop的HDFS文件系统存储数据。

2、基于Hive构建数据仓库。

3、使用Zeppelin进行SQL指令交互

4、使用Sqoop导出分析后的数据到传统型数据库,便于后期应用

5、使用Superset来实现数据可视化展示


数据来源

数据主要来源于四个日志文件:

1、用户打车订单日志





2、用户取消订单日志





3、用户支付日志





4、用户评价日志



环境准备:

启动hadoop

启动hive

nohup /opt/module/apache-hive-3.1.2-bin/bin/hive --service metastore &

nohup /opt/module/apache-hive-3.1.2-bin/bin/hive --service hiveserver2 &

打开zeppelin

/opt/module/zeppelin-0.10.0-bin-all/bin/zeppelin-daemon.sh start

在浏览器输入hadoop102:8080(我的主机是hadoop102)


一、创建数据库

根据数仓分层体系架构,我们把数据仓库分为以下三层:

ODS层 ----源数据层

DW层 ----数据仓库层

APP层 ----应用层



一般,ODS层存放我们的源数据,即从业务系统直接拿过来的未经处理的,DW层存放我们处理后的数据,APP层存放我们计算后的统计指标。

所以我们创建三个数据库,来管理每一层的表数据

1、创建ods库

create database if not exists ods_didi;

2、创建dw库

create database if not exists dw_didi;

3、创建app库

create database if not exists app_didi;

4、查看结果

show databases;


二、创建表(ODS层)

1、创建打车订单表

create table if not exists ods_didi.t_user_order(

orderId string comment '订单id',

telephone string comment '打车用户手机',

lng string comment '用户发起打车的经度',

lat string comment '用户发起打车的纬度',

province string comment '所在省份',

city string comment '所在城市',

es_money double comment '预估打车费用',

gender string comment '用户信息 - 性别',

profession string comment '用户信息 - 行业',

age_range string comment '年龄段(70后、80后、...)',

tip double comment '小费',

subscribe int comment '是否预约(0 - 非预约、1 - 预约)',

sub_time string comment '预约时间',

is_agent int comment '是否代叫(0 - 本人、1 - 代叫)',

agent_telephone string comment '预约人手机',

order_time string comment '预约时间'

)

partitioned by (dt string comment '时间分区')

ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ;

2、创建取消订单表

create table if not exists ods_didi.t_user_cancel_order(

orderId string comment '订单ID',

cstm_telephone string comment '客户联系电话',

lng string comment '取消订单的经度',

lat string comment '取消订单的纬度',

province string comment '所在省份',

city string comment '所在城市',

es_distance double comment '预估距离',

gender string comment '性别',

profession string comment '行业',

age_range string comment '年龄段',

reason int comment '取消订单原因(1 - 选择了其他交通方式、2 - 与司机达成一致,取消订单、3 - 投诉司机没来接我、4 - 已不需要用车、5 - 无理由取消订单)',

cancel_time string comment '取消时间'

)

partitioned by (dt string comment '时间分区')

ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ;

3、创建订单支付表

create table if not exists ods_didi.t_user_pay_order(

id string comment '支付订单ID',

orderId string comment '订单ID',

lng string comment '目的地的经度(支付地址)',

lat string comment '目的地的纬度(支付地址)',

province string comment '省份',

city string comment '城市',

total_money double comment '车费总价',

real_pay_money double comment '实际支付总额',

passenger_additional_money double comment '乘客额外加价',

base_money double comment '车费合计',

has_coupon int comment '是否使用优惠券(0 - 不使用、1 - 使用)',

coupon_total double comment '优惠券合计',

pay_way int comment '支付方式(0 - 微信支付、1 - 支付宝支付、3 - QQ钱包支付、4 - 一网通银行卡支付)',

mileage double comment '里程(单位公里)',

pay_time string comment '支付时间'

)

partitioned by (dt string comment '时间分区')

ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ;

4、创建用户评价表

create table if not exists ods_didi.t_user_evaluate(

id string comment '评价日志唯一ID',

orderId string comment '订单ID',

passenger_telephone string comment '用户电话',

passenger_province string comment '用户所在省份',

passenger_city string comment '用户所在城市',

eva_level int comment '评价等级(1 - 一颗星、... 5 - 五星)',

eva_time string comment '评价时间'

)

partitioned by (dt string comment '时间分区')

ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ;

5、查看结果

use ods_didi;

show tables;


三、加载数据

1、加载打车订单日志

load data local inpath '/home/atguigu/data/order.csv' into table ods_didi.t_user_order partition (dt='2020-04-12');

2、加载取消订单日志

load data local inpath '/home/atguigu/data/cancel_order.csv' into table ods_didi.t_user_cancel_order partition (dt='2020-04-12');

3、加载支付日志

load data local inpath '/home/atguigu/data/pay.csv' into table ods_didi.t_user_pay_order partition (dt='2020-04-12');

4、加载评价日志

load data local inpath '/home/atguigu/data/evaluate.csv' into table ods_didi.t_user_evaluate partition (dt='2020-04-12');

5、查看结果

use ods_didi;

select * from t_user_order limit 5;

select * from t_user_cancel_order limit 5;

select * from t_user_pay_order limit 5;

select * from t_user_evaluate limit 5;


四、创建宽表(DW层)

创建宽表用于保存预处理后的数据。宽表的字段比原表多,所以叫宽表。



-- 创建宽表

create table if not exists dw_didi.t_user_order_wide(

orderId string comment '订单id',

telephone string comment '打车用户手机',

lng string comment '用户发起打车的经度',

lat string comment '用户发起打车的纬度',

province string comment '所在省份',

city string comment '所在城市',

es_money double comment '预估打车费用',

gender string comment '用户信息 - 性别',

profession string comment '用户信息 - 行业',

age_range string comment '年龄段(70后、80后、...)',

tip double comment '小费',

subscribe int comment '是否预约(0 - 非预约、1 - 预约)',

subscribe_name string comment '是否预约名称',

sub_time string comment '预约时间',

is_agent int comment '是否代叫(0 - 本人、1 - 代叫)',

is_agent_name string comment '是否代叫名称',

agent_telephone string comment '预约人手机',

order_date string comment '预约时间,

yyyy-MM-dd',order_year string comment '年',

order_month string comment '月',

order_day string comment '日',

order_hour string comment '小时',

order_time_range string comment '时间段',

order_time string comment '预约时间'

)

partitioned by (dt string comment '时间分区')

ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ;


五、数据预处理

预处理需求:

1、过滤掉order_time长度小于8的数据,如果小于8,表示这条数据不合法,不应该参加统计。

2、将一些0、1表示的字段,处理为更容易理解的字段。例如:subscribe字段,0表示非预约、1表示预约。我们需要添加一个额外的字段,用来展示非预约和预约,这样将来我们分析的时候,跟容易看懂数据。

3、order_time字段为2020-4-12 1:15,为了将来更方便处理,我们统一使用类似 2020-04-12 01:15来表示,这样所有的order_time字段长度是一样的。并且将日期获取出来

4、为了方便将来按照年、月、日、小时统计,我们需要新增这几个字段。

5、后续要分析一天内,不同时段的订单量,我们需要在预处理过程中将订单对应的时间段提前计算出来。例如:1:00-5:00为凌晨。



这里有个小bug要注意一下:

SELECT DATE_FORMAT('2022-2-22 9:32','yyyy-MM-dd HH:mm:ss');会出错



SELECT DATE_FORMAT('2022-2-22 9:32:8','yyyy-MM-dd HH:mm:ss');正常



SELECT DATE_FORMAT('2022-2-22 9:32','yyyy-MM-dd HH:mm');也会出错



看来是要把秒补全了,我们可以这么做:

select date_format(concat('2022-2-22 9:32',':00'), 'yyyy-MM-dd HH:mm:ss');

image.png

-- 数据预处理
insert overwrite table dw_didi.t_user_order_wide partition(dt='2020-04-12')
select
orderId,
telephone,
lng,
lat,
province,
city,
es_money,
gender,
profession,
age_range,
tip,
subscribe,
case when subscribe = 0 then '非预约'
when subscribe = 1 then'预约'
end as subscribe_name, --展示预约和非预约
sub_time,
is_agent,
case when is_agent = 0 then '本人'
when is_agent = 1 then '代叫'
end as is_agent_name, --展示本人和代叫
agent_telephone,
date_format(order_time, 'yyyy-MM-dd') as order_date, --修改订单日期格式
year(date_format(order_time, 'yyyy-MM-dd')) as order_year, --提取年
month(date_format(order_time, 'yyyy-MM-dd')) as order_month, --提取月
day(date_format(order_time, 'yyyy-MM-dd')) as order_day, --提取日
hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) as order_hour, --提取小时
case when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 1 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 5 then '凌晨'
when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 5 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 8 then '早上'
when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 8 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 11 then '上午'
when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 11 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 13 then '中午'
when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 13 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 17 then '下午'
when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 17 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 19 then '晚上'
when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 19 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 20 then '半夜'
when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 20 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 24 then '深夜'
when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) >= 0 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 1 then '凌晨'
else 'N/A'
end as order_time_range,
date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss') as order_time --修改订单时间格式
from ods_didi.t_user_order
where dt = '2020-04-12' and length(order_time) >= 8; --过滤order_time长度小于8的数据

-- 查看结果

use dw_didi;

select * from t_user_order_wide limit 5;


六、统计指标

1、订单总笔数

-- 建表(APP层)

create table if not exists app_didi.t_order_total(

date_val string comment '日期(年月日)',

count int comment '订单笔数'

)

partitioned by (month string comment '年月,yyyy-MM')

row format delimited fields terminated by ',';

-- 查询插入数据

insert overwrite table app_didi.t_order_total partition(mOnth='2020-04')

select

'2020-04-12',

count(orderid) as total_cnt

from dw_didi.t_user_order_wide

where dt = '2020-04-12';

-- 查看结果

select * from app_didi.t_order_total;

2、预约和非预约订单占比

-- 建表(APP层)

create table app_didi.t_order_subscribe_total (

date_val string ,

subscribe_name string,

count int

)

partitioned by (month string comment '年月yyyy-MM')

row format delimited fields terminated by ',';

-- 查询插入数据

insert overwrite table app_didi.t_order_subscribe_total partition(mOnth='2020-04')

select

'2020-04-12',

subscribe_name,

count(*)

from dw_didi.t_user_order_wide

where dt = '2020-04-12'

group by subscribe_name;

-- 查看结果

select * from app_didi.t_order_subscribe_total;

3、不同时段订单占比

-- 建表(APP层)

create table if not exists app_didi.t_order_timerange_total(

date_val string comment '日期',

timerange string comment '时间段',

count int comment '订单数量'

)

partitioned by (month string comment '年月,yyyy-MM')

row format delimited fields terminated by ',';

-- 查询插入数据

insert overwrite table app_didi.t_order_timerange_total partition(mOnth= '2020-04')

select

'2020-04-12',

order_time_range,

count(*) as order_cnt

from dw_didi.t_user_order_wide

where dt = '2020-04-12'

group by order_time_range;

-- 查看结果

select * from app_didi.t_order_timerange_total;

4、不同地域订单占比

-- 建表(APP层)

create table if not exists app_didi.t_order_province_total(

order_date string comment '日期',

province string comment '省份',

order_cnt int comment '订单数量'

)

partitioned by (month string comment '年月,yyyy-MM')

row format delimited fields terminated by ',';

-- 查询插入数据

insert overwrite table app_didi.t_order_province_total partition(mOnth= '2020-04')

select

'2020-04-12',

province,

count(*) as order_cnt

from dw_didi.t_user_order_wide

where dt = '2020-04-12'

group by province;

-- 查看结果

select * from app_didi.t_order_province_total;

5、不同年龄段/时段订单占比

-- 建表(APP层)

create table if not exists app_didi.t_order_age_and_time_range_total(

date_val string comment '日期',

age_range string comment '年龄段',

order_time_range string comment '时段',

count int comment '订单数量'

)

partitioned by (month string comment '年月,yyyy-MM')

row format delimited fields terminated by ',';

-- 查询插入数据

insert overwrite table app_didi.t_order_age_and_time_range_total partition(mOnth= '2020-04')

select

'2020-04-12',

age_range,

order_time_range,

count(*) as order_cnt

from dw_didi.t_user_order_wide

where dt = '2020-04-12'

group by age_range,order_time_range;

-- 查看结果

select * from app_didi.t_order_age_and_time_range_total;


七、sqoop导出数据

使用Sqoop导出APP层表到传统型数据库,便于后期应用

一)先在mysql中创建对应的目标数据库和表

mysql -u root -p

-- 创建目标数据库

create database if not exists app_didi;

-- 1.创建订单总笔数表

create table if not exists app_didi.t_order_total(

order_date date,

count int

);

-- 2.创建预约和非预约订单占比表

create table if not exists app_didi.t_order_subscribe_total(

order_date date ,

subscribe_name varchar(20) ,

count int

);

-- 3.创建不同时段订单占比表

create table if not exists app_didi.t_order_timerange_total(

order_date date ,

timerange varchar(20) ,

count int

);

-- 4.创建不同地域订单占比表

create table if not exists app_didi.t_order_province_total(

order_date date ,

province varchar(20) ,

count int

);

-- 5.创建不同年龄段/时段订单占比表

create table if not exists app_didi.t_order_age_and_time_range_total(

order_date date ,

age_range varchar(20) ,

order_time_range varchar(20) ,

count int

);

-- 查看结果

use app_didi;

show tables;

二)导出数据

cd /opt/module/sqoop-1.4.7.bin__hadoop-2.6.0

1、导出订单总笔数表

bin/sqoop export \
--connect jdbc:mysql://hadoop102:3306/app_didi"?useUnicode=true&characterEncoding=utf-8" \
--username root \
--password 456789 \
--table t_order_total \
--export-dir /user/hive/warehouse/app_didi.db/t_order_total/mOnth=2020-04

2、导出预约和非预约订单占比表

bin/sqoop export \
--connect jdbc:mysql://hadoop102:3306/app_didi"?useUnicode=true&characterEncoding=utf-8" \
--username root \
--password 456789 \
--table t_order_subscribe_total \
--export-dir /user/hive/warehouse/app_didi.db/t_order_subscribe_total/mOnth=2020-04

3、导出不同时段订单占比表

bin/sqoop export \
--connect jdbc:mysql://hadoop102:3306/app_didi"?useUnicode=true&characterEncoding=utf-8" \
--username root \
--password 456789 \
--table t_order_timerange_total \
--export-dir /user/hive/warehouse/app_didi.db/t_order_timerange_total/mOnth=2020-04

4、导出不同地域订单占比表

bin/sqoop export \
--connect jdbc:mysql://hadoop102:3306/app_didi"?useUnicode=true&characterEncoding=utf-8" \
--username root \
--password 456789 \
--table t_order_province_total \
--export-dir /user/hive/warehouse/app_didi.db/t_order_province_total/mOnth=2020-04

5、导出不同年龄段/时段订单占比表

bin/sqoop export \
--connect jdbc:mysql://hadoop102:3306/app_didi"?useUnicode=true&characterEncoding=utf-8" \
--username root \
--password 456789 \
--table t_order_age_and_time_range_total \
--export-dir /user/hive/warehouse/app_didi.db/t_order_age_and_time_range_total/mOnth=2020-04

三)到mysql中查看结果

mysql -u root -p

use app_didi;

select * from t_order_total;

select * from t_order_subscribe_total;

select * from t_order_timerange_total;

select * from t_order_province_total;

select * from t_order_age_and_time_range_total;


八、superset数据可视化

1、启动superset

source superset/bin/activate #激活环境

nohup superset run -h hadoop102 -p 8088 & #启动superset

浏览器输入hadoop102:8088 #打开浏览器

2、连接数据库

点击data,点击databases



点击添加database



点击mysql



填写mysql所在的主机、端口,要连接的数据库名,数据库用户名和登录密码,连接名,添加参数charset=UTF8(否则会出现中文乱码)



点击finish



这样我们的页面中就多了这一条连接

3、添加表数据

点击data,点击datasets



点击添加dataset



选择我们刚才创建的连接,选择库,选择表,点击add



这样我们的页面中就多了这一条记录



依次添加完其余4张表

4、创建图表

1)、订单总笔数

点击t_order_total



选择图表类型



选择big number,点击选择



时间字段,no filter不过滤



指标,选择字段count,选择函数sum



点击run query



这样就好了



修改标题



点击save保存



点击保存



这样就保存在charts里了

2)、预约和非预约订单占比

点击t_order_subscribe_total



选择饼图



按subscribe_name分组,选择字段count,选择函数sum



如图



设置格式

3)、不同时段订单占比

点击t_order_timerange_total



选择柱状图



指标



系列,即x轴



设置格式

4)、不同地域订单占比

点击t_order_province_total



选择柱状图



序列即x轴,按省进行划分



设置格式

5)、不同年龄段/时段订单占比

点击t_order_age_and_time_range_total



选择多环图



层级,里边那层选年龄,外边那层选时段



如图

5、创建看板

点击dashboards,点击添加



如图



点击charts



接下来我们就可以把制作好的图表拖到仪表盘中

最终效果如图



推荐阅读
  • 架构升级给DolphScheduler带来2~3倍性能提升
     引言大数据任务调度作为大数据建设中的核心基础设施,在经过社区用户们长期的使用中,不少用户对调度也提出了很多新的要求,为此,ApacheDolphinScheduler(Incub ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 不会搭建大数据平台,我被老板优化了...
    不会,搭建,大数,据,平台,我 ... [详细]
  • 数据仓库中基本概念
    一、数据仓库数据仓库(DataWarehouse)是一个面向主题的、集成的、稳定的且随时间变化的数据集合,用于支持管理人员的决策面向主题主题就是类型的意思。传统数 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • 企业数据应用挑战及元数据管理的重要性
    本文主要介绍了企业在日常经营管理过程中面临的数据应用挑战,包括数据找不到、数据读不懂、数据不可信等问题。针对这些挑战,通过元数据管理可以实现数据的可见、可懂、可用,帮助业务快速获取所需数据。文章提出了“灵魂”三问——元数据是什么、有什么用、又该怎么管,强调了元数据管理在企业数据治理中的基础和前提作用。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • PHP组合工具以及开发所需的工具
    本文介绍了PHP开发中常用的组合工具和开发所需的工具。对于数据分析软件,包括Excel、hihidata、SPSS、SAS、MARLAB、Eview以及各种BI与报表工具等。同时还介绍了PHP开发所需的PHP MySQL Apache集成环境,包括推荐的AppServ等版本。 ... [详细]
  • 开发笔记:Python之路第一篇:初识Python
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Python之路第一篇:初识Python相关的知识,希望对你有一定的参考价值。Python简介& ... [详细]
  • 毕设做到后半部分,需要实现将用户在一段时间(比如1天)内产生的新数据导入HDFS的功能,这样数据仓库中的数据才能和数据库中的数据同步在新建了一个PyDev项目后,需要如下操作(拣最 ... [详细]
  • 【数据结构与算法】——快速排序
    Sqoop是一款开源的工具,主要用于在Hadoop(Hive)与传统的数据库(mysql、postgresql)间进行数据的传递,可以将一个关系型数据库(例如:MySQL,O ... [详细]
  • 前言本文隶属于专栏《1000个问题搞定大数据技术体系》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出, ... [详细]
  • 怎么快速学好大数据开发?
    新如何学习大数据技术?大数据怎么入门?怎么做大数据分析?数据科学需要学习那些技术?大数据的应用前景等等问题,已成为热门大数据领域热门问题,以下是对新手如何学习大数据技术问题的解答! ... [详细]
author-avatar
-cy-小衣橱
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有