diff --git a/api/RYAS-Cloud.postman_collection.json b/api/RYAS-Cloud.postman_collection.json
new file mode 100644
index 00000000..83d0fd45
--- /dev/null
+++ b/api/RYAS-Cloud.postman_collection.json
@@ -0,0 +1,150 @@
+{
+ "info": {
+ "_postman_id": "9067946a-288e-4a37-a276-4c453aa850e1",
+ "name": "RYAS-Cloud",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
+ "_exporter_id": "28622810"
+ },
+ "item": [
+ {
+ "name": "登录",
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"username\": \"admin\",\r\n \"password\": \"admin123\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/auth/specialAuth/login",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "auth",
+ "specialAuth",
+ "login"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "登出",
+ "request": {
+ "method": "DELETE",
+ "header": [],
+ "url": {
+ "raw": "{{baseUrl}}/auth/logout",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "auth",
+ "logout"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "打印测试信息-System",
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{baseUrl}}/system/test/showInfo",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "system",
+ "test",
+ "showInfo"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "打印测试信息-WMS",
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{baseUrl}}/wms/test/showInfo",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "wms",
+ "test",
+ "showInfo"
+ ]
+ }
+ },
+ "response": []
+ }
+ ],
+ "auth": {
+ "type": "apikey",
+ "apikey": [
+ {
+ "key": "value",
+ "value": "{{loginToken}}",
+ "type": "string"
+ },
+ {
+ "key": "key",
+ "value": "Authorization",
+ "type": "string"
+ }
+ ]
+ },
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "type": "text/javascript",
+ "exec": [
+ ""
+ ]
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "type": "text/javascript",
+ "exec": [
+ ""
+ ]
+ }
+ }
+ ],
+ "variable": [
+ {
+ "key": "baseUrl",
+ "value": "http://localhost:8080",
+ "type": "string"
+ },
+ {
+ "key": "loginToken",
+ "value": "",
+ "type": "string"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index f550d313..3a563068 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,6 +36,9 @@
8.2.2
4.1.2
2.14.4
+ 1.18.30
+ 1.4.2
+ 1.5.0
@@ -156,6 +159,13 @@
${druid.version}
+
+
+ org.mybatis.generator
+ mybatis-generator-core
+ ${mybatis.gen.version}
+
+
com.ruoyi
@@ -259,6 +269,19 @@
org.springframework.cloud
spring-cloud-starter-bootstrap
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
diff --git a/ruoyi-api/ruoyi-api-system/pom.xml b/ruoyi-api/ruoyi-api-system/pom.xml
index d79cb7e0..d3df233a 100644
--- a/ruoyi-api/ruoyi-api-system/pom.xml
+++ b/ruoyi-api/ruoyi-api-system/pom.xml
@@ -16,7 +16,7 @@
-
+
com.ruoyi
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUser.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUser.java
index 2f00e102..db30c76e 100644
--- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUser.java
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUser.java
@@ -1,16 +1,18 @@
package com.ruoyi.system.api.model;
+import com.ruoyi.system.api.domain.SysUser;
+
+import java.io.Serial;
import java.io.Serializable;
import java.util.Set;
-import com.ruoyi.system.api.domain.SysUser;
/**
* 用户信息
*
* @author ruoyi
*/
-public class LoginUser implements Serializable
-{
+public class LoginUser implements Serializable {
+ @Serial
private static final long serialVersionUID = 1L;
/**
@@ -58,93 +60,75 @@ public class LoginUser implements Serializable
*/
private SysUser sysUser;
- public String getToken()
- {
+ public String getToken() {
return token;
}
- public void setToken(String token)
- {
+ public void setToken(String token) {
this.token = token;
}
- public Long getUserid()
- {
+ public Long getUserid() {
return userid;
}
- public void setUserid(Long userid)
- {
+ public void setUserid(Long userid) {
this.userid = userid;
}
- public String getUsername()
- {
+ public String getUsername() {
return username;
}
- public void setUsername(String username)
- {
+ public void setUsername(String username) {
this.username = username;
}
- public Long getLoginTime()
- {
+ public Long getLoginTime() {
return loginTime;
}
- public void setLoginTime(Long loginTime)
- {
+ public void setLoginTime(Long loginTime) {
this.loginTime = loginTime;
}
- public Long getExpireTime()
- {
+ public Long getExpireTime() {
return expireTime;
}
- public void setExpireTime(Long expireTime)
- {
+ public void setExpireTime(Long expireTime) {
this.expireTime = expireTime;
}
- public String getIpaddr()
- {
+ public String getIpaddr() {
return ipaddr;
}
- public void setIpaddr(String ipaddr)
- {
+ public void setIpaddr(String ipaddr) {
this.ipaddr = ipaddr;
}
- public Set getPermissions()
- {
+ public Set getPermissions() {
return permissions;
}
- public void setPermissions(Set permissions)
- {
+ public void setPermissions(Set permissions) {
this.permissions = permissions;
}
- public Set getRoles()
- {
+ public Set getRoles() {
return roles;
}
- public void setRoles(Set roles)
- {
+ public void setRoles(Set roles) {
this.roles = roles;
}
- public SysUser getSysUser()
- {
+ public SysUser getSysUser() {
return sysUser;
}
- public void setSysUser(SysUser sysUser)
- {
+ public void setSysUser(SysUser sysUser) {
this.sysUser = sysUser;
}
}
diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/config/SpecialAuthProperties.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/config/SpecialAuthProperties.java
new file mode 100644
index 00000000..976fe09d
--- /dev/null
+++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/config/SpecialAuthProperties.java
@@ -0,0 +1,25 @@
+package com.ruoyi.auth.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Alan Scipio
+ * created on 2024/2/1
+ */
+@Configuration
+@RefreshScope
+@ConfigurationProperties(prefix = "special.auth")
+public class SpecialAuthProperties {
+
+ private Boolean loginEnabled;
+
+ public Boolean getLoginEnabled() {
+ return loginEnabled;
+ }
+
+ public void setLoginEnabled(Boolean loginEnabled) {
+ this.loginEnabled = loginEnabled;
+ }
+}
diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/SpecialAuthController.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/SpecialAuthController.java
new file mode 100644
index 00000000..7b0839c2
--- /dev/null
+++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/SpecialAuthController.java
@@ -0,0 +1,39 @@
+package com.ruoyi.auth.controller;
+
+import com.ruoyi.auth.config.SpecialAuthProperties;
+import com.ruoyi.auth.form.LoginBody;
+import com.ruoyi.common.core.domain.R;
+import jakarta.annotation.Resource;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 特殊登录控制
+ *
+ * @author Alan Scipio
+ * created on 2024/2/1
+ */
+@RequestMapping("/specialAuth")
+@RestController
+public class SpecialAuthController {
+
+ @Resource
+ private TokenController tokenController;
+ @Resource
+ private SpecialAuthProperties specialAuthProperties;
+
+ /**
+ * 不需要验证码的登录(哪怕开启了验证码功能)
+ */
+ @PostMapping("login")
+ public R> login(@RequestBody LoginBody form) {
+ if (specialAuthProperties.getLoginEnabled() == null || !specialAuthProperties.getLoginEnabled()) {
+ return R.fail("Special login is not enabled.");
+ } else {
+ return tokenController.login(form);
+ }
+ }
+
+}
diff --git a/ruoyi-auth/src/test/java/ryas/test/TokenTest.java b/ruoyi-auth/src/test/java/ryas/test/TokenTest.java
new file mode 100644
index 00000000..71ff4909
--- /dev/null
+++ b/ruoyi-auth/src/test/java/ryas/test/TokenTest.java
@@ -0,0 +1,43 @@
+package ryas.test;
+
+import com.ruoyi.common.core.constant.SecurityConstants;
+import com.ruoyi.common.core.utils.JwtUtils;
+import com.ruoyi.common.core.utils.uuid.IdUtils;
+import io.jsonwebtoken.Claims;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Alan Scipio
+ * created on 2024/2/1
+ */
+public class TokenTest {
+
+ @Test
+ public void createToken() {
+ int userId = 1;
+ String userName = "管理员";
+
+ String userKey = IdUtils.fastUUID();
+ Map claimsMap = new HashMap<>();
+ claimsMap.put(SecurityConstants.USER_KEY, userKey);
+ claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
+ claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName);
+
+ String token = JwtUtils.createToken(claimsMap);
+ System.out.println(token);
+ }
+
+ @Test
+ public void parseToken() {
+ String token = "eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VyX2tleSI6IjhkZDc5ZDcxLTg3MGYtNDliMy1hNGVkLTQxNzI5OTdmNTcxNCIsInVzZXJuYW1lIjoiYWRtaW4ifQ.b5230z65JXGzz-JpIa_1fSkDs_gct4nbuQ0wexW78POxixTzyeBAksh1pUUU9hEPW2_cIpK1MSF6ypi9RhxtxQ";
+
+ Claims claims = JwtUtils.parseToken(token);
+ for (Map.Entry entry : claims.entrySet()) {
+ System.out.println(entry.getKey() + ": " + entry.getValue());
+ }
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml
index 36db1db3..cd96eb5b 100644
--- a/ruoyi-common/ruoyi-common-core/pom.xml
+++ b/ruoyi-common/ruoyi-common-core/pom.xml
@@ -47,7 +47,7 @@
transmittable-thread-local
-
+
com.github.pagehelper
pagehelper-spring-boot-starter
@@ -87,7 +87,6 @@
jakarta.xml.bind-api
-
org.apache.commons
@@ -118,6 +117,13 @@
swagger-annotations
+
+
+ org.mybatis.dynamic-sql
+ mybatis-dynamic-sql
+ ${mybatis.dynamic.version}
+
+
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java
index b9874593..c34cbd31 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java
@@ -1,19 +1,19 @@
package com.ruoyi.common.core.web.controller;
-import java.beans.PropertyEditorSupport;
-import java.util.Date;
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.web.bind.WebDataBinder;
-import org.springframework.web.bind.annotation.InitBinder;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.PageUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.TableDataInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+
+import java.beans.PropertyEditorSupport;
+import java.util.Date;
+import java.util.List;
/**
* web层通用数据处理
@@ -59,7 +59,7 @@ public class BaseController {
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.SUCCESS);
rspData.setRows(list);
- rspData.setMsg("查询成功");
+ rspData.setMsg("Query success");
rspData.setTotal(new PageInfo(list).getTotal());
return rspData;
}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java
index 1327c9bc..8798d13b 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java
@@ -1,118 +1,119 @@
package com.ruoyi.common.core.web.domain;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonInclude;
/**
* Entity基类
- *
+ *
* @author ruoyi
*/
-public class BaseEntity implements Serializable
-{
+public class BaseEntity implements Serializable {
+ @Serial
private static final long serialVersionUID = 1L;
- /** 搜索值 */
+ /**
+ * 搜索值
+ */
@JsonIgnore
private String searchValue;
- /** 创建者 */
+ /**
+ * 创建者
+ */
private String createBy;
- /** 创建时间 */
+ /**
+ * 创建时间
+ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
- /** 更新者 */
+ /**
+ * 更新者
+ */
private String updateBy;
- /** 更新时间 */
+ /**
+ * 更新时间
+ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
- /** 备注 */
+ /**
+ * 备注
+ */
private String remark;
- /** 请求参数 */
+ /**
+ * 请求参数
+ */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map params;
- public String getSearchValue()
- {
+ public String getSearchValue() {
return searchValue;
}
- public void setSearchValue(String searchValue)
- {
+ public void setSearchValue(String searchValue) {
this.searchValue = searchValue;
}
- public String getCreateBy()
- {
+ public String getCreateBy() {
return createBy;
}
- public void setCreateBy(String createBy)
- {
+ public void setCreateBy(String createBy) {
this.createBy = createBy;
}
- public Date getCreateTime()
- {
+ public Date getCreateTime() {
return createTime;
}
- public void setCreateTime(Date createTime)
- {
+ public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
- public String getUpdateBy()
- {
+ public String getUpdateBy() {
return updateBy;
}
- public void setUpdateBy(String updateBy)
- {
+ public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
- public Date getUpdateTime()
- {
+ public Date getUpdateTime() {
return updateTime;
}
- public void setUpdateTime(Date updateTime)
- {
+ public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
- public String getRemark()
- {
+ public String getRemark() {
return remark;
}
- public void setRemark(String remark)
- {
+ public void setRemark(String remark) {
this.remark = remark;
}
- public Map getParams()
- {
- if (params == null)
- {
+ public Map getParams() {
+ if (params == null) {
params = new HashMap<>();
}
return params;
}
- public void setParams(Map params)
- {
+ public void setParams(Map params) {
this.params = params;
}
}
diff --git a/ruoyi-common/ruoyi-common-datasource/pom.xml b/ruoyi-common/ruoyi-common-datasource/pom.xml
index 0523e8ee..6d6e04a1 100644
--- a/ruoyi-common/ruoyi-common-datasource/pom.xml
+++ b/ruoyi-common/ruoyi-common-datasource/pom.xml
@@ -32,5 +32,12 @@
${dynamic-ds.version}
+
+
+ org.mybatis.generator
+ mybatis-generator-core
+ true
+
+
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-datasource/src/main/java/com/ruoyi/common/datasource/mybatis/gen/MyBatisGenerationHelper.java b/ruoyi-common/ruoyi-common-datasource/src/main/java/com/ruoyi/common/datasource/mybatis/gen/MyBatisGenerationHelper.java
new file mode 100644
index 00000000..da40ed74
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-datasource/src/main/java/com/ruoyi/common/datasource/mybatis/gen/MyBatisGenerationHelper.java
@@ -0,0 +1,62 @@
+package com.ruoyi.common.datasource.mybatis.gen;
+
+import org.mybatis.generator.api.MyBatisGenerator;
+import org.mybatis.generator.config.Configuration;
+import org.mybatis.generator.config.xml.ConfigurationParser;
+import org.mybatis.generator.internal.DefaultShellCallback;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * MyBatis代码生成辅助工具
+ *
+ * @author Alan Scipio
+ * created on 2024/1/3
+ * @see 官方文档
+ */
+public class MyBatisGenerationHelper {
+
+ /**
+ * 执行生成
+ *
+ * @param configFilePath [可选]配置文件路径,默认为 /generatorConfig.xml
+ */
+ public static void generate(String configFilePath) {
+ if (configFilePath == null || configFilePath.isEmpty()) {
+ configFilePath = "/generatorConfig.xml";
+ }
+ try (InputStream in = MyBatisGenerationHelper.class.getResourceAsStream(configFilePath)) {
+ if (in == null) {
+ throw new FileNotFoundException(configFilePath);
+ }
+
+ List warnings = new ArrayList<>();
+
+ ConfigurationParser cp = new ConfigurationParser(warnings);
+ System.out.println("Start to parse config file...");
+ Configuration config = cp.parseConfiguration(in);
+ System.out.println("Config file parsed");
+
+ DefaultShellCallback callback = new DefaultShellCallback(true);
+ MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
+
+ System.out.println("Start to generate code files...");
+ myBatisGenerator.generate(null);
+ System.out.println("Generation finished");
+
+ for (String warning : warnings) {
+ System.out.println("[warning] " + warning);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void generate() {
+ generate(null);
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-log/pom.xml b/ruoyi-common/ruoyi-common-log/pom.xml
index f9384418..1c5fa34b 100644
--- a/ruoyi-common/ruoyi-common-log/pom.xml
+++ b/ruoyi-common/ruoyi-common-log/pom.xml
@@ -10,13 +10,13 @@
4.0.0
ruoyi-common-log
-
+
ruoyi-common-log日志记录
-
+
com.ruoyi
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/auth/AuthLogic.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/auth/AuthLogic.java
index 1fa56bab..03e8d823 100644
--- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/auth/AuthLogic.java
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/auth/AuthLogic.java
@@ -1,9 +1,5 @@
package com.ruoyi.common.security.auth;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-import org.springframework.util.PatternMatchUtils;
import com.ruoyi.common.core.context.SecurityContextHolder;
import com.ruoyi.common.core.exception.auth.NotLoginException;
import com.ruoyi.common.core.exception.auth.NotPermissionException;
@@ -17,18 +13,26 @@ import com.ruoyi.common.security.annotation.RequiresRoles;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.model.LoginUser;
+import org.springframework.util.PatternMatchUtils;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
/**
* Token 权限验证,逻辑实现类
- *
+ *
* @author ruoyi
*/
-public class AuthLogic
-{
- /** 所有权限标识 */
+public class AuthLogic {
+ /**
+ * 所有权限标识
+ */
private static final String ALL_PERMISSION = "*:*:*";
- /** 管理员角色权限标识 */
+ /**
+ * 管理员角色权限标识
+ */
private static final String SUPER_ADMIN = "admin";
public TokenService tokenService = SpringUtils.getBean(TokenService.class);
@@ -36,11 +40,9 @@ public class AuthLogic
/**
* 会话注销
*/
- public void logout()
- {
+ public void logout() {
String token = SecurityUtils.getToken();
- if (token == null)
- {
+ if (token == null) {
return;
}
logoutByToken(token);
@@ -49,34 +51,29 @@ public class AuthLogic
/**
* 会话注销,根据指定Token
*/
- public void logoutByToken(String token)
- {
+ public void logoutByToken(String token) {
tokenService.delLoginUser(token);
}
/**
* 检验用户是否已经登录,如未登录,则抛出异常
*/
- public void checkLogin()
- {
+ public void checkLogin() {
getLoginUser();
}
/**
* 获取当前用户缓存信息, 如果未登录,则抛出异常
- *
+ *
* @return 用户缓存信息
*/
- public LoginUser getLoginUser()
- {
+ public LoginUser getLoginUser() {
String token = SecurityUtils.getToken();
- if (token == null)
- {
+ if (token == null) {
throw new NotLoginException("未提供token");
}
LoginUser loginUser = SecurityUtils.getLoginUser();
- if (loginUser == null)
- {
+ if (loginUser == null) {
throw new NotLoginException("无效的token");
}
return loginUser;
@@ -84,64 +81,54 @@ public class AuthLogic
/**
* 获取当前用户缓存信息, 如果未登录,则抛出异常
- *
+ *
* @param token 前端传递的认证信息
* @return 用户缓存信息
*/
- public LoginUser getLoginUser(String token)
- {
+ public LoginUser getLoginUser(String token) {
return tokenService.getLoginUser(token);
}
/**
* 验证当前用户有效期, 如果相差不足120分钟,自动刷新缓存
- *
+ *
* @param loginUser 当前用户信息
*/
- public void verifyLoginUserExpire(LoginUser loginUser)
- {
+ public void verifyLoginUserExpire(LoginUser loginUser) {
tokenService.verifyToken(loginUser);
}
/**
* 验证用户是否具备某权限
- *
+ *
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
- public boolean hasPermi(String permission)
- {
+ public boolean hasPermi(String permission) {
return hasPermi(getPermiList(), permission);
}
/**
* 验证用户是否具备某权限, 如果验证未通过,则抛出异常: NotPermissionException
- *
+ *
* @param permission 权限字符串
- * @return 用户是否具备某权限
*/
- public void checkPermi(String permission)
- {
- if (!hasPermi(getPermiList(), permission))
- {
+ public void checkPermi(String permission) {
+ if (!hasPermi(getPermiList(), permission)) {
throw new NotPermissionException(permission);
}
}
/**
* 根据注解(@RequiresPermissions)鉴权, 如果验证未通过,则抛出异常: NotPermissionException
- *
+ *
* @param requiresPermissions 注解对象
*/
- public void checkPermi(RequiresPermissions requiresPermissions)
- {
+ public void checkPermi(RequiresPermissions requiresPermissions) {
SecurityContextHolder.setPermission(StringUtils.join(requiresPermissions.value(), ","));
- if (requiresPermissions.logical() == Logical.AND)
- {
+ if (requiresPermissions.logical() == Logical.AND) {
checkPermiAnd(requiresPermissions.value());
- }
- else
- {
+ } else {
checkPermiOr(requiresPermissions.value());
}
}
@@ -151,13 +138,10 @@ public class AuthLogic
*
* @param permissions 权限列表
*/
- public void checkPermiAnd(String... permissions)
- {
+ public void checkPermiAnd(String... permissions) {
Set permissionList = getPermiList();
- for (String permission : permissions)
- {
- if (!hasPermi(permissionList, permission))
- {
+ for (String permission : permissions) {
+ if (!hasPermi(permissionList, permission)) {
throw new NotPermissionException(permission);
}
}
@@ -165,78 +149,64 @@ public class AuthLogic
/**
* 验证用户是否含有指定权限,只需包含其中一个
- *
+ *
* @param permissions 权限码数组
*/
- public void checkPermiOr(String... permissions)
- {
+ public void checkPermiOr(String... permissions) {
Set permissionList = getPermiList();
- for (String permission : permissions)
- {
- if (hasPermi(permissionList, permission))
- {
+ for (String permission : permissions) {
+ if (hasPermi(permissionList, permission)) {
return;
}
}
- if (permissions.length > 0)
- {
+ if (permissions.length > 0) {
throw new NotPermissionException(permissions);
}
}
/**
* 判断用户是否拥有某个角色
- *
+ *
* @param role 角色标识
* @return 用户是否具备某角色
*/
- public boolean hasRole(String role)
- {
+ public boolean hasRole(String role) {
return hasRole(getRoleList(), role);
}
/**
* 判断用户是否拥有某个角色, 如果验证未通过,则抛出异常: NotRoleException
- *
+ *
* @param role 角色标识
*/
- public void checkRole(String role)
- {
- if (!hasRole(role))
- {
+ public void checkRole(String role) {
+ if (!hasRole(role)) {
throw new NotRoleException(role);
}
}
/**
* 根据注解(@RequiresRoles)鉴权
- *
+ *
* @param requiresRoles 注解对象
*/
- public void checkRole(RequiresRoles requiresRoles)
- {
- if (requiresRoles.logical() == Logical.AND)
- {
+ public void checkRole(RequiresRoles requiresRoles) {
+ if (requiresRoles.logical() == Logical.AND) {
checkRoleAnd(requiresRoles.value());
- }
- else
- {
+ } else {
checkRoleOr(requiresRoles.value());
}
}
/**
* 验证用户是否含有指定角色,必须全部拥有
- *
+ *
* @param roles 角色标识数组
*/
- public void checkRoleAnd(String... roles)
- {
+ public void checkRoleAnd(String... roles) {
Set roleList = getRoleList();
- for (String role : roles)
- {
- if (!hasRole(roleList, role))
- {
+ for (String role : roles) {
+ if (!hasRole(roleList, role)) {
throw new NotRoleException(role);
}
}
@@ -244,129 +214,106 @@ public class AuthLogic
/**
* 验证用户是否含有指定角色,只需包含其中一个
- *
+ *
* @param roles 角色标识数组
*/
- public void checkRoleOr(String... roles)
- {
+ public void checkRoleOr(String... roles) {
Set roleList = getRoleList();
- for (String role : roles)
- {
- if (hasRole(roleList, role))
- {
+ for (String role : roles) {
+ if (hasRole(roleList, role)) {
return;
}
}
- if (roles.length > 0)
- {
+ if (roles.length > 0) {
throw new NotRoleException(roles);
}
}
/**
* 根据注解(@RequiresLogin)鉴权
- *
+ *
* @param at 注解对象
*/
- public void checkByAnnotation(RequiresLogin at)
- {
+ public void checkByAnnotation(RequiresLogin at) {
this.checkLogin();
}
/**
* 根据注解(@RequiresRoles)鉴权
- *
+ *
* @param at 注解对象
*/
- public void checkByAnnotation(RequiresRoles at)
- {
+ public void checkByAnnotation(RequiresRoles at) {
String[] roleArray = at.value();
- if (at.logical() == Logical.AND)
- {
+ if (at.logical() == Logical.AND) {
this.checkRoleAnd(roleArray);
- }
- else
- {
+ } else {
this.checkRoleOr(roleArray);
}
}
/**
* 根据注解(@RequiresPermissions)鉴权
- *
+ *
* @param at 注解对象
*/
- public void checkByAnnotation(RequiresPermissions at)
- {
+ public void checkByAnnotation(RequiresPermissions at) {
String[] permissionArray = at.value();
- if (at.logical() == Logical.AND)
- {
+ if (at.logical() == Logical.AND) {
this.checkPermiAnd(permissionArray);
- }
- else
- {
+ } else {
this.checkPermiOr(permissionArray);
}
}
/**
* 获取当前账号的角色列表
- *
+ *
* @return 角色列表
*/
- public Set getRoleList()
- {
- try
- {
+ public Set getRoleList() {
+ try {
LoginUser loginUser = getLoginUser();
return loginUser.getRoles();
- }
- catch (Exception e)
- {
+ } catch (Exception e) {
return new HashSet<>();
}
}
/**
* 获取当前账号的权限列表
- *
+ *
* @return 权限列表
*/
- public Set getPermiList()
- {
- try
- {
+ public Set getPermiList() {
+ try {
LoginUser loginUser = getLoginUser();
return loginUser.getPermissions();
- }
- catch (Exception e)
- {
+ } catch (Exception e) {
return new HashSet<>();
}
}
/**
* 判断是否包含权限
- *
+ *
* @param authorities 权限列表
- * @param permission 权限字符串
+ * @param permission 权限字符串
* @return 用户是否具备某权限
*/
- public boolean hasPermi(Collection authorities, String permission)
- {
+ public boolean hasPermi(Collection authorities, String permission) {
return authorities.stream().filter(StringUtils::hasText)
.anyMatch(x -> ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission));
}
/**
* 判断是否包含角色
- *
+ *
* @param roles 角色列表
- * @param role 角色
+ * @param role 角色
* @return 用户是否具备某角色权限
*/
- public boolean hasRole(Collection roles, String role)
- {
+ public boolean hasRole(Collection roles, String role) {
return roles.stream().filter(StringUtils::hasText)
.anyMatch(x -> SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role));
}
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java
index 393819e2..524fca06 100644
--- a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java
@@ -1,14 +1,5 @@
package com.ruoyi.common.security.service;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import jakarta.servlet.http.HttpServletRequest;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.utils.JwtUtils;
@@ -19,6 +10,15 @@ import com.ruoyi.common.core.utils.uuid.IdUtils;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.model.LoginUser;
+import jakarta.servlet.http.HttpServletRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
/**
* token验证处理
@@ -56,13 +56,13 @@ public class TokenService {
refreshToken(loginUser);
// Jwt存储信息
- Map claimsMap = new HashMap();
+ Map claimsMap = new HashMap<>();
claimsMap.put(SecurityConstants.USER_KEY, token);
claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName);
// 接口返回信息
- Map rspMap = new HashMap();
+ Map rspMap = new HashMap<>();
rspMap.put("access_token", JwtUtils.createToken(claimsMap));
rspMap.put("expires_in", expireTime);
return rspMap;
@@ -97,8 +97,8 @@ public class TokenService {
LoginUser user = null;
try {
if (StringUtils.isNotEmpty(token)) {
- String userkey = JwtUtils.getUserKey(token);
- user = redisService.getCacheObject(getTokenKey(userkey));
+ String userKey = JwtUtils.getUserKey(token);
+ user = redisService.getCacheObject(getTokenKey(userKey));
return user;
}
} catch (Exception e) {
diff --git a/ruoyi-modules/ruoyi-system/pom.xml b/ruoyi-modules/ruoyi-system/pom.xml
index 4bc4f00c..889f8752 100644
--- a/ruoyi-modules/ruoyi-system/pom.xml
+++ b/ruoyi-modules/ruoyi-system/pom.xml
@@ -8,21 +8,21 @@
3.6.3
4.0.0
-
+
ruoyi-modules-system
ruoyi-modules-system系统模块
-
+
-
-
+
+
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
-
+
com.alibaba.cloud
@@ -34,14 +34,14 @@
nacos-client
${nacos-client.version}
-
-
+
+
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
-
-
+
+
org.springframework.boot
spring-boot-starter-actuator
@@ -63,31 +63,38 @@
com.mysql
mysql-connector-j
-
+
com.ruoyi
ruoyi-common-datasource
-
+
com.ruoyi
ruoyi-common-datascope
-
+
com.ruoyi
ruoyi-common-log
-
+
com.ruoyi
ruoyi-common-swagger
+
+
+ org.mybatis.generator
+ mybatis-generator-core
+ test
+
+
@@ -124,5 +131,5 @@
-
+
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/SystemAllApplication.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/SystemAllApplication.java
index a8c29d32..680bdaf3 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/SystemAllApplication.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/SystemAllApplication.java
@@ -1,11 +1,6 @@
package com.ruoyi;
-import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure;
-import com.ruoyi.common.security.annotation.EnableCustomConfig;
-import com.ruoyi.common.security.annotation.EnableRyFeignClients;
-import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* all-in-one架构模式下的启动入口类
@@ -19,6 +14,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
//@SpringBootApplication(exclude = {DruidDataSourceAutoConfigure.class})
public class SystemAllApplication {
public static void main(String[] args) {
+ System.setProperty("pagehelper.banner", "false"); //关闭pagehelper的banner
SpringApplication.run(SystemAllApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 系统模块(All-in-one模式)启动成功 ლ(´ڡ`ლ)゙");
}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/RuoYiSystemApplication.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/RuoYiSystemApplication.java
index 1ff73587..32a9509d 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/RuoYiSystemApplication.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/RuoYiSystemApplication.java
@@ -18,6 +18,7 @@ import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
@SpringBootApplication(exclude = {DruidDataSourceAutoConfigure.class})
public class RuoYiSystemApplication {
public static void main(String[] args) {
+ System.setProperty("pagehelper.banner", "false"); //关闭pagehelper的banner
SpringApplication.run(RuoYiSystemApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 系统模块(常规模式)启动成功 ლ(´ڡ`ლ)゙");
}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
index ffa68e5b..7ccd91c3 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
@@ -1,23 +1,5 @@
package com.ruoyi.system.controller;
-import java.io.IOException;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import jakarta.servlet.http.HttpServletResponse;
-import org.apache.commons.lang3.ArrayUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.poi.ExcelUtil;
@@ -33,12 +15,17 @@ import com.ruoyi.system.api.domain.SysDept;
import com.ruoyi.system.api.domain.SysRole;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.model.LoginUser;
-import com.ruoyi.system.service.ISysConfigService;
-import com.ruoyi.system.service.ISysDeptService;
-import com.ruoyi.system.service.ISysPermissionService;
-import com.ruoyi.system.service.ISysPostService;
-import com.ruoyi.system.service.ISysRoleService;
-import com.ruoyi.system.service.ISysUserService;
+import com.ruoyi.system.service.*;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* 用户信息
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserOnlineController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserOnlineController.java
index d15b0ae0..ef4cd424 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserOnlineController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserOnlineController.java
@@ -1,15 +1,5 @@
package com.ruoyi.system.controller;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.controller.BaseController;
@@ -22,16 +12,22 @@ import com.ruoyi.common.security.annotation.RequiresPermissions;
import com.ruoyi.system.api.model.LoginUser;
import com.ruoyi.system.domain.SysUserOnline;
import com.ruoyi.system.service.ISysUserOnlineService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
/**
* 在线用户监控
- *
+ *
* @author ruoyi
*/
@RestController
@RequestMapping("/online")
-public class SysUserOnlineController extends BaseController
-{
+public class SysUserOnlineController extends BaseController {
@Autowired
private ISysUserOnlineService userOnlineService;
@@ -40,27 +36,18 @@ public class SysUserOnlineController extends BaseController
@RequiresPermissions("monitor:online:list")
@GetMapping("/list")
- public TableDataInfo list(String ipaddr, String userName)
- {
+ public TableDataInfo list(String ipaddr, String userName) {
Collection keys = redisService.keys(CacheConstants.LOGIN_TOKEN_KEY + "*");
- List userOnlineList = new ArrayList();
- for (String key : keys)
- {
+ List userOnlineList = new ArrayList<>();
+ for (String key : keys) {
LoginUser user = redisService.getCacheObject(key);
- if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
- {
+ if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
- }
- else if (StringUtils.isNotEmpty(ipaddr))
- {
+ } else if (StringUtils.isNotEmpty(ipaddr)) {
userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
- }
- else if (StringUtils.isNotEmpty(userName))
- {
+ } else if (StringUtils.isNotEmpty(userName)) {
userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
- }
- else
- {
+ } else {
userOnlineList.add(userOnlineService.loginUserToUserOnline(user));
}
}
@@ -75,8 +62,7 @@ public class SysUserOnlineController extends BaseController
@RequiresPermissions("monitor:online:forceLogout")
@Log(title = "在线用户", businessType = BusinessType.FORCE)
@DeleteMapping("/{tokenId}")
- public AjaxResult forceLogout(@PathVariable String tokenId)
- {
+ public AjaxResult forceLogout(@PathVariable String tokenId) {
redisService.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId);
return success();
}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/TestController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/TestController.java
index 0ff84d19..0dc501cd 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/TestController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/TestController.java
@@ -16,7 +16,7 @@ public class TestController extends BaseController {
@GetMapping("/showInfo")
public AjaxResult showInfo() {
- return success("Hello World!");
+ return success("Hello World! This is system module.");
}
}
diff --git a/ruoyi-modules/ruoyi-system/src/test/java/ryas/gen/GenSystem.java b/ruoyi-modules/ruoyi-system/src/test/java/ryas/gen/GenSystem.java
new file mode 100644
index 00000000..40580fc2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/test/java/ryas/gen/GenSystem.java
@@ -0,0 +1,21 @@
+package ryas.gen;
+
+import com.ruoyi.common.datasource.mybatis.gen.MyBatisGenerationHelper;
+
+/**
+ * MyBatis代码生成
+ *
+ * @author Alan Scipio
+ * created on 2024/2/1
+ */
+public class GenSystem {
+
+ public static void main(String[] args) {
+ try {
+ MyBatisGenerationHelper.generate();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-system/src/test/java/ryas/test/Common1Test.java b/ruoyi-modules/ruoyi-system/src/test/java/ryas/test/Common1Test.java
new file mode 100644
index 00000000..a1e8ea44
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/test/java/ryas/test/Common1Test.java
@@ -0,0 +1,16 @@
+package ryas.test;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author Alan Scipio
+ * created on 2024/2/1
+ */
+public class Common1Test {
+
+ @Test
+ public void test0() {
+ System.out.println("abc");
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-system/src/test/resources/generatorConfig.xml b/ruoyi-modules/ruoyi-system/src/test/resources/generatorConfig.xml
new file mode 100644
index 00000000..29fbfe11
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/test/resources/generatorConfig.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file