畅购商城4.0

畅购商城4.0
1.走进电商
1.1电商行业分析
近年来,世界经济正向数字化转型,大力发展数字经济成为全球共识。党的十九大报告明确提出要建设“数字中国”“网络强国”,我国数字经济发展进入新阶段,市场规模位居全球第二,数字经济与实体经济深度融合,有力促进了供给侧结构性改革。电子商务是数字经济的重要组成部分,是数字经济最活跃、最集中的表现形式之一。
我国电子商务交易规模继续扩大,全国电子商务交易额达保持高速增长。国家统计局数据显示,2017年29.16万亿元,2018年31.64万亿元,2019年34.81万亿元,2020年交易额达38.21万亿元。
最近几年天猫双十一成交额

1.2主要电商模式
B2B
B2B ( Business to Business)是指进行电子商务交易的供需双方都是商家(或企业、公司),她(他)们使用了互联网的技术或各种商务网络平台,完成商务交易的过程。电子商务是现代 B2B marketing的一种具体主要的表现形式。
案例:阿里巴巴、慧聪网

C2C
C2C即 Customer(Consumer) to Customer(Consumer),意思就是消费者个人间的电子商务行为。比如一个消费者有一台电脑,通过网络进行交易,把它出售给另外一个消费者,此种交易类型就称为C2C电子商务。
案例:淘宝、易趣、瓜子二手车

B2C
B2C是Business-to-Customer的缩写,而其中文简称为“商对客”。“商对客”是电子商务的一种模式,也就是通常说的直接面向消费者销售产品和服务商业零售模式。这种形式的电子商务一般以网络零售业为主,主要借助于互联网开展在线销售活动。B2C即企业通过互联网为消费者提供一个新型的购物环境——网上商店,消费者通过网络在网上购物、网上支付等消费行为。
案例:唯品会、乐蜂网

C2B
C2B(Consumer to Business,即消费者到企业),是互联网经济时代新的商业模式。这一模式改变了原有生产者(企业和机构)和消费者的关系,是一种消费者贡献价值(Create Value), 企业和机构消费价值(Consume Value)。

C2B模式和我们熟知的供需模式(DSM, Demand SupplyModel)恰恰相反,真正的C2B 应该先有消费者需求产生而后有企业生产,即先有消费者提出需求,后有生产企业按需求组织生产。通常情况为消费者根据自身需求定制产品和价格,或主动参与产品设计、生产和定价,产品、价格等彰显消费者的个性化需求,生产企业进行定制化生产。
案例:海尔商城、 尚品宅配

O2O
O2O即Online To Offline(在线离线/线上到线下),是指将线下的商务机会与互联网结合,让互联网成为线下交易的平台,这个概念最早来源于美国。O2O的概念非常广泛,既可涉及到线上,又可涉及到线下,可以通称为O2O。主流商业管理课程均对O2O这种新型的商业模式有所介绍及关注。
案例:美团、饿了吗

B2B2C
B2B2C是一种电子商务类型的网络购物商业模式,B是BUSINESS的简称,C是CUSTOMER的简称,第一个B指的是商品或服务的供应商,第二个B指的是从事电子商务的企业,C则是表示消费者。

案例:京东商城、天猫商城

注:我们《畅购电商系统开发》课程采用B2C模式

2.畅购前台-需求分析与系统设计
电商项目分为:前台和后台
前台:提供用户使用
后台:提供商家进行管理
本项目主要讲解时:前台
2.1需求分析

2.2系统设计
2.2.1技术架构

2.2.2架构图

3.架构搭建
3.1数据库环境

本项目的重点在前端和后端,提供的数据库,没有分库分表。

3.2后端环境
3.2.1父工程:changgou4_parent_ali
修改pom.xml文件,确定spring boot、spring cloud、spring cloud Alibaba 等版本

<?xml version="1.0" encoding="UTF-8"?>


4.0.0

<groupId>com.czxy.changgou</groupId>
<artifactId>changgou4-parent-ali</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
    <module>changgou4_common</module>
    <module>changgou4_common_auth</module>
    <module>changgou4_common_db</module>
    <module>changgou4_gateway</module>
    <module>changgou4_pojo</module>
    <module>changgou4_service_web</module>
</modules>

<!-- 1 确定spring boot的版本-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
</parent>

<!--2  确定版本-->
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
    <spring-cloud-release.version>Hoxton.SR3</spring-cloud-release.version>
    <nacos.version>1.1.0</nacos.version>
    <alibaba.cloud.version>2.2.1.RELEASE</alibaba.cloud.version>
    <mysql.version>5.1.32</mysql.version>
    <mybatis.plus.version>3.4.0</mybatis.plus.version>
    <druid.starter.version>1.1.9</druid.starter.version>
    <jwt.jjwt.version>0.9.0</jwt.jjwt.version>
    <jwt.joda.version>2.9.7</jwt.joda.version>
    <swagger.version>2.7.0</swagger.version>
    <beanutils.version>1.9.3</beanutils.version>
    <aliyun.sdk.core.version>3.3.1</aliyun.sdk.core.version>
    <aliyun.sdk.dysmsapi.version>1.0.0</aliyun.sdk.dysmsapi.version>
    <changgou4.common.version>1.0-SNAPSHOT</changgou4.common.version>
    <changgou4.common.auth.version>1.0-SNAPSHOT</changgou4.common.auth.version>
    <changgou4.common.db.version>1.0-SNAPSHOT</changgou4.common.db.version>
    <changgou4.pojo.version>1.0-SNAPSHOT</changgou4.pojo.version>

</properties>

<!-- 3 锁定版本-->
<dependencyManagement>
    <dependencies>
        <!-- sprig cloud-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud-release.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--nacos -->
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>${nacos.version}</version>
        </dependency>

        <!--nacos cloud 发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>${alibaba.cloud.version}</version>
        </dependency>

        <!--nacos cloud 配置 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>${alibaba.cloud.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>${alibaba.cloud.version}</version>
        </dependency>

        <!-- mybatis plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis.plus.version}</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-annotation</artifactId>
            <version>${mybatis.plus.version}</version>
        </dependency>

        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <!-- druid启动器 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.starter.version}</version>
        </dependency>

        <!--swagger2-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>

        <!--jwt-->
        <!--JavaBean工具类,用于JavaBean数据封装-->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>${beanutils.version}</version>
        </dependency>

        <!--jwt工具-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jwt.jjwt.version}</version>
        </dependency>

        <!--joda 时间工具类 -->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>${jwt.joda.version}</version>
        </dependency>

        <!--短信-->
        <dependency>
            <groupId>com.aliyuncs</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>${aliyun.sdk.core.version}</version>
        </dependency>
        <dependency>
            <groupId>com.aliyuncs.dysmsapi</groupId>
            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
            <version>${aliyun.sdk.dysmsapi.version}</version>
        </dependency>

        <!--自定义项目-->
        <dependency>
            <groupId>com.czxy.changgou</groupId>
            <artifactId>changgou4_common</artifactId>
            <version>${changgou4.common.version}</version>
        </dependency>
        <dependency>
            <groupId>com.czxy.changgou</groupId>
            <artifactId>changgou4_common_auth</artifactId>
            <version>${changgou4.common.auth.version}</version>
        </dependency>
        <dependency>
            <groupId>com.czxy.changgou</groupId>
            <artifactId>changgou4_common_db</artifactId>
            <version>${changgou4.common.db.version}</version>
        </dependency>
        <dependency>
            <groupId>com.czxy.changgou</groupId>
            <artifactId>changgou4_pojo</artifactId>
            <version>${changgou4.pojo.version}</version>
        </dependency>


    </dependencies>

</dependencyManagement>

3.2.2公共项目(基础):changgou4-common

通用工具项目
基于spring cloud开发基本依赖
web开发常见的工具类

步骤一:修改pom.xml文件,添加依赖

<?xml version="1.0" encoding="UTF-8"?>



changgou4-parent-ali
com.czxy.changgou
1.0-SNAPSHOT

4.0.0

<artifactId>changgou4-common</artifactId>

<dependencies>
    <!--短信-->
    <dependency>
        <groupId>com.aliyuncs</groupId>
        <artifactId>aliyun-java-sdk-core</artifactId>
    </dependency>
    <dependency>
        <groupId>com.aliyuncs.dysmsapi</groupId>
        <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

步骤二:拷贝工具类

3.2.3 Proyecto público (autenticación): changgou4-common-auth
 Proyecto de herramienta común de autenticación

Paso 1: Modificar el archivo pom.xml

<?versión xml="1.0" codificación="UTF-8"?>



changgou4-parent-ali
com.czxy.changgou
1.0-INSTANTÁNEA

4.0.0

<artifactId>changgou4-common-auth</artifactId>

<dependencies>
    <!--通用基础-->
    <dependency>
        <groupId>com.czxy.changgou</groupId>
        <artifactId>changgou4_common</artifactId>
    </dependency>
    <!--JavaBean工具类,用于JavaBean数据封装-->
    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
    </dependency>

    <!--jwt工具-->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
    </dependency>

    <!--joda 时间工具类 -->
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
    </dependency>
</dependencies>

Paso 2: Clase de herramienta de copia

3.2.4 Proyecto público (base de datos): changgou4-common-db
Proyecto de herramienta común de base de datos

Paso 1: Modificar el archivo pom.xml

<?versión xml="1.0" codificación="UTF-8"?>



changgou4-parent-ali
com.czxy.changgou
1.0-INSTANTÁNEA

4.0.0

<artifactId>changgou4-common-db</artifactId>

<dependencies>
    <!--通用基础-->
    <dependency>
        <groupId>com.czxy.changgou</groupId>
        <artifactId>changgou4-common</artifactId>
    </dependency>
    <!-- mybatis plus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>
    <!-- mysql驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!-- druid启动器 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
    </dependency>
</dependencies>

Paso 2: Clase de configuración
El nombre del paquete de todos los elementos de servicio debe ser "com.czxy.changgou4", de lo contrario, la configuración no se puede escanear y cada elemento debe copiarse por separado.

3.2.5 Proyecto POJO: changgou4-pojo
Gestión unificada de todos los JavaBean

 Modifique el archivo pom.xml



org.projectlombok
lombok



org.springframework.boot
spring-boot-starter-json



com.baomidou
mybatis-plus-annotation

3.2.6 Gateway: changgou4-gateway
 Modificar el documento pom.xml

<?versión xml="1.0" codificación="UTF-8"?>



changgou4-parent-ali
com.czxy.changgou
1.0-INSTANTÁNEA

4.0.0

<artifactId>changgou4-gateway</artifactId>

<dependencies>
    <!--自定义项目-->
    <dependency>
        <groupId>com.czxy.changgou</groupId>
        <artifactId>changgou4-common-auth</artifactId>
    </dependency>
    <dependency>
        <groupId>com.czxy.changgou</groupId>
        <artifactId>changgou4-pojo</artifactId>
    </dependency>
    <!-- 网关 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <!-- nacos 服务发现 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
</dependencies>

创建application.yml文档
#端口号
server:
port: 10010
spring:
application:
name: changgou4-gateway
servlet:
multipart:
max-file-size: 2MB #上传文件的大小
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos服务地址
gateway:
discovery:
locator:
enabled: true #开启服务注册和发现的功能,自动创建router以服务名开头的请求路径转发到对应的服务
lowerCaseServiceId: true #将请求路径上的服务名配置为小写

拷贝跨域配置类 GlobalCorsConfig

package com.czxy.changgou4.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

/**

  • @author 桐叔

  • @email [email protected]
    */
    @Configuration
    public class GlobalCorsConfig {

    @Bean
    public WebFilter corsFilter2() {
    return (ServerWebExchange ctx, WebFilterChain chain) -> {
    ServerHttpRequest request = ctx.getRequest();
    if (CorsUtils.isCorsRequest(request)) {
    HttpHeaders requestHeaders = request.getHeaders();
    ServerHttpResponse response = ctx.getResponse();
    HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
    HttpHeaders headers = response.getHeaders();
    headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
    headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,
    requestHeaders.getAccessControlRequestHeaders());
    if (requestMethod != null) {
    headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
    }
    headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, “true”);
    encabezados.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, “*”);
    if (solicitud.getMethod() == HttpMethod.OPCIONES) { respuesta.setStatusCode(HttpStatus.OK); return Mono.vacío(); } } return cadena.filtro(ctx); }; }






}

 Crear una clase de inicio

paquete com.czxy.changgou4;

importar org.springframework.boot.SpringApplication;
importar org.springframework.boot.autoconfigure.SpringBootApplication;
importar org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**

  • @autor Tío Tong
  • @email [email protected]
    */
    @SpringBootApplication
    @EnableDiscoveryClient
    public class CGGatewayApplication { public static void main(String[] args) { SpringApplication.run(CGGatewayApplication.class, args ); } }



3.3 Entorno de front-end
3.3.1 Proyecto de compilación: changgou4-fore
Paso 1: Use scaffolding para compilar el proyecto
npx create-nuxt-app changgou4-fore

Paso 2: método de empaquetado npm, módulo de terceros axios, modo de representación SSR

3.3.2 Integre axios
Paso 1: Cree el archivo ~/plugins/apiclient.js
Paso 2: Escriba la plantilla de integración nuxt para la gestión unificada de la ruta de solicitud ajax
const request = { test : ()=> { return axios.get( ' /prueba') } }



var axios =
valor predeterminado de exportación nulo ({ $axios, redirigir }, inyectar) => {

//Asignar
axios = $axios

//4) 将自定义函数交于nuxt
// 使用方式1:在vue中,this. r e q u e s t . x x x ( ) / / 使用方式 2 :在 n u x t 的 a s y n c D a t a 中, c o n t e n t . a p p . request.xxx() // 使用方式2:在nuxt的asyncData中,content.app. request.xxx()//使用方式2:在nuxtasyncData中,content.app.request.xxx()
inject(‘request’, request)
}

步骤三:配置apiclient.js插件,修改nuxt.conf.js配置文件完成操作

plugins: [
{ src: ‘~plugins/apiclient.js’}
],

步骤四:修改nuxt.conf.js配置文件,配置axios通用设置

axios: {
baseURL: ‘http://localhost:10010’
},

步骤五:测试 apiclient.js是否配置成功,访问test时,出现404
async mounted() {
let { data } = await this.$request.test()
console.info(data)
},

3.3.3拷贝静态资源
将所有静态资源拷贝到static目录中

通过浏览器访问静态页面
http://localhost:3000/index.html

3.3.4修改Nuxt项目默认项
1)修改默认布局,删除已有样式

2)删除pages目录下的所有内容

3.3.5配置公共js和css
修改默认布局,添加公共js和css

4.用户模块(8081)
4.1搭建环境
4.1.1后端web服务:changgou4-service-web
修改pom.xml文档

<?xml version="1.0" encoding="UTF-8"?>



changgou4-parent-ali
com.czxy.changgou
1.0-INSTANTÁNEA

4.0.0

<artifactId>changgou4_service_web</artifactId>

<dependencies>
    <!--自定义项目-->
    <dependency>
        <groupId>com.czxy.changgou</groupId>
        <artifactId>changgou4_common_db</artifactId>
    </dependency>
    <dependency>
        <groupId>com.czxy.changgou</groupId>
        <artifactId>changgou4_pojo</artifactId>
    </dependency>
    <!--web起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- nacos 客户端 -->
    <dependency>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-client</artifactId>
    </dependency>

    <!-- nacos 服务发现 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--redis-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
    </dependency>
    <!--swagger2-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
    </dependency>
</dependencies>

 Crear documento application.yml

#Servidor de número de puerto
:
puerto: 8081

spring:
aplicación:
nombre: servicio web #Servicio
fuente de datos de nombre:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/changgou_db?useUnicode=true&characterEncoding=utf8
nombre de usuario:
contraseña raíz: 1234
druid: #druid configuración del grupo de conexiones
tamaño inicial: 1 #Inicializar el tamaño del grupo de conexiones
min-idle: 1 #Número mínimo de conexiones
max-active: 20 #Número máximo de conexiones
test-on-borrow: verdadero #Verificación al obtener una conexión afectará el rendimiento
redis:
base de datos: 0
host: 127.0.0.1
puerto: 6379
nube:
nacos:
descubrimiento:
dirección del servidor: 127.0.0.1:8848 #nacos dirección del servicio
centinela:
transporte:
tablero: 127.0.0.1:8080

 Crear una clase de inicio

paquete com.czxy.changgou4;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**

  • @author 桐叔
  • @email [email protected]
    */
    @SpringBootApplication
    @EnableDiscoveryClient
    public class Web-serviceApplication {
    public static void main(String[] args) {
    SpringApplication.run( Web-serviceApplication.class , args );
    }
    }

4.1.2后端创建JavaBean:User
在changgou4-pojo项目中添加User对象

package com.czxy.changgou4.pojo;

importar com.baomidou.mybatisplus.annotation.IdType;
importar com.baomidou.mybatisplus.annotation.TableField;
importar com.baomidou.mybatisplus.annotation.TableId;
importar com.baomidou.mybatisplus.annotation.TableName;
importar lombok.AllArgsConstructor;
importar lombok.Datos;
importar lombok.NoArgsConstructor;
importar lombok.ToString;

importar java.beans.Transient;
importar java.util.Date;

/** JavaBean correspondiente a la base de datos

  • Creado por liantong.
    /
    @TableName(“tb_user”)
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User { /

    CREATE TABLE tb_user(
    idint(10) sin firmar NOT NULL AUTO_INCREMENT,
    created_attimestamp NULL DEFAULT NULL,
    updated_attimestamp NULL DEFAULT NULL,
    emailvarchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMENT 'Correo electrónico',
    mobilevarchar(20) COLLATE utf8_unicode_ci NO COMENTARIO NULO '手机号码',
    usernamevarchar(255) COLLATE utf8_unicode_ci NO COMENTARIO NULO '昵称',
    passwordchar(60) COLLATE utf8_unicode_ci NO COMENTARIO NULO '密码',
    facevarchar(255) COLLATE ut f8_unicode_ci COMENTARIO NULO POR DEFECTO '头像',
    exprieceint(10) sin signo COMENTARIO POR DEFECTO '0' '经验值',
    CLAVE PRINCIPAL (id),
    UNIQUE KEY users_mobile_unique (mobile),
    UNIQUE KEY users_name_unique (name),
    UNIQUE KEY users_email_unique (email)
    ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
    */
    @TableId(value=“id”,type = IdType.AUTO)
    private Long id;

    @TableField(value=“username”)
    private String username;

    @TableField(value=“password”)
    private String password;

    @TableField(value=“face”)
    private String face;

    @TableField(value=“expriece”)
    private Integer expriece;

    @TableField(value=“email”)
    private String email;

    @TableField(value=“mobile”)
    private String mobile;

    @TableField(value=“created_at”)
    private Fecha de creación en;

    @TableField(value=“updated_at”)
    private Fecha de actualización en;

    @TableField(exist = false)
    privado Código de cadena;
    @TableField(existir = falso)
    private String contraseña_confirmar;

}

4.1.3 Página de inicio: Crear componentes públicos
1) Eliminar todo el contenido en el directorio de componentes y crear 3 nuevos componentes

2) Cree un componente TopNav.vue para configurar la "navegación superior"

  
    
      


      


        

  •           
  • ¡Hola, bienvenido a Changgou! [ Iniciar sesión ] [ Registro gratuito ]

  •           
  • |

  •           
  • Mi pedido

  •           
  • |

  •           
  • Servicio al Cliente


      
    
  
  

3) Cree un componente HeaderLogo.vue para configurar "encabezado de página, solo LOGO"

  
       
  

4) Cree el componente Footer.vue para configurar "derechos de autor inferiores"

     

4.2 Registro de Usuario: Nombre de Usuario Ocupación

4.2.1接口
http://localhost:10010/web-service/user/checkusername
{ “nombre de usuario”:“jack1” }

4.2.2 Backend
Crear las interfaces o clases requeridas por las tres capas

Paso 1: Cree UserMapper, escriba findByUsername() para completar el
paquete "consultar usuarios por nombre de usuario" com.czxy.changgou4.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.czxy.changgou4.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

/**

  • Created by liangtong.
    /
    @org.apache.ibatis.annotations.Mapper
    public interface UserMapper extends BaseMapper {
    /
    *
    • 通过用户名查询
    • @param username
    • @return
      */
      @Select(“select * from tb_user where username = #{username}”)
      User findByUsername(@Param(“username”) String username);
      }

步骤二:创建UserService接口,查询功能

package com.czxy.changgou4.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.czxy.changgou4.pojo.User;

/**

  • @author 桐叔
  • @email [email protected]
    /
    interfaz pública UserService extiende IService { /
    *
    • Consulta por nombre de usuario
    • @param nombre de usuario
    • @return
      */
      usuario público findByUsername(String nombre de usuario);
      }

Paso 3: Cree la clase de implementación UserServiceImpl, consulte
el paquete de funciones com.czxy.changgou4.service.impl;

importar com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
importar com.czxy.changgou4.mapper.UserMapper;
importar com.czxy.changgou4.pojo.User;
importar com.czxy.changgou4.service.UserService;
importar org.springframework.stereotype.Service;
importar org.springframework.transaction.annotation.Transactional;

/**

  • @autor Tío Tong
  • @email [email protected]
    */
    @Service
    @Transactional
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Override public User findByUsername(String nombre de usuario) { return baseMapper.findByUsername(nombre de usuario); } }




Paso 4: Cree un UserController y complete el
paquete de verificación de nombre de usuario com.czxy.changgou4.controller;

importar com.czxy.changgou4.pojo.User;
importar com.czxy.changgou4.service.UserService;
importar com.czxy.changgou4.vo.BaseResult;
importar org.springframework.web.bind.annotation.*;

importar javax.anotación.Recurso;

/**

  • Created by liangtong.
    */
    @RestController
    @RequestMapping(“/user”)
    public class UserController {

    @Resource
    private UserService userService;

    @PostMapping(“/checkusername”)
    public BaseResult checkUsername(@RequestBody User user){
    //查询用户
    User findUser = userService.findByUsername( user.getUsername() );
    //判断
    if(findUser != null){
    return BaseResult.error(“用户名已经存在”);
    } else {
    return BaseResult.ok(“用户名可用”);
    }
    }

}

4.2.3前端
步骤一:创建Register.vue

步骤二:添加公共组件

步骤三:编写注册表单,并导入独有样式

     


  
  


Paso 4: Modifique api.js y escriba una función ajax para verificar el nombre de usuario
const request = { test : ()=> { return axios.get('/test') }, //verifique el nombre de usuario checkUsername : (username) = > { return axios.post('/web-service/user/checkusername', { nombre de usuario }) } }







Paso 5: Modifique la página Register.vue para completar la función de verificación
Envíe ajax para verificar si el nombre de usuario está disponible
Si está disponible, muestre la información correspondiente y use el estilo de éxito para mostrar
Si no está disponible, muestre el correspondiente información y use el indicador de estilo de error

     


  
  


4.3 Registro de usuario: comprobación de número de móvil
4.3.1 Interfaz
http://localhost:10010/web-service/user/checkmobile
{ "mobile": "13344445555" }

4.3.2 Servidor

Paso 1: Modifique UserService, agregue el método findByMobile() para consultar el número de teléfono
/**

  • Consulta por número de teléfono móvil
  • @param móvil
  • @return
    */
    Usuario findByMobile(String móvil);

Paso 2: Escriba UserServiceImpl e implemente el método findByMobile()
@Override
public User findByMobile(String mobile) { // Combine las condiciones QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq(“mobile”, mobile); // Consulta un List list = baseMapper.selectList(queryWrapper); if(list.size() == 1) { return list.get(0); } return null; }









Paso 3: Modificar UserController, agregar el método checkMobile()
/**

  • Consulta por número de teléfono móvil
  • usuario @param
  • @return
    */
    @PostMapping(“/checkmobile”)
    public BaseResult checkMobile(@RequestBody User user){
    //查询用户
    User findUser = userService.findByMobile( user.getMobile() );
    //判断
    if(findUser != null){
    return BaseResult.error(“电话号码已经注册”);
    } else {
    return BaseResult.ok(“电话号码可用”);
    }
    }

4.3.3前端
步骤一:修改api.js,添加 checkMobile() 函数

const request = { prueba : ()=> { return axios.get('/test') }, //verificar nombre de usuario checkUsername : (username )=> { return axios.post('/web-service/user/checkusername ' , { nombre de usuario }) }, //verificar número de teléfono checkMobile: (móvil)=> { return axios.post('/web-service/user/checkmobile', { mobile }) } }











Paso 2: Modifique Register.vue, agregue checkMobileFn() para verificar el número de teléfono móvil

métodos: { async checkUsernameFn() { //检查用户名let {datos} = esperar esto. Error de análisis de KaTeX: se esperaba 'EOF', se obtuvo '}' en la posición 88: …ata = data }̲, async che… request.checkMobile( this.user.mobile ) this.userMsg.mobileData = data } },





Paso 3: escriba las 2 variables requeridas
data() { return { usuario : { // datos del paquete de formulario         nombre de usuario : "",         móvil : "" }, mensaje de usuario : { // datos de solicitud de error nombre de usuario : "", datos móviles : " " } } },










Paso 4: Procesar la página

  • Versión completa

         


      
      


    4.4 Registro de Usuario: Pre-Tecnología – Redis

    4.5 Registro de Usuario: Pre-Tecnología – Ali Big Fish

    4.6 Registro de usuario: código de verificación SMS
    4.6.1 Análisis

    4.6.2接口
    http://localhost:10010/web-service/sms
    { “móvil”:“13344445555”, “nombre de usuario”: “jack” }


    4.6.3 Backend
    Cree la clase SmsController, llame a la clase de herramientas Ali Dayu y envíe SMS.

    paquete com.czxy.changgou4.controller;

    importar com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
    importar com.czxy.changgou4.pojo.User;
    importar com.czxy.changgou4.utils.SmsUtil;
    importar com.czxy.changgou4.vo.BaseResult;
    importar org.apache.commons.lang.RandomStringUtils;
    importar org.springframework.data.redis.core.StringRedisTemplate;
    importar org.springframework.http.ResponseEntity;
    importar org.springframework.web.bind.annotation.PostMapping;
    importar org.springframework.web.bind.annotation.RequestBody;
    importar org.springframework.web.bind.annotation.RequestMapping;
    importar org.springframework.web.bind.annotation.RestController;

    importar javax.anotación.Recurso;
    importar java.util.concurrent.TimeUnit;

    /**

    • Created by liangtong.
      */
      @RestController
      @RequestMapping(“/sms”)
      public class SmsController {

      @Resource
      private StringRedisTemplate redisTemplate;

      @PostMapping
      public BaseResult sendSms(@RequestBody User user){
      long start = System.currentTimeMillis();
      try {
      //发送短信
      //1 生产验证码
      String code = RandomStringUtils.randomNumeric(4);
      System.out.println(“验证码:” + code);

           //2 并存放到reids中 , key: "sms_register" + 手机号 , value:验证码 , 1小时
           redisTemplate.opsForValue().set( "sms_register" + user.getMobile() , code , 1 , TimeUnit.HOURS);
      
           /**/
           //3 发送短信
           SendSmsResponse smsResponse = SmsUtil.sendSms(user.getMobile(), user.getUsername() , code, "", "");
      
           //https://help.aliyun.com/document_detail/55284.html?spm=5176.doc55322.6.557.KvvIJx
           if("OK".equalsIgnoreCase(smsResponse.getCode())){
               return BaseResult.ok("发送成功");
           } else {
               return BaseResult.error(smsResponse.getMessage());
           }
      
           /*
           //模拟数据
           System.out.println("验证码:" + code);
           return BaseResult.ok("发送成功");
           */
       } catch (Exception e) {
           long end = System.currentTimeMillis();
           System.out.println( end - start);
           return BaseResult.error("发送失败" );
       }
      

      }
      }

    4.6.4前端
    步骤一:修改apiclient.js,发送短信ajax操作

    //发短信
    sendSms : ( user )=> {
    return axios.post(‘/web-service/sms’, user )
    }

    Paso 2: modifique la página Register.vue y vincule el evento de clic sendSmsFn al "enviar código de verificación"
    <button @click.prevent="sendSmsFn" >
    para enviar el código de verificación durante 5 segundos

    Paso 3: Modifique la página Register.vue y escriba la función sendSmsFn. Se recomienda usar ajax...then()...catch para manejar excepciones

    sendSmsFn () { this.$request.sendSms( this.user ) .then(( response )=>{ //Enviar mensaje mensaje this.userMsg.smsData = response.data }) .catch(( error )=> { / / Alerta de mensaje de error (mensaje de error) }) }









    Paso 4: Modifique la página Register.vue y proporcione la variable smsData

    userMsg : { //Datos de solicitud de error
    usernameData : "",
    mobileData : "",
    smsData : ""
    }

    Paso 5: Modifique la página Register.vue para mostrar información de solicitud de smsData

    { {mensaje de usuario.smsData.message}}

    4.6.5 Cuenta regresiva
    Paso 1: proporcione 3 variables para controlar la cuenta regresiva

      btnDisabled : false,  //倒计时控制变量
    

    segundos: 5, //
    temporizador de segundos de cuenta regresiva predeterminado: nulo, // temporizador de recepción, temporizador de borrado

    Paso 2: Controle la visualización de la cuenta regresiva en la etiqueta
    <button :disabled="btnDisabled" @click.prevent="sendSmsFn" >
    Envíe el código de verificación { {segundos}} segundos

    步骤三:发送短信后,开启倒计时控制
    sendSmsFn () {
    this.$request.sendSms( this.user )
    .then(( response )=>{
    //发送短信的提示信息
    this.userMsg.smsData = response.data
    //按钮不可用
    this.btnDisabled = true;
    //倒计时
    this.timer = setInterval( ()=>{
    if(this.seconds <= 1){
    //结束
    // 重置秒数
    this.seconds = 5;
    // 按钮可用
    this.btnDisabled = false;
    // 停止定时器
    clearInterval(this.timer);
    } else {
    this.seconds --;
    }
    } , 1000);
    })
    .catch(( error )=>{
    //错误提示信息
    alert( error.message )
    })
    }

    4.7用户注册
    4.7.1接口
    http://localhost:10010/web-service/user/register
    {
    “mobile”:“13612345677”,
    “password”:“1234”,
    “username”:“jack3”,
    “code”:“3919”
    }

    4.7.2后端
    保存前需要再次进行服务端校验
    用户名是否注册
    手机号是否注册
    验证码是否失效
    验证码是否错误
    密码需要使用 BCrypt进行加密

    步骤一:修改UserService接口,添加register方法
    /**

    • 用户注册
    • @param user
    • @return
      */
      public boolean register(User user) ;

    步骤二:完善UserServiceImpl实现类

    @Override
    public boolean register(User user) {
    //密码加密
    String newPassword = BCrypt.hashpw(user.getPassword());
    user.setPassword(newPassword);

    //处理数据
    user.setCreatedAt(new Date());
    user.setUpdatedAt(user.getCreatedAt());
    
    int insert = baseMapper.insert(user);
    
    return insert == 1;
    

    }

    步骤三:修改UserController,添加register方法

    /**

    • 用户注册

    • @param user

    • @return
      */
      @PostMapping(“/registro”)
      public BaseResult register(@RequestBody Usuario usuario){

      //
      Usuario de verificación del servidor findUser = userService.findByUsername(user.getUsername());
      if(findUser != null) { return BaseResult.error("El nombre de usuario ya existe"); }

      findUser = userService.findByMobile(user.getMobile());
      if(findUser != null) { return BaseResult.error("El número de teléfono ya existe"); }

      //Código de verificación
      Código de cadena = stringRedisTemplate.opsForValue().get("sms_register" + user.getMobile()); //
      Eliminar el código de verificación en redis
      stringRedisTemplate.delete("sms_register" + user.getMobile());
      if (código == nulo) { return BaseResult.error("El código de verificación no es válido"); } if(!code.equals(user.getCode())) { return BaseResult.error("El código de verificación es incorrecto") ; }




      //注册
      registro booleano = userService.register(usuario);

      if(registrar) { return BaseResult.ok("registro exitoso"); } return BaseResult.error("registro fallido"); }



    4.7.3 Procesamiento de fecha (opcional)
     Escriba DateMetaObjectHandler para procesar el
    paquete "hora de creación" y "fecha de modificación" com.czxy.changgou4.handler;

    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import org.apache.ibatis.reflection.MetaObject;
    import org.springframework.stereotype.Component;

    import java.util.Date;

    /**

    • @author 桐叔

    • @email [email protected]
      */
      @Component
      public class DateMetaObjectHandler implements MetaObjectHandler {
      @Override
      public void insertFill(MetaObject metaObject) {
      this.setFieldValByName(“createdAt”, new Date(), metaObject);
      this.setFieldValByName(“updatedAt”, new Date(), metaObject);
      }

      @Override
      public void updateFill(MetaObject metaObject) {
      this.setFieldValByName(“updatedAt”, new Date(), metaObject);
      }
      }

    完善User JavaBean,设置填充方式
    @TableField(value=“created_at”,fill = FieldFill.INSERT)
    private Date createdAt;

    @TableField(value=“updated_at”,fill = FieldFill.INSERT_UPDATE)
    private Date updatedAt;

    4.7.4前端
    步骤一:修改 api.js ,添加注册函数

    //注册
    register : ( user )=> {
    return axios.post(‘/web-service/user/register’, user )
    }

    步骤二:处理表单,验证码input绑定数据,提交按钮绑定事件


  •               
                  
                  <button :disabled=“btnDisabled” @click.prevent=“sendSmsFn” >
    发送验证码{ {seconds}}秒

    { {userMsg.smsData.message}}


                

  •             

  •               
                   我已阅读并同意《用户注册协议》
                

  •             

  •               
                  <input type=“submit” value=“” @click.prevent=“registerFn” class=“login_btn” />
                
  • 步骤三:完善data区域的user数据

      user : {  //表单封装数据
    

    username : “”, //用户名
            mobile : “13699282444”, //手机号
            password : “”, //密码
            code : “” //验证码
    },

    步骤四:编写registerFn函数
    async registerFn() {
    let { data } = await this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …功 this.router.push(‘/login’)
    } else {
    //失败–与发送验证码使用一个位置显示错误信息
    this.userMsg.smsData = data
    }
    }

    4.8用户登录
    4.8.1构建页面:Login.vue
    步骤一:创建Login.vue

    步骤二:绘制通用模块

    <div style="clear:both;"></div>
    <Footer></Footer>
    

    步骤三:绘制登录表单

    4.8.2分析

    4.8.3验证码:接口
    http://localhost:10010/web-service/verifycode?username=jack

    4.8.4 Código de verificación: generación y visualización
     Paso 1: código de verificación de producción back-end y guardar al usuario en Redis
    Almacene el formato de la clave del código de verificación en Redis: "iniciar sesión" + nombre de usuario

    paquete com.czxy.changgou4.controller;

    importar org.springframework.data.redis.core.StringRedisTemplate;
    importar org.springframework.stereotype.Controller;
    importar org.springframework.web.bind.annotation.GetMapping;
    importar org.springframework.web.bind.annotation.RequestMapping;

    importar javax.anotación.Recurso;
    importar javax.imageio.ImageIO;
    importar javax.servlet.http.HttpServletResponse;
    importar java.awt.*;
    importar java.awt.image.BufferedImage;
    importar java.io.IOException;
    importar java.util.Random;
    importar java.util.concurrent.TimeUnit;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Controller
      @RequestMapping(“/verifycode”)
      public class VerifyCodeController {

      @Resource
      private StringRedisTemplate stringRedisTemplate;

      @GetMapping
      public void verifyCode(String username , HttpServletResponse response ) throws IOException {

       //字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
       String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
      
       int IMG_WIDTH = 72;
       int IMG_HEIGTH = 27;
       Random random = new Random();
       //创建图片
       BufferedImage image = new BufferedImage(IMG_WIDTH, IMG_HEIGTH, BufferedImage.TYPE_INT_RGB);
       //画板
       Graphics g = image.getGraphics();
       //填充背景
       g.setColor(Color.WHITE);
       g.fillRect(1,1,IMG_WIDTH-2,IMG_HEIGTH-2);
      
       g.setFont(new Font("楷体", Font.BOLD,25));
      
       StringBuilder sb = new StringBuilder();
       //写字
       for(int i = 1 ; i <= 4 ; i ++){
           //随机颜色
           g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)));
           int len = random.nextInt(VERIFY_CODES.length());
           String str = VERIFY_CODES.substring(len,len+1);
           sb.append(str);
           g.drawString(str, IMG_WIDTH / 6 * i , 22 );
       }
      
       //将验证码存放到redis
       stringRedisTemplate.opsForValue().set( "login" + username , sb.toString() , 1 , TimeUnit.HOURS);
      
      
       // 生成随机干扰线
       for (int i = 0; i < 30; i++) {
           //随机颜色
           g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)));
           int x = random.nextInt(IMG_WIDTH - 1);
           int y = random.nextInt(IMG_HEIGTH - 1);
           int x1 = random.nextInt(12) + 1;
           int y1 = random.nextInt(6) + 1;
           g.drawLine(x, y, x - x1, y - y1);
       }
      
       //响应到浏览器
       ImageIO.write(image,"jpeg", response.getOutputStream());
      

      }
      }

    步骤二:点击“换一张”显示验证码
    默认不显示验证码
    点击“换一张”获得验证码

    4.8.5通过用户名查询:接口
    http://localhost:10010/web-service/user/findByUsername
    {
    “username”:“jack”
    }

    4.8.6通过用户名查询:实现
    修改UserController,添加 findByUsername函数

    /**

    • 通过用户名查询
    • @param user
    • @return devuelve el objeto de usuario
      */
      @PostMapping(“/findByUsername”)
      public User findByUsername(@RequestBody User user){ //Consulta usuario User findUser = userService.findByUsername( user.getUsername() ); return findUser; }



    4.8.7 Servicio de autenticación: Proyecto de compilación (changgou4-service-auth)
    Paso 1: Proyecto de compilación

    Paso 2: Cree un archivo pom.xml

    com.czxy.changgou changgou4_common_auth
    <!--web起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- nacos 客户端 -->
    <dependency>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-client</artifactId>
    </dependency>
    
    <!-- nacos 服务发现 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--redis-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
    </dependency>
    <!--swagger2-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
    </dependency>
    

    Paso 3: Cree un
    servidor de archivos yml:
    puerto: 8085
    spring:
    application:
    name: auth-service
    cloud:
    nacos:
    discovery:
    server-addr: 127.0.0.1:8848 #nacos service address

    sc:
    jwt:
    secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥
    pubKeyPath: D:/rsa/rsa.pub # 公钥地址
    priKeyPath: D:/rsa/rsa.pri # 私钥地址
    expire: 360 # 过期时间,单位分钟

    步骤四:配置启动类

    package com.czxy.changgou4;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;

    /**

    • @author 桐叔
    • @email [email protected]
      */
      @SpringBootApplication
      @EnableDiscoveryClient
      @EnableFeignClients
      public class CGAuthServiceApplication { public static void main(String[] args) { SpringApplication.run(CGAuthServiceApplication.class, args); } }



    Paso 5: Clase de configuración

    4.8.8 Servicio de autenticación: Backend de inicio de sesión de usuario
    Paso 1: Crear objeto de paquete AuthUser (en comparación con Usuario, falta de comentarios relacionados con la base de datos)
    paquete com.czxy.changgou4.domain;

    importar lombok.Datos;

    importar java.util.Date;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Data
      public class AuthUser { private Long id;

      nombre de usuario de cadena privado;

      contraseña de cadena privada;

      cara de cadena privada;

      experiencia de entero privado;

      correo electrónico privado de cadena;

      móvil de cadena privado;

      Fecha de creación privada en;

      private Date updatedAt;

      private String code;

      private String password_confirm;
      }

    步骤二:创建UserFeign,完成远程用户查询功能
    package com.czxy.changgou4.feign;

    import com.czxy.changgou4.domain.AuthUser;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;

    /**

    • @author 桐叔

    • @email [email protected]
      */
      @FeignClient(value = “web-service”,path=“/user”)
      public interface UserFeign {

      @PostMapping(“/findByUsername”)
      public AuthUser findByUsername(@RequestBody AuthUser user);

    }

    步骤三:创建AuthService接口,编写登录方法 login()
    package com.czxy.changgou4.service;

    importar com.czxy.changgou4.dominio.AuthUser;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      interfaz pública AuthService {

      /**

      • Inicio de sesión de usuario
      • usuario @param
      • @return
        */
        inicio de sesión de AuthUser público (usuario de AuthUser);
        }

    Paso 4: cree una clase de implementación de AuthService y verifique la contraseña a través de BCrypt

    paquete com.czxy.changgou4.service.impl;

    importar com.czxy.changgou4.dominio.AuthUser;
    importar com.czxy.changgou4.feign.UserFing;
    importar com.czxy.changgou4.service.AuthService;
    importar com.czxy.changgou4.utils.BCrypt;
    importar org.springframework.stereotype.Service;

    importar javax.anotación.Recurso;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Service
      public class AuthServiceImpl implements AuthService { @Resource private UserFeign userFeign;

      /**

      • Inicio de sesión de usuario
      • usuario @param
      • @return
        */
        public AuthUser login(AuthUser user ) {
        //远程查询用户
        AuthUser findUser = userFeign.findByUsername(user);
        if(findUser == null) {
        return null;
        }
        //校验密码是否正确
        boolean checkpw = BCrypt.checkpw( user.getPassword(), findUser.getPassword());
        if(checkpw){
        return findUser;
        }
        return null;
        }

    }

    步骤五:创建AuthController,添加login方法
    redis中登录验证码和用户输入的验证码进行匹配

    package com.czxy.changgou4.controller;

    /**

    import com.czxy.changgou4.domain.AuthUser;
    import com.czxy.changgou4.service.AuthService;
    import com.czxy.changgou4.vo.BaseResult;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import javax.annotation.Resource;

    /**

    • Created by liangtong.
      */
      @RestController
      @RequestMapping(“/auth”)
      public class AuthController {

      @Resource
      private AuthService authService;

      @Resource
      privado StringRedisTemplate stringRedisTemplate;

      @PostMapping(“/login”)
      public BaseResult login(@RequestBody AuthUser user){
      //校验验证码–使用后删除
      String redisCode = stringRedisTemplate.opsForValue().get( “login” + user.getUsername() );
      stringRedisTemplate.delete( “login” + user.getUsername() );
      if(redisCode == null) {
      return BaseResult.error(“验证码无效”);
      }
      if(! redisCode.equalsIgnoreCase(user.getCode())) {
      return BaseResult.error(“验证码错误”);
      }
      //登录
      AuthUser loginUser = authService.login(user);
      if(loginUser != null ) {
      return BaseResult.ok(“登录成功”).append(“loginUser”,loginUser);
      } else {
      return BaseResult.error(“用户名或密码不匹配”);
      }
      }
      }

    4.8.9 Servicio de autenticación: Front-end de inicio de sesión de usuario
    Paso 1: Modifique apiclient.js, agregue la función de inicio de sesión
    //iniciar
    sesión: (usuario)=> { return axios.post('/auth-service/auth/login', usuario) }

    Paso 2: Modifique Login.vue y vincule las variables al código de verificación

  • Paso 3: modifique Login.vue para vincular un evento al botón de envío

  • Paso 4: escriba loginFn para completar la función de inicio de sesión
    Inicio de sesión exitoso, salte a la página de inicio
    No se pudo iniciar sesión, proporcione un mensaje
    async loginFn() { let { data } = espere esto Error de análisis de KaTeX: esperado '}', obtenido 'EOF' al final de la entrada: ... página this.router.push ('/') } else { this.errorMsg = data.message } }




    Paso 5: Cree la página de inicio ~/pages/index.vue,

    Paso 6: Cambie el nombre de la página estática ~/static/index.html a ~/static/home.html

    4.8.10 Modificar el componente TopNav.vue

     Mejore la barra de navegación y muestre contenido diferente según los datos en vuex

    Paso 1: Crear ~/store/index.js y escribir contenido vuex

    exportar estado constante = () => ({ usuario: nulo })

    //Configuración general
    exportar const mutaciones = { setData( state , obj ) { state[obj.key] = obj.value } }



    Paso 2: el inicio de sesión de la página es exitoso y la información del usuario se guarda en vuex

    // Guarda la información del usuario en vuex
    this.$store.commit('setData', {key:'user',value: data.data })

    Paso 3: Modificar la navegación superior TopNav.vue
    Datos de vuex

      
        
          


          


            

    •           
    • Hola, { {user.username}} ¡Bienvenido a Changgou! <a href="" @click.prevent="cerrar sesión">Cerrar sesión

    • |

    •           
    • [ Iniciar sesión ] [ Registro gratuito ]

    •           
    • |

    •           
    • Mi pedido

    •           
    • |

    •           
    • Servicio al Cliente


          
        
      
      

    4.8.11 Pérdida de datos de actualización de Vuex
    Operación de actualización:
    Haga clic en el botón Actualizar
    Haga clic en el botón Atrás
    Ingrese la dirección directamente en la barra de direcciones
    Fenómeno:
    Después de la operación de actualización de Vuex, los datos se pierden
    Solución Solución
    1: No es un componente público: página En el directorio de páginas, nuxt.js puede proporcionar búsqueda para operar.
    Esquema 2: Es un componente público: el componente se almacena en el directorio de componentes con la ayuda de un tercero (cookie, localStorage, sessionStorage)
    Operaciones específicas:
    Si no hay datos en vuex, use los datos de sessionStorage para completar vuex.
    Modificar la página TopNav.vue

      
        
          


          


            

    • Hola, { {user.username}}¡Bienvenido a Changgou!

    •           

    • [Acceso]


    • [Registro gratis]


    • [abandonar]

    •           
    • |

    •           
    • Mi pedido

    •           
    • |

    •           
    • Servicio al Cliente

    •         

          

        
      
      

    4.9 Integrar JWT
     Generar token: después de que el usuario inicie sesión correctamente, genere un token de identificación de inicio de sesión de acuerdo con la información de inicio de sesión del usuario y devuélvalo al navegador.
    Use token: mejore la solicitud ajax, agregue encabezados de solicitud antes de la solicitud y configure el token
    Verify token: escriba un filtro en la puerta de enlace, intercepte la solicitud y verifique el token.
    Lista blanca: se puede acceder directamente a las solicitudes en la lista blanca sin token.
    4.9.1 Generar token
    El usuario inicia sesión correctamente, genera un token y envía la respuesta del token al navegador. (Servicio de autenticación AuthService)
    Paso 1: verifique el archivo application.yml para determinar la información de configuración de jwt

    步骤二:创建JwtProperties文件,用于加载sc.jwt配置信息

    package com.czxy.changgou4.config;

    import com.czxy.changgou4.utils.RsaUtils;
    import lombok.Data;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;

    import javax.annotation.PostConstruct;
    import java.io.File;
    import java.security.PrivateKey;
    import java.security.PublicKey;

    /**

    • @author 桐叔

    • @email [email protected]
      */
      @Data
      @ConfigurationProperties(prefix = “sc.jwt”)
      @Component
      public class JwtProperties {

      private String secret; // 密钥

      private String pubKeyPath;// 公钥

      private String priKeyPath;// 私钥

      expiración int privada;//hora de expiración del token

      clave pública privada clave pública; // 公钥

      private PrivateKey privateKey; // clave privada

      registrador registrador final estático privado = LoggerFactory.getLogger(JwtProperties.class);

      @PostConstruct
      public void init(){ try { File pubFile = new File(this.pubKeyPath); Archivo priFile = nuevo archivo (this.priKeyPath); if( !pubFile.exists() || !priFile.exists()){ RsaUtils.generateKey( this.pubKeyPath ,this.priKeyPath , this.secret); } this.publicKey = RsaUtils.getPublicKey( this.pubKeyPath ); this.privateKey = RsaUtils.getPrivateKey( this.priKeyPath ); } catch (Excepción e) { throw new RuntimeException(e.getMessage()); } }











    }
    步骤三:修改AuthController,注入JwtProperties,并使用JwtUtils生成token
    package com.czxy.changgou4.controller;

    /**

    import com.czxy.changgou4.config.JwtProperties;
    import com.czxy.changgou4.domain.AuthUser;
    import com.czxy.changgou4.service.AuthService;
    import com.czxy.changgou4.utils.JwtUtils;
    import com.czxy.changgou4.vo.BaseResult;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import javax.annotation.Resource;

    /**

    • Created by liangtong.
      */
      @RestController
      @RequestMapping(“/auth”)
      public class AuthController {

      @Resource
      private AuthService authService;

      @Resource
      private StringRedisTemplate stringRedisTemplate;

      @Resource
      private JwtProperties jwtProperties;

      @PostMapping(“/login”)
      inicio de sesión público de BaseResult(@RequestBody AuthUser user){ //Código de verificación de verificación: eliminar después de usar String redisCode = stringRedisTemplate.opsForValue().get(“login” + user.getUsername() ); stringRedisTemplate .delete( "iniciar sesión" + usuario.getUsername() ); if(redisCode == null) { return BaseResult.error("Código de verificación no válido"); } if(! redisCode.equalsIgnoreCase(user.getCode())) { return BaseResult.error("Error de código de verificación"); } //Iniciar sesión Usuario auth loginUser = authService.login(usuario); if(loginUser != null ) { //Generar Token String token = JwtUtils.generateToken(loginUser, jwtProperties. getExpire (), jwtProperties.getPrivateKey());













           return BaseResult.ok("登录成功").append("loginUser",loginUser).append("token", token);
       } else {
           return BaseResult.error("用户名或密码不匹配");
       }
      

      }
      }

    4.9.2 Uso del token
    Paso 1: guarde el token después de iniciar sesión correctamente y modifique la página Login.vue

    async loginFn() {
      let { data } = await this.$request.login( this.user )
      if( data.code == 20000) {
        //成功
        sessionStorage.setItem('user' , JSON.stringify(data.other.loginUser) )
        //保存token
        sessionStorage.setItem('token' , data.other.token )
        //跳转到首页
        this.$router.push('/')
      } else {
        this.errorMsg = data.message
      }
    }
    

    Paso 2: la solicitud lleva automáticamente el token, modifica apiclient.js y agrega el token al encabezado de la solicitud

    //参考 https://axios.nuxtjs.org/helpers
    let token = sessionStorage.getItem('token')
    if( token ) { // Agrega encabezado: a todas las solicitudes // this.$axios.setToken('123' ) $axios.setToken( token ) }
    Authorization: 123


    Paso 3: verifique nuxt.conf.js, cambie el modo del complemento a "cliente"
    De lo contrario, lance una excepción "el almacenamiento de sesión no está definido"
    complementos: [
    { src: '~plugins/apiclient.js', modo: ' cliente' }
    ] ,

    4.9.3 Verificación
    del token La verificación del token se completa en el proyecto de puerta de enlace
    Paso 1: Modifique application.yml para agregar la configuración de jwt

    #Contenido personalizado
    sc:
    jwt:
    secret: sc@Login(Auth}*^31)&czxy% # Clave de verificación de inicio de sesión
    pubKeyPath: D:/rsa/rsa.pub # Dirección de clave pública
    priKeyPath: D:/rsa/ rsa.pri # Caducidad de la dirección de clave privada
    : 360 # Tiempo de caducidad, en minutos

    Paso 2: Crear JwtProperties para cargar archivos de configuración

    paquete com.czxy.changgou4.config;

    importar com.czxy.changgou4.utils.RsaUtils;
    importar lombok.Datos;
    importar org.slf4j.Logger;
    importar org.slf4j.LoggerFactory;
    importar org.springframework.boot.context.properties.ConfigurationProperties;
    importar org.springframework.stereotype.Component;

    importar javax.anotación.PostConstruct;
    importar java.io.Archivo;
    importar java.seguridad.PrivateKey;
    importar java.seguridad.PublicKey;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Data
      @ConfigurationProperties(prefix = “sc.jwt”)
      public class JwtProperties {

      private String secret; // 密钥

      private String pubKeyPath;// 公钥

      private String priKeyPath;// 私钥

      private int expire;// token过期时间

      private PublicKey publicKey; // 公钥

      private PrivateKey privateKey; // 私钥

      private static final Logger logger = LoggerFactory.getLogger(JwtProperties.class);

      @PostConstruct
      public void init(){ try { File pubFile = new File(this.pubKeyPath); Archivo priFile = nuevo archivo (this.priKeyPath); if( !pubFile.exists() || !priFile.exists()){ RsaUtils.generateKey( this.pubKeyPath ,this.priKeyPath , this.secret); } this.publicKey = RsaUtils.getPublicKey( this.pubKeyPath ); this.privateKey = RsaUtils.getPrivateKey( this.priKeyPath ); } catch (Excepción e) { throw new RuntimeException(e.getMessage()); } }











    }

    Paso 3: escriba un filtro para interceptar todas las rutas

    paquete com.czxy.changgou4.filter;

    import com.czxy.changgou4.config.FilterProperties;
    import com.czxy.changgou4.config.JwtProperties;
    import com.czxy.changgou4.pojo.User;
    import com.czxy.changgou4.utils.JwtUtils;
    import com.czxy.changgou4.utils.RsaUtils;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    importar org.springframework.http.server.reactive.ServerHttpResponse;
    importar org.springframework.stereotype.Component;
    importar org.springframework.web.server.ServerWebExchange;
    importar reactor.core.publisher.Flux;
    importar reactor.core.publisher.Mono;

    importar javax.anotación.Recurso;
    importar java.nio.charset.StandardCharsets;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Component
      public class LoginFilter implementa GlobalFilter, Ordered {

      @Resource
      privado JwtProperties jwtProperties;

      @Override
      public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { //1 获得请求路径ServerHttpRequest request = exchange.getRequest(); Ruta de cadena = solicitud.getURI().getPath(); System.out.println(ruta);



       //2 白名单放行
      
      
       //3 获得token
       String token = request.getHeaders().getFirst("Authorization");
       //4 校验token
       try {
           JwtUtils.getObjectFromToken(token, RsaUtils.getPublicKey(jwtProperties.getPubKeyPath()), User.class);
           return chain.filter(exchange);
       } catch (Exception e) {
           ServerHttpResponse response = exchange.getResponse();
           response.setStatusCode(HttpStatus.UNAUTHORIZED);
           response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
           DataBuffer wrap = response.bufferFactory().wrap("没有权限".getBytes(StandardCharsets.UTF_8));
           return exchange.getResponse().writeWith(Flux.just(wrap));
       }
      

      }

      @Override
      public int getOrder() { return 1; } }


    Paso 4: Modifique el archivo front-end apiclient.js para manejar las excepciones 401

    // Manejar la excepción de respuesta
    $axios.onError(error => { // token no válido, el servidor responde 401 if(error.response.status === 401) { console.error(error.response.data) redirect('/login ') } })





    api.js código completo

    var axios =
    defecto de exportación nulo ({ $axios, redirigir, procesar }, inyectar) => {

    //参考 https://axios.nuxtjs.org/helpers
    let token = sessionStorage.getItem(‘token’)
    if( token ) {
    // Adds header: Authorization: 123 to all requests
    // this.$axios.setToken(‘123’)
    $axios.setToken( token )
    }

    //处理响应异常
    $axios.onError(error => {
    // token失效,服务器响应401
    if(error.response.status === 401) {
    console.error(error.response.data)
    redirect(‘/login’)
    }
    })

    //赋值
    axios = $axios

    //4) 将自定义函数交于nuxt
    // 使用方式1:在vue中,this. r e q u e s t . x x x ( ) / / 使用方式 2 :在 n u x t 的 a s y n c D a t a 中, c o n t e n t . a p p . request.xxx() // 使用方式2:在nuxt的asyncData中,content.app. request.xxx()//使用方式2:在nuxtasyncData中,content.app.request.xxx()
    inject(‘request’, request)
    }

    4.9.4白名单
    不需要拦截的资源都配置到yml文件中,在过滤器直接放行
    步骤一:修改application.yml文件

    #Contenido personalizado
    sc:
    jwt:
    secret: sc@Login(Auth}*^31)&czxy% # Clave de verificación de inicio de sesión
    pubKeyPath: D:/rsa/rsa.pub # Dirección de clave pública
    priKeyPath: D:/rsa/ rsa.pri # Caducidad de la dirección de clave privada
    : 360 # Tiempo de caducidad, en minutos
    filtro:
    allowPaths:
    - /checkusername
    - /checkmobile
    - /sms
    - /register
    - /login

    • /código de verificación
      • /categorias
      • /noticias
      • /marcas
      • /especificaciones
      • /buscar
      • /bienes
      • /comentarios
    • pavonearse
    • /api-docs

    Paso 2: Cree un archivo de configuración de FilterProperties para almacenar las rutas permitidas

    paquete com.czxy.changgou4.config;

    importar lombok.Datos;
    importar org.springframework.boot.context.properties.ConfigurationProperties;

    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Data
      @ConfigurationProperties(prefix=“sc.filter”)
      public class FilterProperties {

      //Permitir el acceso a la colección de rutas
      lista privada allowPaths;
      }

    Paso 3: Modifique LoginFilter, el paquete de ruta com.czxy.changgou4.filter configurado en la lista de versiones
    ;

    importar com.czxy.changgou4.config.FilterProperties;
    importar com.czxy.changgou4.config.JwtProperties;
    importar com.czxy.changgou4.pojo.User;
    importar com.czxy.changgou4.utils.JwtUtils;
    importar com.czxy.changgou4.utils.RsaUtils;
    importar org.springframework.boot.context.properties.EnableConfigurationProperties;
    importar org.springframework.cloud.gateway.filter.GatewayFilterChain;
    importar org.springframework.cloud.gateway.filter.GlobalFilter;
    importar org.springframework.core.Ordered;
    importar org.springframework.core.io.buffer.DataBuffer;
    importar org.springframework.http.HttpStatus;
    importar org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;

    import javax.annotation.Resource;
    import java.nio.charset.StandardCharsets;

    /**

    • @author 桐叔

    • @email [email protected]
      */
      @Component
      //2.1 加载JWT配置类
      @EnableConfigurationProperties({FilterProperties.class} ) //加载配置类
      public class LoginFilter implements GlobalFilter, Ordered {

      @Resource
      private FilterProperties filterProperties;

      @Resource
      private JwtProperties jwtProperties;

      @Override
      public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { //1 获得请求路径ServerHttpRequest request = exchange.getRequest(); Ruta de cadena = solicitud.getURI().getPath(); System.out.println(ruta);



       //2 白名单放行
       for (String allowPath  : filterProperties.getAllowPaths()) {
           //判断包含
           if(path.contains(allowPath)){
               return chain.filter(exchange);
           }
       }
      
      
      
       //3 获得token
       String token = request.getHeaders().getFirst("Authorization");
       //4 校验token
       try {
           JwtUtils.getObjectFromToken(token, RsaUtils.getPublicKey(jwtProperties.getPubKeyPath()), User.class);
           return chain.filter(exchange);
       } catch (Exception e) {
           ServerHttpResponse response = exchange.getResponse();
           response.setStatusCode(HttpStatus.UNAUTHORIZED);
           response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
           DataBuffer wrap = response.bufferFactory().wrap("没有权限".getBytes(StandardCharsets.UTF_8));
           return exchange.getResponse().writeWith(Flux.just(wrap));
       }
      

      }

      @Override
      public int getOrder() { return 1; } }


    5. Módulo de la página de inicio
    5.1 Construyendo la página de inicio
     Paso 1: Crear la página ~/pages/index.vue

    Paso 2: copie la página estática index.html en index.vue y modifique los métodos de referencia js y css

    Paso 3: Introducir los componentes públicos existentes

    <!-- 头部 start -->
    <!-- 省略 html原有内容  -->
    <!-- 底部导航 end -->
    
    <div style="clear:both;"></div>
    <!-- 底部版权 start -->
    <Footer></Footer>
    <!-- 底部版权 end -->
    

    Paso 4: Extraiga todas las "cabezas" al componente "HeaderSearch"
    1) Modifique la ruta de la imagen

    Paso 5: extraiga todos los componentes de "navegación inferior" de "BottomNav"

    Paso 6: Modifique ~/pages/index.vue, agregue los componentes "HeaderSeach" y "BottomNav"

    <!-- 头部 start -->
    <HeaderSearch></HeaderSearch>
    <!-- 头部 end-->
    
    <div style="clear:both;"></div>
    
    <!-- 
    	省略 html原有内容 
    -->
    
    <div style="clear:both;"></div>
    
    <!-- 底部导航 start -->
    <BottomNav></BottomNav>
    <!-- 底部导航 end -->
    
    <div style="clear:both;"></div>
    <!-- 底部版权 start -->
    <Footer></Footer>
    <!-- 底部版权 end -->
    

    5.2 Express
    5.2.1 Interfaz
    OBTENER http://localhost:10010/web-service/news?pageNum=1&pageSize=5&sortWay=desc

    5.2.2 Implementación de back-end: JavaBean

    步骤一:修改pojo项目,创建分页基类 PageRequest
    package com.czxy.changgou4.vo;

    import lombok.Data;

    /**

    • @author 桐叔
    • @email [email protected]
      */
      @Data
      public class PageRequest {
      private Integer pageNum; //当前页
      private Integer pageSize; //每页条数
      private Integer limit; //限制条数
      private Integer offset; //偏移
      private String sortBy; //排序字段
      private String sortWay; //排序方式(asc | desc)
      }

    步骤二:修改pojo项目,创建NewsVo类,继承PageRequest类
    NewsVo在PageRequest基础上,进行特殊定制。如果不需要任何增强,可以直接使用PageRequest
    package com.czxy.changgou4.vo;

    import lombok.Data;

    /**

    • @author 桐叔
    • @email [email protected]
      */
      @Data
      public class NewsVo extends PageRequest {
      }

    步骤三:根据数据库表创建News对象
    package com.czxy.changgou4.pojo;

    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;

    import java.util.Date;

    /**

    • @author 桐叔

    • @email [email protected]
      */
      @TableName(“tb_news”)
      @Data
      public class News {
      @TableId
      private Integer id;

      @TableField(value = “title”)
      private String title;

      @TableField(value = “content”)
      private String content;

      @TableField(value = “author”)
      private String author;

      @TableField(value = “created_at”)
      private Date createdAt;

      @TableField(valor = “actualizado_en”)
      privado Fecha actualizado en;

    }

    5.2.3 Implementación backend: función de consulta

    Paso 1: Cree
    el paquete del mapeador com.czxy.changgou4.mapper;

    importar com.baomidou.mybatisplus.core.mapper.BaseMapper;
    importar com.czxy.changgou4.pojo.Noticias;
    importar org.apache.ibatis.annotations.Mapper;

    /**

    • @autor Tío Tong
    • @email [email protected]
      */
      @Mapper
      interfaz pública NewsMapper extiende BaseMapper { }

    Paso 2: Cree una interfaz de servicio

    paquete com.czxy.changgou4.servicio;

    importar com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    importar com.baomidou.mybatisplus.extension.service.IService;
    importar com.czxy.changgou4.pojo.Noticias;
    importar com.czxy.changgou4.vo.NewsVo;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      interfaz pública NewsService extiende IService {

      /**

      • revisa todas las noticias
      • @param noticiasVo
      • @return
        */
        página pública findAll(NewsVo newsVo);
        }

    Paso 3: Cree una clase de implementación de servicio y clasifíquela según el tiempo de creación y el método de clasificación

    paquete com.czxy.changgou4.service.impl;

    importar com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    importar com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    importar com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    importar com.czxy.changgou4.mapper.NewsMapper;
    importar com.czxy.changgou4.pojo.Noticias;
    importar com.czxy.changgou4.service.NewsService;
    importar com.czxy.changgou4.vo.NewsVo;
    importar org.springframework.stereotype.Service;
    importar org.springframework.transaction.annotation.Transactional;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Service
      @Transactional
      public class NewsServiceImpl extends ServiceImpl<NewsMapper, News> implements NewsService {
      @Override
      public Page findAll(NewsVo newsVo) {
      //1 条件
      QueryWrapper queryWrapper = new QueryWrapper();
      if(“asc”.equals(newsVo.getSortWay())) {
      queryWrapper.orderByAsc(“created_at”);
      } else {
      queryWrapper.orderByDesc(“created_at”);
      }

       //2 分页
       Page<News> page = new Page<>(newsVo.getPageNum(), newsVo.getPageSize());
      
       //3 查询
       baseMapper.selectPage(page, queryWrapper);
      
       return page;
      

      }
      }

    步骤四:修改controller

    package com.czxy.changgou4.controller;

    importar com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    importar com.czxy.changgou4.pojo.Noticias;
    importar com.czxy.changgou4.service.NewsService;
    importar com.czxy.changgou4.vo.BaseResult;
    importar com.czxy.changgou4.vo.NewsVo;
    importar org.springframework.web.bind.annotation.GetMapping;
    importar org.springframework.web.bind.annotation.RequestMapping;
    importar org.springframework.web.bind.annotation.RestController;

    importar javax.anotación.Recurso;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @RestController
      @RequestMapping(“/noticias”)
      public class NewsController {

      @Resource
      servicio de noticias privado servicio de noticias;

      @GetMapping
      public BaseResult findAll(NewsVo newsVo){
      //1 查询
      Page page = this.newsService.findAll( newsVo );
      //2 封装
      return BaseResult.ok(“成功”, page);
      }

    }

    5.2.4前端实现
    步骤一:编写服务端apiserver.js

    步骤二:编写服务端apiserver.js,修改nuxt.config.js 配置仅服务器可用

    plugins: [
    { src: ‘~plugins/apiclient.js’,mode: ‘client’},
    { src: ‘~plugins/apiserver.js’},
    ],

    步骤三:修改“apiserver.js”,查询快报
    const request = {
    //快报
    findNews : () => {
    return axios.get(‘/web-service/news’ , {
    params : {
    pageNum : 1,
    pageSize : 8 ,
    sortWay : ‘asc’
    }
    })
    }
    }

    var axios = null
    export default ({ $axios, redirect, process }, inject) => {

    //赋值
    axios = $axios

    //4) 将自定义函数交于nuxt
    // 使用方式1:在vue中,this. r e q u e s t . x x x ( ) / / 使用方式 2 :在 n u x t 的 a s y n c D a t a 中, c o n t e n t . a p p . request.xxx() // 使用方式2:在nuxt的asyncData中,content.app. request.xxx()//使用方式2:在nuxtasyncData中,content.app.requestServer.xxx()
    inject(‘requestServer’, request)
    }

    步骤四:修改首页 index.vue,服务端查询快报

    async asyncData( { app } ) {
    let { data } = await app.$requestServer.findNews()

    return {
      newsList : data.data.records
    }
    

    },

    步骤五:遍历数据

            <li v-for="(n,index) in newsList" :title="n.title" :key="index" :class="{'odd': index%2==0 }">
    

    { {n.title}}
                
    步骤六:优化,超过的字符串显示“…”

    5.3 Categoría
    5.3.1 Interfaz
    GET http://localhost:10010/web-service/categorys
    { "código": 1, "mensaje": "consulta exitosa", "datos": [ { "id": 1, "niños ": [ { "id": 2, "hijos": [ { "id": 3, "hijos": [], "cat_name": "eBook", "parent_id": 2, "is_parent": false } , { "id": 4, "niños": [], "cat_name": "Original de Internet", "parent_id": 2, "is_parent": false } ], "cat_name": "e-book", "parent_id" : 1, "es_padre": verdadero } ],





























    “cat_name”: “图书、音像、电子书刊”,
    “parent_id”: 0,
    “is_parent”: true
    }
    ],
    “other”: {}
    }

    5.3.2后端实现:JavaBean
    package com.czxy.changgou4.pojo;

    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import lombok.Data;

    import java.util.ArrayList;
    import java.util.List;

    /**

    • @author 桐叔

    • @email [email protected]
      */
      @TableName(“tb_category”)
      @Data
      public class Category {

      @TableId
      private Integer id;

      @TableField(value = “cat_name”)
      @JsonProperty(“cat_name”)
      private String catName;

      @TableField(value = “parent_id”)
      @JsonProperty(“parent_id”)
      private Integer parentId;

      @TableField(value = “is_parent”)
      @JsonProperty(“is_parent”)
      private Boolean isParent;

      //当前分类具有的所有孩子
      @TableField(exist = false)
      @JsonInclude(JsonInclude.Include.NON_EMPTY)
      private List children = new ArrayList<>();

    }

    5.3.3后端实现:查询

    步骤一:创建mapper
    package com.czxy.changgou4.mapper;

    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.czxy.changgou4.pojo.Category;
    import org.apache.ibatis.annotations.Mapper;

    /**

    • @author 桐叔
    • @email [email protected]
      */
      @Mapper
      interfaz pública CategoryMapper extiende BaseMapper { }

    Paso 2: Cree una interfaz de servicio, agregue
    el paquete de consulta de todos los métodos com.czxy.changgou4.service;

    importar com.baomidou.mybatisplus.extension.service.IService;
    importar com.czxy.changgou4.pojo.Categoría;

    importar java.util.List;

    /**

    • @autor Tío Tong
    • @email [email protected]
      /
      interfaz pública CategoryService extiende IService { /
      *
      • Consulta todas las categorías de primer nivel, cada categoría contiene subcategorías
      • @return
        */
        lista pública findAll();
        }

    Paso 3: Cree una clase de implementación de servicio, ordene en orden ascendente por parentId y procese todas las clasificaciones por nivel.

    paquete com.czxy.changgou4.service.impl;

    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.czxy.changgou4.mapper.CategoryMapper;
    import com.czxy.changgou4.pojo.Category;
    import com.czxy.changgou4.service.CategoryService;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    /**

    • @author 桐叔

    • @email [email protected]
      */
      @Service
      @Transactional
      public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
      @Override
      public List findAll() {
      //1 条件
      QueryWrapper queryWrapper = new QueryWrapper<>();
      queryWrapper.orderByAsc(“parent_id”);

       //2 查询所有
       List<Category> findList = baseMapper.selectList(queryWrapper);
      
       //2 存放所有的一级分类
       List<Category> list = new ArrayList<>();
       // 3.1 存放每一个分类(此分类将来要做父分类的)
       Map<Integer , Category> cache = new HashMap<>();
       for (Category category : findList) {
           //2.1 过滤所有一级 parentId==0
           if(category.getParentId() == 0){
               list.add( category );
           }
      
           //3.2 向map存一份
           cache.put( category.getId() , category );
      
           //3.3 获得当前父分类
           Category parentCategory = cache.get( category.getParentId() );
           if( parentCategory != null) {
               parentCategory.getChildren().add( category );
           }
      
       }
      
       //2.2 返回处理结果
       return list;
      

      }
      }

    步骤四:创建controller

    package com.czxy.changgou4.controller;

    importar com.czxy.changgou4.pojo.Categoría;
    importar com.czxy.changgou4.service.CategoryService;
    importar com.czxy.changgou4.vo.BaseResult;
    importar org.springframework.web.bind.annotation.GetMapping;
    importar org.springframework.web.bind.annotation.RequestMapping;
    importar org.springframework.web.bind.annotation.RestController;

    importar javax.anotación.Recurso;
    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @RestController
      @RequestMapping(“/categorys”)
      public class CategoryController {

      @Resource
      privado CategoríaServicio categoríaServicio;

      @GetMapping
      public BaseResult findAll(){
      //1 查询
      List list = categoryService.findAll();
      //2 封装
      return BaseResult.ok(“查询成功” , list );
      }

    }

    5.3.4前端实现
    步骤一:修改“apiserver.js”,查询所有分类

    //查询所有分类
    findCategorys : () => {
    return axios.get(“/web-service/categorys”)
    }

    步骤二:修改index.vue,使用asyncData查询分类
    asyncData函数只能在 pages 目录下使用

    async asyncData( { app } ) {
    // 查询快报 + 查询分类
    let [{data:newsData}, {data: categoryData}] = await Promise.all([
    app. r e q u e s t S e r v e r . f i n d N e w s ( ) , a p p . requestServer.findNews(), app. requestServer.findNews(),app.requestServer.findCategorys()
    ])
    return {
    newsList : newsData.data.records,
    categorysList : categoryData.data
    }

    },

    步骤三:修改index.vue,将查询的结果,通过属性传递给组件

    <!-- 头部 start -->
    <HeaderSearch :list="categorysList"></HeaderSearch>
    <!-- 头部 end-->
    

    步骤四:修改组件,声明list属性

    export default {
    props: {
    isFirst: { //是否是首页
    type: Boolean,
    default: true
    },
    list: { //数据
    type: Array
    }

    }
    }

    步骤五:遍历数据

    6.搜索模块
    6.1构建列表页
    步骤一:创建 ~/pages/list/_cid.vue 页面

    步骤二:复制静态list.html页面到 list/_cid.vue中,并修改js和css引用方式
    1)修改页面,完善标签

    2)替换图片路径

    <!-- 
    	省略 html原有内容 
    -->
    
    <!-- 底部版权 end -->
    

    步骤三:添加已有组件

    <div style="clear:both;"></div>
    
    <!-- 头部 start -->
    <HeaderSearch></HeaderSearch>
    <!-- 头部 end-->
    
    <div style="clear:both;"></div>
    
    <!-- 列表主体 start -->
    <!-- 省略 html原有内容 -->
    <!-- 列表主体 end-->
    
    <div style="clear:both;"></div>
    <!-- 底部导航 start -->
    <BottomNav></BottomNav>
    <!-- 底部导航 end -->
    
    <div style="clear:both;"></div>
    <!-- 底部版权 start -->
    <Footer></Footer>
    <!-- 底部版权 end -->
    

    6.2优化组件HeaderSearch组件
    非首页“HeaderSearch”组件,不能隐藏分类

    步骤一:修改HeaderSearch组件,添加props
    export default {
    props: {
    isFirst: { //是否是首页
    type: Boolean,
    default: true
    }
    }
    }

    步骤二:如果不在首页时,在指定的3处添加样式

    步骤三:修改 ~/pages/list/_id.vue ,将HeaderSearch组件的isFirst属性修改成false

    步骤四:显示分类数据

    <HeaderSearch :isFirst="false" :list="categorysList"></HeaderSearch>
    <!-- 头部 end-->
    

    async asyncData( { app } ) { // Query Express + Query Category let [{data: categoryData}] = await Promise.all([ app.$requestServer.findCategorys() ]) return { categoriasList : categoryData.data } }







    Paso 5: Modificar la ruta de acceso al logotipo

    6.3 Todas las marcas en la categoría especificada
    6.3.1 Interfaz
    GET http://localhost:10010/web-service/brands/category/76

    6.3.2 Implementación backend: JavaBean
    Análisis de la estructura de la tabla:
    Tabla de categorías: tb_category
    Tabla de marcas: tb_brand
    Relación: muchos a muchos (diferentes categorías tienen diferentes marcas) tb_category_brand

    paquete com.czxy.changgou4.pojo;

    importar com.baomidou.mybatisplus.annotation.IdType;
    importar com.baomidou.mybatisplus.annotation.TableField;
    importar com.baomidou.mybatisplus.annotation.TableId;
    importar com.baomidou.mybatisplus.annotation.TableName;
    importar lombok.Datos;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Data
      @TableName(“tb_brand”)
      public class Brand { @TableId(type = IdType.AUTO) private Integer id; //品牌id

      @TableField(value = "brand_name")
      private String brandName; //nombre de la marca

      @TableField(value = “logo”)
      private String logo; //Dirección de la imagen de la marca

    }

    6.3.3 Implementación de backend: consulta

    Paso 1: Cree una interfaz de BrandMapper para consultar todas las marcas según la clasificación

    paquete com.czxy.changgou4.mapper;

    importar com.baomidou.mybatisplus.core.mapper.BaseMapper;
    importar com.czxy.changgou4.pojo.Marca;
    importar org.apache.ibatis.anotaciones.*;

    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Mapper
      interfaz pública BrandMapper extiende BaseMapper {

      /**

      • Consultar todas las marcas por categoría
      • @param categoríaId
      • @return
        /
        @Select("seleccione b.
        de tb_brand b ,tb_category_brand cb " +
        " donde b.id = cb.brand_id y cb.category_id = #{categoryId}")

      public List findAll(@Param(“categoryId”) Integer categoryId);

    }

    Paso 2: Crear interfaz BrandService

    paquete com.czxy.changgou4.servicio;

    importar com.baomidou.mybatisplus.extension.service.IService;
    importar com.czxy.changgou4.pojo.Marca;

    importar java.util.List;

    /**

    • @autor Tío Tong
    • @email [email protected]
      /
      interfaz pública BrandService extiende IService { /
      *
      • Consultar todas las marcas por categoría
      • @param categoríaId
      • @return
        */
        public List findAll(Integer categoryId);
        }

    Paso 3: escribir la clase de implementación de BrandService

    paquete com.czxy.changgou4.service.impl;

    importar com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    importar com.czxy.changgou4.mapper.BrandMapper;
    importar com.czxy.changgou4.pojo.Marca;
    importar com.czxy.changgou4.service.BrandService;
    importar org.springframework.stereotype.Service;
    importar org.springframework.transaction.annotation.Transactional;

    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Service
      @Transactional
      public class BrandServiceImpl extends ServiceImpl<BrandMapper, Brand> implements BrandService {

      @Override
      public List findAll(Integer categoryId) { return baseMapper.findAll(categoryId); } }


    Paso 4: Cree un BrandController que se ajuste al
    paquete de especificación de interfaz com.czxy.changgou4.controller;

    importar com.czxy.changgou4.pojo.Marca;
    importar com.czxy.changgou4.service.BrandService;
    importar com.czxy.changgou4.vo.BaseResult;
    importar org.springframework.web.bind.annotation.GetMapping;
    importar org.springframework.web.bind.annotation.PathVariable;
    importar org.springframework.web.bind.annotation.RequestMapping;
    importar org.springframework.web.bind.annotation.RestController;

    importar javax.anotación.Recurso;
    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @RestController
      @RequestMapping(“/brands”)
      public class BrandController {

      @Resource
      privado BrandService brandService;

      /**

      • //GET /marcas/categoría/76
      • @param categoríaId
      • @return
        */
        @GetMapping(“/category/{categoryId}”)
        public BaseResult findAll(@PathVariable(“categoryId”) Integer categoryId){ //1 Lista de consultas list = this.brandService.findAll(categoryId); //2 Encapsulación return BaseResult.ok("consulta exitosa", lista); }




    }

    6.3.4 Implementación front-end
     Paso 1: Modificar apiclient.js y escribir ajax

    //Marca findBrand de la categoría especificada
    : (categoryId) => { return axios.get('/web-service/brands/category/' + categoryId) }

    Paso 2: Modifique la página ~/pages/list/_cid, después de que la página se cargue correctamente, obtenga la identificación de clasificación

    data() { return { searchMap: { //objeto de condición de búsqueda catId: '' //id de categoría } } }, asíncrono montado() { //establecer id this.searchMap.catId = this.$route.params.cid








    },

    步骤三:修改 ~/pages/list/_cid.vue 页面,查询品牌

    data() {
    return {
    searchMap: { //搜索条件对象
    catId: ‘’ //分类id
    },
    brandList : [], //所有品牌

    }
    

    },
    methods: {
    async findAllBrandFn() {
    let { data : brandData } = await this.$request.findBrand( this.searchMap.catId )
    this.brandList = brandData.data
    }
    },
    async mounted() {

    //设置id
    this.searchMap.catId = this.$route.params.cid
    // 查询所有的品牌
    this.findAllBrandFn()
    

    },

    步骤四:修改 ~/pages/list/_id 页面,显示分类数据

    品牌:

    步骤五:修改 ~/pages/list/_id 页面,回显选中数据

    methods: {
    brandSearch (bid){
    //记录品牌id
    this.searchMap.brandId = bid;
    //查询
    this.searchList();
    },
    searchList () {
    console.info(this.searchMap)
    }
    },

    6.4 Todas las especificaciones de la categoría especificada
    6.4.1 Interfaz
    GET http://localhost:10010/web-service/specifications/category/76

    6.4.2 Implementación de back-end: JavaBean
    Análisis de la estructura de la tabla:
    Tabla de especificaciones: tb_specification
    Tabla de opciones de especificación: tb_specification_option
    Relación: uno a muchos (una especificación tiene múltiples opciones de especificación)

    Paquete de opciones de especificación
    com.czxy.changgou4.pojo;

    importar com.baomidou.mybatisplus.annotation.IdType;
    importar com.baomidou.mybatisplus.annotation.TableField;
    importar com.baomidou.mybatisplus.annotation.TableId;
    importar com.baomidou.mybatisplus.annotation.TableName;
    importar com.fasterxml.jackson.annotation.JsonProperty;
    importar lombok.Datos;

    /**

    • @autor Tío Tong

    • @email [email protected]
      /
      @TableName(“tb_specification_option”)
      @Data
      public class SpecificationOption {
      /

      CREATE TABLE tb_specification_option (
      id int(10) unsigned NOT NULL AUTO_INCREMENT,
      spec_id int(10) unsigned NOT NULL COMMENT ‘规格ID’,
      option_name varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT ‘选项名称’,
      PRIMARY KEY (id),
      KEY specification_options_spec_id_index (spec_id)
      )
      */
      @TableId(type = IdType.AUTO)
      private Integer id;

      @TableField(value=“spec_id”)
      @JsonProperty(“spec_id”)
      private Integer specId; //外键,规格ID

      private Specification specification; //外键对应对象

      @TableField(value=“option_name”)
      @JsonProperty(“option_name”)
      private String optionName; //选项名称

    }

    规格
    package com.czxy.changgou4.pojo;

    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import lombok.Data;

    import java.util.List;

    /**

    • @author 桐叔

    • @email [email protected]
      /
      @TableName(“tb_specification”)
      @Data
      public class Specification {
      /

      CREATE TABLE tb_specification (
      id int(10) unsigned NOT NULL AUTO_INCREMENT,
      spec_name varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT ‘规格名称’,
      category_id int(10) unsigned NOT NULL COMMENT ‘分类ID’,
      PRIMARY KEY (id)
      )
      */
      @TableId(type = IdType.AUTO)
      private Integer id;

      @TableField(value = “spec_name”)
      @JsonProperty(“spec_name”)
      private String specName; //规格名称

      @TableField(value = “category_id”)
      private Integer categoryId; //分类外键
      private Category category; //分类外键对应对象

      private List options; //一个规格,具有多个规格选项

    }

    6.4.3 Implementación de back-end: consulta
     Al consultar las especificaciones, consulte las opciones de especificación correspondientes

    Paso 1: Cree un SpecificationOptionMapper, que se utiliza para consultar todas las "Opciones de especificación" de la "Especificación" especificada

    paquete com.czxy.changgou4.mapper;

    importar com.baomidou.mybatisplus.core.mapper.BaseMapper;
    importar com.czxy.changgou4.pojo.SpecificationOption;
    importar org.apache.ibatis.anotaciones.*;

    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Mapper
      interfaz pública SpecificationOptionMapper extiende BaseMapper {

      /**

      • Consultar todas las opciones de especificación de una especificación específica
      • @param spceId
      • @return
        */
        @Select(“select * from tb_specification_option where spec_id = #{specId}”)
        @Results({
        @Result(property = “id”,column = “id”),
        @Result(property = “specId”,column = “spec_id”),
        @Result(property = “optionName”,column = “option_name”),
        })
        public List findSpecOptionBySpecId(@Param(“specId”) Integer spceId);

    }

    步骤二:创建SpecificationMapper,查询指定分类的所有规格,含规格选项信息

    package com.czxy.changgou4.mapper;

    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.czxy.changgou4.pojo.Specification;
    import org.apache.ibatis.annotations.*;

    import java.util.List;

    /**

    • @author 桐叔
    • @email [email protected]
      /
      @Mapper
      interfaz pública SpecificationMapper extiende BaseMapper { /
      *
      • Consultar todas las especificaciones de la categoría especificada
      • @param categoríaId
      • @return
        */
        @Select(“select * from tb_specification where category_id = #{categoryId}”)
        @Results({ @Result(propiedad = “id”, columna = “id”), @Result(propiedad = “specName”, column = “spec_name”), @Result(propiedad = “categoryId”, columna = “category_id”), @Result(propiedad = “opciones”, many=@Many(select=“com.czxy.changgou4.mapper.SpecificationOptionMapper. findSpecOptionBySpecId”), columna = “id”), }) public List findSpecificationByCategoryId(@Param(“categoryId”) Integer categoryId);





    }

    Paso 3: Cree una interfaz de SpecificationService

    package com.czxy.changgou4.service;

    import com.baomidou.mybatisplus.extension.service.IService;
    import com.czxy.changgou4.pojo.Specification;

    import java.util.List;

    /**

    • @author 桐叔
    • @email [email protected]
      /
      public interface SpecificationService extends IService {
      /
      *
      • 查询指定分类的所有规格
      • @param categoryId
      • @return
        */
        public List findSpecificationByCategoryId(Integer categoryId);
        }

    步骤四:创建SpecificationService实现类

    package com.czxy.changgou4.service.impl;

    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.czxy.changgou4.mapper.SpecificationMapper;
    import com.czxy.changgou4.pojo.Specification;
    import com.czxy.changgou4.service.SpecificationService;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.util.List;

    /**

    • @author 桐叔
    • @email [email protected]
      */
      @Service
      @Transactional
      clase pública SpecificationServiceImpl extiende ServiceImpl<SpecificationMapper, Specification> implements SpecificationService { @Override public List findSpecificationByCategoryId(Integer categoryId) { return baseMapper.findSpecificationByCategoryId(categoryId); } }




    Paso 5: Cree
    el paquete SpecificationController com.czxy.changgou4.controller;

    import com.czxy.changgou4.pojo.Specification;
    import com.czxy.changgou4.service.SpecificationService;
    import com.czxy.changgou4.vo.BaseResult;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import javax.annotation.Resource;
    import java.util.List;

    /**

    • @author 桐叔

    • @email [email protected]
      */
      @RestController
      @RequestMapping(“/specifications”)
      public class SpecificationController {
      @Resource
      private SpecificationService specificationService;

      /**

      • GET /specifications/category/:catid
      • @param categoryId
      • @return
        */
        @GetMapping(“/category/{catId}”)
        public BaseResult findSpecificationByCategoryId(@PathVariable(“catId”) Integer categoryId){
        //1 查询
        List list = this.specificationService.findSpecificationByCategoryId(categoryId);
        //2 封装
        return BaseResult.ok(“查询成功”, list);
        }

    }

    6.4.4前端实现
    步骤一:修改 api.js ,查询规格

    //指定分类的规格
    findSpec : (categoryId) => {
    return axios.get(‘/web-service/specifications/category/’ + categoryId)
    },

    Paso 2: modifique páginas/lista/_id, la página se carga correctamente, consulte los
    datos de especificación() { return { searchMap: { //objeto de condición de búsqueda catId: '', //id de categoría brandId: '', // marca id } , brandList : [], //todas las marcas specList : [], //todas las especificaciones






    }
    

    },
    métodos: { async findAllBrandFn() { let { data : brandData } = esperar esto. Error de análisis de KaTeX: se esperaba 'EOF', se obtuvo '}' en la posición 85: …dData.data }̲, async fin… request.findSpec( this.searchMap.catId ) this.specList = specData.data }, brandSearch(bid) { //记录品牌id this.searchMap.brandId = oferta;






      //查询
      this.searchList();
    },
    searchList () {
      console.info(this.searchMap)
    }
    

    },
    asíncrono montado() {

    //设置id
    this.searchMap.catId = this.$route.params.cid
    // 查询所有的品牌
    this.findAllBrandFn()
    // 查询所有的规格
    this.findAllSpecFn()
    

    },

    Paso 3: Mostrar datos

            <dl v-for="(sl,sli) in specList" :key="sli">
              <dt>{
          
          {sl.spec_name}}:</dt>
              <dd :class="{'cur': sl.selectId == ''}"><a href="" @click.prevent="specSearch(sl ,'' , )">不限</a></dd>
              <dd :class="{'cur' : sl.selectId == op.id }" v-for="(op,opi) in sl.options" :key="opi">
                <a href="" @click.prevent="specSearch(sl ,op , )">{
          
          {op.option_name}}</a>
              </dd>
            </dl>
    

    Paso 4: seleccione la especificación y se mostrará el contenido general

    specSearch (spec , option ) {
    
      //记录选中的id
      this.$set(spec,'selectId', option ? option.id : '' )
    
    }
    

    6.5 Tecnología front-end:
    Referencia de ElasticSearch "introducción de elasticsearch.doc"

    6.6 Concepto de comercio electrónico: SKU y SPU
    SPU = Unidad de producto estándar (unidad de producto estándar)
    SPU es la unidad más pequeña de agregación de información de productos, y es un conjunto de información estandarizada reutilizable y fácil de recuperar que describe un producto características.
    En términos sencillos, los productos con el mismo valor de atributo y características pueden denominarse SPU.
    Por ejemplo:
    iphone X es una SPU, que no tiene nada que ver con el color, el estilo y el paquete.
    SKU=unidad de mantenimiento de existencias (stockkeeping unit)
    SKU es la unidad de medida de entrada y salida de inventario, que puede ser en piezas, cajas, tarimas, etc.
    SKU es la unidad de mantenimiento de existencias físicamente indivisible más pequeña.
    Por ejemplo:
    iPhone X 128G negro es un SKU

    Resumen
    SPU: Puede entenderse como un término general para una clase de bienes (una colección de características del producto)
    SKU: Puede denominarse la unidad de mantenimiento de existencias más pequeña, que es el identificador único que puede determinar de manera única un producto.

    6.7 Análisis de procesos
    Función de consulta de Sku: consulta toda la información relacionada con sku de la base de datos
    Sincronización de datos: sincroniza los datos en la base de datos con es. ()
    La búsqueda de consultas en la base de datos es lenta, la velocidad de consulta es rápida
    Tiempo de sincronización: 1. Temporizador (teoría), 2. Programa de prueba (implementación)
    Servicio de búsqueda: busca datos de elasticsearch, por defecto no hay datos en es, a través de Después de la operación de "sincronización de datos", es tiene datos

     Proceso de versión detallada

    6.8 Análisis de la estructura de la tabla
     Descripción general de la tabla relacionada con los productos básicos

     Relación entre tablas

    6.9查询所有SKU
    6.9.1接口
    GET http://localhost:10010/web-service/sku/esData

    6.9.2后端实现:JavaBean

    数据库相关JavaBean
    Spu类似分类
    Sku 商品详情
    SkuComment sku的评论
    ES相关JavaBean
    ESData 准备存放到es中的数据,是以上三种数据的组合数据。

    SPU

    package com.czxy.changgou4.pojo;

    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;

    import java.util.Date;

    /**

    • Created by liangtong.
      */
      @TableName(“tb_spu”)
      @Data
      public class Spu {

      @TableId(type = IdType.AUTO)
      private Integer id;

      //spu名字
      @TableField(value=“spu_name”)
      private String spuName;
      //spu副名称
      @TableField(value=“spu_subname”)
      private String spuSubname;
      //商品logo
      @TableField(value=“logo”)
      private String logo;
      //分类1Id
      @TableField(value=“cat1_id”)
      private Integer cat1Id;
      //分类2ID
      @TableField(value=“cat2_id”)
      private Integer cat2Id;
      //分类3Id
      @TableField(value=“cat3_id”)
      private Integer cat3Id;

      @TableField(value="brand_id")
      private Integer brandId;
      @TableField(exist = false)
      private Brand brand;
      //Hora de auditoría
      @TableField(value="check_time")
      private String checkTime;
      //Estado de auditoría Estado de auditoría, 0: No revisado, 1: Aprobado, 2: Fallido
      @TableField(value="check_status")
      private String checkStatus;
      //Precio
      @TableField(value="price")
      private String price;
      //¿Está en los estantes
      @TableField(value = "está_a_la_venta")
      private Integer isOnSale;
      //tiempo de estantería
      @TableField(value="on_sale_time")
      private Date onSaleTime;
      //tiempo de eliminación
      @TableField(value="deleted_at")
      private String deleteAt;

      @TableField(value=“weight”)
      private String peso;

      //商品描述
      @TableField(value=“description”)
      private String description;
      //规格与包装
      @TableField(value=“packages”)
      private String packages;
      //售后保障
      @TableField(value=“aftersale”)
      private String aftersale;
      //规格列表,json串
      @TableField(value=“spec_list”)
      private String specList;

      @TableField(value=“created_at”)
      private String createdAt;
      @TableField(value=“updated_at”)
      private String updatedAt;

    }

    Sku
    package com.czxy.changgou4.pojo;

    importar com.baomidou.mybatisplus.annotation.IdType;
    importar com.baomidou.mybatisplus.annotation.TableField;
    importar com.baomidou.mybatisplus.annotation.TableId;
    importar com.baomidou.mybatisplus.annotation.TableName;
    importar lombok.AllArgsConstructor;
    importar lombok.Datos;
    importar lombok.NoArgsConstructor;

    /**

    • Creado por liantong.
      */
      @TableName(“tb_sku”)
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class Sku {

      @TableId(tipo = IdType.AUTO)
      id entero privado;
      //库存量
      @TableField(value=“stock”)
      private Integer stock;

      @TableField(value=“spu_id”)
      private Integer spuId;
      @TableField(exist = false)
      private Spu spu;
      //sku名字
      @TableField(value=“sku_name”)
      private String skuName;

      @TableField(value=“images”)
      private String images;
      @TableField(value=“price”)
      private Double price;

      //1:1|2:6|6:22
      @TableField(value=“spec_info_id_list”)
      private String specInfoIdList;
      //规格列表码,格式:{“机身颜色”:“白色”,“内存”:“3GB”,“机身存储”:“16GB”}
      @TableField(value=“spec_info_id_txt”)
      private String specInfoIdTxt;

    }

    SkuComment

    package com.czxy.changgou4.pojo;

    importar com.baomidou.mybatisplus.annotation.IdType;
    importar com.baomidou.mybatisplus.annotation.TableField;
    importar com.baomidou.mybatisplus.annotation.TableId;
    importar com.baomidou.mybatisplus.annotation.TableName;
    importar lombok.AllArgsConstructor;
    importar lombok.Datos;
    importar lombok.NoArgsConstructor;

    importar java.util.Date;

    /**

    • Creado por liantong.
      */
      @TableName(“tb_sku_comment”)
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class SkuComment {

      @TableId(tipo = IdType.AUTO)
      id entero privado;
      @TableField(value=“created_at”)
      private Fecha de creación en;
      @TableField(value=“updated_at”)
      private Fecha de actualización en;

      @TableField(value=“user_id”)
      private Integer userId;
      @TableField(exist = false)
      private User user;

      @TableField(value=“spu_id”)
      private Integer spuId;
      @TableField(exist = false)
      private Spu spu;

      @TableField(value=“sku_id”)
      private Integer skuId;
      @TableField(exist = false)
      private Sku sku;

      @TableField(value=“ratio”)
      private String ratio;

      @TableField(value=“spec_list”)
      private String specList;

      @TableField(value=“content”)
      private String content;
      @TableField(value=“star”)
      private Integer star;
      @TableField(value=“isshow”)
      private String isShow;

      @TableField(value=“sn”)
      private String sn;

    }

    SkuPhoto

    package com.czxy.changgou4.pojo;

    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import lombok.Data;

    /**

    • Created by liangtong.
      */
      @TableName(“tb_sku_photo”)
      @Data
      public class SkuPhoto {

      @TableId(type = IdType.AUTO)
      private Integer id;
      //外键
      @TableField(value=“sku_id”)
      @JsonProperty(“sku_id”)
      private Integer skuId;
      @TableField(exist = false)
      private Sku sku;

      @TableField(value=“url”)
      private String url;

    }

    ESData
    package com.czxy.changgou4.vo;

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;

    import java.util.Date;
    import java.util.Map;

    /**

    • Created by liangtong.
      */
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class ESData {

      private Integer id; // skuId
      private String logo;//dirección de la imagen
      private String skuName;//sku name
      private String all; //toda la información que debe buscarse, incluido el título, la categoría e incluso la marca
      private Date onSaleTime; //Tiempo de venta
      //Número de marca
      private Integer brandId;
      //Category id
      private Integer catId;
      //Lista de especificaciones
      private Map<String, Object> specs;//Parámetros de especificación que se pueden buscar, la clave es el nombre del parámetro y el valor es el valor del parámetro
      private Double price ;//Price
      private String spuName;
      private Integer stock;
      private String description;
      private String packages;//Especificación y embalaje
      private String aftersale;//Garantía posventa
      private String midlogo;
      //Número de evaluación
      private Integer commentCount;
      //Ventas
      private Integer sellerCount;

    }

    6.9.3后端实现:搭建环境
    步骤一:修改父项目的pom.xml,锁定fastjson的依赖

    <fastjson.version>1.2.9</fastjson.version>

    com.alibaba fastjson ${fastjson.version}

    步骤二:修改web服务,添加fastjson依赖

    com.alibaba fastjson

    步骤三:修改网关,确定 ESData路径

    6.9.4后端实现:查询

    SpuMapper:通过SpuId查询详情,含品牌信息

    package com.czxy.changgou4.mapper;

    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.czxy.changgou4.pojo.Spu;
    import org.apache.ibatis.annotations.*;

    /**

    • Created by liangtong.
      */
      @Mapper
      public interface SpuMapper extends BaseMapper {

      @Select(“select * from tb_spu where id = #{spuId}”)
      @Results({ @Result(propiedad = “spuName”, columna = “spu_name”), @Result(propiedad = “spuSubname”, columna = “spu_subname ”), @Result(propiedad = “cat1Id”, columna = “cat1_id”), @Result(propiedad = “cat2Id”, columna = “cat2_id”), @Result(propiedad = “cat3Id”, columna = “cat3_id”) , @Result(propiedad = “brandId”, columna = “brand_id”), @Result(propiedad = “checkTime”, columna = “check_time”), @Result(propiedad = “checkStatus”, columna = “check_status”), @ Result(propiedad = “estáEnVenta”, columna = “está_en_venta”), @Resultado(propiedad = “hora_en_venta”, columna = “hora_en_venta”), @Resultado(propiedad = “eliminado en”, columna = “eliminado_en”),











      @Result(property = “specList” , column = “spec_list”),
      @Result(property = “onSaleTime” , column = “on_sale_time”),
      @Result(property = “createdAt” , column = “created_at”),
      @Result(property = “updatedAt” , column = “updated_at”),
      @Result(property=“brand”, column=“brand_id”,
      one=@One(
      select=“com.czxy.changgou4.mapper.BrandMapper.selectById”
      ))
      })
      public Spu findSpuById(@Param(“spuId”) Integer spuId);

    }

    SkuCommentMapper:查询评论数

    package com.czxy.changgou4.mapper;

    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.czxy.changgou4.pojo.SkuComment;
    import org.apache.ibatis.annotations.*;

    import java.util.List;

    /**

    • Creado por liantong.
      */
      @Mapper
      interfaz pública SkuCommentMapper extiende BaseMapper {

      /**

      • Consultar el número de comentarios
      • @param spu_id
      • @return
        /
        @Select("select count(
        ) from tb_sku_comment where spu_id = #{spu_id}")
        public Integer findNumBySpuId(@Param(“spu_id”) Integer spu_id);

      }

    SkuMapper: consulta todos los sku, incluido el
    paquete de información spu correspondiente com.czxy.changgou4.mapper;

    importar com.baomidou.mybatisplus.core.mapper.BaseMapper;
    importar com.czxy.changgou4.pojo.Sku;
    importar org.apache.ibatis.anotaciones.*;

    importar java.util.List;

    /**

    • Creado por liantong.
      */ La interfaz pública
      de @Mapper
      SkuMapper amplía BaseMapper {

      @Select(“seleccionar * de tb_sku”)
      @Results(id=“skuResult”, value={ @Result(id=true,column=“id”,property=“id”), @Result(column=“stock” ,property=“stock”), @Result(column=“spu_id”,property=“spuId”), @Result(column=“sku_name”,property=“skuName”), @Result(column=“spec_info_id_list”,property =“specInfoIdList”), @Result(columna=“spec_info_id_txt”,propiedad=“specInfoIdTxt”), @Result(columna=“spu_id”,propiedad=“spu”, one=@One( select=“com.czxy.changgou4 .mapper.SpuMapper.findSpuById” )) }) lista pública findAllSkus();











    }

    SkuPhotoMapper

    paquete com.czxy.changgou4.mapper;

    importar com.baomidou.mybatisplus.core.mapper.BaseMapper;
    importar com.czxy.changgou4.pojo.SkuPhoto;
    importar org.apache.ibatis.annotations.Mapper;
    importar org.apache.ibatis.anotaciones.Resultado;
    importar org.apache.ibatis.anotaciones.Resultados;
    importar org.apache.ibatis.annotations.Select;

    importar java.util.List;

    /**

    • Creado por liantong.
      */ La interfaz pública
      de @Mapper
      SkuPhotoMapper amplía BaseMapper {

    }

    paquete com.czxy.changgou4.servicio;

    importar com.baomidou.mybatisplus.extension.service.IService;
    importar com.czxy.changgou4.pojo.Sku;
    importar com.czxy.changgou4.vo.ESData;

    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      interfaz pública SkuService extiende IService {

      /**
      *

      • @return
        */
        lista pública findESData();
        }

    paquete com.czxy.changgou4.service.impl;

    importar com.alibaba.fastjson.JSON;
    importar com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    importar com.czxy.changgou4.mapper.SkuCommentMapper;
    importar com.czxy.changgou4.mapper.SkuMapper;
    importar com.czxy.changgou4.pojo.Sku;
    importar com.czxy.changgou4.service.SkuService;
    importar com.czxy.changgou4.vo.ESData;
    importar org.springframework.stereotype.Service;
    importar org.springframework.transaction.annotation.Transactional;

    importar javax.anotación.Recurso;
    importar java.util.ArrayList;
    importar java.util.List;
    importar java.util.Map;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Service
      @Transactional
      public class SkuServiceImpl extends ServiceImpl<SkuMapper, Sku> implements SkuService {

      @Resource
      private SkuCommentMapper skuCommentMapper;

      @Override
      public List findESData() {
      //1 查询所有详情sku
      List skulist = baseMapper.findAllSkus();

       //2 将SKU 转换成 ESData
       List<ESData> esDataList = new ArrayList<>();
      
       for (Sku sku:skulist){
           ESData esData = new ESData();
           // id
           esData.setId(sku.getId());
           // 图片地址
           esData.setLogo(sku.getSpu().getLogo());
           // 商品名称
           esData.setSkuName(sku.getSkuName());
           // all  “华为xx {"机身颜色":"白色","内存":"3GB","机身存储":"16GB"} 荣耀 ”
           esData.setAll(sku.getSkuName()+"   " + sku.getSpecInfoIdTxt() + "   " +sku.getSpu().getBrand().getBrandName());
           // on_sale_time
           esData.setOnSaleTime(sku.getSpu().getOnSaleTime());
           // brand_id
           esData.setBrandId(sku.getSpu().getBrandId());
           // cat_id
           esData.setCatId(sku.getSpu().getCat3Id());
           //  Map<String, Object> specs;// 可搜索的规格参数,key是参数名,值是参数值
           Map<String,Object> specs = JSON.parseObject(sku.getSpecInfoIdTxt(), Map.class);
      

    // Map newSpecs = new HashMap();
    // for(String key : specs.keySet()){
    // newSpecs.put(“spec” + key , specs.get(key));
    // }

            esData.setSpecs(specs);
            // price 价格
            esData.setPrice(sku.getPrice());
            // spu_name
            esData.setSpuName(sku.getSpu().getSpuName());
            // stock 库存
            esData.setStock(sku.getStock());
            // description
            esData.setDescription(sku.getSpu().getDescription());
            // packages;//规格与包装
            esData.setPackages(sku.getSpu().getPackages());
            // aftersale;//售后保障
            esData.setAftersale(sku.getSpu().getAftersale());
            // midlogo;
            esData.setMidlogo(sku.getSpu().getLogo());
            // comment_count; 评价数
            Integer comment_count = skuCommentMapper.findNumBySpuId(sku.getSpu().getId());
            esData.setCommentCount(comment_count);
    
            //销售量
            esData.setSellerCount(10);
    
            esDataList.add(esData);
        }
    
        return esDataList;
    
    }
    

    }

    package com.czxy.changgou4.controller;

    importar com.czxy.changgou4.service.SkuService;
    importar com.czxy.changgou4.vo.BaseResult;
    importar com.czxy.changgou4.vo.ESData;
    importar org.springframework.beans.factory.annotation.Autowired;
    importar org.springframework.http.ResponseEntity;
    importar org.springframework.web.bind.annotation.GetMapping;
    importar org.springframework.web.bind.annotation.RequestMapping;
    importar org.springframework.web.bind.annotation.RestController;

    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @RestController
      @RequestMapping(“/sku”)
      clase pública SkuController {

      @Autowired
      privado SkuService skuService;

      @GetMapping(“/esData”)
      public BaseResult<List> findESData(){

       List<ESData> esData = skuService.findESData();
       return BaseResult.ok("查询成功", esData);
      

      }

    }

    6.10初始化数据
    6.10.1搭建环境:搜索服务
    步骤一:构建项目changgou4-service-search

    步骤二:修改pom.xml,添加依赖



    com.czxy.changgou
    changgou4_common


    com.czxy.changgou
    changgou4_pojo



    org.springframework.boot
    spring-boot-starter-web

    <!-- nacos 客户端 -->
    <dependency>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-client</artifactId>
    </dependency>
    
    <!-- nacos 服务发现 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
    <!-- openfeign 远程调用 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
    <!--swagger2-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
    </dependency>
    
    <!--elasticsearch-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    

    步骤三:创建yml文件

    server:
    port: 8090
    spring:
    application:
    name: search-service
    cloud:
    nacos:
    discovery:
    server-addr: 127.0.0.1:8848 #nacos服务地址

    拷贝配置类

    package com.czxy.changgou4.config;

    importar org.elasticsearch.client.RestHighLevelClient;
    importar org.springframework.context.annotation.Bean;
    importar org.springframework.context.annotation.Configuration;
    importar org.springframework.data.elasticsearch.client.ClientConfiguration;
    importar org.springframework.data.elasticsearch.client.RestClients;
    importar org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
    importar org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Configuration
      @EnableElasticsearchRepositories
      clase pública RestClientConfig extiende AbstractElasticsearchConfiguration {

      @Override
      @Bean
      public RestHighLevelClient elasticsearchClient() {

       final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
               .connectedTo("localhost:9200")
               .build();
      
       return RestClients.create(clientConfiguration).rest();
      

      }
      }


    Paquete de clase de inicio com.czxy.changgou4;

    importar org.springframework.boot.SpringApplication;
    importar org.springframework.boot.autoconfigure.SpringBootApplication;
    importar org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    importar org.springframework.cloud.openfeign.EnableFeignClients;

    /**

    • @autor Tío Tong
    • @email [email protected]
      */
      @SpringBootApplication
      @EnableDiscoveryClient
      @EnableFeignClients
      public class CGSearchServiceApplication {
      public static void main(String[] args) {
      SpringApplication.run(CGSearchServiceApplication.class,args);
      }
      }

    6.10.2远程调用:查询所有

    步骤一:编写JavaBean,拷贝ESData,并添加elasticsearch相关注解
    package com.czxy.changgou4.vo;

    importar lombok.AllArgsConstructor;
    importar lombok.Datos;
    importar lombok.NoArgsConstructor;
    importar org.springframework.data.annotation.Id;
    importar org.springframework.data.elasticsearch.annotations.Document;
    importar org.springframework.data.elasticsearch.annotations.Field;
    importar org.springframework.data.elasticsearch.annotations.FieldType;

    importar java.util.Date;
    importar java.util.Map;

    /**

    • Creado por liangtong.
      */
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @Document(indexName = “skus”, type = “docs” )
      public class SearchSku { @Id private Long id; // skuId @Field(type = FieldType.Text) private String logo;//Dirección de la imagen @Field(type = FieldType.Text ,analyzer = “ik_max_word”) private String skuName;//sku name @Field(type = FieldType.Text ,analyzer = “ik_max_word”) private String all; / / Toda la información que debe buscarse, incluido el título, la categoría e incluso la marca @Field(type = FieldType.Date) private Date onSaleTime;//Business time //Número de marca @Field(type = FieldType.Integer) private Integer brandId ; // identificación de la categoría














      @Field(type = FieldType.Integer)
      private Integer catId;

      //规格列表
      //@IndexDynamicSettings
      private Map<String, Object> specs;// 可搜索的规格参数,key是参数名,值是参数值
      @Field(type = FieldType.Double)
      private Double price;// 价格
      @Field(type = FieldType.Text)
      private String spuName;
      @Field(type = FieldType.Integer)
      private Integer stock;
      @Field(type = FieldType.Text)
      private String description;
      @Field(type = FieldType.Text)
      private String packages;//规格与包装
      @Field(type = FieldType.Text)
      private String aftersale;//售后保障
      @Field(type = FieldType.Text)
      private String midlogo;
      //评价数
      @Field(type = FieldType.Integer)
      private Integer commentCount;
      // 销量
      @Field(type = FieldType.Integer)
      private Integer sellerCount;

    }

    步骤二:远程调用

    package com.czxy.changgou4.feign;

    import com.czxy.changgou4.vo.BaseResult;
    import com.czxy.changgou4.vo.ESData;
    import com.czxy.changgou4.vo.SearchSku;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;

    import java.util.List;

    /**

    • Created by liangtong.
      */
      @FeignClient(value=“web-service”,path = “/sku”)
      public interface SkuFeign {

      @GetMapping(“/esData”)
      public BaseResult<List> findESData();

    }

    步骤三:测试类

    package com.czxy.changgou4;

    importar com.czxy.changgou4.fingir.SkuFingir;
    importar com.czxy.changgou4.repository.SkuRepository;
    importar com.czxy.changgou4.vo.BaseResult;
    importar com.czxy.changgou4.vo.SearchSku;
    importar org.junit.Prueba;
    importar org.junit.runner.RunWith;
    importar org.springframework.boot.test.context.SpringBootTest;
    importar org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
    importar org.springframework.http.ResponseEntity;
    importar org.springframework.test.context.junit4.SpringRunner;

    importar javax.anotación.Recurso;
    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @RunWith(SpringRunner.class)
      @SpringBootTest(classes = CGSearchServiceApplication.class)
      public class TestSkuClient { @Resource private SkuFeign skuFeign;

      @Test
      public void findAllSkus() lanza una excepción { BaseResult<List> baseResult = skuFeign.findESData(); Lista lista = baseResult.getData(); System.out.println(“总条数:” + list.size()); for(SearchSku ss:list){ System.out.println(ss); } }






    }

    6.10.3 Integrar ElasticSearch
    Paso 1: Crear clase de repositorio

    paquete com.czxy.changgou4.repository;

    import com.czxy.changgou4.vo.SearchSku;
    import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

    /**

    • Created by liangtong.
      */
      public interface SkuRepository extends ElasticsearchRepository<SearchSku, Long> {
      }

    步骤二:测试创建索引
    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Test
    public void createIndex(){
    // 创建索引
    this.elasticsearchRestTemplate.createIndex(SearchSku.class);
    // 配置映射
    this.elasticsearchRestTemplate.putMapping(SearchSku.class);
    }

    步骤三:测试装载数据
    @Resource
    private SkuRepository skuRepository;

    @Test
    public void loadData() arroja una excepción { BaseResult<List> baseResult = skuFeign.findESData(); Lista lista = baseResult.getData(); for(SearchSku ss:list){ ss.setAll(ss.toString()); Sistema.salida.println(ss); } this.skuRepository.saveAll(lista); }







    6.11 Consulta básica
    6.11.1 Consultar todos
    los paquetes com.czxy.changgou4;

    importar com.czxy.changgou4.repository.SkuRepository;
    importar com.czxy.changgou4.vo.SearchSku;
    importar org.elasticsearch.index.query.BoolQueryBuilder;
    importar org.elasticsearch.index.query.QueryBuilders;
    importar org.junit.Prueba;
    importar org.junit.runner.RunWith;
    importar org.springframework.boot.test.context.SpringBootTest;
    importar org.springframework.data.domain.Page;
    importar org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
    importar org.springframework.test.context.junit4.SpringRunner;

    importar javax.anotación.Recurso;
    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @RunWith(SpringRunner.class)
      @SpringBootTest(classes = CGSearchServiceApplication.class)
      public class TestSkuRepository {
      @Resource
      private SkuRepository skuRepository;

      @Test
      public void testFind() {
      //1 简单条件
      BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

       //2 复合条件
       NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
       queryBuilder.withQuery(boolQueryBuilder);
      
       //3 查询
       Page<SearchSku> pageInfo = this.skuRepository.search(queryBuilder.build());
      
       // 总条数
       System.out.println(pageInfo.getTotalElements());
      
       // 查询结果
       pageInfo.getContent().forEach(System.out::println);
      

      }

    }

    6.11.2根据分类查询
    package com.czxy.changgou4;

    importar com.czxy.changgou4.repository.SkuRepository;
    importar com.czxy.changgou4.vo.SearchSku;
    importar org.elasticsearch.index.query.BoolQueryBuilder;
    importar org.elasticsearch.index.query.QueryBuilders;
    importar org.junit.Prueba;
    importar org.junit.runner.RunWith;
    importar org.springframework.boot.test.context.SpringBootTest;
    importar org.springframework.data.domain.Page;
    importar org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
    importar org.springframework.test.context.junit4.SpringRunner;

    importar javax.anotación.Recurso;
    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @RunWith(SpringRunner.class)
      @SpringBootTest(classes = CGSearchServiceApplication.class)
      public class TestSkuRepository { @Resource private SkuRepository skuRepository;

      @Test
      public void testFind() { //1 condición simple BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

       // 1.1 根据分类查询
       boolQueryBuilder.must(QueryBuilders.termQuery("catId",76));
      
      
       //2 复合条件
       NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
       queryBuilder.withQuery(boolQueryBuilder);
      
       //3 查询
       Page<SearchSku> pageInfo = this.skuRepository.search(queryBuilder.build());
      
       // 总条数
       System.out.println(pageInfo.getTotalElements());
      
       // 查询结果
       pageInfo.getContent().forEach(System.out::println);
      

      }

    }

    6.11.3
    Paquete de consulta com.czxy.changgou4 según la marca;

    importar com.czxy.changgou4.repository.SkuRepository;
    importar com.czxy.changgou4.vo.SearchSku;
    importar org.elasticsearch.index.query.BoolQueryBuilder;
    importar org.elasticsearch.index.query.QueryBuilders;
    importar org.junit.Prueba;
    importar org.junit.runner.RunWith;
    importar org.springframework.boot.test.context.SpringBootTest;
    importar org.springframework.data.domain.Page;
    importar org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
    importar org.springframework.test.context.junit4.SpringRunner;

    importar javax.anotación.Recurso;
    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @RunWith(SpringRunner.class)
      @SpringBootTest(classes = CGSearchServiceApplication.class)
      public class TestSkuRepository {
      @Resource
      private SkuRepository skuRepository;

      @Test
      public void testFind() {
      //1 简单条件
      BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

       // 1.1 根据分类查询
      

    // boolQueryBuilder.must(QueryBuilders.termQuery(“catId”,76));

        // 1.2 根据品牌查询
        boolQueryBuilder.must(QueryBuilders.termQuery("brandId",15127));
    
        //2 复合条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(boolQueryBuilder);
    
        //3 查询
        Page<SearchSku> pageInfo = this.skuRepository.search(queryBuilder.build());
    
        // 总条数
        System.out.println(pageInfo.getTotalElements());
    
        // 查询结果
        pageInfo.getContent().forEach(System.out::println);
    
    }
    

    }

    6.11.4根据指定规格进行查询
    package com.czxy.changgou4;

    importar com.czxy.changgou4.repository.SkuRepository;
    importar com.czxy.changgou4.vo.SearchSku;
    importar org.elasticsearch.index.query.BoolQueryBuilder;
    importar org.elasticsearch.index.query.QueryBuilders;
    importar org.junit.Prueba;
    importar org.junit.runner.RunWith;
    importar org.springframework.boot.test.context.SpringBootTest;
    importar org.springframework.data.domain.Page;
    importar org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
    importar org.springframework.test.context.junit4.SpringRunner;

    importar javax.anotación.Recurso;
    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @RunWith(SpringRunner.class)
      @SpringBootTest(classes = CGSearchServiceApplication.class)
      public class TestSkuRepository { @Resource private SkuRepository skuRepository;

      @Test
      public void testFind() { //1 condición simple BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

       // 1.1 根据分类查询
      

    // boolQueryBuilder.must(QueryBuilders.termQuery(“catId”,76));

        // 1.2 根据品牌查询
    

    // boolQueryBuilder.must(QueryBuilders.termQuery(“brandId”,15127));

        // 1.3 根据规格进行查询 机身颜色=灰色
        boolQueryBuilder.must(QueryBuilders.termQuery( "specs.机身颜色.keyword", "灰色"));
    
        //2 复合条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(boolQueryBuilder);
    
        //3 查询
        Page<SearchSku> pageInfo = this.skuRepository.search(queryBuilder.build());
    
        // 总条数
        System.out.println(pageInfo.getTotalElements());
    
        // 查询结果
        pageInfo.getContent().forEach(System.out::println);
    
    }
    

    }

    6.11.5
    Paquete de consulta de rango de precios com.czxy.changgou4;

    import com.czxy.changgou4.repository.SkuRepository;
    import com.czxy.changgou4.vo.SearchSku;
    import org.elasticsearch.index.query.BoolQueryBuilder;
    import org.elasticsearch.index.query.QueryBuilders;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.domain.Page;
    import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
    import org.springframework.test.context.junit4.SpringRunner;

    import javax.annotation.Resource;
    import java.util.List;

    /**

    • @author 桐叔

    • @email [email protected]
      */
      @RunWith(SpringRunner.class)
      @SpringBootTest(classes = CGSearchServiceApplication.class)
      public class TestSkuRepository {
      @Resource
      private SkuRepository skuRepository;

      @Test
      public void testFind() {
      //1 简单条件
      BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

       // 1.1 根据分类查询
      

    // boolQueryBuilder.must(QueryBuilders.termQuery(“catId”,76));

        // 1.2 根据品牌查询
    

    // boolQueryBuilder.must(QueryBuilders.termQuery(“brandId”,15127));

        // 1.3 根据规格进行查询 机身颜色=灰色
    

    // boolQueryBuilder.must(QueryBuilders.termQuery( “specs.机身颜色.keyword”, “灰色”));

        // 1.4 范围查询:
        boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(100000).lt(140000));
    
    
        //2 复合条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(boolQueryBuilder);
    
        //3 查询
        Page<SearchSku> pageInfo = this.skuRepository.search(queryBuilder.build());
    
        // 总条数
        System.out.println(pageInfo.getTotalElements());
    
        // 查询结果
        pageInfo.getContent().forEach(System.out::println);
    
    }
    

    }

    6.11.6关键字查询
    matchQuery 首先会解析查询字符串,进行分词,然后查询
    term query 不分词,直接查询

    package com.czxy.changgou4;

    importar com.czxy.changgou4.repository.SkuRepository;
    importar com.czxy.changgou4.vo.SearchSku;
    importar org.elasticsearch.index.query.BoolQueryBuilder;
    importar org.elasticsearch.index.query.QueryBuilders;
    importar org.elasticsearch.search.sort.SortBuilders;
    importar org.elasticsearch.search.sort.SortOrder;
    importar org.junit.Prueba;
    importar org.junit.runner.RunWith;
    importar org.springframework.boot.test.context.SpringBootTest;
    importar org.springframework.data.domain.Page;
    importar org.springframework.data.domain.PageRequest;
    importar org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
    importar org.springframework.test.context.junit4.SpringRunner;

    importar javax.anotación.Recurso;
    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @RunWith(SpringRunner.class)
      @SpringBootTest(classes = CGSearchServiceApplication.class)
      public class TestSkuRepository { @Resource private SkuRepository skuRepository;

      @Test
      public void testFind() { //1 condición simple BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

       // 1.1 根据分类查询
      

    // boolQueryBuilder.must(QueryBuilders.termQuery(“catId”,76));

        // 1.2 根据品牌查询
    

    // boolQueryBuilder.must(QueryBuilders.termQuery(“brandId”,15127));

        // 1.3 根据规格进行查询 机身颜色=灰色
    

    // boolQueryBuilder.must(QueryBuilders.termQuery( "specs.body color.keyword", "gray"));

        // 1.4 范围查询:
    

    // boolQueryBuilder.must(QueryBuilders.rangeQuery(“precio”).gte(100000).lt(140000));

        // 1.5 关键字查询
        boolQueryBuilder.must(QueryBuilders.matchQuery("skuName","华为"));
    
    
        //2 复合条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(boolQueryBuilder);
    
        //3 查询
        Page<SearchSku> pageInfo = this.skuRepository.search(queryBuilder.build());
    
        // 总条数
        System.out.println(pageInfo.getTotalElements());
    
        // 查询结果
        pageInfo.getContent().forEach(System.out::println);
    
    }
    

    }

    6.11.7
    paquete de paginación com.czxy.changgou4;

    importar com.czxy.changgou4.repository.SkuRepository;
    importar com.czxy.changgou4.vo.SearchSku;
    importar org.elasticsearch.index.query.BoolQueryBuilder;
    importar org.elasticsearch.index.query.QueryBuilders;
    importar org.junit.Prueba;
    importar org.junit.runner.RunWith;
    importar org.springframework.boot.test.context.SpringBootTest;
    importar org.springframework.data.domain.Page;
    importar org.springframework.data.domain.PageRequest;
    importar org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
    importar org.springframework.test.context.junit4.SpringRunner;

    importar javax.anotación.Recurso;
    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @RunWith(SpringRunner.class)
      @SpringBootTest(classes = CGSearchServiceApplication.class)
      public class TestSkuRepository { @Resource private SkuRepository skuRepository;

      @Test
      public void testFind() { //1 condición simple BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

       // 1.1 根据分类查询
      

    // boolQueryBuilder.must(QueryBuilders.termQuery(“catId”,76));

        // 1.2 根据品牌查询
    

    // boolQueryBuilder.must(QueryBuilders.termQuery(“brandId”,15127));

        // 1.3 根据规格进行查询 机身颜色=灰色
    

    // boolQueryBuilder.must(QueryBuilders.termQuery( "specs.body color.keyword", "gray"));

        // 1.4 范围查询:
    

    // boolQueryBuilder.must(QueryBuilders.rangeQuery(“precio”).gte(100000).lt(140000));

        //2 复合条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(boolQueryBuilder);
    
        //2.1 分页查询
        // 第一页 : 从0开始
        queryBuilder.withPageable(PageRequest.of(0  ,6));
    
    
        //3 查询
        Page<SearchSku> pageInfo = this.skuRepository.search(queryBuilder.build());
    
        // 总条数
        System.out.println(pageInfo.getTotalElements());
    
        // 查询结果
        pageInfo.getContent().forEach(System.out::println);
    
    }
    

    }

    6.11.8 Ordenar
    paquete com.czxy.changgou4;

    importar com.czxy.changgou4.repository.SkuRepository;
    importar com.czxy.changgou4.vo.SearchSku;
    importar org.elasticsearch.index.query.BoolQueryBuilder;
    importar org.elasticsearch.index.query.QueryBuilders;
    importar org.elasticsearch.search.sort.SortBuilders;
    importar org.elasticsearch.search.sort.SortOrder;
    importar org.junit.Prueba;
    importar org.junit.runner.RunWith;
    importar org.springframework.boot.test.context.SpringBootTest;
    importar org.springframework.data.domain.Page;
    importar org.springframework.data.domain.PageRequest;
    importar org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
    importar org.springframework.test.context.junit4.SpringRunner;

    importar javax.anotación.Recurso;
    importar java.util.List;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @RunWith(SpringRunner.class)
      @SpringBootTest(classes = CGSearchServiceApplication.class)
      public class TestSkuRepository { @Resource private SkuRepository skuRepository;

      @Test
      public void testFind() { //1 condición simple BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

       // 1.1 根据分类查询
      

    // boolQueryBuilder.must(QueryBuilders.termQuery(“catId”,76));

        // 1.2 根据品牌查询
    

    // boolQueryBuilder.must(QueryBuilders.termQuery(“brandId”,15127));

        // 1.3 根据规格进行查询 机身颜色=灰色
    

    // boolQueryBuilder.must(QueryBuilders.termQuery( "specs.body color.keyword", "gray"));

        // 1.4 范围查询:
    

    // boolQueryBuilder.must(QueryBuilders.rangeQuery(“precio”).gte(100000).lt(140000));

        //2 复合条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(boolQueryBuilder);
    
        //2.1 分页查询
        // 第一页 : 从0开始
    

    // queryBuilder.withPageable(PageRequest.of(0 ,6));

        //2.2 排序
        queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC));
    
        //3 查询
        Page<SearchSku> pageInfo = this.skuRepository.search(queryBuilder.build());
    
        // 总条数
        System.out.println(pageInfo.getTotalElements());
    
        // 查询结果
        pageInfo.getContent().forEach(System.out::println);
    
    }
    

    }

    6.12后端实现:商品搜索
    6.12.1分析

    6.12.2接口
    POST http://localhost:10010/search-service/sku/search
    {
    “catId”: 76,
    “current”: 1,
    “size”: 5,
    “keyword”: “64GB”,
    “brandId”: “8557”,
    “specList”: {
    “机身颜色”: “金色”
    },
    “minPrice”: “100000”,
    “maxPrice”: “140000”,
    “sortBy”:“xl|jg|pl|sj”,
    “sortWay”:“asc|desc”

    }

    sortBy: 综合(zh)、销量(xl )、价格(jg)、评论(pl)、时间(sj)
    sortWay: 升序(asc)、降序(desc)

    6.12.3初始化数据
    update tb_spu set logo = ‘https://img12.360buyimg.com/n1/jfs/t17050/106/1124838205/250590/7e63050a/5abb6be2N853106d9.jpg’
    where id in (2,3,4,5,7,124);

    6.12.4封装对象

    SearchVo,用于封装搜索条件,含分页数据,继承PageRequest
    package com.czxy.changgou4.vo;

    import com.fasterxml.jackson.annotation.JsonProperty;
    import lombok.Data;

    import java.util.Map;

    /**

    • 封装搜索条件,含分页数据
    • Created by liangtong.
      */
      @Data
      public class SearchVo extends PageRequest {
      private String keyword; // 关键字搜索,预留
      private Integer catId; // 3 级类目
      private Integer brandId; // 品牌
      private Map<String,String> specList; // 规格选项列表
      private Double minPrice; //最低价格
      private Double maxPrice; //最高价格
      }

    ReturnSku ,用于封装sku的响应结果(可以理解SearchSku缩减版,为页面定制)

    package com.czxy.changgou4.vo;

    import lombok.Data;

    /**

    • Sku页面定制响应对象
    • Creado por liantong.
      */
      @Data
      public class ReturnSku { private Long id; private String bienesNombre; //sku名称private Precio doble; //价格private String midlogo; //sku logo private Integer commentCount; //sku的评论}





    6.12.5 Implementación
    Paso 1: Escribir interfaz de servicio, usar SkuRepository para consultar desde es
    Escribir 2: Escribir clase de implementación de servicio
    Paso 3: Escribir controlador, consultar información de sku de clasificación especificada desde es

    Paso 1: escriba la interfaz de servicio, use SkuRepository para consultar el paquete com.czxy.changgou4.service desde es
    ;

    importar com.czxy.changgou4.vo.SearchVo;

    importar java.util.Map;

    /**

    • @autor Tío Tong
    • @email [email protected]
      /
      interfaz pública SkuSearchService { /
      *
      • Consultar sku de acuerdo con las condiciones y encapsular los resultados en un mapa
      • map.count , el número total de entradas
      • map.list , registros totales
      • @param buscarVo
      • @return 自定义封装数据
        */
        public Map search(SearchVo searchVo);
        }

    步骤二:编写service实现类

    package com.czxy.changgou4.service.impl;

    importar com.czxy.changgou4.repository.SkuRepository;
    importar com.czxy.changgou4.service.SkuSearchService;
    importar com.czxy.changgou4.vo.ReturnSku;
    importar com.czxy.changgou4.vo.SearchSku;
    importar com.czxy.changgou4.vo.SearchVo;
    importar org.apache.commons.lang3.StringUtils;
    importar org.elasticsearch.index.query.BoolQueryBuilder;
    importar org.elasticsearch.index.query.QueryBuilders;
    importar org.elasticsearch.search.sort.SortBuilders;
    importar org.elasticsearch.search.sort.SortOrder;
    importar org.springframework.data.domain.Page;
    importar org.springframework.data.domain.PageRequest;
    importar org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
    importar org.springframework.stereotype.Service;

    importar javax.anotación.Recurso;
    importar java.util.ArrayList;
    importar java.util.HashMap;
    importar java.util.List;
    importar java.util.Map;

    /**

    • @autor Tío Tong

    • @email [email protected]
      */
      @Service
      clase pública SkuSearchServiceImpl implementa SkuSearchService { @Resource private SkuRepository skuRepository;

      @Override
      public Map search(SearchVo searchVo) { //1 Crear generador de consultas BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

       //1.1 分类查询(必须)
       boolQueryBuilder.must(QueryBuilders.termQuery("catId",searchVo.getCatId()));
      
       //1.2 关键字查询
       if(StringUtils.isNotBlank(searchVo.getKeyword())){
           boolQueryBuilder.must(QueryBuilders.matchQuery("skuName",searchVo.getKeyword()));
       }
      
       //1.3 品牌查询
       if(searchVo.getBrandId()!=null){
           boolQueryBuilder.must(QueryBuilders.termQuery("brandId",searchVo.getBrandId()));
       }
      
       //1.4 规格查询
       Map<String, String> filter = searchVo.getSpecList();
       if(filter!=null){
           for (Map.Entry<String, String> entry : filter.entrySet()) {
               String key = entry.getKey();
               String value = entry.getValue();
               boolQueryBuilder.must(QueryBuilders.termQuery( "specs." + key + ".keyword", value));
           }
       }
      
       //1.5 价格区间,统一单位:前端元 * 100 --> 后端分
       if(searchVo.getMinPrice()!=null ){
           boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(searchVo.getMinPrice() * 100));
       }
       if(searchVo.getMaxPrice()!=null){
           boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lte(searchVo.getMaxPrice() * 100));
       }
      
       //2 复杂查询
       NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
       queryBuilder.withQuery( boolQueryBuilder );
      
       //2.1 排序
       // sortBy: 综合(zh)、销量(xl )、价格(jg)、评价(pj)、时间(sj)
       // sortBy: 升序(asc)、降序(desc)
       if (searchVo.getSortBy()!=null){
           if(searchVo.getSortBy().equals("xl")&&searchVo.getSortWay().equals("asc")){
               //销量升序
               queryBuilder.withSort(SortBuilders.fieldSort("sellerCount").order(SortOrder.ASC));
           }else  if(searchVo.getSortBy().equals("xl")&&searchVo.getSortWay().equals("desc")) {
               // 销量降序
               queryBuilder.withSort(SortBuilders.fieldSort("sellerCount").order(SortOrder.DESC));
           }else if(searchVo.getSortBy().equals("jg")&&searchVo.getSortWay().equals("asc")){
               // 价格升序
               queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC));
           }else  if(searchVo.getSortBy().equals("jg")&&searchVo.getSortWay().equals("desc")) {
               // 价格降序
               queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC));
           }else if(searchVo.getSortBy().equals("pl")&&searchVo.getSortWay().equals("asc")){
               // 评论升序
               queryBuilder.withSort(SortBuilders.fieldSort("commentCount").order(SortOrder.ASC));
           }else  if(searchVo.getSortBy().equals("pl")&&searchVo.getSortWay().equals("desc")) {
               // 评论降序
               queryBuilder.withSort(SortBuilders.fieldSort("commentCount").order(SortOrder.DESC));
           }else if(searchVo.getSortBy().equals("sj")&&searchVo.getSortWay().equals("asc")){
               // 上架时间
               queryBuilder.withSort(SortBuilders.fieldSort("onSaleTime").order(SortOrder.ASC));
           }else  if(searchVo.getSortBy().equals("sj")&&searchVo.getSortWay().equals("desc")) {
               // 上架时间
               queryBuilder.withSort(SortBuilders.fieldSort("onSaleTime").order(SortOrder.DESC));
           }
       }
      
       // 2.2 分页
       queryBuilder.withPageable(PageRequest.of(searchVo.getPageNum() - 1 ,searchVo.getPageSize()));
      
      
       //3 查询,获取结果
       // 3.1 查询
       Page<SearchSku> pageInfo = this.skuRepository.search(queryBuilder.build());
       // 2.2 总条数
       long total = pageInfo.getTotalElements();
      
       // 3.3 获取返回结果 ,组装返回数据
       List<SearchSku> list = pageInfo.getContent();
       // 返回的结果
       List<ReturnSku> returnList = new ArrayList<>();
       for(SearchSku sku:list){
           ReturnSku rs = new ReturnSku();
           rs.setId(sku.getId().intValue());
           rs.setGoodsName(sku.getSkuName());
           rs.setMidlogo(sku.getLogo());
           rs.setCommentCount(sku.getCommentCount());
           rs.setPrice(sku.getPrice());
      
           returnList.add(rs);
       }
      
       // 3.4 封装
       Map result = new HashMap();
       result.put("count" , total);
       result.put("list" ,returnList);
      
       return result;
      

      }
      }

    Paso 3: escriba un controlador para consultar la información de sku de la categoría especificada de es

    paquete com.czxy.changgou4.controller;

    import com.czxy.changgou4.service.SkuSearchService;
    import com.czxy.changgou4.vo.BaseResult;
    import com.czxy.changgou4.vo.SearchVo;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import javax.annotation.Resource;
    import java.util.Map;

    /**

    • @author 桐叔

    • @email [email protected]
      */
      @RestController
      @RequestMapping(“/sku”)
      public class SkuSearchController {
      @Resource
      private SkuSearchService skuSearchService;

      @PostMapping(“/buscar”)
      public BaseResult findSkus(@RequestBody SearchVo searchVo){

       if(searchVo.getCatId()==null){
           return BaseResult.error("分类不能为空");
       }
      
       Map search = skuSearchService.search(searchVo);
       return BaseResult.ok("查询成功",search);
      

      }
      }

    6.12.6 Pruebe
    http://localhost:10010/search-service/swagger-ui.html
    { "catId": "76", "pageNum": 1, "pageSize": 3, "skuName": "Mobile Phone" , "brandId": 8557, "specList": {"Color del cuerpo": "Blanco", "Memoria": "4GB", "Almacenamiento del cuerpo": "64GB"}, "minPrice": 80000, "maxPrice" : 90000 }








    6.13 Implementación front-end:
    6.13.1 Búsqueda de productos
    Paso 1: Modificar apiclient.js y enviar ajax

    //搜索
    buscar: (params) => { return axios.post('/search-service/sku/search', params ) }

    Paso 2: Modifique el área de datos, agregue condiciones de paginación, consulte los resultados searchResult

    data() { return { searchMap: { //objeto de condición de búsqueda catId: '', //id de categoría brandId: '', //id de marca pageNum: 1, //page: qué página pageSize: 10, //page : el número de elementos que se muestran en cada página





      },
      brandList : [],   //所有品牌
      specList : [],    //所有规格
      searchResult: {} ,  //搜索结果
    
    }
    

    },

    Paso 3: Modificar la función searchList

    async searchList () {
      // ajax 查询
      let { data } = await this.$request.search( this.searchMap )
      // 处理结果
      this.searchResult = data.data
    },
    

    Paso 4: la página se carga correctamente y se consultan los datos

    Paso 5: Mostrar datos

        <!-- 商品列表 start-->
        <div class="goodslist mt10">
          <ul>
            <li v-for="(goods,index) in searchResult.list" :key="index">
              <dl>
                <dt><a href=""><img :src="goods.midlogo" alt="" /></a></dt>
                <dd><a href="goods.html">{
          
          {goods.goodsName}}</a></dd>
                <dd><strong>¥{
          
          {goods.price | priceHandler}}</strong></dd>
                <dd><a href=""><em>已有{
          
          {goods.commentCount}}人评价</em></a></dd>
              </dl>
            </li>
          </ul>
        </div>
        <!-- 商品列表 end-->
    

    Paso 6: optimización de precios, convertir centavos en yuanes y mantener 2 decimales

    filtros: { priceHandler(valor) { if(valor) { return (Número(valor) / 100).toFixed(2) } return value }, },






    Paso 7: Optimización del nombre del producto

    filtros: { substr(value, len, suffix) { // Si la longitud de la cadena que se va a procesar es menor que la longitud especificada, devuelve la cadena que se va a procesar // Si es mayor que la longitud de ejecución, intercepta la cadena y agregue un sufijo return value.length < len ? value : value.substring(0,len) + sufijo }, },





    6.13.2 Búsqueda de productos: especificaciones
    Paso 1: modificar el mapa de búsqueda y mejorar las condiciones de búsqueda

    Paso 2: Mejorar la función specSearch
    specSearch (spec, opción) { //Registrar el id seleccionado this.$set(spec,'selectId', option ? option.id : '' ) //Establecer el parámetro seleccionado this.searchMap. specList [spec.spec_name] = option.option_name //consultar this.searchList() }






    6.13.3 Búsqueda de productos: clasificación
    Paso 1: modifique el área de datos y agregue condiciones de clasificación

    Paso 2: Modificar la etiqueta

    Ordenar por:

    Paso 3: escribir la función sortSearch

    sortSearch(sortBy) {
      if(this.searchMap.sortBy == sortBy) {
        //点击第二次切换排序方式
        this.searchMap.sortWay =  this.searchMap.sortWay == 'asc' ? 'desc' : 'asc'
      } else {
        //第一次默认升序
        this.searchMap.sortBy = sortBy
        this.searchMap.sortWay = 'asc'
      }
      this.searchList()
    }
    

    6.13.4 Búsqueda de productos: rango de precios

    Precio:
    -

    <input type="submit" value="search" @click.prevent="searchList" />

     Verificar: ¿Son consistentes la unidad de precio inicial y la unidad de precio final?
    Unidad de precio final: centavo
    Unidad de precio inicial: yuan

    6.13.5 Búsqueda de productos – Paginación
    Paso 1: Copie el componente de paginación

    Paso 2: Importe el componente de paginación

    Paso 3: Use el componente de paginación

        <!--
    

    组件
                total属性:总条数 (查询结果)
                page_size:每页显示个数(查询条件)
                @page_changed :每一页需要执行函数
                  参数pageNum:表示第几页(当前页)
    –>
            <pagination :total=“searchResult.count”
                  :page_size=“searchMap.pageSize”
                  @page_changed=“pageChanged”>
            

    步骤四:编写处理函数pageChanged

    pageChanged : function( pageNum ){
      //根据第几页查询数据
      this.searchMap.pageNum = pageNum;
      //查询
      this.searchList();
    },
    

    6.13.6商品搜索–关键字搜索
    分析

    步骤一:修改 ~/pages/store/index.js ,在state区域添加keyword关键字

    步骤二:修改 ~components/HeaderSearch.vue,搜索框绑定变量keyword

    data() {
    return {
    keyword: ‘’
    }
    },

    步骤三:给搜索按钮绑定事件,将搜索框绑定的数据同步到vuex

    // 获得vuex中map方法
    import {mapMutations} from ‘vuex’

    export default { métodos: { ...mapMutations(['setData']), // Obtenga el método setData search() en vuex { // Llame al método setData para sincronizar los datos con la variable de palabra clave en vuex this.setData( {'clave': 'palabra clave', 'valor': esta.palabra clave}) } }, }







    <input type="submit" class="btn" @click.prevent="search" value="search" /> Paso
    4: Modifique ~/pages/list/_cid, controle la palabra clave en vuex, si la transmisión de datos cambios, continuar consulta de palabra clave

    ver: { '$store.state.keyword'(newValue, oldValue) { this.searchMap.keyword = newValue this.searchList() }, },




    6.13.7 Optimización de la condición de marca
    Requisito: hay demasiadas condiciones de marca y se proporcionan botones de control para mostrar u ocultar marcas redundantes.

    Paso 1: Proporcione la variable brandMore para controlar la visualización y ocultación de marcas de departamentos redundantes.

    Paso 2: Proporcione atributos calculados para controlar la visualización de los datos de la marca

    calculado: { brandFilter() { // Mostrar todo o mostrar los primeros 19 return this.brandMore ? this.brandList : this.brandList.slice(0,19) }, },




    Paso 3: Mostrar información de solicitud

    <span style="cursor: pointer;" @click="changeBrandMore">{ {this.brandMore ? 'Receive': 'More'}}

    Paso 4: Escriba la función changeBrandMore() para cambiar brandMore
    changeBrandMore() { this.brandMore=!this.brandMore }

    7. Módulo de página de detalles
    7.1 Página de detalles de compilación
    Paso 0: Determine la ruta de acceso
    http://localhost:3000?Goods?id=1

    Paso 1: Cree una página ~pages/Goods.vue

    Paso 2: copie el contenido de ~/static/goods.html e importe recursos de terceros (css, js)
    head: { title: 'list page', link: [ {rel:'stylesheet',href: '/ estilo/bienes .css'}, {rel:'hoja de estilo',href: '/estilo/común.css'}, {rel:'hoja de estilo',href: '/estilo/bottomnav.css'}, {rel:' hoja de estilo', href: '/style/jqzoom.css'}, ], script: [ { tipo: 'texto/javascript', src: '/js/header.js' }, { tipo: 'texto/javascript', origen: '/js/goods.js' }, { tipo: 'texto/javascript', origen: '/js/jqzoom-core.js' }, ] },












    Paso 3: Importar recursos públicos

    Paso 4: agregue la página original js efectos especiales

    7.2 Detalles
    7.2.1 Análisis

    7.2.2接口
    GET http://localhost:10010/web-service/sku/goods/2600242
    返回值
    {
       id:“商品ID,skuid”,
       goods_name:“商品名称”,
       price:“价格”,
       on_sale_date:“上架时间”,
       comment_count:“评论数量”,
       comment_level:“评论级别(1-5)”,
       cat1_info:{
           id:“分类ID”,
           cat_name:“分类名称”
      },
       cat2_info:{
           id:“分类ID”,
           cat_name:“分类名称”
      },
       cat3_info:{
           id:“分类ID”,
           cat_name:“分类名称”
      },
       logo:{
           smlogo:“小LOGO(50x50)”,
           biglogo:“大LOGO(350x350)”,
           xbiglogo: "LOGOTIPO súper grande (800x800)"
      },
       fotos:[
          {            smimg: "Imagen del producto (50x50)",            bigimg: "Imagen del producto (350x350)",            xbigimg: "Imagen del producto (800x800)"       },        …   ],    descripción: "Descripción del producto",    posventa: "Postventa",    existencias: "Inventario",    spec_list:[       {            id: "Specification ID",            spec_name: "Specification Name",            options:[               {                    id: "Option ID",                    option_name: "nombre de la opción"               }                ...           ]       }        ...   ],























       spec_info:{            id_list: "ID de especificación: ID de opción|ID de especificación: ID de opción|...",            id_txt: "Nombre de especificación: Opción de especificación|Nombre de especificación: Opción de especificación|..."   },    sku_list: [       {            skuid: "SKUID",            id_list: "ID de especificación: ID de opción|ID de especificación: ID de opción|..."       },        ...   ] }










    7.2.3初始化数据
    insertar en tb_sku_photo(sku_id,url) valores(2600242,'http://img12.360buyimg.com/n1/s450x450_jfs/t1/100605/24/7603/222062/5dfc6d30Ec375b f0a/e29b6690731acb24.jpg');
    insertar en tb_sku_photo(sku_id,url) valores(2600242,'http://img12.360buyimg.com/n1/s450x450_jfs/t1/110371/2/1323/189888/5dfc6d30E073c3495/cb256ec2d3cf9ae2.jpg');
    insertar en tb_sku_photo(sku_id,url) valores(2600242,'http://img12.360buyimg.com/n1/s450x450_jfs/t1/95005/38/7465/139593/5dfc6d2fEd2317126/63b5253237353618.jpg' );

    7.2.4 Implementación de back-end: JavaBean

    SkuPhoto: todas las imágenes correspondientes a sku
    OneSkuResult: se utiliza para encapsular detalles de sku

    Paso 1: Cree una SkuPhoto, escriba el
    paquete de contenido com.czxy.changgou4.pojo de acuerdo con la tabla tb_sku_photo;

    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import lombok.Data;

    /**

    • Created by liangtong.
      */
      @TableName(“tb_sku_photo”)
      @Data
      public class SkuPhoto {

      @TableId(type = IdType.AUTO)
      private Integer id;
      //外键
      @TableField(value=“sku_id”)
      @JsonProperty(“sku_id”)
      private Integer skuId;
      @TableField(exist = false)
      private Sku sku;

      @TableField(value=“url”)
      private String url;

    }

    步骤二:创建OneSkuResult,根据接口返回结果编写内容
    package com.czxy.changgou4.vo;

    import com.czxy.changgou4.pojo.Category;
    import com.czxy.changgou4.pojo.Specification;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import lombok.Data;

    import java.util.Date;
    import java.util.List;
    import java.util.Map;

    /**

    • @author 桐叔

    • @email [email protected]
      */
      @Data
      public class OneSkuResult {

      skuid de entero privado;
      spuid entero privado;
      @JsonProperty(“nombre_bienes”)
      private String nombreBienes;
      precio doble privado;
      @JsonProperty(“en_fecha_de_venta”)
      private Date enSaleDate;
      @JsonProperty(“recuento_de_comentarios”)
      private Integer recuento de comentarios;
      @JsonProperty(“comment_level”)
      private Integer commentLevel;
      @JsonProperty(“cat1_info”)
      categoría privada cat1Info;
      @JsonProperty(“cat2_info”)
      categoría privada cat2Info;
      @JsonProperty(“cat3_info”)
      categoría privada cat3Info;
      Logotipo de Map<String, String> privado;
      lista privada photos;
      private String description;
      private String aftersale;
      private Integer stock;
      @JsonProperty(“spec_list”)
      private List specList;
      // id_list:‘规格ID:选项ID|规格ID:选项ID|…’,
      // id_txt:‘规格名称:选项名称|规格名称:选项名称|…’
      @JsonProperty(“spec_info”)
      private Map<String, String> specInfo;
      @JsonProperty(“sku_list”)
      private List<Map<String, String>> skuList;

    }

    7.2.5 Implementación de backend: Mapeador

    Paso 1: Modifique skuCommentMapper para completar la función de "nivel de comentario"

    @Select(“SELECT * FROM tb_sku where spu_id = #{spuId}”)
    @ResultMap(“skuResultMap”)
    public List findSkuBySpuId(@Param(“spuId”) Integer spuId);

    步骤二:创建SkuPhotoMapper,完成“通过skuId查询对应的所有的图片”功能
    package com.czxy.changgou4.mapper;

    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.czxy.changgou4.pojo.SkuPhoto;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Result;
    import org.apache.ibatis.annotations.Results;
    import org.apache.ibatis.annotations.Select;

    import java.util.List;

    /**

    • Created by liangtong.
      */
      @Mapper
      public interface SkuPhotoMapper extends BaseMapper {

      /**

      • 通过skuId查询对应的所有的图片
      • @param spuId
      • @return
        */
        @Select(“select * from tb_sku_photo where sku_id = #{spuId}”)
        @Results({ @Result(propiedad=“id”, columna=“id”), @Result(propiedad=“skuId”, column=“sku_id”), @Result(property=“url”, column=“url”) }) public List findSkuPhotoBySkuId(Integer spuId);




    }

    Paso 3: Modifique SkuMapper para agregar la función de "consultar todos los skus con spuId especificado"

    /**

    • Consultar todos los skus del spuId especificado
    • @param spId
    • @return
      */
      @Select(“select * from tb_sku where spu_id = #{spuId}”)
      @ResultMap(“skuResult”)
      public List findSkuBySpuId(Integer spuId);

    7.2.6 Implementación de backend:
    Paso 1: Modificar SkuService y agregar el método findSkuById

    /**

    • Detalles de la Consulta
    • @param skuid
    • @return
      */
      public OneSkuResult findSkuById(Integer skuid);

    Paso 2: Modifique SkuServiceImpl para completar la función "Detalles de consulta"

    paquete com.czxy.changgou4.service.impl;

    importar com.alibaba.fastjson.JSON;
    importar com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    importar com.czxy.changgou4.mapper.*;
    importar com.czxy.changgou4.pojo.Sku;
    importar com.czxy.changgou4.pojo.SkuPhoto;
    importar com.czxy.changgou4.pojo.Especificación;
    importar com.czxy.changgou4.pojo.Spu;
    importar com.czxy.changgou4.service.SkuService;
    importar com.czxy.changgou4.vo.ESData;
    importar com.czxy.changgou4.vo.OneSkuResult;
    importar org.springframework.stereotype.Service;
    importar org.springframework.transaction.annotation.Transactional;

    import javax.annotation.Resource;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    /**

    • @author 桐叔

    • @email [email protected]
      */
      @Service
      @Transactional
      public class SkuServiceImpl extends ServiceImpl<SkuMapper, Sku> implements SkuService {

      @Resource
      private SkuCommentMapper skuCommentMapper;

      @Resource
      private SpuMapper spuMapper;

      @Resource
      private CategoryMapper categoryMapper;

      @Resource
      private SkuPhotoMapper skuPhotoMapper;

      @Resource
      private SpecificationMapper specificationMapper;

      @Override
      public List findESData() { //1 Consulta todos los detalles sku List skulist = baseMapper.findAllSkus();

       //2 将SKU 转换成 ESData
       List<ESData> esDataList = new ArrayList<>();
      
       for (Sku sku:skulist){
           ESData esData = new ESData();
           // id
           esData.setId(sku.getId());
           // 图片地址
           esData.setLogo(sku.getSpu().getLogo());
           // 商品名称
           esData.setSkuName(sku.getSkuName());
           // all  “华为xx {"机身颜色":"白色","内存":"3GB","机身存储":"16GB"} 荣耀 ”
           esData.setAll(sku.getSkuName()+"   " + sku.getSpecInfoIdTxt() + "   " +sku.getSpu().getBrand().getBrandName());
           // on_sale_time
           esData.setOnSaleTime(sku.getSpu().getOnSaleTime());
           // brand_id
           esData.setBrandId(sku.getSpu().getBrandId());
           // cat_id
           esData.setCatId(sku.getSpu().getCat3Id());
           //  Map<String, Object> specs;// 可搜索的规格参数,key是参数名,值是参数值
           Map<String,Object> specs = JSON.parseObject(sku.getSpecInfoIdTxt(), Map.class);
      

    // Mapa newSpecs = new HashMap();
    // for(String key : specs.keySet()){ // newSpecs.put(“spec” + key , specs.get(key)); // }

            esData.setSpecs(specs);
            // price 价格
            esData.setPrice(sku.getPrice());
            // spu_name
            esData.setSpuName(sku.getSpu().getSpuName());
            // stock 库存
            esData.setStock(sku.getStock());
            // description
            esData.setDescription(sku.getSpu().getDescription());
            // packages;//规格与包装
            esData.setPackages(sku.getSpu().getPackages());
            // aftersale;//售后保障
            esData.setAftersale(sku.getSpu().getAftersale());
            // midlogo;
            esData.setMidlogo(sku.getSpu().getLogo());
            // comment_count; 评价数
            Integer comment_count = skuCommentMapper.findNumBySpuId(sku.getSpu().getId());
            esData.setCommentCount(comment_count);
    
            //销售量
            esData.setSellerCount(10);
    
            esDataList.add(esData);
        }
    
        return esDataList;
    
    }
    
    @Override
    public OneSkuResult findSkuById(Integer skuid) {
        OneSkuResult skuResult = new OneSkuResult();
        // 1 查找sku基本信息
        Sku sku = baseMapper.selectById(skuid);
        // 2 根据sku查找spu信息
        Spu spu = spuMapper.findSpuById(sku.getSpuId());
    
        // 3 赋值
        // skuid;
        skuResult.setSkuid(sku.getId());
        // spuid;
        skuResult.setSpuid(sku.getSpuId());
        // 商品名称
        skuResult.setGoodsName(sku.getSkuName());
        // 价格
        skuResult.setPrice(sku.getPrice());
        // 上架时间
        skuResult.setOnSaleDate(spu.getOnSaleTime());
        // 评价数
        Integer comment_count = skuCommentMapper.findNumBySpuId(spu.getId());
        skuResult.setCommentCount(comment_count);
        // 评论级别
        skuResult.setCommentLevel(skuCommentMapper.findAvgStarBySkuId(sku.getId()));
        // 一级分类
        skuResult.setCat1Info(categoryMapper.selectById(spu.getCat1Id()));
        // 二级分类
        skuResult.setCat2Info(categoryMapper.selectById(spu.getCat2Id()));
        // 三级分类
        skuResult.setCat3Info(categoryMapper.selectById(spu.getCat3Id()));
        // 第一张图片
        Map<String,String> logo = new HashMap();
        logo.put("smlogo",spu.getLogo());
        logo.put("biglogo",spu.getLogo());
        logo.put("xbiglogo",spu.getLogo());
        skuResult.setLogo(logo);
        // 通过skuId查询对应的所有的图片
        List<SkuPhoto> skuPhotoList = skuPhotoMapper.findSkuPhotoBySkuId(sku.getId());
        List<Map> photos = new ArrayList<>();
        for(SkuPhoto sp:skuPhotoList){
            Map<String,String> map = new HashMap();
            map.put("smimg",sp.getUrl());
            map.put("bigimg",sp.getUrl());
            map.put("xbigimg",sp.getUrl());
            photos.add(map);
        }
        skuResult.setPhotos(photos);
    
        // description;
        skuResult.setDescription(spu.getDescription());
        // aftersale;
        skuResult.setAftersale(spu.getAftersale());
        // stock;
        skuResult.setStock(sku.getStock());
        // List<SpecResult> spec_list; 根据分类查找规格和规格选项
        List<Specification> spec_list = specificationMapper.findSpecificationByCategoryId(spu.getCat3Id());
        skuResult.setSpecList(spec_list);
        // //id_list:'规格ID:选项ID|规格ID:选项ID|...',
        //  //id_txt:'规格名称:选项名称|规格名称:选项名称|...'
        // Map<String, String> spec_info;
        Map<String,String> spec_info = new HashMap<>();
        spec_info.put("id_list",sku.getSpecInfoIdList());
        spec_info.put("id_txt",sku.getSpecInfoIdTxt());
        skuResult.setSpecInfo(spec_info);
        // List<Map<String, String>> sku_list;
        List<Sku> skuBySpuIdList = baseMapper.findSkuBySpuId(spu.getId());
        List<Map<String, String>> sku_list = new ArrayList<>();
        for(Sku s : skuBySpuIdList){
            Map<String,String> map = new HashMap<>();
            map.put("skuid",s.getId().toString());
            map.put("id_list",s.getSpecInfoIdList());
            sku_list.add(map);
        }
        skuResult.setSkuList(sku_list);
        // 返回结果
        return skuResult;
    }
    

    }

    Paso 3: Modifique SkuController para completar la función "Consultar detalles"

    /**

    • Detalles de la Consulta

    • @param skuid

    • @return
      */
      @GetMapping(“/goods/{skuid}”)
      public BaseResult findSkuById(@PathVariable(“skuid”) Integer skuid){ OneSkuResult sku = skuService.findSkuById(skuid);

      return BaseResult.ok("consulta exitosa", sku);
      }

    7.2.7 Implementación front-end
     La página de detalles debe ser SSR

    Paso 1: Modifique "apiserver.js" y verifique los detalles

    //Detalles del producto
    getGoodsInfo: (skuId) => { return axios.get(“/web-service/sku/goods/” + skuId) }

    步骤二:修改 Goods.vue 页面,使用asyncData进行查询

    async asyncData( { app, query } ) {
    //查询
    let {data:goodsInfoData} = await app.$requestServer.getGoodsInfo( query.id )
    //返回
    return {
    goodsInfo : goodsInfoData.data
    }
    },

    步骤三:修改 Goods.vue 页面,显示当前位置

    步骤四:修改 Goods.vue 页面,处理放大镜图片



    •               
    •                    
    • 步骤五:修改 Goods.vue 页面,商品详情

      • { {spec.spec_name}}:
        { {option.option_name}}

      编写specOptionSelect方法
      methods: {
      specOptionSelect(spec,option) {
      // 拼接标记符,规格id:选项id
      let flag = spec.id + ‘:’ + option.id
      // 判断id_list中是否有‘标记符’,如果没有返回-1
      return this.goodsInfo.spec_info.id_list.indexOf(flag) != -1
      }
      },

      步骤六:修复bug,图片大小的原因,导致“放大镜”中等图太大,遮盖小图
      问题图示

      解决

      7.3规格操作
      点击“规格”时,切换SKU的id

      步骤一:修改 Goods.vue 页面,给每一个规格选项绑定点击事件

      <a @click.prevent=“selectSpec(sl.id , op.id)” v-for=“(op,opi) in sl.options” :key=“opi”

      步骤二:修改 Goods.vue 页面,完成 selectSpec 函数

      selectSpec(specId, optionId) {
        // 1. spec标记, 规格id:选项id,例如,1:1
        var flag = specId + ":" + optionId;
        // 2. 将当前记录规则替换了  1:2|2:6  --> 1:1|2:6
        var re = new RegExp( specId + ":\\d+");
        var newFlag = this.goodsInfo.spec_info.id_list.replace(re ,flag);
      
        // 3. 从sku列表中,获得skuId,跳转页面
        for(var i = 0 ; i< this.goodsInfo.sku_list.length ; i ++ ){
          var id_list = this.goodsInfo.sku_list[i].id_list;
          if(id_list == newFlag) {
            location.href = "goods?id=" + this.goodsInfo.sku_list[i].skuid;
            break;
          }
        }
      }
      

      7.4评论
      7.4.1接口
      GET http://localhost:10010/web-service/comments/spu/2?current=1&size=2
      {
      “code”: 20000,
      “message”: “查询成功”,
      “data”: {
      “impressions”: [
      {
      “id”: 1,
      “title”: “口感不错”,
      “count”: 15326,
      “spu_id”: 2,
      “sku_id”: 2600242
      }
      ],
      “ratio”: {
      “common”: “33.33”,
      “bad”: “33.33”,
      “goods”: “33.33”
      },
      “comment_count”: 3,
      “comments”: [
      {
      “id”: 3,
      “userId”: 1,
      “user”: {
      “face”: “images/user3.jpg”,
      },
      “spuId”: 2,
      “skuId”: 2600248,
      “proporción”: “2”,
      “contenido”: “差”,
      }
      ]
      },
      “otro”: {}
      }

      7.4.2 Análisis

      7.4.3 Implementación de back-end: JavaBean

      Paso 1: Cree una impresión para encapsular los datos en la tabla de impresiones

      paquete com.czxy.changgou4.pojo;

      importar com.baomidou.mybatisplus.annotation.IdType;
      importar com.baomidou.mybatisplus.annotation.TableField;
      importar com.baomidou.mybatisplus.annotation.TableId;
      importar com.baomidou.mybatisplus.annotation.TableName;
      importar com.fasterxml.jackson.annotation.JsonProperty;
      importar lombok.Datos;

      /** Impresión

      • @autor Tío Tong

      • @email [email protected]
        */
        @TableName(“tb_impression”)
        @Data
        public class Impression {

        @TableId(tipo = IdType.AUTO)
        id entero privado;

        private String title;
        private Integer count;

        @TableField(“spu_id”)
        @JsonProperty(“spu_id”)
        private Integer spuId;

        @TableField(“sku_id”)
        @JsonProperty(“sku_id”)
        private Integer skuId;

      }

      步骤二:创建 CommentResult,用于封装评论相关的信息
      package com.czxy.changgou4.pojo;

      import lombok.Data;

      import java.util.List;
      import java.util.Map;

      /**

      • @author 桐叔
      • @email [email protected]
        */
        @Data
        public class CommentResult {
        private List impressions; //印象
        private Map<String,Object> ratio; //好评度
        private Integer comment_count; //评论数
        private List comments; //评论

      }

      7.4.4后端实现:
      步骤一:创建 ImpressionMapper,完成 “查询指定SpuId的所有印象”

      paquete com.czxy.changgou4.mapper;

      importar com.baomidou.mybatisplus.core.mapper.BaseMapper;
      importar com.czxy.changgou4.pojo.Impresión;
      importar org.apache.ibatis.annotations.Mapper;
      importar org.apache.ibatis.anotaciones.Param;
      importar org.apache.ibatis.annotations.Select;

      importar java.util.List;

      /**

      • @autor Tío Tong

      • @email [email protected]
        */
        @Mapper
        interfaz pública ImpressionMapper extiende BaseMapper {

        /**

        • Consultar todas las impresiones del SpuId especificado
        • @param escupió
        • @return
          */
          @Select(“select * from tb_impression where spu_id = #{spuid}”)
          public List findImpressionsBySpuid(@Param(“spuid”) Integer spuid);

      }

      Paso 2: Modifique SkuCommentMapper para completar "Consulte la cantidad de comentarios con una calificación favorable específica"

      /**

      • Consultar el número de comentarios con una calificación positiva específica
      • @param escupió
      • @param ratio 0 buena revisión, 1 revisión media, 2 mala revisión
      • @return
        /
        @Select("select count(
        ) from tb_sku_comment where spu_id = #{spuid} and ratio = #{ratio}")
        public Integer findCommentCountByRatio(@Param(“spuid”)Integer spuid,@Param(“ratio”) proporción de enteros);

      Paso 3: Modifique SkuCommentMapper y complete "Consultar el número de comentarios de SpuId"

      /**

      • Consultar el número de comentarios de SpuId
      • @param escupió
      • @return
        /
        @Select("select count(
        ) from tb_sku_comment where spu_id = #{spuid}")
        public Integer findTotalCommentBySpuid(@Param("spuid") Integer spuid);

      Paso 4: Modifique skuCommentMapper para completar "consultar todos los comentarios del spu especificado"

      /**

      • Consultar todos los comentarios del spu especificado
      • @param escupió
      • @return
        */
        @Select(“select * from tb_sku_comment where spu_id = #{spuid} limit #{startIndex},#{size}”)
        @Results({
        @Result(property = “createdAt” , column = “created_at”),
        @Result(property = “updatedAt” , column = “updated_at”),
        @Result(property = “userId” , column = “user_id”),
        @Result(property = “skuId” , column = “sku_id”),
        @Result(property = “specList” , column = “spec_list”),
        @Result(property = “user” , one = @One(select = “com.czxy.changgou4.mapper.UserMapper.selectById”), column = “user_id”),
        })
        public List findCommentsBySpuid(@Param(“spuid”) Integer spuid, @Param(“startIndex”) Integer startIndex, @Param(“size”) Integer size);

      步骤五:创建 SkuCommentService接口,定义“查询指定spu的所有评论”方法

      paquete com.czxy.changgou4.servicio;

      importar com.baomidou.mybatisplus.extension.service.IService;
      importar com.czxy.changgou4.pojo.SkuComment;
      importar com.czxy.changgou4.vo.CommentResult;
      importar com.czxy.changgou4.vo.PageRequest;

      /**

      • @autor Tío Tong
      • @email [email protected]
        /
        interfaz pública SkuCommentService extiende IService { /
        *
        *
        • @param escupió
        • @param pageRequest
        • @return
          */
          public CommentResult findComments(Integer spuid, PageRequest pageRequest);
          }

      Paso 6: Cree una clase de implementación SkuCommentServiceImpl

      paquete com.czxy.changgou4.service.impl;

      import com.baomidou.mybatisplus.extension.service.IService;
      import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
      import com.czxy.changgou4.mapper.ImpressionMapper;
      import com.czxy.changgou4.mapper.SkuCommentMapper;
      import com.czxy.changgou4.mapper.SkuMapper;
      import com.czxy.changgou4.pojo.Impression;
      import com.czxy.changgou4.pojo.SkuComment;
      import com.czxy.changgou4.service.SkuCommentService;
      import com.czxy.changgou4.vo.CommentResult;
      import com.czxy.changgou4.vo.PageRequest;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      import org.springframework.transaction.annotation.Transactional;

      import javax.annotation.Resource;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;

      /**

      • @author 桐叔

      • @email [email protected]
        */
        @Service
        @Transactional
        public class SkuCommentServiceImpl extends ServiceImpl<SkuCommentMapper, SkuComment> implements SkuCommentService {

        @Resource
        private ImpressionMapper impressionMapper;

        public CommentResult findComments(Integer spuid, PageRequest pageRequest){

         CommentResult commentResult = new CommentResult();
        
         //查询所有印象
         List<Impression> impressionList = impressionMapper.findImpressionsBySpuid(spuid);
         commentResult.setImpressions(impressionList);
        
         //好评度
         Integer goodCount = baseMapper.findCommentCountByRatio(spuid,0);// 好评
         Integer commonCount = baseMapper.findCommentCountByRatio(spuid,1);// 中评
         Integer badCount = baseMapper.findCommentCountByRatio(spuid,2);// 差评
         Integer totalCount = baseMapper.findTotalCommentBySpuid(spuid);//
        
         Map<String,Object> ratio = new HashMap<>();
         ratio.put("goods", String.format("%.2f" , goodCount * 100.0 / totalCount ));
         ratio.put("common",String.format("%.2f" , commonCount * 100.0 / totalCount ));
         ratio.put("bad",String.format("%.2f" , badCount * 100.0 / totalCount ));
         commentResult.setRatio( ratio );
        
         //总评论数
         Integer comment_count = baseMapper.findNumBySpuId(spuid);
         commentResult.setComment_count(comment_count);
        
        
         //查询所有
         int startIndex = (pageRequest.getPageNum() - 1) * pageRequest.getPageSize();
         List<SkuComment> comments = baseMapper.findCommentsBySpuid(spuid, startIndex ,pageRequest.getPageSize());
         commentResult.setComments(comments);
        
        
         return commentResult;
        

        }

      }

      步骤七:创建 SkuCommentController,完成“查询指定spu的所有评论”
      package com.czxy.changgou4.controller;

      import com.czxy.changgou4.service.SkuCommentService;
      import com.czxy.changgou4.vo.BaseResult;
      import com.czxy.changgou4.vo.CommentResult;
      import com.czxy.changgou4.vo.PageRequest;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.PathVariable;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;

      import javax.annotation.Resource;

      /**

      • @author 桐叔
      • @email [email protected]
        */
        @RestController
        @RequestMapping(“/comments”)

      public class SkuCommentController {

      @Resource
      private SkuCommentService skuCommentService;
      
      
      @GetMapping("/spu/{spuid}")
      public BaseResult findCommentsByPage(@PathVariable("spuid") Integer spuid, PageRequest pageRequest){
          CommentResult comments = skuCommentService.findComments(spuid, pageRequest);
          return BaseResult.ok("查询成功", comments);
      }
      

      }

      7.4.5 Implementación front-end
       Paso 1: modificar apiclient.js, agregar la función getComments y completar la operación de comentario de consulta

      //评论
      getComments: (spuid, pageSize, pageNum) => { return axios.get( ,{ params: { pageSize: pageSize, pageNum: pageNum } }) },
      /web-service/comments/spu/${spuid}





      Paso 2: Modifique Goods.vue y escriba la función de comentario de consulta para admitir la paginación

      data() { return { commentVo: { size: 2, current: 1, }, commentResult: { ratio: {} } } } ,









      async pageChanged (num) {
        this.commentVo.current = num
        // ajax 查询
        let { data } = await this.$request.getComments( this.goodsInfo.spuid, this.commentVo.current, this.commentVo.size)
        // 处理结果
        this.commentResult = data.data
      }
      

      Paso 3: Modifique Goods.vue, después de que la página se haya cargado correctamente, consulte los comentarios (primera página)

      //Comenta
      esto.pageChanged(1)

      Paso 4: Modifique Goods.vue para mostrar "buena calificación"

      <div class="rate fl">
           <strong><em>{
              
              {comments.ratio.goods}}</em>%</strong> <br />
           <span>好评度</span>
      </div>
      <div class="percent fl">
          <dl>
               <dt>好评({
              
              {comments.ratio.goods}}%)</dt>
               <dd><div :style="{'width': comments.ratio.goods + 'px'}"></div></dd>
           </dl>
           <dl>
               <dt>中评({
              
              {comments.ratio.common}}%)</dt>
               <dd><div :style="{'width':comments.ratio.common + 'px'}"></div></dd>
           </dl>
           <dl>
               <dt>差评({
              
              {comments.ratio.bad}}%)</dt>
               <dd><div :style="{'width': comments.ratio.bad + 'px'}" ></div></dd>
           </dl>
      
      Paso 5: Modifique Goods.vue para mostrar la "impresión del comprador"
      Impresión del comprador:
      { {ci.title}} ({ {ci.count}})

      步骤六:修改 Goods.vue ,展示“评论”


                      

                        

                          
                          
      { {cc.user.name}}

                        

                      

                      

                        

                           { {cc.created_at}}
                          
                        

                        

                          { {cc.content}}
                        

                        

                           回复(0)
                           有用(0)
                        

                      

                      

                    

                    

      步骤七:修改 Goods.vue ,展示“分页条”

      import Pagination from ‘…/components/Pagination’

                <pagination :total="comments.comment_count"
      

      :page_size=“commentPageSize”
                          @page_changed=“pageChanged” >

      8.购物车模块
      购物车数据2种形态:
      登录态:保存到服务器端的redis中
      没登录:保存在浏览器端 localStorage 中

      8.1搭建购物车服务:8095
      步骤一:创建changgou4-service-cart 项目

      步骤二:修改pom.xml文件,添加坐标

      com.czxy.changgou changgou4-common-auth com.czxy.changgou changgou4-pojo org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-redis redis.clients jedis com. alibaba.nacos nacos-cliente
      <!-- nacos 服务发现 -->
      <dependency>
          <groupId>com.alibaba.cloud</groupId>
          <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
      </dependency>
      
      <!-- openfeign 远程调用 -->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-openfeign</artifactId>
      </dependency>
      
      <!--swagger2-->
      <dependency>
          <groupId>io.springfox</groupId>
          <artifactId>springfox-swagger2</artifactId>
      </dependency>
      <dependency>
          <groupId>io.springfox</groupId>
          <artifactId>springfox-swagger-ui</artifactId>
      </dependency>
      
      <!--fastjson-->
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>fastjson</artifactId>
      </dependency>
      

      Paso 3: Cree un archivo yml,

      #Número de puerto
      servidor:
      puerto: 8095
      spring:
      aplicación:
      nombre: cart-service
      redis:
      host: 127.0.0.1
      nube:
      nacos:
      descubrimiento:
      server-addr: 127.0.0.1:8848 #dirección del servicio nacos
      #contenido personalizado
      sc:
      jwt:
      secret: sc@Login(Auth}*^31)&czxy% # Clave para verificación de inicio de sesión
      pubKeyPath: D:/rsa/rsa.pub # Dirección de clave pública
      priKeyPath: D:/rsa/rsa.pri # Caducidad de dirección de clave privada
      : 360 # Tiempo de caducidad, en minutos

      Paso 4: copie la clase de coordinación JWT + Swagger + Redis

      Paso 5: Comience la clase

      paquete com.czxy.changgou4;

      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
      import org.springframework.cloud.openfeign.EnableFeignClients;

      /**

      • @author 桐叔
      • @email [email protected]
        */
        @SpringBootApplication
        @EnableDiscoveryClient
        @EnableFeignClients
        public class CGCartServiceApplication {
        public static void main(String[] args) {
        SpringApplication.run(CGCartServiceApplication.class, args);
        }
        }

      8.2添加到购物车
      8.2.1整体分析

      8.2.2接口
      POST http://localhost:10010/cart-service/carts
      {
      “skuid”: 2600242,
      “count”: 5,
      “checked”: true
      }

      8.2.3后端实现:JavaBean

      购物车列表项对象:CartItem (某一件商品的购买情况:商品、购买数量 等)
      package com.czxy.changgou4.cart;

      import com.fasterxml.jackson.annotation.JsonProperty;
      import lombok.Data;

      /**

      • @author 桐叔
      • @email [email protected]
        */
        @Data
        public class CartItem { private Integer skuid; spuid entero privado; @JsonProperty(“nombre_bienes”) private String nombreBienes; precio doble privado; recuento de enteros privados;//购买数量Booleano privado verificado; midlogo cadena privado; @JsonProperty(“spec_info”) private String specInfo;









      }

      Objeto del carrito de compras: Carrito

      paquete com.czxy.changgou4.cart;

      importar lombok.Datos;

      importar java.util.HashMap;
      importar java.util.Map;

      /**

      • @autor Tío Tong

      • @email [email protected]
        */
        @Data
        public class Cart {

        mapa privado<Integer, CartItem> data = new HashMap<>();
        privada Doble total;

        public Double getTotal() {
        double sum = 0.0;
        for (CartItem cartItem : data.values()) {
        //只统计勾选的价格
        if(cartItem.getChecked()){
        sum += ( cartItem.getPrice() * cartItem.getCount());
        }
        }
        return sum;
        }

        public void addCart(CartItem cartItem) {
        CartItem temp = data.get(cartItem.getSkuid());
        if(temp == null) {
        data.put( cartItem.getSkuid() , cartItem);
        } else {
        temp.setCount( cartItem.getCount() + temp.getCount() );
        }
        }

        public void updateCart(Integer skuid, Integer count , Boolean checked) {
        CartItem temp = data.get(skuid);
        if(temp != null) {
        temp.setCount( count );
        temp.setChecked(checked);
        }
        }

        public void deleteCart(Integer skuid) {
        data.remove( skuid );
        }

      }

      购物车专门定制的对象
      CartCategory

      package com.czxy.changgou4.vo;

      import com.fasterxml.jackson.annotation.JsonInclude;
      import com.fasterxml.jackson.annotation.JsonProperty;
      import lombok.Data;

      import java.util.ArrayList;
      import java.util.List;

      /**

      • @author 桐叔

      • @email [email protected]
        */
        @Data
        public class CartCategory {

        private Integer id;

        @JsonProperty(“cat_name”)
        private String catName;

        @JsonProperty(“parent_id”)
        private Integer parentId;

        @JsonProperty(“is_parent”)
        private Boolean isParent;

        //当前分类具有的所有孩子
        @JsonInclude(JsonInclude.Include.NON_EMPTY)
        private List children = new ArrayList<>();

      }

      CartSpecificationOption
      package com.czxy.changgou4.vo;

      import com.fasterxml.jackson.annotation.JsonProperty;
      import lombok.Data;

      /**

      • @author 桐叔

      • @email [email protected]
        */
        @Data
        public class CartSpecificationOption {

        private Integer id;

        @JsonProperty(“spec_id”)
        private Integer specId; //外键,规格ID

        private CartSpecification specification; //外键对应对象

        @JsonProperty("option_name")
        private String optionName; //Nombre de la opción

      }

      CartSpecification

      paquete com.czxy.changgou4.vo;

      importar com.fasterxml.jackson.annotation.JsonProperty;
      importar lombok.Datos;

      importar java.util.List;

      /**

      • @autor Tío Tong

      • @email [email protected]
        */
        @Data
        public class CartSpecification {

        id entero privado;

        @JsonProperty(“spec_name”)
        private String specName; //Nombre de especificación

        private Integer categoryId; //categoría clave foránea
        private CartCategory categoría; //categoría clave foránea objeto correspondiente

        Lista privada de opciones; //Una especificación con múltiples opciones de especificación

      }

      CartOneSkuResult

      paquete com.czxy.changgou4.vo;

      importar com.fasterxml.jackson.annotation.JsonProperty;
      importar lombok.Datos;

      importar java.util.Date;
      importar java.util.List;
      importar java.util.Map;

      /**

      • @autor Tío Tong

      • @email [email protected]
        */
        @Data
        public class CartOneSkuResult {

        private Integer skuid;
        private Integer spuid;
        @JsonProperty(“goods_name”)
        private String goodsName;
        private Double price;
        @JsonProperty(“on_sale_date”)
        private Date onSaleDate;
        @JsonProperty(“comment_count”)
        private Integer commentCount;
        @JsonProperty(“comment_level”)
        private Integer commentLevel;
        @JsonProperty(“cat1_info”)
        private CartCategory cat1Info;
        @JsonProperty(“cat2_info”)
        private CartCategory cat2Info;
        @JsonProperty(“cat3_info”)
        private CartCategory cat3Info;
        private Map<String, String> logo;
        private List photos;
        private String description;
        private String aftersale;
        private Integer stock;
        @JsonProperty(“spec_list”)
        private List specList;
        // id_list:‘规格ID:选项ID|规格ID:选项ID|…’,
        // id_txt:‘规格名称:选项名称|规格名称:选项名称|…’
        @JsonProperty(“spec_info”)
        private Map<String, String> specInfo;
        @JsonProperty(“sku_list”)
        private List<Map<String, String>> skuList;

      }

      8.2.4后端实现

      步骤一:创建CartVo,用于封装请求参数

      package com.czxy.changgou4.vo;

      import lombok.Data;

      /**

      • @author 桐叔

      • @email [email protected]
        */
        @Data
        public class CartVo {

        skuid de entero privado; //"SKUID",
        recuento de entero privado; //"Cantidad de compra"
        booleano privado verificado; //"Cantidad de compra"

      }

      Paso 2: Cree un SkuClient para consultar detalles

      paquete com.czxy.changgou4.fingir;

      importar com.czxy.changgou4.vo.BaseResult;
      importar com.czxy.changgou4.vo.OneSkuResult;
      importar org.springframework.cloud.openfeign.FeignClient;
      importar org.springframework.web.bind.annotation.GetMapping;
      importar org.springframework.web.bind.annotation.PathVariable;

      /**

      • @autor Tío Tong

      • @email [email protected]
        */
        @FeignClient(value=“web-service”,path = “/sku”)
        interfaz pública SkuClient {

        @GetMapping(“/goods/{skuid}”)
        public BaseResult findSkuById(@PathVariable(“skuid”) Integer skuid);

      }

      步骤三:创建CartService接口,用于完成添加业务逻辑

      package com.czxy.changgou4.service;

      import com.czxy.changgou4.pojo.User;
      import com.czxy.changgou4.vo.CartVo;

      /**

      • @author 桐叔

      • @email [email protected]
        */
        public interface CartService {

        /**

        • 给指定用户添加商品
        • @param user
        • @param cartVo
          */
          public void addCart(User user , CartVo cartVo);
          }

      步骤四:创建CartService实现类

      package com.czxy.changgou4.service.impl;

      importar com.alibaba.fastjson.JSON;
      importar com.czxy.changgou4.cart.Cart;
      importar com.czxy.changgou4.cart.CartItem;
      importar com.czxy.changgou4.feign.SkuClient;
      importar com.czxy.changgou4.pojo.User;
      importar com.czxy.changgou4.service.CartService;
      importar com.czxy.changgou4.vo.BaseResult;
      importar com.czxy.changgou4.vo.CartOneSkuResult;
      importar com.czxy.changgou4.vo.CartVo;
      importar com.czxy.changgou4.vo.OneSkuResult;
      importar org.springframework.data.redis.core.StringRedisTemplate;
      importar org.springframework.stereotype.Service;
      importar org.springframework.transaction.annotation.Transactional;

      importar javax.anotación.Recurso;

      /**

      • @autor Tío Tong

      • @email [email protected]
        */
        @Service
        @Transactional
        public class CartServiceImpl implements CartService {

        @Resource
        private StringRedisTemplate stringRedisTemplate;

        @Resource
        private SkuClient skuClient;

        @Override
        public void addCart(User user, CartVo cartVo) {
        //1 获得购物车
        Cart cart;
        String key = “cart” + user.getId();
        String cartStr = stringRedisTemplate.opsForValue().get(key);
        // 处理是否有购物车,没有创建,有转换(jsonStr --> java对象 )
        if(cartStr != null){
        //如果有,将json字符串转换购物车对象
        cart = JSON.parseObject( cartStr , Cart.class);

         } else {
             //如果没有创建一个
             cart = new Cart();
         }
        
         //2 保存商品
         // 2.1 确定购物买商品
         BaseResult<CartOneSkuResult> entity = skuClient.findSkuById(cartVo.getSkuid());
         CartOneSkuResult oneSkuResult = entity.getData();
        
         // * 将OneSkuResult 转换成 CartItem
         CartItem cartItem = new CartItem();
         cartItem.setSkuid( oneSkuResult.getSkuid() );
         cartItem.setSpuid( oneSkuResult.getSpuid() );
         cartItem.setGoodsName( oneSkuResult.getGoodsName() );
         cartItem.setPrice( oneSkuResult.getPrice() );
         cartItem.setCount( cartVo.getCount() );        //购买数量,用户传递的
         cartItem.setChecked(true);
         cartItem.setMidlogo( oneSkuResult.getLogo().get("biglogo"));
         cartItem.setSpecInfo( JSON.toJSONString( oneSkuResult.getSpecInfo() ) );   //将对象转换json字符串
        
         // 2.2 添加到购物车
         cart.addCart( cartItem );
        
         System.out.println(JSON.toJSONString(cart) );
         //3 保存购物车
         stringRedisTemplate.opsForValue().set( key , JSON.toJSONString(cart) );
        

        }
        }

      步骤五:创建CartController

      package com.czxy.changgou4.controller;

      import com.czxy.changgou4.config.JwtProperties;
      import com.czxy.changgou4.pojo.User;
      import com.czxy.changgou4.service.CartService;
      import com.czxy.changgou4.utils.JwtUtils;
      import com.czxy.changgou4.vo.BaseResult;
      import com.czxy.changgou4.vo.CartVo;
      import org.springframework.web.bind.annotation.PostMapping;
      import org.springframework.web.bind.annotation.RequestBody;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;

      import javax.annotation.Resource;
      import javax.servlet.http.HttpServletRequest;

      /**

      • @author 桐叔

      • @email [email protected]
        */
        @RestController
        @RequestMapping(“/carts”)
        public class CartController {

        @Resource
        private CartService cartService;

        @Resource
        private HttpServletRequest request;

        @Resource
        private JwtProperties jwtProperties;

        @PostMapping
        public BaseResult addCart(@RequestBody CartVo cartVo){

         //1 获得用户信息
         // 1.1 获得token
         String token = request.getHeader("Authorization");
         // 1.2 解析token
         User loginUser = null;
         try {
             loginUser = JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(),User.class);
         } catch (Exception e) {
             return BaseResult.error("token失效或未登录");
         }
        
         //2 添加操作
         cartService.addCart( loginUser , cartVo );
        
         //3 提示
         return BaseResult.ok("添加成功");
        

        }

      }

      步骤六:测试

      8.2.5前端实现:购买数量
      步骤一:修改Goods.vue ,为文本框添加键盘事件,用于校验输入的数据

      <input type=“text” name=“amount” v-model=“buyCount” @keyup.prevent=“updateCount($event)” value=“1” class=“amount”/>

      步骤二:修改Goods.vue ,完成updateCount函数

      updateCount : function(e){
        // e.target.value 获得用户输入的数据
        //使用正则处理数字
        if( /^\d+$/.test(e.target.value) ){
          //如果是数字,小于1,默认为1
          if( e.target.value < 1) {
            this.buyCount = 1;
          }
        } else {
          //默认为1
          this.buyCount = 1;
        }
      },
      

      步骤三:检查+和-已完成功能

      8.2.6前端实现:
      步骤一:修改 api.js ,完成“添加到购物车”方法

      //添加到购物车
      addToCart : ( params ) => {
      return axios.post(“/cart-service/carts”, params )
      },

      步骤二:修改Goods.vue,给“加入购物车”绑定点击事件 addToCartFn

      <input type=“submit” value=“” class=“add_btn” @click.prevent=“addToCartFn” />

      步骤三:修改Goods.vue,完成addToCartFn功能
      未登录:保存到sessionStorage
      登录:保存到redis
      待完善功能:用户登录时,将sessionStorage保存的商品信息合并到redis中
      async addToCartFn(){
      //获得登录标识
      let token = sessionStorage.getItem(“token”);
      if(token != null){
      //登录:发送ajax进行添加
      let newGoods = {
      skuid: this.KaTeX parse error: Expected 'EOF', got '}' at position 55: …yCount }̲; //登录状…request.addToCart(newGoods)
      if(data.code == 20000){
      //location.href = “flow1”
      this.$router.push(‘flow1’)
      } else {
      alert(data.data.errmsg);
      }
      return;
      }

        //未登录:在浏览器保存
        //1 准备添加物品数据
        var newGoods = {
          skuid: this.goodsInfo.skuid,
          goods_name:this.goodsInfo.goods_name,
          price:this.goodsInfo.price,
          count:this.buyCount,
          checked:true,
          midlogo:this.goodsInfo.logo.smlogo,
          spec_info: JSON.stringify(this.goodsInfo.spec_info)
        };
        //2 维护数据:本地已经存储信息 和 新添加信息 合并
        var cartStr = localStorage.getItem("cart");
        var cart;
        if(cartStr == null) {
          // 2.1 第一次添加,直接已数组方式添加
          cart = [newGoods];
        } else {
          //判断是否存在(将字符串转换数组、依次遍历)
          cart = JSON.parse(cartStr);
          //是否为新物品,默认为新的
          let isNew = true;
      
          cart.forEach( g => {
            //已有数据的id 和 新物品id 是否一样
            if(g.skuid == newGoods.skuid){
              //不是新物品
              isNew = false;
              // 2.3 已有,重复,先获得对应,修改数量
              g.count += parseInt(newGoods.count);
            }
          });
      
          if(isNew == true){
            // 2.2 已有,不重复,先获得数组,后追加
            cart.push(newGoods);
          }
      
        }
        //3 存放到浏览器
        var cartStr = JSON.stringify(cart);
        localStorage.setItem("cart" , cartStr );
        //4 跳转页面
        location.href = "flow1"
      }
      

      步骤四:编写flow1页面

      购物车

      8.3 Ver el carrito de compras
      8.3.1 Análisis
       Si el usuario no ha iniciado sesión, el carrito de compras se almacena en el almacenamiento local del navegador y se almacena en una matriz.
      Si el usuario inicia sesión, el carrito de compras se almacena en redis y se almacena como una cadena de objeto de carrito.
      Problema: los datos de front-end y back-end son inconsistentes, y flow1.vue no se puede usar para la visualización de datos
      Solución: Simplifique los datos del carrito de back-end, Objeto de carrito -> Mapa (datos) -> Lista (valores)

      Conclusión: el front-end proporciona una matriz unificada a la página y la página puede mostrar los datos.

      8.3.2接口
      OBTENGA http://localhost:10010/cart-service/carts

      {
          “code”: 1,
          “message”: “查询成功”,
          “data”: {
              “data”: {
                  “2600242”: {
                      “skuid”: 2600242,
                      “spuid”: 2,
                      “price”: 84900.0,
                      “count”: 17,
                      “checked”: true,
                      “midlogo”: null,
                      “goods_name”: “华为 G9 青春版 白色 移动联通电信4G手机 双卡双待”,
                      “spec_info”: “{“id_list”:“1:1|2:6|6:22”,“id_txt”:”{\“机身颜色\”:\“白色\”,\“内存\”:\“3GB\”,\“机身存储\”:\“16GB\”}“}”
                  }
              },
              “total”: 1443300.0
          },
          “other”: {}
      }

      8.3.3 Implementación de back-end
       Paso 1: Modifique CartService, agregue el método queryCartList, consulte la información del carrito de compras de redis 
      Paso 2: Modifique CartController, agregue el método queryCartList, devuelva solo los datos en el carrito de compras

      Paso 1: Modifique CartService, agregue el método queryCartList,
      /**
      *

      • usuario @param
      • @return
        */
        public Cart queryCartList(User user);

      Paso 2: Modifique CartServiceImpl para consultar la información del carrito de compras de redis
      /**

      • Consultar carro de la compra

      • usuario @param

      • @return
        */
        public Cart queryCartList(User user) { String key = "cart" + user.getId(); // Obtener el objeto de operación hash String cartString = this.stringRedisTemplate.opsForValue().get(key);


        // 2 Obtener el carrito de compras, si no crear uno
        return JSON.parseObject(cartString, Cart.class);
        }

      Paso 3: Modifique CartController, agregue el método queryCartList y solo devuelva los datos en el carrito de compras

      /**

      • Consultar carro de la compra

      • @return
        */
        @GetMapping
        public BaseResult queryCartList() {

        //1 获得用户信息
        // 1.1 获得token
        String token = request.getHeader(“Authorization”);
        // 1.2 解析token
        User loginUser = null;
        try {
        loginUser = JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(),User.class);
        } catch (Exception e) {
        return BaseResult.error(“token失效或未登录”);
        }

        Cart cart = this.cartService.queryCartList(loginUser);

        return BaseResult.ok(“查询成功”, cart.getData().values());

      }

      8.3.4前端实现:显示页面
      步骤一:创建 ~/pages/flow1.vue 组件,拷贝 ~/static/flow1.html内容

      步骤二:导入js和css

      head: {
      title: ‘首页’,
      link: [
      {rel:‘stylesheet’,href: ‘/style/cart.css’},
      ],
      script: [
      { type: ‘text/javascript’, src: ‘/js/cart1.js’ },
      ]
      },

      步骤三:添加公共组件

      8.3.5前端实现:显示购物车信息
      步骤一:修改api.js 查询购物车信息
      步骤二:页面加载成功后,获得购物车信息(如果登录从后端获取,如果没有登录从浏览器端获得)
      步骤三:遍历显示购物车信息,
      步骤四:通过计算属性,计算总价格

      步骤一:修改apiclient.js 查询购物车信息
      //查询购物车
      getCart : () => {
      return axios.get(“/cart-service/carts”)
      },

      步骤二:页面加载成功后,获得购物车信息(如果登录从后端获取,如果没有登录从浏览器端获得)
      data() {
      return {
      cart : [],        //购物车对象
      }
      },
      async mounted() {
      //获得token信息,表示登录
      this.token = sessionStorage.getItem(“token”)
          if(this.token != null) {
            //登陆:服务器获得数据
            let { data } = await this.$request.getCart()
            this.cart = data.data
          } else {

      //未登录:从浏览器本地获得购物车
            let cartStr = localStorage.getItem(“cart”);
            if(cartStr != null) {
              this.cart = JSON.parse(cartStr);
            }
          }
      },

      步骤三:遍历显示购物车信息

          <tbody>
            <!-- 购物车列表 start -->
            <tr v-for="(goods,k) in cart" :key="k">
              <td class="col1">
                <a href=""><img :src="goods.midlogo" alt="" /></a>
                <strong><a href="">{
              
              {goods.goods_name}}</a></strong>
              </td>
              <td class="col2">
                <span style="display: block;" v-for="(value,key,index) in JSON.parse(JSON.parse(goods.spec_info).id_txt)"
                  :key="index">{
              
              {key}}:{
              
              {value}}</span>
              </td>
              <td class="col3">¥<span>{
              
              { (goods.price/100).toFixed(2) }}</span></td>
              <td class="col4">
                <a href="javascript:;" class="reduce_num"  @click.prevent="minus(goods)"></a>
                <input type="text" name="amount" v-model="goods.count" @keyup="updateCount(goods,$event)" value="1" class="amount"/>
                <a href="javascript:;" class="add_num"  @click.prevent="plus(goods)"></a>
              </td>
              <td class="col5">¥<span>{
              
              { (goods.price / 100 * goods.count).toFixed(2) }}</span></td>
              <td class="col6"><a href="" @click.prevent="del(k)">删除</a></td>
            </tr>
            <!-- 购物车列表 end -->
          </tbody>
      

      步骤四:通过计算属性,计算总价格

      calculado : {     precio total : function(){ //calcular el precio total       //suma de todos los subtotales       let sum = 0 ;       this.cart.forEach( g => {         sum += (g.price * g.count);       } );       return (suma/100).toFixed(2); }   },








      8.4 Operación del carrito de compras: Modificación a
      8.4.1 Análisis

      8.4.2 Interfaz
      PUT http://localhost:10010/cart-service/carts

      8.4.3 Implementación de back-end: Actualización
      Paso 1: Modificar la interfaz de servicio
      /**

      • Actualizar operación: modificar los datos si los datos existen, eliminar los datos si los datos no existen
      • usuario @param
      • @param cartVoList
        */
        public void updateCart(User user, List cartVoList) ;

      Paso 2: Modificar la clase de implementación del servicio

      /**

      • Actualizar operación: modificar los datos si los datos existen, eliminar los datos si los datos no existen

      • usuario @param

      • @param cartVoList
        */
        public void updateCart(User user, List cartVoList) { //1 Obtener carrito de compras String key = “cart” + user.getId(); String cartStr = stringRedisTemplate.opsForValue().get(key); / / Procesar si hay un carrito de compras Cart cart = JSON.parseObject( cartStr , Cart.class); if(cart == null) { throw new RuntimeException("El carrito de compras no existe"); }







        //2 Actualización
        //2.1 Procesamiento de datos de solicitud
        Map<Integer, CartVo> requestMap = new HashMap<>();
        for (CartVo cartVo : cartVoList) { requestMap.put(cartVo.getSkuid(), cartVo); }

        //2.2 Manejar la actualización y eliminar
        Set keySet = new HashSet<>(cart.getData().keySet());
        for (Integer skuid : keySet) { CartVo cartRequest = requestMap.get(skuId); if(cartRequest != null ) { // Actualizar cart.updateCart(cartRequest.getSkuid() , cartRequest.getCount() , cartRequest.getChecked()); } else { // Eliminar cart.deleteCart(skuId); } }








        //3 Guarde el carrito de compras
        stringRedisTemplate.opsForValue().set( key , JSON.toJSONString(cart) );
        }

      Paso 3: Modificar el controlador

      @PutMapping
      public BaseResult updateCart(@RequestBody List cartVoList) {

      //1 获得用户信息
      // 1.1 获得token
      String token = request.getHeader("Authorization");
      // 1.2 解析token
      User loginUser = null;
      try {
          loginUser = JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(),User.class);
      } catch (Exception e) {
          return BaseResult.error("token失效或未登录");
      }
      
      try {
          this.cartService.updateCart(loginUser ,cartVoList);
      
          return BaseResult.ok("成功");
      } catch (Exception e) {
          return BaseResult.error("失败");
      }
      

      }

      8.4.4前端实现:修改
      步骤0:修改apiclient.js,添加 updateCart函数
      步骤一:修改flow1.vue 给按钮和文本框添加事件
      步骤二:编写修改对应的事件
      步骤三:编写购物车cart的监听函数,只要数据发生改变立即保存
      步骤四:删除之前绑定js

      步骤0:修改apiclient.js,添加 updateCart函数

      updateCart : ( params ) => {
      return axios.put(“/gccartservice/carts” ,params )
      },

      步骤一:修改flow1.vue 给按钮和文本框添加事件

      步骤二:编写修改对应的事件
      methods: {
      minus : function(goods){
      if( goods.count > 1) {
      goods.count --;
      }
      },
      plus : function(goods){
      //可以考虑库存
      goods.count ++;
      },
      updateCount : function(goods,e){
      console.info(e.target.value);
      if( /^\d+$/.test( e.target.value) ){
      goods.count = e.target.value;
      } else {
      goods.count = 1;
      }
      },

      步骤三:编写购物车cart的监听函数,只要数据发生改变立即保存
      watch: {
          //深度监听
          cart : {
            async handler(val, oldVal) {
      //1 更新/删除,数据
      if(this.token != null){
                let { data } = await this.$request.updateCart( val )
                if(data.code == 1){
                  //alert(data.message);
                }
              } else {
                //未登录
                localStorage.setItem(“cart” , JSON.stringify( val ))
      }
            },
            immediate: false, //true代表如果在 watch 里声明了之后,就会立即先去执行里面的handler方法
            deep: true      //深度监听,常用于对象下面属性的改变
          }

      },

      步骤四:删除之前绑定js

      8.4.5前端实现:全选
      步骤一:修改表格
      步骤二:修改样式
      步骤三:添加全选方法
      步骤四:监听购物车数据,修改全选状态

      步骤一:修改表格

                

      步骤二:修改样式

      .mycart .col0{width: 5%;}
      .mycart .col1{width: 35%;}

      步骤三:添加全选方法

      data() {
      return {
      cart : [],        //购物车对象
      allChecked : false, //全选状态
      }
      },

      methods: {
      checkAll : function(){
      //所有列表项的状态,与全选的状态,相反
      this.cart.forEach( g => {
      g.checked = !this.allChecked;
      });
      },
      },

      步骤四:监听购物车数据,修改全选状态
      watch: {
          //深度监听
          cart : {
            async handler(val, oldVal) {
      //1 更新/删除,数据
      if(this.token != null){
                let { data } = await this.$request.updateCart( val )
                if(data.code == 1){
                  //alert(data.message);
                }
              } else {
                //未登录
                localStorage.setItem(“cart” , JSON.stringify( val ))
      }
      //2 处理全选
      let checkCount = 0;
      this.cart.forEach( g => {
      if( g.checked ) {
      checkCount ++;
      }
      });
      //全选状态,选中个数 与 总个数 比较结果
      this.allChecked = (checkCount == this.cart.length);

      },
            inmediato: falso, //verdadero significa que si se declara en el reloj, ejecutará inmediatamente el método del controlador dentro de
            profundo: verdadero //Monitoreo profundo, a menudo utilizado para cambiar las propiedades debajo del objeto
          }

      },

      8.4.6 Implementación de backend: eliminar datos
       Solo necesita modificar los datos, el reloj ha completado la operación de eliminación

      Paso 1: Modificar el evento de eliminación de enlace html

      Paso 2: escribir la función de eliminación

      del (index){
        if(window.confirm("您确定要删除吗?")){
          this.cart.splice(index , 1)
        }
      },
      

      8.5 Liquidación

      8.5.1 Página de salto
      Paso 1: vincular eventos a la liquidación

      submit : function(){
        if(this.token != null){
          //登录
          //location.href = "flow2.html";
          this.$router.push('flow2')
        } else {
          //确定登录成功后调整的页面
          localStorage.setItem("returnURL","flow2");
          //没有登录
          //location.href = "login";
          this.$router.push('login')
      
        }
      
      },
      

      Paso 2: escriba el componente flow2.vue

      Paso 3: Complete el inicio de sesión

          let returnURL = localStorage.getItem("returnURL");
          if(returnURL != null){
            //删除跳转页面缓存
            localStorage.removeItem("returnURL");
            //需要跳转页面
            location.href= returnURL;
          } else {
            //默认:跳转首页
            location.href = "/";
          }
      

      9. Módulo de pedidos
      9.1 Servicio de pedidos de compilación: 8100
      Paso 1: Proyecto de compilación, changgou4-service-orders

      Paso 2: Modifique el archivo pom.xml y agregue dependencias

      com.czxy.changgou changgou4-common-db com.czxy.changgou changgou4-common-auth com.czxy.changgou changgou4-pojo
      <!--web起步依赖-->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      
      <!-- nacos 客户端 -->
      <dependency>
          <groupId>com.alibaba.nacos</groupId>
          <artifactId>nacos-client</artifactId>
      </dependency>
      
      <!-- nacos 服务发现 -->
      <dependency>
          <groupId>com.alibaba.cloud</groupId>
          <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
      </dependency>
      <!--redis-->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
      <dependency>
          <groupId>redis.clients</groupId>
          <artifactId>jedis</artifactId>
      </dependency>
      <!-- openfeign 远程调用 -->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-openfeign</artifactId>
      </dependency>
      <!--swagger2-->
      <dependency>
          <groupId>io.springfox</groupId>
          <artifactId>springfox-swagger2</artifactId>
      </dependency>
      <dependency>
          <groupId>io.springfox</groupId>
          <artifactId>springfox-swagger-ui</artifactId>
      </dependency>
      <!--fastjson-->
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>fastjson</artifactId>
      </dependency>
      
      <!--微信支付-->
      <dependency>
          <groupId>com.github.wxpay</groupId>
          <artifactId>wxpay-sdk</artifactId>
          <version>0.0.3</version>
      </dependency>
      

      Paso 3: Modificar el archivo yml

      servidor:
      puerto: 8100

      primavera:
      aplicación:
      nombre: fuente de datos del servicio de pedidos
      :
      url: jdbc:mysql://127.0.0.1:3306/changgou_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
      nombre de usuario: root
      contraseña: 1234
      driver-class-name: com. mysql.jdbc.Driver
      druid:
      tamaño inicial: 5
      min-idle: 5
      max-active: 20
      max-wait: 1000
      test-on-borrow: true
      redis:
      base de datos: 0
      host: 127.0.0.1
      puerto: 6379
      nube:
      nacos :
      descubrimiento:
      dirección-servidor: 127.0.0.1:8848 #nacos服务地址

      sc:
      trabajador:
      workerId: 1
      datacenterId: 1
      jwt:
      secret: sc@Login(Auth}*^31)&czxy% # clave de verificación de inicio de sesión
      pubKeyPath: D:/rsa/rsa.pub # dirección de clave pública
      priKeyPath: D :/rsa /rsa.pri # Caducidad de la dirección de clave privada
      : 360 # Tiempo de caducidad,
      pago en minutos:
      appID: wx8397f8696b538317
      mchID: 1473426802
      clave: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
      httpConnectTimeoutMs: 5000
      httpReadTimeoutM s: 10000

      Paso 4: Inicie el
      paquete de clase com.czxy.changgou4;

      importar org.springframework.boot.SpringApplication;
      importar org.springframework.boot.autoconfigure.SpringBootApplication;
      importar org.springframework.cloud.client.discovery.EnableDiscoveryClient;
      importar org.springframework.cloud.openfeign.EnableFeignClients;

      @SpringBootApplication
      @EnableDiscoveryClient
      @EnableFeignClients
      public class CGOrderServiceApplication {
      public static void main(String[] args) {
      SpringApplication.run(CGOrderServiceApplication.class, args);
      }
      }

      9.2收货人列表
      9.2.1接口
      GET http://localhost:10010/order-service/address
      {
          “code”: 20000,
          “message”: “查询成功”,
          “data”: [
              {
                  “id”: 1,
                  “userId”: 1,
                  “shr_name”: “小明”,
                  “shr_mobile”: “13344445555”,
                  “shr_province”: “江苏省”,
                  “shr_city”: “宿迁市”,
                  “shr_area”: “沭阳县”,
                  “shr_address”: “常州路57号”,
                  “isdefault”: 0
              }
          ],
          “other”: {}
      }

      9.2.2后端实现:JavaBean
      步骤一:创建Address地址对象

      步骤二:根据表结构和接口规范,编写内容

      package com.czxy.changgou4.pojo;

      importar com.baomidou.mybatisplus.annotation.IdType;
      importar com.baomidou.mybatisplus.annotation.TableField;
      importar com.baomidou.mybatisplus.annotation.TableId;
      importar com.baomidou.mybatisplus.annotation.TableName;
      importar com.fasterxml.jackson.annotation.JsonProperty;
      importar lombok.Datos;

      /**

      • Creado por liantong.
        */
        @TableName(“tb_address”)
        @Data
        clase pública Dirección {

        @TableId(type = IdType.AUTO)
        private Integer id;
        //用户ID
        @TableField(value = “user_id”)
        private Long userId;
        //收货人姓名
        @TableField(value = “shr_name”)
        @JsonProperty(“shr_name”)
        private String shrName;
        //收货人手机
        @TableField(value = “shr_mobile”)
        @JsonProperty(“shr_mobile”)
        private String shrMobile;
        //收货人省份
        @TableField(value = “shr_province”)
        @JsonProperty(“shr_province”)
        private String shrProvince;
        //收货人城市
        @TableField(value = “shr_city”)
        @JsonProperty(“shr_city”)
        private String shrCity;
        //收货人地区
        @TableField(value = “shr_area”)
        @JsonProperty(“shr_area”)
        private String shrArea;
        //Dirección de detalles del destinatario
        @TableField(value = “shr_address”)
        @JsonProperty(“shr_address”)
        private String shrAddress;
        //1: predeterminado; 0: no
        @TableField( value = "es predeterminado")
        @JsonProperty("es predeterminado")
        private Integer es predeterminado;

      }

      9.2.3 Implementación de back-end:
      Requisitos: consultar la lista de destinatarios del usuario conectado actualmente
      La información del usuario debe obtenerse a través del token

      Paso 1: Copie la clase de configuración

      Paso 2: escriba el mapeador, solo use el mapeador general

      paquete com.czxy.changgou4.mapper;

      importar com.baomidou.mybatisplus.core.mapper.BaseMapper;
      importar com.czxy.changgou4.pojo.Dirección;
      importar org.apache.ibatis.annotations.Mapper;

      La interfaz pública de @Mapper
      AddressMapper amplía BaseMapper

      { }

      Paso 3: escriba la interfaz de servicio y escriba el método findAllByUserId para completar la función

      package com.czxy.changgou4.service;

      import com.baomidou.mybatisplus.extension.service.IService;
      import com.czxy.changgou4.pojo.Address;

      import java.util.List;

      public interface AddressService extends IService

      {

      /**
       * 查询指定用户的所有地址
       * @param userId
       * @return
       */
      public List<Address> findAllByUserId(Long userId) ;
      

      }

      步骤四:编写service实现

      步骤五:编写controller

      package com.czxy.changgou4.controller;

      import com.czxy.changgou4.config.JwtProperties;
      import com.czxy.changgou4.pojo.Address;
      import com.czxy.changgou4.pojo.User;
      import com.czxy.changgou4.service.AddressService;
      import com.czxy.changgou4.utils.JwtUtils;
      import com.czxy.changgou4.vo.BaseResult;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;

      importar javax.anotación.Recurso;
      importar javax.servlet.http.HttpServletRequest;
      importar java.util.List;

      @RestController
      @RequestMapping(“/dirección”)
      public class AddressController {

      @Resource
      private AddressService addressService;
      
      @Resource
      private JwtProperties jwtProperties;
      
      @Resource
      private HttpServletRequest request;
      
      @GetMapping
      public BaseResult queryAddress(){
      
          //1 获得用户信息
          // 1.1 获得token
          String token = request.getHeader("Authorization");
          // 1.2 解析token
          User loginUser = null;
          try {
              loginUser = JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(),User.class);
          } catch (Exception e) {
              return BaseResult.error("token失效或未登录");
          }
      
          //2 查询
          List<Address> list = this.addressService.findAllByUserId( loginUser.getId() );
      
          return BaseResult.ok("查询成功", list );
      }
      

      }

      9.2.4 Implementación front-end
       Requisito: consultar todas las direcciones de los destinatarios
       Para facilitar la operación, debe usar el filtro para filtrar la dirección predeterminada

      Paso 1: Modifique apiclient.js y agregue la función de consultar la lista de destinatarios

      getAddress : () => {     return axios.get(“/order-service/address”)   },

      Paso 2: Modifique el componente flow2.vue, la página se carga correctamente y consulte todas las direcciones de destinatario del usuario que ha iniciado sesión actualmente
      Filtrar los datos de dirección predeterminados al mismo tiempo

      mount() { // Consulta la dirección del destinatario this.getAddressFn() }, data() { return { addressList: [], //todas las direcciones defaultAddress: {}, //dirección predeterminada } }, métodos: { async getAddressFn ( ) { let { data } = esperar esto.$request.getAddress() // Todas las direcciones de los recolectores this.addressList = data.data //Dirección predeterminada this.defaultAddress = this.addressList.filter(item => item.isdefault == 1)[0]; } },

















      Paso 3: Modifique el componente flow2.vue para mostrar la dirección predeterminada

      { {dirección predeterminada.shr_name}} { {dirección predeterminada.shr_mobile}}

      { {defaultAddress.shr_province}} { {defaultAddress.shr_city}} { {defaultAddress.shr_area}} { {defaultAddress.shr_address}}

      步骤四:修改flow2.vue,显示收货人地址列表


                    


    •                 
                        { {addr.shr_name}} { {addr.shr_province}} { {addr.shr_city}} { {addr.shr_area}} { {addr.shr_address}} { {addr.shr_mobile}}
                      设为默认地址
                      编辑
                      删除
                    
    • 9.3添加联系人
      9.3.1需求
      需求:新添加的联系人为默认联系人

      9.3.2显示添加表单
      修改flow2.vue,显示添加表单

      data() {     return {       addressList: [], //todas las direcciones       defaultAddress: {}, //dirección predeterminada       showNew: false, //si mostrar nuevas direcciones     }   },





      Use variables para controlar la visualización y ocultación del formulario agregado


                    


    •                 <input type="radio" name="dirección" marcada="addr.isdefault == 1" @click="clickRadio(addr)" /> { { addr.shr_name }} { {addr.shr_province}
                        } { { addr .shr_city}} { {addr.shr_area}} { {addr.shr_address}} { {addr.shr_mobile}}
                      establecer como dirección predeterminada
                      editar
                      eliminar { {showNew==false && addr.isdefault == 1}} 
                    

    •               
    • <input type="radio" name="address" class="new_address" @click="showNew=true" /> Usar nueva dirección

    •             
                  

      9.3.3接口
      POST http://localhost:10010/order-service/address
      {
      “shr_name”: “张三”,
      “shr_mobile”: “13344445555”,
      “shr_province”: “江苏省”,
      “shr_city”: “宿迁市”,
      “shr_area”: “沭阳县”,
      “shr_address”: “常州路57号”
      }

      9.3.4后端实现
      步骤一:修改mapper,添加指定用户的地址默认状态

      package com.czxy.changgou4.mapper;

      import com.baomidou.mybatisplus.core.mapper.BaseMapper;
      import com.czxy.changgou4.pojo.Address;
      import org.apache.ibatis.annotations.Mapper;
      import org.apache.ibatis.annotations.Param;
      import org.apache.ibatis.annotations.Update;

      @Mapper
      public interface AddressMapper extends BaseMapper

      {

      /**
       * 修改用户默认地址
       * @param userId
       * @param defaultValue
       */
      @Update("update tb_address set isdefault = #{defaultValue} where user_id = #{userId}")
      public void updateDefault(@Param("userId") Integer userId, @Param("defaultValue") int defaultValue);
      

      }

      步骤二:修改service,添加地址,并将新地址设置成默认地址。

      /**

      • 添加新地址,并设置成默认地址
      • @param address
        */
        public void addAddress(Address address) ;

      步骤三:修改service实现

      /**

      • 添加新地址,并设置成默认地址

      • @param address
        */
        public void addAddress(Address address) {
        //修改指定用户的状态
        this.addressMapper.updateDefault(address.getUserId(),0);

        //添加新地址,默认
        address.setIsdefault(1);
        this.addressMapper.insert(address);
        }

      步骤四:修改controller
      @PostMapping
      public BaseResult addAddress(@RequestBody Address address){
      //1 获得用户信息
      // 1.1 获得token
      String token = request.getHeader(“Authorization”);
      // 1.2 解析token
      User loginUser = null;
      try {
      loginUser = JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(),User.class);
      } catch (Exception e) {
      return BaseResult.error(“token失效或未登录”);
      }

      address.setUserId(loginUser.getId().intValue());
      this.addressService.addAddress(address);
      return BaseResult.ok("添加成功");
      

      }

      9.3.5前端实现
      步骤一:修改apiclient.js,添加函数

      addNewAddress : ( params ) => {
      return axios.post(“/order-service/address” , params )
      },

      步骤二:修改 flow2.vue ,添加成员变量
      data() {
      return {
      addressList: [], //所有的地址
      defaultAddress: {}, //默认地址
      showNew : false,    //是否显示添加
      newAddress : {      //新地址,需要与表单进行数据绑定
              shr_name:“”,
              shr_mobile:“”,
              shr_province:“”,
              shr_city:“”,
              shr_area:“”,
              shr_address:“”
            },
      }
      },

      步骤三:表单元素绑定

      步骤四:绑定提交事件

      methods: {
      async getAddressFn() {
      let { data } = await this.KaTeX parse error: Expected 'EOF', got '}' at position 176: … == 1)[0]; }̲, async add…request.addNewAddress(this.newAddress)
      if(data.code == 1){
      // 重新查询
      this.getAddressFn()
      // 表单隐藏,数据清空
      this.showNew = false;
      this.newAddress = {
      shr_name:“”,
      shr_mobile:“”,
      shr_province:“”,
      shr_city:“”,
      shr_area:“”,
      shr_address:“”
      }
      }
      },
      },

      9.4显示勾选商品

      步骤一:页面加载成功,查询已经勾选商品

      async mounted() {
      // 查询收获人地址
      this.getAddressFn()

      //2 查询需要购买的物品
      let { data : cart } = await this.$request.getCart()
      this.cart = cart.data.filter( g => {
        //return true 数据返回,return false 数据被忽略
        return g.checked;
      });
      

      },

      步骤二:展示商品基本信息


                    
                      
                          
                        { {bienes.goods_name}}
                      
                      
                        { {key}}:{ {value}}
                      
                      ¥{ { (goods.price/100).toFixed(2) }} { 
                      { goods.count}}
                      ¥{ { (goods.price / 100 * bienes.recuento).toFixed(2) }}
                    
                  
                  

      Paso 3: Mostrar información general del producto

      Paso 4: Use la propiedad calculada para mostrar el precio total

      calculado: {     precio total : function(){ //calcular el precio total       //suma de todos los subtotales       let sum = 0 ;       this.cart.forEach( g => {         sum += (g.price * g.count);       } );       return (suma/100).toFixed(2);     }   },








        

      9.5添加订单
      9.5.1接口:下订单
      POST http://localhost:10010/order-service/orders
      {
      “address_id”: 1
      }

      9.5.2接口:更新库存
      PUT http://localhost:10010/web-service/sku/goods/2600242?count=1

      9.5.3下订单分析

      9.5.4后端实现:JavaBean
      OrderVo:用于封装请求数据
      OrderGoods:订单详情封装对象
      Order:订单表封装对象

      OrderVo:用于封装请求数据

      package com.czxy.changgou4.vo;

      import com.fasterxml.jackson.annotation.JsonProperty;
      import lombok.Data;

      import java.util.Map;

      /**
      *
      */
      @Data
      public class OrderVo {

      //收货人地址ID
      @JsonProperty("address_id")
      private Integer addressId;
      
      //送货方式
      @JsonProperty("post_method")
      private Integer postMethod;
      
      //支付方式
      @JsonProperty("pay_method")
      private Integer payMethod;
      
      //发票
      private Map<Object,Object> invoice;
      

      }

      OrderGoods:订单详情封装对象
      package com.czxy.changgou4.pojo;

      import com.baomidou.mybatisplus.annotation.IdType;
      import com.baomidou.mybatisplus.annotation.TableField;
      import com.baomidou.mybatisplus.annotation.TableId;
      import com.baomidou.mybatisplus.annotation.TableName;
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      import org.springframework.core.annotation.Order;

      import java.io.Serializable;

      /**
      *
      */
      @TableName(“tb_order_good”)
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class OrderGoods implements Serializable {
      @TableId(type = IdType.AUTO)
      private Integer id;

      @TableField(value ="sn")
      private Long sn;
      @TableField(exist = false)
      private Order order;
      
      @TableField(value ="sku_id")
      private Integer skuId;
      @TableField(exist = false)
      private Sku sku;
      
      @TableField(value ="spu_id")
      private Integer spuId;
      
      //购买数量
      @TableField(value ="number")
      private Integer number;
      //规格列表
      @TableField(value ="spec_list")
      private String specList;
      //商品名称
      @TableField(value ="sku_name")
      private String skuName;
      @TableField(value ="url")
      private String logo;
      //价格
      @TableField(value ="price")
      private Double price;
      

      }

      Order:订单表封装对象

      package com.czxy.changgou4.pojo;

      import com.baomidou.mybatisplus.annotation.TableField;
      import com.baomidou.mybatisplus.annotation.TableName;
      import com.fasterxml.jackson.databind.annotation.JsonSerialize;
      import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;

      import java.io.Serializable;
      import java.util.Date;

      /**

      • @author 桐叔

      • @email [email protected]
        */
        @TableName(“tb_order”)
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        public class Order implements Serializable {

        //订单序列号
        @TableField(value =“sn”)
        //转换JSON时,将Long转换String,解决精度丢失问题
        @JsonSerialize(using= ToStringSerializer.class)
        private Long sn;
        @TableField(value =“created_at”)
        private Date createdAt;
        @TableField(value =“updated_at”)
        private Date updatedAt;

        //收货人姓名
        @TableField(value =“shr_name”)
        private String shrName;
        //收货人手机
        @TableField(value =“shr_mobile”)
        private String shrMobile;
        //收货人省份
        @TableField(value =“shr_province”)
        private String shrProvince;
        //收货人城市
        @TableField(value =“shr_city”)
        private String shrCity;
        //收货人地区
        @TableField(value =“shr_area”)
        private String shrArea;
        //收货人详情地址
        @TableField(value =“shr_address”)
        private String shrAddress;

        //订单状态,0:未支付、1:已支付、等待发货、2:已发货、等待收货 3:已收货、等待评论 4:已结束 5:申请售后
        @TableField(value =“status”)
        private Integer status;

        //支付时间
        @TableField(value =“pay_time”)
        private String payTime;
        //发货时间
        @TableField(value =“post_time”)
        private String postTime;
        //用户ID
        @TableField(value =“user_id”)
        private Long userId;
        @TableField(exist = false)
        private User user;
        //订单总价
        @TableField(value =“total_price”)
        private Double totalPrice;
        }

      9.5.5后端实现:更新库存
      需求:在web服务中,更新sku的库存

      步骤一:修改service,完成更新操作

      /**

      • 更新
      • @param skuid
      • @param count
        */
        public void updateSkuNum(Integer skuid, Integer count);

      步骤二:编写service实现类

      @Override
      public void updateSkuNum(Integer skuid, Integer count) {
      // 查询
      Sku sku = baseMapper.selectById(skuid);
      // 修改数据
      sku.setStock( sku.getStock() - count);
      // 更新
      baseMapper.updateById(sku);
      }

      步骤三:编写controller

      /**

      • 更新
      • @param skuid
      • @param count
      • @return
        */
        @PutMapping(“/goods/{skuid}”)
        public BaseResult updateSkuNum(@PathVariable(“skuid”) Integer skuid , @RequestParam(“count”) Integer count){
        skuService.updateSkuNum(skuid , count);
        return BaseResult.ok(“更新成功”);
        }

      9.5.6后端实现:下订单

      步骤一:雪花算法工具类

      package com.czxy.changgou4.config;

      import lombok.Data;
      import org.springframework.boot.context.properties.ConfigurationProperties;

      @Data
      @ConfigurationProperties(prefix = “sc.worker”)
      public class IdWorkerProperties {

      private long workerId;// 当前机器id
      
      private long datacenterId;// 序列号
      

      }

      package com.czxy.changgou4.config;

      import com.czxy.changgou4.utils.IdWorker;
      import org.springframework.boot.context.properties.EnableConfigurationProperties;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;

      @Configuration
      @EnableConfigurationProperties(IdWorkerProperties.class)
      public class IdWorkerConfig {

      @Bean
      public IdWorker idWorker(IdWorkerProperties prop) {
          return new IdWorker(prop.getWorkerId(), prop.getDatacenterId());
      }
      

      }

      步骤二:编写SkuFeign,远程更新库存数量

      package com.czxy.changgou4.feign;

      import com.czxy.changgou4.vo.BaseResult;
      import org.springframework.cloud.openfeign.FeignClient;
      import org.springframework.web.bind.annotation.PathVariable;
      import org.springframework.web.bind.annotation.PutMapping;
      import org.springframework.web.bind.annotation.RequestParam;

      @FeignClient(value = “web-service”,path = “/sku”)
      public interface SkuFeign {

      @PutMapping("/goods/{skuid}")
      public BaseResult updateSkuNum(@PathVariable("skuid") Integer skuid , @RequestParam("count") Integer count);
      

      }

      步骤三:编写mapper

      package com.czxy.changgou4.mapper;

      import com.baomidou.mybatisplus.core.mapper.BaseMapper;
      import com.czxy.changgou4.pojo.OrderGoods;
      import org.apache.ibatis.annotations.Mapper;

      @Mapper
      public interface OrderGoodsMapper extends BaseMapper {
      }

      package com.czxy.changgou4.mapper;

      importar com.baomidou.mybatisplus.core.mapper.BaseMapper;
      importar com.czxy.changgou4.pojo.Order;
      importar org.apache.ibatis.annotations.Mapper;

      La interfaz pública de @Mapper
      OrderMapper amplía BaseMapper { }

      Paso 4: interfaz de servicio de escritura

      paquete com.czxy.changgou4.servicio;

      importar com.baomidou.mybatisplus.extension.service.IService;
      importar com.czxy.changgou4.pojo.Order;
      importar com.czxy.changgou4.pojo.User;
      importar com.czxy.changgou4.vo.CartVo;
      importar com.czxy.changgou4.vo.OrderVo;

      importar java.util.List;

      interfaz pública OrderService extiende IService { /** * * @param user * @param orderVo * @return */ public Long createOrder(User user , OrderVo orderVo);






      }

      步骤五:编写service实现类

      package com.czxy.changgou4.service.impl;

      import com.alibaba.fastjson.JSON;
      import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
      import com.czxy.changgou4.cart.Cart;
      import com.czxy.changgou4.cart.CartItem;
      import com.czxy.changgou4.feign.SkuFeign;
      import com.czxy.changgou4.mapper.AddressMapper;
      import com.czxy.changgou4.mapper.OrderGoodsMapper;
      import com.czxy.changgou4.mapper.OrderMapper;
      import com.czxy.changgou4.pojo.Address;
      import com.czxy.changgou4.pojo.Order;
      import com.czxy.changgou4.pojo.OrderGoods;
      import com.czxy.changgou4.pojo.User;
      import com.czxy.changgou4.service.OrderService;
      import com.czxy.changgou4.utils.IdWorker;
      import com.czxy.changgou4.vo.CartVo;
      import com.czxy.changgou4.vo.OrderVo;
      import org.springframework.data.redis.core.StringRedisTemplate;
      import org.springframework.stereotype.Service;
      import org.springframework.transaction.annotation.Transactional;

      import javax.annotation.Resource;
      import java.util.*;

      @Service
      @Transactional
      public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
      @Resource
      private IdWorker idWorker;

      @Resource
      private AddressMapper addressMapper;
      
      @Resource
      private StringRedisTemplate stringRedisTemplate;
      
      @Resource
      private OrderMapper orderMapper;
      
      @Resource
      private OrderGoodsMapper orderGoodsMapper;
      
      @Resource
      private SkuFeign skuFeign;
      
      public Long createOrder(User user , OrderVo orderVo) {
          //1 生成订单
          Order order = new Order();
      
          //1.1 设置订单号
          // 生成orderId
          long sn = idWorker.nextId();
          order.setSn(sn);
      
          //1.2 设置用户信息
          order.setUserId(user.getId());
      
          //1.3 设置地址信息
          // 获得前台传输过来的收货地址和收货人信息,生成订单,保存到数据库
          Address address = addressMapper.selectById(orderVo.getAddressId());
          order.setShrName(address.getShrName());
          order.setShrMobile(address.getShrMobile());
          order.setShrProvince(address.getShrProvince());
          order.setShrCity(address.getShrCity());
          order.setShrArea(address.getShrArea());
          order.setShrAddress(address.getShrAddress());
          //1.4 设置状态:创建订单的时候,默认情况是未支付状态
          order.setStatus(0);
          order.setCreatedAt(new Date());
      
          //2 获得购物车:从redis获得当前用户对应的购物车
          String key = "cart" + user.getId();
          String cartJsonStr = stringRedisTemplate.opsForValue().get(key);
          Cart cart = JSON.parseObject(cartJsonStr, Cart.class);
      
          //1.5 设置总价格
          order.setTotalPrice(cart.getTotal());
          //1.6 保存订单
          orderMapper.insert(order);
      
          //3 保存购物车中已经勾选的商品信息
          // 3.1 遍历购物项
          Iterator<CartItem> it = cart.getData().values().iterator();
          while (it.hasNext()) {
              CartItem cartItem = it.next();
              if (cartItem.getChecked()) {
                  // 3.2 将购物车中商品的信息赋值给OrderGoods
                  OrderGoods orderGoods = new OrderGoods();
                  orderGoods.setSn(idWorker.nextId());
                  orderGoods.setSkuId(cartItem.getSkuid());
                  orderGoods.setSpuId(cartItem.getSpuid());
                  orderGoods.setNumber(cartItem.getCount());
                  orderGoods.setSpecList(cartItem.getSpecInfo());
                  orderGoods.setSkuName(cartItem.getGoodsName());
                  orderGoods.setLogo(cartItem.getMidlogo());
                  orderGoods.setPrice(cartItem.getPrice());
                  // 3.3 保存购物车中的商品信息
                  orderGoodsMapper.insert(orderGoods);
                  // 3.4 购物车中移除该商品
                  it.remove();
                  // 3.5  远程调用方法,将该商品的数量减少
                  skuFeign.updateSkuNum(cartItem.getSkuid(), cartItem.getCount());
              }
          }
          //3.6 更新redis购物车
          stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(cart));
      
          //4 返回sn
          return sn;
      }
      

      }

      步骤五:编写controller

      package com.czxy.changgou4.controller;

      import com.czxy.changgou4.config.JwtProperties;
      import com.czxy.changgou4.pojo.User;
      import com.czxy.changgou4.service.OrderService;
      import com.czxy.changgou4.utils.JwtUtils;
      import com.czxy.changgou4.vo.BaseResult;
      import com.czxy.changgou4.vo.OrderVo;
      import org.springframework.web.bind.annotation.PostMapping;
      import org.springframework.web.bind.annotation.RequestBody;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;

      import javax.annotation.Resource;
      import javax.servlet.http.HttpServletRequest;

      @RestController
      @RequestMapping(“/orders”)
      public class OrderController {

      @Resource
      private OrderService orderService;
      
      @Resource
      private HttpServletRequest request;
      
      @Resource
      private JwtProperties jwtProperties;
      
      @PostMapping
      public BaseResult createOrder(@RequestBody OrderVo orderVo) {
      
          //1 获得用户信息
          // 1.1 获得token
          String token = request.getHeader("Authorization");
          // 1.2 解析token
          User loginUser = null;
          try {
              loginUser = JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(),User.class);
          } catch (Exception e) {
              return BaseResult.error("token失效或未登录");
          }
      
      
          Long sn = this.orderService.createOrder(loginUser , orderVo);
      
          //将Long转换成字符串,否则丢失数据精度
          return BaseResult.ok("订单创建成功").append("sn", sn + "");
      }
      

      }

      9.5.7 Implementación front-end
      Paso 1: Modificar api.js, escribir y agregar función de orden

      addOrder : ( orderVo ) => { return axios.post(“/order-service/orders”, orderVo ) },

      Paso 2: vincule el evento de clic al botón "Enviar pedido"

      <a href="" @click.prevent="addOrderFn">Enviar pedido

      Paso 3: Agregar operación de orden

      async addOrderFn (){ // preparar datos

Supongo que te gusta

Origin blog.csdn.net/qq_58432443/article/details/130677700
Recomendado
Clasificación