p2 #5

Closed
pb4nq52pf wants to merge 7 commits from branch_ltt into feature/cx

@ -8,20 +8,38 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
// 这是一个Spring Boot应用的主启动类使用了多个注解来配置应用的相关特性和功能。
@SpringBootApplication
// @SpringBootApplication注解是一个复合注解它包含了多个用于开启Spring Boot自动配置、组件扫描等功能的注解
// 表明这是一个Spring Boot应用的入口点会自动配置很多默认的Spring组件和行为方便快速搭建项目框架。
@EnableDiscoveryClient
// @EnableDiscoveryClient注解用于启用服务发现功能使得该应用能够注册到服务注册中心如Eureka、Consul等
// 并且可以从注册中心发现其他服务,方便在微服务架构中实现服务之间的相互调用和协作。
@EnableZuulProxy
@PropertySource(value="classpath:parameter.properties")
// @EnableZuulProxy注解用于启用Zuul作为微服务架构中的API网关代理Zuul可以对外部请求进行路由转发、过滤等操作
// 例如根据不同的请求路径将请求转发到相应的后端微服务上,还能实现诸如限流、权限校验等功能。
@PropertySource(value = "classpath:parameter.properties")
// @PropertySource注解用于指定外部属性文件的位置这里表示从类路径下的parameter.properties文件中加载配置属性
// 可以在该文件中定义各种项目中需要用到的配置项如数据库连接信息、服务端口等Spring会读取并注入这些配置到相应的组件中使用。
public class SnailmallApiGatewayApplication {
// 应用的主方法是Java应用程序的入口点用于启动Spring Boot应用。
public static void main(String[] args) {
// 调用SpringApplication的静态run方法来启动Spring Boot应用传入当前主启动类的Class对象以及命令行参数
// Spring Boot会根据配置自动完成一系列的初始化、组件加载、配置注入等操作然后启动应用并监听相应的端口等待外部请求。
SpringApplication.run(SnailmallApiGatewayApplication.class, args);
}
// 使用@Bean注解定义一个Spring Bean该方法返回的对象会被Spring容器管理可在其他组件中通过依赖注入的方式使用。
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
// 创建并返回一个PropertySourcesPlaceholderConfigurer对象它主要用于处理属性占位符的替换功能
// 在Spring应用中如果配置文件中的属性使用了占位符例如${property.name}这种形式),该配置器会帮助将占位符替换为实际配置的值,
// 确保应用能够正确使用配置文件中的各项配置属性。
return new PropertySourcesPlaceholderConfigurer();
}
}
}

@ -12,43 +12,63 @@ import redis.clients.jedis.JedisPool;
* @Date 2019/1/1 15:03
* @CONTACT 317758022@qq.com
* @DESC
* Redis
* Spring使JedisRedis
*/
@Component
@Slf4j
public class CommonCacheUtil {
// 通过依赖注入获取JedisPoolWrapper实例用于后续获取JedisPool来操作Redis
@Autowired
private JedisPoolWrapper jedisPoolWrapper;
/**
* key
* RediskeyvalueRedis
* SnailmallException
*
* @param key
* @param value
*/
public void cache(String key, String value) {
try {
// 通过JedisPoolWrapper获取JedisPool实例JedisPool用于管理Jedis连接资源
JedisPool pool = jedisPoolWrapper.getJedisPool();
if (pool != null) {
if (pool!= null) {
// 从JedisPool中获取一个Jedis连接资源使用try-with-resources语句确保资源能自动关闭
try (Jedis Jedis = pool.getResource()) {
// 选择Redis的数据库这里选择索引为0的数据库通常用于存放通用缓存数据等情况
Jedis.select(0);
// 将键值对存入Redis
Jedis.set(key, value);
}
}
} catch (Exception e) {
// 如果出现异常,记录详细的错误日志,日志内容为"redis存值失败"以及异常信息
log.error("redis存值失败", e);
// 抛出自定义的SnailmallException异常异常信息为"redis报错"
throw new SnailmallException("redis报错");
}
}
/**
* key
* Rediskeyvalue
*
*
* @param key
* @return Redisnull
*/
public String getCacheValue(String key) {
String value = null;
try {
JedisPool pool = jedisPoolWrapper.getJedisPool();
if (pool != null) {
if (pool!= null) {
try (Jedis Jedis = pool.getResource()) {
Jedis.select(0);
// 从Redis中获取指定键的值
value = Jedis.get(key);
}
}
@ -61,15 +81,24 @@ public class CommonCacheUtil {
/**
* key
*
* Redissetnx
*
* @param key
* @param value
* @param expire Redis
* @return jedis.setnx10
*/
public long cacheNxExpire(String key, String value, int expire) {
long result = 0;
try {
JedisPool pool = jedisPoolWrapper.getJedisPool();
if (pool != null) {
if (pool!= null) {
try (Jedis jedis = pool.getResource()) {
jedis.select(0);
// 原子性地设置缓存值(当键不存在时才设置),返回设置结果
result = jedis.setnx(key, value);
// 为该键设置过期时间
jedis.expire(key, expire);
}
}
@ -83,13 +112,18 @@ public class CommonCacheUtil {
/**
* key
* Rediskey
*
*
* @param key
*/
public void delKey(String key) {
JedisPool pool = jedisPoolWrapper.getJedisPool();
if (pool != null) {
if (pool!= null) {
try (Jedis jedis = pool.getResource()) {
jedis.select(0);
try {
// 调用Jedis的del方法删除指定的键
jedis.del(key);
} catch (Exception e) {
log.error("从redis中删除失败", e);
@ -101,4 +135,4 @@ public class CommonCacheUtil {
}
}

@ -14,30 +14,53 @@ import javax.annotation.PostConstruct;
* @Date 2019/1/1 15:00
* @CONTACT 317758022@qq.com
* @DESC redisredishash
* JedisPool便JedisPoolRedis
* RedisRedishash
*/
@Component
@Slf4j
public class JedisPoolWrapper {
// 通过Spring的依赖注入获取Parameters对象该对象应该包含了Redis相关的配置参数
@Autowired
private Parameters parameters;
// 用于存放JedisPool实例初始化为null后续在初始化方法中进行实例化赋值
private JedisPool jedisPool = null;
/**
* @PostConstruct
* JedisPool
*/
@PostConstruct
public void init(){
public void init() {
try {
// 创建JedisPoolConfig对象用于配置JedisPool的各项参数
JedisPoolConfig config = new JedisPoolConfig();
// 设置JedisPool中最大连接数参数值从Parameters对象中获取
config.setMaxTotal(parameters.getRedisMaxTotal());
// 设置JedisPool中空闲连接的最大数量同样从Parameters对象获取对应参数值
config.setMaxIdle(parameters.getRedisMaxIdle());
// 设置获取连接时的最大等待时间单位为毫秒也是从Parameters对象获取对应参数值
config.setMaxWaitMillis(parameters.getRedisMaxWaitMillis());
jedisPool = new JedisPool(config,parameters.getRedisHost(),parameters.getRedisPort(),2000,"xxx");
// 创建JedisPool实例传入配置对象、Redis服务器主机地址、端口号、超时时间以及密码等信息
// 这里密码写为"xxx"实际应用中应替换为真实的Redis访问密码
jedisPool = new JedisPool(config, parameters.getRedisHost(), parameters.getRedisPort(), 2000, "xxx");
// 如果初始化成功记录日志信息表示初始化Redis连接池成功
log.info("【初始化redis连接池成功】");
}catch (Exception e){
log.error("【初始化redis连接池失败】",e);
} catch (Exception e) {
// 如果在初始化过程中出现异常,记录详细的错误日志,日志内容为"【初始化redis连接池失败】"以及异常信息
log.error("【初始化redis连接池失败】", e);
}
}
/**
* JedisPool
* JedisPoolRedis
*
* @return JedisPoolnull
*/
public JedisPool getJedisPool() {
return jedisPool;
}
}
}

@ -11,23 +11,54 @@ import java.util.List;
* @Date 2019/1/1 14:27
* @CONTACT 317758022@qq.com
* @DESC
* Spring
* 使Lombok@DataGetterSetterToString便访
*/
@Component
@Data
public class Parameters {
/*****redis config start*******/
/**
* @Valueapplication.propertiesapplication.yml"redis.host"
* Redis
*/
@Value("${redis.host}")
private String redisHost;
/**
* 使@Value"redis.port"
* Redis
*/
@Value("${redis.port}")
private int redisPort;
/**
* max-idlemaxIdlemax-totalmaxTotal
* "redis.max-idle"Redis
*/
@Value("${redis.max-idle}")
private int redisMaxTotal;
/**
* @Value"redis.max-total"
* Redis
*/
@Value("${redis.max-total}")
private int redisMaxIdle;
/**
* "redis.max-wait-millis"
* Redis
*/
@Value("${redis.max-wait-millis}")
private int redisMaxWaitMillis;
/*****redis config end*******/
/**
* 使@ValueSpELSpring"security.noneSecurityAdminPaths"
* List<String>
*
*/
@Value("#{'${security.noneSecurityAdminPaths}'.split(',')}")
private List<String> noneSecurityAdminPaths;
}
}

@ -11,22 +11,41 @@ import java.util.Arrays;
/**
*
* C - Cross O - Origin R - Resource S - Sharing
* Spring使访
*
*/
@Configuration
public class CorsConfig {
/**
* @BeanBeanSpring
* CorsFilterBeanSpring
*
*/
@Bean
public CorsFilter corsFilter() {
// 创建一个基于URL的跨域配置源对象用于根据不同的URL路径来应用不同的跨域配置规则
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 创建一个CorsConfiguration对象用于具体设置跨域相关的各种配置参数
final CorsConfiguration config = new CorsConfiguration();
// 设置是否允许携带凭证例如Cookie等进行跨域请求这里设置为true表示允许
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("*")); //http:www.a.com
// 设置允许跨域访问的源Origin列表这里使用Arrays.asList("*")表示允许所有来源的请求跨域访问,
// 在实际应用中更安全的做法是明确指定具体的源地址例如http://www.a.com这种形式
config.setAllowedOrigins(Arrays.asList("*"));
// 设置允许跨域请求包含的头部信息列表使用Arrays.asList("*")表示允许所有头部信息,
// 但在更严格的安全场景下,应该明确列出允许的头部字段
config.setAllowedHeaders(Arrays.asList("*"));
// 设置允许的跨域请求方法列表如GET、POST等这里使用Arrays.asList("*")表示允许所有请求方法跨域访问
config.setAllowedMethods(Arrays.asList("*"));
// 设置浏览器对预检请求OPTIONS请求的缓存时间单位为秒这里设置为300秒
// 即在这个时间范围内,对于相同源的相同请求,浏览器无需再次发送预检请求
config.setMaxAge(300l);
// 将上述配置应用到所有路径("/**"表示匹配所有路径)上,这样所有接口都会应用此跨域配置
source.registerCorsConfiguration("/**", config);
// 返回创建好的CorsFilter对象该过滤器会在Spring处理Web请求时根据配置拦截并处理跨域相关情况
return new CorsFilter(source);
}
}
}

@ -5,42 +5,72 @@ package com.njupt.swg.constants;
* @Date 2019/1/1 13:19
* @CONTACT 317758022@qq.com
* @DESC
* 便
*/
public class Constants {
/**自定义状态码 start**/
/**
* start
* 便
*/
// 表示请求成功的状态码对应HTTP状态码中的200表示操作成功完成
public static final int RESP_STATUS_OK = 200;
// 表示未授权的状态码对应HTTP状态码中的401通常用于用户未通过认证或授权时返回的状态
public static final int RESP_STATUS_NOAUTH = 401;
// 表示服务器内部错误的状态码对应HTTP状态码中的500用于在服务器端出现异常等内部问题时返回该状态告知客户端
public static final int RESP_STATUS_INTERNAL_ERROR = 500;
// 表示请求参数错误的状态码对应HTTP状态码中的400当客户端发送的请求参数不符合要求时返回此状态
public static final int RESP_STATUS_BADREQUEST = 400;
/**
* end
*/
/**自定义状态码 end**/
/***redis user相关的key以这个打头**/
/**
* *redis userkey
* Rediskey便
*/
public static final String TOKEN_PREFIX = "user_";
/**
* redis
* Redis便
*/
public interface RedisCacheExtime{
int REDIS_SESSION_EXTIME = 60* 30;//30分钟
// 定义用户登录信息在Redis中的过期时间以秒为单位这里设置为30分钟60秒 * 30方便后续统一管理和修改该时间设置
int REDIS_SESSION_EXTIME = 60 * 30; //30分钟
}
/** 用户注册判断重复的参数类型 start **/
/**
* start
* 便使
*/
// 表示注册时判断邮箱是否重复的参数类型对应的常量字符串,用于标识邮箱字段
public static final String EMAIL = "email";
// 表示注册时判断用户名是否重复的参数类型对应的常量字符串,用于标识用户名字段
public static final String USERNAME = "username";
/** 用户注册判断重复的参数类型 end **/
/**
* end
*/
/** 用户角色 **/
/**
*
* 便使
*/
public interface Role{
int ROLE_CUSTOME = 0;//普通用户
int ROLE_ADMIN = 1;//管理员用户
// 表示普通用户角色对应的整数值赋值为0方便在代码中通过该值来判断用户是否为普通用户角色
int ROLE_CUSTOME = 0; //普通用户
// 表示管理员用户角色对应的整数值赋值为1用于判断用户是否为管理员角色
int ROLE_ADMIN = 1; //管理员用户
}
/**用户注册分布式锁路径***/
/**
*
* 使便
*/
public static final String USER_REGISTER_DISTRIBUTE_LOCK_PATH = "/user_reg";
}
}

@ -11,17 +11,30 @@ import org.springframework.web.bind.annotation.RestController;
* @Date 2019/1/2 21:21
* @CONTACT 317758022@qq.com
* @DESC
* Spring MVCSpring BootErrorController
*
*/
@RestController
public class ErrorHandleController implements ErrorController {
/**
* ErrorController
* "/error""/error"
*/
@Override
public String getErrorPath() {
return "/error";
}
/**
* 使@RequestMapping"/error"
* "/error"
* ServerResponseServerResponse
* ResponseEnum
*/
@RequestMapping("/error")
public ServerResponse error() {
return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登陆或者权限不足");
return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登陆或者权限不足");
}
}
}

@ -13,31 +13,45 @@ import java.util.Date;
* @Date 2018/12/31 21:01
* @CONTACT 317758022@qq.com
* @DESC
* 使Lombok
* GetterSetterToStringJava
*/
@Data
// 使用@Data注解由Lombok自动为类中的所有非final字段生成Getter、Setter方法同时还生成了equals、hashCode和toString方法方便对对象属性的访问和操作。
@NoArgsConstructor
// 使用@NoArgsConstructor注解由Lombok自动生成一个无参构造函数方便在某些情况下如通过反射等方式创建对象实例对对象进行初始化。
@AllArgsConstructor
// 使用@AllArgsConstructor注解由Lombok自动生成一个包含所有参数的构造函数可用于在创建对象时一次性传入所有必要的属性值来初始化对象。
@ToString
// 使用@ToString注解由Lombok自动为该类生成一个toString方法便于在调试等场景下直观地查看对象的属性值信息。
public class User implements Serializable {
// 用户的唯一标识符,通常对应数据库表中的主键,用于区分不同的用户记录,类型为整数类型。
private Integer id;
// 用户的用户名,用于用户登录等场景下的身份标识,是一个字符串类型的属性。
private String username;
// 用户的登录密码,存储用户登录时需要验证的密码信息,为字符串类型,通常会进行加密存储以保证安全性。
private String password;
// 用户的电子邮箱地址,可用于找回密码、接收系统通知等功能,是字符串类型的属性。
private String email;
// 用户的电话号码,可能用于联系用户、短信验证等相关业务场景,同样为字符串类型。
private String phone;
// 用户设置的密保问题,用于在忘记密码等情况下辅助验证用户身份,属于字符串类型的属性。
private String question;
// 用户对密保问题设置的答案与question属性对应用于验证用户身份也是字符串类型。
private String answer;
//角色0-管理员,1-普通用户
// 用户角色通过整数值来表示不同的角色类型0代表管理员1代表普通用户用于权限控制等相关业务逻辑。
private Integer role;
// 用户账号创建的时间记录用户首次注册创建账号的具体日期和时间类型为Java中的Date类型。
private Date createTime;
// 用户信息最后更新的时间每次用户修改自身信息等操作后会更新该时间同样是Date类型的属性。
private Date updateTime;
}

@ -8,18 +8,33 @@ import lombok.Getter;
* @Date 2019/1/1 13:18
* @CONTACT 317758022@qq.com
* @DESC
* JavaRuntimeException
* 便
*/
@Getter
public class SnailmallException extends RuntimeException{
// 使用Lombok的@Getter注解自动为类中的私有属性生成对应的Getter方法使得外部可以获取这些属性的值。
public class SnailmallException extends RuntimeException {
// 用于存储异常对应的状态码初始化为ResponseEnum.ERROR.getCode(),即默认的错误状态码,后续可通过构造函数进行修改。
private int exceptionStatus = ResponseEnum.ERROR.getCode();
public SnailmallException(String msg){
/**
* msg
* RuntimeException便
* @param msg
*/
public SnailmallException(String msg) {
super(msg);
}
public SnailmallException(int code,String msg){
/**
* codemsg
* exceptionStatus
* 便
* @param code
* @param msg msg
*/
public SnailmallException(int code, String msg) {
super(msg);
exceptionStatus = code;
}
}
}

@ -27,26 +27,43 @@ import static org.springframework.cloud.netflix.zuul.filters.support.FilterConst
* @Date 2019/1/3 10:21
* @CONTACT 317758022@qq.com
* @DESC controller
* userID,controller
* userIDcontroller
* ZuulFilterZuul
*
*/
@Slf4j
@Component
public class AdminUserFilter extends ZuulFilter {
// 通过依赖注入获取CommonCacheUtil实例用于后续从缓存Redis中获取用户相关信息
@Autowired
private CommonCacheUtil commonCacheUtil;
// 通过依赖注入获取Parameters实例该实例可能包含了一些系统配置参数比如不需要安全校验的路径等信息
@Autowired
private Parameters parameters;
/**
*
* Spring Cloud ZuulPRE_TYPE
*/
@Override
public String filterType() {
return PRE_TYPE;
}
/**
*
* PRE_DECORATION_FILTER_ORDER1使
*/
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER-1;
return PRE_DECORATION_FILTER_ORDER - 1;
}
/**
* URL
* HttpServletRequestURL
* URLfalsetrue
*/
@Override
public boolean shouldFilter() {
RequestContext requestContext = RequestContext.getCurrentContext();
@ -54,63 +71,68 @@ public class AdminUserFilter extends ZuulFilter {
//获取当前请求的url
String url = request.getRequestURI();
//前端的路径不在这里校验,直接放过
if (!url.contains("manage")){
log.info("【{}不需要进行权限校验】",url);
if (!url.contains("manage")) {
log.info("【{}不需要进行权限校验】", url);
return false;
}
if(url.contains("upload")){
log.info("【{}不需要进行权限校验】",url);
if (url.contains("upload")) {
log.info("【{}不需要进行权限校验】", url);
return false;
}
//从配置文件获取所有门户需要校验的路径
// String[] passUrls = parameters.getNoneSecurityAdminPaths().toArray(new String[parameters.getNoneSecurityAdminPaths().size()]);
// for(String str:passUrls){
// for (String str : passUrls) {
// //指定的路径比较特殊,也不在这里校验
// if(url.contains("manage") && url.contains(str)){
// log.info("【{}不需要进行权限校验】",url);
// if (url.contains("manage") && url.contains(str)) {
// log.info("【{}不需要进行权限校验】", url);
// return false;
// }
// }
log.info("【{}----需要进行权限校验,必须是管理员身份才可以进入】",url);
log.info("【{}----需要进行权限校验,必须是管理员身份才可以进入】", url);
return true;
}
/**
* shouldFiltertrue
*
*
*/
@Override
public ServerResponse run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//校验是否为管理员身份
String loginToken = CookieUtil.readLoginToken(request);
log.info("【获取cookie----{}】",loginToken);
if(StringUtils.isEmpty(loginToken)){
log.info("【获取cookie----{}】", loginToken);
if (StringUtils.isEmpty(loginToken)) {
// // 过滤该请求,不对其进行路由
// requestContext.setSendZuulResponse(false);
// //返回错误代码
// requestContext.setResponseStatusCode(Constants.RESP_STATUS_NOAUTH);
// return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息");
this.returnMsg(requestContext);
return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息");
return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息");
//throw new SnailmallException("用户未登录,无法获取当前用户信息");
}
//2.从redis中获取用户信息
String userStr = commonCacheUtil.getCacheValue(loginToken);
log.info("【从redis中获取用户信息:{}】",userStr);
if(userStr == null){
log.info("【从redis中获取用户信息:{}】", userStr);
if (userStr == null) {
// // 过滤该请求,不对其进行路由
// requestContext.setSendZuulResponse(false);
// //返回错误代码
// requestContext.setResponseStatusCode(Constants.RESP_STATUS_NOAUTH);
// return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); //SnailmallException(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息");
this.returnMsg(requestContext);
return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息");
return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息");
}
String url = request.getRequestURI();
log.info("【获取当前url:{}】",url);
if(url.contains("manage")){
log.info("【获取当前url:{}】", url);
if (url.contains("manage")) {
log.info("【来到了管理后台,需要校验权限】");
User currentUser = JsonUtil.Str2Obj(userStr,User.class);
log.info("【当前登陆的用户为:{}】",currentUser);
if(!currentUser.getRole().equals(Constants.Role.ROLE_ADMIN)){
User currentUser = JsonUtil.Str2Obj(userStr, User.class);
log.info("【当前登陆的用户为:{}】", currentUser);
if (!currentUser.getRole().equals(Constants.Role.ROLE_ADMIN)) {
//不是管理员报错
log.error("【当前登陆用户不是管理员身份】");
// 过滤该请求,不对其进行路由
@ -119,7 +141,7 @@ public class AdminUserFilter extends ZuulFilter {
// requestContext.setResponseStatusCode(Constants.RESP_STATUS_NOAUTH);
// return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息");
this.returnMsg(requestContext);
return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息");
return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息");
//throw new SnailmallException("用户权限不够");
}
}
@ -127,10 +149,14 @@ public class AdminUserFilter extends ZuulFilter {
}
//返回没权限消息
private void returnMsg(RequestContext ctx){
private void returnMsg(RequestContext ctx) {
// 设置响应的内容类型为JSON格式字符编码为utf-8确保返回的错误信息能被正确解析
ctx.getResponse().setContentType("application/json; charset=utf-8");
ctx.setSendZuulResponse(false); //令zuul过滤该请求不对其进行路由
// 令zuul过滤该请求不对其进行路由即阻止请求继续往后端服务转发
ctx.setSendZuulResponse(false);
// 设置响应的状态码这里设置为Constants.RESP_STATUS_OK可能需要根据实际情况确认是否合适一般权限相关错误可以考虑用对应的错误状态码
ctx.setResponseStatusCode(Constants.RESP_STATUS_OK);
ctx.setResponseBody(JsonUtil.obj2String(ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息")));
// 将错误提示信息转换为JSON字符串并设置为响应体的内容以便客户端能获取到具体的错误提示信息
ctx.setResponseBody(JsonUtil.obj2String(ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息")));
}
}
}

@ -16,37 +16,64 @@ import static org.springframework.cloud.netflix.zuul.filters.support.FilterConst
* @Date 2019/1/3 11:21
* @CONTACT 317758022@qq.com
* @DESC
* ZuulFilterZuul
*
*/
@Component
public class RateLimitFilter extends ZuulFilter {
//放100个令牌
// 使用RateLimiter类来自Google Guava库创建一个令牌桶限流器设置初始令牌数量为100个
// 意味着令牌桶一开始有100个可用令牌后续请求需要获取令牌才能通过该过滤器继续路由。
private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);
/**
*
* Spring Cloud ZuulPRE_TYPE
*/
@Override
public String filterType() {
return PRE_TYPE;
}
/**
*
* SERVLET_DETECTION_FILTER_ORDER1使
*/
@Override
public int filterOrder() {
return SERVLET_DETECTION_FILTER_ORDER - 1;
}
/**
* true
*
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* shouldFiltertrue
*
*
*/
@Override
public Object run() throws ZuulException {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
if(!RATE_LIMITER.tryAcquire()){
// 使用tryAcquire方法尝试从令牌桶中获取一个令牌该方法会立即返回获取结果
// 如果获取成功即令牌桶中有可用令牌则返回true请求可以继续往后执行如果获取失败令牌桶中无可用令牌则返回false。
if (!RATE_LIMITER.tryAcquire()) {
//没有取到一个令牌的话,可以这样返回信息给前端
context.set("状态码",401);
context.set("error.message","用户没有获取到令牌");
// 在请求上下文中设置一个名为"状态码"的属性值为401表示未授权状态这里只是简单示例设置状态码实际应用可能需遵循规范的状态码使用方式
// 用于后续可能的错误处理或者向客户端返回相应的提示信息,告知客户端请求因未获取到令牌而被限制。
context.set("状态码", 401);
// 在请求上下文中设置一个名为"error.message"的属性,值为"用户没有获取到令牌"
// 用于更详细地向客户端说明请求被限制的原因,方便客户端知晓具体的错误情况。
context.set("error.message", "用户没有获取到令牌");
}
return null;
}
}
}

@ -7,19 +7,34 @@ import lombok.Getter;
* @Date 2018/12/31 20:15
* @CONTACT 317758022@qq.com
* @DESC
*
* 便使
*/
@Getter
// 使用Lombok的@Getter注解会自动为枚举中的code和desc属性生成对应的Getter方法便于外部获取这些属性的值。
public enum ResponseEnum {
SUCCESS(0,"SUCCESS"),
ERROR(1,"ERROR"),
ILLEGAL_ARGUMENTS(2,"ILLEGAL_ARGUMENTS"),
NEED_LOGIN(10,"NEED_LOGIN");
// 定义一个名为SUCCESS的枚举常量表示操作成功的响应状态其对应的状态码为0描述信息为"SUCCESS"
// 可用于在返回响应给客户端等场景下表示请求处理成功的情况。
SUCCESS(0, "SUCCESS"),
// 定义一个名为ERROR的枚举常量表示操作出现错误的响应状态状态码为1描述信息为"ERROR"
// 当系统出现一般性错误时可使用该枚举值来构建相应的错误响应返回给客户端。
ERROR(1, "ERROR"),
// 定义一个名为ILLEGAL_ARGUMENTS的枚举常量用于表示请求参数不合法的响应状态状态码为2描述信息为"ILLEGAL_ARGUMENTS"
// 当客户端发送的请求参数不符合要求时,可以用此枚举值来构造响应告知客户端参数非法情况。
ILLEGAL_ARGUMENTS(2, "ILLEGAL_ARGUMENTS"),
// 定义一个名为NEED_LOGIN的枚举常量用于表示需要用户登录的响应状态状态码为10描述信息为"NEED_LOGIN"
// 当客户端发起需要登录才能访问的请求但未登录时,可通过该枚举值构建响应提示客户端进行登录操作。
NEED_LOGIN(10, "NEED_LOGIN");
// 用于存储响应状态对应的代码,不同的枚举常量有不同的整数值,以此区分不同的响应情况。
private int code;
// 用于存储响应状态对应的描述信息,以字符串形式展示具体的状态含义,方便直观理解每个枚举常量所代表的情况。
private String desc;
ResponseEnum(int code,String desc){
// 枚举类型的构造函数用于初始化每个枚举常量对应的code和desc属性值
// 在定义枚举常量时传入相应的代码和描述信息,以此来完成每个枚举常量的具体状态设置。
ResponseEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
}
}

@ -11,65 +11,110 @@ import java.io.Serializable;
* @Date 2018/12/31 20:11
* @CONTACT 317758022@qq.com
* @DESC
*
* 便
*/
@Getter
// 使用Lombok的@Getter注解自动为类中的私有属性status、msg、data生成对应的Getter方法便于外部获取这些属性的值。
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
// 使用@JsonSerialize注解并指定Inclusion.NON_NULL配置用于在将对象转换为JSON格式时忽略值为null的属性
// 这样在返回给客户端的JSON数据中就不会包含那些没有实际值的属性减少数据传输量并使数据结构更简洁。
public class ServerResponse<T> implements Serializable {
// 用于存储响应的状态码,通过不同的状态码值来表示请求处理的结果情况,如成功、失败、需要登录等不同状态。
private int status;
// 用于存储响应的提示消息,以字符串形式向客户端传达一些与响应相关的额外信息,比如成功时的提示、失败原因等。
private String msg;
// 用于存储响应中具体的数据内容,该数据类型通过泛型参数<T>来灵活指定,根据不同的业务场景可以是各种类型的数据(如对象、列表等)。
private T data;
private ServerResponse(int status){
// 私有构造函数接收一个状态码参数用于创建只包含状态码的ServerResponse对象实例
// 通常在一些内部初始化或者特定场景下使用,其他更完整的构造函数会基于此进一步设置更多属性值。
private ServerResponse(int status) {
this.status = status;
}
private ServerResponse(int status,String msg){
// 私有构造函数接收状态码和提示消息两个参数用于创建包含状态码和提示消息的ServerResponse对象实例
// 可用于在需要返回带有特定提示信息的响应情况时进行对象初始化,比如返回错误提示消息等场景。
private ServerResponse(int status, String msg) {
this.status = status;
this.msg = msg;
}
private ServerResponse(int status,T data){
// 私有构造函数接收状态码和具体数据两个参数用于创建包含状态码和具体业务数据的ServerResponse对象实例
// 当有需要返回成功处理后的数据给客户端时,可以使用此构造函数来初始化响应对象。
private ServerResponse(int status, T data) {
this.status = status;
this.data = data;
}
private ServerResponse(int status,String msg,T data){
// 私有构造函数接收状态码、提示消息以及具体数据三个参数用于创建一个完整包含状态码、提示消息和业务数据的ServerResponse对象实例
// 可以在需要同时返回详细提示信息和数据的场景下使用此构造函数进行对象初始化。
private ServerResponse(int status, String msg, T data) {
this.status = status;
this.msg = msg;
this.data = data;
}
/**
* @JsonIgnore使JSON
* ResponseEnum.SUCCESS.getCode()
* truefalse
*/
@JsonIgnore
public boolean isSuccess(){
public boolean isSuccess() {
return this.status == ResponseEnum.SUCCESS.getCode();
}
/**
*
* ServerResponse便
*
*/
public static <T>ServerResponse<T> createBySuccess(){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),ResponseEnum.SUCCESS.getDesc());
// 创建一个表示成功的ServerResponse对象实例状态码使用ResponseEnum.SUCCESS中定义的成功状态码提示消息也使用ResponseEnum.SUCCESS中定义的描述信息
// 适用于简单的成功情况,不需要额外传递具体业务数据和自定义提示消息的场景。
public static <T> ServerResponse<T> createBySuccess() {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getDesc());
}
public static <T>ServerResponse<T> createBySuccessMessage(String message){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message);
// 创建一个表示成功的ServerResponse对象实例状态码使用ResponseEnum.SUCCESS中定义的成功状态码提示消息使用传入的自定义参数message
// 适用于需要返回成功状态但想自定义提示消息给客户端的场景,比如提示操作成功的具体说明等情况。
public static <T> ServerResponse<T> createBySuccessMessage(String message) {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), message);
}
public static <T>ServerResponse<T> createBySuccess(T data){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),data);
// 创建一个表示成功的ServerResponse对象实例状态码使用ResponseEnum.SUCCESS中定义的成功状态码具体业务数据使用传入的参数data
// 适用于有具体业务数据需要返回给客户端以表示操作成功且携带对应数据的场景,比如查询数据成功后返回查询到的数据列表等情况。
public static <T> ServerResponse<T> createBySuccess(T data) {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), data);
}
public static <T>ServerResponse<T> createBySuccess(String message,T data){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message,data);
// 创建一个表示成功的ServerResponse对象实例状态码使用ResponseEnum.SUCCESS中定义的成功状态码提示消息使用传入的参数message具体业务数据使用传入的参数data
// 适用于既需要返回自定义的提示消息又要携带具体业务数据来表示成功情况的场景,更加灵活地满足不同业务需求下成功响应的构建。
public static <T> ServerResponse<T> createBySuccess(String message, T data) {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), message, data);
}
/**
*
* ServerResponse便
*
*/
public static <T>ServerResponse<T> createByError(){
return new ServerResponse<>(ResponseEnum.ERROR.getCode(),ResponseEnum.ERROR.getDesc());
}
public static <T>ServerResponse<T> createByErrorMessage(String msg){
return new ServerResponse<>(ResponseEnum.ERROR.getCode(),msg);
}
public static <T>ServerResponse<T> createByErrorCodeMessage(int code,String msg){
return new ServerResponse<>(code,msg);
// 创建一个表示失败的ServerResponse对象实例状态码使用ResponseEnum.ERROR中定义的表示错误的状态码提示消息也使用ResponseEnum.ERROR中定义的描述信息
// 适用于一般性的错误情况,返回默认的错误提示给客户端表示操作出现问题的场景。
public static <T> ServerResponse<T> createByError() {
return new ServerResponse<>(ResponseEnum.ERROR.getCode(), ResponseEnum.ERROR.getDesc());
}
// 创建一个表示失败的ServerResponse对象实例状态码使用ResponseEnum.ERROR中定义的表示错误的状态码提示消息使用传入的自定义参数msg
// 适用于需要返回特定错误提示消息给客户端以说明具体错误原因的场景,比如参数错误、业务规则不满足等各种导致失败的具体情况说明。
public static <T> ServerResponse<T> createByErrorMessage(String msg) {
return new ServerResponse<>(ResponseEnum.ERROR.getCode(), msg);
}
// 创建一个表示失败的ServerResponse对象实例状态码使用传入的自定义参数code通常是根据具体错误类型定义的不同状态码提示消息使用传入的参数msg
// 适用于需要根据不同错误类型返回对应状态码和具体错误提示消息的场景,更加灵活准确地向客户端反馈错误情况,比如不同业务模块下的不同错误状态反馈等。
public static <T> ServerResponse<T> createByErrorCodeMessage(int code, String msg) {
return new ServerResponse<>(code, msg);
}
}
}

@ -9,40 +9,59 @@ import javax.servlet.http.HttpServletResponse;
/**
* cookie
* CookieWebCookie
* 便Cookie
*/
@Slf4j
public class CookieUtil {
// 定义Cookie的域名用于指定该Cookie在哪个域名下有效这里设置为"oursnail.cn"表示该Cookie在该域名及其子域名下都可以被识别和使用。
private final static String COOKIE_DOMAIN = "oursnail.cn";
// 定义Cookie的名称用于唯一标识该Cookie这里设置为"snailmall_login_token",通常用于存储用户登录相关的标识信息(如登录令牌等)。
private final static String COOKIE_NAME = "snailmall_login_token";
/**
* cookie
* @param response
* @param token
* Cookie便
* @param response HttpServletResponseCookie
* @param token CookieCOOKIE_NAMECookie
*/
public static void writeLoginToken(HttpServletResponse response,String token){
Cookie ck = new Cookie(COOKIE_NAME,token);
public static void writeLoginToken(HttpServletResponse response, String token) {
// 创建一个新的Cookie对象指定Cookie的名称使用预先定义的COOKIE_NAME和要存储的值传入的token参数
Cookie ck = new Cookie(COOKIE_NAME, token);
// 设置Cookie的域名使其在指定的域名COOKIE_DOMAIN及其子域名下有效确保在相应的网站范围内能正确识别该Cookie。
ck.setDomain(COOKIE_DOMAIN);
ck.setPath("/");//设值在根目录
ck.setHttpOnly(true);//不允许通过脚本访问cookie,避免脚本攻击
ck.setMaxAge(60*60*24*365);//一年,-1表示永久,单位是秒maxage不设置的话cookie就不会写入硬盘只会写在内存只在当前页面有效
log.info("write cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
// 设置Cookie的路径为根目录"/"表示该Cookie在整个网站的所有页面路径下都可以被访问到即全站有效。
ck.setPath("/"); //设值在根目录
// 设置Cookie为HttpOnly属性为true这样可以防止通过客户端脚本如JavaScript访问该Cookie有效避免脚本攻击提高安全性。
ck.setHttpOnly(true); //不允许通过脚本访问cookie,避免脚本攻击
// 设置Cookie的最大存活时间这里设置为一年60秒 * 60分钟 * 24小时 * 365天单位是秒
// 如果设置为-1则表示永久有效若不设置该属性maxage不设置的话Cookie就不会写入硬盘只会写在内存只在当前页面有效。
ck.setMaxAge(60 * 60 * 24 * 365); //一年,-1表示永久,单位是秒maxage不设置的话cookie就不会写入硬盘只会写在内存只在当前页面有效
// 记录日志输出要写入的Cookie的名称和值方便在调试等场景下查看具体的Cookie写入情况。
log.info("write cookieName:{},cookieValue:{}", ck.getName(), ck.getValue());
// 将创建并配置好的Cookie添加到响应中这样客户端收到响应后就会将该Cookie保存下来以便后续请求时携带该Cookie信息。
response.addCookie(ck);
}
/**
* cookie
* @param request
* @return
* CookieCOOKIE_NAMECookie
* null
* @param request HttpServletRequestCookie
* @return Cookienull
*/
public static String readLoginToken(HttpServletRequest request){
public static String readLoginToken(HttpServletRequest request) {
// 从请求中获取所有的Cookie数组客户端发送请求时会携带之前服务器写入的Cookie信息这里获取到这些Cookie以便后续查找特定的Cookie。
Cookie[] cks = request.getCookies();
if(cks != null){
for(Cookie ck:cks){
log.info("cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue());
if(StringUtils.equals(ck.getName(),COOKIE_NAME)){
log.info("return cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue());
if (cks!= null) {
for (Cookie ck : cks) {
// 记录每个Cookie的名称和值的日志信息方便在调试等场景下查看请求中携带的Cookie情况。
log.info("cookieName:{},cookieBValue:{}", ck.getName(), ck.getValue());
// 通过比较Cookie的名称是否与预先定义的登录相关Cookie名称COOKIE_NAME相等来判断是否是我们要找的登录Cookie。
if (StringUtils.equals(ck.getName(), COOKIE_NAME)) {
// 如果找到对应的登录Cookie记录其名称和值的日志信息并返回该Cookie存储的值通常是登录令牌等关键信息
log.info("return cookieName:{},cookieBValue:{}", ck.getName(), ck.getValue());
return ck.getValue();
}
}
@ -52,18 +71,27 @@ public class CookieUtil {
/**
*
* @param request
* @param response
* Cookie使Cookie
*
* @param request HttpServletRequestCookie便Cookie
* @param response HttpServletResponseCookie使
*/
public static void delLoginToken(HttpServletRequest request,HttpServletResponse response){
public static void delLoginToken(HttpServletRequest request, HttpServletResponse response) {
// 从请求中获取客户端携带的所有Cookie数组用于查找要删除的登录相关Cookie。
Cookie[] cks = request.getCookies();
if(cks != null){
for(Cookie ck:cks) {
if(StringUtils.equals(ck.getName(),COOKIE_NAME)){
if (cks!= null) {
for (Cookie ck : cks) {
// 通过比较Cookie的名称是否与预先定义的登录相关Cookie名称COOKIE_NAME相等来确定是否是要删除的Cookie。
if (StringUtils.equals(ck.getName(), COOKIE_NAME)) {
// 设置要删除的Cookie的域名确保与之前写入时的域名一致以便准确删除对应的Cookie。
ck.setDomain(COOKIE_DOMAIN);
// 设置Cookie的路径为根目录"/"与之前写入时的路径设置保持一致保证删除的是对应路径下的该Cookie。
ck.setPath("/");
ck.setMaxAge(0);//0表示消除此cookie
log.info("del cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue());
// 设置Cookie的最大存活时间为0表示立即删除该Cookie客户端收到响应后会将该Cookie从本地移除。
ck.setMaxAge(0); //0表示消除此cookie
// 记录要删除的Cookie的名称和值的日志信息方便在调试等场景下查看具体的删除操作情况。
log.info("del cookieName:{},cookieBValue:{}", ck.getName(), ck.getValue());
// 将配置好的即将要删除的Cookie添加到响应中客户端收到响应后会根据设置删除对应的Cookie。
response.addCookie(ck);
return;
}
@ -71,4 +99,4 @@ public class CookieUtil {
}
}
}
}

@ -4,65 +4,117 @@ import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @DESC
* Joda-TimeJava
* 便
*/
public class DateTimeUtil {
//joda-time
// 用于说明该工具类在处理时间相关操作时使用了Joda-Time库它是一个功能强大的日期时间处理库能更方便地进行各种日期时间的操作和格式化等工作。
//str->Date
//Date->str
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
// 注释说明该工具类提供了将字符串转换为日期对象以及将日期对象转换为字符串这两种方向的时间转换功能,方便在不同业务场景下对时间数据进行处理。
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
// 定义了一个标准的日期时间格式字符串常量,用于在没有指定特定格式时,作为默认的日期时间格式化模板,符合常见的年-月-日 时:分:秒的格式表示。
public static Date strToDate(String dateTimeStr, String formatStr){
/**
*
* 使Joda-TimeDateTimeFormatter
* DateTimeJavaDate
*
* @param dateTimeStr formatStr
* @param formatStr "yyyy-MM-dd HH:mm:ss"
* @return JavaDateJoda-Time
*/
public static Date strToDate(String dateTimeStr, String formatStr) {
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
public static String dateToStr(Date date,String formatStr){
if(date == null){
/**
*
* nullnull
* null使Joda-TimeDateTime
*
* @param date JavaDate
* @param formatStr
* @return null
*/
public static String dateToStr(Date date, String formatStr) {
if (date == null) {
return StringUtils.EMPTY;
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(formatStr);
}
//固定好格式
public static Date strToDate(String dateTimeStr){
/**
* "yyyy-MM-dd HH:mm:ss"
* 使STANDARD_FORMATDateTimeFormatter
* DateTimeJavaDate
*
* @param dateTimeStr "yyyy-MM-dd HH:mm:ss"
* @return JavaDateJoda-Time
*/
public static Date strToDate(String dateTimeStr) {
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
public static String dateToStr(Date date){
if(date == null){
/**
* "yyyy-MM-dd HH:mm:ss"
* nullnull
* null使Joda-TimeDateTime
*
* @param date JavaDate
* @return null"yyyy-MM-dd HH:mm:ss"
*/
public static String dateToStr(Date date) {
if (date == null) {
return StringUtils.EMPTY;
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(STANDARD_FORMAT);
}
//Date -> 时间戳
/**
* 19701100:00:00 UTC
* nullnullnull
* null使JavaSimpleDateFormat"yyyy-MM-dd HH:mm:ss"
* Date
*
* @param date JavaDate
* @return Long19701100:00:00 UTCnullnull
* ParseException
*/
public static Long dateToChuo(Date date) throws ParseException {
if(date == null){
if (date == null) {
return null;
}
SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.parse(String.valueOf(date)).getTime();
}
/**
* Date
*
*
* @param args 使
* @throws ParseException DateJava
*/
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
String time="1970-01-06 11:45:55";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = "1970-01-06 11:45:55";
Date date = format.parse(time);
System.out.print("Format To times:"+date.getTime());
System.out.print("Format To times:" + date.getTime());
}
}
}

@ -8,119 +8,154 @@ import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.type.TypeReference;
import java.io.IOException;
import java.text.SimpleDateFormat;
/**
* jackson
* JacksonJSONJSON
* JacksonObjectMapperJSON
*/
@Slf4j
public class JsonUtil {
// 创建一个ObjectMapper对象它是Jackson库中用于进行JSON序列化和反序列化操作的核心类后续的各种序列化和反序列化操作都依赖它来实现。
private static ObjectMapper objectMapper = new ObjectMapper();
static {
//所有字段都列入进行转换
// 设置ObjectMapper在序列化时的包含规则这里设置为JsonSerialize.Inclusion.ALWAYS表示所有的字段都会被包含进序列化的结果中
// 即使字段的值为null也会进行序列化处理确保完整的对象结构能在JSON字符串中体现出来。
objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.ALWAYS);
//取消默认转换timestamp形式
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS,false);
// 配置ObjectMapper在序列化日期类型字段时取消将日期转换为时间戳的默认行为这样在序列化后的JSON字符串中日期会以指定的格式呈现而不是时间戳形式。
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
//忽略空bean转json的错误
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS,false);
// 配置ObjectMapper在尝试将一个空的Java对象没有任何属性值的对象转换为JSON字符串时忽略可能出现的错误
// 使得即使对象为空也能正常进行序列化操作,避免抛出异常导致程序中断。
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
//统一时间的格式
// 设置ObjectMapper在处理日期类型数据时使用的统一日期格式通过传入一个SimpleDateFormat对象指定了格式为DateTimeUtil.STANDARD_FORMAT在其他地方定义的标准格式
// 保证序列化和反序列化过程中日期格式的一致性和规范性。
objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT));
//忽略json存在属性但是java对象不存在属性的错误
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,false);
// 配置ObjectMapper在进行反序列化时忽略JSON字符串中存在但对应的Java对象中不存在的属性所引发的错误
// 这样在JSON数据结构可能发生变化或者不完全匹配Java对象结构时依然能够正常进行反序列化操作提高了程序的兼容性和容错性。
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
/**
*
* @param obj
* @param <T>
* @return
* JavaJSONnullnull
* IOnull
*
* @param obj Java<T>
* @param <T> JSON
* @return JSONnullnull
*/
public static <T> String obj2String(T obj){
if(obj == null){
public static <T> String obj2String(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
// 判断传入的对象是否本身就是字符串类型如果是则直接返回该字符串否则使用ObjectMapper将对象转换为JSON字符串并返回。
return obj instanceof String? (String) obj : objectMapper.writeValueAsString(obj);
} catch (IOException e) {
log.warn("parse object to string error",e);
log.warn("parse object to string error", e);
return null;
}
}
/**
* 便
* @param obj
* @param <T>
* @return
* obj2StringJavaJSON使JSON
* 使JSON便null
*
* @param obj Java<T>
* @param <T> JSON
* @return JSONnullnullJSON
*/
public static <T> String obj2StringPretty(T obj){
if(obj == null){
public static <T> String obj2StringPretty(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
// 使用ObjectMapper的writerWithDefaultPrettyPrinter方法获取一个能够生成美化格式JSON字符串的写入器
// 然后通过该写入器将对象转换为美化后的JSON字符串并返回。
return obj instanceof String? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (IOException e) {
log.warn("parse object to string error",e);
log.warn("parse object to string error", e);
return null;
}
}
/**
*
* @param str
* @param clazz
* @param <T>
* @return
* JSONJavanull
* nullIOJSONnull
*
* @param str JSONJava
* @param clazz JavaClass<T>JSON
* @param <T> clazz
* @return Javanullnull
*/
public static <T> T String2Obj(String str,Class<T> clazz){
if(StringUtils.isEmpty(str) || clazz == null){
public static <T> T String2Obj(String str, Class<T> clazz) {
if (StringUtils.isEmpty(str) || clazz == null) {
return null;
}
try {
return clazz.equals(String.class)?(T)str:objectMapper.readValue(str,clazz);
// 判断传入的目标类是否就是字符串类型的Class对象如果是则直接将传入的字符串强制转换并返回因为本身就是字符串无需真正的反序列化操作
// 否则使用ObjectMapper将JSON字符串按照指定的目标类进行反序列化并返回对应的对象。
return clazz.equals(String.class)? (T) str : objectMapper.readValue(str, clazz);
} catch (IOException e) {
log.warn("parse string to obj error",e);
log.warn("parse string to obj error", e);
return null;
}
}
/**
*
* @param str
* @param typeReference
* @param <T>
* @return
* JSON
* TypeReferenceTypeReferencenullnull
* null
*
* @param str JSONJava
* @param typeReference TypeReference
* @param <T> typeReference
* @return JavatypeReferencenullnull
*/
public static <T> T Str2Obj(String str, TypeReference typeReference){
if(StringUtils.isEmpty(str) || typeReference == null){
public static <T> T Str2Obj(String str, TypeReference typeReference) {
if (StringUtils.isEmpty(str) || typeReference == null) {
return null;
}
try {
return (T) (typeReference.getType().equals(String.class)?str:objectMapper.readValue(str,typeReference));
// 判断传入的TypeReference所表示的类型是否就是字符串类型如果是则直接返回传入的字符串无需反序列化操作
// 否则使用ObjectMapper按照TypeReference指定的复杂类型信息将JSON字符串进行反序列化并返回对应的对象。
return (T) (typeReference.getType().equals(String.class)? str : objectMapper.readValue(str, typeReference));
} catch (IOException e) {
log.warn("parse string to obj error",e);
log.warn("parse string to obj error", e);
return null;
}
}
/**
*
* @param str
* @param collectionClass
* @param elementClasses
* @param <T>
* @return
* ClassClass
* ObjectMapperJavaType
* null
*
* @param str JSONJava
* @param collectionClass ClassListSet
* @param elementClasses Class
* @param <T> collectionClasselementClasses
* @return Javanull
*/
public static <T> T Str2Obj(String str,Class<?> collectionClass,Class<?>... elementClasses){
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses);
public static <T> T Str2Obj(String str, Class<?> collectionClass, Class<?>... elementClasses) {
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
try {
return objectMapper.readValue(str,javaType);
return objectMapper.readValue(str, javaType);
} catch (IOException e) {
log.warn("parse string to obj error",e);
log.warn("parse string to obj error", e);
return null;
}
}
}
}

@ -4,13 +4,28 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* SnailmallCategoryServiceApplicationSpring Boot
* mainSpring BootWebTomcat
* 使Spring BootSpring Cloud
*/
@SpringBootApplication
// 这是一个组合注解,相当于同时使用了@Configuration、@EnableAutoConfiguration和@ComponentScan三个注解。
// @Configuration表示该类是一个配置类可用于定义一些Spring的Bean或者配置相关的属性等
// @EnableAutoConfiguration用于启用Spring Boot的自动配置功能会根据项目依赖自动配置各种组件比如数据源、Web相关配置等
// @ComponentScan用于指定Spring扫描组件如带有@Component、@Service、@Controller等注解的类的基础包路径默认会扫描当前类所在的包及其子包下的组件。
@EnableDiscoveryClient
// 该注解用于开启服务发现客户端功能使得应用可以注册到服务注册中心比如Eureka、Consul等并且可以从注册中心发现其他服务方便实现微服务之间的调用和协作。
public class SnailmallCategoryServiceApplication {
/**
* mainJavaSpringApplicationrunSpring Boot
* ClassSnailmallCategoryServiceApplication.classargs
* Spring Boot使
* @param args
*/
public static void main(String[] args) {
SpringApplication.run(SnailmallCategoryServiceApplication.class, args);
}
}
}

@ -1,22 +1,37 @@
package com.njupt.swg.common.constants;
/**
* Constants
* @Author swg.
* @Date 2019/1/1 13:19
* @CONTACT 317758022@qq.com
* @DESC
*/
public class Constants {
/**自定义状态码 start**/
/**
*
*/
/**
* HTTP200
*/
public static final int RESP_STATUS_OK = 200;
/**
* HTTP401
*/
public static final int RESP_STATUS_NOAUTH = 401;
/**
* HTTP500
*/
public static final int RESP_STATUS_INTERNAL_ERROR = 500;
/**
* HTTP400
*/
public static final int RESP_STATUS_BADREQUEST = 400;
/**自定义状态码 end**/
}
/**
*
*/
}

@ -1,6 +1,5 @@
package com.njupt.swg.common.exception;
import com.njupt.swg.common.constants.Constants;
import com.njupt.swg.common.resp.ServerResponse;
import lombok.extern.slf4j.Slf4j;
@ -9,25 +8,41 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* ExceptionHandlerAdvice
* @Author swg.
* @Date 2019/1/1 13:21
* @CONTACT 317758022@qq.com
* @DESC
*/
@ControllerAdvice
// 表示该类中处理异常的方法返回的结果会直接作为响应体返回给客户端,而不是进行视图解析等操作。
@ResponseBody
// 使用Slf4j注解方便进行日志记录简化日志输出的代码编写。
@Slf4j
public class ExceptionHandlerAdvice {
/**
* Exception
* 便
* 使Constants
* @param e Exception
* @return ServerResponse
*/
@ExceptionHandler(Exception.class)
public ServerResponse handleException(Exception e){
log.error(e.getMessage(),e);
return ServerResponse.createByErrorCodeMessage(Constants.RESP_STATUS_INTERNAL_ERROR,"系统异常,请稍后再试");
public ServerResponse handleException(Exception e) {
log.error(e.getMessage(), e);
return ServerResponse.createByErrorCodeMessage(Constants.RESP_STATUS_INTERNAL_ERROR, "系统异常,请稍后再试");
}
/**
* SnailmallException
*
*
* @param e SnailmallException
* @return ServerResponse
*/
@ExceptionHandler(SnailmallException.class)
public ServerResponse handleException(SnailmallException e){
log.error(e.getMessage(),e);
return ServerResponse.createByErrorCodeMessage(e.getExceptionStatus(),e.getMessage());
public ServerResponse handleException(SnailmallException e) {
log.error(e.getMessage(), e);
return ServerResponse.createByErrorCodeMessage(e.getExceptionStatus(), e.getMessage());
}
}
}

@ -4,22 +4,38 @@ import com.njupt.swg.common.resp.ResponseEnum;
import lombok.Getter;
/**
* SnailmallExceptionRuntimeExceptionSnailmall
* RuntimeException便
* @Author swg.
* @Date 2019/1/1 13:18
* @CONTACT 317758022@qq.com
* @DESC
*/
@Getter
public class SnailmallException extends RuntimeException{
// 使用lombok的@Getter注解自动生成获取exceptionStatus属性值的get方法方便外部获取该异常状态码。
public class SnailmallException extends RuntimeException {
// 定义一个表示异常状态码的属性初始值设置为从ResponseEnum.ERROR中获取的状态码通常用于表示默认的错误状态码情况。
private int exceptionStatus = ResponseEnum.ERROR.getCode();
public SnailmallException(String msg){
/**
* msgSnailmallException
* RuntimeExceptionmsg使ResponseEnum.ERROR.getCode()
* 使
* @param msg
*/
public SnailmallException(String msg) {
super(msg);
}
public SnailmallException(int code,String msg){
/**
* codemsg
* RuntimeExceptionmsgcodeexceptionStatus
*
* @param code
* @param msg
*/
public SnailmallException(int code, String msg) {
super(msg);
exceptionStatus = code;
}
}
}

@ -3,23 +3,36 @@ package com.njupt.swg.common.resp;
import lombok.Getter;
/**
* ResponseEnum
* 便使
* @Author swg.
* @Date 2018/12/31 20:15
* @CONTACT 317758022@qq.com
* @DESC
*/
@Getter
// 使用lombok的@Getter注解自动为枚举中的code和desc属性生成对应的获取方法方便外部获取这些属性值。
public enum ResponseEnum {
SUCCESS(0,"SUCCESS"),
ERROR(1,"ERROR"),
ILLEGAL_ARGUMENTS(2,"ILLEGAL_ARGUMENTS"),
NEED_LOGIN(10,"NEED_LOGIN");
// 表示成功的返回状态状态码为0对应的描述信息为"SUCCESS",用于标识业务操作成功完成的情况。
SUCCESS(0, "SUCCESS"),
// 表示出现错误的返回状态状态码为1对应的描述信息为"ERROR",可用于一般性的错误情况,较为笼统地表示操作未成功。
ERROR(1, "ERROR"),
// 表示参数非法的返回状态状态码为2对应的描述信息为"ILLEGAL_ARGUMENTS",用于客户端传入的参数不符合要求等情况。
ILLEGAL_ARGUMENTS(2, "ILLEGAL_ARGUMENTS"),
// 表示需要登录的返回状态状态码为10对应的描述信息为"NEED_LOGIN",用于提示客户端当前操作需要先进行登录认证的场景。
NEED_LOGIN(10, "NEED_LOGIN");
private int code;
private String desc;
ResponseEnum(int code,String desc){
/**
*
* SUCCESSERRORcodedesc
* @param code
* @param desc 便
*/
ResponseEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
}
}

@ -4,75 +4,168 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* ServerResponse
* <T>便
* @Author swg.
* @Date 2018/12/31 20:11
* @CONTACT 317758022@qq.com
* @DESC
*/
@Getter
// 使用lombok的@Getter注解自动生成获取status、msg、data属性值的方法方便外部获取这些成员变量的值。
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
// 通过该注解配置Jackson序列化时的行为这里表示只序列化非空的属性避免返回包含大量空值的JSON数据给客户端节省网络传输和客户端解析成本。
public class ServerResponse<T> implements Serializable {
// 表示响应的状态码,用于客户端判断请求的处理结果状态,通常会对应一些预定义的状态码值,比如成功、失败等情况。
private int status;
// 表示响应的提示消息,用于向客户端传达一些关于请求处理结果的文字描述信息,便于客户端展示给用户知晓具体情况。
private String msg;
// 泛型成员变量,用于承载具体的业务数据,其类型根据实际业务场景而定,可以是对象、集合等各种数据结构类型。
private T data;
public ServerResponse(){}
public ServerResponse() {
}
public ServerResponse(int status){
/**
* ServerResponse
*
* @param status
*/
public ServerResponse(int status) {
this.status = status;
}
public ServerResponse(int status,String msg){
/**
* ServerResponse
*
* @param status
* @param msg
*/
public ServerResponse(int status, String msg) {
this.status = status;
this.msg = msg;
}
public ServerResponse(int status,T data){
/**
* ServerResponse
*
* @param status
* @param data
*/
public ServerResponse(int status, T data) {
this.status = status;
this.data = data;
}
public ServerResponse(int status,String msg,T data){
/**
* ServerResponse
*
* @param status
* @param msg
* @param data
*/
public ServerResponse(int status, String msg, T data) {
this.status = status;
this.msg = msg;
this.data = data;
}
/**
* JSONJSON
* ResponseEnum.SUCCESS
* 便
* @return truefalse
*/
@JsonIgnore
public boolean isSuccess(){
public boolean isSuccess() {
return this.status == ResponseEnum.SUCCESS.getCode();
}
/**
*
* 便ServerResponse
*/
public static <T>ServerResponse<T> createBySuccess(){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),ResponseEnum.SUCCESS.getDesc());
}
public static <T>ServerResponse<T> createBySuccessMessage(String message){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message);
}
public static <T>ServerResponse<T> createBySuccess(T data){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),data);
}
public static <T>ServerResponse<T> createBySuccess(String message,T data){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message,data);
/**
* ServerResponse使ResponseEnum.SUCCESS
*
* @param <T> ServerResponse
* @return ServerResponse
*/
public static <T> ServerResponse<T> createBySuccess() {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getDesc());
}
/**
*
* ServerResponse使
*
* @param <T> ServerResponse
* @param message
* @return ServerResponse
*/
public static <T>ServerResponse<T> createByError(){
return new ServerResponse<>(ResponseEnum.ERROR.getCode(),ResponseEnum.ERROR.getDesc());
public static <T> ServerResponse<T> createBySuccessMessage(String message) {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), message);
}
public static <T>ServerResponse<T> createByErrorMessage(String msg){
return new ServerResponse<>(ResponseEnum.ERROR.getCode(),msg);
/**
* ServerResponse使ResponseEnum.SUCCESS
*
* @param <T> ServerResponse
* @param data
* @return ServerResponse
*/
public static <T> ServerResponse<T> createBySuccess(T data) {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), data);
}
public static <T>ServerResponse<T> createByErrorCodeMessage(int code,String msg){
return new ServerResponse<>(code,msg);
/**
* ServerResponse使
*
* @param <T> ServerResponse
* @param message
* @param data
* @return ServerResponse
*/
public static <T> ServerResponse<T> createBySuccess(String message, T data) {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), message, data);
}
/**
* 便ServerResponse
*/
/**
* ServerResponse使ResponseEnum.ERROR
*
* @param <T> ServerResponse
* @return ServerResponse
*/
public static <T> ServerResponse<T> createByError() {
return new ServerResponse<>(ResponseEnum.ERROR.getCode(), ResponseEnum.ERROR.getDesc());
}
}
/**
* ServerResponse使
*
* @param <T> ServerResponse
* @param msg
* @return ServerResponse
*/
public static <T> ServerResponse<T> createByErrorMessage(String msg) {
return new ServerResponse<>(ResponseEnum.ERROR.getCode(), msg);
}
/**
* ServerResponse
*
* @param <T> ServerResponse
* @param code
* @param msg
* @return ServerResponse
*/
public static <T> ServerResponse<T> createByErrorCodeMessage(int code, String msg) {
return new ServerResponse<>(code, msg);
}
}

@ -7,8 +7,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* CategoryControllerSpringRestController
* 使访TODO
* @Author swg.
* @Date 2019/1/2 12:57
* @CONTACT 317758022@qq.com
@ -18,55 +19,69 @@ import org.springframework.web.bind.annotation.RestController;
//TODO 这里首先实现业务 关于这里重复的鉴权,后面将会移植到网关中统一去做
//TODO 先开放GET请求
@RestController
// 表明该类是一个Spring RESTful风格的控制器其内部的方法返回值会直接序列化为JSON格式并返回给客户端无需额外配置视图解析等。
@RequestMapping("/manage/category/")
// 定义该控制器处理请求的基础路径,所有该控制器下的接口请求路径都以此为前缀。
public class CategoryController {
@Autowired
// 使用Spring的依赖注入功能自动注入ICategoryService的实现类实例以便在控制器方法中调用对应的业务逻辑方法。
private ICategoryService categoryService;
/**
* ()
* ()
* categoryIdInteger0
* categoryServicegetCategoryServerResponse
*
*/
@RequestMapping("get_category.do")
public ServerResponse getCategory(@RequestParam(value = "categoryId",defaultValue = "0") Integer categoryId){
public ServerResponse getCategory(@RequestParam(value = "categoryId", defaultValue = "0") Integer categoryId) {
ServerResponse response = categoryService.getCategory(categoryId);
return response;
}
/**
*
*
* categoryNameStringparentIdint0
* IDcategoryServiceaddCategory
* ServerResponse
*/
@RequestMapping("add_category.do")
public ServerResponse addCategory(String categoryName, @RequestParam(value = "parentId",defaultValue = "0")int parentId){
ServerResponse response = categoryService.addCategory(categoryName,parentId);
public ServerResponse addCategory(String categoryName, @RequestParam(value = "parentId", defaultValue = "0") int parentId) {
ServerResponse response = categoryService.addCategory(categoryName, parentId);
return response;
}
/**
*
*
* categoryNameStringcategoryIdIntegerID
* categoryServiceupdateCategoryNameServerResponse<String>
* <String>
*/
@RequestMapping("set_category_name.do")
public ServerResponse<String> set_category_name(String categoryName,Integer categoryId){
return categoryService.updateCategoryName(categoryName,categoryId);
public ServerResponse<String> set_category_name(String categoryName, Integer categoryId) {
return categoryService.updateCategoryName(categoryName, categoryId);
}
/**
*
*
* categoryIdInteger0
* categoryServiceselectCategoryAndDeepChildrenByIdServerResponse
* 便
*/
@RequestMapping("get_deep_category.do")
public ServerResponse get_deep_category(@RequestParam(value = "categoryId",defaultValue = "0") Integer categoryId){
public ServerResponse get_deep_category(@RequestParam(value = "categoryId", defaultValue = "0") Integer categoryId) {
return categoryService.selectCategoryAndDeepChildrenById(categoryId);
}
/**
*
*
* categoryIdIntegerID
* categoryServicegetCategoryDetailServerResponse
* 便使
*/
@RequestMapping("get_category_detail.do")
public ServerResponse get_category_detail(Integer categoryId){
public ServerResponse get_category_detail(Integer categoryId) {
return categoryService.getCategoryDetail(categoryId);
}
}
}

@ -1,24 +1,79 @@
package com.njupt.swg.dao;
import com.njupt.swg.entity.Category;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* CategoryMapperMyBatisMapper
* CategoryMyBatisXMLSQL
*
* @MapperMyBatisMapperSpringMyBatis
* 便
*/
@Mapper
public interface CategoryMapper {
/**
* idIntegerid
* 11
* MyBatis<delete>SQL
* @param id
* @return
*/
int deleteByPrimaryKey(Integer id);
/**
* CategoryCategoryrecord
* 11
* MyBatisSQL<insert>
* @param record Category
* @return
*/
int insert(Category record);
/**
* CategoryCategoryrecord
* insertrecordnullnull
* MyBatisSQL<insertSelective>
* @param record Categorynull
* @return
*/
int insertSelective(Category record);
/**
* idCategoryIntegerid
* Categorynull
* MyBatisSQL<select><resultMap>Category
* @param id
* @return Categorynull
*/
Category selectByPrimaryKey(Integer id);
/**
* CategoryCategoryrecord
* recordnull
* MyBatisSQL<updateByPrimaryKeySelective>
* @param record Categorynull
* @return
*/
int updateByPrimaryKeySelective(Category record);
/**
* CategoryCategoryrecord
* record
* MyBatisSQL<updateByPrimaryKey>
* @param record Category
* @return
*/
int updateByPrimaryKey(Category record);
/**
* categoryIdIntegercategoryId
* List<Category>Category
* MyBatisSQL<selectCategoryChildrenByParentId>
* Category
* @param categoryId
* @return Category
*/
List<Category> selectCategoryChildrenByParentId(Integer categoryId);
}

@ -1,6 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!-- 该mapper标签定义了MyBatis的映射文件namespace属性指定了对应的Mapper接口的全限定名
用于将XML中的SQL语句与Java代码中的Mapper接口方法关联起来 -->
<mapper namespace="com.njupt.swg.dao.CategoryMapper" >
<!-- 定义了一个名为BaseResultMap的结果映射用于将从数据库查询结果映射到Java中的com.njupt.swg.entity.Category实体类对象。
通过<constructor>标签内的<idArg><arg>元素来指定构造函数参数与数据库表字段的对应关系,
包括字段名、JDBC类型以及对应的Java类型等信息 -->
<resultMap id="BaseResultMap" type="com.njupt.swg.entity.Category" >
<constructor >
<idArg column="id" jdbcType="INTEGER" javaType="java.lang.Integer" />
@ -12,111 +18,136 @@
<arg column="update_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
</constructor>
</resultMap>
<!-- 定义了一个名为Base_Column_List的SQL片段包含了要从数据库表中查询的列名列表
后续其他SQL语句可以通过<include>标签引用这个片段,方便复用代码 -->
<sql id="Base_Column_List" >
id, parent_id, name, status, sort_order, create_time, update_time
</sql>
<!-- 定义了一个名为selectByPrimaryKey的查询语句用于根据主键id查询记录。
resultMap属性指定了使用前面定义的BaseResultMap来映射查询结果parameterType指定了传入参数的类型为Integer对应主键的类型
SQL语句通过<include>标签引用了Base_Column_List片段来获取指定列的数据并根据传入的主键值进行筛选查询 -->
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
select
<include refid="Base_Column_List" />
from mmall_category
where id = #{id,jdbcType=INTEGER}
</select>
<!-- 定义了一个名为deleteByPrimaryKey的删除语句用于根据主键id删除记录parameterType指定了传入参数的类型为Integer对应主键的类型
SQL语句指定了从mmall_category表中删除满足id条件的记录 -->
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
delete from mmall_category
where id = #{id,jdbcType=INTEGER}
</delete>
<!-- 定义了一个名为insert的插入语句用于向mmall_category表中插入一条完整的记录parameterType指定了传入参数的类型为com.njupt.swg.entity.Category实体类
SQL语句明确列出了要插入的列名以及对应的通过#{属性名,jdbcType=类型}方式获取实体类属性值来插入数据对于create_time和update_time使用了数据库函数now()来获取当前时间插入 -->
<insert id="insert" parameterType="com.njupt.swg.entity.Category" >
insert into mmall_category (id, parent_id, name,
status, sort_order, create_time,
update_time)
values (#{id,jdbcType=INTEGER}, #{parentId,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR},
#{status,jdbcType=BIT}, #{sortOrder,jdbcType=INTEGER}, now(),
now())
insert into mmall_category (id, parent_id, name,
status, sort_order, create_time,
update_time)
values (#{id,jdbcType=INTEGER}, #{parentId,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR},
#{status,jdbcType=BIT}, #{sortOrder,jdbcType=INTEGER}, now(),
now())
</insert>
<!-- 定义了一个名为insertSelective的插入语句相比于insert语句更加灵活它会根据传入的com.njupt.swg.entity.Category实体类对象中属性是否为null来决定是否插入对应的列数据。
通过<trim>标签结合<if>条件判断来动态构建插入语句的列名和对应的值部分避免插入不必要的null值到数据库表中 -->
<insert id="insertSelective" parameterType="com.njupt.swg.entity.Category" >
insert into mmall_category
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="id != null" >
<if test="id!= null" >
id,
</if>
<if test="parentId != null" >
<if test="parentId!= null" >
parent_id,
</if>
<if test="name != null" >
<if test="name!= null" >
name,
</if>
<if test="status != null" >
<if test="status!= null" >
status,
</if>
<if test="sortOrder != null" >
<if test="sortOrder!= null" >
sort_order,
</if>
<if test="createTime != null" >
<if test="createTime!= null" >
create_time,
</if>
<if test="updateTime != null" >
<if test="updateTime!= null" >
update_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="id != null" >
<if test="id!= null" >
#{id,jdbcType=INTEGER},
</if>
<if test="parentId != null" >
<if test="parentId!= null" >
#{parentId,jdbcType=INTEGER},
</if>
<if test="name != null" >
<if test="name!= null" >
#{name,jdbcType=VARCHAR},
</if>
<if test="status != null" >
<if test="status!= null" >
#{status,jdbcType=BIT},
</if>
<if test="sortOrder != null" >
<if test="sortOrder!= null" >
#{sortOrder,jdbcType=INTEGER},
</if>
<if test="createTime != null" >
<if test="createTime!= null" >
now(),
</if>
<if test="updateTime != null" >
<if test="updateTime!= null" >
now(),
</if>
</trim>
</insert>
<!-- 定义了一个名为updateByPrimaryKeySelective的更新语句根据传入的com.njupt.swg.entity.Category实体类对象中属性是否为null来决定是否更新对应的列数据。
通过<set>标签结合<if>条件判断来动态构建更新语句的列赋值部分只更新有值变化的列避免不必要的全列更新最后根据主键id来定位要更新的记录 -->
<update id="updateByPrimaryKeySelective" parameterType="com.njupt.swg.entity.Category" >
update mmall_category
<set >
<if test="parentId != null" >
<if test="parentId!= null" >
parent_id = #{parentId,jdbcType=INTEGER},
</if>
<if test="name != null" >
<if test="name!= null" >
name = #{name,jdbcType=VARCHAR},
</if>
<if test="status != null" >
<if test="status!= null" >
status = #{status,jdbcType=BIT},
</if>
<if test="sortOrder != null" >
<if test="sortOrder!= null" >
sort_order = #{sortOrder,jdbcType=INTEGER},
</if>
<if test="createTime != null" >
<if test="createTime!= null" >
create_time = #{createTime,jdbcType=TIMESTAMP},
</if>
<if test="updateTime != null" >
<if test="updateTime!= null" >
update_time = now(),
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<!-- 定义了一个名为updateByPrimaryKey的更新语句用于根据传入的com.njupt.swg.entity.Category实体类对象来更新mmall_category表中对应主键id的记录的所有列数据
直接列出了要更新的列名和对应从实体类获取的属性值最后根据主键id来定位要更新的记录 -->
<update id="updateByPrimaryKey" parameterType="com.njupt.swg.entity.Category" >
update mmall_category
set parent_id = #{parentId,jdbcType=INTEGER},
name = #{name,jdbcType=VARCHAR},
status = #{status,jdbcType=BIT},
sort_order = #{sortOrder,jdbcType=INTEGER},
create_time = #{createTime,jdbcType=TIMESTAMP},
update_time = now()
name = #{name,jdbcType=VARCHAR},
status = #{status,jdbcType=BIT},
sort_order = #{sortOrder,jdbcType=INTEGER},
create_time = #{createTime,jdbcType=TIMESTAMP},
update_time = now()
where id = #{id,jdbcType=INTEGER}
</update>
<!-- 定义了一个名为selectCategoryChildrenByParentId的查询语句用于根据父节点的categoryId查询其对应的子节点记录。
parameterType指定了传入参数的类型为int对应categoryId的类型resultMap指定了使用前面定义的BaseResultMap来映射查询结果
SQL语句通过<include>标签引用了Base_Column_List片段来获取指定列的数据并根据传入的父节点categoryId值进行筛选查询 -->
<select id="selectCategoryChildrenByParentId" parameterType="int" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/> from mmall_category where parent_id = #{categoryId}
</select>

@ -3,24 +3,32 @@ package com.njupt.swg.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* Category
* 使LombokGetter/Setter
* Java
*/
@Data
// 使用Lombok的@Data注解会自动为类中的所有非静态、非final属性生成Getter、Setter方法同时还会生成toString、equals、hashCode等方法方便对象的操作和比较等。
@AllArgsConstructor
// 使用Lombok的@AllArgsConstructor注解会自动生成一个包含所有属性的构造函数方便在创建对象时一次性传入所有属性值进行初始化。
@NoArgsConstructor
// 使用Lombok的@NoArgsConstructor注解会自动生成一个无参构造函数在一些框架如MyBatis在实例化对象等场景可能需要使用无参构造函数的情况下很有用。
public class Category {
// 对应数据库表中品类记录的唯一标识字段用于区分不同的品类类型为Integer。
private Integer id;
// 表示品类的父节点ID用于构建品类之间的层级关系类型为Integer通过该字段可以知道某个品类所属的上级品类若为顶级品类则可能为特定的默认值如0等表示无父节点
private Integer parentId;
// 品类的名称类型为String用于直观地标识和区分不同的品类例如“电子产品”“服装”等具体的品类名称。
private String name;
// 表示品类的状态类型为Boolean可用于标记品类是否有效、是否启用等业务状态例如true表示该品类正常可用false表示已停用等情况。
private Boolean status;
// 用于指定品类在展示、排序等场景下的顺序类型为Integer数值越小可能表示在排序中越靠前等方便对多个品类进行顺序排列。
private Integer sortOrder;
// 记录品类创建的时间类型为Date用于记录该品类首次被添加到系统中的时间点方便后续进行时间相关的查询、统计等操作。
private Date createTime;
// 记录品类最后一次更新的时间类型为Date每次对品类的相关信息如名称、状态等进行修改操作后会更新该时间字段便于追踪品类信息的变更情况。
private Date updateTime;
}

@ -4,40 +4,45 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
import java.util.Date;
/**
* UserJava
* 使LombokGetter/SettertoString
* Serializable便
* @Author swg.
* @Date 2018/12/31 21:01
* @CONTACT 317758022@qq.com
* @DESC
*/
@Data
// 使用Lombok的@Data注解会自动为类中的所有非静态、非final属性生成Getter、Setter方法同时还会生成toString、equals、hashCode等方法方便对用户对象进行操作、比较以及以字符串形式展示等操作。
@NoArgsConstructor
// 使用Lombok的@NoArgsConstructor注解会自动生成一个无参构造函数在一些框架初始化对象或者通过反射创建对象等场景中可能会需要使用无参构造函数。
@AllArgsConstructor
// 使用Lombok的@AllArgsConstructor注解会自动生成一个包含所有属性的构造函数方便在创建用户对象时一次性传入所有属性值来进行初始化操作。
@ToString
// 使用Lombok的@ToString注解会自动重写toString方法使得在调用该方法时能够以一种比较友好的格式输出用户对象的各个属性值便于调试和查看对象内容。
public class User implements Serializable {
// 用户在系统中的唯一标识通常由数据库自动生成或者按照一定规则分配类型为Integer用于区分不同的用户个体。
private Integer id;
// 用户登录系统时使用的用户名类型为String要求具有唯一性一般情况下方便用户进行身份识别和登录操作。
private String username;
// 用户登录系统时使用的密码类型为String通常会经过加密处理后存储以保障用户账户的安全性。
private String password;
// 用户的电子邮箱地址类型为String可用于接收系统发送的通知、验证邮件等也是一种常用的用户联系方式和身份验证方式之一。
private String email;
// 用户的手机号码类型为String同样可用于接收短信通知、作为登录验证的一种方式等是重要的用户联系方式。
private String phone;
// 用户设置的密保问题类型为String用于在用户忘记密码等情况下进行身份验证辅助找回密码等操作。
private String question;
// 用户针对密保问题设置的答案类型为String必须与密保问题相对应用于验证用户身份的合法性。
private String answer;
//角色0-管理员,1-普通用户
// 用于标识用户在系统中的角色类型为Integer0表示管理员角色拥有较高的系统操作权限1表示普通用户角色权限相对受限只能进行一些常规的业务操作。
private Integer role;
// 记录用户账号创建的时间类型为Date便于后续查询用户注册时间、统计不同时间段的注册人数等与时间相关的业务操作。
private Date createTime;
// 记录用户账号信息最后一次更新的时间类型为Date比如用户修改密码、更新联系方式等操作后会更新该时间字段方便追踪用户信息的变更情况。
private Date updateTime;
}

@ -16,115 +16,165 @@ import java.util.List;
import java.util.Set;
/**
* CategoryServiceImplICategoryService
* CategoryMapperSnailmallException
* 使ServerResponse
* @Author swg.
* @Date 2019/1/2 12:54
* @CONTACT 317758022@qq.com
* @DESC
*/
@Service
// 该注解表明这个类是Spring框架中的一个服务层组件Spring会自动扫描并管理这个类使其可以被注入到其他需要使用的地方。
@Slf4j
public class CategoryServiceImpl implements ICategoryService{
// 使用lombok的@Slf4j注解自动生成一个名为log的日志记录对象方便在类中记录日志信息便于后续调试和查看业务执行情况。
public class CategoryServiceImpl implements ICategoryService {
@Autowired
// 使用Spring的依赖注入功能自动注入CategoryMapper的实例以便调用其定义的数据库操作方法实现与数据库的交互。
private CategoryMapper categoryMapper;
/**
* ID
*
* @param categoryId IDnull
* @return ServerResponse
*/
@Override
public ServerResponse getCategory(Integer categoryId) {
//1.校验参数
if(categoryId == null){
// 1.校验参数
if (categoryId == null) {
// 如果品类ID为null说明参数不符合要求抛出SnailmallException异常提示“未找到该品类”由全局异常处理机制进行处理。
throw new SnailmallException("未找到该品类");
}
//2.根据父亲id获取这个父亲下一级所有子ID
// 2.根据父亲id获取这个父亲下一级所有子ID
List<Category> categoryList = categoryMapper.selectCategoryChildrenByParentId(categoryId);
if(CollectionUtils.isEmpty(categoryList)){
if (CollectionUtils.isEmpty(categoryList)) {
// 如果获取到的子品类列表为空,说明该节点下没有任何子节点,记录相应的日志信息供后续查看。
log.info("该节点下没有任何子节点");
}
return ServerResponse.createBySuccess(categoryList);
}
/**
* ID
*
* @param categoryName
* @param parentId ID0
* @return ServerResponse
*/
@Override
public ServerResponse addCategory(String categoryName, int parentId) {
//1.校验参数
if(StringUtils.isBlank(categoryName)){
// 1.校验参数
if (StringUtils.isBlank(categoryName)) {
// 如果品类名称为空字符串说明参数不符合要求抛出SnailmallException异常提示“品类名字不能为空”由全局异常处理机制进行处理。
throw new SnailmallException("品类名字不能为空");
}
//2.创建类目
// 2.创建类目
Category category = new Category();
category.setName(categoryName);
category.setParentId(parentId);
category.setStatus(true);
int resultCount = categoryMapper.insert(category);
if(resultCount > 0){
if (resultCount > 0) {
// 如果数据库插入操作影响的行数大于0说明插入成功返回包含“添加品类成功”提示消息的成功响应。
return ServerResponse.createBySuccessMessage("添加品类成功");
}
return ServerResponse.createByErrorMessage("添加品类失败");
}
/**
* ID
*
* @param categoryName
* @param categoryId ID
* @return ServerResponse<String>
* <String>
*/
@Override
public ServerResponse<String> updateCategoryName(String categoryName, Integer categoryId) {
//1.校验参数
if(StringUtils.isBlank(categoryName)){
// 1.校验参数
if (StringUtils.isBlank(categoryName)) {
// 如果品类名称为空字符串说明参数不符合要求抛出SnailmallException异常提示“品类名字不能为空”由全局异常处理机制进行处理。
throw new SnailmallException("品类名字不能为空");
}
//2.根据id获取品类
// 2.根据id获取品类
Category tmpCat = categoryMapper.selectByPrimaryKey(categoryId);
if(tmpCat == null){
if (tmpCat == null) {
// 如果根据ID没有查询到对应的品类说明品类不存在抛出SnailmallException异常提示“品类不存在”由全局异常处理机制进行处理。
throw new SnailmallException("品类不存在");
}
//3.更新品类名称
// 3.更新品类名称
Category category = new Category();
category.setId(categoryId);
category.setName(categoryName);
int resultCount = categoryMapper.updateByPrimaryKeySelective(category);
if(resultCount > 0){
if (resultCount > 0) {
// 如果数据库更新操作影响的行数大于0说明更新成功返回包含“更新品类名称成功”提示消息的成功响应。
return ServerResponse.createBySuccessMessage("更新品类名称成功");
}
return ServerResponse.createByErrorMessage("更新品类名称失败");
}
/**
* IDID
*
* @param categoryId IDnull
* @return ServerResponseIDID
*/
@Override
public ServerResponse selectCategoryAndDeepChildrenById(Integer categoryId) {
//1、创建一个空Set用来存放不重复的品类对象--去重
// 1、创建一个空Set用来存放不重复的品类对象--去重
Set<Category> categorySet = Sets.newHashSet();
//2、递归获取所有的子节点儿子、孙子、等等包括自己也添加进去
findChildCategory(categorySet,categoryId);
//3、将递归获取到的品类id取出来放进list中
// 2、递归获取所有的子节点儿子、孙子、等等包括自己也添加进去
findChildCategory(categorySet, categoryId);
// 3、将递归获取到的品类id取出来放进list中
List<Integer> categoryIdList = new ArrayList<>();
if(categoryId != null){
for(Category category:categorySet){
if (categoryId!= null) {
for (Category category : categorySet) {
categoryIdList.add(category.getId());
}
}
return ServerResponse.createBySuccess(categoryIdList);
}
private Set<Category> findChildCategory(Set<Category> categorySet,Integer categoryId){
//4、如果自己不为空的话首先把自己添加进去如果自己为空这个递归分支就结束所以也是一个停止条件
/**
* selectCategoryAndDeepChildrenByIdSet
*
* @param categorySet Set
* @param categoryId IDnull
* @return Set便
*/
private Set<Category> findChildCategory(Set<Category> categorySet, Integer categoryId) {
// 4、如果自己不为空的话首先把自己添加进去如果自己为空这个递归分支就结束所以也是一个停止条件
Category category = categoryMapper.selectByPrimaryKey(categoryId);
if(category != null){
if (category!= null) {
categorySet.add(category);
}
//5、根据父亲id获取下一级所有品类即先获取儿子们
// 5、根据父亲id获取下一级所有品类即先获取儿子们
List<Category> categoryList = categoryMapper.selectCategoryChildrenByParentId(categoryId);
//6、根据每一个儿子再获取儿子的儿子们递归下去
for(Category categoryItem:categoryList){
findChildCategory(categorySet,categoryItem.getId());
// 6、根据每一个儿子再获取儿子的儿子们递归下去
for (Category categoryItem : categoryList) {
findChildCategory(categorySet, categoryItem.getId());
}
return categorySet;
}
/**
* ID
*
* @param categoryId IDnull
* @return ServerResponse
*/
@Override
public ServerResponse getCategoryDetail(Integer categoryId) {
if(categoryId == null){
if (categoryId == null) {
return ServerResponse.createByErrorMessage("参数不能为空");
}
Category category = categoryMapper.selectByPrimaryKey(categoryId);
if(category == null){
if (category == null) {
return ServerResponse.createByErrorMessage("品类不存在");
}
return ServerResponse.createBySuccess(category);
}
}
}

@ -4,6 +4,9 @@ import com.njupt.swg.common.resp.ServerResponse;
import com.njupt.swg.entity.Category;
/**
* ICategoryService
* CategoryServiceImpl便
*
* @Author swg.
* @Date 2019/1/2 12:54
* @CONTACT 317758022@qq.com
@ -11,19 +14,50 @@ import com.njupt.swg.entity.Category;
*/
public interface ICategoryService {
/** 根据类目id获取其下面所有的一级子类目 **/
/**
* id
* ID使ServerResponse便
* @param categoryId ID
* @return ServerResponse
*/
ServerResponse getCategory(Integer categoryId);
/** 新建一个商品类目 **/
/**
*
* ID使ServerResponse
*
* @param categoryName
* @param parentId ID0
* @return ServerResponse
*/
ServerResponse addCategory(String categoryName, int parentId);
/** 更新品类名称 **/
/**
*
* ID使ServerResponse<String>
* <String>
* @param categoryName
* @param categoryId
* @return ServerResponse<String>
*/
ServerResponse<String> updateCategoryName(String categoryName, Integer categoryId);
/** 递归查询出所有品类 **/
/**
*
* ID使ServerResponse便
*
* @param categoryId null
* @return ServerResponseID
*/
ServerResponse selectCategoryAndDeepChildrenById(Integer categoryId);
/** 被其他服务调用的接口 **/
/**
*
* ID使ServerResponse便
* 使
* @param categoryId ID
* @return ServerResponse
*/
ServerResponse getCategoryDetail(Integer categoryId);
}
}

@ -104,6 +104,11 @@
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<dependency>
<groupId>backport-util-concurrent</groupId>
<artifactId>backport-util-concurrent</artifactId>
<version>3.1</version>
</dependency>
</dependencies>

@ -8,73 +8,96 @@ import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
* CommonCacheUtilRedis
* JedisPoolWrapperJedisPoolRedisSnailmallException
* Spring@Component便Spring使
* @Author swg.
* @Date 2019/1/1 15:03
* @CONTACT 317758022@qq.com
* @DESC
*/
@Component
// 该注解表明这个类是Spring框架中的一个组件Spring会自动扫描并将其纳入到容器管理中使其可以被其他需要使用的类进行依赖注入。
@Slf4j
// 使用lombok的@Slf4j注解自动生成一个名为log的日志记录对象方便在类中记录日志信息便于后续调试和查看与Redis交互过程中出现的问题情况。
public class CommonCacheUtil {
@Autowired
// 使用Spring的依赖注入功能自动注入JedisPoolWrapper的实例通过它来获取JedisPool从而能够获取Jedis客户端连接到Redis服务器进行操作。
private JedisPoolWrapper jedisPoolWrapper;
/**
* key
* keyRedis
*
* @param key Redis
* @param value RedisJSON
*/
public void cache(String key, String value) {
try {
JedisPool pool = jedisPoolWrapper.getJedisPool();
if (pool != null) {
try (Jedis Jedis = pool.getResource()) {
Jedis.select(0);
Jedis.set(key, value);
if (pool!= null) {
try (Jedis jedis = pool.getResource()) {
jedis.select(0);
// 使用Jedis客户端将指定的键值对存储到Redis中默认存储到Redis的第0个数据库Redis可以有多个数据库通过select方法选择
jedis.set(key, value);
}
}
} catch (Exception e) {
log.error("redis存值失败", e);
// 如果在存储过程中出现异常记录错误日志并抛出SnailmallException异常提示“redis报错”由全局异常处理机制进行处理。
throw new SnailmallException("redis报错");
}
}
/**
* key
* keyRedis
*
* @param key RedisRedis
* @return Redisnull
*/
public String getCacheValue(String key) {
String value = null;
try {
JedisPool pool = jedisPoolWrapper.getJedisPool();
if (pool != null) {
try (Jedis Jedis = pool.getResource()) {
Jedis.select(0);
value = Jedis.get(key);
if (pool!= null) {
try (Jedis jedis = pool.getResource()) {
jedis.select(0);
// 使用Jedis客户端根据指定的键从Redis中获取对应的值默认从Redis的第0个数据库中查找。
value = jedis.get(key);
}
}
} catch (Exception e) {
log.error("redis获取指失败", e);
// 如果在获取过程中出现异常记录错误日志并抛出SnailmallException异常提示“redis报错”由全局异常处理机制进行处理。
throw new SnailmallException("redis报错");
}
return value;
}
/**
* key
* keyRedis
*
* @param key Redis
* @param value RedisJSON
* @param expire Redis
* @return 10
*/
public long cacheNxExpire(String key, String value, int expire) {
long result = 0;
try {
JedisPool pool = jedisPoolWrapper.getJedisPool();
if (pool != null) {
if (pool!= null) {
try (Jedis jedis = pool.getResource()) {
jedis.select(0);
// 先使用Jedis的setnx方法尝试设置键值对只有在键不存在时才会设置成功返回1若键已存在则设置失败返回0。这是一个原子操作。
result = jedis.setnx(key, value);
// 如果设置键值对成功即setnx返回1再使用expire方法设置该键对应的缓存数据的过期时间单位为秒。
jedis.expire(key, expire);
}
}
} catch (Exception e) {
log.error("redis塞值和设置缓存时间失败", e);
// 如果在设置过程中出现异常记录错误日志并抛出SnailmallException异常提示“redis报错”由全局异常处理机制进行处理。
throw new SnailmallException("redis报错");
}
@ -82,23 +105,24 @@ public class CommonCacheUtil {
}
/**
* key
* keyRedis
*
* @param key Redis
*/
public void delKey(String key) {
JedisPool pool = jedisPoolWrapper.getJedisPool();
if (pool != null) {
if (pool!= null) {
try (Jedis jedis = pool.getResource()) {
jedis.select(0);
try {
// 使用Jedis客户端根据指定的键从Redis中删除对应的缓存数据默认从Redis的第0个数据库中删除。
jedis.del(key);
} catch (Exception e) {
log.error("从redis中删除失败", e);
// 如果在删除过程中出现异常记录错误日志并抛出SnailmallException异常提示“redis报错”由全局异常处理机制进行处理。
throw new SnailmallException("redis报错");
}
}
}
}
}
}

@ -5,38 +5,57 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import javax.annotation.PostConstruct;
/**
* JedisPoolWrapperJedisPoolJedisPool便便JedisPoolRedis
* ParametersRedis@PostConstructJedisPoolJedisPool使
* RedisRedisRedis使
* @Author swg.
* @Date 2019/1/1 15:00
* @CONTACT 317758022@qq.com
* @DESC redisredishash
*/
@Component
// 该注解表明这个类是Spring框架中的一个组件Spring会自动扫描并将其纳入到容器管理中使其可以被其他需要使用的类进行依赖注入。
@Slf4j
// 使用lombok的@Slf4j注解自动生成一个名为log的日志记录对象方便在类中记录日志信息便于后续调试和查看与Redis连接池初始化等相关的情况。
public class JedisPoolWrapper {
@Autowired
// 使用Spring的依赖注入功能自动注入Parameters对象该对象应该包含了如Redis的最大连接数、最大空闲连接数、最大等待时间等配置参数相关信息用于构建JedisPool。
private Parameters parameters;
private JedisPool jedisPool = null;
/**
* @PostConstruct
* JedisPoolParametersJedisPool使JedisRedis
*/
@PostConstruct
public void init(){
public void init() {
try {
JedisPoolConfig config = new JedisPoolConfig();
// 设置JedisPool中最大的连接总数从Parameters对象中获取对应的配置参数用于控制同时可以从连接池获取的Jedis客户端的最大数量避免过多连接导致资源耗尽等问题。
config.setMaxTotal(parameters.getRedisMaxTotal());
// 设置JedisPool中最大的空闲连接数从Parameters对象中获取对应的配置参数空闲连接是指在连接池中暂时未被使用的连接合理设置可提高连接复用效率。
config.setMaxIdle(parameters.getRedisMaxIdle());
// 设置获取连接时的最大等待时间单位为毫秒从Parameters对象中获取对应的配置参数当连接池中的连接都被占用时新的获取连接请求会等待超过该时间则抛出异常避免长时间阻塞。
config.setMaxWaitMillis(parameters.getRedisMaxWaitMillis());
jedisPool = new JedisPool(config,parameters.getRedisHost(),parameters.getRedisPort(),2000,"xxx");
// 创建JedisPool对象传入配置信息、Redis服务器的主机地址、端口号以及连接超时时间这里设置为2000毫秒和密码示例中密码为"xxx",实际中应替换为真实密码)等参数,
// 这样就构建好了一个可以连接到指定Redis服务器的连接池对象后续可以通过它获取Jedis客户端进行Redis操作。
jedisPool = new JedisPool(config, parameters.getRedisHost(), parameters.getRedisPort(), 2000, "xxx");
log.info("【初始化redis连接池成功】");
}catch (Exception e){
log.error("【初始化redis连接池失败】",e);
} catch (Exception e) {
log.error("【初始化redis连接池失败】", e);
}
}
/**
* JedisPool使JedisPool
* JedisRedis
* @return JedisPoolinitjedisPoolnullnull
*/
public JedisPool getJedisPool() {
return jedisPool;
}
}
}

@ -5,29 +5,43 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* Parameters使Spring@Valueapplication.propertiesapplication.yml
* 便使RedisZookeeper
* 使Lombok@DataGetterSetter便访
* @Author swg.
* @Date 2019/1/1 14:27
* @CONTACT 317758022@qq.com
* @DESC
*/
@Component
// 该注解表明这个类是Spring框架中的一个组件Spring会自动扫描并将其纳入到容器管理中使其可以被其他需要使用的类进行依赖注入方便获取配置参数。
@Data
// 使用Lombok的@Data注解会自动为类中的所有非静态、非final属性生成Getter、Setter方法同时还会生成toString、equals、hashCode等方法方便对配置参数对象进行操作和使用。
public class Parameters {
/*****redis config start*******/
// 使用Spring的@Value注解从配置文件中读取名为"redis.host"的配置项的值并将其注入到redisHost属性中该属性用于存储Redis服务器的主机地址方便后续连接Redis时使用。
@Value("${redis.host}")
private String redisHost;
// 使用Spring的@Value注解从配置文件中读取名为"redis.port"的配置项的值并将其注入到redisPort属性中该属性用于存储Redis服务器的端口号以便在创建连接时指定正确的端口进行通信。
@Value("${redis.port}")
private int redisPort;
// 此处可能存在属性名的配置错误通常max-idle应该对应MaxIdlemax-total对应MaxTotal但按照代码逻辑该属性用于从配置文件中读取名为"redis.max-idle"的配置项的值,
// 并注入到redisMaxTotal属性中本意可能是存储Redis连接池的最大空闲连接数相关参数需确认配置项与属性的正确对应关系
@Value("${redis.max-idle}")
private int redisMaxTotal;
// 此处同样可能存在属性名的配置错误通常max-total应该对应MaxTotalmax-idle对应MaxIdle按照代码逻辑该属性用于从配置文件中读取名为"redis.max-total"的配置项的值,
// 并注入到redisMaxIdle属性中本意可能是存储Redis连接池的最大连接总数相关参数需确认配置项与属性的正确对应关系
@Value("${redis.max-total}")
private int redisMaxIdle;
// 使用Spring的@Value注解从配置文件中读取名为"redis.max-wait-millis"的配置项的值并将其注入到redisMaxWaitMillis属性中该属性用于存储获取Redis连接时的最大等待时间单位为毫秒
// 控制当连接池无可用连接时的等待时长,避免长时间阻塞。
@Value("${redis.max-wait-millis}")
private int redisMaxWaitMillis;
/*****redis config end*******/
/*****curator config start*******/
// 使用Spring的@Value注解从配置文件中读取名为"zk.host"的配置项的值并将其注入到zkHost属性中该属性用于存储Zookeeper服务器的主机地址方便后续与Zookeeper进行交互时使用。
@Value("${zk.host}")
private String zkHost;
/*****curator config end*******/
}
}

@ -1,46 +1,89 @@
package com.njupt.swg.common.constants;
/**
* ConstantsRedis
* 便使
* @Author swg.
* @Date 2019/1/1 13:19
* @CONTACT 317758022@qq.com
* @DESC
*/
public class Constants {
/**自定义状态码 start**/
/**
* 便使
*/
/**
* HTTP200使
*/
public static final int RESP_STATUS_OK = 200;
/**
* HTTP401访
*/
public static final int RESP_STATUS_NOAUTH = 401;
/**
* HTTP500
*/
public static final int RESP_STATUS_INTERNAL_ERROR = 500;
/**
* HTTP400
*/
public static final int RESP_STATUS_BADREQUEST = 400;
/**自定义状态码 end**/
/**
*
*/
/***redis user相关的key以这个打头**/
/**
* RediskeytokenRedis便
*/
public static final String TOKEN_PREFIX = "user_";
/**
* redis
* RedisCacheExtimeRedis
*/
public interface RedisCacheExtime{
int REDIS_SESSION_EXTIME = 60 * 60 * 10;//30分钟
public interface RedisCacheExtime {
/**
* Redis60 * 60 * 10103010
* Redis
*/
int REDIS_SESSION_EXTIME = 60 * 60 * 10;
}
/** 用户注册判断重复的参数类型 start **/
/**
* 使便
*/
/**
*
*/
public static final String EMAIL = "email";
/**
* 使
*/
public static final String USERNAME = "username";
/** 用户注册判断重复的参数类型 end **/
/**
*
*/
/** 用户角色 **/
public interface Role{
int ROLE_CUSTOME = 0;//普通用户
int ROLE_ADMIN = 1;//管理员用户
/**
* Role便
*/
public interface Role {
/**
* 访
*/
int ROLE_CUSTOME = 0;
/**
*
*/
int ROLE_ADMIN = 1;
}
/**用户注册分布式锁路径***/
/**
* 使线Zookeeper
*/
public static final String USER_REGISTER_DISTRIBUTE_LOCK_PATH = "/user_reg";
}
}

@ -1,6 +1,5 @@
package com.njupt.swg.common.exception;
import com.njupt.swg.common.constants.Constants;
import com.njupt.swg.common.resp.ServerResponse;
import lombok.extern.slf4j.Slf4j;
@ -9,25 +8,50 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* `ExceptionHandlerAdvice` Spring
* 使 `@ControllerAdvice``@ResponseBody` `@Slf4j` `@ExceptionHandler`
*
*
* @Author swg.
* @Date 2019/1/1 13:21
* @CONTACT 317758022@qq.com
* @DESC
*/
@ControllerAdvice
// `@ControllerAdvice` 注解表明这个类是一个全局的异常处理组件它可以对整个应用程序中的控制器Controller层抛出的异常进行统一处理。
@ResponseBody
// `@ResponseBody` 注解表示该类中处理异常的方法返回的结果会直接作为响应体返回给客户端,而不是进行视图解析等操作,通常用于返回 JSON 格式等数据给客户端。
@Slf4j
// `@Slf4j` 注解使用了 Lombok 提供的日志功能,会自动生成一个名为 `log` 的日志记录对象,方便在类中记录异常相关的日志信息,便于后续排查问题。
public class ExceptionHandlerAdvice {
/**
* 使 `@ExceptionHandler(Exception.class)` `Exception`
* `Exception`
* 便
*
* 使 `Constants` `RESP_STATUS_INTERNAL_ERROR`
*
* @param e `Exception`
* @return `ServerResponse`
*/
@ExceptionHandler(Exception.class)
public ServerResponse handleException(Exception e){
log.error(e.getMessage(),e);
return ServerResponse.createByErrorCodeMessage(Constants.RESP_STATUS_INTERNAL_ERROR,"系统异常,请稍后再试");
public ServerResponse handleException(Exception e) {
log.error(e.getMessage(), e);
return ServerResponse.createByErrorCodeMessage(Constants.RESP_STATUS_INTERNAL_ERROR, "系统异常,请稍后再试");
}
/**
* 使 `@ExceptionHandler(SnailmallException.class)` `SnailmallException`
* `SnailmallException`
* `e.getExceptionStatus()` `e.getMessage()`
*
*
* @param e `SnailmallException`
* @return `ServerResponse`
*/
@ExceptionHandler(SnailmallException.class)
public ServerResponse handleException(SnailmallException e){
log.error(e.getMessage(),e);
return ServerResponse.createByErrorCodeMessage(e.getExceptionStatus(),e.getMessage());
public ServerResponse handleException(SnailmallException e) {
log.error(e.getMessage(), e);
return ServerResponse.createByErrorCodeMessage(e.getExceptionStatus(), e.getMessage());
}
}
}

@ -4,22 +4,43 @@ import com.njupt.swg.common.resp.ResponseEnum;
import lombok.Getter;
/**
* `SnailmallException` `java.lang.RuntimeException`
* 使便
*
* @Author swg.
* @Date 2019/1/1 13:18
* @CONTACT 317758022@qq.com
* @DESC
*/
@Getter
public class SnailmallException extends RuntimeException{
// 使用 Lombok 的 `@Getter` 注解,会自动为类中的 `exceptionStatus` 属性生成对应的 `get` 方法,方便外部获取该异常状态码的值,遵循了 Java 的封装原则,同时简化了代码编写。
public class SnailmallException extends RuntimeException {
// 定义一个用于存储异常状态码的属性,初始值设置为从 `ResponseEnum.ERROR` 中获取的状态码,
// `ResponseEnum` 应该是一个枚举类,用于定义各种响应状态的枚举值,这里默认使用 `ERROR` 对应的状态码作为异常状态码的初始值,
// 意味着在没有显式指定异常状态码时,将采用这个默认值来表示异常情况。
private int exceptionStatus = ResponseEnum.ERROR.getCode();
public SnailmallException(String msg){
/**
* `SnailmallException` `msg`
* `RuntimeException` `msg` 使 `ResponseEnum.ERROR.getCode()`
* 使使
*
* @param msg 便
*/
public SnailmallException(String msg) {
super(msg);
}
public SnailmallException(int code,String msg){
/**
* `code` `msg`
* `RuntimeException` `msg` `code` `exceptionStatus`
*
*
* @param code 便
* @param msg `msg`
*/
public SnailmallException(int code, String msg) {
super(msg);
exceptionStatus = code;
}
}
}

@ -3,23 +3,44 @@ package com.njupt.swg.common.resp;
import lombok.Getter;
/**
* `ResponseEnum`
* 便使
* 使
*
* @Author swg.
* @Date 2018/12/31 20:15
* @CONTACT 317758022@qq.com
* @DESC
*/
@Getter
// 使用 Lombok 的 `@Getter` 注解,会自动为枚举中的 `code` 和 `desc` 属性生成对应的获取方法,方便外部代码获取这些属性值,
// 这样其他类在需要使用返回状态的状态码或者描述信息时,可以直接调用相应的获取方法,遵循了 Java 的封装原则,同时减少了代码冗余。
public enum ResponseEnum {
SUCCESS(0,"SUCCESS"),
ERROR(1,"ERROR"),
ILLEGAL_ARGUMENTS(2,"ILLEGAL_ARGUMENTS"),
NEED_LOGIN(10,"NEED_LOGIN");
// 表示业务操作成功的枚举常量,状态码为 `0`,对应的描述信息为 `"SUCCESS"`,通常用于标识业务操作顺利完成的情况,
// 例如查询操作成功获取到了期望的数据、新增数据成功插入到数据库等场景下,可以使用该枚举常量来表示操作成功的返回状态。
SUCCESS(0, "SUCCESS"),
// 表示业务操作出现错误的枚举常量,状态码为 `1`,对应的描述信息为 `"ERROR"`,这是一个比较笼统的错误标识,
// 可用于一般性的业务操作失败情况,比如在执行某个业务逻辑时发生了未预期的异常、或者满足了某种错误条件但不需要细分具体错误类型时,可以使用该枚举常量返回给调用者表示操作失败。
ERROR(1, "ERROR"),
// 表示参数非法的枚举常量,状态码为 `2`,对应的描述信息为 `"ILLEGAL_ARGUMENTS"`,主要用于客户端传入的参数不符合要求的场景,
// 例如参数格式错误、缺少必要参数、参数取值超出合法范围等情况,服务端在进行参数校验后发现参数不合法时,就可以返回该枚举常量对应的状态来告知调用者参数存在问题。
ILLEGAL_ARGUMENTS(2, "ILLEGAL_ARGUMENTS"),
// 表示需要登录的枚举常量,状态码为 `10`,对应的描述信息为 `"NEED_LOGIN"`,用于提示客户端当前操作需要先进行登录认证的场景,
// 比如客户端尝试访问某些需要登录权限才能操作的接口,但却未提供有效的登录凭证时,服务端就可以返回该枚举常量对应的状态,告知客户端需要先登录再进行后续操作。
NEED_LOGIN(10, "NEED_LOGIN");
private int code;
private String desc;
ResponseEnum(int code,String desc){
/**
*
* `SUCCESS``ERROR` `code` `desc`
*
* @param code 便
* @param desc 便
*/
ResponseEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
}
}

@ -5,75 +5,167 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* `ServerResponse`
* `T` 使 Jackson
* 便 `Serializable` 使
*
* @Author swg.
* @Date 2018/12/31 20:11
* @CONTACT 317758022@qq.com
* @DESC
*/
@Getter
// 使用 Lombok 的 `@Getter` 注解,会自动为类中的 `status`、`msg` 和 `data` 属性生成对应的 `get` 方法,方便外部获取这些属性的值,遵循了 Java 的封装原则,简化了代码编写。
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
// 使用 Jackson 的 `@JsonSerialize` 注解,配置序列化时的行为,这里指定了只包含非 `null` 的属性进行序列化,
// 也就是说在将 `ServerResponse` 对象转换为 JSON 格式(例如返回给客户端时),如果 `msg` 或者 `data` 等属性为 `null`,则不会包含在序列化后的 JSON 数据中,减少不必要的数据传输。
public class ServerResponse<T> implements Serializable {
// 用于存储操作的状态码,通过与 `ResponseEnum` 枚举类中的状态码进行对比,可以判断操作是成功还是失败以及具体的状态类型,例如 `0` 表示成功等情况。
private int status;
// 用于存储提示消息,一般是对操作结果的文字描述,比如操作成功时可以是 "操作成功",失败时可以是具体的错误原因等,方便客户端理解服务端返回的结果含义。
private String msg;
// 泛型属性,用于存储具体的业务数据内容,类型根据实际业务场景而定,可以是单个对象、对象列表或者其他复杂的数据结构等,比如查询用户信息时可以是 `User` 对象,查询用户列表时可以是 `List<User>` 类型的数据。
private T data;
public ServerResponse(){}
public ServerResponse() {}
private ServerResponse(int status){
// 私有构造方法,接收一个状态码参数,用于创建只包含状态码的 `ServerResponse` 对象实例,通常在一些内部初始化或者特定场景下使用,
// 外部一般通过静态方法来创建完整的响应对象,该构造方法主要是为了代码复用和方便在类内部构建不同情况的对象。
private ServerResponse(int status) {
this.status = status;
}
private ServerResponse(int status,String msg){
// 私有构造方法,接收状态码和提示消息两个参数,用于创建包含状态码和提示消息的 `ServerResponse` 对象实例,
// 方便在已知操作状态码和对应的提示消息时构建响应对象,比如在出现错误时,传入错误状态码和具体的错误消息来创建相应的返回对象。
private ServerResponse(int status, String msg) {
this.status = status;
this.msg = msg;
}
private ServerResponse(int status,T data){
// 私有构造方法,接收状态码和业务数据两个参数,用于创建包含状态码和具体数据的 `ServerResponse` 对象实例,
// 常用于操作成功且有具体数据需要返回给客户端的场景,例如查询操作成功获取到了数据,就可以通过传入成功状态码和查询到的数据来构建响应对象。
private ServerResponse(int status, T data) {
this.status = status;
this.data = data;
}
private ServerResponse(int status,String msg,T data){
// 私有构造方法,接收状态码、提示消息和业务数据三个参数,用于创建包含完整信息(状态码、提示消息、业务数据)的 `ServerResponse` 对象实例,
// 适用于需要详细反馈操作结果,既包含状态、提示又有具体数据的各种复杂场景,比如部分更新操作,成功时可以传入成功状态码、更新成功的提示以及更新后的数据等情况来构建返回对象。
private ServerResponse(int status, String msg, T data) {
this.status = status;
this.msg = msg;
this.data = data;
}
/**
* 使 `@JsonIgnore` `ServerResponse` JSON
* `ResponseEnum.SUCCESS` `0`
* 便便
*
* @return `true` `false`
*/
@JsonIgnore
public boolean isSuccess(){
public boolean isSuccess() {
return this.status == ResponseEnum.SUCCESS.getCode();
}
/**
*
* `ServerResponse` 便
*/
public static <T>ServerResponse<T> createBySuccess(){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),ResponseEnum.SUCCESS.getDesc());
}
public static <T>ServerResponse<T> createBySuccessMessage(String message){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message);
}
public static <T>ServerResponse<T> createBySuccess(T data){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),data);
}
public static <T>ServerResponse<T> createBySuccess(String message,T data){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message,data);
/**
* `ResponseEnum.SUCCESS.getDesc()` `ServerResponse`
* 使
*
* @param <T> 使
* @return `ServerResponse`
*/
public static <T> ServerResponse<T> createBySuccess() {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getDesc());
}
/**
*
* `ServerResponse`
* `message`
* "数据新增成功,已保存到数据库"
*
* @param <T>
* @param message
* @return `ServerResponse`
*/
public static <T>ServerResponse<T> createByError(){
return new ServerResponse<>(ResponseEnum.ERROR.getCode(),ResponseEnum.ERROR.getDesc());
public static <T> ServerResponse<T> createBySuccessMessage(String message) {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), message);
}
public static <T>ServerResponse<T> createByErrorMessage(String msg){
return new ServerResponse<>(ResponseEnum.ERROR.getCode(),msg);
/**
* `ServerResponse`
* `data` `T`
* `User`
*
* @param <T>
* @param data `ServerResponse` `data`
* @return `ServerResponse`
*/
public static <T> ServerResponse<T> createBySuccess(T data) {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), data);
}
public static <T>ServerResponse<T> createByErrorCodeMessage(int code,String msg){
return new ServerResponse<>(code,msg);
/**
* `ServerResponse`
* `message` `data`
* "用户信息更新成功" `User`
*
* @param <T> `data`
* @param message
* @param data `ServerResponse` `data`
* @return `ServerResponse`
*/
public static <T> ServerResponse<T> createBySuccess(String message, T data) {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), message, data);
}
/**
* `ServerResponse` 便
*/
/**
* `ResponseEnum.ERROR.getDesc()` `ServerResponse`
* 使使
*
* @param <T>
* @return `ServerResponse`
*/
public static <T> ServerResponse<T> createByError() {
return new ServerResponse<>(ResponseEnum.ERROR.getCode(), ResponseEnum.ERROR.getDesc());
}
}
/**
* `ServerResponse`
* `msg`
* "参数不符合要求,请检查输入参数"
*
* @param <T>
* @param msg
* @return `ServerResponse`
*/
public static <T> ServerResponse<T> createByErrorMessage(String msg) {
return new ServerResponse<>(ResponseEnum.ERROR.getCode(), msg);
}
/**
* `ServerResponse`
* `code` `msg`
*
*
* @param <T>
* @param code 便
* @param msg
* @return `ServerResponse`
*/
public static <T> ServerResponse<T> createByErrorCodeMessage(int code, String msg) {
return new ServerResponse<>(code, msg);
}
}

@ -2,47 +2,63 @@ package com.njupt.swg.common.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* cookie
* `CookieUtil` `Cookie` `Cookie` `Cookie` `Cookie`
* `Cookie` 便 `Cookie` 使
*
* @Slf4j `log` 便 `Cookie` 便 `Cookie`
*/
@Slf4j
public class CookieUtil {
// 定义了一个静态的常量字符串,表示 `Cookie` 所属的域名,用于设置 `Cookie` 的作用域名范围,这里固定为 "oursnail.cn",意味着只有在该域名下的页面请求才会携带和使用此 `Cookie`。
private final static String COOKIE_DOMAIN = "oursnail.cn";
// 定义了一个静态的常量字符串,用于表示存储登录相关信息的 `Cookie` 的名称,后续通过这个名称来识别和操作特定的登录 `Cookie`,固定为 "snailmall_login_token"。
private final static String COOKIE_NAME = "snailmall_login_token";
/**
* cookie
* @param response
* @param token
* `Cookie` `token` `Cookie`
*
* @param response `HttpServletResponse` `Cookie` 使 `Cookie`
* @param token `COOKIE_NAME` `Cookie`
*/
public static void writeLoginToken(HttpServletResponse response,String token){
Cookie ck = new Cookie(COOKIE_NAME,token);
public static void writeLoginToken(HttpServletResponse response, String token) {
// 创建一个新的 `Cookie` 对象,传入 `COOKIE_NAME` 作为 `Cookie` 的名称,`token` 作为 `Cookie` 的值,以此来构建用于存储登录凭证的 `Cookie`。
Cookie ck = new Cookie(COOKIE_NAME, token);
// 设置 `Cookie` 的域名属性,使其作用范围限定在指定的域名 "oursnail.cn" 下,只有访问该域名下的页面时,浏览器才会自动携带这个 `Cookie` 发送请求。
ck.setDomain(COOKIE_DOMAIN);
ck.setPath("/");//设值在根目录
ck.setHttpOnly(true);//不允许通过脚本访问cookie,避免脚本攻击
ck.setMaxAge(60*60*24*365);//一年,-1表示永久,单位是秒maxage不设置的话cookie就不会写入硬盘只会写在内存只在当前页面有效
log.info("write cookieName:{},cookieValue:{}",ck.getName(),ck.getValue());
// 设置 `Cookie` 的路径属性为根目录 "/",意味着在该域名下的所有页面请求都会携带这个 `Cookie`,如果设置为其他具体路径,则只有访问该路径及其子路径下的页面时才会携带。
ck.setPath("/");
// 设置 `Cookie` 的 `HttpOnly` 属性为 `true`,这是一种安全机制,使得客户端脚本(如 JavaScript无法访问该 `Cookie`可以有效避免跨站脚本攻击XSS等安全问题提高系统的安全性。
ck.setHttpOnly(true);
// 设置 `Cookie` 的最大存活时间,这里设置为 `60 * 60 * 24 * 365` 秒,即一年,表示该 `Cookie` 在客户端浏览器保存的有效期为一年,超过这个时间后,浏览器会自动删除该 `Cookie`。
// 如果设置为 `-1`,则表示永久有效(不过不同浏览器对于永久有效的处理方式可能略有差异),若不设置该属性(`maxAge` 为默认值),则 `Cookie` 不会写入硬盘,只会保存在内存中,仅在当前页面会话有效。
ck.setMaxAge(60 * 60 * 24 * 365);
log.info("write cookieName:{},cookieValue:{}", ck.getName(), ck.getValue());
// 将构建好并设置好属性的 `Cookie` 添加到 `HttpServletResponse` 的响应头中,发送给客户端浏览器,使得浏览器能够保存这个 `Cookie` 用于后续请求携带。
response.addCookie(ck);
}
/**
* cookie
* @param request
* @return
* `Cookie` `Cookie` `COOKIE_NAME` `Cookie`
* `Cookie``token` `null` `Cookie`
*
* @param request `HttpServletRequest` `Cookie` `Cookie`
* @return `String` `COOKIE_NAME` `Cookie` `token` `null`
*/
public static String readLoginToken(HttpServletRequest request){
public static String readLoginToken(HttpServletRequest request) {
// 通过 `HttpServletRequest` 的 `getCookies` 方法获取客户端发送过来的所有 `Cookie`,以数组形式返回,如果没有 `Cookie` 则返回 `null`。
Cookie[] cks = request.getCookies();
if(cks != null){
for(Cookie ck:cks){
log.info("cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue());
if(StringUtils.equals(ck.getName(),COOKIE_NAME)){
log.info("return cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue());
if (cks!= null) {
for (Cookie ck : cks) {
log.info("cookieName:{},cookieBValue:{}", ck.getName(), ck.getValue());
// 使用 `StringUtils` 的 `equals` 方法(该方法可以避免空指针异常等情况,进行安全的字符串比较)比较当前遍历到的 `Cookie` 的名称与预定义的 `COOKIE_NAME` 是否相等,
// 如果相等,则说明找到了存储登录凭证的 `Cookie`,返回其值(即登录 `token`)。
if (StringUtils.equals(ck.getName(), COOKIE_NAME)) {
log.info("return cookieName:{},cookieBValue:{}", ck.getName(), ck.getValue());
return ck.getValue();
}
}
@ -51,24 +67,29 @@ public class CookieUtil {
}
/**
*
* @param request
* @param response
* `Cookie` `Cookie` `COOKIE_NAME` `Cookie`
* `0` `Cookie`
*
* @param request `HttpServletRequest` `Cookie` 便 `Cookie`
* @param response `HttpServletResponse` `0` `Cookie` `Cookie`
*/
public static void delLoginToken(HttpServletRequest request,HttpServletResponse response){
public static void delLoginToken(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cks = request.getCookies();
if(cks != null){
for(Cookie ck:cks) {
if(StringUtils.equals(ck.getName(),COOKIE_NAME)){
if (cks!= null) {
for (Cookie ck : cks) {
if (StringUtils.equals(ck.getName(), COOKIE_NAME)) {
// 找到名称为 `COOKIE_NAME` 的 `Cookie` 后,先设置其域名属性为预定义的 `COOKIE_DOMAIN`,确保域名范围的一致性,与写入 `Cookie` 时的设置相匹配。
ck.setDomain(COOKIE_DOMAIN);
// 设置 `Cookie` 的路径属性为根目录 "/",同样与写入 `Cookie` 时的路径设置保持一致,确保能够正确地删除对应的 `Cookie`。
ck.setPath("/");
ck.setMaxAge(0);//0表示消除此cookie
log.info("del cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue());
// 设置 `Cookie` 的最大存活时间为 `0`,这表示通知客户端浏览器立即删除该 `Cookie`,使其失效,不再携带和使用该 `Cookie` 进行后续请求。
ck.setMaxAge(0);
log.info("del cookieName:{},cookieBValue:{}", ck.getName(), ck.getValue());
// 将设置好属性的 `Cookie`(有效期为 `0`)重新添加到 `HttpServletResponse` 的响应头中,发送给客户端,触发客户端执行删除该 `Cookie` 的操作。
response.addCookie(ck);
return;
}
}
}
}
}
}

@ -4,65 +4,116 @@ import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* `DateTimeUtil` `Date``String`
* `Joda-Time` 便
* `main`
*
* @DESC
*/
public class DateTimeUtil {
//joda-time
//str->Date
//Date->str
// 使用 `Joda-Time` 库来处理时间相关操作,以下定义的是一个常量字符串,表示时间格式的标准模板,用于在没有显式指定格式时作为默认的日期时间字符串格式,方便统一格式化规则。
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static Date strToDate(String dateTimeStr, String formatStr){
/**
* `Date` `Joda-Time` `Date`
*
* @param dateTimeStr "2024-12-17 10:30:00" `formatStr`
* @param formatStr `dateTimeStr` "yyyy-MM-dd HH:mm:ss" `Date`
* @return `Date` `Date` `Joda-Time`
*/
public static Date strToDate(String dateTimeStr, String formatStr) {
// 根据传入的格式字符串创建 `DateTimeFormatter` 对象,它用于定义如何解析日期时间字符串,按照指定的格式模板来解析输入的字符串内容。
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr);
// 使用创建好的 `DateTimeFormatter` 对输入的日期时间字符串进行解析,得到 `DateTime` 对象,`DateTime` 是 `Joda-Time` 库中用于表示日期时间的核心类,提供了丰富的日期时间操作方法。
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
// 将 `DateTime` 对象转换为 `Java` 标准库中的 `Date` 对象并返回,以便在基于 `Java` 标准库的其他代码中使用该日期时间数据。
return dateTime.toDate();
}
public static String dateToStr(Date date,String formatStr){
if(date == null){
/**
* `Date` `Date` `null`
* `null` `Joda-Time`
*
* @param date `Date` `null` `null`
* @param formatStr "yyyy-MM-dd HH:mm:ss" `Date`
* @return `Date` `null` `StringUtils.EMPTY` `null`
*/
public static String dateToStr(Date date, String formatStr) {
if (date == null) {
return StringUtils.EMPTY;
}
// 根据传入的 `Date` 对象创建 `DateTime` 对象,以便使用 `Joda-Time` 库的功能进行后续的格式化操作,`DateTime` 类提供了方便的方法将日期时间转换为不同格式的字符串。
DateTime dateTime = new DateTime(date);
return dateTime.toString(formatStr);
}
//固定好格式
public static Date strToDate(String dateTimeStr){
/**
* `STANDARD_FORMAT` "yyyy-MM-dd HH:mm:ss" `Date`
* 使
*
* @param dateTimeStr "yyyy-MM-dd HH:mm:ss"
* @return `Date` `Date` `Joda-Time`
*/
public static Date strToDate(String dateTimeStr) {
// 根据默认的标准格式创建 `DateTimeFormatter` 对象,用于解析输入的日期时间字符串。
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT);
// 使用创建好的 `DateTimeFormatter` 对输入的日期时间字符串进行解析,得到 `DateTime` 对象。
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
// 将 `DateTime` 对象转换为 `Java` 标准库中的 `Date` 对象并返回。
return dateTime.toDate();
}
public static String dateToStr(Date date){
if(date == null){
/**
* `Date` `STANDARD_FORMAT` "yyyy-MM-dd HH:mm:ss"
* `Date` 使 `Date` `null`
*
* @param date `Date` `null` `null`
* @return `Date` `null` `StringUtils.EMPTY` `null` "yyyy-MM-dd HH:mm:ss"
*/
public static String dateToStr(Date date) {
if (date == null) {
return StringUtils.EMPTY;
}
// 根据传入的 `Date` 对象创建 `DateTime` 对象,以便使用 `Joda-Time` 库的功能将其转换为默认格式的字符串。
DateTime dateTime = new DateTime(date);
return dateTime.toString(STANDARD_FORMAT);
}
//Date -> 时间戳
/**
* `Date` 19701100:00:00 UTC `Date` `null`
* `null` `null` `Java` `SimpleDateFormat` "yyyy-MM-dd HH:mm:ss" `Date`
* `Date` `SimpleDateFormat` `parse` `Date`
* `Date` `ParseException`
*
* @param date `Date` `null` `null` `null`
* @return `Date` `null` `null` `null`19701100:00:00 UTC
* @throws ParseException 使 `SimpleDateFormat` `Date`
*/
public static Long dateToChuo(Date date) throws ParseException {
if(date == null){
if (date == null) {
return null;
}
SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.parse(String.valueOf(date)).getTime();
}
/**
* `main` `Java` `dateToChuo` `SimpleDateFormat` `Date`
* `dateToChuo` 使 `ParseException`
* 使 `JUnit``TestNG`
*
* @param args 使
* @throws ParseException `main` `dateToChuo` `ParseException`
*/
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
String time="1970-01-06 11:45:55";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = "1970-01-06 11:45:55";
Date date = format.parse(time);
System.out.print("Format To times:"+date.getTime());
System.out.print("Format To times:" + date.getTime());
}
}
}

@ -8,119 +8,153 @@ import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.type.TypeReference;
import java.io.IOException;
import java.text.SimpleDateFormat;
/**
* jackson
* `JsonUtil` `Jackson`
* `ObjectMapper`
* 便 `JSON` `Java`
* `null`
*
* @Slf4j `log` 便 `IOException` 便
*/
@Slf4j
public class JsonUtil {
// 创建一个静态的 `ObjectMapper` 对象,它是 `Jackson` 库中用于进行序列化和反序列化操作的核心类,后续所有的配置和转换操作都基于这个对象来进行。
private static ObjectMapper objectMapper = new ObjectMapper();
static {
//所有字段都列入进行转换
// 以下是对 `ObjectMapper` 对象进行一系列的配置操作,通过静态代码块在类加载时执行这些配置,确保整个应用程序中使用的 `ObjectMapper` 具有统一的序列化和反序列化行为。
// 设置序列化时包含所有字段,即无论字段是否为 `null`,都将其列入进行转换,通过指定 `JsonSerialize.Inclusion.ALWAYS`,使得在将 `Java` 对象转换为 `JSON` 字符串时,所有的对象属性都会参与序列化过程。
objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.ALWAYS);
//取消默认转换timestamp形式
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS,false);
//忽略空bean转json的错误
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS,false);
//统一时间的格式
// 取消默认将日期转换为时间戳形式的行为,配置 `SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS` 为 `false`
// 这样在序列化包含日期类型的对象时,会按照后续配置的日期格式(如通过 `setDateFormat` 方法指定的格式)进行序列化,而不是转换为时间戳数值。
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
// 忽略空 `bean`(即没有属性值的 `Java` 对象)转 `JSON` 的错误,配置 `SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS` 为 `false`
// 当尝试序列化一个没有属性值的对象时,不会抛出异常,而是正常返回相应的 `JSON` 表示(可能为空对象的 `JSON` 格式),提高了序列化操作的容错性。
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
// 统一时间的格式,通过创建 `SimpleDateFormat` 对象并传入 `DateTimeUtil.STANDARD_FORMAT`(这应该是项目中定义的统一的日期时间格式字符串,例如 "yyyy-MM-dd HH:mm:ss"
// 然后使用 `setDateFormat` 方法设置到 `ObjectMapper` 中,使得在序列化和反序列化日期类型数据时,都按照这个统一格式进行处理,保证日期格式的一致性。
objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT));
//忽略json存在属性但是java对象不存在属性的错误
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,false);
// 忽略 `JSON` 中存在属性,但对应的 `Java` 对象不存在该属性的错误,配置 `DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES` 为 `false`
// 在将 `JSON` 字符串反序列化为 `Java` 对象时,如果 `JSON` 字符串中包含了 `Java` 对象中不存在的属性,不会抛出异常,只会忽略这些未知属性进行反序列化操作,增强了反序列化操作的兼容性。
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
/**
*
* @param obj
* @param <T>
* @return
* `Java` `JSON`
* `null` `null` `null`使 `ObjectMapper`
* `String` `writeValueAsString` `JSON`
* `IOException` `null`
*
* @param obj `Java` `T` `Java` `POJO` `Jackson`
* @param <T> 使
* @return `JSON` `null` `IOException` `null` `JSON`
*/
public static <T> String obj2String(T obj){
if(obj == null){
public static <T> String obj2String(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
return obj instanceof String? (String) obj : objectMapper.writeValueAsString(obj);
} catch (IOException e) {
log.warn("parse object to string error",e);
log.warn("parse object to string error", e);
return null;
}
}
/**
* 便
* @param obj
* @param <T>
* @return
* `obj2String` `Java` `JSON` 便
* `null` `null` `null` `String` `writerWithDefaultPrettyPrinter`
* 使 `writeValueAsString` `JSON` `IOException` `null`
*
* @param obj `Java` `T` `obj2String` `Jackson`
* @param <T> `obj2String`
* @return `JSON` `null` `IOException` `null` `JSON` 便
*/
public static <T> String obj2StringPretty(T obj){
if(obj == null){
public static <T> String obj2StringPretty(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
return obj instanceof String? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (IOException e) {
log.warn("parse object to string error",e);
log.warn("parse object to string error", e);
return null;
}
}
/**
*
* @param str
* @param clazz
* @param <T>
* @return
* `JSON` `Java`
* `JSON` `StringUtils.isEmpty` `Java` `null` `null`
* `String`
* 使 `ObjectMapper` `readValue` `JSON` `Java` `IOException` `null`
*
* @param str `JSON` `Jackson` `Java`
* @param clazz `Java` `Class<T>` `JSON` `User.class` `User` `Jackson`
* @param <T> `clazz`
* @return `Java` `JSON` `null` `IOException` `null` `Java`
*/
public static <T> T String2Obj(String str,Class<T> clazz){
if(StringUtils.isEmpty(str) || clazz == null){
public static <T> T String2Obj(String str, Class<T> clazz) {
if (StringUtils.isEmpty(str) || clazz == null) {
return null;
}
try {
return clazz.equals(String.class)?(T)str:objectMapper.readValue(str,clazz);
return clazz.equals(String.class)? (T) str : objectMapper.readValue(str, clazz);
} catch (IOException e) {
log.warn("parse string to obj error",e);
log.warn("parse string to obj error", e);
return null;
}
}
/**
*
* @param str
* @param typeReference
* @param <T>
* @return
* `TypeReference` `JSON` `Java`
* `JSON` `TypeReference` `null` `null` `TypeReference` `String`
* 使 `ObjectMapper` `readValue` `TypeReference` `JSON` `Java`
* `IOException` `null`
*
* @param str `JSON` `Jackson` `TypeReference`
* @param typeReference `TypeReference` `List<User>` `Jackson`
* @param <T> `TypeReference`
* @return `Java` `JSON` `TypeReference` `null` `IOException` `null` `Java`
*/
public static <T> T Str2Obj(String str, TypeReference typeReference){
if(StringUtils.isEmpty(str) || typeReference == null){
public static <T> T Str2Obj(String str, TypeReference typeReference) {
if (StringUtils.isEmpty(str) || typeReference == null) {
return null;
}
try {
return (T) (typeReference.getType().equals(String.class)?str:objectMapper.readValue(str,typeReference));
return (T) (typeReference.getType().equals(String.class)? str : objectMapper.readValue(str, typeReference));
} catch (IOException e) {
log.warn("parse string to obj error",e);
log.warn("parse string to obj error", e);
return null;
}
}
/**
*
* @param str
* @param collectionClass
* @param elementClasses
* @param <T>
* @return
* `JavaType` `JSON` `Java`
* 使 `objectMapper.getTypeFactory().constructParametricType` `JavaType`
* 使 `ObjectMapper` `readValue` `JavaType` `JSON` `Java` `IOException` `null`
* `JSON` `List<User>` 使
*
* @param str `JSON` `Jackson` `JavaType`
* @param collectionClass `List.class``Set.class` `Jackson`
* @param elementClasses `User.class` `Jackson`
* @param <T> `JavaType`
* @return `Java` `IOException` `null` `Java` `JSON` `List<User>`
*/
public static <T> T Str2Obj(String str,Class<?> collectionClass,Class<?>... elementClasses){
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses);
public static <T> T Str2Obj(String str, Class<?> collectionClass, Class<?>... elementClasses) {
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
try {
return objectMapper.readValue(str,javaType);
return objectMapper.readValue(str, javaType);
} catch (IOException e) {
log.warn("parse string to obj error",e);
log.warn("parse string to obj error", e);
return null;
}
}
}
}

@ -3,9 +3,19 @@ package com.njupt.swg.common.utils;
import java.security.MessageDigest;
/**
* MD5
* `MD5Util` `MD5` `MD5`
* `MD5` 便 `UTF-8` `MD5`
* `main` `MD5`
*/
public class MD5Util {
/**
*
* `byteToHexString`
* `MD5` 使
*
* @param b `MD5` 便使
* @return
*/
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
@ -14,6 +24,15 @@ public class MD5Util {
return resultSb.toString();
}
/**
*
* `0` `256` `Java` `-128` `127` `0` `255`
* `16` `16` `hexDigits`
* `byteArrayToHexString`
*
* @param b 便
* @return `2` `0f``1a`
*/
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
@ -24,11 +43,15 @@ public class MD5Util {
}
/**
* MD5
* `MD5`
* `resultString` `MessageDigest.getInstance` `MD5` `MessageDigest`
* 使 `resultString` `MD5` `byteArrayToHexString` `resultString`
* `UTF-8` `resultString` `MD5` `resultString` `resultString` `MD5`
* `catch`
*
* @param origin
* @param charsetname
* @return
* @param origin `MD5`
* @param charsetname "utf-8""GBK" `null` 使
* @return `MD5` `null`
*/
private static String MD5Encode(String origin, String charsetname) {
String resultString = null;
@ -44,16 +67,33 @@ public class MD5Util {
return resultString.toUpperCase();
}
/**
* `UTF-8` `MD5` 便 `MD5Encode` "utf-8"
* `UTF-8` `MD5`
* `MD5Encode`
*
* @param origin `MD5` `UTF-8`
* @return `UTF-8` `MD5` `MD5Encode` `null`
*/
public static String MD5EncodeUtf8(String origin) {
//这里可以加盐
return MD5Encode(origin, "utf-8");
}
/**
* `main` `Java` `MD5EncodeUtf8` "123456" `MD5EncodeUtf8` `MD5`
* 使 `JUnit``TestNG`
*
* @param args 使
*/
public static void main(String[] args) {
System.out.println(MD5EncodeUtf8("123456"));
}
/**
* `hexDigits` `0` `9` `a` `f`
* `byteToHexString`
*/
private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
}
}

@ -9,26 +9,46 @@ import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* `ZkClient` `Zookeeper` `Curator`
* `Parameters` `CuratorFrameworkFactory` `CuratorFramework`
* 使 `@Bean` `Spring` 便使
* `Spring` 便 `Zookeeper` `Zookeeper`
*
* @Author swg.
* @Date 2019/1/1 17:57
* @CONTACT 317758022@qq.com
* @DESC zkcurator
*/
@Component
// 该注解表明这个类是 `Spring` 框架中的一个组件,`Spring` 会自动扫描并将其纳入到容器管理中,使得这个类可以参与依赖注入等 `Spring` 容器相关的操作。
public class ZkClient {
@Autowired
// 使用 `Spring` 的依赖注入功能,自动注入 `Parameters` 类的实例,通过 `Parameters` 可以获取到项目中关于 `Zookeeper` 的配置参数,比如 `Zookeeper` 服务器的主机地址等信息,用于后续构建 `Curator` 客户端。
private Parameters parameters;
/**
* `@Bean` `Spring` `Bean` `CuratorFramework` `Zookeeper`
* `Spring` `CuratorFramework` `Spring` 使 `Zookeeper`
*
* @return `CuratorFramework` `Zookeeper` 使
*/
@Bean
public CuratorFramework getZkClient(){
CuratorFrameworkFactory.Builder builder= CuratorFrameworkFactory.builder()
public CuratorFramework getZkClient() {
// 使用 `CuratorFrameworkFactory` 的 `builder` 方法创建一个构建器对象,通过这个构建器可以方便地设置各种创建 `CuratorFramework` 客户端所需的配置参数,逐步构建出符合需求的客户端实例。
CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()
// 设置要连接的 `Zookeeper` 服务器的连接字符串,通过从注入的 `Parameters` 实例中获取 `zkHost` 属性来获取 `Zookeeper` 服务器的主机地址信息,
// 连接字符串的格式通常类似于 "ip1:port1,ip2:port2,...",用于指定要连接的 `Zookeeper` 集群中的服务器节点地址列表。
.connectString(parameters.getZkHost())
// 设置连接超时时间,单位为毫秒,这里设置为 `3000` 毫秒,表示客户端尝试连接 `Zookeeper` 服务器时,如果在 `3000` 毫秒内还未成功建立连接,就认为连接超时,触发相应的超时处理逻辑。
.connectionTimeoutMs(3000)
// 设置重试策略,这里使用 `RetryNTimes` 策略,表示当连接 `Zookeeper` 出现问题时,会进行重试操作,最多重试 `5` 次,每次重试间隔 `10` 毫秒,
// 通过这种重试机制来增加客户端连接成功的概率,提高客户端在面对网络抖动等临时问题时的健壮性。
.retryPolicy(new RetryNTimes(5, 10));
// 通过构建器对象的 `build` 方法创建出最终的 `CuratorFramework` 客户端实例,此时实例已经包含了前面设置的连接字符串、连接超时时间以及重试策略等配置信息。
CuratorFramework framework = builder.build();
// 启动 `CuratorFramework` 客户端,使其开始尝试连接 `Zookeeper` 服务器,在启动后客户端会根据配置的参数进行连接操作以及后续的相关初始化工作,进入可使用状态,
// 可以通过这个客户端实例执行诸如创建节点、获取节点数据、设置分布式锁等各种与 `Zookeeper` 交互的操作。
framework.start();
return framework;
}
}
}

@ -21,182 +21,222 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* @Author swg.
* @Date 2019/1/1 13:12
* @CONTACT 317758022@qq.com
* @DESC POSTMAN
*/
//TODO 先全部开放GET请求
// 为该控制器类下的所有请求路径设置统一的前缀为 "user",方便对用户模块相关的接口进行统一管理和组织。
@RequestMapping("user")
// 表明这个类是一个RESTful风格的控制器其方法返回的数据会直接作为响应体返回通常以JSON等格式而不是进行视图解析等操作。
@RestController
// 使用Slf4j注解生成一个名为log的日志记录对象方便在类中记录与用户操作相关的日志信息。
@Slf4j
// 表示标识这个类是swagger的资源
// 表示标识这个类是swagger的资源用于生成接口文档通过value属性指定接口文档中的名称tags属性用于对接口进行分类标记。
@Api(value = "UserController", tags = {"用户服务接口"})
public class UserController {
// 通过Spring的依赖注入功能自动注入IUserService接口的实现类实例用于调用用户相关的业务逻辑方法。
@Autowired
private IUserService userService;
// 注入CommonCacheUtil实例用于操作缓存如在用户登录等操作中与缓存进行交互。
@Autowired
private CommonCacheUtil commonCacheUtil;
/**
* cookieredis
* key
*
* 1.
* 2. userServiceloginServerResponse<UserResVO>
* 3. sessionIdcookie便
* 4. redis30便
* 5.
*/
@ApiOperation(value="用户登陆", notes="输入用户名,密码,不能为空")
@ApiOperation(value = "用户登陆", notes = "输入用户名,密码,不能为空")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String"),
@ApiImplicitParam(name = "password", value = "用户密码", required = true, dataType = "String")
})
@RequestMapping("/login.do")
public ServerResponse<UserResVO> login(HttpSession session, HttpServletResponse response, String username, String password){
log.info("【用户{}开始登陆】",username);
ServerResponse<UserResVO> userVOServerResponse = userService.login(username,password);
if(userVOServerResponse.isSuccess()){
//登陆成功那么需要在redis中存储并且将代表用户的sessionId写到前端浏览器的cookie中
log.info("【用户{}cookie开始写入】",username);
CookieUtil.writeLoginToken(response,session.getId());
//写到redis中将用户信息序列化设置过期时间为30分钟
log.info("【用户{}redis开始写入】",username);
public ServerResponse<UserResVO> login(HttpSession session, HttpServletResponse response, String username, String password) {
// 记录用户开始登录的日志,方便后续排查问题,如查看登录失败的原因等。
log.info("【用户{}开始登陆】", username);
ServerResponse<UserResVO> userVOServerResponse = userService.login(username, password);
if (userVOServerResponse.isSuccess()) {
// 记录开始写入cookie的日志用于跟踪cookie写入操作是否正常。
log.info("【用户{} cookie开始写入】", username);
// 将用户的sessionId写入cookie使浏览器保存该cookie用于后续请求识别用户登录状态。
CookieUtil.writeLoginToken(response, session.getId());
// 记录开始写入redis的日志便于监控redis写入操作的情况。
log.info("【用户{} redis开始写入】", username);
// 将用户信息序列化后存入redis缓存设置过期时间为30分钟提高后续获取用户信息的效率。
commonCacheUtil.cacheNxExpire(session.getId(), JsonUtil.obj2String(userVOServerResponse.getData()), Constants.RedisCacheExtime.REDIS_SESSION_EXTIME);
}
log.info("【用户{}登陆成功】",username);
// 记录用户登录成功的日志,完整记录登录操作过程。
log.info("【用户{}登陆成功】", username);
return userVOServerResponse;
}
/**
*
*
* 1.
* 2. userServiceregister使
* 3.
*/
@ApiOperation(value="创建用户", notes="根据User对象创建用户")
@ApiOperation(value = "创建用户", notes = "根据User对象创建用户")
@ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
@RequestMapping("/register.do")
public ServerResponse register(User user){
public ServerResponse register(User user) {
// 记录开始注册的日志,有助于排查注册过程中出现的问题,如用户名或邮箱重复等情况。
log.info("【开始注册】");
//这里模拟高并发的注册场景,防止用户名字注册重复,所以需要加上分布式锁
// 这里模拟高并发的注册场景,防止用户名字注册重复,所以需要加上分布式锁调用userService的register方法进行实际注册操作。
ServerResponse response = userService.register(user);
// 记录用户注册成功的日志,方便监控注册操作的整体情况。
log.info("【用户注册成功】");
return response;
}
/**
*
*
* 1.
* 2. userServicecheckValid
* 3.
*/
@ApiOperation(value="验证用户名和邮箱是否重复", notes="用户名和邮箱都不能用已经存在的")
@ApiOperation(value = "验证用户名和邮箱是否重复", notes = "用户名和邮箱都不能用已经存在的")
@ApiImplicitParams({
@ApiImplicitParam(name = "str", value = "输入参数", required = true, dataType = "String"),
@ApiImplicitParam(name = "type", value = "参数类型", required = true, dataType = "String")
})
@RequestMapping("/check_valid.do")
public ServerResponse checkValid(@RequestParam("str") String str,
@RequestParam("type") String type){
@RequestParam("type") String type) {
// 记录开始验证用户名和邮箱是否重复的日志,方便后续查看验证操作的执行情况。
log.info("【开始验证用户名和邮箱是否重复】");
ServerResponse response = userService.checkValid(str,type);
ServerResponse response = userService.checkValid(str, type);
return response;
}
/**
*
* cookieoursnai.cnhosts127.0.0.1 oursnail.cn
* loginGEThttp://oursnail.cn:8081/user/login.do?username=admin&password=123456
* tokenhttp://oursnail.cn:8081/user/get_user_info.do
*
* 1. token
* 2. token
* 3. UseruserServicegetUserInfoFromDB
* 4.
*/
@ApiOperation(value="获取用户个人信息", notes="登陆状态下获取")
@ApiOperation(value = "获取用户个人信息", notes = "登陆状态下获取")
@RequestMapping("/get_user_info.do")
public ServerResponse getUserInfo(HttpServletRequest request){
public ServerResponse getUserInfo(HttpServletRequest request) {
// 从请求中读取登录token用于判断用户是否登录。
String loginToken = CookieUtil.readLoginToken(request);
if(StringUtils.isEmpty(loginToken)){
if (StringUtils.isEmpty(loginToken)) {
// 记录用户未登录的日志,方便排查问题,如用户未登录却尝试获取信息的情况。
log.info("【用户未登录,无法获取当前用户信息】");
return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");
}
// 从缓存中获取用户信息字符串,若为空则表示用户未登录或缓存信息丢失等情况。
String userStr = commonCacheUtil.getCacheValue(loginToken);
if(userStr == null){
if (userStr == null) {
// 记录用户未登录的日志,可能是缓存问题导致无法获取用户信息。
log.info("【用户未登录,无法获取当前用户信息】");
return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");
}
User currentUser = JsonUtil.Str2Obj(userStr,User.class);
// 将用户信息字符串反序列化为User对象以便后续获取详细信息。
User currentUser = JsonUtil.Str2Obj(userStr, User.class);
UserResVO userResVO = userService.getUserInfoFromDB(currentUser.getId());
return ServerResponse.createBySuccess("登陆用户获取自身信息成功",userResVO);
return ServerResponse.createBySuccess("登陆用户获取自身信息成功", userResVO);
}
/**
*
*
* 1.
* 2. userServicegetQuestionByUsername
* 3.
*/
@ApiOperation(value="根据用户名去拿到对应的问题", notes="忘记密码时首先根据用户名去获取设置的问题")
@ApiOperation(value = "根据用户名去拿到对应的问题", notes = "忘记密码时首先根据用户名去获取设置的问题")
@ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String")
@RequestMapping("/forget_get_question.do")
public ServerResponse forgetGetQuestion(String username){
log.info("【用户{}忘记密码,点击忘记密码输入用户名】",username);
public ServerResponse forgetGetQuestion(String username) {
// 记录用户忘记密码并进行获取问题操作的日志,方便后续排查忘记密码流程中的问题,如用户名不存在等情况。
log.info("【用户{}忘记密码,点击忘记密码输入用户名】", username);
ServerResponse response = userService.getQuestionByUsername(username);
return response;
}
/**
*
*
* 1.
* 2. userServicecheckAnswer
* 3.
*/
@ApiOperation(value="校验答案是否正确", notes="忘记密码时输入正确的用户名之后就可以获取到问题,此时就可以输入答案")
@ApiOperation(value = "校验答案是否正确", notes = "忘记密码时输入正确的用户名之后就可以获取到问题,此时就可以输入答案")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String"),
@ApiImplicitParam(name = "question", value = "设置的问题", required = true, dataType = "String"),
@ApiImplicitParam(name = "answer", value = "提交的答案", required = true, dataType = "String")
})
@RequestMapping("/forget_check_answer.do")
public ServerResponse forgetCheckAnswer(String username,String question,String answer){
log.info("【用户{}忘记密码,提交问题答案】",username);
ServerResponse response = userService.checkAnswer(username,question,answer);
public ServerResponse forgetCheckAnswer(String username, String question, String answer) {
// 记录用户忘记密码并提交答案进行校验的日志,有助于排查忘记密码答案校验过程中的问题,如答案错误等情况。
log.info("【用户{}忘记密码,提交问题答案】", username);
ServerResponse response = userService.checkAnswer(username, question, answer);
return response;
}
/**
*
* token
* 1. token
* 2. userServiceforgetResetPasswdtokentoken
* 3.
*/
@ApiOperation(value="忘记密码的重置密码", notes="输入新的密码要进行token的校验")
@ApiOperation(value = "忘记密码的重置密码", notes = "输入新的密码要进行token的校验")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String"),
@ApiImplicitParam(name = "passwordNew", value = "新密码", required = true, dataType = "String"),
@ApiImplicitParam(name = "forgetToken", value = "前端保存的token", required = true, dataType = "String")
})
@RequestMapping("/forget_reset_password.do")
public ServerResponse forgetResetPasswd(String username,String passwordNew,String forgetToken){
log.info("【用户{}忘记密码,输入新密码】",username);
ServerResponse response = userService.forgetResetPasswd(username,passwordNew,forgetToken);
public ServerResponse forgetResetPasswd(String username, String passwordNew, String forgetToken) {
// 记录用户忘记密码并进行重置密码操作的日志,方便后续排查忘记密码重置过程中的问题,如新密码不符合要求等情况。
log.info("【用户{}忘记密码,输入新密码】", username);
ServerResponse response = userService.forgetResetPasswd(username, passwordNew, forgetToken);
return response;
}
/**
*
*
* 1. cookietoken
* 2. tokenredis
* 3. User
* 4. userServiceresetPasswdID
* 5.
*/
@ApiOperation(value="登陆状态的重置密码", notes="登陆的时候只需要输入老的密码和新密码即可")
@ApiOperation(value = "登陆状态的重置密码", notes = "登陆的时候只需要输入老的密码和新密码即可")
@ApiImplicitParams({
@ApiImplicitParam(name = "passwordOld", value = "老密码", required = true, dataType = "String"),
@ApiImplicitParam(name = "passwordNew", value = "新密码", required = true, dataType = "String")
})
@RequestMapping("/reset_password.do")
public ServerResponse resetPasswd(String passwordOld,String passwordNew,HttpServletRequest request){
//1.读取cookie
public ServerResponse resetPasswd(String passwordOld, String passwordNew, HttpServletRequest request) {
// 1.读取cookie
String loginToken = CookieUtil.readLoginToken(request);
if(StringUtils.isEmpty(loginToken)){
if (StringUtils.isEmpty(loginToken)) {
return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");
}
//2.从redis中获取用户信息
// 2.从redis中获取用户信息
String userStr = commonCacheUtil.getCacheValue(loginToken);
if(userStr == null){
if (userStr == null) {
return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");
}
User currentUser = JsonUtil.Str2Obj(userStr,User.class);
log.info("【用户{}重置密码】",currentUser);
User currentUser = JsonUtil.Str2Obj(userStr, User.class);
// 记录用户登录状态下重置密码的日志,有助于排查登录用户重置密码过程中的问题,如老密码错误等情况。
log.info("【用户{}重置密码】", currentUser);
ServerResponse response = userService.resetPasswd(passwordOld,passwordNew,currentUser.getId());
ServerResponse response = userService.resetPasswd(passwordOld, passwordNew, currentUser.getId());
return response;
}
/**
*
*
* 1. `token`
* 2. `token` `redis`
* 3. `User` `userService` `updateInfomation` `ID`
* 4.
*/
@ApiOperation(value="更新当前登陆用户信息", notes="更新用户信息")
@ApiOperation(value = "更新当前登陆用户信息", notes = "更新用户信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "email", value = "邮箱", required = true, dataType = "String"),
@ApiImplicitParam(name = "phone", value = "电话", required = true, dataType = "String"),
@ -204,40 +244,41 @@ public class UserController {
@ApiImplicitParam(name = "answer", value = "答案", required = true, dataType = "String")
})
@RequestMapping("/update_information.do")
public ServerResponse updateInformation(String email,String phone,String question,String answer,HttpServletRequest request){
//1.读取cookie
public ServerResponse updateInformation(String email, String phone, String question, String answer, HttpServletRequest request) {
// 1.读取cookie
String loginToken = CookieUtil.readLoginToken(request);
if(StringUtils.isEmpty(loginToken)){
if (StringUtils.isEmpty(loginToken)) {
return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");
}
//2.从redis中获取用户信息
// 2.从redis中获取用户信息
String userStr = commonCacheUtil.getCacheValue(loginToken);
if(userStr == null){
if (userStr == null) {
return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");
}
User currentUser = JsonUtil.Str2Obj(userStr,User.class);
User currentUser = JsonUtil.Str2Obj(userStr, User.class);
ServerResponse response = userService.updateInfomation(email,phone,question,answer,currentUser.getId());
ServerResponse response = userService.updateInfomation(email, phone, question, answer, currentUser.getId());
return response;
}
/**
* ,cookieredis
* `cookie` `redis`
* 1. `token` `cookie`
* 2. 使 `CookieUtil` `delLoginToken` `cookie` 使
* 3. `redis` 使 `commonCacheUtil` `delKey` `redis`
* 4.
*/
@ApiOperation(value="登出", notes="退出登陆删除cookie和redis缓存")
@ApiOperation(value = "登出", notes = "退出登陆删除cookie和redis缓存")
@RequestMapping("/logout.do")
public ServerResponse logout(HttpServletRequest request,HttpServletResponse response){
public ServerResponse logout(HttpServletRequest request, HttpServletResponse response) {
log.info("【用户删除cookie】");
//1.删除cookie
// 1.删除cookie
String loginToken = CookieUtil.readLoginToken(request);
CookieUtil.delLoginToken(request,response);
CookieUtil.delLoginToken(request, response);
log.info("【用户删除redis缓存】");
//2.删除redis中缓存记录
// 2.删除redis中缓存记录
commonCacheUtil.delKey(loginToken);
return ServerResponse.createBySuccess();
}
}
}

@ -5,34 +5,149 @@ import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* `UserMapper` 访DAO
*
* `MyBatis` 使便
* 访
*
* @Author swg.
* @Date 2018/12/31 21:03
* @CONTACT 317758022@qq.com
* @DESC Mapper
*/
@Mapper
// `@Mapper` 注解是 `MyBatis` 框架用于标识该接口为一个映射器接口的注解,
// 告诉 `MyBatis` 框架在启动时扫描并生成该接口的代理实现类,该代理类负责将接口中的方法调用转换为对应的数据库操作(通过执行预定义的 `SQL` 语句,这些语句通常在与接口同名的 `XML` 文件中定义,虽然此处未展示相关 `XML` 文件内容),从而实现与数据库的交互。
public interface UserMapper {
/**
* `id`
* `id` `1` `0`
*
* @param id `Integer`
* @return
*/
int deleteByPrimaryKey(Integer id);
/**
*
* `User` `User`
* `User` `MyBatis`
* `1` `0`
*
* @param record `User`
* @return
*/
int insert(User record);
/**
* `insert` `User` `null`
* `null`
* `1` `0`
*
* @param record `null` `null` `User`
* @return
*/
int insertSelective(User record);
/**
* `id`
* `id` `User` `User`
* `null` `MyBatis` `User`
*
* @param id `Integer`
* @return `User` `null` `User`
*/
User selectByPrimaryKey(Integer id);
/**
* `id` `User` `null`
* `1`
* `0`
*
* @param record `null` `id` `User`
* @return
*/
int updateByPrimaryKeySelective(User record);
/**
* `id` `User`
* `User`
* `1` `0`
*
* @param record `id` `User`
* @return
*/
int updateByPrimaryKey(User record);
/**
* `id`
* `id` `id`
* `null` `MyBatis`
* `id` `id`
*
* @param username `String`
* @return `id` `null`
*/
Integer selectByUsername(String username);
User selectByUsernameAndPasswd(@Param("username") String username,@Param("password") String md5Passwd);
/**
* `MD5`
* `User` `User`
* `null` `MyBatis` `User`
*
*
* @param username `String`
* @param md5Passwd `MD5` `String`
* @return `User` `null` `User`
*/
User selectByUsernameAndPasswd(@Param("username") String username, @Param("password") String md5Passwd);
/**
* `id`
* `id` `id`
* `null` `MyBatis`
* `id`
*
* @param str `String`
* @return `id` `null`
*/
Integer selectByEmail(String str);
/**
*
* `User` `User`
* `null` `MyBatis` `User`
* 使
*
* @param username `String`
* @return `User` `null` `User`
*/
User getUserByUsername(String username);
/**
*
* `User` `User`
* `null` `MyBatis` `User`
* 便
*
* @param username `String`
* @param question `String`
* @param answer `String`
* @return `User` `null` `User`
*/
User getUserByUsernameQuestionAnswer(String username, String question, String answer);
/**
* `id`使
* 使 `1`
* 使 `0`
*
* @param email 使 `String`
* @param userId `id` `null` `id`
* `id` `id` `Integer` `@Param` `userId` `required = false`
* @return `1` `0`
*/
Integer checkEmailValid(@Param("email") String email,@Param("userId") Integer userId);
}
}

@ -9,35 +9,54 @@ import java.io.Serializable;
import java.util.Date;
/**
* `User`
* 访
* 使 `Lombok` `Getter` `Setter` `toString`
*
* @Author swg.
* @Date 2018/12/31 21:01
* @CONTACT 317758022@qq.com
* @DESC
*/
// `@Data` 注解是 `Lombok` 提供的一个便捷注解,它会自动为类中的所有非静态、非 `final` 的字段生成 `Getter`、`Setter` 方法,
// 以及 `equals`、`hashCode` 和 `toString` 方法(不过这里因为又单独使用了 `@ToString` 注解,所以 `toString` 方法会按照 `@ToString` 注解的配置来生成),方便对类中属性的访问和操作,减少了大量重复的代码编写工作。
@Data
// `@NoArgsConstructor` 注解由 `Lombok` 提供,它会为该类生成一个无参的构造函数,这在一些情况下(比如使用某些框架进行对象实例化时要求类必须有默认的无参构造函数)是很有用的,使得创建 `User` 类的实例更加方便灵活。
@NoArgsConstructor
// `@AllArgsConstructor` 注解同样来自 `Lombok`,它会为类生成一个包含所有字段的全参构造函数,这样在初始化 `User` 类对象时,可以通过传入所有属性的值一次性完成对象的创建,便于在需要完整初始化对象的场景中使用。
@AllArgsConstructor
// `@ToString` 注解用于生成一个方便查看对象信息的 `toString` 方法,默认情况下它会输出类名以及所有字段的名称和对应的值,便于在调试代码、打印对象信息等场景中直观地了解对象的状态。
@ToString
// 实现 `Serializable` 接口表示该类的对象可以被序列化,意味着可以将 `User` 类的对象转换为字节流进行存储(比如保存到文件中或者在网络传输过程中进行传递等),
// 并且在需要的时候能够从字节流中反序列化还原出原来的对象,这在很多涉及数据持久化和分布式系统的场景中是非常必要的操作。
public class User implements Serializable {
// 用户的唯一标识符,通常对应数据库表中的主键字段,用于在系统中唯一确定一个用户,其数据类型为 `Integer`,可以存储整数形式的用户 `ID` 值。
private Integer id;
// 用户名,用于用户登录以及在系统中标识用户的身份,是一个字符串类型的属性,存储用户在注册时设定的用户名信息。
private String username;
// 用户密码,存储用户登录时需要输入验证的密码信息,以加密后的字符串形式存储(在实际应用中应该采用安全的加密算法进行加密处理,比如 `MD5` 等常见加密方式),保证用户密码的安全性,数据类型为 `String`。
private String password;
// 用户的电子邮箱地址,可用于接收系统发送的通知邮件、找回密码等功能相关的邮件信息,是一个字符串类型的属性,存储符合邮箱格式规范的邮箱地址。
private String email;
// 用户的电话号码,可用于联系用户或者作为一些验证操作(如手机验证码登录等功能,如果有相关拓展功能的话)的依据,数据类型为 `String`,存储用户的手机号码等电话号码信息。
private String phone;
// 安全问题,通常用于用户忘记密码时,通过回答预先设置的安全问题来验证身份,进而进行密码重置等操作,是一个字符串类型的属性,存储用户自己设置的安全问题内容。
private String question;
// 安全问题的答案,与 `question` 属性相对应,用于在忘记密码验证身份环节,用户输入答案与数据库中存储的答案进行比对验证,数据类型为 `String`,存储用户针对所设置安全问题的答案内容。
private String answer;
//角色0-管理员,1-普通用户
// 用户角色字段,用于区分不同权限的用户,这里定义了两种角色,`0` 表示管理员,拥有系统的高级管理权限(如用户管理、系统配置等权限),`1` 表示普通用户,只具有普通的使用系统功能的权限,数据类型为 `Integer`。
private Integer role;
// 用户创建时间,记录用户账号在系统中被创建的具体时间点,数据类型为 `Date`,可以准确记录创建时间的日期和时间信息,方便后续进行数据分析、用户行为统计等操作。
private Date createTime;
// 用户信息更新时间,每当用户的相关信息(如密码、邮箱、电话号码等)发生修改时,会更新这个字段的值,记录最后一次更新的时间点,数据类型为 `Date`,同样便于跟踪用户信息的变更情况以及进行相关的数据统计和分析。
private Date updateTime;
}

@ -5,35 +5,123 @@ import com.njupt.swg.entity.User;
import com.njupt.swg.vo.UserResVO;
/**
* `IUserService`
*
* 访
* 便
*
* @Author swg.
* @Date 2018/12/31 21:07
* @CONTACT 317758022@qq.com
* @DESC
*/
public interface IUserService {
/** 用户登陆 **/
ServerResponse<UserResVO> login(String username,String password);
/** 用户注册 **/
/**
*
* 访 `UserMapper`
*
* `ServerResponse<UserResVO>` `ServerResponse`
* `UserResVO` `UserResVO`
*
* @param username `String`
* @param password `String` `MD5`
* @return `ServerResponse<UserResVO>`
*/
ServerResponse<UserResVO> login(String username, String password);
/**
*
* `User` `User`
* 访使
* `ServerResponse`
*
* @param user `User` `User`
* @return `ServerResponse`
*/
ServerResponse register(User user);
/** 判断用户名和邮箱是否重复 **/
/**
*
* `type` `str` 访
* `ServerResponse`
*
* @param str `type` `String`
* @param type `str` `str` "username" "email" `String`
* @return `ServerResponse`
*/
ServerResponse checkValid(String str, String type);
/** 根据用户名去获取设置的忘记密码的问题 **/
/**
*
* 访
* `ServerResponse`
*
* @param username `String`
* @return `ServerResponse`
*/
ServerResponse getQuestionByUsername(String username);
/** 校验问题对应的答案是否正确 **/
/**
*
* 访
* `ServerResponse`
*
* @param username `String`
* @param question `String`
* @param answer `String`
* @return `ServerResponse`
*/
ServerResponse checkAnswer(String username, String question, String answer);
/** 重置密码 **/
/**
*
* `forgetToken`
* 访 `forgetToken`
* `ServerResponse`
*
* @param username `String`
* @param passwordNew `String`
* @param forgetToken `String`
* @return `ServerResponse`
*/
ServerResponse forgetResetPasswd(String username, String passwordNew, String forgetToken);
/** 登陆状态下重置密码 **/
/**
*
* `ID` 访 `ID`
*
* `ServerResponse`
*
* @param passwordOld 使 `String`
* @param passwordNew `String`
* @param userId `int`
* @return `ServerResponse`
*/
ServerResponse resetPasswd(String passwordOld, String passwordNew, int userId);
/** 登陆状态下更新个人信息(更新策略为:如果用户某一项不填,表示保持原样不变) **/
/**
*
* `ID` 访
*
* `ServerResponse`
*
* @param email `String`
* @param phone `String`
* @param question `String`
* @param answer `String`
* @param userId `Integer`
* @return `ServerResponse`
*/
ServerResponse updateInfomation(String email, String phone, String question, String answer, Integer userId);
/**
* `ID` `ID`
* 访 `ID` `UserResVO` `UserResVO` 使
* `null` `UserResVO`
*
* @param userId `Integer`
* @return `UserResVO` `null` `UserResVO`
*/
UserResVO getUserInfoFromDB(Integer userId);
}
}

@ -8,6 +8,7 @@ import com.njupt.swg.common.utils.MD5Util;
import com.njupt.swg.dao.UserMapper;
import com.njupt.swg.entity.User;
import com.njupt.swg.vo.UserResVO;
import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.framework.CuratorFramework;

@ -9,34 +9,52 @@ import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
/**
* `SwaggerConfig` `Swagger` `Spring` `@Configuration`
* `Swagger` `Swagger` `Docket`使便便
*
* @Author swg.
* @Date 2019/1/8 11:39
* @CONTACT 317758022@qq.com
* @DESC
*/
@Configuration
// `@Configuration` 注解表明这个类是一个 `Spring` 的配置类,`Spring` 容器在启动时会扫描并解析该类,将其中定义的 `Bean`(通过 `@Bean` 注解方法定义的对象)注册到容器中,
// 以便在项目的其他地方可以方便地注入和使用这些对象,在这里主要用于配置 `Swagger` 相关的组件和信息。
public class SwaggerConfig {
// 接口版本号
// 接口版本号,用于标识当前接口文档所对应的项目接口版本,方便使用者了解接口的迭代情况以及不同版本之间的差异,这里定义为 "3.0",数据类型为 `String`。
private final String version = "3.0";
// 接口大标题
// 接口大标题,通常展示在接口文档的顶部,是对整个接口文档内容的一个总体概括性标题,此处设置为 "快乐蜗牛商城V3.0文档",用于明确文档所属的项目及版本,数据类型为 `String`。
private final String title = "快乐蜗牛商城V3.0文档";
// 具体的描述
// 具体的描述,用于详细说明接口文档涵盖的服务内容等相关信息,在这里明确指出是 "用户服务",让查看文档的人能快速知晓该文档主要聚焦的业务范围,数据类型为 `String`。
private final String description = "用户服务";
// 服务说明url
// 服务说明url,一般指向一个网页地址,该地址可以提供关于整个服务更详细的说明、使用条款、服务协议等相关信息,这里设置为 "http://www.kingeid.com",数据类型为 `String`。
private final String termsOfServiceUrl = "http://www.kingeid.com";
// 接口作者联系方式
// 接口作者联系方式,通过 `Contact` 类的实例来封装作者的相关联系信息,包括姓名、个人主页(如 `GitHub` 主页等)以及邮箱地址,方便使用者在有问题或者需要进一步沟通时能联系到接口的作者,这里进行了相应的初始化设置。
private final Contact contact = new Contact("fourColor", "https://github.com/sunweiguo", "sunweiguode@gmail.com");
/**
* 使 `@Bean` `Docket` `Docket` `Swagger`
* `Docket`
* `Docket` `Spring`
*
* @return `Docket` `Swagger`
*/
@Bean
public Docket buildDocket() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(buildApiInf())
.select().build();
}
/**
* `ApiInfo` `ApiInfo`
* `Swagger` 使 `ApiInfoBuilder` `ApiInfo`
*
* @return `ApiInfo` `Docket`
*/
private ApiInfo buildApiInf() {
return new ApiInfoBuilder().title(title).termsOfServiceUrl(termsOfServiceUrl).description(description)
.version(version).contact(contact).build();
}
}

@ -1,24 +1,38 @@
package com.njupt.swg.vo;
import lombok.Data;
import java.util.Date;
/**
* `UserResVO` Value Object VO使
* 使 `com.njupt.swg.entity.User`
* 使 `Lombok` `@Data` `Getter``Setter`
*
* @Author swg.
* @Date 2019/1/1 11:50
* @CONTACT 317758022@qq.com
* @DESC
*/
@Data
// `@Data` 注解是 `Lombok` 提供的一个便捷注解,它会自动为类中的所有非静态、非 `final` 的字段生成 `Getter`、`Setter` 方法,
// 以及 `equals`、`hashCode` 和 `toString` 方法,方便对类中属性的访问和操作,减少了大量重复的代码编写工作,使得在使用这个类的对象时,可以很方便地获取和设置各个属性的值。
public class UserResVO {
// 用户的唯一标识符,用于在系统中唯一确定一个用户,该属性会传递到前端,方便前端在进行一些与用户相关的操作(如展示用户特定信息、发起针对特定用户的请求等)时使用,数据类型为 `int`。
private int id;
// 用户名,用于在前端页面展示给用户或者进行一些基于用户名的操作(如显示欢迎信息、根据用户名查找相关记录等),是一个字符串类型的属性,存储用户在注册时设定的用户名信息,数据类型为 `String`。
private String username;
// 用户的电子邮箱地址,前端可能会利用该邮箱地址展示给用户查看,或者用于一些与邮件相关的功能提示(如告知用户可以通过该邮箱接收系统通知等),数据类型为 `String`。
private String email;
// 用户角色字段,用于区分不同权限的用户,前端可以根据该角色信息来展示不同的用户界面或者控制用户对某些功能的访问权限,这里定义了不同的整数值表示不同角色(具体含义可能在业务中有相应定义),数据类型为 `int`。
private int role;
// 用户的电话号码,前端可以展示该电话号码信息给用户,或者用于一些与电话相关的功能交互(如点击拨打按钮等,若有相关功能拓展的话),数据类型为 `String`。
private String phone;
// 安全问题,通常用于在前端页面提示用户之前设置过的安全问题相关信息(比如在忘记密码等流程中展示给用户回顾),是一个字符串类型的属性,存储用户自己设置的安全问题内容,数据类型为 `String`。
private String question;
// 安全问题的答案,虽然从安全角度来说一般不会直接展示给用户,但在某些特定的业务逻辑中(比如验证用户是否正确输入过答案等情况)可能会涉及到对该属性的使用,数据类型为 `String`。
private String answer;
private Date createTime;//返回前端的是时间戳
// 用户创建时间,不过这里需要注意的是,在返回前端时通常是以时间戳的形式(便于前端进行时间的格式化展示等操作,根据前端具体的时间处理机制来定),用于前端展示用户账号的创建时间信息,数据类型为 `Date`。
private Date createTime;
// 用户信息更新时间,同样可以在前端展示给用户,让用户了解自己的信息最后一次更新是什么时候,数据类型为 `Date`。
private Date updateTime;
}
}
Loading…
Cancel
Save