【电商项目】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

sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine

(2)安装docker依赖包

sudo yum install -y yum-utils device-mapper-persistent-data lvm2

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

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的镜像仓库失败,切换成阿里云的镜像下载

yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

(4)安装docker

sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

(5)启动docker

sudo systemctl start docker

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

systemctl enable docker

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

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

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

{"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"]
}

配置完镜像源后需要重启

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

 3.3 docker安装mysql

(1)下载镜像文件

docker pull mysql:5.7

(2)创建实例并启动

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控制台:

docker exec -it mysql /bin/bash

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

cp /etc/skel/.bashrc /root/ 
cp /etc/skel/.bash_profile /root/

mysql配置

vi /mydata/mysql/conf/my.cnf

[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)下载镜像文件

docker pull redis

(2)创建实例并启动

mkdir -p /mydata/redis/conf

touch /mydata/redis/conf/redis.conf

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

# 配置用户名
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 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模板 

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下载的时候请求下载的很慢

设置最新淘宝镜像

npm config set registry https://registry.npmmirror.com/

查看是否已经配置成功

npm config get registry

安装pnpm 

npm install --global pnpm

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

pnpm config set registry https://registry.npmmirror.com

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

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项目中引入如下。进行统一管理 

<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

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

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

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

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

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 配置文件中给微服务起注册中心的名字

spring.application.name=gulimall-coupon

5.1.2 Nacos作为配置中心

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

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

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

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

#设置服务名
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下载的时候请求下载的很慢

设置最新淘宝镜像

npm config set registry https://registry.npmmirror.com/

查看是否已经配置成功

npm config get registry

安装pnpm 

npm install --global pnpm

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

pnpm config set registry https://registry.npmmirror.com

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

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服务:

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服务:

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 {@Autowiredprivate 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

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 {@Autowiredprivate AttrService attrService;@AutowiredProductAttrValueService 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 

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*/@Overridepublic List<ProductAttrValueEntity> baseAttrlistforspu(Long spuId) {List<ProductAttrValueEntity> entities = this.baseMapper.selectList(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id", spuId));return entities;}
}

 ②修改商品规格

接口文档

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

Controller 

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 {@Autowiredprivate AttrService attrService;@AutowiredProductAttrValueService 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、更新(这里其实就是新插入);

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@Overridepublic 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

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查找出所有采购项(采购需求);修改采购项的状态为正在采购

/*** 领取采购单* @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 

package com.atguigu.gulimall.ware.vo;import lombok.Data;import javax.validation.constraints.NotNull;
import java.util.List;//完成采购单Vo
@Data
public class PurchaseDoneVo {@NotNullprivate Long id;//采购单idprivate List<PurchaseItemDoneVo> items;
}

完成采购项Vo

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

@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 

package com.atguigu.gulimall.ware.service.impl;@Service("purchaseService")
public class PurchaseServiceImpl extends ServiceImpl<PurchaseDao, PurchaseEntity> implements PurchaseService {@AutowiredPurchaseDetailService detailService;@AutowiredWareSkuService wareSkuService;/*** 07、完成采购 /ware/purchase/done* @param doneVo*/@Transactional@Overridepublic 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() 

package com.atguigu.gulimall.ware.service.impl;@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {@AutowiredWareSkuDao wareSkuDao;@AutowiredProductFeignService productFeignService;/*** 添加商品库存 gulimall_wms.wms_ware_sku* @param skuId* @param wareId* @param skuNum*/@Overridepublic 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()  

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 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()

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

spring:jackson:date-format: yyyy-MM-dd HH:mm:ss

MyBatis分页配置

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 {//引入分页插件@Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
//        paginationInterceptor.setOverflow(true);
//        // 设置最大单页限制数量,默认 500 条,-1 不受限制
//        paginationInterceptor.setLimit(1000);return paginationInterceptor;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/451452.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【问题解决】——当出现0xc000007b和缺少mfc140.dll时,该怎么做才能让软件可以打开

目录 事情起因 问题处理 明确定义 填坑之路 最后我是怎么解决的&#xff08;不想看故事直接到这里&#xff09; 事情起因 最近想要重新安装西门子博途来做西门子的一些算法的时候&#xff0c;发现自己软件装的是V15.1的版本&#xff0c;而买的plc1200固件版本要求至少16以…

性能评测第一,阿里开源可商用AI模型Ovis 1.6使用指南,AI多模态大模型首选

什么是 Ovis 1.6 Gemma 2 9B&#xff1f; Ovis 1.6 Gemma 2 9B 是阿里国际AI团队推出的最新多模态大模型&#xff08;Multimodal Large Language Model&#xff0c;MLLM&#xff09;。该模型旨在结构化地对齐视觉和文本嵌入&#xff0c;能够处理和理解多种不同类型的数据输入&…

抑郁症自测量表 API 接口,洞察情绪状态

抑郁症是一种常见的心理疾病&#xff0c;会给患者的生活和工作带来很大的困扰。为了帮助人们更好地了解自己的情绪状态&#xff0c;有一种抑郁症自测量表&#xff08;简称SDS&#xff09;&#xff0c;它是一种能够反映病人主观抑郁症状的自评量表。下面我们将通过调用抑郁症自测…

基于FreeRTOS的LWIP移植

目录 前言一、移植准备工作二、以太网固件库与驱动2.1 固件库文件添加2.2 库文件修改2.3 添加网卡驱动 三、LWIP 数据包和网络接口管理3.1 添加LWIP源文件3.2 Lwip文件修改3.2.1 修改cc.h3.2.2 修改lwipopts.h3.2.3 修改icmp.c3.2.4 修改sys_arch.h和sys_arch.c3.2.5 修改ether…

Linux·文件与IO

1. 回忆文件操作相关知识 我们首先回忆一下关于文件的一些知识。 如果一个文件没有内容&#xff0c;那它到底有没有再磁盘中存在&#xff1f;答案是存在&#xff0c;因为 文件 内容 属性&#xff0c;即使文件内容为空&#xff0c;但属性信息也是要记录的。就像进程的…

硬件产品经理的开店冒险之旅(下篇)

缘起&#xff1a;自己为何想要去寻找职业第二曲线 承接上篇的内容&#xff0c;一名工作13年的普通硬件产品经理将尝试探索第二职业曲线。根本原因不是出于什么高大上的人生追求或者什么职业理想主义&#xff0c;就是限于目前的整体就业形式到了40岁的IT从业人员基本不可能在岗…

【Python】selenium遇到“InvalidArgumentException”的解决方法

在使用try……except 的时候捕获到这个错误&#xff1a; InvalidArgumentException: invalid argument (Session info: chrome112.0.5614.0) 这个错误代表的是&#xff0c;当传入的参数不符合期望时&#xff0c;就会抛出这个异常&#xff1a; InvalidArgumentException: invali…

day-69 使二进制数组全部等于 1 的最少操作次数 II

思路 与3191. 使二进制数组全部等于 1 的最少操作次数 I思路类似&#xff0c;区别在于该题每次将下标i开始一直到数组末尾所有元素反转&#xff0c;所以我们用一个变量可以统计翻转次数 解题过程 从左向右遍历数组的过程中&#xff0c;有两种情况需要进行翻转&#xff1a;1.当…

多媒体(4)

PNG PNG&#xff08;流式网络图像&#xff09;文件采用【无损压缩】算法&#xff0c;压缩比高于GIF文件&#xff0c;支持 图像透明 PNG文件的色彩深度可以是灰度图像的16位&#xff0c;彩色图像的48位&#xff0c;是一种新兴的 网络图像格式 矢量图 矢量图是一组指令集合描述图…

Sentinel 介绍

随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件&#xff0c;主要以流量为切入点&#xff0c;从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开…

芯知识 | NVH-FLASH语音芯片支持平台做语音—打造音频IC技术革新

随着科技的飞速发展&#xff0c;人们对于电子产品的音频性能要求越来越高。在这种背景下&#xff0c;NVH-FLASH系列语音芯片应运而生&#xff0c;作为音频IC领域的一次重大技术革新&#xff0c;NVH-FLASH系列语音芯片凭借其卓越的性能与灵活的支持平台&#xff0c;正逐步引领着…

Linux——网络层协议

前言 网络层&#xff1a;在复杂的网络环境中确定一个合适的路径 目录 前言 一IP协议 1预备知识 2基本概念 3格式 4网段划分 4.1理解IP 4.2IP组成 4.3划分方式 4.4为什么要网段划分 5特殊的IP地址 6IP地址的限制 7私有IP和公网IP 8NAT技术 9理解公网 10路由 …

使用RNN、LSTM和Transformer进行时间序列预测

文章目录 1 RNN & LSTMRNN结构LSTM结构样本和标签 2 Transformertransformer结构位置编码 1 RNN & LSTM RNN结构 LSTM结构 代码&#xff08;使用CPU&#xff09;&#xff1a; import numpy as np import torch from matplotlib import pyplot as plt from torch impo…

SQLite 上手指南 -- 基础语法

目录 一、数据库操作1.1 新建数据库1.2 查看数据库1.3 查看帮助指令 二、表操作2.1 创建表2.2 表信息2.3 表索引信息2.4 表结构信息2.5 删除表 三、数据记录操作3.1 新增记录3.2 查看记录3.3 不同格式输出 四、运算符4.1 算术运算符4.2 比较运算符4.3 逻辑运算符4.4 位运算符 S…

【热门】用ChatGPT做智慧农业云平台——农业ERP管控系统

随着科技的进步,原有农业种植方式已经不能满足社会发展的需要,必须对传统的农业进行技术更新和改造。经过多年的实践,人们总结出一种新的种植方法——温室农业,即“用人工设施控制环境因素,使作物获得最适宜的生长条件,从而延长生产季节,获得最佳的产出”。这种农业生产方式…

Linux安装 php5.6

Linux安装 php5.6.30 下载-解压-配置-安装 下载到 /usr/local wget http://am1.php.net/distributions/php-5.6.30.tar.gztar -zxvf php-5.6.30.tar.gz cd php-5.6.30#编译配置 ./configure --prefix/usr/local/php --with-curl/usr/local/curl --with-freetype-dir --wit…

无mac电脑在苹果开发者上传构建版本

我们登录苹果开发者网站的后台&#xff0c;进入app store后&#xff0c;发现上架的页面需要上传一个构建版本。 这个构建版本的意思就是我们的应用二进制文件&#xff0c;是上架最重要的文件。但是在苹果开发者后台是无法直接上传这个文件的&#xff0c;它提示我们可以使用xco…

R语言机器学习教程大纲

文章目录 介绍机器学习算法监督学习Supervised Learning分类Classification回归Regression 无监督学习 Unsupervised Learning聚类 Clustering降纬 Dimensionality Reduction相关Association 强化学习Reinforcement Learning模型自由 Model-Free Methods模型驱动 Model-Based M…

服务器托管的优缺点有哪些?

由于数字化程度不断提高&#xff0c;服务器在日常业务中发挥着越来越重要的作用。在大多数情况下&#xff0c;服务器由公司自己维护和管理。但对于一些公司来说&#xff0c;托管服务器(将这些任务交给专业人员)是更好的选择。 关于服务器的优缺点&#xff0c;有一点是明确的&am…

【SpringBoot】16 文件上传(Thymeleaf + MySQL)

Gitee仓库 https://gitee.com/Lin_DH/system 介绍 文件上传是指将本地的图片、视频、音频等文件上传到服务器&#xff0c;供其他用户浏览下载的过程&#xff0c;文件上传在日常项目中用的非常广泛。 实现代码 第一步&#xff1a;在配置文件新增如下配置 application.yml s…