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