云商城--基础数据处理和分布式文件存储
第2章 基础数据处理和分布式文件存储
1.分布式文件存储系统Ceph学习
1).掌握Ceph架构
2).掌握Ceph组件
3).搭建Ceph集群(了解)
2.Ceph使用
1).基于Ceph实现文件上传
2).基于Ceph实现文件下载
3.SKU、SPU管理
1).掌握SKU和SPU关系
2).理解商品发布中商品属性、商品分类、商品品牌加载方案
3).实现SKU和SKU管理(商品发布)
4.品牌管理、分类管理、属性管理(作业)
1).实现品牌管理(增删改查)
2).实现分类管理(增删改查)
3).实现属性管理(增删改查)
5.掌握MyBatisPlus代码生成
1).掌握MyBatisPlus代码生成配置
2).掌握MyBatisPlus代码生成controller,service,mapper
1.分布式文件存储系统Ceph
Ceph是一个统一的分布式存储系统,设计初衷是提供较好的性能、可靠性和可扩展性
对比说明 | FASTDFS | CEPH |
---|---|---|
开发语言 | C | C++ |
数据存储方式 | 文件/Trunk | 对象/文件/块 |
在线扩容 | 支持 | 支持 |
冗余备份 | 支持 | 支持 |
单点故障 | 不存在 | 不存在 |
易用性 | 安装简单,社区相对活跃 | 安装有一定复杂度 |
适用场景 | 单集群的中小文件 | 单集群的大中小文件 |
1.1 Ceph介绍
Ceph于2004年发表,并随后贡献给开源社区。在经过了数年的发展之后,目前已得到众多云计算厂商的支持并被广泛应用。RedHat及OpenStack都可与Ceph整合以支持虚拟机镜像的后端存储
Ceph特点:
CRUSH算法:Crush算法是ceph的两大创新之一,简单来说,ceph摒弃了传统的集中式存储元数据寻址的方案,转而使用CRUSH算法完成数据的寻址操作。CRUSH在一致性哈希基础上很好的考虑了容灾域的隔离,能够实现各类负载的副本放置规则,例如跨机房、机架感知等。Crush算法有相当强大的扩展性,理论上支持数千个存储节点
高性能:Ceph中的数据副本数量可以由管理员自行定义,并可以通过CRUSH算法指定副本的物理存储位置以分隔故障域,支持数据强一致性; ceph可以忍受多种故障场景并自动尝试并行修复
高扩展性:Ceph本身并没有主控节点,扩展起来比较容易,并且理论上,它的性能会随着磁盘数量的增加而线性增长
特性丰富:Ceph支持三种调用接口:对象存储,块存储,文件系统挂载。三种方式可以一同使用。在国内一些公司的云环境中,通常会采用ceph作为openstack的唯一后端存储来提升数据转发效率
中文学习网:http://docs.ceph.org.cn/
Ceph架构:
组件对象讲解:
RADOS:就是这样一个可用于PB级规模数据存储集群的可伸缩的、可靠的对象存储服务,可以理解成Ceph的整个存储对象,包括逻辑对象
File:用户上传的文件
object:上传的文件被切成N个小文件块对象,RADOS的基本存储单元
MDS:元数据的内存缓存,为了加快元数据的访问
CRUSH:Ceph寻址算法,用于计算当前文件存储到哪个PG对应的OSD中
PG:对object的存储进行组织和位置映射。具体而言,一个PG负责组织若干个object(可以为数千个甚至更多),但一个object只能被映射到一个PG中,即,PG和object之间是“一对多”映射关系。同时,一个PG会被映射到n个OSD上,而每个OSD上都会承载大量的PG,即,PG和OSD之间是“多对多”映射关系
OSD:RADOS中的存储节点被称为OSD
架构图讲解:
1.文件上传,先将文件切片成N个object(如果开启了cephFS,可以使用MDS缓存)
2.切片后的文件object会存入到Ceph中
3.文件存储前,会经过CRUSH算法,计算当前文件存储归结于哪个PG
4.PG是逻辑概念上对文件存储范围划分的索引
5.根据PG索引将文件存储到指定服务器的OSD中
1.2 Ceph集群搭建
集群结构如上图,server1作为主节点(Dashbaord、mon、mds、rgw、mgr、osd),server2和server3作为子节点(mon、mds、rgw、mgr、osd)
节点中信息说明:
dashbaord:Ceph可视化管理界面
rgw:RADOSGW,Ceph对象网关,使客户端能够利用标准对象存储API来访问Ceph集群
mgr:ceph-mgr,主要目标实现 ceph 集群的管理,为外界提供统一的入口
节点信息:
server1:192.168.100.131
server2:192.168.100.132
server3:192.168.100.133
1.2.1 准备工作
1)机器名称修改(131、132、133都执行)
我们给每台机器一个别名192.168.100.131
->CENTOS1,192.168.100.132
->CENTOS2,192.168.100.133
->CENTOS3
修改192.168.100.131
/etc/hostname,添加CENTOS1
修改192.168.100.(同上)
/etc/hostname,添加CENTOS2(同上)
修改192.168.100.133
/etc/hostname,添加CENTOS3(同上)
配置名字解析IP:分别修改131
、132
、133
的/etc/hosts
文件,添加如下映射:
192.168.100.131 CENTOS1
192.168.100.132 CENTOS2
192.168.100.133 CENTOS3
2)YUM源修改(131、132、133都执行)
这里采用清华镜像源,提升加载速度
vi /etc/yum.repos.d/ceph.repo
,添加如下内容:
[Ceph]
name=Ceph packages for $basearch
baseurl=https://mirrors.tuna.tsinghua.edu.cn/ceph/rpm-mimic/el7/x86_64/
enabled=1
gpgcheck=1
type=rpm-md
gpgkey=https://download.ceph.com/keys/release.asc[Ceph-noarch]
name=Ceph noarch packages
# 清华源
baseurl=https://mirrors.tuna.tsinghua.edu.cn/ceph/rpm-mimic/el7/noarch/
enabled=1
gpgcheck=1
type=rpm-md
gpgkey=https://download.ceph.com/keys/release.asc[ceph-source]
name=Ceph source packages
baseurl=https://mirrors.tuna.tsinghua.edu.cn/ceph/rpm-mimic/el7/SRPMS/
enabled=1
gpgcheck=1
type=rpm-md
gpgkey=https://download.ceph.com/keys/release.asc
3)ceph与ceph-deploy安装(131)
更新yum源,并安装ceph
和ceph-deploy
,这个过程非常耗时间,执行如下命令:
yum update && yum -y install ceph ceph-deploy
注意:yum update
和yum -y install python2-pip
最好在每台机器都更新下yum。
安装过程中, 如果执行ceph-deploy出现ImportError: No module named pkg_resources
,则需要安装python2-pip,执行yum -y install python2-pip
安装即可
如果遇到如下错误,安装下epel即可
操作命令:(最好先执行该命令)
yum install epel-release -yyum install lttng-ust -y
4)NTP时间同步工具(131执行)
为了保证时间同步,我们需要安装NTP时间同步工具:
yum install ntp ntpdate ntp-doc -y
设为开机启动:
systemctl enable ntpd
设置每隔1小时自动校准同步。编辑 vi /etc/rc.d/rc.local
追加:
/usr/sbin/ntpdate ntp1.aliyun.com > /dev/null 2>&1; /sbin/hwclock -w
配置定时任务, 执行crontab -e 加入:
0 */1 * * * ntpdate ntp1.aliyun.com > /dev/null 2>&1; /sbin/hwclock -w
5)免密配置(131、132、133都执行)
官方建议不用系统内置用户, 创建名为cuser用户, 密码也设为cuser:
useradd -d /home/cuser -m cuser
passwd cuser
设置sudo权限:(免密+只读权限)
echo "cuser ALL = (root) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/cuser
sudo chmod 0440 /etc/sudoers.d/cuser
6)生成秘钥:(131执行)
切换用户: su cuser
执行ssh-keygen,一直按默认提示点击生成RSA密钥信息。
分发密钥至各机器节点
ssh-copy-id cuser@CENTOS1
ssh-copy-id cuser@CENTOS2
ssh-copy-id cuser@CENTOS3
修改管理节点上的 ~/.ssh/config
(当前用户目录下的.ssh/config)文件, 简化SSH远程连接时的输入信息:
管理节点是会有root和cuser多个用户, ssh远程连接默认会以当前用户身份进行登陆, 如果我们是root身份进行远程连接, 还是需要输入密码,我们可以修改配置 使用root远程连接时也不用输入密码。
切换root身份,
su root
编辑config
vi ~/.ssh/config
添加如下内容:
Host CENTOS1Hostname CENTOS1User cuser
Host CENTOS2Hostname CENTOS2User cuser
Host CENTOS3Hostname CENTOS3User cuser
修改文件权限:
chmod 600 ~/.ssh/config
禁用SELINUX:
vi /etc/selinux/configSELINUX=disabled
1.2.2 集群搭建
安装集群,用root安装,可以避免很多权限问题。
1)创建集群管理目录,作为ceph配置信息存储目录
mkdir -p /usr/local/gupao/cephclustercd /usr/local/gupao/cephcluster
2)创建集群
ceph-deploy new CENTOS1 CENTOS2 CENTOS3
创建成功后, 会生配置文件和秘钥信息
3)修改配置文件
编辑ceph.conf文件vi /usr/local/gupao/cephcluster/ceph.conf
,添加如下配置:
#对外开放网段
public network = 192.168.100.0/24
# 设置pool池默认分配数量
osd pool default size = 2
# 容忍更多的时钟误差
mon clock drift allowed = 2
mon clock drift warn backoff = 30
# 允许删除pool
mon_allow_pool_delete = true
[mgr]
# 开启WEB仪表盘
mgr modules = dashboard
注意:Pool是存储对象的逻辑分区,它规定了数据冗余的类型和对应的副本分布策略
完整内容如下:
文件修改后执行安装(131执行),此时3台机器都会执行安装执行如下安装命令:
ceph-deploy install CENTOS1 CENTOS2 CENTOS3
如果出现ceph_deploy][ERROR ] RuntimeError: Failed to execute command: ceph --version
错误,可以直接在每个节点单独执行yum -y install ceph
进行单独安装。如果没有仓库文件ceph.repo, 按上面的步骤手工创建
4)初始化Monitor信息
ceph-deploy mon create-initial
此时会生成很多秘钥文件信息
5)同步管理信息
ceph-deploy admin CENTOS1 CENTOS2 CENTOS3
6)安装mgr(管理守护进程)
ceph-deploy mgr create CENTOS1 CENTOS2 CENTOS3
7)安装rgw
ceph-deploy rgw create CENTOS1 CENTOS2 CENTOS3
mds服务:
ceph-deploy mds create CENTOS1 CENTOS2 CENTOS3
注意:任意一个环节安装失败了,需要卸载重装:
ceph-deploy purge CENTOS1 CENTOS2 CENTOS3
ceph-deploy purgedata CENTOS1 CENTOS2 CENTOS3
ceph-deploy forgetkeys
将三台节点的mon信息也删除:
rm -rf /var/run/ceph/
如果出现错误:
ceph_deploy][ERROR ] RuntimeError: Failed to execute command: ceph --version
可以在各节点上单独进行安装:
yum -y install ceph
8)OSD安装
OSD服务是对象存储守护进程, 负责把对象存储到本地文件系统, 必须要有一块独立的磁盘作为存储。如果没有独立磁盘,怎么办? 可以在Linux下面创建一个虚拟磁盘进行挂载
添加磁盘:
执行fdisk -l
查看磁盘信息如下,我们需要添加一个磁盘,直接用VMware
添加即可。
使用VMware
选择设置->硬盘->添加,如下图:
一直点击下一步,设置磁盘空间大小为10G即可。操作完后重启虚拟机,并输入fdisk -l
查看磁盘信息如下,明显多了/dev/sdb
10G大小
执行创建OSD命令:(注意,每条命令都是在131中执行,不要在每台机器中单独执行)
ceph-deploy osd create --data /dev/sdb CENTOS1ceph-deploy osd create --data /dev/sdb CENTOS2ceph-deploy osd create --data /dev/sdb CENTOS3
Monitor查看
在/usr/bin
下执行./ceph -s
可以查看集群状态
可以执行ntpdate ntp1.aliyun.com
同步各个节点的时间
如果出现如下情况,执行systemctl restart ceph.target
重启每个节点即可(131,132,133都执行)
1.2.3 dashboard安装
Ceph 提供了原生的Dashboard功能,通过Dashboard可以获取Ceph集群的各种基本状态信息。我们接下来安装一下Dashboard,并使用它的功能
1)开启dashboard模块
ceph mgr module enable dashboard
2)生成签名
ceph dashboard create-self-signed-cert
3)创建目录
mkdir -p /usr/local/gupao/cephcluster/mgr-dashboard
4)生成密钥对
openssl req -new -nodes -x509 -subj "/O=IT/CN=ceph-mgr-dashboard" -days 3650 -keyout dashboard.key -out dashboard.crt -extensions v3_ca
5)启动dashboard
ceph mgr module disable dashboard
ceph mgr module enable dashboard
6)设置IP与PORT
ceph config set mgr mgr/dashboard/server_addr 192.168.100.131
ceph config set mgr mgr/dashboard/server_port 9001
7)关闭HTTPS
ceph config set mgr mgr/dashboard/ssl false
8)查看服务信息
ceph mgr services
9)设置管理员账号密码
ceph dashboard set-login-credentials admin admin
10)访问<https://192.168.100.131:8443/#/dashboard>
11)RGW访问
访问 http://192.168.100.131:7480/ 效果如下:
1.3 Cephfs管理
集群创建完后, 默认没有文件系统, 我们创建一个Cephfs可以支持对外访问的文件系统。
1)创建两个存储池, 执行两条命令:
ceph osd pool create cephfs_data 128
ceph osd pool create cephfs_metadata 64
少于5个OSD可把pg_num设置为128
OSD数量在5到10,可以设置pg_num为512
OSD数量在10到50,可以设置pg_num为4096
OSD数量大于50,需要计算pg_num的值
通过下面命令可以列出当前创建的存储池:
ceph osd lspools
2)创建fs, 名称为fs_test:
ceph fs new fs_test cephfs_metadata cephfs_data
3)状态查看, 以下信息代表正常
ceph fs ls
:
name: fs_test, metadata pool: cephfs_metadata, data pools: [cephfs_data ]
ceph mds stat
:
fs_test-0/0/1 up
4)fuse挂载
先确定ceph-fuse命令能执行, 如果没有, 则安装:
yum -y install ceph-fuse
创建挂载目录
mkdir -p /usr/local/gupao/cephfs_directory
挂载cephfs
ceph-fuse -k /etc/ceph/ceph.client.admin.keyring -m 192.168.100.131:6789 /usr/local/gupao/cephfs_directory
出现下面信息表示挂载成功了:
ceph-fuse[28003]: starting fuse
5)挂载信息查看
[root@CENTOS1 cephcluster]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 4.1G 0 4.1G 0% /dev
tmpfs 4.1G 0 4.1G 0% /dev/shm
tmpfs 4.1G 20M 4.1G 1% /run
tmpfs 4.1G 0 4.1G 0% /sys/fs/cgroup
/dev/mapper/centos-root 17G 2.0G 16G 12% /
/dev/sda1 1014M 189M 826M 19% /boot
tmpfs 4.1G 28K 4.1G 1% /var/lib/ceph/osd/ceph-0
tmpfs 838M 0 838M 0% /run/user/0
ceph-fuse 13G 0 13G 0% /usr/local/gupao/cephfs_directory
1.4 Ceph Swift API接口开发
Swift是由Rackspace开发的用来为云计算提供可扩展存储的项目。专注于对象存储, 并提供一套REST风格的Api来访问, 与Ceph强一致性不同, 它是最终一致性。两者都是优秀的开源项目, 并无明显优劣之分,在使用场景上有所不同, 如果是专注于对象存储, 那么可以选择swift即可满足需要, 如果还有块存储要求, 那么选择Ceph更为合适。这里选择Ceph, 因为通过网关可以适配兼容swift api, 同时在数据访问上具有较强的扩展性
1.4.1 准备工作
创建Swift用户, 用于接口请求认证
sudo radosgw-admin user create --subuser="cephtester:subtester" --uid="cephtester" --display-name="cephtester" --key-type=swift --secret="gupao" --access=full
uid 为主用户, subuser为子用户信息, secret指定密钥, 不指定则随机生成, access拥有权限设定,代码中需使用返回信息中的user和secret_key
swift_keys:
"swift_keys": [{"user": "cephtester:subtester","secret_key": "gupao"}
],
创建管理员账号:
radosgw-admin user create --uid=mgruser --display-name=mgruser --system
返回信息如下:
{"user_id": "mgruser","display_name": "mgruser","email": "","suspended": 0,"max_buckets": 1000,"auid": 0,"subusers": [],"keys": [{"user": "mgruser","access_key": "AZ6L40PH9WB37EKVVMCZ","secret_key": "rk8PEjtYaMTo7nMDM62hqqN1tOnZPBEe4GA0LQMW"}],"swift_keys": [],"caps": [],"op_mask": "read, write, delete","system": "true","default_placement": "","placement_tags": [],"bucket_quota": {"enabled": false,"check_on_raw": false,"max_size": -1,"max_size_kb": 0,"max_objects": -1},"user_quota": {"enabled": false,"check_on_raw": false,"max_size": -1,"max_size_kb": 0,"max_objects": -1},"temp_url_keys": [],"type": "rgw","mfa_ids": []
}
根据生成的access_key与secret_key, 执行:
ceph dashboard set-rgw-api-access-key AZ6L40PH9WB37EKVVMCZ
ceph dashboard set-rgw-api-secret-key rk8PEjtYaMTo7nMDM62hqqN1tOnZPBEe4GA0LQMW
打开管理界面,http://192.168.100.131:9001/#/rgw/user 可以查看到我们刚才创建的两个用户:
1.4.2 文件服务搭建
我们搭建一个单独的工程,专门用于实现文件上传和文件下载,工程坐标如下:
<groupId>com.gupaoedu.vip.mall</groupId>
<version>0.0.1-SNAPSHOT</version>
<artifactId>mall-file-service</artifactId>
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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>mall-service</artifactId><groupId>com.gupaoedu.vip.mall</groupId><version>0.0.1-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>mall-file-service</artifactId><description>文件上传微服务</description><dependencies><!-- Rados Java Api依赖 --><dependency><groupId>com.ceph</groupId><artifactId>rados</artifactId><version>0.6.0</version></dependency><!-- Cephfs 文件系统依赖 --><dependency><groupId>com.ceph</groupId><artifactId>libcephfs</artifactId><version>0.80.5</version></dependency><!--swift--><dependency><groupId>org.javaswift</groupId><artifactId>joss</artifactId><version>0.10.2</version></dependency></dependencies>
</project>
bootstrap.yml:
server:port: 8082
spring:application:name: mall-filecloud:nacos:config:file-extension: yamlserver-addr: 192.168.1.11:8848discovery:#Nacos的注册地址server-addr: 192.168.1.11:8848ceph:username: cephtester:subtester #Ceph配置 主用户名:子用户名password: gupao #秘钥authUrl: http://192.168.100.131:7480/auth/1.0 #接口访问路径defaultContainerName: user_datainfo #默认容器名字
#图片路径
cephurl: http://localhost:8082/file/download/#日志配置
logging:pattern:console: "%msg%n"
创建com.gupaoedu.vip.mall.file.ceph.ContainerConfig
配置类,在类中创建Account
和Container
对象,代码如下:
/*** 容器的初始化操作*/
@Data
@Configuration
@ConfigurationProperties(prefix = "ceph") //yaml配置文件中配置的属性
public class ContainerConfig {private String username;private String password;private String authUrl;private String defaultContainerName;/*** 1.创建账号信息*/@Beanpublic Account account(){AccountConfig config = new AccountConfig();//配置认证信息config.setUsername(username);config.setPassword(password);config.setAuthUrl(authUrl);//配置认证方式config.setAuthenticationMethod(AuthenticationMethod.BASIC);return new AccountFactory(config).createAccount();}/*** 2.创建容器对象*/@Beanpublic Container container(){//获取账号信息Container container = account().getContainer(defaultContainerName);if(!container.exists()){return container.create();}return container;}}
创建文件上传下载工具类com.gupaoedu.vip.mall.file.ceph.FileHandler
,代码如下:
@Component
public class FileHandler {@Autowiredprivate Container container;/*** 文件上传*/public void upload(String filename,byte[] buffer) {//buffer:文件内容字节//获取容器对象StoredObject object = container.getObject(filename);//文件上传object.uploadObject(buffer);}/** 文件下载*/public byte[] download(String filename){//获取容器中远程存储的信息StoredObject object = container.getObject(filename);//执行文件下载byte[] bytes = object.downloadObject();return bytes;}
}
控制器创建:com.gupaoedu.vip.mall.file.controller.FileController
@RestController
@RequestMapping(value = "/file")
public class FileController {@Autowiredprivate FileHandler fileHandler;@Value("${cephurl}")private String cephurl;/*** 文件上传*/@PostMapping(value = "/upload")public RespResult upload(MultipartFile file) throws IOException {//上传fileHandler.upload(file.getOriginalFilename(),file.getBytes());return RespResult.ok(cephurl+file.getOriginalFilename());}/*** 下载*/@GetMapping(value = "/download/{filename}")public void download(@PathVariable String filename, HttpServletResponse response) throws IOException {//下载byte[] bytes = fileHandler.download(filename);//输出文件ServletOutputStream os = response.getOutputStream();os.write(bytes);}
}
创建启动类:com.gupaoedu.vip.mall.file.MallFileApplication
/*** http://localhost:8082/file/download/news1.jpg*/
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//屏蔽掉数据库的设置,这里不需要用到数据库
public class MallFileApplication {public static void main(String[] args) {SpringApplication.run(MallFileApplication.class,args);}
}
文件上传测试http://localhost:8082/file/upload
文件下载测试http://localhost:8082/file/download/20200716-af9fc1ae9d24f880.png
2.Spu和Sku(产品发布)
2.1 掌握SKU和SPU关系
SPU = Standard Product Unit (标准化产品单元),SPU是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU
SKU=stock keeping unit(库存量单位),SKU即库存进出计量的单位, 可以是以件、盒、托盘等为单位。SKU是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理。在服装、鞋类商品中使用最多最普遍
举个例子:
购买手机的时候,你可以选择华为Mate40系列手机,Mate40系列手机的生产制造商是华为,品牌是华为,手机分类也是华为,不过Mate40系列手机有多款,比如 Mate40 、Mate40 Pro 、 Mate40 Pro +,每款手机的架构也不一样,颜色也不一定一样,那么这个例子中哪些是Spu哪些是Sku呢?
Spu:
手机系列:Mate40系列
厂家:华为
品牌:华为
分类:手机
Sku:价格、颜色、网络格式
2.2 表结构设计
2.2.1 Spu和Sku
spu:
CREATE TABLE `spu` (`id` varchar(60) NOT NULL COMMENT '主键',`name` varchar(100) DEFAULT NULL COMMENT 'SPU名',`intro` varchar(200) DEFAULT NULL COMMENT '简介',`brand_id` int(11) DEFAULT NULL COMMENT '品牌ID',`category_one_id` int(20) DEFAULT NULL COMMENT '一级分类',`category_two_id` int(10) DEFAULT NULL COMMENT '二级分类',`category_three_id` int(10) DEFAULT NULL COMMENT '三级分类',`images` varchar(1000) DEFAULT NULL COMMENT '图片列表',`after_sales_service` varchar(50) DEFAULT NULL COMMENT '售后服务',`content` longtext COMMENT '介绍',`attribute_list` varchar(3000) DEFAULT NULL COMMENT '规格列表',`is_marketable` int(1) DEFAULT '0' COMMENT '是否上架,0已下架,1已上架',`is_delete` int(1) DEFAULT '0' COMMENT '是否删除,0:未删除,1:已删除',`status` int(1) DEFAULT '0' COMMENT '审核状态,0:未审核,1:已审核,2:审核不通过',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
sku:
CREATE TABLE `sku` (`id` varchar(60) NOT NULL COMMENT '商品id',`name` varchar(200) NOT NULL COMMENT 'SKU名称',`price` int(20) NOT NULL DEFAULT '1' COMMENT '价格(分)',`num` int(10) DEFAULT '100' COMMENT '库存数量',`image` varchar(200) DEFAULT NULL COMMENT '商品图片',`images` varchar(2000) DEFAULT NULL COMMENT '商品图片列表',`create_time` datetime DEFAULT NULL COMMENT '创建时间',`update_time` datetime DEFAULT NULL COMMENT '更新时间',`spu_id` varchar(60) DEFAULT NULL COMMENT 'SPUID',`category_id` int(10) DEFAULT NULL COMMENT '类目ID',`category_name` varchar(200) DEFAULT NULL COMMENT '类目名称',`brand_id` int(11) DEFAULT NULL COMMENT '品牌id',`brand_name` varchar(100) DEFAULT NULL COMMENT '品牌名称',`sku_attribute` varchar(200) DEFAULT NULL COMMENT '规格',`status` int(1) DEFAULT '1' COMMENT '商品状态 1-正常,2-下架,3-删除',PRIMARY KEY (`id`),KEY `cid` (`category_id`),KEY `status` (`status`),KEY `updated` (`update_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品表';
2.2.2 商品发布流程分析
商品发布流程如下:
1.分类选择:三级树形结构
发布商品前,需要先选择发布商品所属分类,分类严格定义为3级分类,三级加载
分类表:
CREATE TABLE `category` (`id` int(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID',`name` varchar(50) DEFAULT NULL COMMENT '分类名称',`sort` int(11) DEFAULT NULL COMMENT '排序',`parent_id` int(20) DEFAULT NULL COMMENT '上级ID',PRIMARY KEY (`id`),KEY `parent_id` (`parent_id`)
) ENGINE=InnoDB AUTO_INCREMENT=11182 DEFAULT CHARSET=utf8 COMMENT='商品类目';
2.选择品牌:分类选择完成后,需要加载品牌,品牌加载并非一次性加载完成,而是根据选择的分类进行加载
分类品牌关系表:
CREATE TABLE `category_brand` (`category_id` int(11) NOT NULL COMMENT '分类ID',`brand_id` int(11) NOT NULL COMMENT '品牌ID',PRIMARY KEY (`brand_id`,`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
品牌表:
CREATE TABLE `brand` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '品牌id',`name` varchar(100) NOT NULL COMMENT '品牌名称',`image` varchar(1000) DEFAULT '' COMMENT '品牌图片地址',`initial` varchar(1) DEFAULT '' COMMENT '品牌的首字母',`sort` int(11) DEFAULT NULL COMMENT '排序',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COMMENT='品牌表';
3.属性加载:当选择分类后,加载分类对应的属性
分类属性表:
CREATE TABLE `category_attr` (`category_id` int(11) NOT NULL,`attr_id` int(11) NOT NULL COMMENT '属性分类表',PRIMARY KEY (`category_id`,`attr_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
属性表:
CREATE TABLE `sku_attribute` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',`name` varchar(50) DEFAULT NULL COMMENT '属性名称',`options` varchar(2000) DEFAULT NULL COMMENT '属性选项',`sort` int(11) DEFAULT NULL COMMENT '排序',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
2.3 商品发布加载功能
对应的Bean写好
Category
package com.gupaoedu.vip.mall.goods.model;@Data
@AllArgsConstructor
@NoArgsConstructor
//MyBatisPlus表映射注解
@TableName(value = "category")
public class Category implements Serializable {@TableId(type = IdType.AUTO)private Integer id;private String name;private Integer sort;private Integer parentId;
}
2.3.1 分类加载
分类功能需要实现按照父ID查询,最开始初始化加载的是顶级父类,parent_id=0,后面每次点击的时候都根据传入的id查询子分类
1)Mapper
创建com.gupaoedu.vip.mall.goods.mapper.CategoryMapper
,代码如下:
public interface CategoryMapper extends BaseMapper<Category> {
}
2)Service
接口:com.gupaoedu.vip.mall.goods.service.CategoryService
代码如下:
public interface CategoryService extends IService<Category> {/*** 根据父ID查询子分类*/List<Category> queryByParentId(Integer pid);
}
实现类:com.gupaoedu.vip.mall.goods.service.impl.CategoryServiceImpl
代码如下:
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper,Category> implements CategoryService {@Autowiredprivate CategoryMapper categoryMapper;/**** 根据父ID查询子分类*/@Overridepublic List<Category> queryByParentId(Integer pid) {//条件封装QueryWrapper<Category> queryWrapper = new QueryWrapper<Category>();queryWrapper.eq("parent_id",pid);return categoryMapper.selectList(queryWrapper);}
}
3)Controller
创建com.gupaoedu.vip.mall.goods.controller.CategoryController
代码如下;
在SpringMVC使用@CrossOrigin注解来解决跨域问题
@RestController
@RequestMapping(value = "/category")
@CrossOrigin //允许跨域
public class CategoryController {@Autowiredprivate CategoryService categoryService;/***** 根据父ID查询子分类*/@GetMapping(value = "/parent/{pid}")public RespResult<List<Category>> list(@PathVariable(value = "pid")Integer pid){List<Category> categories = categoryService.queryByParentId(pid);return RespResult.ok(categories);}
}
2.3.2 品牌加载
品牌需要根据分类进行加载,当用户选择第3级分类的时候,加载品牌,品牌数据需要经过category_brand
表关联查询
我们可以按照如下步骤实现:
1、查询category_brand中指定分类对应的品牌ID集合
2、从brand查出品牌集合
1)Mapper
修改com.gupaoedu.vip.mall.goods.mapper.BrandMapper
,添加根据分类ID查询品牌ID集合:
public interface BrandMapper extends BaseMapper<Brand> {//根据分类ID查询品牌集合@Select("select brand_id from category_brand where category_id=#{id}")List<Integer> queryBrandIds(Integer id);
}
2)Service
接口:com.gupaoedu.vip.mall.goods.service.BrandService
中添加根据分类ID查询品牌集合方法
//根据分类ID查询品牌
List<Brand> queryByCategoryId(Integer id);
实现类:com.gupaoedu.vip.mall.goods.service.impl.BrandServiceImpl
/**** 根据分类ID查询品牌*/
@Override
public List<Brand> queryByCategoryId(Integer id) {//查询分类ID对应的品牌集合List<Integer> brandIds = brandMapper.queryBrandIds(id);//根据品牌ID集合查询品牌信息List<Brand> brands = brandMapper.selectBatchIds(brandIds);return brands;
}
3)Controller
修改com.gupaoedu.vip.mall.goods.controller.BrandController
添加根据分类ID查询品牌集合
/***** 根据分类ID查询品牌*/
@GetMapping(value = "/category/{id}")
public RespResult<List<Brand>> categoryBrands(@PathVariable(value = "id")Integer id){List<Brand> brands = brandService.queryByCategoryId(id);return RespResult.ok(brands);
}
2.3.3 属性加载
属性也称为规格,属性也需要根据分类查询,我们可以按照如下思路实现:
1、先从category_attr根据分类ID查询出当前分类拥有的属性ID集合
2、从sku_attribute中查询属性集合
1)Mapper
创建com.gupaoedu.vip.mall.goods.mapper.SkuAttributeMapper
实现根据分类ID查询属性信息。
public interface SkuAttributeMapper extends BaseMapper<SkuAttribute> {/**** 根据分类ID查询属性集合*/@Select("SELECT * FROM sku_attribute WHERE id IN(SELECT attr_id FROM category_attr WHERE category_id=#{id})")List<SkuAttribute> queryByCategoryId(Integer id);
}
2)Service
接口:com.gupaoedu.vip.mall.goods.service.SkuAttributeService
添加根据分类ID查询属性集合方法
public interface SkuAttributeService extends IService<SkuAttribute> {//根据分类ID查询属性集合List<SkuAttribute> queryList(Integer id);
}
实现类:com.gupaoedu.vip.mall.goods.service.impl.SkuAttributeServiceImpl
添加如下实现方法
@Service
public class SkuAttributeServiceImpl extends ServiceImpl<SkuAttributeMapper, SkuAttribute> implements SkuAttributeService {@Autowiredprivate SkuAttributeMapper skuAttributeMapper;/**** 根据分类ID查询属性集合*/@Overridepublic List<SkuAttribute> queryList(Integer id) {return skuAttributeMapper.queryByCategoryId(id);}
}
3)Controller
创建com.gupaoedu.vip.mall.goods.controller.SkuAttributeController
,添加如下方法
@RestController
@RequestMapping(value = "/skuAttribute")
@CrossOrigin
public class SkuAttributeController {@Autowiredprivate SkuAttributeService skuAttributeService;/**** 根据分类ID查询属性集合*/@GetMapping(value = "/category/{id}")public RespResult<List<SkuAttributeController>> categorySkuAttributeList(@PathVariable(value = "id")Integer id){List<SkuAttribute> skuAttributes = skuAttributeService.queryList(id);return RespResult.ok(skuAttributes);}
}
2.3.4 测试
选择对应的三级分类
选择好分类,就会对应渲染出品牌和属性信息
2.4 商品发布
2.4.1 复合对象分析
商品发布,如上图,我们可以发现发布的商品信息包含Sku和Spu,因此我们应该在后端能有一个对象同时能接到Spu和多个Sku,方法有很多种,我们可以直接在Spu中写一个List<Sku>
,但这种方法不推荐,按照对象设计原则,对一个对象进行扩展时,尽量避免对原始对象造成改变,因此我们可以使用复合类,可以创建一个Prodcut
类,该类中有Spu也有List<Sku>
,代码如下:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {// Spuprivate Spu spu;// Skuprivate List<Sku> skus;
}
2.4.2 添加商品
添加商品的时候,我们需要保存Spu,同时需要添加多个Sku。我们可以在华为商城中看看真实电商中Sku名字特征,每次点击不同属性的时候,前部分名字一样,只是将名字中的规格替换了,也就是说Sku的名字其实是组合成的,一部分是Spu的一部分是Sku的,可以进行组合
1.名字分析
添加商品的时候,会将商品的属性传入后台,格式如下,如果把规格名字添加到名字中,那就是华为商城中的效果了,我们可以这么做,把属性解析成Map,然后每个属性值添加到商品名字中即可
{"适合人群":"有一定java基础的人","书籍分类":"软件编程"}
2.实现代码
Mapper
com.gupaoedu.vip.mall.goods.mapper.SpuMapper
代码如下:
public interface SpuMapper extends BaseMapper<Spu> {
}
com.gupaoedu.vip.mall.goods.mapper.SkuMapper
代码如下:
public interface SkuMapper extends BaseMapper<Sku> {
}
Service
com.gupaoedu.vip.mall.goods.service.SpuService
中添加产品方法如下
public interface SpuService extends IService<Spu> {//保存商品void saveProduct(Product product);
}
com.gupaoedu.vip.mall.goods.service.impl.SpuServiceImpl
中添加产品方法如下:
@Service
public class SpuServiceImpl extends ServiceImpl<SpuMapper,Spu> implements SpuService {@Autowiredprivate SkuMapper skuMapper;@Autowiredprivate SpuMapper spuMapper;@Autowiredprivate CategoryMapper categoryMapper;@Autowiredprivate BrandMapper brandMapper;// 保存商品@Overridepublic void saveProduct(Product product) {//SpuSpu spu = product.getSpu();//上架spu.setIsMarketable(1);//未删除spu.setIsDelete(0);//状态spu.setStatus(1);//添加spuMapper.insert(spu);//查询三级分类Category category = categoryMapper.selectById(spu.getCategoryThreeId());//查询品牌Brand brand = brandMapper.selectById(spu.getBrandId());//当前时间Date now = new Date();//新增Sku集合for (Sku sku : product.getSkus()) {//设置名字String skuName = spu.getName();//{"适合人群":"有一定java基础的人","书籍分类":"软件编程"}Map<String,String> attrMap = JSON.parseObject(sku.getSkuAttribute(), Map.class);for (Map.Entry<String, String> entry : attrMap.entrySet()) {skuName+= " "+entry.getValue();}sku.setName(skuName);//设置图片sku.setImages(spu.getImages());//设置状态sku.setStatus(1);//设置类目IDsku.setCategoryId(spu.getCategoryThreeId());//设置类目名称sku.setCategoryName(category.getName());//设置品牌IDsku.setBrandId(brand.getId());//设置品牌名称sku.setBrandName(brand.getName());//设置Spuidsku.setSpuId(spu.getId());//时间sku.setCreateTime(now);sku.setUpdateTime(now);//增加skuMapper.insert(sku);}}
}
Controller
创建com.gupaoedu.vip.mall.goods.controller.SpuController
,添加产品代码如下:
@RestController
@RequestMapping("/spu")
@CrossOrigin
public class SpuController {@Autowiredprivate SpuService spuService;/**** 保存*/@PostMapping(value = "/save")public RespResult save(@RequestBody Product product){//保存spuService.saveSpuAndSku(product);return RespResult.ok();}
}
2.4.3 产品修改
产品修改其实和产品添加几乎一致,只需要做小改动即可,实现步骤如下:
1、如果Spu的id值不为空,说明是修改操作
2、如果是修改操作,先删除之前对应的Sku集合
3、其他流程和添加商品一致
修改com.gupaoedu.vip.mall.goods.service.impl.SpuServiceImpl
的save
方法,代码如下:
源码如下:
@Override
public void saveProduct(Product product) {//SpuSpu spu = product.getSpu();//如果ID为空,则增加if(StringUtils.isEmpty(spu.getId())){//上架spu.setIsMarketable(1);//未删除spu.setIsDelete(0);//状态spu.setStatus(1);//添加spuMapper.insert(spu);}else{//ID 不为空,则修改spuMapper.updateById(spu);//删除之前的Sku记录skuMapper.delete(new QueryWrapper<Sku>().eq("spu_id",spu.getId()));}//查询三级分类Category category = categoryMapper.selectById(spu.getCategoryThreeId());//查询品牌Brand brand = brandMapper.selectById(spu.getBrandId());//当前时间Date now = new Date();//新增Sku集合for (Sku sku : product.getSkus()) {//设置名字String skuName = spu.getName();Map<String,String> attrMap = JSON.parseObject(sku.getSkuAttribute(), Map.class);for (Map.Entry<String, String> entry : attrMap.entrySet()) {skuName+= " "+entry.getValue();}sku.setName(skuName);//设置图片sku.setImages(spu.getImages());//设置状态sku.setStatus(1);//设置类目IDsku.setCategoryId(spu.getCategoryThreeId());//设置类目名称sku.setCategoryName(category.getName());//设置品牌IDsku.setBrandId(brand.getId());//设置品牌名称sku.setBrandName(brand.getName());//设置Spuidsku.setSpuId(spu.getId());//时间sku.setCreateTime(now);sku.setUpdateTime(now);//增加skuMapper.insert(sku);}
}
2.4.4 测试
输入对应的数据保存
相关文章:
![](https://i-blog.csdnimg.cn/direct/53782d26dc784eb3836266c89b9cc455.png#pic_center)
云商城--基础数据处理和分布式文件存储
第2章 基础数据处理和分布式文件存储 1.分布式文件存储系统Ceph学习 1).掌握Ceph架构 2).掌握Ceph组件 3).搭建Ceph集群(了解) 2.Ceph使用 1).基于Ceph实现文件上传 2).基于Ceph实现文件下载 3.SKU、SPU管理 1).掌握SKU和SPU关系 2).理解商品发…...
![](https://www.ngui.cc/images/no-images.jpg)
六十九:基于openssl实战验证RSA
RSA(Rivest-Shamir-Adleman)是一种非对称加密算法,广泛应用于数据加密和数字签名领域。在实际开发和学习过程中,理解 RSA 的工作原理和使用场景非常重要。本文将以 OpenSSL 工具为基础,通过实例操作来验证和理解 RSA 的…...
![](https://www.ngui.cc/images/no-images.jpg)
Three.js 用户交互:构建沉浸式3D体验的关键
文章目录 前言一、基本交互:鼠标与触摸事件二、高级交互:键盘控制与游戏手柄支持三、物理模拟与碰撞检测四、手势识别与多点触控五、增强现实(AR)与虚拟现实(VR)六、触觉反馈与震动效果七、语音控制八、眼球…...
![](https://www.ngui.cc/images/no-images.jpg)
Android车机DIY开发之学习篇(五)默认应用修改
Android车机DIY开发之学习篇(五)默认应用修改 android默认应用位置 sdk/packages/apps InitRC配置 应用安装的目录 /system/priv-app 该路径存放一些系统底层的应用,比如Setting,systemUI等。该目录中的app拥有较高的系统权限,而且如果要使…...
![](https://www.ngui.cc/images/no-images.jpg)
linux 设置mysql 外网访问
1、修改 MySQL 配置文件 找到并编辑配置文件:在Linux系统中,MySQL的配置文件通常是/etc/mysql/my.cnf,使用命令sudo vim /etc/mysql/my.cnf打开文件。 注释或修改 bindaddress:找到bindaddress 127.0.0.1,将其注释掉…...
![](https://www.ngui.cc/images/no-images.jpg)
SQL UNION 操作符
SQL UNION 操作符 SQL UNION 操作符用于合并两个或多个 SELECT 语句的结果集。它将多个结果集组合成一个单独的结果集,并去除重复的行。为了使用 UNION,每个 SELECT 语句必须具有相同的列数,并且对应列的数据类型必须兼容。 语法 SELECT c…...
![](https://www.ngui.cc/images/no-images.jpg)
c++ 17 constexpr
未来已来:从SFINAE到concepts #include <type_traits> #include <vector> #include <list> #include <iostream> // 一个通用的容器打印函数,支持任何带 begin()/end() 的容器 template<typename Container> …...
![](https://www.ngui.cc/images/no-images.jpg)
Java QueryWrapper groupBy自定义字段,以及List<Map>转List<Entity>
Java queryWrapper groupby自定义字段 String sql "data_id,(select value from lz_html a where a.data_id lz_html.data_id and class_nametest-item-status) status," "(select value from lz_html a where a.data_id lz_html.data_id and class_nametes…...
![](https://i-blog.csdnimg.cn/direct/c3b136c8cc67455eb1ec02b4d8fb2b0e.png)
【Rust自学】11.7. 按测试的名称运行测试
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 11.7.1. 按名称运行测试的子集 如果想要选择运行的测试,就将测试的名称(一个或多个)作为cargo test的…...
![](https://i-blog.csdnimg.cn/direct/8cd19cd732a84741abb6ab054adf5dc0.png)
Git:Cherry-Pick 的使用场景及使用流程
前面我们说了 Git合并、解决冲突、强行回退等解决方案 >> 点击查看 这里再说一下 Cherry-Pick功能,Cherry-Pick不是merge,只是把部分功能代码Cherry-Pick到远程的目标分支 git cherry-pick功能简介: git cherry-pick 是用来从一个分…...
![](https://www.ngui.cc/images/no-images.jpg)
Ubuntu 24.04 LTS系统安装Docker踩的坑
一开始我跟着Docker给出的官网文档 Ubuntu | Docker Docs 流程走,倒腾了两个多小时,遇到了各种坑,最后放弃了。在我们使用脚本安装Docker命令前,我们先把已经安装的Docker全部卸载掉。 卸载Docker 1.删除docker及安装时自动安装…...
![](https://www.ngui.cc/images/no-images.jpg)
工作生活的感悟
前言 这篇博客基本每年都更新,每年都有新的感悟,作为一个记录吧!以后按照年来记录 2022年 不经意间,已在职场耕耘数载,特此记录以作回顾。 无用之用,方为大用: 年岁渐长,愈发体会…...
![](https://i-blog.csdnimg.cn/direct/767beed36da949e0be7b5106c38f1268.png)
NCCL学习笔记-函数解析
前言 1.NCCL 是一个专注于 GPU 间高性能通信的库,不提供进程管理或安全通信功能。 2.用户需要依赖应用程序的进程管理系统(如 MPI)来管理进程,并确保 NCCL 在安全的网络环境中运行。 3.通过正确配置环境变量(如 NCCL_…...
![](https://i-blog.csdnimg.cn/direct/2b91c70a34b8473c8a26f4787ac7345f.png)
windows系统如何将基座大模型私有化部署
1.windows10系统 安装npm、node、 git 最新版本 安装vmware虚拟机 内存8GB以上 双核4线程 2.vmware虚拟机 安装ubuntu系统 22.04版本 3.进入ubuntu系统 3.1 安装Ollama 基座大模型工具 在命令行中执行 curl -fsSL https://ollama.com/install.sh | sh 浏览器打开 …...
![](https://i-blog.csdnimg.cn/direct/f21c1283977a4f16b2ea0d19c91f5905.png)
牛客网刷题 ——C语言初阶(6指针)——BC106 上三角矩阵判定
1. 题目描述——BC106 上三角矩阵判定 牛客网OJ题链接 描述 KiKi想知道一个n阶方矩是否为上三角矩阵,请帮他编程判定。上三角矩阵即主对角线以下的元素都为0的矩阵,主对角线为从矩阵的左上角至右下角的连线。 示例 输入: 3 1 2 3 0 4 5 0 0…...
![](https://i-blog.csdnimg.cn/direct/217cd5f1b6f54031b383139259c5fba6.png)
CentOS 7 下 MySQL 5.7 的详细安装与配置
1、安装准备 下载mysql5.7的安装包 https://dev.mysql.com/get/mysql-5.7.29-1.el7.x86_64.rpm-bundle.tar 下载后上传至/home目录下 2、mysql5.7安装 2.1、更新yum并安装依赖 yum update -y sudo yum install -y wget sudo yum install libaio sudo yum install perl su…...
![](https://i-blog.csdnimg.cn/direct/b4e2a0c0a2be4c59a3059e718a59788e.png)
【深度学习】数据预处理
为了能用深度学习来解决现实世界的问题,我们经常从预处理原始数据开始, 而不是从那些准备好的张量格式数据开始。 在Python中常用的数据分析工具中,我们通常使用pandas软件包。 像庞大的Python生态系统中的许多其他扩展包一样,pan…...
![](https://i-blog.csdnimg.cn/blog_migrate/f5e62f99d78efcc273a160465d5b327a.png)
day01-HTML-CSS——基础标签样式表格标签表单标签
目录 此篇为简写笔记下端1-3为之前笔记(强迫症、保证文章连续性)完整版笔记代码模仿新浪新闻首页完成审核不通过发不出去HTMLCSS1 HTML1.1 介绍1.1.1 WebStrom中基本配置 1.2 快速入门1.3 基础标签1.3.1 标题标签1.3.2 hr标签1.3.3 字体标签1.3.4 换行标…...
![](https://www.ngui.cc/images/no-images.jpg)
无需昂贵GPU:本地部署开源AI项目LocalAI在消费级硬件上运行大模型
无需昂贵GPU:本地部署开源AI项目LocalAI在消费级硬件上运行大模型 随着人工智能技术的快速发展,越来越多的AI模型被广泛应用于各个领域。然而,运行这些模型通常需要高性能的硬件支持,特别是GPU(图形处理器)…...
![](https://i-blog.csdnimg.cn/direct/8109ad707f7c48488710874a9bedcb5a.png)
搭建prometheus+grafana监控系统抓取Linux主机系统资源数据
Prometheus 和 Grafana 是两个非常流行的开源工具,通常结合使用来实现监控、可视化和告警功能。它们在现代 DevOps 和云原生环境中被广泛使用。 1. Prometheus 定义:Prometheus 是一个开源的系统监控和告警工具包,最初由 SoundCloud 开发&am…...
![](https://i-blog.csdnimg.cn/direct/5d319afb0118475dbb074551cd1dbd11.png)
uni-app无限级树形组件简单实现
因为项目一些数据需要树形展示,但是官网组件没有。现在简单封装一个组件在app中使用,可以无线嵌套,展开,收缩,获取子节点数据等。 简单效果 组件TreeData <template><view class"tree"><te…...
![](https://i-blog.csdnimg.cn/direct/ec296943668244dd84cef6d21c966b2f.gif)
基于华为ENSP的OSPF状态机、工作过程、配置保姆级别详解(2)
本篇技术博文摘要 🌟 基于华为enspOSPF状态机、OSPF工作过程、.OSPF基本配置等保姆级别具体详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法 引言 📘 在这个快速发展的技术时代,与时俱进是每个IT人的必修课。我…...
![](https://i-blog.csdnimg.cn/direct/b2581780598343708022bf79a607329b.png)
请求方式(基于注解实现)
1.编写web.xml文件配置启动信息 <!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app><display-name>Archetype Created Web Application</di…...
![](https://www.ngui.cc/images/no-images.jpg)
day38 tcp 并发 ,linux下的IO模型----IO多路复用
TCP 并发 由于tcp协议只能实现一对一的通信模式。为了实现一对多,有以下的的处理方式 1. 多进程 开销大 效率低 2. 多线程 创建线程需要耗时 3. 线程池 多线程模型创建线程耗时问题,提前创建 4. IO多路复用 在不创建进程和线程的前提下,对…...
![](https://i-blog.csdnimg.cn/img_convert/eb54d6000c9a516fc637e46b3b07b7a5.png)
更新Office后,LabVIEW 可执行程序生成失败
问题描述: 在计算机中,LabVIEW 开发的源程序运行正常,但在生成可执行程序时提示以下错误: A VI broke during the build process from being saved without a block diagram. Either open the build specification to include…...
![](https://i-blog.csdnimg.cn/img_convert/6133dbbc4d05238a4155d8adbe5bdf32.png)
重塑视频创作的格局!ComfyUI-Mochi本地部署教程
一、介绍 mochi是近期Genmo公司开源的先进视频生成模型,具有高保真运动和强大的提示遵循性。此模型的发布极大的缩小了闭源和开源视频生成系统之间的差距。 目前,视频生成模型与现实之间存在巨大差距。其中最影响视频生成的两个关键功能也就是运动质量和…...
![](https://i-blog.csdnimg.cn/direct/eb2bbdf65ba24809a65b524b166fb6d5.png)
如何理解机器学习中的非线性模型 ?
在机器学习中,非线性模型是指能够捕捉输入特征与输出之间复杂非线性关系的一类模型。与线性模型不同,非线性模型的假设更加灵活,因此可以更好地处理真实世界中复杂、多样的数据分布。以下是对非线性模型的理解: 1. 非线性模型的核…...
![](https://www.ngui.cc/images/no-images.jpg)
Web 品质样式表
《Web 品质样式表》是一个重要的指南,旨在帮助开发者提升网站的整体质量和用户体验。以下是一些关键点: 避免使用 <font> 标签:应使用 CSS 来设置显示网页上的字体尺寸。使用 <font> 标签会增加文档的规模,且使每次改…...
![](https://i-blog.csdnimg.cn/direct/c6547e2c764f4633b723b6c4adc49986.png)
计算机网络 笔记 数据链路层3(局域网,广域网,网桥,交换机)
局域网: LAN:在某一区域内由多台计算机互联成的计算机组,使用广播信道 特点: 覆盖范围有限:通常局限在几千米范围内,比如一栋办公楼、一个校园或一个工厂等相对较小的地理区域。 数据传输速率高:一般能达到 10Mbps…...
![](https://i-blog.csdnimg.cn/direct/5113826bdcce49c59969dd4ad2914931.png)
centos7.6 安装nginx 1.21.3与配置ssl
1 安装依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel2 下载Nginx wget http://nginx.org/download/nginx-1.21.3.tar.gz3 安装目录 mkdir -p /data/apps/nginx4 安装 4.1 创建用户 创建用户nginx使用的nginx用户。 #添加www组 # groupa…...
烟台H5网站设计公司/东莞网站建设推广技巧
点击上方“Github爱好者社区”,选择星标回复“资料”,获取小编整理的一份资料作者 l Hollis来源 l Hollis在我的博客和公众号中,发表过很多篇关于并发编程的文章,之前的文章中我们介绍过了两个在Java并发编程中比较重要的两个关键…...
![](/images/no-images.jpg)
毕业设计做网站难吗/关键词优化哪个好
| 来源:知乎无意间刷到的一篇文章https://zhuanlan.zhihu.com/p/998520592019.11.27 ~ 2019.12.27 入职字节整整一个月了,这是我人生中第一份实习,也是我职场生涯的第一步,真的很幸运能够加入字节这样一个扁平、年轻且…...
![](/images/no-images.jpg)
做网站用php吗/专业营销推广团队
转载:http://blog.csdn.net/lushuaiyin/article/details/7564876 对于js中eval()函数的理解 和 写一个函数trim() 去掉字符串左右空格。 trim()是参照了jquery的源码,你可以放心使用。 对于js中eval()函数的理解是本人心得不一定正确。 <!DOCTYPE HTM…...
![](/images/no-images.jpg)
jira confluence做网站/自动收录网
导读:电脑是一种高科技产品,它能够给工作、生活带来极大的方便,同时还具有娱乐功能。它操作一点都不费力,特别适合中老年人的生理特点。经常使用电脑可以健脑增智。电脑的操作需要手眼的配合,人的手指内有丰富的神经&a…...
![](https://p-blog.csdn.net/images/p_blog_csdn_net/x3dcn/EntryImages/20090918/vt_vr.jpg)
什么网站做广告效果好/常州网站优化
尽管很多深入使用者反馈,该软件无论是功能模块还是界面布局上都存在较多的Bug,但并不影响Virtools成为一款优秀的VR、GAME开发工具。 Virtools联机文档的《基本概念》中说过: "Virtools包括了创作软件、行为引擎、渲染引擎、Web播放器、…...
衡水建设局网站首页/视频推广一条多少钱
letswave教程:脑电数据图形绘制、批处理以及脚本生成1 单主题图形生成1.1 打开图形模块1.2 创建子图1.3 添加内容1.4 设置轴参数1.5 导出图形2 多主题图形生成2.1 打开图形模块2.2 创建子图2.3 添加内容2.4 设置轴参数3 批处理3.1 数据导入3.2 删除无用的通道3.3 过…...