版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/woshimuyi1025/article/details/81357262
1. 对于前后端传递token验证的方式,每次都需要页面加载才能在后端存放token,这样会导致用户在第一次提交表单失败后就无法提交成功,需要刷新页面。
2. 利用session去给前后端的token存放获取,这对于APP来说不协调,适合用redis。
使用哪种方法要根据自己项目去考虑,比如单纯做网页的用session也不错。 我这里后台是提供给微信端和APP端,所以使用了第四种方法:使用Redis和AOP自定义切入实现
参考文章:
https://blog.csdn.net/wangdengyang/article/details/81095734
https://www.cnblogs.com/huanghuizhou/p/9153837.html
实现原理:
- 自定义防止重复提交标记(@AvoidRepeatableCommit)。
- 对需要防止重复提交的Congtroller里的mapping方法加上该注解。
- 新增Aspect切入点,为@AvoidRepeatableCommit加入切入点。
- 每次提交表单时,Aspect都会保存当前key到reids(须设置过期时间)。
- 重复提交时Aspect会判断当前redis是否有该key,若有则拦截。
自定义标签
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 避免重复提交
*
* @author hhz
* @version
* @since
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Resubmit {
/**
* 指定时间内不可重复提交,单位秒
*
* @return
*/
long timeout() default 3;
}
自定义切入点Aspect
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import org.apache.poi.ss.formula.functions.T;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import com.ac.sdk.annotation.Resubmit;
import com.ac.sdk.response.Result;
import com.ac.sdk.util.HttpUtils;
import com.ac.sdk.util.RedisUtils;
/**
* 重复提交aop
*
* @author
* @date
*/
@Aspect
@Component
@EnableAspectJAutoProxy(exposeProxy = true)
public class ResubmitAspect {
@Autowired
HttpServletRequest request; // 这里可以获取到request
@Autowired
private RedisUtils redisUtils;
@Autowired
StringRedisTemplate stringRedisTemplate;
/**
* @param point
*/
@SuppressWarnings("unchecked")
@Around("@annotation(com.ac.sdk.annotation.Resubmit)")
public Result<T> around(ProceedingJoinPoint point) throws Throwable {
// 获取请求头中的token
String accesstoken = request.getHeader("access-token");
if (accesstoken == null) {
accesstoken = "";
}
// 通过请求获取
String ip = RequestUtils.getIpAddr(request);
// 获取注解
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
// 目标类、方法
String className = method.getDeclaringClass().getName();
String name = method.getName();
String ipKey = String.format("%s#%s", className, name, accesstoken);
int hashCode = Math.abs(ipKey.hashCode());
String key = String.format("%s_%d", ip, hashCode);
// log.info("ipKey={},hashCode={},key={}",ipKey,hashCode,key);
Resubmit resubmit = method.getAnnotation(Resubmit.class);
Long timeout = resubmit.timeout();
if (timeout < 0) {
timeout = 3L;
}
long count = redisUtils.incrBy(key, 1);
// 设置三秒的有效期
if (count == 1) {
redisUtils.expire(key, 3);
Object object = point.proceed();
return (Result<T>) object;
} else {
return Result.fail("请勿重复提交");
}
}
}
测试使用案例代码:
@Resubmit
@PostMapping
public Result<ClassSchVO> insertSelective(@RequestBody ClassSchVO vo)
throws BizException, ParseException, IOException {
int ret = classSchService.insertSelective(vo);
return ret == 1 ? Result.success() : Result.fail("添加失败");
}
涉及到的获得ip工具类:
public class HttpUtils {
/*
* 通过request得到IP地址 参数: X-Forwarded-For:Squid 服务代理 Proxy-Client-IP:apache 服务代理
* WL-Proxy-Client-IP:weblogic 服务代理 HTTP_CLIENT_IP:有些代理服务器 X-Real-IP:nginx服务代理
*/
public static String getIPAddress(HttpServletRequest request) {
String ip = null;
// X-Forwarded-For:Squid 服务代理
String ipAddresses = request.getHeader("X-Forwarded-For");
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
// Proxy-Client-IP:apache 服务代理
ipAddresses = request.getHeader("Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
// WL-Proxy-Client-IP:weblogic 服务代理
ipAddresses = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
// HTTP_CLIENT_IP:有些代理服务器
ipAddresses = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
// X-Real-IP:nginx服务代理
ipAddresses = request.getHeader("X-Real-IP");
}
// 有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
if (ipAddresses != null && ipAddresses.length() != 0) {
ip = ipAddresses.split(",")[0];
}
// 还是不能获取到,最后再通过request.getRemoteAddr();获取
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
涉及到的redis工具类
package com.ac.sdk.util;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;
/**
* redis工具类,方法定义:http://doc.redisfans.com
*
* @author caiLinFeng
* @date 2018年1月30日
*/
@Service
public class RedisUtils {
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 从列表左边添加
*
* @param k
* @param v
*/
public void lPush(String k, Object v) {
redisTemplate.opsForList().leftPush(k, v);
}
/**
* 列表获取
*
* @param k
* @param l
* @param l1
* @return
*/
public List<Object> lRange(String k, long l, long l1) {
return redisTemplate.opsForList().range(k, l, l1);
}
/**
* 保持链表只有N位
*
* @param k
* @param N
*/
public void lTrim(String k, int N) {
redisTemplate.opsForList().trim(k, 0, N - 1);
}
/**
* 删除key
*
* @author caiLinFeng
* @date 2018年1月30日
*
*/
public void del(String key) {
redisTemplate.delete(key);
}
/**
* 批量删除key
*
* @author caiLinFeng
* @date 2018年1月30日
*/
public void del(Collection<String> keys) {
redisTemplate.delete(keys);
}
/**
* 检查给定 key是否存在
*
* @author caiLinFeng
* @date 2018年1月30日
*/
public Boolean exists(String key) {
return redisTemplate.hasKey(key);
}
/**
* 设置过期时间
*
* @author caiLinFeng
* @date 2018年1月30日
* @param timeout
* 单位秒
*
*
*/
public Boolean expire(String key, long timeout) {
return redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 设置过期时间
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Boolean expire(String key, long timeout, TimeUnit timeUtit) {
return redisTemplate.expire(key, timeout, timeUtit);
}
/**
* 设置过期时间
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Boolean expireAt(String key, Date date) {
return redisTemplate.expireAt(key, date);
}
/**
* 返回给定 key 的剩余生存时间,以秒为单位
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long ttl(String key) {
return redisTemplate.getExpire(key);
}
/******************* String **********************/
/**
* 将 key所储存的值加上增量 delta,返回增加后的值
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long incrBy(String key, long delta) {
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 将字符串值 value 关联到 key
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 将字符串值 value 关联到 key
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public void setex(String key, Object value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
/**
* 将 key的值设为 value ,当且仅当 key 不存在
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Boolean setnx(String key, Object value) {
return redisTemplate.opsForValue().setIfAbsent(key, value);
}
/**
* 关联到 key
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public void mset(Map<String, Object> map) {
redisTemplate.opsForValue().multiSet(map);
}
/**
* 返回 key所关联的字符串
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
/******************* Hash **********************/
/**
* 删除哈希表 key中的一个或多个指定域,不存在的域将被忽略
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long hdel(String key, Object... hashKeys) {
return redisTemplate.opsForHash().delete(key, hashKeys);
}
/**
* 将哈希表 key中的域 field 的值设为 value
*
* @author caiLinFeng
* @Description
* @date 2018年1月30日
* @param
*/
public void hset(String key, String hashKey, Object hashValue) {
redisTemplate.opsForHash().put(key, hashKey, hashValue);
}
/**
* 同时将多个 field-value (域-值)对设置到哈希表 key 中
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public void hmset(String key, Map<String, Object> map) {
redisTemplate.opsForHash().putAll(key, map);
}
/**
* 将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Boolean hsetnx(String key, String hashKey, Object hashValue) {
return redisTemplate.opsForHash().putIfAbsent(key, hashKey, hashValue);
}
/**
* 返回哈希表 key 中给定域 field 的值
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Object hget(String key, String hashKey) {
return redisTemplate.opsForHash().get(key, hashKey);
}
/**
* 返回哈希表 key 中,所有的域和值
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Map<Object, Object> hgetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* 返回哈希表 key 中的所有域
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Set<Object> hkeys(String key) {
return redisTemplate.opsForHash().keys(key);
}
/**
* 返回哈希表 key 中所有域的值
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public List<Object> hvals(String key) {
return redisTemplate.opsForHash().values(key);
}
/**
* 为哈希表 key 中的域 field 的值加上增量 delta
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long hincrBy(String key, String hashKey, long delta) {
return redisTemplate.opsForHash().increment(key, hashKey, delta);
}
/**
* 查看哈希表 key 中,给定域 field 是否存在
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Boolean hexists(final String key, String hashKey) {
return redisTemplate.opsForHash().hasKey(key, hashKey);
}
/******************* List **********************/
/**
* 删除并获取列表中的第一个元素
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Object lpop(String key) {
return redisTemplate.opsForList().leftPop(key);
}
/**
* 删除并获取列表中的第一个元素,或阻塞,直到有一个元素可用
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Object blpop(String key, long timeout, TimeUnit unit) {
return redisTemplate.opsForList().leftPop(key, timeout, unit);
}
/**
* 删除并获取列表中的最后一个元素
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Object rpop(String key) {
return redisTemplate.opsForList().rightPop(key);
}
/**
* 删除并获取列表中的最后一个元素,或阻塞,直到有一个元素可用
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Object brpop(String key, long timeout, TimeUnit unit) {
return redisTemplate.opsForList().rightPop(key, timeout, unit);
}
/**
* 返回列表 key 的长度
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long llen(String key) {
return redisTemplate.opsForList().size(key);
}
/**
* 将value 插入到列表 key 的表头
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long lpush(String key, Object value) {
return redisTemplate.opsForList().leftPush(key, value);
}
/**
* 将值 value 插入到列表 key 的表头,当且仅当 key 存在并且是一个列表
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long lpushx(String key, Object value) {
return redisTemplate.opsForList().leftPushIfPresent(key, value);
}
/**
* 将value 插入到列表 key 的表尾
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long rpush(String key, Object value) {
return redisTemplate.opsForList().rightPush(key, value);
}
/**
* 将值 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long rpushx(String key, Object value) {
return redisTemplate.opsForList().rightPushIfPresent(key, value);
}
/******************* Set **********************/
/**
* 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long sadd(String key, Object... values) {
return redisTemplate.opsForSet().add(key, values);
}
/**
* 返回集合 key 的基数(集合中元素的数量)
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long scard(String key) {
return redisTemplate.opsForSet().size(key);
}
/**
* 返回一个集合的全部成员,该集合是所有给定集合之间的差集
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Set<Object> sdiff(String key, String otherKey) {
return redisTemplate.opsForSet().difference(key, otherKey);
}
/**
* 返回一个集合的全部成员,该集合是所有给定集合之间的差集
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Set<Object> sdiff(String key, Collection<String> otherKeys) {
return redisTemplate.opsForSet().difference(key, otherKeys);
}
/**
* 返回一个集合的全部成员,该集合是所有给定集合的交集
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Set<Object> sinter(String key, String otherKey) {
return redisTemplate.opsForSet().intersect(key, otherKey);
}
/**
* 返回一个集合的全部成员,该集合是所有给定集合的交集
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Set<Object> sinter(String key, Collection<String> otherKeys) {
return redisTemplate.opsForSet().intersect(key, otherKeys);
}
/**
* 判断 member 元素是否集合 key 的成员
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Boolean sismember(String key, Object member) {
return redisTemplate.opsForSet().isMember(key, member);
}
/**
* 返回集合 key 中的所有成员
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Set<Object> smembers(String key) {
return redisTemplate.opsForSet().members(key);
}
/**
* 移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long srem(String key, Object... values) {
return redisTemplate.opsForSet().remove(key, values);
}
/**
* 返回一个集合的全部成员,该集合是所有给定集合的并集
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Set<Object> sunion(String key, String otherKey) {
return redisTemplate.opsForSet().union(key, otherKey);
}
/**
* 返回一个集合的全部成员,该集合是所有给定集合的并集
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Set<Object> sunion(String key, Collection<String> otherKeys) {
return redisTemplate.opsForSet().union(key, otherKeys);
}
/******************* Zset **********************/
/**
* 将一个或多个 member 元素及其 score 值加入到有序集 key 当中v
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Boolean zadd(String key, Object value, double score) {
return redisTemplate.opsForZSet().add(key, value, score);
}
/**
* 返回有序集 key 的基数
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long zcard(String key) {
return redisTemplate.opsForZSet().zCard(key);
}
/**
* 返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max)的成员的数量
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long zcount(String key, double min, double max) {
return redisTemplate.opsForZSet().count(key, min, max);
}
/**
* 为有序集 key 的成员 member 的 score 值加上增量 delta
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Double zincrby(String key, Object value, double delta) {
return redisTemplate.opsForZSet().incrementScore(key, value, delta);
}
/**
* 返回有序集 key 中,指定区间内的成员,其中成员的位置按 score 值递增(从小到大)来排序
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Set<Object> zrange(String key, long start, long end) {
return redisTemplate.opsForZSet().range(key, start, end);
}
/**
* 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max)的成员。有序集成员按
* score,值递增(从小到大)次序排列
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Set<Object> zrangeByScore(String key, double min, double max) {
return redisTemplate.opsForZSet().rangeByScore(key, min, max);
}
/**
* 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。排名以 0 为底
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long zrank(String key, String member) {
return redisTemplate.opsForZSet().rank(key, member);
}
/**
* 移除有序集 key 中,指定排名(rank)区间内的所有成员
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long zremrangeByRank(String key, long start, long end) {
return redisTemplate.opsForZSet().removeRange(key, start, end);
}
/**
* 移除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long zremrangeByScore(String key, double min, double max) {
return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
}
/**
* 返回有序集 key 中,指定区间内的成员。其中成员的位置按 score 值递减(从大到小)来排列。
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Set<Object> zrevrange(String key, long start, long end) {
return redisTemplate.opsForZSet().reverseRange(key, start, end);
}
/**
* 返回有序集 key 中, score 值介于 max 和 min 之间(默认包括等于 max 或 min)的所有的成员。有序集成员按
* score,值递减(从大到小)的次序排列
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Set<Object> zrevrangeByScore(String key, double min, double max) {
return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
}
/**
* 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序。排名以 0 为底
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Long zrevrank(String key, String member) {
return redisTemplate.opsForZSet().reverseRank(key, member);
}
/**
* 返回有序集 key 中,成员 member 的 score 值
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public Double zscore(String key, String member) {
return redisTemplate.opsForZSet().score(key, member);
}
/******************* Pub/Sub **********************/
/**
* 将信息 message 发送到指定的频道 channel
*
* @author caiLinFeng
* @date 2018年1月30日
* @param chanel
*/
public void publish(String channel, Object message) {
redisTemplate.convertAndSend(channel, message);
}
/******************* serial **********************/
/**
* 获取redisTemplate的序列化
*
* @author caiLinFeng
* @date 2018年1月30日
* @param
*/
public RedisSerializer<?> getDefaultSerializer() {
return redisTemplate.getDefaultSerializer();
}
public RedisSerializer<?> getStringSerializer() {
return redisTemplate.getStringSerializer();
}
public RedisSerializer<?> getValueSerializer() {
return redisTemplate.getValueSerializer();
}
}
返回结果工具类
package com.ac.sdk.response;
import java.io.Serializable;
import java.util.Date;
import com.ac.sdk.constant.ErrorCodeEnum;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
/**
* API返回类
*
* @author caiLinFeng
* @date 2018年1月11日
*/
@Data
public class Result<T> implements Serializable {
private final static long serialVersionUID = 1L;
/**
* 错误码
*/
private int errorCode;
/**
* 错误提示
*/
private String errorMessage;
/**
* 数据
*/
private T data;
/**
* 总数
*/
private Integer total;
/**
* 当前时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh", timezone = "GMT+8")
private Date currentTime = new Date();
/**
* 额外数据
*/
private Object attach;
public static <T> Result<T> success() {
Result<T> result = new Result<T>();
result.setErrorCode(ErrorCodeEnum.OK.getCode());
return result;
}
public static <T> Result<T> success(T data) {
Result<T> result = success();
result.setData(data);
return result;
}
public static <T> Result<T> success(T data, int total) {
Result<T> result = success(data);
result.setTotal(total);
return result;
}
public static <T> Result<T> fail() {
Result<T> result = new Result<T>();
result.setErrorCode(ErrorCodeEnum.UNDEFINE_ERROR.getCode());
result.setErrorMessage(ErrorCodeEnum.UNDEFINE_ERROR.getMessage());
return result;
}
public static <T> Result<T> fail(String errorMessag) {
Result<T> result = fail();
result.setErrorMessage(errorMessag);
return result;
}
public static <T> Result<T> fail(ErrorCodeEnum errorCodeEnum) {
Result<T> result = fail();
result.setErrorCode(errorCodeEnum.getCode());
result.setErrorMessage(errorCodeEnum.getMessage());
return result;
}
public boolean isSuccess() {
return ErrorCodeEnum.OK.getCode() == errorCode;
}
@Override
public String toString() {
return "Result [errorCode=" + errorCode + ", errorMessage=" + errorMessage + ", data=" + data + ", total="
+ total + ", attach=" + attach + "]";
}
}