Web实战课的学习笔记7

缓存

页面缓存+URL缓存+对象缓存,页面缓存适用于变换不大的,实际中分页是值缓存前几页,不会全都缓存。

页面缓存

主要步骤:
第一步:取缓存
第二步:手动渲染模板
第三步:结果输出
具体实现:

  1. 新建GoodKey用于redis中货物模块key的前缀。
public class GoodsKey extends BasePrefix {
    public GoodsKey(int expireSeconds, String prefix) {
        super(prefix);
    }

    public static GoodsKey getGoodsList = new GoodsKey("gl");

}
  1. 将GoodController类中的用于返回商品列表的方法进行修改。为方法添加@RsponseBody注解,和为注解@RequestMapping添加属性produces = “text/html”。进入list方法先使用redisService的get方法从缓存中进行取值。若得到的值不为空,将得到的值直接进行返回。若为空,使用thymeleafViewResolver进行手动渲染。使用thymeleafViewResolver的getTrmplateEngine().process(模版名称,context)方法,其中的模版名称就是这个类返回的html页面的名称。context包含业务数据。这里的context是IContext,IContext与springboot结合的对象为SpringWebContext。若是模版不为空,就将其保存到缓存中。最终返回html页面对象。
 @RequestMapping(value = "/to_list",produces = "text/html")
 @ResponseBody
public String list(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user) {
        model.addAttribute("user", user);
        //取缓存
        String html = redisService.get(GoodsKey.getGoodsList, "", String.class);
        if (!StringUtils.isEmpty(html)){
            return html;
        }
        //查询商品列表,包括商品和秒杀商品
        List<GoodsVo> goodsList = goodsService.listGoodsVo();
        model.addAttribute("goodsList", goodsList);     //放到Model中,供前端展示使用。
//        return "goods_list";
        //手动渲染
        SpringWebContext ctx = new SpringWebContext(request, response, request.getServletContext(), request.getLocale(), model.asMap(), applicationContext);
        html = thymeleafViewResolver.getTemplateEngine().process("goods_list", ctx);
        if (!StringUtils.isEmpty(html)){
            redisService.set(GoodsKey.getGoodsList, "", html);
        }
        return html;
    }
  1. 因为页面缓存的有效期较短,所以在GoodKey中设置有效期,设为60s。
 public GoodsKey(int expireSeconds, String prefix) {
        super(expireSeconds, prefix);
    }

    public static GoodsKey getGoodsList = new GoodsKey(60, "gl");
  1. 运行测试,进入redis根据key值进行查找,看页面是否正确的缓存到的redis中。

URL缓存

  1. 进入代码详情页的方法,依旧是修改和添加注解。进入方法后先取缓存。若不为空,就继续进行手动渲染,渲染后将结果进行输出。因为 redisService.set(GoodsKey.getGoodsDetail, “”+goodsId, html),使不同的商品有不同的详情页,这个被称为url的缓存。
@RequestMapping(value = "/to_detail/{goodsId}", produces = "text/html")
    @ResponseBody
    public String detail(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user, @PathVariable("goodsId") long goodsId) {
        model.addAttribute("user", user);
        //取缓存
        String html = redisService.get(GoodsKey.getGoodsDetail, ""+goodsId, String.class);
        if (!StringUtils.isEmpty(html)){
            return html;
        }
        GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
        model.addAttribute("goods", goods);

        long startAt = goods.getStartDate().getTime();  //转化为毫秒
        long endAt = goods.getEndDate().getTime();
        long now = System.currentTimeMillis();

        int miaoshaStatus = 0;  //秒杀状态
        int remainSeconds = 0;  //距离开始秒杀还有多久

        if (now < startAt){     //秒杀还没开始,倒计时
            miaoshaStatus = 0;
            remainSeconds = (int)((startAt - now)/1000);
        }else if (now > endAt){ //秒杀已结束
            miaoshaStatus = 2;
            remainSeconds = -1;
        }else {                 //秒杀进行中
            miaoshaStatus = 1;
            remainSeconds = 0;
        }
        model.addAttribute("miaoshaStatus", miaoshaStatus);
        model.addAttribute("remainSeconds", remainSeconds);
//        return "goods_detail";
        //手动渲染
        SpringWebContext ctx = new SpringWebContext(request, response, request.getServletContext(), request.getLocale(), model.asMap(), applicationContext);
        html = thymeleafViewResolver.getTemplateEngine().process("goods_detail", ctx);
        if (!StringUtils.isEmpty(html)){
            redisService.set(GoodsKey.getGoodsDetail, ""+goodsId, html);
        }
        return html;
    }
  1. 为GoodKey中添加getGoodsDetail
 public static GoodsKey getGoodsDetail = new GoodsKey(60, "gd");

遇到的问题

  1. 没有SpringWebContext对象
    错误原因:
    springboot的版本太高,在这个版本中是thymeleaf.spring5。没有这个对象spring4中有这个对象。
    解决方案:
    修改springboot的版本
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
  1. Maven中org.apache.commons.commons-lang3显示unknown
    错误原因:
    导入了多个不同版本的相同jar包
    解决方案:
    删除多余的jar包,只保留一个导入信息
  2. idea控制台输出的中文不能识别
    在这里插入图片描述
    解决方案:
    进入settings->editor->file encodings,将其中的GlobalEncoding、ProjectEcoding和Properties Files中的Default encoding for properties files都选为utf-8,点击apply。重启运行,控制台的中文可以正常阅读。
    在这里插入图片描述
  3. 页面th参下有红色波浪线
    在这里插入图片描述
    解决方案:在下添加<!--suppress All-->或进入Editor > Inspections >Expression variables validation的对勾取消。在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
  4. 从登录页面进入商品列表页面出500错误,并返回了商品列表的html代码和template might not exist or might not be accessible by any of the configured Template Resolvers(模板可能不存在,或者任何已配置的模板解析器都无法访问)
    解决方案:
    为list方法添加@RsponseBody注解,和为注解@RequestMapping添加属性produces = “text/html”。

对象缓存

更细的粒度缓存

  1. 进入MiaoshaUserService,找到getById方法,先从redis中取缓存的用户信息,若是没有从缓存中取到数据,就向数据库中进行取值。
 public MiaoshaUser getById(long id){
        //取缓存
        MiaoshaUser miaoshaUser =redisService.get(MiaoshaUserKey.getById, ""+id, MiaoshaUser.class);
        if (miaoshaUser != null){
            return miaoshaUser;
        }
        //取数据库
        miaoshaUser = miaoshaUserMapper.getById(id);
        if (miaoshaUser != null){
            redisService.set(MiaoshaUserKey.getById, ""+id, miaoshaUser);
        }
        return miaoshaUser;
    }
  1. 在MiaoshaUserKey中添加getById,希望对象缓存是永久有效的,所以不舍有效期。
public static MiaoshaUserKey getById = new MiaoshaUserKey(0, "id");
  1. 创建一个用于更新密码的方法updatePassword,先判断用户是否存在,若是存在就进行对数据库中数据的更新,在将数据库中的数据更新后,再对缓存中的用户数据进行更新。更新的缓存token和getByI的两个,通过对相应数据的删除,再添加实现数据的更新。为了求效率修改什么字段就更新什么字段。
 public boolean updatePassword(String token, long id, String formPass){
        //取user
        MiaoshaUser miaoshaUser = getById(id);
        if (miaoshaUser == null){
            throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
        }
        //更新数据库
        MiaoshaUser toBeUpdate = new MiaoshaUser();
        toBeUpdate.setId(id);
        toBeUpdate.setPassword(MD5Util.formPassToDBPass(formPass, miaoshaUser.getSalt()));
        miaoshaUserMapper.update(toBeUpdate);
        //处理缓存
        redisService.delete(MiaoshaUserKey.getById, ""+id);
        miaoshaUser.setPassword(toBeUpdate.getPassword());
        redisService.set(MiaoshaUserKey.token, token, miaoshaUser);
        return true;
    }
  1. 在RedisService中新建delete方法,用于删除数据。
 public Boolean delete(KeyPrefix prefix, String key){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String realPrefix = prefix.getPrefix() + key;
            long ret = jedis.del(realPrefix);
            return ret > 0;
        }finally {
            returnToPool(jedis);
        }
    }

注:在service中使用其他的Mapper对象,只能是通过其service进行使用。因为如果在service中有缓存,直接调用mapper会将缓存略过。

  1. 在MiaoshaUserMapper中添加update方法。
@Update("update miaosha_user set password = #{password} where id = #{id}")
    public void update(MiaoshaUser toBeUpdate);

压测

将代码达成jar包,传到linux系统中,输入命令进行对代码的压测。详细步骤见上一次笔记。通过将结果下载载入到JMeter中可以看到,在使用了缓存后,负载下降了。

静态化

实现了前后端的分离,将页面缓存到了浏览器中。在用户访问页面时,不需要与服务端进行交互,极大的节省了网络的流量。
常用的技术AngularJS,Vue.js。
前后端分离的实质是htm+ajax。

商品详情静态化

  1. 进入GoodsController类,复制一份detail方法,将其中一份更名为detail2,路径中的detail中改为detail2.将复制出的detail,将取缓存的部分删除和手动渲染的部分删除。将方法的返回对象改为Result,将返回给前台的信息存入Result的data属性中。
 @RequestMapping(value = "/detail/{goodsId}")
    @ResponseBody
    public Result<GoodsDetailVo> detail(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user, @PathVariable("goodsId") long goodsId) {
        GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);

        long startAt = goods.getStartDate().getTime();  //转化为毫秒
        long endAt = goods.getEndDate().getTime();
        long now = System.currentTimeMillis();

        int miaoshaStatus = 0;  //秒杀状态
        int remainSeconds = 0;  //距离开始秒杀还有多久

        if (now < startAt){     //秒杀还没开始,倒计时
            miaoshaStatus = 0;
            remainSeconds = (int)((startAt - now)/1000);
        }else if (now > endAt){ //秒杀已结束
            miaoshaStatus = 2;
            remainSeconds = -1;
        }else {                 //秒杀进行中
            miaoshaStatus = 1;
            remainSeconds = 0;
        }
        GoodsDetailVo goodsDetailVo = new GoodsDetailVo();
        goodsDetailVo.setGoodsVo(goods);
        goodsDetailVo.setMiaoshaUser(user);
        goodsDetailVo.setMiaoshaStatus(miaoshaStatus);
        goodsDetailVo.setRemainSeconds(remainSeconds);
        return Result.success(goodsDetailVo);
    }

  1. 新建GoodDetailVo类用于封装返回的信息,其中有秒杀的状态,秒杀剩余的时间,商品的信息和秒杀对象的信息。
public class GoodsDetailVo {
    private int miaoshaStatus = 0;
    private int remainSeconds = 0;
    private GoodsVo goodsVo;
    private MiaoshaUser miaoshaUser;

    public int getMiaoshaStatus() {
        return miaoshaStatus;
    }

    public void setMiaoshaStatus(int miaoshaStatus) {
        this.miaoshaStatus = miaoshaStatus;
    }

    public int getRemainSeconds() {
        return remainSeconds;
    }

    public void setRemainSeconds(int remainSeconds) {
        this.remainSeconds = remainSeconds;
    }

    public GoodsVo getGoodsVo() {
        return goodsVo;
    }

    public void setGoodsVo(GoodsVo goodsVo) {
        this.goodsVo = goodsVo;
    }

    public MiaoshaUser getMiaoshaUser() {
        return miaoshaUser;
    }

    public void setMiaoshaUser(MiaoshaUser miaoshaUser) {
        this.miaoshaUser = miaoshaUser;
    }
}
  1. 进入goods_list.html页面,见其中通过服务端跳转到detail页面的部分进行修改。因为静态页面一般应该在static下,所以将要进行静态化的页面goods_detail.html复制到static下,又因为在application中配置了.html后缀的页面在templates下进行查找,所以将移到static下的detail页面的后缀修改为.htm。
<td>
  <a th:href="'/goods_detail.htm?goodsId=' + ${goods.id}">详情</a>
</td>
  1. 进入goods_detail.htm中进行修改,删除关于thymeleaf的内容。将页面获取后端传输过来的数据${数据名称}删除,为标签添加id,id的值为所需数据的名称。
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>商品详情</title>

    <!-- jquery -->
    <script type="text/javascript" src="/js/jquery.min.js"></script>
    <!-- bootstrap -->
    <link rel="stylesheet" type="text/css" href="/bootstrap/css/bootstrap.min.css" />
    <script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script>
    <!-- jquery-validator -->
    <script type="text/javascript" src="/jquery-validation/jquery.validate.min.js"></script>
    <script type="text/javascript" src="/jquery-validation/localization/messages_zh.min.js"></script>
    <!-- layer -->
    <script type="text/javascript" src="/layer/layer.js"></script>
    <!-- md5.js -->
    <script type="text/javascript" src="/js/md5.min.js"></script>
    <!-- common.js -->
    <script type="text/javascript" src="/js/common.js"></script>

</head>
<body>
    <div class="panel panel-default">
        <div class="panel-heading">秒杀商品详情</div>
        <div class="panel-body">
            <span id="userTip">您还没有登录,请登录后再操作...<br/></span>
            <span>没有收货地址的提示...</span>
        </div>
        <table class="table" id="goodsList">
            <tr>
                <td>商品名称</td>
                <td colspan="3" id="goodsName"></td>
            </tr>
            <tr>
                <td>商品图片</td>
                <td colspan="3"><img id="goodsImg" width="200" height="200"/></td>
            </tr>
            <tr>
                <td>秒杀开始时间</td>
                <td id="startTime"></td>
                <td>
                    <input type="hidden" id="remainSeconds"/>
                    <span id="miaoshaTip"></span>
                </td>
                <td>
                   <form id="miaoshaForm" method="post" action="/miaosha/do_miaosha">
                      <button class="btn btn-primary btn-block" type="submit" id="buyButton">立即秒杀</button>
                      <input type="hidden" name="goodsId" value="${goods.id}"/>
                   </form>
                </td>
            </tr>
            <tr>
                <td>商品原价</td>
                <td colspan="3" id="goodsPrice"></td>
            </tr>
            <tr>
                <td>秒杀价</td>
                <td colspan="3" id="miaoshaPrice"></td>
            </tr>
            <tr>
                <td>库存数量</td>
                <td colspan="3" id="stockCount"></td>
            </tr>
        </table>
    </div>
</body>
<script>    //页面初始化就执行```


  1. function中第一步为去服务端取出所需数据。在getDetail方法中通过使用g_getQueryString函数获取从url中传入的goodsId。
$(function () {
   // countDown();
    getDetail();
});

function getDetail() {
    var goodsId = g_getQueryString("goodsId");
    $.ajax({
        url:"/goods/detail/" + goodsId,
        type:"GET",
        success:function (data) {
            if (data.code == 0){
                render(data.data);
            } else{
                layer.msg(data.msg);
            }
        },
        error:function () {
            layer.msg("客户端请求有误");
        }
    })
}
  1. 写一个渲染页面的的方法,其中判读用户是否存在,若存在则是处于登录状态访问此页面,将商品的信息从后端传入的数据中分离出来。其中在时间格式进行设置。
function render(detail){
    var miaoshaStatus = detail.miaoshaStatus;
    var remainSeconds = detail.remainSeconds;
    var goods = detail.goodsVo;
    var user = detail.miaoshaUser;
    if (user){
        $("#userTip").hide();
    }
    $("#goodsName").text(goods.goodsName);
    $("#goodsImg").attr("src", goods.goodsImg);
    $("#startTime").text(new Date(goods.startDate).format("yyyy-MM-dd hh:mm:ss"));
    $("#remainSeconds").val(remainSeconds);
    $("#goodsId").val(goods.id);
    $("#goodsPrice").text(goods.goodsPrice);
    $("#miaoshaPrice").text(goods.miaoshaPrice);
    $("#stockCount").text(goods.stockCount);
    countDown();
}
</script>
</html>
  1. 修改实现页面中秒杀功能的方法。
function countDown() {
    // var remainSeconds = $("#countDown").text();     <!-- 这样写的话,在秒杀进行中和秒杀已结束时就没有值 -->
    var remainSeconds = $("#remainSeconds").val();  <!-- 在隐藏域中取 -->
    var timeout;
    if (remainSeconds > 0){ //秒杀还没开始,倒计时
        $("#buyButton").attr("disabled", true); //按钮不能点
        $("#miaoshaTip").html("秒杀倒计时:" + remainSeconds + "秒");
        timeout = setTimeout(function () {
            $("#countDown").text(remainSeconds - 1);    //input标签用的是value       文案随着改
            $("#remainSeconds").val(remainSeconds - 1); //span标签用的是text
            countDown();    //不断回调countDown方法
        }, 1000);   //过1秒之后,setTimeout就会执行
    }else if(remainSeconds == 0){   //秒杀进行中
        $("#buyButton").attr("disabled", false);
        if (timeout){
            clearTimeout(timeout);  //自带的清除函数吧
        }
        $("#miaoshaTip").html("秒杀进行中"); //等到remainSeconds减到0时,改文案
    }else { //秒杀已经结束
        $("#buyButton").attr("disabled", true);
        $("#miaoshaTip").html("秒杀已结束");
    }
}
  1. 在static中的common.js页面中添加获取url中的参数的函数和设定时间格式化的函数。
// 获取url参数
function g_getQueryString(name) {
	var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
	var r = window.location.search.substr(1).match(reg);
	if(r != null) return unescape(r[2]);
	return null;
};
//设定时间格式化函数,使用new Date().format("yyyyMMddhhmmss");
Date.prototype.format = function (format) {
	var args = {
		"M+": this.getMonth() + 1,
		"d+": this.getDate(),
		"h+": this.getHours(),
		"m+": this.getMinutes(),
		"s+": this.getSeconds(),
	};
	if (/(y+)/.test(format))
		format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
	for (var i in args) {
		var n = args[i];
		if (new RegExp("(" + i + ")").test(format))
			format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? n : ("00" + n).substr(("" + n).length));
	}
	return format;
};

秒杀静态化

  1. 修改goods_detail中的秒杀功能的逻辑实现部分不需要那么麻烦,只需要button就可以。将form的部分忽略掉。
 <button class="btn btn-primary btn-block" type="button" id="buyButton" οnclick="doMiaosha()">立即秒杀</button>
 <input type="hidden" name="goodsId" id="goodsId"/>
  1. 新建秒杀方法,先用ajax调用数据。
function doMiaosha() {
    $.ajax({
        url:"miaosha/do_miaosha",
        type:"POST",
        data:{
            goodsId:$("#goodsId").val(),
        },
        success:function (data) {
           if (data.code == 0){
               window.location.href = "/order_detail.htm?orderId=" + data.data.id;
           } else{
               layer.msg(data.msg);
           }
        } ,
        error:function () {
            layer.msg("客户端请求有误");
        }
    });
}
  1. 将MiaoshaController类中的list方法,将返回值类型换为Result类型,将OrderInfo数据存入data传送到前端页面。限制这个方法只能通过POST方法提交。判断用户是否处于登录状态和库存,若是用户信息和库存为空,返回error。判断是否秒杀到,若是秒杀到就将订单信息进行返回。
 @RequestMapping(value = "/do_miaosha", method = RequestMethod.POST)
    @ResponseBody
    public Result<OrderInfo> miaosha(Model model, MiaoshaUser user, @RequestParam("goodsId") long goodsId){
        model.addAttribute("user", user);
        if (user == null){
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        //判断秒杀库存
        GoodsVo goodsVo = goodsService.getGoodsVoByGoodsId(goodsId);
        int stock = goodsVo.getStockCount();
        if (stock <= 0){
            return Result.error(CodeMsg.MIAO_SHA_OVER);
        }
        //判断是否已经秒杀到
        MiaoshaOrder miaoshaOrder = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
        if (miaoshaOrder != null){
            return Result.error(CodeMsg.REPEATE_MIAOSHA);
        }
        //进行秒杀步骤:减库存 创建普通订单 创建秒杀订单    注意这是个事务操作
        OrderInfo orderInfo = miaoshaService.miaosha(user, goodsVo);
        return Result.success(orderInfo);
    }

补充:
GET,POST有什么区别?
GET幂等的代表从服务端获取数据,无论调用多少次,最终产生的结果都是一样的,不会对服务端数据产生影响。
POST不是幂等的, 向服务端提交数据,会对服务端的数据发生变化。

  1. 进入CodeMsg添加session不存在的错误信息。
    public static final CodeMsg SESSION_ERROR = new CodeMsg(500210, "Session不存在或者已经失效");
  1. 将order_detail.html页面复制到static下,并将后缀改为.htm。同样删除关于thymeleaf的内容。将页面获取后端传输过来的数据${数据名称}删除。为标签添加id,id的值为所需数据的名称。将其中订单状态的td标签注释。
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>订单详情</title>

    <!-- jquery -->
    <script type="text/javascript" src="/js/jquery.min.js"></script>
    <!-- bootstrap -->
    <link rel="stylesheet" type="text/css" href="/bootstrap/css/bootstrap.min.css" />
    <script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script>
    <!-- jquery-validator -->
    <script type="text/javascript" src="/jquery-validation/jquery.validate.min.js"></script>
    <script type="text/javascript" src="/jquery-validation/localization/messages_zh.min.js"></script>
    <!-- layer -->
    <script type="text/javascript" src="/layer/layer.js"></script>
    <!-- md5.js -->
    <script type="text/javascript" src="/js/md5.min.js"></script>
    <!-- common.js -->
    <script type="text/javascript" src="/js/common.js"></script>

</head>
<body>
    <div class="panel panel-default">
        <div class="panel-heading">秒杀订单详情</div>
        <table class="table" id="goodslist">
            <tr>
                <td>商品名称</td>
                <td id="goodsName" colspan="3"></td>
            </tr>
            <tr>
                <td>商品图片</td>
                <td colspan="2">
                    <img id="goodsImg" width="200" height="200"/>
                </td>
            </tr>
            <tr>
                <td>订单价格</td>
                <td colspan="2" id="orderPrice"></td>
            </tr>
            <tr>
                <td>下单时间</td>
                <td id="createDate" colspan="2"></td>
            </tr>
            <tr>
                <td>订单状态</td>
                <td id="orderStatus"></td>
                 <!--<td>-->
                    <!--<span th:if="${orderInfo.status eq 0}">未支付</span>-->
                    <!--<span th:if="${orderInfo.status eq 1}">待发货</span>-->
                    <!--<span th:if="${orderInfo.status eq 2}">已发货</span>-->
                    <!--<span th:if="${orderInfo.status eq 3}">已收货</span>-->
                    <!--<span th:if="${orderInfo.status eq 4}">已退款</span>-->
                    <!--<span th:if="${orderInfo.status eq 5}">已完成</span>-->
                <!--</td>-->
                <td>
                    <button class="btn btn-primary btn-block" type="submit" id="payButton">立即支付</button>
                </td>
            </tr>
            <tr>
                <td>收货人</td>
                <td colspan="2">yanguobin 15842674359</td>
            </tr>
            <tr>
                <td>收获地址</td>
                <td colspan="2">中国 北京</td>
            </tr>
        </table>
    </div>
</body>
</html>
<script>
    function render(detail){
        var goods = detail.goodsVo;
        var order = detail.orderInfo;
        $("#goodsName").text(goods.goodsName);
        $("#goodsImg").attr("src", goods.goodsImg);
        $("#orderPrice").text(order.goodsPrice);
        $("#createDate").text(new Date(order.createDate).format("yyyy-MM-dd hh:mm:ss"));
        var status = "";
        if (order.status == 0){
            status = "未支付";
        } else if(order.status =1){
            status = "待发货";
        }
        $("#orderStatus").text(status);
    }

    $(function () {
        getOrderDetail();
    })

    function getOrderDetail() {
        var orderId = g_getQueryString("orderId");
        $.ajax({
            url:"/order/detail",
            type:"GET",
            data:{
                orderId:orderId,
            },
            success:function (data) {
                if (data.code == 0){
                    render(data.data);
                } else{
                    layer.msg(data.msg);
                }
            },
            error:function () {
                layer.msg("客户端请求有误");
            }
        })
    }
</script>
  1. 如何实现客户端直接从浏览器取数据,不需要询问服务端。进入到application.properties,写入静态配置。
spring.resources.意思
add-mappings启用默认的资源处理
cache-period资源处理程序提供的资源的缓存周期,单位为秒
chain.cache在资源链中启用缓存
chain.enabled启用Spring资源处理链。默认情况下禁用,除非至少启用了一个策略
chain.gzipped启用解析已经经过gzip压缩的资源
html-application-cache启用HTMLS应用程序缓存清单重写
chain.strategy.content.enabled启用内容版本策略
chain.strategy.content.paths逗号分隔的用于版本策略的模式列表
chain.strategy.fixed.enabled启用固定版本策略
chain.strategy.fixed.paths逗号分隔的用于版本策略的模式列表
chain.strategy.fixed.version用于版本策略的版本字符串
static-locationsstatic的路径

订单静态化

  1. 与商品详情页的静态化步骤相似,根据参数获取服务端数据,将页面渲染出来。
<script>
    function render(detail){
        var goods = detail.goodsVo;
        var order = detail.orderInfo;
        $("#goodsName").text(goods.goodsName);
        $("#goodsImg").attr("src", goods.goodsImg);
        $("#orderPrice").text(order.goodsPrice);
        $("#createDate").text(new Date(order.createDate).format("yyyy-MM-dd hh:mm:ss"));
        var status = "";
         if (order.status == 0){
            status = "未支付";
        } else if(order.status =1){
            status = "待发货";
        } else if(order.status = 2){
            status = "已发货";
        } else if(order.status = 3){
            status = "已收货";
        } else if(order.status = 4){
            status = "已退款";
        } else if(order.status = 5){
            status = "已完成";
        }
        $("#orderStatus").text(status);
    }

    $(function () {
        getOrderDetail();
    })

    function getOrderDetail() {
        var orderId = g_getQueryString("orderId");
        $.ajax({
            url:"/order/detail",
            type:"GET",
            data:{
                orderId:orderId,
            },
            success:function (data) {
                if (data.code == 0){
                    render(data.data);
                } else{
                    layer.msg(data.msg);
                }
            },
            error:function () {
                layer.msg("客户端请求有误");
            }
        })
    }
</script>
  1. 新建OrderController类。创建detail方法,判断用户是否为空,获取oder订单信息,将商品的信息通getById方法获取出来。将获取的的值存入OrderDetailVo中。
@Controller
@RequestMapping("/order")
public class OrderController {

    @Autowired
    OrderService orderService;

    @Autowired
    GoodsService goodsService;

    @RequestMapping("/detail")
    @ResponseBody
    public Result<OrderDetailVo> detail(MiaoshaUser miaoshaUser, @RequestParam("orderId") long orderId){
        if (miaoshaUser == null) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        OrderInfo orderInfo = orderService.getOrderById(orderId);
        if (orderInfo == null) {
            return Result.error(CodeMsg.ORDER_NOT_EXIST);
        }
        long goodsId = orderInfo.getGoodsId();
        GoodsVo goodsVo = goodsService.getGoodsVoByGoodsId(goodsId);
        OrderDetailVo orderDetailVo = new OrderDetailVo();
        orderDetailVo.setOrderInfo(orderInfo);
        orderDetailVo.setGoodsVo(goodsVo);
        return Result.success(orderDetailVo);
    }
}
  1. 新建OrderDetailVo类
public class OrderDetailVo {
    private GoodsVo goodsVo;
    private OrderInfo orderInfo;

    public GoodsVo getGoodsVo() {
        return goodsVo;
    }

    public void setGoodsVo(GoodsVo goodsVo) {
        this.goodsVo = goodsVo;
    }

    public OrderInfo getOrderInfo() {
        return orderInfo;
    }

    public void setOrderInfo(OrderInfo orderInfo) {
        this.orderInfo = orderInfo;
    }
}

  1. 向 OrderService中添加getOrderById方法。
  public OrderInfo getOrderById(long orderId) {
        return orderInfoMapper.getOrderById(orderId);
    }
  1. 向OrderInfoMapper添加getOrderById方法。
@Select("select * from oder_info where id = #{orderId}")
    public OrderInfo getOrderById(@Param("orderId") long orderId);
  1. 在 CodeMsg中添加ORDER_NOT_EXIST表示订单不存在。
//订单模块 5004xx
    public static final CodeMsg ORDER_NOT_EXIST = new CodeMsg(500400, "订单不存在");

解决卖超

  1. 数据库加唯一索引:防止用户重复购买。
  2. SQL加库存数量判断:防止库存变成负数。
    将GoodsMapper中的reduceStock方法的update注解中的sql语句添加一个对库存的判断。
 @Update("update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId} and stock_count>0")

在用户发送了两个请求时,有可能会将同一件商品秒杀两次。向表中创建唯一的索引。
在这里插入图片描述
优化:
在判断是否已经秒杀到时,不查数据库,查缓存。进入OrderService,将 getMiaoshaOrderByUserIdGoodsId的返回值进行替换。为使用redis,添加可自动注入的对象RedisService。

return redisService.get(OrderKey.getMiaoshaOrderByUidGid, ""+userId+"_"+goodsId, MiaoshaOrder.class);

当订单生成后将其写入redis缓存中。在createOrder中添加redisService.set()。


        myRedisUtil.set(OrderKey.getMiaoshaOrderByUidGid, ""+user.getId()+"_"+goodsVo.getId(), miaoshaOrder);

在OderKey中添加getMiaoshaOrderByUidGid。

public static OrderKey getMiaoshaOrderByUidGid = new OrderKey("moug");

静态资源优化

主要包括:

  1. JS/CSS压缩,减少流量
  2. 多个JS/CSS组合,减少 连接数
  3. CDN就近访问
    并发大的问题的瓶颈在数据库,最有效的解决方法是添加缓存。用户的角度来说,从用户发起请求的时候,从浏览器开始,通过做页面的静态化,直接将页面缓存到用户的浏览器端。请求在到达网站之前,可以部署一些CDN节点,使请求首先访问CDN。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
SpringBoot项目实战笔记可以按照以下步骤进行: 1. 首先,你可以通过观看B站上的教程视频来学习SpringBoot项目实战。在视频中,你可以学习到如何使用SpringBoot、MyBatis和MySQL创建一个电脑商城项目。 2. 确保你的SpringBoot项目能够成功启动。找到被@SpringBootApplication注解修饰的入口启动类,并运行该类。如果你能够观察到图形化的界面,那么说明你的项目成功启动了。 3. 如果你还没有创建SpringBoot项目,你可以使用Spring Initializr来初始化一个新的项目。Spring Initializr是一个Web应用程序,可以为你生成Spring Boot项目的基本结构。你可以选择使用Maven或Gradle作为构建工具,并添加适合你的项目的依赖。然后,你只需要编写应用程序的代码即可。 希望以上信息对你有帮助!如果还有其他问题,请随时提问。123 #### 引用[.reference_title] - *1* *2* [SpringBoot项目实战笔记:电脑商城项目实战(SpringBoot+MyBatis+MySQL)](https://blog.csdn.net/weixin_44260350/article/details/127746667)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] - *3* [《SpringBoot实战》读书笔记](https://blog.csdn.net/sanhewuyang/article/details/104494202)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值