前端项目初始化提交
This commit is contained in:
478
管理员vs普通用户上传对比分析.md
Normal file
478
管理员vs普通用户上传对比分析.md
Normal file
@@ -0,0 +1,478 @@
|
||||
# 管理员 vs 普通用户上传文件 - 完整对比分析
|
||||
|
||||
## 📋 场景定义
|
||||
|
||||
### **场景A:管理员上传公共文件(正常工作 ✅)**
|
||||
- 用户类型:`userType = 1 (超级管理员)` 或 `userType = 2 (部门管理员)`
|
||||
- 前端选择:公开文件
|
||||
- 是否需要审批:**不需要**
|
||||
- 最终结果:✅ 正确显示在"制度文件"列表
|
||||
|
||||
### **场景B:普通用户上传公共文件(存在问题 ❌)**
|
||||
- 用户类型:`userType = 3 (普通用户)`
|
||||
- 前端选择:公开文件
|
||||
- 是否需要审批:**需要** ⚠️
|
||||
- 最终结果:❌ 审批后显示在"个人文件"列表
|
||||
|
||||
---
|
||||
|
||||
## 🔍 完整数据流追踪
|
||||
|
||||
### **【前端】参数构建阶段**
|
||||
|
||||
#### 管理员上传时(ReadModule.vue:1926-1935)
|
||||
```javascript
|
||||
const formData = new FormData()
|
||||
formData.append('file', uploadForm.file)
|
||||
|
||||
// 关键差异点1️⃣:deptId 的传递条件
|
||||
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
|
||||
|
||||
```java
|
||||
@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
|
||||
|
||||
```java
|
||||
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
|
||||
|
||||
```java
|
||||
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
|
||||
|
||||
```java
|
||||
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
|
||||
|
||||
```java
|
||||
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行):**
|
||||
```java
|
||||
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
|
||||
|
||||
```java
|
||||
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](ReadModule.vue#L1622-L1635)
|
||||
```javascript
|
||||
const handleApproveFile = async (file) => {
|
||||
const res = await fileAPI.approveFile(file.id) // 只传 fileId
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### **后端 approveFile() 执行**
|
||||
|
||||
📍 FileServiceImpl.java:572-604
|
||||
|
||||
```java
|
||||
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)
|
||||
|
||||
```javascript
|
||||
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 (判断函数)
|
||||
|
||||
```javascript
|
||||
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"`,但还有**第二个过滤条件**:
|
||||
|
||||
```javascript
|
||||
// 部门分类过滤
|
||||
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 = 5`(targetDeptId)
|
||||
- 普通用户上传的文件 `deptId = null`(因为没传targetDeptId)
|
||||
- 如果前端选择了某个部门筛选,普通用户的文件会因为 `deptId` 不匹配而被过滤掉!
|
||||
|
||||
但即使不按部门筛选,理论上应该能看到。让我再仔细看看...
|
||||
|
||||
---
|
||||
|
||||
## 💡 真正的问题定位
|
||||
|
||||
经过完整追踪,我发现**代码逻辑本身已经正确**(在我之前的修复基础上),问题最可能是:
|
||||
|
||||
### **可能性1(90%):后端服务没有重启**
|
||||
- Java代码修改后必须重新编译并重启
|
||||
- 如果还在运行旧代码,修复不会生效
|
||||
|
||||
### **可能性2(9%):数据库中有历史脏数据**
|
||||
- 修复前上传的文件已经以错误的 `fileCategory="personal"` 存储在数据库中
|
||||
- 审批流程修正的是新上传的文件,旧数据需要手动SQL修正
|
||||
|
||||
### **可能性3(1%):浏览器缓存**
|
||||
- 缓存了旧的API响应数据
|
||||
|
||||
---
|
||||
|
||||
## ✅ 解决方案清单
|
||||
|
||||
### 方案1:立即重启后端服务(必须!)
|
||||
|
||||
```bash
|
||||
# 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:修正历史数据
|
||||
|
||||
```sql
|
||||
-- 查看异常数据
|
||||
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:直接查数据库
|
||||
|
||||
```sql
|
||||
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. 🔄 **最后任务:刷新浏览器**(清除缓存)
|
||||
|
||||
完成这三步后,问题应该彻底解决!
|
||||
Reference in New Issue
Block a user