【电商项目】1分布式基础篇

1 项目简介

1.2 项目架构图

1.2.1 项目微服务架构图

1.2.2 微服务划分图

2 分布式基础概念

3 Linux系统环境搭建

查看网络IP和网关

linux网络环境配置

补充P123(修改linux网络设置&开启root密码访问)

设置主机名和hosts映射

主机名解析过程分析(Hosts、DNS)

3.1 安装linux虚拟机

3.2 安装docker

官网安装地址:Install Docker Engine on CentOS | Docker Docs

(1)卸载系统中的docker

bash 复制代码
sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

(2)安装docker依赖包

bash 复制代码
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

(3)设置docker仓库地址(告诉linux去哪里下载docker)

bash 复制代码
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

可能会报错:

Could not fetch/save url https://download.docker.com/linux/centos/docker-ce.repo to file /etc/yum.repos.d/docker-ce.repo: [Errno 14] curl#7 - "Failed to connect to 2a03:2880:f12a:83:face:b00c:0:25de: Network is unreachable"

解决办法:服务器下载docker的镜像仓库失败,切换成阿里云的镜像下载

bash 复制代码
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

(4)安装docker

bash 复制代码
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

(5)启动docker

bash 复制代码
sudo systemctl start docker

(6)设置docker开机自动启动

bash 复制代码
systemctl enable docker

(7)配置docker阿里云镜像加速

阿里云官网:阿里云权益中心_助力学生、开发者、企业用云快速上云-阿里云

bash 复制代码
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://mysouset.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

如果后续docker pull拉取镜像失败,添加以下库:

vi /etc/docker/daemon.json

bash 复制代码
{
	"registry-mirrors": [
		"https://docker.m.daocloud.io",
		"https://docker.jianmuhub.com",
		"https://huecker.io",
		"https://dockerhub.timeweb.cloud",
		"https://dockerhub1.beget.com",
		"https://noohub.ru"
	]
}

配置完镜像源后需要重启

bash 复制代码
# 重新加载配置文件
sudo systemctl daemon-reload
# 重新启动docker服务
sudo systemctl restart docker

3.3 docker安装mysql

(1)下载镜像文件

bash 复制代码
docker pull mysql:5.7

(2)创建实例并启动

bash 复制代码
docker run -p 3306:3306 --privileged=true --name mysql \
-v /mydata/mysql/log:/var/log/mysql \
-v /mydata/mysql/data:/var/lib/mysql \
-v /mydata/mysql/conf:/etc/mysql/config.d \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7

docker容器文件挂载与端口映射

访问容器内部,进入Linux的/bin/bash控制台:

bash 复制代码
docker exec -it mysql /bin/bash

进入docker容器显示bash-4.2# 而不是root@容器id 的方式,这是由于docker容器/etc/skel目录下缺失2个配置文件,从默认配置中拷贝过来就可以解决了:

bash 复制代码
cp /etc/skel/.bashrc /root/ 
cp /etc/skel/.bash_profile /root/

mysql配置

vi /mydata/mysql/conf/my.cnf

bash 复制代码
[client]
default-character-set=utf8

[mysql]
default-character-set=utf8

[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve

修改配置后重启容器

docker restart mysql

3.4 docker安装redis

(1)下载镜像文件

bash 复制代码
docker pull redis

(2)创建实例并启动

mkdir -p /mydata/redis/conf

touch /mydata/redis/conf/redis.conf

bash 复制代码
docker run -p 6379:6379 --name redis \
-v /mydata/redis/data:/data \
-v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \
-d redis redis-server /etc/redis/redis.conf

(3)检查redis是否启动成功

docker exec -it redis redis-cli

(4)redis开启持久化

vi /mydata/redis/conf/redis.conf

让redis启用AOF的持久化方式

appendonly yes

3.5 开发环境统一

3.5.1 Maven

3.5.2 IDEA&VS Code

VS Code插件:

Auto Close Ta;Auto Rename Tag;ESLint;HTML CSS Support;HTML Snippets;JavaScript(ES6);Live Server(实时服务器);open in browser(在浏览器打开页面);Vetur(开发Vue项目常用的工具);Vue 2 Snippet,Vue 3 Snippets(Vue语法提示)

3.5.3 安装配置git

1、下载git:https://git-scm.com

2、配置git,进入git bash

bash 复制代码
# 配置用户名
git config --global user.name "username"
# 配置游戏
git config --global user.email "username@email.com"

保存于 ~/.gitconfig文件中

3、配置ssh免密登录

3.5.4 逆向工程使用

1、导入项目逆向工程

2、下载人人开源后台管理系统脚手架工程

(1)导入工程,创建数据库

(2)修改工程shiro依赖为SpringSecurity

(3)删除部分暂时不需要的业务

3、下载人人开源后台管理系统vue端脚手架工程

(1)vscode导入前端项目

(2)前后端联调测试基本功能

3.6 创建项目微服务

商品服务、仓储服务、订单服务、优惠券服务、用户服务

共同:

1)web、openfeign

2)每一个服务,包名 com.atguigu.gulimall.xxx(product/order/ware/coupon/member)

3)模块名:gulimall-xxx

新建gitee仓库

创建微服务模块

商品服务

将gulimall设为总项目,聚合其他项目

pom.xml

XML 复制代码
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.atguigu.gulimall</groupId>
	<artifactId>gulimall</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>gulimall</name>
	<description>聚合服务</description>
	<packaging>pom</packaging>

	<modules>
		<module>gulimall-coupon</module>
		<module>gulimall-member</module>
		<module>gulimall-order</module>
		<module>gulimall-product</module>
		<module>gulimall-ware</module>
	</modules>

</project>

修改总项目的.gitignore模板

bash 复制代码
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar

#任意路径下忽略mvnw文件
**/mvnw
#任意路径下忽略mvnw.cmd文件
**/mvnw.cmd
#任意路径下忽略.mvn目录
**/.mvn
#任意路径下忽略target目录
**/target/

#忽略同级的.idea
.idea

#子模块的.gitignore
**/.gitignore

commit提交时,去掉:分析所有代码;检查哪些没有做两个勾选

数据库初始化

powerdesigner安装(不要汉化,亲测汉化后缺少功能)

PowerDesigner安装详细教程_powerdesigner安装步骤-CSDN博客

设docker启动后容器自动启动

docker update mysql --restart=always

docker update redis --restart=always

4 前端开发基础知识

4.1 Node.js

npm install下载所需的依赖,package.json描述每一个依赖所需要的版本

在使用原来的淘宝镜像地址https://registry.npm.taobao.org/时,浏览器会自动跳转https://registry.npmmirror.com/,可能会导致我们使用npm下载的时候请求下载的很慢

设置最新淘宝镜像

bash 复制代码
npm config set registry https://registry.npmmirror.com/

查看是否已经配置成功

bash 复制代码
npm config get registry

安装pnpm

bash 复制代码
npm install --global pnpm

配置PNPM镜像源(默认已经为新版淘宝镜像)

bash 复制代码
pnpm config set registry https://registry.npmmirror.com

解决node-sass问题,支持高版本node以及pnpm(gitee评论区大神)

bash 复制代码
pnpm install --ignore-scripts
pnpm remove node-sass sass-loader
pnpm install --save sass-loader@7 sass babel-runtime qs vue-hot-reload-api svg-baker-runtime

5 分布式组件

微服务-注册中心、配置中心、网关

Spring Cloud Alibaba:

https://github.com/alibaba/spring-cloud-alibaba

在common项目中引入如下。进行统一管理

XML 复制代码
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.1.0.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

5.1 Nacos

5.1.1 Nacos作为注册中心

1)下载nacos-server

https://github.com/alibaba/nacos/releases

2)启动nacos-server

双击bin中的startup.cmd文件;访问https://localhost:8848/nacos/;使用默认的nacos/nacos进行登录

3)将微服务注册到nacos中

1 首先,修改pom.xml文件,引入Nacos Discovery Starter

XML 复制代码
<!--服务注册/发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

2 在应用的 /src/main/resources/application.properties 配置文件中配置 Nacos Server地址

XML 复制代码
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

3 使用 @EnableDiscoveryClient 注解开启服务注册与发现功能

java 复制代码
package com.atguigu.gulimall.coupon;

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

@EnableDiscoveryClient
@SpringBootApplication
public class GulimallCouponApplication {

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

}

4 启动应用,观察nacos服务列表是否已经注册上服务

注意:每一个应用都应该有名字,这样才能注册上去。

在应用的 /src/main/resources/application.properties 配置文件中给微服务起注册中心的名字

XML 复制代码
spring.application.name=gulimall-coupon

5.1.2 Nacos作为配置中心

1、如何使用Nacos作为配置中心统一管理配置

1)首先,修改pom.xml文件,引入Nacos Config Starter

XML 复制代码
<!--配置中心来做配置管理-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

2)在应用的 /src/main/resources/bootstrap.properties 配置文件中配置 Nacos Config元数据

XML 复制代码
#设置服务名
spring.application.name=gulimall-coupon

#设置配置中心地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

3)需要给配置中心添加一个叫数据集(Data Id)gulimall-coupon.properties

默认规则:应用名.properties

4)给应用名.properties添加任何配置

5)动态获取配置

@RefreshScope:动态获取并刷新配置

@Value("${配置项的名}"):获取到配置

如果配置中心和当前的配置文件中都配置了相同的项,优先使用配置中心的配置

2、细节

1)命名空间:配置隔离;

默认:public(保留空间);默认新增的所有配置都在public空间。

1、开发,测试,生产:利用命名空间来做环境隔离。

注意:在bootstrap.properties;配置上,需要使用哪个命名空间下的配置,

spring.cloud.nacos.config.namespace=9de62e44-cd2a-4a82-bf5c-95878bd5e871

2、每一个微服务之间互相隔离配置,每一个微服务都创建自己的命名空间,只加载自己命名空间下的所有配置
2)、配置集:所有的配置的集合

3)、配置集ID:类似文件名。

Data ID:类似文件名

4)、配置分组:

默认所有的配置集都属于:DEFAULT_GROUP;

1111,618,1212

项目中的使用:每个微服务创建自己的命名空间,使用配置分组区分环境,dev,test,prod

3、同时加载多个配置集

1)微服务任何配置信息,任何配置文件都可以放在配置中心中

2)只需要在bootstrap.properties说明加载配置中心中哪些配置文件即可

3)@Value,@ConfigurationProperties。。。

以前SpringBoot任何方法从配置文件中获取值,都能使用。

配置中心有的优先使用配置中心中的

5.1.3 Gateway网关

客户端直接请求服务

客户端请求API网关,由网关代转给各个服务

工作流程

5.2 SpringCloud Feign 声明式远程调用

6 前端基础

前端技术栈类比

6.1 VSCode

6.2 ES6

6.2.1 简介

6.2.2 什么是ECMAScript

6.2.3 ES6新特性

6.2.3.1 let声明变量
6.2.3.2 const声明变量(只读变量/常量)
6.2.3.3 解构表达式

1)数组解构

2)对象解构

6.2.3.4 字符串扩展

1)几个新的API

2)字符串模板

6.2.3.5 函数优化

1)函数参数默认值

2)不定参数

3)箭头函数

ES6中定义函数的简写方式

一个参数时:

多个参数:

4)实战:箭头函数结合解构表达式

6.2.3.6 对象优化

1)新增的API

2)声明对象简写

3)对象的函数属性简写

4)对象拓展运算符

6.2.3.7 map和reduce
6.2.3.8 Promise

1)Promise语法

2)处理异步结果

3)Promise改造以前嵌套方式

4)优化处理

6.2.3.9 模块化

1)什么是模块化

2)export

3)import

6.3 Node.js

npm install下载所需的依赖,package.json描述每一个依赖所需要的版本

在使用原来的淘宝镜像地址https://registry.npm.taobao.org/时,浏览器会自动跳转https://registry.npmmirror.com/,可能会导致我们使用npm下载的时候请求下载的很慢

设置最新淘宝镜像

bash 复制代码
npm config set registry https://registry.npmmirror.com/

查看是否已经配置成功

bash 复制代码
npm config get registry

安装pnpm

bash 复制代码
npm install --global pnpm

配置PNPM镜像源(默认已经为新版淘宝镜像)

bash 复制代码
pnpm config set registry https://registry.npmmirror.com

解决node-sass问题,支持高版本node以及pnpm(gitee评论区大神)

bash 复制代码
pnpm install --ignore-scripts
pnpm remove node-sass sass-loader
pnpm install --save sass-loader@7 sass babel-runtime qs vue-hot-reload-api svg-baker-runtime

6.4 Vue

浏览器安装Vue-Devtools(P37)

6.4.1 MVVM思想

6.4.2 Vue简介

6.4.3 入门案例

6.4.4 概念

6.4.5 指令

6.4.5.1 插值表达式

1)花括号

2)插值闪烁

3)v-text和v-html

6.4.5.2 v-bind(单向绑定)简写为 :
6.4.5.3 v-model(双向绑定)
6.4.5.4 v-on(绑定事件)简写为 @

1、基本用法

2、事件修饰符

3、按键修饰符

4、组合按钮

6.4.5.5 v-for
6.4.5.6 v-if和v-show
6.4.5.7 v-else和v-else-if

6.4.6 计算属性和侦听器

老写法:

ES6语法:

计算属性和侦听器

过滤器

6.4.7 组件化

1、全局组件

2、组件的复用

3、局部组件

6.4.8 生命周期钩子函数

1、生命周期

2、钩子函数

6.4.9 vue模块化开发

防止安装的时候在cmd窗口鼠标点击卡住,去掉快速编辑模式勾选

1、npm install webpack -g

全局安装webpack

2、npm install -g @vue/cli-init

全局安装vue脚手架

3、初始化vue项目

vue init webpack appname:vue脚手架使用webpack模板初始化一个appname项目

4、启动vue项目

项目的package.json中有scripts,代表我们能运行的命令

npm start = npm run dev:启动项目

npm run build:将项目打包

5、模块化开发

1)项目结构

2)Vue单文件组件

3)vscode添加用户代码片段(快速生成vue模板)

4)导入element-ui快速开发

6.5 Babel

6.6 Webpack

7 商品服务

7.1 基础概念

7.1.1 三级分类

7.1.2 SPU与SKU

P70

7.1.3 基本属性【规格参数】与销售属性

7.2 接口编写

7.2.1 HTTP请求模板

7.2.2 JSR303 数据局校验

7.2.3 全局异常处理

7.2.4 接口文档地址

https://easydoc.net/#/s/78237135

7.2.5 Obect划分★

7.2.5.1 PO(persistant object) 持久对象

PO就是对应数据库中某个表的一条记录,多个记录可以用PO的集合。PO中应该不包含任何对数据库的操作

7.2.5.2 DO(Domain Object)领域对象

就是从现实世界中抽象出来的有形或无形的业务实体

7.2.5.3 TO(Transfer Object),数据传输对象

不同的应用程序之间传输的对象

7.2.5.4 DTO(Data Transfer Object) 数据传输对象

这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,泛指用于展示层与服务层之间的数据传输对象

7.2.5.5 VO(value obect)值对象

通常用于业务层之前的数据传递,和PO一样也是仅仅包含数据而已。但应是抽象出的业务对象,可以和表对应,也可以不,这根据业务的需要。用new关键字创建,由GC回收的。

也可以叫

View object:视图对象;更形象

接收页面传递来的数据,封装对象

将业务处理完成的对象,封装成页面要用的数据

7.2.5.6 BO(business object) 业务对象
7.2.5.7 POJO(plain ordinary java object) 简单无规则java对象
7.2.5.8 DAO(data access object) 数据访问对象

7.3 网关统一配置跨域

P47

跨域

跨域流程

官方文档对跨域解释说明:

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

7.3.1 解决跨域

开发阶段先采用方式二:

8 商品服务-品牌管理

8.1 阿里云对象存储oss

P62阿里云文件存储

8.2 JSR303数据校验

P65前端表单校验

P66后端校验

P68JSR303分组校验

P69JSR303自定义校验注解

8.3 错误码和错误信息定义类

P67统一异常处理(AOP)

/***

* 错误码和错误信息定义类

* 1. 错误码定义规则为5位数字

* 2. 前两位表示业务场景,最后三位表示错误码。例如:10001。10:通用 001:系统未知异常

* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式

* 错误码列表:

* 10:通用

* 001:参数格式校验

* 11:商品

* 12:订单

* 13:购物车

* 14:物流

*

*

*/

9 商品服务-属性分组

P71父子组件交互

10 商品服务-平台属性

P76 vo

11 商品服务-新增商品

11.1 项目笔记:

远程服务调用

P9,11'

gulimall-product中的CouponFeignService.saveSpuBounds(spuBoundT) 远程调用了gulimall-coupon服务,传递了一个对象,不是基本类型的数据。

SpringCloud做的第一步:

1)通过@RequestBody将这个对象转为json

2)在注册中心找到gulimall-coupon服务,给/coupon/spubounds/save发送请求,将上一步转的json放在请求体位置,发送请求;

gulimall-product服务:

java 复制代码
package com.atguigu.gulimall.product.feign;

import com.atguigu.common.to.SpuBoundTo;
import com.atguigu.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

@FeignClient("gulimall-coupon")
public interface CouponFeignService {


    /**
     * 1、CouponFeignService.saveSpuBounds(spuBoundTo);
     *  1)、@RequestBody将这个对象转为json。
     *  2)、找到gulimall-coupon服务,给/coupon/spubounds/save发送请求。
     *       将上一步转的json放在请求体位置,发送请求;
     *  3)、对方服务收到请求。请求体里有json数据。
     *     (@RequestBody SpuBoundsEntity spuBounds);将请求体的json转为SpuBoundsEntity;
     * 只要json数据模型是兼容的。双方服务无需使用同一个to
     * @param spuBoundTo
     * @return
     */
    @PostMapping("/coupon/spubounds/save")
    R saveSpuBounds(@RequestBody SpuBoundTo spuBoundTo);
}

gulimall-coupon服务:

java 复制代码
package com.atguigu.gulimall.coupon.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import com.atguigu.gulimall.coupon.entity.SpuBoundsEntity;
import com.atguigu.gulimall.coupon.service.SpuBoundsService;
import com.atguigu.common.utils.R;

/**
 * 商品spu积分设置
 */
@RestController
@RequestMapping("coupon/spubounds")
public class SpuBoundsController {
    @Autowired
    private SpuBoundsService spuBoundsService;

    /**
     * 保存
     */
    @PostMapping("/save")
    //@RequiresPermissions("coupon:spubounds:save")
    public R save(@RequestBody SpuBoundsEntity spuBounds){
		spuBoundsService.save(spuBounds);

        return R.ok();
    }

}

11.2 设置服务内存占用与批量重启

设置批量重启:

设置服务内存占用:

12 商品服务-商品管理

12.1 SPU检索

SPU(Standard Product Unit):标准化产品单元。是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU。

12.2 SKU检索

最小存货单位(SKU),全称为Stock Keeping Unit,即库存进出计量的基本单元,可以是以件,盒,托盘等为单位。SKU这是对于大型连锁超市DC(配送中心物流管理的一个必要的方法。2023年已经被引申为产品统一编号的简称,每种产品均对应有SKU号。单品:对一种商品而言,当其品牌、型号、配置、等级、花色、包装容量、单位、生产日期保质期、用途、价格、产地等属性中任一属性与其他商品存在不同时,可称为一个单品。

12.3 SPU规格维护P100

菜单:商品系统-商品维护-spu管理

显示400的,评论区答案

{ path: '/product-attrupdate', component: _import('modules/product/attrupdate'), name: 'attr-update', ​编辑meta: { title: '规格维护', isTab: true } }

**功能描述:**第一次添加商品时,指定了一些属性。点击规格可以进行修改:①首先进行商品规格信息回显;②修改商品规格

①查询商品规格属性

接口文档

商品系统:22、获取spu规格

数据库表

gulimall_pms.pms_product_attr_value(spu属性值)

Controller

java 复制代码
package com.atguigu.gulimall.product.controller;

/**
 * 商品属性
 *
 * @author daijinyu
 * @email daijinyu@gmail.com
 * @date 2024-09-09 01:55:03
 */
@RestController
@RequestMapping("product/attr")
public class AttrController {
    @Autowired
    private AttrService attrService;

    @Autowired
    ProductAttrValueService productAttrValueService;

    /**
     * 22、获取spu规格 /product/attr/base/listforspu/{spuId}
     * @param spuId
     * @return
     */
    @GetMapping("/base/listforspu/{spuId}")
    public R baseAttrlistforspu(@PathVariable("spuId") Long spuId){

        List<ProductAttrValueEntity> entities = productAttrValueService.baseAttrlistforspu(spuId);

        return R.ok().put("data",entities);
    }
}

ServiceImpl

java 复制代码
package com.atguigu.gulimall.product.service.impl;

@Service("productAttrValueService")
public class ProductAttrValueServiceImpl extends ServiceImpl<ProductAttrValueDao, ProductAttrValueEntity> implements ProductAttrValueService {
    /**
     * 22、获取spu规格 /product/attr/base/listforspu/{spuId}
     * @param spuId
     * @return
     */
    @Override
    public List<ProductAttrValueEntity> baseAttrlistforspu(Long spuId) {
        List<ProductAttrValueEntity> entities = this.baseMapper.selectList(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id", spuId));
        return entities;
    }
}

②修改商品规格

接口文档

商品系统:23、修改商品规格

Controller

java 复制代码
package com.atguigu.gulimall.product.controller;

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

import com.atguigu.gulimall.product.entity.ProductAttrValueEntity;
import com.atguigu.gulimall.product.service.ProductAttrValueService;
import com.atguigu.gulimall.product.vo.AttrGroupRelationVo;
import com.atguigu.gulimall.product.vo.AttrRespVo;
import com.atguigu.gulimall.product.vo.AttrVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import com.atguigu.gulimall.product.entity.AttrEntity;
import com.atguigu.gulimall.product.service.AttrService;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.R;


/**
 * 商品属性
 *
 * @author daijinyu
 * @email daijinyu@gmail.com
 * @date 2024-09-09 01:55:03
 */
@RestController
@RequestMapping("product/attr")
public class AttrController {
    @Autowired
    private AttrService attrService;

    @Autowired
    ProductAttrValueService productAttrValueService;

    /**
     * 23、修改商品规格 /product/attr/update/{spuId}
     * @param spuId
     * @param entities
     * @return
     */
    @PostMapping("/update/{spuId}")
    public R updateSpuAttr(@PathVariable("spuId") Long spuId,
                           @RequestBody List<ProductAttrValueEntity> entities){

        productAttrValueService.updateSpuAttr(spuId,entities);

        return R.ok();
    }
}

ServiceImpl

由于更新存在这样的问题:以前没录入的数据新录入了,以前录入的数据现在删除了。所以直接先将旧数据删除,再插入新数据

1、删除这个spuId之前对应的所有属性;

2、更新(这里其实就是新插入);

java 复制代码
package com.atguigu.gulimall.product.service.impl;

@Service("productAttrValueService")
public class ProductAttrValueServiceImpl extends ServiceImpl<ProductAttrValueDao, ProductAttrValueEntity> implements ProductAttrValueService {

    /**
     * 23、修改商品规格 /product/attr/update/{spuId}
     * @param spuId
     * @param entities
     */
    @Transactional
    @Override
    public void updateSpuAttr(Long spuId, List<ProductAttrValueEntity> entities) {
        //1、删除这个spuId之前对应的所有属性
        this.baseMapper.delete(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id",spuId));


        List<ProductAttrValueEntity> collect = entities.stream().map(item -> {
            item.setSpuId(spuId);
            return item;
        }).collect(Collectors.toList());
        this.saveBatch(collect);
    }
}

13 仓储服务-仓库管理

13.1 合并采购需求P97

菜单:库存系统-采购单维护

采购单状态的枚举类写在gulimall-common

java 复制代码
package com.atguigu.common.constant;

public class WareConstant {

    //采购单-状态
    public enum  PurchaseStatusEnum{
        CREATED(0,"新建"),ASSIGNED(1,"已分配"),
        RECEIVE(2,"已领取"),FINISH(3,"已完成"),
        HASERROR(4,"有异常");
        private int code;
        private String msg;

        PurchaseStatusEnum(int code,String msg){
            this.code = code;
            this.msg = msg;
        }

        public int getCode() {
            return code;
        }

        public String getMsg() {
            return msg;
        }
    }

    //采购需求-状态
    public enum  PurchaseDetailStatusEnum{
        CREATED(0,"新建"),ASSIGNED(1,"已分配"),
        BUYING(2,"正在采购"),FINISH(3,"已完成"),
        HASERROR(4,"采购失败");
        private int code;
        private String msg;

        PurchaseDetailStatusEnum(int code,String msg){
            this.code = code;
            this.msg = msg;
        }

        public int getCode() {
            return code;
        }

        public String getMsg() {
            return msg;
        }
    }
}

①点击库存系统-采购单维护-采购需求 的合并整单,查询新建与已分配的采购单

接口文档

库存系统:05、查询未领取的采购单

②点击库存系统-采购单维护-采购需求 的合并整单的确定,将库存系统-采购单维护-采购需求 合并到库存系统-采购单维护-采购单

合并整单时,可以不选择采购单,则自动新建一条采购单;如果选择采购单,则将当前采购需求合并到对应的采购单。

采购需求(wms_purchase_detail)合并到采购单(wms_purchase)时:修改wms_purchase_detail的purchase_id和status。purchase_id修改为新增的采购单id或者选择的采购单id;status修改为已分配

接口文档

库存系统:04、合并采购需求

Controller

13.2 领取采购单P98

采购人员领取采购单,完成采购,将商品入库。

采购单一经领取,采购单状态变为已领取。同时,已领取的采购单,采购需求不能再被分配,且采购需求状态变为正在采购。

接口文档

库存系统:06、领取采购单

①领取新建或者已分配状态的采购单

根据传参过来的采购单,先查询出所有采购单;通过stream流的filter过滤方法过滤出新建或者已分配状态的采购单;

②改变采购单的状态

将过滤出的采购单状态修改为已领取;

③改变采购项(采购需求)的状态

通过采购单id查找出所有采购项(采购需求);修改采购项的状态为正在采购;

java 复制代码
/**
 * 领取采购单
 * @param ids 采购单id
 */
@Override
public void received(List<Long> ids) {
    //1、确认当前采购单是新建或者已分配状态
    List<PurchaseEntity> collect = ids.stream().map(id -> {
        PurchaseEntity byId = this.getById(id);
        return byId;
    }).filter(item -> {
        if (item.getStatus() == WareConstant.PurchaseStatusEnum.CREATED.getCode() ||
                item.getStatus() == WareConstant.PurchaseStatusEnum.ASSIGNED.getCode()) {
            return true;
        }
        return false;
    }).map(item->{
        item.setStatus(WareConstant.PurchaseStatusEnum.RECEIVE.getCode());
        item.setUpdateTime(new Date());
        return item;
    }).collect(Collectors.toList());

    //2、改变采购单的状态
    this.updateBatchById(collect);

    //3、改变采购项的状态
    collect.forEach((item)->{
        List<PurchaseDetailEntity> entities = detailService.listDetailByPurchaseId(item.getId());
        List<PurchaseDetailEntity> detailEntities = entities.stream().map(entity -> {
            PurchaseDetailEntity entity1 = new PurchaseDetailEntity();
            entity1.setId(entity.getId());
            entity1.setStatus(WareConstant.PurchaseDetailStatusEnum.BUYING.getCode());
            return entity1;
        }).collect(Collectors.toList());
        detailService.updateBatchById(detailEntities);
    });
}

13.3 完成采购 P99

采购人员通过手机app领取采购单,领取完成以后点击完成,完成采购单,即:采购需求中的商品id和对应的采购数量入库保存

完成采购:传入采购单id,采购单关联的采购项(采购需求),采购人员在app上进行勾选,改变采购项的状态。采购失败(没勾选的,没完成的)需要填写采购失败原因

待完善:

采购项表gulimall_wms.wms_purchase_detail没有失败原因reason字段

接口文档

库存系统:07、完成采购

完成采购单Vo

java 复制代码
package com.atguigu.gulimall.ware.vo;

import lombok.Data;

import javax.validation.constraints.NotNull;
import java.util.List;

//完成采购单Vo
@Data
public class PurchaseDoneVo {

    @NotNull
    private Long id;//采购单id

    private List<PurchaseItemDoneVo> items;
}

完成采购项Vo

java 复制代码
package com.atguigu.gulimall.ware.vo;

import lombok.Data;

//完成采购项Vo
@Data
public class PurchaseItemDoneVo {
    //{itemId:1,status:4,reason:""}
    private Long itemId;
    private Integer status;
    private String reason;
}

Controller

java 复制代码
@Autowired
private PurchaseService purchaseService;

/**
 * 07、完成采购 /ware/purchase/done
 * @param doneVo
 * @return
 */
@PostMapping("/done")
public R finish(@RequestBody PurchaseDoneVo doneVo){

    purchaseService.done(doneVo);

    return R.ok();
}

ServiceImpl

java 复制代码
package com.atguigu.gulimall.ware.service.impl;

@Service("purchaseService")
public class PurchaseServiceImpl extends ServiceImpl<PurchaseDao, PurchaseEntity> implements PurchaseService {

    @Autowired
    PurchaseDetailService detailService;

    @Autowired
    WareSkuService wareSkuService;

    /**
     * 07、完成采购 /ware/purchase/done
     * @param doneVo
     */
    @Transactional
    @Override
    public void done(PurchaseDoneVo doneVo) {
        Long id = doneVo.getId();

        //2、改变采购项的状态
        Boolean flag = true;
        List<PurchaseItemDoneVo> items = doneVo.getItems();

        List<PurchaseDetailEntity> updates = new ArrayList<>();
        for (PurchaseItemDoneVo item : items) {
            PurchaseDetailEntity detailEntity = new PurchaseDetailEntity();
            if(item.getStatus() == WareConstant.PurchaseDetailStatusEnum.HASERROR.getCode()){
                flag = false;
                detailEntity.setStatus(item.getStatus());
            }else{
                detailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.FINISH.getCode());
                3、将成功采购的进行入库
                PurchaseDetailEntity entity = detailService.getById(item.getItemId());
                wareSkuService.addStock(entity.getSkuId(),entity.getWareId(),entity.getSkuNum());

            }
            detailEntity.setId(item.getItemId());
            updates.add(detailEntity);
        }

        detailService.updateBatchById(updates);

        //1、改变采购单状态
        PurchaseEntity purchaseEntity = new PurchaseEntity();
        purchaseEntity.setId(id);
        purchaseEntity.setStatus(flag?WareConstant.PurchaseStatusEnum.FINISH.getCode():WareConstant.PurchaseStatusEnum.HASERROR.getCode());
        purchaseEntity.setUpdateTime(new Date());
        this.updateById(purchaseEntity);

    }
}

WareSkuServiceImpl.addStock()

java 复制代码
package com.atguigu.gulimall.ware.service.impl;

@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    WareSkuDao wareSkuDao;

    @Autowired
    ProductFeignService productFeignService;

    /**
     * 添加商品库存 gulimall_wms.wms_ware_sku
     * @param skuId
     * @param wareId
     * @param skuNum
     */
    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        //1、判断如果还没有这个库存记录新增
        List<WareSkuEntity> entities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if(entities == null || entities.size() == 0){
            WareSkuEntity skuEntity = new WareSkuEntity();
            skuEntity.setSkuId(skuId);
            skuEntity.setStock(skuNum);
            skuEntity.setWareId(wareId);
            skuEntity.setStockLocked(0);
            //TODO 远程查询sku的名字,如果失败,整个事务无需回滚
            //1、自己catch异常
            //TODO 还可以用什么办法让异常出现以后不回滚?高级
            try {
                R info = productFeignService.info(skuId);
                Map<String,Object> data = (Map<String, Object>) info.get("skuInfo");

                if(info.getCode() == 0){
                    skuEntity.setSkuName((String) data.get("skuName"));
                }
            }catch (Exception e){

            }

            wareSkuDao.insert(skuEntity);
        }else{
            wareSkuDao.addStock(skuId,wareId,skuNum);
        }
    }
}

WareSkuDao.addStock()

java 复制代码
package com.atguigu.gulimall.ware.dao;

import com.atguigu.gulimall.ware.entity.WareSkuEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
 * 商品库存
 * 
 * @author daijinyu
 * @email daijinyu@gmail.com
 * @date 2024-09-09 22:38:50
 */
@Mapper
public interface WareSkuDao extends BaseMapper<WareSkuEntity> {

    void addStock(@Param("skuId") Long skuId, @Param("wareId") Long wareId, @Param("skuNum") Integer skuNum);
}
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.gulimall.ware.dao.WareSkuDao">

    <update id="addStock">
        UPDATE `wms_ware_sku` SET stock=stock+#{skuNum} 
        WHERE sku_id=#{skuId} AND ware_id=#{wareId}
    </update>

</mapper>

ProductFeignService.info()

java 复制代码
package com.atguigu.gulimall.ware.feign;

import com.atguigu.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient("gulimall-product")
public interface ProductFeignService {

    /**
     *      /product/skuinfo/info/{skuId}
     *
     *
     *   1)、让所有请求过网关;
     *          1、@FeignClient("gulimall-gateway"):给gulimall-gateway所在的机器发请求
     *          2、/api/product/skuinfo/info/{skuId}
     *   2)、直接让后台指定服务处理
     *          1、@FeignClient("gulimall-product")
     *          2、/product/skuinfo/info/{skuId}
     *
     * @return
     */
    @RequestMapping("/product/skuinfo/info/{skuId}")
    public R info(@PathVariable("skuId") Long skuId);
}

②改变采购项的状态

给定一个标志位flag(默认true),遍历所有采购项,只要有一个采购项的状态为采购失败,标志位就置为false;

更新采购项的状态,失败的更新为4-采购失败,成功的更新为3-已完成

③将成功采购的进行入库

根据当前item采购项的id查出采购项的详细信息(gulimall_wms.wms_purchase_detail采购项表:sku_id 采购商品id,ware_id 仓库id,sku_num 采购数量);

新建WareSkuServiceImpl.addStock() 方法,将以上3个参数传入,对gulimall_wms.wms_ware_sku商品库存表进行入库操作;

如果商品库存表中还没有这个库存记录时,需要先新增库存记录。根据商品id和仓库id查询商品库存表,如果查询为空进行新增操作:将以上3个参入存入对应字段,stock_locked锁定库存默认设为0,sku_name商品名字通过远程查询获:根据sku_id调用远程接口查询详细信息;

如果商品库存表中有这个库存记录时,更新库存数。WareSkuServiceImpl.addStock() 方法中新建wareSkuDao.addStock()方法,传入以上三个参数,更新gulimall_wms.wms_ware_sku商品库存表 stock 库存数;

①改变采购单状态

根据flag的值更新采购单状态,如果flag=true,采购单状态更新为3-已完成;如果flag=false,采购单状态更新为4-有异常;

14 分布式基础篇总结P101

附录:通用设置

日期格式化

application.yml

java 复制代码
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss

MyBatis分页配置

java 复制代码
package com.atguigu.gulimall.ware.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableTransactionManagement
@MapperScan("com.atguigu.gulimall.ware.dao")
@Configuration
public class WareMyBatisConfig {

    //引入分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
//        paginationInterceptor.setOverflow(true);
//        // 设置最大单页限制数量,默认 500 条,-1 不受限制
//        paginationInterceptor.setLimit(1000);
        return paginationInterceptor;
    }
}
相关推荐
Java方文山10 个月前
【电商项目实战】MD5登录加密及JSR303自定义注解
redis·cookie·jsr303·电商项目·全局异常抓捕·md5加密
匿瘾:1 年前
java_web_电商项目
java·前端·eclipse·web·电商项目
heshengfu12111 年前
微服务版mall电商项目本地开发环境启动前后端服务详细指导教程
spring cloud·电商项目