4 商品详情页

4.1 分析

​ 秒杀活动中,热卖商品的详情页访问频率非常高,详情页的数据加载,我们可以采用直接从数据库查询加载,但这种方式会给数据库带来极大的压力,甚至崩溃,这种方式我们并不推荐。

​ 商品详情页主要有商品介绍、商品标题、商品图片、商品价格、商品数量等,大部分数据几乎不变,可能只有数量会变,因此我们可以考虑把商品详情页做成静态页,每次访问只需要加载库存数量,这样就可以大大降低数据库的压力。

​ 我们这里将采用freemarker来实现商品详情页的静态化,关于freemarker的语法我们就不在这里讲解了,大家可以自行去网上查阅相关API。

4.2 工程搭建

我们创建一个静态页生成工程,用于生成商品详情页。

pom.xml

<dependencies>
 <!--依赖web-->
 <dependency>
     <groupId>com.seckill</groupId>
     <artifactId>seckill-web</artifactId>
     <version>0.0.1-SNAPSHOT</version>
 </dependency>
 
 <!--api-->
 <dependency>
     <groupId>com.seckill</groupId>
     <artifactId>seckill-goods-api</artifactId>
     <version>0.0.1-SNAPSHOT</version>
 </dependency>
 
 <!--freemarker-->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-freemarker</artifactId>
 </dependency>
</dependencies>

bootstrap.yml

server:
  port: 18087
spring:
  application:
    name: seckill-page
  cloud:
    nacos:
      config:
        file-extension: yaml
        server-addr: nacos-server:8848
      discovery:
        #Nacos的注册地址
        server-addr: nacos-server:8848
  profiles:
    active: dev #dev 开发环境 #test测试环境 #pro生产环境
  main:
    allow-bean-definition-overriding: true
  #freemarker配置
  freemarker:
    cache: false  #关闭模板缓存,方便测试
    settings:
      template_update_delay: 0  #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
    template-loader-path: classpath:/templates
    charset: UTF-8
    check-template-location: true
    suffix: .ftl
    content-type: text/html
    expose-request-attributes: true
    expose-session-attributes: true
    request-context-attribute: request
#超时配置
ribbon:
  ReadTimeout: 300000
  ConnectTimeout: 30000
 
#静态页位置
htmlPath: D:/page/html

创建文件生成对象:

public class BaseProcess {
 
    @Autowired
    private Configuration configuration;
 
    /***
     * 生成静态页
     * @param dataMap
     *          dataMap.templateName: 模板名字
     *          dataMap.path: 生成文件存储路径
     *          dataMap.name: 生成的文件名字
     * @throws Exception
     */
    public void writerPage(Map<String,Object> dataMap) throws Exception {
        //获取模板名字
        String templateName = dataMap.get("templateName").toString();
 
        //文件生存的路径
        String path = dataMap.get("path").toString();
 
        //文件路径如果不存在,则创建
        File file = new File(path);
        if(!file.exists()){
            file.mkdirs();
        }
 
        //获取文件名字
        String fileName = dataMap.get("name").toString();
 
        //获取模板对象
        Template template = configuration.getTemplate(templateName);
 
        //模板处理,获取生成的文件字符串
        String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, dataMap);
 
        //生成文件
        FileUtils.writeStringToFile(new File(path,fileName),content);
    }
}

BaseProcess介绍:

该类用于生成静态文件,调用writerPage方法即可实现,其中有3个变量,变量注释如下:

templateName:模板名字,例如item.ftl,模板放到templates目录下
path:生成文件的路径,例如D:/page/html
name:生成静态页文件的名字,例如:1.html

4.3 商品详情静态页生成

生成商品详情页,我们需要提供商品信息(sku),并且将商品数据存储到数据模型Map中,然后在页面渲染。

1)静态页生成

seckill-page中创建com.seckill.page.service.SkuPageService,添加生成静态页方法,代码如下:

/***
 * 生成静态页
 */
void itemPage(Map<String,Object> dataMap) throws Exception;

seckill-page中创建com.seckill.page.service.impl.SkuPageServiceImpl,添加生成静态页方法,代码如下:

@Service
public class SkuPageServiceImpl extends BaseProcess implements SkuPageService  {
 
    /***
     * 生成静态页
     */
    @Override
    public void itemPage(Map<String,Object> dataMap) throws Exception {
        dataMap.put("username","王五");
 
        //生成静态页
        super.writerPage(dataMap);
    }
}

seckill-page中创建com.seckill.page.controller.SkuPageController,添加生成静态页方法,代码如下:

@RestController
@RequestMapping(value = "/page")
public class SkuPageController {
 
    @Autowired
    private SkuPageService skuPageService;
 
    @Value("${htmlPath}")
    private String htmlPath;
 
    /****
     * 生成商品详情静态页
     */
    @PostMapping(value = "/html")
    public Result html(@RequestBody Sku sku) throws Exception {
        //数据模型
        Map<String,Object> dataMap = new HashMap<String,Object>();
        dataMap.put("name",sku.getId()+".html"); //生成静态页的文件名字
        dataMap.put("path",htmlPath);            //文件路径
        dataMap.put("templateName","item.ftl"); //模板名字
        dataMap.put("sku",sku);                  //商品数据
 
        //生成静态页
        skuPageService.itemPage(dataMap);
        return new Result(true, StatusCode.OK,"生成成功!");
    }
}

注意:其中bootstrap.yml中添加htmlPath: D:/page/html配置。

关于Freemarker学习:FreeMarker的jar包下载引入

现在已进入面向接口编程的前后端分离时代,大概看下即可

2)Feign调用

seckill-page-api中创建com.seckill.page.feign.SkuPageFeign实现调用,代码如下:

@FeignClient(value = "seckill-page")
public interface SkuPageFeign {
 
    /****
     * 生成商品详情静态页
     */
    @PostMapping(value = "/page/html")
    Result html(@RequestBody Sku sku) throws Exception;
}

4.4 静态页删除

​ 当商品变成普通商品或者商品售罄的时候,需要删除详情页,因此还需要实现一个根据id删除详情页的方法。

seckill-page中创建com.seckill.page.service.SkuPageService,添加删除静态页方法,代码如下:

/***
 * 删除静态页
 * @param id
 * @param htmlPath
 */
void delItemPage(String id,String htmlPath);

seckill-page中创建com.seckill.page.service.impl.SkuPageServiceImpl,添加删除静态页方法,代码如下:

/***
 * 删除静态页
 * @param id
 * @param htmlPath
 */
@Override
public void delItemPage(String id, String htmlPath) {
    File file = new File(htmlPath,id+".html");
    if(file.exists()){
        file.delete();
    }
}

seckill-page中创建com.seckill.page.controller.SkuPageController,添加删除静态页方法,代码如下:

/****
 * 删除商品详情静态页
 */
@DeleteMapping(value = "/html/{id}")
public Result delHtml(@PathVariable(value = "id")String id) throws Exception {
    //删除静态页
    skuPageService.delItemPage(id,htmlPath);
    return new Result(true, StatusCode.OK,"删除成功!");
}

2)Feign调用

seckill-page-api中创建com.seckill.page.feign.SkuPageFeign实现调用,代码如下:

/****
 * 删除商品详情静态页
 */
@DeleteMapping(value = "/page/html/{id}")
Result delHtml(@PathVariable(value = "id")String id) throws Exception;