目录
一、界面效果
二、前端代码
三、后端代码(redis+blacklist)
3.1 ProducatController
3.2 ProductService
3.3 ProductDao
3.4 映射文件
一、界面效果
二、前端代码
商品详情前端代码
<template>
<van-nav-bar
title="商品详情"
left-text="返回"
right-text="分享"
left-arrow
@click-left="onClickLeft"
@click-right="onClickRight"
/>
<van-swipe :autoplay="3000" lazy-render>
<van-swipe-item v-for="image in product.img" :key="image">
<img :src="image" style="width: 100%; height: 250px" @click="show" />
</van-swipe-item>
</van-swipe>
<div class="Detail">
<div class="price">
<div>
到手价¥<span>{{ product.price }}</span>
</div>
</div>
<h4>{{ product.name }}</h4>
<van-text-ellipsis
rows="1"
:content="product.subName"
expand-text="展开"
collapse-text="收起"
/>
</div>
<div class="warp">
<van-tabs v-model:active="active" swipeable>
<van-tab title="商品介绍">
<div v-html="product.brief"></div>
</van-tab>
<van-tab title="规格参数"> 3333 </van-tab>
<van-tab title="售后保障">
<div>
<div class="mod_tit_line">
<h3>权利声明</h3>
</div>
京东商城上的所有商品信息、客户评价、商品咨询、网友讨论等内容,是京东商城重要的经营资源,未经许可,禁止非法转载使用。
<div class="for_separator"></div>
<p>
<b>注:</b
>本站商品信息均来自于厂商,其真实性、准确性和合法性由信息拥有者(厂商)负责。本站不提供任何保证,并不承担任何法律责任。
</p>
</div>
</van-tab>
</van-tabs>
</div>
<van-action-bar>
<van-action-bar-icon icon="chat-o" text="客服" color="#ee0a24" />
<van-action-bar-icon icon="cart-o" text="购物车" />
<van-action-bar-icon icon="star" text="已收藏" color="#ff5000" />
<van-action-bar-button type="warning" text="加入购物车" />
<van-action-bar-button type="danger" text="立即购买" />
</van-action-bar>
<van-share-sheet
v-model:show="showShare"
title="立即分享给好友"
:options="options"
@select="onSelect"
/>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { showToast } from "vant";
import { useRoute, useRouter } from "vue-router";
import { productApi } from "@/api/index";
import { showImagePreview } from "vant";
const onClickLeft = () => history.back();
const onClickRight = () => {
showShare.value = true;
};
const active = ref(0);
const route = useRoute();
onMounted(() => {
callDetail();
});
const showShare = ref(false);
const options = [
{ name: "微信", icon: "wechat" },
{ name: "微博", icon: "weibo" },
{ name: "复制链接", icon: "link" },
{ name: "分享海报", icon: "poster" },
{ name: "二维码", icon: "qrcode" },
];
const onSelect = (option) => {
showToast(option.name);
showShare.value = false;
};
const show = () => {
showImagePreview({
images: product.value.img.split(","),
});
};
const callDetail = () => {
productApi.selectById.call({ id: route.query.id }).then((res: any) => {
console.log(res);
product.value = res;
product.value.img=res.img.split(',')
});
};
const product: any = ref({
});
</script>
<style>
.warp img {
width: 100%;
}
</style>
<style scoped>
.van-action-bar {
z-index: 50;
}
.price {
color: red;
font-size: 14px;
}
.price span {
font-size: 20px;
}
.Detail {
background-color: #fff;
padding: 5px;
border-radius: 0 0 10px 10px;
}
.Detail h4 {
margin: 5px 0;
}
.Detail p {
font-size: 15px;
}
.warp {
margin-top: 10px;
background-color: #fff;
padding: 5px;
border-radius: 10px 10px 0 0;
}
</style>
三、后端代码(redis+blacklist)
3.1 ProducatController
/**
* 商品详情
* @param id
* @return
*/
@GetMapping("/detail")
public Product detail(Integer id) {
int a=10;
return productService.detail(id);
}
3.2 ProductService
package com.beimao.service;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.smart.core.exception.BizException;
import com.beimao.common.model.Product;
import com.beimao.dao.ProductDao;
import com.beimao.dao.ProductMapper;
import com.beimao.model.EsProduct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class ProductService {
/**
* redis黑名单解决击穿问题
*/
@Autowired
private ProductDao productDao;
@Resource(name = "redisTemplate")
private HashOperations<String,String,String> hashOperations;
@Resource(name = "redisTemplate")
private ValueOperations<String, Product> valueOperations;
// 黑名单key
public static final String BLACKLIST_KEY = "product.hei";
// 商品详情key
public static final String ProductDetail_KEY = "product.detail";
/**
* 根据商品id 给商品详情
* 先从redis中查询,如果redis中没有,则从数据库中查询
*
* @param id
* @return
*/
public Product detail(Integer id) {
/**
* 先验证商品id是否合法
*/
if (id <= 0) {
log.debug("商品id=》{}不合法", id);
throw new BizException(100, "商品id不合法");
}
/**
* 从redis里先检查 黑名单里是否有该商品
* 如果有,抛出异常
*/
Boolean b = hashOperations.hasKey(BLACKLIST_KEY, id.toString());
if (b) {
log.debug("商品不存在,商品在黑名单里,商品id=>{}", id);
throw new BizException(101, "商品不存在");
}
/**
* 黑名单没有
* 直接从redis里查询是否有该商品
* 有就返回,没有就查数据库
*/
Product product = valueOperations.get(ProductDetail_KEY+id);
if (ObjectUtil.isNotEmpty(product)) {
log.debug("商品id=>{},从redis里查询到商品", id);
return product;
}
/**
* redis里面没有 就从数据库里面查
* 数据库有就加入redis里面,没有就加入黑名单
*/
product = productDao.detail(id);
if (ObjectUtil.isEmpty(product)) {
// 数据库查不到就加入黑名单
hashOperations.put(BLACKLIST_KEY, id.toString(), DateUtil.now());
log.debug("数据库里没有该商品,商品id=>{}加入黑名单", id);
}
// 数据库里面有就将该商品存到redis里面,设置ttl 一天
/**
* 错峰解决redis里的雪崩问题
*/
int expire= RandomUtil.randomInt(-60 , 60);
valueOperations.set(ProductDetail_KEY+id, product,24*60+expire, TimeUnit.MINUTES);
return product;
}
}
3.3 ProductDao
/**
* 根据商品id查询商品详情
* @param id
* @return
*/
Product detail(Integer id);
3.4 映射文件
<!-- 根据id查询商品详情-->
<select id="detail" resultType="com.beimao.common.model.Product">
select id,subName,status,price,seq,tags,`name`,categoryId,img,brief
from 205_product where id = #{id}
</select>