开发小记
# 开发小记
# 开发整体流程
任务安排 -> 环境服务配置 -> 开发 -> 本地测试 -> Maven 打包 -> 服务器测试 -> 发布上线
具体开发情况
- 先写个小 demo
- 然后逐步完善功能,以最快的方式完成任务需求
- 融进项目模块,上传到远程仓库
- 优化代码结构
- 完善代码逻辑,比如让日志打印的更加清晰,方便线上排查问题
# 开发注意事项
- 需打印日志,方便后续排查问题(控制台打印不行,因为看不到)
- 遇到写动态 URL 的场景,要把服务 ip 和参数写活,然后字符串拼接,服务 ip 地址可以在 nacos 上配置,然后动态注入
使用服务器配置属性示例:
@Data
@Component("properties")
@Configuration
@RefreshScope
public class ConfigProperties {
/**
* 图像链接前缀
*/
@Value("${url.pre}")
private String urlPre;
}
2
3
4
5
6
7
8
9
10
11
12
13
为什么 @Component("properties") 这里要加参数?
@Component
注解用于将一个类标记为 Spring 容器中的一个组件,以便 Spring 在启动时能够扫描并将该类初始化为一个 Bean。
通常情况下,@Component
注解不需要参数,但在特定情况下,你可能会为该注解提供一个参数。
在上面的代码中,@Component("properties")
中的参数 "properties"
是一个用来标识该组件的名称。这个名称在 Spring 容器中是唯一的,可以用来作为 Bean 的标识符。使用该参数的目的通常是为了明确指定这个 Bean 的名称,而不是使用默认的类名首字母小写作为 Bean 的名称。
例如,如果没有指定参数 "properties"
,Spring 容器会默认使用类名 Properties
首字母小写作为 Bean 的名称。但是,类名 Properties
可能与 Java 标准库中的 java.util.Properties
类名相同,可能会引起混淆。为了避免这种混淆,并明确指定 Bean 的名称,你可以使用 @Component("properties")
。
总之,参数 "properties"
并不是必需的,但它可以帮助你在 Spring 容器中标识和管理这个组件,特别是当类名可能引起命名冲突时。
使用
只需要在指定类里面注入 bean 即可。
@Resource
private Properties properties;
2
然后通过 get 方法获取对应值
String urlPre = properties.getUrlPre();
# 代码优化
优化考虑点:
- 重复代码的提取: 重复的代码通常可以通过创建一个单独的方法来提取。这可以减少代码的重复性,使代码更易维护。确保避免“复制粘贴”代码的做法,因为这可能会导致后续的维护问题。
- 日志统一管理: 使用一个专门的日志管理工具,例如
SLF4J
,以确保日志输出格式的一致性。这还可以让你更容易地控制日志级别和输出位置,以便于调试和监控。 - 使用常量: 避免在代码中硬编码字符串,特别是消息通道名称。将这些名称定义为常量或配置参数,以提高代码的可维护性和降低出现拼写错误的风险。
- 添加错误异常处理: 在发送消息的代码块中,确保添加适当的异常处理。这可以包括捕获异常、记录异常信息以及采取适当的操作来处理异常情况,例如重试或回退策略。
- 字段处理抽取方法: 对于复杂的字段处理逻辑,将其抽取为单独的方法是个好主意。这可以提高代码的可读性和可测试性,并允许你在多个地方重复使用这些处理逻辑。
- 魔法数值的使用: 避免硬编码魔法数值(数字或字符串字面值),因为它们通常缺乏可读性和维护性。使用有意义的常量或枚举值来代替这些数值,以便其他人能够理解代码的含义。
- 逻辑分离: 将逻辑分解成小的、单一职责的方法是一个良好的做法。这样的方法应该独立于其他逻辑,使得代码更易理解和维护。
- DTO 对象的构建: 对于复杂的 DTO 对象,使用构建者模式或 Lombok 的
@Builder
注解可以简化对象的创建过程,减少样板代码。 - 并发和性能优化: 如果你的应用需要处理高并发或大数据量,需要考虑并发编程和性能优化,包括使用线程池、缓存、异步处理等技术。
- 内存管理: 注意内存管理,尤其是在处理大型数据集时,避免内存泄漏和过多的对象创建。
其他建议:
- 单元测试: 编写单元测试来验证代码的正确性,确保你的优化不引入新的问题。
- 代码审查: 进行代码审查,让其他开发人员审查你的代码,以获得反馈和改进意见。
- 文档: 编写清晰的代码注释和文档,使代码易于理解和维护。
- 团队协作: 考虑团队协作和代码风格规范,以便多个开发人员能够协同工作。
- 扩展性: 考虑代码的扩展性,以便将来可以轻松添加新功能或模块。
- 安全性: 如果应用涉及敏感数据或操作,确保实施适当的安全措施,如身份验证和授权。
- 数据库性能: 如果应用与数据库交互,需要优化数据库查询,包括索引的使用和查询性能。
- 可读性: 始终将代码的可读性放在首位,使用有意义的命名和代码结构,使代码容易理解。
优化代码不仅仅是为了提高性能,还包括提高代码的可读性、可维护性和可测试性。
# Git 相关操作
写代码前,务必先拉取项目
# 代码仓库提交流程
先写好 .gitignore 文件 -> 创建本地仓库 -> 添加目录文件 -> 提交 -> 拉取 -> 推送
先拉取,再提交
拉取时遇到文件冲突
eg:
远程仓库中有 README 文件,本地项目也有 README 文件,拉取时会起冲突。
解决方法:拉取时选择 -rebase
参数
# 压缩提交
针对于本地提交,可对多次提交进行压缩,压缩成为一次提交
# 排查冗余数据
1、排查表中的 t_agile_user_org
表中不存在的用户或组织的关联记录
# 排查用户-组织关联表
SELECT * FROM t_agile_user_org
WHERE user_id NOT IN (SELECT id FROM t_agile_user)
OR org_id NOT IN (SELECT id FROM t_agile_org);
2
3
4
2、排查未使用的角色:查询 t_agile_role
表中未与任何用户绑定的角色。
# 排查用户-角色关联表
SELECT * FROM t_agile_role
WHERE id NOT IN (SELECT role_id FROM t_agile_user_role);
2
3
3、排查每个用户除了自身是否绑定了其他角色 -- 绑定了多个角色的用户信息
SELECT user_id, COUNT(*) as role_count FROM t_agile_user_role
GROUP BY user_id
HAVING role_count > 1;
2
3
携带 账号 和 手机号 联表查询
# 查询绑定了多个角色的用户信息 -- 记录数不符合预期,错误
SELECT ur.user_id, u.account, u.phone, COUNT(ur.user_id) as role_count
FROM t_agile_user_role ur, t_agile_user u
GROUP BY ur.user_id, u.account, u.phone
HAVING role_count > 1;
# 正确写法
SELECT u.account, u.phone, COUNT(ur.user_id) as role_count
FROM t_agile_user_role ur
INNER JOIN t_agile_user u ON ur.user_id = u.id
GROUP BY u.account, u.phone
HAVING role_count > 1;
# 另一种写法 -- 注意 in 在实际开发中性能比较低(记录数大的时候)
SELECT id, account, phone
FROM t_agile_user
WHERE id in (SELECT user_id FROM t_agile_user_role
GROUP BY user_id
HAVING COUNT(*) > 1);
# 优化后的写法
SELECT u.id, u.account, u.phone
FROM t_agile_user u
INNER JOIN (
SELECT user_id
FROM t_agile_user_role
GROUP BY user_id
HAVING COUNT(*) > 1
) r ON u.id = r.user_id;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
参考文章:ChatGPT大模型告诉了我SQL中替换In查询的10种方法,太赞了 (opens new window)
# 数据脱敏
BeanUtils.copyProperties()
工具方法的使用
它的目的是将两个对象之间的属性值进行复制。这是一种更通用的方式,适用于需要在不同类之间复制属性的情况,特别是当类的属性较多时,可以减少重复的手动属性赋值代码。(Spring BeanUtils 性能最好,但是其底层是用反射实现的,也会造成一些不必要的性能损耗)
- BeanUtils工具类中的copyProperties方法使用 (opens new window)
- 为什么不建议使用甚至禁止使用Beanutils进行属性的copy_beanutils.copy (opens new window)
- BeanUtils.copyProperties使用和性能分析 (opens new window)
- BeanUtils.copyProperties的11个坑,附替换方案 (opens new window)
# 时间字段比较处理
1、第一种使用日历 Calendar
:
Date startDay = DateUtil.parse(dto.getStartDay(), "yy-MM-dd");
Date endDay = DateUtil.parse(dto.getEndDay(), "yy-MM-dd"); // 注意这里是 dto.getEndDay()
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDay);
while (calendar.getTime().before(endDay) || calendar.getTime().equals(endDay)) {
// 执行你的逻辑
aiAlarmStatMapper.getFilePath(dto, orgId, alertType);
// 增加一天
calendar.add(Calendar.DATE, 1);
}
2
3
4
5
6
7
8
9
10
11
2、转换成整数
// 将 "yyyy-MM-dd" 格式的日期字符串转换为整数
int startDay = Integer.parseInt(dto.getStartDay().replaceAll("-", ""));
int endDay = Integer.parseInt(dto.getEndDay().replaceAll("-", ""));
2
3
# 直接展示图片功能
代码:
- 其中 imageUrl 是个图片文件路径(没有存储到云上的URL文件),没有存储在服务器,不能通过浏览器直接打开链接查看图片
- 总的来说,以下这段代码的作用是从指定的图片 URL 获取字节流,并通过
ResponseEntity
对象将字节流以输入流资源的形式返回给客户端,达到直接展示图片的功能。
@GetMapping("/image")
public ResponseEntity<InputStreamResource> getImage() throws IOException {
// 图片url示例
String imageUrl = "https:xxx";
// 打开图片URL连接 - 网络编程知识点:URL 和 URLConnection
// 创建一个URL对象以便访问图片URL
URL url = new URL(Objects.requireNonNull(imageUrl));
// 打开与图片URL的连接
URLConnection connection = url.openConnection();
// 获取连接的输入流,以获取图片数据
InputStream inputStream = connection.getInputStream();
// 创建一个 InputStreamResource 对象,将图片的输入流封装为资源对象
InputStreamResource resource = new InputStreamResource(inputStream);
// 构建并返回一个ResponseEntity,包含图片资源和响应头信息,设置响应内容类型为图片类型,将资源对象作为响应主体返回
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG)
.body(resource);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
涉及到的知识点如下:
- 使用 URL 类和 URLConnection 类获取网络资源:
URL
类用于表示一个统一资源定位符,可以通过它打开连接,读取数据等操作。URLConnection
类用于建立一个链接到指定 URL 资源的连接。
- 读取网络资源的字节流:
- 使用
URLConnection
对象的getInputStream()
方法获取与 URL 连接的输入流,即获取网络资源的字节流。
- 使用
- 使用 InputStreamResource 类进行字节流的封装:
InputStreamResource
是org.springframework.core.io.InputStreamResource
类,是 Spring Framework 提供的一个实现了Resource
接口的类- 用于将字节流包装成一个资源对象,并能够与
ResponseEntity
一起返回给客户端。
- 使用
ResponseEntity.ok()
构建响应实体:ResponseEntity
是 Spring Framework 提供的一个带有状态码、响应头和响应体的对象,用于构造 HTTP 响应返回给客户端。ok()
方法表示请求成功,并返回一个带有 200 状态码的响应实体。
- 设置响应体的内容类型:
- 使用
contentType(MediaType.IMAGE_PNG)
方法设置响应的 Content-Type 为 "image/png",以告知客户端返回的是 PNG 图片类型的数据。
- 使用
# 事务处理
事务的两种使用方式:
- 编写一个配置类:
全局事务处理器
- 使用
@Transactional
注解来声明一个事务方法
不指定
rollbackFor
属性,表示对所有 RuntimeException 及其子类进行回滚,此时可以不再抛出异常,由 Spring 默认处理回滚。并且不需要在该方法及其子方法中显式地抛出异常,如果子方法中发生了异常,并且不被
catch
块处理,这个异常会传播到remove
方法中,从而触发事务回滚。但编译器不推荐使用,会爆黄!
使用示例如下:
@Override
@Transactional(rollbackFor = Exception.class) // 指定需要回滚的异常类型
public R<Object> remove(IdsDTO ids) {
try {
// 省略其他代码
// ...
return R.status(true);
} catch (Exception e) {
log.error("删除数据失败,企业ID: {}, 错误原因: {}", ids.getIds(), e.getMessage(), e);
// 在 catch 块中抛出 RuntimeException 或其子类,以触发事务回滚,子方法有异常会无效
throw new RuntimeException(e);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
注意事项:
- 如果方法中的异常是
MinioException
或其子类,而MinioException
不是RuntimeException
的子类,Spring 默认情况下是不会触发事务回滚的,因为 Spring 的事务管理默认只对RuntimeException
及其子类进行回滚。 - 如若调用到子方法,需要一直向上抛到顶层才可触发事务回滚!
# 单点登录
单点登录是指:一次登录,到处访问。
单点登录(Single Sign-On, SSO)是一种身份验证机制,允许用户在一个系统中进行一次认证后,就可以访问其他相关联的多个系统,而无需再次输入用户名和密码。这种技术提高了用户体验,因为用户只需要记住一个账户凭证,并且减少了在不同系统之间切换时重复登录的麻烦。
在单点登录系统中,有一个中心化的身份验证服务(通常称为认证服务器或身份提供者),它负责验证用户的凭据并为用户提供一个令牌或票证,这个令牌可以用来证明用户已经通过了身份验证。当用户尝试访问一个支持单点登录的服务时,该服务会检查用户是否持有有效的令牌。如果令牌有效,那么用户就可以直接访问该服务;如果令牌无效或者不存在,用户则需要重新进行身份验证。
单点登录系统可以在企业内部使用,也可以用于跨组织的联盟环境,例如互联网上的合作伙伴网站。为了实现安全的单点登录,通常会使用加密和其他安全措施来保护传输中的数据以及存储的凭据。常见的协议和技术包括 Kerberos、SAML、OAuth 和 OpenID Connect 等。
# 平台模型
# rbac 模型
RBAC(Role-Based Access Control)模型是一种访问控制模型,用于管理和控制用户对系统资源的访问权限。在 RBAC 模型中,权限和角色是核心概念,用户被分配到角色,而角色与权限相关联。以下是 RBAC 模型的核心组成部分:
- 用户(User): 用户是系统的最终使用者,他们需要访问系统的资源。每个用户可以被分配一个或多个角色。
- 角色(Role): 角色是一组相关权限的集合。角色定义了用户可以执行的操作和访问的资源。例如,一个系统可以有 "管理员"、"普通用户"、"访客" 等角色。
- 权限(Permission): 权限是系统资源上的操作或访问规则。它们定义了用户在系统中的具体行为,例如读取、写入、删除等操作。权限通常与角色相关联,一个角色可以包含多个权限。
- 分配(Assignment): 分配是将用户与角色关联起来的过程。一个用户可以被分配到一个或多个角色,从而获得这些角色的权限。
- 访问控制矩阵(Access Control Matrix): 这是一个表示用户、角色、权限之间关系的矩阵。它清楚地定义了谁可以做什么。
RBAC 模型的主要优点包括:
- 简化权限管理: RBAC将权限分配和管理集中在角色级别,使权限管理更加简单和可维护。
- 增强安全性: 通过将用户分配给角色,RBAC可以确保每个用户只有必要的权限,从而降低了潜在的风险。
- 易于扩展: 当需要添加新的权限或角色时,RBAC模型使扩展变得相对容易,不会影响到整个系统。
- 提高可维护性: RBAC模型使系统更易于维护,因为角色和权限的变化可以在中央位置进行管理。
RBAC 模型在许多应用程序和系统中得到广泛使用,特别是在需要强大的访问控制和安全性的环境中,例如企业应用程序、数据库管理系统、操作系统等。它有助于确保系统的安全性和合规性,同时提高了管理和维护的效率。