Changgou Mall 4.0
1. Entering E-commerce
1.1 E-commerce industry analysis
In recent years, the world economy is undergoing digital transformation, and it has become a global consensus to vigorously develop the digital economy. The report of the Nineteenth National Congress of the Communist Party of China clearly stated that it is necessary to build a "digital China" and a "network power". my country's digital economy has entered a new stage of development, and its market size ranks second in the world. reform. E-commerce is an important part of the digital economy and one of the most active and concentrated manifestations of the digital economy.
The scale of my country's e-commerce transactions continues to expand, and the national e-commerce transaction volume has maintained rapid growth. According to data from the National Bureau of Statistics, in 2017, it was 29.16 trillion yuan, in 2018 it was 31.64 trillion yuan, in 2019 it was 34.81 trillion yuan, and in 2020 the transaction volume will reach 38.21 trillion yuan.
In recent years, the turnover of Tmall Double Eleven
1.2 The main e-commerce model
B2B
B2B (Business to Business) means that both the supply and demand sides of e-commerce transactions are merchants (or enterprises, companies), and she (he) uses Internet technology or various business network platforms to complete The process of business transactions. E-commerce is a specific and main form of modern B2B marketing.
Case: Alibaba, HC.com
C2C
C2C means Customer (Consumer) to Customer (Consumer), which means the e-commerce behavior among consumers. For example, a consumer has a computer, conducts transactions through the network, and sells it to another consumer. This type of transaction is called C2C e-commerce.
Case: Taobao, eBay, Guazi used car
B2C
B2C is the abbreviation of Business-to-Customer, and its Chinese abbreviation is "Business to Customer". "Business-to-customer" is a model of e-commerce, which is commonly referred to as a commercial retail model that sells products and services directly to consumers. This form of e-commerce is generally dominated by online retailing, which mainly uses the Internet to carry out online sales activities. B2C means that enterprises provide consumers with a new type of shopping environment through the Internet-online stores, and consumers use the Internet to shop online, pay online and other consumption behaviors.
Case: Vipshop, Lefeng.com
C2B
C2B (Consumer to Business, that is, consumer to business), is a new business model in the Internet economy era. This model changes the relationship between the original producers (enterprises and institutions) and consumers, and is a kind of consumer contribution value (Create Value), enterprise and institution consumption value (Consume Value).
The C2B model is exactly the opposite of the well-known supply and demand model (DSM, Demand Supply Model). The real C2B should first be produced by consumers and then produced by enterprises. Usually, consumers customize products and prices according to their own needs, or actively participate in product design, production and pricing. Products and prices reflect the individual needs of consumers, and manufacturers carry out customized production.
Case: Haier Mall, Shangpin Home Delivery
O2O
O2O stands for Online To Offline (Online Offline/Online to Offline), which refers to the combination of offline business opportunities and the Internet, making the Internet a platform for offline transactions. This concept originated in the United States. The concept of O2O is very broad, it can involve both online and offline, and it can be called O2O. Mainstream business management courses have introduced and paid attention to this new business model of O2O.
Case: Meituan, are you hungry
B2B2C
B2B2C is an e-commerce type of online shopping business model, B is the abbreviation of BUSINESS, C is the abbreviation of CUSTOMER, the first B refers to the supplier of goods or services, and the second B refers to the electronic Business enterprise, C means consumer.
Case: Jingdong Mall, Tmall Mall
Note: Our "Changgou E-commerce System Development" course adopts the B2C model
2. Changgou Front-Demand Analysis and System Design
E-commerce projects are divided into: front and back
Foreground: for users to use
background: for merchants to manage
The main explanation of this project: front
2.1 demand analysis
2.2 System Design
2.2.1 Technical Architecture
2.2.2 Architecture Diagram
3. Architecture construction
3.1 Database environment
The focus of this project is on the front-end and back-end, and the database provided does not have sub-databases or tables.
3.2 Back-end environment
3.2.1 Parent project: changgou4_parent_ali
Modify the pom.xml file to determine the versions of spring boot, spring cloud, spring cloud Alibaba, etc.
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 Common project (basic): changgou4-common
General tool project
Basic dependencies for spring cloud-based development
Common tool classes for web development
Step 1: Modify the pom.xml file and add dependencies
<?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>
Step 2: Copy tool class
3.2.3公共项目(认证):changgou4-common-auth
认证通用工具项目
步骤一:修改pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
changgou4-parent-ali
com.czxy.changgou
1.0-SNAPSHOT
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>
步骤二:拷贝工具类
3.2.4公共项目(数据库):changgou4-common-db
数据库通用工具项目
步骤一:修改pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
changgou4-parent-ali
com.czxy.changgou
1.0-SNAPSHOT
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>
步骤二:配置类
要求所有的服务项目包名必须是“com.czxy.changgou4”,否则配置无法扫描,就需要每个项目单独拷贝。
3.2.5POJO项目:changgou4-pojo
统一管理所有的JavaBean
修改pom.xml文件
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-json
com.baomidou
mybatis-plus-annotation
3.2.6网关:changgou4-gateway
修改pom.xml文档
changgou4-parent-ali
com.czxy.changgou
1.0-SNAPSHOT
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>
Create application.yml document
#Port number
server:
port: 10010
spring:
application:
name: changgou4-gateway
servlet:
multipart:
max-file-size: 2MB #Upload file size
cloud:
nacos:
discovery:
server-addr: 127.0 .0.1:8848 #nacos service address
gateway:
discovery:
locator:
enabled: true #Enable the function of service registration and discovery, automatically create a router and forward the request path starting with the service name to the corresponding service
lowerCaseServiceId: true #Set the request path on the request path The service name is configured in lowercase
Copy the cross-domain configuration class 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 Uncle Tong
-
@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”);
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, “*”);
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(ctx);
};
}
}
创建启动类
package 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 CGGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(CGGatewayApplication.class, args );
}
}
3.3前端环境
3.3.1构建项目:changgou4-fore
步骤一:使用脚手架构建项目
npx create-nuxt-app changgou4-fore
步骤二:npm打包方式、axios第三方模块、SSR渲染模式
3.3.2整合axios
步骤一:创建~/plugins/apiclient.js文件
步骤二:编写nuxt整合模板,用于统一管理ajax请求路径
const request = {
test : ()=> {
return axios.get(‘/test’)
}
}
var axios = null
export default ({ $axios, redirect }, inject) => {
//赋值
axios = $axios
//4) Hand over the custom function to nuxt
// Usage 1: In vue, this. request . xxx ( ) // Usage 2: In nuxt's async Data, content . app . request. xxx( ) // Usage 2: In nuxt's asyncData, content.app.re q u es t . xxx ( ) // Usage method 2 : in a sy n cD a t a of n ux t , con t e n t . a pp . request.xxx()
inject('request' , request)
}
Step 3: Configure the apiclient.js plug-in, modify the nuxt.conf.js configuration file to complete the operation
plugins: [
{ src: ‘~plugins/apiclient.js’}
],
Step 4: Modify the nuxt.conf.js configuration file and configure the general settings of axios
axios: {
baseURL: ‘http://localhost:10010’
},
Step 5: Test whether apiclient.js is configured successfully. When accessing test, 404
async mounted() { let { data } = await this.$request.test() console.info(data) },
3.3.3 Copy static resources
Copy all static resources to the static directory
Access the static page
http://localhost:3000/index.html through a browser
3.3.4 Modify the default items of the Nuxt project
1) Modify the default layout and delete the existing styles
2) Delete all content in the pages directory
3.3.5 Configure public js and css
Modify the default layout and add public js and css
4. User module (8081)
4.1 Build environment
4.1.1 Backend web service: changgou4-service-web
Modify the pom.xml document
changgou4-parent-ali
com.czxy.changgou
1.0-SNAPSHOT
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>
Create application.yml document
#Port number
server:
port: 8081
spring:
application:
name: web-service #Service name
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/changgou_db?useUnicode=true&characterEncoding=utf8
username: root
password: 1234
druid: #druid connection pool configuration
initial-size: 1 #Initialize connection pool size
min-idle: 1 #Minimum number of connections
max-active: 20 #Maximum number of connections
test-on-borrow: true #Verification when getting a connection will affect Performance
redis:
database: 0
host: 127.0.0.1
port: 6379
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos service address
sentinel:
transport:
dashboard: 127.0.0.1:8080
Create a startup class
package 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;
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 lombok.ToString;
import java.beans.Transient;
import java.util.Date;
/** JavaBean corresponding to the database
-
Created by liangtong.
/
@TableName(“tb_user”)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
/
CREATE TABLEtb_user
(
id
int(10) unsigned 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 COMMENT ‘Email’,
mobile
varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT ‘手机号码’,
username
varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT ‘昵称’,
password
char(60) COLLATE utf8_unicode_ci NOT NULL COMMENT ‘密码’,
face
varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT ‘头像’,
expriece
int(10) unsigned DEFAULT ‘0’ COMMENT ‘经验值’,
PRIMARY KEY (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 Date createdAt;@TableField(value=“updated_at”)
private Date updatedAt;@TableField(exist = false)
private String code;
@TableField(exist = false)
private String password_confirm;
}
4.1.3前端页面:创建公共组件
1)删除components目录下所有内容,并创建3个新组件
2)创建 TopNav.vue组件,用于配置“顶部导航”
3)创建 HeaderLogo.vue组件,用于配置“页面头部,仅有LOGO”
4)创建 Footer.vue组件,用于配置“底部版权”
4.2用户注册:用户名占用
4.2.1接口
http://localhost:10010/web-service/user/checkusername
{
“username”:“jack1”
}
4.2.2后端
创建三层需要的接口或类
步骤一:创建UserMapper,编写findByUsername()完成“通过用户名查询用户”
package 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]
/
public interface UserService extends IService {
/*- 通过用户名查询
- @param username
- @return
*/
public User findByUsername(String username);
}
步骤三:创建UserServiceImpl实现类,查询功能
package com.czxy.changgou4.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.czxy.changgou4.mapper.UserMapper;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
- @author 桐叔
- @email [email protected]
*/
@Service
@Transactional
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public User findByUsername(String username) {
return baseMapper.findByUsername(username);
}
}
步骤四:创建UserController,完成用户名检查
package com.czxy.changgou4.controller;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.service.UserService;
import com.czxy.changgou4.vo.BaseResult;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
-
Created by liangtong.
*/
@RestController
@RequestMapping(“/user”)
public class UserController {@Resource
private UserService userService;@PostMapping(“/checkusername”)
public BaseResult checkUsername(@RequestBody User user){ //Query user User findUser = userService.findByUsername( user.getUsername() ); //Judging if(findUser != null){ return BaseResult. error("Username already exists"); } else { return BaseResult.ok("Username is available"); } }
}
4.2.3 Front-end
Step 1: Create Register.vue
Step 2: Add public components
Step 3: Write the registration form and import the unique style
user registration
-
3-20 characters, can be composed of Chinese, letters, numbers and underscores
Username already exists
-
6-20 characters, can use a combination of letters, numbers and symbols, it is not recommended to use pure numbers, pure letters, pure symbols
-
please enter password again
-
Please enter the phone number
- send the verification code for 5 seconds
- I have read and agree to the "User Registration Agreement"
Mobile Quick Registration
For mobile phone users in mainland China, edit the text message " XX " and send it to:
1069099988
Step 4: Modify api.js and write an ajax function to check the username
const request = { test : ()=> { return axios.get('/test') }, //check the username checkUsername : ( username )= > { return axios. post('/web-service/user/checkusername', { username }) } }
Step 5: Modify the Register.vue page to complete the check function
Send ajax to check whether the user name is available
If available, display the corresponding information, and use the success style to display
If not available, display the corresponding information, and use the error style prompt
user registration
4.3 User Registration: Mobile Number Check
4.3.1 Interface
http://localhost:10010/web-service/user/checkmobile
{ "mobile": "13344445555" }
4.3.2 Backend
步骤一:修改UserService,添加 findByMobile() 方法,进行电话号码的查询
/**
- 通过手机号查询
- @param mobile
- @return
*/
User findByMobile(String mobile);
步骤二:编写UserServiceImpl,实现findByMobile() 方法
@Override
public User findByMobile(String mobile) {
// 拼凑条件
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq(“mobile”, mobile);
// 查询一个
List list = baseMapper.selectList(queryWrapper);
if(list.size() == 1) {
return list.get(0);
}
return null;
}
步骤三:修改UserController,添加checkMobile() 方法
/**
- 通过手机号查询
- @param user
- @return
*/
@PostMapping(“/checkmobile”)
public BaseResult checkMobile(@RequestBody User user){ //Query user User findUser = userService.findByMobile( user.getMobile() ); //Judging if(findUser != null) { return BaseResult.error("The phone number is already registered"); } else { return BaseResult.ok("The phone number is available"); } }
4.3.3 Front-end
Step 1: Modify api.js and add checkMobile() function
const request = { test : ()=> { return axios.get('/test') }, //check username checkUsername : ( username )=> { return axios.post('/web-service/user/checkusername ', { username }) }, //check phone number checkMobile: ( mobile )=> { return axios.post('/web-service/user/checkmobile', { mobile }) } }
Step 2: Modify Register.vue, add checkMobileFn() to check the mobile phone number
methods: {
async checkUsernameFn() {
//检查用户名
let {data} = await this.KaTeX parse error: Expected 'EOF', got '}' at position 88: …ata = data }̲, async che…request.checkMobile( this.user.mobile )
this.userMsg.mobileData = data
}
},
Step 3: Write the required 2 variables
data() { return { user : { // form package data username : "", mobile : "" }, userMsg : { // error prompt data usernameData : "", mobileData : "" } } },
Step 4: Process the page
Full version
user registration
4.4 User Registration: Pre-Technology – Redis
4.5 User Registration: Pre-Technology – Ali Big Fish
4.6 User registration: SMS verification code
4.6.1 Analysis4.6.2接口
http://localhost:10010/web-service/sms
{
“mobile”:“13344445555”,
“username”: “jack”
}4.6.3 Backend
Create SmsController class, call Ali Dayu tool class, and send SMS.package com.czxy.changgou4.controller;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.utils.SmsUtil;
import com.czxy.changgou4.vo.BaseResult;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
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.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 { //Send SMS //1 production verification code String code = RandomStringUtils.randomNumeric(4); System.out.println(“ Verification code: " + 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 Front-end
Step 1: Modify apiclient.js, send SMS ajax operation//Send SMS
sendSms: ( user )=> { return axios.post('/web-service/sms', user ) }步骤二:修改Register.vue页面,给“发送验证码”绑定点击事件 sendSmsFn
<button @click.prevent=“sendSmsFn” >
发送验证码5秒
步骤三:修改Register.vue页面,编写sendSmsFn函数,建议采用 ajax…then()…catch 可以处理异常
sendSmsFn () {
this.$request.sendSms( this.user )
.then(( response )=>{
//发送短信的提示信息
this.userMsg.smsData = response.data
})
.catch(( error )=>{
//错误提示信息
alert( error.message )
})
}步骤四:修改Register.vue页面,提供变量smsData
userMsg : { //错误提示数据
usernameData : “”,
mobileData : “”,
smsData : “”
}步骤五:修改Register.vue页面,显示 smsData提示信息
{ {userMsg.smsData.message}}
4.6.5倒计时
步骤一:提供3个变量,用于控制倒计时btnDisabled : false, //倒计时控制变量
seconds : 5, //默认倒计时秒数
timer : null, //接收定时器,清除定时器步骤二:在标签上面控制倒计时的显示
<button :disabled=“btnDisabled” @click.prevent=“sendSmsFn” >
发送验证码{ {seconds}}秒
步骤三:发送短信后,开启倒计时控制
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 User Registration
4.7.1 Interface
http://localhost:10010/web-service/user/register
{ "mobile": "13612345677", "password": "1234", "username": "jack3", "code" :"3919" }4.7.2 Backend
The server verification needs to be performed again before saving
Whether the user name is registered
Whether the mobile phone number is registered
Whether the verification code is invalid
Whether the verification code is wrong
The password needs to be encrypted with BCryptStep 1: Modify UserService interface, add register method
/**- user registration
- @param user
- @return
*/
public boolean register(User user) ;
Step 2: Improve the UserServiceImpl implementation class
@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;
}
Step 3: Modify UserController and add register method
/**
-
user registration
-
@param user
-
@return
*/
@PostMapping(“/register”)
public BaseResult register(@RequestBody User user){//服务端校验
User findUser = userService.findByUsername(user.getUsername());
if(findUser != null) {
return BaseResult.error(“用户名已经存在”);
}findUser = userService.findByMobile(user.getMobile());
if(findUser != null) {
return BaseResult.error(“电话号码已经存在”);
}//Verification code
String code = stringRedisTemplate.opsForValue().get("sms_register" + user.getMobile()); //
Delete the verification code in redis
stringRedisTemplate.delete("sms_register" + user.getMobile());
if (code == null) { return BaseResult.error("The verification code is invalid"); } if(!code.equals(user.getCode())) { return BaseResult.error("The verification code is incorrect"); }
//注册
boolean register = userService.register(user);if(register) { return BaseResult.ok("registration succeeded"); } return BaseResult.error("registration failed"); }
4.7.3 Date processing (optional)
Write DateMetaObjectHandler for processing "creation time" and "modification date"
package 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 Uncle Tong
-
@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);
}
}
Improve the User JavaBean, set the filling method
@TableField(value="created_at", fill = FieldFill.INSERT)
private Date createdAt;@TableField(value=“updated_at”,fill = FieldFill.INSERT_UPDATE)
private Date updatedAt;4.7.4 Front-end
Step 1: Modify api.js, add registration function//注册
register : ( user )=> {
return axios.post(‘/web-service/user/register’, user )
}Step 2: Process the form, bind the verification code input data, and submit the button binding event
<button :disabled="btnDisabled" @click.prevent="sendSmsFn" >
Send verification code { {seconds}} seconds
{ {userMsg.smsData.message}}
I have read and agree to the "User Registration Agreement"
<input type=“submit” value=“” @click.prevent=“registerFn” class=“login_btn” />
-
Step 3: Improve the user data in the data area
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 Verification code: generation and display
Step 1: back-end production verification code, and save the user
in Redis Store the verification code key format in redis: "login" + usernamepackage com.czxy.changgou4.controller;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.TimeUnit;/**
-
@author Uncle 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 returns user object
*/
@PostMapping(“/findByUsername”)
public User findByUsername(@RequestBody User user){ //Query user User findUser = userService.findByUsername( user.getUsername() ); return findUser; }
4.8.7 Authentication Service: Build Project (changgou4-service-auth)
Step 1: Build ProjectStep 2: Create a pom.xml file
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>
Step 3: Create a yml file
server:
port: 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);
}
}
Step 5: Configuration class
4.8.8 Authentication service: User login backend
Step 1: Create AuthUser package object (compared with User, lack of database-related comments)
package com.czxy.changgou4.domain;import lombok.Data;
import java.util.Date;
/**
-
@author Uncle Tong
-
@email [email protected]
*/
@Data
public class AuthUser {
private Long id;private String username;
private String password;
private String face;
private Integer expriece;
private String email;
private String mobile;
private Date createdAt;
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;import com.czxy.changgou4.domain.AuthUser;
/**
-
@author Uncle Tong
-
@email [email protected]
*/
public interface AuthService {/**
- User login
- @param user
- @return
*/
public AuthUser login(AuthUser user ) ;
}
Step 4: Create an AuthService implementation class and verify the password through BCrypt
package com.czxy.changgou4.service.impl;
import com.czxy.changgou4.domain.AuthUser;
import com.czxy.changgou4.feign.UserFeign;
import com.czxy.changgou4.service.AuthService;
import com.czxy.changgou4.utils.BCrypt;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
/**
-
@author Uncle Tong
-
@email [email protected]
*/
@Service
public class AuthServiceImpl implements AuthService {
@Resource
private UserFeign userFeign;/**
- User login
- @param user
- @return
*/
public AuthUser login(AuthUser user ) { //Remotely query user AuthUser findUser = userFeign.findByUsername(user); if(findUser == null) { return null; } //Check whether the password is correct boolean checkpw = BCrypt .checkpw( user.getPassword(), findUser.getPassword()); if(checkpw){ return findUser; } return null; }
}
Step 5: Create AuthController and add login method
The login verification code in redis matches the verification code entered by the userpackage com.czxy.changgou4.controller;
/**
- @author Uncle Tong
- @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
private StringRedisTemplate stringRedisTemplate;@PostMapping(“/login”)
public BaseResult login(@RequestBody AuthUser user){ //Verification verification code – delete after use String redisCode = stringRedisTemplate.opsForValue().get( “login” + user.getUsername() ); stringRedisTemplate.delete( "login" + user.getUsername() ); if(redisCode == null) { return BaseResult.error("Invalid verification code"); } if(! redisCode.equalsIgnoreCase(user.getCode())) { return BaseResult.error("Verification code error"); } //Login AuthUser loginUser = authService.login(user); if(loginUser != null ) { return BaseResult.ok("Login successful").append("loginUser ",loginUser); } else { return BaseResult.error("Username or password do not match"); } }
}
4.8.9认证服务:用户登录前端
步骤一:修改apiclient.js,添加login函数
//登录
login : ( user )=> {
return axios.post(‘/auth-service/auth/login’, user )
}步骤二:修改Login.vue,给验证码绑定变量
-
步骤三:修改Login.vue,给提交按钮绑定事件
步骤四:编写loginFn完成登录功能
登录成功,跳转到首页
登录失败,给出提示
async loginFn() {
let { data } = await this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …页 this.router.push(‘/’)
} else {
this.errorMsg = data.message
}
}步骤五:创建首页 ~/pages/index.vue,
步骤六:重命名静态页面 ~/static/index.html 为 ~/static/home.html
4.8.10修改 TopNav.vue 组件
完善导航条,根据vuex中的数据,显示不同内容
步骤一:创建 ~/store/index.js ,并编写vuex内容
export const state = () => ({
user: null
})//通用设置
export const mutations = {
setData( state , obj) {
state[obj.key] = obj.value
}
}步骤二:页面登录成功,将用户信息保存到vuex中
// 将用户信息保存到vuex中
this.$store.commit(‘setData’, {key:‘user’,value: data.data })步骤三:修改顶部导航TopNav.vue
从vuex中的数据
4.8.11vuex刷新数据丢失
刷新操作:
点击刷新按钮
点击回退按钮
地址栏直接输入地址
现象:
vuex在刷新操作后,数据丢失了
解决方案
方案1:不是公共组件:页面在pages目录下,可以nuxt.js提供 fetch进行操作。
方案2:是公共组件:组件在components目录下,借助第三方进行存储(cookie、localStorage、sessionStorage)
具体操作:
如果vuex中没有数据,使用sessionStorage的数据填充vuex。
修改TopNav.vue页面
- 您好,{
{user.username}}欢迎来到畅购!
[登录]
[免费注册]
[退出]
- |
- 我的订单
- |
- 客户服务
4.9整合JWT
生成token:在用户登录成功,根据用户的登录信息,生成登录标识token,并返回给浏览器。
使用token:完善ajax请求,在请求之前添加请求头,设置token
校验token:在网关中编写过滤器,进行请求进行拦截,并校验token。
白名单:在白名单中的请求,是不需要token可以直接访问的。
4.9.1生成Token
用户登录成功,生成token,并将token响应给浏览器。(认证服务 AuthService)
步骤一:查看 application.yml文件,确定 jwt配置信息Step 2: Create a JwtProperties file for loading sc.jwt configuration information
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 Uncle Tong
-
@email [email protected]
*/
@Data
@ConfigurationProperties(prefix = “sc.jwt”)
@Component
public class JwtProperties {private String secret; // key
private String pubKeyPath;// public key
private String priKeyPath;// private key
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);
File priFile = new File(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 (Exception 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”)
public BaseResult login(@RequestBody AuthUser user){ //Verification verification code – delete after use String redisCode = stringRedisTemplate.opsForValue().get( “login” + user.getUsername() ); stringRedisTemplate.delete( "login" + user.getUsername() ); if(redisCode == null) { return BaseResult.error("Invalid verification code"); } if(! redisCode.equalsIgnoreCase(user.getCode())) { return BaseResult.error("Verification code error"); } //Login AuthUser loginUser = authService.login(user); if(loginUser != null ) { //Generate 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 Using token
Step 1: Save the token after successful login, and modify the Login.vue pageasync 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 } }
Step 2: The request automatically carries the token, modify apiclient.js, and add the token to the request header
//参考 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 )
}Step 3: Check nuxt.conf.js, change the plug-in mode to "client"
Otherwise throw an exception "sessionStorage is not defined"
plugins: [
{ src: '~plugins/apiclient.js', mode: 'client' }
] ,4.9.3 Verification
of token Token verification is completed at the gateway project
Step 1: Modify application.yml to add jwt configuration#Custom content
sc:
jwt:
secret: sc@Login(Auth}*^31)&czxy% # Login verification key
pubKeyPath: D:/rsa/rsa.pub # Public key address
priKeyPath: D:/rsa/ rsa.pri # Private key address
expire: 360 # Expiration time, in minutesStep 2: Create JwtProperties for loading configuration files
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 Uncle Tong
-
@email [email protected]
*/
@Data
@ConfigurationProperties(prefix = “sc.jwt”)
public class JwtProperties {private String secret; // key
private String pubKeyPath;// public key
private String priKeyPath;// private key
private int expire;//token expiration time
private PublicKey publicKey; // 公钥
private PrivateKey privateKey; // private key
private static final Logger logger = LoggerFactory.getLogger(JwtProperties.class);
@PostConstruct
public void init(){
try {
File pubFile = new File(this.pubKeyPath);
File priFile = new File(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 (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}
步骤三:编写过滤器,对所有路径进行拦截
package 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;
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 Uncle Tong
-
@email [email protected]
*/
@Component
public class LoginFilter implements GlobalFilter, Ordered {@Resource
private JwtProperties jwtProperties;@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1 获得请求路径
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
System.out.println(path);//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;
}
}
Step 4: Modify the front-end apiclient.js file to handle 401 exceptions
// Handle response exception
$axios.onError(error => { // token invalid, server responds 401 if(error.response.status === 401) { console.error(error.response.data) redirect('/login ') } })
api.js complete code
var axios = null
export default ({ $axios, redirect, process }, inject) => {//参考 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 )
}// Handle response exception
$axios.onError(error => { // token invalid, server responds 401 if(error.response.status === 401) { console.error(error.response.data) redirect('/login ') } })
//Assign
axios = $axios//4) Hand over the custom function to nuxt
// Usage 1: In vue, this. request . xxx ( ) // Usage 2: In nuxt's async Data, content . app . request. xxx( ) // Usage 2: In nuxt's asyncData, content.app.re q u es t . xxx ( ) // Usage method 2 : in a sy n cD a t a of n ux t , con t e n t . a pp . request.xxx()
inject('request' , request)
}4.9.4 Whitelist
All resources that do not need to be intercepted are configured in the yml file and released directly in the filter
Step 1: Modify the application.yml file#自定义内容
sc:
jwt:
secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥
pubKeyPath: D:/rsa/rsa.pub # 公钥地址
priKeyPath: D:/rsa/rsa.pri # 私钥地址
expire: 360 # 过期时间,单位分钟
filter:
allowPaths:
- /checkusername
- /checkmobile
- /sms
- /register
- /login- /verifycode
- /categorys
- /news
- /brands
- /specifications
- /search
- /goods
- /comments
- swagger
- /api-docs
步骤二:创建FilterProperties配置文件,用于存放允许放行的路径
package com.czxy.changgou4.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;import java.util.List;
/**
-
@author 桐叔
-
@email [email protected]
*/
@Data
@ConfigurationProperties(prefix=“sc.filter”)
public class FilterProperties {//允许访问路径集合
private List allowPaths;
}
步骤三:修改 LoginFilter,放行名单中配置的路径
package 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;
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();
String path = request.getURI().getPath();
System.out.println(path);//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.首页模块
5.1构建首页
步骤一:创建 ~/pages/index.vue页面步骤二:复制静态index.html页面到 index.vue中,并修改js和css引用方式
步骤三:引入已有公共组件
<!-- 头部 start --> <!-- 省略 html原有内容 --> <!-- 底部导航 end --> <div style="clear:both;"></div> <!-- 底部版权 start --> <Footer></Footer> <!-- 底部版权 end -->
步骤四:将所有“头部”抽取到“HeaderSearch”组件
1)修改图片路径步骤五:将所有“底部导航”抽取“BottomNav”组件
步骤六:修改 ~/pages/index.vue,添加“HeaderSeach”和“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快报
5.2.1接口
GET http://localhost:10010/web-service/news?pageNum=1&pageSize=5&sortWay=desc5.2.2后端实现: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 {
}
Step 3: Create the News object
package com.czxy.changgou4.pojo according to the database table;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 Uncle Tong
-
@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(value = “updated_at”)
private Date updatedAt;
}
5.2.3后端实现:查询功能
步骤一:创建mapper
package com.czxy.changgou4.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.czxy.changgou4.pojo.News;
import org.apache.ibatis.annotations.Mapper;/**
- @author 桐叔
- @email [email protected]
*/
@Mapper
public interface NewsMapper extends BaseMapper {
}
步骤二:创建service接口
package com.czxy.changgou4.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.czxy.changgou4.pojo.News;
import com.czxy.changgou4.vo.NewsVo;/**
-
@author 桐叔
-
@email [email protected]
*/
public interface NewsService extends IService {/**
- 查询所有新闻
- @param newsVo
- @return
*/
public Page findAll(NewsVo newsVo);
}
步骤三:创建service实现类,按照创建时间,根据排序方式进行排序
package com.czxy.changgou4.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.czxy.changgou4.mapper.NewsMapper;
import com.czxy.changgou4.pojo.News;
import com.czxy.changgou4.service.NewsService;
import com.czxy.changgou4.vo.NewsVo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;/**
-
@author Uncle 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;
}
}
Step 4: Modify the controller
package com.czxy.changgou4.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.czxy.changgou4.pojo.News;
import com.czxy.changgou4.service.NewsService;
import com.czxy.changgou4.vo.BaseResult;
import com.czxy.changgou4.vo.NewsVo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
/**
-
@author Uncle Tong
-
@email [email protected]
*/
@RestController
@RequestMapping(“/news”)
public class NewsController {@Resource
private NewsService newsService;@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) => {//Assign
axios = $axios//4) Hand over the custom function to nuxt
// Usage 1: In vue, this. request . xxx ( ) // Usage 2: In nuxt's async Data, content . app . request. xxx( ) // Usage 2: In nuxt's 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分类
5.3.1接口
GET http://localhost:10010/web-service/categorys
{
“code”: 1,
“message”: “查询成功”,
“data”: [
{
“id”: 1,
“children”: [
{
“id”: 2,
“children”: [
{
“id”: 3,
“children”: [],
“cat_name”: “电子书”,
“parent_id”: 2,
“is_parent”: false
},
{
“id”: 4,
“children”: [],
“cat_name”: “网络原创”,
“parent_id”: 2,
“is_parent”: false
}
],
“cat_name”: “电子书刊”,
“parent_id”: 1,
“is_parent”: true
}
],
“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;//All children of the current category
@TableField(exist = false)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List children = new ArrayList<>();
}
5.3.3 Backend Implementation: Query
Step 1: Create 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 Uncle Tong
- @email [email protected]
*/
@Mapper
public interface CategoryMapper extends BaseMapper {
}
步骤二:创建service接口, 添加查询所有方法
package com.czxy.changgou4.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.czxy.changgou4.pojo.Category;import java.util.List;
/**
- @author 桐叔
- @email [email protected]
/
public interface CategoryService extends IService {
/*- 查询所有一级分类,每一个分类中含子分类
- @return
*/
public List findAll();
}
步骤三:创建service实现类,按照parentId升序排序,并将所有的分类进行按照级别进行处理。
package 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 Uncle Tong
-
@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;
}
}
Step 4: Create a controller
package com.czxy.changgou4.controller;
import com.czxy.changgou4.pojo.Category;
import com.czxy.changgou4.service.CategoryService;
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;import javax.annotation.Resource;
import java.util.List;/**
-
@author Uncle Tong
-
@email [email protected]
*/
@RestController
@RequestMapping(“/categorys”)
public class CategoryController {@Resource
private CategoryService categoryService;@GetMapping
public BaseResult findAll(){ //1 query List list = categoryService.findAll(); //2 package return BaseResult.ok("query successful", list); }
}
5.3.4 Front-end implementation
Step 1: Modify "apiserver.js" to query all categories//Query all categories
findCategorys: () => { return axios.get(“/web-service/categorys”) }Step 2: Modify index.vue, use asyncData query classification
asyncData function can only be used in the pages directoryasync asyncData( { app } ) { // Query News + Query Category let [{data:newsData}, {data: categoryData}] = await Promise.all([ app. request Server . find News ( ) , app . 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 } ) {
// 查询快报 + 查询分类
let [{data: categoryData}] = await Promise.all([
app.$requestServer.findCategorys()
])
return {
categorysList : categoryData.data
}
}步骤五:修改logo访问路径
6.3指定分类的所有品牌
6.3.1接口
GET http://localhost:10010/web-service/brands/category/766.3.2后端实现:JavaBean
表结构分析:
分类表:tb_category
品牌表:tb_brand
关系:多对多(不同的分类,拥有不同的品牌)tb_category_brandpackage 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;/**
-
@author 桐叔
-
@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; //品牌名称@TableField(value = “logo”)
private String logo; //品牌图片地址
}
6.3.3后端实现:查询
步骤一:创建BrandMapper接口,根据分类查询所有的品牌
package com.czxy.changgou4.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.czxy.changgou4.pojo.Brand;
import org.apache.ibatis.annotations.*;import java.util.List;
/**
-
@author 桐叔
-
@email [email protected]
*/
@Mapper
public interface BrandMapper extends BaseMapper {/**
- 根据分类查询所有的品牌
- @param categoryId
- @return
/
@Select("select b. from tb_brand b ,tb_category_brand cb " +
" where b.id = cb.brand_id and cb.category_id = #{categoryId}")
public List findAll(@Param(“categoryId”) Integer categoryId);
}
步骤二:创建BrandService接口
package com.czxy.changgou4.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.czxy.changgou4.pojo.Brand;import java.util.List;
/**
- @author 桐叔
- @email [email protected]
/
public interface BrandService extends IService {
/*- 根据分类查询所有的品牌
- @param categoryId
- @return
*/
public List findAll(Integer categoryId);
}
步骤三:编写BrandService实现类
package com.czxy.changgou4.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.czxy.changgou4.mapper.BrandMapper;
import com.czxy.changgou4.pojo.Brand;
import com.czxy.changgou4.service.BrandService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;
/**
-
@author 桐叔
-
@email [email protected]
*/
@Service
@Transactional
public class BrandServiceImpl extends ServiceImpl<BrandMapper, Brand> implements BrandService {@Override
public List findAll(Integer categoryId) {
return baseMapper.findAll(categoryId);
}
}
步骤四:创建BrandController,符合接口规范
package com.czxy.changgou4.controller;import com.czxy.changgou4.pojo.Brand;
import com.czxy.changgou4.service.BrandService;
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 Uncle Tong
-
@email [email protected]
*/
@RestController
@RequestMapping(“/brands”)
public class BrandController {@Resource
private BrandService brandService;/**
- //GET /brands/category/76
- @param categoryId
- @return
*/
@GetMapping(“/category/{categoryId}”)
public BaseResult findAll(@PathVariable(“categoryId”) Integer categoryId){
//1 查询
List list = this.brandService.findAll( categoryId );
//2 封装
return BaseResult.ok(“查询成功”, list );
}
}
6.3.4前端实现
步骤一:修改apiclient.js,编写ajax//指定分类的品牌
findBrand : (categoryId) => {
return axios.get(‘/web-service/brands/category/’ + categoryId)
}步骤二:修改 ~/pages/list/_cid 页面,页面加载成功后,获得分类id
data() {
return {
searchMap: { //搜索条件对象
catId: ‘’ //分类id
}
}
},
async mounted() {
//设置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指定分类的所有规格
6.4.1接口
GET http://localhost:10010/web-service/specifications/category/766.4.2后端实现:JavaBean
表结构分析:
规格表:tb_specification
规格选项表:tb_specification_option
关系:一对多(一个规格,拥有多个规格选项)规格选项
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;/**
-
@author 桐叔
-
@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; //Specification name@TableField(value = "category_id")
private Integer categoryId; //category foreign key
private Category category; //category foreign key corresponding objectprivate List options; //One specification with multiple specification options
}
6.4.3后端实现:查询
查询规格的同时,查询对应的规格选项步骤一:创建 SpecificationOptionMapper,用于查询指定“规格”的所有“规格选项”
package com.czxy.changgou4.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.czxy.changgou4.pojo.SpecificationOption;
import org.apache.ibatis.annotations.*;import java.util.List;
/**
-
@author 桐叔
-
@email [email protected]
*/
@Mapper
public interface SpecificationOptionMapper extends BaseMapper {/**
- 查询指定规格的所有规格选项
- @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
public interface SpecificationMapper extends BaseMapper {
/*- 查询指定分类的所有规格
- @param categoryId
- @return
*/
@Select(“select * from tb_specification where category_id = #{categoryId}”)
@Results({
@Result(property = “id”, column = “id”),
@Result(property = “specName”, column = “spec_name”),
@Result(property = “categoryId”, column = “category_id”),
@Result(property = “options”, many=@Many(select=“com.czxy.changgou4.mapper.SpecificationOptionMapper.findSpecOptionBySpecId”), column = “id”),
})
public List findSpecificationByCategoryId(@Param(“categoryId”) Integer categoryId);
}
步骤三:创建SpecificationService接口
package com.czxy.changgou4.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.czxy.changgou4.pojo.Specification;import java.util.List;
/**
- @author Uncle Tong
- @email [email protected]
/
public interface SpecificationService extends IService {
/*- Query all specifications of the specified category
- @param categoryId
- @return
*/
public List findSpecificationByCategoryId(Integer categoryId);
}
Step 4: Create a SpecificationService implementation class
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 Uncle Tong
- @email [email protected]
*/
@Service
@Transactional
public class SpecificationServiceImpl extends ServiceImpl<SpecificationMapper, Specification> implements SpecificationService {
@Override
public List findSpecificationByCategoryId(Integer categoryId) {
return baseMapper.findSpecificationByCategoryId(categoryId);
}
}
步骤五:创建 SpecificationController
package 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 Front-end implementation
Step 1: modify api.js, query specifications//specify the category
findSpec : (categoryId) => { return axios.get('/web-service/specifications/category/' + categoryId) },Step 2: Modify pages/list/_id, the page is loaded successfully, query specification
data() { return { searchMap: { //search condition object catId: '', //category id brandId: '', // brand id } , brandList : [], //all brands specList : [], //all specifications
}
},
methods: {
async findAllBrandFn() {
let { data : brandData } = await this.KaTeX parse error: Expected 'EOF', got '}' at position 85: …dData.data }̲, async fin…request.findSpec( this.searchMap.catId )
this.specList = specData.data
},
brandSearch(bid) {
//记录品牌id
this.searchMap.brandId = bid;//查询 this.searchList(); }, searchList () { console.info(this.searchMap) }
},
async mounted() {//设置id this.searchMap.catId = this.$route.params.cid // 查询所有的品牌 this.findAllBrandFn() // 查询所有的规格 this.findAllSpecFn()
},
Step 3: Display data
<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>
Step 4: Select the specification, and the overall content will be displayed
specSearch (spec , option ) { //记录选中的id this.$set(spec,'selectId', option ? option.id : '' ) }
6.5前置技术:ElasticSearch
参考《elasticsearch入门.doc》6.6电商概念:SKU和SPU
SPU = Standard Product Unit (标准产品单位)
SPU是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。
通俗点讲,属性值、特性相同的商品就可以称为一个SPU。
例如:
iphone X就是一个SPU,与颜色、款式、套餐都无关。
SKU=stock keeping unit(库存量单位)
SKU即库存进出计量的单位, 可以是以件、盒、托盘等为单位。
SKU是物理上不可分割的最小存货单元。
例如:
iPhone X 128G 黑色,就是一个SKU总结
SPU:可以理解为一类商品的总称(一组产品特性的集合)
SKU:可以称之为最小库存单位,也就是所能唯一确定一件商品的唯一标识。6.7流程分析
sku查询功能:从数据库查询sku相关的所有信息
数据同步:将数据库中的数据,同步到es中。()
数据库查询搜索慢、es查询速度快
同步时机:1. 定时器(理论)、2. 测试程序(实施)
搜索服务:从elasticsearch中搜索数据,默认情况下es中没有数据,通过“数据同步”操作后,es有数据详细版流程
6.8表结构分析
商品相关表总览表间关系
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;
//审核时间
@TableField(value=“check_time”)
private String checkTime;
//审核状态 审核状态,0:未审核,1:已通过,2:未通过
@TableField(value=“check_status”)
private String checkStatus;
//价格
@TableField(value=“price”)
private String price;
//是否上架
@TableField(value=“is_on_sale”)
private Integer isOnSale;
//上架时间
@TableField(value=“on_sale_time”)
private Date onSaleTime;
//删除时间
@TableField(value=“deleted_at”)
private String deletedAt;@TableField(value=“weight”)
private String weight;//商品描述
@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;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;/**
-
Created by liangtong.
*/
@TableName(“tb_sku”)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Sku {@TableId(type = IdType.AUTO)
private Integer id;
//库存量
@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;
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 java.util.Date;
/**
-
Created by liangtong.
*/
@TableName(“tb_sku_comment”)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SkuComment {@TableId(type = IdType.AUTO)
private Integer id;
@TableField(value=“created_at”)
private Date createdAt;
@TableField(value=“updated_at”)
private Date updatedAt;@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;//图片地址
private String skuName;//sku名字
private String all; // 所有需要被搜索的信息,包含标题,分类,甚至品牌
private Date onSaleTime;//上架时间
//品牌编号
private Integer brandId;
//分类id
private Integer catId;
//规格列表
private Map<String, Object> specs;// 可搜索的规格参数,key是参数名,值是参数值
private Double price;// 价格
private String spuName;
private Integer stock;
private String description;
private String packages;//规格与包装
private String aftersale;//售后保障
private String midlogo;
//评价数
private Integer commentCount;
// 销量
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(property = “spuName” , column = “spu_name”),
@Result(property = “spuSubname” , column = “spu_subname”),
@Result(property = “cat1Id” , column = “cat1_id”),
@Result(property = “cat2Id” , column = “cat2_id”),
@Result(property = “cat3Id” , column = “cat3_id”),
@Result(property = “brandId” , column = “brand_id”),
@Result(property = “checkTime” , column = “check_time”),
@Result(property = “checkStatus” , column = “check_status”),
@Result(property = “isOnSale” , column = “is_on_sale”),
@Result(property = “onSaleTime” , column = “on_sale_time”),
@Result(property = “deletedAt” , column = “deleted_at”),
@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: Query the number of comments
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;
/**
-
Created by liangtong.
*/
@Mapper
public interface SkuCommentMapper extends BaseMapper {/**
- 查询评论数
- @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:查询所有sku,含对应的spu信息
package com.czxy.changgou4.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.czxy.changgou4.pojo.Sku;
import org.apache.ibatis.annotations.*;import java.util.List;
/**
-
Created by liangtong.
*/
@Mapper
public interface SkuMapper extends BaseMapper {@Select(“select * from 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(column=“spec_info_id_txt”,property=“specInfoIdTxt”),
@Result(column=“spu_id”,property=“spu”,
one=@One(
select=“com.czxy.changgou4.mapper.SpuMapper.findSpuById”
))
})
public List findAllSkus();
}
SkuPhotoMapper
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 {
}
package com.czxy.changgou4.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.czxy.changgou4.pojo.Sku;
import com.czxy.changgou4.vo.ESData;import java.util.List;
/**
-
@author Uncle Tong
-
@email [email protected]
*/
public interface SkuService extends IService {/**
*- @return
*/
public List findESData();
}
- @return
package com.czxy.changgou4.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.czxy.changgou4.mapper.SkuCommentMapper;
import com.czxy.changgou4.mapper.SkuMapper;
import com.czxy.changgou4.pojo.Sku;
import com.czxy.changgou4.service.SkuService;
import com.czxy.changgou4.vo.ESData;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;/**
-
@author Uncle 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;
import com.czxy.changgou4.service.SkuService;
import com.czxy.changgou4.vo.BaseResult;
import com.czxy.changgou4.vo.ESData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;
/**
-
@author Uncle Tong
-
@email [email protected]
*/
@RestController
@RequestMapping(“/sku”)
public class SkuController {@Autowired
private 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;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;/**
-
@author 桐叔
-
@email [email protected]
*/
@Configuration
@EnableElasticsearchRepositories
public class RestClientConfig extends AbstractElasticsearchConfiguration {@Override
@Bean
public RestHighLevelClient elasticsearchClient() {final ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo("localhost:9200") .build(); return RestClients.create(clientConfiguration).rest();
}
}
Startup class
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 Uncle 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;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;import java.util.Date;
import java.util.Map;/**
-
Created by liangtong.
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = “skus”, type = “docs” )
public class SearchSku {
@Id
private Long id; // skuId
@Field(type = FieldType.Text)
private String logo;//图片地址
@Field(type = FieldType.Text ,analyzer = “ik_max_word”)
private String skuName;//sku名字
@Field(type = FieldType.Text ,analyzer = “ik_max_word”)
private String all; // 所有需要被搜索的信息,包含标题,分类,甚至品牌
@Field(type = FieldType.Date)
private Date onSaleTime;//商家时间
//品牌编号
@Field(type = FieldType.Integer)
private Integer brandId;
// 分类id
@Field(type = FieldType.Integer)
private Integer catId;//Specification list
//@IndexDynamicSettings
private Map<String, Object> specs;// Searchable specification parameters, key is parameter name, value is parameter value
@Field(type = FieldType.Double)
private Double price;// 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;//Specification and packaging
@Field(type = FieldType.Text)
private String aftersale;//After-sale guarantee
@Field(type = FieldType.Text)
private String midlogo;
//Number of evaluations
@Field(type = FieldType.Integer)
private Integer commentCount;
// sales
@Field(type = FieldType.Integer)
private Integer sellerCount;
}
Step 2: Remote call
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();
}
Step 3: Test class
package com.czxy.changgou4;
import com.czxy.changgou4.feign.SkuFeign;
import com.czxy.changgou4.repository.SkuRepository;
import com.czxy.changgou4.vo.BaseResult;
import com.czxy.changgou4.vo.SearchSku;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;import javax.annotation.Resource;
import java.util.List;/**
-
@author Uncle Tong
-
@email [email protected]
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CGSearchServiceApplication.class)
public class TestSkuClient {
@Resource
private SkuFeign skuFeign;@Test
public void findAllSkus() throws Exception {
BaseResult<List> baseResult = skuFeign.findESData();
List list = baseResult.getData();
System.out.println(“总条数:” + list.size());
for(SearchSku ss:list){
System.out.println(ss);
}
}
}
6.10.3整合ElasticSearch
步骤一:创建repository类package 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() throws Exception {
BaseResult<List> baseResult = skuFeign.findESData();
List list = baseResult.getData();
for(SearchSku ss:list){
ss.setAll(ss.toString());
System.out.println(ss);
}
this.skuRepository.saveAll(list);
}6.11基本查询
6.11.1查询所有
package 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 Uncle 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;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)); //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根据品牌查询
package 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)); //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;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", "灰色")); //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价格区间查询
package 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;
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.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
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.domain.PageRequest;
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));
// 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 paging
package 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.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.junit4.SpringRunner;import javax.annotation.Resource;
import java.util.List;/**
-
@author Uncle 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));
// 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); //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排序
package 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.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
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.domain.PageRequest;
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); //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页面定制响应对象
- Created by liangtong.
*/
@Data
public class ReturnSku {
private Long id;
private String goodsName; //sku名称
private Double price; //价格
private String midlogo; //sku logo
private Integer commentCount; //sku的评论
}
6.12.5 Implementation
Step 1: Write service interface, use SkuRepository to query from es
Write 2: Write service implementation class
Step 3: Write controller, query sku information of specified classification from esStep 1: Write service interface, use SkuRepository to query package com.czxy.changgou4.service from es
;import com.czxy.changgou4.vo.SearchVo;
import java.util.Map;
/**
- @author Uncle Tong
- @email [email protected]
/
public interface SkuSearchService {
/*- Query sku according to conditions and encapsulate the results into a Map
- map.count , the total number of entries
- map.list , total records
- @param searchVo
- @return custom package data
*/
public Map search(SearchVo searchVo);
}
Step 2: Write service implementation class
package com.czxy.changgou4.service.impl;
import com.czxy.changgou4.repository.SkuRepository;
import com.czxy.changgou4.service.SkuSearchService;
import com.czxy.changgou4.vo.ReturnSku;
import com.czxy.changgou4.vo.SearchSku;
import com.czxy.changgou4.vo.SearchVo;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;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
public class SkuSearchServiceImpl implements SkuSearchService {
@Resource
private SkuRepository skuRepository;@Override
public Map search(SearchVo searchVo) {
//1 创建查询构建器
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;
}
}
步骤三:编写controller,从es查询指定分类的sku信息
package 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 Uncle Tong
-
@email [email protected]
*/
@RestController
@RequestMapping(“/sku”)
public class SkuSearchController {
@Resource
private SkuSearchService skuSearchService;@PostMapping(“/search”)
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测试
http://localhost:10010/search-service/swagger-ui.html
{
“catId”: “76”,
“pageNum”: 1,
“pageSize”: 3,
“skuName”: “手机”,
“brandId”: 8557,
“specList”: {“机身颜色”:“白色”,“内存”:“4GB”,“机身存储”:“64GB”},
“minPrice”: 80000,
“maxPrice”: 90000
}6.13前端实现:
6.13.1商品搜索
步骤一:修改apiclient.js,发送ajax//搜索
search : ( params ) => {
return axios.post(‘/search-service/sku/search’, params )
}步骤二:修改data区域,添加分页条件、查询结果searchResult
data() { return { searchMap: { //search condition object catId: '', //category id brandId: '', //brand id pageNum: 1, //page: which page pageSize: 10, //page : the number of items displayed on each page
}, brandList : [], //所有品牌 specList : [], //所有规格 searchResult: {} , //搜索结果 }
},
Step 3: Modify the searchList function
async searchList () { // ajax 查询 let { data } = await this.$request.search( this.searchMap ) // 处理结果 this.searchResult = data.data },
Step 4: The page is loaded successfully, and the data is queried
Step 5: Display data
<!-- 商品列表 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-->
Step 6: Price optimization, convert cents into yuan, and keep 2 decimal places
filters: {
priceHandler(value) {
if(value) {
return (Number(value) / 100).toFixed(2)
}
return value
},
},Step 7: Product name optimization
filters: { substr(value, len, suffix) { // If the length of the string to be processed is less than the specified length, return the string to be processed // If it is greater than the execution length, intercept the string and append a suffix return value.length < len ? value : value.substring(0,len) + suffix }, },
6.13.2 Commodity Search – Specifications
Step 1: Modify searchMap and improve search conditionsStep 2: Improve specSearch Function
specSearch (spec , option ) { //Record the selected id this.$set(spec,'selectId', option ? option.id : '' ) //Set the selected parameter this.searchMap.specList [spec.spec_name] = option.option_name //query this.searchList() }6.13.3 Product Search – Sorting
Step 1: Modify the data area and add sorting conditionsStep 2: Modify the label
-
Sort by:
Step 3: Write the sortSearch function
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 Commodity Search – Price Range
Price:
-
<input type="submit" value="search" @click.prevent="searchList" /> Check: Are the front-end price unit and the back-end price unit consistent?
Back-end price unit: cent
Front-end price unit: yuan6.13.5 Product Search – Paging
Step 1: Copy the paging componentStep 2: Import the pagination component
Step 3: Use the pagination component
<!--
组件
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 { methods: { ...mapMutations(['setData']), // Get the setData method search() in vuex { // Call the setData method to synchronize the data to the keyword variable in vuex this.setData({'key': 'keyword', 'value': this.keyword}) } }, }
<input type="submit" class="btn" @click.prevent="search" value="search" /> Step
4: Modify ~/pages/list/_cid, monitor the keyword in vuex, if the data transmission changes, proceed keyword querywatch: {
‘$store.state.keyword’(newValue, oldValue) {
this.searchMap.keyword = newValue
this.searchList()
},
},6.13.7 Brand condition optimization
Requirement: There are too many brand conditions, and control buttons are provided to display or hide redundant brands.Step 1: Provide the variable brandMore to control the display and hiding of redundant department brands.
Step 2: Provide calculated attributes to control the display of brand data
computed: { brandFilter() { // Display all or display the first 19 return this.brandMore ? this.brandList : this.brandList.slice(0,19) }, },
Step 3: Display prompt information
<span style="cursor: pointer;" @click="changeBrandMore">{ {this.brandMore ? 'Receive': 'More'}}
Step 4: Write the changeBrandMore() function to switch brandMore
changeBrandMore() { this.brandMore=!this.brandMore }7. Details page module
7.1 Build details page
Step 0: Determine the access path
http://localhost:3000?Goods?id=1Step 1: Create ~pages/Goods.vue page
Step 2: Copy the content of ~/static/goods.html and import third-party resources (css, js)
head: { title: 'list page', link: [ {rel:'stylesheet',href: '/style/goods .css'}, {rel:'stylesheet',href: '/style/common.css'}, {rel:'stylesheet',href: '/style/bottomnav.css'}, {rel:'stylesheet', href: '/style/jqzoom.css'}, ], script: [ { type: 'text/javascript', src: '/js/header.js' }, { type: 'text/javascript', src: ' /js/goods.js' }, { type: 'text/javascript', src: '/js/jqzoom-core.js' }, ] },Step 3: Import public resources
Step 4: Add the original page js special effects
7.2 Details
7.2.1 Analysis7.2.2 Interface
GET http://localhost:10010/web-service/sku/goods/2600242
return value
{ id: "product ID, skuid", goods_name: "product name", price: "price", on_sale_date: " Shelving time", comment_count: "Number of comments", comment_level: "Comment level (1-5)", cat1_info:{ id: "Category ID", cat_name: "Category name" }, cat2_info:{ id: "Category ID" , cat_name: "category name" }, cat3_info:{ id: "category ID", cat_name: "category name" }, logo:{ smlogo: "small LOGO (50x50)", biglogo: "big LOGO (350x350)",
xbiglogo:“超大LOGO(800x800)”
},
photos:[
{
smimg:“商品图片(50x50)”,
bigimg:“商品图片(350x350)”,
xbigimg:“商品图片(800x800)”
},
…
],
description:“商品描述”,
aftersale:“售后”,
stock:“库存量”,
spec_list:[
{
id:“规格ID”,
spec_name:“规格名称”,
options:[
{
id:“选项ID”,
option_name:“选项名称”
}
…
]
}
…
],
spec_info: { id_list: "Specification ID: Option ID|Specification ID: Option ID|...", id_txt: "Specification Name: Specification Option|Specification Name: Specification Option|..." }, sku_list: [ { skuid: "SKUID", id_list: "Specification ID: Option ID|Specification ID: Option ID|..." }, ... ] }
7.2.3初始化数据
insert into tb_sku_photo(sku_id,url) values(2600242,‘http://img12.360buyimg.com/n1/s450x450_jfs/t1/100605/24/7603/222062/5dfc6d30Ec375bf0a/e29b6690731acb24.jpg’);
insert into tb_sku_photo(sku_id,url) values(2600242,‘http://img12.360buyimg.com/n1/s450x450_jfs/t1/110371/2/1323/189888/5dfc6d30E073c3495/cb256ec2d3cf9ae2.jpg’);
insert into tb_sku_photo(sku_id,url) values(2600242,‘http://img12.360buyimg.com/n1/s450x450_jfs/t1/95005/38/7465/139593/5dfc6d2fEd2317126/63b5253237353618.jpg’);7.2.4后端实现:JavaBean
SkuPhoto : sku对应的所有图片
OneSkuResult:用于封装sku详情步骤一:创建SkuPhoto,根据tb_sku_photo表编写内容
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;
}
步骤二:创建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 {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 Category cat1Info;
@JsonProperty(“cat2_info”)
private Category cat2Info;
@JsonProperty(“cat3_info”)
private Category cat3Info;
private Map<String, String> logo;
private List
}
7.2.5后端实现:Mapper
步骤一:修改skuCommentMapper,完成“评论级别”功能
@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(property=“id”, column=“id”),
@Result(property=“skuId”, column=“sku_id”),
@Result(property=“url”, column=“url”)
})
public List findSkuPhotoBySkuId(Integer spuId);
}
步骤三:修改SkuMapper,添加“查询指定spuId的所有sku”功能
/**
- 查询指定spuId的所有sku
- @param spuId
- @return
*/
@Select(“select * from tb_sku where spu_id = #{spuId}”)
@ResultMap(“skuResult”)
public List findSkuBySpuId(Integer spuId);
7.2.6后端实现:
步骤一:修改SkuService,添加findSkuById 方法/**
- 查询详情
- @param skuid
- @return
*/
public OneSkuResult findSkuById(Integer skuid);
步骤二:修改SkuServiceImpl,完成“查询详情”功能
package com.czxy.changgou4.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.czxy.changgou4.mapper.*;
import com.czxy.changgou4.pojo.Sku;
import com.czxy.changgou4.pojo.SkuPhoto;
import com.czxy.changgou4.pojo.Specification;
import com.czxy.changgou4.pojo.Spu;
import com.czxy.changgou4.service.SkuService;
import com.czxy.changgou4.vo.ESData;
import com.czxy.changgou4.vo.OneSkuResult;
import org.springframework.stereotype.Service;
import 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 查询所有详情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; } @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; }
}
步骤三:修改SkuController,完成“查询详情”功能
/**
-
查询详情
-
@param skuid
-
@return
*/
@GetMapping(“/goods/{skuid}”)
public BaseResult findSkuById(@PathVariable(“skuid”) Integer skuid){
OneSkuResult sku = skuService.findSkuById(skuid);return BaseResult.ok(“查询成功”, sku);
}
7.2.7前端实现
详情页面需要进行SSR步骤一:修改 “apiserver.js”,查询详情
//商品详情
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 Comments
7.4.1 Interface
GET http://localhost:10010/web-service/comments/spu/2?current=1&size=2
{ "code": 20000, "message": "query successful", "data": { "impressions": [ { "id": 1, "title": "Good taste", "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,
“ratio”: “2”,
“content”: “差”,
}
]
},
“other”: {}
}7.4.2分析
7.4.3后端实现:JavaBean
步骤一:创建 Impression ,用于封装印象表中的数据
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;/** 印象
-
@author 桐叔
-
@email [email protected]
*/
@TableName(“tb_impression”)
@Data
public class Impression {@TableId(type = IdType.AUTO)
private Integer id;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的所有印象”package com.czxy.changgou4.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.czxy.changgou4.pojo.Impression;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;import java.util.List;
/**
-
@author 桐叔
-
@email [email protected]
*/
@Mapper
public interface ImpressionMapper extends BaseMapper {/**
- 查询指定SpuId的所有印象
- @param spuid
- @return
*/
@Select(“select * from tb_impression where spu_id = #{spuid}”)
public List findImpressionsBySpuid(@Param(“spuid”) Integer spuid);
}
步骤二:修改 SkuCommentMapper,完成“查询指定好评度的评论数”
/**
- 查询指定好评度的评论数
- @param spuid
- @param ratio 0好评、1中评、2差评
- @return
/
@Select("select count() from tb_sku_comment where spu_id = #{spuid} and ratio = #{ratio}")
public Integer findCommentCountByRatio(@Param(“spuid”)Integer spuid,@Param(“ratio”)Integer ratio);
步骤三:修改 SkuCommentMapper,完成“查询SpuId的评论数”
/**
- 查询SpuId的评论数
- @param spuid
- @return
/
@Select("select count() from tb_sku_comment where spu_id = #{spuid}")
public Integer findTotalCommentBySpuid(@Param(“spuid”) Integer spuid);
步骤四:修改 skuCommentMapper,完成“查询指定spu的所有评论”
/**
- 查询指定spu的所有评论
- @param spuid
- @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的所有评论”方法
package com.czxy.changgou4.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.czxy.changgou4.pojo.SkuComment;
import com.czxy.changgou4.vo.CommentResult;
import com.czxy.changgou4.vo.PageRequest;/**
- @author 桐叔
- @email [email protected]
/
public interface SkuCommentService extends IService {
/*
*- @param spuid
- @param pageRequest
- @return
*/
public CommentResult findComments(Integer spuid, PageRequest pageRequest);
}
步骤六:创建 SkuCommentServiceImpl实现类
package 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前端实现
步骤一:修改 apiclient.js,添加 getComments 函数,完成查询评论操作//评论
getComments : (spuid , pageSize, pageNum ) => {
return axios.get(/web-service/comments/spu/${spuid}
,{
params: {
pageSize: pageSize,
pageNum: pageNum
}
})
},步骤二:修改 Goods.vue ,编写查询评论函数,用于支持分页
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 }
步骤三:修改 Goods.vue ,页面加载成功后,查询评论(第一页)
//评论
this.pageChanged(1)步骤四:修改 Goods.vue ,展示“好评度”
步骤五:修改 Goods.vue ,展示“买家印象”<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>
-
买家印象:
- { {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-client<!-- 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>
步骤三:创建yml文件,
#端口号
server:
port: 8095
spring:
application:
name: cart-service
redis:
host: 127.0.0.1
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos服务地址
#自定义内容
sc:
jwt:
secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥
pubKeyPath: D:/rsa/rsa.pub # 公钥地址
priKeyPath: D:/rsa/rsa.pri # 私钥地址
expire: 360 # 过期时间,单位分钟步骤四:拷贝JWT配合类 + Swagger + Redis
步骤五:启动类
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 Uncle Tong
- @email [email protected]
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class CGCartServiceApplication {
public static void main(String[] args) {
SpringApplication.run(CGCartServiceApplication.class, args);
}
}
8.2 Add to cart
8.2.1 Overall analysis8.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;
private Integer spuid;
@JsonProperty(“goods_name”)
private String goodsName;
private Double price;
private Integer count;//购买数量
private Boolean checked;
private String midlogo;
@JsonProperty(“spec_info”)
private String specInfo;
}
Shopping cart object: Cart
package com.czxy.changgou4.cart;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;/**
-
@author Uncle Tong
-
@email [email protected]
*/
@Data
public class Cart {private Map<Integer , CartItem > data = new HashMap<>();
private Double 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 );
}
}
Shopping cart specially customized object
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 Uncle Tong
-
@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; //选项名称
}
CartSpecification
package com.czxy.changgou4.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;import java.util.List;
/**
-
@author 桐叔
-
@email [email protected]
*/
@Data
public class CartSpecification {private Integer id;
@JsonProperty(“spec_name”)
private String specName; //规格名称private Integer categoryId; //分类外键
private CartCategory category; //分类外键对应对象private List options; //一个规格,具有多个规格选项
}
CartOneSkuResult
package com.czxy.changgou4.vo;
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 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 {private Integer skuid ; //"SKUID",
private Integer count; //"Purchase Quantity"
private Boolean checked; //"Purchase Quantity"
}
Step 2: Create a SkuClient for querying details
package com.czxy.changgou4.feign;
import com.czxy.changgou4.vo.BaseResult;
import com.czxy.changgou4.vo.OneSkuResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;/**
-
@author Uncle Tong
-
@email [email protected]
*/
@FeignClient(value=“web-service”,path = “/sku”)
public interface SkuClient {@GetMapping(“/goods/{skuid}”)
public BaseResult findSkuById(@PathVariable(“skuid”) Integer skuid);
}
Step 3: Create a CartService interface for adding business logic
package com.czxy.changgou4.service;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.vo.CartVo;/**
-
@author Uncle Tong
-
@email [email protected]
*/
public interface CartService {/**
- Add items to specified users
- @param user
- @param cartVo
*/
public void addCart(User user , CartVo cartVo);
}
Step 4: Create a CartService implementation class
package com.czxy.changgou4.service.impl;
import com.alibaba.fastjson.JSON;
import com.czxy.changgou4.cart.Cart;
import com.czxy.changgou4.cart.CartItem;
import com.czxy.changgou4.feign.SkuClient;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.service.CartService;
import com.czxy.changgou4.vo.BaseResult;
import com.czxy.changgou4.vo.CartOneSkuResult;
import com.czxy.changgou4.vo.CartVo;
import com.czxy.changgou4.vo.OneSkuResult;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;
/**
-
@author 桐叔
-
@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 ,完成“添加到购物车”方法//add to cart
addToCart : ( params ) => { return axios.post(“/cart-service/carts”, params ) },Step 2: Modify Goods.vue, and bind the click event addToCartFn to "add to shopping cart"
<input type=“submit” value=“” class=“add_btn” @click.prevent=“addToCartFn” />
Step 3: Modify Goods.vue to complete the addToCartFn function
Not logged in: save to sessionStorage
Login: save to redis
Function to be improved: when the user logs in, merge the product information saved in sessionStorage into redis
async addToCartFn(){ / /Get login ID let token = sessionStorage.getItem(“token”); if(token != null){ //Login: send ajax to add let newGoods = { skuid: this. KaTeX parse error: Expected 'EOF', got '}' at position 55: …yCount }̲; //login status… 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" }
Step 4: Write the flow1 page
shopping cart8.3 Viewing the shopping cart
8.3.1 Analysis
If the user has not logged in, the shopping cart is stored in the localStorage of the browser and stored in an array.
If the user logs in, the shopping cart is stored in redis and stored as a Cart object string.
Problem: The front-end and back-end data are inconsistent, and flow1.vue cannot be used for data display
Solution: Simplify the back-end cart data, Cart object –>Map(data)–>List(values)Conclusion: The front end provides a unified array to the page, and the page can display the data.
8.3.2接口
GET 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后端实现
步骤一:修改CartService,添加 queryCartList 方法,从redis查询的购物车信息
步骤二:修改CartController,添加queryCartList 方法,仅返回购物车中的数据步骤一:修改CartService,添加 queryCartList 方法,
/**
*- @param user
- @return
*/
public Cart queryCartList(User user);
步骤二:修改CartServiceImpl,从redis查询的购物车信息
/**-
查询购物车
-
@param user
-
@return
*/
public Cart queryCartList(User user) {
String key = “cart” + user.getId();
// 获取hash操作对象
String cartString = this.stringRedisTemplate.opsForValue().get(key);// 2 获得购物车,如果没有创建一个
return JSON.parseObject(cartString, Cart.class);
}
步骤三:修改CartController,添加queryCartList 方法,仅返回购物车中的数据
/**
-
查询购物车
-
@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>
步骤四:通过计算属性,计算总价格
computed : {
totalPrice : function(){ //计算总价格
//所有小计的和
let sum = 0 ;
this.cart.forEach( g => {
sum += (g.price * g.count);
});
return (sum/100).toFixed(2);
}
},8.4购物车操作:修改
8.4.1分析8.4.2接口
PUT http://localhost:10010/cart-service/carts8.4.3后端实现:更新
步骤一:修改service接口
/**- 更新操作:如果数据存在修改数据,如果数据不存在删除数据
- @param user
- @param cartVoList
*/
public void updateCart(User user, List cartVoList) ;
步骤二:修改service实现类
/**
-
更新操作:如果数据存在修改数据,如果数据不存在删除数据
-
@param user
-
@param cartVoList
*/
public void updateCart(User user, List cartVoList) {
//1 获得购物车
String key = “cart” + user.getId();
String cartStr = stringRedisTemplate.opsForValue().get(key);
// 处理是否有购物车
Cart cart = JSON.parseObject( cartStr , Cart.class);
if(cart == null) {
throw new RuntimeException(“购物车不存在”);
}//2 更新
//2.1 处理请求数据
Map<Integer,CartVo> requestMap = new HashMap<>();
for (CartVo cartVo : cartVoList) {
requestMap.put(cartVo.getSkuid(), cartVo);
}//2.2 处理更新和删除
Set keySet = new HashSet<>(cart.getData().keySet());
for (Integer skuid : keySet) {
CartVo cartRequest = requestMap.get(skuId);
if(cartRequest != null) {
// 更新
cart.updateCart(cartRequest.getSkuid() , cartRequest.getCount() ,cartRequest.getChecked());
} else {
// 删除
cart.deleteCart(skuId);
}
}//3 保存购物车
stringRedisTemplate.opsForValue().set( key , JSON.toJSONString(cart) );
}
步骤三:修改controller
@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 Front-end implementation: modification
Step 0: Modify apiclient.js, add updateCart function
Step 1: Modify flow1.vue to add events to buttons and text boxes
Step 2: Write and modify corresponding events
Step 3: Write shopping cart Cart’s monitoring function, as long as the data changes, it will be saved immediately
Step 4: Bind js before deletingStep 0: Modify apiclient.js, add updateCart function
updateCart : ( params ) => {
return axios.put(“/gccartservice/carts” ,params )
},Step 1: Modify flow1.vue to add events to buttons and text boxes
Step 2: Write and modify the corresponding event
methods: { minus : function(goods){ if( goods.count > 1) { goods.count --; } }, plus : function(goods){ //You can consider inventory 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 Front-end implementation: Select all
Step 1: Modify the form
Step 2: Modify the style
Step 3: Add the method of selecting all
Step 4: Monitor the shopping cart data and modify the status of the selectionStep 1: Modify the form
Step 2: Modify the style
.mycart .col0{width: 5%;}
.mycart .col1{width: 35%;}Step 3: Add the Select All method
data() { return { cart : [], //shopping cart object allChecked : false, //full selection state } },
methods: { checkAll : function(){ //The state of all list items is the opposite of the state of all selected 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);},
immediate: false, //true means that if it is declared in the watch, it will immediately execute the handler method inside
deep: true //Deep monitoring, often used to change the properties below the object
}},
8.4.6 Backend implementation: delete data
Only need to modify the data, watch has completed the delete operationStep 1: Modify the html binding delete event
Step 2: Write the delete function
del (index){ if(window.confirm("您确定要删除吗?")){ this.cart.splice(index , 1) } },
8.5 Settlement
8.5.1 Jump page
Step 1: Bind events to settlementsubmit : 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') } },
Step 2: Write the flow2.vue component
Step 3: Complete login
let returnURL = localStorage.getItem("returnURL"); if(returnURL != null){ //删除跳转页面缓存 localStorage.removeItem("returnURL"); //需要跳转页面 location.href= returnURL; } else { //默认:跳转首页 location.href = "/"; }
9. Order module
9.1 Build order service: 8100
Step 1: Build project, changgou4-service-ordersStep 2: Modify the pom.xml file and add dependencies
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>
Step 3: Modify the yml file
server:
port: 8100spring:
application:
name: order-service
datasource:
url: jdbc:mysql://127.0.0.1:3306/changgou_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 1234
driver-class-name: com.mysql.jdbc.Driver
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 1000
test-on-borrow: true
redis:
database: 0
host: 127.0.0.1
port: 6379
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos服务地址sc:
worker:
workerId: 1
datacenterId: 1
jwt:
secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥
pubKeyPath: D:/rsa/rsa.pub # 公钥地址
priKeyPath: D:/rsa/rsa.pri # 私钥地址
expire: 360 # 过期时间,单位分钟
pay:
appID: wx8397f8696b538317
mchID: 1473426802
key: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
httpConnectTimeoutMs: 5000
httpReadTimeoutMs: 10000步骤四:启动类
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;@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class CGOrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(CGOrderServiceApplication.class, args);
}
}9.2 List of consignees
9.2.1 Interface
GET http://localhost:10010/order-service/address
{ "code": 20000, "message": "Query succeeded", "data": [ { "id": 1 , "userId": 1, "shr_name": "Xiao Ming", "shr_mobile": "13344445555", "shr_province": "Jiangsu Province", "shr_city": "Suqian City", "shr_area": "Shuyang County", "shr_address": "No. 57, Changzhou Road", "isdefault": 0 } ], "other": {} }
9.2.2 Backend implementation: JavaBean
Step 1: Create Address objectStep 2: Write the content according to the table structure and interface specification
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_address”)
@Data
public class Address {@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;
//Consignee details address
@TableField(value = “shr_address”)
@JsonProperty(“shr_address”)
private String shrAddress;
//1: default; 0: not
@TableField( value = "isdefault")
@JsonProperty("isdefault")
private Integer isdefault;
}
9.2.3 Backend implementation:
Requirements: Query the consignee list of the currently logged-in user
User information needs to be obtained through tokenStep 1: Copy the configuration class
Step 2: Write mapper, just use general 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;@Mapper
{
public interface AddressMapper extends BaseMapper
}Step 3: Write the service interface and write the findAllByUserId method to complete the function
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) ;
}
Step 4: Write service implementation
Step 5: Write the 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;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;@RestController
@RequestMapping(“/address”)
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前端实现
需求:查询所有的收货人地址
为了操作方便,需要使用filter过滤默认地址步骤一:修改 apiclient.js ,添加查询收货人列表函数
getAddress : () => {
return axios.get(“/order-service/address”)
},步骤二:修改 flow2.vue 组件,页面加载成功,查询当前登录用户所有的收货人地址
同时过滤默认地址数据mounted() {
// 查询收获人地址
this.getAddressFn()
},
data() {
return {
addressList: [], //所有的地址
defaultAddress: {}, //默认地址
}
},
methods: {
async getAddressFn() {
let { data } = await this.$request.getAddress()
// 所有收获人地址
this.addressList = data.data
//默认地址
this.defaultAddress = this.addressList.filter(item => item.isdefault == 1)[0];
}
},步骤三:修改 flow2.vue 组件,显示默认地址
{ {defaultAddress.shr_name}} { {defaultAddress.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: [], //所有的地址
defaultAddress: {}, //默认地址
showNew: false, //是否显示新地址
}
},使用变量,控制添加表单的显示与隐藏
<input type=“radio” name=“address” checked=“addr.isdefault == 1” @click=“clickRadio(addr)” />
{ {addr.shr_name}} { {addr.shr_province}} { {addr.shr_city}} { {addr.shr_area}} { {addr.shr_address}} { {addr.shr_mobile}}
设为默认地址
编辑
删除{ {showNew==false && addr.isdefault == 1}}
- <input type=“radio” name=“address” class=“new_address” @click=“showNew=true” />使用新地址
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; });
},
步骤二:展示商品基本信息
{ {goods.goods_name}}
{ {key}}:{ {value}}
¥{ { (goods.price/100).toFixed(2) }}
{ {goods.count}}
¥{ { (goods.price / 100 * goods.count).toFixed(2) }}
步骤三:展示商品概述信息
步骤四:使用计算属性显示总价格
computed: {
totalPrice : function(){ //计算总价格
//所有小计的和
let sum = 0 ;
this.cart.forEach( g => {
sum += (g.price * g.count);
});
return (sum/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: order table package object
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;//Consignee name
@TableField(value = "shr_name")
private String shrName;
//Consignee mobile phone
@TableField(value = "shr_mobile")
private String shrMobile;
//Consignee province
@TableField(value = " shr_province")
private String shrProvince;
// consignee city
@TableField(value = "shr_city")
private String shrCity;
// consignee area
@TableField(value = "shr_area")
private String shrArea;
// consignee Detailed address
@TableField(value = "shr_address")
private String shrAddress;//Order status, 0: unpaid, 1: paid, waiting to be shipped, 2: shipped, waiting to receive 3: received, waiting for comments 4: completed 5: after-sales application @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);
}
Step 3: Write 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;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.czxy.changgou4.pojo.Order;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface OrderMapper extends BaseMapper {
}步骤四:编写service接口
package com.czxy.changgou4.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.czxy.changgou4.pojo.Order;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.vo.CartVo;
import com.czxy.changgou4.vo.OrderVo;import java.util.List;
public interface OrderService extends IService {
/**
*
* @param user
* @param orderVo
* @return
*/
public Long createOrder(User user , OrderVo orderVo);}
Step 5: Write service implementation class
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; }
}
Step 5: Write the 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前端实现
步骤一:修改api.js,编写添加订单函数addOrder : ( orderVo ) => {
return axios.post(“/order-service/orders”, orderVo )
},步骤二:给“提交订单”按钮绑定点击事件
<a href=“” @click.prevent=“addOrderFn”>提交订单
步骤三:添加订单操作
async addOrderFn (){
//准备数据
{ {cc.created_at}}
{ {cc.content}}
回复(0)
有用(0)