项目GitHub地址:https://github.com/niuxvdong/Guli_Project

零、在线教育项目简介

工程结构

模块说明

guli-parent:在线教学根目录(父工程),管理四个子模块:

  • canal-client:canal数据库表同步模块(统计同步数据)
  • common:公共模块父节点
    • common-util:工具类模块,所有模块都可以依赖于它
    • service-base:service服务的base包,包含service服务的公共配置类,所有service模块依赖于它
    • spring-security:认证与授权模块,需要认证授权的service服务依赖于它
  • infrastructure:基础服务模块父节点
    • api-gateway:api网关服务
  • service:api接口服务父节点
    • service-acl:用户权限管理api接口服务(用户管理、角色管理和权限管理等)
    • service-cms:cms api接口服务
    • service-edu:教学相关api接口服务
    • service-msm:短信api接口服务
    • service-order:订单相关api接口服务
    • service-oss:阿里云oss api接口服务
    • service-statistics:统计报表api接口服务
    • service-ucenter:会员api接口服务
    • service-vod:视频点播api接口服务

版本说明

SpringBoot基于2.2.1,SpringCloud基于H版本!

其他版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<properties>
<java.version>1.8</java.version>
<guli.version>0.0.1-SNAPSHOT</guli.version>
<mybatis-plus.version>3.0.5</mybatis-plus.version>
<velocity.version>2.0</velocity.version>
<swagger.version>2.7.0</swagger.version>
<aliyun.oss.version>2.8.3</aliyun.oss.version>
<jodatime.version>2.10.1</jodatime.version>
<poi.version>3.17</poi.version>
<commons-fileupload.version>1.3.1</commons-fileupload.version>
<commons-io.version>2.6</commons-io.version>
<httpclient.version>4.5.1</httpclient.version>
<jwt.version>0.7.0</jwt.version>
<aliyun-java-sdk-core.version>4.3.3</aliyun-java-sdk-core.version>
<aliyun-sdk-oss.version>3.1.0</aliyun-sdk-oss.version>
<aliyun-java-sdk-vod.version>2.15.2</aliyun-java-sdk-vod.version>
<aliyun-java-vod-upload.version>1.4.11</aliyun-java-vod-upload.version>
<aliyun-sdk-vod-upload.version>1.4.11</aliyun-sdk-vod-upload.version>
<fastjson.version>1.2.28</fastjson.version>
<gson.version>2.8.2</gson.version>
<json.version>20170516</json.version>
<commons-dbutils.version>1.7</commons-dbutils.version>
<canal.client.version>1.1.0</canal.client.version>
<docker.image.prefix>zx</docker.image.prefix>
<cloud-alibaba.version>0.2.2.RELEASE</cloud-alibaba.version>
</properties>

一、在线教育项目功能点

1、后台管理系统功能

登录功能

整合了 SpringSecurity 框架!

权限管理模块

  • 菜单管理:列表、添加、修改、删除
  • 角色管理:列表、添加、修改、删除、批量删除,为角色分配菜单
  • 用户管理:列表、添加、修改、删除、批量删除,为用户分配角色

权限管理用到的表

五张表,用户、角色、权限、以及相邻两表之间的关系表!

讲师管理模块

条件查询分页、列表、添加、修改、删除

课程分类模块

  • 添加课程分类读取Excel里面课程分类数据,添加到数据库中
  • 课程分类列表:使用树形结构显示课程分类列表

课程管理模块

  • 课程列表功能
  • 添加课程:课程发布流程:第一步填写课程基本信息,第二步添加课程大纲(章节和小节),第三步课程信息确认,最终课程发布
  • 添加小节上传课程视频:阿里oss和视频点播

课程如何判断是否已经被发布了?

使用status字段

课程添加过程中,中途把课程停止添加,重新去添加新的课程,如何找到之前没有发布完成课程,继续进行发布?

到课程列表中根据课程状态查询未发布的课程,点击课程右边超链接把课程继续发布完成!

统计分析模块

  • 生成统计数据:远程调用其他模块接口,实现数据统计进行展示

  • 统计数据图表显示:使用echarts显示

2、项目前台用户系统功能

首页数据显示

  • 显示幻灯片功能:根据id排序显示前几条
  • 显示热门课程:根据时间或播放量排序显示前几条
  • 显示名师:根据时间或播放量排序显示前几条

注册功能

  • 获取手机验证码:整合了阿里云短信服务

登录功能

  • 普通登录和退出:使用token实现

SSO(单点登录)三种方式:

token生成方式:

使用JWT生成token字符串!

JWT有三部分组成:

登录实现流程

登录调用登录接口返回token字符串,把返回token字符串放到cookie里面,创建前端拦截器进行判断,如果cookie里面包含token字符串,把token字符串放到header里面。调用接口根据token获取用户信息,把用户信息放到cookie里面,进行显示!

  • 微信扫码登录

OAuth2

  • 是针对特定问题解决方案
  • 主要有两个问题:开放系统间授权,分布式访问(单点登录)
  • 令牌机制,按照规则生成字符串,字符串包含用户信息,发送请求时携带字符串!
  • JWT是一种针对OAuth2的实现

如何获取扫描人信息过程?

扫描之后微信接口返回code临时票据),拿着code值请求微信固定地址,得到两个值:access_token(访问凭证)和openid(微信唯一标识),你拿着这两个值再去请求微信固定的地址,得到微信扫描人信息(比如昵称,头像等等)。

名师列表功能

条件查询带分页实现!

名师详情功能

显示名师的基本信息和所讲课程!

课程列表功能

  • 条件查询分页列表功能

课程详情页

  • 课程信息显示(包含课程基本信息,分类,讲师,课程大纲)
  • 判断课程是否需要购买

课程视频在线播放

整合阿里云视频点播和阿里云视频播放器做到!

课程支付功能(微信支付)

  1. 生成课程订单
  2. 生成微信支付二维码
  3. 微信最终支付

微信支付实现流程:

  • 如果课程是收费课程,点击立即购买,生成课程订单
  • 点击订单页面去支付,生成微信支付二维码
  • 使用微信扫描支付二维码实现支付
  • 支付之后,每隔3秒查询支付状态(是否支付成功),如果没有支付成功等待,如果支付成功之后,更新订单状态(已经支付状态),向支付记录表添加支付成功记录

二、在线教育项目技术点

1、前端技术点

在线教育项目采用前后端分离开发

vue

  • 基本语法
  • 常见指令 : v-bind v-model v-if v-for v-html
  • 绑定事件: v-on-click @click
  • 生命周期:created() 页面渲染之前 mounted()页面渲染之后

ES6规范

箭头函数等等!

Element-ui

nodejs

是JavaScript运行环境,不需要浏览器直接运行js代码,模拟服务器效果!

NPM

包管理工具,类似于Maven

npm命令: npm init npm install 依赖名称

Babel

转码器,可以把ES6代码转换成ES5代码

前端模块化

  • 通过一个页面或者一个js文件,调用另外一个js文件里面的方法
  • 问题:ES6的模块化无法在Node.js中执行,需要用Babel编辑成ES5后再执行

vue-admin-template

  • 后台系统使用 vue-admin-template
  • 基于vue+Element-ui

Nuxt

  • 前台系统使用 Nuxt
  • 基于vue
  • 服务器渲染技术 利于SEO

Echarts

图表工具

2、后端技术点

项目采用微服务架构,每个模块都可以独立部署,独立运行!

SpringBoot

  • SpringBoot本质是就是Spring,只是快速构建Spring工程脚手架

  • SpringBoot配置文件

    • 配置文件类型:properties和yml
    • 配置文件加载顺序:bootstrap application application-dev

SpringCloud

  • 是很多框架总称,使用这些框架实现微服务架构,基于SpringBoot实现
  • 组成框架有哪些?

  • 项目中,使用阿里巴巴Nacos,替代SpringCloud一些组件

  • Nacos

    • 使用Nacos作为注册中心
    • 使用Nacos作为配置中心
  • Feign

服务调用,一个微服务调用另外一个微服务,实现远程调用

  • Hystrix熔断器

容错处理!

  • Gateway网关

SpringCloud之前zuul网关,目前Gateway网关!

可以统一解决跨域问题!

MyBatisPlus

  1. MyBatisPlus就是对MyBatis做增强
  2. 自动填充,自动填充时间 @TableField
  3. 乐观锁,version字段 @Version
  4. 逻辑删除 @TableLogic
  5. 代码生成器
  6. 分布式id生成

分布式id生成策略:https://www.cnblogs.com/haoxinyue/p/5208136.html

EasyExcel

  • 阿里巴巴提供操作excel工具,代码简洁,效率很高
  • EasyExcel对poi进行封装,采用SAX方式解析(一行行解析)
  • 项目应用在添加课程分类,读取excel数据

SpringSecurity

  1. 在项目整合框架实现权限管理功能
  2. SpringSecurity框架组成:认证和授权
  3. SpringSecurity登录认证过程

  • SpringSecurity代码执行过程

Redis

  • 首页数据通过Redis进行缓存
  • Redis数据类型,string、set、list、hash、zset
  • 使用Redis作为缓存,不太重要或者不经常改变数据适合放到Redis作为缓存

Nginx

  • 反向代理服务器
  • 请求转发,负载均衡,动静分离

OAuth2+JWT

  • OAuth2针对特定问题解决方案
  • JWT包含三部分(见功能点详解)

HttpClient

  • 发送请求返回响应的工具,不需要浏览器完成请求和响应的过程
  • 应用场景:微信登录获取扫描人信息,微信支付查询支付状态

Cookie特点:

  • 客户端技术
  • 每次发送请求带着cookie值进行发送
  • cookie有默认会话级别,关闭浏览器cookie默认不存在了,
  • 但是可以设置cookie有效时长 setMaxAge

微信登录

见功能点详解

微信支付

见功能点详解

阿里云OSS

  • 文件存储服务器
  • 添加讲师时候上传讲师头像

阿里云视频点播

  • 视频上传、删除、播放
  • 整合阿里云视频播放器进行视频播放
  • 使用视频播放凭证

阿里云短信服务

  • 注册时候,发送手机验证码

Git

  • 代码提交到远程Git仓库

Docker+Jenkins

  • 手动打包运行
  • idea打包
  • jenkins自动化部署过程

三、在线教育项目问题总结

1、前端问题-路由切换问题

  • 多次路由跳转到同一个vue页面,页面中created方法只会执行一次
  • 解决方案:使用vue监听watch

2、前端问题-ES6模块化运行问题

Nodejs不能直接运行ES6模块化代码,需要使用Babel把ES6模块化代码转换ES5代码执行

3、mp生成19位id值

mp生成id值是19位,JavaScript处理数字类型值时候,只会处理到16位

@TableId(value = "id", type = IdType.ID_WORKER_STR)

4、跨域问题

  • 访问协议,ip地址,端口号,这三个如果有任何一个不一样,产生跨域
  • 跨域解决:
    • 在Controller添加注解 @CrossOrigin
    • 通过网关解决

网关统一解决跨域:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
public class CorsConfig {
// 网关跨域和controller 只能有一个,因此将此处注掉!
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);

return new CorsWebFilter(source);
}
}

5、413问题

  • 上传视频时候,因为Nginx有上传文件大小限制,如果超过Nginx大小,出现413
  • 413错误:请求体过大
  • 在Nginx配置客户端大小,http块中配置 client_max_body_size 1024m;
  • 响应状态码:413 403(跨域)302(请求重定向)

6、Maven加载问题

  • maven加载项目时候,默认不会加载src-java文件夹里面xml类型文件的
  • 解决方案:
    • 直接复制xml文件到target目录
    • 通过配置实现

application.properties文件

1
2
#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/itnxd/eduservice/mapper/xml/*.xml

pom.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>

四、项目面试总结

1、项目描述

在线教育系统,分为前台网站系统和后台运营平台,B2C模式。

前台用户系统包括课程、讲师、问答、文章几大大部分,使用了微服务技术架构,前后端分离开发

后端的主要技术架构是:SpringBoot + SpringCloud + MyBatis-Plus + HttpClient + MySQL +Maven+EasyExcel+ nginx

前端的架构是:Node.js + Vue.js +element-ui+NUXT+ECharts

其他涉及到的中间件包括Redis、阿里云OSS、阿里云视频点播业务中使用了ECharts做图表展示,使用EasyExcel完成分类批量添加、注册分布式单点登录使用了JWT.

项目前后端分离开发,后端采用SpringCloud微服务架构,持久层用的是MyBatis-Plus,微服务分库设计,使用Swagger生成接口文档,接入了阿里云视频点播、阿里云OSS。系统分为前台用户系统和后台管理系统两部分。

前台用户系统包括:首页、课程、名师、问答、文章。

后台管理系统包括:讲师管理、课程分类管理、课程管理、统计分析、Banner管理、订单管理、权限管理等功能。

2、这是一个项目还是一个产品

这是一个产品

  • 1.0版本是单体应用:SSM面试总结
  • 2.0版本加入了SpringCloud,将一些关键业务和访问量比较大的部分分离了出去
  • 目前独立出来的服务有教学服务、视频点播服务、用户服务、统计分析服务、网关服务

3、系统中都有那些角色?数据库是怎么设计的?

  • 前台:会员(学员)
  • 后台:系统管理员、运营人员
  • 后台分库,每个微服务一个独立的数据库,使用了分布式id生成器

分布式id生成策略:https://www.cnblogs.com/haoxinyue/p/5208136.html

4、视频点播是怎么实现的(流媒体你们是怎么实现的)

  • 我们直接接入了阿里云的云视频点播。云平台上的功能包括视频上传、转码、加密、智能审核、监控统计等。
  • 还包括视频播放功能,阿里云还提供了一个视频播放器。

5、前后端联调经常遇到的问题

  • 请求方式post、get
  • json、x-wwww-form-urlencoded混乱的错误
  • 后台必要的参数,前端省略了
  • 数据类型不匹配
  • 空指针异常
  • 分布式系统中分布式id生成器生成的id 长度过大(19个字符长度的整数),js无法解析(js智能解析16个长度:2的53次幂)
  • id策略改成 ID_WORKER_STR

6、前后端分离项目中的跨域问题是如何解决的

  • 后端服务器配置:我们的项目中是通过Spring注解解决跨域的 @CrossOrigin
  • 也可以使用nginx反向代理、httpClient、网关

7、说说你做了哪个部分、遇到了什么问题、怎么解决的

分布式id生成器在前端无法处理,总是在后三位进行四舍五入。

分布式id生成器生成的id是19个字符的长度,前端javascript脚本对整数的处理能力只有2的53次方,也就是最多只能处理16个字符

解决的方案是把id在程序中设置成了字符串的性质

了解:

项目迁移到Spring-Cloud的时候,经过网关时,前端传递的cookie后端一只获取不了,看了cloud中zuul的源码,发现向下游传递数据的时候,zull默认过滤了敏感信息,将cookie过滤掉了。解决的方案是在配置文件中将请求头的过滤清除掉,使cookie可以向下游传递。

8、CAP理论

之前的笔记!

9、前端渲染和后端渲染有什么区别

  • 前端渲染是返回json给前端,通过javascript将数据绑定到页面上
  • 后端渲染是在服务器端将页面生成直接发送给服务器,有利于SEO的优化

10、能画一下系统架构图吗

五、常用的工具类

JwtUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.itnxd.commonutils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
* @author helen
* @since 2019/10/16
*/
public class JwtUtils {

public static final long EXPIRE = 1000 * 60 * 60 * 24;
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";

public static String getJwtToken(String id, String nickname){

String JwtToken = Jwts.builder()
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
.setSubject("guli-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.claim("id", id)
.claim("nickname", nickname)
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();

return JwtToken;
}

/**
* 判断token是否存在与有效
* @param jwtToken
* @return
*/
public static boolean checkToken(String jwtToken) {
if(StringUtils.isEmpty(jwtToken)) return false;
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}

/**
* 判断token是否存在与有效
* @param request
* @return
*/
public static boolean checkToken(HttpServletRequest request) {
try {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return false;
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}

/**
* 根据token获取会员id
* @param request
* @return
*/
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
return (String)claims.get("id");
}
}

MD5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.itnxd.commonutils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;


public final class MD5 {

public static String encrypt(String strSrc) {
try {
char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
byte[] bytes = strSrc.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
bytes = md.digest();
int j = bytes.length;
char[] chars = new char[j * 2];
int k = 0;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
chars[k++] = hexChars[b >>> 4 & 0xf];
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错!!+" + e);
}
}

public static void main(String[] args) {
System.out.println(MD5.encrypt("111111"));
}

}

统一结果集R

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.itnxd.commonutils;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.HashMap;
import java.util.Map;

/**
* 统一返回结果类!
*
* @author ITNXD
* @create 2021-11-02 21:11
*/
@Data
public class R {

@ApiModelProperty(value = "是否成功")
private Boolean success;
@ApiModelProperty(value = "返回码")
private Integer code;
@ApiModelProperty(value = "返回消息")
private String message;
@ApiModelProperty(value = "返回数据")
private Map<String, Object> data = new HashMap<String, Object>();

// 私有构造:不能new
private R() {
}

public static R ok() {
R r = new R();
r.setSuccess(true);
r.setCode(ResultCode.SUCCESS);
r.setMessage("成功");
return r;
}

public static R error() {
R r = new R();
r.setSuccess(false);
r.setCode(ResultCode.ERROR);
r.setMessage("失败");
return r;
}

public R success(Boolean success) {
this.setSuccess(success);
return this;
}

public R message(String message) {
this.setMessage(message);
return this;
}

public R code(Integer code) {
this.setCode(code);
return this;
}

public R data(String key, Object value) {
this.data.put(key, value);
return this;
}

public R data(Map<String, Object> map) {
this.setData(map);
return this;
}
}

RandomUtil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.itnxd.commonutils;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

/**
* 获取随机数
*
* @author qianyi
*
*/
public class RandomUtil {

private static final Random random = new Random();

private static final DecimalFormat fourdf = new DecimalFormat("0000");

private static final DecimalFormat sixdf = new DecimalFormat("000000");

public static String getFourBitRandom() {
return fourdf.format(random.nextInt(10000));
}

public static String getSixBitRandom() {
return sixdf.format(random.nextInt(1000000));
}

/**
* 给定数组,抽取n个数据
* @param list
* @param n
* @return
*/
public static ArrayList getRandom(List list, int n) {

Random random = new Random();

HashMap<Object, Object> hashMap = new HashMap<Object, Object>();

// 生成随机数字并存入HashMap
for (int i = 0; i < list.size(); i++) {

int number = random.nextInt(100) + 1;

hashMap.put(number, i);
}

// 从HashMap导入数组
Object[] robjs = hashMap.values().toArray();

ArrayList r = new ArrayList();

// 遍历数组并打印数据
for (int i = 0; i < n; i++) {
r.add(list.get((int) robjs[i]));
System.out.print(list.get((int) robjs[i]) + "\t");
}
System.out.print("\n");
return r;
}
}

SwaggerConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.itnxd.servicebase;

import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
* @author ITNXD
* @create 2021-11-02 20:34
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {

@Bean
public Docket webApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
//.paths(Predicates.not(PathSelectors.regex("/admin/.*")))
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build();
}

private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("网站-课程中心API文档")
.description("本文档描述了课程中心微服务接口定义")
.version("1.0")
.contact(new Contact("ITNXD", "https://www.itnxd.cn/",
"158903258@qq.com"))
.build();
}
}

HttpClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package com.itnxd.eduorder.utils;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
* http请求客户端
*
* @author qy
*
*/
public class HttpClient {
private String url;
private Map<String, String> param;
private int statusCode;
private String content;
private String xmlParam;
private boolean isHttps;

public boolean isHttps() {
return isHttps;
}

public void setHttps(boolean isHttps) {
this.isHttps = isHttps;
}

public String getXmlParam() {
return xmlParam;
}

public void setXmlParam(String xmlParam) {
this.xmlParam = xmlParam;
}

public HttpClient(String url, Map<String, String> param) {
this.url = url;
this.param = param;
}

public HttpClient(String url) {
this.url = url;
}

public void setParameter(Map<String, String> map) {
param = map;
}

public void addParameter(String key, String value) {
if (param == null)
param = new HashMap<String, String>();
param.put(key, value);
}

public void post() throws ClientProtocolException, IOException {
HttpPost http = new HttpPost(url);
setEntity(http);
execute(http);
}

public void put() throws ClientProtocolException, IOException {
HttpPut http = new HttpPut(url);
setEntity(http);
execute(http);
}

public void get() throws ClientProtocolException, IOException {
if (param != null) {
StringBuilder url = new StringBuilder(this.url);
boolean isFirst = true;
for (String key : param.keySet()) {
if (isFirst)
url.append("?");
else
url.append("&");
url.append(key).append("=").append(param.get(key));
}
this.url = url.toString();
}
HttpGet http = new HttpGet(url);
execute(http);
}

/**
* set http post,put param
*/
private void setEntity(HttpEntityEnclosingRequestBase http) {
if (param != null) {
List<NameValuePair> nvps = new LinkedList<NameValuePair>();
for (String key : param.keySet())
nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数
http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数
}
if (xmlParam != null) {
http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
}
}

private void execute(HttpUriRequest http) throws ClientProtocolException,
IOException {
CloseableHttpClient httpClient = null;
try {
if (isHttps) {
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(null, new TrustStrategy() {
// 信任所有
public boolean isTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
return true;
}
}).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext);
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
.build();
} else {
httpClient = HttpClients.createDefault();
}
CloseableHttpResponse response = httpClient.execute(http);
try {
if (response != null) {
if (response.getStatusLine() != null)
statusCode = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
// 响应内容
content = EntityUtils.toString(entity, Consts.UTF_8);
}
} finally {
response.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
httpClient.close();
}
}

public int getStatusCode() {
return statusCode;
}

public String getContent() throws ParseException, IOException {
return content;
}

}