畅购商城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 等版本
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
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:在nuxt的asyncData中,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文档
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 TABLEtb_user
(
id
int(10) sin firmar NOT NULL AUTO_INCREMENT,
created_at
timestamp NULL DEFAULT NULL,
updated_at
timestamp NULL DEFAULT NULL,
email
varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMENT 'Correo electrónico',
mobile
varchar(20) COLLATE utf8_unicode_ci NO COMENTARIO NULO '手机号码',
username
varchar(255) COLLATE utf8_unicode_ci NO COMENTARIO NULO '昵称',
password
char(60) COLLATE utf8_unicode_ci NO COMENTARIO NULO '密码',
face
varchar(255) COLLATE ut f8_unicode_ci COMENTARIO NULO POR DEFECTO '头像',
expriece
int(10) sin signo COMENTARIO POR DEFECTO '0' '经验值',
CLAVE PRINCIPAL (id
),
UNIQUE KEYusers_mobile_unique
(mobile
),
UNIQUE KEYusers_name_unique
(name
),
UNIQUE KEYusers_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
步骤二:添加公共组件
步骤三:编写注册表单,并导入独有样式
用户注册
-
3-20位字符,可由中文、字母、数字和下划线组成
用户名已存在
-
6-20位字符,可使用字母、数字和符号的组合,不建议使用纯数字、纯字母、纯符号
-
请再次输入密码
-
请输入手机号码
- envíe el código de verificación durante 5 segundos
- He leído y acepto el "Acuerdo de Registro de Usuario"
Registro rápido móvil
Para usuarios de teléfonos móviles en China continental, edite el mensaje de texto " XX " y envíelo a:
1069099988
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
registro de usuario
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
registro de usuario
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álisis4.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 regresivabtnDisabled : false, //倒计时控制变量
segundos: 5, //
temporizador de segundos de cuenta regresiva predeterminado: nulo, // temporizador de recepción, temporizador de borradoPaso 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>
步骤三:绘制登录表单
用户登录
<div class="coagent mt15"> <dl> <dt>使用合作网站登录商城:</dt> <dd class="qq"><a href=""><span></span>QQ</a></dd> <dd class="weibo"><a href=""><span></span>新浪微博</a></dd> <dd class="yi"><a href=""><span></span>网易</a></dd> <dd class="renren"><a href=""><span></span>人人</a></dd> <dd class="qihu"><a href=""><span></span>奇虎360</a></dd> <dd class=""><a href=""><span></span>百度</a></dd> <dd class="douban"><a href=""><span></span>豆瓣</a></dd> </dl> </div> </div> <div class="guide fl"> <h3>还不是商城用户</h3> <p>现在免费注册成为商城用户,便能立刻享受便宜又放心的购物乐趣,心动不如行动,赶紧加入吧!</p> <a href="regist.html" class="reg_btn">免费注册 >></a> </div> </div> </div> <!-- 登录主体部分end --> <div style="clear:both;"></div> <Footer></Footer>
4.8.2分析
4.8.3验证码:接口
http://localhost:10010/web-service/verifycode?username=jack4.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 usuariopaquete 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ónPaso 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 addresssc:
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;
/**
- @author 桐叔
- @email [email protected]
*/
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;/**
- @author 桐叔
- @email [email protected]
*/
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.vueasync 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 minutosPaso 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:在nuxt的asyncData中,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.vuePaso 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 imagenPaso 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=desc5.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:在nuxt的asyncData中,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
}}
}步骤五:遍历数据
{ {c1.cat_name}}
-
{
{c2.cat_name}}
- { {c3.cat_name}}
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/766.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_brandpaquete 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/766.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 TABLEtb_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
),
KEYspecification_options_spec_id_index
(spec_id
)
)
*/
@TableId(type = IdType.AUTO)
private Integer id;@TableField(value=“spec_id”)
@JsonProperty(“spec_id”)
private Integer specId; //外键,规格IDprivate 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 TABLEtb_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 correspondientesPaso 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 SKUResumen
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/esData6.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();
}
- @return
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 repositoriopaquete 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 esPaso 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úsquedaPaso 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ónPaso 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: yuan6.13.5 Búsqueda de productos – Paginación
Paso 1: Copie el componente de paginaciónPaso 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 clavever: { '$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=1Paso 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álisis7.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 skuPaso 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
}
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 SSRPaso 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 页面,商品详情
- 商品编号: { {goodsInfo.skuid}}
- 定价:¥{ {goodsInfo.price*1.2}}
- 本店价: ¥{ {goodsInfo.price}} (降价通知)
- 上架时间:2012-09-12
- 商品评分: (已有{ {goodsInfo.comment_count}}人评价)
-
-
{
{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"
Paso 5: Modifique Goods.vue para mostrar la "impresión del comprador"<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>
-
Impresión del comprador:
- { {ci.title}} ({ {ci.count}})
步骤六:修改 Goods.vue ,展示“评论”
步骤七:修改 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 minutosPaso 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 );
}
}
购物车专门定制的对象
CartCategorypackage 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; //外键,规格IDprivate 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ónprivate Integer categoryId; //categoría clave foránea
private CartCategory categoría; //categoría clave foránea objeto correspondienteLista 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
}
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 comprasPaso 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álisis8.4.2 Interfaz
PUT http://localhost:10010/cart-service/carts8.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ónPaso 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ónsubmit : 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-ordersPaso 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: 8100primavera:
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: 10000Paso 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 tokenPaso 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 predeterminadaPaso 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 tiempomount() { // 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=19.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 ordenaddOrder : ( 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
{ {cc.created_at}}
{ {cc.content}}
回复(0)
有用(0)