“0826权限已修复”

This commit is contained in:
weizhong committed 2025-08-26 13:24:55 +08:00
1 parent 93711d76a3
commit 3dc8cf677a
10 files changed
+271 -10

No files matched your search

@@ -27,6 +27,7 @@ import jakarta.servlet.http.HttpServletResponse;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysUserService;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
/**
* 培训申请控制器
@@ -48,7 +49,7 @@ public class RsTrainingApplicationController extends BaseController {
private ISysUserService sysUserService;
/**
* 查询培训申请列表
* 查询培训申请列表(供管理员和审批人使用)
*/
@SaCheckPermission("rs:trainingapplication:list")
@GetMapping("/list")
@@ -249,9 +250,31 @@ public class RsTrainingApplicationController extends BaseController {
}
/**
* 查询我的培训申请列表
* 查询待审批的培训申请列表(供审批人使用)
*/
@SaCheckPermission("rs:trainingapplication:list")
@SaCheckPermission("rs:trainingapproval:process")
@GetMapping("/pending/list")
public TableDataInfo<RsTrainingApplication> pendingList(RsTrainingApplication application, PageQuery pageQuery) {
// 设置租户ID
if (application.getTenantId() == null || application.getTenantId().isEmpty()) {
application.setTenantId(LoginHelper.getTenantId());
}
// 只查询待审核状态的申请(排除已通过状态)
application.setExcludeStatus("1"); // 排除已通过状态
log.info("查询待审批培训申请列表 - 用户: {}, 角色: {}, 查询条件: {}",
LoginHelper.getUsername(),
StpUtil.getRoleList(),
application);
return trainingApplicationService.selectPageRsTrainingApplicationList(application, pageQuery);
}
/**
* 查询我的培训申请列表(供申请人使用)
*/
@SaCheckPermission("rs:trainingapplication:query")
@GetMapping("/my")
public TableDataInfo<RsTrainingApplication> listMyApplications(RsTrainingApplication application, PageQuery pageQuery) {
return trainingApplicationService.selectPageMyApplications(StpUtil.getLoginIdAsLong(), application, pageQuery);
@@ -40,7 +40,17 @@ public class RsTrainingApprovalController extends BaseController {
*/
@SaCheckPermission("rs:trainingapproval:list")
@GetMapping("/list")
public TableDataInfo<RsTrainingApproval> list(RsTrainingApproval approval, PageQuery pageQuery) {
public TableDataInfo<RsTrainingApproval> list(RsTrainingApproval approval, PageQuery pageQuery,
@RequestParam(value = "courseName", required = false) String courseName,
@RequestParam(value = "userName", required = false) String userName) {
// 参数转换:将前端传递的参数名转换为后端Mapper期望的参数名
if (courseName != null && !courseName.trim().isEmpty()) {
approval.setCourseTitle(courseName.trim());
}
if (userName != null && !userName.trim().isEmpty()) {
approval.setApplicationUserName(userName.trim());
}
// 使用带详细信息的查询方法,返回申请人姓名和课程名称
return trainingApprovalService.selectPageRsTrainingApprovalWithDetailsList(approval, pageQuery);
}
@@ -118,4 +118,10 @@ public class RsTrainingApplication extends BaseEntity {
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date rejectTime;
/**
* 排除状态(查询时排除指定状态,不存储到数据库)
*/
@TableField(exist = false)
private String excludeStatus;
}
@@ -84,6 +84,9 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
} else if (StpUtil.hasRole("rs-admin")) {
// 人事管理员可以看到所有申请(包括审批和部门管理功能)
// 不设置额外过滤条件,让管理员能看到所有申请
} else if (StpUtil.hasRole("rs-Approver")) {
// 审批人角色:可以看到所有申请(用于审批)
// 不设置过滤条件,允许查看所有申请
} else {
// 默认情况:只能看到自己创建的申请
application.setCreateBy(currentUserId);
@@ -196,6 +199,10 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
lqw.eq(application.getStatus() != null, RsTrainingApplication::getStatus, application.getStatus());
lqw.like(application.getLocation() != null, RsTrainingApplication::getLocation, application.getLocation());
lqw.eq(application.getDuration() != null, RsTrainingApplication::getDuration, application.getDuration());
// 课程名称搜索:搜索自定义课程名称字段
lqw.like(application.getCourseName() != null, RsTrainingApplication::getCourseName, application.getCourseName());
// 排除指定状态的条件(用于待审批接口)
lqw.ne(application.getExcludeStatus() != null, RsTrainingApplication::getStatus, application.getExcludeStatus());
return lqw;
}
@@ -78,9 +78,9 @@ public class RsTrainingApprovalServiceImpl implements IRsTrainingApprovalService
// 申请人只能看到与自己相关的审批记录
// 这里需要通过申请表关联查询
lqw.exists("SELECT 1 FROM rs_training_application app WHERE app.application_id = rs_training_approval.application_id AND app.user_id = " + currentUserId);
} else if (StpUtil.hasRole("rs-admin")) {
// 人事管理员可以看到所有审批记录
// 不设置额外过滤条件,让管理员能看到所有审批
} else if (StpUtil.hasRole("rs-admin") || StpUtil.hasRole("rs-Approver")) {
// 人事管理员和审批人可以看到所有审批记录
// 不设置额外过滤条件,让管理员和审批人能看到所有审批
} else {
// 默认情况:只能看到自己创建或处理的审批
lqw.and(wrapper -> wrapper.eq(RsTrainingApproval::getCreateBy, currentUserId)
@@ -126,7 +126,15 @@ public class RsTrainingApprovalServiceImpl implements IRsTrainingApprovalService
@Override
public TableDataInfo<RsTrainingApproval> selectPagePendingApprovals(Long approverId, RsTrainingApproval approval, PageQuery pageQuery) {
LambdaQueryWrapper<RsTrainingApproval> lqw = buildQueryWrapper(approval);
lqw.eq(RsTrainingApproval::getApproverId, approverId);
// 简化权限模型:审批人和管理员可以看到所有待审核记录
if (StpUtil.hasRole("rs-admin") || StpUtil.hasRole("rs-Approver")) {
// 管理员和审批人可以看到所有待审核记录,不限制approverId
} else {
// 其他角色只能看到指派给自己的待审核记录
lqw.eq(RsTrainingApproval::getApproverId, approverId);
}
lqw.eq(RsTrainingApproval::getStatus, "0"); // 待审核状态
com.baomidou.mybatisplus.extension.plugins.pagination.Page<RsTrainingApproval> page = trainingApprovalMapper.selectPage(pageQuery.build(), lqw);
return TableDataInfo.build(page);
@@ -21,6 +21,8 @@ import org.slf4j.LoggerFactory;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.dromara.common.satoken.utils.LoginHelper;
import cn.dev33.satoken.stp.StpUtil;
/**
* 培训记录服务实现类
@@ -61,6 +63,28 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService, Ap
*/
@Override
public TableDataInfo<RsTrainingRecord> selectPageRsTrainingRecordList(RsTrainingRecord record, PageQuery pageQuery) {
// ===== 业务逻辑层权限过滤 =====
// 根据用户角色进行数据权限过滤
if (!LoginHelper.isSuperAdmin()) {
// 获取当前登录用户信息
Long currentUserId = LoginHelper.getUserId();
// 检查用户角色并应用相应的数据过滤
if (StpUtil.hasRole("rs-Applicant")) {
// 申请人只能看到自己的培训记录
record.setUserId(currentUserId);
} else if (StpUtil.hasRole("rs-admin")) {
// 人事管理员可以看到所有培训记录
// 不设置额外过滤条件,让管理员能看到所有记录
} else if (StpUtil.hasRole("rs-Approver")) {
// 审批人角色:可以看到所有培训记录(用于管理)
// 不设置过滤条件,允许查看所有记录
} else {
// 默认情况:只能看到自己的培训记录
record.setUserId(currentUserId);
}
}
// 使用手动分页方式,参考培训申请的实现
List<RsTrainingRecord> allList = trainingRecordMapper.selectRsTrainingRecordList(record);
@@ -60,6 +60,16 @@
<if test="tenantId != null and tenantId != ''">and a.tenant_id = #{tenantId}</if>
<if test="location != null and location != ''">and a.location like concat('%', #{location}, '%')</if>
<if test="duration != null">and a.duration = #{duration}</if>
<!-- 课程名称搜索条件:支持自定义课程名称和标准课程标题 -->
<if test="courseName != null and courseName != ''">
and (
(a.course_id = 0 and a.course_name like concat('%', #{courseName}, '%'))
or
(a.course_id != 0 and c.title like concat('%', #{courseName}, '%'))
)
</if>
<!-- 排除指定状态的条件(用于待审批接口) -->
<if test="excludeStatus != null and excludeStatus != ''">and a.status != #{excludeStatus}</if>
</where>
ORDER BY a.create_time DESC
</select>
@@ -80,6 +90,18 @@
<if test="app.applyTime != null ">and a.apply_time = #{app.applyTime}</if>
<if test="app.currentApprover != null and app.currentApprover != ''">and a.current_approver = #{app.currentApprover}</if>
<if test="app.tenantId != null and app.tenantId != ''">and a.tenant_id = #{app.tenantId}</if>
<if test="app.location != null and app.location != ''">and a.location like concat('%', #{app.location}, '%')</if>
<if test="app.duration != null">and a.duration = #{app.duration}</if>
<!-- 课程名称搜索条件:支持自定义课程名称和标准课程标题 -->
<if test="app.courseName != null and app.courseName != ''">
and (
(a.course_id = 0 and a.course_name like concat('%', #{app.courseName}, '%'))
or
(a.course_id != 0 and c.title like concat('%', #{app.courseName}, '%'))
)
</if>
<!-- 排除指定状态的条件(用于待审批接口) -->
<if test="app.excludeStatus != null and app.excludeStatus != ''">and a.status != #{app.excludeStatus}</if>
</where>
ORDER BY a.create_time DESC
LIMIT #{offset}, #{limit}
@@ -668,3 +668,144 @@
**📝 维护**: 物业报修管理 API 文档
**🔄 更新**: 2025-07-01
**�� 文件**: 物业报修管理 API.md
## 📊 统计分析接口
**详细信息参考**: `docs/业务模块/物业报修管理/统计分析功能.md`
---
## 🔧 培训申请待审批接口 (新增 2025-01-03)
### 查询待审批培训申请列表
**接口地址**: `GET /rs/training/application/pending/list`
**权限要求**: `rs:trainingapproval:process`
**接口说明**: 专门为审批人员提供的待审批培训申请列表接口,解决了rs-Approver角色权限问题
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| pageNum | number | 否 | 页码,默认1 |
| pageSize | number | 否 | 每页数量,默认10 |
| courseName | string | 否 | 课程名称(模糊查询) |
| location | string | 否 | 培训地点(模糊查询) |
| status | string | 否 | 申请状态 |
| excludeStatus | string | 否 | 排除状态(默认排除已通过状态"1") |
#### 响应参数
```json
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"applicationId": 1234567890123456789,
"courseId": 1,
"courseName": "Spring Boot开发培训",
"userId": 123,
"userName": "张三",
"department": "技术部",
"position": "开发工程师",
"reason": "提升技术能力",
"status": "0",
"applyTime": "2025-01-03 10:00:00",
"location": "培训中心A座",
"duration": 3,
"startTime": "2025-01-10 09:00:00",
"endTime": "2025-01-12 17:00:00"
}
],
"total": 15
}
```
#### 状态码说明
| 状态 | 含义 | 类型 |
|------|------|------|
| 0 | 待审核 | warning |
| 1 | 已通过 | success |
| 2 | 已拒绝 | danger |
| 3 | 已取消 | info |
| 4 | 已完成 | success |
#### 权限控制
- **接口权限**: `rs:trainingapproval:process`
- **角色支持**: rs-Approver, rs-admin
- **数据过滤**:
- rs-Approver角色:可查看所有待审批申请
- rs-admin角色:可查看所有申请
- 自动排除已通过状态的申请
#### 使用示例
**前端调用**:
```typescript
import { listPendingTrainingApplication } from '@/api/rs/training'
// 查询待审批列表
const getPendingList = async () => {
const params = {
pageNum: 1,
pageSize: 10,
excludeStatus: '1' // 排除已通过状态
}
const response = await listPendingTrainingApplication(params)
return response
}
```
**Curl示例**:
```bash
curl -X GET "http://localhost:8080/rs/training/application/pending/list?pageNum=1&pageSize=10" \
-H "Authorization: Bearer your-token" \
-H "clientid: e5cd7e4891bf95d1d19206ce24a7b32e" \
-H "referer: http://localhost"
```
#### 与原接口对比
| 项目 | 原接口 | 新接口 |
|------|--------|--------|
| 接口地址 | `/rs/training/application/list` | `/rs/training/application/pending/list` |
| 权限要求 | `rs:trainingapplication:list` | `rs:trainingapproval:process` |
| 角色支持 | rs-admin | rs-Approver, rs-admin |
| 设计目的 | 申请管理 | 审批处理 |
| 权限独立性 | ❌ 权限混合 | ✅ 权限分离 |
#### 技术实现
**后端Controller**:
```java
@SaCheckPermission("rs:trainingapproval:process")
@GetMapping("/pending/list")
public TableDataInfo<RsTrainingApplication> pendingList(
RsTrainingApplication application,
PageQuery pageQuery) {
// 设置租户ID和排除状态
application.setTenantId(LoginHelper.getTenantId());
application.setExcludeStatus("1"); // 排除已通过状态
return trainingApplicationService.selectPageRsTrainingApplicationList(
application, pageQuery);
}
```
**权限过滤逻辑**:
- Service层根据rs-Approver角色自动放开数据权限
- Mapper层支持excludeStatus参数过滤
- 前端调用专门的审批接口,权限控制更精确
---
**接口文档最后更新**: 2025-01-03
**维护责任人**: 系统架构师
**相关文档**:
- `docs/开发文档/培训申请权限一致性修复报告.md`
- `.cursor/cursorrules-backend.md` - 后端开发规范
+19
View File
@@ -52,6 +52,7 @@ export interface ApplicationQuery {
pageSize: number;
courseId?: number | string;
courseName?: string;
location?: string; // 培训地点
userId?: number | string;
userName?: string;
department?: string;
@@ -175,6 +176,15 @@ export function listTrainingApplication(query: ApplicationQuery) {
});
}
// 查询待审批的培训申请列表(供审批人使用)
export function listPendingTrainingApplication(query: ApplicationQuery) {
return request({
url: '/rs/training/application/pending/list',
method: 'get',
params: query
});
}
// 获取培训申请详情
export function getTrainingApplication(applicationId: string | number) {
return request({
@@ -245,6 +255,15 @@ export function listTrainingRecord(query: RecordQuery) {
});
}
// 获取我的培训记录列表
export function listMyTrainingRecord(query: RecordQuery) {
return request({
url: '/rs/training/record/my',
method: 'get',
params: query
});
}
// 获取培训记录详情
export function getTrainingRecord(recordId: string | number) {
return request({
+3 -2
View File
@@ -916,10 +916,11 @@ const handleViewApprovalHistory = async (row: any) => {
}
// 设置当前应用信息
// 兼容两种数据结构:培训申请(courseName, userName) 和 培训记录(courseTitle, applicationUserName)
Object.assign(currentApplication, {
applicationId: applicationId,
courseName: row.courseName || '未知课程',
userName: row.userName || '未知用户',
courseName: row.courseName || row.courseTitle || '未知课程',
userName: row.userName || row.applicationUserName || '未知用户',
applyTime: row.applyTime || '',
status: row.status || '0'
});