1. 修改 SysNotification 实体,新增 senderId, senderName, targetType 字段 2. 新增 SendNotificationRequest 请求DTO 3. 扩展通知类型至6种(新增用户通知、课程通知) 4. 实现角色层级权限控制,支持多级管理员通知下级 5. 支持老师群发课程通知给学生 6. 新增批量发送接口和权限配置
170 lines
6.8 KiB
Java
170 lines
6.8 KiB
Java
package art.kexue.sxwz.aspect;
|
||
|
||
import art.kexue.sxwz.exception.BizException;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.aspectj.lang.ProceedingJoinPoint;
|
||
import org.aspectj.lang.annotation.Around;
|
||
import org.aspectj.lang.annotation.Aspect;
|
||
import org.aspectj.lang.reflect.MethodSignature;
|
||
import org.redisson.api.RedissonClient;
|
||
import org.springframework.stereotype.Component;
|
||
|
||
import javax.annotation.Resource;
|
||
import java.util.Collections;
|
||
import java.util.List;
|
||
|
||
@Aspect
|
||
@Component
|
||
@Slf4j
|
||
public class EduPermissionAspect {
|
||
|
||
@Resource
|
||
private RedissonClient redissonClient;
|
||
|
||
@Around("execution(* art.kexue.sxwz.service.impl.Edu*ServiceImpl.*(..))")
|
||
public Object checkEduPermission(ProceedingJoinPoint joinPoint) throws Throwable {
|
||
try {
|
||
cn.dev33.satoken.stp.StpUtil.checkLogin();
|
||
|
||
Long userId = cn.dev33.satoken.stp.StpUtil.getLoginIdAsLong();
|
||
String token = cn.dev33.satoken.stp.StpUtil.getTokenValue();
|
||
|
||
// SUPER角色拥有所有权限,直接放行
|
||
if (cn.dev33.satoken.stp.StpUtil.hasRole("SUPER")) {
|
||
log.info("用户ID: {} 拥有SUPER角色,直接放行,访问方法: {}", userId, joinPoint.getSignature().getName());
|
||
return joinPoint.proceed();
|
||
}
|
||
|
||
List<String> permissions = getUserPermissions(token);
|
||
|
||
String requiredPermission = generatePermissionCode(joinPoint);
|
||
|
||
log.info("用户ID: {}, 访问方法: {}, 需要权限: {}",
|
||
userId,
|
||
joinPoint.getSignature().getName(),
|
||
requiredPermission);
|
||
|
||
if (permissions == null || permissions.isEmpty()) {
|
||
log.warn("用户未配置任何权限");
|
||
throw new BizException(403, "没有相应操作权限");
|
||
}
|
||
|
||
if (!permissions.contains(requiredPermission)) {
|
||
log.warn("用户缺少权限: {}", requiredPermission);
|
||
throw new BizException(403, "没有相应操作权限");
|
||
}
|
||
|
||
log.info("权限验证通过: {}", requiredPermission);
|
||
|
||
} catch (cn.dev33.satoken.exception.NotLoginException e) {
|
||
log.error("未登录:{}", e.getMessage());
|
||
throw new BizException(401, "请先登录认证后操作");
|
||
} catch (BizException e) {
|
||
throw e;
|
||
} catch (Exception e) {
|
||
log.error("权限验证失败:{}", e.getMessage());
|
||
throw new BizException(403, "权限验证失败");
|
||
}
|
||
|
||
return joinPoint.proceed();
|
||
}
|
||
|
||
private List<String> getUserPermissions(String token) {
|
||
try {
|
||
String loginUserJson = (String) redissonClient.getBucket("loginUser:" + token).get();
|
||
if (loginUserJson != null && !loginUserJson.isEmpty()) {
|
||
cn.hutool.json.JSONObject jsonObject = cn.hutool.json.JSONUtil.parseObj(loginUserJson);
|
||
cn.hutool.json.JSONArray permissionsArray = jsonObject.getJSONArray("permissions");
|
||
if (permissionsArray != null) {
|
||
return permissionsArray.toList(String.class);
|
||
}
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("从Redis获取用户权限失败:{}", e.getMessage());
|
||
}
|
||
return Collections.emptyList();
|
||
}
|
||
|
||
private String generatePermissionCode(ProceedingJoinPoint joinPoint) {
|
||
String className = joinPoint.getTarget().getClass().getSimpleName();
|
||
String methodName = joinPoint.getSignature().getName();
|
||
|
||
String module = extractModule(className);
|
||
String action = extractAction(methodName);
|
||
|
||
return String.format("%s:%s", module, action);
|
||
}
|
||
|
||
private String extractModule(String className) {
|
||
if (className.startsWith("EduCourse")) {
|
||
return "course";
|
||
} else if (className.startsWith("EduStudent")) {
|
||
return "user:student";
|
||
} else if (className.startsWith("EduTeacher")) {
|
||
return "user:teacher";
|
||
} else if (className.startsWith("EduHomework")) {
|
||
return "homework";
|
||
} else if (className.startsWith("EduExam")) {
|
||
return "exam";
|
||
} else if (className.startsWith("EduAttendance")) {
|
||
return "attendance";
|
||
} else if (className.startsWith("EduExcellentWork")) {
|
||
return "excellent";
|
||
}else if (className.startsWith("EduSchool")) {
|
||
return "school";
|
||
} else {
|
||
return className.replace("Edu", "").replace("ServiceImpl", "").toLowerCase();
|
||
}
|
||
}
|
||
|
||
private String extractAction(String methodName) {
|
||
if (methodName.startsWith("save") || methodName.startsWith("create")) {
|
||
return "add";
|
||
} else if (methodName.startsWith("update") && !methodName.startsWith("updateStatus")) {
|
||
return "update"; // 修改:update方法返回update而非edit
|
||
} else if (methodName.startsWith("updateStatus")) {
|
||
return "updateStatus"; // 新增:状态变更方法
|
||
} else if (methodName.startsWith("edit")) {
|
||
return "edit";
|
||
} else if (methodName.startsWith("delete") || methodName.startsWith("remove")) {
|
||
return "delete";
|
||
} else if (methodName.equals("pageList")) {
|
||
return "pageList"; // 新增:分页查询方法
|
||
} else if (methodName.startsWith("query") || methodName.startsWith("get") || methodName.startsWith("list")) {
|
||
return "query";
|
||
} else if (methodName.startsWith("addStudent")) {
|
||
return "student:add";
|
||
} else if (methodName.startsWith("removeStudent")) {
|
||
return "student:remove";
|
||
} else if (methodName.startsWith("submit")) {
|
||
return "submit";
|
||
} else if (methodName.startsWith("grade")) {
|
||
return "grade";
|
||
} else if (methodName.startsWith("publish")) {
|
||
return "publish";
|
||
} else if (methodName.startsWith("mark")) {
|
||
return "mark";
|
||
} else if (methodName.startsWith("addLike")) {
|
||
return "like";
|
||
} else if (methodName.startsWith("removeLike")) {
|
||
return "like:remove";
|
||
} else if (methodName.startsWith("countLikes")) {
|
||
return "like:count";
|
||
} else if (methodName.startsWith("recordAttendance")) {
|
||
return "record";
|
||
} else if (methodName.startsWith("batchCreate")) {
|
||
return "batch:add";
|
||
} else if (methodName.startsWith("isStudentIn")) {
|
||
return "student:exists";
|
||
} else if (methodName.startsWith("sendNotification")) {
|
||
return "notification:send";
|
||
} else if (methodName.startsWith("markAsRead")) {
|
||
return "notification:read";
|
||
} else if (methodName.startsWith("createMakeup")) {
|
||
return "makeup";
|
||
} else {
|
||
return methodName.toLowerCase();
|
||
}
|
||
}
|
||
}
|