3.秒杀商品-首页
秒杀商品首页会显示处于秒杀中以及未开始秒杀的商品。
3.1 秒杀首页实现分析
秒杀首页需要显示不同时间段的秒杀商品信息,然后当用户选择不同的时间段,查询该时间段下的秒杀商品,实现过程分为两大过程:
1) 加载时间菜单
2)加载时间菜单下秒杀商品信息
3.1.1 加载时间菜单分析
每2个小时就会切换一次抢购活动,所以商品发布的时候,我们将时间定格在2小时内抢购,每次发布商品的时候,商品抢购开始时间和结束时间是这2小时的边界。
每2小时会有一批商品参与抢购,所以我们可以将24小时切分为12个菜单,每个菜单都是个2小时的时间段,当前选中的时间菜单需要根据当前时间判断,判断当前时间属于哪个秒杀时间段,然后将该时间段作为选中的第1个时间菜单。
3.1.2 加载对应秒杀商品分析
进入首页时,到后台查询时间菜单信息,然后将第1个菜单的时间段作为key,在Redis中查询秒杀商品集合,并显示到页面,页面每次点击切换不同时间段菜单的时候,都将时间段传入到后台,后台根据时间段获取对应的秒杀商品集合。
3.2 秒杀渲染服务 - 渲染秒杀首页
3.2.1 新建秒杀渲染服务
1)创建工程changgou_web_seckill,用于秒杀页面渲染
2) 添加依赖
<dependencies>
<dependency>
<groupId>com.changgou</groupId>
<artifactId>changgou_service_seckill_api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
- 添加启动类
package com.changgou.seckill.web;
import com.changgou.interceptor.FeignInterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.changgou.seckill.feign"})
public class SecKillWebApplication {
public static void main(String[] args) {
SpringApplication.run(SecKillWebApplication.class,args);
}
@Bean
public FeignInterceptor feignInterceptor(){
return new FeignInterceptor();
}
/**
* 设置 redisTemplate 的序列化设置
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 1.创建 redisTemplate 模版
RedisTemplate<Object, Object> template = new RedisTemplate<>();
// 2.关联 redisConnectionFactory
template.setConnectionFactory(redisConnectionFactory);
// 3.创建 序列化类
GenericToStringSerializer genericToStringSerializer = new GenericToStringSerializer(Object.class);
// 6.序列化类,对象映射设置
// 7.设置 value 的转化格式和 key 的转化格式
template.setValueSerializer(genericToStringSerializer);
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
- 添加application.yml
server:
port: 9104
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:6868/eureka
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
spring:
jackson:
time-zone: GMT+8
thymeleaf:
cache: false
application:
name: seckill-web
main:
allow-bean-definition-overriding: true
redis:
host: 192.168.200.128
#hystrix 配置
hystrix:
command:
default:
execution:
timeout:
#如果enabled设置为false,则请求超时交给ribbon控制
enabled: true
isolation:
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 60000
#请求处理的超时时间
ribbon:
ReadTimeout: 4000
#请求连接的超时时间
ConnectTimeout: 3000
- 添加静态化资源 (联系我qq1712841217)
6)对接网关 (gateway服务)
#秒杀渲染微服务
- id: changgou_seckill_web_route
uri: lb://seckill-web
predicates:
- Path=/api/wseckillgoods/**
filters:
- StripPrefix=1
3.3 时间菜单实现
时间菜单显示,先运算出每2小时一个抢购,就需要实现12个菜单,可以先计算出每个时间的临界值,然后根据当前时间判断需要显示12个时间段菜单中的哪个菜单,再在该时间菜单的基础之上往后挪4个菜单,一直显示5个时间菜单。
3.3.1 时间菜单获取
changgou_web_seckill新增控制类SecKillGoodsController
package com.changgou.seckill.web.controller;
import com.changgou.entity.Result;
import com.changgou.seckill.feign.SecKillGoodsFeign;
import com.changgou.seckill.pojo.SeckillGoods;
import com.changgou.util.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Controller
@RequestMapping("/wseckillgoods")
public class SecKillGoodsController {
//获取秒杀时间段集合信息
@RequestMapping("/timeMenus")
@ResponseBody
public List<String> dateMenus(){
//获取当前时间段相关的信息集合
List<Date> dateMenus = DateUtil.getDateMenus();
List<String> result = new ArrayList<>();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (Date dateMenu : dateMenus) {
String format = simpleDateFormat.format(dateMenu);
result.add(format);
}
return result;
}
}
3.3.2 页面加载时间菜单
修改seckill-index.html
var app = new Vue({
el: '#app',
data() {
return {
goodslist: [],
dateMenus:[]
}
},
methods:{
loadMenus:function () {
axios.get("/api/wseckill/timeMenus").then(function(response) {
app.dateMenus=response.data;
//查询当前时间段对应的秒杀商品
})
}
},
created:function () {
this.loadMenus();
}
})
</script>
效果如下:
3.3.3 时间格式化
上面菜单循环输出后,会出现如上图效果,时间格式全部不对,我们需要引入一个moment.min.js来格式化时间。
1)引入moment.min.js
2)添加过滤器 (html-index 336行)
Vue.filter("dateFilter", function(date, formatPattern){
return moment(date).format(formatPattern || "YYYY-MM-DD HH:mm:ss");
});
- 取值格式化
<div class="time-clock">{{item | dateFilter('HH:mm')}}</div>
重新访问:http://localhost:9104/wseckill/toIndex 。时间菜单效果如下
3.3.4 选中实现
3.3.4.1 思路分析
根据原型图,是让当前第一个时间菜单为选中状态,并且加载第一个菜单对应的数据。
我们可以先定义一个ctime=0,用来记录当前选中的菜单下标,因为默认第一个选中,第一个下标为0,所以初始值为0,每次点击对应菜单的时候,将被点击的菜单的下标值赋值给ctime,然后在每个菜单上判断,下标=ctime则让该菜单选中。
3.3.4.2 代码实现
1)定义ctime=0
var app = new Vue({
el: '#app',
data() {
return {
goodslist: [],
dateMenus:[],
ctime:0, //当前时间菜单选中的下标,
}
}
})
2)页面样式控制:
3.3.5 倒计时实现
3.3.5.1 倒计时实现
3.3.5.1.1 基础数据显示
定义一个集合,用于存放五个时间段的倒计时时间差,集合中每一个角标都对应一个倒计时时间差,比如:集合角标为0,对应第一个倒计时时间差。集合角标为1,对应第二个倒计时时间差,依次类推。
因为要有倒计时的效果,所以后续会遍历该时间集合,并让集合中的每一个时间循环递减即可。
从该集合中获取内容,并更新倒计时时间
3.3.5.1.2 每个时间差倒计时实现
周期执行函数用法如下:
window.setInterval(function(){//要做的事},1000);
结束执行周期函数用法如下:
window.clearInterval(timers);
具体代码如下:
//时间差递减实现
let timers = window.setInterval(function () {
for(var i=0;i<app.alltimes.length;i++){
//时间递减
app.$set(app.alltimes,i,app.alltimes[i]-1000);
if (app.alltimes[i]<=0){
//停止倒计时并重新刷新秒杀时间段
window.clearInterval(timers);
app.loadMenus();
}
}
},1000);
测试访问:http://localhost:9104/wseckill/toIndex 。可以发现每一个时间段的时间都在每秒递减。
3.3.5.1.3 倒计时时间格式化
//将毫秒转换成时分秒
timedown:function(num) {
var oneSecond = 1000;
var oneMinute=oneSecond*60;
var oneHour=oneMinute*60
//小时
var hours =Math.floor(num/oneHour);
//分钟
var minutes=Math.floor((num%oneHour)/oneMinute);
//秒
var seconds=Math.floor((num%oneMinute)/oneSecond);
//拼接时间格式
var str = hours+':'+minutes+':'+seconds;
return str;
},
修改时间差显示设置
重新访问进行测试。效果如下:
3.3.5.1.4 正确倒计时时间显示
现在页面中,对于倒计时时间集合内的数据,暂时写的为假数据,现在需要让集合内容的数据是经过计算得出的。第一个是距离结束时间倒计时,后面的4个都是距离开始倒计时,每个倒计时其实就是2个时差,计算方式如下:
第1个时差:第2个抢购开始时间-当前时间,距离结束时间
第2个时差:第2个抢购开始时间-当前时间,距离开始时间
第3个时差:第3个抢购开始时间-当前时间,距离开始时间
第4个时差:第4个抢购开始时间-当前时间,距离开始时间
第5个时差:第5个抢购开始时间-当前时间,距离开始时间
loadMenus:function () {
axios.get("/api/wseckillgoods/timeMenus").then(function (response) {
app.dateMenus = response.data;
//查询当前秒杀时间段下的秒杀商品列表
app.searchList(app.dateMenus[0]);
//计算正确的倒计时时间差
for(var i=0;i<app.dateMenus.length;i++){
if (i==0){
var x =i+1;
app.$set(app.alltimes,i,new Date(app.dateMenus[x]).getTime() - new Date().getTime());
} else{
app.$set(app.alltimes,i,new Date(app.dateMenus[i]).getTime() - new Date().getTime());
}
}
//时间差递减实现
let timers = window.setInterval(function () {
for(var i=0;i<app.alltimes.length;i++){
//时间递减
app.$set(app.alltimes,i,app.alltimes[i]-1000);
if (app.alltimes[i]<=0){
//停止倒计时并重新刷新秒杀时间段
window.clearInterval(timers);
app.loadMenus();
}
}
},1000);
})