[
  {
    "path": "README.md",
    "content": "# springboot-weapp-demo\n微信小程序服务端接口，支持普通Http请求、上传文件、长连接,微信登录及敏感数据解密。后台服务使用springboot框架搭建，mongodb做数据库，redis做缓存。\n\n运行环境：JDK8+\n\n<font color=\"red\">注意：如果你是本地运行，需要修改为你本地对应的主机和端口。</font>\n长连接需使用ws协议\n\n####更新日志:\n- 2016-12-18\n\t- 拦截器记录口访问日志存储mongodb\n\t\n- 2016-11-24\n\t- 小程序code换取session_key和openid\n\t- 小程序登录用户敏感数据解密\n\t\n- 2016-11-22\n\t- 配置Https\n\t\n- 2016-11-18\n\t- 重写小程序http测试和上传文件接口\n\t- 统一接口返回返回状态码和格式\n\t\n- 2016-11-20\n\t- 添加Redis缓存\n\t- 添加微信登录状态维护和用户数据解密接口\n\n#### 一、测试小程序wx.request接口\n```javascript\n\nwx.request({\n\turl: 'http://localhost:9090/weappservice/api/v1/user/get/{id}',\n\t\n    data: {appId: 'JWEJIJ345QHWJKENVKF', apiName: 'GET_USER'},\n    \n    method: 'GET',\n    \n    //return JSON format,like: {\"id\":\"{id}\"}\n    success: function(res){\n\t\tconsole.log(res.data);\n    },\n    fail: function(res){\n    \n    },\n    complete: function(res){\n    \n    }\n});\n```\n\n#### 二、测试小程序wx.uploadFile接口,单张上传\n```javascript\n\nwx.uploadFile({\n    url: 'http://localhost:9090/weappservice/api/v1/upload/image',\n    \n    //文件临时路径\n    filePath: tempFilePath,\n    \n    name: 'file',\n    \n    header: {},\n    \n    formData: {appId: 'JWEJIJ345QHWJKENVKF', apiName: 'UPLOAD_IMAGE'},\n    \n    success: function(res){\n      console.log(res.data)\n    },\n    \n    fail: function(res){\n    \n    },\n    \n    complete: function(res){\n    \n    }\n})；\n```\n\n<table>\n\t<tr>\n\t\t<th>状态码(errcode)</th>\n\t\t<th>说明(msg)</th>\n\t</tr>\n\t<tr>\n\t\t<td>0</td>\n\t\t<td>图片路径</td>\n\t</tr>\n\t<tr>\n\t\t<td>40010</td>\n\t\t<td>请选择上传文件!</td>\n\t</tr>\n\t<tr>\n\t\t<td>40011</td>\n\t\t<td>文件上传失败</td>\n\t</tr>\n</table>\n\n#### 三、测试小程序websocket相关接口\n```javascript\n\n//发起websocket连接\nwx.connectSocket({\n\turl: 'ws://localhost:9090/weappservice/websocket?name=xiaoqiang',\n  \t//这里写了参数，但是参数没有发送出去，大家可以试试，已经邮件反馈微信团队了，等待回复。所以把参数拼接在url后面。\n  \tdata: {\n  \t\t'name1': 'xiaoqiang1'\n  \t}\n}),\n\n//监听打开事件\nwx.onSocketOpen(function(res) {\n  \tconsole.log('WebSocket连接已打开！');\n}),\n\n//接收消息，接收的消息是json字符串，需要JSON.parse转成JSON对象\nwx.onSocketMessage(function(res){\n\tvar data = JSON.parse(res.data);\n\tconsole.log(data);\n}),\n\n//发送消息,消息对象属性key(user和content)不能自定义。\nwx.sendSocketMessage({\n    data: JSON.stringify({\n      user: 'xiaoqaing',\n      content: 'Hi, My name is xiaoqiang'\n    }),\n    success: function(res){\n    \tconsole.log('消息发送成功！')\n    }\n})\n```\n\n我的微信小程序DEMO：[weixin_smallexe](https://github.com/cocoli/weixin_smallexe)\n\nPS:我的公众号：\n\n![](https://github.com/cocoli/weixin_smallexe/blob/master/screenshot/dingyuhao.JPG?raw=true)\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.wxapp</groupId>\n\t<artifactId>wxapp</artifactId>\n\t<version>1.0.0</version>\n\t<packaging>jar</packaging>\n\n\t<name>wxchatdemo1.0.0</name>\n\t<description>wechat app API Project</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.4.2.RELEASE</version>\n\t\t<relativePath />\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t\t<bcprov.version>1.54</bcprov.version>\n\t\t<swagger2.version>2.2.2</swagger2.version>\n\t\t<jsoup.version>1.8.3</jsoup.version>\n\t</properties>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter</artifactId>\n\t\t</dependency>\n\t\t<!-- 长连接 -->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-websocket</artifactId>\n\t\t</dependency>\n\t\t<!-- mongoDB -->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-data-mongodb</artifactId>\n\t\t</dependency>\n\t\t<!-- redis -->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-redis</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-aop</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t<!-- alibaba fastjson -->\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>fastjson</artifactId>\n\t\t\t<version>1.2.31</version>\n\t\t</dependency>\n\t\t<!-- lombok -->\n\t\t<dependency>\n\t\t\t<groupId>org.projectlombok</groupId>\n\t\t\t<artifactId>lombok</artifactId>\n\t\t</dependency>\n\t\t<!-- google guava -->\n\t\t<dependency>\n\t\t\t<groupId>com.google.guava</groupId>\n\t\t\t<artifactId>guava</artifactId>\n\t\t\t<version>19.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-logging</groupId>\n\t\t\t<artifactId>commons-logging</artifactId>\n\t\t\t<version>1.2</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t<artifactId>commons-lang3</artifactId>\n\t\t\t<version>3.4</version>\n\t\t</dependency>\n\t\t<!-- http -->\n\t\t<dependency>\n\t\t\t<groupId>org.apache.httpcomponents</groupId>\n\t\t\t<artifactId>httpclient</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.httpcomponents</groupId>\n\t\t\t<artifactId>httpcore</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-codec</groupId>\n\t\t\t<artifactId>commons-codec</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.bouncycastle</groupId>\n\t\t\t<artifactId>bcprov-jdk15on</artifactId>\n\t\t\t<version>${bcprov.version}</version>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>io.springfox</groupId>\n\t\t\t<artifactId>springfox-swagger2</artifactId>\n\t\t\t<version>${swagger2.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.springfox</groupId>\n\t\t\t<artifactId>springfox-swagger-ui</artifactId>\n\t\t\t<version>${swagger2.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.jsoup</groupId>\n\t\t\t<artifactId>jsoup</artifactId>\n\t\t\t<version>${jsoup.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter</artifactId>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t\t<artifactId>spring-boot-starter-logging</artifactId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>\n"
  },
  {
    "path": "src/main/java/com/weapp/Application.java",
    "content": "package com.weapp;\n\nimport java.io.IOException;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.apache.catalina.connector.Connector;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;\nimport org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.core.io.support.PropertiesLoaderUtils;\nimport org.springframework.util.SocketUtils;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.google.common.collect.Maps;\nimport com.weapp.common.constant.ApiConstant;\nimport com.weapp.common.properties.WxAuth;\nimport com.weapp.entity.auth.AppKey;\nimport com.weapp.repository.AppKeyRepository;\n\n/**\n *\n * @author Shanqiang Ke\n * @version 1.0.0\n * @blog http://nosqlcoco.cnblogs.com\n * @since 2016-10-15\n */\n@SpringBootApplication\n@ComponentScan(value = \"com.weapp\")\n@EnableConfigurationProperties(value={WxAuth.class})\npublic class Application implements CommandLineRunner{\n\t@Autowired\n\tprivate AppKeyRepository repository;\n\n\tprivate static ImmutableMap<String, String>errorCodeMap = null;\n\tstatic {\n\t\ttry {\n\t\t\tProperties prop = PropertiesLoaderUtils.loadAllProperties(\"error_code.properties\");\n\t\t\terrorCodeMap = Maps.fromProperties(prop);\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class, args);\n\t}\n\n\t@Override\n\tpublic void run(String... arg0) throws Exception {\n\t\trepository.deleteAll();\n\t\tString[] apiNames = new String[]{ApiConstant.GET_USER,ApiConstant.POST_USER,ApiConstant.PUT_USER,\n\t\t\t\tApiConstant.DELETE_USER,ApiConstant.WX_CODE,ApiConstant.WX_DECODE_USERINFO,\n\t\t\t\tApiConstant.WX_CLUB_ARTICLES,ApiConstant.WX_CLUB_SEARCH};\n\t\t\n\t\tMap<String, Map<String,Integer>> apiMap = Maps.newHashMap();\n\t\tfor(String apiName : apiNames){\n\t\t\tMap<String,Integer>tmpMap = new HashMap<String,Integer>();\n\t\t\ttmpMap.put(\"calltimes\", 0);\n\t\t\ttmpMap.put(\"alltimes\", 10000);\n\t\t\tapiMap.put(apiName, tmpMap);\n\t\t}\n\t\trepository.save(new AppKey(\"JWEJIJ345QHWJKENVKF\", \"sdsd\", new Date(), new Date(), \"1\", false, apiMap));\n\t}\n\t@Bean\n\tpublic ImmutableMap<String, String> errorCodeMap(){\n\t\treturn errorCodeMap;\n\t}\n\n\t@Bean\n\tpublic EmbeddedServletContainerFactory servletContainer() {\n\t\tTomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();\n\t\ttomcat.addAdditionalTomcatConnectors(createStandardConnector());\n\t\treturn tomcat;\n\t}\n\t@Bean\n\tpublic Integer port() {\n\t\treturn SocketUtils.findAvailableTcpPort();\n\t}\n\tprivate Connector createStandardConnector() {\n\t\tConnector connector = new Connector(\"org.apache.coyote.http11.Http11NioProtocol\");\n\t\tconnector.setScheme(\"http\");\n\t\tconnector.setPort(port());\n\t\treturn connector;\n\t}\n}"
  },
  {
    "path": "src/main/java/com/weapp/SwaggerConfig.java",
    "content": "package com.weapp;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.ResponseEntity;\n\nimport springfox.documentation.builders.PathSelectors;\nimport springfox.documentation.builders.RequestHandlerSelectors;\nimport springfox.documentation.spi.DocumentationType;\nimport springfox.documentation.spring.web.plugins.Docket;\nimport springfox.documentation.swagger2.annotations.EnableSwagger2;\n\n@Configuration\n@EnableSwagger2\npublic class SwaggerConfig {\n\t@Value(\"${server.context-path}\")\n\tprivate String pathMapping;\n\n\t@Bean\n\tpublic Docket createRestApi() {\n\t\tSystem.out.println(\"http://localhost:8080\" + pathMapping + \"/swagger-ui.html\");\n\t\treturn new Docket(DocumentationType.SWAGGER_2)\n\t\t\t\t.groupName(\"test\")\n\t\t\t\t.genericModelSubstitutes(ResponseEntity.class)\n\t\t\t\t.useDefaultResponseMessages(true)\n\t\t\t\t.forCodeGeneration(false)\n\t\t\t\t.pathMapping(pathMapping)\n\t\t\t\t.select()\n\t\t\t\t.apis(RequestHandlerSelectors.basePackage(\"com.weapp.controller\"))\n\t\t\t\t.paths(PathSelectors.any())\n\t\t\t\t.build();\n\t\t\t\t//.apiInfo(apiInfo());\n\t}\n//\tprivate ApiInfo apiInfo() {\n//\t\treturn new ApiInfoBuilder()\n//\t\t\t\t.title(\"微信小程序后台服务API\")\n//\t\t\t\t.description(\"更多小程序知识，请关注微信公众号『柯善强的随思笔记』\")\n//\t\t\t\t.termsOfServiceUrl(\"http://www.cnblogs.com/nosqlcoco/\")\n//\t\t\t\t.contact(\"柯善强\")\n//\t\t\t\t.version(\"1.0\").build();\n//\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/aop/ApiAspect.java",
    "content": "package com.weapp.aop;\n\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Pointcut;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StopWatch;\n\n@Aspect\n@Component\npublic class ApiAspect {\n\t\n\t@Pointcut(value=\"@annotation(com.weapp.common.annotation.Api)\")\n\tpublic void apiAspect(){\n\t\t\n\t}\n\t\n\t@Around(\"apiAspect()\")\n\tpublic Object doAround(ProceedingJoinPoint pjp) throws Exception{\n\t\tObject result = null;\n\t\t\n\t\tStopWatch sw = new StopWatch(getClass().getSimpleName());\n\t\ttry {\n\t\t\tsw.start(pjp.getSignature().getName());\n\t\t\tresult = pjp.proceed();\n\t\t\t\n\t\t} catch(Throwable e){\n\t\t\te.printStackTrace();\n\t\t}finally {\n\t\t\tsw.stop();\n\t\t}\n\t\treturn result;\n\t\t\n\t\t\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/common/aes/AES.java",
    "content": "package com.weapp.common.aes;\n\nimport java.io.UnsupportedEncodingException;\nimport java.security.AlgorithmParameters;\nimport java.security.InvalidAlgorithmParameterException;\nimport java.security.InvalidKeyException;\nimport java.security.Key;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.NoSuchProviderException;\nimport java.security.Security;\n\nimport javax.crypto.BadPaddingException;\nimport javax.crypto.Cipher;\nimport javax.crypto.IllegalBlockSizeException;\nimport javax.crypto.NoSuchPaddingException;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport org.apache.commons.codec.binary.Base64;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;  \n\npublic class AES {  \n\tpublic static boolean initialized = false;  \n\t\n\t/**\n\t * AES解密\n\t * @param content 密文\n\t * @return\n\t * @throws InvalidAlgorithmParameterException \n\t * @throws NoSuchProviderException \n\t */\n\tpublic byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException {\n\t\tinitialize();\n\t\ttry {\n\t\t\tCipher cipher = Cipher.getInstance(\"AES/CBC/PKCS7Padding\");\n\t\t\tKey sKeySpec = new SecretKeySpec(keyByte, \"AES\");\n\t\t\t\n\t\t\tcipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化 \n\t\t\tbyte[] result = cipher.doFinal(content);\n\t\t\treturn result;\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\te.printStackTrace();  \n\t\t} catch (NoSuchPaddingException e) {\n\t\t\te.printStackTrace();  \n\t\t} catch (InvalidKeyException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (IllegalBlockSizeException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (BadPaddingException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (NoSuchProviderException e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t} catch (Exception e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}  \n\t\n\tpublic static void initialize(){  \n        if (initialized) return;  \n        Security.addProvider(new BouncyCastleProvider());  \n        initialized = true;  \n    }\n\t//生成iv  \n    public static AlgorithmParameters generateIV(byte[] iv) throws Exception{  \n        AlgorithmParameters params = AlgorithmParameters.getInstance(\"AES\");  \n        params.init(new IvParameterSpec(iv));  \n        return params;  \n    }  \n}  "
  },
  {
    "path": "src/main/java/com/weapp/common/annotation/Api.java",
    "content": "package com.weapp.common.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n/**\n * API注解，使用在Controller控制器方法上\n * @author xiaoqiang\n *\n */\n\n@Target({ElementType.METHOD})  \n@Retention(RetentionPolicy.RUNTIME)  \n@Documented \npublic @interface Api {\n\t/*接口名称*/\n\tString name();\n\t/*每天上限*/\n\tint accessLimit() default 10000;\n\t/*接口版本*/\t\n\tString version() default \"v1\";\n\t/*接口禁用*/\t\n\tboolean disabled() default false;\n\t/*参数解密算法*/\t\n\tString algorithm() default \"none\";\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/common/constant/ApiConstant.java",
    "content": "package com.weapp.common.constant;\n\n/**\n * API常量\n * @author xiaoqiang\n *\n */\npublic class ApiConstant {\n\t/*测试*/\t\n\tpublic final static String TEST_HTTP = \"TEST_HTTP\";\n\t\n\t/*获取用户信息*/\t\n\tpublic final static String GET_USER = \"GET_USER\";\n\t\n\t/*创建用户信息*/\t\n\tpublic final static String POST_USER = \"POST_USER\";\n\t\n\t/*更新用户信息*/\t\n\tpublic final static String PUT_USER = \"PUT_USER\";\n\t\n\t/*删除用户信息*/\t\n\tpublic final static String DELETE_USER = \"DELETE_USER\";\n\t\n\t/*上传图片*/\t\n\tpublic final static String UPLOAD_IMAGE = \"UPLOAD_IMAGE\";\n\t\n\t/*微信code*/\n\tpublic static final String WX_CODE = \"WX_CODE\";\n\t\n\t/*校验微信用户信息完整性*/\n\tpublic static final String WX_CHECK_USER = \"WX_CHECK_USER\";\n\n\t/*解密用户信息*/\n\tpublic static final String WX_DECODE_USERINFO = \"WX_DECODE_USERINFO\";\n\t\n\t/* club社区专栏 */\t\n\tpublic static final String WX_CLUB_ARTICLES = \"WX_CLUB_ARTICLES\";\n\t\n\t/* club社区专栏文章搜索 */\n\tpublic static final String WX_CLUB_SEARCH = \"WX_CLUB_SEARCH\";\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/common/properties/WxAuth.java",
    "content": "package com.weapp.common.properties;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\n\nimport lombok.Data;\n\n@ConfigurationProperties(prefix = \"wxapp\")\n@Data\npublic class WxAuth {\n\tprivate String appId;\n\t\n\tprivate String secret;\n\t\n\tprivate String grantType;\n\t\n\tprivate String sessionHost;\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/common/util/HttpRequest.java",
    "content": "/**\n *\n * @project ApiService\n * @filename HttpRequest.java\n * @date 2015年8月16日\n * @author KeShanqiang\n *\n */\n\npackage com.weapp.common.util;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintWriter;\nimport java.net.URL;\nimport java.net.URLConnection;\n\npublic class HttpRequest {\n\t/**\n\t * 向指定URL发送GET方法的请求\n\t * \n\t * @param url\n\t *            发送请求的URL\n\t * @param param\n\t *            请求参数，请求参数应该是 name1=value1&name2=value2 的形式。\n\t * @return URL 所代表远程资源的响应结果\n\t */\n\tpublic static String sendGet(String url, String param) {\n\t\tString result = \"\";\n\t\tBufferedReader in = null;\n\t\ttry {\n\t\t\tString urlNameString = url + \"?\" + param;\n\t\t\tURL realUrl = new URL(urlNameString);\n\t\t\t// 打开和URL之间的连接\n\t\t\tURLConnection connection = realUrl.openConnection();\n\t\t\t\n\t\t\t// 设置通用的请求属性\n\t\t\tconnection.setRequestProperty(\"accept\", \"*/*\");\n\t\t\tconnection.setRequestProperty(\"connection\", \"Keep-Alive\");\n\t\t\tconnection.setRequestProperty(\"user-agent\",\n\t\t\t\t\t\"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)\");\n\t\t\t// 建立实际的连接\n\t\t\tconnection.connect();\n\t\t\t// 定义 BufferedReader输入流来读取URL的响应\n\t\t\tin = new BufferedReader(new InputStreamReader(\n\t\t\t\t\tconnection.getInputStream()));\n\t\t\tString line;\n\t\t\twhile ((line = in.readLine()) != null) {\n\t\t\t\tresult += line;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\t// 使用finally块来关闭输入流\n\t\tfinally {\n\t\t\ttry {\n\t\t\t\tif (in != null) {\n\t\t\t\t\tin.close();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} catch (Exception e2) {\n\t\t\t\te2.printStackTrace();\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * 向指定 URL 发送POST方法的请求\n\t * \n\t * @param url\n\t *            发送请求的 URL\n\t * @param param\n\t *            请求参数，请求参数应该是 name1=value1&name2=value2 的形式。\n\t * @return 所代表远程资源的响应结果\n\t */\n\tpublic static String sendPost(String url, String param) {\n\t\tPrintWriter out = null;\n\t\tBufferedReader in = null;\n\t\tString result = \"\";\n\t\ttry {\n\t\t\tURL realUrl = new URL(url);\n\t\t\t// 打开和URL之间的连接\n\t\t\tURLConnection conn = realUrl.openConnection();\n\t\t\t// 设置通用的请求属性\n\t\t\tconn.setRequestProperty(\"authorization\", \"Authorization: Basic token=0abf1040cda747f1bd724719fd2c8496\");\n\t\t\tconn.setRequestProperty(\"accept\", \"*/*\");\n\t\t\tconn.setRequestProperty(\"connection\", \"Keep-Alive\");\n\t\t\tconn.setRequestProperty(\"user-agent\",\n\t\t\t\t\t\"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)\");\n\t\t\t// 发送POST请求必须设置如下两行\n\t\t\tconn.setDoOutput(true);\n\t\t\tconn.setDoInput(true);\n\t\t\t// 获取URLConnection对象对应的输出流\n\t\t\tout = new PrintWriter(conn.getOutputStream());\n\t\t\tout.print(param);\n\t\t\t// flush输出流的缓冲\n\t\t\tout.flush();\n\t\t\t// 定义BufferedReader输入流来读取URL的响应\n\t\t\tin = new BufferedReader(\n\t\t\t\t\tnew InputStreamReader(conn.getInputStream()));\n\t\t\tString line;\n\t\t\twhile ((line = in.readLine()) != null) {\n\t\t\t\tresult += line;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\t//使用finally块来关闭输出流、输入流\n\t\tfinally{\n\t\t\ttry{\n\t\t\t\tif(out!=null){\n\t\t\t\t\tout.close();\n\t\t\t\t}\n\t\t\t\tif(in!=null){\n\t\t\t\t\tin.close();\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch(IOException ex){\n\t\t\t\tex.printStackTrace();\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}    \n}"
  },
  {
    "path": "src/main/java/com/weapp/common/util/MongoPageable.java",
    "content": "package com.weapp.common.util;\n\nimport java.io.Serializable;\n\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.domain.Sort;\n\npublic class MongoPageable implements Serializable, Pageable{\n\n\t/**\n\t * mongodb分页\n\t */\n\tprivate static final long serialVersionUID = 1L;\n\t// 当前页  \n    private Integer pagenumber = 1;  \n    // 当前页面条数  \n    private Integer pagesize = 10;  \n   \n\t@Override\n\tpublic int getPageNumber() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getPageSize() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getOffset() {\n\t\treturn 0;\n\t}\n\t\n\t@Override\n\tpublic Pageable next() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Pageable previousOrFirst() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Pageable first() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean hasPrevious() {\n\t\treturn false;\n\t}\n\n\tpublic Integer getPagenumber() {\n\t\treturn pagenumber;\n\t}\n\n\tpublic void setPagenumber(Integer pagenumber) {\n\t\tthis.pagenumber = pagenumber;\n\t}\n\n\tpublic Integer getPagesize() {\n\t\treturn pagesize;\n\t}\n\n\tpublic void setPagesize(Integer pagesize) {\n\t\tthis.pagesize = pagesize;\n\t}\n\n\t@Override\n\tpublic Sort getSort() {\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/controller/AppUserController.java",
    "content": "package com.weapp.controller;\n\nimport java.util.Map;\n\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.weapp.common.annotation.Api;\nimport com.weapp.common.constant.ApiConstant;\n\n@RestController\n@RequestMapping\npublic class AppUserController {\n\t\n\t@Api(name=ApiConstant.GET_USER)\n\t@RequestMapping(value = \"/api/v1/user/{id}\", method = RequestMethod.GET, produces = \"application/json\")\n\tpublic Map<String, String> get(@PathVariable String id){\n\t\tImmutableMap<String, String> map = ImmutableMap.of(\"id\", id);\n\t\treturn map;\n\t}\n\t\n\t@Api(name=ApiConstant.POST_USER)\n\t@RequestMapping(value = \"/api/v1/user/{id}\", method = RequestMethod.POST, produces = \"application/json\")\n\tpublic Map<String, String> post(@PathVariable String id){\n\t\tImmutableMap<String, String> map = ImmutableMap.of(\"id\", id);\n\t\treturn map;\n\t}\n\t\n\t@Api(name=ApiConstant.PUT_USER)\n\t@RequestMapping(value = \"/api/v1/user/{id}\", method = RequestMethod.PUT, produces = \"application/json\")\n\tpublic Map<String, String> put(@PathVariable String id){\n\t\tImmutableMap<String, String> map = ImmutableMap.of(\"id\", id);\n\t\treturn map;\n\t}\n\t\n\t@Api(name=ApiConstant.DELETE_USER)\n\t@RequestMapping(value = \"/api/v1/user/{id}\", method = RequestMethod.DELETE, produces = \"application/json\")\n\tpublic Map<String, String> delete(@PathVariable String id){\n\t\tImmutableMap<String, String> map = ImmutableMap.of(\"id\", id);\n\t\treturn map;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/controller/BaseController.java",
    "content": "package com.weapp.controller;\n\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport com.google.common.collect.ImmutableMap;\n\n@Component\npublic abstract class BaseController {\n\t@Autowired\n\tprivate ImmutableMap<String, String> errorCodeMap;\n\n\t/**\n\t * 接口数据返回\n\t * @param errorCode\n\t * @param data\n\t * @return\n\t */\n\tprotected Map<String,Object> rtnParam(Integer errorCode,Object data) {\n\t\t//正常的业务逻辑 \n\t\tif(errorCode == 0){\n\t\t\treturn ImmutableMap.of(\"errorCode\", errorCode,\"data\", (data == null)? new Object() : data);\n\t\t}else{\n\t\t\treturn ImmutableMap.of(\"errorCode\", errorCode, \"msg\", errorCodeMap.get(String.valueOf(errorCode)));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/controller/UploadController.java",
    "content": "package com.weapp.controller;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Map;\n\nimport org.apache.commons.lang3.RandomStringUtils;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.util.FileCopyUtils;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.weapp.common.annotation.Api;\nimport com.weapp.common.constant.ApiConstant;\n\n\n@RestController\npublic class UploadController extends BaseController{\n\t//文件存储路径\n\t@Value(\"${img.local.path}\")\n\tprivate String imgLocalPath;\n\t//文件网络访问路径\n\t@Value(\"${img.host}\")\n\tprivate String imgHost;\n\t\n\t/**\n\t * 上传文件\n\t * @param file\n\t * @return\n\t */\n\t@Api(name = ApiConstant.UPLOAD_IMAGE)\n\t@RequestMapping(value = \"/api/v1/upload/image\", method = RequestMethod.POST, produces = \"application/json\")\n\tpublic Map<String,Object> uploadImage(@RequestParam(required=true,value=\"file\")MultipartFile file){\n\t\tif(null == file){\n\t\t\treturn rtnParam(40010, null);\n\t\t}\n\t\tString random = RandomStringUtils.randomAlphabetic(16);\n\t\tString fileName = random + \".jpg\";\n\t\ttry {\n\t\t\tString uploadDirName = imgLocalPath.substring(imgLocalPath.lastIndexOf(\"/\"), imgLocalPath.length());\n\t\t\tFileCopyUtils.copy(file.getBytes(), new File(imgLocalPath + \"/\", fileName));\n\t\t\treturn rtnParam(0, ImmutableMap.of(\"url\", imgHost + uploadDirName + \"/\" + fileName));\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn rtnParam(40011, null);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/controller/WxAuthController.java",
    "content": "package com.weapp.controller;\n\nimport java.io.UnsupportedEncodingException;\nimport java.security.InvalidAlgorithmParameterException;\nimport java.util.Arrays;\nimport java.util.Map;\n\nimport org.apache.commons.codec.binary.Base64;\nimport org.apache.commons.codec.digest.DigestUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.weapp.common.aes.AES;\nimport com.weapp.common.annotation.Api;\nimport com.weapp.common.constant.ApiConstant;\nimport com.weapp.redis.RedisUtil;\nimport com.weapp.service.WxService;\n\nimport io.swagger.annotations.ApiImplicitParam;\nimport io.swagger.annotations.ApiOperation;\n/**\n * 微信用户认证相关\n * @author xiaoqiang\n *\n */\n@RestController\npublic class WxAuthController extends BaseController{\n\t@Autowired\n\tprivate WxService wxService;\n\t@Autowired\n\tprivate RedisUtil redisUtil;\n\n\t/**\n\t * 根据客户端传过来的code从微信服务器获取appid和session_key，然后生成3rdkey返回给客户端，后续请求客户端传3rdkey来维护客户端登录态\n\t * @param wxCode\t小程序登录时获取的code\n\t * @return\n\t */\n\t@ApiOperation(value = \"获取sessionId\", notes = \"小用户允许登录后，使用code 换取 session_key api，将 code 换成 openid 和 session_key\")\n\t@ApiImplicitParam(name = \"code\", value = \"用户登录回调内容会带上 \", required = true, dataType = \"String\")\n\t@Api(name = ApiConstant.WX_CODE)\n\t@RequestMapping(value = \"/api/v1/wx/getSession\", method = RequestMethod.GET, produces = \"application/json\")\n\tpublic Map<String,Object> createSssion(@RequestParam(required = true,value = \"code\")String wxCode){\n\t\tMap<String,Object> wxSessionMap = wxService.getWxSession(wxCode);\n\n\t\tif(null == wxSessionMap){\n\t\t\treturn rtnParam(50010, null);\n\t\t}\n\t\t//获取异常\n\t\tif(wxSessionMap.containsKey(\"errcode\")){\n\t\t\treturn rtnParam(50020, null);\n\t\t}\n\t\tString wxOpenId = (String)wxSessionMap.get(\"openid\");\n\t\tString wxSessionKey = (String)wxSessionMap.get(\"session_key\");\n\t\tSystem.out.println(wxSessionKey);\n\t\tLong expires = Long.valueOf(String.valueOf(wxSessionMap.get(\"expires_in\")));\n\t\tString thirdSession = wxService.create3rdSession(wxOpenId, wxSessionKey, expires);\n\t\treturn rtnParam(0, ImmutableMap.of(\"sessionId\",thirdSession));\n\t}\n\n\t/**\n\t * 验证用户信息完整性\n\t * @param rawData\t微信用户基本信息\n\t * @param signature\t数据签名\n\t * @param sessionId\t会话ID\n\t * @return\n\t */\n\t@Api(name = ApiConstant.WX_CHECK_USER)\n\t@RequestMapping(value = \"/api/v1/wx/checkUserInfo\", method = RequestMethod.GET, produces = \"application/json\")\n\tpublic Map<String,Object> checkUserInfo(@RequestParam(required = true,value = \"rawData\")String rawData,\n\t\t\t@RequestParam(required = true,value = \"signature\")String signature,\n\t\t\t@RequestParam(required = true,defaultValue = \"sessionId\")String sessionId){\n\t\tObject wxSessionObj = redisUtil.get(sessionId);\n\t\tif(null == wxSessionObj){\n\t\t\treturn rtnParam(40008, null);\n\t\t}\n\t\tString wxSessionStr = (String)wxSessionObj;\n\t\tString sessionKey = wxSessionStr.split(\"#\")[0];\n\t\tStringBuffer sb = new StringBuffer(rawData);\n\t\tsb.append(sessionKey);\n\n\t\tbyte[] encryData = DigestUtils.sha1(sb.toString());\n\t\tbyte[] signatureData = signature.getBytes();\n\t\tBoolean checkStatus = Arrays.equals(encryData, signatureData);\n\t\treturn rtnParam(0, ImmutableMap.of(\"checkPass\", checkStatus));\n\t}\n\n\t/**\n\t * 获取用户openId和unionId数据(如果没绑定微信开放平台，解密数据中不包含unionId)\n\t * @param encryptedData 加密数据\n\t * @param iv\t\t\t加密算法的初始向量\t\n\t * @param sessionId\t\t会话ID\n\t * @return\n\t */\n\t@Api(name = ApiConstant.WX_DECODE_USERINFO)\n\t@RequestMapping(value = \"/api/v1/wx/decodeUserInfo\", method = RequestMethod.GET, produces = \"application/json\")\n\tpublic Map<String,Object> decodeUserInfo(@RequestParam(required = true,value = \"encryptedData\")String encryptedData,\n\t\t\t@RequestParam(required = true,defaultValue = \"iv\")String iv,\n\t\t\t@RequestParam(required = true,defaultValue = \"sessionId\")String sessionId){\n\t\tSystem.out.println(encryptedData);\n\t\tSystem.out.println(iv);\n\t\t//从缓存中获取session_key\n\t\tObject wxSessionObj = redisUtil.get(sessionId);\n\t\tif(null == wxSessionObj){\n\t\t\treturn rtnParam(40008, null);\n\t\t}\n\t\tString wxSessionStr = (String)wxSessionObj;\n\t\tString sessionKey = wxSessionStr.split(\"#\")[0];\n\n\t\ttry {\n\t\t\tAES aes = new AES();\n\t\t\tbyte[] resultByte = aes.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv));\n\t\t\tif(null != resultByte && resultByte.length > 0){\n\t\t\t\tString userInfo = new String(resultByte, \"UTF-8\");\n\t\t\t\treturn rtnParam(0, userInfo);\n\t\t\t}\n\t\t} catch (InvalidAlgorithmParameterException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn rtnParam(50021, null);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/controller/WxClubController.java",
    "content": "package com.weapp.controller;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport com.weapp.common.annotation.Api;\nimport com.weapp.common.constant.ApiConstant;\nimport com.weapp.common.util.MongoPageable;\nimport com.weapp.entity.wxclub.Article;\nimport com.weapp.service.WxClubService;\n/**\n * 微信小程序Club专栏文章接口\n * @site http://www.wxappclub.com\n * @author xiaoqiang\n *\n */\n@RestController\npublic class WxClubController extends BaseController{\n\t@Autowired\n\tprivate WxClubService wxClubService;\n\t\n\t/**\n\t * 专栏文章列表\n\t * @param id\n\t * @param page\n\t * @return\n\t */\n\t@Api(name = ApiConstant.WX_CLUB_ARTICLES)\n\t@RequestMapping(value = \"/api/v1/wxclub/column/{id}/{page}\", method = RequestMethod.GET, produces = \"application/json\")\n\tpublic Map<String, ? extends Object>getArticles(@PathVariable String id,@PathVariable Integer page){\n\t\tList<Article>list = wxClubService.getByGroupPath(\"/column/\" + id);\n\t\treturn rtnParam(0, list);\n\t}\n\t/**\n\t * 文章搜索\n\t * @param pageNo\n\t * @param searchText\n\t * @return\n\t */\n\t@Api(name = ApiConstant.WX_CLUB_SEARCH)\n\t@RequestMapping(value = \"/api/v1/wxclub/column/search/{pageNo}\", method = RequestMethod.GET, produces = \"application/json\")\n\tpublic Map<String, ? extends Object>getArticles(@PathVariable(value=\"pageNo\",required=false) Integer pageNo,\n\t\t\t@RequestParam(required=false,value=\"text\",defaultValue=\"\")String searchText){\n\t\t\n\t\tMongoPageable page = new MongoPageable();\n\t\tpage.setPagenumber(pageNo);\n\t\tList<Article>list = wxClubService.getByTitle(searchText, page);\n\t\treturn rtnParam(0, list);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/entity/app/TUser.java",
    "content": "package com.weapp.entity.app;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n/**\n * App用户实体类\n * @author xiaoqiang\n *\n */\n@Document(collection=\"t_users\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class TUser {\n\t@Id\n\tprivate String id;\n\tprivate String appId;\n\tprivate String password;\n\tprivate String realName;\n\tprivate String nickName;\n\n\tprivate String gender;\n\tprivate String avatar;\n\tprivate String signature;\n\t\n\tprivate String wxOpenId;\n\tprivate String wxUnionId;\n\t\n\tprivate String birthday;\n\tprivate String address;\n\t\n\tprivate String createDate;\n\tprivate String updateDate;\n\tprivate String lastLoginDate;\n\tprivate Boolean disabled;\n\t\n\tprivate String phone;\n\tprivate Boolean isPhoneActive;\n\tprivate String email;\n\tprivate Boolean isEmailActive;\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/entity/auth/AccessLog.java",
    "content": "package com.weapp.entity.auth;\n\nimport java.util.Date;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n/**\n * API访问日志\n * @author xiaoqiang\n *\n */\n@Document(collection=\"t_access_logs\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class AccessLog {\n\t@Id\n\tprivate String id;\n\t/*api名称*/\t\n\tprivate String apiName;\n\t/*接口路径*/\n\tprivate String uri;\n\t/*访问时间*/\t\n\tprivate Date accessDate;\n\t/*请求参数*/\t\n\tprivate String reqParam;\n\t/*返回参数*/\t\n\tprivate String resParam;\n\t/*异常内容*/\t\n\tprivate String exp;\n\n\tpublic AccessLog(String apiName, String uri, Date accessDate, String reqParam, String resParam, String exp) {\n\t\tsuper();\n\t\tthis.apiName = apiName;\n\t\tthis.uri = uri;\n\t\tthis.accessDate = accessDate;\n\t\tthis.reqParam = reqParam;\n\t\tthis.resParam = resParam;\n\t\tthis.exp = exp;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/entity/auth/ApiInfo.java",
    "content": "package com.weapp.entity.auth;\n\nimport java.util.Date;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n/**\n * API接口管理\n * @author xiaoqiang\n *\n */\n@Document(collection=\"t_apis\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class ApiInfo {\n\t@Id\n\tprivate String id;\n\t/*接口名称*/\n\tprivate String name;\n\t/*接口地址*/\n\tprivate String uri;\n\t/*权限之和\n\t * Get = 1\n\t * POST = 2 \n\t * PUT = 4\n\t * DELETE = 8\n\t */\n\tprivate Integer crud;\n\t/*每天调用次数上限*/\n\tprivate Integer accessLimit;\n\t/*版本号*/\t\n\tprivate String version;\n\t/*是否可用*/\n\tprivate boolean disabled;\n\t/*解密算法*/\t\n\tprivate String algorithm;\n\t\n\tprivate Date createDate;\n\tpublic ApiInfo(String name,String uri, Integer accessLimit, String version, boolean disabled, String algorithm, Date createDate) {\n\t\tsuper();\n\t\tthis.name = name;\n\t\tthis.uri = uri;\n\t\tthis.accessLimit = accessLimit;\n\t\tthis.version = version;\n\t\tthis.disabled = disabled;\n\t\tthis.algorithm = algorithm;\n\t\tthis.createDate = createDate;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/entity/auth/AppKey.java",
    "content": "package com.weapp.entity.auth;\n\nimport java.util.Date;\nimport java.util.Map;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n/**\n * app认证实体\n * @author xiaoqiang\n *\n */\n@Document(collection=\"t_appkeys\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class AppKey {\n\t@Id\n\tprivate String id;\n\t/*分发的应用ID*/\n\tprivate String appId;\n\t/*密钥*/\t\n\tprivate String secretKey;\n\t/*创建日期*/\t\n\tprivate Date createDate;\n\t/*有效截止日期，为2030-01-01 00：00:00表示无日期限制*/\t\n\tprivate Date validDate;\n\t/*应用权限等级*/\t\n\tprivate String appGrade;\n\t/*是否禁用*/\t\n\tprivate Boolean disabled;\n\t/*拥有的api，及调用次数上限*/\t\n\tprivate Map<String, Map<String,Integer>> apis;\n\n\tpublic AppKey(String appId, String secretKey, Date createDate, Date validDate, String appGrade, Boolean disabled,\n\t\t\tMap<String, Map<String,Integer>> apis) {\n\t\tsuper();\n\t\tthis.appId = appId;\n\t\tthis.secretKey = secretKey;\n\t\tthis.createDate = createDate;\n\t\tthis.validDate = validDate;\n\t\tthis.appGrade = appGrade;\n\t\tthis.disabled = disabled;\n\t\tthis.apis = apis;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/entity/wxclub/Article.java",
    "content": "package com.weapp.entity.wxclub;\n\nimport java.util.Map;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Document(collection=\"t_articles\")\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Article {\n\t@Id\n\tprivate String id;\n\t\n\tprivate String path;\n\t\n\tprivate String groupPath;\n\t\n\tprivate String title;\n\t\n\tprivate String digest;\n\t\n\tprivate String createTime;\n\t\n\tprivate Integer browers;\n\t\n\tprivate Integer comments;\n\t\n\tprivate Map<String,String> author;\n\t\n\tprivate String mark;\n\t\n\tprivate String content;\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/interceptor/ApiInterceptor.java",
    "content": "package com.weapp.interceptor;\n\nimport java.io.PrintWriter;\nimport java.util.Date;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.servlet.HandlerInterceptor;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.weapp.entity.auth.AccessLog;\nimport com.weapp.entity.auth.ApiInfo;\nimport com.weapp.entity.auth.AppKey;\nimport com.weapp.service.AccessLogService;\nimport com.weapp.service.ApiInfoService;\nimport com.weapp.service.AppKeyService;\n\n/**\n * api接口拦截处理\n * @author xiaoqiang\n *\n */\npublic class ApiInterceptor implements HandlerInterceptor {\n\tprivate static ImmutableMap<String,Integer>methodMap = ImmutableMap.of(\"GET\", 1, \"POST\", 2, \"PUT\", 4, \"DELETE\", 8);\n\t@Autowired\n\tprivate AppKeyService appKeyService;\n\t@Autowired\n\tprivate ApiInfoService apiInfoService;\n\t@Autowired\n\tprivate ImmutableMap<String, String> errorCodeMap;\n\t@Autowired\n\tprivate AccessLogService accessLogService;\n\t\n\t@Override\n\tpublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception exp)\n\t\t\tthrows Exception {\n\t\tString apiName = request.getParameter(\"apiName\");\n\t\tif(!StringUtils.isEmpty(apiName)){\n\t\t\tAccessLog accessLog = new AccessLog();\n\t\t\taccessLog.setAccessDate(new Date());\n\t\t\taccessLog.setApiName(apiName);\n\t\t\taccessLog.setUri(request.getRequestURI());\n\t\t\tif(exp != null){\n\t\t\t\taccessLog.setExp(exp.getMessage());\n\t\t\t}\n\t\t\t//拼接请求参数，key1=value1&key2=value2的形式\n\t\t\tString paramStr = \"\";\n\t\t\tMap<String,String[]> params = request.getParameterMap();\n\t\t\tif (params != null && params.size() > 0) {\n\t\t\t\tfor(Map.Entry<String, String[]> p : params.entrySet()){\n\t\t\t\t\tif(p.getValue() == null || p.getValue().length == 0){\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tparamStr += p.getKey() + \"=\" + p.getValue()[0] + \"&\";\n\t\t\t\t}\n\t\t\t}\n\t\t\taccessLog.setReqParam(paramStr.substring(0, paramStr.length() - 1));\n\t\t\tSystem.out.println(accessLog.getReqParam());\n\t\t\taccessLogService.save(accessLog);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)\n\t\t\tthrows Exception {\n\n\t}\n\n\t@Override\n\tpublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {\n\n\t\tString apiName = request.getParameter(\"apiName\");\n\t\tif(null == apiName || \"\".equals(apiName)){\n\t\t\tresponse.setStatus(HttpServletResponse.SC_NOT_FOUND);\n\t\t\treturn false;\n\t\t}\n\t\tApiInfo apiInfo = apiInfoService.getByApiName(apiName);\n\t\tif(null == apiInfo){\n\t\t\tresponse.setStatus(HttpServletResponse.SC_NOT_FOUND);\n\t\t\treturn false;\n\t\t}\n\t\tresponse.setCharacterEncoding(\"UTF-8\");  \n\t\tresponse.setContentType(\"application/json; charset=utf-8\"); \n\t\tPrintWriter out = null;\n\t\t//判断接口状态；\n\t\tif(apiInfo.isDisabled()){\n\t\t\t//接口已禁用\n\t\t\tout = response.getWriter();\n\t\t\tout.append(getResStr(\"40003\"));\n\t\t\tout.flush();\n\t\t\tout.close();\n\t\t\treturn false;\n\t\t}\n\n\t\tString method = request.getMethod();\n\t\tif(Integer.compare(methodMap.get(method), apiInfo.getCrud()) != 0){\n\t\t\t//http method不匹配 apiInfo.getCrud()\n\t\t\tout = response.getWriter();\n\t\t\tout.append(getResStr(\"40005\"));\n\t\t\tout.flush();\n\t\t\tout.close();\n\t\t\treturn false;\n\t\t}\n\t\tString appId = request.getParameter(\"appId\");\n\t\tif(null == appId || \"\".equals(appId)){\n\t\t\tout = response.getWriter();\n\t\t\tout.append(getResStr(\"40001\"));\n\t\t\tout.flush();\n\t\t\tout.close();\n\t\t\treturn false;\n\t\t}\n\t\t//获取appid,请求是否合法\n\t\tAppKey appKey = appKeyService.getByAppId(appId);\n\t\tif(null == appKey){\n\t\t\tout = response.getWriter();\n\t\t\tout.append(getResStr(\"40001\"));\n\t\t\tout.flush();\n\t\t\tout.close();\n\t\t\treturn false;\n\t\t}\n\t\t//判断是否有接口调用权限\n\t\tMap<String,Map<String,Integer>>apiMap = appKey.getApis();\n\t\tif(null == apiMap || apiMap.size() == 0 || !apiMap.containsKey(apiName)){\n\t\t\t//无调用权限\n\t\t\tout = response.getWriter();\n\t\t\tout.append(getResStr(\"40006\"));\n\t\t\tout.flush();\n\t\t\tout.close();\n\t\t\treturn false;\n\t\t}\n\t\t//调用次数是否超出上限；\n\t\tMap<String,Integer>methodInfo = apiMap.get(apiName);\n\t\tif(methodInfo.get(\"calltimes\") > methodInfo.get(\"alltimes\")){\n\t\t\t//超出调用次数\n\t\t\tout = response.getWriter();\n\t\t\tout.append(getResStr(\"40007\"));\n\t\t\tout.flush();\n\t\t\tout.close();\n\t\t\treturn false;\n\t\t}\n\t\t//参数校验\n\n\t\t//通过，更新调用次数\n\t\tmethodInfo.put(\"calltimes\", methodInfo.get(\"calltimes\") + 1);\n\t\tapiMap.put(apiName, methodInfo);\n\t\tappKey.setApis(apiMap);\n\t\t//记录访问日志\n\t\tappKeyService.update(appKey);\n\t\treturn true;\n\t}\n\tprivate String getResStr(String errorCode){\n\t\treturn \"{\\\"errorCode\\\":\" + errorCode + \",\\\"msg\\\":\\\"\" + errorCodeMap.get(errorCode) + \"\\\"}\";\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/interceptor/ApiWebConfigure.java",
    "content": "package com.weapp.interceptor;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;\n\n/**\n * API接口拦截配置\n * @author xiaoqiang\n *\n */\n@Configuration\npublic class ApiWebConfigure extends WebMvcConfigurerAdapter {\n\t@Override\n\tpublic void addInterceptors(InterceptorRegistry registry){\n\t\tregistry.addInterceptor(getApiInterceptor())\n\t\t\t.addPathPatterns(\"/api/**\");\n\t}\n\t@Bean\n\tpublic ApiInterceptor getApiInterceptor(){\n\t\treturn new ApiInterceptor();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/listener/ApiServletContextListener.java",
    "content": "package com.weapp.listener;\n\nimport java.io.IOException;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.servlet.ServletContextEvent;\nimport javax.servlet.ServletContextListener;\nimport javax.servlet.annotation.WebListener;\n\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.WebApplicationContextUtils;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;\nimport org.springframework.web.servlet.mvc.method.RequestMappingInfo;\nimport org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.google.common.collect.Lists;\nimport com.weapp.common.annotation.Api;\nimport com.weapp.entity.auth.ApiInfo;\nimport com.weapp.service.ApiInfoService;\nimport com.weapp.service.WxClubService;\n/**\n * Get == 1\n * POST == 2 \n * PUT == 4\n * DELETE == 8\n * @author xiaoqiang\n *\n */\n@WebListener\n@Component\npublic class ApiServletContextListener implements ServletContextListener {\n\tprivate static ImmutableMap<String,Integer>methodMap = ImmutableMap.of(\"GET\", 1, \"POST\", 2, \"PUT\", 4, \"DELETE\", 8);\n\t@Override\n\tpublic void contextDestroyed(ServletContextEvent arg0) {\n\t\tSystem.out.println(\"destory\");\n\t}\n\n\t@Override\n\tpublic void contextInitialized(ServletContextEvent arg0) {\n\t\tWebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(arg0.getServletContext());\n\t\t\n\t\tApiInfoService apiInfoService = wc.getBean(ApiInfoService.class);\n\t\tWxClubService clubService = wc.getBean(WxClubService.class);\n\t\t\n\t\tRequestMappingHandlerMapping rmhp = wc.getBean(RequestMappingHandlerMapping.class);  \n\t\tMap<RequestMappingInfo, HandlerMethod> map = rmhp.getHandlerMethods();\n\t\tList<ApiInfo>apiInfolist = Lists.newArrayList();\n\t\tApiInfo apiInfo = null;\n\t\tDate curDate = new Date();\n\t\tfor(RequestMappingInfo info : map.keySet()){\n\t\t\tRequestMethodsRequestCondition requestMethodsRequestCondition = info.getMethodsCondition();\n\t\t\tSet<RequestMethod>methods = requestMethodsRequestCondition.getMethods();\n\t\t\tif(methods.size() == 0){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tHandlerMethod handlerMethod = map.get(info);\n\t\t\tif(!handlerMethod.hasMethodAnnotation(Api.class)){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tApi api = handlerMethod.getMethodAnnotation(Api.class);\n\t\t\t\n\t\t\tapiInfo = new ApiInfo();\n\t\t\tapiInfo.setUri(info.getPatternsCondition().getPatterns().toArray()[0].toString());\n\t\t\tapiInfo.setVersion(api.version());\n\t\t\tapiInfo.setName(api.name());\n\t\t\tapiInfo.setDisabled(api.disabled());\n\t\t\tapiInfo.setAlgorithm(api.algorithm());\n\t\t\tapiInfo.setAccessLimit(api.accessLimit());\n\t\t\tapiInfo.setCrud(methodMap.get(methods.toArray()[0].toString()));\n\t\t\tapiInfo.setCreateDate(curDate);\n\t\t\tapiInfolist.add(apiInfo);\n\t\t}  \n\t\tapiInfoService.deleteAll();\n\t\tapiInfoService.saveList(apiInfolist);\n\t\t\n\t\tclubService.deleteAll();\n\t\ttry {\n\t\t\tclubService.getWxClubColumn();\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/redis/MyRedisCacheConfig.java",
    "content": "package com.weapp.redis;\n\nimport java.lang.reflect.Method;\n\nimport org.springframework.cache.CacheManager;\nimport org.springframework.cache.annotation.CachingConfigurerSupport;\nimport org.springframework.cache.annotation.EnableCaching;\nimport org.springframework.cache.interceptor.KeyGenerator;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.cache.RedisCacheManager;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.PropertyAccessor;\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n@Configuration\n@EnableCaching\npublic class MyRedisCacheConfig extends CachingConfigurerSupport {\n\t\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\t@Bean\n\tpublic RedisTemplate<String, String> redisTemplate(\n\t\t\tRedisConnectionFactory factory) {\n\t\tRedisTemplate<String, String> template = new StringRedisTemplate(factory);\n\t\tJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);\n\t\t\n\t\tObjectMapper om = new ObjectMapper();\n\t\tom.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);\n\t\tom.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);\n\t\n\t\tjackson2JsonRedisSerializer.setObjectMapper(om);\n\t\t\n\t\ttemplate.setValueSerializer(jackson2JsonRedisSerializer);\n\t\ttemplate.afterPropertiesSet();\n\t\t\n\t\treturn template;\n\t}\n\t\n\t@Bean  \n\tpublic KeyGenerator wiselyKeyGenerator(){  \n\t\treturn new KeyGenerator() {  \n\t\t\t@Override  \n\t\t\tpublic Object generate(Object target, Method method, Object... params) {  \n\t\t\t\tStringBuilder sb = new StringBuilder();  \n\t\t\t\tsb.append(target.getClass().getName());  \n\t\t\t\tsb.append(method.getName());  \n\t\t\t\tfor (Object obj : params) {  \n\t\t\t\t\tsb.append(obj.toString());  \n\t\t\t\t}  \n\t\t\t\treturn sb.toString();  \n\t\t\t}  \n\t\t};  \n\t}  \n\n\t@Bean  \n\tpublic CacheManager cacheManager(  \n\t\t\t@SuppressWarnings(\"rawtypes\") RedisTemplate redisTemplate) {  \n\t\treturn new RedisCacheManager(redisTemplate);  \n\t}  \n\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/redis/RedisUtil.java",
    "content": "package com.weapp.redis;\n\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.data.redis.connection.RedisConnection;\nimport org.springframework.data.redis.core.RedisCallback;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.serializer.RedisSerializer;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.Assert;\n/**\n * Jedis增删改查操作\n * @author xiaoqiang\n *\n * @param <T>\n */\n@Service\npublic class RedisUtil {\n\t@Autowired(required=true)\n\tprivate RedisTemplate<String,String> redisTemplate;\n\n\t/**\n\t * 添加对象\n\t */\n\tpublic boolean add(final String key, final String value) {\n\t\tredisTemplate.execute(new RedisCallback<Object>() {  \n\t\t\t@Override  \n\t\t\tpublic Object doInRedis(RedisConnection connection)  \n\t\t\t\t\tthrows DataAccessException {  \n\t\t\t\tconnection.set(  \n\t\t\t\t\t\tredisTemplate.getStringSerializer().serialize(key),  \n\t\t\t\t\t\tredisTemplate.getStringSerializer().serialize(value));\n\t\t\t\treturn true;  \n\t\t\t}  \n\t\t});\n\t\treturn false;  \n\t}  \n\t/**\n\t * 添加对象\n\t */\n\tpublic boolean add(final String key, final Long expires, final String value) {\n\t\tredisTemplate.execute(new RedisCallback<Object>() {  \n\t\t\t@Override  \n\t\t\tpublic Object doInRedis(RedisConnection connection)  \n\t\t\t\t\tthrows DataAccessException {  \n\t\t\t\tconnection.setEx(\n\t\t\t\t\t\tredisTemplate.getStringSerializer().serialize(key), \n\t\t\t\t\t\texpires, \n\t\t\t\t\t\tredisTemplate.getStringSerializer().serialize(value)\n\t\t\t\t\t\t);\n\t\t\t\treturn true;  \n\t\t\t}  \n\t\t});\n\t\treturn false;  \n\t}  \n\t/**\n\t * 添加Map\n\t */\n\tpublic boolean add(final Map<String,String>map) {\n\t\tAssert.notEmpty(map);  \n\t\tboolean result = redisTemplate.execute(new RedisCallback<Boolean>() {  \n\t\t\tpublic Boolean doInRedis(RedisConnection connection)  \n\t\t\t\t\tthrows DataAccessException {  \n\t\t\t\tRedisSerializer<String> serializer = redisTemplate.getStringSerializer();  \n\t\t\t\tfor (Map.Entry<String, String> entry : map.entrySet()) {  \n\t\t\t\t\tbyte[] key  = serializer.serialize(entry.getKey());  \n\t\t\t\t\tbyte[] name = serializer.serialize(entry.getValue());  \n\t\t\t\t\tconnection.setNX(key, name);  \n\t\t\t\t}  \n\t\t\t\treturn true;  \n\t\t\t}  \n\t\t}, false, true);  \n\t\treturn result; \n\t}  \n\n\t/**\n\t * 删除对象 ,依赖key\n\t */\n\tpublic void delete(String key) {  \n\t\tredisTemplate.delete(key);\n\t}  \n\n\t/**\n\t * 修改对象 \n\t */\n\tpublic boolean update(final String key,final String value) {\n\t\tif (get(key) == null) {  \n\t\t\tthrow new NullPointerException(\"数据行不存在, key = \" + key);  \n\t\t}  \n\t\tboolean result = redisTemplate.execute(new RedisCallback<Boolean>() {  \n\t\t\tpublic Boolean doInRedis(RedisConnection connection)  \n\t\t\t\t\tthrows DataAccessException {  \n\t\t\t\tRedisSerializer<String> serializer = redisTemplate.getStringSerializer();  \n\t\t\t\tconnection.set(serializer.serialize(key), serializer.serialize(value));  \n\t\t\t\treturn true;  \n\t\t\t}  \n\t\t});  \n\t\treturn result;   \n\n\t}  \n\n\t/**\n\t * 根据key获取对象\n\t */\n\tpublic Object get(final String keyId) {\n\t\tObject result = redisTemplate.execute(new RedisCallback<Object>() {  \n\t\t\tpublic Object doInRedis(RedisConnection connection)  \n\t\t\t\t\tthrows DataAccessException {  \n\t\t\t\tRedisSerializer<String> serializer = redisTemplate.getStringSerializer();  \n\t\t\t\tbyte[] key = serializer.serialize(keyId);  \n\t\t\t\tbyte[] value = connection.get(key);  \n\t\t\t\tif (value == null) {  \n\t\t\t\t\treturn null;  \n\t\t\t\t}  \n\t\t\t\treturn serializer.deserialize(value);\n\t\t\t}  \n\t\t});  \n\t\treturn result;  \n\t} \n}\n"
  },
  {
    "path": "src/main/java/com/weapp/repository/AccessLogRepository.java",
    "content": "package com.weapp.repository;\n\nimport org.springframework.data.mongodb.repository.MongoRepository;\n\nimport com.weapp.entity.auth.AccessLog;\n/**\n * 访问日志操作\n * @author xiaoqiang\n *\n */\npublic interface AccessLogRepository extends MongoRepository<AccessLog, String> {\n\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/repository/ApiInfoRepository.java",
    "content": "package com.weapp.repository;\n\nimport org.springframework.data.mongodb.repository.MongoRepository;\n\nimport com.weapp.entity.auth.ApiInfo;\n\n/**\n * api管理操作\n * @author xiaoqiang\n *\n */\npublic interface ApiInfoRepository extends MongoRepository<ApiInfo, String> {\n\n\tApiInfo findByName(String apiName);\n\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/repository/AppKeyRepository.java",
    "content": "package com.weapp.repository;\n\nimport org.springframework.data.mongodb.repository.MongoRepository;\n\nimport com.weapp.entity.auth.AppKey;\n/**\n * appkey管理操作\n * @author xiaoqiang\n *\n */\npublic interface AppKeyRepository extends MongoRepository<AppKey, String> {\n\t\n\tAppKey findByAppId(String appId);\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/repository/WxClubRepository.java",
    "content": "package com.weapp.repository;\n\nimport java.util.List;\n\nimport org.springframework.data.mongodb.repository.MongoRepository;\n\nimport com.weapp.common.util.MongoPageable;\nimport com.weapp.entity.wxclub.Article;\n\npublic interface WxClubRepository extends MongoRepository<Article, String> {\n\n\tList<Article> findByGroupPath(String groupPath);\n\n\tList<Article> findByTitleLike(String title, MongoPageable page);\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/service/AccessLogService.java",
    "content": "package com.weapp.service;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport com.weapp.entity.auth.AccessLog;\nimport com.weapp.repository.AccessLogRepository;\n\n@Service\n@Transactional(readOnly=true)\npublic class AccessLogService {\n\t\n\t@Autowired\n\tprivate AccessLogRepository accessLogRepository;\n\t\n\t@Transactional(readOnly=false)\n\tpublic void save(AccessLog accessLog) {\n\t\taccessLogRepository.save(accessLog);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/service/ApiInfoService.java",
    "content": "package com.weapp.service;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport com.weapp.entity.auth.ApiInfo;\nimport com.weapp.repository.ApiInfoRepository;\n\n@Transactional(readOnly=false)\n@Service\npublic class ApiInfoService {\n\t@Autowired\n\tprivate ApiInfoRepository apiInfoRepository;\n\t\n\tpublic void deleteAll(){\n\t\tapiInfoRepository.deleteAll();\n\t}\n\t\n\tpublic void saveList(Iterable<ApiInfo>list){\n\t\tapiInfoRepository.save(list);\n\t}\n\t\n\tpublic ApiInfo getByApiName(String apiName){\n\t\treturn apiInfoRepository.findByName(apiName);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/service/AppKeyService.java",
    "content": "package com.weapp.service;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.mongodb.core.MongoTemplate;\nimport org.springframework.data.mongodb.core.query.Update;\n\nimport static org.springframework.data.mongodb.core.query.Criteria.where;\nimport static org.springframework.data.mongodb.core.query.Query.query;\n\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport com.weapp.entity.auth.AppKey;\nimport com.weapp.repository.AppKeyRepository;\n\n@Service\n@Transactional(readOnly=true)\npublic class AppKeyService {\n\t@Autowired\n\tAppKeyRepository appKeyRepository;\n\t@Autowired\n\tMongoTemplate mongoTemplate;\n\t\n\tpublic AppKey getByAppId(String appId){\n\t\treturn appKeyRepository.findByAppId(appId);\n\t}\n\t\n\tpublic void update(AppKey appKey){\n\t\tmongoTemplate.updateFirst(query(where(\"_id\").is(appKey.getId())), Update.update(\"apis\", appKey.getApis()), AppKey.class);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/service/WxClubService.java",
    "content": "package com.weapp.service;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jsoup.Jsoup;\nimport org.jsoup.nodes.Document;\nimport org.jsoup.nodes.Element;\nimport org.jsoup.select.Elements;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.weapp.common.util.MongoPageable;\nimport com.weapp.entity.wxclub.Article;\nimport com.weapp.repository.WxClubRepository;\n\n@Service\npublic class WxClubService {\n\t@Autowired\n\tprivate WxClubRepository clubRepository;\n\tprivate static final String CLUBHOST = \"http://www.wxappclub.com\";\n\t\n\tprivate static final String WX_CLUB_HOST = \"http://www.wxappclub.com\";\n\tprivate static final String[] paths = {\"/column/1\",\"/column/2\",\"/column/3\",\"/column/4\",\"/column/5\",\"/column/6\",\"/column/7\",\"/column/8\",\"/column/10\"};\n\t//抓取小程序专栏内容\n\tpublic void getWxClubColumn() throws IOException{\n\t\tList<Article>list = Lists.newArrayList();\n\t\tfor(String path: paths){\n\t\t\tDocument document = Jsoup.connect(WX_CLUB_HOST + path).get();\n\t\t\tif(document == null){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tElements els = document.select(\".topic_list li\");\n\t\t\tif(els == null || els.size() == 0){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString detailPath = \"\";\n\t\t\tString title = \"\";\n\t\t\tString desc = \"\";\n\t\t\tString content = \"\";\n\t\t\tMap<String,String> authorMap = null;\n\t\t\tString createTime = \"\";\n\t\t\tString comment = \"0\";\n\t\t\tString brower = \"0\";\n\t\t\tString remark = \"\";\n\n\t\t\tfor(Element element : els){\n\t\t\t\tElement titleEle = element.select(\".topic_title a\").first();\n\t\t\t\tdetailPath = titleEle.absUrl(\"href\");\n\t\t\t\ttitle = titleEle.text();\n\t\t\t\t\n\t\t\t\tauthorMap = Maps.newHashMap();\n\t\t\t\tDocument detailDoc = Jsoup.connect(detailPath).get();\n\t\t\t\tif(detailDoc != null){\n\t\t\t\t\tElement detailEl = detailDoc.select(\".topic_content\").first();\n\t\t\t\t\tElements imgEls = detailEl.select(\"img[src]\");\n\t\t\t\t\t\n\t\t\t\t\tfor (Element el : imgEls) {\n\t\t\t\t\t\tString imgUrl = el.attr(\"src\");\n\t\t\t\t\t\timgUrl =CLUBHOST + \"/\" +imgUrl;\n\t\t\t\t\t\tel.attr(\"src\", imgUrl);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tcontent = detailEl.html();\n\t\t\t\t\tString headimg = detailDoc.select(\".panel-body p .avatar\").first().absUrl(\"src\");\n\t\t\t\t\tString nickname = detailDoc.select(\".panel-body p .username\").first().text();\n\t\t\t\t\t\n\t\t\t\t\tElements markEls = detailDoc.select(\".panel-body .userremark\");\n\t\t\t\t\tif(markEls != null){\n\t\t\t\t\t\tremark = markEls.first().text();\n\t\t\t\t\t}\n\t\t\t\t\tauthorMap.put(\"headimg\", headimg);\n\t\t\t\t\tauthorMap.put(\"nickname\", nickname);\n\t\t\t\t\tauthorMap.put(\"remark\", remark);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tElements descEls = element.select(\".topic_desc\");\n\t\t\t\tif(descEls != null && descEls.size() > 0){\n\t\t\t\t\tif(descEls.size() == 1){\n\t\t\t\t\t\tcreateTime = descEls.get(0).select(\".last_time\").first().text();\n\n\t\t\t\t\t\tElements elss = descEls.get(0).select(\".reply_count .count_of_visits\");\n\t\t\t\t\t\tif(elss != null && elss.size() == 2){\n\t\t\t\t\t\t\tcomment = elss.get(0).text().replace(\"评论\", \"\").replace(\" \", \"\");\n\t\t\t\t\t\t\tbrower = elss.get(1).text().replace(\"浏览\", \"\").replace(\" \", \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}else {\n\t\t\t\t\t\tdesc = descEls.get(0).text();\n\t\t\t\t\t\tcreateTime = descEls.get(1).select(\".last_time\").first().text();\n\n\t\t\t\t\t\tElements elss = descEls.get(1).select(\".reply_count .count_of_visits\");\n\t\t\t\t\t\tif(elss != null && elss.size() == 2){\n\t\t\t\t\t\t\tcomment = elss.get(0).text().replace(\"评论\", \"\").replace(\" \", \"\");\n\t\t\t\t\t\t\tbrower = elss.get(1).text().replace(\"浏览\", \"\").replace(\" \", \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tArticle article = new Article();\n\t\t\t\tarticle.setAuthor(authorMap);\n\t\t\t\tarticle.setPath(detailPath);\n\t\t\t\tarticle.setGroupPath(path);\n\t\t\t\tarticle.setBrowers(Integer.valueOf(brower));\n\t\t\t\tarticle.setComments(Integer.valueOf(comment));\n\t\t\t\tarticle.setContent(content);\n\t\t\t\tarticle.setCreateTime(createTime);\n\t\t\t\tarticle.setDigest(desc);\n\t\t\t\tarticle.setTitle(title);\n\t\t\t\tlist.add(article);\n\t\t\t\t\n\t\t\t\tdetailPath = \"\";\n\t\t\t\ttitle = \"\";\n\t\t\t\tdesc = \"\";\n\t\t\t\tauthorMap = null;\n\t\t\t\tcreateTime = \"\";\n\t\t\t\tcomment = \"0\";\n\t\t\t\tbrower = \"0\";\n\t\t\t\tcontent = \"\";\n\t\t\t}\n\t\t}\n\t\tclubRepository.save(list);\n\t}\n\t/**\n\t * 清空\n\t */\n\tpublic void deleteAll() {\n\t\tclubRepository.deleteAll();\n\t}\n\t/**\n\t * 根据专栏ID搜索\n\t * @param groupPath\n\t * @return\n\t */\n\tpublic List<Article> getByGroupPath(String groupPath) {\n\t\treturn clubRepository.findByGroupPath(groupPath);\n\t}\n\t/**\n\t * 根据文章标题模糊搜索（分页）\n\t * @param title\n\t * @param page\n\t * @return\n\t */\n\tpublic List<Article> getByTitle(String title, MongoPageable page) {\n\t\treturn clubRepository.findByTitleLike(title,page);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/service/WxService.java",
    "content": "package com.weapp.service;\n\nimport java.util.Map;\n\nimport org.apache.commons.lang3.RandomStringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport com.alibaba.fastjson.JSON;\nimport com.weapp.common.properties.WxAuth;\nimport com.weapp.common.util.HttpRequest;\nimport com.weapp.redis.RedisUtil;\n\n@Service\npublic class WxService {\n\t@Autowired\n\tprivate WxAuth wxAuth;\n\t@Autowired\n\tprivate RedisUtil redisUtil;\n\t/**\n\t * 根据小程序登录返回的code获取openid和session_key\n\t * https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html?t=20161107\n\t * @param wxcode\n\t * @return\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic Map<String,Object>getWxSession(String wxCode){\n\t\tStringBuffer sb = new StringBuffer();\n\t\tsb.append(\"appid=\").append(wxAuth.getAppId());\n\t\tsb.append(\"&secret=\").append(wxAuth.getSecret());\n\t\tsb.append(\"&js_code=\").append(wxCode);\n\t\tsb.append(\"&grant_type=\").append(wxAuth.getGrantType());\n\t\tString res = HttpRequest.sendGet(wxAuth.getSessionHost(), sb.toString());\n\t\tif(res == null || res.equals(\"\")){\n\t\t\treturn null;\n\t\t}\n\t\treturn JSON.parseObject(res, Map.class);\n\t}\n\t/**\n\t * 缓存微信openId和session_key\n\t * @param wxOpenId\t\t微信用户唯一标识\n\t * @param wxSessionKey\t微信服务器会话密钥\n\t * @param expires\t\t会话有效期, 以秒为单位, 例如2592000代表会话有效期为30天\n\t * @return\n\t */\n\tpublic String create3rdSession(String wxOpenId, String wxSessionKey, Long expires){\n\t\tString thirdSessionKey = RandomStringUtils.randomAlphanumeric(64);\n\t\tStringBuffer sb = new StringBuffer();\n\t\tsb.append(wxSessionKey).append(\"#\").append(wxOpenId);\n\t\tredisUtil.add(thirdSessionKey, expires, sb.toString());\n\t\treturn thirdSessionKey;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/webconfig/WebConfigurer.java",
    "content": "/**\n *\n * @project xundaowei\n * @filename WebConfigurer.java\n * @date 2016年12月8日\n * @author KeShanqiang\n *\n */\n\npackage com.weapp.webconfig;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;\n\n@Component\npublic class WebConfigurer extends WebMvcConfigurerAdapter {\n\t@Value(\"${img.local.path}\")\n\tprivate String imgPath;\n\t\n\tpublic void addResourceHandlers(ResourceHandlerRegistry registry) {\n\t\tString localPath = \"file://\" + imgPath;\n\t\t\n\t\tString osName = System.getProperty(\"os.name\");\n\t\t//判断操作系统类型\n\t\tif(osName.toLowerCase().contains(\"win\")){\n\t\t\tlocalPath += \"/\";\n\t\t}\n        registry.addResourceHandler(\"/upload/**\").addResourceLocations(localPath);  \n    }  \n}\n"
  },
  {
    "path": "src/main/java/com/weapp/websocket/ChatWebSocketHandler.java",
    "content": "package com.weapp.websocket;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.socket.CloseStatus;\nimport org.springframework.web.socket.TextMessage;\nimport org.springframework.web.socket.WebSocketSession;\nimport org.springframework.web.socket.handler.TextWebSocketHandler;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\n\n\n/**\n * 微信小程序WebSocket\n * @author Shanqinag Ke\n * @since 2016-10-15\n */\npublic class ChatWebSocketHandler extends TextWebSocketHandler{\n\tprivate static final Logger logger =  LoggerFactory.getLogger(ChatWebSocketHandler.class);\n\n\tprivate final static List<WebSocketSession> sessions = Collections.synchronizedList(new ArrayList<WebSocketSession>());\n\n\t/**\n\t * 处理接收文本\n\t */\n\t@Override\n\tprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {\n\t\tString cont = message.getPayload();\n\t\tif(StringUtils.isEmpty(cont)){\n\t\t\treturn;\n\t\t}\n\t\t//校验JSON格式\n\t\tif(!cont.startsWith(\"{\") || !cont.endsWith(\"}\")){\n\t\t\treturn;\n\t\t}\n\t\tJSONObject json = JSON.parseObject(cont);\n\t\tif(!json.containsKey(\"user\") || !json.containsKey(\"content\")){\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\t//发送消息\n\t\t\tsendChatMessage(json.getString(\"user\"), json.getString(\"content\"));\n\t\t\tsuper.handleTextMessage(session, message);\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t}\n\t\n\t@Override\n\tpublic void afterConnectionEstablished(WebSocketSession session) throws Exception {\n\t\tlogger.debug(\"connect to the websocket chat success......\");\n\t\tsessions.add(session);\n\t}\n\n\t@Override\n\tpublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {\n\t\tif(session.isOpen()){\n\t\t\tsession.close();\n\t\t}\n\t\tlogger.debug(\"websocket chat connection closed......\");\n\t\tsessions.remove(session);\n\t}\n\n\t@Override\n\tpublic void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {\n\t\tlogger.debug(\"websocket chat connection closed......\");\n\t\tsessions.remove(session);\n\t}\n\n\t@Override\n\tpublic boolean supportsPartialMessages() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * 判断用户是否在线\n\t * @param userName\t登录用户名\n\t * @return\n     */\n\tpublic boolean isUserConnected(String userName){\n\t\tif(org.springframework.util.StringUtils.isEmpty(userName)){\n\t\t\treturn false;\n\t\t}\n\t\tfor (WebSocketSession user : sessions) {\n\t\t\tif (user.getAttributes().get(\"user\").equals(userName)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 发送消息\n\t * @param userName \t昵称\n\t * @param content\t发送内容\n\t */\n\tpublic static void sendChatMessage(String userName,String content){\n\t\tif(StringUtils.isEmpty(userName)){\n\t\t\treturn;\n\t\t}\n\t\tif (content == null){\n\t\t\treturn;\n\t\t}\n\t\tfor (WebSocketSession session : sessions) {\n\t\t\tif (!session.getAttributes().get(\"user\").equals(userName)) {\n\t\t\t\tif(session.isOpen()){\n\t\t\t\t\ttry{\n\t\t\t\t\t\tMap<String,Object> retMap = new HashMap<String,Object>();\n\t\t\t\t\t\tretMap.put(\"user\",userName);\n\t\t\t\t\t\tretMap.put(\"content\",content);\n\t\t\t\t\t\tsession.sendMessage(new TextMessage(JSON.toJSONString(retMap)));\n\t\t\t\t\t}catch (IOException ioe){\n\t\t\t\t\t\tioe.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/websocket/WebSocketConfig.java",
    "content": "package com.weapp.websocket;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;\nimport org.springframework.web.socket.config.annotation.EnableWebSocket;\nimport org.springframework.web.socket.config.annotation.WebSocketConfigurer;\nimport org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;\n\n/**\n * 配置websocket\n * @author Shanqinag Ke\n * @since 2016-10-15\n */\n@Configuration\n@EnableWebMvc\n@EnableWebSocket\npublic class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer{\n\n\t@Override\n\tpublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {\n\t\t//注册通道\n\t\tregistry.addHandler(chatWebSocketHandler(),\"/websocket\").setAllowedOrigins(\"*\").addInterceptors(myInterceptor());\n\t\tregistry.addHandler(chatWebSocketHandler(), \"/sockjs/websocket\").setAllowedOrigins(\"*\").addInterceptors(myInterceptor()).withSockJS();\n\t}\n\t//消息处理Handler\n\t@Bean\n\tpublic ChatWebSocketHandler chatWebSocketHandler() {\n\t\treturn new ChatWebSocketHandler();\n\t}\n\t\n\t//websocket拦截器\n\t@Bean\n\tpublic WebSocketHandshakeInterceptor myInterceptor(){\n\t\treturn new WebSocketHandshakeInterceptor();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/weapp/websocket/WebSocketHandshakeInterceptor.java",
    "content": "package com.weapp.websocket;\n\nimport java.util.Map;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpSession;\n\nimport org.springframework.http.server.ServerHttpRequest;\nimport org.springframework.http.server.ServerHttpResponse;\nimport org.springframework.http.server.ServletServerHttpRequest;\nimport org.springframework.web.socket.WebSocketHandler;\nimport org.springframework.web.socket.server.HandshakeInterceptor;\nimport org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;\n\nimport com.alibaba.fastjson.JSON;\n\n/**\n * websocket拦截器\n * @author Shanqinag Ke\n * @since 2016-10-15\n */\npublic class WebSocketHandshakeInterceptor extends HttpSessionHandshakeInterceptor implements HandshakeInterceptor{\n\n\t@Override\n    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {\n\t\t\n        if (request instanceof ServletServerHttpRequest) {\n            //解决The extension [x-webkit-deflate-frame] is not supported问题\n            if(request.getHeaders().containsKey(\"Sec-WebSocket-Extensions\")) {\n                request.getHeaders().set(\"Sec-WebSocket-Extensions\", \"permessage-deflate\");\n            }\n            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;\n            HttpSession session = servletRequest.getServletRequest().getSession();\n            if (session != null) {\n                //使用user区分WebSocketHandler，以便定向发送消息\n            \tHttpServletRequest req = servletRequest.getServletRequest();\n                String name = req.getParameter(\"name\");\n                System.out.println(JSON.toJSONString(req.getParameterNames()));\n                if(null != name && !\"\".equals(name)){\n                \tattributes.put(\"user\", name);\n                }\n                \n            }\n        }\n        return super.beforeHandshake(request, response, wsHandler, attributes);\n    }\n\n\t@Override\n\tpublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,\n\t\t\tException exception) {\n\t\tsuper.afterHandshake(request, response, wsHandler, exception);\n\t}\n    \n}\n"
  },
  {
    "path": "src/main/resources/application.properties",
    "content": "#server\nserver.context-path=/weappservice\nserver.display-name=weappservice\n\nserver.port=8443\nserver.ssl.key-store=classpath:https-site.jks\nserver.ssl.key-store-password=xxx\nserver.ssl.key-password=xxx\n#file\nimg.host=http://localhost/wximg\nimg.local.path=/usr/share/nginx/html/wximg\n\napi.v1=/api/v1\n\n#mongodb config\nspring.data.mongodb.uri=mongodb://localhost:27017/test\n\n# REDIS (RedisProperties)\nspring.redis.database=0\nspring.redis.host=xxx.xxx.xxx.xxx\nspring.redis.password=xxx\nspring.redis.port=6379\nspring.redis.pool.max-idle=20\nspring.redis.pool.min-idle=5\nspring.redis.pool.max-active=100\nspring.redis.pool.max-wait=1000\n\n#open aop\nspring.aop.auto=true\n\nwxapp.sessionHost=https://api.weixin.qq.com/sns/jscode2session\nwxapp.appId=xxx\nwxapp.secret=xxx\nwxapp.grantType=authorization_code\n\n"
  },
  {
    "path": "src/main/resources/error_code.properties",
    "content": "0=\n40001=\\u4E0D\\u5408\\u6CD5\\u7684appId\n40002=\\u63A5\\u53E3\\u4E0D\\u5B58\\u5728\n40003=\\u63A5\\u53E3\\u88AB\\u7981\\u6B62\\u8C03\\u7528\n40005=Http Method\\u4E0D\\u5339\\u914D\n40006=\\u63A5\\u53E3\\u672A\\u6388\\u6743\n40007=\\u9891\\u7387\\u8D85\\u9650\n40008=\\u7528\\u6237\\u8EAB\\u4EFD\\u5DF2\\u8FC7\\u671F\n\n\n40010=\\u8BF7\\u9009\\u62E9\\u4E0A\\u4F20\\u6587\\u4EF6\n40011=\\u4E0A\\u4F20\\u5931\\u8D25\n\n50010=\\u4E0E\\u7B2C\\u4E09\\u65B9\\u901A\\u8BAF\\u5931\\u8D25\n50020=\\u83B7\\u53D6\\u5FAE\\u4FE1session_key\\u5931\\u8D25\n50021=\\u7528\\u6237\\u654F\\u611F\\u6570\\u636E\\u89E3\\u5BC6\\u5931\\u8D25"
  },
  {
    "path": "src/main/resources/log4j.properties",
    "content": "log4j.rootCategory=info,stdout,logfile\r\n#stdout configure\r\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\r\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\r\nlog4j.appender.stdout.layout.ConversionPattern= %d %p [%c] - <%m>%n\r\n#logfile configure\r\nlog4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender\r\nlog4j.appender.logfile.File=../logs/server.log\r\nlog4j.appender.logfile.layout=org.apache.log4j.PatternLayout\r\nlog4j.appender.logfile.layout.ConversionPattern= %d %p [%c] - %m%n\r\nlog4j.logger.java.sql.PreparedStatement=DEBUG\r\n"
  },
  {
    "path": "src/test/java/com/weapp/ApplicationTest.java",
    "content": "package com.weapp;\n\nimport static org.hamcrest.Matchers.equalTo;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.MockitoAnnotations;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.test.context.ActiveProfiles;\nimport org.springframework.test.context.TestPropertySource;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.RequestBuilder;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\nimport com.weapp.controller.AppUserController;\n\n@ActiveProfiles(\"test\")\n@RunWith(SpringJUnit4ClassRunner.class)\n@SpringBootTest(classes = MockServletContext.class)\n@WebAppConfiguration\n@TestPropertySource(\"classpath:application-test.properties\")\npublic class ApplicationTest {\n\tprivate MockMvc mvc;\n\t@Before\n\tpublic void setUp() throws Exception {\n\t\tmvc = MockMvcBuilders.standaloneSetup(new AppUserController()).build();\n\t\tMockitoAnnotations.initMocks(this);\n\t}\n\t//测试获取数据接口\n\t@Test\n\tpublic void test() throws Exception{\n\t\tRequestBuilder request = null;\n\t\trequest = MockMvcRequestBuilders.get(\"/api/v1/user/123123\")\n\t\t\t\t.param(\"appId\", \"test123\").param(\"apiName\", \"GET_USER\");\n\t\t\t\t\n\t\tmvc.perform(request)\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(equalTo(\"{\\\"id\\\":\\\"123123\\\"}\")));\n\t}\n}\n"
  },
  {
    "path": "src/test/resources/application-test.properties",
    "content": "server.port=9090\nserver.context-path=/weappservice\nserver.display-name=weappservice\nimgPath=http://120.26.231.155/wximg/\nlocalPath=/usr/share/nginx/html/wximg/\napi.v1=/api/v1\nspring.data.mongodb.uri=mongodb://localhost:27017/test\nspring.aop.auto=true"
  }
]