news 2026/10/20 9:46:09

OA系统请假审批SQL拆解:待审批数据查询与权限隔离实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OA系统请假审批SQL拆解:待审批数据查询与权限隔离实践

做OA系统做到第二十个模块,核心其实就两个字:审批。而请假审批又是审批模块里最典型的一个——单子本身不复杂,无非是员工填申请、主管点同意、人事做归档,但偏偏就是这种看着简单的功能,牵扯到的角色权限、状态流转、SQL查询最容易出幺蛾子。这一篇我准备把【请假审批】这个内嵌界面里最核心的数据查询——【待审批请假数据】的SQL语句,从表结构设计到业务逻辑,一路拆到底。前面先讲清楚审批功能的设计思路,后面直接给出能抄的SQL和排查经验的清单。

先说明一下适用场景。这套东西在某个公司的OA项目里已经跑了两三年,技术栈是Spring Boot + MyBatis + 前端iframe内嵌页面,数据库用的MySQL。所以你会看到分页用的LIMIT,参数传的#{},Mapper里写的是XML。如果你的项目用了其他ORM或者非MySQL数据库,思路完全一样,SQL方言稍微改改就行。

1. 请假审批功能模块的整体定位

1.1 审批流在OA系统里到底管什么

很多人一听到"请假审批",第一反应就是"这不就是一个表单加一个状态字段吗"。确实,单看请假这个场景,业务上就是这么回事。但把请假审批放到整个OA系统里,它的意义就完全不一样了——它是审批流这套机制的第一个标准化落地场景。

我在做这个模块之前,OA系统里已经有员工管理、考勤打卡、流程引擎这几个基础模块。到了第二十个模块安排请假审批,本质上是把流程引擎真正用起来。请假的审批链路是"申请人提交、部门主管审批、HR确认、归档",链路不长,但每一步都有明确的角色边界、状态变化和历史记录要求。把这个链路跑通,后面再做报销审批、用章申请、加班申请,几乎就是同一套代码换个表单的事。

这也是我特别看重"待审批数据"这个查询的原因。它不只是把表里的数据捞出来,它代表了"当前登录人此刻要处理的所有任务"。这个查询在OA里几乎是所有审批类模块通用的:请假审批要查待审批,报销审批也要查待审批,只是查询的明细字段不同。所以这个SQL写得对不对、快不快、权限隔离严不严,直接决定了整个审批体系的地基质量。

1.2 请假审批涉及的核心角色与数据权限

在做表结构和SQL之前,先把角色定清楚。请假审批这个界面服务四类人:

  • 普通员工:可以发起请假申请,查看自己提交过的单子状态,在审批人还没处理时可以撤销。
  • 部门主管:处理本部门或者自己管辖的下属提交的请假单,可以同意也可以驳回。
  • 人事考勤岗:查看所有审批通过的单子,核对考勤、计算假期余额、处理异常。
  • 系统管理员:维护请假类型、配置审批链路、调整审批人。

这四类人看到的数据范围完全不一样。普通员工只能看自己的,部门主管只能看自己下属的,人事看到的是全量。但是有一点很关键:即使我有管理员的越权查看能力,"待审批请假数据"永远只属于"当前审批人"本人。

也就是说,这个列表的过滤条件不能简单地写成status=审批中。一个员工提交之后,如果当前流程节点在部门主管那里,那这条数据只出现在部门主管的待审批列表里,申请人自己、人事、甚至更高层领导都不应该在这个待审批列表里看到它。这个逻辑如果没想清楚,后面就会做出那种"审批人能看到所有人待审批单子"的权限事故。

1.3 审批状态机的设计思路

请假单的状态我这边用了一个tinyint字段,定义如下:

状态值含义触发动作
0草稿员工填写未提交
1审批中员工提交
2已通过最后一个审批节点通过
3已驳回任一审批节点驳回
4已撤销审批中时申请人主动撤销

状态流转很简单:提交的时候从0跳到1;审批通过时,如果还有下一节点就继续保持1,把current_approver指向下一个审批人;如果已经是最后一个节点就变成2;任意节点驳回直接变成3;在1状态下申请人可以撤销变成4。

这里有个很重要的设计点:不要把"审批通过"直接理解成一次操作就完事。请假审批如果只有一层主管审批,那确实从1直接到2。但如果公司规定请假超过3天要总经理审批,那么主管通过之后,这条单子仍然处于"审批中",只是current_approver从主管换成了总经理。也就是说,状态为1的单子里,既包括还没到我这边的,也包括正在等我审批的。待审批列表必须区分这两类,否则就会把"已经在我上级那里流转的单子"也显示给我。

这个区分靠的就是下一节要讲的current_approver字段,或者更规范一点,靠一张独立的审批任务表。我们项目里一开始用的是字段方案,后来说要支持复杂的加签、转派,就补了一张审批记录表配合使用。但不管哪种方案,"当前登录人=当前节点处理人"这个过滤条件都是唯一的。

2. 待审批数据查询的SQL拆解

2.1 先搞清楚"待审批"的业务含义

写SQL之前,先把"待审批请假数据"翻译成逻辑条件,这一步比写SQL本身更重要。一条请假单,要在"待审批"列表里出现,必须同时满足三个条件:

  1. 这条单子是已经提交并且在流转中的,status = 1。草稿、已通过、已驳回、已撤销的都不要。
  2. 当前流程节点正等在当前登录人手上,也就是current_approver = 当前登录人ID。这里注意,不是applicant_id等于登录人,而是处理人等于登录人。
  3. 如果有审批记录表,还要保证这个登录人没有重复处理过这条单子或者已经从中间环节被替换掉。

把这些条件想透了,SQL骨架就出来了:一个主表查询加上必要的关联表,过滤条件就两个核心——status和current_approver,然后按提交时间倒序。别小看这两个条件,实际开发里80%的"待审批列表数据不对"问题,都出在这个过滤条件写歪了。

2.2 数据库表结构与关键字段设计

这个查询涉及四张表,先列出关键字段,方便后面看SQL的时候对照。

请假申请表 t_oa_leave_apply 是主表,每个字段都影响查询结果:

字段类型说明
apply_idbigint主键
apply_novarchar(32)申请单号,例如LV20250601001
applicant_idbigint申请人ID
leave_type_idint请假类型,关联请假类型表
begin_timedatetime请假开始时间
end_timedatetime请假结束时间
leave_daysdecimal(4,1)请假天数,支持半天0.5
reasonvarchar(500)请假事由
statustinyint0草稿 1审批中 2已通过 3已驳回 4已撤销
current_approverbigint当前节点审批人ID
create_timedatetime提交时间
update_timedatetime更新时间

员工表 t_oa_employee 提供申请人姓名和所属部门:

字段类型说明
emp_idbigint员工ID
emp_namevarchar(50)员工姓名
dept_idbigint部门ID

部门表 t_oa_dept 提供部门名称:

字段类型说明
dept_idbigint部门ID
dept_namevarchar(50)部门名称

请假类型表 t_oa_leave_type:

字段类型说明
type_idint类型ID
type_namevarchar(20)类型名称,例如事假、病假、年假
annual_flagtinyint是否消耗年假余额

审批记录表 t_oa_approval_record:

字段类型说明
record_idbigint记录ID
apply_idbigint请假申请ID
approver_idbigint审批人ID
approval_levelint第几级审批
approval_resulttinyint1通过 2驳回
approval_commentvarchar(300)审批意见
approval_timedatetime审批时间

2.3 核心SQL语句与逐行解析

先给最终版本,一条列表查询一条计数查询,这两条是待审批界面加载时的第一波请求。

SELECT la.apply_id, la.apply_no, e.emp_name AS applicant_name, d.dept_name, lt.type_name, la.begin_time, la.end_time, la.leave_days, la.reason, la.create_time FROM t_oa_leave_apply la INNER JOIN t_oa_employee e ON e.emp_id = la.applicant_id INNER JOIN t_oa_dept d ON d.dept_id = e.dept_id INNER JOIN t_oa_leave_type lt ON lt.type_id = la.leave_type_id WHERE la.status = 1 AND la.current_approver = #{loginUserId} ORDER BY la.create_time DESC LIMIT #{offset}, #{pageSize}
SELECT COUNT(*) FROM t_oa_leave_apply la WHERE la.status = 1 AND la.current_approver = #{loginUserId}

拆开说。SELECT部分有三个信息维度是用户最关心的:谁请的假(申请人、部门)、请的什么假(类型、起止时间、天数)、为什么请假(事由)。所以要把请假主表关联到员工表拿到姓名,再关联到部门表拿到部门名称,再关联到请假类型表拿到类型中文名。

为什么不把员工姓名、部门名称直接冗余在请假表里?我早期图省事干过这事,结果员工改名或者部门调整之后,历史审批单上显示的还是老信息,人事查账的时候对不上,被吐槽过好几次。用JOIN实时关联虽然多几次查表,但能保证展示的数据永远跟员工最新档案一致。当然,如果你的OA系统特别看重审批单的历史快照,那就在提交申请时把当时的姓名部门冗余进去,这也是一种合理设计,两条路都有取舍。

WHERE部分,两个条件我在前面已经解释过,这里补充一个细节:current_approver比对的是当前登录人的ID。在实际接口里,登录人ID是从服务端Session或者Token里解析出来的,不是前端传过来的参数。谁传谁信,谁就等着被越权。我见过有人为了图方便,把当前用户ID也放到前端请求参数里,这在内部OA里风险不大,但一旦系统要对外或者接入移动端,这就是妥妥的漏洞。

ORDER BY create_time DESC,让最新的请假单排在最上面,这是审批场景的固定操作。审批人打开列表,肯定先处理刚提交的,这点不用多说。

LIMIT分页是MyBatis里最常规的写法。offset从(pageNum-1)*pageSize算出来,pageSize一般是10、20或者50,我在请假审批列表里用的20,一屏放得下,审批人不用翻太多次。

这条SQL里还有一个容易翻车的点:t_oa_employee和t_oa_dept用的是INNER JOIN,如果某个员工被删除了或者部门被解散了,对应的请假单会从列表里消失。审批人看到列表突然少了一条,第一反应就是数据丢了。这种情况我推荐在主查询里保持关联查询不变,但在后台管理里不允许物理删除员工和部门,只做逻辑删除打标记。后面排查问题那节我会再展开说一次。

2.4 索引设计:为什么这条SQL必须快

待审批列表是一个高频率查询。审批人打开页面就触发一次,点击审批完成之后列表要刷新,前端轮询还会定时刷。如果每次查询全表扫描,等到请假单累积几万条的时候,接口延迟会从几十毫秒飙到几百毫秒,审批人就会开始骂系统卡。

好在过滤条件非常固定,就两个字段:status和current_approver。直接给这两个字段建一个联合索引就够了:

CREATE INDEX idx_status_approver ON t_oa_leave_apply(status, current_approver);

联合索引的顺序有讲究。等值过滤条件都放在前面,最左前缀原则决定status在前current_approver在后,这样先按status过滤掉绝大多数历史数据,再按当前审批人精确匹配。如果反过来建,每个审批人ID下面都要扫描所有状态的数据,效果差一大截。

ORDER BY create_time DESC这个排序没法完全靠索引避免,因为排序字段不在上面的联合索引里。好在经过两个等值条件过滤之后,结果集已经很小,MySQL用filesort排序也就几十条数据,开销完全可以接受。如果以后数据量进一步增长,可以考虑在联合索引里把create_time带进去,做成(status, current_approver, create_time),让索引直接覆盖排序。

对了,用EXPLAIN验证一下:

EXPLAIN SELECT la.apply_id, la.apply_no, e.emp_name AS applicant_name FROM t_oa_leave_apply la INNER JOIN t_oa_employee e ON e.emp_id = la.applicant_id WHERE la.status = 1 AND la.current_approver = 10086 ORDER BY la.create_time DESC LIMIT 0, 20;

type应该是ref,key用上了idx_status_approver,rows预估的数量不会超过这个审批人名下正在审批的单子数。如果看到type是ALL,或者key是空的,说明索引没建上,或者查询条件里对字段做了函数运算。比如你写成了WHERE status = 1 AND current_approver + 0 = 10086,这就是典型的让索引失效的写法。别问我为什么知道,我踩过。

3. 内嵌界面的数据交互逻辑

3.1 前端菜单与iframe装载

题目里提到的"内嵌界面",在咱们这个OA项目里就是主框架右侧的内容区用iframe嵌套了一个独立的请假审批页面。主菜单点击"请假审批",框架动态拼一个URL,塞进iframe的src里。这个URL指向请假审批模块的首页,页面上方是几个Tab,默认选中的就是"待审批"。

"待审批"Tab加载时,页面JS立刻发起一个Ajax请求,调用后端的列表接口。这个接口取名/leave/approve/pendingList,GET方法,参数就是pageNum和pageSize。后端从当前会话取登录人ID,拼上分页参数去查MyBatis的Mapper,Mapper执行的就是上一节那条SQL。

内嵌页面这种方式在传统OA里非常普遍,好处是开发简单、模块之间隔离得干净、不会因为主框架升级把子功能搞崩。缺点是iframe之间的通信麻烦点,比如审批完成后要刷新另一个iframe里的考勤汇总,就得靠父页面的消息总线转发一下。这里不展开,只提示别再去用top.location这种老掉牙的做法,用postMessage是正路。

3.2 列表接口与分页参数的使用

后端接口我给出核心骨架,实际项目里Controller、Service、Mapper三层。Controller层只做参数接收和会话解析:

@GetMapping("/leave/approve/pendingList") public Result<PageResult<LeaveApplyVO>> pendingList( @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "20") Integer pageSize, HttpServletRequest request) { Long loginUserId = SessionUtils.getCurrentUserId(request); PageResult<LeaveApplyVO> page = leaveApproveService.queryPending(loginUserId, pageNum, pageSize); return Result.success(page); }

Service层算好offset,调用Mapper:

public PageResult<LeaveApplyVO> queryPending(Long loginUserId, Integer pageNum, Integer pageSize) { int offset = (pageNum - 1) * pageSize; List<LeaveApplyVO> list = leaveApplyMapper.selectPendingList(loginUserId, offset, pageSize); Long total = leaveApplyMapper.countPending(loginUserId); return new PageResult<>(list, total); }

Mapper XML里就是上一节的SQL,注意count查询里不需要JOIN那些表。因为COUNT(*)只需要知道满足过滤条件的行数,关联员工表反而会增加无关的开销。等数据量大之后,SELECT COUNT(*)这种查询也可以用定时任务缓存"各审批人待办数",不过这是后话。

有个细节容易忽略:分页参数要加默认值,否则前端一个请求漏传pageSize,后端就要么报错要么返回空。我在项目里统一用@RequestParam的defaultValue兜底,遇到参数缺失也能正常响应。

3.3 与详情页、审批动作的数据联动

待审批列表只能看到摘要信息。审批人要看完整请假单,得点行打开详情。详情数据来自另一个接口:

SELECT la.apply_id, la.apply_no, e.emp_name, d.dept_name, lt.type_name, la.begin_time, la.end_time, la.leave_days, la.reason, ar.approver_id, ar.approval_comment, ar.approval_time FROM t_oa_leave_apply la LEFT JOIN t_oa_employee e ON e.emp_id = la.applicant_id LEFT JOIN t_oa_dept d ON d.dept_id = e.dept_id LEFT JOIN t_oa_leave_type lt ON lt.type_id = la.leave_type_id LEFT JOIN t_oa_approval_record ar ON ar.apply_id = la.apply_id WHERE la.apply_id = #{applyId}

这里用的是LEFT JOIN,因为审批记录可能还没有(刚提交还没人处理过),不能用INNER JOIN把主单丢掉。如果想让审批历史按时间正序展示,可以再加ORDER BY ar.approval_time ASC,这个排序在审批记录很少的情况下几乎没有开销。

审批人点"同意"或"驳回"之后,前端调审批接口,后端在一个事务里做三件事:往审批记录表插一条数据、更新请假单的current_approver指向下一个节点、必要时更新status。事务提交成功后,前端刷新待审批列表,这条数据立刻从列表里消失。如果它出现在"已审批"列表里,说明状态推进成功。整个过程要保证原子性,否则会出现"审批记录有了,但列表里的单子还在"这种让审批人点第二次的尴尬情况。

我在这个环节吃过一次大亏。当时审批接口和记录插入分了两个事务,审批人点同意后,记录插入失败但状态更新成功,导致这条单子在待审批列表里消失,在已审批列表里也找不到,完全变成了"幽灵单"。最后是让运维翻数据库日志才捞回来。从那之后我再也没有把状态更新和审批记录分到两个独立事务里写过。

4. 实操中的常见问题与排查实录

4.1 明明有数据却查不到

这是待审批列表最经典的问题:某个审批人打开页面,总感觉少了几条单子。我自己排查过的原因有三种。

第一种,状态值约定不一致。有的人把"已提交"定成2,有的人把"审批中"定成1,前端传过来的status和数据库里实际存的status对不上,导致status = 1查出来的是另一批数据。这种问题查的方法是直接看库里一条已知单子的status值,再比对SQL里的条件。

第二种,current_approver没被正确写入。流程引擎在提交申请时如果没把第一个审批人写到current_approver字段里,那么这条单子status=1但current_approver=NULL,任何审批人都查不到它。这个字段为空的时候,我建议在提交接口里做防空判断,一旦发现为空直接报错并提示"该员工没有配置审批人"。

第三种,JOIN把我坑了。员工如果离职被物理删除,或者部门被物理删除,INNER JOIN会把对应的请假单整个过滤掉。待审批列表看起来少数据,其实数据在,只是被关联表的残缺拖下水了。所以我在项目里定了一条硬规矩:业务主档一律不物理删除,全部用一个deleted_flag逻辑删除。这条规矩救了我后面很多次。

4.2 审批人权限串号

权限串号的典型表现是:登录人A的待审批列表里出现了本应只属于登录人B的单子。排查的时候先看SQL的WHERE条件,十有八九是条件写成了:

WHERE la.status = 1 AND la.applicant_id = #{loginUserId}

这一行把"当前审批人=登录人"写成了"申请人=登录人",结果就是普通员工看到自己提交的单子里那些还没被审批的单子,而不是他们需要处理的单子。员工看到后一脸懵,审批人漏处理了一堆单子却不知道。

还有一种更隐蔽的情况:数据权限虽然写在查询里了,但Controller直接从请求参数里接收了userId,导致有人手工构造请求就能查询别人的待办。我在前面已经强调过,这个字段必须从服务端会话里取。这个问题的排查办法很简单:用A的账号登录,把请求里的userId参数改成B的ID,如果接口返回了B的数据,那这个接口就是有权限漏洞的,必须改。

4.3 状态字段更新丢失

状态更新丢失通常表现为:审批人明确点了"同意",但下次打开待审批列表,这条单子还在。很多人第一反应是"SQL更新没生效",其实我遇到过两次真正的原因。

一次是并发问题。审批人在两个标签页打开了同一个详情页,同时点了同意。代码里的更新语句长这样:

UPDATE t_oa_leave_apply SET status = 2, current_approver = NULL WHERE apply_id = #{applyId} AND status = 1

两个请求同时进来,第一个把status改成了2,第二个因为WHERE条件里加上了status = 1,更新了0行,也不会把审批记录再插一遍。这就是乐观锁的思想,用旧状态作为更新条件,保证同一条单子不会被审批两次。如果没加这个条件,两个请求都执行成功,审批记录表里就会多出一条重复记录,后续统计全乱。

另一次是缓存问题。早期前端列表缓存了接口数据,审批完成后只刷新了当前页,但缓存里的数据还是旧的。这不是后端Bug,是前端没做好失效。解决办法是在审批成功的回调里,拿到返回结果后强制调用一次列表刷新接口,同时清掉本地缓存。

还有个很容易被忽略的细节:审批完成之后,如果current_approver字段没有更新成下一个审批人或者置空,这条单子会一直出现在原审批人的待审批列表里。我在代码里是这么设计的:如果还有下一节点,就把current_approver改成下一节点审批人;如果这是最后一节点,就把current_approver改成NULL。这个字段要么指向一个真实的人,要么就是NULL,绝不能保留原来的审批人ID。

4.4 慢查询优化记录

最后说一个真实遇到的慢查询案例。系统上线半年之后,请假数据到了三四万条,某个大部门的审批人打开待审批列表,接口要两秒多才返回。查了日志发现列表查询走了全表扫描。

当时的SQL和索引情况:t_oa_leave_apply表只有主键索引,列表SQL的WHERE条件还是status和current_approver两个字段,完全没有索引可用。EXPLAIN结果type=ALL,rows显示三万多行,这就解释了为什么慢。

处理办法就是建索引。我建了:

CREATE INDEX idx_status_approver ON t_oa_leave_apply(status, current_approver);

建完之后再EXPLAIN,type变成了ref,rows降到了几十行,接口延迟从两秒多降到了三十毫秒以内。那段时间我正在优化整个OA系统的性能,这条索引是效果立竿见影的一个。

另外一个关于排序的优化细节。之前列表SQL里用了ORDER BY create_time DESC,当分页页码比较深,比如翻到第30页,LIMIT 580, 20这种写法,MySQL还是要先扫出前600条再丢弃前580条,页码越深越慢。但我观察了一段时间,审批人基本不会翻超过5页,所以这个优化暂时没做。真到了要做的那天,可以考虑用游标分页,也就是记录上一页最后一条的create_time,用WHERE create_time < #{lastCreateTime} 来取下一页。这个方案在深分页场景下的性能提升非常明显,但会牺牲页码跳转的灵活性,要不要做,取决于产品需求。

其实做请假审批这个模块,最花时间的不是写SQL,而是把"待审批"这三个字的边界反复确认清楚。它看似是一个简单查询,背后藏着角色权限、状态机、流程节点的三层设计。SQL本身可能只需要十分钟,但前面那些设计思考,决定了这十分钟写出来的东西是能撑两年,还是上线两周就返工。

我个人这部分做完之后的体会是:只要把status和current_approver这两个字段的语义定义清楚,再配合一个简单的联合索引,待审批列表这块基本就稳了。后面如果再扩展多级审批、加签转派,无非是再引入一张审批任务表,查询条件从"直接比对字段"改成"关联任务表",思路是同一个。这个模块做完,接手的同事都说这块逻辑好懂,我觉得这就是最大的认可了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 9:45:59

Transformer长上下文之困:SSM与混合架构如何破局?

1. 为什么Transformer的“上下文墙”不只是算力问题&#xff0c;而是记忆结构问题我一开始接触长文本任务的时候&#xff0c;也以为只要把显卡堆上去、把序列长度拉到128K、甚至1M&#xff0c;Transformer就能从容应对。直到有一次跑一个金融公告的风险因子抽取任务&#xff0c…

作者头像 李华
网站建设 2026/10/20 9:45:37

Miniconda安装与使用:Python环境管理与依赖隔离实战指南

搞Python开发&#xff0c;环境管理是一道绕不过去的坎。我见过太多人因为把依赖装进系统环境&#xff0c;导致某个包升级后整个项目跑不起来&#xff0c;最后只能重装系统。Pip能解决的包依赖问题在纯Python项目里够用&#xff0c;但一旦涉及Python版本切换、原生库编译、CUDA版…

作者头像 李华
网站建设 2026/10/20 9:45:27

AI自动化测试平台AITS实战解析:从脚本维护到智能断言落地

1. AITS到底解决什么问题&#xff1a;从传统自动化的痛点说起先交代一下背景。我所在的团队负责多条业务线的回归测试&#xff0c;自动化脚本攒了快三千条。表面上跑得热火朝天&#xff0c;实际上每个迭代光修脚本就要花掉三四个人日&#xff0c;脚本写得越多&#xff0c;维护欠…

作者头像 李华
网站建设 2026/10/20 9:45:10

ImageMagick入门:批量缩放、格式转换、加水印的自动化图片处理指南

别把 ImageMagick 当成一个“命令行版 PS”&#xff0c;它更像是一个能批量化、自动化处理图片的瑞士军刀。我第一次接触它是因为要给一批几百张的产品图标统一尺寸和格式&#xff0c;PS 里一张一张导出简直要命&#xff0c;用 ImageMagick 一条命令几秒钟就全部跑完了。从那以…

作者头像 李华
网站建设 2026/10/20 9:44:02

Unity吃豆人核心框架:状态机驱动的幽灵AI与网格碰撞系统

简介&#xff1a;本资源是一份基于Unity3D 5.3开发的经典吃豆人&#xff08;Pac-Man&#xff09;游戏完整源码&#xff0c;面向Unity初学者与中级开发者&#xff0c;聚焦游戏逻辑实现、角色行为控制与跨模块协同开发能力提升。压缩包为ZIP格式&#xff0c;大小23.89MB&#xff…

作者头像 李华
网站建设 2026/10/20 9:43:43

自托管RustDesk会话审计实战:日志采集、操作记录与告警排查

自托管RustDesk用了一段时间之后&#xff0c;我最大的感受是&#xff1a;远程桌面工具本身好不好用是一回事&#xff0c;能不能在出问题时把当时谁连了谁、做了什么、会话持续了多久这些事说清楚&#xff0c;是另一回事。尤其当RustDesk承担的是办公终端远程维护、业务系统排障…

作者头像 李华