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

news2024/11/25 3:04:09

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

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 

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 

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、更新(这里其实就是新插入);

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

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 {

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

    private 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 {

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

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

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 {

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

金九银十互联网大厂Java高频面试题(2024最新含答案)

2024 年的互联网行业竞争越来越严峻&#xff0c;面试也是越来越难&#xff0c;一直以来我都想整理一套完美的面试宝典&#xff0c;奈何难抽出时间&#xff0c;这套 1200道的 Java 面试手册我整理了整整 1 个月&#xff0c;上传到 Git 上目前 star 数达到了 30K 这套互联网 Jav…

冲击美团!已成功 OC

这是一位训练营学员的美团面经&#xff0c;目前已经 OC 。 在此之前他已经拿到了不少公司的offer&#xff0c;但是都达不到他的预期&#xff0c;美团给的待遇就非常不错&#xff0c;大厂不愧是大厂&#xff0c;就是不知道工作强度如何。 他经历了一共三场面试&#xff0c;一面…

实现省略号查看详情样式

1.期望实现效果 2.目前实现效果 3.实现代码 1.wxml<view class"desc-text"><view class"show-more">查看详情 >></view><!-- <rich-text nodes"{{富文本接口数据内容 }}"></rich-text> --><text&…

python反爬

1.无限debug无法f12 关闭掉 Deactivate Breakpoints

【闲谈程序设计例三则:抛弃传统单步进初级阶段,用推导归纳出来的规律写代码,进入进阶阶段,人类自性的高级活动。】2024-10-21

闲谈程序设计三则&#xff1a;抛弃传统单步进&#xff0c;用推导归纳出来的规律写代码。 本论坛常见新学提问都是一些入门级别的问题&#xff0c;近来AI活跃抢答&#xff0c;然而&#xff0c;对于有些问题AI可以说是答非所问&#xff0c;令人哭笑不得&#xff0c;而AI能回答的…

MacOS安装BurpSuite

文章目录 一、下载地址二、下载注册机三、安装教程四、启动burpsuit五、免责声明 一、下载地址 https://portswigger-cdn.net/burp/releases/download?productpro&version2024.7.1&typeMacOsx二、下载注册机 https://github.com/NepoloHebo/BurpSuite-BurpLoaderKey…

B站协议登录到实现各种功能完整代码(专栏总结)

B站协议登录、点赞、收藏、转发实现及代码 关注、动态转发实现动态抽奖实现及代码 直播预约抽奖实现及代码 本文为本专栏的总结文章 一、扫码登录 请求获取二维码包&#xff0c;得到二维码链接和qrcode_key参数之后&#xff0c;利用qrcode_key循环GET请求登录状态包即可&#x…

【word】页眉横线无法取消

小伙伴们日常想在页眉里加横线&#xff0c;直接双击页眉&#xff0c;然后在页眉横线里选择自己喜欢的横线样式就可以了。 但今天我遇到的这个比较奇特&#xff0c;有些页有这个横线&#xff0c;有些页没有&#xff0c;就很奇怪。 最后排查完&#xff0c;发现是只有标题2的页…

WPF入门_02依赖属性

1、依赖属性主要有以下三个优点 1)依赖属性加入了属性变化通知、限制、验证等功能。这样可以使我们更方便地实现应用,同时大大减少了代码量 2)节约内存:在WinForm中,每个UI控件的属性都赋予了初始值,这样每个相同的控件在内存中都会保存一份初始值。而WPF依赖属性很好地…

《向量数据库指南》揭秘:Mlivus Cloud如何赋能GraphRAG应用

嘿,各位向量数据库和AI领域的探索者们,我是你们的老朋友,大禹智库的向量数据库高级研究员王帅旭,也是《向量数据库指南》的作者。今天,咱们来聊聊一个既前沿又实用的话题——检索增强生成(Retrieval Augmented Generation,简称RAG)及其面临的挑战,特别是如何用Mlivus …

前端/node.js锁定依赖版本、锁定依赖的依赖的版本

一、知识前提 version&#xff1a;必须依赖某个具体的版本。如&#xff1a;vue的3.2.0&#xff0c;表示必须安装3.2.0版本。>version&#xff1a;必须大于某个版本。>version&#xff1a;大于或等于某个版本。<version&#xff1a;必须小于某个版本。<version&…

多线程——单例模式

目录 前言 一、设计模式 二、饿汉模式 三、懒汉模式 1.单线程版 2.多线程版 结尾 前言 前面的几篇文章中介绍了多线程编程的基础知识&#xff0c;在本篇文章开始&#xff0c;就会利用前面的多线程编程知识来编写一些代码案例&#xff0c;从而使大家可以更好的理解运用多…

扩散模型对抗蒸馏:ADD 和 Latent-ADD

扩散模型对抗蒸馏&#xff1a;ADD 和 Latent-ADD ADD&#xff08;Adversarial Diffusion Distillation&#xff09;和 Latent-ADD 是 StabilityAI 公司提出的一系列针对 Stable Diffusion 的扩散模型对抗蒸馏方法&#xff0c;通过对抗训练和蒸馏训练来提高扩散模型的采样速度&…

python基于图片内容识别的微信自动发送信息(对其中逻辑修改一些可以改为自动化回复)

1.内容基于python日常生活问题帮助 2.主要框架 import time from datetime import datetimeimport pyperclip import win32api import win32con import os import refrom Image_Content_Text_Recognition import ICTR from screenshot import img 上面是逻辑部分主要框架 i…

【开源免费】基于SpringBoot+Vue.JS在线视频教育平台(JAVA毕业设计)

本文项目编号 T 027 &#xff0c;文末自助获取源码 \color{red}{T027&#xff0c;文末自助获取源码} T027&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 新…

解析 Vue 模板的本质:从语法糖到渲染过程

大家耳熟能详的表述如下&#xff1a;Vue 模板的本质其实是一种 声明式渲染 的形式&#xff0c;它在开发过程中提供了将组件的结构与逻辑分离的便利。 也就是说&#xff0c;模板 template 的存在只是为了让我们以更直观的方式描述界面的结构&#xff0c;然而在运行时&#xff0…

Android Framework AMS(09)service组件分析-3(bindService和unbindService关键流程分析)

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;上上一章节主要解读应用层service组件启动的2种方式startService和bindService&#xff0c;以及从APP层到AMS调用之间的打通。上一章节我们关注了s…

北京大学冯惠:与卓越者同行,方能更快的成长 | OceanBase数据库大赛获奖选手访谈

本文邀请2022 OceanBase 数据库大赛的季军&#xff0c;来自北京大学的冯惠同学&#xff0c;与我们分享如何寻找自己的兴趣&#xff1b;在一番经历后&#xff0c;对于产品与研发的职业方向观察&#xff1b;以及如何在学生时期提升个人专业能力&#xff0c;和参加数据库大赛的个人…

【Python技术】利用akshare定时获取股票实时价,低于5日线钉钉通知报警

今天看了下大盘&#xff0c;临时有个想法&#xff0c;我想知道某个股票回踩5日线的价格&#xff0c;如果实时价格低于5日线通过钉钉报警通知我。 说干就干&#xff0c;临时撸了下简单的代码&#xff0c;仅做演示。 1、计算5日线思路 很多券商软件的MA5价格是近5个交易日收盘…

Java项目-基于springboot框架的医患档案管理系统项目实战(附源码+文档)

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…