ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

EOS 8.3.3附件多选上传:如何实现只能删除自己上传的附件?

EOS 8.3.3附件多选上传:如何实现只能删除自己上传的附件? 1. 需求拆解与方案选型1.1 标题里藏着的三个关键点做EOS 8.3.3相关的企业项目时附件模块看起来是标准件实际上每次都能整出新花样。这个标题描述了一个非常典型的场景附件允许多选上传一张业务单据上可能有不同人员先后上传的附件现在要求“只能删除自己上传的附件”。这个需求乍一听很简单但拆开来看有三个关键点。第一附件允许多选。这意味着附件列表和业务单据是一对多的关系附件集合的构成来源是混合的不能假设“这个单据上的附件都是同一个人传的”。第二多个附件不是同一个人上传的。这决定了权限判断的粒度必须是“单条附件”而不是“整单”。如果一张单上的附件只能由上传人自己管理那么权限标记必须细化到附件表的每一行。第三只能删除自己上传的。这里的核心是“删除动作”与“上传人身份”的强绑定。它不只是前端隐藏一个删除按钮的问题还包含了数据归属校验、接口防绕过、批量操作校验等环节。我建议把目标简化为两句话列表层面每条附件要能准确告诉前端“这条当前用户可不可以删”接口层面不管前端怎么调后端必须再做一次归属校验非本人附件一律拒绝删除。1.2 我踩过的三种做法对比这个需求我第一次处理时直接在页面上把删除按钮藏掉了后来测试人员换了个浏览器直接调接口把别人上传的附件删了幸好是在测试环境。后来我把方案整理成了三种对比如下方案实现方式用户体验安全性推荐度前端隐藏按钮列表渲染时根据当前登录人控制“删除”按钮显隐中但可直接绕过低接口无校验不推荐单独使用前端判断通用删除接口前端比对后传附件ID后端只做记录删除较好中仍可构造请求不推荐前端控制后端强制校验列表返回可删标识后端删除时二次校验归属好高强烈推荐第三种方案是我最终采用的也是标题这个场景下比较标准的解法。它没有把所有判断都压给前端也没有把权限判断做成“黑盒”而是把前端和接口各管一段。前端管体验看得见、点得动、有提示后端管安全凡是删除动作都必须验证当前登录人是否就是附件上传人身份不符直接拒绝。1.3 为什么推荐“前端控制后端强制校验”这套组合的关键在于权限控制不能靠“用户看不到按钮”来兜底。我见过太多系统只做UI层控制结果被会抓包的人轻易绕过。企业应用里的附件往往关联合同、工单、报销单一旦删错恢复的成本很高。还有一个容易被忽略的点删除按钮隐藏和删除按钮禁用用户体验差别很大。隐藏状态下普通用户不知道是自己没权限还是功能坏了禁用状态下最好能加一个提示“附件由XXX上传无权删除”。这个信息从哪里来就是列表接口返回的上传人信息和可删标识。所以我的经验是先在前端把“能删”和“不能删”的视觉区分做好然后在后端把“这个用户能不能删这条附件”的判断写死。前端控制是礼貌后端校验是底线。2. 数据层面先把“谁上传的”做实2.1 附件表结构设计权限判断要落地附件表的设计必须支持“找到上传人”这件最基本的事。很多项目附件表只有文件名和存储路径这是不够的。建议至少包含以下字段字段名类型说明idvarchar(32)附件主键biz_typevarchar(50)业务类型标识如contract、workorderbiz_idvarchar(32)业务单据主键file_namevarchar(200)原始文件名file_pathvarchar(500)存储路径或对象地址file_sizeint文件大小file_typevarchar(20)扩展名uploader_idvarchar(32)上传人IDuploader_namevarchar(50)上传人姓名upload_timedatetime上传时间del_flagvarchar(1)逻辑删除标记delete_timedatetime删除时间delete_user_idvarchar(32)删除人ID这里有两个字段我要特别提醒。第一个是uploader_id它是权限判断的主依据一定要存ID而不是只存姓名原因是姓名会重复、会改名ID才是唯一身份。第二个是del_flag建议所有企业级附件删除都走逻辑删除这是为了避免用户误删之后追不回来也为了后面处理“同一个附件被多处引用”的场景。2.2 上传人信息的正确写入方式上传接口在写入附件记录时uploader_id必须取自服务端会话不能信任前端传来的任何参数。我见过有人在附件表单里放一个“上传人”输入框这在小工具里没问题但放在有权限诉求的企业应用里就是灾难改个请求参数就能把别人的附件记到自己名下。EOS 8.3.3里获取当前登录人一般有两种方式。一种是在逻辑流中通过平台提供的上下文或会话取用户信息另一种是在自定义Java扩展类中通过request.getSession()获取登录对象。无论用哪种拿到当前用户后直接赋值给uploader_id前端不需要传、也不要显示编辑入口。这一步做扎实了后面的删除校验才有依据。如果表里一堆附件都没有上传人信息后面再怎么判断都是空谈。2.3 列表返回把可删标识计算好附件列表接口在做查询的时候我建议直接把canDelete这个标记计算好和附件记录一起返回给前端。SQL层面可以这样做select a.id, a.file_name, a.file_size, a.uploader_id, a.uploader_name, a.upload_time, case when a.uploader_id :currentUserId and a.del_flag 0 then 1 else 0 end as can_delete from biz_attachment a where a.biz_type :bizType and a.biz_id :bizId and a.del_flag 0有人可能会问前端拿到uploader_id自己跟当前登录人比一下不就行了何必让后端多算一个字段我的理由有三点。第一前端每次都要拿当前登录人信息和附件信息做比对等于把业务规则散落在页面脚本里不利于维护。第二如果后端返回的登录人ID结构变了前端所有校验都得跟着改。第三也是最重要的一点把判断逻辑收口到服务端之后后续想扩展规则比如管理员也可删、流程节点处理人也可删只需要改后端计算前端不用动。3. EOS 8.3.3里的核心实现列表与删除3.1 前端列表渲染的控制写法列表渲染这块我在实际项目里用的是“单元格内条件渲染”的方式。删除按钮默认不显示只有canDelete为1时才展示。如果用JavaScript模板渲染大致是这样button th:if${item.canDelete 1} classbtn-delete onclickdeleteAttachment(${item.id}, ${item.fileName}) 删除 /button button th:if${item.canDelete ! 1} classbtn-disabled title非本人上传无权删除 无权删除 /button注意一个细节无权时我没有直接不渲染任何东西而是显示一个置灰的“无权删除”按钮并带tooltip提示。用户看到之后知道自己是“没有权限”而不是怀疑功能坏了。这种交互上的小差别在真实业务里能少接很多无谓的咨询电话。另外列表里的“上传人”列建议保留方便同一张单上的用户互相知道附件是谁传的这在多人协作的场景里非常实用。3.2 后端删除接口的归属校验逻辑后端删除接口是整个方案的重中之重。前端隐藏按钮只是第一步接口必须有独立的校验。常规逻辑如下public void deleteAttachment(ListString attachmentIds, String currentUserId) { ListAttachment attachments attachmentMapper.selectByIds(attachmentIds); if (CollectionUtils.isEmpty(attachments)) { throw new BusinessException(附件不存在请刷新后重试); } for (Attachment att : attachments) { if (!currentUserId.equals(att.getUploaderId())) { throw new BusinessException(只能删除自己上传的附件 att.getFileName()); } if (1.equals(att.getDelFlag())) { throw new BusinessException(附件已被删除 att.getFileName()); } } // 校验通过执行逻辑删除 int count attachmentMapper.logicDelete(attachmentIds, currentUserId); if (count ! attachmentIds.size()) { throw new BusinessException(删除失败可能存在并发操作请重试); } }这里面有几个点要强调。删除时必须用附件ID去查附件记录再判断归属而不是信任前端传来的文件名或路径去拼接文件地址。这样可以防止有人构造请求删除任意路径下的文件。判断归属时用当前登录人ID与附件记录里的uploader_id比对而不是让前端把“这个附件是谁的”作为参数传到后端。后端必须自行获取会话中的当前用户。校验通过后再执行删除逻辑删除用update语句更新del_flag同时记录删除人ID和删除时间方便日后追溯。这一步做得好被删的数据永远能查出来是谁删的、什么时候删的。3.3 EOS中两种实现方式逻辑流与Java扩展类EOS 8.3.3的常见开发方式有两种逻辑流可视化和自定义Java扩展类。这个需求我建议用Java扩展类实现校验逻辑再用逻辑流调用。逻辑流方式适合简单流程比如“接收参数-数据查询-判断-返回结果”比较直观。但如果批量删除时有多层循环校验、异常信息需要带附件名这种动态数据逻辑流会显得笨重调试起来也费劲。我当时的做法是写一个自定义扩展类AttachmentDeletionService里面提供一个deleteAttachmentsWithPermissionCheck方法方法内部完成查询、校验、删除、日志四件事。然后在EOS逻辑流里加一个“调用Java扩展类”节点参数传附件ID数组和当前登录人ID返回值给前端结果信息。这样的好处是校验代码好维护以后想加“允许部门管理员删除”只需要在这个方法里再补一段逻辑即可逻辑流不用改。3.4 删除动作的执行细节与日志记录删除动作建议分成两层先更新数据库标记再异步清理物理文件。数据库记录和物理文件删除天然不是同一个事务如果先删文件后删记录万一数据库操作失败文件没了但记录还在用户再点删除就会提示“文件不存在”。所以我倾向先逻辑删除附件记录让用户在业务界面上立刻看不到这个附件然后在后台异步执行物理文件清除。物理文件删除失败的情况也要处理。不能因为文件删不掉就把业务操作回滚而是记录一条日志比如“附件ID为xxx的物理文件清理失败路径xxx”后续人工处理。附件删除日志的内容建议包括删除人ID、删除人姓名、附件ID、附件名、删除时间、操作结果这既是审计的需要也是排查问题时的第一手材料。4. 批量删除场景多选附件的权限控制细节4.1 多选删除的前端交互标题里的场景说的是“附件允许多选”所以前端一定有批量删除的入口。批量删除和单条删除的最大差别在于选中的附件里可能混有别人上传的附件。交互上我建议采用“严格模式”只要选中项里有一条不是当前用户上传的点“批量删除”就弹提示说明哪些附件无权删除一个都不删。原因很简单用户在操作时心理预期是“把我选中的东西删掉”如果系统悄悄跳过几条用户会以为删除逻辑有bug。具体判断逻辑在前端做就行因为列表接口已经返回了canDeletehandleBatchDelete() { const selected this.rows.filter(row row.checked); const hasNoPermission selected.some(row row.canDelete ! 1); if (hasNoPermission) { this.$message.warning(所选附件中包含非本人上传的附件请取消勾选后再执行删除); return; } const ids selected.map(row row.id); this.confirmBatchDelete(ids); }4.2 后端批量校验的两种策略后端批量删除的时候也要明确策略。我建议默认“全有或全无”只要有一条无权删整批拒绝。我在前面的Java示例代码里用的就是这个策略循环里发现任何一条不满足条件就抛异常。另一种策略是“跳过无权限项删除可删除项”。这种策略需要一个更精细的返回结构告诉前端哪些删成功了、哪些跳过了、跳过原因是什么。从技术上说不算难但从用户感知来说很容易让人困惑“它怎么给我删了一半另一半为什么不删”所以在非特殊要求下我建议优先用全有或全无。4.3 多选删除的并发问题多选删除还有一个并发问题值得说一说。假设A和B都在处理同一张业务单A把附件1和附件2勾选删除B同时也打开了附件列表。A删完之后B再提交删除如果B在前端没有刷新列表它提交的附件ID里可能就有已经被逻辑删除的记录。所以在后端删除逻辑里校验通过之后真正执行update时要按id和del_flag0作为更新条件并判断更新行数。更新行数和请求的附件数对不上就说明有附件被并发删除或状态已变化这时要返回一个明确的提示让用户刷新列表后重试。这段校验代码虽然多几行但能挡掉很多说不清的线上问题。5. 常见问题与避坑实录5.1 “前端隐藏按钮后接口还是能被调用”这是权限控制最容易踩的坑。浏览器里看不到删除按钮不代表不能调接口。开发者工具打开找到删除接口直接用控制台或工具重放请求带上一别人的附件ID如果后端没做校验附件就被删了。所以在这个需求里我反复强调前端控制只是用户体验层面的保护真正的权限边界必须由后端接口保证。如果你接手的老系统后端完全没有校验优先补后端前端隐藏按钮只能算临时止血。5.2 历史数据没有uploader_id老项目里常见的情况是附件表早期没记录上传人字段为空或默认值是“admin”。这种情况下权限判断会变成“谁都不是上传人”导致所有人都删不了附件。我的处理经验是按三种情况分别处理有创建人字段的用创建人字段反填附件表的上传人字段分不清归属的就设置成“系统迁移”或“管理员”然后规定这类附件只能由系统管理员删除业务要求不高的场景直接在上传人为空时放行管理员、拦截普通用户。无论选哪个都要在删除接口里考虑空值分支否则会变成“空指针”或“判断永远不成立”这是实现时最容易忽略的边界。5.3 同一个附件被多个业务记录引用“附件属于某条业务记录”是我们默认的模型但实际企业场景里附件可能被复制、被关联到多个单据。比如一个产品说明文档既挂在合同A上又挂在合同B上。这时候如果A的经办人删了附件B的记录就变成“引用了一个不存在的文件”。我的经验是物理删除必须非常谨慎尽量用逻辑删除。被引用多的附件要么在删除接口里做引用计数检查有引用时提示“该附件正在被X个单据使用不能删除”要么在业务层面规定附件是共享资源只有上传人且引用数为0时才能删除。标题这个需求没提引用场景但做企业应用久了你会发现这个坑迟早会遇到。5.4 附件权限和业务状态打架只判断“是不是本人上传”还不够很多业务单据有状态机。我举个例子报销单在“草稿”状态时经办人可以自由增删附件提交进入审批流之后附件内容就要固化删除操作应该被禁止。这时候删除校验就不能只看上传人还要检查单据的当前状态。实现上在删除接口里先查业务主表状态状态不允许删除附件时直接返回“当前单据状态不允许删除附件”。这个校验可以放在归属校验之前也可以放在之后看具体业务流程要求。标题场景没有提及状态但如果你做的系统里业务单据有流转状态建议顺手把这个判断加上。5.5 EOS 8.3.3平台相关的两个开发小坑EOS平台开发过程中我遇到过两个很典型的问题顺便记录一下。第一个是Java扩展类修改后没有生效。EOS逻辑流在调用扩展类时有时会走缓存本地开发环境中改了Java代码后如果没重新启动服务或没清缓存调用结果可能还是旧逻辑。排查办法是确认部署路径有没有覆盖掉旧的class文件必要时重启服务后再验证。第二个是附件存储根路径的配置不一致。上传时用的路径配置和删除时清理物理文件的路径配置必须保持一致如果一个是相对路径一个是绝对路径或者一个带结尾斜杠一个不带很容易出现“记录删了但文件没删掉”的情况。这些细节点排查起来很费时间建议一开始就用统一的常量配置不要各处手写路径。6. 最后说点实在的这个需求做完之后我自己最大的体会是权限控制这东西一定要分三层来看。数据层要把归属关系记录清楚服务层要把校验逻辑收死前端层只负责把权限状态用合理的交互呈现给用户。少任何一层短期看都能跑长期看都是隐患。再分享一个小技巧做附件功能的时候不管需求里有没有提审计要求建议把“谁在什么时候删了哪个附件”这个日志顺手加上。日志加上之后不会立刻有什么收益但一旦出了数据丢失或者权限纠纷它是唯一的排查线索。如果你也在做类似的EOS项目或者正好在处理“附件多选上传、多人协作、权限控制”这一类问题希望这篇内容能帮你少踩几个坑。权限设计不复杂但细节决定了这套功能能不能在真实业务里撑得住。
返回列表