“0826权限已修复”
This commit is contained in:
1 parent
93711d76a3
commit
3dc8cf677a
10 files changed
+271
-10
No files matched your search
+26
-3
@@ -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);
|
||||
|
||||
+11
-1
@@ -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);
|
||||
}
|
||||
|
||||
+6
@@ -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;
|
||||
}
|
||||
+7
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+12
-4
@@ -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);
|
||||
|
||||
+24
@@ -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);
|
||||
|
||||
|
||||
+22
@@ -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` - 后端开发规范
|
||||
@@ -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({
|
||||
|
||||
@@ -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'
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user