Authored by csgyoho

Merge branch 'master' into dev-inbox-20180911

Showing 50 changed files with 3298 additions and 371 deletions

Too many changes to show.

To preserve performance only 50 of 50+ files are displayed.

users/.settings/
order/.settings/
product/.settings/
product/.gitignore
*.project
*.classpath
payment/.settings/
common/.settings/
order/.gitignore
deploy/.gitignore
deploy/.settings/
.idea/
*.iml
*.ipr
*.iws
.settings/
.project
*/target/
*/.classpath
*/bin/
MANIFEST.MF
deploy/.gitignore
order/.gitignore
product/.gitignore
... ...
package com.yohoufo.common;
import com.alibaba.fastjson.JSON;
import com.yoho.core.common.utils.MD5;
import com.yoho.error.GatewayError;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import java.util.ArrayList;
/**
* API 响应格式
* /**
* 响应为:
* <pre>
* {
* "code": 200,
* "message": "\u767b\u5f55\u6210\u529f",
* “alg": "SALT_MD5"
* "data": {
* "uid": "10216497",
* "passport": "18751986615",
* "session_key": "fa31d3a5d069c6c98cd8c38c3a5f89e6",
* "vip": 0
* },
* "md5": "fa5b07f95a0bf95c26ac50abf0024eed"
* }
* Created by chang@yoho.cn on 2015/11/3.
*/
public class ApiResponse {
private static String DEFAULT_MSG = "操作成功";
private static int DEFAULT_CODE = 200;
private static final String MD5_SALT = "fd4ad5fcsa0de589af23234ks1923ks";
private int code;
private String message;
private String md5;
//如果客户端判断有这个,则校验MD5
private String alg = "SALT_MD5";
private Object data;
public ApiResponse() {
this(200, DEFAULT_MSG, null);
}
public ApiResponse(int code, String message, Object data) {
this.code = code;
if (StringUtils.isNotEmpty(message)) {
this.message = message;
}
this.data = data;
}
public String getAlg() {
return alg;
}
public void setAlg(String alg) {
this.alg = alg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getMd5() {
return md5;
}
public void setMd5(String md5) {
this.md5 = md5;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
/**
* 构造响应。 使用方式:
* <p/>
* <pre>
* ApiResponse.ApiResponseBuilder builder = new ApiResponse.ApiResponseBuilder();
* ApiResponse apiResponse = builder.code(200).message("coupons total").data(new Total("0")).build();
* </pre>
*/
public static class ApiResponseBuilder {
ApiResponse apiResponse;
public ApiResponseBuilder() {
apiResponse = new ApiResponse();
}
/**
* 设置错误码。默认200
*
* @param code 错误码
* @return ApiResponseBuilder
*/
public ApiResponseBuilder code(int code) {
apiResponse.code = code;
return this;
}
/**
* 设置消息。默认[操作成功]
*
* @param message 错误消息
* @return ApiResponseBuilder
*/
public ApiResponseBuilder message(String message) {
apiResponse.message = message;
return this;
}
/**
* 从Errorcode中设置错误码和错误消息
*
* @param gatewayError
* @return
*/
public ApiResponseBuilder code(GatewayError gatewayError) {
apiResponse.code = gatewayError.getCode();
apiResponse.message = gatewayError.getMessage();
return this;
}
/**
* 设置响应的具体内容
*
* @param data 响应的具体内容
* @return 内容
*/
public ApiResponseBuilder data(Object data) {
apiResponse.data = data;
return this;
}
/**
* 构造响应
*
* @return 响应
*/
public ApiResponse build() {
//参数校验, 并且设置默认值
if (this.apiResponse.code <= 0) {
this.apiResponse.code = DEFAULT_CODE;
}
if (StringUtils.isEmpty(apiResponse.message)) {
this.apiResponse.message = DEFAULT_MSG;
}
//构造JSON
apiResponse.md5 = getMd5();
return apiResponse;
}
//计算MD5
private String getMd5() {
if (this.apiResponse.data == null) {
this.apiResponse.data = new ArrayList<>(0);
}
String json = JSON.toJSONString(this.apiResponse.data); // []
return MD5.md5(MD5_SALT+":"+json);
}
}
}
... ...
/*
* Copyright (C), 2016-2017, yoho
* FileName: NetworkControl.java
* Author: Maelk_liu
* Date: 2017年4月10日 上午11:53:33
* Description: //模块目的、功能描述
* History: //修改记录
* <author> <time> <version> <description>
* 修改人姓名 修改时间 版本号 描述
*/
package com.yohoufo.common.annotation;
import java.lang.annotation.*;
/**
* 忽略session验证
*
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface IgnoreSession {
}
... ...
/*
* Copyright (C), 2016-2017, yoho
* FileName: NetworkControl.java
* Author: Maelk_liu
* Date: 2017年4月10日 上午11:53:33
* Description: //模块目的、功能描述
* History: //修改记录
* <author> <time> <version> <description>
* 修改人姓名 修改时间 版本号 描述
*/
package com.yohoufo.common.annotation;
import java.lang.annotation.*;
/**
* 忽略消息签名验证
*
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface IgnoreSignature {
String clientType() default ""; //指定忽略验签的客户端,不指定时忽略验证所有客户端,配置多个时用逗号分隔
String version() default ""; //忽略验签的版本, 低于该版本不校验
}
... ...
/*
* Copyright (C), 2016-2017, yoho
* FileName: NetworkControl.java
* Author: Maelk_liu
* Date: 2017年4月10日 上午11:53:33
* Description: //模块目的、功能描述
* History: //修改记录
* <author> <time> <version> <description>
* 修改人姓名 修改时间 版本号 描述
*/
package com.yohoufo.common.annotation;
import java.lang.annotation.*;
/**
* 控制内网访问接口权限
*
* @author Maelk_liu
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface InnerApi {
}
... ...
package com.yohoufo.common.cache;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标记Controller要进行Cache的方法.
* Created by chzhang@yoho.cn on 2015/11/11.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cachable {
/**
* Expire time in seconds. default 取配置文件中的expire时间
* @return expire time
*/
int expire() default -1;
/**
* 是否需要对key进行md5。如果key 超长,memcache会有问题. 会自动根据key的长度,进行md5操作
* @return 是否需要对key进行md5
* @deprecated
*/
boolean needMD5() default false;
/**
* 一级缓存排除掉那些参数
* @return 要排除的参数次序,从0开始
*/
int[] excludeArgs() default {};
/**
* 二级缓存排除掉那些参数
* @return 要排除的参数次序,从0开始
*/
int[] excludeL2Args() default {};
/**
* 后置处理器bean的名字
* @return
*/
String afterProcessHandlerBeanName() default "";
/**
* 缓存时间key
* @return
*/
String cacheTimeKey() default "";
}
... ...
package com.yohoufo.common.cache;
import java.util.Collection;
import java.util.Map;
/**
* 缓存客户端
*/
public interface CacheClient {
/**
* 设置cache
* @param key 缓存的key
* @param expireInSeconds 缓存失效时间,秒
* @param o 要缓存的对象
*/
public void set(String key, int expireInSeconds, Object o);
/**
* 设置cache,并同步删除
* @param key 缓存的key
* @param expireInSeconds 缓存失效时间,秒
* @param o 要缓存的对象
*/
public void setAndSync(String key, int expireInSeconds, Object o);
/**
* 获取cache的对象
* @param key 要cache的KEY
* @param clazz 对象
* @param <T> 对象类型
* @return cache的对象
*/
public <T> T get(String key, Class<T> clazz);
/**
* 批量获取
* @param keys keys
* @param clazz 类型
* @param <T> 类型
* @return key-response的map
*/
public <T> Map<String, T> getBulk(Collection<String> keys, Class<T> clazz);
/**
* 删除缓存
* @param key 缓存的key
*/
public void delete(String key);
public void mset(Map<String, ? extends Object> map, long timeout);
public <T> void hashPut(String key, String field, T value, long timeout);
public <T> T hashGet(String key, String field, Class<T> clazz);
}
... ...
package com.yohoufo.common.cache;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.Resource;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import com.yoho.core.common.utils.MD5;
import com.yoho.core.config.ConfigReader;
import com.yoho.core.rest.exception.ServiceNotAvaibleException;
/**
* 对controller进行AOP,实现HTTP Restful接口的cache。 请求的处理流程:
* 1.从cache中找,看是否hit,如果hit,直接返回结果
* 2. 如果cache miss,则调用服务,
* 2.1 如果服务调用成功,则将服务调用结果返回,并且设置到cache中;
* <p/>
* Created by chzhang@yoho.cn on 2015/11/11.
*/
@Aspect
@Component
public class ControllerCacheAop implements ApplicationContextAware{
private static final Logger logger = LoggerFactory.getLogger(ControllerCacheAop.class);
@Autowired
private CacheClient cacheClient;
@Resource(name = "core-config-reader")
ConfigReader configReader;
ApplicationContext applicationContext;
//we define a pointcut for all controllers
@Pointcut("within(@org.springframework.stereotype.Controller *)")
public void controllerPointcut() {
}
@Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
public void restControllerPointcut() {
}
/**
* a pointcut for all class with annotation : {@link Cachable }
*/
@Pointcut("@annotation(com.yohoufo.common.cache.Cachable)")
public void cacheAnnotationPointCut() {
}
/**
* Operations
* 先从cache中,找不到则调用controller原来的实现(一般是调用服务),如果调用服务有异常 {@link ServiceNotAvaibleException},则从二级缓存中取
*/
@Around("(controllerPointcut()|| restControllerPointcut()) && cacheAnnotationPointCut() ")
public Object cacheAop(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//方法名称
final String methodName = signature.getMethod().getName();
//返回类型
final Class<?> returnType = signature.getMethod().getReturnType();
logger.debug("Enter controller pointcut: {}", methodName);
//some info
Cachable cacheExpire = signature.getMethod().getAnnotation(Cachable.class);
//caculate cache key
final String level1_cache_key = this.getL1CacheKey(joinPoint, cacheExpire);
//final String level2_cache_key = this.getL2CacheKey(joinPoint, cacheExpire);
//从一级缓存中获取数据
try {
Object level1_obj = this.cacheClient.get(level1_cache_key, returnType);
if (level1_obj != null) {
logger.debug("Cache1 hit for method:{} at key:{}.", methodName, level1_cache_key);
return level1_obj;
}
} catch (Exception e) {
logger.warn("get from level 1 cache exception.", e);
}
//cache miss at level1: 调用原来的请求,然后将结果缓存到cache1&cache2中。如果调用异常,则从cache2中查找
Object httpResponse;
try {
httpResponse = joinPoint.proceed();
if (httpResponse != null) {
this.saveToCache(level1_cache_key, cacheExpire, httpResponse);
}
} catch (Throwable throwable) {
logger.error("Cache1 & Cache2 miss for method:{} .", methodName);
throw throwable;
}
return httpResponse;
}
/**
* 将结果保存在缓存中
* @param level1_cache_key
* @param cacheExpire
* @param httpResponse
*/
public void saveToCache(String level1_cache_key, Cachable cacheExpire, Object httpResponse) {
int cacheExpireTime = getCacheTime(cacheExpire);
// 一级缓存失效的时间,如果指定了,则使用指定的值。如果没有指定,则使用配置的值
if (cacheExpireTime <= 0) {
cacheExpireTime = 60;
}
this.cacheClient.set(level1_cache_key, cacheExpireTime, httpResponse);
}
/**
* 单位是s
* @param cacheExpire
* @return
*/
private int getCacheTime(Cachable cacheExpire) {
if (StringUtils.isNotBlank(cacheExpire.cacheTimeKey())) {
return configReader.getInt(cacheExpire.cacheTimeKey(), 60);
}
return cacheExpire.expire();
}
/**
* 根据拦截的方法的参数,生成cache的key. yh_gw:METHOD_NAME:METHOD_PARAM
*
* @param joinPoint 拦截点
* @param cacheExpire
* @return key
*/
private String getL1CacheKey(ProceedingJoinPoint joinPoint, Cachable cacheExpire) {
int[] excludeParams = cacheExpire.excludeArgs();
String cacheKey = this.getCacheKey(joinPoint, excludeParams);
String level1_cache_key = "YH:UFOGW:L1:" + cacheKey;
/**
* 最大250,超过限制需要MD5
* Data stored by memcached is identified with the help of a key. A key
is a text string which should uniquely identify the data for clients
that are interested in storing and retrieving it. Currently the
length limit of a key is set at 250 characters (of course, normally
clients wouldn't need to use such long keys); the key must not include
control characters or whitespace.
*/
if( level1_cache_key.toCharArray().length > 250 ){
level1_cache_key = MD5.md5(level1_cache_key);
}
return level1_cache_key;
}
private String getCacheKey(ProceedingJoinPoint joinPoint, int[] excludeParams ){
//args params sign
Object arguments[] = joinPoint.getArgs();
String args_sign = this.getStrings(arguments, excludeParams);
//method name
String cacheKeyPrefix = "yh_ufogw:" + joinPoint.getSignature().getName();
return cacheKeyPrefix + ":" + args_sign;
}
/**
* 获取参数的签名
* public for test
* @param arguments arguments
* @param excludeParams arguments to exclude
* @return params sign
*/
public String getStrings(Object arguments[], int[] excludeParams) {
if (ArrayUtils.isEmpty(arguments)) {
return StringUtils.EMPTY;
}
List<String> methodParams = new LinkedList<>();
for (int i = 0; i < arguments.length; i++) {
//排除掉某些参数
if (ArrayUtils.contains(excludeParams, i)) {
continue;
}
Object arg = arguments[i];
String param = this.argToString(arg);
methodParams.add(param);
}
String arg_sign = String.join("-", methodParams).replaceAll("\\s+","");
return arg_sign;
}
/**
* 把参数对象转化为字符串,使用JSON转化
* A key can be up to 250 bytes. It may not contain:
* null (0x00)
* space (0x20)
* tab (0x09)
* newline (0x0a)
* carriage-return (0x0d)
*
* @param arg 参数对象
* @return 字符串
* @auth chunhua.zhang@yoho.cn
*/
private String argToString(Object arg) {
if (arg == null) {
return StringUtils.EMPTY;
}
if(arg instanceof String[]){
return String.join("-", (String[]) arg);
}else{
return String.valueOf(arg);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext=applicationContext;
}
}
... ...
package com.yohoufo.common.cache;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.yoho.core.redis.cluster.annotation.Redis;
import com.yoho.core.redis.cluster.operations.nosync.YHHashOperations;
import com.yoho.core.redis.cluster.operations.nosync.YHRedisTemplate;
import com.yoho.core.redis.cluster.operations.nosync.YHValueOperations;
import com.yoho.core.redis.cluster.operations.serializer.RedisKeyBuilder;
public class RedisGwCacheClient implements CacheClient {
private static final Logger LOGGER = LoggerFactory.getLogger(RedisGwCacheClient.class);
@Redis("gwNoSyncRedis")
private YHRedisTemplate redis;
@Redis("gwNoSyncRedis")
private YHValueOperations valueOperations;
@Redis("gwNoSyncRedis")
private YHHashOperations hashOperations;
@Override
public void set(String key, int expireInSeconds, Object o) {
if (expireInSeconds > 120) {
expireInSeconds = expireInSeconds + getRandomNum(-20, 60);
}
try {
String compressStr= SnappyZipUtils.compress(JSON.toJSONString(o));
this.valueOperations.set(RedisKeyBuilder.newInstance().appendFixed(key), compressStr,
expireInSeconds, TimeUnit.SECONDS);
} catch (Exception e) {
LOGGER.warn("set into redis failed!! key is :{} ,e is :{}", key,e);
// do nothings
}
}
@Override
public void setAndSync(String key, int expireInSeconds, Object o) {
if (expireInSeconds > 120) {
expireInSeconds = expireInSeconds + getRandomNum(-20, 60);
}
try {
String compressStr= SnappyZipUtils.compress(JSON.toJSONString(o));
this.valueOperations.setAndAsyncClean(RedisKeyBuilder.newInstance().appendFixed(key), compressStr,
expireInSeconds, TimeUnit.SECONDS);
} catch (Exception e) {
LOGGER.warn("set into redis failed!! key is :{} ,e is :{}", key,e);
// do nothing
}
}
@Override
public <T> T get(String key, Class<T> clazz) {
try {
String value = this.valueOperations.get(RedisKeyBuilder.newInstance().appendFixed(key));
if (StringUtils.isNotEmpty(value)) {
String unCompressStr= SnappyZipUtils.uncompress(value);
return JSON.parseObject(unCompressStr, clazz);
} else {
LOGGER.debug("can not get data from redis by key {}.", key);
return null;
}
} catch (Exception e) {
LOGGER.warn("get from redis failed!! key is:{},e is :{}", key,e);
return null;
}
}
@Override
public <T> Map<String, T> getBulk(Collection<String> keys, Class<T> clazz) {
List<RedisKeyBuilder> redisKeyBuilders=convert(keys);
try {
List<String> multiGet =valueOperations.multiGet(redisKeyBuilders);
int size = multiGet.size();
Map<String, T> result = new HashMap<String, T>(size);
String curItem;
for (int i = 0; i < size; i++) {
curItem = multiGet.get(i);
if (null == curItem) {
result.put(redisKeyBuilders.get(i).getKey(), null);
} else {
result.put(redisKeyBuilders.get(i).getKey(), JSON.parseObject(curItem, clazz));
}
}
return result;
} catch (Exception e) {
LOGGER.warn("getBulk from redis failed!!", e);
return Maps.newHashMap();
}
}
@Override
public void delete(String key) {
try {
redis.delete(RedisKeyBuilder.newInstance().appendFixed(key));
} catch (Exception e) {
LOGGER.warn("delete from redis failed!! key is:{},e is :{}", key,e);
}
}
@Override
public void mset(Map<String, ? extends Object> map, long timeout) {
if (MapUtils.isEmpty(map)) {
return;
}
Map<RedisKeyBuilder, String> cacheMap = new HashMap<RedisKeyBuilder, String>(map.size());
try{
for (String key : map.keySet()) {
Object value = map.get(key);
RedisKeyBuilder keyBuilder = RedisKeyBuilder.newInstance().appendFixed(key);
if (value instanceof String) {
cacheMap.put(keyBuilder, (String) value);
} else if (value instanceof Integer) {
cacheMap.put(keyBuilder, value + "");
} else {
cacheMap.put(keyBuilder, JSON.toJSONString(value));
}
}
redis.mset(cacheMap, timeout);
}catch (Exception e){
LOGGER.warn("mset cache failed!!! e is: {}", e);
}
}
@Override
public <T> void hashPut(String key, String field, T value, long timeout) {
if(null == value){
return;
}
RedisKeyBuilder keyBuilder = RedisKeyBuilder.newInstance().appendFixed(key);
try{
hashOperations.put(keyBuilder, field, JSON.toJSONString(value));
redis.longExpire(keyBuilder, timeout, TimeUnit.SECONDS);
}catch (Exception e){
LOGGER.warn("hashPut redis value operation failed. e is: {}", e);
}
}
@Override
public <T> T hashGet(String key, String field, Class<T> clazz){
String value = null;
try {
RedisKeyBuilder keyBuilder = RedisKeyBuilder.newInstance().appendFixed(key);
value = hashOperations.get(keyBuilder, field);
return getValue(value, clazz);
}catch (Exception e){
LOGGER.warn("hashGet redis value operation failed. e is: {}", e);
}
return null;
}
/**
* 获取正负区间的一个随机数
*
* @param smallistNum
* @param biggestNum
* @return
*/
private int getRandomNum(int smallistNum, int biggestNum) {
Random random = new Random();
return (Math.abs(random.nextInt()) % (biggestNum - smallistNum + 1)) + smallistNum;
}
private List<RedisKeyBuilder> convert(Collection<String> keys) {
return keys.stream().map(key -> RedisKeyBuilder.newInstance().appendFixed(key)).collect(Collectors.toList());
}
@SuppressWarnings("unchecked")
private <T> T getValue(String value, Class<T> clazz) {
T t = null;
if (clazz.equals(String.class)) {
t = (T) value;
} else if (clazz.equals(Integer.class)) {
t = (T) Integer.valueOf(value);
} else {
try{
t = JSON.parseObject(value, clazz);
}catch (Exception e) {
LOGGER.warn("get value cache failed!!! value is: "+ value,e);
}
}
return t;
}
}
... ...
package com.yohoufo.common.cache;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xerial.snappy.Snappy;
import java.nio.charset.Charset;
public class SnappyZipUtils {
private final static Logger logger = LoggerFactory.getLogger(SnappyZipUtils.class);
/**
* 压缩
* @param obj
* @return
*/
public static String compress(Object obj){
if(null==obj){
return StringUtils.EMPTY;
}
//序列化使用json,并压缩缓存对象
String value=null;
if(obj instanceof String){
value=(String)obj;
}else{
value=JSON.toJSONString(obj);
}
try {
byte[] compressVal = Snappy.compress(value.getBytes("UTF-8") );
String compressStr = new String( compressVal, Charset.forName("ISO8859-1") );
return compressStr;
} catch (Exception e) {
//do nothing
logger.warn("compress the object failed",e);
return StringUtils.EMPTY;
}
}
/**
* 解压
* @param obj
* @return
*/
public static String uncompress(Object obj)
{
if(null==obj){
return StringUtils.EMPTY;
}
//序列化使用json,解压缩缓存对象
try {
byte[] unCompressVal = Snappy.uncompress( ((String)obj).getBytes(Charset.forName("ISO8859-1")) ) ;
String unCompressStr = new String( unCompressVal , "UTF-8");
return unCompressStr;
} catch (Exception e) {
//do nothing
logger.warn("compress the object failed",e);
return StringUtils.EMPTY;
}
}
}
... ...
package com.yohoufo.common.exception;
/**
*
* API Gateway异常父类
*
* Created by chang@yoho.cn on 2015/11/3.
*/
public class GatewayException extends Exception{
private int code;
private String desc;
/**
*/
public GatewayException(int code, String desc){
this.code = code;
this.desc=desc;
}
@Override
public String getMessage() {
return "[" + this.code + ":" + this.desc + "]";
}
public String getDesc() {
return desc;
}
public int getErrorCode() {
return code;
}
}
... ...
package com.yohoufo.common.exception;
/**
* Created by xjipeng on 16/2/15.
*/
public class SessionExpireException extends GatewayException {
public SessionExpireException() {
super(401, "登录会话超时,请退出重新登录.");
}
}
... ...
package com.yohoufo.common.exception;
/**
* 参数验签不正确
*
*/
public class SignatureNotMatchException extends GatewayException {
/**
* 异常
*
*/
public SignatureNotMatchException() {
super(508, "");
//客户端会透传,暂时不作提示
//super(508, "数据签名验证错误.");
}
}
... ...
package com.yohoufo.common.exception;
/**
* Created by lenovo on 2017/4/9.
*/
public class VersionNotSupportException extends GatewayException {
/**
* 版本不支持异常
*/
public VersionNotSupportException() {
super(500, "请升级至最新版本");
}
}
... ...
package com.yohoufo.common.filter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.netflix.config.DynamicPropertyFactory;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.Assert;
import org.springframework.util.DigestUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;
/**
* 对响应的body添加MD5
*/
public class ResponseChecksumFilter extends OncePerRequestFilter {
private static final String HEADER_CHECKSUM = "X-YH-Response-Checksum";
private static final String HEADER_CLOUD = "X-YH-Response-Cloud";
private static final String MD5_SALT = "fd4ad5fcsa0de589af23234ks1923ks";
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
/**** 不处理下面的情况 **/
String path = request.getRequestURI();
if(path.contains("hystrix.stream")){
return true;
}
return false;
}
/**
* The default value is "false" so that the filter may delay the generation of
* an ETag until the last asynchronously dispatched thread.
*/
@Override
protected boolean shouldNotFilterAsyncDispatch() {
return false;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpServletResponse responseToUse = response;
//先不考虑异步的情况
if (!(response instanceof ContentCachingResponseWrapper)) {
responseToUse = new ContentCachingResponseWrapper(response);
}
filterChain.doFilter(request, responseToUse);
//update response if needed
updateResponse(responseToUse);
}
private void updateResponse(HttpServletResponse response) throws IOException {
ContentCachingResponseWrapper responseWrapper =
WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
Assert.notNull(responseWrapper, "ContentCachingResponseWrapper not found");
HttpServletResponse rawResponse = (HttpServletResponse) responseWrapper.getResponse();
int statusCode = responseWrapper.getStatusCode();
if (rawResponse.isCommitted()) {
responseWrapper.copyBodyToResponse();
} else if (isNeedAddMD5(statusCode)) {
String md5 = this.generateMD5(responseWrapper.getContentInputStream());
rawResponse.setHeader(HEADER_CHECKSUM, md5);
// 响应中返回来自哪个云
String cloud = DynamicPropertyFactory.getInstance().getStringProperty("cloud", null).get();
rawResponse.setHeader(HEADER_CLOUD, cloud);
responseWrapper.copyBodyToResponse();
} else {
if (logger.isTraceEnabled()) {
logger.trace("Response with status code [" + statusCode + "] not eligible for md5");
}
responseWrapper.copyBodyToResponse();
}
}
/**
* 是否需要添加MD5 正常的响应才需要
*/
protected boolean isNeedAddMD5(int responseStatusCode) {
if (responseStatusCode >= 200 && responseStatusCode < 300) {
return true;
}
return false;
}
/**
* 生成MD5: md5(salt:md5(body))
*
* @param inputStream
* @return
* @throws IOException
*/
protected String generateMD5(InputStream inputStream) throws IOException {
// md5(body)
StringBuilder builder = new StringBuilder(32);
DigestUtils.appendMd5DigestAsHex(inputStream, builder);
//md5(salt:md5(body))
StringBuilder md5 = new StringBuilder(32);
String now = MD5_SALT + ":" + builder.toString();
InputStream bodyStream = new ByteArrayInputStream(now.getBytes(Charset.forName("UTF-8")));
DigestUtils.appendMd5DigestAsHex(bodyStream, md5);
IOUtils.closeQuietly(bodyStream);
return md5.toString();
}
}
\ No newline at end of file
... ...
package com.yohoufo.common.helper;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
/**
* 图片URL拼接的辅助接口
* @author mali
*
*/
public class ImageUrlAssist {
private static final String INIT_POSITION = "center";
private static final String INIT_BACKGROUND = "d2hpdGU=";
private static final String INIT_SIZE = "source";
private static final Logger logger = LoggerFactory.getLogger(ImageUrlAssist.class);
/**
* 图片尺寸字符串的分隔符
*/
private static final String SEPRATOR_SIZE = "x";
/**
* 获取图片的完整路径 包含参数
* @param imageUrl 相对路径
* @param bucket
* @param position
* @param background
* @return 图片的完整路径
*/
public static String getAllProductPicUrl(Object imageUrl, String bucket, String position, String background) {
if(logger.isDebugEnabled()){
logger.debug("getAllProductPicUrl imageUrl:{},bucket:{},position:{},background:{}", imageUrl, bucket, position, background);
}
if (!(imageUrl instanceof String) || StringUtils.isEmpty((String)imageUrl)) {
return "";
}
if (StringUtils.isEmpty(background)) {
background = INIT_BACKGROUND;
}
if (StringUtils.isEmpty(position)) {
position = INIT_POSITION;
}
return getUrl((String)imageUrl, bucket, null, null) + "?imageMogr2/thumbnail/{width}x{height}/background/"
+ background + "/position/" + position + "/quality/80";
}
/**
* 获取图片的路径,不含参数
* @param imageUrl
* @param bucket
* @return
*/
public static String getUrl(String imageUrl, String bucket, String source, Integer mode) {
if (StringUtils.isEmpty(imageUrl)) {
return "";
}
source = StringUtils.isEmpty(source) ? INIT_SIZE : source;
mode = null == mode ? 1 : mode;
Integer width = null;
Integer height = null;
if (StringUtils.isEmpty(source) && !INIT_SIZE.equals(source)) {
String[] split = source.split(SEPRATOR_SIZE);
if (split.length == 2) {
try {
width = Integer.valueOf(split[0]);
height = Integer.valueOf(split[1]);
} catch (NumberFormatException e) {
logger.warn("The source of goodsPic is not number. imageUrl is " + imageUrl + "; source : " + source, e);
}
}
}
String domain = getDomain(imageUrl);
if (StringUtils.isEmpty(domain)) {
return "";
}
return getImgPrivateUrl(bucket + imageUrl, width, height, mode, domain);
}
/**
* 获取私有的图片地址
* @param fileName
* @param width
* @param height
* @param mode
* @param domain
* @return
*/
private static String getImgPrivateUrl(String fileName, Integer width, Integer height, Integer mode, String domain) {
if (null == mode) {
mode = 1;
}
if (StringUtils.isEmpty(domain)) {
domain = "yhfair.qiniudn.com";
}
return makeRequest("http://" + domain + "/" + fileName.replaceAll("%2F", "/"), mode, width, height, domain, null, null);
}
/**
* 拼接图片的URL参数
* @param baseUrl
* @param mode
* @param width
* @param height
* @param domain
* @param quality
* @param format
* @return
*/
private static String makeRequest(String baseUrl, Integer mode, Integer width, Integer height, String domain,
String quality, String format) {
StringBuilder sb = new StringBuilder();
if (null != mode) {
sb.append(mode);
}
if (null != width) {
sb.append("/w/").append(width);
}
if (null != height) {
sb.append("/h/").append(height);
}
if (null != quality) {
sb.append("/q/").append(quality);
}
if (null != format) {
sb.append("/format/").append(format);
}
if (0 == sb.length()) {
return baseUrl;
}
if (null == width || null == height) {
return baseUrl;
}
return baseUrl + "?imageView/" + sb.toString();
}
public static String getUrlWhitImageView2(String url) {
return url + "?imageView2/{mode}/w/{width}/h/{height}/q/60";
}
private static String getDomain(String imageUrl) {
if (imageUrl.length() < 17) {
return "";
}
String node = imageUrl.substring(15, 17);
List<String> domainList = getDomainList(node);
if (domainList.isEmpty()) {
return "";
}
return domainList.get(Math.random() > 0.5 ? 1 : 0);
}
/**
* 图片的节点服务器列表
* @param node
* @return
*/
private static List<String> getDomainList(String node) {
if ("01".equals(node)) {
return Lists.newArrayList("img10.static.yhbimg.com", "img11.static.yhbimg.com");
}else if("02".equals(node)) {
return Lists.newArrayList("img12.static.yhbimg.com", "img13.static.yhbimg.com");
}
return new ArrayList<String>(0);
}
}
... ...
package com.yohoufo.common.interceptor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 获取客户端IP保存在thread local中。 调用方法:{@link RemoteIPInterceptor#getRemoteIP()}
* Created by chzhang@yoho.cn on 2015/11/5.
*/
public class RemoteIPInterceptor implements HandlerInterceptor {
private static final ThreadLocal<String> localIp = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
String ip = httpServletRequest.getHeader("X-Real-IP");
if (StringUtils.isEmpty(ip)) {
ip = httpServletRequest.getRemoteAddr();
}
localIp.set(ip);
return true;
}
/**
* 获取客户端IP地址
* @return 客户端请求IP地址
*/
public static String getRemoteIP(){
return localIp.get();
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
localIp.remove();
}
}
... ...
package com.yohoufo.common.interceptor;
import com.yoho.core.config.ConfigReader;
import com.yoho.core.redis.cluster.annotation.Redis;
import com.yoho.core.redis.cluster.operations.nosync.YHValueOperations;
import com.yoho.core.redis.cluster.operations.serializer.RedisKeyBuilder;
import com.yoho.core.rest.client.ServiceCaller;
import com.yoho.error.event.LogEvent;
import com.yoho.service.model.request.UserSessionReqBO;
import com.yohoufo.common.annotation.IgnoreSession;
import com.yohoufo.common.exception.GatewayException;
import com.yohoufo.common.exception.SessionExpireException;
import com.yohoufo.common.exception.VersionNotSupportException;
import com.yohoufo.common.utils.ServletUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class SecurityInterceptor implements HandlerInterceptor, ApplicationEventPublisherAware {
private final Logger logger = LoggerFactory.getLogger(SecurityInterceptor.class);
//session缓存key前缀
private static final String SESSION_CACHE_KEY_PRE = "yh:sessionid:";
//是否启用
private boolean isDebugEnable = false;
// 这些url不会进行校验。 例如 "/notify"
private List<String> excludeUrls;
//限制本地IP访问
private List<String> local = new LinkedList<>();
//有货需要检查session接口, 客户端是否已经登录
private List<String> checkSessionMethods = new LinkedList<>();
@Redis("yohoNoSyncRedis")
private YHValueOperations valueOperations;
@Resource
ServiceCaller serviceCaller;
@Resource(name = "core-config-reader")
private ConfigReader configReader;
private ApplicationEventPublisher publisher;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
Map<String, String> params = this.getRequestInfo(httpServletRequest);
//(1)校验版本
this.validVersion(params, httpServletRequest);
//(2)不需要校验SESSION的场景. (1)exclude和debug模式,(2)私有网络模式,(3)配置了不需要校验的注解.
if (this.isIgnore(httpServletRequest, params, o)) {
return true;
}
//(3)校验session
this.validateSession(httpServletRequest, params);
return true;
}
private void validateSession(HttpServletRequest httpServletRequest, Map<String, String> params) throws SessionExpireException, VersionNotSupportException {
// params为空,说明接口无参数, 无需校验
if (params == null || params.size() == 0) {
return;
}
String clientType = params.get("client_type");
String sessionType = params.get("session_type");
String method = params.get("method");
String uid = params.get("uid");
String appVersion = params.get("app_version");
//==============以下是完全不校验的场景=========================
//2 是否校验全部接口,开关-true:校验全部接口(除去@IgnoreSession注解接口) 开关-false:只校验核心接口
boolean isVerifyAllMethod = configReader.getBoolean("gateway.session.isVerifyAllMethod", true);
if(!isVerifyAllMethod){
//2.1 当前接口不再校验的范围, 直接返回, 不校验.
if(StringUtils.isEmpty(method) || !checkSessionMethods.contains(method)){
return ;
}
}
//============ 以下是必须校验的场景 =====================
//3 如果没有传入UID, 校验不通过
if(StringUtils.isEmpty(uid) || !StringUtils.isNumeric(uid) || Integer.valueOf(uid) < 1){
return;
}
//4 需要校验接口没传appVersion提示升级
if(StringUtils.isEmpty(appVersion)){
logger.warn("need to check session info, appVersion is null, method {} is {} ", method);
throw new VersionNotSupportException();
}
//5 解析客户端传入的COOKIE中的session值
Cookie[] cookies = httpServletRequest.getCookies();
String jSessionID = null;
if (cookies != null) {
for (Cookie cookie : cookies) {
//解析sessionid
if ("JSESSIONID".equals(cookie.getName())) {
jSessionID = cookie.getValue();
break;
}
}
}
//6 如果cookie中没有jSessionID , 但接口又必须校验会话, 则返回 HTTP 401, 需要重新登录.
if (jSessionID == null) {
logger.warn("check session failed, can not find session id in cookies, check session info failed, method {}, uid {}, appVersion is {}, clientType is {}, sessionType is {}", method, uid, appVersion, clientType, sessionType);
this.verifyFailReport(uid, method, clientType);
throw new SessionExpireException(); //重新登录
}
//7 从REDIS中获取服务端session的值. 如果REDIS中获取不到,可能存在双中心延迟的情况, 回源数据库查询
String sessionInfo;
try {
RedisKeyBuilder cacheKey = getSessionCacheKey(jSessionID, clientType, sessionType);
sessionInfo = valueOperations.get(cacheKey);
if(null == sessionInfo){ //如果REDIS主从延迟, 从主REDIS中获取SESSION
cacheKey = RedisKeyBuilder.newInstance().appendFixed(SESSION_CACHE_KEY_PRE).appendVar(jSessionID);
sessionInfo = valueOperations.get(cacheKey);
}
}catch (Exception redisException){
//如果redis异常,直接放通
logger.warn("redis exception {} when get session", redisException.getMessage());
return;
}
//8 session双云同步延迟时,获取用户session
if(null == sessionInfo){
sessionInfo = this.getUserSesion(uid, jSessionID, clientType, sessionType);
}
//9 校验SESSION, 校验不通过重新登录
if (uid == null || sessionInfo == null || !StringUtils.equals(sessionInfo, uid)) {
logger.warn("check session failed, session unmatched uid, session id {}, uid {} , session info {}, method {}, version is {}, clientType is {}, sessionType is {}", jSessionID, params.get("uid"), sessionInfo, method, appVersion, clientType, sessionType);
this.verifyFailReport(uid, method, clientType);
throw new SessionExpireException(); //重新登录
}
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
//do nothing
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
/**
* session验证失败上报事件
* @param uid
* @param method
* @param clientType
*/
private void verifyFailReport(String uid, String method, String clientType){
try{
LogEvent logEvent = new LogEvent.Builder("sessionFail").addArg("uid", uid).addArg("method", method).addArg("clientType", clientType).build();
publisher.publishEvent(logEvent);
}catch (Exception e){
logger.warn("verifyFailReport: report session verify event faild, uid is {}, method is {}, error is {}", uid, method, e);
}
}
private RedisKeyBuilder getSessionCacheKey(String sessionKey, String clientType, String sessionType){
RedisKeyBuilder keyBuilder = RedisKeyBuilder.newInstance().appendFixed(SESSION_CACHE_KEY_PRE);
//微信商城和h5共用同一session
if ("wechat".equalsIgnoreCase(clientType)){
keyBuilder.appendFixed(SessionTypeEnum.H5.getName()).appendFixed(":");
keyBuilder.appendVar(sessionKey);
return keyBuilder;
}
//h5嵌入其他端如iphone,android的场景
if(StringUtils.isNotEmpty(sessionType)){
if(SessionTypeEnum.contains(sessionType)) {
keyBuilder.appendFixed(sessionType).appendFixed(":");
keyBuilder.appendVar(sessionKey);
return keyBuilder;
}else{
keyBuilder.appendVar(sessionKey);
return keyBuilder;
}
}
if(SessionTypeEnum.contains(clientType)){
keyBuilder.appendFixed(clientType).appendFixed(":");
keyBuilder.appendVar(sessionKey);
return keyBuilder;
}
keyBuilder.appendVar(sessionKey);
return keyBuilder;
}
private boolean isIgnore(HttpServletRequest request, Map<String, String> params, Object o) {
//如果请求url包含在过滤的url,则直接返回. 请求url可能是 "/gateway/xxx”这种包含了context的。
logger.debug("enter isIgnore");
if (excludeUrls != null) {
final String requestUri = request.getRequestURI();
if (this.urlContains(requestUri, excludeUrls)) {
logger.debug("isIgnore check url in excludeUrls");
return true;
}
}
//如果请求是本地请求(来自私有网络)
if (this.isLocalRequestMatch(request)) {
logger.debug("isIgnore check ip is local");
return true;
}
//配置文件配置为 is_debug_enable 为true,并且请求携带参数debug为XYZ,就放行
if (isDebugEnable && "XYZ".equals(request.getParameter("debug"))) {
logger.debug("isIgnore check debug model");
return true;
}
logger.debug("end to isIgnore check");
//含有IgnoreSession注解的接口放行
if(o.getClass().isAssignableFrom(HandlerMethod.class)){
HandlerMethod handlerMethod = (HandlerMethod)o;
Method bridgedMethod =handlerMethod.getMethod();
if(bridgedMethod.isAnnotationPresent(IgnoreSession.class)){
return true;
}
}
//检查zk中设置的忽略验证接口(线上环境可以动态忽略/取消忽略指定接口)
String ignoreMethods = configReader.getString("gateway.session.ignoreMethods", "");
if(StringUtils.isEmpty(ignoreMethods)){
return false;
}
List<String> ignoreMethodList = Arrays.asList(ignoreMethods.split(","));
String method = params.get("method");
String verifyMethod = StringUtils.isEmpty(method) ? request.getRequestURI().substring(8) : method;
if(StringUtils.isNotEmpty(verifyMethod) && ignoreMethodList.contains(verifyMethod)){
return true;
}
return false;
}
/**
* 校验客户端版本
*
* @param params
*/
private void validVersion(Map<String, String> params, HttpServletRequest request) throws GatewayException {
String uid = params.get("uid");
int uidLen = configReader.getInt("gateway.limit.uid.length", 10);
if(StringUtils.isNotEmpty(uid) && (!StringUtils.isNumeric(uid) || uid.length() > uidLen)){
logger.warn("validVersion: uid is illegal: param is {}, ip is {}", params, getIP(request));
throw new SessionExpireException();
}
}
/**
* 获取请求信息: requestParam
*
* @param httpServletRequest
* @return
*/
private Map<String, String> getRequestInfo(HttpServletRequest httpServletRequest) {
return ServletUtils.getRequestParams(httpServletRequest);
}
/**
* 本地IP限制
* @param request
* @return
*/
private boolean isLocalRequestMatch(HttpServletRequest request) {
if (CollectionUtils.isEmpty(this.local)) {
return false;
}
final String requestUri = request.getRequestURI();
final String ip = this.getIP(request);
//ip is blank or has multi ip
if(StringUtils.isEmpty(ip) || ip.contains(",")){
return false;
}
try {
InetAddress inetAddress = InetAddress.getByName(ip);
if (this.urlContains(requestUri, local) && (inetAddress.isSiteLocalAddress() || inetAddress.isLoopbackAddress())) {
return true;
}
} catch (UnknownHostException e) {
logger.error("unknown ip", e);
}
return false;
}
private boolean urlContains(String requestUri, List<String> excludeUrls) {
for (String excludeUri : excludeUrls) {
if (requestUri.equals(excludeUri) || requestUri.startsWith(excludeUri) || requestUri.startsWith("/gateway" + excludeUri) || requestUri.endsWith(excludeUri)) {
return true;
}
}
return false;
}
/**
* 获取用户session信息,解决双云session同步延迟问题
* @param sessionKey
* @return
*/
private String getUserSesion(String uid, String sessionKey, String clientType, String sessionType){
try{
boolean degrade_getSession_enable = configReader.getBoolean("gateway.degrade.users.getUserSesion.enable",false);
if(degrade_getSession_enable){
return null;
}
UserSessionReqBO reqBO = new UserSessionReqBO();
reqBO.setUid(uid == null ? null : Integer.valueOf(uid));
reqBO.setSessionKey(sessionKey);
reqBO.setClientType(clientType);
reqBO.setSessionType(sessionType);
UserSessionReqBO result = serviceCaller.call("uic.selectUserSession", reqBO, UserSessionReqBO.class);
logger.debug("SecurityInterceptor: call uic.selectUserSession, uid is {}, sessionKey is {}");
if(result == null || result.getUid() == null){
return null;
}
return String.valueOf(result.getUid());
}catch(Exception e){
logger.warn("SecurityInterceptor: getUserSession failed ! uid is {}, sessionKey is {}, error is {}", uid, sessionKey, e);
return null;
}
}
public void setIsDebugEnable(boolean isDebugEnable) {
this.isDebugEnable = isDebugEnable;
}
/**
* 设置不校验的url地址
*/
public void setExcludeUrls(List<String> excludeUrls) {
this.excludeUrls = excludeUrls;
}
public void setCheckSessionMethods(Map<String, Object> validateMethods) {
List<String> methods = (List<String>)validateMethods.get("methods");
if(methods != null) {
this.checkSessionMethods.addAll(methods);
}
}
private String getIP(HttpServletRequest httpServletRequest) {
String ip = httpServletRequest.getHeader("X-Real-IP");
if (StringUtils.isEmpty(ip)) {
ip = httpServletRequest.getRemoteAddr();
}
return ip;
}
public void setLocal(List<String> local) {
this.local = local;
}
public enum SessionTypeEnum {
IPHONE(1,"iphone"),
ANDROID(2, "android"),
WEB(3, "web"),
H5(4, "h5");
private String name;
private int type;
public String getName() {
return name;
}
public int getType() {
return type;
}
SessionTypeEnum(int type, String name) {
this.type = type;
this.name = name;
}
/**
* 是否包含
* @param name
* @return
*/
public static boolean contains(String name) {
if (StringUtils.isEmpty(name)) {
return false;
}
for (SessionTypeEnum e : values()) {
if (name.equals(e.getName())){
return true;
}
}
return false;
}
}
}
... ...
package com.yohoufo.common.interceptor;
import java.lang.reflect.Method;
import java.util.*;
import javax.annotation.Resource;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.yoho.error.event.LogEvent;
import com.yohoufo.common.annotation.IgnoreSignature;
import com.yohoufo.common.exception.SignatureNotMatchException;
import com.yohoufo.common.utils.MD5Util;
import com.yohoufo.common.utils.ServletUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.google.common.collect.ImmutableList;
import com.netflix.config.DynamicPropertyFactory;
import com.yoho.core.common.utils.MD5;
import com.yoho.core.config.ConfigReader;
public class SignatureVerifyInterceptor implements HandlerInterceptor, ApplicationEventPublisherAware {
private final Logger logger = LoggerFactory.getLogger(SignatureVerifyInterceptor.class);
//验签算法
private final static String VERIFY_ARITHMETIC = "HmacSHA256";
//客户端加签请求头
private final static String HTTP_HEADER_VERIFY_DATA = "x-yoho-verify";
//是否调试模式
private boolean isDebugEnable = false;
//这些方法需要校验
private List<String> checkMethods = new LinkedList<>();
@Resource(name = "core-config-reader")
private ConfigReader configReader;
private ApplicationEventPublisher publisher;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
//(1) 验签开关, 控制是否需要验签. 默认需要验证
boolean isSignatureVerifyEnable = configReader.getBoolean("gateway.security.isSignatureVerifyEnable", true);
if(!isSignatureVerifyEnable){
logger.debug("gateway.security.isSignatureVerifyEnable is false");
return true;
}
//(2) 排除掉debug模式
Map<String, String> params = this.getRequestInfo(httpServletRequest);
String method = params.get("method");
String clientType = params.get("client_type");
String appVersion = params.get("app_version");
String udid = params.get("udid");
if (this.isIgnore(httpServletRequest, o, method, clientType, appVersion)) {
return true;
}
//(3) 排除不需要校验的场景
if(this.validateReqParams(httpServletRequest, params)){
return true;
}
//(4) 验证消息签名.
String message = this.getPramsString(params);
String verifyMessage = httpServletRequest.getHeader(HTTP_HEADER_VERIFY_DATA);
logger.debug("verifySignature: message is {}, verifyMessage is {}", message, verifyMessage);
boolean verifyRes = this.verifySignatureNew(udid, message, verifyMessage);
logger.debug("verifySignature: message is {}, verifyMessage is {}, res is {}", message, verifyMessage, verifyRes);
if(!verifyRes){
logger.warn("verifySignature: signature failed, uri is {}, message is {}, verifyMessage is {}, verifyType is {}", httpServletRequest.getRequestURI(), message, verifyMessage, "");
this.verifyFailReport(message, "", verifyMessage, "");
throw new SignatureNotMatchException(); //验证不通过
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
//do nothing
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
private boolean isIgnore(HttpServletRequest request, Object o, String method, String clientType, String appVersion) {
logger.debug("enter isIgnore");
//(1) 配置文件配置为 is_debug_enable 为true,并且请求携带参数debug为XYZ,就放行
if (isDebugEnable && "XYZ".equals(request.getParameter("debug"))) {
logger.debug("isIgnore check debug model");
return true;
}
//(2) 是否含有IgnoreSignature、IgnoreClientSecret和innerApi注解
if(o.getClass().isAssignableFrom(HandlerMethod.class)){
HandlerMethod handlerMethod = (HandlerMethod)o;
//忽略验签接口放行
if(isVerifySignature(handlerMethod, clientType, appVersion)) {
return true;
}
}
//(3) 检查zk中设置的忽略验签接口(线上环境可以动态忽略/取消忽略指定接口)
String ignoreMethods = configReader.getString("gateway.signature.ignoreMethods", "");
if(StringUtils.isEmpty(ignoreMethods)){
return false;
}
List<String> ignoreMethodList = Arrays.asList(ignoreMethods.split(","));
String verifyMethod = StringUtils.isEmpty(method) ? request.getRequestURI().substring(8) : method;
if(StringUtils.isNotEmpty(verifyMethod) && ignoreMethodList.contains(verifyMethod)){
return true;
}
return false;
}
/**
* 验证请求参数
* @param params 请求参数
* @return
*/
private boolean validateReqParams(HttpServletRequest request, Map<String, String> params){
//是否校验全部接口,开关-true:校验全部接口(除含@IgnoreSignature注解接口) 开关-false:只校验核心接口
boolean isVerifyAllMethod = configReader.getBoolean("gateway.signature.isVerifyAllMethod", false);
if(!isVerifyAllMethod){
String method = params.get("method");
//如果请求method在校验包内,则校验
if (StringUtils.isNotEmpty(method) && checkMethods.contains(method)) {
logger.debug("method {} to verify signature", method);
return false;
}
return true;
}
return false;
}
/**
* 获取请求信息: requestParam
*
* @param httpServletRequest
* @return
*/
private Map<String, String> getRequestInfo(HttpServletRequest httpServletRequest) {
return ServletUtils.getRequestParams(httpServletRequest);
}
/**
* 拼接请求参数,k1=v1&k2=v2
* @param reqParams
* @return
*/
private String getPramsString(Map<String, String> reqParams) {
ImmutableList list = ImmutableList.of();
SortedMap<String, String> filtedMap = new TreeMap<>();
for (Map.Entry<String, String> entry : reqParams.entrySet()) {
String k = entry.getKey();
if (!list.contains(k)) {
filtedMap.put(k, entry.getValue());
}
}
//string: k1=v1&k2=v2
List<String> array = new LinkedList<>();
for (Map.Entry<String, String> entry : filtedMap.entrySet()) {
String pair = entry.getKey() + "=" + entry.getValue();
array.add(pair.trim());
}
String paramStr = String.join("&", array);
logger.debug("getRequestInfo: param str is: {}", paramStr);
return paramStr;
}
/**
* 验证签名
* @param udid
* @param message
* @param verifyMessage
* @return
*/
private boolean verifySignatureNew(String udid, String message, String verifyMessage){
//(1) 验证信息为空或者udid为空,直接返回
if(StringUtils.isEmpty(verifyMessage) || StringUtils.isEmpty(udid)){
return false;
}
//(2) 获取秘钥
String md5Salt = DynamicPropertyFactory.getInstance().getStringProperty("gateway.signature.key.salt", "mQyMTMwZjlmZTZmYjY4UjkNmYwZGM0OTk0Y").get();
String encryptStr = udid + md5Salt;
//对udid加盐后二次md5
String signatureKey = MD5.md5(MD5.md5(encryptStr));
//(3) 对消息加签
String signatureRes = this.HmacSHA256(message, signatureKey);
if(verifyMessage.equals(signatureRes)){
return true;
}
logger.warn("verifySignatureNew: signature verify failed: message is {}, verifyMessage is {}, udid is {}, key is {}", message, verifyMessage, udid, signatureKey);
return false;
}
/**
* 签名失败上报事件
* @param message
* @param secretkey
* @param verifyMsg
* @param verifyType
*/
private void verifyFailReport(String message, String secretkey, String verifyMsg, String verifyType){
try{
LogEvent logEvent = new LogEvent.Builder("signatureFail").addArg("message", message).addArg("key", secretkey).addArg("verifyMsg", verifyMsg).addArg("verifyType", verifyType).build();
publisher.publishEvent(logEvent);
}catch (Exception e){
logger.warn("verifyFailReport: report signature verify event faild, message is {}, error is {}",message, e);
}
}
/**
* 使用hmac加密
* @param data
* @param secret
* @return
*/
private String HmacSHA256(String data, String secret){
String res = null;
try{
Mac sha256_HMAC = Mac.getInstance(VERIFY_ARITHMETIC);
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), VERIFY_ARITHMETIC);
sha256_HMAC.init(secret_key);
byte[] encryptByte = sha256_HMAC.doFinal(data.getBytes());
res = MD5Util.byteArrayToHexString(encryptByte);
logger.debug("HmacSHA256: data is {}, secret is {}, res is {}", data, secret, res);
}catch (Exception e){
logger.warn("HmacSHA256: hmac failed, data is {}, error is {}", data, e);
}
return res;
}
//含忽略签名校验注解的接口,不验证
private boolean isVerifySignature(HandlerMethod handlerMethod, String clientType, String appVersion){
Method bridgedMethod =handlerMethod.getMethod();
if(bridgedMethod.isAnnotationPresent(IgnoreSignature.class)){
IgnoreSignature is = bridgedMethod.getAnnotation(IgnoreSignature.class);
String ignoreClient = is.clientType();
String ignoreVersion = is.version();
//(1) 忽略验证客户端和版本都没配置,默认全部忽略
if(StringUtils.isEmpty(ignoreClient) && StringUtils.isEmpty(ignoreVersion)){
return true;
}
//(2) 指定忽略验证客户端包含传入客户端,则不验证
if(StringUtils.isNotEmpty(ignoreClient) && StringUtils.isNotEmpty(clientType)
&& Arrays.asList(ignoreClient.split(",")).contains(clientType)){
return true;
}
//(3) 传入客户端版本低于指定忽略验签的版本,则不验签
if(StringUtils.isNotEmpty(ignoreVersion)){
if(StringUtils.isEmpty(appVersion)){
return true;
}
if(ignoreVersion.compareTo(appVersion) > 0){
return true;
}
}
}
return false;
}
public void setIsDebugEnable(boolean isDebugEnable) {
this.isDebugEnable = isDebugEnable;
}
public void setCheckMethods(Map<String, Object> checkMethods) {
List<String> methods = (List<String>)checkMethods.get("methods");
if(methods != null) {
this.checkMethods.addAll(methods);
}
}
}
... ...
package com.yohoufo.common.utils;
import java.security.MessageDigest;
public class MD5Util {
public static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
public static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}
... ...
package com.yohoufo.common.utils;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* utils
* Created by chunhua.zhang@yoho.cn on 2016/3/24.
*/
public class ServletUtils {
public final static String getServiceName(HttpServletRequest request) {
//如果直接请求/gateway 或者/,则取method请求参数作为method
final List<String> CONTEXTS = Lists.newArrayList("/", "/gateway", "/gateway/");
String requestUrl = request.getRequestURI();
String service = requestUrl;
if (CONTEXTS.contains(requestUrl)) {
service = request.getParameter("method");
}
return service;
}
public final static Map<String, String> getRequestParams(HttpServletRequest httpServletRequest) {
Map<String, String> map = new HashMap<>();
Enumeration paramNames = httpServletRequest.getParameterNames();
while (paramNames.hasMoreElements()) {
String key = (String) paramNames.nextElement();
String value = httpServletRequest.getParameter(key);
map.put(key, value);
}
return map;
}
public static String getRemoteIP( final HttpServletRequest httpServletRequest ) {
String ip = httpServletRequest.getHeader( "X-Forwarded-For" );
if ( StringUtils.isEmpty( ip ) ) {
ip = httpServletRequest.getRemoteAddr();
}
return ip;
}
}
... ...
... ... @@ -23,7 +23,7 @@
<!-- ******************** interceptors ******************** -->
<bean id="securityInterceptor" class="com.yoho.gateway.common.interceptor.SecurityInterceptor">
<bean id="securityInterceptor" class="com.yohoufo.common.interceptor.SecurityInterceptor">
<property name="isDebugEnable" value="${is_debug_enable:false}" />
<property name="excludeUrls">
<list>
... ... @@ -37,49 +37,16 @@
<property name="local">
<value>/service_control</value>
</property>
<property name="checkSessionShopsMethods">
<list>
<value>app.shopInbox.getList</value>
<value>app.sellerShop.sellType</value>
<value>app.shops.shopbrandrank</value>
<value>app.shops.shopbusinessoverview</value>
<value>app.shopInbox.getShopInboxTotal</value>
<value>app.shops.sales</value>
<value>app.shops.refund</value>
<value>app.shops.storagestatistics</value>
<value>app.shops.storagein</value>
<value>app.shops.storageout</value>
<value>app.purchase.delivery</value>
<value>app.express.getExpressList</value>
<value>app.purchase.queryBySupplierId</value>
<value>app.shops.accountbalance</value>
<value>app.shopInbox.batchSetIsRead</value>
<value>app.purchase.stockOut</value>
<value>app.purchase.list</value>
<value>app.shops.checkAppVersion</value>
<value>app.followPrice.list</value>
<value>app.followPrice.getFrontPageStatisticsData</value>
<value>app.followPrice.changeVipStatus</value>
<value>app.followPrice.follow</value>
</list>
</property>
<property name="checkSessionMethods" ref="validateMethodMap" />
</bean>
<bean id="signatureVerifyInterceptor" class="com.yoho.gateway.common.interceptor.SignatureVerifyInterceptor">
<bean id="signatureVerifyInterceptor" class="com.yohoufo.common.interceptor.SignatureVerifyInterceptor">
<property name="isDebugEnable" value="${is_debug_enable:false}" />
<property name="checkMethods" ref="validateMethodMap"/>
</bean>
<bean id="httpRequestInterceptor" class="com.yoho.gateway.common.interceptor.HttpRequestInterceptor" />
<bean id="localIpInterceptor" class="com.yoho.gateway.common.interceptor.RemoteIPInterceptor" />
<bean id="accessStatistics" class="com.yoho.gateway.common.interceptor.AccessStatistics" />
<bean id="productBrowserStatistic" class="com.yoho.gateway.user.controller.browse.ProductBrowserStatistic" />
<bean id="accessLogInterceptor" class="com.yoho.gateway.common.interceptor.AccessLogInterceptor" />
<bean id="innerApiInterceptor" class="com.yoho.gateway.common.interceptor.InnerApiInterceptor">
<property name="enableDebug" value="${is_debug_enable:false}" />
</bean>
<bean id="localIpInterceptor" class="com.yohoufo.common.interceptor.RemoteIPInterceptor" />
<!-- end -->
<bean id="redisGwCacheClient" class="com.yoho.gateway.common.cache.RedisGwCacheClient">
<bean id="redisGwCacheClient" class="com.yohoufo.common.cache.RedisGwCacheClient">
</bean>
<bean id="consultConfigMap" class="org.springframework.beans.factory.config.YamlMapFactoryBean">
<property name="resources">
... ...
package com.yohoufo.product.dal;
import com.yohoufo.product.dal.model.Brand;
import java.util.List;
public interface BrandMapper {
int deleteByPrimaryKey(Short id);
int insert(Brand record);
Brand selectByPrimaryKey(Short id);
List<Brand> selectAll();
int updateByPrimaryKey(Brand record);
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal;
import com.yohoufo.product.dal.model.BrandSeries;
import java.util.List;
public interface BrandSeriesMapper {
int deleteByPrimaryKey(Short id);
int insert(BrandSeries record);
BrandSeries selectByPrimaryKey(Short id);
List<BrandSeries> selectAll();
int updateByPrimaryKey(BrandSeries record);
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal;
import com.yohoufo.product.dal.model.GoodsImages;
import java.util.List;
public interface GoodsImagesMapper {
int deleteByPrimaryKey(Integer id);
int insert(GoodsImages record);
GoodsImages selectByPrimaryKey(Integer id);
List<GoodsImages> selectAll();
int updateByPrimaryKey(GoodsImages record);
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal;
import com.yohoufo.product.dal.model.Goods;
import java.util.List;
public interface GoodsMapper {
int deleteByPrimaryKey(Integer id);
int insert(Goods record);
Goods selectByPrimaryKey(Integer id);
List<Goods> selectAll();
int updateByPrimaryKey(Goods record);
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal;
import com.yohoufo.product.dal.model.ProductColor;
import java.util.List;
public interface ProductColorMapper {
int deleteByPrimaryKey(Short id);
int insert(ProductColor record);
ProductColor selectByPrimaryKey(Short id);
List<ProductColor> selectAll();
int updateByPrimaryKey(ProductColor record);
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal;
import com.yohoufo.product.dal.model.Product;
import java.util.List;
public interface ProductMapper {
int deleteByPrimaryKey(Integer id);
int insert(Product record);
Product selectByPrimaryKey(Integer id);
List<Product> selectAll();
int updateByPrimaryKey(Product record);
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal;
import com.yohoufo.product.dal.model.ProductPoolDetail;
import java.util.List;
public interface ProductPoolDetailMapper {
int deleteByPrimaryKey(Integer id);
int insert(ProductPoolDetail record);
ProductPoolDetail selectByPrimaryKey(Integer id);
List<ProductPoolDetail> selectAll();
int updateByPrimaryKey(ProductPoolDetail record);
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal;
import com.yohoufo.product.dal.model.ProductPool;
import java.util.List;
public interface ProductPoolMapper {
int deleteByPrimaryKey(Integer id);
int insert(ProductPool record);
ProductPool selectByPrimaryKey(Integer id);
List<ProductPool> selectAll();
int updateByPrimaryKey(ProductPool record);
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal;
import com.yohoufo.product.dal.model.ProductSort;
import java.util.List;
public interface ProductSortMapper {
int deleteByPrimaryKey(Short id);
int insert(ProductSort record);
ProductSort selectByPrimaryKey(Short id);
List<ProductSort> selectAll();
int updateByPrimaryKey(ProductSort record);
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal;
import com.yohoufo.product.dal.model.Size;
import java.util.List;
public interface SizeMapper {
int deleteByPrimaryKey(Short id);
int insert(Size record);
Size selectByPrimaryKey(Short id);
List<Size> selectAll();
int updateByPrimaryKey(Size record);
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal;
import com.yohoufo.product.dal.model.Storage;
import java.util.List;
public interface StorageMapper {
int deleteByPrimaryKey(Integer id);
int insert(Storage record);
Storage selectByPrimaryKey(Integer id);
List<Storage> selectAll();
int updateByPrimaryKey(Storage record);
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal;
import com.yohoufo.product.dal.model.StoragePrice;
import java.util.List;
public interface StoragePriceMapper {
int deleteByPrimaryKey(Integer id);
int insert(StoragePrice record);
StoragePrice selectByPrimaryKey(Integer id);
List<StoragePrice> selectAll();
int updateByPrimaryKey(StoragePrice record);
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal.model;
public class Brand {
private Short id;
private String brandName;
private String brandNameEn;
private String brandLog;
private String brandSearch;
private Byte status;
private Integer createTime;
private Integer editTime;
private Integer editPid;
public Short getId() {
return id;
}
public void setId(Short id) {
this.id = id;
}
public String getBrandName() {
return brandName;
}
public void setBrandName(String brandName) {
this.brandName = brandName;
}
public String getBrandNameEn() {
return brandNameEn;
}
public void setBrandNameEn(String brandNameEn) {
this.brandNameEn = brandNameEn;
}
public String getBrandLog() {
return brandLog;
}
public void setBrandLog(String brandLog) {
this.brandLog = brandLog;
}
public String getBrandSearch() {
return brandSearch;
}
public void setBrandSearch(String brandSearch) {
this.brandSearch = brandSearch;
}
public Byte getStatus() {
return status;
}
public void setStatus(Byte status) {
this.status = status;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
public Integer getEditTime() {
return editTime;
}
public void setEditTime(Integer editTime) {
this.editTime = editTime;
}
public Integer getEditPid() {
return editPid;
}
public void setEditPid(Integer editPid) {
this.editPid = editPid;
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal.model;
public class BrandSeries {
private Short id;
private String seriesName;
private String seriesSearch;
private Byte status;
private Integer createTime;
private Integer updateTime;
private Integer brandId;
public Short getId() {
return id;
}
public void setId(Short id) {
this.id = id;
}
public String getSeriesName() {
return seriesName;
}
public void setSeriesName(String seriesName) {
this.seriesName = seriesName;
}
public String getSeriesSearch() {
return seriesSearch;
}
public void setSeriesSearch(String seriesSearch) {
this.seriesSearch = seriesSearch;
}
public Byte getStatus() {
return status;
}
public void setStatus(Byte status) {
this.status = status;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
public Integer getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Integer updateTime) {
this.updateTime = updateTime;
}
public Integer getBrandId() {
return brandId;
}
public void setBrandId(Integer brandId) {
this.brandId = brandId;
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal.model;
public class Goods {
private Integer id;
private Integer productId;
private Short colorId;
private String colorName;
private String goodsName;
private String colorImage;
private String isDefault;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public Short getColorId() {
return colorId;
}
public void setColorId(Short colorId) {
this.colorId = colorId;
}
public String getColorName() {
return colorName;
}
public void setColorName(String colorName) {
this.colorName = colorName;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
public String getColorImage() {
return colorImage;
}
public void setColorImage(String colorImage) {
this.colorImage = colorImage;
}
public String getIsDefault() {
return isDefault;
}
public void setIsDefault(String isDefault) {
this.isDefault = isDefault;
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal.model;
public class GoodsImages {
private Integer id;
private Integer goodsId;
private Integer productId;
private String isDefault;
private String imageUrl;
private Byte orderBy;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getGoodsId() {
return goodsId;
}
public void setGoodsId(Integer goodsId) {
this.goodsId = goodsId;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public String getIsDefault() {
return isDefault;
}
public void setIsDefault(String isDefault) {
this.isDefault = isDefault;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public Byte getOrderBy() {
return orderBy;
}
public void setOrderBy(Byte orderBy) {
this.orderBy = orderBy;
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal.model;
import java.math.BigDecimal;
public class Product {
private Integer id;
private String productName;
private String productCode;
private Short sortId;
private Short brandId;
private Short seriesId;
private String gender;
private Integer saleTime;
private BigDecimal minPrice;
private BigDecimal maxPrice;
private Integer createTime;
private Integer updateTime;
private Integer shelveTime;
private Integer editTime;
private Byte shelveStatus;
private Integer storage;
private String keyWords;
private Byte delStatus;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductCode() {
return productCode;
}
public void setProductCode(String productCode) {
this.productCode = productCode;
}
public Short getSortId() {
return sortId;
}
public void setSortId(Short sortId) {
this.sortId = sortId;
}
public Short getBrandId() {
return brandId;
}
public void setBrandId(Short brandId) {
this.brandId = brandId;
}
public Short getSeriesId() {
return seriesId;
}
public void setSeriesId(Short seriesId) {
this.seriesId = seriesId;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getSaleTime() {
return saleTime;
}
public void setSaleTime(Integer saleTime) {
this.saleTime = saleTime;
}
public BigDecimal getMinPrice() {
return minPrice;
}
public void setMinPrice(BigDecimal minPrice) {
this.minPrice = minPrice;
}
public BigDecimal getMaxPrice() {
return maxPrice;
}
public void setMaxPrice(BigDecimal maxPrice) {
this.maxPrice = maxPrice;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
public Integer getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Integer updateTime) {
this.updateTime = updateTime;
}
public Integer getShelveTime() {
return shelveTime;
}
public void setShelveTime(Integer shelveTime) {
this.shelveTime = shelveTime;
}
public Integer getEditTime() {
return editTime;
}
public void setEditTime(Integer editTime) {
this.editTime = editTime;
}
public Byte getShelveStatus() {
return shelveStatus;
}
public void setShelveStatus(Byte shelveStatus) {
this.shelveStatus = shelveStatus;
}
public Integer getStorage() {
return storage;
}
public void setStorage(Integer storage) {
this.storage = storage;
}
public String getKeyWords() {
return keyWords;
}
public void setKeyWords(String keyWords) {
this.keyWords = keyWords;
}
public Byte getDelStatus() {
return delStatus;
}
public void setDelStatus(Byte delStatus) {
this.delStatus = delStatus;
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal.model;
public class ProductColor {
private Short id;
private String colorName;
private String colorCode;
private String colorValue;
private Integer createTime;
private Integer updateTime;
public Short getId() {
return id;
}
public void setId(Short id) {
this.id = id;
}
public String getColorName() {
return colorName;
}
public void setColorName(String colorName) {
this.colorName = colorName;
}
public String getColorCode() {
return colorCode;
}
public void setColorCode(String colorCode) {
this.colorCode = colorCode;
}
public String getColorValue() {
return colorValue;
}
public void setColorValue(String colorValue) {
this.colorValue = colorValue;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
public Integer getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Integer updateTime) {
this.updateTime = updateTime;
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal.model;
public class ProductPool {
private Integer id;
private String poolName;
private Integer createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPoolName() {
return poolName;
}
public void setPoolName(String poolName) {
this.poolName = poolName;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal.model;
public class ProductPoolDetail {
private Integer id;
private Integer poolId;
private Integer productId;
private Integer createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getPoolId() {
return poolId;
}
public void setPoolId(Integer poolId) {
this.poolId = poolId;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal.model;
public class ProductSort {
private Short id;
private String sortName;
private Byte status;
private Integer createTime;
private Integer updateTime;
public Short getId() {
return id;
}
public void setId(Short id) {
this.id = id;
}
public String getSortName() {
return sortName;
}
public void setSortName(String sortName) {
this.sortName = sortName;
}
public Byte getStatus() {
return status;
}
public void setStatus(Byte status) {
this.status = status;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
public Integer getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Integer updateTime) {
this.updateTime = updateTime;
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal.model;
public class Size {
private Short id;
private String sizeName;
private Short sortId;
private Integer orderBy;
private Integer createTime;
private Integer updateTime;
public Short getId() {
return id;
}
public void setId(Short id) {
this.id = id;
}
public String getSizeName() {
return sizeName;
}
public void setSizeName(String sizeName) {
this.sizeName = sizeName;
}
public Short getSortId() {
return sortId;
}
public void setSortId(Short sortId) {
this.sortId = sortId;
}
public Integer getOrderBy() {
return orderBy;
}
public void setOrderBy(Integer orderBy) {
this.orderBy = orderBy;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
public Integer getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Integer updateTime) {
this.updateTime = updateTime;
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal.model;
public class Storage {
private Integer id;
private Integer productId;
private Integer goodsId;
private Short sizeId;
private Integer storageNum;
private Integer updateTime;
private Integer createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public Integer getGoodsId() {
return goodsId;
}
public void setGoodsId(Integer goodsId) {
this.goodsId = goodsId;
}
public Short getSizeId() {
return sizeId;
}
public void setSizeId(Short sizeId) {
this.sizeId = sizeId;
}
public Integer getStorageNum() {
return storageNum;
}
public void setStorageNum(Integer storageNum) {
this.storageNum = storageNum;
}
public Integer getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Integer updateTime) {
this.updateTime = updateTime;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.dal.model;
import java.math.BigDecimal;
public class StoragePrice {
private Integer id;
private Integer productId;
private Integer goodsId;
private Short storageId;
private Integer depotNum;
private Integer sellerUid;
private BigDecimal price;
private Byte status;
private Integer updateTime;
private Integer createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public Integer getGoodsId() {
return goodsId;
}
public void setGoodsId(Integer goodsId) {
this.goodsId = goodsId;
}
public Short getStorageId() {
return storageId;
}
public void setStorageId(Short storageId) {
this.storageId = storageId;
}
public Integer getDepotNum() {
return depotNum;
}
public void setDepotNum(Integer depotNum) {
this.depotNum = depotNum;
}
public Integer getSellerUid() {
return sellerUid;
}
public void setSellerUid(Integer sellerUid) {
this.sellerUid = sellerUid;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Byte getStatus() {
return status;
}
public void setStatus(Byte status) {
this.status = status;
}
public Integer getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Integer updateTime) {
this.updateTime = updateTime;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
}
\ No newline at end of file
... ...
<?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 namespace="com.yohobuy.platform.dal.product.ActivityTagMapper">
<resultMap id="BaseResultMap" type="com.yohobuy.platform.dal.product.model.ActivityTag">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="activity_name" jdbcType="VARCHAR" property="activityName" />
<result column="tag_url" jdbcType="VARCHAR" property="tagUrl" />
<result column="status" jdbcType="INTEGER" property="status" />
<result column="begin_time" jdbcType="INTEGER" property="beginTime" />
<result column="end_time" jdbcType="INTEGER" property="endTime" />
<result column="create_time" jdbcType="INTEGER" property="createTime" />
<result column="update_time" jdbcType="INTEGER" property="updateTime" />
<result column="edit_user" jdbcType="VARCHAR" property="editUser" />
<result column="pool_id" jdbcType="VARCHAR" property="poolId" />
<result column="before_img_url" jdbcType="VARCHAR" property="beforeImgUrl" />
<result column="after_img_url" jdbcType="VARCHAR" property="afterImgUrl" />
</resultMap>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from customize_tag
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.yohobuy.platform.dal.product.model.ActivityTag">
insert into customize_tag (id, activity_name, tag_url,
status, begin_time, end_time,
create_time, update_time, edit_user,
pool_id, before_img_url, after_img_url)
values (#{id,jdbcType=INTEGER}, #{activityName,jdbcType=VARCHAR}, #{tagUrl,jdbcType=VARCHAR},
#{status,jdbcType=INTEGER}, #{beginTime,jdbcType=INTEGER}, #{endTime,jdbcType=INTEGER},
#{createTime,jdbcType=INTEGER}, #{updateTime,jdbcType=INTEGER}, #{editUser,jdbcType=VARCHAR},
#{poolId,jdbcType=VARCHAR}, #{beforeImgUrl,jdbcType=VARCHAR}, #{afterImgUrl,jdbcType=VARCHAR})
</insert>
<update id="updateByPrimaryKey" parameterType="com.yohobuy.platform.dal.product.model.ActivityTag">
update customize_tag
set
update_time = #{updateTime,jdbcType=INTEGER}
<if test="activityName != null">
,activity_name = #{activityName,jdbcType=VARCHAR}
</if>
<if test="tagUrl != null">
,tag_url = #{tagUrl,jdbcType=VARCHAR}
</if>
<if test="status != null">
,status = #{status,jdbcType=INTEGER}
</if>
<if test="beginTime != null">
,begin_time = #{beginTime,jdbcType=INTEGER}
</if>
<if test="endTime != null">
,end_time = #{endTime,jdbcType=INTEGER}
</if>
<if test="editUser != null">
,edit_user = #{editUser,jdbcType=VARCHAR}
</if>
<if test="poolId != null">
,pool_id = #{poolId,jdbcType=VARCHAR}
</if>
<if test="beforeImgUrl != null">
,before_img_url = #{beforeImgUrl,jdbcType=VARCHAR}
</if>
<if test="afterImgUrl != null">
,after_img_url = #{afterImgUrl,jdbcType=VARCHAR}
</if>
where id = #{id,jdbcType=INTEGER}
</update>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select id, activity_name, tag_url, status, begin_time, end_time, create_time, update_time,
edit_user, pool_id, before_img_url, after_img_url
from customize_tag
where id = #{id,jdbcType=INTEGER}
</select>
<select id="selectAll" resultMap="BaseResultMap">
select id, activity_name, tag_url, status, begin_time, end_time, create_time, update_time,
edit_user, pool_id, before_img_url, after_img_url
from customize_tag
</select>
<sql id="selectCondition">
<if test="activityName != null and activityName !=''">
and activity_name like CONCAT('%',#{activityName,jdbcType=VARCHAR},'%')
</if>
<if test="status != null and status !=0 ">
and status = #{status,jdbcType=INTEGER}
</if>
<if test="timeStatus != null">
<if test="timeStatus == 1">
and begin_time &gt; UNIX_TIMESTAMP()
</if>
<if test="timeStatus == 2">
and begin_time &lt;= UNIX_TIMESTAMP() and UNIX_TIMESTAMP() &lt;= end_time
</if>
<if test="timeStatus == 3">
and end_time &lt; UNIX_TIMESTAMP()
</if>
</if>
<if test="beginTime != null">
and begin_time &gt;= #{beginTime,jdbcType=INTEGER}
</if>
<if test="endTime != null">
and begin_time &lt;= #{endTime,jdbcType=INTEGER}
</if>
</sql>
<select id="selectProductPromotionTagList" resultMap="BaseResultMap" parameterType="com.yohobuy.platform.model.product.req.ProductPromotionTagReq">
select id, activity_name, tag_url, status, begin_time, end_time, create_time, update_time,
edit_user, pool_id, before_img_url, after_img_url
from customize_tag
where 1=1
<include refid="selectCondition" />
limit #{start,jdbcType=INTEGER}, #{rows,jdbcType=INTEGER}
</select>
<select id="selectProductPromotionTagCount" resultType="java.lang.Integer" parameterType="com.yohobuy.platform.model.product.req.ProductPromotionTagReq">
select count(*) from customize_tag
where 1=1
<include refid="selectCondition" />
</select>
<delete id="deleteProductCustomizeTag" parameterType="java.lang.Integer">
delete from customize_tag where id = #{id,jdbcType=INTEGER}
</delete>
</mapper>
\ No newline at end of file
<?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 namespace="com.yohobuy.platform.dal.product.BaseGoodsMapper" >
<resultMap id="BaseResultMap" type="com.yohobuy.platform.dal.product.model.BaseGoods" >
<id column="product_skc" property="productSkc" jdbcType="INTEGER" />
<result column="product_skn" property="productSkn" jdbcType="INTEGER" />
<result column="goods_name" property="goodsName" jdbcType="VARCHAR" />
<result column="factory_goods_name" property="factoryGoodsName" jdbcType="VARCHAR" />
<result column="goods_color_image" property="goodsColorImage" jdbcType="VARCHAR" />
<result column="color_id" property="colorId" jdbcType="INTEGER" />
<result column="create_time" property="createTime" jdbcType="INTEGER" />
<result column="factory_code" property="factoryCode" jdbcType="VARCHAR" />
<result column="update_time" property="updateTime" jdbcType="INTEGER" />
</resultMap>
<sql id="Base_Column_List" >
product_skc, product_skn, goods_name, factory_goods_name, goods_color_image, color_id, create_time, factory_code,
update_time
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from base_goods
where product_skc = #{productSkc,jdbcType=INTEGER}
</select>
<select id="selectBatchBySkn" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from base_goods
where product_skn in
<foreach collection="productSkns" item="item" index="index"
separator="," open="(" close=")">
#{item}
</foreach>
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
delete from base_goods
where product_skc = #{productSkc,jdbcType=INTEGER}
</delete>
<delete id="deleteByProductSkn" parameterType="java.lang.Integer" >
delete from base_goods
where product_skn = #{productSkn,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.yohobuy.platform.dal.product.model.BaseGoods" >
<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="productSkc">
SELECT LAST_INSERT_ID() AS productSkc
</selectKey>
insert into base_goods
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="productSkc != null" >
product_skc,
</if>
<if test="productSkn != null" >
product_skn,
</if>
<if test="goodsName != null" >
goods_name,
</if>
<if test="factoryGoodsName != null" >
factory_goods_name,
</if>
<if test="goodsColorImage != null" >
goods_color_image,
</if>
<if test="colorId != null" >
color_id,
</if>
<if test="createTime != null" >
create_time,
</if>
<if test="factoryCode != null" >
factory_code,
</if>
<if test="updateTime != null" >
update_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="productSkc != null" >
#{productSkc,jdbcType=INTEGER},
</if>
<if test="productSkn != null" >
#{productSkn,jdbcType=INTEGER},
</if>
<if test="goodsName != null" >
#{goodsName,jdbcType=VARCHAR},
</if>
<if test="factoryGoodsName != null" >
#{factoryGoodsName,jdbcType=VARCHAR},
</if>
<if test="goodsColorImage != null" >
#{goodsColorImage,jdbcType=VARCHAR},
</if>
<if test="colorId != null" >
#{colorId,jdbcType=INTEGER},
</if>
<if test="createTime != null" >
#{createTime,jdbcType=INTEGER},
</if>
<if test="factoryCode != null" >
#{factoryCode,jdbcType=VARCHAR},
</if>
<if test="updateTime != null" >
#{updateTime,jdbcType=INTEGER},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.yohobuy.platform.dal.product.model.BaseGoods" >
update base_goods
<set >
<if test="productSkn != null" >
product_skn = #{productSkn,jdbcType=INTEGER},
</if>
<if test="goodsName != null" >
goods_name = #{goodsName,jdbcType=VARCHAR},
</if>
<if test="factoryGoodsName != null" >
factory_goods_name = #{factoryGoodsName,jdbcType=VARCHAR},
</if>
<if test="goodsColorImage != null" >
goods_color_image = #{goodsColorImage,jdbcType=VARCHAR},
</if>
<if test="colorId != null" >
color_id = #{colorId,jdbcType=INTEGER},
</if>
<if test="factoryCode != null" >
factory_code = #{factoryCode,jdbcType=VARCHAR},
</if>
<if test="updateTime != null" >
update_time = #{updateTime,jdbcType=INTEGER},
</if>
</set>
where product_skc = #{productSkc,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.yohobuy.platform.dal.product.model.BaseGoods" >
update base_goods
set product_skn = #{productSkn,jdbcType=INTEGER},
goods_name = #{goodsName,jdbcType=VARCHAR},
factory_goods_name = #{factoryGoodsName,jdbcType=VARCHAR},
goods_color_image = #{goodsColorImage,jdbcType=VARCHAR},
color_id = #{colorId,jdbcType=INTEGER},
factory_code = #{factoryCode,jdbcType=VARCHAR},
update_time = #{updateTime,jdbcType=INTEGER}
where product_skc = #{productSkc,jdbcType=INTEGER}
</update>
<update id="updateBatchSkcFactoryCode">
update base_goods
<trim prefix="set" suffixOverrides=",">
<trim prefix="factory_code =case" suffix="end,">
<foreach collection="goodsList" item="item" index="index">
when product_skc=#{item.productSkc,jdbcType=INTEGER} then #{item.factoryCode,jdbcType=VARCHAR}
</foreach>
</trim>
</trim>
where
<foreach collection="goodsList" separator="or" item="item" index="index" >
product_skc=#{item.productSkc}
</foreach>
</update>
<select id="selectBatchBySkcs" resultMap="BaseResultMap">
select g.product_skc
from base_goods g
<if test="shopId != null" >
, base_product p
</if>
where
<if test="shopId != null" >
g.product_skn = p.product_skn
AND p.shop_id = #{shopId, jdbcType=INTEGER}
and p.del_status = 0
</if>
<foreach collection="skcSets" separator="or" item="item" index="index" open="(" close=")">
g.product_skc = #{item}
</foreach>
</select>
<update id="updateBatchSellerGoods">
update base_goods
<trim prefix="set" suffixOverrides=",">
<trim prefix="factory_code =case" suffix="end,">
<foreach collection="goodsList" item="item" index="index">
when product_skc = #{item.productSkc, jdbcType=INTEGER} then #{item.factoryCode, jdbcType=VARCHAR}
</foreach>
</trim>
<trim prefix="factory_goods_name =case" suffix="end,">
<foreach collection="goodsList" item="item" index="index">
when product_skc = #{item.productSkc, jdbcType=INTEGER} then #{item.factoryGoodsName, jdbcType=VARCHAR}
</foreach>
</trim>
</trim>
where
<foreach collection="goodsList" separator="or" item="item" index="index" >
product_skc = #{item.productSkc}
</foreach>
</update>
</mapper>
\ No newline at end of file