前面我们只讲了将静态页面上传到Fastdfs 今天我们将页面作为消息发送Message到mq中 然后前台取到页面
步骤
首先我们可以在公共的模块中建一个子模块hrm-basic-rabbitmq
导入spirngboot集成rabbitmq的依赖
<!--spirngboot集成rabbitmq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
创建一个存放rabbitmq常量的工具类
//存放rabbitmq的常量,就是消费者和生产者公共东西
public class RabbitMqConstants {
public static final String EXCHANGE_TOPICS_PAGE = "exchange_topics_page";
public static final String FILE_SYS_TYPE = "fileSysType";
public static final String PAGE_URL = "pageUrl";
public static final String PHYSICAL_PATH = "physicalPath";
}
然后在page模块中引入rabbitmq的模块引入mq的jar包
<dependency>
<groupId>org.leryoo</groupId>
<artifactId>hrm-basic-rabbitmq</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
Service层
String routingKey = siteMapper.selectById(pager.getSiteId()).getSn();
//消息的内容
Map<String,Object> params = new HashMap<>();
params.put(RabbitMqConstants.FILE_SYS_TYPE,pageConfig.getDfsType()); //文件系统,从哪儿系统系统
params.put(RabbitMqConstants.PAGE_URL,pageConfig.getPageUrl());//从那个路径下载
params.put(RabbitMqConstants.PHYSICAL_PATH,pageConfig.getPhysicalPath());//下载完了放到哪儿
rabbitTemplate.convertAndSend(
RabbitMqConstants.EXCHANGE_TOPICS_PAGE,routingKey
,JSONObject.toJSONString(params));
创建一个hrm-page-agent-parent模块
1 这个模块用来从文件系统下载静态化页面的
2 上线时要作为一个jar和nginx部署在一个服务器上面
3 他没有common模块(没有domain),没有client(也不给别人调用),只有servie模块
4 servie模块开发步骤
1) jar springboot,rabbitmq
2) 配置-rabbitmq(configserver需要)
3 ) 入口类(Eureka)
====
4)zuul访问映射
5)swagger
4,5不需要,不暴露服务
6)声明交换机,声明队列,把队列绑定到交换机
7)消费者的handler绑定到队列
8)实现handler的功能给你
子模块hrm-page-agent-service-2040
导入相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Eureka 客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--配置中心支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- 通过公共rabbitmq的模块引入mq的jar包-->
<dependency>
<groupId>org.leryoo</groupId>
<artifactId>hrm-basic-rabbitmq</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- fastdfsclient支持-->
<dependency>
<groupId>org.leryoo</groupId>
<artifactId>hrm-common-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<!-- 给调用的模块来转换json-->
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson 调用者需要转换-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
然后是配置文件bootstrap.yml
spring:
profiles:
active: dev
cloud:
config:
name: application-page-agent #github上面名称
profile: ${spring.profiles.active} #环境 java -jar -D xxx jar
label: master #分支
discovery:
enabled: true #从eureka上面找配置服务
service-id: hrm-config-server #指定服务名
#uri: http://127.0.0.1:1299 #配置服务器 单机配置
eureka: #eureka不能放到远程配置中
client:
service-url:
defaultZone: http://localhost:1010/eureka #告诉服务提供者要把服务注册到哪儿 #单机环境
instance:
prefer-ip-address: true #显示客户端真实ip
feign:
hystrix:
enabled: true #开启熔断支持
client:
config:
remote-service: #服务名,填写default为所有服务
connectTimeout: 3000
readTimeout: 3000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
配置仓库中的配置
server:
port: 2040
spring:
application:
name: hrm-page-agent
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtualHost: /
routingKey: hrmCourseSit
注意因为生产者和消费者都需要连接rabbitmq 所以在application-page-dev.yml中也需要配置
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtualHost: /
然后是RabbitMqConfig.java
用来声明交换机和队列 并将交换机和队列绑定到在一起
import org.leryoo.util.RabbitMqConstants;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMqConfig {
//队列
public static final String QUEUE_PAGE_STATIC = "queue_page_static";
//交换机名字
public static final String EXCHANGE_TOPICS_PAGE = RabbitMqConstants.EXCHANGE_TOPICS_PAGE;
@Value("${rabbitmq.routingKey}")
private String routingKey;
/**
* 交换机配置
* ExchangeBuilder提供了fanout、direct、topic、header交换机类型的配置
*
* @return the exchange
*/
@Bean(EXCHANGE_TOPICS_PAGE) //spring中bean
public Exchange EXCHANGE_TOPICS_INFORM() {
//durable(true)持久化,消息队列重启后交换机仍然存在
return ExchangeBuilder.topicExchange(EXCHANGE_TOPICS_PAGE).durable(true).build();
}
//声明队列
@Bean(QUEUE_PAGE_STATIC)
public Queue QUEUE_INFORM_SMS() {
Queue queue = new Queue(QUEUE_PAGE_STATIC);
return queue;
}
/**
*
* @param queue the queue
* @param exchange the exchange
* @return the binding
*/
@Bean
public Binding BINDING_QUEUE_INFORM_SMS(@Qualifier(QUEUE_PAGE_STATIC) Queue queue, //通过名字从spring获取bean
@Qualifier(EXCHANGE_TOPICS_PAGE) Exchange exchange) {
//每个站点的routing可以是不一样的
System.out.println(routingKey);
return BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs();
}
}
StaticPageHandler.java
用来接受消息 并下载到指定的目录
import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.Channel;
import feign.Response;
import org.apache.commons.io.IOUtils;
import org.leryoo.client.FastDfsClient;
import org.leryoo.config.RabbitMqConfig;
import org.leryoo.util.RabbitMqConstants;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
@Component
public class StaticPageHandler {
@RabbitListener(queues = RabbitMqConfig.QUEUE_PAGE_STATIC)
public void handle(String msg, Message message, Channel channel){
System.out.println("接收消息:"+msg);
Map map = JSONObject.parseObject(msg, Map.class);
Integer fileSysType = (Integer) map.get(RabbitMqConstants.FILE_SYS_TYPE);
String pageUrl = (String) map.get(RabbitMqConstants.PAGE_URL);
String physicalPath = (String) map.get(RabbitMqConstants.PHYSICAL_PATH);
//判断是那个文件系统,分别做处理
switch(fileSysType){
case 0 : //fastdfs
downloadAndCopyOfFastDfs(pageUrl,physicalPath);
break;
case 1 : //hdfs
downloadAndCopyOfHdfs(pageUrl,physicalPath);
break;
}
}
@Autowired
private FastDfsClient fastDfsClient;
//fastdfs支持
private void downloadAndCopyOfFastDfs(String pageUrl, String physicalPath) {
InputStream is = null;
FileOutputStream os = null;
try{
//以pageUrl到fastdfs下载文件
Response response = fastDfsClient.download(pageUrl);
is = response.body().asInputStream();
//放入特定文件
System.out.println(physicalPath);
os = new FileOutputStream(physicalPath);
IOUtils.copy(is,os) ;
}catch (Exception e){
e.printStackTrace();
}
finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//@TODO hdfs以后支持
private void downloadAndCopyOfHdfs(String pageUrl, String physicalPath) {
}
}
最后别忘了写一个启动类
然后启动服务 就ok啦
需要注意的是 因为我们这里是生成的静态页面 所以我们如果修改了数据后 我们都需要重新生成静态页面
课程列表页
因为是新的页面 所以注意跨域问题
1)主页里面关键字搜索
2)主页里面通过类型导航过去.
这里因为没找到课程商城的模板 就随便找了一个 实现起来都差不多
实现
主页跳转列表页
分析:
①主页携带参数跳转到列表页.
location.href = list.html?keyword =xx 关键字
转发到列表页后进行回显 回显到输入框
然后是一个小功能 面包屑导航
如果是通过这个地方点进来的 会去搜索文件 也会有一个面包屑的导航
后台的实现:
@Override
public List<Map<String, Object>> queryCrumbs(Long courseTypeId) {
List<Map<String,Object>> result = new ArrayList<>();
//1 通过courseTypeId获取CourseType,从而得到path,就能得到层次结构 .1.2.3. List
String path = courseTypeMapper.selectById(courseTypeId).getPath(); //.1.2.3.
path = path.substring(1,path.length()-1); // 1.2.3
System.out.println(path);
String[] paths = path.split("\\."); //[1,2,3]
//2 对每个节点进行处理 Map
for (String idStr : paths) {
Map<String,Object> node = new HashMap<>();
//2.1 自己
CourseType owerCourseType = courseTypeMapper.selectById(Long.valueOf(idStr));
node.put("ownerCourseType",owerCourseType);
//2.2 兄弟
//2.1.1 获取父亲的所有儿子
Long pid = owerCourseType.getPid();
List<CourseType> allChirdren = courseTypeMapper
.selectList(new EntityWrapper<CourseType>().eq("pid", pid));
//2.1.2 删除自己
Iterator<CourseType> iterator = allChirdren.iterator();
while (iterator.hasNext()){
CourseType type = iterator.next();
//自己id为遍历出来的id
if (owerCourseType.getId().intValue()==type.getId().intValue()){
iterator.remove();
break;
}
}
node.put("otherCourseTypes",allChirdren);
result.add(node);
}
return result;
}
然后是搜索功能
后台实现
@Override
public PageList<EsCourse> queryCourses(CourseQuery query) {
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
//1条件
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
builder.withQuery(boolQuery);
//2 排序
FieldSortBuilder order = SortBuilders.fieldSort(query.getSortField()).order(SortOrder.DESC);
builder.withSort(order);
//3 分页
builder.withPageable(PageRequest.of(query.getPage()-1,query.getRows()));
//4 截取字段 先不多
//5 查询封装
NativeSearchQuery esQuery = builder.build();
org.springframework.data.domain.Page<EsCourse> page =
repository.search(esQuery);
return new PageList<>(page.getTotalElements(),page.getContent());
}