refactor:基于ruoyi等对框架调整

V0.5.x
xiwa 2023-05-15 08:31:02 +08:00
parent bb94a47c7e
commit 1a38916458
415 changed files with 21392 additions and 2198 deletions

View File

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>iotkit-parent</artifactId>
<groupId>cc.iotkit</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>iot-auth-server</artifactId>
<description>
此模块为认证服务提供oauth2认证界面和接口
</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-oauth2</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
</dependency>
<dependency>
<groupId>com.ejlchina</groupId>
<artifactId>okhttps</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-data-service</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,138 +0,0 @@
/*
* +----------------------------------------------------------------------
* | Copyright (c) 2021-2022 All rights reserved.
* +----------------------------------------------------------------------
* | Licensed
* +----------------------------------------------------------------------
* | Author: xw2sy@163.com
* +----------------------------------------------------------------------
*/
package cc.iotkit.oauth.controller;
import cc.iotkit.common.Constants;
import cc.iotkit.common.utils.CodecUtil;
import cc.iotkit.common.utils.ReflectUtil;
import cc.iotkit.data.IOauthClientData;
import cc.iotkit.data.IUserInfoData;
import cc.iotkit.model.OauthClient;
import cc.iotkit.model.UserInfo;
import cc.iotkit.oauth.vo.UserInfoVo;
import cc.iotkit.utils.SoMap;
import cn.dev33.satoken.stp.SaLoginConfig;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.ejlchina.okhttps.OkHttps;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.view.RedirectView;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/oauth2")
public class AuthClientController {
@Value("${oauth2.auth-server-url}")
private String serverUrl;
@Autowired
@Qualifier("oauthClientDataCache")
private IOauthClientData oauthClientData;
@Autowired
@Qualifier("userInfoDataCache")
private IUserInfoData userInfoData;
/**
* Code Access-Token
*/
@RequestMapping("/codeLogin")
public SaResult codeLogin(String code, String clientId) {
OauthClient oauthClient = oauthClientData.findByClientId(clientId);
if (oauthClient == null) {
return SaResult.error("clientId does not exist");
}
String clientSecret = oauthClient.getClientSecret();
// 调用Server端接口获取 Access-Token 以及其他信息
String str = OkHttps.sync(serverUrl + "/oauth2/token")
.addBodyPara("grant_type", "authorization_code")
.addBodyPara("code", code)
.addBodyPara("client_id", clientId)
.addBodyPara("client_secret", clientSecret)
.post()
.getBody()
.toString();
SoMap so = SoMap.getSoMap().setJsonString(str);
log.info("get token by code result:{}", so);
// 存在code,不是token结构
if (so.getInt("code") != 0) {
return SaResult.error(so.getString("msg"));
}
// 根据openid获取其对应的userId
String uid = getUserIdByOpenid(so.getString("openid"));
String access_token = so.getString("access_token");
UserInfoVo userVo = getUserInfo(uid);
BeanMap beanMap = BeanMap.create(userVo);
Map<String, Object> data = new HashMap<>();
beanMap.forEach((key, value) -> {
data.put(key.toString(), value);
});
data.put("access_token", access_token);
// 返回相关参数
StpUtil.login(uid, SaLoginConfig.setToken(access_token));
return SaResult.data(data);
}
/**
*
*/
@RequestMapping("/logout")
public RedirectView logout(String accessToken, String redirect_uri) {
//先注销client中cookie的token
StpUtil.logout();
//再注销web页面使用的token
StpUtil.logoutByTokenValue(accessToken);
return new RedirectView(redirect_uri);
}
/**
*
*/
@GetMapping("/checkLogin")
public SaResult checkLogin() {
try {
String uid = StpUtil.getLoginId().toString();
UserInfoVo userVo = getUserInfo(uid);
return SaResult.ok().setData(userVo);
} catch (Throwable e) {
return SaResult.error("no login");
}
}
@SneakyThrows
private String getUserIdByOpenid(String openid) {
String clientIdLoginId = CodecUtil.aesDecrypt(openid, Constants.ACCOUNT_SECRET);
return clientIdLoginId.split(":")[1];
}
private UserInfoVo getUserInfo(String uid) {
UserInfo userInfo = userInfoData.findById(uid);
UserInfoVo userVo = new UserInfoVo();
ReflectUtil.copyNoNulls(userInfo, userVo);
return userVo;
}
}

View File

@ -1,99 +0,0 @@
/*
* +----------------------------------------------------------------------
* | Copyright (c) 2021-2022 All rights reserved.
* +----------------------------------------------------------------------
* | Licensed
* +----------------------------------------------------------------------
* | Author: xw2sy@163.com
* +----------------------------------------------------------------------
*/
package cc.iotkit.oauth.controller;
import cc.iotkit.common.utils.JsonUtil;
import cc.iotkit.data.IUserInfoData;
import cc.iotkit.model.UserInfo;
import cc.iotkit.oauth.service.TokenRequestHandler;
import cc.iotkit.utils.AuthUtil;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
public class AuthServerController {
@Autowired
private IUserInfoData userInfoData;
/**
* OAuth
*/
@RequestMapping("/oauth2/*")
public Object request(HttpServletRequest request) {
Object result = TokenRequestHandler.serverRequest();
log.info("oauth path:{},result:{}", request.getRequestURI(), JsonUtil.toJsonString(result));
return result;
}
/**
* Sa-OAuth2
*/
@Autowired
public void setSaOAuth2Config(SaOAuth2Config cfg) {
cfg.
// 未登录的视图
setNotLoginView(() -> new ModelAndView("login.html")).
// 登录处理函数
setDoLoginHandle((name, pwd) -> {
try {
UserInfo userInfo = userInfoData.findByUid(name);
if (userInfo != null) {
String secret = userInfo.getSecret();
if (AuthUtil.checkPwd(pwd, secret)) {
StpUtil.login(userInfo.getId(), "PC");
return SaResult.ok();
}
}
} catch (Throwable e) {
return SaResult.error("账号名或密码错误");
}
return SaResult.error("账号名或密码错误");
}).
// 授权确认视图
setConfirmView((clientId, scope) -> {
Map<String, Object> map = new HashMap<>();
map.put("clientId", clientId);
map.put("scope", scope);
return new ModelAndView("confirm.html", map);
})
;
//开启密码授权、刷新token和client授权模式
cfg.setIsPassword(true);
cfg.setIsNewRefresh(true);
cfg.setIsClient(true);
}
// 全局异常拦截
@ExceptionHandler
public SaResult handlerException(Exception e) {
e.printStackTrace();
return SaResult.error(e.getMessage());
}
@RequestMapping("/oauth2/userinfo")
public SaResult userinfo() {
return SaResult.ok();
}
}

View File

@ -1,61 +0,0 @@
/*
* +----------------------------------------------------------------------
* | Copyright (c) 2021-2022 All rights reserved.
* +----------------------------------------------------------------------
* | Licensed
* +----------------------------------------------------------------------
* | Author: xw2sy@163.com
* +----------------------------------------------------------------------
*/
package cc.iotkit.oauth.service;
import cc.iotkit.common.Constants;
import cc.iotkit.common.utils.CodecUtil;
import cc.iotkit.data.IOauthClientData;
import cc.iotkit.model.OauthClient;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Template;
import cn.dev33.satoken.oauth2.model.SaClientModel;
import cn.dev33.satoken.stp.StpUtil;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class SaOAuth2TemplateImpl extends SaOAuth2Template {
@Autowired
@Qualifier("oauthClientDataCache")
private IOauthClientData oauthClientData;
// 根据 id 获取 Client 信息
@Override
public SaClientModel getClientModel(String clientId) {
OauthClient client = oauthClientData.findByClientId(clientId);
if (client == null) {
return null;
}
return new SaClientModel()
.setClientId(client.getClientId())
.setClientSecret(client.getClientSecret())
.setAllowUrl(client.getAllowUrl())
.setContractScope("userinfo")
.setIsAutoMode(true);
}
// 根据ClientId 和 LoginId 获取openid
@SneakyThrows
@Override
public String getOpenid(String clientId, Object loginId) {
// 此为模拟数据,真实环境需要从数据库查询
return CodecUtil.aesEncrypt(clientId + ":" + loginId, Constants.ACCOUNT_SECRET);
}
@Override
public String randomAccessToken(String clientId, Object loginId, String scope) {
return StpUtil.createLoginSession(loginId);
}
}

View File

@ -1,46 +0,0 @@
/*
* +----------------------------------------------------------------------
* | Copyright (c) 2021-2022 All rights reserved.
* +----------------------------------------------------------------------
* | Licensed
* +----------------------------------------------------------------------
* | Author: xw2sy@163.com
* +----------------------------------------------------------------------
*/
package cc.iotkit.oauth.service;
import cc.iotkit.data.IUserInfoData;
import cc.iotkit.model.UserInfo;
import cn.dev33.satoken.stp.StpInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class StpInterfaceImpl implements StpInterface {
@Autowired
@Qualifier("userInfoDataCache")
private IUserInfoData userInfoData;
/**
*
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
UserInfo userInfo = userInfoData.findById(loginId.toString());
return userInfo.getPermissions();
}
/**
* ()
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
UserInfo userInfo = userInfoData.findById(loginId.toString());
return userInfo.getRoles();
}
}

View File

@ -1,124 +0,0 @@
/*
* +----------------------------------------------------------------------
* | Copyright (c) 2021-2022 All rights reserved.
* +----------------------------------------------------------------------
* | Licensed
* +----------------------------------------------------------------------
* | Author: xw2sy@163.com
* +----------------------------------------------------------------------
*/
package cc.iotkit.oauth.service;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Handle;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
import cn.dev33.satoken.oauth2.model.ClientTokenModel;
import cn.dev33.satoken.oauth2.model.RequestAuthModel;
import cn.dev33.satoken.oauth2.model.SaClientModel;
import cn.dev33.satoken.stp.StpUtil;
public class TokenRequestHandler {
public static Object serverRequest() {
SaRequest req = SaHolder.getRequest();
SaResponse res = SaHolder.getResponse();
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
SaClientModel cm;
if (req.isPath(SaOAuth2Consts.Api.authorize) && req.isParam(SaOAuth2Consts.Param.response_type, SaOAuth2Consts.ResponseType.code)) {
cm = SaOAuth2Handle.currClientModel();
if (!cfg.getIsCode() || !cm.isCode && !cm.isAutoMode) {
throw new SaOAuth2Exception("暂未开放的授权模式");
} else {
return SaOAuth2Handle.authorize(req, res, cfg);
}
} else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.authorization_code)) {
return token(req, res, cfg);
} else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.refresh_token)) {
return refreshToken(req);
} else if (req.isPath(SaOAuth2Consts.Api.revoke)) {
return SaOAuth2Handle.revokeToken(req);
} else if (req.isPath(SaOAuth2Consts.Api.doLogin)) {
return SaOAuth2Handle.doLogin(req, res, cfg);
} else if (req.isPath(SaOAuth2Consts.Api.doConfirm)) {
return SaOAuth2Handle.doConfirm(req);
} else if (req.isPath(SaOAuth2Consts.Api.authorize) && req.isParam(SaOAuth2Consts.Param.response_type, SaOAuth2Consts.ResponseType.token)) {
cm = SaOAuth2Handle.currClientModel();
if (!cfg.getIsImplicit() || !cm.isImplicit && !cm.isAutoMode) {
throw new SaOAuth2Exception("暂未开放的授权模式");
} else {
return SaOAuth2Handle.authorize(req, res, cfg);
}
} else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.password)) {
cm = SaOAuth2Handle.currClientModel();
if (!cfg.getIsPassword() || !cm.isPassword && !cm.isAutoMode) {
throw new SaOAuth2Exception("暂未开放的授权模式");
} else {
return password(req, res, cfg);
}
} else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.client_credentials)) {
cm = SaOAuth2Handle.currClientModel();
if (!cfg.getIsClient() || !cm.isClient && !cm.isAutoMode) {
throw new SaOAuth2Exception("暂未开放的授权模式");
} else {
return clientToken(req, res, cfg);
}
} else {
return "{\"msg\": \"not handle\"}";
}
}
public static Object token(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
String code = req.getParamNotNull(SaOAuth2Consts.Param.code);
String clientId = req.getParamNotNull(SaOAuth2Consts.Param.client_id);
String clientSecret = req.getParamNotNull(SaOAuth2Consts.Param.client_secret);
String redirectUri = req.getParam(SaOAuth2Consts.Param.redirect_uri);
SaOAuth2Util.checkGainTokenParam(code, clientId, clientSecret, redirectUri);
AccessTokenModel token = SaOAuth2Util.generateAccessToken(code);
return token.toLineMap();
}
public static Object refreshToken(SaRequest req) {
String clientId = req.getParamNotNull(SaOAuth2Consts.Param.client_id);
String clientSecret = req.getParamNotNull(SaOAuth2Consts.Param.client_secret);
String refreshToken = req.getParamNotNull(SaOAuth2Consts.Param.refresh_token);
SaOAuth2Util.checkRefreshTokenParam(clientId, clientSecret, refreshToken);
return SaOAuth2Util.refreshAccessToken(refreshToken).toLineMap();
}
public static Object password(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
String username = req.getParamNotNull(SaOAuth2Consts.Param.username);
String password = req.getParamNotNull(SaOAuth2Consts.Param.password);
String clientId = req.getParamNotNull(SaOAuth2Consts.Param.client_id);
String scope = req.getParam(SaOAuth2Consts.Param.scope, "");
SaOAuth2Util.checkContract(clientId, scope);
SaHolder.getStorage().set(StpUtil.stpLogic.splicingKeyJustCreatedSave(), "no-token");
Object retObj = cfg.getDoLoginHandle().apply(username, password);
if (!StpUtil.isLogin()) {
return retObj;
} else {
RequestAuthModel ra = new RequestAuthModel();
ra.clientId = clientId;
ra.loginId = StpUtil.getLoginId();
ra.scope = scope;
AccessTokenModel at = SaOAuth2Util.generateAccessToken(ra, true);
return at.toLineMap();
}
}
public static Object clientToken(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
String clientId = req.getParamNotNull(SaOAuth2Consts.Param.client_id);
String clientSecret = req.getParamNotNull(SaOAuth2Consts.Param.client_secret);
String scope = req.getParam(SaOAuth2Consts.Param.scope);
SaOAuth2Util.checkContract(clientId, scope);
SaOAuth2Util.checkClientSecret(clientId, clientSecret);
ClientTokenModel ct = SaOAuth2Util.generateClientToken(clientId, scope);
return ct.toLineMap();
}
}

View File

@ -1,79 +0,0 @@
/*
* +----------------------------------------------------------------------
* | Copyright (c) 2021-2022 All rights reserved.
* +----------------------------------------------------------------------
* | Licensed
* +----------------------------------------------------------------------
* | Author: xw2sy@163.com
* +----------------------------------------------------------------------
*/
package cc.iotkit.oauth.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserInfoVo {
/**
*
*/
private String uid;
/**
*
*/
private String nickName;
/**
* 0- 1-male,2-female
*/
private Integer gender;
/**
*
*/
private String avatarUrl;
private String email;
private String address;
/**
* Id
*/
private String currHomeId;
/**
*
* 0:
* 1:
*/
private Integer type;
/**
*
*/
private List<String> roles = new ArrayList<>();
/**
*
*/
private List<String> permissions = new ArrayList<>();
/**
* 使
* :Constants.THIRD_PLATFORM
*/
private List<String> usePlatforms = new ArrayList<>();
}

View File

@ -1,51 +0,0 @@
/*
* +----------------------------------------------------------------------
* | Copyright (c) 2021-2022 All rights reserved.
* +----------------------------------------------------------------------
* | Licensed
* +----------------------------------------------------------------------
* | Author: xw2sy@163.com
* +----------------------------------------------------------------------
*/
package cc.iotkit.utils;
import cc.iotkit.common.Constants;
import cc.iotkit.common.utils.CodecUtil;
import cn.dev33.satoken.stp.StpUtil;
import org.apache.commons.lang3.RandomUtils;
import java.util.List;
public class AuthUtil {
public static String getUserId() {
return StpUtil.getLoginId().toString();
}
public static List<String> getUserRoles() {
return StpUtil.getRoleList();
}
public static boolean isAdmin() {
return AuthUtil.getUserRoles().contains(Constants.ROLE_ADMIN);
}
public static boolean isClientUser() {
return AuthUtil.getUserRoles().contains(Constants.ROLE_CLIENT);
}
public static boolean hasWriteRole() {
return AuthUtil.getUserRoles().contains(Constants.ROLE_WRITE);
}
public static String enCryptPwd(String pwd) throws Exception {
return CodecUtil.aesEncrypt(CodecUtil.md5Str(pwd) + ":"
+ RandomUtils.nextInt(1000, 9999), Constants.ACCOUNT_SECRET);
}
public static boolean checkPwd(String pwd, String secret) throws Exception {
String code = CodecUtil.aesDecrypt(secret, Constants.ACCOUNT_SECRET);
String[] arr = code.split(":");
return arr.length > 0 && CodecUtil.md5Str(pwd).equals(arr[0]);
}
}

View File

@ -1,837 +0,0 @@
/*
* +----------------------------------------------------------------------
* | Copyright (c) 2021-2022 All rights reserved.
* +----------------------------------------------------------------------
* | Licensed
* +----------------------------------------------------------------------
* | Author: xw2sy@163.com
* +----------------------------------------------------------------------
*/
package cc.iotkit.utils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import cc.iotkit.common.utils.JsonUtil;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* Map< String, Object> Map
* <p>MapMap使
* <p>2020-12-10
*
* @author kong
*/
public class SoMap extends LinkedHashMap<String, Object> {
private static final long serialVersionUID = 1L;
public SoMap() {
}
/**
* isNullNull
*/
public static final Object[] NULL_ELEMENT_ARRAY = {null, ""};
public static final List<Object> NULL_ELEMENT_LIST;
static {
NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY);
}
// ============================= 读值 =============================
/**
*
*/
@Override
public Object get(Object key) {
if ("this".equals(key)) {
return this;
}
return super.get(key);
}
/**
*
*/
public Object get(Object key, Object defaultValue) {
Object value = get(key);
if (valueIsNull(value)) {
return defaultValue;
}
return value;
}
/**
* String
*/
public String getString(String key) {
Object value = get(key);
if (value == null) {
return null;
}
return String.valueOf(value);
}
/**
*
*/
public String getString(String key, String defaultValue) {
Object value = get(key);
if (valueIsNull(value)) {
return defaultValue;
}
return String.valueOf(value);
}
/**
* int
*/
public int getInt(String key) {
Object value = get(key);
if (valueIsNull(value)) {
return 0;
}
return Integer.parseInt(String.valueOf(value));
}
/**
* int
*/
public int getInt(String key, int defaultValue) {
Object value = get(key);
if (valueIsNull(value)) {
return defaultValue;
}
return Integer.parseInt(String.valueOf(value));
}
/**
* long
*/
public long getLong(String key) {
Object value = get(key);
if (valueIsNull(value)) {
return 0;
}
return Long.parseLong(String.valueOf(value));
}
/**
* double
*/
public double getDouble(String key) {
Object value = get(key);
if (valueIsNull(value)) {
return 0.0;
}
return Double.parseDouble(String.valueOf(value));
}
/**
* boolean
*/
public boolean getBoolean(String key) {
Object value = get(key);
if (valueIsNull(value)) {
return false;
}
return Boolean.parseBoolean(String.valueOf(value));
}
/**
* Date
*/
public Date getDateByFormat(String key, String format) {
try {
return new SimpleDateFormat(format).parse(getString(key));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Date yyyy-MM-dd
*/
public Date getDate(String key) {
return getDateByFormat(key, "yyyy-MM-dd");
}
/**
* Date yyyy-MM-dd HH:mm:ss
*/
public Date getDateTime(String key) {
return getDateByFormat(key, "yyyy-MM-dd HH:mm:ss");
}
/**
* Map
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public SoMap getMap(String key) {
Object value = get(key);
if (value == null) {
return SoMap.getSoMap();
}
if (value instanceof Map) {
return SoMap.getSoMap((Map) value);
}
if (value instanceof String) {
return SoMap.getSoMap().setJsonString((String) value);
}
throw new RuntimeException("值无法转化为SoMap: " + value);
}
/**
* ()
*/
@SuppressWarnings("unchecked")
public List<Object> getList(String key) {
Object value = get(key);
List<Object> list;
if (value == null || value.equals("")) {
list = new ArrayList<>();
} else if (value instanceof List) {
list = (List<Object>) value;
} else {
list = new ArrayList<>();
list.add(value);
}
return list;
}
/**
* ()
*/
public <T> List<T> getList(String key, Class<T> cs) {
List<Object> list = getList(key);
List<T> list2 = new ArrayList<T>();
for (Object obj : list) {
T objC = getValueByClass(obj, cs);
list2.add(objC);
}
return list2;
}
/**
* ()()
*/
public <T> List<T> getListByComma(String key, Class<T> cs) {
String listStr = getString(key);
if (listStr == null || listStr.equals("")) {
return new ArrayList<>();
}
// 开始转化
String[] arr = listStr.split(",");
List<T> list = new ArrayList<T>();
for (String str : arr) {
if (cs == int.class || cs == Integer.class || cs == long.class || cs == Long.class) {
str = str.trim();
}
T objC = getValueByClass(str, cs);
list.add(objC);
}
return list;
}
/**
* map
*/
public <T> T getModel(Class<T> cs) {
try {
return getModelByObject(cs.newInstance());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* map
*/
public <T> T getModelByObject(T obj) {
// 获取类型
Class<?> cs = obj.getClass();
// 循环复制
for (Field field : cs.getDeclaredFields()) {
try {
// 获取对象
Object value = this.get(field.getName());
if (value == null) {
continue;
}
field.setAccessible(true);
Object valueConvert = getValueByClass(value, field.getType());
field.set(obj, valueConvert);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException("属性取值出错:" + field.getName(), e);
}
}
return obj;
}
/**
*
*
* @param obj
* @param cs
* @param <T>
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getValueByClass(Object obj, Class<T> cs) {
String obj2 = String.valueOf(obj);
Object obj3;
if (cs.equals(String.class)) {
obj3 = obj2;
} else if (cs.equals(int.class) || cs.equals(Integer.class)) {
obj3 = Integer.valueOf(obj2);
} else if (cs.equals(long.class) || cs.equals(Long.class)) {
obj3 = Long.valueOf(obj2);
} else if (cs.equals(short.class) || cs.equals(Short.class)) {
obj3 = Short.valueOf(obj2);
} else if (cs.equals(byte.class) || cs.equals(Byte.class)) {
obj3 = Byte.valueOf(obj2);
} else if (cs.equals(float.class) || cs.equals(Float.class)) {
obj3 = Float.valueOf(obj2);
} else if (cs.equals(double.class) || cs.equals(Double.class)) {
obj3 = Double.valueOf(obj2);
} else if (cs.equals(boolean.class) || cs.equals(Boolean.class)) {
obj3 = Boolean.valueOf(obj2);
} else {
obj3 = (T) obj;
}
return (T) obj3;
}
// ============================= 写值 =============================
/**
* keykeyset
*/
public void setDefaultValue(String key, Object defaultValue) {
if (isNull(key)) {
set(key, defaultValue);
}
}
/**
* set
*/
public SoMap set(String key, Object value) {
// 防止敏感key
if (key.toLowerCase().equals("this")) {
return this;
}
put(key, value);
return this;
}
/**
* MapSoMap
*/
public SoMap setMap(Map<String, ?> map) {
if (map != null) {
for (String key : map.keySet()) {
this.set(key, map.get(key));
}
}
return this;
}
/**
* SoMap
*/
public SoMap setModel(Object model) {
if (model == null) {
return this;
}
Field[] fields = model.getClass().getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
boolean isStatic = Modifier.isStatic(field.getModifiers());
if (!isStatic) {
this.set(field.getName(), field.get(model));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return this;
}
/**
* jsonSoMap
*/
public SoMap setJsonString(String jsonString) {
return this.setMap(JsonUtil.parse(jsonString, Map.class));
}
// ============================= 删值 =============================
/**
* delete
*/
public SoMap delete(String key) {
remove(key);
return this;
}
/**
* valuenull
*/
public SoMap clearNull() {
Iterator<String> iterator = this.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
if (this.isNull(key)) {
iterator.remove();
this.remove(key);
}
}
return this;
}
/**
* key
*/
public SoMap clearIn(String... keys) {
List<String> keys2 = Arrays.asList(keys);
Iterator<String> iterator = this.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
if (keys2.contains(key)) {
iterator.remove();
this.remove(key);
}
}
return this;
}
/**
* key
*/
public SoMap clearNotIn(String... keys) {
List<String> keys2 = Arrays.asList(keys);
Iterator<String> iterator = this.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
if (!keys2.contains(key)) {
iterator.remove();
this.remove(key);
}
}
return this;
}
/**
* key
*/
public SoMap clearAll() {
clear();
return this;
}
// ============================= 快速构建 =============================
/**
* SoMap
*/
public static SoMap getSoMap() {
return new SoMap();
}
/**
* SoMap
*/
public static SoMap getSoMap(String key, Object value) {
return new SoMap().set(key, value);
}
/**
* SoMap
*/
public static SoMap getSoMap(Map<String, ?> map) {
return new SoMap().setMap(map);
}
/**
* SoMap
*/
public static SoMap getSoMapByModel(Object model) {
return SoMap.getSoMap().setModel(model);
}
/**
* SoMap
*/
public static List<SoMap> getSoMapByList(List<?> list) {
List<SoMap> listMap = new ArrayList<>();
for (Object model : list) {
listMap.add(getSoMapByModel(model));
}
return listMap;
}
/**
* keySoMap
*/
public SoMap cloneKeys(String... keys) {
SoMap so = new SoMap();
for (String key : keys) {
so.set(key, this.get(key));
}
return so;
}
/**
* keySoMap
*/
public SoMap cloneSoMap() {
SoMap so = new SoMap();
for (String key : this.keySet()) {
so.set(key, this.get(key));
}
return so;
}
/**
* key
*/
public SoMap toUpperCase() {
SoMap so = new SoMap();
for (String key : this.keySet()) {
so.set(key.toUpperCase(), this.get(key));
}
this.clearAll().setMap(so);
return this;
}
/**
* key
*/
public SoMap toLowerCase() {
SoMap so = new SoMap();
for (String key : this.keySet()) {
so.set(key.toLowerCase(), this.get(key));
}
this.clearAll().setMap(so);
return this;
}
/**
* key线线 (kebab-case)
*/
public SoMap toKebabCase() {
SoMap so = new SoMap();
for (String key : this.keySet()) {
so.set(wordEachKebabCase(key), this.get(key));
}
this.clearAll().setMap(so);
return this;
}
/**
* key线
*/
public SoMap toHumpCase() {
SoMap so = new SoMap();
for (String key : this.keySet()) {
so.set(wordEachBigFs(key), this.get(key));
}
this.clearAll().setMap(so);
return this;
}
/**
* key线
*/
public SoMap humpToLineCase() {
SoMap so = new SoMap();
for (String key : this.keySet()) {
so.set(wordHumpToLine(key), this.get(key));
}
this.clearAll().setMap(so);
return this;
}
// ============================= 辅助方法 =============================
/**
* keynull NULL_ELEMENT_ARRAY
*/
public boolean isNull(String key) {
return valueIsNull(get(key));
}
/**
* keyvaluenullnulltrue
*/
public boolean isContainNull(String... keys) {
for (String key : keys) {
if (this.isNull(key)) {
return true;
}
}
return false;
}
/**
* isNull()
*/
public boolean isNotNull(String key) {
return !isNull(key);
}
/**
* keyvaluenullisNotNull()
*/
public boolean has(String key) {
return !isNull(key);
}
/**
* valueSoMapnull
*/
public boolean valueIsNull(Object value) {
return NULL_ELEMENT_LIST.contains(value);
}
/**
* key
*/
public SoMap checkNull(String... keys) {
for (String key : keys) {
if (this.isNull(key)) {
throw new RuntimeException("参数" + key + "不能为空");
}
}
return this;
}
static Pattern patternNumber = Pattern.compile("[0-9]*");
/**
* key
*/
public boolean isNumber(String key) {
String value = getString(key);
if (value == null) {
return false;
}
return patternNumber.matcher(value).matches();
}
/**
* JSON
*/
public String toJsonString() {
return JsonUtil.toJsonString(this);
}
// ============================= web辅助 =============================
/**
* request
*
* @return
*/
public static SoMap getRequestSoMap() {
// 大善人SpringMVC提供的封装
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (servletRequestAttributes == null) {
throw new RuntimeException("当前线程非JavaWeb环境");
}
// 当前request
HttpServletRequest request = servletRequestAttributes.getRequest();
if (request.getAttribute("currentSoMap") == null || !(request.getAttribute("currentSoMap") instanceof SoMap)) {
initRequestSoMap(request);
}
return (SoMap) request.getAttribute("currentSoMap");
}
/**
* request SoMap
*/
private static void initRequestSoMap(HttpServletRequest request) {
SoMap soMap = new SoMap();
Map<String, String[]> parameterMap = request.getParameterMap(); // 获取所有参数
for (String key : parameterMap.keySet()) {
try {
String[] values = parameterMap.get(key); // 获得values
if (values.length == 1) {
soMap.set(key, values[0]);
} else {
soMap.set(key, Arrays.asList(values));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
request.setAttribute("currentSoMap", soMap);
}
/**
* 线JavaWeb
*
* @return
*/
public static boolean isJavaWeb() {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装
if (servletRequestAttributes == null) {
return false;
}
return true;
}
// ============================= 常见key 以下key经常用所以封装以下方便写代码 =============================
/**
* get
*/
public int getKeyPageNo() {
int pageNo = getInt("pageNo", 1);
if (pageNo <= 0) {
pageNo = 1;
}
return pageNo;
}
/**
* get
*/
public int getKeyPageSize() {
int pageSize = getInt("pageSize", 10);
if (pageSize <= 0 || pageSize > 1000) {
pageSize = 10;
}
return pageSize;
}
/**
* get
*/
public int getKeySortType() {
return getInt("sortType");
}
// ============================= 工具方法 =============================
/**
*
*
* @param list
* @param idKey idkey
* @param parentIdKey idkey
* @param childListKey key
* @return tree
*/
public static List<SoMap> listToTree(List<SoMap> list, String idKey, String parentIdKey, String childListKey) {
// 声明新的集合存储tree形数据
List<SoMap> newTreeList = new ArrayList<>();
// 声明hash-Map方便查找数据
SoMap hash = new SoMap();
// 将数组转为Object的形式key为数组中的id
for (SoMap json : list) {
hash.put(json.getString(idKey), json);
}
// 遍历结果集
for (SoMap soMap : list) {
// 单条记录
// 在hash中取出key为单条记录中pid的值
SoMap hashVp = (SoMap) hash.get(soMap.get(parentIdKey, "").toString());
// 如果记录的pid存在则说明它有父节点将她添加到孩子节点的集合中
if (hashVp != null) {
// 检查是否有child属性有则添加没有则新建
if (hashVp.get(childListKey) != null) {
@SuppressWarnings("unchecked")
List<SoMap> ch = (List<SoMap>) hashVp.get(childListKey);
ch.add(soMap);
hashVp.put(childListKey, ch);
} else {
List<SoMap> ch = new ArrayList<SoMap>();
ch.add(soMap);
hashVp.put(childListKey, ch);
}
} else {
newTreeList.add(soMap);
}
}
return newTreeList;
}
/**
* 线
*/
private static String wordEachBig(String str) {
StringBuilder newStr = new StringBuilder();
for (String s : str.split("_")) {
newStr.append(wordFirstBig(s));
}
return newStr.toString();
}
/**
* 线
*/
private static String wordEachBigFs(String str) {
return wordFirstSmall(wordEachBig(str));
}
/**
*
*/
private static String wordFirstBig(String str) {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
/**
*
*/
private static String wordFirstSmall(String str) {
return str.substring(0, 1).toLowerCase() + str.substring(1);
}
/**
* 线线
*/
private static String wordEachKebabCase(String str) {
return str.replaceAll("_", "-");
}
/**
* 线
*/
private static String wordHumpToLine(String str) {
return str.replaceAll("[A-Z]", "_$0").toLowerCase();
}
}

View File

@ -1,149 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>奇特物联认证授权</title>
<style type="text/css">
body{
min-height: 500px;
background: linear-gradient( 270deg, rgb(46 124 255) 0%, rgb(52 3 65) 100% );
}
.bgimg{
position:absolute;
width:100%;
height:100%;
}
.bgimg::before {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 60%;
overflow: hidden;
height: 80%;
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='1200' height='770' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cg%3E%3Cpath id='svg_1' d='M58.4 47.77C104.6 59.51 135.26 67.37 162.11 78.04C188.97 88.72 226.33 102.69 265.92 123.55C305.51 144.4 366.96 167.09 441.43 121.52C515.9 75.95 546.48 61.01 577.69 46.27C608.9 31.53 625.86 23.69 680.26 12.28C734.65 0.87 837.29 10.7 867.29 21.8C897.29 32.9 935.51 51.9 962.21 95.45C988.9 139.01 972.91 177.36 951.37 221.39C929.83 265.43 883.49 306 890.44 337.33C897.4 368.66 974.73 412.18 974.73 411.47C974.73 412.18 1066.36 457.62 1106.36 491.06C1146.36 524.5 1178.8 563.36 1184.03 579.63C1189.26 595.9 1200.4 622.49 1181.55 676.88C1162.71 731.26 1127.16 764.32 1115.31 778.64C1103.45 792.96 5.34 783.61 4.32 784.63C3.3 785.65 -172.34 2.38 1.13 35.04L58.4 47.77L58.4 47.77Z' fill='%23409eff'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background: #2e72f08f;
transition: all .3s ease;
}
.bgimg::after {
content: "";
width: 150px;
height: 300px;
position: absolute;
right: 0;
top: 0;
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='150' height='300' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cg%3E%3Cpath id='svg_1' d='M-0.56 -0.28C41.94 36.17 67.73 18.94 93.33 33.96C118.93 48.98 107.58 73.56 101.94 89.76C96.29 105.96 50.09 217.83 47.87 231.18C45.64 244.52 46.02 255.2 64.4 270.05C82.79 284.91 121.99 292.31 111.98 289.81C101.97 287.32 153.96 301.48 151.83 299.9C149.69 298.32 149.98 -1.36 149.71 -1.18C149.98 -1.36 -43.06 -36.74 -0.56 -0.28L-0.56 -0.28Z' fill='%23409eff'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background: #330b4e8c;
transition: all .3s ease;
}
*{margin: 0px; padding: 0px;}
.login-box{
position: absolute;
right: 100px;
width: 400px;
height: 400px;
margin: 20vh auto;
padding: 30px 50px;
background-color: #fff;
overflow: inherit;
border-radius: 10px;
}
.login-box .title{color:#333;font-size:22px;text-align:center;margin:20px 20px}
.login-box button{padding: 5px 15px; cursor: pointer; }
#loginImg{
-webkit-animation:bounce 3s 1s infinite linear normal;
-moz-animation:bounce 3s 1s infinite linear normal;}
@-webkit-keyframes bounce{
40%{-webkit-transform:translateY(-20px)}
60%{-webkit-transform:translateY(-10px)}
}
}
</style>
</head>
<body>
<div class="bgimg">
<div style="position:absolute;top:40px;left:100px;font-size:30px;color:#FFF">©奇特物联</div>
<img id="loginImg" src="http://iotkit-img.oss-cn-shenzhen.aliyuncs.com/product/KdJYpTp5ywNhmrmC/cover.png?Expires=1970997977&OSSAccessKeyId=LTAI5t8UFEH5eGrBUS5zSiof&Signature=0YV5y27iIYYL5XQ2qMeBV%2FloYFU%3D" style="position: absolute;left:100px;bottom: -20px;"/>
</div>
<div class="login-box">
<div class="title">授权确认</div> <br>
<div>
正在确认授权...
</div>
<div id="divFailed"></div>
</div>
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
<script>window.jQuery || alert('当前页面CDN服务商已宕机请将所有js包更换为本地依赖')</script>
<script type="text/javascript">
// 同意授权
function yes() {
$.ajax({
url: '/oauth2/doConfirm',
data: {
client_id: getParam('client_id'),
scope: getParam('scope')
},
dataType: 'json',
success: function(res) {
if(res.code == 200) {
location.reload(true);
} else {
// 重定向至授权失败URL
$("#divFailed").text('授权失败!');
}
},
error: function(e) {
console.log('error');
}
});
}
// 拒绝授权
function no() {
var url = joinParam(getParam('redirect_uri'), "handle=refuse&msg=用户拒绝了授权");
location.href = url;
}
// 从url中查询到指定名称的参数值
function getParam(name, defaultValue){
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == name){return pair[1];}
}
return(defaultValue == undefined ? null : defaultValue);
}
// 在url上拼接上kv参数并返回
function joinParam(url, parameStr) {
if(parameStr == null || parameStr.length == 0) {
return url;
}
var index = url.indexOf('?');
// ? 不存在
if(index == -1) {
return url + '?' + parameStr;
}
// ? 是最后一位
if(index == url.length - 1) {
return url + parameStr;
}
// ? 是其中一位
if(index > -1 && index < url.length - 1) {
// 如果最后一位是 不是&, 且 parameStr 第一位不是 &, 就增送一个 &
if(url.lastIndexOf('&') != url.length - 1 && parameStrindexOf('&') != 0) {
return url + '&' + parameStr;
} else {
return url + parameStr;
}
}
}
yes();
</script>
</body>
</html>

View File

@ -1,154 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>奇特物联登录授权</title>
<style type="text/css">
body{
min-height: 500px;
background: linear-gradient( 270deg, rgb(46 124 255) 0%, rgb(52 3 65) 100% );
}
.bgimg{
position:absolute;
width:100%;
height:100%;
}
.bgimg::before {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 60%;
overflow: hidden;
height: 80%;
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='1200' height='770' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cg%3E%3Cpath id='svg_1' d='M58.4 47.77C104.6 59.51 135.26 67.37 162.11 78.04C188.97 88.72 226.33 102.69 265.92 123.55C305.51 144.4 366.96 167.09 441.43 121.52C515.9 75.95 546.48 61.01 577.69 46.27C608.9 31.53 625.86 23.69 680.26 12.28C734.65 0.87 837.29 10.7 867.29 21.8C897.29 32.9 935.51 51.9 962.21 95.45C988.9 139.01 972.91 177.36 951.37 221.39C929.83 265.43 883.49 306 890.44 337.33C897.4 368.66 974.73 412.18 974.73 411.47C974.73 412.18 1066.36 457.62 1106.36 491.06C1146.36 524.5 1178.8 563.36 1184.03 579.63C1189.26 595.9 1200.4 622.49 1181.55 676.88C1162.71 731.26 1127.16 764.32 1115.31 778.64C1103.45 792.96 5.34 783.61 4.32 784.63C3.3 785.65 -172.34 2.38 1.13 35.04L58.4 47.77L58.4 47.77Z' fill='%23409eff'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background: #2e72f08f;
transition: all .3s ease;
}
.bgimg::after {
content: "";
width: 150px;
height: 300px;
position: absolute;
right: 0;
top: 0;
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='150' height='300' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cg%3E%3Cpath id='svg_1' d='M-0.56 -0.28C41.94 36.17 67.73 18.94 93.33 33.96C118.93 48.98 107.58 73.56 101.94 89.76C96.29 105.96 50.09 217.83 47.87 231.18C45.64 244.52 46.02 255.2 64.4 270.05C82.79 284.91 121.99 292.31 111.98 289.81C101.97 287.32 153.96 301.48 151.83 299.9C149.69 298.32 149.98 -1.36 149.71 -1.18C149.98 -1.36 -43.06 -36.74 -0.56 -0.28L-0.56 -0.28Z' fill='%23409eff'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background: #330b4e8c;
transition: all .3s ease;
}
*{margin: 0px; padding: 0px;}
.login-box{
position: absolute;
right: 100px;
width: 400px;
height: 400px;
margin: 20vh auto;
padding: 30px 50px;
background-color: #fff;
overflow: inherit;
border-radius: 10px;
}
.login-box .title{color:#333;font-size:22px;text-align:center;margin:20px 20px}
.login-box .form{list-style:none; padding:20px 20px;}
.login-box li{
padding:10px 10px;
line-height:30px;
text-align:center;
}
.login-box input{
width:100%;
line-height: 30px;
margin-bottom: 10px;
border-radius: 5px;
border: 1px solid #999;
padding:5px 10px;
}
.login-box button{
padding: 5px 15px;
cursor: pointer;
width: 100%;
line-height:30px;
border: 1px solid #5e77ff;
background-color: #5e77ff;
color: #fff;
border-radius: 5px;
}
.login-box button:hover{
background-color: #2746f1;
}
.login-box .tip{
margin-top:20px;
color:#666;
}
.tip.error{
color:red;
}
#loginImg{
-webkit-animation:bounce 3s 1s infinite linear normal;
-moz-animation:bounce 3s 1s infinite linear normal;}
@-webkit-keyframes bounce{
40%{-webkit-transform:translateY(-20px)}
60%{-webkit-transform:translateY(-10px)}
}
}
</style>
</head>
<body>
<div class="bgimg">
<div style="position:absolute;top:40px;left:100px;font-size:30px;color:#FFF">©奇特物联</div>
<img id="loginImg" src="http://iotkit-img.oss-cn-shenzhen.aliyuncs.com/product/KdJYpTp5ywNhmrmC/cover.png?Expires=1970997977&OSSAccessKeyId=LTAI5t8UFEH5eGrBUS5zSiof&Signature=0YV5y27iIYYL5XQ2qMeBV%2FloYFU%3D" style="position: absolute;left:100px;bottom: -20px;"/>
</div>
<div class="login-box">
<div class="title">登录授权</div>
<ul class="form">
<li>
<input name="name" placeholder="请输入账号"/>
</li>
<li>
<input name="pwd" type="password" placeholder="请输入密码" /> <br>
</li>
<li>
<button onclick="doLogin()">登录</button>
</li>
<li class="tip" id="liTip"></li>
<li style="color:#aaa;">
Copyright © 2021 2022 奇特物联
</li>
</ul>
</div>
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
<script>window.jQuery || alert('当前页面CDN服务商已宕机请将所有js包更换为本地依赖')</script>
<script type="text/javascript">
// 登录方法
function doLogin() {
var name=$.trim($('[name=name]').val());
var pwd=$.trim($('[name=pwd]').val());
if(!name || !pwd){
$("#liTip").addClass("error").text("账号或密码不能为空!");
}
$.ajax({
url: '/oauth2/doLogin',
data: {
name: name,
pwd: pwd
},
dataType: 'json',
success: function(res) {
if(res.code == 200) {
$("#liTip").removeClass("error").text("登录成功!");
setTimeout(function() {
location.reload(true);
}, 800);
} else {
$("#liTip").addClass("error").text(res.msg);
}
},
error: function(e) {
console.log('error');
}
});
}
</script>
</body>
</html>

View File

@ -1,13 +0,0 @@
import cc.iotkit.utils.AuthUtil;
import org.junit.Test;
public class GenPwdSecret {
@Test
public void gen() throws Exception {
//生成密码加密内容
String secret = AuthUtil.enCryptPwd("iotkitadmin");
System.out.println(secret);
System.out.println(AuthUtil.checkPwd("guest123", secret));
}
}

View File

@ -16,12 +16,6 @@
</description> </description>
<dependencies> <dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency> <dependency>
<groupId>commons-codec</groupId> <groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId> <artifactId>commons-codec</artifactId>
@ -47,12 +41,89 @@
<artifactId>commons-beanutils</artifactId> <artifactId>commons-beanutils</artifactId>
</dependency> </dependency>
<!-- Spring框架基本的核心工具 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- SpringWeb模块 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- 自定义验证注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-json</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<optional>true</optional>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>

View File

@ -1,5 +1,8 @@
package cc.iotkit.common.api; package cc.iotkit.common.api;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable; import java.io.Serializable;
/** /**
@ -8,26 +11,38 @@ import java.io.Serializable;
* @date:created in 2023/5/10 23:15 * @date:created in 2023/5/10 23:15
* @modificed by: * @modificed by:
*/ */
@EqualsAndHashCode(callSuper = true)
@Data
public class PageRequest<T> extends Request<T> implements Serializable { public class PageRequest<T> extends Request<T> implements Serializable {
private Integer pageNo = 1;
private Integer pageSize = 20;
public PageRequest() { /**
} *
*/
private Integer pageSize;
public Integer getPageNo() { /**
return this.pageNo; *
} */
private Integer pageNum;
public Integer getPageSize() { /**
return this.pageSize; *
} */
private String orderByColumn;
public void setPageNo(Integer pageNo) { /**
this.pageNo = pageNo; * descasc
} */
private String isAsc;
/**
*
*/
public static final int DEFAULT_PAGE_NUM = 1;
/**
*
*/
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
} }

View File

@ -1,31 +1,15 @@
package cc.iotkit.common.api; package cc.iotkit.common.api;
import lombok.Data;
/** /**
* @author: Longjun.Tu * @author: Longjun.Tu
* @description: * @description:
* @date:created in 2023/5/10 23:16 * @date:created in 2023/5/10 23:16
* @modificed by: * @modificed by:
*/ */
@Data
public class PageRequestEmpty { public class PageRequestEmpty {
private Integer pageNo = 1; private Integer page = 1;
private Integer pageSize = 20; private Integer size = 20;
public PageRequestEmpty() {
}
public Integer getPageNo() {
return this.pageNo;
}
public Integer getPageSize() {
return this.pageSize;
}
public void setPageNo(Integer pageNo) {
this.pageNo = pageNo;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
} }

View File

@ -1,5 +1,7 @@
package cc.iotkit.common.api; package cc.iotkit.common.api;
import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
import java.util.UUID; import java.util.UUID;
@ -9,20 +11,10 @@ import java.util.UUID;
* @date:created in 2023/5/10 23:14 * @date:created in 2023/5/10 23:14
* @modificed by: * @modificed by:
*/ */
@Data
public class Request<T> extends RequestEmpty implements Serializable { public class Request<T> extends RequestEmpty implements Serializable {
private T data; private T data;
public Request() {
}
public T getData() {
return this.data;
}
public void setData(T data) {
this.data = data;
}
public static <T> Request<T> of(T data) { public static <T> Request<T> of(T data) {
Request<T> request = new Request(); Request<T> request = new Request();
request.setData(data); request.setData(data);

View File

@ -1,5 +1,7 @@
package cc.iotkit.common.api; package cc.iotkit.common.api;
import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
import java.util.UUID; import java.util.UUID;
@ -9,34 +11,14 @@ import java.util.UUID;
* @date:created in 2023/5/10 23:12 * @date:created in 2023/5/10 23:12
* @modificed by: * @modificed by:
*/ */
@Data
public class RequestEmpty implements Serializable { public class RequestEmpty implements Serializable {
private String language; private String language;
@Deprecated @Deprecated
private String requestId; private String requestId;
public RequestEmpty() {
}
public String getLanguage() {
return this.language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getRequestId() {
return this.requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
public static RequestEmpty of() { public static RequestEmpty of() {
RequestEmpty request = new RequestEmpty(); RequestEmpty request = new RequestEmpty();
request.setRequestId(UUID.randomUUID().toString()); request.setRequestId(UUID.randomUUID().toString());

View File

@ -0,0 +1,15 @@
package cc.iotkit.common.api;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Response {
private int code;
private String message;
private Object data;
private long timestamp;
}

View File

@ -0,0 +1,42 @@
package cc.iotkit.common.config;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
import java.io.IOException;
/**
* JS
*
* @author Lion Li
*/
@JacksonStdImpl
public class BigNumberSerializer extends NumberSerializer {
/**
* JS Number.MAX_SAFE_INTEGER Number.MIN_SAFE_INTEGER
*/
private static final long MAX_SAFE_INTEGER = 9007199254740991L;
private static final long MIN_SAFE_INTEGER = -9007199254740991L;
/**
*
*/
public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class);
public BigNumberSerializer(Class<? extends Number> rawType) {
super(rawType);
}
@Override
public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
// 超出范围 序列化位字符串
if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
super.serialize(value, gen, provider);
} else {
gen.writeString(value.toString());
}
}
}

View File

@ -0,0 +1,46 @@
package cc.iotkit.common.config;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
/**
* jackson
*
* @author Lion Li
*/
@Slf4j
@AutoConfiguration(before = JacksonAutoConfiguration.class)
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> {
// 全局配置序列化返回 JSON 处理
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE);
javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE);
javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE);
javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
builder.modules(javaTimeModule);
builder.timeZone(TimeZone.getDefault());
log.info("初始化 jackson 配置");
};
}
}

View File

@ -0,0 +1,25 @@
package cc.iotkit.common.constant;
/**
* key
*
* @author Lion Li
*/
public interface CacheConstants {
/**
* 线 redis key
*/
String ONLINE_TOKEN_KEY = "online_tokens:";
/**
* cache key
*/
String SYS_CONFIG_KEY = "sys_config:";
/**
* cache key
*/
String SYS_DICT_KEY = "sys_dict:";
}

View File

@ -0,0 +1,63 @@
package cc.iotkit.common.constant;
/**
*
* <p>
* key cacheNames#ttl#maxIdleTime#maxSize
* <p>
* ttl 0 0
* maxIdleTime LRU 0 0
* maxSize LRU 0 0
* <p>
* : test#60stest#0#60stest#0#1m#1000test#1h#0#500
*
* @author Lion Li
*/
public interface CacheNames {
/**
*
*/
String DEMO_CACHE = "demo:cache#60s#10m#20";
/**
*
*/
String SYS_CONFIG = "sys_config";
/**
*
*/
String SYS_DICT = "sys_dict";
/**
*
*/
String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
/**
*
*/
String SYS_USER_NAME = "sys_user_name#30d";
/**
*
*/
String SYS_DEPT = "sys_dept#30d";
/**
* OSS
*/
String SYS_OSS = "sys_oss#30d";
/**
* OSS
*/
String SYS_OSS_CONFIG = "sys_oss_config";
/**
* 线
*/
String ONLINE_TOKEN = "online_tokens";
}

View File

@ -1,16 +1,83 @@
/* package cc.iotkit.common.constant;
* +----------------------------------------------------------------------
* | Copyright (c) 2021-2022 All rights reserved.
* +----------------------------------------------------------------------
* | Licensed
* +----------------------------------------------------------------------
* | Author: xw2sy@163.com
* +----------------------------------------------------------------------
*/
package cc.iotkit.common;
/**
*
*
* @author ruoyi
*/
public interface Constants { public interface Constants {
/**
* UTF-8
*/
String UTF8 = "UTF-8";
/**
* GBK
*/
String GBK = "GBK";
/**
* www
*/
String WWW = "www.";
/**
* http
*/
String HTTP = "http://";
/**
* https
*/
String HTTPS = "https://";
/**
*
*/
String SUCCESS = "0";
/**
*
*/
String FAIL = "1";
/**
*
*/
String LOGIN_SUCCESS = "Success";
/**
*
*/
String LOGOUT = "Logout";
/**
*
*/
String REGISTER = "Register";
/**
*
*/
String LOGIN_FAIL = "Error";
/**
*
*/
Integer CAPTCHA_EXPIRATION = 2;
/**
*
*/
String TOKEN = "token";
/**
* id
*/
Long TOP_PARENT_ID = 0L;
String PRODUCT_SECRET = "xdkKUymrEGSCYWswqCvSPyRSFvH5j7CU"; String PRODUCT_SECRET = "xdkKUymrEGSCYWswqCvSPyRSFvH5j7CU";
String ACCOUNT_SECRET = "3n1z33kzvpgz1foijpkepyd3e8tw84us"; String ACCOUNT_SECRET = "3n1z33kzvpgz1foijpkepyd3e8tw84us";
@ -224,3 +291,4 @@ public interface Constants {
String DEVICE_SUBSCRIBE_TOPIC = "^/sys/.+/.+/c/#$"; String DEVICE_SUBSCRIBE_TOPIC = "^/sys/.+/.+/c/#$";
} }
} }

View File

@ -0,0 +1,34 @@
package cc.iotkit.common.constant;
/**
* key (key)
*
* @author Lion Li
*/
public interface GlobalConstants {
/**
* redis key (key)
*/
String GLOBAL_REDIS_KEY = "global:";
/**
* redis key
*/
String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:";
/**
* redis key
*/
String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:";
/**
* redis key
*/
String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:";
/**
* redis key
*/
String PWD_ERR_CNT_KEY = GLOBAL_REDIS_KEY + "pwd_err_cnt:";
}

View File

@ -0,0 +1,93 @@
package cc.iotkit.common.constant;
/**
*
*
* @author Lion Li
*/
public interface HttpStatus {
/**
*
*/
int SUCCESS = 200;
/**
*
*/
int CREATED = 201;
/**
*
*/
int ACCEPTED = 202;
/**
*
*/
int NO_CONTENT = 204;
/**
*
*/
int MOVED_PERM = 301;
/**
*
*/
int SEE_OTHER = 303;
/**
*
*/
int NOT_MODIFIED = 304;
/**
*
*/
int BAD_REQUEST = 400;
/**
*
*/
int UNAUTHORIZED = 401;
/**
* 访
*/
int FORBIDDEN = 403;
/**
*
*/
int NOT_FOUND = 404;
/**
* http
*/
int BAD_METHOD = 405;
/**
*
*/
int CONFLICT = 409;
/**
*
*/
int UNSUPPORTED_TYPE = 415;
/**
*
*/
int ERROR = 500;
/**
*
*/
int NOT_IMPLEMENTED = 501;
/**
*
*/
int WARN = 601;
}

View File

@ -0,0 +1,45 @@
package cc.iotkit.common.constant;
/**
*
*
* @author Lion Li
*/
public interface TenantConstants {
/**
*
*/
String NORMAL = "0";
/**
*
*/
String DISABLE = "1";
/**
* ID
*/
Long SUPER_ADMIN_ID = 1L;
/**
* roleKey
*/
String SUPER_ADMIN_ROLE_KEY = "superadmin";
/**
* roleKey
*/
String TENANT_ADMIN_ROLE_KEY = "admin";
/**
*
*/
String TENANT_ADMIN_ROLE_NAME = "管理员";
/**
* ID
*/
String DEFAULT_TENANT_ID = "000000";
}

View File

@ -0,0 +1,132 @@
package cc.iotkit.common.constant;
/**
*
*
* @author ruoyi
*/
public interface UserConstants {
/**
*
*/
String SYS_USER = "SYS_USER";
/**
*
*/
String NORMAL = "0";
/**
*
*/
String EXCEPTION = "1";
/**
*
*/
String USER_NORMAL = "0";
/**
*
*/
String USER_DISABLE = "1";
/**
*
*/
String ROLE_NORMAL = "0";
/**
*
*/
String ROLE_DISABLE = "1";
/**
*
*/
String DEPT_NORMAL = "0";
/**
*
*/
String DEPT_DISABLE = "1";
/**
*
*/
String DICT_NORMAL = "0";
/**
*
*/
String YES = "Y";
/**
*
*/
String YES_FRAME = "0";
/**
*
*/
String NO_FRAME = "1";
/**
*
*/
String MENU_NORMAL = "0";
/**
*
*/
String MENU_DISABLE = "1";
/**
*
*/
String TYPE_DIR = "M";
/**
*
*/
String TYPE_MENU = "C";
/**
*
*/
String TYPE_BUTTON = "F";
/**
* Layout
*/
String LAYOUT = "Layout";
/**
* ParentView
*/
String PARENT_VIEW = "ParentView";
/**
* InnerLink
*/
String INNER_LINK = "InnerLink";
/**
*
*/
int USERNAME_MIN_LENGTH = 2;
int USERNAME_MAX_LENGTH = 20;
/**
*
*/
int PASSWORD_MIN_LENGTH = 5;
int PASSWORD_MAX_LENGTH = 20;
/**
* ID
*/
Long SUPER_ADMIN_ID = 1L;
}

View File

@ -0,0 +1,40 @@
package cc.iotkit.common.domain.model;
import lombok.Data;
import java.util.Date;
/**
* Entity
*
* @author Lion Li
*/
@Data
public class BaseEntity {
/**
*
*/
private Long createDept;
/**
*
*/
private Long createBy;
/**
*
*/
private Date createTime;
/**
*
*/
private Long updateBy;
/**
*
*/
private Date updateTime;
}

View File

@ -0,0 +1,47 @@
package cc.iotkit.common.domain.model;
import cc.iotkit.common.constant.UserConstants;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
/**
*
*
* @author Lion Li
*/
@Data
public class LoginBody {
/**
* ID
*/
@NotBlank(message = "{tenant.number.not.blank}")
private String tenantId;
/**
*
*/
@NotBlank(message = "{user.username.not.blank}")
@Length(min = UserConstants.USERNAME_MIN_LENGTH, max = UserConstants.USERNAME_MAX_LENGTH, message = "{user.username.length.valid}")
private String username;
/**
*
*/
@NotBlank(message = "{user.password.not.blank}")
@Length(min = UserConstants.PASSWORD_MIN_LENGTH, max = UserConstants.PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
private String password;
/**
*
*/
private String code;
/**
*
*/
private String uuid;
}

View File

@ -0,0 +1,123 @@
package cc.iotkit.common.domain.model;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.common.core.domain.dto.RoleDTO;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
/**
*
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
public class LoginUser implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
private String tenantId;
/**
* ID
*/
private Long userId;
/**
* ID
*/
private Long deptId;
/**
*
*/
private String deptName;
/**
*
*/
private String token;
/**
*
*/
private String userType;
/**
*
*/
private Long loginTime;
/**
*
*/
private Long expireTime;
/**
* IP
*/
private String ipaddr;
/**
*
*/
private String loginLocation;
/**
*
*/
private String browser;
/**
*
*/
private String os;
/**
*
*/
private Set<String> menuPermission;
/**
*
*/
private Set<String> rolePermission;
/**
*
*/
private String username;
/**
*
*/
private List<RoleDTO> roles;
/**
* ID
*/
private Long roleId;
/**
* id
*/
public String getLoginId() {
if (userType == null) {
throw new IllegalArgumentException("用户类型不能为空");
}
if (userId == null) {
throw new IllegalArgumentException("用户ID不能为空");
}
return userType + ":" + userId;
}
}

View File

@ -0,0 +1,17 @@
package cc.iotkit.common.domain.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
*
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RegisterBody extends LoginBody {
private String userType;
}

View File

@ -0,0 +1,25 @@
package cc.iotkit.common.domain.vo;
import lombok.Data;
import java.util.List;
/**
*
* @author sjg
*/
@Data
public class PagedDataVo<T> {
/**
*
*/
private long total;
/**
*
*/
private List<T> rows;
}

View File

@ -0,0 +1,32 @@
package cc.iotkit.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
*
*
* @author Lion Li
*/
@Getter
@AllArgsConstructor
public enum DeviceType {
/**
* pc
*/
PC("pc"),
/**
* app
*/
APP("app"),
/**
*
*/
XCX("xcx");
private final String device;
}

View File

@ -3,25 +3,23 @@ package cc.iotkit.common.enums;
/** /**
* @author tfd * @author tfd
* *
*
*/ */
public enum ErrCode implements IEnum{ public enum ErrCode implements IEnum {
/** /**
* *
* */ */
PARAMS_EXCEPTION(00000000, "参数异常"), PARAMS_EXCEPTION(10000001, "参数异常"),
SYSTEM_EXCEPTION(00000000, "系统异常"), SYSTEM_EXCEPTION(10000002, "系统异常"),
UNKNOWN_EXCEPTION(00000000, "未知异常"), UNKNOWN_EXCEPTION(10000003, "未知异常"),
SYSTEM_ERROR(00000000, "服务器内部错误"), SYSTEM_ERROR(10000004, "服务器内部错误"),
METHOD_NOT_ALLOWED(00000000, "请求方法不支持"), METHOD_NOT_ALLOWED(10000005, "请求方法不支持"),
NOT_FOUND(00000000, "请求资源不存在"), NOT_FOUND(10000006, "请求资源不存在"),
FORBIDDEN(00000000, "请求被拒绝"), FORBIDDEN(10000007, "请求被拒绝"),
UNAUTHORIZED_EXCEPTION(00000000, "未授权访问"), UNAUTHORIZED_EXCEPTION(10000008, "未授权访问"),
/** /**
* *
* */ */
GET_COMPONENT_INSTANCE_ERROR(00000000, "获取通讯组件实例失败"), GET_COMPONENT_INSTANCE_ERROR(00000000, "获取通讯组件实例失败"),
GET_COMPONENT_SCRIPT_ERROR(00000000, "获取通讯组件脚本失败"), GET_COMPONENT_SCRIPT_ERROR(00000000, "获取通讯组件脚本失败"),
GET_CONVERT_ERROR(00000000, "获取转换器失败"), GET_CONVERT_ERROR(00000000, "获取转换器失败"),
@ -49,7 +47,7 @@ public enum ErrCode implements IEnum{
/** /**
* *
* */ */
TASK_NOT_SUPPORT_RENEW(00000000, "任务不支持续订"), TASK_NOT_SUPPORT_RENEW(00000000, "任务不支持续订"),
GROUP_ALREADY(00000000, "分组已经存在"), GROUP_ALREADY(00000000, "分组已经存在"),
GROUP_NOT_FOUND(00000000, "分组不存在"), GROUP_NOT_FOUND(00000000, "分组不存在"),

View File

@ -0,0 +1,37 @@
package cc.iotkit.common.enums;
import cc.iotkit.common.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
*
*
* @author Lion Li
*/
@Getter
@AllArgsConstructor
public enum UserType {
/**
* pc
*/
SYS_USER("sys_user"),
/**
* app
*/
APP_USER("app_user");
private final String userType;
public static UserType getUserType(String str) {
for (UserType value : values()) {
if (StringUtils.contains(str, value.getUserType())) {
return value;
}
}
throw new RuntimeException("'UserType' not found By " + str);
}
}

View File

@ -10,42 +10,54 @@
package cc.iotkit.common.exception; package cc.iotkit.common.exception;
import cc.iotkit.common.enums.ErrCode; import cc.iotkit.common.enums.ErrCode;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
public class BizException extends RuntimeException{ @EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BizException extends RuntimeException {
private int code; /**
*
*/
private String module;
/**
*
*/
private String code;
/**
*
*/
private String message; private String message;
public BizException() {
}
public BizException(String message) { public BizException(String message) {
super(message); super(message);
this.code = ErrCode.SYSTEM_EXCEPTION.getValue();
} }
/** /**
* *
* *
* @param ErrCode * @param errCode
*/ */
public BizException(ErrCode ErrCode) { public BizException(ErrCode errCode) {
this.message = ErrCode.getValue(); this.message = errCode.getValue();
} }
public BizException(ErrCode ErrCode, Throwable cause) { public BizException(ErrCode errCode, Throwable cause) {
super(cause); super(cause);
this.message = ErrCode.getValue(); this.message = errCode.getValue();
} }
/** public BizException(ErrCode errCode, String message) {
*
*
* @param code
* @param message
*/
public BizException(int code, String message) {
this.code = code;
this.message = message; this.message = message;
this.code = errCode.getValue();
} }
public BizException(String message, Throwable cause) { public BizException(String message, Throwable cause) {
@ -56,20 +68,4 @@ public class BizException extends RuntimeException{
super(cause); super(cause);
} }
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
} }

View File

@ -0,0 +1,40 @@
package cc.iotkit.common.exception;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
*
*
* @author sjg
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class ViewException extends RuntimeException {
public static final int CODE_FAILED = 0;
public static final int CODE_WARN = 1;
private int code;
private String message;
private Object data;
public ViewException() {
}
public ViewException(String message) {
super(message);
}
public ViewException(int code, String message) {
this.code = code;
this.message = message;
}
public ViewException(int code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
}

View File

@ -0,0 +1,18 @@
package cc.iotkit.common.service;
/**
*
*
* @author Lion Li
*/
public interface ConfigService {
/**
* key
*
* @param configKey key
* @return
*/
String getConfigValue(String configKey);
}

View File

@ -0,0 +1,18 @@
package cc.iotkit.common.service;
/**
*
*
* @author Lion Li
*/
public interface DeptService {
/**
* ID
*
* @param deptIds ID
* @return
*/
String selectDeptNameByIds(String deptIds);
}

View File

@ -0,0 +1,57 @@
package cc.iotkit.common.service;
/**
*
*
* @author Lion Li
*/
public interface DictService {
/**
*
*/
String SEPARATOR = ",";
/**
*
*
* @param dictType
* @param dictValue
* @return
*/
default String getDictLabel(String dictType, String dictValue) {
return getDictLabel(dictType, dictValue, SEPARATOR);
}
/**
*
*
* @param dictType
* @param dictLabel
* @return
*/
default String getDictValue(String dictType, String dictLabel) {
return getDictValue(dictType, dictLabel, SEPARATOR);
}
/**
*
*
* @param dictType
* @param dictValue
* @param separator
* @return
*/
String getDictLabel(String dictType, String dictValue, String separator);
/**
*
*
* @param dictType
* @param dictLabel
* @param separator
* @return
*/
String getDictValue(String dictType, String dictLabel, String separator);
}

View File

@ -0,0 +1,18 @@
package cc.iotkit.common.service;
/**
* OSS
*
* @author Lion Li
*/
public interface OssService {
/**
* ossIdurl
*
* @param ossIds ossId
* @return url
*/
String selectUrlByIds(String ossIds);
}

View File

@ -0,0 +1,18 @@
package cc.iotkit.common.service;
/**
*
*
* @author Lion Li
*/
public interface UserService {
/**
* ID
*
* @param userId ID
* @return
*/
String selectUserNameById(Long userId);
}

View File

@ -1,44 +0,0 @@
/*
* +----------------------------------------------------------------------
* | Copyright (c) 2021-2022 All rights reserved.
* +----------------------------------------------------------------------
* | Licensed
* +----------------------------------------------------------------------
* | Author: xw2sy@163.com
* +----------------------------------------------------------------------
*/
package cc.iotkit.common.utils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.SneakyThrows;
public final class JsonUtil {
private final static ObjectMapper MAPPER = new ObjectMapper()
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
@SneakyThrows
public static String toJsonString(Object obj) {
return MAPPER.writeValueAsString(obj);
}
@SneakyThrows
public static <T> T parse(String json, Class<T> cls) {
return MAPPER.readValue(json, cls);
}
@SneakyThrows
public static <T> T parse(String json, TypeReference<T> type) {
return MAPPER.readValue(json, type);
}
@SneakyThrows
public static JsonNode parse(String json) {
return MAPPER.readTree(json);
}
}

View File

@ -0,0 +1,109 @@
package cc.iotkit.common.utils;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* JSON
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JsonUtils {
private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class);
public static ObjectMapper getObjectMapper() {
return OBJECT_MAPPER;
}
public static String toJsonString(Object object) {
if (ObjectUtil.isNull(object)) {
return null;
}
try {
return OBJECT_MAPPER.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(String text, Class<T> clazz) {
if (StringUtils.isEmpty(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
if (ArrayUtil.isEmpty(bytes)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(bytes, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, typeReference);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static Dict parseMap(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class));
} catch (MismatchedInputException e) {
// 类型不匹配说明不是json
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static List<Dict> parseArrayMap(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> List<T> parseArray(String text, Class<T> clazz) {
if (StringUtils.isEmpty(text)) {
return new ArrayList<>();
}
try {
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,92 @@
package cc.iotkit.common.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import io.github.linpeilie.Converter;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
/**
* Mapstruct
* <p><a href="https://mapstruct.plus/guide/quick-start">mapstruct-plus</a></p>
*
* @author Michelle.Chung
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MapstructUtils {
private final static Converter CONVERTER = SpringUtils.getBean(Converter.class);
/**
* T desc
*
* @param source
* @param desc
* @return desc
*/
public static <T, V> V convert(T source, Class<V> desc) {
if (ObjectUtil.isNull(source)) {
return null;
}
if (ObjectUtil.isNull(desc)) {
return null;
}
return CONVERTER.convert(source, desc);
}
/**
* T desc desc
*
* @param source
* @param desc
* @return desc
*/
public static <T, V> V convert(T source, V desc) {
if (ObjectUtil.isNull(source)) {
return null;
}
if (ObjectUtil.isNull(desc)) {
return null;
}
return CONVERTER.convert(source, desc);
}
/**
* T desc
*
* @param sourceList
* @param desc
* @return desc
*/
public static <T, V> List<V> convert(List<T> sourceList, Class<V> desc) {
if (ObjectUtil.isNull(sourceList)) {
return null;
}
if (CollUtil.isEmpty(sourceList)) {
return CollUtil.newArrayList();
}
return CONVERTER.convert(sourceList, desc);
}
/**
* Map beanClass
*
* @param map
* @param beanClass bean
* @return bean
*/
public static <T> T convert(Map<String, Object> map, Class<T> beanClass) {
if (MapUtil.isEmpty(map)) {
return null;
}
if (ObjectUtil.isNull(beanClass)) {
return null;
}
return CONVERTER.convert(map, beanClass);
}
}

View File

@ -0,0 +1,193 @@
package cc.iotkit.common.utils;
import cn.hutool.core.convert.Convert;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.http.HttpStatus;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
*
*
* @author ruoyi
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ServletUtils extends JakartaServletUtil {
/**
* String
*/
public static String getParameter(String name) {
return getRequest().getParameter(name);
}
/**
* String
*/
public static String getParameter(String name, String defaultValue) {
return Convert.toStr(getRequest().getParameter(name), defaultValue);
}
/**
* Integer
*/
public static Integer getParameterToInt(String name) {
return Convert.toInt(getRequest().getParameter(name));
}
/**
* Integer
*/
public static Integer getParameterToInt(String name, Integer defaultValue) {
return Convert.toInt(getRequest().getParameter(name), defaultValue);
}
/**
* Boolean
*/
public static Boolean getParameterToBool(String name) {
return Convert.toBool(getRequest().getParameter(name));
}
/**
* Boolean
*/
public static Boolean getParameterToBool(String name, Boolean defaultValue) {
return Convert.toBool(getRequest().getParameter(name), defaultValue);
}
/**
*
*
* @param request {@link ServletRequest}
* @return Map
*/
public static Map<String, String[]> getParams(ServletRequest request) {
final Map<String, String[]> map = request.getParameterMap();
return Collections.unmodifiableMap(map);
}
/**
*
*
* @param request {@link ServletRequest}
* @return Map
*/
public static Map<String, String> getParamMap(ServletRequest request) {
Map<String, String> params = new HashMap<>();
for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
params.put(entry.getKey(), StringUtils.join(entry.getValue(), StringUtils.SEPARATOR));
}
return params;
}
/**
* request
*/
public static HttpServletRequest getRequest() {
return getRequestAttributes().getRequest();
}
/**
* response
*/
public static HttpServletResponse getResponse() {
return getRequestAttributes().getResponse();
}
/**
* session
*/
public static HttpSession getSession() {
return getRequest().getSession();
}
public static ServletRequestAttributes getRequestAttributes() {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) attributes;
}
/**
*
*
* @param response
* @param string
*/
public static void renderString(HttpServletResponse response, String string) {
try {
response.setStatus(HttpStatus.HTTP_OK);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
response.getWriter().print(string);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Ajax
*
* @param request
*/
public static boolean isAjaxRequest(HttpServletRequest request) {
String accept = request.getHeader("accept");
if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) {
return true;
}
String xRequestedWith = request.getHeader("X-Requested-With");
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
return true;
}
String uri = request.getRequestURI();
if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
return true;
}
String ajax = request.getParameter("__ajax");
return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml");
}
public static String getClientIP() {
return getClientIP((HttpServletRequest)getRequest());
}
/**
*
*
* @param str
* @return
*/
public static String urlEncode(String str) {
return URLEncoder.encode(str, StandardCharsets.UTF_8);
}
/**
*
*
* @param str
* @return
*/
public static String urlDecode(String str) {
return URLDecoder.decode(str, StandardCharsets.UTF_8);
}
}

View File

@ -0,0 +1,62 @@
package cc.iotkit.common.utils;
import cn.hutool.extra.spring.SpringUtil;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* spring
*
* @author Lion Li
*/
@Component
public final class SpringUtils extends SpringUtil {
/**
* BeanFactorybeantrue
*/
public static boolean containsBean(String name) {
return getBeanFactory().containsBean(name);
}
/**
* beansingletonprototype
* beanNoSuchBeanDefinitionException
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().isSingleton(name);
}
/**
* @return Class
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().getType(name);
}
/**
* beanbean
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().getAliases(name);
}
/**
* aop
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker) {
return (T) AopContext.currentProxy();
}
/**
* spring
*/
public static ApplicationContext context() {
return getApplicationContext();
}
}

View File

@ -0,0 +1,254 @@
package cc.iotkit.common.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* stream
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StreamUtils {
/**
* collection
*
* @param collection
* @param function
* @return list
*/
public static <E> List<E> filter(Collection<E> collection, Predicate<E> function) {
if (CollUtil.isEmpty(collection)) {
return CollUtil.newArrayList();
}
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
return collection.stream().filter(function).collect(Collectors.toList());
}
/**
* collection
*
* @param collection
* @param function
* @return list
*/
public static <E> String join(Collection<E> collection, Function<E, String> function) {
return join(collection, function, StringUtils.SEPARATOR);
}
/**
* collection
*
* @param collection
* @param function
* @param delimiter
* @return list
*/
public static <E> String join(Collection<E> collection, Function<E, String> function, CharSequence delimiter) {
if (CollUtil.isEmpty(collection)) {
return StringUtils.EMPTY;
}
return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter));
}
/**
* collection
*
* @param collection
* @param comparing
* @return list
*/
public static <E> List<E> sorted(Collection<E> collection, Comparator<E> comparing) {
if (CollUtil.isEmpty(collection)) {
return CollUtil.newArrayList();
}
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
return collection.stream().sorted(comparing).collect(Collectors.toList());
}
/**
* collectionmap<br>
* <B>{@code Collection<V> ----> Map<K,V>}</B>
*
* @param collection
* @param key VKlambda
* @param <V> collection
* @param <K> mapkey
* @return map
*/
public static <V, K> Map<K, V> toIdentityMap(Collection<V> collection, Function<V, K> key) {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection.stream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l));
}
/**
* Collectionmap(valuecollection)<br>
* <B>{@code Collection<E> -----> Map<K,V> }</B>
*
* @param collection
* @param key EKlambda
* @param value EVlambda
* @param <E> collection
* @param <K> mapkey
* @param <V> mapvalue
* @return map
*/
public static <E, K, V> Map<K, V> toMap(Collection<E> collection, Function<E, K> key, Function<E, V> value) {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection.stream().collect(Collectors.toMap(key, value, (l, r) -> l));
}
/**
* collection(id)map<br>
* <B>{@code Collection<E> -------> Map<K,List<E>> } </B>
*
* @param collection
* @param key
* @param <E> collection
* @param <K> mapkey
* @return map
*/
public static <E, K> Map<K, List<E>> groupByKey(Collection<E> collection, Function<E, K> key) {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection
.stream()
.collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList()));
}
/**
* collection(id,id)map<br>
* <B>{@code Collection<E> ---> Map<T,Map<U,List<E>>> } </B>
*
* @param collection
* @param key1
* @param key2
* @param <E>
* @param <K> mapkey
* @param <U> mapkey
* @return map
*/
public static <E, K, U> Map<K, Map<U, List<E>>> groupBy2Key(Collection<E> collection, Function<E, K> key1, Function<E, U> key2) {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection
.stream()
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList())));
}
/**
* collection(id,id)map<br>
* <B>{@code Collection<E> ---> Map<T,Map<U,E>> } </B>
*
* @param collection
* @param key1
* @param key2
* @param <T> mapkey
* @param <U> mapkey
* @param <E> collection
* @return map
*/
public static <E, T, U> Map<T, Map<U, E>> group2Map(Collection<E> collection, Function<E, T> key1, Function<E, U> key2) {
if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) {
return MapUtil.newHashMap();
}
return collection
.stream()
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l)));
}
/**
* collectionList<br>
* <B>{@code Collection<E> ------> List<T> } </B>
*
* @param collection
* @param function collectionlistlambda
* @param <E> collection
* @param <T> List
* @return list
*/
public static <E, T> List<T> toList(Collection<E> collection, Function<E, T> function) {
if (CollUtil.isEmpty(collection)) {
return CollUtil.newArrayList();
}
return collection
.stream()
.map(function)
.filter(Objects::nonNull)
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
.collect(Collectors.toList());
}
/**
* collectionSet<br>
* <B>{@code Collection<E> ------> Set<T> } </B>
*
* @param collection
* @param function collectionsetlambda
* @param <E> collection
* @param <T> Set
* @return Set
*/
public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function) {
if (CollUtil.isEmpty(collection) || function == null) {
return CollUtil.newHashSet();
}
return collection
.stream()
.map(function)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
/**
* keymap
*
* @param map1 map
* @param map2 map
* @param merge lambdakey value1 value2,value
* @param <K> mapkey
* @param <X> mapvalue
* @param <Y> mapvalue
* @param <V> mapvalue
* @return map
*/
public static <K, X, Y, V> Map<K, V> merge(Map<K, X> map1, Map<K, Y> map2, BiFunction<X, Y, V> merge) {
if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) {
return MapUtil.newHashMap();
} else if (MapUtil.isEmpty(map1)) {
map1 = MapUtil.newHashMap();
} else if (MapUtil.isEmpty(map2)) {
map2 = MapUtil.newHashMap();
}
Set<K> key = new HashSet<>();
key.addAll(map1.keySet());
key.addAll(map2.keySet());
Map<K, V> map = new HashMap<>();
for (K t : key) {
X x = map1.get(t);
Y y = map2.get(t);
V z = merge.apply(x, y);
if (z != null) {
map.put(t, z);
}
}
return map;
}
}

View File

@ -0,0 +1,321 @@
package cc.iotkit.common.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.util.AntPathMatcher;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
*
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StringUtils extends org.apache.commons.lang3.StringUtils {
public static final String SEPARATOR = ",";
/**
*
*
* @param str defaultValue value
* @return value
*/
public static String blankToDefault(String str, String defaultValue) {
return StrUtil.blankToDefault(str, defaultValue);
}
/**
* *
*
* @param str String
* @return true false
*/
public static boolean isEmpty(String str) {
return StrUtil.isEmpty(str);
}
/**
* *
*
* @param str String
* @return true false
*/
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
/**
*
*/
public static String trim(String str) {
return StrUtil.trim(str);
}
/**
*
*
* @param str
* @param start
* @return
*/
public static String substring(final String str, int start) {
return substring(str, start, str.length());
}
/**
*
*
* @param str
* @param start
* @param end
* @return
*/
public static String substring(final String str, int start, int end) {
return StrUtil.sub(str, start, end);
}
/**
* , {} <br>
* {} <br>
* {} 使 \\ { {} \ 使 \\\\ <br>
* <br>
* 使format("this is {} for {}", "a", "b") -> this is a for b<br>
* {} format("this is \\{} for {}", "a", "b") -> this is {} for a<br>
* \ format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
*
* @param template {}
* @param params
* @return
*/
public static String format(String template, Object... params) {
return StrUtil.format(template, params);
}
/**
* http(s)://开头
*
* @param link
* @return
*/
public static boolean ishttp(String link) {
return Validator.isUrl(link);
}
/**
* set
*
* @param str
* @param sep
* @return set
*/
public static Set<String> str2Set(String str, String sep) {
return new HashSet<>(str2List(str, sep, true, false));
}
/**
* list
*
* @param str
* @param sep
* @param filterBlank
* @param trim
* @return list
*/
public static List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
List<String> list = new ArrayList<>();
if (isEmpty(str)) {
return list;
}
// 过滤空白字符串
if (filterBlank && isBlank(str)) {
return list;
}
String[] split = str.split(sep);
for (String string : split) {
if (filterBlank && isBlank(string)) {
continue;
}
if (trim) {
string = trim(string);
}
list.add(string);
}
return list;
}
/**
*
*
* @param cs
* @param searchCharSequences
* @return
*/
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
return StrUtil.containsAnyIgnoreCase(cs, searchCharSequences);
}
/**
* 线
*/
public static String toUnderScoreCase(String str) {
return StrUtil.toUnderlineCase(str);
}
/**
*
*
* @param str
* @param strs
* @return true
*/
public static boolean inStringIgnoreCase(String str, String... strs) {
return StrUtil.equalsAnyIgnoreCase(str, strs);
}
/**
* 线线 HELLO_WORLD->HelloWorld
*
* @param name 线
* @return
*/
public static String convertToCamelCase(String name) {
return StrUtil.upperFirst(StrUtil.toCamelCase(name));
}
/**
* user_name->userName
*/
public static String toCamelCase(String s) {
return StrUtil.toCamelCase(s);
}
/**
*
*
* @param str
* @param strs
* @return
*/
public static boolean matches(String str, List<String> strs) {
if (isEmpty(str) || CollUtil.isEmpty(strs)) {
return false;
}
for (String pattern : strs) {
if (isMatch(pattern, str)) {
return true;
}
}
return false;
}
/**
* url:
* ? ;
* * ;
* ** ;
*
* @param pattern
* @param url url
*/
public static boolean isMatch(String pattern, String url) {
AntPathMatcher matcher = new AntPathMatcher();
return matcher.match(pattern, url);
}
/**
* 0使size size
*
* @param num
* @param size
* @return
*/
public static String padl(final Number num, final int size) {
return padl(num.toString(), size, '0');
}
/**
* ssizesize
*
* @param s
* @param size
* @param c
* @return
*/
public static String padl(final String s, final int size, final char c) {
final StringBuilder sb = new StringBuilder(size);
if (s != null) {
final int len = s.length();
if (s.length() <= size) {
sb.append(String.valueOf(c).repeat(size - len));
sb.append(s);
} else {
return s.substring(len - size, len);
}
} else {
sb.append(String.valueOf(c).repeat(Math.max(0, size)));
}
return sb.toString();
}
/**
* ()
*
* @param str
* @return
*/
public static List<String> splitList(String str) {
return splitTo(str, Convert::toStr);
}
/**
*
*
* @param str
* @param separator
* @return
*/
public static List<String> splitList(String str, String separator) {
return splitTo(str, separator, Convert::toStr);
}
/**
* ()
*
* @param str
* @param mapper
* @return
*/
public static <T> List<T> splitTo(String str, Function<? super Object, T> mapper) {
return splitTo(str, SEPARATOR, mapper);
}
/**
*
*
* @param str
* @param separator
* @param mapper
* @return
*/
public static <T> List<T> splitTo(String str, String separator, Function<? super Object, T> mapper) {
if (isBlank(str)) {
return new ArrayList<>(0);
}
return StrUtil.split(str, separator)
.stream()
.filter(Objects::nonNull)
.map(mapper)
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,28 @@
package cc.iotkit.common.utils;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validator;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.Set;
/**
* Validator
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ValidatorUtils {
private static final Validator VALID = SpringUtils.getBean(Validator.class);
public static <T> void validate(T object, Class<?>... groups) {
Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);
if (!validate.isEmpty()) {
throw new ConstraintViolationException("参数校验异常", validate);
}
}
}

View File

@ -0,0 +1,43 @@
package cc.iotkit.common.utils.file;
import cn.hutool.core.io.FileUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
*
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class FileUtils extends FileUtil {
/**
*
*
* @param response
* @param realFileName
*/
public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) {
String percentEncodedFileName = percentEncode(realFileName);
String contentDispositionValue = String.format("attachment; filename=%s;filename*=utf-8''%s", percentEncodedFileName, percentEncodedFileName);
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
response.setHeader("Content-disposition", contentDispositionValue);
response.setHeader("download-filename", percentEncodedFileName);
}
/**
*
*
* @param s
* @return
*/
public static String percentEncode(String s) {
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8);
return encode.replaceAll("\\+", "%20");
}
}

View File

@ -0,0 +1,40 @@
package cc.iotkit.common.utils.file;
/**
*
*
* @author ruoyi
*/
public class MimeTypeUtils {
public static final String IMAGE_PNG = "image/png";
public static final String IMAGE_JPG = "image/jpg";
public static final String IMAGE_JPEG = "image/jpeg";
public static final String IMAGE_BMP = "image/bmp";
public static final String IMAGE_GIF = "image/gif";
public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
public static final String[] FLASH_EXTENSION = {"swf", "flv"};
public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
"asf", "rm", "rmvb"};
public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"};
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
// 图片
"bmp", "gif", "jpg", "jpeg", "png",
// word excel powerpoint
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
// 压缩文件
"rar", "zip", "gz", "bz2",
// 视频格式
"mp4", "avi", "rmvb",
// pdf
"pdf"};
}

View File

@ -0,0 +1,9 @@
package cc.iotkit.common.validate;
/**
* add
*
* @author Lion Li
*/
public interface AddGroup {
}

View File

@ -0,0 +1,9 @@
package cc.iotkit.common.validate;
/**
* edit
*
* @author Lion Li
*/
public interface EditGroup {
}

View File

@ -0,0 +1,9 @@
package cc.iotkit.common.validate;
/**
* query
*
* @author Lion Li
*/
public interface QueryGroup {
}

View File

@ -2,14 +2,14 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>iot-components</artifactId> <artifactId>iot-common</artifactId>
<groupId>cc.iotkit</groupId> <groupId>cc.iotkit</groupId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>iot-component-swagger</artifactId> <artifactId>iot-common-doc</artifactId>
<dependencies> <dependencies>
<!-- SpringBoot Web --> <!-- SpringBoot Web -->
@ -50,4 +50,17 @@
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>

View File

@ -9,8 +9,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import springfox.documentation.builders.*;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder; import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.Docket;

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>iot-common</artifactId>
<groupId>cc.iotkit</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>iot-common-excel</artifactId>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-common-core</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,23 @@
package cc.iotkit.common.excel.annotation;
import java.lang.annotation.*;
/**
* excel ()
*
* {@link CellMergeStrategy} 使
*
* @author Lion Li
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface CellMerge {
/**
* col index
*/
int index() default -1;
}

View File

@ -0,0 +1,33 @@
package cc.iotkit.common.excel.annotation;
import cc.iotkit.common.utils.StringUtils;
import java.lang.annotation.*;
/**
*
*
* @author Lion Li
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelDictFormat {
/**
* type (: sys_user_sex)
*/
String dictType() default "";
/**
* (: 0=,1=,2=)
*/
String readConverterExp() default "";
/**
*
*/
String separator() default StringUtils.SEPARATOR;
}

View File

@ -0,0 +1,30 @@
package cc.iotkit.common.excel.annotation;
import java.lang.annotation.*;
/**
*
*
* @author Liang
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelEnumFormat {
/**
*
*/
Class<? extends Enum<?>> enumClass();
/**
* codecode
*/
String codeField() default "code";
/**
* texttext
*/
String textField() default "text";
}

View File

@ -0,0 +1,52 @@
package cc.iotkit.common.excel.convert;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
/**
*
* Excel 15 15
*
* @author Lion Li
*/
@Slf4j
public class ExcelBigNumberConvert implements Converter<Long> {
@Override
public Class<Long> supportJavaTypeKey() {
return Long.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
return Convert.toLong(cellData.getData());
}
@Override
public WriteCellData<Object> convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
if (ObjectUtil.isNotNull(object)) {
String str = Convert.toStr(object);
if (str.length() > 15) {
return new WriteCellData<>(str);
}
}
WriteCellData<Object> cellData = new WriteCellData<>(new BigDecimal(object));
cellData.setType(CellDataTypeEnum.NUMBER);
return cellData;
}
}

View File

@ -0,0 +1,73 @@
package cc.iotkit.common.excel.convert;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.utils.ExcelUtil;
import java.lang.reflect.Field;
/**
*
*
* @author Lion Li
*/
@Slf4j
public class ExcelDictConvert implements Converter<Object> {
@Override
public Class<Object> supportJavaTypeKey() {
return Object.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return null;
}
@Override
public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
ExcelDictFormat anno = getAnnotation(contentProperty.getField());
String type = anno.dictType();
String label = cellData.getStringValue();
String value;
if (StringUtils.isBlank(type)) {
value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator());
} else {
value = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator());
}
return Convert.convert(contentProperty.getField().getType(), value);
}
@Override
public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
if (ObjectUtil.isNull(object)) {
return new WriteCellData<>("");
}
ExcelDictFormat anno = getAnnotation(contentProperty.getField());
String type = anno.dictType();
String value = Convert.toStr(object);
String label;
if (StringUtils.isBlank(type)) {
label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator());
} else {
label = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator());
}
return new WriteCellData<>(label);
}
private ExcelDictFormat getAnnotation(Field field) {
return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class);
}
}

View File

@ -0,0 +1,75 @@
package cc.iotkit.common.excel.convert;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.excel.annotation.ExcelEnumFormat;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
*
*
* @author Liang
*/
@Slf4j
public class ExcelEnumConvert implements Converter<Object> {
@Override
public Class<Object> supportJavaTypeKey() {
return Object.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return null;
}
@Override
public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
Object codeValue = cellData.getData();
// 如果是空值
if (ObjectUtil.isNull(codeValue)) {
return null;
}
Map<Object, String> enumValueMap = beforeConvert(contentProperty);
String textValue = enumValueMap.get(codeValue);
return Convert.convert(contentProperty.getField().getType(), textValue);
}
@Override
public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
if (ObjectUtil.isNull(object)) {
return new WriteCellData<>("");
}
Map<Object, String> enumValueMap = beforeConvert(contentProperty);
String value = Convert.toStr(enumValueMap.get(object), "");
return new WriteCellData<>(value);
}
private Map<Object, String> beforeConvert(ExcelContentProperty contentProperty) {
ExcelEnumFormat anno = getAnnotation(contentProperty.getField());
Map<Object, String> enumValueMap = new HashMap<>();
Enum<?>[] enumConstants = anno.enumClass().getEnumConstants();
for (Enum<?> enumConstant : enumConstants) {
Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField());
String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField());
enumValueMap.put(codeValue, textValue);
}
return enumValueMap;
}
private ExcelEnumFormat getAnnotation(Field field) {
return AnnotationUtil.getAnnotation(field, ExcelEnumFormat.class);
}
}

View File

@ -0,0 +1,122 @@
package cc.iotkit.common.excel.core;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.excel.annotation.CellMerge;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*
* @author Lion Li
*/
@Slf4j
public class CellMergeStrategy extends AbstractMergeStrategy {
private final List<?> list;
private final boolean hasTitle;
private int rowIndex;
public CellMergeStrategy(List<?> list, boolean hasTitle) {
this.list = list;
this.hasTitle = hasTitle;
// 行合并开始下标
this.rowIndex = hasTitle ? 1 : 0;
}
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
List<CellRangeAddress> cellList = handle(list, hasTitle);
// judge the list is not null
if (CollUtil.isNotEmpty(cellList)) {
// the judge is necessary
if (cell.getRowIndex() == rowIndex && cell.getColumnIndex() == 0) {
for (CellRangeAddress item : cellList) {
sheet.addMergedRegion(item);
}
}
}
}
@SneakyThrows
private List<CellRangeAddress> handle(List<?> list, boolean hasTitle) {
List<CellRangeAddress> cellList = new ArrayList<>();
if (CollUtil.isEmpty(list)) {
return cellList;
}
Field[] fields = ReflectUtils.getFields(list.get(0).getClass(), field -> !"serialVersionUID".equals(field.getName()));
// 有注解的字段
List<Field> mergeFields = new ArrayList<>();
List<Integer> mergeFieldsIndex = new ArrayList<>();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.isAnnotationPresent(CellMerge.class)) {
CellMerge cm = field.getAnnotation(CellMerge.class);
mergeFields.add(field);
mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index());
if (hasTitle) {
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
rowIndex = Math.max(rowIndex, property.value().length);
}
}
}
Map<Field, RepeatCell> map = new HashMap<>();
// 生成两两合并单元格
for (int i = 0; i < list.size(); i++) {
for (int j = 0; j < mergeFields.size(); j++) {
Field field = mergeFields.get(j);
Object val = ReflectUtils.invokeGetter(list.get(i), field.getName());
int colNum = mergeFieldsIndex.get(j);
if (!map.containsKey(field)) {
map.put(field, new RepeatCell(val, i));
} else {
RepeatCell repeatCell = map.get(field);
Object cellValue = repeatCell.getValue();
if (cellValue == null || "".equals(cellValue)) {
// 空值跳过不合并
continue;
}
if (!cellValue.equals(val)) {
if (i - repeatCell.getCurrent() > 1) {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
map.put(field, new RepeatCell(val, i));
} else if (i == list.size() - 1) {
if (i > repeatCell.getCurrent()) {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
}
}
}
}
}
return cellList;
}
@Data
@AllArgsConstructor
static class RepeatCell {
private Object value;
private int current;
}
}

View File

@ -0,0 +1,106 @@
package cc.iotkit.common.excel.core;
import cc.iotkit.common.utils.JsonUtils;
import cc.iotkit.common.utils.StreamUtils;
import cc.iotkit.common.utils.ValidatorUtils;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelDataConvertException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.Set;
/**
* Excel
*
* @author Yjoioooo
* @author Lion Li
*/
@Slf4j
@NoArgsConstructor
public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements ExcelListener<T> {
/**
* Validator
*/
private Boolean isValidate = Boolean.TRUE;
/**
* excel
*/
private Map<Integer, String> headMap;
/**
*
*/
private ExcelResult<T> excelResult;
public DefaultExcelListener(boolean isValidate) {
this.excelResult = new DefaultExcelResult<>();
this.isValidate = isValidate;
}
/**
*
*
* @param exception ExcelDataConvertException
* @param context Excel
*/
@Override
public void onException(Exception exception, AnalysisContext context) throws Exception {
String errMsg = null;
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
// 如果是某一个单元格的转换异常 能获取到具体行号
Integer rowIndex = excelDataConvertException.getRowIndex();
Integer columnIndex = excelDataConvertException.getColumnIndex();
errMsg = StrUtil.format("第{}行-第{}列-表头{}: 解析异常<br/>",
rowIndex + 1, columnIndex + 1, headMap.get(columnIndex));
if (log.isDebugEnabled()) {
log.error(errMsg);
}
}
if (exception instanceof ConstraintViolationException) {
ConstraintViolationException constraintViolationException = (ConstraintViolationException) exception;
Set<ConstraintViolation<?>> constraintViolations = constraintViolationException.getConstraintViolations();
String constraintViolationsMsg = StreamUtils.join(constraintViolations, ConstraintViolation::getMessage, ", ");
errMsg = StrUtil.format("第{}行数据校验异常: {}", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg);
if (log.isDebugEnabled()) {
log.error(errMsg);
}
}
excelResult.getErrorList().add(errMsg);
throw new ExcelAnalysisException(errMsg);
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
this.headMap = headMap;
log.debug("解析到一条表头数据: {}", JsonUtils.toJsonString(headMap));
}
@Override
public void invoke(T data, AnalysisContext context) {
if (isValidate) {
ValidatorUtils.validate(data);
}
excelResult.getList().add(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.debug("所有数据解析完成!");
}
@Override
public ExcelResult<T> getExcelResult() {
return excelResult;
}
}

View File

@ -0,0 +1,73 @@
package cc.iotkit.common.excel.core;
import cn.hutool.core.util.StrUtil;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
/**
* excel
*
* @author Yjoioooo
* @author Lion Li
*/
public class DefaultExcelResult<T> implements ExcelResult<T> {
/**
* list
*/
@Setter
private List<T> list;
/**
*
*/
@Setter
private List<String> errorList;
public DefaultExcelResult() {
this.list = new ArrayList<>();
this.errorList = new ArrayList<>();
}
public DefaultExcelResult(List<T> list, List<String> errorList) {
this.list = list;
this.errorList = errorList;
}
public DefaultExcelResult(ExcelResult<T> excelResult) {
this.list = excelResult.getList();
this.errorList = excelResult.getErrorList();
}
@Override
public List<T> getList() {
return list;
}
@Override
public List<String> getErrorList() {
return errorList;
}
/**
*
*
* @return
*/
@Override
public String getAnalysis() {
int successCount = list.size();
int errorCount = errorList.size();
if (successCount == 0) {
return "读取失败,未解析到数据";
} else {
if (errorCount == 0) {
return StrUtil.format("恭喜您,全部读取成功!共{}条", successCount);
} else {
return "";
}
}
}
}

View File

@ -0,0 +1,14 @@
package cc.iotkit.common.excel.core;
import com.alibaba.excel.read.listener.ReadListener;
/**
* Excel
*
* @author Lion Li
*/
public interface ExcelListener<T> extends ReadListener<T> {
ExcelResult<T> getExcelResult();
}

View File

@ -0,0 +1,26 @@
package cc.iotkit.common.excel.core;
import java.util.List;
/**
* excel
*
* @author Lion Li
*/
public interface ExcelResult<T> {
/**
*
*/
List<T> getList();
/**
*
*/
List<String> getErrorList();
/**
*
*/
String getAnalysis();
}

View File

@ -0,0 +1,327 @@
package cc.iotkit.common.excel.utils;
import cc.iotkit.common.excel.convert.ExcelBigNumberConvert;
import cc.iotkit.common.excel.core.CellMergeStrategy;
import cc.iotkit.common.excel.core.DefaultExcelListener;
import cc.iotkit.common.excel.core.ExcelListener;
import cc.iotkit.common.excel.core.ExcelResult;
import cc.iotkit.common.utils.StringUtils;
import cc.iotkit.common.utils.file.FileUtils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Excel
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ExcelUtil {
/**
* ()
*
* @param is
* @return
*/
public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();
}
/**
* 使
*
* @param is
* @param clazz
* @param isValidate Validator
* @return
*/
public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate) {
DefaultExcelListener<T> listener = new DefaultExcelListener<>(isValidate);
EasyExcel.read(is, clazz, listener).sheet().doRead();
return listener.getExcelResult();
}
/**
* 使
*
* @param is
* @param clazz
* @param listener
* @return
*/
public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, ExcelListener<T> listener) {
EasyExcel.read(is, clazz, listener).sheet().doRead();
return listener.getExcelResult();
}
/**
* excel
*
* @param list
* @param sheetName
* @param clazz
* @param response
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
try {
resetResponse(sheetName, response);
ServletOutputStream os = response.getOutputStream();
exportExcel(list, sheetName, clazz, false, os);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
}
/**
* excel
*
* @param list
* @param sheetName
* @param clazz
* @param merge
* @param response
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, HttpServletResponse response) {
try {
resetResponse(sheetName, response);
ServletOutputStream os = response.getOutputStream();
exportExcel(list, sheetName, clazz, merge, os);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
}
/**
* excel
*
* @param list
* @param sheetName
* @param clazz
* @param os
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, OutputStream os) {
exportExcel(list, sheetName, clazz, false, os);
}
/**
* excel
*
* @param list
* @param sheetName
* @param clazz
* @param merge
* @param os
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, OutputStream os) {
ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
.autoCloseStream(false)
// 自动适配
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.sheet(sheetName);
if (merge) {
// 合并处理器
builder.registerWriteHandler(new CellMergeStrategy(list, true));
}
builder.doWrite(list);
}
/**
* {.}
*
* @param filename
* @param templatePath resource
* : excel/temp.xlsx
* : resource
* @param data
* @param response
*/
public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {
try {
resetResponse(filename, response);
ServletOutputStream os = response.getOutputStream();
exportTemplate(data, templatePath, os);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
}
/**
* {.}
*
* @param templatePath resource
* : excel/temp.xlsx
* : resource
* @param data
* @param os
*/
public static void exportTemplate(List<Object> data, String templatePath, OutputStream os) {
ClassPathResource templateResource = new ClassPathResource(templatePath);
ExcelWriter excelWriter = EasyExcel.write(os)
.withTemplate(templateResource.getStream())
.autoCloseStream(false)
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
if (CollUtil.isEmpty(data)) {
throw new IllegalArgumentException("数据为空");
}
// 单表多数据导出 模板格式为 {.属性}
for (Object d : data) {
excelWriter.fill(d, writeSheet);
}
excelWriter.finish();
}
/**
* {key.}
*
* @param filename
* @param templatePath resource
* : excel/temp.xlsx
* : resource
* @param data
* @param response
*/
public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) {
try {
resetResponse(filename, response);
ServletOutputStream os = response.getOutputStream();
exportTemplateMultiList(data, templatePath, os);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
}
/**
* {key.}
*
* @param templatePath resource
* : excel/temp.xlsx
* : resource
* @param data
* @param os
*/
public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) {
ClassPathResource templateResource = new ClassPathResource(templatePath);
ExcelWriter excelWriter = EasyExcel.write(os)
.withTemplate(templateResource.getStream())
.autoCloseStream(false)
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
if (CollUtil.isEmpty(data)) {
throw new IllegalArgumentException("数据为空");
}
for (Map.Entry<String, Object> map : data.entrySet()) {
// 设置列表后续还有数据
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
if (map.getValue() instanceof Collection) {
// 多表导出必须使用 FillWrapper
excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
} else {
excelWriter.fill(map.getValue(), writeSheet);
}
}
excelWriter.finish();
}
/**
*
*/
private static void resetResponse(String sheetName, HttpServletResponse response) {
String filename = encodingFilename(sheetName);
FileUtils.setAttachmentResponseHeader(response, filename);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
}
/**
* 0=,1=,2=
*
* @param propertyValue
* @param converterExp
* @param separator
* @return
*/
public static String convertByExp(String propertyValue, String converterExp, String separator) {
StringBuilder propertyString = new StringBuilder();
String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
for (String item : convertSource) {
String[] itemArray = item.split("=");
if (StringUtils.containsAny(propertyValue, separator)) {
for (String value : propertyValue.split(separator)) {
if (itemArray[0].equals(value)) {
propertyString.append(itemArray[1]).append(separator);
break;
}
}
} else {
if (itemArray[0].equals(propertyValue)) {
return itemArray[1];
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* =0,=1,=2
*
* @param propertyValue
* @param converterExp
* @param separator
* @return
*/
public static String reverseByExp(String propertyValue, String converterExp, String separator) {
StringBuilder propertyString = new StringBuilder();
String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
for (String item : convertSource) {
String[] itemArray = item.split("=");
if (StringUtils.containsAny(propertyValue, separator)) {
for (String value : propertyValue.split(separator)) {
if (itemArray[1].equals(value)) {
propertyString.append(itemArray[0]).append(separator);
break;
}
}
} else {
if (itemArray[1].equals(propertyValue)) {
return itemArray[0];
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
*
*/
public static String encodingFilename(String filename) {
return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
}
}

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>iot-common</artifactId>
<groupId>cc.iotkit</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>iot-common-log</artifactId>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-common-core</artifactId>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-common-security</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>iot-common</artifactId>
<groupId>cc.iotkit</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>iot-common-oss</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>iot-common</artifactId>
<groupId>cc.iotkit</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>iot-common-redis</artifactId>
<dependencies>
<!-- RuoYi Common Core-->
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-common-core</artifactId>
</dependency>
<!--redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,130 @@
package cc.iotkit.common.redis.config;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dromara.common.redis.config.properties.RedissonProperties;
import org.dromara.common.redis.handler.KeyPrefixHandler;
import org.dromara.common.redis.manager.PlusSpringCacheManager;
import lombok.extern.slf4j.Slf4j;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
/**
* redis
*
* @author Lion Li
*/
@Slf4j
@AutoConfiguration
@EnableCaching
@EnableConfigurationProperties(RedissonProperties.class)
public class RedisConfig {
@Autowired
private RedissonProperties redissonProperties;
@Autowired
private ObjectMapper objectMapper;
@Bean
public RedissonAutoConfigurationCustomizer redissonCustomizer() {
return config -> {
config.setThreads(redissonProperties.getThreads())
.setNettyThreads(redissonProperties.getNettyThreads())
.setCodec(new JsonJacksonCodec(objectMapper));
RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
if (ObjectUtil.isNotNull(singleServerConfig)) {
// 使用单机模式
config.useSingleServer()
//设置redis key前缀
.setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))
.setTimeout(singleServerConfig.getTimeout())
.setClientName(singleServerConfig.getClientName())
.setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
.setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
.setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
.setConnectionPoolSize(singleServerConfig.getConnectionPoolSize());
}
// 集群配置方式 参考下方注释
RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
if (ObjectUtil.isNotNull(clusterServersConfig)) {
config.useClusterServers()
//设置redis key前缀
.setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))
.setTimeout(clusterServersConfig.getTimeout())
.setClientName(clusterServersConfig.getClientName())
.setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())
.setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())
.setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())
.setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())
.setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())
.setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())
.setReadMode(clusterServersConfig.getReadMode())
.setSubscriptionMode(clusterServersConfig.getSubscriptionMode());
}
log.info("初始化 redis 配置");
};
}
/**
* spring-cache
*/
@Bean
public CacheManager cacheManager() {
return new PlusSpringCacheManager();
}
/**
* redis yml
*
* --- # redis ()
* spring:
* redis:
* cluster:
* nodes:
* - 192.168.0.100:6379
* - 192.168.0.101:6379
* - 192.168.0.102:6379
* #
* password:
* #
* timeout: 10s
* # ssl
* ssl: false
*
* redisson:
* # 线
* threads: 16
* # Netty线
* nettyThreads: 32
* #
* clusterServersConfig:
* #
* clientName: ${ruoyi.name}
* # master
* masterConnectionMinimumIdleSize: 32
* # master
* masterConnectionPoolSize: 64
* # slave
* slaveConnectionMinimumIdleSize: 32
* # slave
* slaveConnectionPoolSize: 64
* #
* idleConnectionTimeout: 10000
* #
* timeout: 3000
* #
* subscriptionConnectionPoolSize: 50
* #
* readMode: "SLAVE"
* #
* subscriptionMode: "MASTER"
*/
}

View File

@ -0,0 +1,135 @@
package cc.iotkit.common.redis.config.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.redisson.config.ReadMode;
import org.redisson.config.SubscriptionMode;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Redisson
*
* @author Lion Li
*/
@Data
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
/**
* rediskey
*/
private String keyPrefix;
/**
* 线, = * 2
*/
private int threads;
/**
* Netty线, = * 2
*/
private int nettyThreads;
/**
*
*/
private SingleServerConfig singleServerConfig;
/**
*
*/
private ClusterServersConfig clusterServersConfig;
@Data
@NoArgsConstructor
public static class SingleServerConfig {
/**
*
*/
private String clientName;
/**
*
*/
private int connectionMinimumIdleSize;
/**
*
*/
private int connectionPoolSize;
/**
*
*/
private int idleConnectionTimeout;
/**
*
*/
private int timeout;
/**
*
*/
private int subscriptionConnectionPoolSize;
}
@Data
@NoArgsConstructor
public static class ClusterServersConfig {
/**
*
*/
private String clientName;
/**
* master
*/
private int masterConnectionMinimumIdleSize;
/**
* master
*/
private int masterConnectionPoolSize;
/**
* slave
*/
private int slaveConnectionMinimumIdleSize;
/**
* slave
*/
private int slaveConnectionPoolSize;
/**
*
*/
private int idleConnectionTimeout;
/**
*
*/
private int timeout;
/**
*
*/
private int subscriptionConnectionPoolSize;
/**
*
*/
private ReadMode readMode;
/**
*
*/
private SubscriptionMode subscriptionMode;
}
}

View File

@ -0,0 +1,50 @@
package cc.iotkit.common.redis.handler;
import org.dromara.common.core.utils.StringUtils;
import org.redisson.api.NameMapper;
/**
* rediskey
*
* @author ye
* @date 2022/7/14 17:44
* @since 4.3.0
*/
public class KeyPrefixHandler implements NameMapper {
private final String keyPrefix;
public KeyPrefixHandler(String keyPrefix) {
//前缀为空 则返回空前缀
this.keyPrefix = StringUtils.isBlank(keyPrefix) ? "" : keyPrefix + ":";
}
/**
*
*/
@Override
public String map(String name) {
if (StringUtils.isBlank(name)) {
return null;
}
if (StringUtils.isNotBlank(keyPrefix) && !name.startsWith(keyPrefix)) {
return keyPrefix + name;
}
return name;
}
/**
*
*/
@Override
public String unmap(String name) {
if (StringUtils.isBlank(name)) {
return null;
}
if (StringUtils.isNotBlank(keyPrefix) && name.startsWith(keyPrefix)) {
return name.substring(keyPrefix.length());
}
return name;
}
}

View File

@ -0,0 +1,191 @@
/**
* Copyright (c) 2013-2021 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cc.iotkit.common.redis.manager;
import org.dromara.common.redis.utils.RedisUtils;
import org.redisson.api.RMap;
import org.redisson.api.RMapCache;
import org.redisson.spring.cache.CacheConfig;
import org.redisson.spring.cache.RedissonCache;
import org.springframework.boot.convert.DurationStyle;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
import org.springframework.util.StringUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A {@link org.springframework.cache.CacheManager} implementation
* backed by Redisson instance.
* <p>
* RedissonSpringCacheManager
* cacheName
*
* @author Nikita Koksharov
*
*/
@SuppressWarnings("unchecked")
public class PlusSpringCacheManager implements CacheManager {
private boolean dynamic = true;
private boolean allowNullValues = true;
private boolean transactionAware = true;
Map<String, CacheConfig> configMap = new ConcurrentHashMap<>();
ConcurrentMap<String, Cache> instanceMap = new ConcurrentHashMap<>();
/**
* Creates CacheManager supplied by Redisson instance
*/
public PlusSpringCacheManager() {
}
/**
* Defines possibility of storing {@code null} values.
* <p>
* Default is <code>true</code>
*
* @param allowNullValues stores if <code>true</code>
*/
public void setAllowNullValues(boolean allowNullValues) {
this.allowNullValues = allowNullValues;
}
/**
* Defines if cache aware of Spring-managed transactions.
* If {@code true} put/evict operations are executed only for successful transaction in after-commit phase.
* <p>
* Default is <code>false</code>
*
* @param transactionAware cache is transaction aware if <code>true</code>
*/
public void setTransactionAware(boolean transactionAware) {
this.transactionAware = transactionAware;
}
/**
* Defines 'fixed' cache names.
* A new cache instance will not be created in dynamic for non-defined names.
* <p>
* `null` parameter setups dynamic mode
*
* @param names of caches
*/
public void setCacheNames(Collection<String> names) {
if (names != null) {
for (String name : names) {
getCache(name);
}
dynamic = false;
} else {
dynamic = true;
}
}
/**
* Set cache config mapped by cache name
*
* @param config object
*/
public void setConfig(Map<String, ? extends CacheConfig> config) {
this.configMap = (Map<String, CacheConfig>) config;
}
protected CacheConfig createDefaultConfig() {
return new CacheConfig();
}
@Override
public Cache getCache(String name) {
Cache cache = instanceMap.get(name);
if (cache != null) {
return cache;
}
if (!dynamic) {
return cache;
}
CacheConfig config = configMap.get(name);
if (config == null) {
config = createDefaultConfig();
configMap.put(name, config);
}
// 重写 cacheName 支持多参数
String[] array = StringUtils.delimitedListToStringArray(name, "#");
name = array[0];
if (array.length > 1) {
config.setTTL(DurationStyle.detectAndParse(array[1]).toMillis());
}
if (array.length > 2) {
config.setMaxIdleTime(DurationStyle.detectAndParse(array[2]).toMillis());
}
if (array.length > 3) {
config.setMaxSize(Integer.parseInt(array[3]));
}
if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) {
return createMap(name, config);
}
return createMapCache(name, config);
}
private Cache createMap(String name, CacheConfig config) {
RMap<Object, Object> map = RedisUtils.getClient().getMap(name);
Cache cache = new RedissonCache(map, allowNullValues);
if (transactionAware) {
cache = new TransactionAwareCacheDecorator(cache);
}
Cache oldCache = instanceMap.putIfAbsent(name, cache);
if (oldCache != null) {
cache = oldCache;
}
return cache;
}
private Cache createMapCache(String name, CacheConfig config) {
RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);
Cache cache = new RedissonCache(map, config, allowNullValues);
if (transactionAware) {
cache = new TransactionAwareCacheDecorator(cache);
}
Cache oldCache = instanceMap.putIfAbsent(name, cache);
if (oldCache != null) {
cache = oldCache;
} else {
map.setMaxSize(config.getMaxSize());
}
return cache;
}
@Override
public Collection<String> getCacheNames() {
return Collections.unmodifiableSet(configMap.keySet());
}
}

View File

@ -0,0 +1,75 @@
package cc.iotkit.common.redis.utils;
import org.dromara.common.core.utils.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.RMap;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import java.util.Set;
/**
* {@link }
*
* @author Michelle.Chung
* @date 2022/8/13
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings(value = {"unchecked"})
public class CacheUtils {
private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class);
/**
* KEY
*
* @param cacheNames
*/
public static Set<Object> keys(String cacheNames) {
RMap<Object, Object> rmap = (RMap<Object, Object>) CACHE_MANAGER.getCache(cacheNames).getNativeCache();
return rmap.keySet();
}
/**
*
*
* @param cacheNames
* @param key key
*/
public static <T> T get(String cacheNames, Object key) {
Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key);
return wrapper != null ? (T) wrapper.get() : null;
}
/**
*
*
* @param cacheNames
* @param key key
* @param value
*/
public static void put(String cacheNames, Object key, Object value) {
CACHE_MANAGER.getCache(cacheNames).put(key, value);
}
/**
*
*
* @param cacheNames
* @param key key
*/
public static void evict(String cacheNames, Object key) {
CACHE_MANAGER.getCache(cacheNames).evict(key);
}
/**
*
*
* @param cacheNames
*/
public static void clear(String cacheNames) {
CACHE_MANAGER.getCache(cacheNames).clear();
}
}

View File

@ -0,0 +1,180 @@
package cc.iotkit.common.redis.utils;
import org.dromara.common.core.utils.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
*
* 使 MQ
* redis 5.X
*
* @author Lion Li
* @version 3.6.0
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class QueueUtils {
private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
/**
*
*/
public static RedissonClient getClient() {
return CLIENT;
}
/**
*
*
* @param queueName
* @param data
*/
public static <T> boolean addQueueObject(String queueName, T data) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
return queue.offer(data);
}
/**
* null()
*
* @param queueName
*/
public static <T> T getQueueObject(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
return queue.poll();
}
/**
* ()
*/
public static <T> boolean removeQueueObject(String queueName, T data) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
return queue.remove(data);
}
/**
* ()
*/
public static <T> boolean destroyQueue(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
return queue.delete();
}
/**
*
*
* @param queueName
* @param data
* @param time
*/
public static <T> void addDelayedQueueObject(String queueName, T data, long time) {
addDelayedQueueObject(queueName, data, time, TimeUnit.MILLISECONDS);
}
/**
*
*
* @param queueName
* @param data
* @param time
* @param timeUnit
*/
public static <T> void addDelayedQueueObject(String queueName, T data, long time, TimeUnit timeUnit) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
delayedQueue.offer(data, time, timeUnit);
}
/**
* null
*
* @param queueName
*/
public static <T> T getDelayedQueueObject(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
return delayedQueue.poll();
}
/**
*
*/
public static <T> boolean removeDelayedQueueObject(String queueName, T data) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
return delayedQueue.remove(data);
}
/**
*
*/
public static <T> void destroyDelayedQueue(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
delayedQueue.destroy();
}
/**
*
*
* @param queueName
* @param data
*/
public static <T> boolean addPriorityQueueObject(String queueName, T data) {
RPriorityBlockingQueue<T> priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName);
return priorityBlockingQueue.offer(data);
}
/**
*
*
* @param queueName
* @param capacity
*/
public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity) {
RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
return boundedBlockingQueue.trySetCapacity(capacity);
}
/**
*
*
* @param queueName
* @param capacity
* @param destroy
*/
public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) {
RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
if (boundedBlockingQueue.isExists() && destroy) {
destroyQueue(queueName);
}
return boundedBlockingQueue.trySetCapacity(capacity);
}
/**
*
*
* @param queueName
* @param data
* @return true false
*/
public static <T> boolean addBoundedQueueObject(String queueName, T data) {
RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
return boundedBlockingQueue.offer(data);
}
/**
* ( : )
*/
public static <T> void subscribeBlockingQueue(String queueName, Consumer<T> consumer) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
queue.subscribeOnElements(consumer);
}
}

View File

@ -0,0 +1,462 @@
package cc.iotkit.common.redis.utils;
import cc.iotkit.common.utils.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.*;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* redis
*
* @author Lion Li
* @version 3.1.0
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public class RedisUtils {
private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
/**
*
*
* @param key key
* @param rateType
* @param rate
* @param rateInterval
* @return -1
*/
public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) {
RRateLimiter rateLimiter = CLIENT.getRateLimiter(key);
rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS);
if (rateLimiter.tryAcquire()) {
return rateLimiter.availablePermits();
} else {
return -1L;
}
}
/**
*
*/
public static RedissonClient getClient() {
return CLIENT;
}
/**
*
*
* @param channelKey key
* @param msg
* @param consumer
*/
public static <T> void publish(String channelKey, T msg, Consumer<T> consumer) {
RTopic topic = CLIENT.getTopic(channelKey);
topic.publish(msg);
consumer.accept(msg);
}
public static <T> void publish(String channelKey, T msg) {
RTopic topic = CLIENT.getTopic(channelKey);
topic.publish(msg);
}
/**
*
*
* @param channelKey key
* @param clazz
* @param consumer
*/
public static <T> void subscribe(String channelKey, Class<T> clazz, Consumer<T> consumer) {
RTopic topic = CLIENT.getTopic(channelKey);
topic.addListener(clazz, (channel, msg) -> consumer.accept(msg));
}
/**
* IntegerString
*
* @param key
* @param value
*/
public static <T> void setCacheObject(final String key, final T value) {
setCacheObject(key, value, false);
}
/**
* TTL
*
* @param key
* @param value
* @param isSaveTtl TTL(: setttl90 set90)
* @since Redis 6.X 使 setAndKeepTTL 5.X
*/
public static <T> void setCacheObject(final String key, final T value, final boolean isSaveTtl) {
RBucket<T> bucket = CLIENT.getBucket(key);
if (isSaveTtl) {
try {
bucket.setAndKeepTTL(value);
} catch (Exception e) {
long timeToLive = bucket.remainTimeToLive();
setCacheObject(key, value, Duration.ofMillis(timeToLive));
}
} else {
bucket.set(value);
}
}
/**
* IntegerString
*
* @param key
* @param value
* @param duration
*/
public static <T> void setCacheObject(final String key, final T value, final Duration duration) {
RBatch batch = CLIENT.createBatch();
RBucketAsync<T> bucket = batch.getBucket(key);
bucket.setAsync(value);
bucket.expireAsync(duration);
batch.execute();
}
/**
*
* <p>
* key `notify-keyspace-events` redis
*
* @param key
* @param listener
*/
public static <T> void addObjectListener(final String key, final ObjectListener listener) {
RBucket<T> result = CLIENT.getBucket(key);
result.addListener(listener);
}
/**
*
*
* @param key Redis
* @param timeout
* @return true=false=
*/
public static boolean expire(final String key, final long timeout) {
return expire(key, Duration.ofSeconds(timeout));
}
/**
*
*
* @param key Redis
* @param duration
* @return true=false=
*/
public static boolean expire(final String key, final Duration duration) {
RBucket rBucket = CLIENT.getBucket(key);
return rBucket.expire(duration);
}
/**
*
*
* @param key
* @return
*/
public static <T> T getCacheObject(final String key) {
RBucket<T> rBucket = CLIENT.getBucket(key);
return rBucket.get();
}
/**
* key
*
* @param key
* @return
*/
public static <T> long getTimeToLive(final String key) {
RBucket<T> rBucket = CLIENT.getBucket(key);
return rBucket.remainTimeToLive();
}
/**
*
*
* @param key
*/
public static boolean deleteObject(final String key) {
return CLIENT.getBucket(key).delete();
}
/**
*
*
* @param collection
*/
public static void deleteObject(final Collection collection) {
RBatch batch = CLIENT.createBatch();
collection.forEach(t -> {
batch.getBucket(t.toString()).deleteAsync();
});
batch.execute();
}
/**
*
*
* @param key
*/
public static boolean isExistsObject(final String key) {
return CLIENT.getBucket(key).isExists();
}
/**
* List
*
* @param key
* @param dataList List
* @return
*/
public static <T> boolean setCacheList(final String key, final List<T> dataList) {
RList<T> rList = CLIENT.getList(key);
return rList.addAll(dataList);
}
/**
* List
* <p>
* key `notify-keyspace-events` redis
*
* @param key
* @param listener
*/
public static <T> void addListListener(final String key, final ObjectListener listener) {
RList<T> rList = CLIENT.getList(key);
rList.addListener(listener);
}
/**
* list
*
* @param key
* @return
*/
public static <T> List<T> getCacheList(final String key) {
RList<T> rList = CLIENT.getList(key);
return rList.readAll();
}
/**
* Set
*
* @param key
* @param dataSet
* @return
*/
public static <T> boolean setCacheSet(final String key, final Set<T> dataSet) {
RSet<T> rSet = CLIENT.getSet(key);
return rSet.addAll(dataSet);
}
/**
* Set
* <p>
* key `notify-keyspace-events` redis
*
* @param key
* @param listener
*/
public static <T> void addSetListener(final String key, final ObjectListener listener) {
RSet<T> rSet = CLIENT.getSet(key);
rSet.addListener(listener);
}
/**
* set
*
* @param key key
* @return set
*/
public static <T> Set<T> getCacheSet(final String key) {
RSet<T> rSet = CLIENT.getSet(key);
return rSet.readAll();
}
/**
* Map
*
* @param key
* @param dataMap
*/
public static <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
if (dataMap != null) {
RMap<String, T> rMap = CLIENT.getMap(key);
rMap.putAll(dataMap);
}
}
/**
* Map
* <p>
* key `notify-keyspace-events` redis
*
* @param key
* @param listener
*/
public static <T> void addMapListener(final String key, final ObjectListener listener) {
RMap<String, T> rMap = CLIENT.getMap(key);
rMap.addListener(listener);
}
/**
* Map
*
* @param key
* @return map
*/
public static <T> Map<String, T> getCacheMap(final String key) {
RMap<String, T> rMap = CLIENT.getMap(key);
return rMap.getAll(rMap.keySet());
}
/**
* Mapkey
*
* @param key
* @return key
*/
public static <T> Set<String> getCacheMapKeySet(final String key) {
RMap<String, T> rMap = CLIENT.getMap(key);
return rMap.keySet();
}
/**
* Hash
*
* @param key Redis
* @param hKey Hash
* @param value
*/
public static <T> void setCacheMapValue(final String key, final String hKey, final T value) {
RMap<String, T> rMap = CLIENT.getMap(key);
rMap.put(hKey, value);
}
/**
* Hash
*
* @param key Redis
* @param hKey Hash
* @return Hash
*/
public static <T> T getCacheMapValue(final String key, final String hKey) {
RMap<String, T> rMap = CLIENT.getMap(key);
return rMap.get(hKey);
}
/**
* Hash
*
* @param key Redis
* @param hKey Hash
* @return Hash
*/
public static <T> T delCacheMapValue(final String key, final String hKey) {
RMap<String, T> rMap = CLIENT.getMap(key);
return rMap.remove(hKey);
}
/**
* Hash
*
* @param key Redis
* @param hKeys Hash
* @return Hash
*/
public static <K, V> Map<K, V> getMultiCacheMapValue(final String key, final Set<K> hKeys) {
RMap<K, V> rMap = CLIENT.getMap(key);
return rMap.getAll(hKeys);
}
/**
*
*
* @param key Redis
* @param value
*/
public static void setAtomicValue(String key, long value) {
RAtomicLong atomic = CLIENT.getAtomicLong(key);
atomic.set(value);
}
/**
*
*
* @param key Redis
* @return
*/
public static long getAtomicValue(String key) {
RAtomicLong atomic = CLIENT.getAtomicLong(key);
return atomic.get();
}
/**
*
*
* @param key Redis
* @return
*/
public static long incrAtomicValue(String key) {
RAtomicLong atomic = CLIENT.getAtomicLong(key);
return atomic.incrementAndGet();
}
/**
*
*
* @param key Redis
* @return
*/
public static long decrAtomicValue(String key) {
RAtomicLong atomic = CLIENT.getAtomicLong(key);
return atomic.decrementAndGet();
}
/**
*
*
* @param pattern
* @return
*/
public static Collection<String> keys(final String pattern) {
Stream<String> stream = CLIENT.getKeys().getKeysStreamByPattern(pattern);
return stream.collect(Collectors.toList());
}
/**
*
*
* @param pattern
*/
public static void deleteKeys(final String pattern) {
CLIENT.getKeys().deleteByPattern(pattern);
}
/**
* rediskey
*
* @param key
*/
public static Boolean hasKey(String key) {
RKeys rKeys = CLIENT.getKeys();
return rKeys.countExists(key) > 0;
}
}

View File

@ -0,0 +1 @@
cc.iotkit.common.redis.config.RedisConfig

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>iot-common</artifactId>
<groupId>cc.iotkit</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>iot-common-security</artifactId>
<dependencies>
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
</dependency>
<!-- Sa-Token 整合 jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-common-core</artifactId>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-common-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,43 @@
package cc.iotkit.common.satoken.config;
import cc.iotkit.common.satoken.core.dao.PlusSaTokenDao;
import cc.iotkit.common.satoken.core.service.SaPermissionImpl;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpLogic;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* sa-token
*
* @author Lion Li
*/
@AutoConfiguration
public class SaTokenConfig implements WebMvcConfigurer {
@Bean
public StpLogic getStpLogicJwt() {
// Sa-Token 整合 jwt (简单模式)
return new StpLogicJwtForSimple();
}
/**
* (使bean便)
*/
@Bean
public StpInterface stpInterface() {
return new SaPermissionImpl();
}
/**
* dao
*/
@Bean
public SaTokenDao saTokenDao() {
return new PlusSaTokenDao();
}
}

View File

@ -0,0 +1,176 @@
package cc.iotkit.common.satoken.core.dao;
import cc.iotkit.common.redis.utils.RedisUtils;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.util.SaFoxUtil;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Sa-Token(使RedisUtils )
*
* @author Lion Li
*/
public class PlusSaTokenDao implements SaTokenDao {
/**
* Value
*/
@Override
public String get(String key) {
return RedisUtils.getCacheObject(key);
}
/**
* Value (: )
*/
@Override
public void set(String key, String value, long timeout) {
if (timeout == 0 || timeout <= NOT_VALUE_EXPIRE) {
return;
}
// 判断是否为永不过期
if (timeout == NEVER_EXPIRE) {
RedisUtils.setCacheObject(key, value);
} else {
RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
}
}
/**
* key-value ()
*/
@Override
public void update(String key, String value) {
long expire = getTimeout(key);
// -2 = 无此键
if (expire == NOT_VALUE_EXPIRE) {
return;
}
this.set(key, value, expire);
}
/**
* Value
*/
@Override
public void delete(String key) {
RedisUtils.deleteObject(key);
}
/**
* Value (: )
*/
@Override
public long getTimeout(String key) {
long timeout = RedisUtils.getTimeToLive(key);
return timeout < 0 ? timeout : timeout / 1000;
}
/**
* Value (: )
*/
@Override
public void updateTimeout(String key, long timeout) {
// 判断是否想要设置为永久
if (timeout == NEVER_EXPIRE) {
long expire = getTimeout(key);
if (expire == NEVER_EXPIRE) {
// 如果其已经被设置为永久,则不作任何处理
} else {
// 如果尚未被设置为永久那么再次set一次
this.set(key, this.get(key), timeout);
}
return;
}
RedisUtils.expire(key, Duration.ofSeconds(timeout));
}
/**
* Object
*/
@Override
public Object getObject(String key) {
return RedisUtils.getCacheObject(key);
}
/**
* Object (: )
*/
@Override
public void setObject(String key, Object object, long timeout) {
if (timeout == 0 || timeout <= NOT_VALUE_EXPIRE) {
return;
}
// 判断是否为永不过期
if (timeout == NEVER_EXPIRE) {
RedisUtils.setCacheObject(key, object);
} else {
RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
}
}
/**
* Object ()
*/
@Override
public void updateObject(String key, Object object) {
long expire = getObjectTimeout(key);
// -2 = 无此键
if (expire == NOT_VALUE_EXPIRE) {
return;
}
this.setObject(key, object, expire);
}
/**
* Object
*/
@Override
public void deleteObject(String key) {
RedisUtils.deleteObject(key);
}
/**
* Object (: )
*/
@Override
public long getObjectTimeout(String key) {
long timeout = RedisUtils.getTimeToLive(key);
return timeout < 0 ? timeout : timeout / 1000;
}
/**
* Object (: )
*/
@Override
public void updateObjectTimeout(String key, long timeout) {
// 判断是否想要设置为永久
if (timeout == NEVER_EXPIRE) {
long expire = getObjectTimeout(key);
if (expire == NEVER_EXPIRE) {
// 如果其已经被设置为永久,则不作任何处理
} else {
// 如果尚未被设置为永久那么再次set一次
this.setObject(key, this.getObject(key), timeout);
}
return;
}
RedisUtils.expire(key, Duration.ofSeconds(timeout));
}
/**
*
*/
@Override
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
Collection<String> keys = RedisUtils.keys(prefix + "*" + keyword + "*");
List<String> list = new ArrayList<>(keys);
return SaFoxUtil.searchList(list, start, size, sortType);
}
}

View File

@ -0,0 +1,47 @@
package cc.iotkit.common.satoken.core.service;
import cc.iotkit.common.domain.model.LoginUser;
import cc.iotkit.common.enums.UserType;
import cc.iotkit.common.satoken.utils.LoginHelper;
import cn.dev33.satoken.stp.StpInterface;
import java.util.ArrayList;
import java.util.List;
/**
* sa-token
*
* @author Lion Li
*/
public class SaPermissionImpl implements StpInterface {
/**
*
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
LoginUser loginUser = LoginHelper.getLoginUser();
UserType userType = UserType.getUserType(loginUser.getUserType());
if (userType == UserType.SYS_USER) {
return new ArrayList<>(loginUser.getMenuPermission());
} else if (userType == UserType.APP_USER) {
// 其他端 自行根据业务编写
}
return new ArrayList<>();
}
/**
*
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
LoginUser loginUser = LoginHelper.getLoginUser();
UserType userType = UserType.getUserType(loginUser.getUserType());
if (userType == UserType.SYS_USER) {
return new ArrayList<>(loginUser.getRolePermission());
} else if (userType == UserType.APP_USER) {
// 其他端 自行根据业务编写
}
return new ArrayList<>();
}
}

View File

@ -0,0 +1,139 @@
package cc.iotkit.common.satoken.listener;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.core.domain.dto.UserOnlineDTO;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.enums.UserType;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.ip.AddressUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.stereotype.Component;
import java.time.Duration;
/**
*
*
* @author Lion Li
*/
@RequiredArgsConstructor
@Component
@Slf4j
public class UserActionListener implements SaTokenListener {
private final SaTokenConfig tokenConfig;
/**
*
*/
@Override
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
UserType userType = UserType.getUserType(loginId.toString());
if (userType == UserType.SYS_USER) {
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = ServletUtils.getClientIP();
LoginUser user = LoginHelper.getLoginUser();
UserOnlineDTO dto = new UserOnlineDTO();
dto.setIpaddr(ip);
dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
dto.setBrowser(userAgent.getBrowser().getName());
dto.setOs(userAgent.getOs().getName());
dto.setLoginTime(System.currentTimeMillis());
dto.setTokenId(tokenValue);
dto.setUserName(user.getUsername());
dto.setDeptName(user.getDeptName());
if(tokenConfig.getTimeout() == -1) {
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
} else {
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
}
log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
} else if (userType == UserType.APP_USER) {
// app端 自行根据业务编写
}
}
/**
*
*/
@Override
public void doLogout(String loginType, Object loginId, String tokenValue) {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue);
}
/**
* 线
*/
@Override
public void doKickout(String loginType, Object loginId, String tokenValue) {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue);
}
/**
* 线
*/
@Override
public void doReplaced(String loginType, Object loginId, String tokenValue) {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue);
}
/**
*
*/
@Override
public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
}
/**
*
*/
@Override
public void doUntieDisable(String loginType, Object loginId, String service) {
}
/**
*
*/
@Override
public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
}
/**
* Session
*/
@Override
public void doCloseSafe(String loginType, String tokenValue, String service) {
}
/**
* Session
*/
@Override
public void doCreateSession(String id) {
}
/**
* Session
*/
@Override
public void doLogoutSession(String id) {
}
/**
* Token
*/
@Override
public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
}
}

View File

@ -0,0 +1,172 @@
package cc.iotkit.common.satoken.utils;
import cc.iotkit.common.constant.TenantConstants;
import cc.iotkit.common.constant.UserConstants;
import cc.iotkit.common.domain.model.LoginUser;
import cc.iotkit.common.enums.DeviceType;
import cc.iotkit.common.enums.UserType;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.Set;
/**
*
* <p>
* user_type pc,app
* deivce web,ios
*
* <p>
*
*
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class LoginHelper {
public static final String LOGIN_USER_KEY = "loginUser";
public static final String TENANT_KEY = "tenantId";
public static final String USER_KEY = "userId";
/**
*
*
* @param loginUser
*/
public static void login(LoginUser loginUser) {
loginByDevice(loginUser, null);
}
/**
*
*
*
* @param loginUser
*/
public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
SaStorage storage = SaHolder.getStorage();
storage.set(LOGIN_USER_KEY, loginUser);
storage.set(TENANT_KEY, loginUser.getTenantId());
storage.set(USER_KEY, loginUser.getUserId());
SaLoginModel model = new SaLoginModel();
if (ObjectUtil.isNotNull(deviceType)) {
model.setDevice(deviceType.getDevice());
}
StpUtil.login(loginUser.getLoginId(),
model.setExtra(TENANT_KEY, loginUser.getTenantId())
.setExtra(USER_KEY, loginUser.getUserId()));
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
}
/**
* ()
*/
public static LoginUser getLoginUser() {
LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY);
if (loginUser != null) {
return loginUser;
}
loginUser = (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY);
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
return loginUser;
}
/**
* token
*/
public static LoginUser getLoginUser(String token) {
return (LoginUser) StpUtil.getTokenSessionByToken(token).get(LOGIN_USER_KEY);
}
/**
* id
*/
public static Long getUserId() {
Long userId;
try {
userId = Convert.toLong(SaHolder.getStorage().get(USER_KEY));
if (ObjectUtil.isNull(userId)) {
userId = Convert.toLong(StpUtil.getExtra(USER_KEY));
SaHolder.getStorage().set(USER_KEY, userId);
}
} catch (Exception e) {
return null;
}
return userId;
}
/**
* ID
*/
public static String getTenantId() {
String tenantId;
try {
tenantId = (String) SaHolder.getStorage().get(TENANT_KEY);
if (ObjectUtil.isNull(tenantId)) {
tenantId = (String) StpUtil.getExtra(TENANT_KEY);
SaHolder.getStorage().set(TENANT_KEY, tenantId);
}
} catch (Exception e) {
return null;
}
return tenantId;
}
/**
* ID
*/
public static Long getDeptId() {
return getLoginUser().getDeptId();
}
/**
*
*/
public static String getUsername() {
return getLoginUser().getUsername();
}
/**
*
*/
public static UserType getUserType() {
String loginId = StpUtil.getLoginIdAsString();
return UserType.getUserType(loginId);
}
/**
*
*
* @param userId ID
* @return
*/
public static boolean isSuperAdmin(Long userId) {
return UserConstants.SUPER_ADMIN_ID.equals(userId);
}
public static boolean isSuperAdmin() {
return isSuperAdmin(getUserId());
}
/**
*
*
* @param rolePermission
* @return
*/
public static boolean isTenantAdmin(Set<String> rolePermission) {
return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY);
}
public static boolean isTenantAdmin() {
return isTenantAdmin(getLoginUser().getRolePermission());
}
}

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>iot-common</artifactId>
<groupId>cc.iotkit</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>iot-common-tenant</artifactId>
<dependencies>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-common-core</artifactId>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-common-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,100 @@
package cc.iotkit.common.tenant.config;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.mybatis.config.MybatisPlusConfig;
import org.dromara.common.redis.config.RedisConfig;
import org.dromara.common.redis.config.properties.RedissonProperties;
import org.dromara.common.tenant.core.TenantSaTokenDao;
import org.dromara.common.tenant.handle.PlusTenantLineHandler;
import org.dromara.common.tenant.handle.TenantKeyPrefixHandler;
import org.dromara.common.tenant.manager.TenantSpringCacheManager;
import org.dromara.common.tenant.properties.TenantProperties;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.SingleServerConfig;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import java.util.ArrayList;
import java.util.List;
/**
*
*
* @author Lion Li
*/
@EnableConfigurationProperties(TenantProperties.class)
@AutoConfiguration(after = {RedisConfig.class, MybatisPlusConfig.class})
@ConditionalOnProperty(value = "tenant.enable", havingValue = "true")
public class TenantConfig {
/**
*
*/
@Bean
public boolean tenantInit(MybatisPlusInterceptor mybatisPlusInterceptor,
TenantProperties tenantProperties) {
List<InnerInterceptor> interceptors = new ArrayList<>();
// 多租户插件 必须放到第一位
interceptors.add(tenantLineInnerInterceptor(tenantProperties));
interceptors.addAll(mybatisPlusInterceptor.getInterceptors());
mybatisPlusInterceptor.setInterceptors(interceptors);
return true;
}
/**
*
*/
public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) {
return new TenantLineInnerInterceptor(new PlusTenantLineHandler(tenantProperties));
}
@Bean
public RedissonAutoConfigurationCustomizer tenantRedissonCustomizer(RedissonProperties redissonProperties) {
return config -> {
TenantKeyPrefixHandler nameMapper = new TenantKeyPrefixHandler(redissonProperties.getKeyPrefix());
SingleServerConfig singleServerConfig = ReflectUtils.invokeGetter(config, "singleServerConfig");
if (ObjectUtil.isNotNull(singleServerConfig)) {
// 使用单机模式
// 设置多租户 redis key前缀
singleServerConfig.setNameMapper(nameMapper);
ReflectUtils.invokeSetter(config, "singleServerConfig", singleServerConfig);
}
ClusterServersConfig clusterServersConfig = ReflectUtils.invokeGetter(config, "clusterServersConfig");
// 集群配置方式 参考下方注释
if (ObjectUtil.isNotNull(clusterServersConfig)) {
// 设置多租户 redis key前缀
clusterServersConfig.setNameMapper(nameMapper);
ReflectUtils.invokeSetter(config, "clusterServersConfig", clusterServersConfig);
}
};
}
/**
*
*/
@Primary
@Bean
public CacheManager tenantCacheManager() {
return new TenantSpringCacheManager();
}
/**
* dao
*/
@Primary
@Bean
public SaTokenDao tenantSaTokenDao() {
return new TenantSaTokenDao();
}
}

View File

@ -0,0 +1,21 @@
package cc.iotkit.common.tenant.core;
import cc.iotkit.common.domain.model.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
*
*
* @author Michelle.Chung
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class TenantEntity extends BaseEntity {
/**
*
*/
private String tenantId;
}

View File

@ -0,0 +1,148 @@
package cc.iotkit.common.tenant.core;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.core.dao.PlusSaTokenDao;
import java.time.Duration;
import java.util.List;
/**
* SaToken
*
* @author Lion Li
*/
public class TenantSaTokenDao extends PlusSaTokenDao {
@Override
public String get(String key) {
return super.get(GlobalConstants.GLOBAL_REDIS_KEY + key);
}
@Override
public void set(String key, String value, long timeout) {
super.set(GlobalConstants.GLOBAL_REDIS_KEY + key, value, timeout);
}
/**
* key-value ()
*/
@Override
public void update(String key, String value) {
long expire = getTimeout(key);
// -2 = 无此键
if (expire == NOT_VALUE_EXPIRE) {
return;
}
this.set(key, value, expire);
}
/**
* Value
*/
@Override
public void delete(String key) {
super.delete(GlobalConstants.GLOBAL_REDIS_KEY + key);
}
/**
* Value (: )
*/
@Override
public long getTimeout(String key) {
return super.getTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key);
}
/**
* Value (: )
*/
@Override
public void updateTimeout(String key, long timeout) {
// 判断是否想要设置为永久
if (timeout == NEVER_EXPIRE) {
long expire = getTimeout(key);
if (expire == NEVER_EXPIRE) {
// 如果其已经被设置为永久,则不作任何处理
} else {
// 如果尚未被设置为永久那么再次set一次
this.set(key, this.get(key), timeout);
}
return;
}
RedisUtils.expire(GlobalConstants.GLOBAL_REDIS_KEY + key, Duration.ofSeconds(timeout));
}
/**
* Object
*/
@Override
public Object getObject(String key) {
return super.getObject(GlobalConstants.GLOBAL_REDIS_KEY + key);
}
/**
* Object (: )
*/
@Override
public void setObject(String key, Object object, long timeout) {
super.setObject(GlobalConstants.GLOBAL_REDIS_KEY + key, object, timeout);
}
/**
* Object ()
*/
@Override
public void updateObject(String key, Object object) {
long expire = getObjectTimeout(key);
// -2 = 无此键
if (expire == NOT_VALUE_EXPIRE) {
return;
}
this.setObject(key, object, expire);
}
/**
* Object
*/
@Override
public void deleteObject(String key) {
super.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + key);
}
/**
* Object (: )
*/
@Override
public long getObjectTimeout(String key) {
return super.getObjectTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key);
}
/**
* Object (: )
*/
@Override
public void updateObjectTimeout(String key, long timeout) {
// 判断是否想要设置为永久
if (timeout == NEVER_EXPIRE) {
long expire = getObjectTimeout(key);
if (expire == NEVER_EXPIRE) {
// 如果其已经被设置为永久,则不作任何处理
} else {
// 如果尚未被设置为永久那么再次set一次
this.setObject(key, this.getObject(key), timeout);
}
return;
}
RedisUtils.expire(GlobalConstants.GLOBAL_REDIS_KEY + key, Duration.ofSeconds(timeout));
}
/**
*
*/
@Override
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
return super.searchData(GlobalConstants.GLOBAL_REDIS_KEY + prefix, keyword, start, size, sortType);
}
}

View File

@ -0,0 +1,20 @@
package cc.iotkit.common.tenant.exception;
import cc.iotkit.common.exception.BizException;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
*
*
* @author Lion Li
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class TenantException extends BizException {
public TenantException(String code, String message) {
super("tenant", code, message);
}
}

View File

@ -0,0 +1,59 @@
package cc.iotkit.common.tenant.handle;
import cn.hutool.core.collection.ListUtil;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import lombok.AllArgsConstructor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.StringValue;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.common.tenant.properties.TenantProperties;
import java.util.List;
/**
*
*
* @author Lion Li
*/
@AllArgsConstructor
public class PlusTenantLineHandler implements TenantLineHandler {
private final TenantProperties tenantProperties;
@Override
public Expression getTenantId() {
String tenantId = LoginHelper.getTenantId();
if (StringUtils.isBlank(tenantId)) {
return new NullValue();
}
String dynamicTenantId = TenantHelper.getDynamic();
if (StringUtils.isNotBlank(dynamicTenantId)) {
// 返回动态租户
return new StringValue(dynamicTenantId);
}
// 返回固定租户
return new StringValue(tenantId);
}
@Override
public boolean ignoreTable(String tableName) {
String tenantId = LoginHelper.getTenantId();
// 判断是否有租户
if (StringUtils.isNotBlank(tenantId)) {
// 不需要过滤租户的表
List<String> excludes = tenantProperties.getExcludes();
// 非业务表
List<String> tables = ListUtil.toList(
"gen_table",
"gen_table_column"
);
tables.addAll(excludes);
return tables.contains(tableName);
}
return true;
}
}

View File

@ -0,0 +1,58 @@
package cc.iotkit.common.tenant.handle;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.redis.handler.KeyPrefixHandler;
import org.dromara.common.tenant.helper.TenantHelper;
/**
* rediskey
*
* @author Lion Li
*/
public class TenantKeyPrefixHandler extends KeyPrefixHandler {
public TenantKeyPrefixHandler(String keyPrefix) {
super(keyPrefix);
}
/**
*
*/
@Override
public String map(String name) {
if (StringUtils.isBlank(name)) {
return null;
}
if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
return super.map(name);
}
String tenantId = TenantHelper.getTenantId();
if (StringUtils.startsWith(name, tenantId)) {
// 如果存在则直接返回
return super.map(name);
}
return super.map(tenantId + ":" + name);
}
/**
*
*/
@Override
public String unmap(String name) {
String unmap = super.unmap(name);
if (StringUtils.isBlank(unmap)) {
return null;
}
if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
return super.unmap(name);
}
String tenantId = TenantHelper.getTenantId();
if (StringUtils.startsWith(unmap, tenantId)) {
// 如果存在则删除
return unmap.substring((tenantId + ":").length());
}
return unmap;
}
}

View File

@ -0,0 +1,140 @@
package cc.iotkit.common.tenant.helper;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.spring.SpringMVCUtil;
import cn.hutool.core.convert.Convert;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import java.util.function.Supplier;
/**
*
*
* @author Lion Li
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TenantHelper {
private static final String DYNAMIC_TENANT_KEY = GlobalConstants.GLOBAL_REDIS_KEY + "dynamicTenant";
private static final ThreadLocal<String> TEMP_DYNAMIC_TENANT = new TransmittableThreadLocal<>();
/**
*
*/
public static boolean isEnable() {
return Convert.toBool(SpringUtils.getProperty("tenant.enable"), false);
}
/**
* ( {@link #disableIgnore()} )
*/
public static void enableIgnore() {
InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build());
}
/**
*
*/
public static void disableIgnore() {
InterceptorIgnoreHelper.clearIgnoreStrategy();
}
/**
*
*
* @param handle
*/
public static void ignore(Runnable handle) {
enableIgnore();
try {
handle.run();
} finally {
disableIgnore();
}
}
/**
*
*
* @param handle
*/
public static <T> T ignore(Supplier<T> handle) {
enableIgnore();
try {
return handle.get();
} finally {
disableIgnore();
}
}
/**
* ( )
* <p>
* web 线
*/
public static void setDynamic(String tenantId) {
if (!SpringMVCUtil.isWeb()) {
TEMP_DYNAMIC_TENANT.set(tenantId);
return;
}
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
RedisUtils.setCacheObject(cacheKey, tenantId);
SaHolder.getStorage().set(cacheKey, tenantId);
}
/**
* ( )
* <p>
* web 线
*/
public static String getDynamic() {
if (!SpringMVCUtil.isWeb()) {
return TEMP_DYNAMIC_TENANT.get();
}
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
String tenantId = (String) SaHolder.getStorage().get(cacheKey);
if (StringUtils.isNotBlank(tenantId)) {
return tenantId;
}
tenantId = RedisUtils.getCacheObject(cacheKey);
SaHolder.getStorage().set(cacheKey, tenantId);
return tenantId;
}
/**
*
*/
public static void clearDynamic() {
if (!SpringMVCUtil.isWeb()) {
TEMP_DYNAMIC_TENANT.remove();
return;
}
String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
RedisUtils.deleteObject(cacheKey);
SaHolder.getStorage().delete(cacheKey);
}
/**
* id()
*/
public static String getTenantId() {
String tenantId = TenantHelper.getDynamic();
if (StringUtils.isBlank(tenantId)) {
tenantId = LoginHelper.getTenantId();
}
return tenantId;
}
}

View File

@ -0,0 +1,32 @@
package cc.iotkit.common.tenant.manager;
import cc.iotkit.common.constant.GlobalConstants;
import cc.iotkit.common.redis.manager.PlusSpringCacheManager;
import cc.iotkit.common.tenant.helper.TenantHelper;
import cc.iotkit.common.utils.StringUtils;
import org.springframework.cache.Cache;
/**
* cacheName
*
* @author Lion Li
*/
public class TenantSpringCacheManager extends PlusSpringCacheManager {
public TenantSpringCacheManager() {
}
@Override
public Cache getCache(String name) {
if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
return super.getCache(name);
}
String tenantId = TenantHelper.getTenantId();
if (StringUtils.startsWith(name, tenantId)) {
// 如果存在则直接返回
return super.getCache(name);
}
return super.getCache(tenantId + ":" + name);
}
}

Some files were not shown because too many files have changed in this diff Show More