【电商项目】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;}
}
相关文章:

【电商项目】1分布式基础篇
1 项目简介 1.2 项目架构图 1.2.1 项目微服务架构图 1.2.2 微服务划分图 2 分布式基础概念 3 Linux系统环境搭建 查看网络IP和网关 linux网络环境配置 补充P123(修改linux网络设置&开启root密码访问) 设置主机名和hosts映射 主机名解析过程分析&…...

PHP嵌套函数
PHP嵌套函数(Nested Functions)在标准的PHP语法中并不直接支持,也就是说,你不能在一个函数内部直接定义另一个函数。然而,可以通过闭包(Closures)和匿名函数(Anonymous Functions&am…...

外包干了2个月,技术明显退步
回望过去,我是一名普通的本科生,于2019年通过校招有幸加入了南京某知名软件公司。那时的我,满怀着对未来的憧憬和热情,投入到了功能测试的岗位中。日复一日,年复一年,转眼间,我已经在这个岗位上…...

kaptcha依赖maven无法拉取的问题
老依赖了,就是无法拉取,也不知道为什么,就是用maven一直拉去不成功,还以为是魔法的原因,试了好久发现不是,只好在百度寻求帮助了,好在寻找到了这位大佬的文章Maven - 解决无法安装 Kaptcha 依赖…...

48.旋转图像
秋招未止脚步不止,大厂,我一定要上大厂! 题目链接 . - 力扣(LeetCode) 自己的思路 感觉好难,想不出来. 噫噫噫,我想着想着又想出来了。 //发现规律了,先左右对称, 再…...

每天5分钟玩转C#/.NET之goto跳转语句
前言 在我们日常工作中常用的C#跳转语句有break、continue、return,但是还有一个C#跳转语句很多同学可能都比较的陌生就是goto,今天大姚带大家一起来认识一下goto语句及其它的优缺点。 goto语句介绍 goto 语句由关键字 goto 后跟一个标签名称组成&…...

Java处理大数据小技巧:深入探讨与实践
引言 一、选择合适的数据结构 1. 使用高效的集合 2. 并发安全的数据结构 二、内存管理 1. JVM参数调优 2. 避免内存泄漏 三、并行计算与分布式处理 1. 利用Java并发API 2. 分布式框架 四、数据压缩与序列化 1. 数据压缩 2. 高效序列化 五、外部存储与缓存 1. NoS…...

我开源了Go语言连接数据库和一键生成结构体的包【实用】
项目地址:https://gitee.com/zht639/my_gopkg autosql autosql 是一个简化数据库使用的模块,支持常见的数据库(MySQL、PostgreSQL、SQLite、SQL Server)。该模块不仅提供了数据库连接函数,还能自动生成数据表对应的结…...

Sentinel 快速入门
前置推荐阅读:Sentinel 介绍-CSDN博客 前置推荐阅读:Nacos快速入门-CSDN博客 快速开始 欢迎来到 Sentinel 的世界!这篇新手指南将指引您快速入门 Sentinel。 Sentinel 的使用可以分为两个部分: 核心库(Java 客户端):…...

基于SpringBoot健康生活助手微信小程序【附源码】
基于SpringBoot健康生活助手微信小程序 效果如下: 管理员登录界面 管理员主界面 用户管理界面 健康记录管理界面 健康目标管理界面 微信小程序首页界面 活动信息界面 留言反馈界面 研究背景 近年来,由于计算机技术和互联网技术的飞速发展,…...

功能安全实战系列-软件FEMA分析与组件鉴定
本文框架 前言1. 功能安全分析1.1 Why1.2 What?1.3 How?1.3.1 分析范围确定1.3.2 失效模式分析1.3.3 安全措施制定1.3.4 确认是否满足功能安全目标2. 软件组件鉴定2.1 Why2.2 How?前言 在本系列笔者将结合工作中对功能安全实战部分的开发经验进一步介绍常用,包括Memory(Fl…...

【数据结构与算法】链表(上)
记录自己所学,无详细讲解 无头单链表实现 1.项目目录文件 2.头文件 Slist.h #include <stdio.h> #include <assert.h> #include <stdlib.h> struct Slist {int data;struct Slist* next; }; typedef struct Slist Slist; //初始化 void SlistI…...

svn-拉取与更新代码
右键项目文件 进行更新与提交代码,提交代码选择更改的文件以及填写commit...

【C++ 算法进阶】算法提升四
数组查询问题 (数组优化) 题目 数组为 {3 , 2, 2 ,3 ,1} 查询为(0 ,3 ,2) 这个查询的意义是 在数组下标0~3这个范围上 有多少个2 (答案为2&…...

多种方式实现安全帽佩戴检测
为什么要佩戴安全帽 在探讨安全帽佩戴检测之前,我们先来了解下安全帽佩戴的必要性: 保护头部免受外力伤害 防止物体打击 在建筑施工、矿山开采、工厂车间等场所,经常会有高空坠物的风险。例如在建筑工地上,可能会有工具、材料、…...

基于PHP+MySQL+Vue的网上订餐系统
摘要 本文介绍了一个基于PHPMySQLVue技术的网上订餐系统。该系统旨在为用户提供便捷的在线订餐服务,同时提高餐厅的运营效率。系统后端采用PHP语言开发,利用MySQL数据库进行数据存储与管理,实现了用户注册登录、菜品浏览、购物车管理、订单提…...

Vue学习笔记 Class绑定 Style绑定 侦听器 表单输入绑定 模板引用 组件组成 组件嵌套关系
文章目录 Class绑定绑定对象绑定数组注意事项 style绑定绑定对象代码效果展示 绑定数组 侦听器注意的点代码效果 表单输入绑定示例代码效果展示 修饰符.lazy.number.trim 模板引用组件组成组件组成结构引入组件步骤style中的scoped作用 组件嵌套关系 Class绑定 绑定对象 绑定数…...

【AIGC】ChatGPT与人类理解力的共鸣:人机交互中的心智理论(ToM)探索
博客主页: [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 💯前言💯心智理论(Theory of Mind,ToM)心智理论在心理学与神经科学中的重要性心智理论对理解同理心、道德判断和社交技能的重要性结论 💯乌得勒支大学研究对ChatGPT-4…...

代码训练营 day39|0-1背包问题,LeetCode 416
前言 这里记录一下陈菜菜的刷题记录,主要应对25秋招、春招 个人背景 211CS本CUHK计算机相关硕,一年车企软件开发经验 代码能力:有待提高 常用语言:C 系列文章目录 第九章 动态规划part03 文章目录 前言系列文章目录第九章 动态…...

LeetCode 203 - 移除链表元素
题目描述 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val val 的节点,并返回 新的头节点 。 解题思路 创建一个虚拟头节点dummyHead,并将其next指向给定的头节点head,这样可以避免处理头节点的特…...

【海图界面上一些常见术语UTC、HDG、COG、SOG、LAT、LON的基本解释】
当然,以下是关于海图界面上一些常见术语UTC、HDG、COG、SOG、LAT、LON的基本解释: UTC (Coordinated Universal Time) 定义:UTC 是协调世界时(Coordinated Universal Time)的缩写,是一种与地球自转无关的…...

HL7协议简介及其在STM32上的解析实现
近期完成一个医疗相关的项目,其中包括了体征监测设备,该设备使用的通信协议便是HL7 V2.4 协议,在医疗信息化领域,HL7(Health Level Seven)协议扮演着至关重要的角色。它是一种国际标准,用于定义医疗机构间以及医疗设备与信息系统之间的数据交换格式和通信协议。HL7标准旨…...

TensorRT推理端到端
TensorRT推理端到端 1.参考链接2.宿主机上安装CUDA 12.4.13.安装nvidia-container-toolkit4.创建ghcr.io/intel/llvm/ubuntu2204_base容器5.容器内安装CUDA 12.4.1 + TensorRT10.1.06.安装依赖7.准备resnet50模型8.准备bert模型9.准备yolov5m模型10.编译TensorRT推理程序11.onn…...

获取历史的天气预报数据的网站
要获取从2019年到现在某个中国城市的天气数据,您可以通过以下方法实现: 1. 使用第三方天气数据API 许多天气服务提供商提供了历史天气数据的API接口,您可以通过这些API获取所需的数据。以下是一些常用的天气数据API提供商: 1.1…...

【VUE】Vue中常用的修饰符
事件修饰符 .stop:阻止事件冒泡。.prevent:阻止默认事件。.capture:使用事件捕获模式。.self:只当事件在该元素本身(比如不是子元素)触发时触发回调。.once:只触发一次事件。 按键修饰符 .en…...

数据分箱:如何确定分箱的最优数量?
选择最优分箱可以考虑以下几种方法: 一、基于业务理解 分析业务背景:从业务角度出发,某些特征可能有自然的分组或区间划分。例如,年龄可以根据不同的人生阶段进行分箱,收入可以根据常见的收入等级划分。 优点&#x…...

机器学习核心功能:分类、回归、聚类与降维
机器学习核心功能:分类、回归、聚类与降维 机器学习领域的基本功能类型通常按照学习模式、预测目标和算法适用性来分类。这些类型包括监督学习、无监督学习、半监督学习和强化学习,它们可以进一步细化为特定的任务,如分类、回归、聚类和降维…...

Python爬虫-eBay商品排名数据
前言 本文是该专栏的第39篇,后面会持续分享python爬虫干货知识,记得关注。 本文以eBay为例,通过搜索目标”关键词“,获取相关搜索”关键词“的商品排名数据。废话不多说,具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。接下来,跟着笔者直接往下看正文详…...

LabVIEW提高开发效率技巧----图像处理加速
在现代工业和科研中,图像处理技术被广泛应用于质量检测、自动化控制、机器人导航等领域。然而,随着图像数据量的增加,传统的CPU处理方式可能难以满足实时性和高效处理的需求。LabVIEW通过结合NI Vision模块和FPGA硬件平台,可以显著…...

AcWing1027
题目重述: 题目的核心是找到一条路径的最大权值总和,但路径要从起点 (1, 1) 走到终点 (n, n)。由于两条路径分别经过不同的格子,我们可以巧妙地将问题简化为两次同时出发的路径问题。这种映射的设计让我们能够更方便地处理两条路径重叠在同一…...