苍穹外卖学习记录
苍穹外卖学习
文章目录
- 苍穹外卖学习
- 知识前提:
- **<font color="red">Nginx**
- **<font color="red">Swagger**
- 1.管理员登录
- 思路:
- 详细步骤:
- 1.1新增员工
- 问题1:在新增员工时,需要将当前登录人的id存入表中对应的创建人id和修改人id。
- 问题2:录入的用户名已存在时,抛出异常后并未处理,直接停止服务了。
- 1.2员工信息分页查询
- 问题1:分页查询时,Records中的时间与前端需要的不符
- 知识点:序列化和反序列化
- 1.3修改员工时信息回显
- 1.4 修改员工信息
- 2.删除分类注意事项
- 2.1公共字段自动填充
- 实现思路
- 2.2文件上传功能(阿里云OSS存储)
- 2.3新增菜品功能注意
- DTO/VO/DO/PO理解:
- 2.4分页查询sql
- 2.5删除菜品
- 4.1.3 表设计
- 2.5新增菜品接口
- 3.新增套餐
- 4.SpringCache缓存
知识前提:
Nginx
前端请求通过Nginx转发到后端服务器,从而实现反向代理。
Swagger
-
在配置类中加入 knife4j 相关配置
WebMvcConfiguration.java
/*** 通过knife4j生成接口文档* @return */@Beanpublic Docket docket() {ApiInfo apiInfo = new ApiInfoBuilder().title("苍穹外卖项目接口文档").version("2.0").description("苍穹外卖项目接口文档").build();Docket docket = new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo).select().apis(RequestHandlerSelectors.basePackage("com.sky.controller")).paths(PathSelectors.any()).build();return docket;}
-
设置静态资源映射,否则接口文档页面无法访问
WebMvcConfiguration.java
/*** 设置静态资源映射* @param registry */ protected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); }
1.管理员登录
思路:
- 前端发起登录请求:前端通过 HTTP 请求向后端发送登录信息,包括用户名和密码。
- 后端接收并处理请求:后端接收请求,将前端传来的数据封装成
EmployeeLoginDTO
对象,并进行登录校验。 - 业务逻辑处理:在业务层进行具体的登录逻辑处理,包括数据库查询和密码校验。
- 生成 JWT 令牌:登录成功后,生成 JWT 令牌,并将其与管理员信息一起封装成
EmployeeLoginVO
对象。 - 响应结果:将封装好的
EmployeeLoginVO
对象作为响应返回给前端。
详细步骤:
- 前端发起登录请求
- 前端通过 POST 请求向后端发送管理员的登录信息(用户名和密码)。
- 后端接收并处理请求
- 控制层接收前端传来的登录信息,并将其封装成
EmployeeLoginDTO
对象。 - 调用业务层的登录方法进行校验。
- 控制层接收前端传来的登录信息,并将其封装成
MD5加密:password = DigestUtils.md5DigestAsHex(password.getBytes()); //MD5加密
业务逻辑处理
- 在业务层通过用户名查询数据库
- 判断用户名是否存在或锁定
- 接着进行密码校验,校验完毕后,成功返回管理员对象,否则报异常(用户名不存在、密码不对、账号被锁定)
生成 JWT 令牌
-
此时验证成功后,生成包含管理员信息的 JWT 令牌,格式是Map键值对
-
通过
yaml
文件的配置生成JWT令牌。通常 JWT 令牌会包含管理员的 ID 和一些其他必要的信息。
封装响应对象
- 将管理员信息和 JWT 令牌封装进
EmployeeLoginVO
对象中。 EmployeeLoginVO
类实现了Serializable
接口,确保对象可以被序列化。- 使用 Lombok 的
@Data
、@Builder
等注解简化对象的创建和使用。
这里VO
对象封装是使用了构造器模式和序列化接口等技术,使对象的创建和使用更方便高效。
1.1新增员工
问题1:在新增员工时,需要将当前登录人的id存入表中对应的创建人id和修改人id。
可以通过ThreadLocal进行单一的线程存储,存储的id实在JWT令牌解析后,直接存进Threadlocal,因为线程都是一致的,所以可以直接使用。
问题2:录入的用户名已存在时,抛出异常后并未处理,直接停止服务了。
这里可以看出,报错的异常名为SQLIntegrityConstraintViolationException
Duplicate entry ‘zhangsan’ for key ‘employee.idx_username’ 意思就是重复的用户名,所以就需要设置自定义异常
1.2员工信息分页查询
注意事项:
- 请求参数类型为Query,不是json格式提交,在路径后直接拼接。/admin/employee/page?name=zhangsan
- 返回数据中records数组中使用Employee实体类对属性进行封装。
思考:
- 通过PageHelper分页插件进行分页查询,在业务层方面使用前端传入的DTO对象,将当前页码和每页的大小传入分页插件,通过Mapper进行分页查询后,需要将指定数据传回前端。
- 例如Total(总记录数/总页数)和Records(当前页数据集合),此时page中已经完成分页查询并动态计算出当前页面数据和总页数记录,只需get、set即可传入PageResult
- PageResult是一个封装分页查询结果的类
问题1:分页查询时,Records中的时间与前端需要的不符
- 方法1:通过@JsonFormat进行时间格式规范化
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”)
- 方法2:通过在WebMvcConfiguration中扩展SpringMVC的消息转换器,统一对日期类型进行格式处理
/*** 扩展Spring MVC框架的消息转化器* @param converters*/protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {log.info("扩展消息转换器...");//创建一个消息转换器对象MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();//需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为json数据converter.setObjectMapper(new JacksonObjectMapper());//将自己的消息转化器加入容器中converters.add(0,converter);}
这里的JacksonObjectMapper是一个对象映射器,通过规范化定义时间格式确保日期和时间以特定的格式进行序列化和反序列化
知识点:序列化和反序列化
在这个场景中,序列化和反序列化指的是将 Java 对象转换为 JSON 格式的字符串(序列化),以及将 JSON 格式的字符串转换回 Java 对象(反序列化)的过程。
序列化 (Serialization)
- 序列化是将 Java 对象的状态信息转换为可以存储或传输的形式的过程。在这个例子中,序列化是将 Java 对象转换为 JSON 格式的字符串。
- 例如,如果你有一个 Employee 类,序列化会将 Employee 对象的信息转换为一个 JSON 字符串,如下所示:
Employee employee = new Employee("John Doe", 30, LocalDate.now());// 序列化为 JSON
String json = objectMapper.writeValueAsString(employee);
System.out.println(json);
输出的 JSON 字符串可能会类似于:
{"name": "John Doe","age": 30,"birthdate": "2024-08-14"
}
反序列化 (Deserialization)
反序列化是从序列化的表示形式中提取数据,并重新创建 Java 对象的过程。在这个例子中,反序列化是从 JSON 字符串中恢复出原始的 Java 对象。例如:
String json = “{“name”:“John Doe”,“age”:30,“birthdate”:“2024-08-14”}”;
// 反序列化为 Java 对象
Employee employee = objectMapper.readValue(json, Employee.class);
System.out.println(employee.getName());
这将输出:
John Doe
总结
序列化:将 Java 对象转换为 JSON 字符串。反序列化:将 JSON 字符串转换回 Java 对象。
自定义序列化器和反序列化器:通过 SimpleModule 添加自定义逻辑来处理特定类型的序列化和反序列化,例如日期和时间类型。
1.3修改员工时信息回显
通过点击当前员工修改按钮,获取该员工id,通过id查询数据库
1.4 修改员工信息
这里可以使用先前启用禁用员工账号的更新方法,万金油,通过参数值是null来排除修改的值,有值才会更修改,null直接跳过
省略类似的CRUD功能
2.删除分类注意事项
- 在删除分类之前判断是否有关联的菜品或套餐是为了保证数据的一致性和完整性。
- 如果直接删除一个被其他数据引用的分类,可能会导致数据库中的数据丢失或者出现“悬挂”引用的情况,即某些记录失去了与之相关的分类信息。
在实际应用中,这样的逻辑是非常重要的,因为它可以帮助开发者确保应用程序的数据一致性。例如,在餐饮管理系统中,如果一个分类被菜品或套餐引用,那么删除该分类可能会导致用户无法查看到这些菜品或套餐所属的分类信息,从而影响用户体验和系统的可靠性。因此,在删除分类之前进行这样的检查是非常必要的。
2.1公共字段自动填充
使用AOP切面编程,实现功能增强,来完成公共字段自动填充功能。
实现思路
在实现公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。在上述的问题分析中,我们提到有四个公共字段,需要在新增/更新中进行赋值操作, 具体情况如下:
序号 | 字段名 | 含义 | 数据类型 | 操作类型 |
---|---|---|---|---|
1 | create_time | 创建时间 | datetime | insert |
2 | create_user | 创建人id | bigint | insert |
3 | update_time | 修改时间 | datetime | insert、update |
4 | update_user | 修改人id | bigint | insert、update |
实现步骤:
1). 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
2). 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
@Slf4j
@Aspect //定义一个切面
@Component
public class AutoFillAspect {/*** 切入点*/@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")public void autoFillPointCut(){}/*** 前置通知,在通知中进行公共字段赋值*/@Before("autoFillPointCut()")public void autoFill(JoinPoint joinPoint){log.info("开始进行公共字段的自动填充...");//获取到当前被拦截的方法上和数据库操作类型MethodSignature signature = (MethodSignature)joinPoint.getSignature(); //方法签名对象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); //获得方法上的注解对象OperationType operationType = autoFill.value(); //获取数据库操作类型//获取到当前被拦截的方法的参数--实体对象Object[] args = joinPoint.getArgs();if(args==null|| args.length==0){return; //判断如果你更新修改的时候里面没有值,我就不要你了}Object entity = args[0];//准备赋值的数据LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();//根据当前不同的操作类型,为对应的属性通过反射来赋值if(operationType == OperationType.INSERT){try {//为4个公共字段赋值Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射为对象属性赋值setCreateTime.invoke(entity,now);setCreateUser.invoke(entity,currentId);setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);}catch (Exception e){e.printStackTrace();;}}else if(operationType == OperationType.UPDATE){try {//为2个公共字段赋值Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射为对象属性赋值setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);}catch (Exception e){e.printStackTrace();;}}}
}
理解:
- 通过给
AutoFillAspect
添加注解 @Aspect 说明这是一个切面类,这个 @Pointcut 是一个切入点,也就是作为拦截的,而execution
(定义切入点表达式)是设置拦截的范围是 com.sky.mapper 包下的所有类和所有方法, @annotation(com.sky.annotation.AutoFill) 表示它只拦截带有AutoFill
注解类型的方法。 - 此时,因为公共字段填充字段是需要在修改字段或者新增字段之前完成填充,所以是前置通知 @before , @Before(“autoFillPointCut()”) 这个参数就是告诉你,这个前置通知的方法是需要找范围和要求的,而这个范围就是这个切入点的范围和要求。
- 通过执行 autoFill 方法,这里参数是
joinPoint
,我认为这是获取的一个切入点,通过getSignature
获取当前切入点的方法签名,例如 update 方法,这只是方法签名,想要真正获取到方法上的注解,就需要进一步获取到该方法上的注解,所以使用getMethod
以及getAnnotation
去指定获取 AutoFill 自定义注解。 - 接下来获取到这个方法上的 AutoFill 注解了,但是我只需要这个数据库操作参数,所以我们可以发现它的
value
值正好就是数据库操作类型, autoFill 是以键值对存储这个操作类型的。 - 通过 getArgs 获取
joinPoint
的实体对象,此时这个实体对象其实就是前端获取到你输入的字段,比如新增的字段或者修改的字段,把它存进 args 里面,判断它非空且长度非零,然后将第一个元素赋给一个 Object 类的实体(其实里头最多就一个元素因为不考虑并发),然后进行准备赋值数据,使用 LocalDateTime.now() 和 BaseContext.getCurrentId() 来获取当前时间和当前操作人的ID。 - 判断操作类型,通过先前获取的数据库操作类型与 insert 和 update 进行比对。例子中是 update ,所以直接进入 else if 。这里通过反射机制获取实体类中的方法:自己定义一个
Method
对象setUpdateTime
,这个对象代表 entity 对象内的一个方法。 - 这个方法里面有两个参数: AutoFillConstant.SET_UPDATE_TIME 表示方法的名字,即 setUpdateTime 。 LocalDateTime.class 表示这个方法接受一个 LocalDateTime 类型的参数。通过
entity.getClass()
获取当前运行的类。再通过getDeclaredMethod
方法,我们获取到了当前实体类中声明的方法 setUpdateTime 。 - setUpdateTime.invoke(entity, now) 反射机制就反着看,
invoke
方法允许我们在运行时动态地调用对象的方法。我们实际上是调用了 entity 对象的 setUpdateTime 方法,并将当前时间 now 作为参数传递给这个方法。
3). 在 Mapper 的方法上加入 AutoFill 注解
2.2文件上传功能(阿里云OSS存储)
配置yaml文件,配置阿里云OSS的相关属性,包括endpoint、accessKeyId、accessKeySecret和bucketName,注意区分开发环境和生产环境的配置差异。创建AliOssProperties类,用@ConfigurationProperties
注解来自动读取这些配置以便后期用
其次,创建一个oss对象配置类,用来返回一个文件上传对象,这个对象里面存储了阿里云对应配置的属性。
接着创建一个AliOssUtil
工具类,里面有一个upload方法里面俩参数:字节数组和目标文件名,只需要搞懂,这个方法可以创建一个ossclient
实例,将oss属性参数存进去,因为存储路径由https://BucketName.Endpoint/ObjectName组成的,所以通过bucketName和endpoint以及最后的objectName获取到文件访问路径。
最后,在控制层进行上传业务,通过对应接口mapping进行上传操作,需要注意,在上传文件之前,需要注意文件名需要保证唯一性,通常的做法是使用UUID生成新的文件名,截取文件的后缀名,并保留原有的文件扩展名最后进行upload操作。
业务流程:
- 从
MultipartFile
对象中读取文件数据,使用getBytes()
方法获取文件的字节流。
生成唯一文件名:截取文件原始名称的扩展名,通常使用UUID来保证唯一性,生成一个新的文件名。 - 调用
aliOssUtil.upload(file.getBytes(), name)
方法上传文件。这里file.getBytes()
获取文件字节流,name
是新生成的唯一文件名。 - 上传成功,返回文件的访问路径,路径由
https://BucketName.Endpoint/ObjectName
组成。
2.3新增菜品功能注意
思考:初步分析新增菜品分为三部分:文件上传、新增菜品和新增菜品口味,所以新增的是两个表的数据
业务层逻辑是向菜品表和口味表插入数据,其中需要注意:
- DTO的类型转换和数据拷贝,插入菜品表是一行数据一行数据插入。
- 口味是有多种口味在一个属性内,就需要插入n条数据并判断非空。
- 口味表的dish_id就是菜品表的id,这里通过数据库的自动生成策略获取新插入菜品的ID,再将此ID设置到每个口味项的dishId上,并批量插入口味数据到dish_flavor表。
- 使用了@Transactional注解保证事务一致性,即菜品和口味的插入操作要么全部成功,要么全部失败。
DishMapper
接口定义了插入菜品数据的方法,并使用@AutoFill注解自动填充创建时间和更新时间等字段。对应XML文件提供了SQL语句实现插入操作,并启用useGeneratedKeys
返回自动生成的主键。
- useGeneratedKeys=“true”: 意思是Mybatis在执行完插入操作后,使用数据库自动生成的主键值填充返回的对象。
- keyProperty=“id”: 指定将自动生成的主键值填充到对象的哪个属性中。在这个例子中,id 是对象的主键属性名称。
DishFlavorMapper
接口用于操作口味数据,定义了批量插入口味数据的方法,其XML文件中通过foreach实现了一次性插入多条口味记录的功能。这里的separator表示分隔符为逗号,collection 属性用于指定集合名称,item表示集合中的每一个元素的别名。
DTO/VO/DO/PO理解:
通俗的解释:领域模型中的实体类分为四种模型:VO、DTO、DO和PO
VO:(View Object):视图对象,用于展示层。
DTO(Data Transfer Object):数据传输对象在后端,它的存在形式是java对象,也就是在controller里面定义的请求参数
DO(Domain Object):领域对象,它用来接收数据库对应的实体,是一种抽象化的数据状态,介于数据库与业务逻辑之间
PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系。
BO (Business Object) :业务对象 BO通常位于中间层或业务逻辑层。BO支持序列化和反序列化,用来是把业务逻辑封装为一个对象
方向:后端–>前端
VO:前端页面显示使用的数据,是后端传递给前端的。
方向:前端–>后端
DTO:前端调用后端接口的时候传递给后端
DO:controller中接收到DTO之后,新建一个DO传递给service,
PO:service接收到传递的DO之后,转换成一个PO,传给mapper的方法,进行持久化处理。
BO:用于微服务之间传输数据
2.4分页查询sql
左外连接:返回的是VO对象,用到了左外连接,连接条件是将category表中id字段等同为dish表中的category_id字段,并根据VO对象属性参数查询dish表和category的name字段。
动态 SQL:使用 标签动态构建 WHERE 子句,根据传入的参数决定是否添加过滤条件。
2.5删除菜品
业务规则:
- 可以一次删除一个菜品,也可以批量删除菜品
- 起售中的菜品不能删除
- 被套餐关联的菜品不能删除
- 删除菜品后,关联的口味数据也需要删除掉
4.1.3 表设计
在进行删除菜品操作时,会涉及到以下三张表。
注意事项:
- 在dish表中删除菜品基本数据时,同时,也要把关联在dish_flavor表中的数据一块删除。
- setmeal_dish表为菜品和套餐关联的中间表。
- 若删除的菜品数据关联着某个套餐,此时,删除失败。
- 若要删除套餐关联的菜品数据,先解除两者关联,再对菜品进行删除。
删除代码优化:
2.5新增菜品接口
1.根据id查询菜品:
2.修改菜品
3.新增套餐
接口设计(共涉及到4个接口):
- 根据类型查询分类(已完成)
- 根据分类id查询菜品
- 图片上传(已完成)
- 新增套餐
业务规则:
- 套餐名称唯一
- 套餐必须属于某个分类
- 套餐必须包含菜品
- 名称、分类、价格、图片为必填项
- 添加菜品窗口需要根据分类类型来展示菜品
- 新增的套餐默认为停售状态
4.SpringCache缓存
其中store是cacheManager缓存,默认存储的一个HashMap类型数据
相关文章:

苍穹外卖学习记录
苍穹外卖学习 文章目录 苍穹外卖学习知识前提:**<font color"red">Nginx****<font color"red">Swagger** 1.管理员登录思路:详细步骤: 1.1新增员工问题1:在新增员工时,需要将当前登录…...
大数据成功应用商业解决方案的例子
大数据技术在商业领域的广泛应用已经成为现代商业决策和运营优化的关键驱动力。企业利用大数据分析获取洞察,从而提高运营效率、改善客户体验并实现更高的盈利。以下是几个典型的成功案例,这些企业通过大数据技术在各自领域中取得了显著的成果。 亚马逊…...
《Python使用sqlite3数据库》
《Python使用sqlite3数据库》 1、连接数据库2、创建游标3、执行SQL语句4、提交更改5、查询数据6、关闭连接 Python可以使用多种数据库,以下是一般步骤和示例: 1、连接数据库 首先要安装对应的数据库驱动。如使用MySQL数据库,要安装pymysql库…...

XHCI 1.2b 规范摘要(14)
系列文章目录 XHCI 1.2b 规范摘要(一) XHCI 1.2b 规范摘要(二) XHCI 1.2b 规范摘要(三) XHCI 1.2b 规范摘要(四) XHCI 1.2b 规范摘要(五) XHCI 1.2b 规范摘要…...
(蓝桥杯C/C++)——基础算法(下)
目录 一、时空复杂度 1.时间复杂度 2.空间复杂度 3.分析技巧 4.代码示例 二、递归 1.递归的介绍 2.递归如何实现 3.递归和循环的比较 4.代码示例 三、差分 1.差分的原理和特点 2.差分的实现 3.例题讲解 四、枚举 1.枚举算法介绍 2.解空间的类型 3. 循环枚举解…...
详解Rust标准库:VecDeque 队列
theme: github highlight: an-old-hope 查看本地官方文档 安装rust后运行 rustup doc查看The Standard Library即可获取标准库内容 std::connections::VecDeque定义 队列是遵循先入先出规则的线性数据结构,在内存中不一定连续 VecDeque定义:可增长…...
网络协议都有哪些?
网络协议是为计算机网络中进行数据交换而建立的规则、标准或约定的集合。以下是一些常见的网络协议: TCP/IP协议:传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础。由网络层的…...
非公平锁和公平锁的区别
公平锁(Fair Lock): 公平锁遵循 FIFO(先进先出)原则。当多个线程在等待锁时,公平锁会确保等待时间最长的线程优先获得锁。 这种锁机制可以避免线程饥饿(starvation),即某…...

11月7日星期四今日早报简报微语报早读
11月7日星期四,农历十月初七,早报#微语早读。 1、河南:旅行社组织1000人次境外游客在豫住宿2夜以上,可申请激励奖补; 2、主播宣称下播后商品恢复原价构成欺诈,广州市监:罚款5万元;…...

【Python】轻松实现机器翻译:Transformers库使用教程
轻松实现机器翻译:Transformers库使用教程 近年来,机器翻译技术飞速发展,从传统的基于规则的翻译到统计机器翻译,再到如今流行的神经网络翻译模型,尤其是基于Transformer架构的模型,翻译效果已经有了质的飞…...

【数据集】【YOLO】【目标检测】道路结冰数据集 1527 张,YOLO目标检测实战训练教程!
数据集介绍 【数据集】道路结冰数据集 1527 张,目标检测,包含YOLO/VOC格式标注。数据集中包含2种分类:“clear_road, ice_road”。数据集来自国内外图片网站和视频截图,部分数据经过数据增强处理。检测范围监控视角检测、无人机视…...

Java链表及源码解析
文章目录 创建一个ILindkedList接口创建方法(模拟实现链表方法)创建MyLinkedList来实现接口的方法创建链表节点addFirst方法(新增头部属性)addLast方法(新增到末尾一个属性)remove方法(删除指定属性)addInd…...
十、快速入门go语言之方法
文章目录 方法:one: 方法的概念:star2: 内嵌类型的方法和继承:star2: 多重继承 📅 2024年5月9日 📦 使用版本为1.21.5 方法 1️⃣ 方法的概念 ⭐️ 在Go语言中没有类这个概念,可以使用结构体来实现,那类方法呢?Go也…...
JVM 处理多线程并发执行
JVM(Java Virtual Machine)在处理多线程并发执行方面具有强大的支持,主要依赖于其内置的线程模型、内存模型以及同步机制。 JVM 通过以下关键机制和组件来管理多线程并发执行: 1. 线程模型 Java 线程与操作系统线程:…...

【D3.js in Action 3 精译_039】4.3 D3 面积图的绘制方法及其边界标签的添加
当前内容所在位置: 第四章 直线、曲线与弧线的绘制 ✔️ 4.1 坐标轴的创建(上篇) 4.1.1 D3 中的边距约定(中篇)4.1.2 坐标轴的生成(中篇) 4.1.2.1 比例尺的声明(中篇)4.1…...

布谷直播源码部署服务器关于数据库配置的详细说明
布谷直播源码搭建部署配置接口数据库 /public/db.php(2019年8月后的系统在该路径下配置数据库,老版本继续走下面的操作) 在项目代码中执行命令安装依赖库(⚠️注意:如果已经有了vendor内的依赖文件的就不用执行了&am…...

Xfce桌面设置右键菜单:用右键打开VSCode
前言 AlmaLinux安装VSCode之后始终没有找到如何用右键菜单打开VSCode,比Windows麻烦多了。每次都需要先找到文件夹,然后用系统自带的Open In Terminal打开终端,再输入code .,才能够在当前文件夹中快速打开VSCode。那么࿰…...

【NLP自然语言处理】深入探索Self-Attention:自注意力机制详解
目录 🍔 Self-attention的特点 🍔 Self-attention中的归一化概述 🍔 softmax的梯度变化 3.1 softmax函数的输入分布是如何影响输出的 3.2 softmax函数在反向传播的过程中是如何梯度求导的 3.3 softmax函数出现梯度消失现象的原因 &…...
Pytorch训练时报nan
0. 引言 Pytorch训练时在batchN时loss为nan。经过断点检查发现在batchN-1时,网络参数非nan,输出非nan,但梯度为nan,导致网络参数已经全部被更新为nan,遇到这种情况应该如何排查,如何避免?由于导…...
JavaScript定时器详解:setTimeout与setInterval的使用与注意事项
在JavaScript中,定时器用于在指定的时间间隔后或周期性地执行代码。JavaScript 提供了两种主要的定时器函数:setTimeout 和 setInterval。以下是它们的详细解释和实现方式: 1. setTimeout setTimeout 函数用于在指定的毫秒数后执行一次函数…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...