|
@@ -0,0 +1,283 @@
|
|
|
|
+package com.fs.framework.aspectj;
|
|
|
|
+
|
|
|
|
+import com.alibaba.druid.support.json.JSONUtils;
|
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
+import com.fs.app.annotation.UserOperationLog;
|
|
|
|
+import com.fs.app.utils.JwtUtils;
|
|
|
|
+import com.fs.common.core.domain.R;
|
|
|
|
+import com.fs.common.param.LoginMaWxParam;
|
|
|
|
+import com.fs.common.utils.DateUtils;
|
|
|
|
+import com.fs.common.utils.ServletUtils;
|
|
|
|
+import com.fs.course.domain.FsCourseQuestionBank;
|
|
|
|
+import com.fs.course.mapper.FsUserCourseVideoMapper;
|
|
|
|
+import com.fs.course.param.FsCourseQuestionAnswerUParam;
|
|
|
|
+import com.fs.course.param.FsCourseSendRewardUParam;
|
|
|
|
+import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
|
|
|
|
+import com.fs.course.vo.FsUserCourseVO;
|
|
|
|
+import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
|
|
|
|
+import com.fs.his.domain.FsUser;
|
|
|
|
+import com.fs.his.domain.FsUserOperationLog;
|
|
|
|
+import com.fs.his.enums.FsUserOperationEnum;
|
|
|
|
+import com.fs.his.mapper.FsUserMapper;
|
|
|
|
+import com.fs.his.mapper.FsUserOperationLogMapper;
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
+import org.aspectj.lang.JoinPoint;
|
|
|
|
+import org.aspectj.lang.Signature;
|
|
|
|
+import org.aspectj.lang.annotation.*;
|
|
|
|
+import org.aspectj.lang.reflect.MethodSignature;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
+
|
|
|
|
+import java.lang.reflect.Method;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Map;
|
|
|
|
+
|
|
|
|
+@Slf4j
|
|
|
|
+@Aspect
|
|
|
|
+@Component
|
|
|
|
+public class UserOperationLogAspect {
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private FsUserOperationLogMapper logMapper;
|
|
|
|
+ @Autowired
|
|
|
|
+ private FsUserMapper userMapper;
|
|
|
|
+ @Autowired
|
|
|
|
+ private FsUserCourseVideoMapper userCourseVideoMapper;
|
|
|
|
+ @Autowired
|
|
|
|
+ JwtUtils jwtUtils;
|
|
|
|
+
|
|
|
|
+ private final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
|
+ private static final ThreadLocal<FsUserOperationLog> LOG_HOLDER = new ThreadLocal<>();
|
|
|
|
+
|
|
|
|
+ @Pointcut("@annotation(com.fs.app.annotation.UserOperationLog)")
|
|
|
|
+ public void logPointcut() {}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ @AfterReturning(pointcut = "logPointcut()", returning = "result")
|
|
|
|
+ public void doAfterReturning(JoinPoint joinPoint, Object result) {
|
|
|
|
+ handleLog(joinPoint, null);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
|
|
|
|
+ public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
|
|
|
|
+ handleLog(joinPoint, e);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @AfterReturning(pointcut = "@annotation(userOpLog)", returning = "result")
|
|
|
|
+ public void afterReturning(JoinPoint joinPoint,UserOperationLog userOpLog, Object result) {
|
|
|
|
+ FsUserOperationLog operationLog = LOG_HOLDER.get();
|
|
|
|
+ if (operationLog == null) return;
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ if (operationLog.getUserId() == null){
|
|
|
|
+ FsUser fsUser = extractUserFromResult(result);
|
|
|
|
+ if (fsUser != null && operationLog.getUserId() == null) {
|
|
|
|
+ operationLog.setUserId(fsUser.getUserId());
|
|
|
|
+ StringBuilder details = getDetail(
|
|
|
|
+ userOpLog,
|
|
|
|
+ FsUserOperationEnum.toType(operationLog.getOperationType()),
|
|
|
|
+ fsUser,
|
|
|
|
+ joinPoint.getArgs());
|
|
|
|
+ operationLog.setDetails(details.toString());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ logMapper.insertFsUserOperationLog(operationLog);
|
|
|
|
+ } catch (Exception ex) {
|
|
|
|
+ log.error("操作日志插入异常", ex);
|
|
|
|
+ } finally {
|
|
|
|
+ LOG_HOLDER.remove();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void handleLog(JoinPoint joinPoint, Exception e) {
|
|
|
|
+ FsUserOperationLog operationLog = new FsUserOperationLog();
|
|
|
|
+ LOG_HOLDER.set(operationLog);
|
|
|
|
+ try {
|
|
|
|
+ //时间
|
|
|
|
+ operationLog.setCreateTime(DateUtils.getNowDate());
|
|
|
|
+ //操作类型
|
|
|
|
+ Method method = getMethod(joinPoint);
|
|
|
|
+ UserOperationLog annotation = method.getAnnotation(UserOperationLog.class);
|
|
|
|
+ if (annotation == null) return;
|
|
|
|
+ operationLog.setOperationType(annotation.operationType().getLabel());
|
|
|
|
+
|
|
|
|
+ //用户
|
|
|
|
+ Long userId =null;
|
|
|
|
+ try {
|
|
|
|
+ userId = Long.valueOf(jwtUtils.getClaimByToken(ServletUtils.getRequest().getHeader("APPToken")).getSubject().toString());
|
|
|
|
+ } catch (Exception ie){
|
|
|
|
+ log.info("获取用户id失败");
|
|
|
|
+ }
|
|
|
|
+ if (userId == null) {
|
|
|
|
+ LOG_HOLDER.set(operationLog);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ FsUser fsUser = userMapper.selectFsUserByUserId(userId);
|
|
|
|
+ if (fsUser == null) {
|
|
|
|
+ LOG_HOLDER.set(operationLog);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ operationLog.setUserId(userId);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ StringBuilder details = getDetail(annotation, annotation.operationType(), fsUser,joinPoint.getArgs());
|
|
|
|
+ operationLog.setDetails(details.toString());
|
|
|
|
+
|
|
|
|
+ if (e != null) {
|
|
|
|
+ details.append(",异常: ").append(e.getMessage());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ LOG_HOLDER.set(operationLog);
|
|
|
|
+ } catch (Exception ex) {
|
|
|
|
+ log.error("记录操作日志异常", ex);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private StringBuilder getDetail(UserOperationLog annotation, FsUserOperationEnum opType, FsUser fsUser,Object[] args) {
|
|
|
|
+ StringBuilder details = new StringBuilder();
|
|
|
|
+ if (annotation.detail() == null || annotation.detail().isEmpty()) {
|
|
|
|
+ switch (opType.getValue()) {
|
|
|
|
+ case 1: // 小程序登录
|
|
|
|
+ details.append(fsUser.getNickName())
|
|
|
|
+ .append("在")
|
|
|
|
+ .append(DateUtils.getTime())
|
|
|
|
+ .append("登录了小程序");
|
|
|
|
+ break;
|
|
|
|
+ case 2: // h5登录
|
|
|
|
+ details.append(fsUser.getNickName())
|
|
|
|
+ .append("在")
|
|
|
|
+ .append(DateUtils.getTime())
|
|
|
|
+ .append("登录了h5");
|
|
|
|
+ break;
|
|
|
|
+ case 3: // 成为会员
|
|
|
|
+ details.append(fsUser.getNickName())
|
|
|
|
+ .append("在")
|
|
|
|
+ .append(DateUtils.getTime())
|
|
|
|
+ .append("注册成为会员");
|
|
|
|
+ break;
|
|
|
|
+ case 4: // 判断是否成为会员
|
|
|
|
+ break;
|
|
|
|
+ case 5: // 学习课程
|
|
|
|
+ String courseInfo = extractCourseInfo(args);
|
|
|
|
+ details.append(fsUser.getNickName())
|
|
|
|
+ .append("在")
|
|
|
|
+ .append(DateUtils.getTime())
|
|
|
|
+ .append("观看 ").append(courseInfo);
|
|
|
|
+ break;
|
|
|
|
+ case 6: // 答题
|
|
|
|
+ String answerCourse = answerCourse(args);
|
|
|
|
+ details.append(fsUser.getNickName())
|
|
|
|
+ .append("在 ")
|
|
|
|
+ .append(DateUtils.getTime())
|
|
|
|
+ .append("\n")
|
|
|
|
+ .append(answerCourse);
|
|
|
|
+ break;
|
|
|
|
+ case 7: // 发送奖励
|
|
|
|
+ String sendReward = sendReward(args);
|
|
|
|
+
|
|
|
|
+ details.append(fsUser.getNickName())
|
|
|
|
+ .append("在 ")
|
|
|
|
+ .append(DateUtils.getTime())
|
|
|
|
+ .append("领取红包")
|
|
|
|
+ .append("\n")
|
|
|
|
+ .append(sendReward);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ details.append(opType.getLabel());
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ details.append(annotation.detail());
|
|
|
|
+ }
|
|
|
|
+ return details;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private Method getMethod(JoinPoint joinPoint) throws NoSuchMethodException {
|
|
|
|
+ Signature signature = joinPoint.getSignature();
|
|
|
|
+ MethodSignature methodSignature = (MethodSignature) signature;
|
|
|
|
+ return joinPoint.getTarget().getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ private FsUser extractUserFromResult(Object result) {
|
|
|
|
+ if (result instanceof R) {
|
|
|
|
+ R r = (R) result;
|
|
|
|
+ Object user = r.get("user");
|
|
|
|
+ if (user instanceof FsUser) {
|
|
|
|
+ return (FsUser) user;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+ private String extractCourseInfo(Object[] args) {
|
|
|
|
+ if (args == null) return "未知课程";
|
|
|
|
+
|
|
|
|
+ for (Object arg : args) {
|
|
|
|
+ if (arg instanceof FsUserCourseVideoLinkParam) {
|
|
|
|
+ FsUserCourseVideoLinkParam param = (FsUserCourseVideoLinkParam) arg;
|
|
|
|
+ if (param.getVideoId() != null && param.getPeriodId() != null) {
|
|
|
|
+ FsUserCourseVO vo = userCourseVideoMapper.selectFsUserCourseVideoVoByVideoId(param.getVideoId(), param.getPeriodId());
|
|
|
|
+ if (vo != null){
|
|
|
|
+ FsUserOperationLog operationLog = LOG_HOLDER.get();
|
|
|
|
+ operationLog.setParam(JSON.toJSONString(vo));
|
|
|
|
+ LOG_HOLDER.set(operationLog);
|
|
|
|
+ return "课程:"+ vo.getCourseName() + " 的 " + vo.getTitle() + " 小节";
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return "未知课程";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private String answerCourse(Object[] args) {
|
|
|
|
+ if (args == null) return "未知课程";
|
|
|
|
+ for (Object arg : args) {
|
|
|
|
+ if (arg instanceof FsCourseQuestionAnswerUParam) {
|
|
|
|
+ FsCourseQuestionAnswerUParam param = (FsCourseQuestionAnswerUParam) arg;
|
|
|
|
+ if (param.getVideoId() != null && param.getPeriodId() != null) {
|
|
|
|
+ FsUserCourseVO vo = userCourseVideoMapper.selectFsUserCourseVideoVoByVideoId(param.getVideoId(), param.getPeriodId());
|
|
|
|
+ if (vo != null){
|
|
|
|
+ FsUserOperationLog operationLog = LOG_HOLDER.get();
|
|
|
|
+ operationLog.setParam(JSON.toJSONString(vo));
|
|
|
|
+ LOG_HOLDER.set(operationLog);
|
|
|
|
+ StringBuilder details =new StringBuilder("课程:"+ vo.getCourseName() + " 的 " + vo.getTitle() + " 小节 问题为:");
|
|
|
|
+ List<FsCourseQuestionBank> questions = param.getQuestions();
|
|
|
|
+ if (questions != null && !questions.isEmpty()) {
|
|
|
|
+ for (int i = 0; i < questions.size(); i++) {
|
|
|
|
+ details.append("\n").append(i+1).append(".").append(questions.get(i).getTitle());
|
|
|
|
+ details.append(" 提交答案为:").append(questions.get(i).getAnswer());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return details.toString();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return "未知课程";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private String sendReward(Object[] args) {
|
|
|
|
+ if (args == null) return "未知课程";
|
|
|
|
+ for (Object arg : args) {
|
|
|
|
+ if (arg instanceof FsCourseSendRewardUParam) {
|
|
|
|
+ FsCourseSendRewardUParam param = (FsCourseSendRewardUParam) arg;
|
|
|
|
+ if (param.getVideoId() != null && param.getPeriodId() != null) {
|
|
|
|
+ FsUserCourseVO vo = userCourseVideoMapper.selectFsUserCourseVideoVoByVideoId(param.getVideoId(), param.getPeriodId());
|
|
|
|
+ if (vo != null){
|
|
|
|
+ FsUserOperationLog operationLog = LOG_HOLDER.get();
|
|
|
|
+ operationLog.setParam(JSON.toJSONString(vo));
|
|
|
|
+ LOG_HOLDER.set(operationLog);
|
|
|
|
+ StringBuilder details =new StringBuilder();
|
|
|
|
+ details.append("课程:"+ vo.getCourseName() + " 的 " + vo.getTitle() + " 小节");
|
|
|
|
+ return details.toString();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return "未知课程";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|