Explorar o código

Merge remote-tracking branch 'origin/dev_v2'

yangfeng hai 1 ano
pai
achega
b062b3e294
Modificáronse 73 ficheiros con 3876 adicións e 708 borrados
  1. 236 191
      core/src/main/java/org/jeecg/common/system/util/JwtUtil.java
  2. 30 0
      core/src/main/java/org/jeecg/common/util/MinioUtil.java
  3. 64 0
      core/src/main/java/org/jeecg/common/util/TokenUtil.java
  4. 1 0
      core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java
  5. 0 79
      system/system-biz/src/main/java/org/jeecg/modules/system/controller/LoginController.java
  6. 9 2
      system/system-biz/src/main/java/org/jeecg/modules/system/entity/SysAnnouncement.java
  7. 5 0
      system/system-biz/src/main/java/org/jeecg/modules/system/entity/SysUser.java
  8. 13 9
      system/system-biz/src/main/java/org/jeecg/modules/system/mapper/SysAnnouncementMapper.java
  9. 25 0
      system/system-biz/src/main/java/org/jeecg/modules/system/mapper/xml/SysAnnouncementMapper.xml
  10. 16 10
      system/system-biz/src/main/java/org/jeecg/modules/system/service/ISysAnnouncementService.java
  11. 13 0
      system/system-biz/src/main/java/org/jeecg/modules/system/service/ISysUserService.java
  12. 186 148
      system/system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysAnnouncementServiceImpl.java
  13. 36 2
      system/system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysUserServiceImpl.java
  14. 46 0
      web/src/main/java/com/ynfy/app/api/v1/annoation/ApiLog.java
  15. 15 0
      web/src/main/java/com/ynfy/app/api/v1/annoation/IgnoreAuth.java
  16. 264 0
      web/src/main/java/com/ynfy/app/api/v1/aspect/ApiLogAspect.java
  17. 61 0
      web/src/main/java/com/ynfy/app/api/v1/controller/ApiAircleController.java
  18. 226 0
      web/src/main/java/com/ynfy/app/api/v1/controller/ApiAuthController.java
  19. 40 0
      web/src/main/java/com/ynfy/app/api/v1/controller/ApiBaseController.java
  20. 294 0
      web/src/main/java/com/ynfy/app/api/v1/controller/ApiCommonController.java
  21. 187 0
      web/src/main/java/com/ynfy/app/api/v1/controller/ApiCourseController.java
  22. 214 0
      web/src/main/java/com/ynfy/app/api/v1/controller/ApiExamController.java
  23. 171 0
      web/src/main/java/com/ynfy/app/api/v1/controller/ApiIndexController.java
  24. 78 0
      web/src/main/java/com/ynfy/app/api/v1/controller/ApiPracticeController.java
  25. 90 0
      web/src/main/java/com/ynfy/app/api/v1/controller/ApiRepositoryController.java
  26. 13 0
      web/src/main/java/com/ynfy/app/api/v1/entity/dto/ArticleDTO.java
  27. 13 0
      web/src/main/java/com/ynfy/app/api/v1/entity/dto/CourseDTO.java
  28. 17 0
      web/src/main/java/com/ynfy/app/api/v1/entity/dto/ExamDTO.java
  29. 24 0
      web/src/main/java/com/ynfy/app/api/v1/entity/dto/PracticeDTO.java
  30. 13 0
      web/src/main/java/com/ynfy/app/api/v1/entity/dto/RepositoryDTO.java
  31. 21 0
      web/src/main/java/com/ynfy/app/api/v1/entity/dto/UserInfo.java
  32. 12 0
      web/src/main/java/com/ynfy/app/api/v1/entity/dto/WxLoginDTO.java
  33. 90 0
      web/src/main/java/com/ynfy/app/api/v1/interceptor/AuthorizationInterceptor.java
  34. 59 0
      web/src/main/java/com/ynfy/app/api/v1/util/ApiUserUtils.java
  35. 135 0
      web/src/main/java/com/ynfy/app/api/v1/util/HttpDownUtils.java
  36. 162 0
      web/src/main/java/com/ynfy/buss/article/controller/ArticleController.java
  37. 120 0
      web/src/main/java/com/ynfy/buss/article/entity/Article.java
  38. 14 0
      web/src/main/java/com/ynfy/buss/article/mapper/ArticleMapper.java
  39. 5 0
      web/src/main/java/com/ynfy/buss/article/mapper/xml/ArticleMapper.xml
  40. 18 0
      web/src/main/java/com/ynfy/buss/article/service/IArticleService.java
  41. 28 0
      web/src/main/java/com/ynfy/buss/article/service/impl/ArticleServiceImpl.java
  42. 161 0
      web/src/main/java/com/ynfy/buss/banner/controller/BannerController.java
  43. 106 0
      web/src/main/java/com/ynfy/buss/banner/entity/Banner.java
  44. 14 0
      web/src/main/java/com/ynfy/buss/banner/mapper/BannerMapper.java
  45. 5 0
      web/src/main/java/com/ynfy/buss/banner/mapper/xml/BannerMapper.xml
  46. 14 0
      web/src/main/java/com/ynfy/buss/banner/service/IBannerService.java
  47. 18 0
      web/src/main/java/com/ynfy/buss/banner/service/impl/BannerServiceImpl.java
  48. 4 7
      web/src/main/java/com/ynfy/buss/course/course/controller/CourseController.java
  49. 19 13
      web/src/main/java/com/ynfy/buss/course/course/entity/Course.java
  50. 5 0
      web/src/main/java/com/ynfy/buss/course/course/mapper/CourseMapper.java
  51. 53 9
      web/src/main/java/com/ynfy/buss/course/course/mapper/xml/CourseMapper.xml
  52. 18 1
      web/src/main/java/com/ynfy/buss/course/course/service/ICourseService.java
  53. 34 4
      web/src/main/java/com/ynfy/buss/course/course/service/impl/CourseServiceImpl.java
  54. 4 1
      web/src/main/java/com/ynfy/buss/course/coursecatalog/controller/CourseCatalogController.java
  55. 2 2
      web/src/main/java/com/ynfy/buss/course/coursecatalog/service/ICourseCatalogService.java
  56. 1 2
      web/src/main/java/com/ynfy/buss/course/coursecatalog/service/impl/CourseCatalogServiceImpl.java
  57. 4 3
      web/src/main/java/com/ynfy/buss/course/usercoursecatalog/controller/UserCourseCatalogController.java
  58. 1 1
      web/src/main/java/com/ynfy/buss/course/usercoursecatalog/service/IUserCourseCatalogService.java
  59. 4 4
      web/src/main/java/com/ynfy/buss/course/usercoursecatalog/service/impl/UserCourseCatalogServiceImpl.java
  60. 5 1
      web/src/main/java/com/ynfy/buss/exam/exam/controller/ExamController.java
  61. 11 23
      web/src/main/java/com/ynfy/buss/exam/exam/entity/Exam.java
  62. 3 0
      web/src/main/java/com/ynfy/buss/exam/exam/mapper/ExamMapper.java
  63. 92 31
      web/src/main/java/com/ynfy/buss/exam/exam/mapper/xml/ExamMapper.xml
  64. 19 1
      web/src/main/java/com/ynfy/buss/exam/exam/service/IExamService.java
  65. 54 16
      web/src/main/java/com/ynfy/buss/exam/exam/service/impl/ExamServiceImpl.java
  66. 5 6
      web/src/main/java/com/ynfy/buss/exam/userexam/controller/UserExamController.java
  67. 122 123
      web/src/main/java/com/ynfy/buss/exam/userexamresult/controller/UserExamResultController.java
  68. 2 0
      web/src/main/java/com/ynfy/buss/practice/userpractice/controller/UserPracticeController.java
  69. 2 0
      web/src/main/java/com/ynfy/buss/practice/userpractice/entity/dto/UserPracticeDTO.java
  70. 2 3
      web/src/main/java/com/ynfy/buss/practice/userpractice/service/impl/UserPracticeServiceImpl.java
  71. 18 14
      web/src/main/java/com/ynfy/config/WebMvcConfiguration.java
  72. 1 1
      web/src/main/resources/application-dev.yml
  73. 9 1
      web/src/main/resources/application.yml

+ 236 - 191
core/src/main/java/org/jeecg/common/system/util/JwtUtil.java

@@ -1,6 +1,7 @@
 package org.jeecg.common.system.util;
 
 import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTCreator;
 import com.auth0.jwt.JWTVerifier;
 import com.auth0.jwt.algorithms.Algorithm;
 import com.auth0.jwt.exceptions.JWTDecodeException;
@@ -11,11 +12,13 @@ import com.google.common.base.Joiner;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Date;
+import java.util.Map;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.shiro.SecurityUtils;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.constant.CommonConstant;
@@ -36,27 +39,28 @@ import org.jeecg.common.util.oConvertUtils;
  **/
 public class JwtUtil {
 
-	/**Token有效期为7天(Token在reids中缓存时间为两倍)*/
-	public static final long EXPIRE_TIME = (7 * 12) * 60 * 60 * 1000;
-	static final String WELL_NUMBER = SymbolConstant.WELL_NUMBER + SymbolConstant.LEFT_CURLY_BRACKET;
+    /**
+     * Token有效期为7天(Token在reids中缓存时间为两倍)
+     */
+    public static final long EXPIRE_TIME = (7 * 12) * 60 * 60 * 1000;
+    static final String WELL_NUMBER = SymbolConstant.WELL_NUMBER + SymbolConstant.LEFT_CURLY_BRACKET;
 
     /**
-     *
      * @param response
      * @param code
      * @param errorMsg
      */
     public static void responseError(ServletResponse response, Integer code, String errorMsg) {
-		HttpServletResponse httpServletResponse = (HttpServletResponse) response;
-		// issues/I4YH95浏览器显示乱码问题
-		httpServletResponse.setHeader("Content-type", "text/html;charset=UTF-8");
+        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+        // issues/I4YH95浏览器显示乱码问题
+        httpServletResponse.setHeader("Content-type", "text/html;charset=UTF-8");
         Result jsonResult = new Result(code, errorMsg);
-		jsonResult.setSuccess(false);
+        jsonResult.setSuccess(false);
         OutputStream os = null;
         try {
             os = httpServletResponse.getOutputStream();
-			httpServletResponse.setCharacterEncoding("UTF-8");
-			httpServletResponse.setStatus(code);
+            httpServletResponse.setCharacterEncoding("UTF-8");
+            httpServletResponse.setStatus(code);
             os.write(new ObjectMapper().writeValueAsString(jsonResult).getBytes("UTF-8"));
             os.flush();
             os.close();
@@ -65,186 +69,227 @@ public class JwtUtil {
         }
     }
 
-	/**
-	 * 校验token是否正确
-	 *
-	 * @param token  密钥
-	 * @param secret 用户的密码
-	 * @return 是否正确
-	 */
-	public static boolean verify(String token, String username, String secret) {
-		try {
-			// 根据密码生成JWT效验器
-			Algorithm algorithm = Algorithm.HMAC256(secret);
-			JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();
-			// 效验TOKEN
-			DecodedJWT jwt = verifier.verify(token);
-			return true;
-		} catch (Exception exception) {
-			return false;
-		}
-	}
-
-	/**
-	 * 获得token中的信息无需secret解密也能获得
-	 *
-	 * @return token中包含的用户名
-	 */
-	public static String getUsername(String token) {
-		try {
-			DecodedJWT jwt = JWT.decode(token);
-			return jwt.getClaim("username").asString();
-		} catch (JWTDecodeException e) {
-			return null;
-		}
-	}
-
-	/**
-	 * 生成签名,5min后过期
-	 *
-	 * @param username 用户名
-	 * @param secret   用户的密码
-	 * @return 加密的token
-	 */
-	public static String sign(String username, String secret) {
-		Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
-		Algorithm algorithm = Algorithm.HMAC256(secret);
-		// 附带username信息
-		return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);
-
-	}
-
-	/**
-	 * 根据request中的token获取用户账号
-	 * 
-	 * @param request
-	 * @return
-	 * @throws JeecgBootException
-	 */
-	public static String getUserNameByToken(HttpServletRequest request) throws JeecgBootException {
-		String accessToken = request.getHeader("X-Access-Token");
-		String username = getUsername(accessToken);
-		if (oConvertUtils.isEmpty(username)) {
-			throw new JeecgBootException("未获取到用户");
-		}
-		return username;
-	}
-	
-	/**
-	  *  从session中获取变量
-	 * @param key
-	 * @return
-	 */
-	public static String getSessionData(String key) {
-		//${myVar}%
-		//得到${} 后面的值
-		String moshi = "";
-		String wellNumber = WELL_NUMBER;
-
-		if(key.indexOf(SymbolConstant.RIGHT_CURLY_BRACKET)!=-1){
-			 moshi = key.substring(key.indexOf("}")+1);
-		}
-		String returnValue = null;
-		if (key.contains(wellNumber)) {
-			key = key.substring(2,key.indexOf("}"));
-		}
-		if (oConvertUtils.isNotEmpty(key)) {
-			HttpSession session = SpringContextUtils.getHttpServletRequest().getSession();
-			returnValue = (String) session.getAttribute(key);
-		}
-		//结果加上${} 后面的值
-		if(returnValue!=null){returnValue = returnValue + moshi;}
-		return returnValue;
-	}
-	
-	/**
-	  * 从当前用户中获取变量
-	 * @param key
-	 * @param user
-	 * @return
-	 */
-	public static String getUserSystemData(String key,SysUserCacheInfo user) {
-		if(user==null) {
-			user = JeecgDataAutorUtils.loadUserInfo();
-		}
-		//#{sys_user_code}%
-		
-		// 获取登录用户信息
-		LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
-		
-		String moshi = "";
+    /**
+     * 校验token是否正确
+     *
+     * @param token  密钥
+     * @param secret 用户的密码
+     * @return 是否正确
+     */
+    public static boolean verify(String token, String username, String secret) {
+        try {
+            // 根据密码生成JWT效验器
+            Algorithm algorithm = Algorithm.HMAC256(secret);
+            JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();
+            // 效验TOKEN
+            DecodedJWT jwt = verifier.verify(token);
+            return true;
+        } catch (Exception exception) {
+            return false;
+        }
+    }
+
+    /**
+     * 获得token中的信息无需secret解密也能获得
+     *
+     * @return token中包含的用户名
+     */
+    public static String getUsername(String token) {
+        try {
+            DecodedJWT jwt = JWT.decode(token);
+            return jwt.getClaim("username").asString();
+        } catch (JWTDecodeException e) {
+            return null;
+        }
+    }
+
+    /**
+     * 生成签名,5min后过期
+     *
+     * @param username 用户名
+     * @param secret   用户的密码
+     * @return 加密的token
+     */
+    public static String sign(String username, String secret) {
+        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
+        Algorithm algorithm = Algorithm.HMAC256(secret);
+        // 附带username信息
+        return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);
+
+    }
+
+    /**
+     * 生成签名,5min后过期
+     *
+     * @param fields 字段名/值MAP
+     * @return 加密的token
+     */
+    public static String sign(Map<String, Object> fields, String secret) {
+        try {
+            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
+            Algorithm algorithm = Algorithm.HMAC256(secret);
+            JWTCreator.Builder builder = JWT.create();
+            for (Map.Entry entry : fields.entrySet()) {
+                builder.withClaim(entry.getKey().toString(), entry.getValue().toString());
+            }
+            return builder.withExpiresAt(date).sign(algorithm);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * 根据request中的token获取用户账号
+     *
+     * @param request
+     * @return
+     * @throws JeecgBootException
+     */
+    public static String getUserNameByToken(HttpServletRequest request) throws JeecgBootException {
+        String accessToken = request.getHeader("X-Access-Token");
+        String username = getUsername(accessToken);
+        if (oConvertUtils.isEmpty(username)) {
+            throw new JeecgBootException("未获取到用户");
+        }
+        return username;
+    }
+
+    /**
+     * 从session中获取变量
+     *
+     * @param key
+     * @return
+     */
+    public static String getSessionData(String key) {
+        //${myVar}%
+        //得到${} 后面的值
+        String moshi = "";
         String wellNumber = WELL_NUMBER;
-		if(key.indexOf(SymbolConstant.RIGHT_CURLY_BRACKET)!=-1){
-			 moshi = key.substring(key.indexOf("}")+1);
-		}
-		String returnValue = null;
-		//针对特殊标示处理#{sysOrgCode},判断替换
-		if (key.contains(wellNumber)) {
-			key = key.substring(2,key.indexOf("}"));
-		} else {
-			key = key;
-		}
-		//替换为系统登录用户帐号
-		if (key.equals(DataBaseConstant.SYS_USER_CODE)|| key.toLowerCase().equals(DataBaseConstant.SYS_USER_CODE_TABLE)) {
-			if(user==null) {
-				returnValue = sysUser.getUsername();
-			}else {
-				returnValue = user.getSysUserCode();
-			}
-		}
-		//替换为系统登录用户真实名字
-		else if (key.equals(DataBaseConstant.SYS_USER_NAME)|| key.toLowerCase().equals(DataBaseConstant.SYS_USER_NAME_TABLE)) {
-			if(user==null) {
-				returnValue = sysUser.getRealname();
-			}else {
-				returnValue = user.getSysUserName();
-			}
-		}
-		
-		//替换为系统用户登录所使用的机构编码
-		else if (key.equals(DataBaseConstant.SYS_ORG_CODE)|| key.toLowerCase().equals(DataBaseConstant.SYS_ORG_CODE_TABLE)) {
-			if(user==null) {
-				returnValue = sysUser.getOrgCode();
-			}else {
-				returnValue = user.getSysOrgCode();
-			}
-		}
-		//替换为系统用户所拥有的所有机构编码
-		else if (key.equals(DataBaseConstant.SYS_MULTI_ORG_CODE)|| key.toLowerCase().equals(DataBaseConstant.SYS_MULTI_ORG_CODE_TABLE)) {
-			if(user==null){
-				//TODO 暂时使用用户登录部门,存在逻辑缺陷,不是用户所拥有的部门
-				returnValue = sysUser.getOrgCode();
-			}else{
-				if(user.isOneDepart()) {
-					returnValue = user.getSysMultiOrgCode().get(0);
-				}else {
-					returnValue = Joiner.on(",").join(user.getSysMultiOrgCode());
-				}
-			}
-		}
-		//替换为当前系统时间(年月日)
-		else if (key.equals(DataBaseConstant.SYS_DATE)|| key.toLowerCase().equals(DataBaseConstant.SYS_DATE_TABLE)) {
-			returnValue = DateUtils.formatDate();
-		}
-		//替换为当前系统时间(年月日时分秒)
-		else if (key.equals(DataBaseConstant.SYS_TIME)|| key.toLowerCase().equals(DataBaseConstant.SYS_TIME_TABLE)) {
-			returnValue = DateUtils.now();
-		}
-		//流程状态默认值(默认未发起)
-		else if (key.equals(DataBaseConstant.BPM_STATUS)|| key.toLowerCase().equals(DataBaseConstant.BPM_STATUS_TABLE)) {
-			returnValue = "1";
-		}
-		//update-begin-author:taoyan date:20210330 for:多租户ID作为系统变量
-		else if (key.equals(TenantConstant.TENANT_ID) || key.toLowerCase().equals(TenantConstant.TENANT_ID_TABLE)){
-			returnValue = SpringContextUtils.getHttpServletRequest().getHeader(CommonConstant.TENANT_ID);
-		}
-		//update-end-author:taoyan date:20210330 for:多租户ID作为系统变量
-		if(returnValue!=null){returnValue = returnValue + moshi;}
-		return returnValue;
-	}
-	
-//	public static void main(String[] args) {
-//		 String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjUzMzY1MTMsInVzZXJuYW1lIjoiYWRtaW4ifQ.xjhud_tWCNYBOg_aRlMgOdlZoWFFKB_givNElHNw3X0";
-//		 System.out.println(JwtUtil.getUsername(token));
-//	}
+
+        if (key.indexOf(SymbolConstant.RIGHT_CURLY_BRACKET) != -1) {
+            moshi = key.substring(key.indexOf("}") + 1);
+        }
+        String returnValue = null;
+        if (key.contains(wellNumber)) {
+            key = key.substring(2, key.indexOf("}"));
+        }
+        if (oConvertUtils.isNotEmpty(key)) {
+            HttpSession session = SpringContextUtils.getHttpServletRequest().getSession();
+            returnValue = (String) session.getAttribute(key);
+        }
+        //结果加上${} 后面的值
+        if (returnValue != null) {
+            returnValue = returnValue + moshi;
+        }
+        return returnValue;
+    }
+
+    /**
+     * 从当前用户中获取变量
+     *
+     * @param key
+     * @param user
+     * @return
+     */
+    public static String getUserSystemData(String key, SysUserCacheInfo user) {
+        if (user == null) {
+            user = JeecgDataAutorUtils.loadUserInfo();
+        }
+        //#{sys_user_code}%
+
+        // 获取登录用户信息
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+
+        String moshi = "";
+        String wellNumber = WELL_NUMBER;
+        if (key.indexOf(SymbolConstant.RIGHT_CURLY_BRACKET) != -1) {
+            moshi = key.substring(key.indexOf("}") + 1);
+        }
+        String returnValue = null;
+        //针对特殊标示处理#{sysOrgCode},判断替换
+        if (key.contains(wellNumber)) {
+            key = key.substring(2, key.indexOf("}"));
+        } else {
+            key = key;
+        }
+        //替换为系统登录用户帐号
+        if (key.equals(DataBaseConstant.SYS_USER_CODE) || key.toLowerCase().equals(DataBaseConstant.SYS_USER_CODE_TABLE)) {
+            if (user == null) {
+                returnValue = sysUser.getUsername();
+            } else {
+                returnValue = user.getSysUserCode();
+            }
+        }
+        //替换为系统登录用户真实名字
+        else if (key.equals(DataBaseConstant.SYS_USER_NAME) || key.toLowerCase().equals(DataBaseConstant.SYS_USER_NAME_TABLE)) {
+            if (user == null) {
+                returnValue = sysUser.getRealname();
+            } else {
+                returnValue = user.getSysUserName();
+            }
+        }
+
+        //替换为系统用户登录所使用的机构编码
+        else if (key.equals(DataBaseConstant.SYS_ORG_CODE) || key.toLowerCase().equals(DataBaseConstant.SYS_ORG_CODE_TABLE)) {
+            if (user == null) {
+                returnValue = sysUser.getOrgCode();
+            } else {
+                returnValue = user.getSysOrgCode();
+            }
+        }
+        //替换为系统用户所拥有的所有机构编码
+        else if (key.equals(DataBaseConstant.SYS_MULTI_ORG_CODE) || key.toLowerCase().equals(DataBaseConstant.SYS_MULTI_ORG_CODE_TABLE)) {
+            if (user == null) {
+                //TODO 暂时使用用户登录部门,存在逻辑缺陷,不是用户所拥有的部门
+                returnValue = sysUser.getOrgCode();
+            } else {
+                if (user.isOneDepart()) {
+                    returnValue = user.getSysMultiOrgCode().get(0);
+                } else {
+                    returnValue = Joiner.on(",").join(user.getSysMultiOrgCode());
+                }
+            }
+        }
+        //替换为当前系统时间(年月日)
+        else if (key.equals(DataBaseConstant.SYS_DATE) || key.toLowerCase().equals(DataBaseConstant.SYS_DATE_TABLE)) {
+            returnValue = DateUtils.formatDate();
+        }
+        //替换为当前系统时间(年月日时分秒)
+        else if (key.equals(DataBaseConstant.SYS_TIME) || key.toLowerCase().equals(DataBaseConstant.SYS_TIME_TABLE)) {
+            returnValue = DateUtils.now();
+        }
+        //流程状态默认值(默认未发起)
+        else if (key.equals(DataBaseConstant.BPM_STATUS) || key.toLowerCase().equals(DataBaseConstant.BPM_STATUS_TABLE)) {
+            returnValue = "1";
+        }
+        //update-begin-author:taoyan date:20210330 for:多租户ID作为系统变量
+        else if (key.equals(TenantConstant.TENANT_ID) || key.toLowerCase().equals(TenantConstant.TENANT_ID_TABLE)) {
+            returnValue = SpringContextUtils.getHttpServletRequest().getHeader(CommonConstant.TENANT_ID);
+        }
+        //update-end-author:taoyan date:20210330 for:多租户ID作为系统变量
+        if (returnValue != null) {
+            returnValue = returnValue + moshi;
+        }
+        return returnValue;
+    }
+
+    /**
+     * 获得token中的信息无需secret解密也能获得
+     *
+     * @param token
+     * @param fieldName
+     * @return
+     */
+    public static String getFieldValue(String token, String fieldName) {
+        if (StringUtils.isBlank(token)) {
+            return null;
+        }
+        try {
+            DecodedJWT jwt = JWT.decode(token);
+            return jwt.getClaim(fieldName).asString();
+        } catch (JWTDecodeException e) {
+            return null;
+        }
+    }
+
 }

+ 30 - 0
core/src/main/java/org/jeecg/common/util/MinioUtil.java

@@ -313,4 +313,34 @@ public class MinioUtil {
         return minioUrl + bucketName + "/" + relativePath;
     }
 
+    /**
+     * 上传文件到minio
+     *
+     * @param stream
+     * @return
+     */
+    public static String uploadFile(InputStream stream, String fileName) throws Exception {
+        try {
+            initMinio(minioUrl, minioName, minioPass);
+            if (minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
+                log.info("Bucket already exists.");
+            } else {
+                // 创建一个名为ota的存储桶
+                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
+                log.info("create a new bucket.");
+            }
+            StringBuilder objectName = new StringBuilder();
+            objectName.append(DateUtil.thisYear()).append("/").append(DateUtil.thisMonth() + 1).append("/");
+            objectName.append(fileName);
+            PutObjectArgs objectArgs = PutObjectArgs.builder().object(objectName.toString())
+                    .bucket(bucketName)
+                    .contentType("application/octet-stream")
+                    .stream(stream, stream.available(), -1).build();
+            minioClient.putObject(objectArgs);
+            return objectName.toString();
+        } finally {
+            stream.close();
+        }
+    }
+
 }

+ 64 - 0
core/src/main/java/org/jeecg/common/util/TokenUtil.java

@@ -0,0 +1,64 @@
+package org.jeecg.common.util;
+
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.common.exception.JeecgBootException;
+import org.jeecg.common.system.util.JwtUtil;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * token工具
+ */
+public class TokenUtil {
+
+    public static final String LOGIN_TOKEN_KEY = "X-Exam-Token";
+
+
+    /**
+     * 获取token
+     *
+     * @param request
+     * @return
+     */
+    public static String getToken(HttpServletRequest request) {
+        //从header中获取token
+        String token = request.getHeader(LOGIN_TOKEN_KEY);
+        //如果header中不存在token,则从参数中获取token
+        if (StringUtils.isBlank(token)) {
+            token = request.getParameter(LOGIN_TOKEN_KEY);
+        }
+        //token为空
+        if (StringUtils.isBlank(token)) {
+            return null;
+        }
+        return token;
+    }
+
+
+    /**
+     * 获取用户名
+     *
+     * @param token
+     * @return
+     */
+    public static String getUserName(String token) {
+        //token为空
+        if (StringUtils.isBlank(token)) {
+            throw new JeecgBootException("请先登录");
+        }
+
+        return JwtUtil.getUsername(token);
+    }
+
+    /**
+     * 获取用户id
+     *
+     * @param token
+     * @return
+     */
+    public static String getUserId(String token) {
+        return JwtUtil.getFieldValue(token, "userId");
+    }
+
+
+}

+ 1 - 0
core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java

@@ -149,6 +149,7 @@ public class ShiroConfig {
         //错误路径排除
         filterChainDefinitionMap.put("/error", "anon");
         // update-end--author:liusq Date:20230522 for:[issues/4829]访问不存在的url时会提示Token失效,请重新登录呢
+        filterChainDefinitionMap.put("/api/v1/**", "anon");
 
         // 添加自己的过滤器并且取名为jwt
         Map<String, Filter> filterMap = new HashMap<String, Filter>(1);

+ 0 - 79
system/system-biz/src/main/java/org/jeecg/modules/system/controller/LoginController.java

@@ -24,7 +24,6 @@ import org.jeecg.config.JeecgBaseConfig;
 import org.jeecg.modules.base.service.BaseCommonService;
 import org.jeecg.modules.system.entity.SysDepart;
 import org.jeecg.modules.system.entity.SysRoleIndex;
-import org.jeecg.modules.system.entity.SysTenant;
 import org.jeecg.modules.system.entity.SysUser;
 import org.jeecg.modules.system.enums.UserType;
 import org.jeecg.modules.system.model.SysLoginModel;
@@ -579,84 +578,6 @@ public class LoginController {
         return res;
     }
 
-    /**
-     * app登录
-     *
-     * @param sysLoginModel
-     * @return
-     * @throws Exception
-     */
-    @RequestMapping(value = "/mLogin", method = RequestMethod.POST)
-    public Result<JSONObject> mLogin(@RequestBody SysLoginModel sysLoginModel) throws Exception {
-        Result<JSONObject> result = new Result<JSONObject>();
-        String username = sysLoginModel.getUsername();
-        String password = sysLoginModel.getPassword();
-        JSONObject obj = new JSONObject();
-
-        //update-begin-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
-        if (isLoginFailOvertimes(username)) {
-            return result.error500("该用户登录失败次数过多,请于10分钟后再次登录!");
-        }
-        //update-end-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
-        //1. 校验用户是否有效
-        SysUser sysUser = sysUserService.getUserByName(username);
-        result = sysUserService.checkUserIsEffective(sysUser);
-        if (!result.isSuccess()) {
-            return result;
-        }
-
-        //2. 校验用户名或密码是否正确
-        String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
-        String syspassword = sysUser.getPassword();
-        if (!syspassword.equals(userpassword)) {
-            //update-begin-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
-            addLoginFailOvertimes(username);
-            //update-end-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
-            result.error500("用户名或密码错误");
-            return result;
-        }
-
-        //3.设置登录部门
-        String orgCode = sysUser.getOrgCode();
-        if (oConvertUtils.isEmpty(orgCode)) {
-            //如果当前用户无选择部门 查看部门关联信息
-            List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
-            //update-begin-author:taoyan date:20220117 for: JTC-1068【app】新建用户,没有设置部门及角色,点击登录提示暂未归属部,一直在登录页面 使用手机号登录 可正常
-            if (departs == null || departs.size() == 0) {
-				/*result.error500("用户暂未归属部门,不可登录!");
-				return result;*/
-            } else {
-                orgCode = departs.get(0).getOrgCode();
-                sysUser.setOrgCode(orgCode);
-                this.sysUserService.updateUserDepart(username, orgCode, null);
-            }
-            //update-end-author:taoyan date:20220117 for: JTC-1068【app】新建用户,没有设置部门及角色,点击登录提示暂未归属部,一直在登录页面 使用手机号登录 可正常
-        }
-
-        //4. 设置登录租户
-        Result<JSONObject> loginTenantError = sysUserService.setLoginTenant(sysUser, obj, username, result);
-        if (loginTenantError != null) {
-            return loginTenantError;
-        }
-
-        //5. 设置登录用户信息
-        obj.put("userInfo", sysUser);
-
-        //6. 生成token
-        String token = JwtUtil.sign(username, syspassword);
-        // 设置超时时间
-        redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
-        redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
-
-        //token 信息
-        obj.put("token", token);
-        result.setResult(obj);
-        result.setSuccess(true);
-        result.setCode(200);
-        baseCommonService.addLog("用户名: " + username + ",登录成功[移动端]!", CommonConstant.LOG_TYPE_1, null);
-        return result;
-    }
-
     /**
      * 图形验证码
      *

+ 9 - 2
system/system-biz/src/main/java/org/jeecg/modules/system/entity/SysAnnouncement.java

@@ -1,6 +1,7 @@
 package org.jeecg.modules.system.entity;
 
 import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
@@ -106,8 +107,8 @@ public class SysAnnouncement implements Serializable {
     /**
      * 创建时间
      */
-    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
-    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
     private java.util.Date createTime;
     /**
      * 更新人
@@ -165,4 +166,10 @@ public class SysAnnouncement implements Serializable {
 
     /**租户ID*/
     private Integer tenantId;
+
+    /**
+     * 查询谁的消息
+     */
+    @TableField(exist = false)
+    private String userId;
 }

+ 5 - 0
system/system-biz/src/main/java/org/jeecg/modules/system/entity/SysUser.java

@@ -210,4 +210,9 @@ public class SysUser implements Serializable {
      * 讲师介绍
      */
     private String teacherIntroduce;
+
+    /**
+     * 微信openid
+     */
+    private String openId;
 }

+ 13 - 9
system/system-biz/src/main/java/org/jeecg/modules/system/mapper/SysAnnouncementMapper.java

@@ -1,33 +1,34 @@
 package org.jeecg.modules.system.mapper;
 
-import java.util.Date;
-import java.util.List;
-
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Param;
 import org.jeecg.modules.system.entity.SysAnnouncement;
 
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import java.util.Date;
+import java.util.List;
 
 /**
  * @Description: 系统通告表
  * @Author: jeecg-boot
- * @Date:  2019-01-02
+ * @Date: 2019-01-02
  * @Version: V1.0
  */
 public interface SysAnnouncementMapper extends BaseMapper<SysAnnouncement> {
 
     /**
      * 通过消息类型和用户id获取系统通告
+     *
      * @param page
-     * @param userId 用户id
+     * @param userId      用户id
      * @param msgCategory 消息类型
      * @return
      */
-	List<SysAnnouncement> querySysCementListByUserId(Page<SysAnnouncement> page, @Param("userId")String userId,@Param("msgCategory")String msgCategory);
+    List<SysAnnouncement> querySysCementListByUserId(Page<SysAnnouncement> page, @Param("userId") String userId, @Param("msgCategory") String msgCategory);
 
     /**
      * 分页查询消息列表
+     *
      * @param page
      * @param userId
      * @param fromUser
@@ -35,5 +36,8 @@ public interface SysAnnouncementMapper extends BaseMapper<SysAnnouncement> {
      * @param endDate
      * @return
      */
-	List<SysAnnouncement> queryMessageList(Page<SysAnnouncement> page, @Param("userId")String userId, @Param("fromUser")String fromUser, @Param("starFlag")String starFlag, @Param("beginDate")Date beginDate, @Param("endDate")Date endDate);
+    List<SysAnnouncement> queryMessageList(Page<SysAnnouncement> page, @Param("userId") String userId, @Param("fromUser") String fromUser, @Param("starFlag") String starFlag, @Param("beginDate") Date beginDate, @Param("endDate") Date endDate);
+
+    List<SysAnnouncement> listNoRead(@Param("announcement") SysAnnouncement announcement);
+
 }

+ 25 - 0
system/system-biz/src/main/java/org/jeecg/modules/system/mapper/xml/SysAnnouncementMapper.xml

@@ -67,5 +67,30 @@
 		</if>
 	   order by b.read_flag ASC,a.create_time DESC
 	</select>
+
+	<select id="listNoRead"  resultMap="SysAnnouncement">
+		SELECT
+			*
+		FROM
+			sys_announcement
+		WHERE
+			send_status = '1'
+			and del_flag='0'
+			<if test="announcement!=null and announcement.msgCategory!=null and announcement.msgCategory!=''">
+				and msg_category = #{announcement.msgCategory}
+			</if>
+		    <if test="announcement!=null and announcement.userId != null and announcement.userId != ''">
+			   AND ID IN (
+			   SELECT
+					annt_id
+			   FROM
+					sys_announcement_send ase
+			   WHERE
+				   user_id = #{announcement.userId}
+				   AND read_flag = '0'
+			   )
+		    </if>
+		ORDER BY send_time DESC
+	</select>
 	
 </mapper>

+ 16 - 10
system/system-biz/src/main/java/org/jeecg/modules/system/service/ISysAnnouncementService.java

@@ -10,45 +10,49 @@ import java.util.List;
 /**
  * @Description: 系统通告表
  * @Author: jeecg-boot
- * @Date:  2019-01-02
+ * @Date: 2019-01-02
  * @Version: V1.0
  */
 public interface ISysAnnouncementService extends IService<SysAnnouncement> {
 
     /**
      * 保存系统通告
+     *
      * @param sysAnnouncement
      */
-	public void saveAnnouncement(SysAnnouncement sysAnnouncement);
+    public void saveAnnouncement(SysAnnouncement sysAnnouncement);
 
     /**
      * 修改系统通告
+     *
      * @param sysAnnouncement
      * @return
      */
-	public boolean upDateAnnouncement(SysAnnouncement sysAnnouncement);
+    public boolean upDateAnnouncement(SysAnnouncement sysAnnouncement);
 
     /**
      * 保存系统通告
-     * @param title 标题
+     *
+     * @param title      标题
      * @param msgContent 信息内容
      */
-	public void saveSysAnnouncement(String title, String msgContent);
+    public void saveSysAnnouncement(String title, String msgContent);
 
     /**
      * 分页查询系统通告
-     * @param page 当前页数
-     * @param userId 用户id
+     *
+     * @param page        当前页数
+     * @param userId      用户id
      * @param msgCategory 消息类型
      * @return Page<SysAnnouncement>
      */
-	public Page<SysAnnouncement> querySysCementPageByUserId(Page<SysAnnouncement> page, String userId, String msgCategory);
+    public Page<SysAnnouncement> querySysCementPageByUserId(Page<SysAnnouncement> page, String userId, String msgCategory);
 
 
     /**
-     *  补全当前登录用户的消息阅读记录 
+     * 补全当前登录用户的消息阅读记录
      */
-	void completeAnnouncementSendInfo();
+    void completeAnnouncementSendInfo();
 
 
     /**
@@ -61,5 +65,7 @@ public interface ISysAnnouncementService extends IService<SysAnnouncement> {
      */
     void updateReaded(List<String> annoceIdList);
 
+    List<SysAnnouncement> listNoRead(SysAnnouncement announcement);
 
+    void readMessage(String id, String userId);
 }

+ 13 - 0
system/system-biz/src/main/java/org/jeecg/modules/system/service/ISysUserService.java

@@ -1,6 +1,7 @@
 package org.jeecg.modules.system.service;
 
 import com.alibaba.fastjson.JSONObject;
+import com.alipay.api.domain.AlipayMsaasMediarecogVoiceMediaaudioUploadModel;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -438,4 +439,16 @@ public interface ISysUserService extends IService<SysUser> {
      * @return
      */
     List<SysUser> getUserByDepIds(List<String> departIds, String username);
+
+
+    /**
+     * 获取登录用户信息
+     *
+     * @param token
+     * @return
+     */
+    LoginUser getLoginUser(String token);
+
+    SysUser queryByOpenId(String openid);
 }
+

+ 186 - 148
system/system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysAnnouncementServiceImpl.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.shiro.SecurityUtils;
 import org.jeecg.common.constant.CommonConstant;
 import org.jeecg.common.system.vo.LoginUser;
@@ -12,9 +13,12 @@ import org.jeecg.modules.system.entity.SysAnnouncement;
 import org.jeecg.modules.system.entity.SysAnnouncementSend;
 import org.jeecg.modules.system.mapper.SysAnnouncementMapper;
 import org.jeecg.modules.system.mapper.SysAnnouncementSendMapper;
+import org.jeecg.modules.system.service.ISysAnnouncementSendService;
 import org.jeecg.modules.system.service.ISysAnnouncementService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
 
 import javax.annotation.Resource;
 import java.util.Arrays;
@@ -25,164 +29,198 @@ import java.util.List;
 /**
  * @Description: 系统通告表
  * @Author: jeecg-boot
- * @Date:  2019-01-02
+ * @Date: 2019-01-02
  * @Version: V1.0
  */
 @Service
 @Slf4j
 public class SysAnnouncementServiceImpl extends ServiceImpl<SysAnnouncementMapper, SysAnnouncement> implements ISysAnnouncementService {
 
-	@Resource
-	private SysAnnouncementMapper sysAnnouncementMapper;
-	
-	@Resource
-	private SysAnnouncementSendMapper sysAnnouncementSendMapper;
-	
-	@Transactional(rollbackFor = Exception.class)
-	@Override
-	public void saveAnnouncement(SysAnnouncement sysAnnouncement) {
-		if(sysAnnouncement.getMsgType().equals(CommonConstant.MSG_TYPE_ALL)) {
-			sysAnnouncementMapper.insert(sysAnnouncement);
-		}else {
-			// 1.插入通告表记录
-			sysAnnouncementMapper.insert(sysAnnouncement);
-			// 2.插入用户通告阅读标记表记录
-			String userId = sysAnnouncement.getUserIds();
-			String[] userIds = userId.substring(0, (userId.length()-1)).split(",");
-			String anntId = sysAnnouncement.getId();
-			Date refDate = new Date();
-			for(int i=0;i<userIds.length;i++) {
-				SysAnnouncementSend announcementSend = new SysAnnouncementSend();
-				announcementSend.setAnntId(anntId);
-				announcementSend.setUserId(userIds[i]);
-				announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG);
-				announcementSend.setReadTime(refDate);
-				sysAnnouncementSendMapper.insert(announcementSend);
-			}
-		}
-	}
-	
-	/**
-	 * @功能:编辑消息信息
-	 */
-	@Transactional(rollbackFor = Exception.class)
-	@Override
-	public boolean upDateAnnouncement(SysAnnouncement sysAnnouncement) {
-		// 1.更新系统信息表数据
-		sysAnnouncementMapper.updateById(sysAnnouncement);
-		String userId = sysAnnouncement.getUserIds();
-		if(oConvertUtils.isNotEmpty(userId)&&sysAnnouncement.getMsgType().equals(CommonConstant.MSG_TYPE_UESR)) {
-			// 2.补充新的通知用户数据
-			String[] userIds = userId.substring(0, (userId.length()-1)).split(",");
-			String anntId = sysAnnouncement.getId();
-			Date refDate = new Date();
-			for(int i=0;i<userIds.length;i++) {
-				LambdaQueryWrapper<SysAnnouncementSend> queryWrapper = new LambdaQueryWrapper<SysAnnouncementSend>();
-				queryWrapper.eq(SysAnnouncementSend::getAnntId, anntId);
-				queryWrapper.eq(SysAnnouncementSend::getUserId, userIds[i]);
-				List<SysAnnouncementSend> announcementSends=sysAnnouncementSendMapper.selectList(queryWrapper);
-				if(announcementSends.size()<=0) {
-					SysAnnouncementSend announcementSend = new SysAnnouncementSend();
-					announcementSend.setAnntId(anntId);
-					announcementSend.setUserId(userIds[i]);
-					announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG);
-					announcementSend.setReadTime(refDate);
-					sysAnnouncementSendMapper.insert(announcementSend);
-				}
-			}
-			// 3. 删除多余通知用户数据
-			Collection<String> delUserIds = Arrays.asList(userIds);
-			LambdaQueryWrapper<SysAnnouncementSend> queryWrapper = new LambdaQueryWrapper<SysAnnouncementSend>();
-			queryWrapper.notIn(SysAnnouncementSend::getUserId, delUserIds);
-			queryWrapper.eq(SysAnnouncementSend::getAnntId, anntId);
-			sysAnnouncementSendMapper.delete(queryWrapper);
-		}
-		return true;
-	}
+    @Resource
+    private SysAnnouncementMapper sysAnnouncementMapper;
+
+    @Resource
+    private SysAnnouncementSendMapper sysAnnouncementSendMapper;
+
+    @Autowired
+    private ISysAnnouncementSendService announcementSendService;
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void saveAnnouncement(SysAnnouncement sysAnnouncement) {
+        if (sysAnnouncement.getMsgType().equals(CommonConstant.MSG_TYPE_ALL)) {
+            sysAnnouncementMapper.insert(sysAnnouncement);
+        } else {
+            // 1.插入通告表记录
+            sysAnnouncementMapper.insert(sysAnnouncement);
+            // 2.插入用户通告阅读标记表记录
+            String userId = sysAnnouncement.getUserIds();
+            String[] userIds = userId.substring(0, (userId.length() - 1)).split(",");
+            String anntId = sysAnnouncement.getId();
+            Date refDate = new Date();
+            for (int i = 0; i < userIds.length; i++) {
+                SysAnnouncementSend announcementSend = new SysAnnouncementSend();
+                announcementSend.setAnntId(anntId);
+                announcementSend.setUserId(userIds[i]);
+                announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG);
+                announcementSend.setReadTime(refDate);
+                sysAnnouncementSendMapper.insert(announcementSend);
+            }
+        }
+    }
+
+    /**
+     * @功能:编辑消息信息
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean upDateAnnouncement(SysAnnouncement sysAnnouncement) {
+        // 1.更新系统信息表数据
+        sysAnnouncementMapper.updateById(sysAnnouncement);
+        String userId = sysAnnouncement.getUserIds();
+        if (oConvertUtils.isNotEmpty(userId) && sysAnnouncement.getMsgType().equals(CommonConstant.MSG_TYPE_UESR)) {
+            // 2.补充新的通知用户数据
+            String[] userIds = userId.substring(0, (userId.length() - 1)).split(",");
+            String anntId = sysAnnouncement.getId();
+            Date refDate = new Date();
+            for (int i = 0; i < userIds.length; i++) {
+                LambdaQueryWrapper<SysAnnouncementSend> queryWrapper = new LambdaQueryWrapper<SysAnnouncementSend>();
+                queryWrapper.eq(SysAnnouncementSend::getAnntId, anntId);
+                queryWrapper.eq(SysAnnouncementSend::getUserId, userIds[i]);
+                List<SysAnnouncementSend> announcementSends = sysAnnouncementSendMapper.selectList(queryWrapper);
+                if (announcementSends.size() <= 0) {
+                    SysAnnouncementSend announcementSend = new SysAnnouncementSend();
+                    announcementSend.setAnntId(anntId);
+                    announcementSend.setUserId(userIds[i]);
+                    announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG);
+                    announcementSend.setReadTime(refDate);
+                    sysAnnouncementSendMapper.insert(announcementSend);
+                }
+            }
+            // 3. 删除多余通知用户数据
+            Collection<String> delUserIds = Arrays.asList(userIds);
+            LambdaQueryWrapper<SysAnnouncementSend> queryWrapper = new LambdaQueryWrapper<SysAnnouncementSend>();
+            queryWrapper.notIn(SysAnnouncementSend::getUserId, delUserIds);
+            queryWrapper.eq(SysAnnouncementSend::getAnntId, anntId);
+            sysAnnouncementSendMapper.delete(queryWrapper);
+        }
+        return true;
+    }
 
     /**
      * 流程执行完成保存消息通知
-     * @param title 标题
+     *
+     * @param title      标题
      * @param msgContent 信息内容
      */
-	@Override
-	public void saveSysAnnouncement(String title, String msgContent) {
-		SysAnnouncement announcement = new SysAnnouncement();
-		announcement.setTitile(title);
-		announcement.setMsgContent(msgContent);
-		announcement.setSender("JEECG BOOT");
-		announcement.setPriority(CommonConstant.PRIORITY_L);
-		announcement.setMsgType(CommonConstant.MSG_TYPE_ALL);
-		announcement.setSendStatus(CommonConstant.HAS_SEND);
-		announcement.setSendTime(new Date());
-		announcement.setDelFlag(CommonConstant.DEL_FLAG_0.toString());
-		sysAnnouncementMapper.insert(announcement);
-	}
-
-	@Override
-	public Page<SysAnnouncement> querySysCementPageByUserId(Page<SysAnnouncement> page, String userId, String msgCategory) {
-		if (page.getSize() == -1) {
-			return page.setRecords(sysAnnouncementMapper.querySysCementListByUserId(null, userId, msgCategory));
-		} else {
-			return page.setRecords(sysAnnouncementMapper.querySysCementListByUserId(page, userId, msgCategory));
-		}
-	}
-
-	@Override
-	public void completeAnnouncementSendInfo() {
-		LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
-		String userId = sysUser.getId();
-		// 1.将系统消息补充到用户通告阅读标记表中
-		LambdaQueryWrapper<SysAnnouncement> querySaWrapper = new LambdaQueryWrapper<SysAnnouncement>();
-		//全部人员
-		querySaWrapper.eq(SysAnnouncement::getMsgType, CommonConstant.MSG_TYPE_ALL);
-		//未删除
-		querySaWrapper.eq(SysAnnouncement::getDelFlag, CommonConstant.DEL_FLAG_0.toString());
-		//已发布
-		querySaWrapper.eq(SysAnnouncement::getSendStatus, CommonConstant.HAS_SEND);
-		//新注册用户不看结束通知
-		querySaWrapper.ge(SysAnnouncement::getEndTime, sysUser.getCreateTime());
-		//update-begin--Author:liusq  Date:20210108 for:[JT-424] 【开源issue】bug处理--------------------
-		querySaWrapper.notInSql(SysAnnouncement::getId,"select annt_id from sys_announcement_send where user_id='"+userId+"'");
-		//update-begin--Author:liusq  Date:20210108  for: [JT-424] 【开源issue】bug处理--------------------
-		List<SysAnnouncement> announcements = this.list(querySaWrapper);
-		if(announcements.size()>0) {
-			for(int i=0;i<announcements.size();i++) {
-				//update-begin--Author:wangshuai  Date:20200803  for: 通知公告消息重复LOWCOD-759--------------------
-				//因为websocket没有判断是否存在这个用户,要是判断会出现问题,故在此判断逻辑
-				LambdaQueryWrapper<SysAnnouncementSend> query = new LambdaQueryWrapper<>();
-				query.eq(SysAnnouncementSend::getAnntId,announcements.get(i).getId());
-				query.eq(SysAnnouncementSend::getUserId,userId);
-				SysAnnouncementSend one = sysAnnouncementSendMapper.selectOne(query);
-				if(null==one){
-					SysAnnouncementSend announcementSend = new SysAnnouncementSend();
-					announcementSend.setAnntId(announcements.get(i).getId());
-					announcementSend.setUserId(userId);
-					announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG);
-					sysAnnouncementSendMapper.insert(announcementSend);
-					log.info("announcementSend.toString()",announcementSend.toString());
-				}
-				//update-end--Author:wangshuai  Date:20200803  for: 通知公告消息重复LOWCOD-759------------
-			}
-		}
-		
-	}
-
-	@Override
-	public List<SysAnnouncement> querySysMessageList(int pageSize, int pageNo, String fromUser, String starFlag, Date beginDate, Date endDate) {
-		//1. 补全send表的数据
-		completeAnnouncementSendInfo();
-		LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
-		Page<SysAnnouncement> page = new Page<SysAnnouncement>(pageNo,pageSize);
-		// 2. 查询消息数据
-		List<SysAnnouncement> list = baseMapper.queryMessageList(page, sysUser.getId(), fromUser, starFlag, beginDate, endDate);
-		return list;
-	}
-
-	@Override
-	public void updateReaded(List<String> annoceIdList) {
-		LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
-		sysAnnouncementSendMapper.updateReaded(sysUser.getId(), annoceIdList);
-	}
+    @Override
+    public void saveSysAnnouncement(String title, String msgContent) {
+        SysAnnouncement announcement = new SysAnnouncement();
+        announcement.setTitile(title);
+        announcement.setMsgContent(msgContent);
+        announcement.setSender("JEECG BOOT");
+        announcement.setPriority(CommonConstant.PRIORITY_L);
+        announcement.setMsgType(CommonConstant.MSG_TYPE_ALL);
+        announcement.setSendStatus(CommonConstant.HAS_SEND);
+        announcement.setSendTime(new Date());
+        announcement.setDelFlag(CommonConstant.DEL_FLAG_0.toString());
+        sysAnnouncementMapper.insert(announcement);
+    }
+
+    @Override
+    public Page<SysAnnouncement> querySysCementPageByUserId(Page<SysAnnouncement> page, String userId, String msgCategory) {
+        if (page.getSize() == -1) {
+            return page.setRecords(sysAnnouncementMapper.querySysCementListByUserId(null, userId, msgCategory));
+        } else {
+            return page.setRecords(sysAnnouncementMapper.querySysCementListByUserId(page, userId, msgCategory));
+        }
+    }
+
+    @Override
+    public void completeAnnouncementSendInfo() {
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        String userId = sysUser.getId();
+        // 1.将系统消息补充到用户通告阅读标记表中
+        LambdaQueryWrapper<SysAnnouncement> querySaWrapper = new LambdaQueryWrapper<SysAnnouncement>();
+        //全部人员
+        querySaWrapper.eq(SysAnnouncement::getMsgType, CommonConstant.MSG_TYPE_ALL);
+        //未删除
+        querySaWrapper.eq(SysAnnouncement::getDelFlag, CommonConstant.DEL_FLAG_0.toString());
+        //已发布
+        querySaWrapper.eq(SysAnnouncement::getSendStatus, CommonConstant.HAS_SEND);
+        //新注册用户不看结束通知
+        querySaWrapper.ge(SysAnnouncement::getEndTime, sysUser.getCreateTime());
+        //update-begin--Author:liusq  Date:20210108 for:[JT-424] 【开源issue】bug处理--------------------
+        querySaWrapper.notInSql(SysAnnouncement::getId, "select annt_id from sys_announcement_send where user_id='" + userId + "'");
+        //update-begin--Author:liusq  Date:20210108  for: [JT-424] 【开源issue】bug处理--------------------
+        List<SysAnnouncement> announcements = this.list(querySaWrapper);
+        if (announcements.size() > 0) {
+            for (int i = 0; i < announcements.size(); i++) {
+                //update-begin--Author:wangshuai  Date:20200803  for: 通知公告消息重复LOWCOD-759--------------------
+                //因为websocket没有判断是否存在这个用户,要是判断会出现问题,故在此判断逻辑
+                LambdaQueryWrapper<SysAnnouncementSend> query = new LambdaQueryWrapper<>();
+                query.eq(SysAnnouncementSend::getAnntId, announcements.get(i).getId());
+                query.eq(SysAnnouncementSend::getUserId, userId);
+                SysAnnouncementSend one = sysAnnouncementSendMapper.selectOne(query);
+                if (null == one) {
+                    SysAnnouncementSend announcementSend = new SysAnnouncementSend();
+                    announcementSend.setAnntId(announcements.get(i).getId());
+                    announcementSend.setUserId(userId);
+                    announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG);
+                    sysAnnouncementSendMapper.insert(announcementSend);
+                    log.info("announcementSend.toString()", announcementSend.toString());
+                }
+                //update-end--Author:wangshuai  Date:20200803  for: 通知公告消息重复LOWCOD-759------------
+            }
+        }
+
+    }
+
+    @Override
+    public List<SysAnnouncement> querySysMessageList(int pageSize, int pageNo, String fromUser, String starFlag, Date beginDate, Date endDate) {
+        //1. 补全send表的数据
+        completeAnnouncementSendInfo();
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        Page<SysAnnouncement> page = new Page<SysAnnouncement>(pageNo, pageSize);
+        // 2. 查询消息数据
+        List<SysAnnouncement> list = baseMapper.queryMessageList(page, sysUser.getId(), fromUser, starFlag, beginDate, endDate);
+        return list;
+    }
+
+    @Override
+    public void updateReaded(List<String> annoceIdList) {
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        sysAnnouncementSendMapper.updateReaded(sysUser.getId(), annoceIdList);
+    }
+
+    @Override
+    public List<SysAnnouncement> listNoRead(SysAnnouncement announcement) {
+        return sysAnnouncementMapper.listNoRead(announcement);
+    }
+
+    @Override
+    public void readMessage(String id, String userId) {
+        //未登录的话
+        if (StringUtils.isBlank(userId)) {
+            return;
+        }
+        LambdaQueryWrapper<SysAnnouncementSend> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SysAnnouncementSend::getAnntId, id);
+        queryWrapper.eq(SysAnnouncementSend::getUserId, userId);
+        List<SysAnnouncementSend> messageUserList = announcementSendService.list(queryWrapper);
+        if (CollectionUtils.isEmpty(messageUserList)) {
+            SysAnnouncementSend messageUser = new SysAnnouncementSend();
+            messageUser.setAnntId(id);
+            messageUser.setUserId(userId);
+            messageUser.setReadFlag(CommonConstant.HAS_READ_FLAG);
+            messageUser.setReadTime(new Date());
+            announcementSendService.save(messageUser);
+        } else {
+            SysAnnouncementSend messageUser = messageUserList.get(0);
+            messageUser.setReadFlag(CommonConstant.HAS_READ_FLAG);
+            messageUser.setReadTime(new Date());
+            announcementSendService.updateById(messageUser);
+        }
+    }
 
 }

+ 36 - 2
system/system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysUserServiceImpl.java

@@ -1,6 +1,5 @@
 package org.jeecg.modules.system.service.impl;
 
-import cn.hutool.core.collection.CollectionUtil;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -15,6 +14,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.shiro.SecurityUtils;
+import org.jeecg.common.api.CommonAPI;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.config.TenantContext;
 import org.jeecg.common.constant.CacheConstant;
@@ -24,6 +24,7 @@ import org.jeecg.common.constant.SymbolConstant;
 import org.jeecg.common.constant.enums.RoleIndexConfigEnum;
 import org.jeecg.common.desensitization.annotation.SensitiveEncode;
 import org.jeecg.common.exception.JeecgBootException;
+import org.jeecg.common.system.util.JwtUtil;
 import org.jeecg.common.system.vo.LoginUser;
 import org.jeecg.common.system.vo.SysUserCacheInfo;
 import org.jeecg.common.util.*;
@@ -39,7 +40,6 @@ import org.jeecg.modules.system.vo.lowapp.DepartAndUserInfo;
 import org.jeecg.modules.system.vo.lowapp.DepartInfo;
 import org.jeecg.modules.system.vo.lowapp.UpdateDepartInfo;
 import org.springframework.beans.BeanUtils;
-import org.springframework.beans.factory.NoSuchBeanDefinitionException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
@@ -97,6 +97,15 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
     @Autowired
     private SysUserTenantMapper userTenantMapper;
 
+    @Lazy
+    @Resource
+    private CommonAPI commonApi;
+
+    @Lazy
+    @Resource
+    private RedisUtil redisUtil;
+    private String token;
+
     @Override
     public Result<IPage<SysUser>> queryPageList(HttpServletRequest req, QueryWrapper<SysUser> queryWrapper, Integer pageSize, Integer pageNo) {
         Result<IPage<SysUser>> result = new Result<IPage<SysUser>>();
@@ -1284,4 +1293,29 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
         return userMapper.getUserByDepIds(departIds, username);
     }
 
+    /**
+     * 获取登录用户信息
+     *
+     * @param token
+     * @return
+     */
+    @Override
+    public LoginUser getLoginUser(String token) {
+        this.token = token;
+        if (StringUtils.isBlank(token)) {
+            return null;
+        }
+        String username = JwtUtil.getUsername(token);
+        // 查询用户信息
+        return TokenUtils.getLoginUser(username, commonApi, redisUtil);
+    }
+
+    @Override
+    public SysUser queryByOpenId(String openid) {
+        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper();
+        //用户所有角色
+        queryWrapper.eq(SysUser::getOpenId, openid);
+        return getOne(queryWrapper);
+    }
+
 }

+ 46 - 0
web/src/main/java/com/ynfy/app/api/v1/annoation/ApiLog.java

@@ -0,0 +1,46 @@
+package com.ynfy.app.api.v1.annoation;
+
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.constant.enums.ModuleType;
+
+import java.lang.annotation.*;
+
+/**
+ * 系统日志注解
+ * 
+ * @Author scott
+ * @email jeecgos@163.com
+ * @Date 2019年1月14日
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface ApiLog {
+
+	/**
+	 * 日志内容
+	 * 
+	 * @return
+	 */
+	String value() default "";
+
+	/**
+	 * 日志类型
+	 * 
+	 * @return 0:操作日志;1:登录日志;2:定时任务;
+	 */
+	int logType() default CommonConstant.LOG_TYPE_2;
+	
+	/**
+	 * 操作日志类型
+	 * 
+	 * @return (1查询,2添加,3修改,4删除)
+	 */
+	int operateType() default 0;
+
+	/**
+	 * 模块类型 默认为common
+	 * @return
+	 */
+	ModuleType module() default ModuleType.COMMON;
+}

+ 15 - 0
web/src/main/java/com/ynfy/app/api/v1/annoation/IgnoreAuth.java

@@ -0,0 +1,15 @@
+package com.ynfy.app.api.v1.annoation;
+
+
+import java.lang.annotation.*;
+
+/**
+ * 忽略Token验证
+ *
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface IgnoreAuth {
+
+}

+ 264 - 0
web/src/main/java/com/ynfy/app/api/v1/aspect/ApiLogAspect.java

@@ -0,0 +1,264 @@
+package com.ynfy.app.api.v1.aspect;
+
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.serializer.PropertyFilter;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.jeecg.common.api.dto.LogDTO;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.constant.enums.ModuleType;
+import org.jeecg.common.constant.enums.OperateTypeEnum;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.IpUtils;
+import org.jeecg.common.util.SpringContextUtils;
+import org.jeecg.common.util.TokenUtil;
+import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.modules.base.service.BaseCommonService;
+import org.jeecg.modules.system.service.ISysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.util.Objects;
+
+
+/**
+ * 移动端api日志,切面处理类
+ */
+@Aspect
+@Component
+public class ApiLogAspect {
+
+    @Resource
+    private BaseCommonService baseCommonService;
+
+    @Autowired
+    private ISysUserService sysUserService;
+
+    @Pointcut("@annotation(com.ynfy.app.api.v1.annoation.ApiLog)")
+    public void logPointCut() {
+
+    }
+
+    @Around("logPointCut()")
+    public Object around(ProceedingJoinPoint point) throws Throwable {
+        long beginTime = System.currentTimeMillis();
+        //执行方法
+        Object result = point.proceed();
+        //执行时长(毫秒)
+        long time = System.currentTimeMillis() - beginTime;
+
+        //保存日志
+        saveSysLog(point, time, result);
+
+        return result;
+    }
+
+    private void saveSysLog(ProceedingJoinPoint joinPoint, long time, Object obj) {
+        //获取request
+        HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
+
+        //获取登录用户信息
+        LoginUser sysUser = sysUserService.getLoginUser(TokenUtil.getToken(request));
+        if (Objects.isNull(sysUser)) {
+            return;
+        }
+        LogDTO dto = new LogDTO();
+        dto.setUserid(sysUser.getUsername());
+        dto.setUsername(sysUser.getRealname());
+
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+
+
+        AutoLog syslog = method.getAnnotation(AutoLog.class);
+        if (syslog != null) {
+            //update-begin-author:taoyan date:
+            String content = syslog.value();
+            if (syslog.module() == ModuleType.ONLINE) {
+                content = getOnlineLogContent(obj, content);
+            }
+            //注解上的描述,操作日志内容
+            dto.setLogType(syslog.logType());
+            dto.setLogContent(content);
+        }
+
+        //请求的方法名
+        String className = joinPoint.getTarget().getClass().getName();
+        String methodName = signature.getName();
+        dto.setMethod(className + "." + methodName + "()");
+
+
+        //设置操作类型
+        if (CommonConstant.LOG_TYPE_2 == dto.getLogType()) {
+            dto.setOperateType(getOperateType(methodName, syslog.operateType()));
+        }
+
+        //请求的参数
+        dto.setRequestParam(getReqestParams(request, joinPoint));
+        //设置IP地址
+        dto.setIp(IpUtils.getIpAddr(request));
+
+        //耗时
+        dto.setCostTime(time);
+        dto.setCreateTime(new Date());
+        //保存系统日志
+        baseCommonService.addLog(dto);
+    }
+
+
+    /**
+     * 获取操作类型
+     */
+    private int getOperateType(String methodName, int operateType) {
+        if (operateType > 0) {
+            return operateType;
+        }
+        //update-begin---author:wangshuai ---date:20220331  for:阿里云代码扫描规范(不允许任何魔法值出现在代码中)------------
+        return OperateTypeEnum.getTypeByMethodName(methodName);
+        //update-end---author:wangshuai ---date:20220331  for:阿里云代码扫描规范(不允许任何魔法值出现在代码中)------------
+    }
+
+    /**
+     * @param request:   request
+     * @param joinPoint: joinPoint
+     * @Description: 获取请求参数
+     * @author: scott
+     * @date: 2020/4/16 0:10
+     * @Return: java.lang.String
+     */
+    private String getReqestParams(HttpServletRequest request, JoinPoint joinPoint) {
+        String httpMethod = request.getMethod();
+        String params = "";
+        if (CommonConstant.HTTP_POST.equals(httpMethod) || CommonConstant.HTTP_PUT.equals(httpMethod) || CommonConstant.HTTP_PATCH.equals(httpMethod)) {
+            Object[] paramsArray = joinPoint.getArgs();
+            // java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
+            //  https://my.oschina.net/mengzhang6/blog/2395893
+            Object[] arguments = new Object[paramsArray.length];
+            for (int i = 0; i < paramsArray.length; i++) {
+                if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) {
+                    //ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
+                    //ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
+                    continue;
+                }
+                arguments[i] = paramsArray[i];
+            }
+            //update-begin-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
+            PropertyFilter profilter = new PropertyFilter() {
+                @Override
+                public boolean apply(Object o, String name, Object value) {
+                    int length = 500;
+                    if (value != null && value.toString().length() > length) {
+                        return false;
+                    }
+                    return true;
+                }
+            };
+            params = JSONObject.toJSONString(arguments, profilter);
+            //update-end-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
+        } else {
+            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+            Method method = signature.getMethod();
+            // 请求的方法参数值
+            Object[] args = joinPoint.getArgs();
+            // 请求的方法参数名称
+            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
+            String[] paramNames = u.getParameterNames(method);
+            if (args != null && paramNames != null) {
+                for (int i = 0; i < args.length; i++) {
+                    params += "  " + paramNames[i] + ": " + args[i];
+                }
+            }
+        }
+        return params;
+    }
+
+    /**
+     * online日志内容拼接
+     *
+     * @param obj
+     * @param content
+     * @return
+     */
+    private String getOnlineLogContent(Object obj, String content) {
+        if (Result.class.isInstance(obj)) {
+            Result res = (Result) obj;
+            String msg = res.getMessage();
+            String tableName = res.getOnlTable();
+            if (oConvertUtils.isNotEmpty(tableName)) {
+                content += ",表名:" + tableName;
+            }
+            if (res.isSuccess()) {
+                content += "," + (oConvertUtils.isEmpty(msg) ? "操作成功" : msg);
+            } else {
+                content += "," + (oConvertUtils.isEmpty(msg) ? "操作失败" : msg);
+            }
+        }
+        return content;
+    }
+
+
+    /*    private void saveSysLog(ProceedingJoinPoint joinPoint, long time, Object obj) {
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+
+        SysLog sysLog = new SysLog();
+        AutoLog syslog = method.getAnnotation(AutoLog.class);
+        if(syslog != null){
+            //update-begin-author:taoyan date:
+            String content = syslog.value();
+            if(syslog.module()== ModuleType.ONLINE){
+                content = getOnlineLogContent(obj, content);
+            }
+            //注解上的描述,操作日志内容
+            sysLog.setLogContent(content);
+            sysLog.setLogType(syslog.logType());
+        }
+
+        //请求的方法名
+        String className = joinPoint.getTarget().getClass().getName();
+        String methodName = signature.getName();
+        sysLog.setMethod(className + "." + methodName + "()");
+
+
+        //设置操作类型
+        if (sysLog.getLogType() == CommonConstant.LOG_TYPE_2) {
+            sysLog.setOperateType(getOperateType(methodName, syslog.operateType()));
+        }
+
+        //获取request
+        HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
+        //请求的参数
+        sysLog.setRequestParam(getReqestParams(request,joinPoint));
+
+        //设置IP地址
+        sysLog.setIp(IPUtils.getIpAddr(request));
+
+        //获取登录用户信息
+        LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
+        if(sysUser!=null){
+            sysLog.setUserid(sysUser.getUsername());
+            sysLog.setUsername(sysUser.getRealname());
+
+        }
+        //耗时
+        sysLog.setCostTime(time);
+        sysLog.setCreateTime(new Date());
+        //保存系统日志
+        sysLogService.save(sysLog);
+    }*/
+}

+ 61 - 0
web/src/main/java/com/ynfy/app/api/v1/controller/ApiAircleController.java

@@ -0,0 +1,61 @@
+package com.ynfy.app.api.v1.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ynfy.app.api.v1.annoation.IgnoreAuth;
+import com.ynfy.app.api.v1.entity.dto.ArticleDTO;
+import com.ynfy.buss.article.entity.Article;
+import com.ynfy.buss.article.service.IArticleService;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.common.api.vo.Result;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+
+@RestController
+@RequestMapping("/api/v1/article")
+@Slf4j
+public class ApiAircleController extends ApiBaseController {
+    @Autowired
+    private IArticleService articleService;
+
+    /**
+     * 分页列表查询
+     *
+     * @return
+     */
+    @ApiOperation(value = "分页列表查询", notes = "分页列表查询")
+    @IgnoreAuth
+    @PostMapping(value = "/list")
+    public Result<IPage<Article>> queryPageList(@RequestBody ArticleDTO dto) {
+        Page<Article> page = new Page<>(dto.getPageNo(), dto.getPageSize());
+        LambdaQueryWrapper<Article> query = new LambdaQueryWrapper<>();
+        if (StringUtils.isNotBlank(dto.getTitle())) {
+            query.like(Article::getTitle, dto.getTitle());
+        }
+        query.eq(Article::getStatus, 1); //已发布
+        IPage<Article> pageList = articleService.page(page, query);
+        return Result.OK(pageList);
+    }
+
+    /**
+     * 通过id查询
+     *
+     * @param id
+     * @return
+     */
+    @ApiOperation(value = "通过id查询", notes = "通过id查询")
+    @GetMapping(value = "/queryById")
+    public Result<Article> queryById(@RequestParam String id) {
+        Article article = articleService.getById(id);
+        if (article == null) {
+            return Result.error("未找到对应数据");
+        }
+        return Result.OK(article);
+    }
+
+
+}

+ 226 - 0
web/src/main/java/com/ynfy/app/api/v1/controller/ApiAuthController.java

@@ -0,0 +1,226 @@
+package com.ynfy.app.api.v1.controller;
+
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.ynfy.app.api.v1.annoation.IgnoreAuth;
+import com.ynfy.app.api.v1.entity.dto.UserInfo;
+import com.ynfy.app.api.v1.entity.dto.WxLoginDTO;
+import com.ynfy.app.api.v1.util.ApiUserUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.system.util.JwtUtil;
+import org.jeecg.common.util.PasswordUtil;
+import org.jeecg.common.util.RedisUtil;
+import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.modules.base.service.BaseCommonService;
+import org.jeecg.modules.system.entity.SysDepart;
+import org.jeecg.modules.system.entity.SysUser;
+import org.jeecg.modules.system.model.SysLoginModel;
+import org.jeecg.modules.system.service.ISysDepartService;
+import org.jeecg.modules.system.service.ISysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.*;
+
+@Api(tags = "API登录授权接口")
+@RestController
+@RequestMapping("/api/v1/auth")
+@Slf4j
+public class ApiAuthController {
+
+    @Autowired
+    private ISysUserService sysUserService;
+    @Autowired
+    private RedisUtil redisUtil;
+    @Autowired
+    private ISysDepartService sysDepartService;
+    @Resource
+    private BaseCommonService baseCommonService;
+
+    /**
+     * app登录
+     *
+     * @param sysLoginModel
+     * @return
+     * @throws Exception
+     */
+    @IgnoreAuth
+    @RequestMapping(value = "/login", method = RequestMethod.POST)
+    public Result<JSONObject> login(@RequestBody SysLoginModel sysLoginModel) throws Exception {
+        Result<JSONObject> result = new Result<JSONObject>();
+        String username = sysLoginModel.getUsername();
+        String password = sysLoginModel.getPassword();
+        JSONObject obj = new JSONObject();
+
+        //update-begin-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
+        if (isLoginFailOvertimes(username)) {
+            return result.error500("该用户登录失败次数过多,请于10分钟后再次登录!");
+        }
+        //update-end-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
+        //1. 校验用户是否有效
+        SysUser sysUser = sysUserService.getUserByName(username);
+        result = sysUserService.checkUserIsEffective(sysUser);
+        if (!result.isSuccess()) {
+            return result;
+        }
+
+        //2. 校验用户名或密码是否正确
+        String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
+        String syspassword = sysUser.getPassword();
+        if (!syspassword.equals(userpassword)) {
+            //update-begin-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
+            addLoginFailOvertimes(username);
+            //update-end-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
+            result.error500("用户名或密码错误");
+            return result;
+        }
+
+        //3.设置登录部门
+        String orgCode = sysUser.getOrgCode();
+        if (oConvertUtils.isEmpty(orgCode)) {
+            //如果当前用户无选择部门 查看部门关联信息
+            List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
+            //update-begin-author:taoyan date:20220117 for: JTC-1068【app】新建用户,没有设置部门及角色,点击登录提示暂未归属部,一直在登录页面 使用手机号登录 可正常
+            if (departs == null || departs.size() == 0) {
+				/*result.error500("用户暂未归属部门,不可登录!");
+				return result;*/
+            } else {
+                orgCode = departs.get(0).getOrgCode();
+                sysUser.setOrgCode(orgCode);
+                this.sysUserService.updateUserDepart(username, orgCode, null);
+            }
+            //update-end-author:taoyan date:20220117 for: JTC-1068【app】新建用户,没有设置部门及角色,点击登录提示暂未归属部,一直在登录页面 使用手机号登录 可正常
+        }
+
+        //4. 设置登录租户
+        Result<JSONObject> loginTenantError = sysUserService.setLoginTenant(sysUser, obj, username, result);
+        if (loginTenantError != null) {
+            return loginTenantError;
+        }
+
+        //5. 设置登录用户信息
+        obj.put("userInfo", sysUser);
+
+        //6. 生成token
+        Map<String, Object> map = new HashMap();
+        map.put("username", username);
+        map.put("realname", sysUser.getRealname());
+        map.put("userId", sysUser.getId());
+        String token = JwtUtil.sign(map, syspassword);
+        // 设置超时时间
+        redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
+        redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
+
+        //token 信息
+        obj.put("token", token);
+        result.setResult(obj);
+        result.setSuccess(true);
+        result.setCode(200);
+        baseCommonService.addLog("用户名: " + username + ",登录成功[移动端]!", CommonConstant.LOG_TYPE_1, null);
+        return result;
+    }
+
+    /**
+     * 登录失败超出次数5 返回true
+     *
+     * @param username
+     * @return
+     */
+    private boolean isLoginFailOvertimes(String username) {
+        String key = CommonConstant.LOGIN_FAIL + username;
+        Object failTime = redisUtil.get(key);
+        if (failTime != null) {
+            Integer val = Integer.parseInt(failTime.toString());
+            if (val > 5) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * 记录登录失败次数
+     *
+     * @param username
+     */
+    private void addLoginFailOvertimes(String username) {
+        String key = CommonConstant.LOGIN_FAIL + username;
+        Object failTime = redisUtil.get(key);
+        Integer val = 0;
+        if (failTime != null) {
+            val = Integer.parseInt(failTime.toString());
+        }
+        // 10分钟
+        redisUtil.set(key, ++val, 10);
+    }
+
+    /**
+     * 微信登录
+     */
+    @ApiOperation(value = "微信登录")
+    @IgnoreAuth
+    @PostMapping("loginByWx")
+    public Result<?> loginByWeixin(@RequestBody WxLoginDTO dto) {
+        if (StringUtils.isBlank(dto.getCode())) {
+            return Result.error("code不能为空");
+        }
+        UserInfo userInfo = dto.getUserInfo();
+        //获取openid
+        String requestUrl = ApiUserUtils.getWebAccess(dto.getCode());//通过自定义工具类组合出小程序需要的登录凭证 code
+        log.info("》》》组合token为:" + requestUrl);
+        JSONObject sessionData = JSON.parseObject(HttpUtil.get(requestUrl, CharsetUtil.CHARSET_UTF_8));
+
+        if (Objects.isNull(sessionData) || StringUtils.isBlank(sessionData.getString("openid"))) {
+            return Result.error("登录失败");
+        }
+        String openId = sessionData.getString("openid");
+        SysUser wxUser = sysUserService.queryByOpenId(openId);
+        if (Objects.isNull(wxUser)) {
+            wxUser = new SysUser();
+            wxUser.setCreateTime(new Date());//设置创建时间
+            String salt = oConvertUtils.randomGen(8);
+            wxUser.setSalt(salt);
+            wxUser.setPassword("123456");
+            wxUser.setUsername(openId);
+            wxUser.setRealname("微信用户");
+            String passwordEncode = PasswordUtil.encrypt(wxUser.getUsername(), wxUser.getPassword(), salt);
+            wxUser.setPassword(passwordEncode);
+            wxUser.setStatus(1);
+            wxUser.setDelFlag(CommonConstant.DEL_FLAG_0);
+            wxUser.setOpenId(openId);
+            wxUser.setAvatar(!Objects.isNull(userInfo) ? userInfo.getAvatarUrl() : null);
+            wxUser.setRealname(!Objects.isNull(userInfo) ? userInfo.getNickName() : null);
+            sysUserService.save(wxUser);
+        } else {
+            wxUser.setAvatar(!Objects.isNull(userInfo) ? userInfo.getAvatarUrl() : null);
+            wxUser.setRealname(!Objects.isNull(userInfo) ? userInfo.getNickName() : null);
+            sysUserService.updateById(wxUser);
+        }
+        Result<JSONObject> result = new Result<JSONObject>();
+        Map<String, Object> map = new HashMap();
+        map.put("username", wxUser.getUsername());
+        map.put("realname", wxUser.getRealname());
+        map.put("userId", wxUser.getId());
+        String token = JwtUtil.sign(map, wxUser.getPassword());
+
+        JSONObject obj = new JSONObject();
+        obj.put("userInfo", wxUser);
+        //token 信息
+        obj.put("token", token);
+        result.setResult(obj);
+        result.setSuccess(true);
+        result.setCode(200);
+        baseCommonService.addLog("用户名: " + wxUser.getUsername() + ",微信登录成功!", CommonConstant.LOG_TYPE_1, null);
+        return result;
+    }
+
+}

+ 40 - 0
web/src/main/java/com/ynfy/app/api/v1/controller/ApiBaseController.java

@@ -0,0 +1,40 @@
+package com.ynfy.app.api.v1.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
+import java.io.IOException;
+
+public class ApiBaseController {
+
+    /**
+     * 得到request对象
+     */
+    @Autowired
+    protected HttpServletRequest request;
+
+    /**
+     * 得到response对象
+     */
+    @Autowired
+    protected HttpServletResponse response;
+
+    public JSONObject getJsonRequest() {
+        JSONObject result = null;
+        StringBuilder sb = new StringBuilder();
+        try (BufferedReader reader = request.getReader()) {
+            char[] buff = new char[1024];
+            int len;
+            while ((len = reader.read(buff)) != -1) {
+                sb.append(buff, 0, len);
+            }
+            result = JSONObject.parseObject(sb.toString());
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+}

+ 294 - 0
web/src/main/java/com/ynfy/app/api/v1/controller/ApiCommonController.java

@@ -0,0 +1,294 @@
+package com.ynfy.app.api.v1.controller;
+
+import com.ynfy.app.api.v1.annoation.IgnoreAuth;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.constant.SymbolConstant;
+import org.jeecg.common.exception.JeecgBootException;
+import org.jeecg.common.util.CommonUtils;
+import org.jeecg.common.util.MinioUtil;
+import org.jeecg.common.util.filter.FileTypeFilter;
+import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.modules.system.entity.FileInfo;
+import org.jeecg.modules.system.service.IFileInfoService;
+import org.jeecg.modules.system.util.FileUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+import org.springframework.web.servlet.HandlerMapping;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.util.Objects;
+
+/**
+ * <p>
+ * 用户表 前端控制器
+ * </p>
+ *
+ * @Author scott
+ * @since 2018-12-20
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/v1/common")
+public class ApiCommonController {
+
+    @Value(value = "${jeecg.path.upload}")
+    private String uploadpath;
+
+    /**
+     * 本地:local minio:minio 阿里:alioss
+     */
+    @Value(value = "${jeecg.uploadType}")
+    private String uploadType;
+
+
+    @Value("${tmpPath}")
+    private String tmpPath;
+
+
+    @Autowired
+    private IFileInfoService fileInfoService;
+
+    /**
+     * 预览图片&下载文件
+     * 请求地址:http://localhost:8080/common/static/{user/20190119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg}
+     *
+     * @param request
+     * @param response
+     */
+    @IgnoreAuth
+    @GetMapping(value = "/static/**")
+    public void view(HttpServletRequest request, HttpServletResponse response) {
+        // ISO-8859-1 ==> UTF-8 进行编码转换
+        String imgPath = extractPathFromPattern(request);
+        if (oConvertUtils.isEmpty(imgPath) || CommonConstant.STRING_NULL.equals(imgPath)) {
+            return;
+        }
+        // 其余处理略
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+        try {
+            imgPath = imgPath.replace("..", "").replace("../", "");
+            if (imgPath.endsWith(SymbolConstant.COMMA)) {
+                imgPath = imgPath.substring(0, imgPath.length() - 1);
+            }
+            String fileName = null;
+            if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)) {
+                String filePath = uploadpath + File.separator + imgPath;
+                File file = new File(filePath);
+                if (!file.exists()) {
+                    response.setStatus(404);
+                    throw new RuntimeException("文件[" + imgPath + "]不存在..");
+                }
+                fileName = file.getName();
+                inputStream = new BufferedInputStream(new FileInputStream(filePath));
+            } else if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
+                inputStream = MinioUtil.getMinioFile(imgPath);
+                FileInfo fileInfo = fileInfoService.listByFileSaveId(imgPath);
+                fileName = !Objects.isNull(fileInfo) ? fileInfo.getFileName() : null;
+            }
+
+            // 设置强制下载不打开
+            response.setContentType("application/force-download");
+            response.addHeader("Content-Disposition", "attachment;fileName=" + new String(fileName.getBytes("UTF-8"), "iso-8859-1"));
+
+            //Accept-Ranges字段为bytes时,表示服务器支持按字节范围请求文件
+            response.addHeader("accept-ranges", "bytes");
+
+            // 设置文件大小
+            long fileSize = MinioUtil.getFileSize(imgPath);
+            response.addHeader("content-length", String.valueOf(fileSize));
+
+            outputStream = response.getOutputStream();
+            byte[] buf = new byte[1024];
+            int len;
+            while ((len = inputStream.read(buf)) > 0) {
+                outputStream.write(buf, 0, len);
+            }
+            response.flushBuffer();
+        } catch (IOException e) {
+            log.error("预览文件失败" + e.getMessage());
+            response.setStatus(404);
+            e.printStackTrace();
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    log.error(e.getMessage(), e);
+                }
+            }
+            if (outputStream != null) {
+                try {
+                    outputStream.close();
+                } catch (IOException e) {
+                    log.info("imgPath:{}", imgPath);
+                    log.error(e.getMessage(), e);
+                }
+            }
+        }
+    }
+
+    /**
+     * @功能:pdf预览
+     */
+    @IgnoreAuth
+    @RequestMapping("/pdf/preview/**")
+    public void pdfPreviewIframe(HttpServletRequest request, HttpServletResponse response) {
+        String imgPath = extractPathFromPattern(request);
+        // 其余处理略
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+        try {
+            inputStream = MinioUtil.getMinioFile(imgPath);
+            outputStream = response.getOutputStream();
+            byte[] buf = new byte[1024];
+            int len;
+            while ((len = inputStream.read(buf)) > 0) {
+                outputStream.write(buf, 0, len);
+            }
+            response.flushBuffer();
+        } catch (Exception e) {
+            log.error("预览文件失败" + e.getMessage());
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    log.error(e.getMessage(), e);
+                }
+            }
+            if (outputStream != null) {
+                try {
+                    outputStream.close();
+                } catch (IOException e) {
+                    log.info("imgPath:{}", imgPath);
+                    log.error(e.getMessage(), e);
+                }
+            }
+        }
+    }
+
+    /**
+     * 把指定URL后的字符串全部截断当成参数
+     * 这么做是为了防止URL中包含中文或者特殊字符(/等)时,匹配不了的问题
+     *
+     * @param request
+     * @return
+     */
+    private static String extractPathFromPattern(final HttpServletRequest request) {
+        String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
+        String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
+        return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
+    }
+
+    /**
+     * 文件上传统一方法
+     *
+     * @param request
+     * @param response
+     * @return
+     */
+    @PostMapping(value = "/upload")
+    @IgnoreAuth
+    public Result<?> upload(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        String bizPath = request.getParameter("biz");
+        //LOWCOD-2580 sys/common/upload接口存在任意文件上传漏洞
+        if (oConvertUtils.isNotEmpty(bizPath)) {
+            if (bizPath.contains(SymbolConstant.SPOT_SINGLE_SLASH) || bizPath.contains(SymbolConstant.SPOT_DOUBLE_BACKSLASH)) {
+                throw new JeecgBootException("上传目录bizPath,格式非法!");
+            }
+        }
+        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
+        // 获取上传文件对象
+        MultipartFile file = multipartRequest.getFile("file");
+        if (oConvertUtils.isEmpty(bizPath)) {
+            if (CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)) {
+                //未指定目录,则用阿里云默认目录 upload
+                bizPath = "upload";
+            } else {
+                bizPath = "";
+            }
+        }
+        String savePath = null;
+        if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)) {
+            FileTypeFilter.fileTypeFilter(file);
+            savePath = this.uploadLocal(file, bizPath);
+        } else {
+            savePath = CommonUtils.upload(file, bizPath, uploadType);
+        }
+
+        //是否计算视频时长
+        Boolean isVideoDuration = Boolean.parseBoolean(request.getParameter("isVideoDuration"));
+        Long videoDuration = null;
+        if (isVideoDuration && FileUtil.isVideo(file.getOriginalFilename())) {
+            File f = new File(tmpPath + File.separator + file.getOriginalFilename());
+            FileUtils.copyInputStreamToFile(file.getInputStream(), f);
+            videoDuration = CommonUtils.getVideoDuration(f);
+        }
+
+        //保存文件信息
+        fileInfoService.saveFile(savePath, file.getOriginalFilename(), videoDuration);
+        if (oConvertUtils.isNotEmpty(savePath)) {
+            return Result.ok(savePath);
+        } else {
+            return Result.error("上传失败!");
+        }
+    }
+
+    /**
+     * 本地文件上传
+     *
+     * @param mf      文件
+     * @param bizPath 自定义路径
+     * @return
+     */
+    private String uploadLocal(MultipartFile mf, String bizPath) {
+        try {
+            String ctxPath = uploadpath;
+            String fileName = null;
+            File file = new File(ctxPath + File.separator + bizPath + File.separator);
+            if (!file.exists()) {
+                // 创建文件根目录
+                file.mkdirs();
+            }
+            // 获取文件名
+            String orgName = mf.getOriginalFilename();
+            orgName = CommonUtils.getFileName(orgName);
+            if (orgName.indexOf(SymbolConstant.SPOT) != -1) {
+                fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
+            } else {
+                fileName = orgName + "_" + System.currentTimeMillis();
+            }
+            String savePath = file.getPath() + File.separator + fileName;
+            File savefile = new File(savePath);
+            FileCopyUtils.copy(mf.getBytes(), savefile);
+            String dbpath = null;
+            if (oConvertUtils.isNotEmpty(bizPath)) {
+                dbpath = bizPath + File.separator + fileName;
+            } else {
+                dbpath = fileName;
+            }
+            if (dbpath.contains(SymbolConstant.DOUBLE_BACKSLASH)) {
+                dbpath = dbpath.replace(SymbolConstant.DOUBLE_BACKSLASH, SymbolConstant.SINGLE_SLASH);
+            }
+            return dbpath;
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        }
+        return "";
+    }
+
+}

+ 187 - 0
web/src/main/java/com/ynfy/app/api/v1/controller/ApiCourseController.java

@@ -0,0 +1,187 @@
+package com.ynfy.app.api.v1.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ynfy.app.api.v1.annoation.IgnoreAuth;
+import com.ynfy.app.api.v1.entity.dto.CourseDTO;
+import com.ynfy.buss.course.course.entity.Course;
+import com.ynfy.buss.course.course.service.ICourseService;
+import com.ynfy.buss.course.coursecatalog.service.ICourseCatalogService;
+import com.ynfy.buss.course.usercoursecatalog.entity.UserCourseCatalog;
+import com.ynfy.buss.course.usercoursecatalog.entity.vo.CatalogTimeVO;
+import com.ynfy.buss.course.usercoursecatalog.entity.vo.UserCourseStudyVO;
+import com.ynfy.buss.course.usercoursecatalog.service.IUserCourseCatalogService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.TokenUtil;
+import org.jeecg.modules.system.service.ISysPositionService;
+import org.jeecg.modules.system.service.ISysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+@Api(tags = "course")
+@RestController
+@RequestMapping("/api/v1/course")
+@Slf4j
+public class ApiCourseController extends ApiBaseController {
+    @Autowired
+    private ICourseService courseService;
+
+    @Autowired
+    private ICourseCatalogService courseCatalogService;
+
+    @Autowired
+    private ISysPositionService sysPositionService;
+
+    @Autowired
+    private IUserCourseCatalogService userCourseCatalogService;
+
+    @Autowired
+    private ISysUserService sysUserService;
+
+
+    /**
+     * 分页列表查询
+     *
+     * @return
+     */
+    @ApiOperation(value = "course-分页列表查询", notes = "course-分页列表查询")
+    @IgnoreAuth
+    @PostMapping(value = "/list")
+    public Result<IPage<Course>> queryPageList(@RequestBody CourseDTO dto) {
+        Page<Course> page = new Page<>(dto.getPageNo(), dto.getPageSize());
+        Course course = new Course();
+        course.setName(dto.getName());
+        LoginUser user = sysUserService.getLoginUser(TokenUtil.getToken(request));
+        IPage<Course> pageList = courseService.selectCourseList(page, course, user);
+
+        //设置课程分类
+        courseService.setCategory(pageList);
+        if (!CollectionUtils.isEmpty(pageList.getRecords())) {
+            List<String> courseIdList = pageList.getRecords().stream().map(Course::getId).collect(Collectors.toList());
+            List<UserCourseStudyVO> studyList = userCourseCatalogService.getUserCourseStudy(courseIdList, TokenUtil.getUserId(TokenUtil.getToken(request)));
+            if (!CollectionUtils.isEmpty(studyList)) {
+                pageList.getRecords().forEach((o) -> courseService.setCourseStudyProcess(studyList, o));
+            }
+        }
+        return Result.OK(pageList);
+    }
+
+    /**
+     * 通过id查询
+     *
+     * @param id
+     * @return
+     */
+    //@AutoLog(value = "course-通过id查询")
+    @ApiOperation(value = "course-通过id查询", notes = "course-通过id查询")
+    @GetMapping(value = "/queryById")
+    public Result<Course> queryById(@RequestParam(name = "id", required = true) String id) {
+        Course course = courseService.queryById(id);
+        if (course == null) {
+            return Result.error("未找到对应数据");
+        }
+        course.setCatalogList(courseCatalogService.listByCourseId(course.getId()));
+        return Result.OK(course);
+    }
+
+    /**
+     * 获取课程学习明细
+     *
+     * @param id
+     * @return
+     */
+    @ApiOperation(value = "获取课程学习明细", notes = "获取课程学习明细")
+    @GetMapping(value = "/getCourseStudyDetail")
+    public Result<Course> getCourseStudyDetail(@RequestParam String id) {
+        Course course = courseService.queryById(id);
+        if (course == null) {
+            return Result.error("未找到对应数据");
+        }
+        course.setCatalogList(courseCatalogService.listByCourseId(course.getId()));
+        if (StringUtils.isNotBlank(course.getTeacherPost())) {
+            course.setTeacherPost(sysPositionService.listByCodes(Arrays.asList(course.getTeacherPost().split(","))));
+        }
+
+        //获取课程学习进度
+        List<String> courseIdList = new ArrayList<>();
+        courseIdList.add(course.getId());
+        courseService.setCourseStudyProcess(userCourseCatalogService.getUserCourseStudy(courseIdList, TokenUtil.getUserId(TokenUtil.getToken(request))), course);
+
+        return Result.OK(course);
+    }
+
+    /**
+     * 计算任务学习时长
+     *
+     * @return
+     */
+    @AutoLog(value = "计算任务学习时长")
+    @ApiOperation(value = "计算任务学习时长", notes = "计算任务学习时长")
+    @PostMapping(value = "/calcStudyTime")
+    public Result<String> calcStudyTime(@RequestBody CatalogTimeVO catalogTimeVO) {
+        userCourseCatalogService.calcStudyTime(catalogTimeVO, TokenUtil.getUserId(TokenUtil.getToken(request)));
+        return Result.OK();
+    }
+
+
+    /**
+     * 获取课程任务学习进度
+     *
+     * @return
+     */
+    @AutoLog(value = "获取课程任务学习进度")
+    @ApiOperation(value = "获取课程任务学习进度", notes = "获取课程任务学习进度")
+    @GetMapping(value = "/getCourseStudyProcess")
+    public Result<?> getCourseStudyProcess(@RequestParam String courseId) {
+        return Result.OK(userCourseCatalogService.getCourseStudyProcess(courseId, TokenUtil.getUserId(TokenUtil.getToken(request))));
+    }
+
+    /**
+     * 获取任务学习记录
+     *
+     * @return
+     */
+    @AutoLog(value = "获取任务学习记录")
+    @ApiOperation(value = "获取任务学习记录", notes = "获取任务学习记录")
+    @GetMapping(value = "/findUserCatalog")
+    public Result<?> findUserCatalog(@RequestParam String courseCatalogId) {
+        return Result.OK(userCourseCatalogService.findUserCatalog(TokenUtil.getUserId(TokenUtil.getToken(request)), courseCatalogId));
+    }
+
+    /**
+     * 检查上一个任务是否完成
+     *
+     * @param id
+     * @return
+     */
+    @GetMapping("/checkPreTaskComplete")
+    public Result<?> checkPreTaskComplete(@RequestParam String id) {
+        LoginUser user = sysUserService.getLoginUser(TokenUtil.getToken(request));
+        return Result.OK(courseCatalogService.checkPreTaskComplete(id, user));
+    }
+
+    @ApiOperation(value = "用户课程任务学习情况-分页列表查询", notes = "用户课程任务学习情况-分页列表查询")
+    @PostMapping(value = "/userCourseCatalog/list")
+    public Result<IPage<UserCourseCatalog>> userCourseList(@RequestBody CourseDTO dto) {
+        Page<UserCourseCatalog> page = new Page<>(dto.getPageNo(), dto.getPageSize());
+        UserCourseCatalog userCourseCatalog = new UserCourseCatalog();
+        userCourseCatalog.setCourseName(dto.getName());
+        userCourseCatalog.setUserId(TokenUtil.getUserId(TokenUtil.getToken(request)));
+        IPage<UserCourseCatalog> pageList = userCourseCatalogService.selectPageList(page, userCourseCatalog);
+        return Result.OK(pageList);
+    }
+
+}

+ 214 - 0
web/src/main/java/com/ynfy/app/api/v1/controller/ApiExamController.java

@@ -0,0 +1,214 @@
+package com.ynfy.app.api.v1.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ynfy.app.api.v1.annoation.IgnoreAuth;
+import com.ynfy.app.api.v1.entity.dto.ExamDTO;
+import com.ynfy.buss.exam.exam.dto.ExamSubmitDTO;
+import com.ynfy.buss.exam.exam.dto.QuestionTypeCountDTO;
+import com.ynfy.buss.exam.exam.entity.Exam;
+import com.ynfy.buss.exam.exam.service.IExamService;
+import com.ynfy.buss.exam.paper.enmus.JoinType;
+import com.ynfy.buss.exam.paper.entity.Paper;
+import com.ynfy.buss.exam.paperrulegroup.service.IPaperRuleGroupService;
+import com.ynfy.buss.exam.question.enums.QuestionType;
+import com.ynfy.buss.exam.userexam.entity.UserExam;
+import com.ynfy.buss.exam.userexam.service.IUserExamService;
+import com.ynfy.buss.exam.userexamresult.entity.UserExamResult;
+import com.ynfy.buss.exam.userexamresult.service.IUserExamResultService;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.TokenUtil;
+import org.jeecg.modules.system.service.ISysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Objects;
+
+@Slf4j
+@RestController
+@RequestMapping("/api/v1/exam")
+public class ApiExamController extends ApiBaseController {
+
+    @Autowired
+    public IExamService examService;
+
+    @Autowired
+    private IUserExamService userExamService;
+
+    @Autowired
+    private IPaperRuleGroupService paperRuleGroupService;
+
+    @Autowired
+    private IUserExamResultService userExamResultService;
+
+    @Autowired
+    private ISysUserService sysUserService;
+
+    /**
+     * 获取考试列表
+     *
+     * @return
+     */
+    @PostMapping("/list")
+    @IgnoreAuth
+    public Result<IPage<Exam>> list(@RequestBody ExamDTO dto) {
+        Page<Exam> page = new Page<>(dto.getPageNo(), dto.getPageSize());
+        Exam exam = new Exam();
+        exam.setTitle(dto.getExamTitle());
+        exam.setState(dto.getState());
+        LoginUser user = sysUserService.getLoginUser(TokenUtil.getToken(request));
+        IPage<Exam> pageList = examService.selectExamList(page, exam, user);
+        return Result.OK(pageList);
+    }
+
+    @GetMapping(value = "/queryById")
+    public Result<Exam> queryById(@RequestParam(name = "id", required = true) String id) {
+        Exam exam = examService.detail(id);
+        if (Objects.isNull(exam)) {
+            return Result.error("考试不存在");
+        }
+        Paper paper = exam.getPaper();
+        if (!Objects.isNull(paper)) {
+            paper.setJoinType_dictText(JoinType.getByCode(paper.getJoinType()).getValue());
+        }
+        List<QuestionTypeCountDTO> dtoList = paperRuleGroupService.sumQuestionCount(exam.getPaperId());
+        if (!CollectionUtils.isEmpty(dtoList)) {
+            dtoList.stream().forEach(dto -> dto.setQuestionTypeName(QuestionType.getByCode(dto.getQuestionType()).getValue()));
+            exam.setQuestionTypeCountList(dtoList);
+        }
+        return Result.OK(exam);
+    }
+
+
+    @GetMapping(value = "/createExam")
+    public Result<?> createExam(@RequestParam(name = "examId") String examId) {
+        return Result.ok(examService.createExam(TokenUtil.getUserId(TokenUtil.getToken(request)), examId));
+    }
+
+    @GetMapping(value = "/listExamIn")
+    public Result<?> listExamIn() {
+        // 校验是否有正在考试的试卷
+        UserExam userExam = userExamService.listExamIn(TokenUtil.getUserId(TokenUtil.getToken(request)));
+        if (!Objects.isNull(userExam)) {
+            return Result.ok(userExam);
+        }
+        return Result.OK();
+    }
+
+    /**
+     * 考试详情,包括答题卡,试题(不包含正确答案)
+     *
+     * @return
+     */
+    @ApiOperation(value = "考试-考试详情")
+    @GetMapping(value = "/examDetail")
+    public Result<?> examDetail(@RequestParam(name = "userExamId") String userExamId) {
+        return Result.ok(examService.examDetail(userExamId));
+    }
+
+    /**
+     * 交卷
+     *
+     * @return
+     */
+    @ApiOperation(value = "考试-交卷")
+    @PostMapping(value = "/submitExam")
+    public Result<?> submitExam(@RequestBody ExamSubmitDTO dto) {
+        examService.submitExam(dto);
+        return Result.ok("提交试卷成功!");
+    }
+
+
+    /**
+     * 缓存考试答案,用于定时任务强制交卷
+     *
+     * @return
+     */
+    @PostMapping(value = "/cacheExamAnswer")
+    public Result<?> cacheExamAnswer(@RequestBody ExamSubmitDTO dto) {
+        examService.cacheExamAnswer(dto);
+        return Result.ok();
+    }
+
+
+    /**
+     * 获取缓存的考试答案
+     *
+     * @return
+     */
+    @GetMapping(value = "/getCacheAnswer")
+    public Result<?> getCacheAnswer(@RequestParam(name = "userExamId") String userExamId) {
+        return Result.ok(examService.getCacheAnswer(userExamId));
+    }
+
+    /**
+     * 用户考试成绩详情,包括答题卡,试题(包含正确答案)
+     *
+     * @param userExamId
+     * @return
+     */
+    @ApiOperation(value = "考试-用户考试成绩详情")
+    @GetMapping(value = "/userExamResultDetail")
+    public Result<?> userExamResultDetail(@RequestParam(name = "userExamId") String userExamId) {
+        return Result.ok(examService.userExamResultDetail(userExamId));
+    }
+
+    @PostMapping(value = "/userExamResult/list")
+    public Result<IPage<UserExamResult>> queryPageList(@RequestBody ExamDTO dto) {
+        Page<UserExamResult> page = new Page<>(dto.getPageNo(), dto.getPageSize());
+        UserExamResult userExamResult = new UserExamResult();
+        userExamResult.setUserId(TokenUtil.getUserId(TokenUtil.getToken(request)));
+        userExamResult.setExamTitle(dto.getExamTitle());
+        IPage<UserExamResult> pageList = userExamResultService.selectPageList(page, userExamResult);
+        return Result.OK(pageList);
+    }
+
+    @PostMapping(value = "/userExamDetail/list")
+    public Result<IPage<UserExam>> userExamDetailList(@RequestBody ExamDTO dto) {
+        Page<UserExam> page = new Page<>(dto.getPageNo(), dto.getPageSize());
+        UserExam userExam = new UserExam();
+        userExam.setUserId(TokenUtil.getUserId(TokenUtil.getToken(request)));
+        userExam.setExamId(dto.getExamId());
+        IPage<UserExam> pageList = userExamService.selectPageList(page, userExam);
+        return Result.OK(pageList);
+    }
+
+    /**
+     * 检查是否达到考试限制次数
+     *
+     * @param examId
+     * @return
+     */
+    @GetMapping(value = "/checkToLimit")
+    public Result<?> checkToLimit(@RequestParam(name = "examId") String examId) {
+        return Result.ok(examService.checkToLimit(TokenUtil.getUserId(TokenUtil.getToken(request)), examId));
+    }
+
+    /**
+     * 检查是否有考试记录
+     *
+     * @param examId
+     * @return
+     */
+    @GetMapping(value = "/examRecordExist")
+    public Result<?> examRecordExist(@RequestParam(name = "examId") String examId) {
+        return Result.ok(examService.examRecordExist(TokenUtil.getUserId(TokenUtil.getToken(request)), examId));
+    }
+
+    /**
+     * 获取用户考试成绩
+     *
+     * @param examId
+     * @return
+     */
+    @GetMapping(value = "/examScore")
+    public Result<?> examScore(@RequestParam(name = "examId") String examId) {
+        return Result.ok(userExamResultService.examScore(TokenUtil.getUserId(TokenUtil.getToken(request)), examId));
+    }
+
+}

+ 171 - 0
web/src/main/java/com/ynfy/app/api/v1/controller/ApiIndexController.java

@@ -0,0 +1,171 @@
+package com.ynfy.app.api.v1.controller;
+
+import cn.hutool.core.date.DateUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ynfy.app.api.v1.annoation.IgnoreAuth;
+import com.ynfy.buss.article.service.IArticleService;
+import com.ynfy.buss.banner.service.IBannerService;
+import com.ynfy.buss.course.course.service.ICourseService;
+import com.ynfy.buss.exam.exam.entity.Exam;
+import com.ynfy.buss.exam.exam.service.IExamService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.util.TokenUtil;
+import org.jeecg.modules.system.entity.SysAnnouncement;
+import org.jeecg.modules.system.entity.SysAnnouncementSend;
+import org.jeecg.modules.system.service.ISysAnnouncementSendService;
+import org.jeecg.modules.system.service.ISysAnnouncementService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Objects;
+
+@Slf4j
+@RestController
+@RequestMapping("/api/v1/index")
+public class ApiIndexController extends ApiBaseController {
+
+    @Autowired
+    private IBannerService bannerService;
+
+    @Autowired
+    private ISysAnnouncementService announcementService;
+
+    @Autowired
+    private ISysAnnouncementSendService announcementSendService;
+
+    @Autowired
+    private ICourseService courseService;
+
+    @Autowired
+    private IExamService examService;
+
+    @Autowired
+    private IArticleService articleService;
+
+    @IgnoreAuth
+    @PostMapping("/banner/list")
+    public Result<?> list() {
+        ;
+        return Result.OK(bannerService.list());
+    }
+
+    /**
+     * 查询通知公告
+     *
+     * @return
+     */
+    @IgnoreAuth
+    @PostMapping("/announcement/list")
+    public Result<?> announcementList() {
+        String userId = TokenUtil.getUserId(TokenUtil.getToken(request));
+        // 1.将系统消息补充到用户通告阅读标记表中
+        LambdaQueryWrapper<SysAnnouncement> querySaWrapper = new LambdaQueryWrapper<SysAnnouncement>();
+        //全部人员
+        querySaWrapper.eq(SysAnnouncement::getMsgType, CommonConstant.MSG_TYPE_ALL);
+        //未删除
+        querySaWrapper.eq(SysAnnouncement::getDelFlag, CommonConstant.DEL_FLAG_0.toString());
+        //已发布
+        querySaWrapper.eq(SysAnnouncement::getSendStatus, CommonConstant.HAS_SEND);
+        if (StringUtils.isNotBlank(userId)) {
+            querySaWrapper.notInSql(SysAnnouncement::getId, "select annt_id from sys_announcement_send where user_id='" + userId + "'");
+        }
+        List<SysAnnouncement> announcements = announcementService.list(querySaWrapper);
+        if (!CollectionUtils.isEmpty(announcements) && StringUtils.isNotBlank(userId)) {
+            for (SysAnnouncement ann : announcements) {
+                LambdaQueryWrapper<SysAnnouncementSend> query = new LambdaQueryWrapper<>();
+                query.eq(SysAnnouncementSend::getAnntId, ann.getId());
+                query.eq(SysAnnouncementSend::getUserId, userId);
+                SysAnnouncementSend sendUser = announcementSendService.getOne(query);
+                if (Objects.isNull(sendUser)) {
+                    SysAnnouncementSend announcementSend = new SysAnnouncementSend();
+                    announcementSend.setAnntId(ann.getId());
+                    announcementSend.setUserId(userId);
+                    announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG);
+                    announcementSendService.save(announcementSend);
+                }
+            }
+        }
+
+        SysAnnouncement announcement = new SysAnnouncement();
+        announcement.setUserId(userId);
+        List<SysAnnouncement> pageList = announcementService.listNoRead(announcement);
+        return Result.OK(pageList);
+    }
+
+    /**
+     * 查看通知公告
+     *
+     * @param id
+     * @return
+     */
+    @GetMapping(value = "/viewMessage")
+    public Result<?> viewMessage(@RequestParam String id) {
+        String userId = TokenUtil.getUserId(TokenUtil.getToken(request));
+        SysAnnouncement announcement = announcementService.getById(id);
+
+        if (!Objects.isNull(announcement)) {
+            if (StringUtils.isNotBlank(userId)) {
+                //阅读通知公告
+                announcementService.readMessage(id, userId);
+            }
+            return Result.OK(announcement);
+        }
+        return null;
+    }
+
+
+    /**
+     * 热门课程
+     *
+     * @return
+     */
+    @IgnoreAuth
+    @GetMapping("/listHotCourse")
+    public Result<?> listHotCourse(Integer limit) {
+        return Result.OK(courseService.listHotCourse(limit));
+    }
+
+    /**
+     * 最新考试
+     *
+     * @return
+     */
+    @IgnoreAuth
+    @GetMapping("/listLatestExam")
+    public Result<?> listLatestExam(Integer limit) {
+        List<Exam> examList = examService.listLatestExam(limit, TokenUtil.getUserId(TokenUtil.getToken(request)));
+        if (!CollectionUtils.isEmpty(examList)) {
+            examList.forEach(item -> item.setAppShowTime(DateUtil.parseDate(DateUtil.formatDate(!Objects.isNull(item.getUpdateTime())
+                    ? item.getUpdateTime() : item.getCreateTime()))));
+        }
+        return Result.OK(examList);
+    }
+
+    /**
+     * 推荐资讯
+     *
+     * @return
+     */
+    @IgnoreAuth
+    @GetMapping("/listRecommendArticle")
+    public Result<?> listRecommendArticle(Integer limit) {
+        return Result.OK(articleService.listRecommendArticle(limit));
+    }
+
+    /**
+     * 查看资讯
+     *
+     * @param id
+     * @return
+     */
+    @GetMapping(value = "/viewArticle")
+    public Result<?> viewArticle(@RequestParam String id) {
+        return Result.OK(articleService.getById(id));
+    }
+
+}

+ 78 - 0
web/src/main/java/com/ynfy/app/api/v1/controller/ApiPracticeController.java

@@ -0,0 +1,78 @@
+package com.ynfy.app.api.v1.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ynfy.app.api.v1.entity.dto.PracticeDTO;
+import com.ynfy.buss.exam.question.service.IQuestionService;
+import com.ynfy.buss.practice.userpractice.entity.UserPractice;
+import com.ynfy.buss.practice.userpractice.entity.dto.UserPracticeDTO;
+import com.ynfy.buss.practice.userpractice.service.IUserPracticeService;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.util.TokenUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@Slf4j
+@RestController
+@RequestMapping("/api/v1/practice")
+public class ApiPracticeController extends ApiBaseController {
+
+    @Autowired
+    public IUserPracticeService userPracticeService;
+
+    @Autowired
+    private IQuestionService questionService;
+
+
+    /**
+     * 按练习模式获取指定题库题目
+     *
+     * @return
+     */
+    @ApiOperation(value = "按练习模式获取指定题库题目", notes = "按练习模式获取指定题库题目")
+    @PostMapping(value = "/listQuestionByMode")
+    public Result<?> listQuestionByPracticeMode(@RequestBody PracticeDTO dto) {
+        return Result.OK(questionService.listQuestionByPracticeMode(dto.getPageNo(), dto.getPageSize(),
+                dto.getRepositoryId(), dto.getQuestionType(), dto.getMode()));
+    }
+
+    /**
+     * 提交练习
+     *
+     * @return
+     */
+    @ApiOperation(value = "提交练习")
+    @PostMapping(value = "/submitPractice")
+    public Result<?> submitPractice(@RequestBody UserPracticeDTO dto) {
+        dto.setUserId(TokenUtil.getUserId(TokenUtil.getToken(request)));
+        return Result.OK("提交练习成功!", userPracticeService.submitPractice(dto));
+    }
+
+
+    /**
+     * 获取练习结果
+     *
+     * @return
+     */
+    @ApiOperation(value = "获取练习结果", notes = "获取练习结果")
+    @GetMapping(value = "/getPracticeResult")
+    public Result<?> getPracticeResult(@RequestParam String id) {
+        return Result.OK(userPracticeService.getPracticeResult(id));
+    }
+
+
+    @ApiOperation(value = "用户练习表-分页列表查询", notes = "用户练习表-分页列表查询")
+    @PostMapping(value = "/userPractice/list")
+    public Result<IPage<UserPractice>> userPracticeList(@RequestBody PracticeDTO dto) {
+        Page<UserPractice> page = new Page<>(dto.getPageNo(), dto.getPageSize());
+        UserPractice userPractice = new UserPractice();
+        userPractice.setUserId(TokenUtil.getUserId(TokenUtil.getToken(request)));
+        userPractice.setPracticeName(dto.getPracticeName());
+        userPractice.setRepositoryName(dto.getRepositoryName());
+        IPage<UserPractice> pageList = userPracticeService.selectPageList(page, userPractice);
+        return Result.OK(pageList);
+    }
+
+}

+ 90 - 0
web/src/main/java/com/ynfy/app/api/v1/controller/ApiRepositoryController.java

@@ -0,0 +1,90 @@
+package com.ynfy.app.api.v1.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ynfy.app.api.v1.annoation.IgnoreAuth;
+import com.ynfy.app.api.v1.entity.dto.RepositoryDTO;
+import com.ynfy.buss.exam.question.dto.QuestionDTO;
+import com.ynfy.buss.exam.question.enums.QuestionType;
+import com.ynfy.buss.exam.question.service.IQuestionService;
+import com.ynfy.buss.exam.repository.entity.Repository;
+import com.ynfy.buss.exam.repository.service.IRepositoryService;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.common.api.vo.Result;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+@RestController
+@RequestMapping("/api/v1/repository")
+public class ApiRepositoryController extends ApiBaseController {
+
+    @Autowired
+    public IRepositoryService repositoryService;
+
+    @Autowired
+    private IQuestionService questionService;
+
+
+    /**
+     * 获取列表
+     *
+     * @return
+     */
+    @PostMapping("/list")
+    @IgnoreAuth
+    public Result<IPage<Repository>> list(@RequestBody RepositoryDTO repositoryDTO) {
+        Page<Repository> page = new Page<>(repositoryDTO.getPageNo(), repositoryDTO.getPageSize());
+        LambdaQueryWrapper<Repository> query = new LambdaQueryWrapper<>();
+        if (StringUtils.isNotBlank(repositoryDTO.getRepositoryTitle())) {
+            query.like(Repository::getTitle, repositoryDTO.getRepositoryTitle());
+        }
+        IPage<Repository> pageList = repositoryService.page(page, query);
+        List<Repository> list = pageList.getRecords();
+        if (!CollectionUtils.isEmpty(list)) {
+            List<String> idList = list.stream().map(Repository::getId).collect(Collectors.toList());
+            List<QuestionDTO> questionDTOList = questionService.countQuestionType(idList);
+            Map<String, List<QuestionDTO>> map = questionDTOList.stream().collect(Collectors
+                    .groupingBy(QuestionDTO::getRepositoryId, Collectors.toList()));
+            list.forEach(l -> {
+                List<QuestionDTO> dtoList = map.get(l.getId());
+                if (!CollectionUtils.isEmpty(dtoList)) {
+                    l.setNum(dtoList.stream().mapToInt(QuestionDTO::getNum).sum());
+                    StringBuilder str = new StringBuilder();
+                    int index = 0;
+                    for (QuestionDTO dto : dtoList) {
+                        str.append(QuestionType.getByCode(dto.getType()).getValue()).append(dto.getNum()).append("题");
+                        if (index < dtoList.size() - 1) {
+                            str.append(",");
+                        }
+                        index++;
+                    }
+                    l.setQuestionRemark(str.toString());
+                }
+            });
+        }
+        return Result.OK(pageList);
+    }
+
+    /**
+     * 计算指定题库的题目题型数量
+     *
+     * @param id
+     * @return
+     */
+    @ApiOperation(value = "计算指定题库的题目题型数量", notes = "计算指定题库的题目题型数量")
+    @GetMapping(value = "/getById")
+    public Result<Repository> getById(@RequestParam String id) {
+        Repository repository = repositoryService.getById(id);
+        repository.setQuestionDTOList(questionService.countQuestionNumByType(id));
+        return Result.OK(repository);
+    }
+}

+ 13 - 0
web/src/main/java/com/ynfy/app/api/v1/entity/dto/ArticleDTO.java

@@ -0,0 +1,13 @@
+package com.ynfy.app.api.v1.entity.dto;
+
+import lombok.Data;
+
+@Data
+public class ArticleDTO {
+
+    private String title;
+
+    private Integer pageNo;
+
+    private Integer pageSize;
+}

+ 13 - 0
web/src/main/java/com/ynfy/app/api/v1/entity/dto/CourseDTO.java

@@ -0,0 +1,13 @@
+package com.ynfy.app.api.v1.entity.dto;
+
+import lombok.Data;
+
+@Data
+public class CourseDTO {
+
+    private String name;
+
+    private Integer pageNo;
+
+    private Integer pageSize;
+}

+ 17 - 0
web/src/main/java/com/ynfy/app/api/v1/entity/dto/ExamDTO.java

@@ -0,0 +1,17 @@
+package com.ynfy.app.api.v1.entity.dto;
+
+import lombok.Data;
+
+@Data
+public class ExamDTO {
+
+    private String examId;
+
+    private String examTitle;
+
+    private Integer state;
+
+    private Integer pageNo;
+
+    private Integer pageSize;
+}

+ 24 - 0
web/src/main/java/com/ynfy/app/api/v1/entity/dto/PracticeDTO.java

@@ -0,0 +1,24 @@
+package com.ynfy.app.api.v1.entity.dto;
+
+import lombok.Data;
+
+@Data
+public class PracticeDTO {
+
+    private String repositoryId;
+
+    private String repositoryName;
+
+    private String practiceName;
+
+    /**
+     * 练习模式
+     */
+    private Integer mode;
+
+    private Integer questionType;
+
+    private Integer pageNo;
+
+    private Integer pageSize;
+}

+ 13 - 0
web/src/main/java/com/ynfy/app/api/v1/entity/dto/RepositoryDTO.java

@@ -0,0 +1,13 @@
+package com.ynfy.app.api.v1.entity.dto;
+
+import lombok.Data;
+
+@Data
+public class RepositoryDTO {
+
+    private String repositoryTitle;
+
+    private Integer pageNo;
+
+    private Integer pageSize;
+}

+ 21 - 0
web/src/main/java/com/ynfy/app/api/v1/entity/dto/UserInfo.java

@@ -0,0 +1,21 @@
+package com.ynfy.app.api.v1.entity.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class UserInfo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String avatarUrl;
+
+    private String city;
+
+    private Integer gender;
+
+    private String nickName;
+
+    private String province;
+}
+

+ 12 - 0
web/src/main/java/com/ynfy/app/api/v1/entity/dto/WxLoginDTO.java

@@ -0,0 +1,12 @@
+package com.ynfy.app.api.v1.entity.dto;
+
+import lombok.Data;
+
+@Data
+public class WxLoginDTO {
+
+    private String code;
+
+    private UserInfo userInfo;
+
+}

+ 90 - 0
web/src/main/java/com/ynfy/app/api/v1/interceptor/AuthorizationInterceptor.java

@@ -0,0 +1,90 @@
+package com.ynfy.app.api.v1.interceptor;
+
+import com.auth0.jwt.exceptions.TokenExpiredException;
+import com.ynfy.app.api.v1.annoation.IgnoreAuth;
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.common.exception.JeecgBootException;
+import org.jeecg.common.system.util.JwtUtil;
+import org.jeecg.common.util.SpringContextUtils;
+import org.jeecg.common.util.TokenUtil;
+import org.jeecg.modules.system.entity.SysUser;
+import org.jeecg.modules.system.service.ISysUserService;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 权限(Token)验证
+ */
+@Component
+public class AuthorizationInterceptor implements HandlerInterceptor {
+
+    @Resource
+    private ISysUserService sysUserService;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+
+        //支持跨域请求
+        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
+        response.setHeader("Access-Control-Allow-Credentials", "true");
+        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,X-Exam-Token,X-URL-PATH,content-type");
+        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
+
+        IgnoreAuth annotation;
+        if (handler instanceof HandlerMethod) {
+            annotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
+        } else {
+            return true;
+        }
+
+        //如果有@IgnoreAuth注解,则不验证token
+        if (annotation != null) {
+            return true;
+        }
+
+        //获取token
+        String token = TokenUtil.getToken(request);
+        if (StringUtils.isBlank(token)) {
+            JwtUtil.responseError(SpringContextUtils.getHttpServletResponse(), 401, "请先登录");
+            return false;
+        }
+        String userName = TokenUtil.getUserName(token);
+
+        if (StringUtils.isEmpty(userName)) {
+            throw new JeecgBootException("token错误");
+        } else {
+            SysUser user = sysUserService.getUserByName(userName);
+            if (user == null) {
+                throw new JeecgBootException("用户不存在!");
+            } else {
+                try {
+                    JwtUtil.verify(token, userName, user.getPassword());
+                } catch (TokenExpiredException var6) {
+                    throw new JeecgBootException("token失效,请重新登录");
+                } catch (Exception e) {
+                    throw new JeecgBootException("token认证失败");
+                }
+            }
+        }
+        return true;
+    }
+
+    //方法执行后
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
+
+    }
+
+    //页面渲染前
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
+
+    }
+
+}

+ 59 - 0
web/src/main/java/com/ynfy/app/api/v1/util/ApiUserUtils.java

@@ -0,0 +1,59 @@
+package com.ynfy.app.api.v1.util;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "wx")
+public class ApiUserUtils {
+
+    private static String getCode;
+
+    private static String webAccessTokenhttps;
+
+    private static String appId;
+
+    private static String secret;
+
+    public static String getCode(String appid, String redirectUri, String scope) {
+        return String.format(getCode, appid, redirectUri, scope);
+    }
+
+    public static String getWebAccess(String code) {
+        return String.format(webAccessTokenhttps, appId, secret, code);
+    }
+
+    public static String getGetCode() {
+        return getCode;
+    }
+
+    public void setGetCode(String getCode) {
+        ApiUserUtils.getCode = getCode;
+    }
+
+
+    public static String getWebAccessTokenhttps() {
+        return webAccessTokenhttps;
+    }
+
+    public void setWebAccessTokenhttps(String webAccessTokenhttps) {
+        ApiUserUtils.webAccessTokenhttps = webAccessTokenhttps;
+    }
+
+    public static String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        ApiUserUtils.appId = appId;
+    }
+
+    public static String getSecret() {
+        return secret;
+    }
+
+    public void setSecret(String secret) {
+        ApiUserUtils.secret = secret;
+    }
+}
+

+ 135 - 0
web/src/main/java/com/ynfy/app/api/v1/util/HttpDownUtils.java

@@ -0,0 +1,135 @@
+package com.ynfy.app.api.v1.util;
+
+import org.jeecg.common.util.MinioUtil;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+
+public class HttpDownUtils {
+
+    /**
+     * 从网络Url中下载文件到本地磁盘
+     *
+     * @param urlStr
+     * @param fileName
+     * @param savePath
+     * @throws IOException
+     */
+    public static void downLoadFromUrl(String urlStr, String fileName, String savePath) throws IOException {
+        URL url = new URL(urlStr);
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        //设置超时间为3秒
+        conn.setConnectTimeout(30 * 1000);
+        conn.setReadTimeout(60 * 1000);
+        //得到输入流
+        InputStream inputStream = conn.getInputStream();
+        //获取自己数组
+        byte[] getData = readInputStream(inputStream);
+
+        //文件保存位置
+        File saveDir = new File(savePath);
+        if (!saveDir.exists()) {
+            saveDir.mkdir();
+        }
+        File file = new File(saveDir + File.separator + fileName);
+        FileOutputStream fos = new FileOutputStream(file);
+        fos.write(getData);
+        if (fos != null) {
+            fos.close();
+        }
+        if (inputStream != null) {
+            inputStream.close();
+        }
+    }
+
+
+    /**
+     * 从输入流中获取字节数组
+     *
+     * @param inputStream
+     * @return
+     * @throws IOException
+     */
+    public static byte[] readInputStream(InputStream inputStream) throws IOException {
+        byte[] buffer = new byte[1024];
+        int len = 0;
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        while ((len = inputStream.read(buffer)) != -1) {
+            bos.write(buffer, 0, len);
+        }
+        bos.close();
+        return bos.toByteArray();
+    }
+
+
+    /**
+     * 从网络url下载excel
+     *
+     * @param url
+     * @param fileName
+     * @param response
+     * @throws Exception
+     */
+    public void downloadExcelByUrl(String url, String fileName, HttpServletResponse response) throws Exception {
+        fileName = URLEncoder.encode(fileName, "UTF-8");
+        response.setContentType("application/vnd.ms-excel;charset=utf-8");
+        response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
+        byte[] buff = new byte[1024];
+        BufferedInputStream bis = null;
+        OutputStream outputStream = null;
+        InputStream inputStream = null;
+        try {
+            URL httpUrl = new URL(url);
+            HttpURLConnection connection = (HttpURLConnection) httpUrl.openConnection();
+            connection.setConnectTimeout(3000);
+            connection.setReadTimeout(5000);
+            outputStream = response.getOutputStream();
+            inputStream = connection.getInputStream();
+            bis = new BufferedInputStream(inputStream);
+            int len;
+            while ((len = bis.read(buff)) > 0) {
+                outputStream.write(buff, 0, len);
+                outputStream.flush();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (bis != null) {
+                bis.close();
+            }
+            if (outputStream != null) {
+                outputStream.close();
+            }
+            if (inputStream != null) {
+                inputStream.close();
+            }
+        }
+    }
+
+
+    /**
+     * 从网络下载文件到本地,然后上传到minio
+     * @param url
+     * @param fileName
+     * @param tmpPath
+     * @return
+     */
+    public static String downFormUrlAndUpload(String url, String fileName, String tmpPath) {
+        try {
+            HttpDownUtils.downLoadFromUrl(url, fileName, tmpPath);
+            String destFilePath = tmpPath + File.separator + fileName;
+            File destFile = new File(destFilePath);
+            InputStream inputStream = new BufferedInputStream(new FileInputStream(destFile));
+            String objectName = MinioUtil.uploadFile(inputStream, fileName);
+            destFile.delete();
+            return objectName;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+}

+ 162 - 0
web/src/main/java/com/ynfy/buss/article/controller/ArticleController.java

@@ -0,0 +1,162 @@
+package com.ynfy.buss.article.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ynfy.buss.article.entity.Article;
+import com.ynfy.buss.article.service.IArticleService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.jeecg.common.system.base.controller.JeecgController;
+import org.jeecg.common.system.query.QueryGenerator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Arrays;
+
+/**
+ * @Description: article
+ * @Author: jeecg-boot
+ * @Date: 2023-12-23
+ * @Version: V1.0
+ */
+@Api(tags = "article")
+@RestController
+@RequestMapping("/article")
+@Slf4j
+public class ArticleController extends JeecgController<Article, IArticleService> {
+    @Autowired
+    private IArticleService articleService;
+
+    /**
+     * 分页列表查询
+     *
+     * @param article
+     * @param pageNo
+     * @param pageSize
+     * @param req
+     * @return
+     */
+    //@AutoLog(value = "article-分页列表查询")
+    @ApiOperation(value = "article-分页列表查询", notes = "article-分页列表查询")
+    @GetMapping(value = "/list")
+    public Result<IPage<Article>> queryPageList(Article article,
+                                                @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
+                                                @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
+                                                HttpServletRequest req) {
+        QueryWrapper<Article> queryWrapper = QueryGenerator.initQueryWrapper(article, req.getParameterMap());
+        Page<Article> page = new Page<Article>(pageNo, pageSize);
+        IPage<Article> pageList = articleService.page(page, queryWrapper);
+        return Result.OK(pageList);
+    }
+
+    /**
+     * 添加
+     *
+     * @param article
+     * @return
+     */
+    @AutoLog(value = "article-添加")
+    @ApiOperation(value = "article-添加", notes = "article-添加")
+    //@RequiresPermissions("article:article:add")
+    @PostMapping(value = "/add")
+    public Result<String> add(@RequestBody Article article) {
+        article.setStatus(1);
+        articleService.save(article);
+        return Result.OK("添加成功!");
+    }
+
+    /**
+     * 编辑
+     *
+     * @param article
+     * @return
+     */
+    @AutoLog(value = "article-编辑")
+    @ApiOperation(value = "article-编辑", notes = "article-编辑")
+    //@RequiresPermissions("article:article:edit")
+    @RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
+    public Result<String> edit(@RequestBody Article article) {
+        articleService.updateById(article);
+        return Result.OK("编辑成功!");
+    }
+
+    /**
+     * 通过id删除
+     *
+     * @param id
+     * @return
+     */
+    @AutoLog(value = "article-通过id删除")
+    @ApiOperation(value = "article-通过id删除", notes = "article-通过id删除")
+    //@RequiresPermissions("article:article:delete")
+    @DeleteMapping(value = "/delete")
+    public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
+        articleService.removeById(id);
+        return Result.OK("删除成功!");
+    }
+
+    /**
+     * 批量删除
+     *
+     * @param ids
+     * @return
+     */
+    @AutoLog(value = "article-批量删除")
+    @ApiOperation(value = "article-批量删除", notes = "article-批量删除")
+    //@RequiresPermissions("article:article:deleteBatch")
+    @DeleteMapping(value = "/deleteBatch")
+    public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
+        this.articleService.removeByIds(Arrays.asList(ids.split(",")));
+        return Result.OK("批量删除成功!");
+    }
+
+    /**
+     * 通过id查询
+     *
+     * @param id
+     * @return
+     */
+    //@AutoLog(value = "article-通过id查询")
+    @ApiOperation(value = "article-通过id查询", notes = "article-通过id查询")
+    @GetMapping(value = "/queryById")
+    public Result<Article> queryById(@RequestParam(name = "id", required = true) String id) {
+        Article article = articleService.getById(id);
+        if (article == null) {
+            return Result.error("未找到对应数据");
+        }
+        return Result.OK(article);
+    }
+
+    /**
+     * 导出excel
+     *
+     * @param request
+     * @param article
+     */
+    //@RequiresPermissions("article:article:exportXls")
+    @RequestMapping(value = "/exportXls")
+    public ModelAndView exportXls(HttpServletRequest request, Article article) {
+        return super.exportXls(request, article, Article.class, "article");
+    }
+
+    /**
+     * 通过excel导入数据
+     *
+     * @param request
+     * @param response
+     * @return
+     */
+    //@RequiresPermissions("article:article:importExcel")
+    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
+    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
+        return super.importExcel(request, response, Article.class);
+    }
+
+}

+ 120 - 0
web/src/main/java/com/ynfy/buss/article/entity/Article.java

@@ -0,0 +1,120 @@
+package com.ynfy.buss.article.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @Description: article
+ * @Author: jeecg-boot
+ * @Date: 2023-12-23
+ * @Version: V1.0
+ */
+@Data
+@TableName("article")
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = false)
+@ApiModel(value = "article对象", description = "article")
+public class Article implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    @TableId(type = IdType.ASSIGN_ID)
+    @ApiModelProperty(value = "id")
+    private String id;
+    /**
+     * 封面
+     */
+    @Excel(name = "封面", width = 15)
+    @ApiModelProperty(value = "封面")
+    private String coverImg;
+    /**
+     * 标题
+     */
+    @Excel(name = "标题", width = 15)
+    @ApiModelProperty(value = "标题")
+    private String title;
+    /**
+     * 摘要
+     */
+    @Excel(name = "摘要", width = 15)
+    @ApiModelProperty(value = "摘要")
+    private String remark;
+    /**
+     * 内容
+     */
+    @Excel(name = "内容", width = 15)
+    @ApiModelProperty(value = "内容")
+    private String content;
+
+    /**
+     * 标签
+     */
+    @Excel(name = "标签", width = 15)
+    @ApiModelProperty(value = "标签")
+    private String tag;
+
+    /**
+     * 是否推荐
+     */
+    @Excel(name = "是否推荐", width = 15)
+    @ApiModelProperty(value = "是否推荐")
+    private Boolean isHot;
+    /**
+     * 是否热门
+     */
+    @Excel(name = "是否热门", width = 15)
+    @ApiModelProperty(value = "是否热门")
+    private Boolean isRecommend;
+    /**
+     * 发布时间
+     */
+    @Excel(name = "发布时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(value = "发布时间")
+    private Date publishTime;
+    /**
+     * 状态:1:已发布,0:草稿
+     */
+    @Excel(name = "状态:1:已发布,0:草稿", width = 15)
+    @ApiModelProperty(value = "状态:1:已发布,0:草稿")
+    private Integer status;
+    /**
+     * createBy
+     */
+    @ApiModelProperty(value = "createBy")
+    private String createBy;
+    /**
+     * createTime
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @ApiModelProperty(value = "createTime")
+    private Date createTime;
+    /**
+     * updateBy
+     */
+    @ApiModelProperty(value = "updateBy")
+    private String updateBy;
+    /**
+     * updateTime
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(value = "updateTime")
+    private Date updateTime;
+}

+ 14 - 0
web/src/main/java/com/ynfy/buss/article/mapper/ArticleMapper.java

@@ -0,0 +1,14 @@
+package com.ynfy.buss.article.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ynfy.buss.article.entity.Article;
+
+/**
+ * @Description: article
+ * @Author: jeecg-boot
+ * @Date: 2023-12-23
+ * @Version: V1.0
+ */
+public interface ArticleMapper extends BaseMapper<Article> {
+
+}

+ 5 - 0
web/src/main/java/com/ynfy/buss/article/mapper/xml/ArticleMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ynfy.buss.article.mapper.ArticleMapper">
+
+</mapper>

+ 18 - 0
web/src/main/java/com/ynfy/buss/article/service/IArticleService.java

@@ -0,0 +1,18 @@
+package com.ynfy.buss.article.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ynfy.buss.article.entity.Article;
+
+import java.util.List;
+
+/**
+ * @Description: article
+ * @Author: jeecg-boot
+ * @Date: 2023-12-23
+ * @Version: V1.0
+ */
+public interface IArticleService extends IService<Article> {
+
+    List<Article> listRecommendArticle(Integer limit);
+
+}

+ 28 - 0
web/src/main/java/com/ynfy/buss/article/service/impl/ArticleServiceImpl.java

@@ -0,0 +1,28 @@
+package com.ynfy.buss.article.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ynfy.buss.article.entity.Article;
+import com.ynfy.buss.article.mapper.ArticleMapper;
+import com.ynfy.buss.article.service.IArticleService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @Description: article
+ * @Author: jeecg-boot
+ * @Date: 2023-12-23
+ * @Version: V1.0
+ */
+@Service
+public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements IArticleService {
+
+    @Override
+    public List<Article> listRecommendArticle(Integer limit) {
+        LambdaQueryWrapper<Article> query = new LambdaQueryWrapper<>();
+        query.eq(Article::getIsRecommend, true).eq(Article::getStatus, 1)
+                .last("LIMIT " + limit).orderByDesc(Article::getCreateTime);
+        return list(query);
+    }
+}

+ 161 - 0
web/src/main/java/com/ynfy/buss/banner/controller/BannerController.java

@@ -0,0 +1,161 @@
+package com.ynfy.buss.banner.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ynfy.buss.banner.entity.Banner;
+import com.ynfy.buss.banner.service.IBannerService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.jeecg.common.system.base.controller.JeecgController;
+import org.jeecg.common.system.query.QueryGenerator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Arrays;
+
+/**
+ * @Description: banner
+ * @Author: jeecg-boot
+ * @Date: 2023-12-17
+ * @Version: V1.0
+ */
+@Api(tags = "banner")
+@RestController
+@RequestMapping("/banner")
+@Slf4j
+public class BannerController extends JeecgController<Banner, IBannerService> {
+    @Autowired
+    private IBannerService bannerService;
+
+    /**
+     * 分页列表查询
+     *
+     * @param banner
+     * @param pageNo
+     * @param pageSize
+     * @param req
+     * @return
+     */
+    //@AutoLog(value = "banner-分页列表查询")
+    @ApiOperation(value = "banner-分页列表查询", notes = "banner-分页列表查询")
+    @GetMapping(value = "/list")
+    public Result<IPage<Banner>> queryPageList(Banner banner,
+                                               @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
+                                               @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
+                                               HttpServletRequest req) {
+        QueryWrapper<Banner> queryWrapper = QueryGenerator.initQueryWrapper(banner, req.getParameterMap());
+        Page<Banner> page = new Page<Banner>(pageNo, pageSize);
+        IPage<Banner> pageList = bannerService.page(page, queryWrapper);
+        return Result.OK(pageList);
+    }
+
+    /**
+     * 添加
+     *
+     * @param banner
+     * @return
+     */
+    @AutoLog(value = "banner-添加")
+    @ApiOperation(value = "banner-添加", notes = "banner-添加")
+    //@RequiresPermissions("banner:banner:add")
+    @PostMapping(value = "/add")
+    public Result<String> add(@RequestBody Banner banner) {
+        bannerService.save(banner);
+        return Result.OK("添加成功!");
+    }
+
+    /**
+     * 编辑
+     *
+     * @param banner
+     * @return
+     */
+    @AutoLog(value = "banner-编辑")
+    @ApiOperation(value = "banner-编辑", notes = "banner-编辑")
+    //@RequiresPermissions("banner:banner:edit")
+    @RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
+    public Result<String> edit(@RequestBody Banner banner) {
+        bannerService.updateById(banner);
+        return Result.OK("编辑成功!");
+    }
+
+    /**
+     * 通过id删除
+     *
+     * @param id
+     * @return
+     */
+    @AutoLog(value = "banner-通过id删除")
+    @ApiOperation(value = "banner-通过id删除", notes = "banner-通过id删除")
+    //@RequiresPermissions("banner:banner:delete")
+    @DeleteMapping(value = "/delete")
+    public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
+        bannerService.removeById(id);
+        return Result.OK("删除成功!");
+    }
+
+    /**
+     * 批量删除
+     *
+     * @param ids
+     * @return
+     */
+    @AutoLog(value = "banner-批量删除")
+    @ApiOperation(value = "banner-批量删除", notes = "banner-批量删除")
+    //@RequiresPermissions("banner:banner:deleteBatch")
+    @DeleteMapping(value = "/deleteBatch")
+    public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
+        this.bannerService.removeByIds(Arrays.asList(ids.split(",")));
+        return Result.OK("批量删除成功!");
+    }
+
+    /**
+     * 通过id查询
+     *
+     * @param id
+     * @return
+     */
+    //@AutoLog(value = "banner-通过id查询")
+    @ApiOperation(value = "banner-通过id查询", notes = "banner-通过id查询")
+    @GetMapping(value = "/queryById")
+    public Result<Banner> queryById(@RequestParam(name = "id", required = true) String id) {
+        Banner banner = bannerService.getById(id);
+        if (banner == null) {
+            return Result.error("未找到对应数据");
+        }
+        return Result.OK(banner);
+    }
+
+    /**
+     * 导出excel
+     *
+     * @param request
+     * @param banner
+     */
+    //@RequiresPermissions("banner:banner:exportXls")
+    @RequestMapping(value = "/exportXls")
+    public ModelAndView exportXls(HttpServletRequest request, Banner banner) {
+        return super.exportXls(request, banner, Banner.class, "banner");
+    }
+
+    /**
+     * 通过excel导入数据
+     *
+     * @param request
+     * @param response
+     * @return
+     */
+    //@RequiresPermissions("banner:banner:importExcel")
+    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
+    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
+        return super.importExcel(request, response, Banner.class);
+    }
+
+}

+ 106 - 0
web/src/main/java/com/ynfy/buss/banner/entity/Banner.java

@@ -0,0 +1,106 @@
+package com.ynfy.buss.banner.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.jeecg.common.aspect.annotation.Dict;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @Description: banner
+ * @Author: jeecg-boot
+ * @Date: 2023-12-17
+ * @Version: V1.0
+ */
+@Data
+@TableName("banner")
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = false)
+@ApiModel(value = "banner对象", description = "banner")
+public class Banner implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(type = IdType.ASSIGN_ID)
+    @ApiModelProperty(value = "主键")
+    private String id;
+    /**
+     * 标题
+     */
+    @Excel(name = "标题", width = 15)
+    @ApiModelProperty(value = "标题")
+    private String title;
+    /**
+     * 链接
+     */
+    @Excel(name = "链接", width = 15)
+    @ApiModelProperty(value = "链接")
+    private String link;
+    /**
+     * 图片路径
+     */
+    @Excel(name = "图片路径", width = 15)
+    @ApiModelProperty(value = "图片路径")
+    private String imgUrl;
+    /**
+     * 视频路径
+     */
+    @Excel(name = "视频路径", width = 15)
+    @ApiModelProperty(value = "视频路径")
+    private String videoUrl;
+    /**
+     * 媒体类型
+     */
+    @Excel(name = "媒体类型", width = 15)
+    @ApiModelProperty(value = "媒体类型")
+    @Dict(dicCode = "media_type")
+    private String mediaType;
+    /**
+     * 子标题
+     */
+    @Excel(name = "子标题", width = 15)
+    @ApiModelProperty(value = "子标题")
+    private String subTitle;
+    /**
+     * 链接名称
+     */
+    @Excel(name = "链接名称", width = 15)
+    @ApiModelProperty(value = "链接名称")
+    private String linkName;
+    /**
+     * 创建时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(value = "创建时间")
+    private Date createTime;
+    /**
+     * 修改时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(value = "修改时间")
+    private Date updateTime;
+    /**
+     * 创建人
+     */
+    @ApiModelProperty(value = "创建人")
+    private String createBy;
+    /**
+     * 修改人
+     */
+    @ApiModelProperty(value = "修改人")
+    private String updateBy;
+}

+ 14 - 0
web/src/main/java/com/ynfy/buss/banner/mapper/BannerMapper.java

@@ -0,0 +1,14 @@
+package com.ynfy.buss.banner.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ynfy.buss.banner.entity.Banner;
+
+/**
+ * @Description: banner
+ * @Author: jeecg-boot
+ * @Date: 2023-12-17
+ * @Version: V1.0
+ */
+public interface BannerMapper extends BaseMapper<Banner> {
+
+}

+ 5 - 0
web/src/main/java/com/ynfy/buss/banner/mapper/xml/BannerMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ynfy.buss.banner.mapper.BannerMapper">
+
+</mapper>

+ 14 - 0
web/src/main/java/com/ynfy/buss/banner/service/IBannerService.java

@@ -0,0 +1,14 @@
+package com.ynfy.buss.banner.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ynfy.buss.banner.entity.Banner;
+
+/**
+ * @Description: banner
+ * @Author: jeecg-boot
+ * @Date: 2023-12-17
+ * @Version: V1.0
+ */
+public interface IBannerService extends IService<Banner> {
+
+}

+ 18 - 0
web/src/main/java/com/ynfy/buss/banner/service/impl/BannerServiceImpl.java

@@ -0,0 +1,18 @@
+package com.ynfy.buss.banner.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ynfy.buss.banner.entity.Banner;
+import com.ynfy.buss.banner.mapper.BannerMapper;
+import com.ynfy.buss.banner.service.IBannerService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @Description: banner
+ * @Author: jeecg-boot
+ * @Date: 2023-12-17
+ * @Version: V1.0
+ */
+@Service
+public class BannerServiceImpl extends ServiceImpl<BannerMapper, Banner> implements IBannerService {
+
+}

+ 4 - 7
web/src/main/java/com/ynfy/buss/course/course/controller/CourseController.java

@@ -211,17 +211,14 @@ public class CourseController extends JeecgController<Course, ICourseService> {
                                        @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
         Result<IPage<Course>> result = new Result<>();
         Page<Course> page = new Page<>(pageNo, pageSize);
-        IPage<Course> pageList = courseService.selectCourseList(page, course);
+        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        IPage<Course> pageList = courseService.selectCourseList(page, course, user);
 
         //设置课程分类
         courseService.setCategory(pageList);
         if (!CollectionUtils.isEmpty(pageList.getRecords())) {
-            LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
-            List<String> courseIdList = pageList.getRecords().stream().map(Course::getId).collect(Collectors.toList());
-            List<UserCourseStudyVO> studyList = userCourseCatalogService.getUserCourseStudy(courseIdList, sysUser.getId());
-            if (!CollectionUtils.isEmpty(studyList)) {
-                pageList.getRecords().forEach((o) -> courseService.setCourseStudyProcess(studyList, o));
-            }
+            //设置用户学习情况
+            courseService.setUserStudyInfo(pageList.getRecords(), user.getId());
         }
         result.setSuccess(true);
         result.setResult(pageList);

+ 19 - 13
web/src/main/java/com/ynfy/buss/course/course/entity/Course.java

@@ -61,6 +61,13 @@ public class Course implements Serializable {
     @TableField(exist = false)
     private String categoryName;
 
+    /**
+     * 课程摘要
+     */
+    @Excel(name = "课程摘要", width = 15)
+    @ApiModelProperty(value = "课程摘要")
+    private String summary;
+
     /**
      * 课程介绍
      */
@@ -115,17 +122,17 @@ public class Course implements Serializable {
     private Boolean mustLearn;
 
     /**
-     * 课程id
+     * 考试id
      */
-    @Excel(name = "课程id", width = 15)
-    @ApiModelProperty(value = "课程id")
+    @Excel(name = "考试id", width = 15)
+    @ApiModelProperty(value = "考试id")
     private String examId;
 
     /**
-     * 课程名称
+     * 考试名称
      */
-    @Excel(name = "课程名称", width = 15)
-    @ApiModelProperty(value = "课程名称")
+    @Excel(name = "考试名称", width = 15)
+    @ApiModelProperty(value = "考试名称")
     @TableField(exist = false)
     private String examTitle;
 
@@ -143,13 +150,6 @@ public class Course implements Serializable {
     @ApiModelProperty(value = "学员")
     private String learner;
 
-    /**
-     * 学习人数
-     */
-    @Excel(name = "学习人数", width = 15)
-    @ApiModelProperty(value = "学习人数")
-    private Integer learnerNumber;
-
     /**
      * 离开多少分钟弹窗
      */
@@ -254,4 +254,10 @@ public class Course implements Serializable {
      */
     @TableField(exist = false)
     private Double courseStudyProcess;
+
+    /**
+     * 学习人数
+     */
+    @TableField(exist = false)
+    private Integer learnerNumber;
 }

+ 5 - 0
web/src/main/java/com/ynfy/buss/course/course/mapper/CourseMapper.java

@@ -6,6 +6,8 @@ import com.ynfy.buss.course.course.entity.Course;
 import org.apache.ibatis.annotations.Param;
 import org.jeecg.common.system.vo.LoginUser;
 
+import java.util.List;
+
 /**
  * @Description: course
  * @Author: jeecg-boot
@@ -17,4 +19,7 @@ public interface CourseMapper extends BaseMapper<Course> {
     IPage<Course> selectCourseList(IPage<Course> page, @Param("course") Course course, @Param("user") LoginUser user);
 
     Course queryById(String id);
+
+
+    List<Course> listHotCourse(Integer limit);
 }

+ 53 - 9
web/src/main/java/com/ynfy/buss/course/course/mapper/xml/CourseMapper.xml

@@ -4,7 +4,8 @@
     <select id="selectCourseList"  resultType="com.ynfy.buss.course.course.entity.Course">
         SELECT
                 tmp1.*,
-                u.realname AS teacherName
+                u.realname AS teacherName,
+                tmp2.learner_number
         FROM
         (
             SELECT
@@ -13,7 +14,7 @@
             WHERE
                 open_type = 1
         <choose>
-            <when test="user.orgCode!=null and user.orgCode!=''">
+            <when test="user!=null and user.orgCode!=null and user.orgCode!=''">
             UNION
                 SELECT
                     *
@@ -22,15 +23,32 @@
                     open_type = 2
                     AND learner like concat('%', #{user.orgCode}, '%')
             </when>
-        </choose>
+
+            <when test="user!=null and user.username!=null and user.username!=''">
             UNION
-            SELECT
-                *
-            FROM course
-            WHERE
-                open_type = 3
-                AND learner like concat('%', #{user.username}, '%')
+                SELECT
+                    *
+                FROM course
+                WHERE
+                    open_type = 3
+                    AND learner like concat('%', #{user.username}, '%')
+            </when>
+        </choose>
         ) tmp1
+        LEFT JOIN
+        (
+            SELECT
+                cc.course_id,
+                count(
+                DISTINCT (ucc.user_id)) AS learner_number
+            FROM
+                user_course_catalog ucc
+            LEFT JOIN
+                course_catalog cc ON ucc.course_catalog_id = cc.id
+            GROUP BY
+                    cc.course_id
+        ) tmp2
+        ON tmp1.id = tmp2.course_id
         LEFT JOIN sys_user u on tmp1.teacher_id=u.id
         <where>
             <if test="course.name!=null and course.name!=''">
@@ -55,4 +73,30 @@
                 LEFT JOIN sys_user u ON c.teacher_id = u.id
         where c.id = #{id}
     </select>
+
+    <select id="listHotCourse" resultType="com.ynfy.buss.course.course.entity.Course">
+        SELECT
+            c.*,
+            tmp.learn_number
+        FROM
+            course c
+        LEFT JOIN
+            (
+                SELECT
+                    cc.course_id,
+                    count(
+                            DISTINCT ( ucc.user_id )) AS learn_number
+                FROM
+                    user_course_catalog ucc
+                        LEFT JOIN course_catalog cc ON ucc.course_catalog_id = cc.id
+                GROUP BY
+                    cc.course_id
+            ) tmp
+            ON c.id = tmp.course_id
+        ORDER BY
+                tmp.learn_number DESC
+        <if test="limit !=null">
+            LIMIT #{limit}
+        </if>
+    </select>
 </mapper>

+ 18 - 1
web/src/main/java/com/ynfy/buss/course/course/service/ICourseService.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ynfy.buss.course.course.entity.Course;
 import com.ynfy.buss.course.usercoursecatalog.entity.vo.UserCourseStudyVO;
+import org.jeecg.common.system.vo.LoginUser;
 
 import java.util.List;
 
@@ -17,7 +18,7 @@ public interface ICourseService extends IService<Course> {
 
     void saveCourse(Course course);
 
-    IPage<Course> selectCourseList(IPage<Course> page, Course course);
+    IPage<Course> selectCourseList(IPage<Course> page, Course course, LoginUser user);
 
     /**
      * 设置课程分类
@@ -36,4 +37,20 @@ public interface ICourseService extends IService<Course> {
      * @param course
      */
     void setCourseStudyProcess(List<UserCourseStudyVO> studyList, Course course);
+
+
+    /**
+     * 查询热门课程
+     *
+     * @return
+     */
+    List<Course> listHotCourse(Integer limit);
+
+    /**
+     * 设置用户学习情况
+     *
+     * @param courseList
+     * @param userId
+     */
+    void setUserStudyInfo(List<Course> courseList, String userId);
 }

+ 34 - 4
web/src/main/java/com/ynfy/buss/course/course/service/impl/CourseServiceImpl.java

@@ -8,8 +8,8 @@ import com.ynfy.buss.course.course.mapper.CourseMapper;
 import com.ynfy.buss.course.course.service.ICourseService;
 import com.ynfy.buss.course.coursecatalog.service.ICourseCatalogService;
 import com.ynfy.buss.course.usercoursecatalog.entity.vo.UserCourseStudyVO;
+import com.ynfy.buss.course.usercoursecatalog.service.IUserCourseCatalogService;
 import org.apache.commons.lang.StringUtils;
-import org.apache.shiro.SecurityUtils;
 import org.jeecg.common.system.vo.LoginUser;
 import org.jeecg.modules.system.entity.SysDepart;
 import org.jeecg.modules.system.service.ISysCategoryService;
@@ -21,6 +21,7 @@ import org.springframework.util.CollectionUtils;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * @Description: course
@@ -43,6 +44,9 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> impleme
     @Autowired
     private ISysCategoryService sysCategoryService;
 
+    @Autowired
+    private IUserCourseCatalogService userCourseCatalogService;
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void saveCourse(Course course) {
@@ -55,12 +59,11 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> impleme
      * 分页查询
      */
     @Override
-    public IPage<Course> selectCourseList(IPage<Course> page, Course course) {
-        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+    public IPage<Course> selectCourseList(IPage<Course> page, Course course, LoginUser user) {
         if (StringUtils.isNotBlank(course.getName())) {
             course.setName(course.getName().replace("*", ""));
         }
-        if (StringUtils.isNotBlank(user.getOrgCode())) {
+        if (!Objects.isNull(user) && StringUtils.isNotBlank(user.getOrgCode())) {
             SysDepart depart = sysDepartService.queryByOrgCode(user.getOrgCode());
             if (!Objects.isNull(depart)) {
                 user.setOrgCode(depart.getId());
@@ -105,4 +108,31 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> impleme
             course.setCourseStudyProcess(NumberUtil.div(studyVO.getFinishNum(), studyVO.getTaskNum()).doubleValue());
         }
     }
+
+
+    /**
+     * 查询热门课程
+     *
+     * @param limit
+     * @return
+     */
+    @Override
+    public List<Course> listHotCourse(Integer limit) {
+        return courseMapper.listHotCourse(limit);
+    }
+
+    /**
+     * 设置用户学习情况
+     *
+     * @param courseList
+     * @param userId
+     */
+    @Override
+    public void setUserStudyInfo(List<Course> courseList, String userId) {
+        List<String> courseIdList = courseList.stream().map(Course::getId).collect(Collectors.toList());
+        List<UserCourseStudyVO> studyList = userCourseCatalogService.getUserCourseStudy(courseIdList, userId);
+        if (!CollectionUtils.isEmpty(studyList)) {
+            courseList.forEach((o) -> setCourseStudyProcess(studyList, o));
+        }
+    }
 }

+ 4 - 1
web/src/main/java/com/ynfy/buss/course/coursecatalog/controller/CourseCatalogController.java

@@ -8,10 +8,12 @@ import com.ynfy.buss.course.coursecatalog.service.ICourseCatalogService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.SecurityUtils;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.aspect.annotation.AutoLog;
 import org.jeecg.common.system.base.controller.JeecgController;
 import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.common.system.vo.LoginUser;
 import org.jeecg.modules.system.entity.FileInfo;
 import org.jeecg.modules.system.service.IFileInfoService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -183,7 +185,8 @@ public class CourseCatalogController extends JeecgController<CourseCatalog, ICou
      */
     @GetMapping("/checkPreTaskComplete")
     public Result<?> checkPreTaskComplete(@RequestParam String id) {
-        return Result.OK(courseCatalogService.checkPreTaskComplete(id));
+        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        return Result.OK(courseCatalogService.checkPreTaskComplete(id, user));
     }
 
 }

+ 2 - 2
web/src/main/java/com/ynfy/buss/course/coursecatalog/service/ICourseCatalogService.java

@@ -2,6 +2,7 @@ package com.ynfy.buss.course.coursecatalog.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ynfy.buss.course.coursecatalog.entity.CourseCatalog;
+import org.jeecg.common.system.vo.LoginUser;
 
 import java.util.List;
 
@@ -30,10 +31,9 @@ public interface ICourseCatalogService extends IService<CourseCatalog> {
     /**
      * 检查上一个任务是否完成
      *
-     * @param id
      * @return
      */
-    boolean checkPreTaskComplete(String id);
+    boolean checkPreTaskComplete(String id, LoginUser user);
 
 
     /**

+ 1 - 2
web/src/main/java/com/ynfy/buss/course/coursecatalog/service/impl/CourseCatalogServiceImpl.java

@@ -126,8 +126,7 @@ public class CourseCatalogServiceImpl extends ServiceImpl<CourseCatalogMapper, C
      * @return
      */
     @Override
-    public boolean checkPreTaskComplete(String id) {
-        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+    public boolean checkPreTaskComplete(String id, LoginUser user) {
         CourseCatalog courseCatalog = getById(id);
         if (!Objects.isNull(courseCatalog)) {
             if (StringUtils.isNotBlank(courseCatalog.getParentId())) { //点击了目录下的任务

+ 4 - 3
web/src/main/java/com/ynfy/buss/course/usercoursecatalog/controller/UserCourseCatalogController.java

@@ -170,7 +170,8 @@ public class UserCourseCatalogController extends JeecgController<UserCourseCatal
     @ApiOperation(value = "计算任务学习时长", notes = "计算任务学习时长")
     @PostMapping(value = "/calcStudyTime")
     public Result<String> calcStudyTime(@RequestBody CatalogTimeVO catalogTimeVO) {
-        userCourseCatalogService.calcStudyTime(catalogTimeVO);
+        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        userCourseCatalogService.calcStudyTime(catalogTimeVO, user.getId());
         return Result.OK();
     }
 
@@ -197,8 +198,8 @@ public class UserCourseCatalogController extends JeecgController<UserCourseCatal
     @ApiOperation(value = "获取任务学习记录", notes = "获取任务学习记录")
     @GetMapping(value = "/findUserCatalog")
     public Result<?> findUserCatalog(@RequestParam String courseCatalogId) {
-        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
-        return Result.OK(userCourseCatalogService.findUserCatalog(sysUser.getId(), courseCatalogId));
+        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        return Result.OK(userCourseCatalogService.findUserCatalog(user.getId(), courseCatalogId));
     }
 
 }

+ 1 - 1
web/src/main/java/com/ynfy/buss/course/usercoursecatalog/service/IUserCourseCatalogService.java

@@ -22,7 +22,7 @@ public interface IUserCourseCatalogService extends IService<UserCourseCatalog> {
      *
      * @return
      */
-    void calcStudyTime(CatalogTimeVO catalogTimeVO);
+    void calcStudyTime(CatalogTimeVO catalogTimeVO, String userId);
 
     UserCourseCatalog findUserCatalog(String userId, String courseCatalogId);
 

+ 4 - 4
web/src/main/java/com/ynfy/buss/course/usercoursecatalog/service/impl/UserCourseCatalogServiceImpl.java

@@ -49,19 +49,19 @@ public class UserCourseCatalogServiceImpl extends ServiceImpl<UserCourseCatalogM
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public synchronized void calcStudyTime(CatalogTimeVO catalogTimeVO) {
+    public synchronized void calcStudyTime(CatalogTimeVO catalogTimeVO, String userId) {
         if (Objects.isNull(catalogTimeVO.getCourseCatalog()) || StringUtils.isBlank(catalogTimeVO.getCourseCatalog().getId())
                 || Objects.isNull(catalogTimeVO.getStartLearnTime()) || Objects.isNull(catalogTimeVO.getEndLearnTime())) {
             return;
         }
         if (DateUtil.compare(catalogTimeVO.getEndLearnTime(), catalogTimeVO.getStartLearnTime()) > 0) {
-            LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+
             CourseCatalog courseCatalog = catalogTimeVO.getCourseCatalog();
-            UserCourseCatalog userCourseCatalog = findUserCatalog(sysUser.getId(), courseCatalog.getId());
+            UserCourseCatalog userCourseCatalog = findUserCatalog(userId, courseCatalog.getId());
             if (Objects.isNull(userCourseCatalog)) {
                 userCourseCatalog = new UserCourseCatalog();
             }
-            userCourseCatalog.setUserId(sysUser.getId());
+            userCourseCatalog.setUserId(userId);
             userCourseCatalog.setCourseCatalogId(courseCatalog.getId());
             long between = DateUtil.between(catalogTimeVO.getStartLearnTime(), catalogTimeVO.getEndLearnTime(), DateUnit.SECOND);
             userCourseCatalog.setTotalLearnTime(Integer.parseInt(String.valueOf(((!Objects.isNull(userCourseCatalog.getTotalLearnTime())

+ 5 - 1
web/src/main/java/com/ynfy/buss/exam/exam/controller/ExamController.java

@@ -76,6 +76,9 @@ public class ExamController extends JeecgController<Exam, IExamService> {
         QueryWrapper<Exam> queryWrapper = QueryGenerator.initQueryWrapper(exam, req.getParameterMap());
         Page<Exam> page = new Page<Exam>(pageNo, pageSize);
         IPage<Exam> pageList = examService.page(page, queryWrapper);
+        if (!CollectionUtils.isEmpty(pageList.getRecords())) {
+            pageList.getRecords().forEach(item -> item.setState(examService.getExamState(item)));
+        }
         return Result.OK(pageList);
     }
 
@@ -191,7 +194,8 @@ public class ExamController extends JeecgController<Exam, IExamService> {
                                           @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
         Result<IPage<Exam>> result = new Result<>();
         Page<Exam> page = new Page<>(pageNo, pageSize);
-        IPage<Exam> pageList = examService.selectExamList(page, exam);
+        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        IPage<Exam> pageList = examService.selectExamList(page, exam, user);
         result.setSuccess(true);
         result.setResult(pageList);
         return result;

+ 11 - 23
web/src/main/java/com/ynfy/buss/exam/exam/entity/Exam.java

@@ -191,27 +191,15 @@ public class Exam implements Serializable {
     private Integer showDeadline;
 
     /**
-     * 是否结束
-     *
-     * @return
-     */
-    public Integer getState() {
-        if (!Objects.isNull(startTime)) {
-            if (System.currentTimeMillis() < startTime.getTime()) {
-                return ExamState.READY_START;
-            }
-        }
-        if (!Objects.isNull(endTime)) {
-            if (System.currentTimeMillis() > endTime.getTime()) {
-                return ExamState.OVERDUE;
-            }
-        }
-        if (!Objects.isNull(startTime) && !Objects.isNull(endTime)) {
-            if (System.currentTimeMillis() > startTime.getTime()
-                    && System.currentTimeMillis() < endTime.getTime()) {
-                return ExamState.ENABLE;
-            }
-        }
-        return null;
-    }
+     * app端展示时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @TableField(exist = false)
+    private Date appShowTime;
+
+    @TableField(exist = false)
+    @ApiModelProperty(value = "是否通过")
+    private Integer passed;
+
 }

+ 3 - 0
web/src/main/java/com/ynfy/buss/exam/exam/mapper/ExamMapper.java

@@ -3,6 +3,7 @@ package com.ynfy.buss.exam.exam.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.ynfy.buss.exam.exam.entity.Exam;
+import io.swagger.models.auth.In;
 import org.apache.ibatis.annotations.Param;
 import org.jeecg.common.system.vo.LoginUser;
 
@@ -27,4 +28,6 @@ public interface ExamMapper extends BaseMapper<Exam> {
      * @return
      */
     List<String> listExamIng();
+
+    List<Exam> listLatestExam(@Param("limit") Integer limit, @Param("userId") String userId);
 }

+ 92 - 31
web/src/main/java/com/ynfy/buss/exam/exam/mapper/xml/ExamMapper.xml

@@ -31,7 +31,8 @@
                 p.create_by,
                 p.create_time,
                 p.question_count,
-                p.has_subjective
+                p.has_subjective,
+                <include refid="stateCondition" />
         FROM exam e
         LEFT JOIN paper p on e.paper_id = p.id
         <where>
@@ -63,6 +64,7 @@
         <result column="image" property="image" jdbcType="VARCHAR"/>
         <result column="exam_result_showtype" property="examResultShowtype" jdbcType="INTEGER"/>
         <result column="show_deadline" property="showDeadline" jdbcType="INTEGER"/>
+        <result column="state" property="state" jdbcType="INTEGER"/>
 
         <association property="paper" javaType="com.ynfy.buss.exam.paper.entity.Paper">
             <id column="paper_id" property="id" jdbcType="VARCHAR"/>
@@ -78,46 +80,62 @@
 
     <select id="selectExamList"  resultType="com.ynfy.buss.exam.exam.entity.Exam">
         SELECT
-            tmp1.* ,
-            tmp2.try_count
+            tmp1.*
+            <choose>
+                <when test="user!=null and user.id!=null and user.id!=''">
+                       ,
+                    tmp2.try_count,
+                    u.passed
+                </when>
+            </choose>
         FROM
         (
             SELECT
-                    *
+                    *,
+                    <include refid="stateCondition" />
             FROM exam
             WHERE
                 open_type = 1
             <choose>
-                <when test="user.orgCode!=null and user.orgCode!=''">
-            UNION
-            SELECT
-                    *
-            FROM exam
-            WHERE
-                open_type = 2
-                AND examiner like concat('%', #{user.orgCode}, '%')
+                <when test="user!=null and user.orgCode!=null and user.orgCode!=''">
+                UNION
+                    SELECT
+                            *,
+                            <include refid="stateCondition" />
+                    FROM exam
+                    WHERE
+                        open_type = 2
+                        AND examiner like concat('%', #{user.orgCode}, '%')
+                </when>
+                <when test="user!=null and user.username!=null and user.username!=''">
+                UNION
+                    SELECT
+                            *,
+                            <include refid="stateCondition" />
+                    FROM exam
+                    WHERE
+                        open_type = 3
+                        AND examiner like concat('%', #{user.username}, '%')
                 </when>
             </choose>
-            UNION
-            SELECT
-                *
-            FROM exam
-            WHERE
-                open_type = 3
-                AND examiner like concat('%', #{user.username}, '%')
         ) tmp1
-        LEFT JOIN (
-            SELECT
-                exam_id,
-                COUNT ( * ) AS try_count
-            FROM
-                user_exam uer
-            WHERE
-                uer.user_id = #{user.id}
-            GROUP BY
-                exam_id
-        ) tmp2
-        ON tmp1.ID = tmp2.exam_id
+        <choose>
+            <when test="user!=null and user.id!=null and user.id!=''">
+                LEFT JOIN (
+                    SELECT
+                        exam_id,
+                        COUNT(*) AS try_count
+                    FROM
+                        user_exam uer
+                    WHERE
+                        uer.user_id = #{user.id}
+                    GROUP BY
+                        exam_id
+                ) tmp2
+                ON tmp1.ID = tmp2.exam_id
+                LEFT JOIN user_exam_result u on u.exam_id = tmp2.exam_id and u.user_id = #{user.id}
+            </when>
+        </choose>
         <where>
             <if test="exam.title!=null and exam.title!=''">
                 AND tmp1.title like concat('%', #{exam.title}, '%')
@@ -125,6 +143,9 @@
             <if test="exam.openType!=null">
                 AND tmp1.open_type = #{exam.openType}
             </if>
+            <if test="exam.state!=null">
+                AND tmp1.state = #{exam.state}
+            </if>
         </where>
     </select>
 
@@ -137,4 +158,44 @@
             start_time &lt; now()
           AND now() &lt; DATE_ADD(end_time , INTERVAL 3 HOUR)
     </select>
+
+    <select id="listLatestExam" resultType="com.ynfy.buss.exam.exam.entity.Exam">
+        SELECT
+            e.*,
+            IFNULL( update_time, create_time ) AS time_sort
+        <choose>
+            <when test="userId !=null and userId != ''">
+               ,tmp.try_count, tmp.passed
+            </when>
+        </choose>
+        FROM
+            exam e
+        <choose>
+           <when test="userId !=null and userId != ''">
+              LEFT JOIN (
+                   SELECT
+                       exam_id,
+                       try_count,
+                       passed
+                   FROM
+                    user_exam_result uer
+                   WHERE
+                    uer.user_id = #{userId}
+              ) tmp ON e.id = tmp.exam_id
+           </when>
+        </choose>
+        ORDER BY
+            time_sort DESC
+        <if test="limit !=null">
+            LIMIT #{limit}
+        </if>
+    </select>
+    <sql id="stateCondition">
+        CASE
+        WHEN NOW() &lt; START_TIME THEN 2
+        WHEN NOW() &gt; end_TIME THEN 3
+        WHEN NOW() &gt; START_TIME AND NOW() &lt; end_TIME THEN 0
+        ELSE NULL
+        END AS state
+    </sql>
 </mapper>

+ 19 - 1
web/src/main/java/com/ynfy/buss/exam/exam/service/IExamService.java

@@ -7,6 +7,7 @@ import com.ynfy.buss.exam.exam.dto.UserExamDTO;
 import com.ynfy.buss.exam.exam.entity.Exam;
 import com.ynfy.buss.exam.question.entity.Question;
 import com.ynfy.buss.exam.userexam.entity.UserExam;
+import org.jeecg.common.system.vo.LoginUser;
 
 import java.util.List;
 
@@ -20,7 +21,7 @@ public interface IExamService extends IService<Exam> {
 
     Exam detail(String id);
 
-    IPage<Exam> selectExamList(IPage<Exam> page, Exam exam);
+    IPage<Exam> selectExamList(IPage<Exam> page, Exam exam, LoginUser use);
 
     /**
      * 创建考试
@@ -124,4 +125,21 @@ public interface IExamService extends IService<Exam> {
      * @return
      */
     String generateQuestionAnswerTag(String userAnswer, Question question);
+
+
+    /**
+     * 最新考试
+     *
+     * @return
+     */
+    List<Exam> listLatestExam(Integer limit, String userId);
+
+
+    /**
+     * 获取考试状态
+     *
+     * @param exam
+     * @return
+     */
+    Integer getExamState(Exam exam);
 }

+ 54 - 16
web/src/main/java/com/ynfy/buss/exam/exam/service/impl/ExamServiceImpl.java

@@ -38,7 +38,6 @@ import com.ynfy.common.utils.CharUtil;
 import com.ynfy.common.utils.CronUtils;
 import com.ynfy.common.utils.ThreadPoolUtil;
 import org.apache.commons.lang.StringUtils;
-import org.apache.shiro.SecurityUtils;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.constant.CommonConstant;
 import org.jeecg.common.exception.JeecgBootException;
@@ -113,12 +112,11 @@ public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IE
      * 分页查询
      */
     @Override
-    public IPage<Exam> selectExamList(IPage<Exam> page, Exam exam) {
-        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
-        if (StringUtils.isNotBlank(exam.getTitle())) {
+    public IPage<Exam> selectExamList(IPage<Exam> page, Exam exam, LoginUser user) {
+        if (!Objects.isNull(exam) && StringUtils.isNotBlank(exam.getTitle())) {
             exam.setTitle(exam.getTitle().replace("*", ""));
         }
-        if (StringUtils.isNotBlank(user.getOrgCode())) {
+        if (!Objects.isNull(user) && StringUtils.isNotBlank(user.getOrgCode())) {
             SysDepart depart = sysDepartService.queryByOrgCode(user.getOrgCode());
             if (!Objects.isNull(depart)) {
                 user.setOrgCode(depart.getId());
@@ -394,8 +392,12 @@ public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IE
     public void setAnswerTag(UserExamDTO userExamDTO) {
         List<UserExamQuestion> userExamQuestionList = userExamDTO.getUserExamQuestionList();
         if (!CollectionUtils.isEmpty(userExamQuestionList)) {
-            userExamQuestionList.stream().forEach(userExamQuestion -> userExamQuestion
-                    .setAnswer(generateQuestionAnswerTag(userExamQuestion.getAnswer(), userExamQuestion.getQuestion())));
+            userExamQuestionList.forEach(userExamQuestion -> {
+                if (QuestionType.RADIO.getCode().equals(userExamQuestion.getQuestionType()) || QuestionType.MULTI.getCode().equals(userExamQuestion.getQuestionType())
+                        || QuestionType.JUDGE.getCode().equals(userExamQuestion.getQuestionType())) {
+                    userExamQuestion.setAnswer(generateQuestionAnswerTag(userExamQuestion.getAnswer(), userExamQuestion.getQuestion()));
+                }
+            });
         }
     }
 
@@ -411,15 +413,13 @@ public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IE
         if (Objects.isNull(question)) {
             return null;
         }
-        if (QuestionType.RADIO.getCode().equals(question.getType()) || QuestionType.MULTI.getCode().equals(question.getType())
-                || QuestionType.JUDGE.getCode().equals(question.getType())) {
-            List<QuestionAnswer> answerList = question.getAnswerList();
-            if (StringUtils.isNotBlank(userAnswer) && !CollectionUtils.isEmpty(answerList)) {
-                List<String> answers = Arrays.asList(userAnswer.split(","));
-                List<QuestionAnswer> resultAnswerList = answerList.stream().filter(item -> answers.contains(item.getId())).collect(Collectors.toList());
-                List<String> tagList = resultAnswerList.stream().map(QuestionAnswer::getTag).collect(Collectors.toList());
-                return tagList.stream().sorted().collect(Collectors.joining(","));
-            }
+
+        List<QuestionAnswer> answerList = question.getAnswerList();
+        if (StringUtils.isNotBlank(userAnswer) && !CollectionUtils.isEmpty(answerList)) {
+            List<String> answers = Arrays.asList(userAnswer.split(","));
+            List<QuestionAnswer> resultAnswerList = answerList.stream().filter(item -> answers.contains(item.getId())).collect(Collectors.toList());
+            List<String> tagList = resultAnswerList.stream().map(QuestionAnswer::getTag).collect(Collectors.toList());
+            return tagList.stream().sorted().collect(Collectors.joining(","));
         }
         return null;
     }
@@ -1092,4 +1092,42 @@ public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IE
         long userExamNum = userExamService.countUserExam(userId, examId);
         return userExamNum > 0;
     }
+
+    /**
+     * 最新考试
+     *
+     * @return
+     */
+    @Override
+    public List<Exam> listLatestExam(Integer limit, String userId) {
+        return examMapper.listLatestExam(limit, userId);
+    }
+
+
+    /**
+     * 获取考试状态
+     *
+     * @param exam
+     * @return
+     */
+    @Override
+    public Integer getExamState(Exam exam) {
+        if (!Objects.isNull(exam.getStartTime())) {
+            if (System.currentTimeMillis() < exam.getStartTime().getTime()) {
+                return ExamState.READY_START;
+            }
+        }
+        if (!Objects.isNull(exam.getEndTime())) {
+            if (System.currentTimeMillis() > exam.getEndTime().getTime()) {
+                return ExamState.OVERDUE;
+            }
+        }
+        if (!Objects.isNull(exam.getStartTime()) && !Objects.isNull(exam.getEndTime())) {
+            if (System.currentTimeMillis() > exam.getStartTime().getTime()
+                    && System.currentTimeMillis() < exam.getEndTime().getTime()) {
+                return ExamState.ENABLE;
+            }
+        }
+        return null;
+    }
 }

+ 5 - 6
web/src/main/java/com/ynfy/buss/exam/userexam/controller/UserExamController.java

@@ -7,12 +7,11 @@ import com.ynfy.buss.exam.userexam.service.IUserExamService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.authz.annotation.Logical;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.apache.shiro.SecurityUtils;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.aspect.annotation.AutoLog;
 import org.jeecg.common.system.base.controller.JeecgController;
-import org.jeecg.modules.system.service.ISysUserService;
+import org.jeecg.common.system.vo.LoginUser;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.servlet.ModelAndView;
@@ -36,8 +35,6 @@ public class UserExamController extends JeecgController<UserExam, IUserExamServi
     @Autowired
     private IUserExamService userExamService;
 
-    @Autowired
-    private ISysUserService sysUserService;
 
     /**
      * 分页列表查询
@@ -54,7 +51,9 @@ public class UserExamController extends JeecgController<UserExam, IUserExamServi
                                                  @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
                                                  @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
                                                  HttpServletRequest req) {
-        Page<UserExam> page = new Page<UserExam>(pageNo, pageSize);
+        Page<UserExam> page = new Page<>(pageNo, pageSize);
+        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        userExam.setUserId(user.getId());
         IPage<UserExam> pageList = userExamService.selectPageList(page, userExam);
         return Result.OK(pageList);
     }

+ 122 - 123
web/src/main/java/com/ynfy/buss/exam/userexamresult/controller/UserExamResultController.java

@@ -8,7 +8,6 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.shiro.SecurityUtils;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.aspect.annotation.AutoLog;
 import org.jeecg.common.system.base.controller.JeecgController;
@@ -21,121 +20,121 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.util.Arrays;
 
- /**
+/**
  * @Description: 用户考试成绩
  * @Author: jeecg-boot
- * @Date:   2023-03-09
+ * @Date: 2023-03-09
  * @Version: V1.0
  */
-@Api(tags="用户考试成绩")
+@Api(tags = "用户考试成绩")
 @RestController
 @RequestMapping("/userExamResult")
 @Slf4j
 public class UserExamResultController extends JeecgController<UserExamResult, IUserExamResultService> {
-	@Autowired
-	private IUserExamResultService userExamResultService;
-	
-	/**
-	 * 分页列表查询
-	 *
-	 * @param userExamResult
-	 * @param pageNo
-	 * @param pageSize
-	 * @return
-	 */
-	@ApiOperation(value="用户考试成绩-分页列表查询", notes="用户考试成绩-分页列表查询")
-	@GetMapping(value = "/list")
-	public Result<IPage<UserExamResult>> queryPageList(UserExamResult userExamResult,
-								   @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
-								   @RequestParam(name="pageSize", defaultValue="10") Integer pageSize) {
-		Page<UserExamResult> page = new Page<UserExamResult>(pageNo, pageSize);
-		IPage<UserExamResult> pageList = userExamResultService.selectPageList(page, userExamResult);
-		return Result.OK(pageList);
-	}
-	
-	/**
-	 *   添加
-	 *
-	 * @param userExamResult
-	 * @return
-	 */
-	@AutoLog(value = "用户考试成绩-添加")
-	@ApiOperation(value="用户考试成绩-添加", notes="用户考试成绩-添加")
-	//@RequiresPermissions("userExamResult:add")
-	@PostMapping(value = "/add")
-	public Result<String> add(@RequestBody UserExamResult userExamResult) {
-		userExamResultService.save(userExamResult);
-		return Result.OK("添加成功!");
-	}
-	
-	/**
-	 *  编辑
-	 *
-	 * @param userExamResult
-	 * @return
-	 */
-	@AutoLog(value = "用户考试成绩-编辑")
-	@ApiOperation(value="用户考试成绩-编辑", notes="用户考试成绩-编辑")
-	//@RequiresPermissions("userExamResult:edit")
-	@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
-	public Result<String> edit(@RequestBody UserExamResult userExamResult) {
-		userExamResultService.updateById(userExamResult);
-		return Result.OK("编辑成功!");
-	}
-	
-	/**
-	 *   通过id删除
-	 *
-	 * @param id
-	 * @return
-	 */
-	@AutoLog(value = "用户考试成绩-通过id删除")
-	@ApiOperation(value="用户考试成绩-通过id删除", notes="用户考试成绩-通过id删除")
-	//@RequiresPermissions("userExamResult:delete")
-	@DeleteMapping(value = "/delete")
-	public Result<String> delete(@RequestParam(name="id",required=true) String id) {
-		userExamResultService.removeById(id);
-		return Result.OK("删除成功!");
-	}
-	
-	/**
-	 *  批量删除
-	 *
-	 * @param ids
-	 * @return
-	 */
-	@AutoLog(value = "用户考试成绩-批量删除")
-	@ApiOperation(value="用户考试成绩-批量删除", notes="用户考试成绩-批量删除")
-	//@RequiresPermissions("userExamResult:deleteBatch")
-	@DeleteMapping(value = "/deleteBatch")
-	public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
-		this.userExamResultService.removeByIds(Arrays.asList(ids.split(",")));
-		return Result.OK("批量删除成功!");
-	}
-	
-	/**
-	 * 通过id查询
-	 *
-	 * @param id
-	 * @return
-	 */
-	//@AutoLog(value = "用户考试成绩-通过id查询")
-	@ApiOperation(value="用户考试成绩-通过id查询", notes="用户考试成绩-通过id查询")
-	@GetMapping(value = "/queryById")
-	public Result<UserExamResult> queryById(@RequestParam(name="id",required=true) String id) {
-		UserExamResult userExamResult = userExamResultService.getById(id);
-		if(userExamResult==null) {
-			return Result.error("未找到对应数据");
-		}
-		return Result.OK(userExamResult);
-	}
+    @Autowired
+    private IUserExamResultService userExamResultService;
 
     /**
-    * 导出excel
-    *
-    * @param request
-    * @param userExamResult
-    */
+     * 分页列表查询
+     *
+     * @param userExamResult
+     * @param pageNo
+     * @param pageSize
+     * @return
+     */
+    @ApiOperation(value = "用户考试成绩-分页列表查询", notes = "用户考试成绩-分页列表查询")
+    @GetMapping(value = "/list")
+    public Result<IPage<UserExamResult>> queryPageList(UserExamResult userExamResult, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
+        Page<UserExamResult> page = new Page<UserExamResult>(pageNo, pageSize);
+        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        userExamResult.setUserId(user.getId());
+        IPage<UserExamResult> pageList = userExamResultService.selectPageList(page, userExamResult);
+        return Result.OK(pageList);
+    }
+
+    /**
+     * 添加
+     *
+     * @param userExamResult
+     * @return
+     */
+    @AutoLog(value = "用户考试成绩-添加")
+    @ApiOperation(value = "用户考试成绩-添加", notes = "用户考试成绩-添加")
+    //@RequiresPermissions("userExamResult:add")
+    @PostMapping(value = "/add")
+    public Result<String> add(@RequestBody UserExamResult userExamResult) {
+        userExamResultService.save(userExamResult);
+        return Result.OK("添加成功!");
+    }
+
+    /**
+     * 编辑
+     *
+     * @param userExamResult
+     * @return
+     */
+    @AutoLog(value = "用户考试成绩-编辑")
+    @ApiOperation(value = "用户考试成绩-编辑", notes = "用户考试成绩-编辑")
+    //@RequiresPermissions("userExamResult:edit")
+    @RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
+    public Result<String> edit(@RequestBody UserExamResult userExamResult) {
+        userExamResultService.updateById(userExamResult);
+        return Result.OK("编辑成功!");
+    }
+
+    /**
+     * 通过id删除
+     *
+     * @param id
+     * @return
+     */
+    @AutoLog(value = "用户考试成绩-通过id删除")
+    @ApiOperation(value = "用户考试成绩-通过id删除", notes = "用户考试成绩-通过id删除")
+    //@RequiresPermissions("userExamResult:delete")
+    @DeleteMapping(value = "/delete")
+    public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
+        userExamResultService.removeById(id);
+        return Result.OK("删除成功!");
+    }
+
+    /**
+     * 批量删除
+     *
+     * @param ids
+     * @return
+     */
+    @AutoLog(value = "用户考试成绩-批量删除")
+    @ApiOperation(value = "用户考试成绩-批量删除", notes = "用户考试成绩-批量删除")
+    //@RequiresPermissions("userExamResult:deleteBatch")
+    @DeleteMapping(value = "/deleteBatch")
+    public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
+        this.userExamResultService.removeByIds(Arrays.asList(ids.split(",")));
+        return Result.OK("批量删除成功!");
+    }
+
+    /**
+     * 通过id查询
+     *
+     * @param id
+     * @return
+     */
+    //@AutoLog(value = "用户考试成绩-通过id查询")
+    @ApiOperation(value = "用户考试成绩-通过id查询", notes = "用户考试成绩-通过id查询")
+    @GetMapping(value = "/queryById")
+    public Result<UserExamResult> queryById(@RequestParam(name = "id", required = true) String id) {
+        UserExamResult userExamResult = userExamResultService.getById(id);
+        if (userExamResult == null) {
+            return Result.error("未找到对应数据");
+        }
+        return Result.OK(userExamResult);
+    }
+
+    /**
+     * 导出excel
+     *
+     * @param request
+     * @param userExamResult
+     */
     //@RequiresPermissions("userExamResult:exportXls")
     @RequestMapping(value = "/exportXls")
     public ModelAndView exportXls(HttpServletRequest request, UserExamResult userExamResult) {
@@ -143,28 +142,28 @@ public class UserExamResultController extends JeecgController<UserExamResult, IU
     }
 
     /**
-      * 通过excel导入数据
-    *
-    * @param request
-    * @param response
-    * @return
-    */
+     * 通过excel导入数据
+     *
+     * @param request
+     * @param response
+     * @return
+     */
     //@RequiresPermissions("userExamResult:importExcel")
     @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
     public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
         return super.importExcel(request, response, UserExamResult.class);
     }
 
-	 /**
-	  * 获取用户考试成绩
-	  *
-	  * @param examId
-	  * @return
-	  */
-	 @GetMapping(value = "/examScore")
-	 public Result<?> examScore(@RequestParam(name = "examId") String examId) {
-		 LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
-		 return Result.ok(userExamResultService.examScore(user.getId(), examId));
-	 }
+    /**
+     * 获取用户考试成绩
+     *
+     * @param examId
+     * @return
+     */
+    @GetMapping(value = "/examScore")
+    public Result<?> examScore(@RequestParam(name = "examId") String examId) {
+        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        return Result.ok(userExamResultService.examScore(user.getId(), examId));
+    }
 
 }

+ 2 - 0
web/src/main/java/com/ynfy/buss/practice/userpractice/controller/UserPracticeController.java

@@ -179,6 +179,8 @@ public class UserPracticeController extends JeecgController<UserPractice, IUserP
     @ApiOperation(value = "提交练习")
     @PostMapping(value = "/submitPractice")
     public Result<?> submitPractice(@RequestBody UserPracticeDTO dto) {
+        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+        dto.setUserId(user.getId());
         return Result.OK("提交练习成功!", userPracticeService.submitPractice(dto));
     }
 

+ 2 - 0
web/src/main/java/com/ynfy/buss/practice/userpractice/entity/dto/UserPracticeDTO.java

@@ -12,6 +12,8 @@ import java.util.List;
 @Data
 public class UserPracticeDTO {
 
+    private String userId;
+
     /**
      * 题库id
      */

+ 2 - 3
web/src/main/java/com/ynfy/buss/practice/userpractice/service/impl/UserPracticeServiceImpl.java

@@ -70,8 +70,7 @@ public class UserPracticeServiceImpl extends ServiceImpl<UserPracticeMapper, Use
             throw new JeecgBootException("练习题目为空");
         }
         UserPractice userPractice = new UserPractice();
-        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
-        userPractice.setUserId(user.getId());
+        userPractice.setUserId(dto.getUserId());
         userPractice.setType(1);
         userPractice.setRepositoryId(dto.getRepositoryId());
         userPractice.setMode(dto.getMode());
@@ -155,7 +154,7 @@ public class UserPracticeServiceImpl extends ServiceImpl<UserPracticeMapper, Use
     }
 
     @Override
-    public IPage<UserPractice> selectPageList(IPage<UserPractice> page, UserPractice userPractice){
+    public IPage<UserPractice> selectPageList(IPage<UserPractice> page, UserPractice userPractice) {
         return userPracticeMapper.selectPageList(page, userPractice);
     }
 }

+ 18 - 14
core/src/main/java/org/jeecg/config/WebMvcConfiguration.java → web/src/main/java/com/ynfy/config/WebMvcConfiguration.java

@@ -1,4 +1,4 @@
-package org.jeecg.config;
+package com.ynfy.config;
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -10,7 +10,10 @@ import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
 import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
 import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
 import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
+import com.ynfy.app.api.v1.interceptor.AuthorizationInterceptor;
 import io.micrometer.prometheus.PrometheusMeterRegistry;
+import org.jeecg.config.CorsFilterCondition;
+import org.jeecg.config.JeecgBaseConfig;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -25,10 +28,7 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
 import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 import org.springframework.web.filter.CorsFilter;
-import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration;
-import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
-import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.springframework.web.servlet.config.annotation.*;
 
 import javax.annotation.Resource;
 import java.text.SimpleDateFormat;
@@ -46,6 +46,8 @@ import java.util.List;
  */
 @Configuration
 public class WebMvcConfiguration implements WebMvcConfigurer {
+    @Resource
+    private AuthorizationInterceptor authorizationInterceptor;
 
     @Resource
     JeecgBaseConfig jeecgBaseConfig;
@@ -146,13 +148,15 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
         return () -> meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, "");
     }
 
-//    /**
-//     * 注册拦截器【拦截器拦截参数,自动切换数据源——后期实现多租户切换数据源功能】
-//     * @param registry
-//     */
-//    @Override
-//    public void addInterceptors(InterceptorRegistry registry) {
-//        registry.addInterceptor(new DynamicDatasourceInterceptor()).addPathPatterns("/test/dynamic/**");
-//    }
-
+    /**
+     * 拦截微信接口
+     *
+     * @param registry
+     */
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        //注册自定义拦截器,添加拦截路径和排除拦截路径
+        registry.addInterceptor(authorizationInterceptor)
+                .addPathPatterns("/api/v1/**");
+    }
 }

+ 1 - 1
web/src/main/resources/application-dev.yml

@@ -208,7 +208,7 @@ cas:
 #Mybatis输出sql日志
 logging:
   level:
-    org.jeecg.modules.system.mapper: info
+    org.jeecg.modules.system.mapper: debug
     com.ynfy.buss.exam.paper.mapper: debug
     com.ynfy.buss.exam.question.mapper: debug
     com.ynfy.buss.exam.questionanswer.mapper: debug

+ 9 - 1
web/src/main/resources/application.yml

@@ -3,4 +3,12 @@ spring:
     name: exam-boot
     version: 1.0.0
   profiles:
-    active: prod
+    active: dev
+
+wx:
+  #小程序ID
+  appId: wxa72f873d39700d3e
+  #小程序密钥
+  secret: 87634c43863301b3f3b7f01bf90046f6
+  #获取Web_access_token https的请求地址
+  webAccessTokenhttps: https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code