diff --git a/mallinkPublicApi/pom.xml b/mallinkPublicApi/pom.xml new file mode 100644 index 000000000..14ec5fb04 --- /dev/null +++ b/mallinkPublicApi/pom.xml @@ -0,0 +1,154 @@ + + + 4.0.0 + + mallink + com.iformall + 1.0 + + + mallinkPublicApi + + + + com.iformall + mallinkService + 1.0 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + ZIP + + antlr, + cn.afterturn, + ch.qos.logback, + com.alibaba, + com.amazonaws, + com.baomidou, + com.mchange, + com.fasterxml.jackson.core, + com.fasterxml.jackson.dataformat, + com.fasterxml.jackson.datatype, + com.fasterxml.jackson.module, + com.fasterxml.uuid, + com.fasterxml, + com.github.axet, + com.github.jsqlparser, + com.github.pagehelper, + com.github.ulisesbocchio, + com.github.virtuald, + com.google.code.findbugs, + com.google.code.gson, + com.google.errorprone, + com.google.guava, + com.google.protobuf, + com.google.zxing, + com.jayway.jsonpath, + com.jhlabs, + com.puppycrawl.tools, + com.rabbitmq, + com.squareup.okhttp3, + com.squareup.okio, + com.sun, + com.sun.mail, + com.thoughtworks.xstream, + com.zaxxer, + commons-beanutils, + commons-cli, + commons-codec, + commons-collections, + commons-fileupload, + commons-io, + commons-logging, + io.lettuce, + io.netty, + io.projectreactor, + io.springfox, + io.swagger, + io.undertow, + javax.activation, + javax.annotation, + javax.mail, + javax.persistence, + javax.servlet, + javax.validation, + javax.xml.bind, + javax.xml.soap, + javax.xml.ws, + joda-time, + junit, + mysql, + net.bytebuddy, + net.minidev, + net.sf.dozer, + net.sf.saxon, + ognl, + org.antlr, + org.apache.commons, + org.apache.httpcomponents, + org.apache.logging.log4j, + org.apache.poi, + org.apache.poi.wso2, + org.apache.rocketmq, + org.apache.shiro, + org.apache.tomcat.embed, + org.apache.xmlbeans, + org.aspectj, + org.assertj, + org.bouncycastle, + org.checkerframework, + org.codehaus.mojo, + org.crazycake, + org.dom4j, + org.flowable, + org.flywaydb, + org.glassfish, + org.hibernate.validator, + org.jasypt, + org.javassist, + org.jboss.logging, + org.jboss.spec.javax.annotation, + org.jboss.spec.javax.websocket, + org.jboss.xnio, + org.jdom, + org.jodd, + org.jvnet.mimepull, + org.jvnet.staxex, + org.mapstruct, + org.mockito, + org.mybatis, + org.mybatis.generator, + org.mybatis.spring.boot, + org.ow2.asm, + org.projectlombok, + org.quartz-scheduler, + org.reactivestreams, + org.reflections, + org.rocketmq.spring.boot, + org.slf4j, + org.springframework, + org.springframework.amqp, + org.springframework.boot, + org.springframework.data, + org.springframework.retry, + org.springframework.ws, + org.yaml, + redis.clients, + software.amazon.ion, + tk.mybatis, + xmlpull, + xpp3 + + + + + + \ No newline at end of file diff --git a/mallinkPublicApi/src/main/java/com/iformall/PublicApiApplication.java b/mallinkPublicApi/src/main/java/com/iformall/PublicApiApplication.java new file mode 100644 index 000000000..bb2e930a5 --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/PublicApiApplication.java @@ -0,0 +1,60 @@ +package com.iformall; + +import org.mybatis.spring.annotation.MapperScan; +import org.rocketmq.starter.annotation.EnableRocketMQ; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties; + +/** + * @author chenkx + * @date 2017-12-26 + */ +@SpringBootApplication +@MapperScan(basePackages = {"com.iformall.mapper"}) +@EnableEncryptableProperties +@EnableRocketMQ +@EnableAspectJAutoProxy(exposeProxy = true) +public class PublicApiApplication { + + @Value("${fm.exception}") + private boolean fmException; + + @Value("${fm.exception_emails}") + private String fmExceptionEmails; + + @Value("${fm.open}") + private boolean fmOpen; + + @Value("${fm.upload_dir}") + private String uploadDir; + + @Bean + public boolean isFmException() { + return fmException; + } + + @Bean + public String fmExceptionEmails() { + return fmExceptionEmails; + } + + @Bean + public boolean isFmOpen() { + return fmOpen; + } + + @Bean + public String fmUploadDir() { + return uploadDir; + } + + + public static void main(String[] args) { + SpringApplication.run(PublicApiApplication.class, args); + } +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/annotation/RedisCache.java b/mallinkPublicApi/src/main/java/com/iformall/annotation/RedisCache.java new file mode 100644 index 000000000..185736606 --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/annotation/RedisCache.java @@ -0,0 +1,53 @@ +package com.iformall.annotation; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + + +/** + * - /api/wxCouponChannel/change + * - /api/user/userinfo + * - /api/wxBusiness/listAll + * - /api/wxCampaign/list + * - /api/mall/mallInfo + * - /api/mall/getAppIcon + * - /api/mall/getWeapNote + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface RedisCache { + + /** + * 缓存key的名称 + * @return + */ + String key() default ""; + + /** + * 自动生成key + * @return + */ + boolean autoKey() default true; + + /** + * 参数md5,解决key过长问题 + * @return + */ + boolean md5() default true ; + + /** + * key 过期日期默认60秒 + * @return + */ + int expireTime() default 60; + + /** + * 时间单位默认为秒 + * @return + */ + TimeUnit dateUnit() default TimeUnit.SECONDS; +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/aop/RedisCacheAspect.java b/mallinkPublicApi/src/main/java/com/iformall/aop/RedisCacheAspect.java new file mode 100644 index 000000000..f001c7912 --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/aop/RedisCacheAspect.java @@ -0,0 +1,161 @@ +package com.iformall.aop; + + +import com.alibaba.fastjson.JSONObject; +import com.iformall.annotation.RedisCache; +import com.iformall.common.ErrorCode; +import com.iformall.domain.po.WxCUser; +import com.iformall.domain.po.base.BaseCUserEntity; +import com.iformall.exception.MallinkException; +import com.iformall.service.CUserTokenService; +import com.iformall.utils.HashUtil; +import com.iformall.utils.RedisLock; +import lombok.extern.slf4j.Slf4j; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Objects; + +@Slf4j +@Aspect +@Component +public class RedisCacheAspect { + + /** + * 参数分隔符 param1|param2|param3 + **/ + private static final String DELIMITER_PARAMS = "|"; + + /** + * key 分隔符 + */ + private static final String DELIMITER_KEY = ":"; + + @Autowired + private StringRedisTemplate stringRedisTemplate; + + @Autowired + RedisLock redisLock; + + @Autowired + private CUserTokenService cUserTokenService; + + @Pointcut("@annotation(com.iformall.annotation.RedisCache)") + public void cacheAspect() { + } + + @Around("cacheAspect()") + public Object round(ProceedingJoinPoint jp) throws Throwable { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + // 查询token信息 + //从header中获取token + String token = request.getHeader("token"); + //如果header中不存在token,则从参数中获取token + if (StringUtils.isBlank(token)) { + token = request.getParameter("token"); + } + if(StringUtils.isBlank(token) || "null".equals(token) || "undefined".equals(token)){ + token=""; + //throw new MallinkException(ErrorCode.NET_TOKEN_EMPTY.getCode(),"token为空["+token+"]"); + } + log.info("token>>>>>>>>>>>>>>>"+token); + String tenantId; + BaseCUserEntity cUser = null ; + if(StringUtils.isNotBlank(token)) { + cUser = cUserTokenService.getByToken(token); + } + if (Objects.isNull(cUser) || cUser.getExpireTime().getTime() < System.currentTimeMillis()) { + tenantId = "0"; + } else { + tenantId = cUser.getTenantId(); + } + + // 请求参数 + Object[] args = jp.getArgs(); + // 接口 返回结果 + Object result; + String redisKey; + + //得到注释的名称 + MethodSignature signature = (MethodSignature) jp.getSignature(); + //判断tag是否在用户权限中 如果存在加入参数查询 + Method method = signature.getMethod(); + RedisCache cache = method.getAnnotation(RedisCache.class) ; + if (cache.autoKey()) { + // 构建 redisKey => reqPath:tenantId:md5(param1_param2_param3) + String requestUrl = request.getRequestURI(); + // 接口路径 reqPath => /api/xxx + String reqPath = requestUrl.substring(requestUrl.indexOf("/api/")); + if (cache.md5()) { + String md5Before = StringUtils.join(args, DELIMITER_PARAMS); + redisKey = StringUtils.join(reqPath, DELIMITER_KEY, tenantId, DELIMITER_KEY, HashUtil.md5(StringUtils.join(token, md5Before, DELIMITER_PARAMS))); + } else { + redisKey = StringUtils.join(reqPath, DELIMITER_KEY, tenantId, DELIMITER_KEY, StringUtils.join(token,args, DELIMITER_PARAMS)); + } + } else { + redisKey = cache.key(); + } + + // redisKey 不存在,获取接口数据并返回 + if (StringUtils.isBlank(redisKey)) { + result = jp.proceed(args); + } else { + ValueOperations valueOperations = stringRedisTemplate.opsForValue(); + String value = valueOperations.get(redisKey); + if (value == null) { + String lockKey = StringUtils.join(redisKey, ":", "lock"); + long time = System.currentTimeMillis() + 2000; + String timeStr = String.valueOf(time); + try { + //分布式锁,保证一个线程读DB,其它线程排队 + if (redisLock.lock2(lockKey, timeStr)) { + log.debug("CacheAspect 读库中, key:{}: " + lockKey); + result = jp.proceed(args); + String json_data = JSONObject.toJSONString(result); + valueOperations.set(redisKey, json_data, cache.expireTime(), cache.dateUnit()); + log.debug("CacheAspect 缓存不存在,获取接口数据并放入缓存,key:{}, expire:{}", redisKey, cache.expireTime()); + } else { + log.debug("CacheAspect 读库等待中, key:{}: " + lockKey); + Thread.sleep(2000); + value = valueOperations.get(redisKey); + result = parseCache(jp, redisKey, value); + } + } catch (Throwable throwable) { + log.error("CacheAspect proceed error , Illegal argument: {} in {}.{}()", Arrays.toString(jp.getArgs()), + jp.getSignature().getDeclaringTypeName(), jp.getSignature().getName(), throwable); + throw throwable; + } finally { + redisLock.unlock(lockKey, timeStr); + } + } else { + result = parseCache(jp, redisKey, value); + } + // 计数器 + stringRedisTemplate.opsForHash().increment("hotapi", redisKey, 1); + } + return result; + } + + private Object parseCache(ProceedingJoinPoint jp, String redisKey, String value) { + if (StringUtils.isBlank(value)) { + log.debug("CacheAspect 缓存不存在,解析缓存数据为空:{},key:{}", value, redisKey); + return null; + } + Class returnType = ((MethodSignature) jp.getSignature()).getReturnType(); + log.debug("CacheAspect 缓存存在,解析缓存并返回,key:{}", redisKey); + return JSONObject.parseObject(value, returnType); + } +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/config/MyBatisConfiguration.java b/mallinkPublicApi/src/main/java/com/iformall/config/MyBatisConfiguration.java new file mode 100644 index 000000000..f58a1dd3b --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/config/MyBatisConfiguration.java @@ -0,0 +1,24 @@ +package com.iformall.config; + +import com.iformall.plugin.MyBatisItercepters; +import com.iformall.plugin.MyBatisPlus; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MyBatisConfiguration extends BaseMyBatisConfiguration { + + @Bean + public MyBatisItercepters intercepters() { + MyBatisItercepters intercepters = new MyBatisItercepters(); + List plugins = new ArrayList(); + plugins.add(baseShardingSpherePlugin()); + + intercepters.setPlugins(plugins); + return intercepters; + } +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/config/RedisConfig.java b/mallinkPublicApi/src/main/java/com/iformall/config/RedisConfig.java new file mode 100644 index 000000000..02e61114a --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/config/RedisConfig.java @@ -0,0 +1,335 @@ +package com.iformall.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.pagehelper.PageInfo; +import com.iformall.domain.po.*; +import com.iformall.domain.po.base.BaseCUserEntity; +import com.iformall.domain.vo.WxCouponCVo; +import com.iformall.domain.vo.WxCouponChannelVo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by Stormeye on 2018/10/1. + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + //缓存管理器 + @Bean + public CacheManager cacheManager(RedisConnectionFactory connectionFactory) { + //user信息缓存配置 + RedisCacheConfiguration userCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(10)).disableCachingNullValues().prefixKeysWith("user"); + Map redisCacheConfigurationMap = new HashMap<>(); + redisCacheConfigurationMap.put("user", userCacheConfiguration); + //初始化一个RedisCacheWriter + RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory); + // 设置CacheManager的值序列化方式为JdkSerializationRedisSerializer,但其实RedisCacheConfiguration默认就是使用StringRedisSerializer序列化key,JdkSerializationRedisSerializer序列化value,所以以下注释代码为默认实现 + // ClassLoader loader = this.getClass().getClassLoader(); + // JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(loader); + // RedisSerializationContext.SerializationPair pair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializer); + // RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair); + RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig(); + //设置默认超过期时间是30秒 + defaultCacheConfig.entryTtl(Duration.ofSeconds(30)); + //初始化RedisCacheManager + RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig, redisCacheConfigurationMap); + return cacheManager; + } + + @Bean("pushLimitRedisTemplate") + public RedisTemplate getPushLimitRedisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer j = new Jackson2JsonRedisSerializer(PushLimit.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + j.setObjectMapper(om); + // value值的序列化 + template.setValueSerializer(j); + template.setHashValueSerializer(j); + + // key的序列化 + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + + return template; + } + + @Bean("scoreRuleRedisTemplate") + public RedisTemplate getScoreRuleRedisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer j = new Jackson2JsonRedisSerializer(WxScoreRules.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + j.setObjectMapper(om); + // value值的序列化 + template.setValueSerializer(j); + template.setHashValueSerializer(j); + + // key的序列化 + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + + return template; + } + + @Bean("cuserTokenRedisTemplate") + public RedisTemplate getCUserTokenRedisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer j = new Jackson2JsonRedisSerializer(WxCUser.class); + + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + j.setObjectMapper(om); + + // value值的序列化 + template.setValueSerializer(j); + template.setHashValueSerializer(j); + + // key的序列化 + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + + return template; + } + + @Bean("baseCUserTokenRedisTemplate") + public RedisTemplate getBaseCUserTokenRedisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate(); + + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer j = new Jackson2JsonRedisSerializer(BaseCUserEntity.class); + + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + j.setObjectMapper(om); + + // value值的序列化 + template.setValueSerializer(j); + template.setHashValueSerializer(j); + + // key的序列化 + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + + return template; + } + + @Bean("cUserBasicInfoRedisTemplate") + public RedisTemplate getCUserBasicInfoRedisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer j = new Jackson2JsonRedisSerializer(WxCUserBasicInfo.class); + + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + j.setObjectMapper(om); + // value值的序列化 + template.setValueSerializer(j); + template.setHashValueSerializer(j); + + // key的序列化 + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + + return template; + } + + @Bean("mallRedisTemplate") + public RedisTemplate getMallRedisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer j = new Jackson2JsonRedisSerializer(WxMall.class); + + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + j.setObjectMapper(om); + // value值的序列化 + template.setValueSerializer(j); + template.setHashValueSerializer(j); + + // key的序列化 + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + + return template; + } + + @Bean("subMallListRedisTemplate") + public RedisTemplate> getSubMallListRedisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate> template = new RedisTemplate>(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer j = new Jackson2JsonRedisSerializer(List.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + j.setObjectMapper(om); + // value值的序列化 + template.setValueSerializer(j); + template.setHashValueSerializer(j); + + // key的序列化 + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + + return template; + } + + @Bean("couponDetailRedisTemplate") + public RedisTemplate getCouponDetailRedisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer j = new Jackson2JsonRedisSerializer(WxCouponCVo.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + j.setObjectMapper(om); + // value值的序列化 + template.setValueSerializer(j); + template.setHashValueSerializer(j); + + // key的序列化 + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + + return template; + } + + @Bean("couponChannelRedisTemplate") + public RedisTemplate> getCouponChannelRedisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate> template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer j = new Jackson2JsonRedisSerializer(PageInfo.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + j.setObjectMapper(om); + + // value值的序列化 + template.setValueSerializer(j); + template.setHashValueSerializer(j); + + // key的序列化 + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + + return template; + } + + @Bean("buserTokenRedisTemplate") + public RedisTemplate getBuserTokenRedisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer j = new Jackson2JsonRedisSerializer(WxBuser.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + j.setObjectMapper(om); + + // value值的序列化 + template.setValueSerializer(j); + template.setHashValueSerializer(j); + + // key的序列化 + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + + return template; + } + + @Bean("pressOrderRedisTemplate") + public RedisTemplate getPressOrderRedisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer j = new Jackson2JsonRedisSerializer(WxOrder.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + j.setObjectMapper(om); + + // value值的序列化 + template.setValueSerializer(j); + template.setHashValueSerializer(j); + + // key的序列化 + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + + return template; + } + + @Bean("stringValueOperations") + public ValueOperations getStringValueOperations(RedisConnectionFactory connectionFactory) { + StringRedisTemplate template = new StringRedisTemplate(); + template.setConnectionFactory(connectionFactory); + template.afterPropertiesSet(); + return template.opsForValue(); + } + + @Bean("objectCommonRedisTemplate") + public RedisTemplate getObjectValueOperations(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer j = new Jackson2JsonRedisSerializer(Object.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + j.setObjectMapper(om); + + // value值的序列化 + template.setValueSerializer(j); + template.setHashValueSerializer(j); + + // key的序列化 + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + return template; + } +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/config/Swagger2Config.java b/mallinkPublicApi/src/main/java/com/iformall/config/Swagger2Config.java new file mode 100644 index 000000000..2118b9dbd --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/config/Swagger2Config.java @@ -0,0 +1,61 @@ +package com.iformall.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.ParameterBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.schema.ModelRef; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Parameter; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.paths.RelativePathProvider; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import javax.servlet.ServletContext; +import java.util.ArrayList; +import java.util.List; + +//参考:http://blog.csdn.net/catoop/article/details/50668896 +@Configuration +@EnableSwagger2 +public class Swagger2Config { + + @Autowired + private ServletContext servletContext; + + @Bean + public Docket createRestApi() { + ParameterBuilder tokenPar = new ParameterBuilder(); + List pars = new ArrayList(); + //增加一个request的header参数 + tokenPar.name("token").description("令牌").modelRef(new ModelRef("string")).parameterType("header").required(false).build(); + pars.add(tokenPar.build()); + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + .apis(RequestHandlerSelectors.basePackage("com.iformall.controller")) + .paths(PathSelectors.any()) + .build() + .globalOperationParameters(pars) + .pathProvider(new RelativePathProvider(servletContext) { + @Override + public String getApplicationBasePath() { + return "/api"; + } + }); + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("c端 api") + .description("c api") + .termsOfServiceUrl("http://localhost:7000") + .version("2.0") + .build(); + } + +} \ No newline at end of file diff --git a/mallinkPublicApi/src/main/java/com/iformall/config/WebMvcConfig.java b/mallinkPublicApi/src/main/java/com/iformall/config/WebMvcConfig.java new file mode 100644 index 000000000..1b2a21515 --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/config/WebMvcConfig.java @@ -0,0 +1,106 @@ +package com.iformall.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.iformall.interceptor.AuthorizationInterceptor; +import com.iformall.interceptor.HttpServletRequestWrapperFilter; +import com.iformall.interceptor.RequestInterceptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.*; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.SimpleDateFormat; +import java.util.List; + +/** + * MVC配置 + * + * @author stormeye.wu + * @email wugq@mippoint.com + * @date 2017-04-20 22:30 + */ +@Configuration +@EnableWebMvc +public class WebMvcConfig implements WebMvcConfigurer { + @Autowired + private AuthorizationInterceptor authorizationInterceptor; + @Autowired + private RequestInterceptor requestInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(authorizationInterceptor).addPathPatterns("/api/**"); + registry.addInterceptor(requestInterceptor).addPathPatterns("/api/**"); + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("swagger-ui.html") + .addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/"); + //registry.addResourceHandler("/app/**").addResourceLocations("classpath:/app/"); + + } + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/api/**") + .allowedOrigins("*") + .allowCredentials(true) + .allowedMethods("GET", "POST", "DELETE", "PUT") + .maxAge(3600); + } + + @Override + public void configureMessageConverters(List> converters) { + MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); + //ObjectMapper 是Jackson库的主要类。它提供一些功能将转换成Java对象匹配JSON结构,反之亦然 + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule simpleModule = new SimpleModule(); + + //不显示为null的字段 + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + DeserializationConfig dc = objectMapper.getDeserializationConfig(); + // 设置反序列化日期格式、忽略不存在get、set的属性 + objectMapper.setConfig( + dc.with(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")) + .without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + ); + //序列化将Long转String类型 + simpleModule.addSerializer(Long.class, ToStringSerializer.instance); + simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); + SimpleModule bigIntegerModule = new SimpleModule(); + //序列化将BigInteger转String类型 + bigIntegerModule.addSerializer(BigInteger.class, ToStringSerializer.instance); + SimpleModule bigDecimalModule = new SimpleModule(); + //序列化将BigDecimal转String类型 + bigDecimalModule.addSerializer(BigDecimal.class, ToStringSerializer.instance); + objectMapper.registerModule(simpleModule); + objectMapper.registerModule(bigDecimalModule); + objectMapper.registerModule(bigIntegerModule); + jackson2HttpMessageConverter.setObjectMapper(objectMapper); + converters.add(jackson2HttpMessageConverter); + } + + @Bean + public FilterRegistrationBean Filters() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(new HttpServletRequestWrapperFilter()); + registrationBean.addUrlPatterns("/*"); + registrationBean.setName("koalaSignFilter"); + return registrationBean; + } +} \ No newline at end of file diff --git a/mallinkPublicApi/src/main/java/com/iformall/controller/BaseController.java b/mallinkPublicApi/src/main/java/com/iformall/controller/BaseController.java new file mode 100644 index 000000000..56a157ed5 --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/controller/BaseController.java @@ -0,0 +1,57 @@ +package com.iformall.controller; + +import com.iformall.utils.IPUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.beans.PropertyEditorSupport; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +@RestController +public class BaseController { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + @Qualifier("objectCommonRedisTemplate") + RedisTemplate objectCommonRedisTemplate; + + @InitBinder + public void InitBinder(WebDataBinder dataBinder) { + dataBinder.registerCustomEditor(Date.class, new PropertyEditorSupport() { + public void setAsText(String value) { + try { + setValue(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value)); + } catch (ParseException e) { + try { + setValue(new SimpleDateFormat("yyyy-MM-dd ").parse(value)); + } catch (ParseException e1) { + setValue(null); + } + } + } + + public String getAsText() { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) getValue()); + } + + }); + } + + public String getIpAddr() { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String ipaddress = IPUtil.getIpAddr(request); + return ipaddress; + } +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/interceptor/AuthorizationInterceptor.java b/mallinkPublicApi/src/main/java/com/iformall/interceptor/AuthorizationInterceptor.java new file mode 100644 index 000000000..f19903526 --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/interceptor/AuthorizationInterceptor.java @@ -0,0 +1,68 @@ +package com.iformall.interceptor; + + +import com.iformall.common.ErrorCode; +import com.iformall.exception.MallinkException; +import com.iformall.utils.HashUtil; +import com.iformall.utils.RedisCacheUtils; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 权限(Token)验证 + * @author stormeye.wu + * @email wuguoqiang@iformall.com + * @date 2017-03-23 15:38 + */ +@Component +public class AuthorizationInterceptor extends HandlerInterceptorAdapter { + + + @Autowired + @Qualifier("objectCommonRedisTemplate") + RedisTemplate redisTemplate; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String cid = request.getParameter("cId");//调用方ID + String nonceStr = request.getParameter("nonceStr");//随机字符串 + String signKey = request.getParameter("signKey");//随机字符串 + + if(StringUtils.isBlank(cid) || "null".equals(cid) || "undefined".equals(cid)){ + throw new MallinkException(ErrorCode.SYS_PARAMETER_ERROR.getCode(),"cId为空["+cid+"]"); + } + + if(StringUtils.isBlank(nonceStr) || "null".equals(nonceStr) || "undefined".equals(nonceStr)){ + throw new MallinkException(ErrorCode.SYS_PARAMETER_ERROR.getCode(),"nonceStr为空["+nonceStr+"]"); + } + + if(StringUtils.isBlank(signKey) || "null".equals(signKey) || "undefined".equals(signKey)){ + throw new MallinkException(ErrorCode.SYS_PARAMETER_ERROR.getCode(),"signKey为空["+signKey+"]"); + } + + //nonceStr必须唯一,为防止接口盗刷,每次只能调用一次 + Integer cache = RedisCacheUtils.getCacheInteger(redisTemplate, "publicApi:"+nonceStr); + if (null != cache) { + throw new MallinkException(ErrorCode.SYS_PARAMETER_ERROR.getCode(),"不能重复调用"); + } + + + //TODO singnKey是根据cid+cid对应的密钥+nonceStr + String secretKey = "";//TODO 根据cid查询密钥,缓存 + String signstr = HashUtil.md5(cid+secretKey+nonceStr); + if (!signKey.equals(signstr)) { + throw new MallinkException(ErrorCode.SYS_PARAMETER_ERROR.getCode(),"加密串校验失败"); + } + + RedisCacheUtils.cache(redisTemplate, "publicApi:"+nonceStr, 1, 600); + return true; + } +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/interceptor/BodyReaderHttpServletRequestWrapper.java b/mallinkPublicApi/src/main/java/com/iformall/interceptor/BodyReaderHttpServletRequestWrapper.java new file mode 100644 index 000000000..38aec3039 --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/interceptor/BodyReaderHttpServletRequestWrapper.java @@ -0,0 +1,76 @@ +package com.iformall.interceptor; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import java.io.*; + +public class BodyReaderHttpServletRequestWrapper extends XssHttpServletRequestWrapper { + private final String body; + + public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { + super(request); + StringBuilder stringBuilder = new StringBuilder(); + BufferedReader bufferedReader = null; + try { + InputStream inputStream = request.getInputStream(); + if (inputStream != null) { + bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"utf-8")); + char[] charBuffer = new char[1024]; + int bytesRead = -1; + while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { + stringBuilder.append(charBuffer, 0, bytesRead); + } + } else { + stringBuilder.append(""); + } + } catch (IOException ex) { + throw ex; + } finally { + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (IOException ex) { + throw ex; + } + } + } + body = stringBuilder.toString(); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + final ByteArrayInputStream byteArrayInputStream = + new ByteArrayInputStream(body.getBytes("utf-8")); + return new ServletInputStream() { + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + + @Override + public int read() throws IOException { + return byteArrayInputStream.read(); + } + }; + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(this.getInputStream())); + } + + public String getBody() { + return this.body; + } +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/interceptor/HttpServletRequestWrapperFilter.java b/mallinkPublicApi/src/main/java/com/iformall/interceptor/HttpServletRequestWrapperFilter.java new file mode 100644 index 000000000..ec4e7cb7d --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/interceptor/HttpServletRequestWrapperFilter.java @@ -0,0 +1,70 @@ +package com.iformall.interceptor; + +import com.iformall.utils.UrlCheck; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +public class HttpServletRequestWrapperFilter implements Filter { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + // 多个跨域域名设置 + // public static final String[] ALLOW_DOMAIN = {"https://admin.malls.iformall.com"}; + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + logger.debug("doFilter start"); + long start = System.currentTimeMillis(); + ServletRequest requestWrapper = null; + + /* // 跨域访问 + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse res = (HttpServletResponse) response; + String originHeader = req.getHeader("Origin"); + + if (Arrays.asList(ALLOW_DOMAIN).contains(originHeader)) { + //通过在响应 header 中设置 ‘*’ 来允许来自所有域的跨域请求访问。 + res.setHeader("Access-Control-Allow-Origin", originHeader); + //通过对 Credentials 参数的设置,就可以保持跨域 Ajax 时的 Cookie + //设置了Allow-Credentials,Allow-Origin就不能为*,需要指明具体的url域 + res.setHeader("Access-Control-Allow-Credentials", "true"); + //请求方式 + res.setHeader("Access-Control-Allow-Methods", "*"); + //(预检请求)的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被缓存多久 + res.setHeader("Access-Control-Max-Age", "86400"); + //首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段 + //res.setHeader("Access-Control-Allow-Headers", "*"); + res.setHeader("Access-Control-Allow-Headers", + "Timestamp,Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token,Access-Control-Allow-Headers"); + } + */ + + //sql,xss过滤 + XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper((HttpServletRequest)request); + String url = ""; + if (request instanceof HttpServletRequest) { + url = ((HttpServletRequest) request).getRequestURI(); + if (!UrlCheck.checkUrl(url)) { + requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request); + } + } + if (null == requestWrapper) { + chain.doFilter(xssHttpServletRequestWrapper, response); + } else { + chain.doFilter(requestWrapper, response); + } + logger.debug("doFilter end: " + url + " "+ (System.currentTimeMillis()- start) + "ms"); + } + + @Override + public void destroy() { + + } +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/interceptor/RequestInterceptor.java b/mallinkPublicApi/src/main/java/com/iformall/interceptor/RequestInterceptor.java new file mode 100644 index 000000000..ff7e81880 --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/interceptor/RequestInterceptor.java @@ -0,0 +1,123 @@ +package com.iformall.interceptor; + +import com.iformall.common.ErrorCode; +import com.iformall.exception.MallinkException; +import com.iformall.utils.HashUtil; +import com.iformall.utils.IPUtil; +import com.iformall.utils.UrlCheck; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.dao.DataAccessException; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; +import redis.clients.jedis.Protocol; +import redis.clients.jedis.util.SafeEncoder; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.Enumeration; +import java.util.concurrent.TimeUnit; + +/** + * 幂等检查 + * @author stormeye.wu + * @email wuguoqiang@iformall.com + * @date 2017-03-23 15:38 + */ +@Component +public class RequestInterceptor extends HandlerInterceptorAdapter { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Resource + private RedisTemplate redisTemplate; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + logger.debug("preHandle start"); + if ("GET".equalsIgnoreCase(request.getMethod())) { + // 获取不检查幂等 + logger.debug("preHandle start 1"); + return true; + } + String ipaddress = IPUtil.getIpAddr(request); + String url = request.getRequestURL().toString(); + if (UrlCheck.checkUrl(url)) { + // pvlog不检查幂等 + // awsFileUpload不检查幂等 + // stopFee + return true; + } + StringBuilder sb = new StringBuilder(); + + sb.append(url); + + sb.append("method=").append(request.getMethod()).append("&"); + + sb.append("ip=").append(ipaddress).append("&"); + + final Enumeration parameterNames = request.getParameterNames(); + while (parameterNames.hasMoreElements()) { + String key = (String) parameterNames.nextElement(); + if(key.equalsIgnoreCase("ran")) // 跳过ran + continue; + String parameter = request.getParameter(key); + sb.append(key).append("=").append(parameter).append("&"); + } + + InputStream inStream = request.getInputStream(); + ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len = 0; + while ((len = inStream.read(buffer)) != -1) { + outSteam.write(buffer, 0, len); + } + String resultBody = new String(outSteam.toByteArray(), Charset.forName("UTF-8")); + inStream.close(); + outSteam.close(); + + sb.append(resultBody); + + String key = "request:C:" + HashUtil.md5(sb.toString()); + Boolean isAbsent = redisTemplate.execute(new RedisCallback() { + @Override + public Boolean doInRedis(RedisConnection connection) throws DataAccessException { + RedisSerializer valueSerializer = redisTemplate.getValueSerializer(); + RedisSerializer keySerializer = redisTemplate.getKeySerializer(); + Object obj = connection.execute("set", keySerializer.serialize(key), + valueSerializer.serialize(key), + SafeEncoder.encode("NX"), + SafeEncoder.encode("EX"), + Protocol.toByteArray(3)); // 3s + return obj != null; + } + }); + if (isAbsent) { + logger.info(key + ": 第一次提交"); + logger.debug("preHandle start 2"); + return true; + } + logger.info(key + ": 第二次提交"); + logger.debug("preHandle start 3"); + throw new MallinkException(ErrorCode.SYS_REPEAT_SUBMIT_EXCEPTION); + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + logger.debug("postHandle"); + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + logger.debug("afterCompletion"); + } +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/interceptor/WebLogAspect.java b/mallinkPublicApi/src/main/java/com/iformall/interceptor/WebLogAspect.java new file mode 100644 index 000000000..c66aeef83 --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/interceptor/WebLogAspect.java @@ -0,0 +1,72 @@ +package com.iformall.interceptor; + +import com.iformall.utils.IPUtil; +import lombok.Data; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.util.Arrays; + +@Aspect +@Component +public class WebLogAspect { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Data + private class MethodInfo { + Long startTime; + String url; + String ip; + String method; + String classMethod; + } + + ThreadLocal startInfo = new ThreadLocal<>(); + + + @Pointcut("execution(public * com.iformall.controller..*.*(..))") + public void webLog() {} + + @Before("webLog()") + public void doBefore(JoinPoint joinPoint) throws Throwable { + logger.debug("aspect start"); + // 接收到请求,记录请求内容 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = attributes.getRequest(); + + MethodInfo info = new MethodInfo(); + info.setUrl(request.getRequestURL().toString()); + info.setStartTime(System.currentTimeMillis()); + info.setIp(IPUtil.getIpAddr(request)); + info.setMethod(request.getMethod()); + info.setClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); + startInfo.set(info); + logger.debug("aspect start ..."); + } + + @AfterReturning(returning = "ret", pointcut = "webLog()") + public void doAfterReturning(Object ret) throws Throwable { + logger.debug("aspect after"); + // 处理完请求,返回内容 + MethodInfo info = startInfo.get(); + StringBuilder sb = new StringBuilder(); + sb.append("URL: ").append(info.getUrl()) + .append(", METHOD: ").append(info.getMethod()) + .append(", DO: ").append(info.getClassMethod()) + .append(", IP: ").append(info.getIp()) + .append(", SPEND TIME: ").append(System.currentTimeMillis() -info.getStartTime()).append("ms"); + logger.info(sb.toString()); + logger.debug("aspect after .."); + } + + +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/interceptor/XssHttpServletRequestWrapper.java b/mallinkPublicApi/src/main/java/com/iformall/interceptor/XssHttpServletRequestWrapper.java new file mode 100644 index 000000000..1d954d163 --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/interceptor/XssHttpServletRequestWrapper.java @@ -0,0 +1,125 @@ +package com.iformall.interceptor; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * 防止sql注入,xss攻击 + * 前端可以对输入信息做预处理,后端也可以做处理。 + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { + private final Logger log = LoggerFactory.getLogger(getClass()); + private static String key = "and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+"; + private static Set notAllowedKeyWords = new HashSet(0); + private static String replacedString="INVALID"; + static { + String keyStr[] = key.split("\\|"); + for (String str : keyStr) { + notAllowedKeyWords.add(str); + } + } + + private String currentUrl; + + public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) { + super(servletRequest); + currentUrl = servletRequest.getRequestURI(); + } + + + /**覆盖getParameter方法,将参数名和参数值都做xss过滤。 + * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取 + * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖 + */ + @Override + public String getParameter(String parameter) { + String value = super.getParameter(parameter); + if (value == null) { + return null; + } + return cleanXSS(value); + } + @Override + public String[] getParameterValues(String parameter) { + String[] values = super.getParameterValues(parameter); + if (values == null) { + return null; + } + int count = values.length; + String[] encodedValues = new String[count]; + for (int i = 0; i < count; i++) { + encodedValues[i] = cleanXSS(values[i]); + } + return encodedValues; + } + @Override + public Map getParameterMap(){ + Map values=super.getParameterMap(); + if (values == null) { + return null; + } + Map result=new HashMap<>(); + for(String key:values.keySet()){ + String encodedKey=cleanXSS(key); + int count=values.get(key).length; + String[] encodedValues = new String[count]; + for (int i = 0; i < count; i++){ + encodedValues[i]=cleanXSS(values.get(key)[i]); + } + result.put(encodedKey,encodedValues); + } + return result; + } + /** + * 覆盖getHeader方法,将参数名和参数值都做xss过滤。 + * 如果需要获得原始的值,则通过super.getHeaders(name)来获取 + * getHeaderNames 也可能需要覆盖 + */ + @Override + public String getHeader(String name) { + if(name.equalsIgnoreCase("user-agent")) { + return super.getHeader(name); + } + String value = super.getHeader(name); + if (value == null) { + return null; + } + return cleanXSS(value); + } + + private String cleanXSS(String valueP) { + // You'll need to remove the spaces from the html entities below + String value = valueP.replaceAll("<", "<").replaceAll(">", ">"); + value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;"); + value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;"); + value = value.replaceAll("'", "& #39;"); + value = value.replaceAll("eval\\((.*)\\)", ""); + value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\""); + value = value.replaceAll("script", ""); + value = cleanSqlKeyWords(value); + return value; + } + + private String cleanSqlKeyWords(String value) { + String paramValue = value; + for (String keyword : notAllowedKeyWords) { + if (paramValue.length() > keyword.length() + 4 + && (paramValue.contains(" "+keyword)||paramValue.contains(keyword+" ")||paramValue.contains(" "+keyword+" "))) { + paramValue = StringUtils.replace(paramValue, keyword, replacedString); + log.error(this.currentUrl + "已被过滤,因为参数中包含不允许sql的关键词(" + keyword + + ")"+";参数:"+value+";过滤后的参数:"+paramValue); + } + } + return paramValue; + } + +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/utils/ImgIdeaUtil.java b/mallinkPublicApi/src/main/java/com/iformall/utils/ImgIdeaUtil.java new file mode 100644 index 000000000..16ebb8d46 --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/utils/ImgIdeaUtil.java @@ -0,0 +1,175 @@ +package com.iformall.utils; + +import net.sourceforge.pinyin4j.PinyinHelper; +import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; +import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; +import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; +import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType; +import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ResourceUtils; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +public class ImgIdeaUtil { + + private static final Logger logger = LoggerFactory.getLogger(ImgIdeaUtil.class); + + //模板图片 + private static final String imgTempPath = "imgTemp/gertificate.jpg"; + //字体路径 + private static final String fontPath = "opt/HWLS.TTF"; + + /** + * 生成图片证书 + * @return + */ + public static byte[] imgGertificate(String name, String englishName, LocalDateTime date){ + if(StringUtils.isBlank(name)){ + logger.error("名字为空,无法生成证书"); + return null; + } + if(name.length() == 2){ + name = name.replaceAll("(\\S)", "$0 "); + } + if(StringUtils.isBlank(englishName)){ + englishName = getUpEname(name); + } + if(date == null){ + date = LocalDateTime.now(); + } + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dMMM yyyy", Locale.UK); + String dateFormat = date.format(formatter); + + try { + + InputStream backImgUrl = ClassLoader.getSystemResourceAsStream(imgTempPath); + //InputStream backImgUrl = ImgIdeaUtil.class.getClassLoader().getResourceAsStream(imgTempPath); + + Font font = Font.createFont(Font.TRUETYPE_FONT, ResourceUtils.getFile("classpath:"+fontPath)); + BufferedImage backImg = ImageIO.read(backImgUrl); + Graphics g = backImg.getGraphics(); + + //图片加中文名 + Font fTxtBottomName = font.deriveFont(Font.PLAIN, 170); + //Font fTxtBottomName = new Font("华文隶书", Font.PLAIN, 170); + FontMetrics metricsName = g.getFontMetrics(fTxtBottomName); + int xName = (backImg.getWidth() - metricsName.stringWidth(name)) / 2; + Color myColorTxtBottomName = Color.BLACK; //颜色 + g.setColor(myColorTxtBottomName); + g.setFont(fTxtBottomName); + g.drawString(name, xName , 1150);//g.drawString(文字, x 位置, y 位置); + + //图片加英文名 + Font fTxtBottomEname = font.deriveFont(Font.PLAIN, 120); + //Font fTxtBottomEname = new Font("华文隶书", Font.PLAIN, 120); + FontMetrics metricsEname = g.getFontMetrics(fTxtBottomEname); + int xEname = (backImg.getWidth() - metricsEname.stringWidth(englishName)) / 2; + Color myColorTxtBottomEname = Color.BLACK; + g.setColor(myColorTxtBottomEname); + g.setFont(fTxtBottomEname); + g.drawString(englishName, xEname, 1300); + + //图片加日期 + Font fTxtBottomDate = font.deriveFont(Font.PLAIN, 90); + //Font fTxtBottomDate = new Font("华文隶书", Font.PLAIN, 90); + Color myColorTxtBottomDate = Color.BLACK; + g.setColor(myColorTxtBottomDate); + g.setFont(fTxtBottomDate); + g.drawString(dateFormat, 440, 2100); + + //调这个方法就是开始这个整合 (可以多张图片,多个文字整合成一张图片,只有把他们放在这方法里面就行) + g.dispose(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ImageIO.write(backImg, "png", out); + + return out.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + logger.error("生成证书异常", e.getMessage()); + } + return null; + } + + /** + * //姓、名的第一个字母需要为大写 + * @param name + * @return + */ + public static String getUpEname(String name) { + char[] strs = name.toCharArray(); + String newname = null; + + if (strs.length == 2) { + newname = toUpCase(getEname("" + strs[0])) + " " + toUpCase(getEname("" + strs[1])); + } else if (strs.length == 3){ + newname = toUpCase(getEname("" + strs[0])) + " " + + toUpCase(getEname("" + strs[1] + strs[2])); + }else if (strs.length == 4){ + newname = toUpCase(getEname("" + strs[0] + strs[1])) + " " + + toUpCase(getEname("" + strs[2] + strs[3])); + } else{ + newname = toUpCase(getEname(name)); + } + return newname; + } + + + /** + * //将中文转换为英文 + * @param name + * @return + */ + public static String getEname(String name){ + HanyuPinyinOutputFormat pyFormat = new HanyuPinyinOutputFormat(); + pyFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE); + pyFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE); + pyFormat.setVCharType(HanyuPinyinVCharType.WITH_V); + String s = ""; + try { + s = PinyinHelper.toHanyuPinyinString(name, pyFormat, ""); + } catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination) { + badHanyuPinyinOutputFormatCombination.printStackTrace(); + } + return s; + } + /** + * 首字母大写 + * @param str + * @return + */ + private static String toUpCase(String str) { + StringBuffer newstr = new StringBuffer(); + newstr.append((str.substring(0, 1)).toUpperCase()).append( + str.substring(1, str.length())); + return newstr.toString(); + } + + +// public static void main(String[] args) { +// String serverUploadImgUrl = "C:/Users/xiaohu/Desktop/img"; // 图片保存路径 +// try { +// byte[] bytes = imgGertificate("郑方元", null, null); +// ByteArrayInputStream bais = new ByteArrayInputStream(bytes); +// BufferedImage bi1 =ImageIO.read(bais); +// +// ImageIO.write(bi1, "png", new File(serverUploadImgUrl+"/0.png")); +// +// } catch (Exception e) { +// e.printStackTrace(); +// } +// System.out.println("结束"); +// } +} diff --git a/mallinkPublicApi/src/main/java/com/iformall/utils/UrlCheck.java b/mallinkPublicApi/src/main/java/com/iformall/utils/UrlCheck.java new file mode 100644 index 000000000..e3961ad71 --- /dev/null +++ b/mallinkPublicApi/src/main/java/com/iformall/utils/UrlCheck.java @@ -0,0 +1,14 @@ +package com.iformall.utils; + +/** + * @author gongbiao + */ +public class UrlCheck { + + public static boolean checkUrl(String url) { + return url.contains("awsFileUpload") + || url.contains("awsFilesUpload") + || url.contains("getCarStopFee"); + } + +} diff --git a/mallinkPublicApi/src/main/resources/application-dev.yml b/mallinkPublicApi/src/main/resources/application-dev.yml new file mode 100644 index 000000000..b2b606aad --- /dev/null +++ b/mallinkPublicApi/src/main/resources/application-dev.yml @@ -0,0 +1,167 @@ +spring: + profiles: + #include: aliyunRocketMQ + include: rabbitMQ + # JDBC + datasource: + url: jdbc:mysql://101.200.130.134:3306/mallink?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useUnicode=true&useSSL=false&useAffectedRows=true + username: root + password: fm2020test + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + filters: stat + maxActive: 20 + initialSize: 1 + maxWait: 60000 + minIdle: 1 + timeBetweenEvictionRunsMillis: 28000 + minEvictableIdleTimeMillis: 28000 + validationQuery: select 'x' + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + poolPreparedStatements: true + maxOpenPreparedStatements: 20 + #jackson: + #date-format: yyyy-MM-dd HH:mm:ss + + # REDIS + redis: + host: 101.200.130.134 + port: 6379 + password: iF0rm@2l2ol9 + timeout: 3600 + expire: 1800 #30分钟 + database: 5 + jedis: + pool: + max-active: 200 + max-idle: 100 + max-wait: -1 + min-idle: 0 + + # SMS + aliyun: + sms: + accessKeyId: LTAI4G7ixY4AhvM35F8o3W3V + accessKeySecret: VfWqGb83qIQrS9us45utskl8itd7ry + product: Dysmsapi + domain: dysmsapi.aliyuncs.com + regionId: cn-hangzhou + dateFormat: yyyyMMdd + endpointName: cn-hangzhou + + oss: + endpoint: oss-cn-beijing.aliyuncs.com + keyid: LTAI4G7ixY4AhvM35F8o3W3V + keysecret: VfWqGb83qIQrS9us45utskl8itd7ry + bucketname: formall + filehost: malinkcapi + filedomain: https://formall.oss-accelerate.aliyuncs.com + + # EMAIL + mail: + host: smtp.exmail.qq.com + username: service@iformall.com + password: jGvApygXN7wc5SzN # 授权密码 + properties: + mail: + smtp: + auth: true + starttls: + enable: true + socketFactory: + port: 465 + class: javax.net.ssl.SSLSocketFactory + # ROCKETMQ + rocketmq: + nameServer: 127.0.0.1:9876 + producer: + retry-times-when-send-async-failed: 0 + send-msg-timeout: 300000 + compress-msg-body-over-howmuch: 4096 + max-message-size: 4194304 + retry-another-broker-when-not-store-ok: false + retry-times-when-send-failed: 2 + # RABBITMQ + rabbitmq: + host: 101.200.130.134 + port: 5672 + username: fumao + password: f9l98 + publisher-confirms: true + publisher-returns: false + virtual-host: / + aliyunRocketmq: + accessKeyId: "LTAI4G7ixY4AhvM35F8o3W3V" + accessKeySecret: "VfWqGb83qIQrS9us45utskl8itd7ry" + groupId: "GID_P_1" + namesrvAddr: "http://MQ_INST_1796289517488555_Bcqaq2is.cn-beijing.mq-internal.aliyuncs.com:8080" + +aws: + clientRegion: cn-northwest-1 + bucketName: iformall-net + access: ENC(3gx5ghDFBqGrEhO3Wf8aYmXsnwHO7Cj3HNKJGOeUj0o=) + secret: ENC(HVKIJwCJKVXLlUpGlQPwNqJOlnpxn4xYuy91SH0seTSm2uAttIQHvA49fXWWax90v5wloIk0QuU=) + +#wechat: +# open: +# componentAppId: "wxdfc8fb4e62d6b52b" +# componentSecret: "98daa62b316dd6feabaad708327ce233" +# componentToken: "formall2018" +# componentAesKey: "htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN" +# redis: +# host: 101.201.103.81 +# port: 6379 +# password: ENC(xPFDlgd6v8zUhJMbcKc0KmmQiqs9qHtk) +# timeout: 3600 +# expire: 1800 #30分钟 +# database: 2 +# defaultExpiration: 2592000 # 默认生命周期30天 +# jedis: +# pool: +# max-active: 100 +# max-idle: 500 +# max-wait: -1 +# min-idle: 10 + +wechat: + web: + appId: "wx091907dd0bfd3f6b" + secret: "2a2ca10738998b9ef92c1fe8a4d366a6" + url: "https://admintest.malls.iformall.com" + open: + componentAppId: wxdfc8fb4e62d6b52b + componentSecret: 98daa62b316dd6feabaad708327ce233 + componentToken: formall2018 + componentAesKey: htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN + redis: + host: 101.200.130.134 + port: 6379 + password: iF0rm@2l2ol9 + timeout: 3600 + expire: 1800 #30分钟 + database: 2 + defaultExpiration: 2592000 # 默认生命周期30天 + jedis: + pool: + max-active: 100 + max-idle: 100 + max-wait: -1 + min-idle: 10 + +jasypt: + encryptor: + password: oRqdnDbK5pj3eMmB + +fm: + exception: false + exception_emails: houtaikaifa@iformall.com + deploy: 1 + open: true + upload_dir: /home/test/server/uploads/ + +logging: + level: + com.iformall: debug + path: ./logs/c \ No newline at end of file diff --git a/mallinkPublicApi/src/main/resources/application-prod.yml b/mallinkPublicApi/src/main/resources/application-prod.yml new file mode 100644 index 000000000..cb6c367fc --- /dev/null +++ b/mallinkPublicApi/src/main/resources/application-prod.yml @@ -0,0 +1,122 @@ +spring: + profiles: + include: aliyunRocketMQ + # JDBC + datasource: + url: jdbc:mysql://zc349w82qvn56ftl5e64-rw4rm.rwlb.rds.aliyuncs.com:3306/mallink?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false&useAffectedRows=true + username: ENC(nUefcxWYlMS/1cDxikIKwA==) + password: ENC(neToS+hzeFjgSFB/7UEl5qYnW2rUjrPq) + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + filters: stat + maxActive: 20 + initialSize: 1 + maxWait: 60000 + minIdle: 1 + timeBetweenEvictionRunsMillis: 28000 + minEvictableIdleTimeMillis: 28000 + validationQuery: select 'x' + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + poolPreparedStatements: true + maxOpenPreparedStatements: 20 + # REDIS + redis: + host: r-2zeaglwf13qqmnllj5.redis.rds.aliyuncs.com + port: 6379 + password: mallone:iF0rm@2l2ol9 + timeout: 3600 + expire: 1800 #30分钟 + database: 1 + defaultExpiration: 2592000 # 默认生命周期30天 + jedis: + pool: + max-active: 100 + max-idle: 100 + max-wait: -1 + min-idle: 10 + # SMS + aliyun: + sms: + accessKeyId: LTAI4G7ixY4AhvM35F8o3W3V + accessKeySecret: VfWqGb83qIQrS9us45utskl8itd7ry + product: Dysmsapi + domain: dysmsapi.aliyuncs.com + regionId: cn-hangzhou + dateFormat: yyyyMMdd + endpointName: cn-hangzhou + oss: + endpoint: oss-cn-beijing.aliyuncs.com + keyid: LTAI4G7ixY4AhvM35F8o3W3V + keysecret: VfWqGb83qIQrS9us45utskl8itd7ry + bucketname: formall + filehost: malinkadmin + filedomain: https://formall.oss-accelerate.aliyuncs.com + + # EMAIL + mail: + host: smtp.exmail.qq.com + username: sysadministor@iformall.com # 登陆密码sysAdmin1231 + password: SWqPekCEmhUYuYCd # 授权密码 + properties: + mail: + smtp: + auth: true + starttls: + enable: true + socketFactory: + port: 465 + class: javax.net.ssl.SSLSocketFactory + # RABBITMQ + rabbitmq: + host: localhost + port: 5672 + username: ENC(lRmLd6EzgeY1RT5ktcHv9g==) + password: ENC(gBI8mCjr3OC0v57jcnSb660Ux7mW03K2oePgvohhg7w=) + publisher-confirms: true + publisher-returns: false + virtual-host: / + aliyunRocketmq: + accessKeyId: "LTAI4G7ixY4AhvM35F8o3W3V" + accessKeySecret: "VfWqGb83qIQrS9us45utskl8itd7ry" + groupId: "GID_P_1" + namesrvAddr: "http://MQ_INST_1796289517488555_Bcqaq2is.cn-beijing.mq-internal.aliyuncs.com:8080" +aws: + clientRegion: cn-northwest-1 + bucketName: iformall-net + access: ENC(a6SN1sZ1enNL49ypiOXkg/pPPAnZD8H4buQFTTKN08s=) + secret: ENC(5P5ff4bTMJUbXVR4ZsM03UHzOKZ4+Zg5Iutcdkyp/Quny/oXg+A4KpfwEyGarlLu3vQMJahGP5M=) + +wechat: + open: + componentAppId: ENC(b3JG0MUZgQfz5Zj+DC2ZM1zDeLOiGTmmeokfe2O8kaM=) + componentSecret: ENC(QVyc4BPGdnSjXGs2ivgpDBE1v8wPWHLLRMYI7Vv8uYwC0SiZHQz0QpyyQV/b48Pb) + componentToken: ENC(rkxj0733WxFFDLgA9x01m2s5Fi2L+0PC) + componentAesKey: ENC(EIbJUBpbYOrLb4YQ/HXLQmxlxgAqIp2ZmpnGICC8pu5xiTz3Cqfkbwd2S8raCcK/IvYcX2GmedI=) + redis: + host: r-2zeaglwf13qqmnllj5.redis.rds.aliyuncs.com + port: 6379 + password: mallone:iF0rm@2l2ol9 + timeout: 3600 + expire: 1800 #30分钟 + database: 2 + defaultExpiration: 2592000 # 默认生命周期30天 + jedis: + pool: + max-active: 100 + max-idle: 100 + max-wait: -1 + min-idle: 10 + +fm: + exception: true + exception_emails: houtaikaifa@iformall.com + deploy: 3 + open: true + upload_dir: /root/uploads/ + +logging: + level: + com.iformall: debug + path: ./logs/c \ No newline at end of file diff --git a/mallinkPublicApi/src/main/resources/application-prodDetry.yml b/mallinkPublicApi/src/main/resources/application-prodDetry.yml new file mode 100644 index 000000000..39184ddd5 --- /dev/null +++ b/mallinkPublicApi/src/main/resources/application-prodDetry.yml @@ -0,0 +1,145 @@ +spring: + profiles: + include: aliyunRocketMQ + # JDBC + datasource: + url: jdbc:mysql://rm-2zel9i9t555zy7lftmo.mysql.rds.aliyuncs.com:3306/mallink?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false&useAffectedRows=true&allowMultiQueries=true + username: mallone + password: m@l9oNEl20#@ + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + filters: stat + maxActive: 20 + initialSize: 1 + maxWait: 60000 + minIdle: 1 + timeBetweenEvictionRunsMillis: 28000 + minEvictableIdleTimeMillis: 28000 + validationQuery: select 'x' + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + poolPreparedStatements: true + maxOpenPreparedStatements: 20 + connectionProperties: "druid.stat.mergeSql=true;druid.stat.slowSqlMillis=60000" + # REDIS + redis: + host: r-2zeaglwf13qqmnllj5pd.redis.rds.aliyuncs.com + port: 6379 + password: mallone:iF0rm@2l2ol9 + timeout: 3600 + expire: 1800 #30分钟 + database: 1 + defaultExpiration: 2592000 # 默认生命周期30天 + jedis: + pool: + max-active: 100 + max-idle: 20 + max-wait: -1 + min-idle: 0 + + # SMS + aliyun: + sms: + accessKeyId: LTAI4G7ixY4AhvM35F8o3W3V + accessKeySecret: VfWqGb83qIQrS9us45utskl8itd7ry + product: Dysmsapi + domain: dysmsapi.aliyuncs.com + regionId: cn-hangzhou + dateFormat: yyyyMMdd + endpointName: cn-hangzhou + oss: + endpoint: oss-cn-beijing.aliyuncs.com + keyid: LTAI4G7ixY4AhvM35F8o3W3V + keysecret: VfWqGb83qIQrS9us45utskl8itd7ry + bucketname: formall + filehost: malinkadmin + filedomain: https://formall.oss-accelerate.aliyuncs.com + # EMAIL + mail: + host: smtp.exmail.qq.com + username: zhengfangyuan@iformall.com + password: xnydCeUzofB2h8qp # 授权密码 + properties: + mail: + smtp: + auth: true + starttls: + enable: true + socketFactory: + port: 465 + class: javax.net.ssl.SSLSocketFactory + # RABBITMQ + rabbitmq: + host: localhost + port: 5672 + username: admin + password: ibh2D9R3v2DEN3gk + #username: ENC(lRmLd6EzgeY1RT5ktcHv9g==) + #password: ENC(gBI8mCjr3OC0v57jcnSb660Ux7mW03K2oePgvohhg7w=) + publisher-confirms: true + publisher-returns: false + virtual-host: / + # + aliyunRocketmq: + accessKeyId: "LTAI4G7ixY4AhvM35F8o3W3V" + accessKeySecret: "VfWqGb83qIQrS9us45utskl8itd7ry" + groupId: "GID_P_1" + namesrvAddr: "http://MQ_INST_1796289517488555_Bcqaq2is.cn-beijing.mq-internal.aliyuncs.com:8080" + flyway: + enabled: false + +aws: + clientRegion: cn-northwest-1 + bucketName: iformall-net + access: AKIAOEHX2MJVBURWTB3Q + secret: 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 + #access: ENC(a6SN1sZ1enNL49ypiOXkg/pPPAnZD8H4buQFTTKN08s=) + #secret: ENC(5P5ff4bTMJUbXVR4ZsM03UHzOKZ4+Zg5Iutcdkyp/Quny/oXg+A4KpfwEyGarlLu3vQMJahGP5M=) + +wechat: + web: + appId: "wx9cc4ca09eb20fe03" + secret: "af1d7f7a1268022a73cb4ce0b9cf0985" + url: "https://admin.malls.iformall.com" + open: + componentAppId: wx7f58f461ec1e2b4f + componentSecret: 7eb9b795606739b5526dd7f61d923f5a + componentToken: formall2018 + componentAesKey: htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN + #componentAppId: ENC(b3JG0MUZgQfz5Zj+DC2ZM1zDeLOiGTmmeokfe2O8kaM=) + #componentSecret: ENC(QVyc4BPGdnSjXGs2ivgpDBE1v8wPWHLLRMYI7Vv8uYwC0SiZHQz0QpyyQV/b48Pb) + #componentToken: ENC(rkxj0733WxFFDLgA9x01m2s5Fi2L+0PC) + #componentAesKey: ENC(EIbJUBpbYOrLb4YQ/HXLQmxlxgAqIp2ZmpnGICC8pu5xiTz3Cqfkbwd2S8raCcK/IvYcX2GmedI=) + redis: + host: r-2zeaglwf13qqmnllj5pd.redis.rds.aliyuncs.com + port: 6379 + password: mallone:iF0rm@2l2ol9 + timeout: 3600 + expire: 1800 #30分钟 + database: 2 + defaultExpiration: 2592000 # 默认生命周期30天 + jedis: + pool: + max-active: 100 + max-idle: 100 + max-wait: -1 + min-idle: 10 + +fm: + exception: true + exception_emails: houtaikaifa@iformall.com + deploy: 3 + open: true + upload_dir: /root/uploads/ + +ueditor: + config: config.json + unified: true + upload-path: ./upload/ + url-prefix: "" + +logging: + level: + com.iformall.mapper: debug + path: ./logs/admin \ No newline at end of file diff --git a/mallinkPublicApi/src/main/resources/application-test.yml-bak b/mallinkPublicApi/src/main/resources/application-test.yml-bak new file mode 100644 index 000000000..4aa4daf72 --- /dev/null +++ b/mallinkPublicApi/src/main/resources/application-test.yml-bak @@ -0,0 +1,100 @@ +spring: + profiles: + include: rabbitMQ + # JDBC + datasource: + url: jdbc:mysql://formalldb.cfqyqflkdlit.rds.cn-northwest-1.amazonaws.com.cn:3306/mallinkTest?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false&useAffectedRows=true + username: ENC(Uc0AjgkytxHHCwZrmDASWg==) + password: ENC(nV4Mi3bEbBx0Fj7uUyYH55eTaqsFMjKvmNzagicH4pc=) + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + filters: stat + maxActive: 20 + initialSize: 1 + maxWait: 60000 + minIdle: 1 + timeBetweenEvictionRunsMillis: 28000 + minEvictableIdleTimeMillis: 28000 + validationQuery: select 'x' + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + poolPreparedStatements: true + maxOpenPreparedStatements: 20 + # REDIS + redis: + host: 127.0.0.1 + port: 6379 + password: ENC(QFwqv3NshvvGhFPiP8rwhvbnxk+rFSqhJi8Pw6TogSg=) + timeout: 3600 + expire: 1800 #30分钟 + database: 1 + defaultExpiration: 2592000 # 默认生命周期30天 + jedis: + pool: + max-active: 8 + max-idle: 8 + max-wait: -1 + min-idle: 0 + # EMAIL + mail: + host: smtp.exmail.qq.com + username: ENC(TtJQE2C4cWo8CpVGMMl5hiy5eykepd96J5XjsW3Kj5fgP+6+5ctDNQ==) + password: ENC(Zh6A6kRtA2ABtH6nzdvsqQSO1/8p3mparJr8neI2BLU=) # 授权密码 + properties: + mail: + smtp: + auth: true + starttls: + enable: true + socketFactory: + port: 465 + class: javax.net.ssl.SSLSocketFactory + # RABBITMQ + rabbitmq: + host: 127.0.0.1 + port: 5672 + username: ENC(aSRr6mnSryEqzHHz1hJf1g==) + password: ENC(GnjF/mdqKdvmDYC0tIIso7+20/jBALPw39tiWCYJ4iw=) + publisher-confirms: true + publisher-returns: false + virtual-host: / + +aws: + clientRegion: cn-northwest-1 + bucketName: iformall-net + access: ENC(NCLcmjwKpAWdn/abD17OKIY7yKepVLWzEpqRYUlURCw=) + secret: ENC(TRcZqql0Rq5PExlMeH/4WiZ/i02b8FXKmLTBChJmbluTa1uoLS9LrHyNEMrqe1DK+QgOAdvqGBo=) + +wechat: + open: + componentAppId: ENC(hnH31XdNzArfMfikKgBFpqQuJUl6rVOPFFcAVyb595o=) + componentSecret: ENC(t/GUVX5L+U7LCwSvZ5WmxAnmTMrc/Uy1nljsrokuzzsBL2j6BEVnrS/n7DCdGc/G) + componentToken: ENC(gUF1m0YgCCI2IY54fX6sGLZVlCswA8tG) + componentAesKey: ENC(TYHcrIkICtfrCfGq4HW6s1b/pHf7OD2uyPN11SzJNB0acqJ/4JVX1nuljo/cAtgMG4O05TImLQc=) + redis: + host: 127.0.0.1 + port: 6379 + password: ENC(QFwqv3NshvvGhFPiP8rwhvbnxk+rFSqhJi8Pw6TogSg=) + timeout: 3600 + expire: 1800 #30分钟 + database: 2 + defaultExpiration: 2592000 # 默认生命周期30天 + jedis: + pool: + max-active: 100 + max-idle: 100 + max-wait: -1 + min-idle: 10 + +fm: + exception: true + exception_emails: houtaikaifa@iformall.com + deploy: 2 + open: true + upload_dir: /home/ec2-user/server/uploads/ + +logging: + level: + com.iformall: debug + path: ./logs/c \ No newline at end of file diff --git a/mallinkPublicApi/src/main/resources/application.yml b/mallinkPublicApi/src/main/resources/application.yml new file mode 100644 index 000000000..324f91cde --- /dev/null +++ b/mallinkPublicApi/src/main/resources/application.yml @@ -0,0 +1,54 @@ +server: + port: 7000 + servlet: + context-path: /C + +spring: + application: + name: mallink + profiles: + active: dev + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + default-property-inclusion: non_null +# rocketmq: +# nameServer: 127.0.0.1:9876 +# producer: +# retry-times-when-send-async-failed: 0 +# send-msg-timeout: 300000 +# compress-msg-body-over-howmuch: 4096 +# max-message-size: 4194304 +# retry-another-broker-when-not-store-ok: false +# retry-times-when-send-failed: 2 + +# MybatisPlus +mybatis-plus: + mapper-locations: classpath:mapper/*Mapper.xml + global-config: + db-config: + id-type: id_worker + field-strategy: not_null + db-type: mysql + configuration: + jdbc-type-for-null: 'null' + cache-enabled: false + call-setters-on-nulls: true + type-aliases-package: com.iformall.domain.po + type-enums-package: com.iformall.enums + +# PageHelper +pagehelper: + helperDialect: mysql + reasonable: false + supportMethodsArguments: true + params: count=countSql + offset-as-page-num: true + page-size-zero: true + row-bounds-with-count: true + +mapper: + mappers: + - com.iformall.common.CommonMapper + +version: @project.version@ \ No newline at end of file diff --git a/mallinkPublicApi/src/main/resources/imgTemp/gertificate.jpg b/mallinkPublicApi/src/main/resources/imgTemp/gertificate.jpg new file mode 100644 index 000000000..6766d3149 Binary files /dev/null and b/mallinkPublicApi/src/main/resources/imgTemp/gertificate.jpg differ diff --git a/mallinkPublicApi/src/main/resources/logback-spring.xml b/mallinkPublicApi/src/main/resources/logback-spring.xml new file mode 100644 index 000000000..3a54d117d --- /dev/null +++ b/mallinkPublicApi/src/main/resources/logback-spring.xml @@ -0,0 +1,100 @@ + + + + + + + + [%date{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] --%mdc{client}%msg%n + + + + + ${logPath}/trace.log + + ${logPath}/daily/trace.%d{yyyy-MM-dd}.log + 30 + + + [%date{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%logger:%line]--%mdc{client} %msg%n + + + + + ${logPath}/info.log + + ${logPath}/daily/info.%d{yyyy-MM-dd}.log + 30 + + + [%date{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%logger:%line]--%mdc{client} %msg%n + + + INFO + ACCEPT + DENY + + + + + ${logPath}/debug.log + + ${logPath}/daily/debug.%d{yyyy-MM-dd}.log + 30 + + + [%date{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%logger:%line]--%mdc{client} %msg%n + + + DEBUG + ACCEPT + DENY + + + + + + ${logPath}/warn.log + + ${logPath}/daily/warn.%d{yyyy-MM-dd}.log + 30 + + + [%date{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%logger:%line]--%mdc{client} %msg%n + + + WARN + ACCEPT + DENY + + + + + + + ${logPath}/error.log + + ${logPath}/daily/error.%d{yyyy-MM-dd}.log + 30 + + + [%date{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%logger:%line]--%mdc{client} %msg%n + + + ERROR + ACCEPT + DENY + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mallinkPublicApi/src/main/resources/opt/HWLS.TTF b/mallinkPublicApi/src/main/resources/opt/HWLS.TTF new file mode 100644 index 000000000..bd038fd18 Binary files /dev/null and b/mallinkPublicApi/src/main/resources/opt/HWLS.TTF differ diff --git a/pom.xml b/pom.xml index 0b3a850db..8318e1289 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ mallinkSchedule mallinkMQConsumer mallinkWebSocketServer + mallinkPublicApi