filtersMap = new LinkedHashMap<>();
+ filtersMap.put("authc",hpeisFormAuthenticationFilter());
+ shiroFilterFactoryBean.setFilters(filtersMap);
+
+ filterChainDefinitionMap.put("/**", "authc");
+ shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
+
+ return shiroFilterFactoryBean;
+ }
+
+ /**
+ * 凭证匹配器
+ * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
+ * )
+ *
+ * @return
+ */
+ @Bean
+ public HashedCredentialsMatcher hashedCredentialsMatcher() {
+ HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
+ hashedCredentialsMatcher.setHashAlgorithmName("SHA-1");//散列算法:这里使用MD5算法;
+ hashedCredentialsMatcher.setHashIterations(1024);//散列的次数,比如散列两次,相当于 md5(md5(""));
+ return hashedCredentialsMatcher;
+ }
+
+ @Bean
+ public HpeisShiroRealm hpeisShiroRealm() {
+ HpeisShiroRealm shiroRealm = new HpeisShiroRealm();
+ shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
+ return shiroRealm;
+ }
+
+
+ @Bean
+ public SecurityManager securityManager() {
+ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
+ securityManager.setRealm(hpeisShiroRealm());
+ // 自定义session管理 使用redis
+ securityManager.setSessionManager(sessionManager());
+ // 自定义缓存实现 使用redis
+ //securityManager.setCacheManager(cacheManager());
+ return securityManager;
+ }
+
+ //自定义sessionManager
+ @Bean
+ public SessionManager sessionManager() {
+ HpeisSessionManager sessionManager = new HpeisSessionManager();
+ sessionManager.setSessionDAO(redisSessionDAO());
+ sessionManager.setSessionIdUrlRewritingEnabled(false);
+ sessionManager.setGlobalSessionTimeout(21600000L);
+ return sessionManager;
+ }
+
+ /**
+ * 配置shiro redisManager
+ *
+ * 使用的是shiro-redis开源插件
+ *
+ * @return
+ */
+
+
+ public RedisManager redisManager() {
+ // 设置redis配置信息
+ RedisManager redisManager = new RedisManager();
+ redisManager.setHost(env.getProperty("spring.redis.host"));
+ redisManager.setPassword(env.getProperty("spring.redis.password"));
+ return redisManager;
+ }
+
+ /**
+ * cacheManager 缓存 redis实现
+ *
+ * 使用的是shiro-redis开源插件
+ *
+ * @return
+ */
+
+
+ @Bean
+ public RedisCacheManager cacheManager() {
+ RedisCacheManager redisCacheManager = new RedisCacheManager();
+ redisCacheManager.setRedisManager(redisManager());
+ return redisCacheManager;
+ }
+
+ /**
+ * RedisSessionDAO shiro sessionDao层的实现 通过redis
+ *
+ * 使用的是shiro-redis开源插件
+ */
+
+
+ @Bean
+ public RedisSessionDAO redisSessionDAO() {
+ RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
+ redisSessionDAO.setRedisManager(redisManager());
+ redisSessionDAO.setExpire(21600);
+// Custom your redis key prefix for session management, if you doesn't define this parameter,
+// shiro-redis will use 'shiro_redis_session:' as default prefix
+// redisSessionDAO.setKeyPrefix("");
+ return redisSessionDAO;
+ }
+
+ /**
+ * 开启shiro aop注解支持.
+ * 使用代理方式;所以需要开启代码支持;
+ *
+ * @param securityManager
+ * @return
+ */
+ @Bean
+ public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
+ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
+ authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
+ return authorizationAttributeSourceAdvisor;
+ }
+
+ /**
+ * Shiro生命周期处理器
+ * @return
+ */
+ @Bean
+ public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
+ return new LifecycleBeanPostProcessor();
+ }
+//
+// /**
+// * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
+// * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
+// *
+// * @return
+// */
+// @Bean
+// @DependsOn({"lifecycleBeanPostProcessor"})
+// public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
+// DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
+// advisorAutoProxyCreator.setProxyTargetClass(true);
+// return advisorAutoProxyCreator;
+// }
+
+ @Bean
+ public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
+ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
+ authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
+ return authorizationAttributeSourceAdvisor;
+ }
+
+ public FormAuthenticationFilter hpeisFormAuthenticationFilter(){
+ FormAuthenticationFilter formAuthenticationFilter = new ShiroLoginFilter();
+ return formAuthenticationFilter;
+ }
+
+ @Bean
+ public FilterRegistrationBean someFilterRegistration() {
+ FilterRegistrationBean registration = new FilterRegistrationBean();
+ FormAuthenticationFilter hpeisFormAuthenticationFilter = new ShiroLoginFilter();
+ registration.setFilter(hpeisFormAuthenticationFilter);
+ registration.setEnabled(false);
+ return registration;
+ }
+
+
+
+
+ /**
+ * 注册全局异常处理
+ *
+ * @return
+ */
+ /*@Bean(name = "exceptionHandler")
+ public HandlerExceptionResolver handlerExceptionResolver() {
+ return new HpeisExceptionHandler();
+ }*/
+}
diff --git a/src/main/java/com/rymcu/vertical/config/ShiroLoginFilter.java b/src/main/java/com/rymcu/vertical/config/ShiroLoginFilter.java
new file mode 100644
index 0000000..1f54a53
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/config/ShiroLoginFilter.java
@@ -0,0 +1,72 @@
+package com.rymcu.vertical.config;
+
+
+import com.alibaba.fastjson.JSONObject;
+import com.rymcu.vertical.core.result.GlobalResultGenerator;
+import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Created by wanwh on 2019/1/24 0024.
+ */
+public class ShiroLoginFilter extends FormAuthenticationFilter {
+
+ private static final Logger log = LoggerFactory.getLogger(FormAuthenticationFilter.class);
+
+ /**
+ * 在访问controller前判断是否登录,返回json,不进行重定向。
+ * @param request
+ * @param response
+ * @return true-继续往下执行,false-该filter过滤器已经处理,不继续执行其他过滤器
+ * @throws Exception
+ */
+
+ @Override
+ protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
+ HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+ if (this.isLoginRequest(request, response)) {
+ if (this.isLoginSubmission(request, response)) {
+ if (log.isTraceEnabled()) {
+ log.trace("Login submission detected. Attempting to execute login.");
+ }
+
+ return this.executeLogin(request, response);
+ } else {
+ if (log.isTraceEnabled()) {
+ log.trace("Login page view.");
+ }
+
+ return true;
+ }
+ }else if(isAjax((HttpServletRequest) request)){
+ httpServletResponse.setContentType("application/json");
+ httpServletResponse.setCharacterEncoding("UTF-8");
+ httpServletResponse.setHeader("sessionstatus", "timeOut");
+ httpServletResponse.addHeader("loginPath", this.getLoginUrl());
+ httpServletResponse.getWriter().write(JSONObject.toJSONString(GlobalResultGenerator.genErrorResult("未登录或已登录超时,请重新登录"),true));
+ return false;
+ }else {
+ if (log.isTraceEnabled()) {
+ log.trace("Attempting to access a path which requires authentication. Forwarding to the Authentication url [" + this.getLoginUrl() + "]");
+ }
+
+ this.saveRequestAndRedirectToLogin(request, response);
+ return false;
+ }
+ }
+
+ private boolean isAjax(HttpServletRequest request) {
+ String requestedWith = request.getHeader("x-requested-with");
+ if (requestedWith != null && requestedWith.equalsIgnoreCase("XMLHttpRequest")) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/com/rymcu/vertical/config/UsernamePasswordToken.java b/src/main/java/com/rymcu/vertical/config/UsernamePasswordToken.java
new file mode 100644
index 0000000..863d4d1
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/config/UsernamePasswordToken.java
@@ -0,0 +1,36 @@
+package com.rymcu.vertical.config;
+
+/**
+ * 用户和密码(包含验证码)令牌类
+ */
+public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken {
+
+ private static final long serialVersionUID = 1L;
+
+ private String captcha;
+ private boolean mobileLogin;
+
+ public UsernamePasswordToken() {
+ super();
+ }
+
+ public UsernamePasswordToken(String username, char[] password,
+ boolean rememberMe, String host, String captcha, boolean mobileLogin) {
+ super(username, password, rememberMe, host);
+ this.captcha = captcha;
+ this.mobileLogin = mobileLogin;
+ }
+
+ public String getCaptcha() {
+ return captcha;
+ }
+
+ public void setCaptcha(String captcha) {
+ this.captcha = captcha;
+ }
+
+ public boolean isMobileLogin() {
+ return mobileLogin;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/rymcu/vertical/config/WebMvcConfigurer.java b/src/main/java/com/rymcu/vertical/config/WebMvcConfigurer.java
new file mode 100644
index 0000000..11c0972
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/config/WebMvcConfigurer.java
@@ -0,0 +1,118 @@
+package com.rymcu.vertical.config;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.alibaba.fastjson.support.config.FastJsonConfig;
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import com.rymcu.vertical.core.result.GlobalResult;
+import com.rymcu.vertical.jwt.aop.RestAuthTokenInterceptor;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Spring MVC 配置
+ */
+@Configuration
+public class WebMvcConfigurer extends WebMvcConfigurationSupport {
+
+ private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class);
+// @Value("${env}")
+// private String env;//当前激活的配置文件
+
+ @Override
+ public void extendMessageConverters(List> converters) {
+ FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
+ FastJsonConfig config = new FastJsonConfig();
+ config.setSerializerFeatures(SerializerFeature.WriteMapNullValue,//保留空的字段
+ SerializerFeature.WriteNullStringAsEmpty);//String null -> ""
+ //SerializerFeature.WriteNullNumberAsZero);//Number null -> 0
+ config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect); //关闭循环引用
+ converter.setFastJsonConfig(config);
+ converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON));
+ converter.setDefaultCharset(Charset.forName("UTF-8"));
+ converters.add(0, converter);
+ }
+
+ //解决跨域问题
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/**")
+ .allowedOrigins("*")
+ .allowCredentials(true)
+ .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH");
+ }
+
+ @Bean
+ public RestAuthTokenInterceptor restAuthTokenInterceptor() {
+ return new RestAuthTokenInterceptor();
+ }
+
+ //添加拦截器
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ // TODO 先不拦截接口,进行测试
+ registry.addInterceptor(restAuthTokenInterceptor()).addPathPatterns("/api/**")
+ .excludePathPatterns("/api/login/**", "/api/logout","/api/member/*",
+ "/api/home/*","/api/item/**","/api/searchHistory/**", "/api/pay/payNotify",
+ "/api/mall/oauth/**","/api/express/**","/api/person/**");
+
+ }
+
+ private void responseResult(HttpServletResponse response, GlobalResult result) {
+ response.setCharacterEncoding("UTF-8");
+ response.setHeader("Content-type", "application/json;charset=UTF-8");
+ response.setStatus(200);
+ try {
+ response.getWriter().write(JSON.toJSONString(result));
+ } catch (IOException ex) {
+ logger.error(ex.getMessage());
+ }
+ }
+
+ /**
+ * 一个简单的签名认证,规则:
+ * 1. 将请求参数按ascii码排序
+ * 2. 拼接为a=value&b=value...这样的字符串(不包含sign)
+ * 3. 混合密钥(secret)进行md5获得签名,与请求的签名进行比较
+ */
+ private boolean validateSign(HttpServletRequest request) {
+ String requestSign = request.getParameter("sign");//获得请求签名,如sign=19e907700db7ad91318424a97c54ed57
+ if (StringUtils.isEmpty(requestSign)) {
+ return false;
+ }
+ List keys = new ArrayList(request.getParameterMap().keySet());
+ keys.remove("sign");//排除sign参数
+ Collections.sort(keys);//排序
+
+ StringBuilder sb = new StringBuilder();
+ for (String key : keys) {
+ sb.append(key).append("=").append(request.getParameter(key)).append("&");//拼接字符串
+ }
+ String linkString = sb.toString();
+ linkString = StringUtils.substring(linkString, 0, linkString.length() - 1);//去除最后一个'&'
+
+ String secret = "Potato";//密钥,自己修改
+ String sign = DigestUtils.md5Hex(linkString + secret);//混合密钥md5
+
+ return StringUtils.equals(sign, requestSign);//比较
+ }
+}
diff --git a/src/main/java/com/rymcu/vertical/core/constant/ProjectConstant.java b/src/main/java/com/rymcu/vertical/core/constant/ProjectConstant.java
new file mode 100644
index 0000000..96eb072
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/constant/ProjectConstant.java
@@ -0,0 +1,17 @@
+package com.rymcu.vertical.core.constant;
+
+/**
+ * 项目常量
+ */
+public final class ProjectConstant {
+ public static final String BASE_PACKAGE = "com.rymcu.vertical";//项目基础包名称,根据自己公司的项目修改
+
+ public static final String DTO_PACKAGE = BASE_PACKAGE + ".dto";//DTO所在包
+ public static final String MODEL_PACKAGE = BASE_PACKAGE + ".entity";//Model所在包
+ public static final String MAPPER_PACKAGE = BASE_PACKAGE + ".mapper";//Mapper所在包
+ public static final String SERVICE_PACKAGE = BASE_PACKAGE + ".service";//Service所在包
+ public static final String SERVICE_IMPL_PACKAGE = SERVICE_PACKAGE + ".impl";//ServiceImpl所在包
+ public static final String CONTROLLER_PACKAGE = BASE_PACKAGE + ".web";//Controller所在包
+
+ public static final String MAPPER_INTERFACE_REFERENCE = BASE_PACKAGE + ".core.mapper.Mapper";//Mapper插件基础接口的完全限定名
+}
diff --git a/src/main/java/com/rymcu/vertical/core/constant/ShiroConstants.java b/src/main/java/com/rymcu/vertical/core/constant/ShiroConstants.java
new file mode 100644
index 0000000..22ff54d
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/constant/ShiroConstants.java
@@ -0,0 +1,63 @@
+package com.rymcu.vertical.core.constant;
+
+/**
+ * Shiro通用常量
+ *
+ */
+public interface ShiroConstants
+{
+ /**
+ * 当前登录的用户
+ */
+ public static final String CURRENT_USER = "currentUser";
+
+ /**
+ * 用户名
+ */
+ public static final String CURRENT_USERNAME = "username";
+
+ /**
+ * 消息key
+ */
+ public static String MESSAGE = "message";
+
+ /**
+ * 错误key
+ */
+ public static String ERROR = "errorMsg";
+
+ /**
+ * 编码格式
+ */
+ public static String ENCODING = "UTF-8";
+
+ /**
+ * 当前在线会话
+ */
+ public String ONLINE_SESSION = "online_session";
+
+ /**
+ * 验证码key
+ */
+ public static final String CURRENT_CAPTCHA = "captcha";
+
+ /**
+ * 验证码开关
+ */
+ public static final String CURRENT_ENABLED = "captchaEnabled";
+
+ /**
+ * 验证码开关
+ */
+ public static final String CURRENT_TYPE = "captchaType";
+
+ /**
+ * 验证码
+ */
+ public static final String CURRENT_VALIDATECODE = "validateCode";
+
+ /**
+ * 验证码错误
+ */
+ public static final String CAPTCHA_ERROR = "captchaError";
+}
diff --git a/src/main/java/com/rymcu/vertical/core/exception/CaptchaException.java b/src/main/java/com/rymcu/vertical/core/exception/CaptchaException.java
new file mode 100644
index 0000000..463ca5c
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/exception/CaptchaException.java
@@ -0,0 +1,17 @@
+package com.rymcu.vertical.core.exception;
+
+import org.apache.shiro.authc.AuthenticationException;
+
+/**
+ * 验证码错误异常类
+ *
+ */
+public class CaptchaException extends AuthenticationException
+{
+ private static final long serialVersionUID = 1L;
+
+ public CaptchaException()
+ {
+ super("验证码不正确");
+ }
+}
diff --git a/src/main/java/com/rymcu/vertical/core/exception/ServiceException.java b/src/main/java/com/rymcu/vertical/core/exception/ServiceException.java
new file mode 100644
index 0000000..fc95680
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/exception/ServiceException.java
@@ -0,0 +1,47 @@
+package com.rymcu.vertical.core.exception;
+
+
+import com.rymcu.vertical.core.result.ResultCode;
+
+/**
+ * 服务(业务)异常如“ 账号或密码错误 ”,该异常只做INFO级别的日志记录 @see WebMvcConfigurer
+ */
+public class ServiceException extends Exception {
+ private int code;
+ private String extraMessage;
+
+ public ServiceException(ResultCode resultCode) {
+ super(resultCode.getMessage());
+ this.code=resultCode.getCode();
+ }
+
+
+ public ServiceException(String message, Throwable cause) {
+
+ }
+ public ServiceException(int code, String message, String extraMessage, Throwable cause){
+ super(message,cause);
+ this.code=code;
+ this.extraMessage=extraMessage;
+ }
+
+
+
+ public ServiceException(ResultCode resultCode, String extraMessage){
+ this(resultCode.getCode(),resultCode.getMessage(),extraMessage,null);
+ }
+
+ public ServiceException(String extraMessage){
+ this(ResultCode.INVALID_PARAM,extraMessage);
+ }
+
+
+ public int getCode() {
+ return code;
+ }
+
+ public String getExtraMessage() {
+ return extraMessage;
+ }
+
+}
diff --git a/src/main/java/com/rymcu/vertical/core/mapper/Mapper.java b/src/main/java/com/rymcu/vertical/core/mapper/Mapper.java
new file mode 100644
index 0000000..dc9744a
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/mapper/Mapper.java
@@ -0,0 +1,17 @@
+package com.rymcu.vertical.core.mapper;
+
+import tk.mybatis.mapper.common.BaseMapper;
+import tk.mybatis.mapper.common.ConditionMapper;
+import tk.mybatis.mapper.common.IdsMapper;
+import tk.mybatis.mapper.common.special.InsertListMapper;
+
+/**
+ * 定制版MyBatis Mapper插件接口,如需其他接口参考官方文档自行添加。
+ */
+public interface Mapper
+ extends
+ BaseMapper,
+ ConditionMapper,
+ IdsMapper,
+ InsertListMapper {
+}
diff --git a/src/main/java/com/rymcu/vertical/core/mapper/TreeMapper.java b/src/main/java/com/rymcu/vertical/core/mapper/TreeMapper.java
new file mode 100644
index 0000000..e339783
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/mapper/TreeMapper.java
@@ -0,0 +1,21 @@
+package com.rymcu.vertical.core.mapper;
+
+import java.util.List;
+
+public interface TreeMapper extends Mapper {
+
+ /**
+ * 找到所有子节点
+ * @param entity
+ * @return
+ */
+ public List findByParentIdsLike(T entity);
+
+ /**
+ * 更新所有父节点字段
+ * @param entity
+ * @return
+ */
+ public int updateParentIds(T entity);
+
+}
diff --git a/src/main/java/com/rymcu/vertical/core/result/GlobalResult.java b/src/main/java/com/rymcu/vertical/core/result/GlobalResult.java
new file mode 100644
index 0000000..c1cb6d4
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/result/GlobalResult.java
@@ -0,0 +1,19 @@
+package com.rymcu.vertical.core.result;
+
+import lombok.Data;
+
+@Data
+public class GlobalResult {
+ private boolean success = false;
+ private T data;
+ private int code;
+ private String message;
+
+ public GlobalResult() {
+ }
+
+ public static GlobalResult newInstance() {
+ return new GlobalResult();
+ }
+
+}
diff --git a/src/main/java/com/rymcu/vertical/core/result/GlobalResultGenerator.java b/src/main/java/com/rymcu/vertical/core/result/GlobalResultGenerator.java
new file mode 100644
index 0000000..1e16b17
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/result/GlobalResultGenerator.java
@@ -0,0 +1,80 @@
+package com.rymcu.vertical.core.result;
+
+import com.rymcu.vertical.util.ErrorCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GlobalResultGenerator {
+ private static final Logger LOGGER = LoggerFactory.getLogger(GlobalResultGenerator.class);
+
+ /**
+ * normal
+ * @param success
+ * @param data
+ * @param message
+ * @param
+ * @return
+ */
+ public static GlobalResult genResult(boolean success, T data, String message) {
+ GlobalResult result = GlobalResult.newInstance();
+ result.setSuccess(success);
+ result.setData(data);
+ result.setMessage(message);
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("generate rest result:{}", result);
+ }
+ return result;
+ }
+
+ /**
+ * success
+ * @param data
+ * @param
+ * @return
+ */
+ public static GlobalResult genSuccessResult(T data) {
+
+ return genResult(true, data, null);
+ }
+
+ /**
+ * error message
+ * @param message error message
+ * @param
+ * @return
+ */
+ public static GlobalResult genErrorResult(String message) {
+
+ return genResult(false, null, message);
+ }
+
+ /**
+ * error
+ * @param error error enum
+ * @param
+ * @return
+ */
+ public static GlobalResult genErrorResult(ErrorCode error) {
+
+ return genErrorResult(error.getMessage());
+ }
+
+ /**
+ * success no message
+ * @return
+ */
+ public static GlobalResult genSuccessResult() {
+ return genSuccessResult(null);
+ }
+
+ /**
+ * success
+ * @param
+ * @return
+ */
+ public static GlobalResult genSuccessResult(String message) {
+
+ return genResult(true, null, message);
+ }
+
+}
diff --git a/src/main/java/com/rymcu/vertical/core/result/ResultCode.java b/src/main/java/com/rymcu/vertical/core/result/ResultCode.java
new file mode 100644
index 0000000..0994c2a
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/result/ResultCode.java
@@ -0,0 +1,33 @@
+package com.rymcu.vertical.core.result;
+
+/**
+ * 响应码枚举,参考HTTP状态码的语义
+ */
+public enum ResultCode {
+ SUCCESS(1, "SUCCESS"),//成功
+ FAIL(400, "访问失败"),//失败
+ UNAUTHORIZED(401, "签名错误"),//未认证(签名错误)
+ NOT_FOUND(404, "此接口不存在"),//接口不存在
+ INTERNAL_SERVER_ERROR(500, "系统繁忙,请稍后再试"),//服务器内部错误
+ INVALID_PARAM(10000, "参数错误"),
+
+
+
+ ;
+ private int code;
+ private String message;
+
+ ResultCode(int code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+
+ public int getCode() {
+ return code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/src/main/java/com/rymcu/vertical/core/service/AbstractService.java b/src/main/java/com/rymcu/vertical/core/service/AbstractService.java
new file mode 100644
index 0000000..ebcab81
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/service/AbstractService.java
@@ -0,0 +1,77 @@
+package com.rymcu.vertical.core.service;
+
+
+import com.rymcu.vertical.core.exception.ServiceException;
+import com.rymcu.vertical.core.mapper.Mapper;
+import org.apache.ibatis.exceptions.TooManyResultsException;
+import org.springframework.beans.factory.annotation.Autowired;
+import tk.mybatis.mapper.entity.Condition;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.util.List;
+
+/**
+ * 基于通用MyBatis Mapper插件的Service接口的实现
+ */
+public abstract class AbstractService implements Service {
+
+ @Autowired
+ protected Mapper mapper;
+
+ private Class modelClass; // 当前泛型真实类型的Class
+
+ public AbstractService() {
+ ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
+ modelClass = (Class) pt.getActualTypeArguments()[0];
+ }
+
+ public void save(T model) {
+ mapper.insertSelective(model);
+ }
+
+ public void save(List models) {
+ mapper.insertList(models);
+ }
+
+ public void deleteById(String id) {
+ mapper.deleteByPrimaryKey(id);
+ }
+
+ public void deleteByIds(String ids) {
+ mapper.deleteByIds(ids);
+ }
+
+ public void update(T model) {
+ mapper.updateByPrimaryKeySelective(model);
+ }
+
+ public T findById(String id) {
+ return mapper.selectByPrimaryKey(id);
+ }
+
+ @Override
+ public T findBy(String fieldName, Object value) throws TooManyResultsException, ServiceException {
+ try {
+ T model = modelClass.newInstance();
+ Field field = modelClass.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(model, value);
+ return mapper.selectOne(model);
+ } catch (ReflectiveOperationException e) {
+ throw new ServiceException(e.getMessage(), e);
+ }
+ }
+
+ public List findByIds(String ids) {
+ return mapper.selectByIds(ids);
+ }
+
+ public List findByCondition(Condition condition) {
+ return mapper.selectByCondition(condition);
+ }
+
+ public List findAll() {
+ return mapper.selectAll();
+ }
+}
diff --git a/src/main/java/com/rymcu/vertical/core/service/LogService.java b/src/main/java/com/rymcu/vertical/core/service/LogService.java
new file mode 100644
index 0000000..cb15af0
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/service/LogService.java
@@ -0,0 +1,12 @@
+package com.rymcu.vertical.core.service;
+
+
+
+/**
+ * 体检者日志接口
+ */
+public interface LogService {
+
+ //void log(LogInfo logInfo);
+
+}
diff --git a/src/main/java/com/rymcu/vertical/core/service/Service.java b/src/main/java/com/rymcu/vertical/core/service/Service.java
new file mode 100644
index 0000000..c09523f
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/service/Service.java
@@ -0,0 +1,23 @@
+package com.rymcu.vertical.core.service;
+
+import com.rymcu.vertical.core.exception.ServiceException;
+import org.apache.ibatis.exceptions.TooManyResultsException;
+import tk.mybatis.mapper.entity.Condition;
+
+import java.util.List;
+
+/**
+ * Service 层 基础接口,其他Service 接口 请继承该接口
+ */
+public interface Service {
+ void save(T model);//持久化
+ void save(List models);//批量持久化
+ void deleteById(String id);//通过主鍵刪除
+ void deleteByIds(String ids);//批量刪除 eg:ids -> “1,2,3,4”
+ void update(T model);//更新
+ T findById(String id);//通过ID查找
+ T findBy(String fieldName, Object value) throws TooManyResultsException, ServiceException; //通过Model中某个成员变量名称(非数据表中column的名称)查找,value需符合unique约束
+ List findByIds(String ids);//通过多个ID查找//eg:ids -> “1,2,3,4”
+ List findByCondition(Condition condition);//根据条件查找
+ List findAll();//获取所有
+}
diff --git a/src/main/java/com/rymcu/vertical/core/service/dynProps4Files/DynProps4FilesService.java b/src/main/java/com/rymcu/vertical/core/service/dynProps4Files/DynProps4FilesService.java
new file mode 100644
index 0000000..21ace24
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/service/dynProps4Files/DynProps4FilesService.java
@@ -0,0 +1,347 @@
+package com.rymcu.vertical.core.service.dynProps4Files;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 动态配置文件,可以设置更新周期
+ * 配置读取读取服务
+ */
+@Component
+public class DynProps4FilesService {
+ private Logger _log = LoggerFactory.getLogger(DynProps4FilesService.class);
+ /**
+ * 属性文件
+ */
+ private File[] fileArray;
+ /**
+ * 启动延时
+ */
+ private long delay;
+ /**
+ * 更新周期
+ */
+ private long period;
+ /**
+ * 属性对象
+ */
+ private Properties property = new Properties();
+ /**
+ * 文件监控器
+ */
+ private List monitors;
+
+ /**
+ * @param files 属性文件
+ * @param delay 从DynProps
被创建到第一次动态监视的时间间隔. 约束范围delay > 0
+ * @param period 动态监视的时间间隔. 约束范围period >= 0;等于0表示不执行动态监视,退化为静态配置文件.
+ */
+ public DynProps4FilesService(File[] files, long delay, long period) throws IOException {
+ this.fileArray = files;
+ this.delay = delay;
+ this.period = period;
+ init();
+ }
+
+ public DynProps4FilesService(List fileNames, long delay, long period) throws IOException {
+ this.delay = delay;
+ this.period = period;
+ fileArray = new File[fileNames.size()];
+ int index = 0;
+ for (String oriFileName : fileNames) {
+ String fileName = oriFileName.trim();
+ if (StringUtils.indexOfIgnoreCase(fileName, "classpath:") == 0) {
+ fileArray[index++] = new File(
+ this.getClass().getClassLoader().getResource("").getPath() + File.separator +
+ fileName.substring("classpath:".length()));
+ } else {
+ fileArray[index++] = new File(fileName);
+ }
+ }
+
+ init();
+ }
+
+
+ public DynProps4FilesService(String fileNames, long delay, long period) throws IOException {
+ this.delay = delay;
+ this.period = period;
+ boolean isClassPath = false;
+ if (fileNames.startsWith("classpath")) {
+ fileNames = fileNames.substring("classpath:".length());
+ isClassPath = true;
+ }
+ String[] fileName = fileNames.split("[,|,|;|;]");
+ fileArray = new File[fileName.length];
+ if (isClassPath) {
+ for (int i = 0; i < fileName.length; i++) {
+ fileArray[i] = new File(this.getClass().getClassLoader().getResource("").getPath() + fileName[i]);
+ }
+ } else {
+ for (int i = 0; i < fileName.length; i++) {
+ fileArray[i] = new File(fileName[i]);
+ }
+ }
+
+ init();
+ }
+
+ public DynProps4FilesService(File[] files, long period) throws IOException {
+ this(files, 0, period);
+ }
+
+ public DynProps4FilesService(String fileNames, long period) throws IOException {
+ this.period = period;
+ this.delay = 0;
+ String[] fileName = fileNames.split("[,|,|;|;]");
+
+ File[] files = new File[fileName.length];
+ for (int i = 0; i < fileName.length; i++) {
+ files[i] = new File(fileName[i]);
+ }
+ init();
+ }
+
+ public DynProps4FilesService() {
+ }
+
+ /**
+ * 加载属性文件,启动监控
+ *
+ * @throws IOException 加载文件时出现IO异常
+ */
+ protected void load() throws IOException {
+ update();
+ if (monitors == null) {
+ monitors = new ArrayList(fileArray.length);
+ } else {
+ for (FileMonitor monitor : monitors) {
+ try {
+ monitor.timer.cancel();
+ } catch (Exception e) {
+ _log.warn(String.format("Timer for file [%s] cancelling failed.", monitor.file.getAbsolutePath()));
+ }
+ }
+ }
+
+ for (File file : fileArray) {
+ long lastModify = file.lastModified();
+ FileMonitor monitor = new FileMonitor(file, lastModify);
+ this.monitors.add(monitor);
+ monitor.doTask();
+ }
+ }
+
+ /**
+ * 如果文件有更新调用此方法载入
+ *
+ * @throws IOException 没有找到文件或读文件错误时抛出
+ */
+ protected void update() throws IOException {
+ for (File file : fileArray) {
+ InputStream in = null;
+ try {
+ in = new FileInputStream(file);
+ this.property.load(in);
+ } catch (Exception e) {
+ if (e instanceof IOException) {
+ throw (IOException) e;
+ }
+
+ throw new IOException(e);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+ }
+
+ /**
+ * @param key 需要获取属性值的KEY
+ * @param def 默认值
+ *
+ * @return 属性值
+ */
+ public String getProperty(String key, String def) {
+ String val = this.property.getProperty(key);
+ return val == null ? def : val.trim();
+ }
+
+ public String getProperty(String key) {
+ String val = this.property.getProperty(key);
+ return val == null ? null : val.trim();
+ }
+
+ /**
+ * 设置属性值
+ *
+ * @param key
+ * @param value
+ */
+ public void setProperty(String key, String value) {
+ this.property.setProperty(key, value);
+ }
+
+ /**
+ * @param key 需要获取属性值的KEY
+ * @param def 默认值
+ *
+ * @return 属性值
+ *
+ * @throws NumberFormatException 如果属性值不是整数形式
+ */
+ public int getInt(String key, int def) throws NumberFormatException {
+ String val = this.getProperty(key);
+ return val == null ? def : Integer.parseInt(val);
+ }
+
+ public int getInt(String key) throws NumberFormatException {
+ return getInt(key, 0);
+ }
+
+ public float getFloat(String key, float def) throws NumberFormatException {
+ String val = this.getProperty(key);
+ return val == null ? def : Float.parseFloat(val);
+ }
+
+ public float getFloat(String key) throws NumberFormatException {
+ return getFloat(key, 0.0f);
+ }
+
+ public double getDouble(String key, double def) {
+ String val = this.getProperty(key);
+ return val == null ? def : Double.parseDouble(val);
+ }
+ public double getDouble(String key) throws NumberFormatException {
+ return getDouble(key,0.0);
+ }
+
+
+
+ public long getLong(String key, long def) {
+ String val = this.getProperty(key);
+ return val == null ? def : Long.parseLong(val);
+ }
+
+ public long getLong(String key) throws NumberFormatException {
+ return getLong(key, 0L);
+ }
+
+ private void init() throws IOException {
+ for (File file : fileArray) {
+ if (!file.exists() || file.length() == 0) {
+ throw new IllegalArgumentException("动态配置文件 " + file.getAbsolutePath() + " 不存在,或是空文件!");
+ }
+ if (delay <= 0) {
+ throw new IllegalArgumentException("定时器延时时间不能为负数!");
+ }
+ if (period <= 0) {
+ throw new IllegalArgumentException("定时器更新周期不能为负数!");
+ }
+ this.property = new Properties();
+ this.load();// 初始构造时,执行第一次加载.
+ }
+ //当进程终止时,取消定时任务
+ Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHook()));
+ }
+
+ private class ShutdownHook implements Runnable {
+ private DynProps4FilesService dynProps4FilesService;
+
+ @Override
+ public void run() {
+ System.out.println("Monitors cancelling start ...");
+ if (monitors != null) {
+ for (FileMonitor monitor : monitors) {
+ try {
+ monitor.timer.cancel();
+ } catch (Exception e) {
+ _log.warn(String.format("Timer for file [%s] cancelling failed.",
+ monitor.file.getAbsolutePath()));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 描述:一个内部私有类,实时监控文件有没有更新,如果更新则自动载入
+ */
+ private class FileMonitor {
+
+ private long lastModifiedTime;
+ private File file;
+ /**
+ * 定时器,以守护线程方式启动
+ */
+ private Timer timer = new Timer(true);
+
+ /**
+ * @param lastMonitorTime 最后的更新时间
+ */
+ private FileMonitor(File file, long lastMonitorTime) {
+ this.file = file;
+ this.lastModifiedTime = lastMonitorTime;
+ }
+
+ /**
+ * 对文件进行实时监控,有更新则自动载入
+ */
+ private void doTask() {
+ if (delay < 0) {
+ delay = 0L;
+ }
+ if (period <= 0) {
+ return;// 如果更新周期非正数,则退化成静态配置文件.
+ }
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ long t = file.lastModified();
+ // 文件被删除
+ // 如果动态更新过程中,配置文件被强制删除了,本次不执行任何更新.或者对配置文件进行恢复
+ if (t == 0) {
+ try {
+ if (file.createNewFile()) {
+ OutputStream fos = new FileOutputStream(file);
+ property.store(fos, "文件被删除,自动恢复.");
+ fos.close();
+ }
+ } catch (IOException ioe2) {
+ // 这里基本上只有磁盘空间满才会发生,暂时不处理
+ }
+ return;
+ }
+ // 文件被更新
+ if (t > lastModifiedTime) {
+ lastModifiedTime = t;
+ // 2秒后还在改变,则本次更新不做处理
+ try {
+ TimeUnit.SECONDS.sleep(2);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ if (t != file.lastModified()) {
+ _log.info("文件可能未更新完成,本次不更新!");
+ } else {
+ try {
+ property.clear();
+ update();
+ _log.info("UPDATED " + file.getAbsolutePath());
+ } catch (IOException ioe) {
+ _log.error("UPDATING " + file.getAbsolutePath() + " failed", ioe);
+ }
+ }
+ _log.debug("-----------------------:" + property.keySet());
+ }
+ }// end run()
+ }, delay, period);
+ }
+ }
+}
diff --git a/src/main/java/com/rymcu/vertical/core/service/redis/RedisResult.java b/src/main/java/com/rymcu/vertical/core/service/redis/RedisResult.java
new file mode 100644
index 0000000..7e63652
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/service/redis/RedisResult.java
@@ -0,0 +1,75 @@
+package com.rymcu.vertical.core.service.redis;
+
+import com.rymcu.vertical.entity.BaseDO;
+
+import java.util.List;
+
+/**
+ * redis 中取得的结果
+ * Created by liwei on 2017/2/7.
+ */
+public class RedisResult extends BaseDO {
+
+ /**
+ * redis中是否存在
+ */
+ private boolean exist = false;
+
+ /**
+ * redis中取得的数据
+ */
+ private T result;
+
+ /**
+ * redis中取得的List数据
+ */
+ private List listResult;
+ /**
+ * redis中的key是否存在。true:表示redis中存在Key,但对应的值为空值标记
+ */
+ private boolean keyExists = false;
+ /**
+ * redis中key 对应在对象值
+ */
+ private T resultObj;
+
+
+ public boolean isExist() {
+ return exist;
+ }
+
+ public void setExist(boolean exist) {
+ this.exist = exist;
+ }
+
+ public T getResult() {
+ return result;
+ }
+
+ public void setResult(T result) {
+ this.result = result;
+ }
+
+ public List getListResult() {
+ return listResult;
+ }
+
+ public void setListResult(List listResult) {
+ this.listResult = listResult;
+ }
+
+ public void setKeyExists(boolean keyExists) {
+ this.keyExists = keyExists;
+ }
+ public boolean isKeyExists() {
+ return keyExists;
+ }
+
+ public T getResultObj() {
+ return resultObj;
+ }
+
+ public void setResultObj(T resultObj) {
+ this.resultObj = resultObj;
+ }
+}
diff --git a/src/main/java/com/rymcu/vertical/core/service/redis/RedisService.java b/src/main/java/com/rymcu/vertical/core/service/redis/RedisService.java
new file mode 100644
index 0000000..ebe522e
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/service/redis/RedisService.java
@@ -0,0 +1,264 @@
+package com.rymcu.vertical.core.service.redis;
+
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.util.Set;
+
+/**
+ * Redis 服务接口
+ * Jimersy Lee
+ * 2017-09-18 14:58:21
+ */
+public interface RedisService {
+ /**
+ * NX: 当且仅当缓存中特定的key不存在时设定数据
+ */
+ String NXXX_SET_IF_NOT_EXISTS = "nx";
+ /**
+ * XX: 当且仅当缓存中特定的key存在时设定数据
+ */
+ String NXXX_SET_IF_EXISTS = "xx";
+
+ /**
+ * EX:缓存失效的时间单位:秒
+ */
+ String EXPX_SECONDS = "ex";
+
+ /**
+ * EX:缓存失效的时间单位:毫秒
+ */
+ String EXPX_MILLISECOND = "px";
+
+ /**
+ * 默认过期时间 ,3600(秒)
+ */
+ int DEFAULT_EXPIRE_TIME = 3600;
+
+ ObjectMapper om = new ObjectMapper();
+
+
+ /**
+ * 空白占位符
+ */
+ String BLANK_CONTENT = "__BLANK__";
+
+ /**
+ * 初始化操作
+ */
+ void init();
+
+
+ void destroy();
+
+ // /**
+ // * 从连接池里取连接(用完连接后必须销毁)
+ // *
+ // * @return
+ // */
+ // Jedis getResource();
+
+ // /**
+ // * 用完后,销毁连接(必须)
+ // *
+ // * @param jedis
+ // */
+ // void destroyResource(Jedis jedis);
+
+ /**
+ * 根据key取数据
+ *
+ * @param key
+ * @return
+ */
+ String get(String key);
+
+
+ /**
+ * 根据key取对象数据(不支持Collection数据类型)
+ *
+ * @param key
+ * @param clazz
+ * @return
+ */
+ T get(String key, Class clazz);
+
+
+ /**
+ * 根据key取对象数据(不支持Collection数据类型)
+ *
+ * @param key
+ * @param clazz
+ * @param
+ * @return
+ */
+ RedisResult getResult(String key, Class clazz);
+
+ /**
+ * 根据key取 Collection 对象数据
+ *
+ * @param key
+ * @param elementClazz 集合元素类型
+ * @param
+ * @return
+ */
+ RedisResult getListResult(String key, Class elementClazz);
+
+
+ /**
+ * 写入/修改 缓存内容
+ *
+ * @param key
+ * @param obj
+ * @return
+ */
+ String set(String key, Object obj);
+
+
+ /**
+ * 写入/修改 缓存内容
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ String set(String key, String value);
+
+
+ /**
+ * 写入/修改 缓存内容(无论key是否存在,均会更新key对应的值)
+ *
+ * @param key
+ * @param obj
+ * @param expireTime 缓存内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期
+ * @return
+ */
+ String set(String key, Object obj, int expireTime);
+
+
+ /**
+ * 写入/修改 缓存内容(无论key是否存在,均会更新key对应的值)
+ *
+ * @param key
+ * @param value
+ * @param expireTime 缓存内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期
+ * @return
+ */
+ String set(String key, String value, int expireTime);
+
+ /**
+ * 写入/修改 缓存内容
+ *
+ * @param key
+ * @param value
+ * @param nxxx 缓存写入值模式 详见 {@link RedisService#NXXX_SET_IF_EXISTS}, {@link RedisService#NXXX_SET_IF_NOT_EXISTS}
+ * @param expx 缓存超时时间单位 详见{@link RedisService#EXPX_SECONDS}, {@link RedisService#EXPX_MILLISECOND}
+ * @param expiredTime 缓存存活时长,必须 大于0
+ * @return
+ */
+ String set(String key, String value, String nxxx, String expx, long expiredTime);
+
+ /**
+ * 仅当redis中不含对应的key时,设定缓存内容
+ *
+ * @param key
+ * @param value
+ * @param expiredTime 缓存内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期
+ * @return
+ */
+ String setnx(String key, String value, long expiredTime);
+
+ /**
+ * 仅当redis中含有对应的key时,修改缓存内容
+ *
+ * @param key
+ * @param value
+ * @param expiredTime 缓存内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期
+ * @return
+ */
+ String setxx(String key, String value, long expiredTime);
+
+
+ /**
+ * 根据key删除缓存,
+ *
+ * @param keys
+ * @return
+ */
+ Long delete(String... keys);
+
+ /**
+ * 判断对应的key是否存在
+ *
+ * @param key
+ * @return
+ */
+ boolean exists(String key);
+
+
+ /**
+ * redis 加法运算
+ *
+ * @param key
+ * @param value
+ * @return 运算结果
+ */
+ Long incrBy(String key, long value);
+
+ /**
+ * 设定redis 对应的key的剩余存活时间
+ *
+ * @param key
+ * @param seconds
+ */
+ void setTTL(String key, int seconds);
+
+ /**
+ * 根据通配符表达式查询key值的set,通配符仅支持*
+ *
+ * @param pattern 如 ke6*abc等
+ * @return
+ */
+ Set keys(String pattern);
+
+ /**
+ * 将对象转为json字符串。若对象为null,则返回 {@link RedisService#BLANK_CONTENT}
+ *
+ * @param object
+ * @return
+ */
+ String toJsonString(Object object);
+
+ /**
+ * json序列化对象。
+ *
+ * @param value
+ * @return 返回序列化后的字符串。若value为null,则返回 {@link RedisService#BLANK_CONTENT}
+ */
+ String makeSerializedString(Object value);
+
+ /**
+ * 写入/修改 缓存内容(无论key是否存在,均会更新key对应的值)
+ *
+ * @param cacheName
+ * @param key
+ * @param value
+ * @return
+ */
+ String put(String cacheName, String key, Object value);
+
+ /**
+ * 写入/修改 缓存内容(无论key是否存在,均会更新key对应的值)
+ *
+ * @param cacheName
+ * @param key
+ * @param value
+ * @param expireTime 缓存 内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期
+ * @return
+ */
+ String put(String cacheName, String key, Object value, int expireTime);
+
+ Object get(String cacheName, String key);
+
+
+}
diff --git a/src/main/java/com/rymcu/vertical/core/service/redis/impl/RedisKeyHelper.java b/src/main/java/com/rymcu/vertical/core/service/redis/impl/RedisKeyHelper.java
new file mode 100644
index 0000000..d3fbb0b
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/service/redis/impl/RedisKeyHelper.java
@@ -0,0 +1,213 @@
+package com.rymcu.vertical.core.service.redis.impl;
+
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+/**
+ * Redis Key 辅助类
+ */
+@Component
+@Lazy(false)
+public class RedisKeyHelper {
+ /**
+ * 应用级别前缀
+ */
+ private static String SYS_PREFIX = "PROJECT_";
+ /**
+ * 用户信息前缀
+ */
+ private static String UIC_PREFIX = SYS_PREFIX + "UIC_";
+ /**
+ * 商品中心信息前缀
+ */
+ private static String IC_PREFIX = SYS_PREFIX + "IC_";
+ /**
+ * 交易中心信息前缀
+ */
+ private static String TC_PREFIX = SYS_PREFIX + "TC_";
+ /**
+ * 优惠中心信息前缀
+ */
+ private static String PROM_PREFIX = SYS_PREFIX + "PROM_";
+ /**
+ * 会员中心信息前缀
+ */
+ private static String MMB_PREFIX = SYS_PREFIX + "MMB_";
+ /**
+ * 分布式互斥锁前缀
+ */
+ private static String LOCK_PREFIX = SYS_PREFIX + "LOCK_";
+
+ /**
+ * 单次登录的默认有效时长(单位:秒)
+ */
+ public static final int DEFAULT_LOGIN_TIMEOUT = 3600 * 24 * 7;
+
+ /**
+ * 签到记录的默认有效时长(单位:秒)
+ */
+ public static final int MMB_SIGN_TIMEOUT = 3600 * 12;
+ /**
+ * redis中加息券信息的保存时间(单位:秒)
+ */
+ public static final int PROM_IRC_TIMEOUT = 60 * 3;
+ /**
+ * redis中借款人信息默认有效效时间(单位:秒)
+ */
+ public static final int BORROWER_EXPIRE_TIMEOUT = 60 * 3;//redis缓存失效时间
+
+
+ //商品中心KEY配置==begin========================================================================================
+ /**
+ * 商品(资产标)前缀
+ */
+ public static final String IC_ITEM_PREFIX = IC_PREFIX + "ITEM_";
+ /**
+ * 商品(资产标)列表前缀
+ */
+ public static final String IC_ITEM_LIST_PREFIX = IC_ITEM_PREFIX + "LIST_";
+ /**
+ * 商品已投金额或份数前缀
+ */
+ public static final String IC_ITEM_INVESTED_AMOUNT_PREFIX = IC_PREFIX + "INTESTED_AMT_";
+ public static final String IC_ITEM_DEAL_CREDITOR_PREFIX = IC_PREFIX + "DEAL_CREDITOR_";
+
+ //商品中心KEY配置==end==========================================================================================
+ //优惠中心KEY配置==begin========================================================================================
+ /**
+ * 加息券信息前缀
+ */
+ public static String PROM_IRC_PREFIX = PROM_PREFIX + "IRC_";
+
+ /**
+ * 优惠配置信息前缀
+ */
+ public static String PROM_CONFIG_PREFIX = PROM_PREFIX + "CONFIG_";
+ /**
+ * 红包列表信息前缀
+ */
+ public static final String PROM_COUPON_LIST_PREFIX = PROM_PREFIX + "COUPON_LIST_";
+ //优惠中心KEY配置==end==========================================================================================
+ //交易中心KEY配置==========================================================================================
+ public static final String TC_TRANS_ACCOUNT_PREFIX = TC_PREFIX + "ACC_";
+
+ public static final String TC_TRANS_CURRENT_DEAL_CONFIG_PREFIX = TC_PREFIX + "CURRENT_DEAL_CONFIG_";
+
+ public static final String TC_TRANS_EXPERIENCE_MONEY_CONFIG_PREFIX = TC_PREFIX + "EXPERIENCE_MONEY_CONFIG_";
+
+ public static final String TC_TRANS_CURRENT_DEAL_LOAD_PREFIX = TC_PREFIX + "CURRENT_DEAL_LOAD_";
+
+ public static final String TC_TRANS_CONST_DEAL_ORDER_LIST_PREFIX = TC_PREFIX + "CURRENT_CONST_DEAL_ORDER_LIST_";
+
+ /**
+ * 流水号redis key前缀
+ */
+ public static final String TC_TRANS_SEQ_PREFIX = TC_PREFIX + "SEQ_";
+
+ //交易中心KEY配置==end==========================================================================================
+ //用户中心KEY配置==========================================================================================
+ /**
+ * redis中登录用户token的key前缀
+ */
+ public static String LOGIN_TOKEN_KEY_PREFIX = UIC_PREFIX + "LOGIN_TOKEN_";
+ /**
+ * redis中登录用户USERID的key前缀
+ */
+ public static String LOGIN_UID_KEY_PREFIX = UIC_PREFIX + "LOGIN_UID_";
+ /**
+ * 用户信息前缀(手机号)
+ */
+ public static String UIC_MOBILE_PREFIX = UIC_PREFIX + "MOB_";
+ /**
+ * 用户角色前缀
+ */
+ public static String UIC_ROLE_PREFIX = UIC_PREFIX + "B_R_";
+ public static String UIC_ROLE_CANCEL_SUFFIX = UIC_PREFIX + "CNL_";
+ //用户中心KEY配置==end==========================================================================================
+
+ /**
+ * 构建分布式锁的key
+ *
+ * @param clazz
+ * @param key
+ * @return
+ */
+ public String makeLockKey(Class clazz, String key) {
+ return buildKeyString(LOCK_PREFIX, clazz.getSimpleName(), key);
+ }
+
+
+ /**
+ * 构建分布式锁的key
+ *
+ * @param key
+ * @return
+ */
+ public String makeLockKey(String key) {
+ return buildKeyString(LOCK_PREFIX, key);
+ }
+
+ /**
+ * 构造商品信息 redis key
+ *
+ * @param itemId
+ * @return
+ */
+ public String makeIcItemKey(long itemId) {
+ return buildKeyString(IC_ITEM_PREFIX, itemId);
+ }
+
+ /**
+ * 构造商品信息列表 redis key
+ *
+ * @param pageSize
+ * @param pageNo
+ * @return
+ */
+ public String makeIcItemListKey(int pageSize, int pageNo) {
+ return buildKeyString(IC_ITEM_LIST_PREFIX, pageSize, pageNo);
+ }
+
+ /**
+ * 构造资产标已投金额前缀
+ *
+ * @param itemId
+ * @return
+ */
+ public String makeInvestedAmountKey(long itemId) {
+ return buildKeyString(IC_ITEM_INVESTED_AMOUNT_PREFIX, itemId);
+ }
+
+ /**
+ * 构造交易中心流水号前缀
+ *
+ * @param flag
+ * @return
+ */
+ public String makeSeqKey(String flag) {
+ return buildKeyString(TC_TRANS_SEQ_PREFIX, flag);
+ }
+
+ public static String buildKeyString(Object... objs) {
+ if (objs == null || objs.length == 0) {
+ return "";
+ }
+ StringBuilder builder = new StringBuilder();
+ boolean isFirst = true;
+ for (Object obj : objs) {
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ builder.append("_");
+ }
+ if (obj instanceof Class) {
+ builder.append(((Class) obj).getName());
+ } else {
+ builder.append(obj);
+ }
+ }
+ return builder.toString();
+ }
+
+
+}
diff --git a/src/main/java/com/rymcu/vertical/core/service/redis/impl/RedisServiceImpl.java b/src/main/java/com/rymcu/vertical/core/service/redis/impl/RedisServiceImpl.java
new file mode 100644
index 0000000..5716c37
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/core/service/redis/impl/RedisServiceImpl.java
@@ -0,0 +1,577 @@
+package com.rymcu.vertical.core.service.redis.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import com.fasterxml.jackson.databind.JavaType;
+import com.rymcu.vertical.core.service.dynProps4Files.DynProps4FilesService;
+import com.rymcu.vertical.core.service.redis.RedisResult;
+import com.rymcu.vertical.core.service.redis.RedisService;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Redis 服务接口实现类
+ *
+ * @author liwei
+ * 16/10/30 下午5:28
+ */
+@Component("redisService")
+public class RedisServiceImpl implements RedisService {
+
+ private static final Logger logger = LoggerFactory.getLogger(RedisServiceImpl.class);
+
+
+ private static JedisPool pool = null;
+
+ @Resource
+ private DynProps4FilesService dynProps4Files;
+
+ @Resource
+ private Environment env;
+
+
+ /**
+ * 初始化操作
+ */
+ @Override
+ @PostConstruct
+ public void init() {
+ if (pool != null) {
+ return;
+ }
+ /*JedisPoolConfig config = new JedisPoolConfig();
+ config.setMaxIdle(dynProps4Files.getInt("REDIS_MAX_IDLE", JedisPoolConfig.DEFAULT_MAX_IDLE));
+ config.setMaxTotal(dynProps4Files.getInt("REDIS_MAX_TOTAL", JedisPoolConfig.DEFAULT_MAX_TOTAL));
+ config.setMaxWaitMillis(dynProps4Files.getLong("REDIS_MAX_WAIT", JedisPoolConfig.DEFAULT_MAX_WAIT_MILLIS));
+ config.setTestOnBorrow(true);
+ pool = new JedisPool(config, dynProps4Files.getProperty("REDIS_HOST"),
+ dynProps4Files.getInt("REDIS_PORT", 6379), dynProps4Files.getInt(
+ "REDIS_MAX_WAIT", 1000), dynProps4Files.getProperty("REDIS_PASSWORD", null));*/
+
+ JedisPoolConfig config = new JedisPoolConfig();
+ config.setMaxIdle(dynProps4Files.getInt("REDIS_MAX_IDLE", JedisPoolConfig.DEFAULT_MAX_IDLE));
+ config.setMaxTotal(dynProps4Files.getInt("REDIS_MAX_TOTAL", JedisPoolConfig.DEFAULT_MAX_TOTAL));
+ config.setMaxWaitMillis(dynProps4Files.getLong("REDIS_MAX_WAIT", JedisPoolConfig.DEFAULT_MAX_WAIT_MILLIS));
+ config.setTestOnBorrow(true);
+ pool = new JedisPool(config, env.getProperty("spring.redis.host"),
+ dynProps4Files.getInt("REDIS_PORT", 6379), dynProps4Files.getInt(
+ "REDIS_MAX_WAIT", 1000), dynProps4Files.getProperty("REDIS_PASSWORD", env.getProperty("spring.redis.password")));
+
+
+ }
+
+ @Override
+ @PreDestroy
+ public void destroy() {
+ try {
+ if (pool != null) {
+ pool.destroy();
+ }
+
+ } catch (Exception e) {
+ //do nothing
+ }
+ }
+
+ /**
+ * 从连接池里取连接(用完连接后必须销毁)
+ *
+ * @return
+ */
+ private Jedis getResource() {
+ return pool.getResource();
+
+ }
+
+ /**
+ * 用完后,销毁连接(必须)
+ *
+ * @param jedis
+ */
+ private void destroyResource(Jedis jedis) {
+ if (jedis == null) {
+ return;
+ }
+ IOUtils.closeQuietly(jedis);
+ }
+
+ /**
+ * 根据key取数据
+ *
+ * @param key
+ * @return
+ */
+ public String get(String key) {
+
+ if (StringUtils.isBlank(key)) {
+ logger.warn("Params key is blank!");
+ return StringUtils.EMPTY;
+ }
+ Jedis jedis = this.getResource();
+ try {
+ return jedis.get(key);
+ } finally {
+ this.destroyResource(jedis);
+ }
+
+ }
+
+ /**
+ * 根据key取对象数据(不支持Collection数据类型)
+ *
+ * @param key
+ * @param clazz
+ * @return
+ */
+ @Override
+ public T get(String key, Class clazz) {
+ if (StringUtils.isBlank(key)) {
+ logger.warn("Params key is blank!");
+ return null;
+ }
+ if (clazz == null) {
+ logger.warn("Params clazz is null!");
+ return null;
+ }
+ String value = get(key);
+ if (StringUtils.isBlank(value) || StringUtils.equalsIgnoreCase(value, BLANK_CONTENT)) {
+ return null;
+ }
+ T obj = null;
+ try {
+ obj = om.readValue(value, clazz);
+ } catch (IOException e) {
+ logger.error("Can not unserialize obj to [{}] with string [{}]", clazz.getName(), value);
+ }
+ return obj;
+ }
+
+ /**
+ * 写入/修改 缓存内容(无论key是否存在,均会更新key对应的值)
+ *
+ * @param key
+ * @param obj
+ * @param expireTime 缓存 内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期
+ * @return
+ */
+ public String set(String key, Object obj, int expireTime) {
+ String value = RedisService.BLANK_CONTENT;
+ if (obj != null) {
+ try {
+ value = RedisService.om.writeValueAsString(obj);
+ } catch (IOException e) {
+ logger.error("Can not write object to redis:" + obj.toString(), e);
+ }
+ }
+ return set(key, value, expireTime);
+ }
+
+ /**
+ * 写入/修改 缓存内容(无论key是否存在,均会更新key对应的值)
+ *
+ * @param key
+ * @param value
+ * @param expireTime 缓存 内容过期时间 (单位:秒) ,若expireTime小于0 则表示该内容不过期
+ * @return
+ */
+ public String set(String key, String value, int expireTime) {
+ if (StringUtils.isBlank(key)) {
+ logger.warn("Params key is blank!");
+ return null;
+ }
+
+ if (value == null) {
+
+ logger.warn("Params value is null!");
+ return null;
+ }
+
+ Jedis jedis = this.getResource();
+ try {
+ String result = jedis.set(key, value);
+ if (expireTime > 0) {
+ jedis.expire(key, expireTime);
+ }
+ return result;
+ } finally {
+ this.destroyResource(jedis);
+ }
+ }
+
+ /**
+ * 根据key取对象数据(不支持Collection数据类型)
+ *
+ * @param key
+ * @param clazz
+ * @return
+ */
+ @Override
+ public RedisResult getResult(String key, Class clazz) {
+ if (StringUtils.isBlank(key)) {
+ logger.warn("Params key is blank!");
+ return null;
+ }
+ if (clazz == null) {
+ logger.warn("Params clazz is null!");
+ return null;
+ }
+ RedisResult redisResult = new RedisResult();
+
+ String value = get(key);
+ if (StringUtils.isBlank(value)) {
+ redisResult.setExist(false);
+ return redisResult;
+ }
+ //到此步,则表明redis中存在key
+ redisResult.setExist(true);
+ if (StringUtils.equalsIgnoreCase(value, BLANK_CONTENT)) {
+ return redisResult;
+ }
+ T obj = null;
+ try {
+ obj = om.readValue(value, clazz);
+ redisResult.setResult(obj);
+ } catch (IOException e) {
+ logger.error("Can not unserialize obj to [{}] with string [{}]", clazz.getName(), value);
+ //到此步直接视为无值
+ redisResult.setExist(false);
+ }
+ return redisResult;
+ }
+
+ /**
+ * 根据key取 Collection 对象数据
+ *
+ * @param key
+ * @param elementClazz 集合元素类型
+ * @return
+ */
+ @Override
+ public RedisResult getListResult(String key, Class elementClazz) {
+ if (StringUtils.isBlank(key)) {
+ logger.warn("Params key is blank!");
+ return null;
+ }
+
+ if (elementClazz == null) {
+ logger.warn("Params elementClazz is null!");
+ return null;
+ }
+ RedisResult redisResult = new RedisResult();
+
+ String value = get(key);
+ if (StringUtils.isBlank(value)) {
+ redisResult.setExist(false);
+ return redisResult;
+ }
+
+ //到此步,则表明redis中存在key
+ redisResult.setExist(true);
+ if (StringUtils.equalsIgnoreCase(value, BLANK_CONTENT)) {
+ return redisResult;
+ }
+
+ List list = null;
+ try {
+ list = om.readValue(value, getCollectionType(List.class, elementClazz));
+ redisResult.setListResult(list);
+ } catch (IOException e) {
+ logger.error("Can not unserialize list to [{}] with string [{}]", elementClazz.getName(), value);
+ //到此步直接视为无值
+ redisResult.setExist(false);
+ }
+
+ return redisResult;
+ }
+
+ /**
+ * 写入/修改 缓存内容
+ *
+ * @param key
+ * @param obj
+ * @return
+ */
+ @Override
+ public String set(String key, Object obj) {
+ String value = RedisService.BLANK_CONTENT;
+ if (obj != null) {
+ try {
+ value = RedisService.om.writeValueAsString(obj);
+ } catch (IOException e) {
+ logger.error("Can not write object to redis:" + obj.toString(), e);
+ }
+ }
+ return set(key, value);
+ }
+
+ private static JavaType getCollectionType(Class extends Collection> collectionClazz,
+ Class elementClazz) {
+ return om.getTypeFactory().constructCollectionType(collectionClazz, elementClazz);
+ }
+
+
+ /**
+ * 写入/修改 缓存内容(默认有过期时间 1小时)
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ @Override
+ public String set(String key, String value) {
+ return this.set(key, value, DEFAULT_EXPIRE_TIME);
+ }
+
+
+ /**
+ * 写入/修改 缓存内容
+ *
+ * @param key
+ * @param value
+ * @param nxxx 缓存写入值模式 详见 {@link RedisService#NXXX_SET_IF_EXISTS}, {@link RedisService#NXXX_SET_IF_NOT_EXISTS}
+ * @param expx 缓存超时时间单位 详见{@link RedisService#EXPX_SECONDS}, {@link RedisService#EXPX_MILLISECOND}
+ * @param expiredTime 缓存存活时长,必须 大于0
+ * @return
+ */
+ @Override
+ public String set(String key, String value, String nxxx, String expx, long expiredTime) {
+ if (StringUtils.isBlank(key)) {
+ logger.warn("Params key is blank!");
+ return null;
+ }
+
+ if (value == null) {
+ logger.warn("Params value is null!");
+ return null;
+ }
+
+ Jedis jedis = this.getResource();
+ try {
+ return jedis.set(key, value);
+ } finally {
+ this.destroyResource(jedis);
+ }
+
+ }
+
+ /**
+ * 仅当redis中不含对应的key时,设定缓存内容
+ *
+ * @param key
+ * @param value
+ * @param expiredTime 缓存内容过期时间 (单位:秒) ,expireTime必须大于0
+ * @return
+ */
+ @Override
+ public String setnx(String key, String value, long expiredTime) {
+ return this.set(key, value, NXXX_SET_IF_NOT_EXISTS, EXPX_SECONDS, expiredTime);
+ }
+
+ /**
+ * 仅当redis中含有对应的key时,修改缓存内容
+ *
+ * @param key
+ * @param value
+ * @param expiredTime 缓存内容过期时间 (单位:秒) ,expireTime必须大于0
+ * @return
+ */
+ @Override
+ public String setxx(String key, String value, long expiredTime) {
+ return this.set(key, value, NXXX_SET_IF_EXISTS, EXPX_SECONDS, expiredTime);
+ }
+
+ /**
+ * 根据key删除缓存
+ *
+ * @param keys
+ * @return
+ */
+ public Long delete(String... keys) {
+ if (keys == null || keys.length == 0) {
+ logger.warn("Params keys is null or 0 length!");
+ return -1L;
+ }
+ Jedis jedis = this.getResource();
+ try {
+ return jedis.del(keys);
+ } finally {
+ this.destroyResource(jedis);
+ }
+ }
+
+ /**
+ * 判断对应的key是否存在
+ *
+ * @param key
+ * @return
+ */
+ public boolean exists(String key) {
+ if (StringUtils.isBlank(key)) {
+ //不接受空值
+ return false;
+ }
+ Jedis jedis = this.getResource();
+ try {
+ return jedis.exists(key);
+ } finally {
+ this.destroyResource(jedis);
+ }
+
+
+ }
+
+ /**
+ * redis 加法运算
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ @Override
+ public Long incrBy(String key, long value) {
+ if (StringUtils.isBlank(key)) {
+ logger.warn("Params key is blank!");
+ return null;
+ }
+ Jedis jedis = this.getResource();
+ try {
+ return jedis.incrBy(key, value);
+ } finally {
+ this.destroyResource(jedis);
+ }
+ }
+
+ /**
+ * 设定redis 对应的key的剩余存活时间
+ *
+ * @param key
+ * @param seconds
+ */
+ @Override
+ public void setTTL(String key, int seconds) {
+ if (seconds < 0) {
+ return;
+ }
+ if (StringUtils.isBlank(key)) {
+ logger.warn("Params key is blank!");
+ return;
+ }
+ Jedis jedis = this.getResource();
+ try {
+ jedis.expire(key, seconds);
+ } finally {
+ this.destroyResource(jedis);
+ }
+ }
+
+ /**
+ * 根据通配符表达式查询key值的set,通配符仅支持*
+ *
+ * @param pattern 如 ke6*abc等
+ * @return
+ */
+ public Set keys(String pattern) {
+
+ if (StringUtils.isBlank(pattern)) {
+ logger.warn("Params pattern is blank!");
+ return Collections.emptySet();
+ }
+ Jedis jedis = this.getResource();
+ try {
+ return jedis.keys(pattern);
+ } finally {
+ this.destroyResource(jedis);
+ }
+ }
+
+
+ /**
+ * 将对象转为json字符串。若对象为null,则返回 {@link RedisService#BLANK_CONTENT}
+ *
+ * @param object
+ * @return
+ */
+ @Override
+ public String toJsonString(Object object) {
+ if (object == null) {
+ return BLANK_CONTENT;
+ }
+
+ if ((object instanceof Collection) && CollectionUtils.isEmpty((Collection) object)) {
+ return BLANK_CONTENT;
+ }
+
+ if ((object instanceof Map) && CollectionUtils.isEmpty((Map) object)) {
+ return BLANK_CONTENT;
+ }
+
+ try {
+ return om.writeValueAsString(object);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public String makeSerializedString(Object value) {
+ if (value == null) {
+ return BLANK_CONTENT;
+ }
+
+ if ((value instanceof Collection) && ((Collection) value).size() == 0) {
+ return BLANK_CONTENT;
+ }
+
+ if ((value instanceof Map) && ((Map) value).size() == 0) {
+ return BLANK_CONTENT;
+ }
+
+
+ return JSON.toJSONString(value);
+ }
+
+ @Override
+ public String put(String cacheName, String key, Object value) {
+ String result = get(cacheName);
+ Map map = new HashMap();
+ if (StringUtils.isNotBlank(result)){
+ map = JSON.parseObject(result, new TypeReference