java电商项目(六)

本文涉及的产品
实时数仓Hologres,5000CU*H 100GB 3个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: Thymeleaf 是一个类似于 FreeMarker 的模板引擎,能够完全替代 JSP。它支持动静结合,无网络时显示静态内容,有网络时用后台数据替换静态内容,并且与 Spring Boot 完美整合。本文介绍了如何使用 Thymeleaf 生成商品详情页的静态页面。具体步骤包括创建商品静态化微服务、配置项目依赖、创建 Controller 和 Service、生成静态页面、模板填充、静态资源过滤以及启动测试。此外,还介绍了如何通过 Canal 监听商品数据变化,自动触发静态页面的生成或删除。

[TOC]

1.Thymeleaf介绍

概念

Thymeleaf 是一个跟 FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下特点:

  • 动静结合:Thymeleaf 在有网络和无网络的环境下皆可运行,无网络显示静态内容,有网络用后台得到数据替换静态内容
  • 与SpringBoot完美整合,springboot默认整合thymeleaf

之前springboot课程中已经讲解过thymeleaf,这里不再重复

2.商品详情页

2.1 需求分析

当系统审核完成商品,需要将商品详情页进行展示,那么采用静态页面生成的方式生成,并部署到高性能的web服务器中进行访问是比较合适的。所以,开发流程如下图所示:

1566736163765

执行步骤解释:

  • 系统管理员(商家运维人员)修改或者审核商品的时候,会触发canal监控数据
  • canal微服务获取修改数据后,调用静态页微服务的方法进行生成静态页
  • 静态页微服务只负责使用thymeleaf的模板技术生成静态页

2.2 商品静态化微服务创建

2.2.1 需求分析

该微服务只用于生成商品静态页,不做其他事情。

2.2.2 搭建项目

(1)创建legou-page工程

1561856105210

(2)legou-page中添加起步依赖,如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>legou-parent</artifactId>
        <groupId>com.lxs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>legou-page</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <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-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>

        <!-- swagger -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

        <!--oauth2-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

        <!--商品微服务-->
        <dependency>
            <groupId>com.lxs</groupId>
            <artifactId>legou-item-instance</artifactId>
            <version>${project.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>com.lxs</groupId>
            <artifactId>legou-common</artifactId>
            <version>${project.version}</version>
        </dependency>

    </dependencies>


</project>

(3)配置文件

legou-page/src/main/resources/bootstrap.yml

spring:
  application:
    name: page-service

# 生成静态页的位置
pagepath: d:\temp

config-repo/page-service.yml

server:
  port: 9008

logging:
  #file: demo.log
  pattern:
    console: "%d - %msg%n"
  level:
    org.springframework.web: debug
    com.lxs: debug

security:
  oauth2:
    resource:
      jwt:
        key-uri: http://localhost:9098/oauth/token_key #如果使用JWT,可以获取公钥用于 token 的验签

(4)创建系统启动类

package com.lxs.legou;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
@EnableCircuitBreaker
public class PageApplication {
   

    public static void main(String[] args) {
   
        SpringApplication.run(PageApplication.class, args);
    }

}

2.3 生成静态页

thymleaf模板和静态资源css,js,图片等直接从资料拷贝即可,这部分内容有前端人员提供

2.3.1 需求分析

页面发送请求,传递要生成的静态页的的商品的SpuID.后台controller 接收请求,调用thyemleaf的原生API生成商品静态页。

1561856657363

上图是要生成的商品详情页,从图片上可以看出需要查询SPU的3个分类作为面包屑显示,同时还需要查询SKU和SPU信息。

2.3.2 Feign创建

一会儿需要查询SPU和SKU以及Category,所以我们需要先创建Feign,创建CategoryClient代码如下

legou-page/src/main/java/com/lxs/legou/page/client/CategoryClient.java

package com.lxs.legou.page.client;

import com.lxs.legou.item.api.CategoryApi;
import com.lxs.legou.item.po.Category;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient(name = "item-service", contextId = "p1", fallback = CategoryClient.CategoryClientFallback.class)
public interface CategoryClient extends CategoryApi {
   

    @Component
    @RequestMapping("/category-fallback2") //这个可以避免容器中requestMapping重复
    class CategoryClientFallback implements CategoryClient {
   

        private static final Logger LOGGER = LoggerFactory.getLogger(CategoryClientFallback.class);

        @Override
        public List<String> queryNameByIds(List<Long> ids) {
   
            LOGGER.info("异常发生,进入fallback方法");
            return null;
        }

        @Override
        public List<Category> list(Category category) {
   
            LOGGER.info("异常发生,进入fallback方法");
            return null;
        }

        @Override
        public Category edit(Long id) {
   
            LOGGER.info("异常发生,进入fallback方法");
            return null;
        }
    }

}

FeignClient中的contextId的应用

比如我们有个item-service服务,但item-service服务中有很多个接口,我们不想将所有的调用接口都定义在一个类中,比如:

@FeignClient(name = "item-service", contextId = "p1")
@RequestMapping("/item/category")
public interface CategoryClient1 {

        @Override
        @PostMapping("/query-name-by-ids")
        public List<String> queryNameByIds(List<Long> ids) {
            LOGGER.info("异常发生,进入fallback方法");
            return null;
        }

}

@FeignClient(name = "item-service", contextId = "p2")
@RequestMapping("/item/category")
public interface CategoryClient2 {

        @Override
        @PostMapping("/query-name-by-ids")
        public List<String> queryNameByIds(List<Long> ids) {
            LOGGER.info("异常发生,进入fallback方法");
            return null;
        }

}

这里使用contextId手动指定一个名称,或者可以做如下配置

spring: 
  main:
    allow-bean-definition-overriding: true

legou-page/src/main/java/com/lxs/legou/page/client/SpuClient.java

package com.lxs.legou.page.client;

import com.lxs.legou.item.api.SpuApi;
import com.lxs.legou.item.po.Spu;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient(name = "item-service", contextId = "p3", fallback = SpuClient.SpuClientFallback.class)
public interface SpuClient extends SpuApi {
   

    @Component
    @RequestMapping("/spu-fallback2") //这个可以避免容器中requestMapping重复
    class SpuClientFallback implements SpuClient {
   

        private static final Logger LOGGER = LoggerFactory.getLogger(SpuClientFallback.class);

        @Override
        public List<Spu> selectAll() {
   
            LOGGER.error("异常发生,进入fallback方法");
            return null;
        }

        @Override
        public Spu edit(Long id) {
   
            LOGGER.error("异常发生,进入fallback方法");
            return null;
        }
    }

}

legou-page/src/main/java/com/lxs/legou/page/client/SpuDetailClient.java

package com.lxs.legou.page.client;

import com.lxs.legou.item.api.SpuDetailApi;
import com.lxs.legou.item.po.SpuDetail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient(name = "item-service", contextId = "p4", fallback = SpuDetailClient.SpuDetailFallback.class)
public interface SpuDetailClient extends SpuDetailApi {
   

    @Component
    @RequestMapping("/spu-detail-fallback2") //这个可以避免容器中requestMapping重复
    class SpuDetailFallback implements SpuDetailClient {
   

        private static final Logger LOGGER = LoggerFactory.getLogger(SpuDetailFallback.class);

        @Override
        public SpuDetail edit(Long id) {
   
            System.out.println("异常发生,进入fallback方法");
            return null;
        }
    }

}

legou-page/src/main/java/com/lxs/legou/page/client/SkuClient.java

package com.lxs.legou.page.client;

import com.lxs.legou.item.api.SkuApi;
import com.lxs.legou.item.po.Sku;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient(name = "item-service", contextId = "p2", fallback = SkuClient.SkuClientFallback.class)
public interface SkuClient extends SkuApi {

    @Component
    @RequestMapping("/sku-fallback2")
            //这个可以避免容器中requestMapping重复
    class SkuClientFallback implements SkuClient {

        private static final Logger LOGGER = LoggerFactory.getLogger(SkuClientFallback.class);

        @Override
        public List<Sku> selectSkusBySpuId(Long spuId) {
            LOGGER.error("异常发生,进入fallback方法");
            return null;
        }
    }

}

2.3.3 静态页生成代码

(1)创建Controller

legou-page/src/main/java/com/lxs/legou/page/controller/PageController.java

package com.lxs.legou.page.controller;

import com.lxs.legou.page.service.PageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/page")
public class PageController {
   
    @Autowired
    private PageService pageService;

    /**
     * 生成静态页面
     * @param id SPU的ID
     * @return
     */
    @RequestMapping("/createHtml/{id}")
    public ResponseEntity<String> createHtml(@PathVariable(name="id") Long id){
   
        pageService.createPageHtml(id);
        return ResponseEntity.ok("生成成功");
    }
}

(2)创建service

接口:

package com.lxs.legou.page.service;


public interface PageService {
   
    //生成静态页
    void createPageHtml(Long id);
}

实现类:

1561857221099

上图代码如下:

package com.lxs.legou.page.service.impl;

import com.lxs.legou.common.utils.JsonUtils;
import com.lxs.legou.core.json.JSON;
import com.lxs.legou.item.po.Category;
import com.lxs.legou.item.po.Sku;
import com.lxs.legou.item.po.Spu;
import com.lxs.legou.item.po.SpuDetail;
import com.lxs.legou.page.client.CategoryClient;
import com.lxs.legou.page.client.SkuClient;
import com.lxs.legou.page.client.SpuClient;
import com.lxs.legou.page.client.SpuDetailClient;
import com.lxs.legou.page.service.PageService;
import com.netflix.discovery.converters.Auto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import java.io.File;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
public class PageServiceImpl implements PageService {
   

    @Autowired
    private SpuClient spuClient;

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private SkuClient skuClient;

    @Autowired
    private SpuDetailClient spuDetailClient;

    @Autowired
    private TemplateEngine templateEngine;

    //生成静态文件路径
    @Value("${pagepath}")
    private String pagepath;

    /**
     * 构建数据模型
     * @param spuId
     * @return
     */
    private Map<String,Object> buildDataModel(Long spuId){
   
        //构建数据模型
        Map<String,Object> dataMap = new HashMap<>();
        //获取spu 和SKU列表
        Spu spu = spuClient.edit(spuId);
        Category c1 = categoryClient.edit(spu.getCid1());
        //获取分类信息
        dataMap.put("category1", categoryClient.edit(spu.getCid1()));
        dataMap.put("category2", categoryClient.edit(spu.getCid2()));
        dataMap.put("category3", categoryClient.edit(spu.getCid3()));

        List<Sku> skus = skuClient.selectSkusBySpuId(spu.getId());
        List<String> images = new ArrayList<>();
        for (Sku sku : skus) {
   
            images.add(sku.getImages());
        }
        dataMap.put("imageList", images);

        SpuDetail spuDetail = spuDetailClient.edit(spu.getId());
//        Map<String, String> genericMap = JsonUtils.parseMap(spuDetail.getGenericSpec(), String.class, String.class);
        Map<String, Object> genericMap = JsonUtils.parseMap(spuDetail.getSpecialSpec(), String.class, Object.class);
//        dataMap.put("specificationList", JSON.parseObject(spu.getSpecItems(),Map.class));

        dataMap.put("specificationList", genericMap);

        dataMap.put("spu",spu);
        dataMap.put("spuDetail",spuDetail);

        //根据spuId查询Sku集合
        dataMap.put("skuList", skus);
        return dataMap;
    }

    /***
     * 生成静态页
     * @param spuId
     */
    @Override
    public void createPageHtml(Long spuId) {
   
        // 1.上下文  模板  +  数据集 =html
        Context context = new Context();
        Map<String, Object> dataModel = buildDataModel(spuId);
        context.setVariables(dataModel);//model.addtribute()
        // 2.准备文件
        File dir = new File(pagepath);
        if (!dir.exists()) {
   
            dir.mkdirs();
        }
        File dest = new File(dir, spuId + ".html");
        // 3.生成页面
        try (PrintWriter writer = new PrintWriter(dest, "UTF-8")) {
   
            //模板的文件
            templateEngine.process("item", context, writer);
        } catch (Exception e) {
   
            e.printStackTrace();
        }
    }
}

2.2.4 模板填充

(1)面包屑数据

修改item.html,填充三个分类数据作为面包屑,代码如下:

1561858073684

(2)商品图片

修改item.html,将商品图片信息输出,在真实工作中需要做空判断,代码如下:

1561858651061

(3)规格输出

1561859448044

(4)默认SKU显示

静态页生成后,需要显示默认的Sku,我们这里默认显示第1个Sku即可,这里可以结合着Vue一起实现。可以先定义一个集合,再定义一个spec和sku,用来存储当前选中的Sku信息和Sku的规格,代码如下:

1561858986838

页面显示默认的Sku信息

1561859241651

(5)记录选中的Sku

在当前Spu的所有Sku中spec值是唯一的,我们可以根据spec来判断用户选中的是哪个Sku,我们可以在Vue中添加代码来实现,代码如下:

1561860041421

添加规格点击事件

1561860156033

(6)样式切换

点击不同规格后,实现样式选中,我们可以根据每个规格判断该规格是否在当前选中的Sku规格中,如果在,则返回true添加selected样式,否则返回false不添加selected样式。

Vue添加代码:

1561860749790

页面添加样式绑定,代码如下:

1561860653870

2.2.5 静态资源过滤

生成的静态页我们可以先放到legou-page工程中,后面项目实战的时候可以挪出来放到Nginx指定发布目录。一会儿我们将生成的静态页放到resources/templates/items目录下,所以请求该目录下的静态页需要直接到该目录查找即可。

我们创建一个EnableMvcConfig类,开启静态资源过滤,代码如下:

@ControllerAdvice
@Configuration
public class EnableMvcConfig implements WebMvcConfigurer{
   

    /***
     * 静态资源放行
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
   
        registry.addResourceHandler("/items/**").addResourceLocations("classpath:/templates/items/");
    }
}

5.4.6 启动测试

启动eurekea服务端

启动商品微服务

启动静态化微服务 legou-page

将静态资源导入到legou-page中,如下图:

1561861540056

生成静态页地址 http://localhost:18085/page/createHtml/1087918019151269888

1561862007448

静态页生成后访问地址 http://localhost:9008/items/1087918019151269888.html

1561862084834

3 canal监听生成静态页(作业)

1566736163765

监听到数据的变化,直接调用feign 生成静态页即可.

3.1 需求分析

当商品微服务审核商品之后,应当发送消息,这里采用了Canal监控数据变化,数据变化后,调用feign实现生成静态页

3.2 Feign创建

(1)Feign创建

在legou-canal中创建PageFeign,代码如下:

@FeignClient(name="item")
@RequestMapping("/page")
public interface PageFeign {
   

    /***
     * 根据SpuID生成静态页
     * @param id
     * @return
     */
    @RequestMapping("/createHtml/{id}")
    Result createHtml(@PathVariable(name="id") Long id);
}

3.3 canal监听数据变化

监听类中,监听商品数据库的tb_spu的数据变化,当数据变化的时候生成静态页或者删除静态页

在原来的监听类中添加如下代码即可,

@Autowired
private PageFeign pageFeign;

@ListenPoint(destination = "example",
        schema = "legou",
        table = {
   "spu"},
        eventType = {
   CanalEntry.EventType.UPDATE, CanalEntry.EventType.INSERT, CanalEntry.EventType.DELETE})
public void onEventCustomSpu(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
   

    //判断操作类型
    if (eventType == CanalEntry.EventType.DELETE) {
   
        String spuId = "";
        List<CanalEntry.Column> beforeColumnsList = rowData.getBeforeColumnsList();
        for (CanalEntry.Column column : beforeColumnsList) {
   
            if (column.getName().equals("id")) {
   
                spuId = column.getValue();//spuid
                break;
            }
        }
        //todo 删除静态页

    }else{
   
        //新增 或者 更新
        List<CanalEntry.Column> afterColumnsList = rowData.getAfterColumnsList();
        String spuId = "";
        for (CanalEntry.Column column : afterColumnsList) {
   
            if (column.getName().equals("id")) {
   
                spuId = column.getValue();
                break;
            }
        }
        //更新 生成静态页
        pageFeign.createHtml(Long.valueOf(spuId));
    }
}
相关文章
|
23天前
|
安全 NoSQL Java
java电商项目(十)
本文介绍了电商系统中订单结算和下单流程的实现。主要包括: 1. **订单结页**: - **收件地址分析**:用户从购物车页面跳转到订单结算页,加载用户收件地址。地址信息存储在 `address_` 表中。 - **实现用户收件地址查询**:通过用户登录名查询收件地址,涉及实体类、DAO、Service 和 Controller 的实现。 2. **下单**: - **业务分析**:下单时创建订单数据,包括订单表 `order_` 和订单明细表 `order_item_`,同时修改商品库存、增加用户积分并删除购物车数据。
31 3
|
23天前
|
消息中间件 安全 Java
java电商项目(十一)
本文接续前几个文章的项目进行讲解!
27 1
|
23天前
|
缓存 NoSQL Java
java电商项目(十二)
本文接续前几个文章的项目进行讲解
84 1
|
23天前
|
存储 NoSQL Java
java电商项目(九)
本文介绍了购物车功能的实现过程,包括用户登录后将商品添加至购物车、购物车列表展示及微服务之间的认证机制。具体步骤如下: 1. **购物车功能**: - 用户选择商品并点击“加入购物车”,系统将商品信息存储到Redis中。 2. **微服务之间认证**: - **传递管理员令牌**:在授权中心微服务调用用户微服务时,生成管理员令牌并通过Header传递。 - **传递当前用户令牌**:用户登录后,通过Feign拦截器将用户令牌传递到其他微服务。 - **获取用户数据**:通过`SecurityContextHolder`获取用户信息,并使用公钥解密令牌以验证用户
26 1
|
23天前
|
监控 算法 Java
java电商项目(七)
微服务网关作为系统唯一对外的入口,位于客户端和服务端之间,处理非业务功能,如路由请求、鉴权、监控、缓存、限流等。它解决了客户端直接调用多个微服务带来的复杂性、跨域请求、认证复杂、难以重构等问题。常用的微服务网关技术有Nginx、Zuul和Spring Cloud Gateway。Spring Cloud Gateway因其集成断路器、路径重写和较好的性能而被广泛使用。本文介绍了如何使用Spring Cloud Gateway搭建后台网关系统,包括引入依赖、配置文件、跨域配置、路由过滤配置、负载均衡、限流等。此外,还详细讲解了RBAC权限数据管理、组织机构管理单点登录(SSO)及JWT鉴权的实现
29 1
|
23天前
|
SQL 自然语言处理 Java
java电商项目(五)
本文介绍了如何构建一个基于Elasticsearch的商品搜索微服务,主要包括以下几个部分: 1. **数据导入ES**: - 搭建搜索工程,创建`legou-search`项目,提供搜索服务和索引数据更新操作。 - 配置`pom.xml`文件,引入必要的依赖。 - 创建启动器和配置文件,配置Elasticsearch连接信息。 - 分析索引库数据格式,确定需要存储的数据字段。 - 实现商品微服务接口,调用其他微服务获取所需数据。 - 创建索引并导入数据,将SPU和SKU数据转换为索引库中的Goods对象。 2. **商品搜索*
26 1
|
23天前
|
canal NoSQL 关系型数据库
java电商项目(四)
本章介绍了如何通过Lua、OpenResty、Nginx限流及Canal的使用,实现电商门户首页的高并发解决方案。主要内容包括: 1. **商城门户搭建**:使用Vue和iView构建前端门户项目,介绍如何展示商品分类和广告数据,并通过Redis缓存提升访问速度。 2. **Lua基础**:介绍Lua的基本概念、特性、应用场景及安装步骤,并通过示例展示了Lua的基本语法和常用功能。 3. **OpenResty介绍**:详细说明OpenResty的特性和优势,包括如何安装OpenResty和配置Nginx,以及如何使用Lua脚本操作Nginx缓存和数据库。
20 1
|
23天前
|
存储 前端开发 JavaScript
java电商项目(二)
本文档详细介绍了商品分类和规格参数的实现过程。商品分类分为三级管理,主要用于首页商品导航和后台商品管理,采用树状结构存储。规格参数则用于描述商品的具体属性,包括SPU和SKU的定义,规格参数与分类绑定,支持搜索过滤。文档涵盖了表结构设计、实体类、持久层、业务层、控制层的实现,并提供了前端组件的示例代码,确保前后端无缝对接。
30 1
|
23天前
|
存储 安全 Java
java电商项目(八)
OAuth 2.0 是一种开放标准,允许用户授权第三方应用访问其在某一网站上的私密资源,而无需提供用户名和密码。它通过提供一个令牌(token)来实现这一功能。OAuth 2.0 主要包括四种授权模式:授权码模式、简化模式、密码模式和客户端模式。授权码模式是最常用的一种,适用于第三方平台登录功能。Spring Security OAuth 2.0 提供了强大的工具来实现授权服务器和资源服务器的集成,支持多种授权模式和令牌存储方式,如内存、数据库、JWT 和
39 0
|
23天前
|
前端开发 算法 JavaScript
java电商项目(三)
本文介绍了乐购商城的商品数据分析和管理功能。首先解释了SPU(标准产品单位)和SKU(库存量单位)的概念,以及它们在商品管理和销售中的作用。接着详细分析了SPU、SPU详情和SKU三个表的结构及其关系。文章还介绍了商品管理的需求分析、实现思路和后台代码,包括实体类、持久层、业务层和控制层的实现。最后,文章讲解了前端组件的设计和实现,包括列表组件、添加修改组件、商品描述、通用规格、SKU特有规格和SKU列表的处理。通过这些内容,读者可以全面了解乐购商城的商品管理和数据分析系统。
30 0