从零开始搭建企业管理系统(七):RBAC 之用户管理
RBAC 之用户管理
- 创建表(Entity)
- 用户表
- 角色表
- 权限表
- 用户角色表
- 关系注解
- @ManyToMany
- 角色权限表
- 接口开发
- UserController
- UserService
- UserServiceImpl
- UserRepository
- 问题解决
- update 更新问题
- 懒加载问题
- JSON 循环依赖问题
根据上一小结对表的设计,我们开始编写代码实现权限管理模块的代码。
我们直接通过编写实体映射类,让JPA自动帮我们生成一下数据库表。
创建表(Entity)
首先通过 JPA 将需要用到的5张表都建好。
先将基础实体类修改一下
@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Schema(description = "基础实体")
public class BaseEntity {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Schema(description = "用户ID")private Long id;@Column(columnDefinition = "tinyint(1) NOT NULL COMMENT '状态'")@Schema(description = "状态")private Integer status;@Column(columnDefinition = "bit NOT NULL COMMENT '0正常,1删除'")@Schema(description = "逻辑删除")private Boolean isDelete;@CreatedBy@Column(updatable = false, columnDefinition = "varchar(32) COMMENT '创建用户'")@Schema(description = "创建用户", hidden = true)private String createUser;@LastModifiedBy@Column(columnDefinition = "varchar(32) COMMENT '修改用户'")@Schema(description = "修改用户", hidden = true)private String updateUser;@CreatedDate@Column(updatable = false, columnDefinition = "datetime COMMENT '创建时间'")@Schema(description = "创建时间", hidden = true)private Date createTime;@LastModifiedDate@Column(columnDefinition = "datetime COMMENT '修改时间'")@Schema(description = "修改时间", hidden = true)private Date updateTime;
}
将一些每个表都存在的共有属性抽离出来当作一个父类,需要这些字段的实体通过继承获得这些字段属性。
@MappedSuperclass
:标注了@MappedSuperclass的类将不是一个完整的实体类,他将不会映射到数据库表,但是他的属性都将映射到其子类的数据库字段中。
@Column
:用来标识实体类中属性与数据表中字段的对应关系。
updatable
:updatable属性表示 在使用“UPDATE”脚本插入数据时,是否需要更新该字段值。
columnDefinition
:表示创建表时,该字段创建的SQL语句,一般用于通过Entity生成表定义时使用。
用户表
先看一下表结构:
字段 | 类型 | 含义 |
---|---|---|
id | bigint(20) | 主键ID |
name | varchar(32) | 用户名 |
mobile | char(11) | 手机号 |
avatar | varchar(255) | 头像 |
varchar(50) | 邮箱 | |
password | varchar(12) | 密码 |
status | tinyint(1) | 状态:0正常,1锁定 |
is_delete | bit | 0删除,1未删除 |
last_login_time | datetime | 最后登录时间 |
create_time | datetime | 创建时间 |
create_user | varchar(32) | 创建用户 |
update_time | datetime | 更新时间 |
update_user | varchar(32) | 更新用户 |
UserEntity:
@Data
@Entity
@Table(name = "sys_user")
@Schema(description = "用户实体")
public class UserEntity extends BaseEntity implements Serializable {@Column(unique = true, columnDefinition = "varchar(32) NOT NULL COMMENT '名称'")@Schema(description = "用户名称")private String name;@Column(columnDefinition = "char(11) COMMENT '手机号'")@Schema(description = "手机号")private String mobile;@Column(columnDefinition = "varchar(255) COMMENT '头像'")@Schema(description = "头像")private String avatar;@Column(columnDefinition = "varchar(50) COMMENT '邮箱'")@Schema(description = "用户邮箱")private String email;@Column(columnDefinition = "varchar(12) NOT NULL COMMENT '密码'")@Schema(description = "用户密码")private String password;@Column(columnDefinition = "datetime COMMENT '最后登录时间'")@Schema(description = "最后登录时间")private Date lastLoginTime;
}
角色表
表结构:
字段 | 类型 | 含义 |
---|---|---|
id | bigint(20) | 主键ID |
name | varchar(32) | 角色名称 |
remark | varchar(255) | 备注 |
status | tinyint(1) | 状态 |
is_delete | bit | 0删除,1未删除 |
create_time | datetime | 创建时间 |
create_user | varchar(32) | 创建用户 |
update_time | datetime | 更新时间 |
update_user | varchar(32) | 更新用户 |
RoleEntity:
@Data
@Entity
@Table(name = "sys_role")
@Schema(description = "角色实体")
public class RoleEntity extends BaseEntity implements Serializable {@Column(unique = true, columnDefinition = "varchar(32) NOT NULL COMMENT '角色名称'")@Schema(description = "角色名称")private String name;@Column(columnDefinition = "varchar(500) COMMENT '备注'")@Schema(description = "备注")private String remark;}
权限表
表结构:
字段 | 类型 | 含义 |
---|---|---|
id | bigint(20) | 主键ID |
pid | bigint(20) | 父菜单ID,一级菜单为0 |
name | varchar(32) | 菜单名称 |
url | varchar(255) | 菜单URL |
perms | varchar(500) | 授权(多个用逗号分隔,如:user:list,user:create) |
type | tinyint(1) | 类型: 0目录, 1菜单, 2按钮 |
icon | varchar(50) | 菜单图标 |
sort | tinyint(2) | 排序 |
status | tinyint(1) | 状态 |
is_delete | bit | 0删除,1未删除 |
create_time | datetime | 创建时间 |
create_user | varchar(32) | 创建用户 |
update_time | datetime | 更新时间 |
update_user | varchar(32) | 更新用户 |
MenuEntity:
@Data
@Entity
@Table(name = "sys_menu")
@Schema(description = "菜单实体")
public class MenuEntity extends BaseEntity implements Serializable {@Column(columnDefinition = "bigint NOT NULL COMMENT '父菜单ID'")@Schema(description = "父菜单ID")private Long pid;@Column(unique = true, columnDefinition = "varchar(32) NOT NULL COMMENT '菜单名称'")@Schema(description = "菜单名称")private String name;@Column(columnDefinition = "varchar(255) COMMENT '菜单url'")@Schema(description = "url")private String url;@Column(columnDefinition = "varchar(500) COMMENT '授权(多个用逗号分隔,如:user:list,user:create)'")@Schema(description = "授权(多个用逗号分隔,如:user:list,user:create)")private String perms;@Column(columnDefinition = "tinyint(1) NOT NULL COMMENT '菜单类型:0目录,1菜单,2按钮'")@Schema(description = "菜单类型:0目录,1菜单,2按钮")private Byte type;@Column(columnDefinition = "varchar(50) COMMENT '菜单图标'")@Schema(description = "菜单图标")private String icon;@Column(columnDefinition = "tinyint(2) COMMENT '排序'")@Schema(description = "排序")private Integer sort;}
编写好3个entity,启动程序查看建表是否成功。控制台打印如下建表语句,同时查看数据库,发现表创建没有问题。
Hibernate: create table sys_menu (id bigint not null auto_increment,create_time datetime COMMENT '创建时间',create_user varchar(32) COMMENT '创建用户',is_delete bit NOT NULL COMMENT '0正常,1删除',status tinyint(1) NOT NULL COMMENT '状态',update_time datetime COMMENT '修改时间',update_user varchar(32) COMMENT '修改用户',icon varchar(50) COMMENT '菜单图标',name varchar(32) NOT NULL COMMENT '菜单名称',perms varchar(500) COMMENT '授权(多个用逗号分隔,如:user:list,user:create)',pid bigint NOT NULL COMMENT '父菜单ID',sort tinyint(2) COMMENT '排序',type tinyint(1) NOT NULL COMMENT '菜单类型:0目录,1菜单,2按钮',url varchar(255) COMMENT '菜单url',primary key (id)) engine=InnoDB
Hibernate: create table sys_role (id bigint not null auto_increment,create_time datetime COMMENT '创建时间',create_user varchar(32) COMMENT '创建用户',is_delete bit NOT NULL COMMENT '0正常,1删除',status tinyint(1) NOT NULL COMMENT '状态',update_time datetime COMMENT '修改时间',update_user varchar(32) COMMENT '修改用户',name varchar(32) NOT NULL COMMENT '角色名称',remark varchar(500) COMMENT '备注',primary key (id)) engine=InnoDB
Hibernate: create table sys_user (id bigint not null auto_increment,create_time datetime COMMENT '创建时间',create_user varchar(32) COMMENT '创建用户',is_delete bit NOT NULL COMMENT '0正常,1删除',status tinyint(1) NOT NULL COMMENT '状态',update_time datetime COMMENT '修改时间',update_user varchar(32) COMMENT '修改用户',avatar varchar(255) COMMENT '头像',email varchar(50) COMMENT '邮箱',last_login_time datetime COMMENT '最后登录时间',mobile char(11) COMMENT '手机号',name varchar(32) NOT NULL COMMENT '名称',password varchar(12) NOT NULL COMMENT '密码',primary key (id)) engine=InnoDB
同时还为name字段添加了索引,因为我们给name字段添加了 unique=true
表示name字段为唯一,jpa 会为这个字段添加索引。
Hibernate: alter table sys_menu add constraint UK_4kk1vl4bvpaho8ked9v4xkr9d unique (name)Hibernate: alter table sys_role add constraint UK_bqy406dtsr7j7d6fawi1ckyn1 unique (name)Hibernate: alter table sys_user add constraint UK_iic0kskryiymn15fg9bqpmw22 unique (name)
用户角色表
使用实体映射来创建单表很好理解,但是怎么创建关联表呢,我们可以通过Spring Data JPA关系映射中对表关系定义的注解来创建表的关系。
关系注解
注解 | 说 |
---|---|
@OneToOne | 定义表之间“一对一”的关系。 |
@OneToMany | 定义表之间“一对多”的关系。 |
@ManyToOne | 定义表之间“多对一”的关系。 |
@ManyToMany | 定义表之间“多对多”的关系。 |
@ManyToMany
我们之前分析了,我们的表之间的关系都是多对多的关系,所以我们使用 @ManyToMany
来定义表之间的关系,ManyToMany总是会使用中间关系连接表来存储关系,Jpa 会自动帮我们创建中间关联表。
@ManyToMany 注解用来定义具有多对多多重性的多值关联。
每个多对多关系都有两个方面,关系的拥有方和非拥有方。连接表(中间表)在关系的拥有方指定。
如果关联是双向的,任何一方都可以被指定为关系的拥有方。
如果关系是双向的,则关系的非拥有方必须使用 @ManyToMany 注解的 mappingBy 属性来指定拥有方的关系字段或属性。
user表作为主表,在 UserEntity 中添加如下字段
@ManyToMany
@JoinTable(name = "sys_user_role",joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
@Schema(description = "用户角色")
private List<RoleEntity> roles;
@JoinTable:@JoinTable 注解用于关联映射,它是在关联的拥有方进行配置。使用 @JoinTable 注解将创建一个连接表,也称为“中间表”。
name:中间连接表名称(sys_user_role),默认是主表_副表(user_role)
joinColumns:连接表主表外键(user_id)
inverseJoinColumns:连接表副表外键(role_id)
role表作为副表,需要使用@ManyToMany 注解的 mappingBy 属性来指定拥有方的关系字段或属性。
@ManyToMany(mappedBy = "roles")
@Schema(description = "角色包含的用户")
private List<UserEntity> users;
**mappedBy:**拥有关系的字段,单向关系不需要指定改属性;双向关系必须指定改属性的值。
添加好两个字段,启动看看是否能够成功创建表,控制台打印了如下sql,没有爆出表明表已经成功。
角色权限表
这个和用户角色表一模一样,直接上代码
@ManyToMany
@JoinTable(name = "sys_role_menu",joinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")},inverseJoinColumns = {@JoinColumn(name = "menu_id", referencedColumnName = "id")})
@Schema(description = "角色菜单")
private List<MenuEntity> menus;
@ManyToMany(mappedBy = "menus")
@Schema(description = "菜单包含的角色")
private List<RoleEntity> roles;
接口开发
先来梳理一下我们有哪些接口需要开发,现在只有 CRUD,后面根据需求慢慢加。
- 分页查询用户
- 根据名称查询用户
- 根据ID查询用户
- 新增|修改用户(批量)
- 删除用户
直接上代码了,毕竟都是简单的crud,容易出错的部分拿出讲一下就好了
UserController
@Tag(name = "用户管理")
@RestController
@RequestMapping("/sys/user")
public class UserController {@Resourceprivate UserService userService;@GetMapping("/page")@Operation(summary = "分页查询")public Page<UserEntity> page(int page, int size) {return userService.page(PageRequest.of(page - 1, size));}@GetMapping("/{id}")@Operation(summary = "根据用户ID查询用户")public UserEntity get(@PathVariable Long id) {return userService.get(id);}@GetMapping("/name/{name}")@Operation(summary = "根据用户名称查询用户")public UserEntity getByName(@PathVariable String name) {return userService.getByName(name);}@PostMapping@Operation(summary = "新增|修改用户")public void upsert(@RequestBody List<UserEntity> users) {userService.upsert(users);}@DeleteMapping("/{id}")@Operation(summary = "根据用户ID删除用户")public void delete(@PathVariable Long id) {userService.delete(id);}
}
UserService
public interface UserService {/*** 分页查询用户** @param pageable 分页参数* @return Page<UserEntity> 分页用户*/Page<UserEntity> page(Pageable pageable);/*** 根据ID查询用户** @param id 用户ID* @return UserEntity 用户信息*/UserEntity get(Long id);/*** 根据名称查询用户** @param name 用户名称* @return UserEntity 用户信息*/UserEntity getByName(String name);/*** 保存/更新用户** @param users 用户信息*/void upsert(List<UserEntity> users);/*** 删除用户** @param id 用户id*/void delete(Long id);}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService {@Resourceprivate UserRepository userRepository;@Overridepublic Page<UserEntity> page(Pageable pageable) {return userRepository.findAll(pageable);}@Overridepublic UserEntity get(Long id) {return userRepository.findById(id).orElse(null);}@Overridepublic UserEntity getByName(String name) {return userRepository.findByName(name);}@Overridepublic void upsert(List<UserEntity> users) {userRepository.saveAll(users);}@Overridepublic void delete(Long id) {userRepository.deleteById(id);}}
UserRepository
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long>, Serializable {/*** 根据用户名称查询用户* 这个相当与一个公式, findBy字段名称,jpa 就会自动实现查询,不用我们再写查询逻辑** @param name 用户名称* @return 用户信息*/UserEntity findByName(String name);}
简单的接口开发完了之后,其实还是存在了3个问题,接下来我们一一来解决。
问题解决
update 更新问题
jpa 中没有单独的 update 方案,他是通过 save 方法来进行更新的,如果 id 为空就是新增,不为空就是修改。
我们先通过新增方法新增一条数据,请求数据如下:
[{"status": 0,"isDelete": true,"name": "金克斯","mobile": "11011101111","avatar": "","email": "xm@xm.com","password": "123456","lastLoginTime": "2023-12-12 12:12:12"}
]
然后假设我们现在想修改密码为:666666,请求体如下:
[{"id": 1,"password": "666666"}
]
按理来说没有如何问题对吧,但是一般这个时候就有问题了,请看VCR。
我们发现请求失败,返回值500,这肯定是后端报错了。
{"code": 500,"message": "系统异常,请稍后重试","body": null
}
查看控制台日志,发现报错信息如下
java.sql.SQLIntegrityConstraintViolationException: Column 'is_delete' cannot be null
说的是字段 is_delete 不能为空,这是数据库的校验,但是我们没有修改这个字段呀,继续看打印的sql
selectue1_0.id,ue1_0.avatar,ue1_0.create_time,ue1_0.create_user,ue1_0.email,ue1_0.is_delete,ue1_0.last_login_time,ue1_0.mobile,ue1_0.name,ue1_0.password,ue1_0.status,ue1_0.update_time,ue1_0.update_user fromsys_user ue1_0 whereue1_0.id=?updatesys_user setavatar=?,email=?,is_delete=?,last_login_time=?,mobile=?,name=?,password=?,status=?,update_time=?,update_user=? whereid=?
ok,答案出来了,jpa 默认更新全部字段,先从数据库查询出这条数据,然后更新全部数据,这可不行,这也太费劲了,我们需要更新我们像更新的字段就行呀,这个可以通过在实体类上添加@DynamicUpdate注解,表示动态更新查询。
@Data
@Entity
@DynamicUpdate
@Table(name = "sys_user")
@Schema(description = "用户实体")
public class UserEntity extends BaseEntity implements Serializable {
重启测试一下,但是发现还是不行,还是更新了全部数据,这是因为jpa会把属性值为null也当成是修改,意思就是当我们没有传入字段时,比如此时 isDelete=null ,但是jpa以为我们要把这个 null 更新到数据库, 这个怎么解决呢,我们自己写一个工具类将属性值为null的数据过滤掉在进行 save 即可,是不是感觉很麻烦。。。
编写一个 JpaUtils:
public class JpaUtils {/*** 从 src 中复制不为 null 的字段到 target** @param src 源实体* @param target 目标实体*/public static void copyNotNullProperties(Object src, Object target) {BeanUtils.copyProperties(src, target, getNullPropertyNames(src));}/*** 获取实体中为null的属性名称** @param source 实体类* @return 需要过滤掉的属性名称*/public static String[] getNullPropertyNames(Object source) {final BeanWrapper src = new BeanWrapperImpl(source);return Arrays.stream(src.getPropertyDescriptors())// 获取每一个属性名称.map(FeatureDescriptor::getName)// 过滤掉属性值不为null的字段.filter(name -> src.getPropertyValue(name) == null).toArray(String[]::new);}}
修改实现类中的upsert方法
@Override
public void upsert(List<UserEntity> users) {users.forEach(user -> {Optional<UserEntity> userEntity = userRepository.findById(user.getId());if (userEntity.isPresent()) {// 修改JpaUtils.copyNotNullProperties(user, userEntity.get());userRepository.save(userEntity.get());} else {// 新增userRepository.save(user);}});
}
ok,到此这个问题解决。
懒加载问题
我们在查询用户时,会抛出异常
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com.xm.module.sys.entity.UserEntity.roles: could not initialize proxy - no Session]
意思是获取 roles 的时候是懒加载的,但是懒加载失败,因为找不到session,那我们就修改为及时加载
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "sys_user_role",joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
@Schema(description = "用户角色")
private List<RoleEntity> roles;
**fetch:**关联是应该延迟加载还是必须马上加载。EAGER 策略表示必须马上获取关联的实体,LAZY 策略表示用到关联对象时才去加载。默认值为 javax.persistence.FetchType.LAZY
JSON 循环依赖问题
我们先为用户关联上一个角色,然后再去查询该用户信息,会显示这个,这是因为循环依赖导致返回数据非常大,因为我们的 UserEntity 中包含了 RoleEntity,而 RoleEntity 中有包含有 UserEntity,就会导致这个问题。
后台报错:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError)]
解决方案:
在副表的字段上添加@JsonIgnore
注解,表示序列化的时候忽略这个子段。
@JsonIgnore
@ManyToMany(mappedBy = "roles")
@Schema(description = "角色包含的用户")
private List<UserEntity> users;@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "sys_role_menu",joinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")},inverseJoinColumns = {@JoinColumn(name = "menu_id", referencedColumnName = "id")})
@Schema(description = "角色菜单")
private List<MenuEntity> menus;
最后解决掉这些问题,查询OK,这个JPA的坑还是有点多,如果不清楚他的原理,很容易冒出一些奇怪的问题。
相关文章:

从零开始搭建企业管理系统(七):RBAC 之用户管理
RBAC 之用户管理 创建表(Entity)用户表角色表权限表用户角色表关系注解ManyToMany 角色权限表 接口开发UserControllerUserServiceUserServiceImplUserRepository 问题解决update 更新问题懒加载问题JSON 循环依赖问题 根据上一小结对表的设计࿰…...

phpstudy搭建WordPress教程
一、phpstudy新建配置WordPress 打开phpstudy,启动Apache(或者Nginx)和MySQL服务 来到数据库部分,点击[创建数据库],填写新建数据库的名称,用户名以及密码,完成后点击确认 来到网站部分&#x…...

typedef的使用
在C语言中,有一个关键字叫做typedef,有些人对此感到很疑惑。不熟悉此知识的同学都会对编程失去细心,直接劝退(因为之前我就是这样)。、 因为好不容易认识了C语言中所有的关键字(就是类型吧,像啥…...

服务器系统启动卡logo问题解决分析
系统版本:Kylin-4.0.2-server-sp4-20200808.J1-57s-xxc_01-20210112-x86_64 问题:系统启动一直卡在麒麟logo那里,进不去系统。在gurb里去掉系统启动参数quiet和splash,发现启动卡在如下界面。 排查:开始怀疑是掉电导致…...

Linux NAPI ------------- epoll边缘触发模式
Linux处理网络数据包的一般流程 分组到达内核的时间是不可预测的。所有现代的设备驱动程序都使用中断来通知内核有分组到达。 网络驱动程序对特定于设备的中断设置了一个处理例程,因此每当该中断被引发时(即分组到达),内核都调用…...
使用poi-tl填充word模板,并转化为pdf输出
后端 依赖 <dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.12.0</version> </dependency>Word版本 Word版本填充代码 // 培训详情HashMap<String, Object> textMap new Ha…...
计算机视觉-机器学习-人工智能 顶会会议召开地址
计算机视觉-机器学习-人工智能 顶会会议召开地址 最近应该要整理中文资料的参考文献,很多会议文献都需要补全会议地点(新国标要求)。四处百度感觉也挺麻烦的,而且没有比较齐全的网站可以搜索。因此自己整理了一下计算机视觉-机器…...

GAN的原理分析与实例
为了便于理解,可以先玩一玩这个网站:GAN Lab: Play with Generative Adversarial Networks in Your Browser! GAN的本质:枯叶蝶和鸟。生成器的目标:让枯叶蝶进化,变得像枯叶,不被鸟准确识别。判别器的目标&…...

什么是POM设计模式?
为什么要用POM设计模式 前期,我们学会了使用PythonSelenium编写Web UI自动化测试线性脚本 线性脚本(以快递100网站登录举栗): import timefrom selenium import webdriver from selenium.webdriver.common.by import Bydriver …...

没有数据线,在手机上查看电脑备忘录怎么操作
在工作中,电脑和手机是我最常用的工具。我经常需要在电脑上记录一些重要的工作事项,然后又需要在手机上查看这些记录,以便随时了解工作进展。但是,每次都需要通过数据线来传输数据,实在是太麻烦了。 有一次࿰…...

Elasitcsearch--解决CPU使用率升高
原文网址:Elasitcsearch--解决CPU使用率升高_IT利刃出鞘的博客-CSDN博客 简介 本文介绍如何解决ES导致的CPU使用率升高的问题。 问题描述 线上环境 Elasticsearch CPU 使用率飙升常见问题如下: Elasticsearch 使用线程池来管理并发操作的 CPU 资源。…...
vue和jQuery有什么区别
Vue 和 jQuery 是两种不同类型的前端工具,它们有一些显著的区别: Vue 响应式数据绑定:Vue 提供了双向数据绑定和响应式更新的能力,使得数据与视图之间的关系更加直观和易于维护。组件化开发:Vue 鼓励使用组件化的方式…...
[Android] Binder all-in-all
前言: Binder 是一种 IPC 机制,使用共享内存实现进程间通讯,既可以传递消息,也可以传递创建在共享内存中的对象,而Binder本身就是用共享内存实现的,因此遵循Binder写法的类是可以实例化后在进程间传递的。…...

无人零售柜:快捷舒适购物体验
无人零售柜:快捷舒适购物体验 通过无人零售柜和人工智能技术,消费者在购物过程中可以自由选择商品,根据个人需求和喜好查询商品清单。这种自主选择的购物环境能够为消费者提供更加舒适和满意的体验。此外,无人零售柜还具有节约时间…...
Bash script进阶笔记
数组类型 arr(1 2 3) # 最基础的方式声明数组,用小括号(),元素之间逗号分隔 arr([1]10 [2]20 [3]30) # 初始化时指定index declare -a arr(1 2 3) # 用declare -a声明数组,小括号外面可选使用单引号、双引号 declare -a arr‘(1 2 3)’…...
OpenCV图像处理——Python开发中OpenCV视频流的多线程处理方式
前言 在做视觉类项目中,常常需要在Python环境下使用OpenCV读取本地的还是网络摄像头的视频流,之后再调入各种模型,如目标分类、目标检测,人脸识别等等。如果使用单线程处理,很多时候会出现比较严重的时延,…...

webGL开发智慧城市流程
开发智慧城市的WebGL应用程序涉及多个方面,包括城市模型、实时数据集成、用户界面设计等。以下是一个一般性的流程,您可以根据项目的具体需求进行调整,希望对大家有所帮助。 1.需求分析: 确定智慧城市应用程序的具体需求和功能。考…...

Django讲课笔记02:Django环境搭建
文章目录 一、学习目标二、相关概念(一)Python(二)Django 三、环境搭建(一)安装Python1. 从官方网站下载最新版本的Python2. 运行安装程序并按照安装向导进行操作3. 勾选添加到路径复选框4. 完成安装过程5.…...

黑豹程序员-原生JS拖动div到任何地方-自定义布局
效果图 代码html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html xmlns"http://www.w3.org/1999/xhtml"> <head> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /…...
<软考高项备考>《论文专题 - 7 论文的项目背景之技术架构》
1 技术架构概况 ➢ 架构前端:HTML ➢ 后端:Java ➢ 数据库: Oracle ➢ 大数据:MapReduce ➢ 人工智能:Python ➢ 物联网:RFID识别,http传输,Java ➢ 开发APP: IOS、Android 2 常用开发语言 序号语言说明1JavaJava是一种跨平台的编程语言,广…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...