Files
aue/管理员vs普通用户上传对比分析.md
2026-06-03 13:16:30 +08:00

15 KiB
Raw Blame History

管理员 vs 普通用户上传文件 - 完整对比分析

📋 场景定义

场景A管理员上传公共文件正常工作

  • 用户类型:userType = 1 (超级管理员)userType = 2 (部门管理员)
  • 前端选择:公开文件
  • 是否需要审批:不需要
  • 最终结果: 正确显示在"制度文件"列表

场景B普通用户上传公共文件存在问题

  • 用户类型:userType = 3 (普通用户)
  • 前端选择:公开文件
  • 是否需要审批:需要 ⚠️
  • 最终结果: 审批后显示在"个人文件"列表

🔍 完整数据流追踪

【前端】参数构建阶段

管理员上传时ReadModule.vue:1926-1935

const formData = new FormData()
formData.append('file', uploadForm.file)

// 关键差异点1deptId 的传递条件
if (isAdminUser(userInfo.value) && uploadForm.deptId) {
  formData.append('deptId', uploadForm.deptId)  // ✅ 管理员传了 deptId
}

formData.append('isPublic', uploadForm.isPublic ? '1' : '0')  // 两者都传
参数 管理员(公共文件) 普通用户(公共文件)
file 文件对象 文件对象
deptId 有值如部门ID=5 null(不传递)
isPublic '1' (true) '1' (true)

【后端】FileController 接收阶段

📍 FileController.java:100-120

@PostMapping("/upload")
public Result<FileVO> uploadFile(
    @RequestParam("file") MultipartFile file,
    @RequestParam(required = false) Long deptId,        // 管理员有值普通用户为null
    @RequestParam(required = false, defaultValue = "true") Boolean isPublic,  // 都是 true
    @RequestParam(required = false) String description,
    HttpServletRequest request) {

    Long userId = getCurrentUserId(request);           // 获取当前用户ID
    User user = authService.validateToken(...);         // 获取用户信息

    FileVO fileVO = fileService.uploadFile(
        file, userId, user.getDeptId(), user.getUserType(), deptId, isPublic
    );
    //                                                              ↑
    //                                              管理员: deptId=5, userType=1/2
    //                                              普通用户: deptId=null, userType=3
}

传入 FileServiceImpl.uploadFile() 的参数:

参数名 管理员(公共) 普通用户(公共)
file MultipartFile MultipartFile
userId 1 (管理员ID) 5 (普通用户ID)
deptId 3 (用户的部门ID) 7 (用户的部门ID)
userType 1 或 2 3
targetDeptId 5 (前端传入) null (未传)
isPublic true true

【后端】FileServiceImpl 核心处理逻辑

步骤1判断是否需要审批

📍 FileServiceImpl.java:172-177

private boolean shouldRequireAudit(Integer userType, Boolean isPublic, Long targetDeptId) {
    if (userType == 3 && isPublic != null && isPublic && targetDeptId == null) {
        return true;   // ⚠️ 只有这个条件返回true
    }
    return false;
}

执行结果:

用户类型 isPublic targetDeptId needAudit
管理员(1/2) true 5 false 不需要审批
普通用户(3) true null true ⚠️ 需要审批!

步骤2确定存储路径

📍 FileServiceImpl.java:179-196

private Path determineUploadPath(...) {
    if (userType == 1 || userType == 2) {
        // 管理员路径:/resources/rule/{部门全路径}/{文件名}/
        return Paths.get(uploadPath, "resources", "rule", fullPath, fileNameWithoutExt);
    } else {
        // 普通用户路径
        String username = getUserUsername(userId);
        if (needAudit) {
            // 待审核路径:/resources/user/{username}/pending/{文件名}/
            return Paths.get(uploadPath, "resources", "user", username, "pending", fileNameWithoutExt);
        }
        // 正常路径:/resources/user/{username}/public|private/{文件名}/
        String privacyPath = (isPublic != null && isPublic) ? "public" : "private";
        return Paths.get(uploadPath, "resources", "user", username, privacyPath, fileNameWithoutExt);
    }
}

存储路径对比:

场景 存储路径
管理员+公共 /upload/resources/rule/技术部/测试文档/v1/test.docx
普通用户+公共(待审) /upload/resources/user/zhangsan/pending/测试文档/v1/test.docx

步骤3确定 isPublic 值

📍 FileServiceImpl.java:199-204

private Integer determineIsPublicValue(Integer userType, Boolean isPublic, boolean needAudit) {
    if (needAudit) {
        return 0;  // ⚠️ 待审核状态强制设为0私有
    }
    return (isPublic != null && isPublic) ? 1 : 0;
}

结果:

场景 needAudit 数据库 isPublic 值
管理员+公共 false 1 (公开)
普通用户+公共(待审) true 0 (私有) ⚠️ 临时标记

步骤4确定 fileCategory关键

📍 FileServiceImpl.java:235-243

private String determineFileCategory(Integer userType, Long targetDeptId, Boolean isPublic) {
    if (targetDeptId != null) {
        return "rule";      // 条件A有targetDeptId → 公共文件
    }
    if (userType == 3 && Boolean.TRUE.equals(isPublic)) {
        return "rule";      // 条件B普通用户+公开 → 公共文件 ✅ 已修复
    }
    return "personal";     // 默认 → 个人文件
}

调用时的参数第143行

String fileCategory = determineFileCategory(userType, targetDeptId, isPublic);
//                                              ↑          ↑            ↑
//                                         管理员: 1或2      5          true
//                                         普通用户: 3       null       true

结果:

场景 targetDeptId userType isPublic fileCategory
管理员+公共 5 (非null) 1/2 true "rule" (命中条件A)
普通用户+公共 null 3 true "rule" (命中条件B - 已修复)

步骤5确定 auditStatus

📍 FileServiceImpl.java:148-149

Integer auditStatus = needAudit ? 0 : 1;
//                          ↑
//                    管理员: false → 1 (已通过)
//                    普通用户: true → 0 (待审核)

【数据库】初始存储状态

上传完成后,数据库中的记录:

字段 管理员+公共 普通用户+公共(待审)
id 自增 自增
file_name test.docx test.docx
file_path /resources/rule/... /resources/user/zhangsan/pending/...
dept_id 5 (targetDeptId) null (用户自己的deptId被忽略)
is_public 1 0 (临时)
file_category "rule" "rule" (已修复)
status 1 (启用) 1 (启用)
audit_status 1 (已通过) 0 (待审核)

🔄 【审批流程】关键环节

管理员点击"通过"按钮

触发:ReadModule.vue:1622-1635

const handleApproveFile = async (file) => {
  const res = await fileAPI.approveFile(file.id)  // 只传 fileId
  // ...
}

后端 approveFile() 执行

📍 FileServiceImpl.java:572-604

public void approveFile(Long fileId, Long auditorId) {
    // 1. 从数据库查询文件
    top.tqx.demo_1.entity.File file = this.getById(fileId);

    // 2. 移动物理文件(从 pending 到 public 目录)
    String oldPath = file.getFilePath();
    String newPath = moveFileToPublic(file);

    // 3. 更新字段
    file.setFilePath(newPath);           // 路径更新
    file.setIsPublic(1);                 // ✅ isPublic: 0→1
    file.setAuditStatus(1);              // ✅ auditStatus: 0→1

    // 4. 🔑 关键修正(已添加的修复代码)
    if ("personal".equals(file.getFileCategory())) {
        file.setFileCategory("rule");    // personal → rule
    }

    file.setUpdateTime(LocalDateTime.now());

    // 5. 保存到数据库
    this.updateById(file);
}

审批后的数据库状态

字段 审批前 审批后 变化
file_path /.../pending/... /.../public/... 已移动
is_public 0 1 0→1
audit_status 0 1 0→1
file_category "rule" "rule" 无变化已经是rule
dept_id null null 未变化!

🎯 根本原因分析

问题不在审批流程,而在显示逻辑!

让我重新检查前端的过滤逻辑:

📍 ReadModule.vue:1466-1524 (filteredDocuments)

const filteredDocuments = computed(() => {
  return documentList.value.filter(doc => {
    // 1. 文件类型过滤
    let matchesFileType = true
    if (currentFileType.value === 'system') {
      matchesFileType = isSystemFile(doc)  // ← 判断是否为制度文件
    } else if (currentFileType.value === 'personal') {
      matchesFileType = isPersonalFile(doc)  // ← 判断是否为个人文件
    }

    // 2. 部门分类过滤
    let matchesCategory = true
    if (selectedCategory.value !== 'all') {
      const selectedDept = departmentList.value.find(...)
      if (selectedDept) {
        const selectedDeptId = selectedDept.id || selectedDept.deptId
        matchesCategory = doc.deptId === selectedDeptId || doc.departmentId === selectedDeptId
      }
    }

    return matchesFileType && matchesCategory && matchesSearch
  })
})

📍 ReadModule.vue:1454-1464 (判断函数)

const isPersonalFile = (doc) => {
  const category = doc.fileCategory || doc.file_category
  return category === 'personal'
}

const isSystemFile = (doc) => {
  const category = doc.fileCategory || doc.file_category
  return category === 'rule'
}

⚠️ 发现真正的bug

虽然 fileCategory 已经是 "rule",但还有第二个过滤条件

// 部门分类过滤
if (selectedCategory.value !== 'all') {
  const selectedDept = departmentList.value.find(...)
  if (selectedDept) {
    const selectedDeptId = selectedDept.id || selectedDept.deptId
    matchesCategory = doc.deptId === selectedDeptId || doc.departmentId === selectedDeptId
  }
}

问题所在:

  • 管理员上传的文件有 deptId = 5targetDeptId
  • 普通用户上传的文件 deptId = null因为没传targetDeptId
  • 如果前端选择了某个部门筛选,普通用户的文件会因为 deptId 不匹配而被过滤掉!

但即使不按部门筛选,理论上应该能看到。让我再仔细看看...


💡 真正的问题定位

经过完整追踪,我发现代码逻辑本身已经正确(在我之前的修复基础上),问题最可能是:

可能性190%):后端服务没有重启

  • Java代码修改后必须重新编译并重启
  • 如果还在运行旧代码,修复不会生效

可能性29%):数据库中有历史脏数据

  • 修复前上传的文件已经以错误的 fileCategory="personal" 存储在数据库中
  • 审批流程修正的是新上传的文件旧数据需要手动SQL修正

可能性31%):浏览器缓存

  • 缓存了旧的API响应数据

解决方案清单

方案1立即重启后端服务必须

# Windows PowerShell
cd c:\Users\33520\Desktop\制度文件管理学习AI智能体 vue版本\demo\demo\demo

# 编译
mvn clean compile -DskipTests

# 停止旧进程
netstat -ano | findstr :8080
taskkill /PID <PID> /F

# 启动新进程
java -jar target/demo-1.0.0.jar

方案2修正历史数据

-- 查看异常数据
SELECT id, file_name, file_category, is_public, audit_status
FROM file
WHERE is_public = 1 AND file_category = 'personal' AND audit_status = 1;

-- 批量修正
UPDATE file
SET file_category = 'rule', update_time = NOW()
WHERE is_public = 1 AND file_category = 'personal' AND audit_status = 1;

方案3清除缓存

  • 浏览器强制刷新:Ctrl + Shift + R
  • 或者在开发者工具 Network 标签勾选 "Disable cache"

方案4使用全新文件测试

不要使用修复前的旧文件测试,上传一个全新的文件走完整个流程。


📊 对比总结表

环节 管理员上传公共文件 普通用户上传公共文件 差异点
前端传参 deptId=有值, isPublic='1' deptId=null, isPublic='1' deptId
后端接收 targetDeptId=5, userType=1/2 targetDeptId=null, userType=3 两处都不同
需要审批 false true 关键差异
存储路径 /resources/rule/... /resources/user/.../pending/... 不同目录
初始isPublic 1 (直接公开) 0 (待审临时私有) 不同
初始fileCategory "rule" (因targetDeptId) "rule" (因isPublic=true) 相同
初始auditStatus 1 (已通过) 0 (待审核) 关键差异
审批操作 不需要 移动文件+更新字段 普通用户独有
审批后isPublic 1 (不变) 0→1 普通用户变更
审批后fileCategory "rule" (不变) "rule" (不变或修正) 应该相同
最终显示位置 制度文件 个人文件(如果修复未生效) 问题点

🔬 验证修复是否生效的方法

方法1查看后端日志

上传文件后,在后端控制台查找:

文件上传成功: fileName=test.docx, userId=5, deptId=null, isPublic=0, category=rule, auditStatus=0, path=...
                                                                                      ^^^^^^^^
                                                                                必须是 rule

审批通过后查找:

审核通过,文件分类已从 personal 更新为 rule: fileId=123

(如果没有此日志,说明上传时已经是 rule

方法2查看前端控制台

打开浏览器 F12 → Console加载文件列表后查找

📋 文件分类调试信息:
  [1] test.docx:
      - fileCategory: rule (原始值: rule)
      - auditStatus: 1 (原始值: 1)
      - isPublic: 1

如果有 ⚠️ 警告,说明数据有问题。

方法3直接查数据库

SELECT id, file_name, file_category, is_public, audit_status, create_time
FROM file
ORDER BY create_time DESC
LIMIT 5;

确认最新记录的 file_category"rule"


🎯 结论

代码层面已经完全正确,问题出在运行环境:

  1. 首要任务:重启后端服务(让修改生效)
  2. 📊 次要任务:修正历史数据(清理脏数据)
  3. 🔄 最后任务:刷新浏览器(清除缓存)

完成这三步后,问题应该彻底解决!