学习目标
因为就互联网平台来说,电商网站有很多典型的特征:
B2B(Business to Business)
B2B ( Business to Business)是指进行电子商务交易的供需双方都是商家(或企业、公司),她(他)们使用了互联网的技术或各种商务网络平台,完成商务交易的过程。电子商务是现代 B2B marketing的一种具体主要的表现形式。
案例:阿里巴巴1688
C2C(Consumer to Customer)
C2C即 Customer(Consumer) to Customer(Consumer),意思就是消费者个人间的电子商务行为。比如一个消费者有一台电脑,通过网络进行交易,把它出售给另外一个消费者,此种交易类型就称为C2C电子商务。
案例:淘宝、易趣、瓜子二手车、闲鱼
B2C(Business-to-Customer)
B2C是Business-to-Customer的缩写,而其中文简称为“商对客”。“商对客”是电子商务的一种模式,也就是通常说的直接面向消费者销售产品和服务商业零售模式。这种形式的电子商务一般以网络零售业为主,主要借助于互联网开展在线销售活动。B2C即企业通过互联网为消费者提供一个新型的购物环境——网上商店,消费者通过网络在网上购物、网上支付等消费行为。
案例:唯品会、乐蜂网、京东
B2B2C(Business to Business to Consumer)
B2B2C是一种电子商务类型的网络购物商业模式,B是BUSINESS的简称,C是CUSTOMER的简称,第一个B指的是商品或服务的供应商,第二个B指的是从事电子商务的企业,C则是表示消费者。
案例:京东商城、天猫商城
C2B(Consumer to Business)
C2B(Consumer to Business,即消费者到企业),是互联网经济时代新的商业模式。这一模式改变了原有生产者(企业和机构)和消费者的关系,是一种消费者贡献价值(Create Value), 企业和机构消费价值(Consume Value)。
C2B模式和我们熟知的供需模式(DSM, Demand SupplyModel)恰恰相反,真正的C2B 应该先有消费者需求产生而后有企业生产,即先有消费者提出需求,后有生产企业按需求组织生产。通常情况为消费者根据自身需求定制产品和价格,或主动参与产品设计、生产和定价,产品、价格等彰显消费者的个性化需求,生产企业进行定制化生产。
案例:猪八戒
O2O(Online To Offline)
O2O即Online To Offline(在线离线/线上到线下),是指将线下的商务机会与互联网结合,让互联网成为线下交易的平台,这个概念最早来源于美国。O2O的概念非常广泛,既可涉及到线上,又可涉及到线下,可以通称为O2O。主流商业管理课程均对O2O这种新型的商业模式有所介绍及关注。
案例:美团、饿了么
巩固以前知识,学习技术点与技术点应用场景,掌握电商业务流程
SpringBoot:简化新Spring应用的初始搭建以及开发过程;
SpringCloud:基于Spring Boot实现的云原生应用开发工具,SpringCloud使用的技术:(Spring Cloud Gateway、Spring Cloud Alibaba Nacos、Spring Cloud Alibaba Sentinel、Spring Cloud Task和Spring Cloud Feign等)
SpringBoot+SpringCloudAlibaba(Nacos,Sentinel)+Cloud OpenFeign
MyBatis-Plus:持久层框架,也依赖mybatis
Redis:内存做缓存
Redisson:基于redis的Java驻内存数据网格 - 框架;操作redis的框架
RabbitMQ:消息中间件;大型分布式项目是标配;分布式事务最终一致性
ElasticSearch+Kibana+Logstash: 全文检索服务器+可视化数据监控:检索
ThreadPoolExecutor:线程池来实现异步操作,提高效率
Thymeleaf: 页面模板技术;JSP、服务端渲染;${},前后分离
Swagger2/Knife4J/YAPI:Api接口文档工具
MinIO(私有化对象存储集群):分布式文件存储 类似于OSS(公有)
支付宝支付:alipay.com
MySQL:关系型数据库 {shardingSphere-jdbc 进行分库,分表}
Lombok: 实体类的中get/set 生成的jar包
Ngrok/natapp:内网穿透
Docker:容器化技术; 生产环境Redis(运维人员);快速搭建环境Docker run
Git:代码管理工具;git使用,拉代码、提交、推送、合并、冲突解决
Jenkins:持续集成工具(devops):部署
前端技术栈
分布式架构、缓存解决方案、分布式事务、后台管理(数据库的可视化界面,renren-fast、ruoyi)、文件管理系统
课前说明:
1、 建议内存16个G以上
2、 培养自己独立阅读代码的能力
3、 帮助大家分析 解构业务需求
4、 新的知识点,难点敲
5、 重复的功能 自己开发
一个项目:如下选型
网络:公有云,私有云
存储:(结构化存储(关系型数据库(MySQL))、非结构化的存储(Redis,mongodb))
消息:(消息队列: RabbitMQ、Kafka)
缓存:(Redis、Memcache)
检索:(ElasticSearch)
微服务治理:(配置中心(Nacos/Spring Cloud Config+Github)、注册中心(Nacos/Eureka)、流量保护(Sentinel/Hystrix),服务调用(Dubbo/OpenFeign))
监控:(运维):Prometheus + Grafana
日志:(大数据+后端),提取有用信息进行数据分析、聚类计算、用户画像
)
首页 | 静态页面,包含了商品分类,搜索栏,商品广告位。 |
---|---|
全文搜索 | 通过搜索栏填入的关键字进行搜索,并列表展示 |
分类查询 | 根据首页的商品类目进行查询 |
商品详情 | 商品的详细信息展示 |
购物车 | 将有购买意向的商品临时存放的地方 |
单点登录 | 用户统一登录的管理 |
结算 | 将购物车中勾选的商品初始化成要填写的订单 |
下单 | 填好的订单提交 |
订单服务 | 负责确认订单是否付款成功,并对接仓储物流系统。 |
支付服务 | 下单后,用户点击支付,负责对接第三方支付系统。 |
仓储物流 | 独立的管理系统,负责商品的库存。 |
后台管理 | 主要维护类目、商品、库存单元、广告位等信息。 |
秒杀 | 秒杀抢购完整方案 |
在线体验地址:
请使用百度云盘VIP进行下载
第一步:
第二步:改NAT模式的子网IP:192.168.200.0
第三步:确定
第四步:启动虚拟机
IP:192.168.200.128
登录用户:root
登录密码:123456
查看所有安装的容器命令如下
docker ps -a {查看docker中所有的容器}
docker ps {表示查看正在运行的容器}
Docker常用命令:
镜像相关命令
docker search 软件 #搜索镜像
docker pull 镜像名称 #下载镜像
docker images # 查询本地存在镜像
docker rmi 镜像名称/镜像ID #删除镜像
容器相关命令
#创建容器指令
docker run --name 容器名称 -p 宿主机端口:容器内部端口 -e 环境变量 -v 宿主机目录文件:/容器目录文件 镜像:版本
docker ps [-a] #查询本地容器列表
docker start/stop/restart 容器ID/容器名称
docker rm -f 容器ID/容器名称 #删除容器
目前在虚拟机中安装Mysql,Redis,Nacos,Sentinel,Elasticsearch,RabbitMQ... 都是开机自启动!
名 | URL | 账号密码 |
---|---|---|
MySQL | 192.168.200.128:3306 | root/123456 |
Redis | 192.168.200.128:6379 | |
Elasticsearch | http://192.168.200.128:9200 | |
Kibana | http://192.168.200.128:5601 | |
Logstash | 收集日志的后台进程,无需访问 | |
Rabbitmq | http://192.168.200.128:15672 | admin/admin |
Zipkin | http://192.168.200.128:9411 | |
Sentinel | 192.168.200.128:8858 | sentinel/sentinel |
Nacos | http://192.168.200.128:8848/nacos | nacos/nacos |
MinIO | http://192.168.200.128:9001 | admin/admin123456 |
YAPI | http://192.168.200.128:3000 | admin@admin.com/ymfe.org |
在搭建环境前准备好数据库环境,将配套资料中的数据库脚本执行
创建工作目录 D:\gmall 不能有中文,不能有空格
前后端分离开发模式下,前端跟后端工程师,需要有开发接口依据,以下信息必须包含在开发接口中
目前企业中开发都会采用在线接口平台,例如项目中使用YAPI,不仅仅看接口信息,还能够直接进行使用
打开idea,选择File–>New–>Project,操作如下
选择下一步
配置:
groupId:com.atguigu.gmall
artifactId:gmall-parent
选择下一步
完成
工程结构如下
由于这是一个父工程,删除src目录
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu.gmall</groupId>
<artifactId>gmall-parent</artifactId>
<version>1.0</version>
<!--所有工程的父工程-->
<packaging>pom</packaging>
<!--引入spring boot 父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
</parent>
<!--定义jar包的版本号-->
<properties>
<elasticsearch.version>7.8.0</elasticsearch.version>
<java.version>1.8</java.version>
<cloud.version>Hoxton.SR7</cloud.version>
<alibaba.version>2.2.5.RELEASE</alibaba.version>
<gmall.version>1.0</gmall.version>
<mybatis-plus.version>3.4.1</mybatis-plus.version>
<mysql.version>5.1.46</mysql.version>
<lombok.version>1.18.10</lombok.version>
<swagger.version>2.9.2</swagger.version>
<knife4j.version>2.0.2</knife4j.version>
<fastjson.version>1.2.58</fastjson.version>
<redisson.version>3.15.3</redisson.version>
<pool2.version>2.6.0</pool2.version>
<httpclient.version>4.5.13</httpclient.version>
</properties>
<!--配置dependencyManagement锁定依赖的版本 并不是实际的依赖。-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--mybatis-plus 持久层-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- redisson 分布式锁-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>${redisson.version}</version>
</dependency>
<!-- spring2.X集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>${pool2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
gmall-common:公共模块父节点
common-util:工具类模块,所有模块都可以依赖于它
service-util:service服务的工具包,包含service服务的公共配置类,所有service模块依赖于它
点击gmall-parent,选择New–>Module,操作如下
选择下一步
选择下一步
完成,删除src目录,结构如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>gmall-parent</artifactId>
<groupId>com.atguigu.gmall</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gmall-common</artifactId>
<!--公共模块父工程打包方式设置为POM-->
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<!--lombok用来简化实体类:需要安装lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--用来转换json使用 {JavaObject - json | json - JavaObject}-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.29</version>
</dependency>
<!-- 服务调用feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-annotation</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
点击gmall-common
,选择New–>Module,操作如下
选择下一步
选择下一步
完成,结构如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>gmall-common</artifactId>
<groupId>com.atguigu.gmall</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-util</artifactId>
<packaging>jar</packaging>
<name>common-util</name>
<description>common-util</description>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>
</project>
GmallException | 自定义全局异常 |
---|---|
Result | API统一返回结果封装类 |
ResultCodeEnum | API统一返回结果状态信息 |
AuthContextHolder | 获取登录用户信息类 |
HttpClientUtil | http客户端类 |
MD5 | 通过MD5给字符串加密的工具类 |
IpUtil | 获取Ip地址的工具类 |
DateUitl | 日期比较工具类 |
搭建过程同common-util
,如图
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>gmall-common</artifactId>
<groupId>com.atguigu.gmall</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service-util</artifactId>
<dependencies>
<dependency>
<groupId>com.atguigu.gmall</groupId>
<artifactId>common-util</artifactId>
<version>1.0</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring2.X集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>
<!-- redisson 分布式锁-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.15.3</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
MybatisPlusConfig | MybatisPlus配置类 |
---|---|
MybatisPlusConfig | MybatisPlus配置类 |
Swagger2Config | Swagger2配置类 |
点击gmall-parent
父工程 选择module模块名称:gmall-model,搭建过程同common父模块
注意:搭建完成后,导入资料项目相关实体类,使用今日下发实体类
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>gmall-parent</artifactId>
<groupId>com.atguigu.gmall</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gmall-model</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<scope>provided</scope>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<scope>provided </scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<scope>provided </scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
</project>
gmall-service:service模块父节点
选中gmall-parent
父工程,新建子模块:gmall-service。搭建过程同common父模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>gmall-parent</artifactId>
<groupId>com.atguigu.gmall</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gmall-service</artifactId>
<packaging>pom</packaging>
<dependencies>
<!--依赖服务的工具类-->
<dependency>
<groupId>com.atguigu.gmall</groupId>
<artifactId>service-util</artifactId>
<version>1.0</version>
</dependency>
<!--数据载体-->
<dependency>
<groupId>com.atguigu.gmall</groupId>
<artifactId>gmall-model</artifactId>
<version>1.0</version>
</dependency>
<!--web 需要启动项目-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
<!-- 服务注册 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 服务配置-->
<!-- <dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>-->
<!-- 服务调用feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 流量控制 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--开发者工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!--链路追踪-->
<!-- <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>-->
</dependencies>
</project>
gmall-service
父工程下新增模块:service-product创建启动类 包名:com.atguigu.gmall 类名:ProductApp
package com.atguigu.gmall;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author: atguigu
* @create: 2022-11-27 23:04
*/
@SpringBootApplication
@EnableDiscoveryClient
public class ProductApp {
public static void main(String[] args) {
SpringApplication.run(ProductApp.class, args);
}
}
在resources下新建配置文件application.yml 说明:后期统一上传nacos配置中心
server:
port: 8206
spring:
application:
name: service-product
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/gmall_product?characterEncoding=utf-8&useSSL=false
username: root
password: root
hikari:
connection-test-query: SELECT 1 # 自动检测连接
connection-timeout: 60000 #数据库连接超时时间,默认30秒
idle-timeout: 500000 #空闲连接存活最大时间,默认600000(10分钟)
max-lifetime: 540000 #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
maximum-pool-size: 12 #连接池最大连接数,默认是10
minimum-idle: 10 #最小空闲连接数量
pool-name: SPHHikariPool # 连接池名称
cloud:
nacos:
discovery:
server-addr: 192.168.200.128:8848
mybatis-plus:
configuration:
# sql输出到控制台,方便开发调试
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
一般情况可以分为两级或者三级。咱们的项目一共分为三级,即一级分类、二级分类、三级分类。
比如:家用电器是一级分类,电视是二级分类,那么超薄电视就是三级分类。
数据库结构
SELECT base_category1.id,base_category1.name,base_category2.id,base_category2.name,base_category3.id,base_category3.name
FROM base_category1
LEFT JOIN base_category2
ON base_category1.id = base_category2.category1_id
LEFT JOIN base_category3
ON base_category2.id = base_category3.category2_id
平台属性和平台属性值
平台属性和平台属性值主要用于商品的检索,每个分类对应的属性都不同,分类包含一级分类、二级分类和三级分类,分类层级区分对应分类。
销售属性,就是商品详情页右边,可以通过销售属性来定位一组spu下的哪款sku。可以让当前的商品详情页,跳转到自己的“兄弟”商品。
一般每种商品的销售属性不会太多,大约1-4种。整个平台的属性种类也不会太多,大概10种以内。比如:颜色、尺寸、版本、套装等等。
SKU=Stock Keeping Unit(库存量单位)。即库存进出计量的基本单元,可以是以件,盒,托盘等为单位。SKU这是对于大型连锁超市DC(配送中心)物流管理的一个必要的方法。现在已经被引申为产品统一编号的简称,每种产品均对应有唯一的SKU号。
SPU(Standard Product Unit):标准化产品单元。是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。
首先通过检索搜索出来的商品列表中,每个商品都是一个sku。每个sku都有自己独立的库存数。也就是说每一个商品详情展示都是一个sku。
那spu又是干什么的呢?
如上图,一般的电商系统你点击进去以后,都能看到这个商品关联了其他好几个类似的商品,而且这些商品很多的信息都是共用的,比如商品图片,海报、销售属性等。
那么系统是靠什么把这些sku识别为一组的呢,那是这些sku都有一个公用的spu信息。而它们公共的信息,都放在spu信息下。所以,sku与spu的结构如下:
图中有两个图片信息表,其中spu_image表示整个spu相关下的所有图片信息,而sku_image表示这个spu下的某个sku使用的图片。sku_image中的图片是从spu_image中选取的。但是由于一个spu下的所有sku的海报都是一样,所以只存一份spu_poster就可以了。
重点:清楚分类、品牌的表关系,独立完成分类查询的后端代码,并返回正确结果
商城的核心自然是商品,而商品多了以后,肯定要进行分类,并且不同的商品会有不同的品牌信息
分类 与 商品 是【一对多】关系
分类 与 品牌 是【多对多】关系,需要一张中间表来维护
品牌 与 商品 是【一对多】关系
简单理解:
YAPI接口文档地址:
编写一个controller一般需要知道四个内容,这些内容来源于开发接口文档:
Controller代码:
package com.atguigu.gmall.product.controller;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.product.model.BaseAttrInfo;
import com.atguigu.gmall.product.model.BaseCategory1;
import com.atguigu.gmall.product.model.BaseCategory2;
import com.atguigu.gmall.product.model.BaseCategory3;
import com.atguigu.gmall.product.service.BaseCategoryService;
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 java.util.List;
/**
* @author: atguigu
* @create: 2022-12-12 00:50
*/
@RequestMapping("admin/product")
@RestController
public class BaseCategoryController {
@Autowired
private BaseCategoryService baseCategoryService;
/**
* 查询所有的一级分类信息
*
* @return
*/
@GetMapping("getCategory1")
public Result<List<BaseCategory1>> getCategory1() {
List<BaseCategory1> list = baseCategoryService.getCategory1();
return Result.ok(list);
}
/**
* 根据一级分类Id 查询二级分类数据
* @param category1Id
* @return
*/
@GetMapping("getCategory2/{category1Id}")
public Result<List<BaseCategory2>> getCategory2(@PathVariable("category1Id") Long category1Id) {
List<BaseCategory2> baseCategory2List = baseCategoryService.getCategory2(category1Id);
return Result.ok(baseCategory2List);
}
/**
* 根据二级分类Id 查询三级分类数据
* @param category2Id
* @return
*/
@GetMapping("getCategory3/{category2Id}")
public Result<List<BaseCategory3>> getCategory3(@PathVariable("category2Id") Long category2Id) {
List<BaseCategory3> baseCategory3List = baseCategoryService.getCategory3(category2Id);
return Result.ok(baseCategory3List);
}
/**
* 根据分类Id 获取平台属性数据
* @param category1Id
* @param category2Id
* @param category3Id
* @return
*/
@GetMapping("attrInfoList/{category1Id}/{category2Id}/{category3Id}")
public Result<List<BaseAttrInfo>> attrInfoList(@PathVariable("category1Id") Long category1Id,
@PathVariable("category2Id") Long category2Id,
@PathVariable("category3Id") Long category3Id) {
List<BaseAttrInfo> baseAttrInfoList = baseCategoryService.getAttrInfoList(category1Id, category2Id, category3Id);
return Result.ok(baseAttrInfoList);
}
}
包名:com.atguigu.gmall.product.service 接口名:BaseCategoryService
package com.atguigu.gmall.product.service;
import com.atguigu.gmall.product.model.BaseAttrInfo;
import com.atguigu.gmall.product.model.BaseCategory1;
import com.atguigu.gmall.product.model.BaseCategory2;
import com.atguigu.gmall.product.model.BaseCategory3;
import java.util.List;
public interface BaseCategoryService {
/**
* 查询所有的一级分类列表
*
* @return
*/
List<BaseCategory1> getCategory1();
/**
* 根据一级分类Id 查询二级分类数据
* @param category1Id
* @return
*/
List<BaseCategory2> getCategory2(Long category1Id);
/**
* 根据二级分类Id 查询三级分类数据
* @param category2Id
* @return
*/
List<BaseCategory3> getCategory3(Long category2Id);
/**
* 根据分类Id 获取平台属性数据
* @param category1Id
* @param category2Id
* @param category3Id
* @return
*/
List<BaseAttrInfo> getAttrInfoList(Long category1Id, Long category2Id, Long category3Id);
}
包名:com.atguigu.gmall.product.service.impl 类名:BaseCategoryServiceImpl
package com.atguigu.gmall.product.service.impl;
import com.atguigu.gmall.product.model.BaseAttrInfo;
import com.atguigu.gmall.product.model.BaseCategory1;
import com.atguigu.gmall.product.model.BaseCategory2;
import com.atguigu.gmall.product.model.BaseCategory3;
import com.atguigu.gmall.product.mapper.BaseAttrInfoMapper;
import com.atguigu.gmall.product.mapper.BaseCategory1Mapper;
import com.atguigu.gmall.product.mapper.BaseCategory2Mapper;
import com.atguigu.gmall.product.mapper.BaseCategory3Mapper;
import com.atguigu.gmall.product.service.BaseCategoryService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author: atguigu
* @create: 2022-12-12 00:52
*/
@Service
public class BaseCategoryServiceImpl implements BaseCategoryService {
@Autowired
private BaseCategory1Mapper baseCategory1Mapper;
@Autowired
private BaseCategory2Mapper baseCategory2Mapper;
@Autowired
private BaseCategory3Mapper baseCategory3Mapper;
@Autowired
private BaseAttrInfoMapper baseAttrInfoMapper;
/**
* 查询所有的一级分类列表
*
* @return
*/
@Override
public List<BaseCategory1> getCategory1() {
LambdaQueryWrapper<BaseCategory1> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(BaseCategory1::getIsDeleted, false);
return baseCategory1Mapper.selectList(queryWrapper);
}
/**
* 根据一级分类Id 查询二级分类数据
*
* @param category1Id
* @return
*/
@Override
public List<BaseCategory2> getCategory2(Long category1Id) {
// select * from baseCategory2 where Category1Id = ?
LambdaQueryWrapper<BaseCategory2> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(BaseCategory2::getCategory1Id, category1Id);
queryWrapper.eq(BaseCategory2::getIsDeleted, false);
return baseCategory2Mapper.selectList(queryWrapper);
}
/**
* 根据二级分类Id 查询三级分类数据
*
* @param category2Id
* @return
*/
@Override
public List<BaseCategory3> getCategory3(Long category2Id) {
// select * from baseCategory3 where Category2Id = ?
LambdaQueryWrapper<BaseCategory3> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(BaseCategory3::getCategory2Id, category2Id);
queryWrapper.eq(BaseCategory3::getIsDeleted, false);
return baseCategory3Mapper.selectList(queryWrapper);
}
/**
* 根据分类Id 获取平台属性数据
* 接口说明:
* 1,平台属性可以挂在一级分类、二级分类和三级分类
* 2,查询一级分类下面的平台属性,传:category1Id,0,0; 取出该分类的平台属性
* 3,查询二级分类下面的平台属性,传:category1Id,category2Id,0;
* 取出对应一级分类下面的平台属性与二级分类对应的平台属性
* 4,查询三级分类下面的平台属性,传:category1Id,category2Id,category3Id;
* 取出对应一级分类、二级分类与三级分类对应的平台属性
*
* @param category1Id
* @param category2Id
* @param category3Id
* @return
*/
@Override
public List<BaseAttrInfo> getAttrInfoList(Long category1Id, Long category2Id, Long category3Id) {
// 调用mapper:
return baseAttrInfoMapper.selectBaseAttrInfoList(category1Id, category2Id, category3Id);
}
}
包名:com.atguigu.gmall.product.mapper 接口名:BaseCategory1Mapper BaseCategory2Mapper BaseCategory3Mapper BaseAttrInfoMapper BaseAttrValueMapper
package com.atguigu.gmall.product.mapper;
import com.atguigu.gmall.product.model.BaseCategory1;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface BaseCategory1Mapper extends BaseMapper<BaseCategory1> {
}
package com.atguigu.gmall.product.mapper;
import com.atguigu.gmall.product.model.BaseCategory2;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface BaseCategory2Mapper extends BaseMapper<BaseCategory2> {
}
package com.atguigu.gmall.product.mapper;
import com.atguigu.gmall.product.model.BaseCategory3;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface BaseCategory3Mapper extends BaseMapper<BaseCategory3> {
}
package com.atguigu.gmall.product.mapper;
import com.atguigu.gmall.product.model.BaseAttrInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
public interface BaseAttrInfoMapper extends BaseMapper<BaseAttrInfo> {
/**
* 根据分类Id 查询平台属性集合对象 | 编写xml 文件
* @param category1Id
* @param category2Id
* @param category3Id
* @return
*/
List<BaseAttrInfo> selectBaseAttrInfoList(Long category1Id, Long category2Id, Long category3Id);
}
package com.atguigu.gmall.product.mapper;
import com.atguigu.gmall.product.model.BaseAttrValue;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface BaseAttrValueMapper extends BaseMapper<BaseAttrValue> {
}
在resources目录添加mapper文件夹,添加 BaseAttrInfoMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.atguigu.gmall.product.mapper.BaseAttrInfoMapper">
<!--自定义类型Map 完成一对多配置
resultMap:标识返回映射结果集
id:唯一标识
type:结果集返回数据类型
autoMapping:自动映射
-->
<resultMap id="baseAttrInfoMap" type="com.atguigu.gmall.product.model.BaseAttrInfo" autoMapping="true">
<!--
id表示注解,property:实体类中属性名称 column:字段名称
-->
<id property="id" column="id"></id>
<!-- 配置一对多-->
<collection property="attrValueList" ofType="com.atguigu.gmall.product.model.BaseAttrValue" autoMapping="true">
<!--对自定义SQL中列的别名进行映射-->
<id property="id" column="attr_value_id"></id>
</collection>
</resultMap>
<!-- 确定查询表:属性名称表 属性值表
关联查询方式:内连接 满足关联条件数据
关联字段:平台属性值表中att_id 跟 平台属性名表中id 关联
-->
<select id="attrInfoList" resultMap="baseAttrInfoMap">
SELECT
bai.id,
bai.attr_name,
bai.category_id,
bai.category_level,
bav.id attr_value_id,
bav.value_name,
bav.attr_id
FROM base_attr_info bai
INNER JOIN base_attr_value bav
ON bav.attr_id = bai.id
<where>
/*trim标签 按照sql正确语法要求,SQL中增加指定符号 prefix开始符号 suffix结束符号 */
<trim prefix="(" suffix=")">
<if test="category1Id!=null and category1Id!=0">
bai.category_id = #{category1Id} and bai.category_level = 1
</if>
<if test="category2Id!=null and category2Id!=0">
or bai.category_id = #{category2Id} and bai.category_level = 2
</if>
<if test="category3Id!=null and category3Id!=0">
or bai.category_id = #{category3Id} and bai.category_level = 3
</if>
</trim>
</where>
order by bai.category_level,bai.id
</select>
</mapper>
(1)简介
Swagger 是一个规范完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务()。 它的主要作用是:
使得前后端分离开发更加方便,有利于团队协作
接口的文档在线自动生成,降低后端开发人员编写接口文档的负担
功能测试
Spring已经将Swagger纳入自身的标准,建立了Spring-swagger项目,现在叫Springfox。通过在项目中引入Springfox ,即可非常简单快捷的使用Swagger。
(2)SpringBoot集成Swagger
引入依赖
在service-util
工程的config包中添加一个配置类
package com.atguigu.gmall.common.config;
import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* Swagger2配置信息
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket webApiConfig(){
//添加head参数start
List<Parameter> pars = new ArrayList<>();
ParameterBuilder tokenPar = new ParameterBuilder();
tokenPar.name("userId")
.description("用户ID")
.defaultValue("1")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build();
pars.add(tokenPar.build());
ParameterBuilder tmpPar = new ParameterBuilder();
tmpPar.name("userTempId")
.description("临时用户ID")
.defaultValue("1")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build();
pars.add(tmpPar.build());
//添加head参数end
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
//过滤掉admin路径下的所有页面
.paths(Predicates.and(PathSelectors.regex("/api/.*")))
//过滤掉所有error或error.*页面
//.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build()
.globalOperationParameters(pars);
}
@Bean
public Docket adminApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("adminApi")
.apiInfo(adminApiInfo())
.select()
//只显示admin路径下的页面
.paths(Predicates.and(PathSelectors.regex("/admin/.*")))
.build();
}
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("网站-API文档")
.description("本文档描述了网站微服务接口定义")
.version("1.0")
.contact(new Contact("Helen", "http://atguigu.com", "55317332@qq.com"))
.build();
}
private ApiInfo adminApiInfo(){
return new ApiInfoBuilder()
.title("后台管理系统-API文档")
.description("本文档描述了后台管理系统微服务接口定义")
.version("1.0")
.contact(new Contact("Helen", "http://atguigu.com", "55317332@qq.com"))
.build();
}
}
在service-product
模块中启动类会自动扫描到该配置类
(3)Swagger常用注解
在Java类中添加Swagger的注解即可生成Swagger接口文档,常用Swagger注解如下:
@Api:修饰整个类,描述Controller的作用
tags 指定名称
@ApiOperation:描述一个类的一个方法,或者说一个接口
value 指定名称 notes 添加备注
@ApiParam:单个参数的描述信息
@ApiModel:用对象来接收参数
@ApiModelProperty:用对象接收参数时,描述对象的一个字段
value 指定字段名称
@ApiResponse:HTTP响应其中1个描述
@ApiResponses:HTTP响应整体描述
@ApiIgnore:使用该注解忽略这个API
@ApiError :发生错误返回的信息
@ApiImplicitParam:一个请求参数
@ApiImplicitParams:多个请求参数的描述信息
(1)简介
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名knife4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍!
gitee地址:https://gitee.com/xiaoym/knife4j
官方文档:https://doc.xiaominfo.com/
效果演示:http://knife4j.xiaominfo.com/doc.html
(2)核心功能
该UI增强包主要包括两大核心功能:文档说明 和 在线调试
(3)快速集成
在gmall-common
模块中的pom.xml
文件中引入knife4j
的依赖(已导入),如下:
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
创建Knife4J配置文件
在service-util
模块中新建包package com.atguigu.gmall.common.config,在此包中新建配置类,代码如下:
package com.atguigu.gmall.common.config;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* Swagger2配置信息
*/
@Configuration
@EnableSwagger2
@EnableKnife4j
@Import(BeanValidatorPluginsConfiguration.class)
public class Knife4jConfig {
@Bean
public Docket webApiConfig(){
//添加head参数start
List<Parameter> pars = new ArrayList<>();
ParameterBuilder tokenPar = new ParameterBuilder();
tokenPar.name("userId")
.description("用户ID")
.defaultValue("1")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build();
pars.add(tokenPar.build());
ParameterBuilder tmpPar = new ParameterBuilder();
tmpPar.name("userTempId")
.description("临时用户ID")
.defaultValue("1")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build();
pars.add(tmpPar.build());
//添加head参数end
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
//过滤掉admin路径下的所有页面
.paths(Predicates.and(PathSelectors.regex("/api/.*")))
//过滤掉所有error或error.*页面
//.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build()
.globalOperationParameters(pars);
}
@Bean
public Docket adminApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("adminApi")
.apiInfo(adminApiInfo())
.select()
//只显示admin路径下的页面
.paths(Predicates.and(PathSelectors.regex("/admin/.*")))
.build();
}
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("网站-API文档")
.description("本文档描述了网站微服务接口定义")
.version("1.0")
.contact(new Contact("Helen", "http://atguigu.com", "55317332@qq.com"))
.build();
}
private ApiInfo adminApiInfo(){
return new ApiInfoBuilder()
.title("后台管理系统-API文档")
.description("本文档描述了后台管理系统微服务接口定义")
.version("1.0")
.contact(new Contact("Helen", "http://atguigu.com", "55317332@qq.com"))
.build();
}
}
以上有两个注解需要特别说明,如下表:
注解 | 说明 |
---|---|
@EnableSwagger2 |
该注解是Springfox-swagger框架提供的使用Swagger注解,该注解必须加 |
@EnableKnife4j |
该注解是knife4j 提供的增强注解,Ui提供了例如动态参数、参数过滤、接口排序等增强功能,如果你想使用这些增强功能就必须加该注解,否则可以不用加 |
http://host:port/doc.html
我们现在访问页面使用的是:
http://localhost:port
有什么问题呢?
实际开发中,会有不同的环境:
如果不同环境使用不同的ip去访问,可能会出现一些问题。为了保证所有环境的一致,我们会在各种环境下都使用域名来访问。
我们将使用以下域名:
主域名是:www.gmall.com - 门户系统
检索系统域名:list.gmall.com
商品系统域名:item.gmall.com
购物车系统域名:cart.gmall.com
登录系统域名: passport.gmal.com
订单系统域名:order.gmall.com
支付系统域名:payment.gmall.com
活动系统域名:activity.gmall.com
网关域名:api.gmall.com
最终,我们希望这些域名指向的还是我们本机的某个端口。
那么,当我们在浏览器输入一个域名时,浏览器是如何找到对应服务的ip和端口的呢?
一个域名一定会被解析为一个或多个ip。这一般会包含两步:
浏览器会首先在本机的hosts文件中查找域名映射的IP地址,如果查找到就返回IP ,没找到则进行域名服务器解析,一般本地解析都会失败,因为默认这个文件是空的。
Windows下的hosts文件地址:
C:/Windows/System32/drivers/etc/hosts
Linux下的hosts文件所在路径:
/etc/hosts
示例如下:
# gmall
127.0.0.1 www.gmall.com item.gmall.com order.gmall.com payment.gmall.com
127.0.0.1 activity.gmall.com passport.gmall.com cart.gmall.com list.gmall.com api.gmall.com
本地解析失败,才会进行域名服务器解析,域名服务器就是网络中的一台计算机,里面记录了所有注册备案的域名和ip映射关系,一般只要域名是正确的,并且备案通过,一定能找到。
注意:手动修改hosts文件不会立即生效,需要刷新本地DNS才会生效
在命令行中执行命令刷新:
ipconfig/flushdns