diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 61d882d78..c80190b98 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ -![Licence](https://img.shields.io/badge/licence-none-green.svg) -[![GitHub Release](https://img.shields.io/github/release/lihengming/spring-boot-api-project-seed.svg)](https://github.com/lihengming/spring-boot-api-project-seed/releases) +[![License](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) +[![GitHub Release](https://img.shields.io/github/release/lihengming/spring-boot-api-project-seed.svg)](https://github.com/lerry903/spring-boot-api-project-seed/releases) ## 简介 -Spring Boot API Project Seed 是一个基于Spring Boot & MyBatis的种子项目,用于快速构建中小型API、RESTful API项目,该种子项目已经有过多个真实项目的实践,稳定、简单、快速,使我们摆脱那些重复劳动,专注于业务代码的编写,减少加班。下面是一个简单的使用演示,看如何基于本项目在短短几十秒钟内实现一套简单的API,并运行提供服务。 +Spring Boot API Project Seed 是一个基于Spring Boot & MyBatis的种子项目,用于快速构建中小型API、RESTful API项目,该种子项目已经有过多个真实项目的实践,稳定、简单、快速,使我们摆脱那些重复劳动,专注于业务代码的编写,减少加班。 -[![请选择超清](https://raw.githubusercontent.com/lihengming/java-codes/master/shared-resources/github-images/project-example-youku.png)](http://v.youku.com/v_show/id_XMjg1NjYwNDgxNg==.html?spm=a2h3j.8428770.3416059.1) ## 特征&提供 -- 最佳实践的项目结构、配置文件、精简的POM([查看项目结构图](https://github.com/lihengming/java-codes/blob/master/shared-resources/github-images/project-struct.png)) -- 统一响应结果封装及生成工具 +- 最佳实践的项目结构、配置文件、精简的POM +- 统一响应结果封装 - 统一异常处理 +- 统一日志打印 +- 开源的Java工具包Hutool - 简单的接口签名认证 - 常用基础方法抽象封装 - 使用Druid Spring Boot Starter 集成Druid数据库连接池与监控 @@ -27,19 +28,23 @@ Spring Boot API Project Seed 是一个基于Spring Boot & MyBatis的种子项目 ## 开发建议 - 表名,建议使用小写,多个单词使用下划线拼接 - Model内成员变量建议与表字段数量对应,如需扩展成员变量(比如连表查询)建议创建DTO,否则需在扩展的成员变量上加```@Transient```注解,详情见[通用Mapper插件文档说明](https://mapperhelper.github.io/docs/2.use/) -- 建议业务失败直接使用```ServiceException("message")```抛出,由统一异常处理器来封装业务失败的响应结果,比如```throw new ServiceException("该手机号已被注册")```,会直接被封装为```{"code":400,"message":"该手机号已被注册"}```返回,无需自己处理,尽情抛出 -- 需要工具类的话建议先从```apache-commons-*```和```guava```中找,实在没有再造轮子或引入类库,尽量精简项目 -- 开发规范建议遵循阿里巴巴Java开发手册([最新版下载](https://github.com/lihengming/java-codes/blob/master/shared-resources/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8CV1.3.0.pdf)) +- 建议业务失败使用```BusinessException```子类抛出,由统一异常处理器来封装业务失败的响应结果,比如```throw new DataNotFoundException()```,会直接被封装为```{"code":50001,"message":"数据未找到"}```返回,无需自己处理,尽情抛出 +- 需要工具类的话建议先从```Hutool```中找,实在没有再继承```Hutool```中相应的工具类造轮子或引入类库,尽量精简项目 +- 开发规范建议遵循阿里巴巴Java开发手册([最新版下载](https://github.com/alibaba/p3c)) - 建议在公司内部使用[ShowDoc](https://github.com/star7th/showdoc)、[SpringFox-Swagger2](https://github.com/springfox/springfox) 、[RAP](https://github.com/thx/RAP)等开源项目来编写、管理API文档   ## 技术选型&文档 -- Spring Boot([查看Spring Boot学习&使用指南](http://www.jianshu.com/p/1a9fd8936bd8)) +- Spring Boot([查看Spring Boot学习&使用指南](https://blog.csdn.net/lsy0903/article/category/6413992)) - MyBatis([查看官方中文文档](http://www.mybatis.org/mybatis-3/zh/index.html)) - MyBatisb通用Mapper插件([查看官方中文文档](https://mapperhelper.github.io/docs/)) - MyBatis PageHelper分页插件([查看官方中文文档](https://pagehelper.github.io/)) - Druid Spring Boot Starter([查看官方中文文档](https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter/)) - Fastjson([查看官方中文文档](https://github.com/Alibaba/fastjson/wiki/%E9%A6%96%E9%A1%B5)) +- Hutool工具包([查看官方文档](http://hutool.mydoc.io/)) - 其他略 ## License -无,纯粹开源分享,感谢大家 [Star](https://github.com/lihengming/spring-boot-api-project-seed/stargazers) & [Fork](https://github.com/lihengming/spring-boot-api-project-seed/network/members) 的支持。 + +用户在遵循本项目协议的同时,如果用户下载、安装、使用本项目中所提供的软件,软件作者对任何原因在使用本项目中提供的软件时可能对用户自己或他人造成的任何形式的损失和伤害不承担任何责任。作者有权根据有关法律、法规的变化修改本项目协议。修改后的协议会随附于本项目的新版本中。当发生有关争议时,以最新的协议文本为准。如果用户不同意改动的内容,用户可以自行删除本项目。如果用户继续使用本项目,则视为您接受本协议的变动。 + +感谢大家 [Star](https://github.com/lerry903/spring-boot-api-project-seed/stargazers) & [Fork](https://github.com/lerry903/spring-boot-api-project-seed/network/members) 的支持。 diff --git a/pom.xml b/pom.xml index 80cf9affc..3a4d39ddf 100644 --- a/pom.xml +++ b/pom.xml @@ -10,14 +10,17 @@ jar + UTF-8 + UTF-8 1.8 + 1.1.10 org.springframework.boot spring-boot-starter-parent - 1.5.13.RELEASE + 1.5.14.RELEASE @@ -83,11 +86,17 @@ fastjson 1.2.47 + + + cn.hutool + hutool-all + 4.1.0 + com.alibaba druid-spring-boot-starter - 1.1.10 + ${druid.version} @@ -102,6 +111,24 @@ 1.3.5 test + + + org.projectlombok + lombok + 1.18.0 + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.aspectj + aspectjweaver + 1.8.13 + diff --git a/src/main/java/com/company/project/EnvironmentEnum.java b/src/main/java/com/company/project/EnvironmentEnum.java new file mode 100644 index 000000000..bc812c01f --- /dev/null +++ b/src/main/java/com/company/project/EnvironmentEnum.java @@ -0,0 +1,35 @@ +package com.company.project; + +import org.springframework.core.env.Environment; +import org.springframework.util.Assert; + +/** + * Created with IntelliJ IDEA. + * Description: + * 环境常量枚举类 + * @author LErry.li + * Date: 2018-06-16 + * Time: 14:43 + */ +public enum EnvironmentEnum { + //开发 + DEV, + //生产 + PROD, + //联调 + FE, + //测试 + QA, + //仿真 + STG; + + public static boolean isProdEnv(Environment env) { + Assert.notNull(env, "env parameter not null."); + return EnvironmentEnum.PROD.name().equalsIgnoreCase(env.getProperty("spring.profiles.active")); + } + + @Override + public String toString() { + return this.name(); + } +} diff --git a/src/main/java/com/company/project/common/annotations/EnumValue.java b/src/main/java/com/company/project/common/annotations/EnumValue.java new file mode 100644 index 000000000..331cd5dcf --- /dev/null +++ b/src/main/java/com/company/project/common/annotations/EnumValue.java @@ -0,0 +1,83 @@ +package com.company.project.common.annotations; + + + +import com.company.project.common.exception.BusinessException; +import com.company.project.common.util.StringUtil; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + + +/** + * Created with IntelliJ IDEA. + * Description: + * 校验枚举值有效性 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = EnumValue.Validator.class) +public @interface EnumValue { + + String message() default "{custom.value.invalid}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + Class> enumClass(); + + String enumMethod(); + + class Validator implements ConstraintValidator { + + private Class> enumClass; + private String enumMethod; + + @Override + public void initialize(EnumValue enumValue) { + enumMethod = enumValue.enumMethod(); + enumClass = enumValue.enumClass(); + } + + @Override + public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) { + if (value == null) { + return Boolean.TRUE; + } + + if (enumClass == null || enumMethod == null) { + return Boolean.TRUE; + } + + Class valueClass = value.getClass(); + + try { + Method method = enumClass.getMethod(enumMethod, valueClass); + if (!Boolean.TYPE.equals(method.getReturnType()) && !Boolean.class.equals(method.getReturnType())) { + throw new BusinessException(StringUtil.formatIfArgs("%s method return is not boolean type in the %s class", enumMethod, enumClass)); + } + + Boolean result = (Boolean)method.invoke(null, value); + return result == null ? false : result; + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new BusinessException(e.getMessage()); + } catch (NoSuchMethodException | SecurityException e) { + throw new BusinessException(StringUtil.formatIfArgs("This %s(%s) method does not exist in the %s", enumMethod, valueClass, enumClass), e); + } + } + + } + +} diff --git a/src/main/java/com/company/project/common/annotations/ServiceLog.java b/src/main/java/com/company/project/common/annotations/ServiceLog.java new file mode 100644 index 000000000..7a144e7b0 --- /dev/null +++ b/src/main/java/com/company/project/common/annotations/ServiceLog.java @@ -0,0 +1,22 @@ +package com.company.project.common.annotations; + +import java.lang.annotation.*; + +/** + * Created with IntelliJ IDEA. + * Description: + * AOP日志记录,自定义注解 + * @author LErry.li + * Date: 2018-06-17 + * Time: 14:45 + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ServiceLog { + + /** + * 日志描述 + */ + String description() default ""; +} diff --git a/src/main/java/com/company/project/common/aspect/RestControllerAspect.java b/src/main/java/com/company/project/common/aspect/RestControllerAspect.java new file mode 100644 index 000000000..cad9f7c6b --- /dev/null +++ b/src/main/java/com/company/project/common/aspect/RestControllerAspect.java @@ -0,0 +1,84 @@ +package com.company.project.common.aspect; + +import com.company.project.common.exception.GlobalExceptionHandler; +import com.company.project.common.util.IpUtil; +import com.company.project.common.util.LogAspectUtil; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; + +/** + * Created with IntelliJ IDEA. + * Description: + * + * @author LErry.li + * Date: 2018-06-16 + * Time: 16:31 + */ +@Aspect +@Component +public class RestControllerAspect { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * 环绕通知 + * @param joinPoint 连接点 + * @return 切入点返回值 + * @throws Throwable 异常信息 + */ + @Around("@within(org.springframework.web.bind.annotation.RestController) || @annotation(org.springframework.web.bind.annotation.RestController)") + public Object apiLog(ProceedingJoinPoint joinPoint) throws Throwable { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + + boolean logFlag = this.needToLog(method); + if (!logFlag) { + return joinPoint.proceed(); + } + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + + String userAgent = request.getHeader("user-agent"); + String ip = IpUtil.getRealIp(request); + String methodName = this.getMethodName(joinPoint); + String params = LogAspectUtil.getMethodParams(joinPoint); + + logger.info("开始请求方法:[{}] 参数:[{}] IP:[{}] userAgent [{}]", methodName, params, ip, userAgent); + long start = System.currentTimeMillis(); + Object result = joinPoint.proceed(); + long end = System.currentTimeMillis(); + String deleteSensitiveContent = LogAspectUtil.deleteSensitiveContent(result); + logger.info("结束请求方法:[{}] 参数:[{}] 返回结果[{}] 耗时:[{}]毫秒 ", + methodName, params, deleteSensitiveContent, end - start); + return result; + } + + private String getMethodName(ProceedingJoinPoint joinPoint) { + String methodName = joinPoint.getSignature().toShortString(); + String shortMethodNameSuffix = "(..)"; + if (methodName.endsWith(shortMethodNameSuffix)) { + methodName = methodName.substring(0, methodName.length() - shortMethodNameSuffix.length()); + } + return methodName; + } + + + /** + * 判断是否需要记录日志 + */ + private boolean needToLog(Method method) { + return method.getAnnotation(GetMapping.class) == null + && !method.getDeclaringClass().equals(GlobalExceptionHandler.class); + } + +} diff --git a/src/main/java/com/company/project/common/aspect/ServiceLogAspect.java b/src/main/java/com/company/project/common/aspect/ServiceLogAspect.java new file mode 100644 index 000000000..c9b8c24a2 --- /dev/null +++ b/src/main/java/com/company/project/common/aspect/ServiceLogAspect.java @@ -0,0 +1,94 @@ +package com.company.project.common.aspect; + +import com.company.project.common.annotations.ServiceLog; +import com.company.project.common.util.LogAspectUtil; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +/** + * Created with IntelliJ IDEA. + * Description: + * service 日志记录切面 + * + * @author LErry.li + * Date: 2018-06-17 + * Time: 14:47 + */ +@Aspect +@Component +public class ServiceLogAspect { + + private final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class); + + /** + * 环绕通知方法 + * @param joinPoint 连接点 + * @return 切入点返回值 + * @throws Throwable 异常信息 + */ + @Around("@within(com.company.project.common.annotations.ServiceLog) ||@annotation(com.company.project.common.annotations.ServiceLog)") + public Object doServiceLog(ProceedingJoinPoint joinPoint) throws Throwable{ + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + // 拦截的实体类 + Object target = joinPoint.getTarget(); + // 拦截的方法名称 + String methodName = signature.getName(); + // 拦截的放参数类型 + Class[] parameterTypes = signature.getMethod().getParameterTypes(); + + Method method = target.getClass().getMethod(methodName, parameterTypes); + if(null == method){ + return joinPoint.proceed(); + } + // 判断是否包含自定义的注解 + if (!method.isAnnotationPresent(ServiceLog.class)) { + return joinPoint.proceed(); + } + String methodParams = LogAspectUtil.getMethodParams(joinPoint); + logger.info("开始请求方法:[{}] 参数:[{}]", methodName, methodParams); + long start = System.currentTimeMillis(); + Object result = joinPoint.proceed(); + long end = System.currentTimeMillis(); + String deleteSensitiveContent = LogAspectUtil.deleteSensitiveContent(result); + logger.info("结束请求方法:[{}] 参数:[{}] 返回结果[{}] 耗时:[{}]毫秒 ", + methodName, methodParams, deleteSensitiveContent, end - start); + return result; + } + + /** + * 获取注解中对方法的描述信息 用于service层注解 + * @param joinPoint + * @return + */ + private String getServiceMethodDescription(ProceedingJoinPoint joinPoint){ + String targetName = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + Object[] arguments = joinPoint.getArgs(); + Class targetClass = null; + try { + targetClass = Class.forName(targetName); + } catch (ClassNotFoundException e) { + throw new ClassCastException(); + } + Method[] methods = targetClass.getMethods(); + String description = ""; + for(Method method : methods) { + if(method.getName().equals(methodName)) { + Class[] clazzs = method.getParameterTypes(); + if(clazzs.length == arguments.length && null != method.getAnnotation(ServiceLog.class)) { + description = method.getAnnotation(ServiceLog.class).description(); + break; + } + } + } + return description; + } + +} diff --git a/src/main/java/com/company/project/common/exception/BaseGlobalExceptionHandler.java b/src/main/java/com/company/project/common/exception/BaseGlobalExceptionHandler.java new file mode 100644 index 000000000..c245f890a --- /dev/null +++ b/src/main/java/com/company/project/common/exception/BaseGlobalExceptionHandler.java @@ -0,0 +1,85 @@ +package com.company.project.common.exception; + +import com.company.project.common.result.DefaultErrorResult; +import com.company.project.common.result.ParameterInvalidItem; +import com.company.project.common.result.ResultCode; +import com.company.project.common.util.ConvertUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.BindException; +import org.springframework.web.bind.MethodArgumentNotValidException; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.ConstraintViolationException; +import java.util.List; + +/** + * Created with IntelliJ IDEA. + * Description: + * 聚合层全局异常处理类 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:40 + */ +public class BaseGlobalExceptionHandler { + + private Logger log = LoggerFactory.getLogger(this.getClass()); + /** + * 违反约束异常 + */ + protected DefaultErrorResult handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) { + log.info("handleConstraintViolationException start, uri:{}, caused by: ", request.getRequestURI(), e); + List parameterInvalidItemList = ConvertUtil.convertCVSetToParameterInvalidItemList(e.getConstraintViolations()); + return DefaultErrorResult.failure(ResultCode.PARAM_IS_INVALID, e, HttpStatus.BAD_REQUEST, parameterInvalidItemList); + } + + /** + * 处理验证参数封装错误时异常 + */ + protected DefaultErrorResult handleConstraintViolationException(HttpMessageNotReadableException e, HttpServletRequest request) { + log.info("handleConstraintViolationException start, uri:{}, caused by: ", request.getRequestURI(), e); + return DefaultErrorResult.failure(ResultCode.PARAM_IS_INVALID, e, HttpStatus.BAD_REQUEST); + } + + /** + * 处理参数绑定时异常(反400错误码) + */ + protected DefaultErrorResult handleBindException(BindException e, HttpServletRequest request) { + log.info("handleBindException start, uri:{}, caused by: ", request.getRequestURI(), e); + List parameterInvalidItemList = ConvertUtil.convertBindingResultToMapParameterInvalidItemList(e.getBindingResult()); + return DefaultErrorResult.failure(ResultCode.PARAM_IS_INVALID, e, HttpStatus.BAD_REQUEST, parameterInvalidItemList); + } + + /** + * 处理使用@Validated注解时,参数验证错误异常(反400错误码) + */ + protected DefaultErrorResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) { + log.info("handleMethodArgumentNotValidException start, uri:{}, caused by: ", request.getRequestURI(), e); + List parameterInvalidItemList = ConvertUtil.convertBindingResultToMapParameterInvalidItemList(e.getBindingResult()); + return DefaultErrorResult.failure(ResultCode.PARAM_IS_INVALID, e, HttpStatus.BAD_REQUEST, parameterInvalidItemList); + } + + /** + * 处理通用自定义业务异常 + */ + protected ResponseEntity handleBusinessException(BusinessException e, HttpServletRequest request) { + log.info("handleBusinessException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage()); + + DefaultErrorResult defaultErrorResult = DefaultErrorResult.failure(e); + return ResponseEntity + .status(HttpStatus.valueOf(defaultErrorResult.getStatus())) + .body(defaultErrorResult); + } + + /** + * 处理运行时系统异常(反500错误码) + */ + protected DefaultErrorResult handleRuntimeException(RuntimeException e, HttpServletRequest request) { + log.error("handleRuntimeException start, uri:{}, caused by: ", request.getRequestURI(), e); + return DefaultErrorResult.failure(ResultCode.SYSTEM_INNER_ERROR, e, HttpStatus.INTERNAL_SERVER_ERROR); + } + +} diff --git a/src/main/java/com/company/project/common/exception/BusinessException.java b/src/main/java/com/company/project/common/exception/BusinessException.java new file mode 100644 index 000000000..7ebfa8399 --- /dev/null +++ b/src/main/java/com/company/project/common/exception/BusinessException.java @@ -0,0 +1,60 @@ +package com.company.project.common.exception; + +import com.company.project.common.result.ResultCode; +import lombok.Data; +import org.springframework.util.StringUtils; + + +/** + * Created with IntelliJ IDEA. + * Description: + * 业务异常类 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +@Data +public class BusinessException extends RuntimeException { + + private static final long serialVersionUID = 2332608236621015980L; + + protected String code; + + protected String message; + + protected ResultCode resultCode; + + protected Object data; + + public BusinessException() { + ExceptionEnum exceptionEnum = ExceptionEnum.getByEClass(this.getClass()); + if (exceptionEnum != null) { + resultCode = exceptionEnum.getResultCode(); + code = exceptionEnum.getResultCode().code().toString(); + message = exceptionEnum.getResultCode().message(); + } + + } + + public BusinessException(String message) { + this(); + this.message = message; + } + + public BusinessException(String format, Object... objects) { + this(); + format = StringUtils.replace(format, "{}", "%s"); + this.message = String.format(format, objects); + } + + public BusinessException(ResultCode resultCode, Object data) { + this(resultCode); + this.data = data; + } + + public BusinessException(ResultCode resultCode) { + this.resultCode = resultCode; + this.code = resultCode.code().toString(); + this.message = resultCode.message(); + } +} diff --git a/src/main/java/com/company/project/common/exception/DataConflictException.java b/src/main/java/com/company/project/common/exception/DataConflictException.java new file mode 100644 index 000000000..25f2a34ae --- /dev/null +++ b/src/main/java/com/company/project/common/exception/DataConflictException.java @@ -0,0 +1,41 @@ +package com.company.project.common.exception; + +import com.company.project.common.result.ResultCode; + +/** + * Created with IntelliJ IDEA. + * Description: + * 数据已存在 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +public class DataConflictException extends BusinessException { + private static final long serialVersionUID = 3721036867889297081L; + + public DataConflictException() { + super(); + } + + public DataConflictException(Object data) { + super(); + super.data = data; + } + + public DataConflictException(ResultCode resultCode) { + super(resultCode); + } + + public DataConflictException(ResultCode resultCode, Object data) { + super(resultCode, data); + } + + public DataConflictException(String msg) { + super(msg); + } + + public DataConflictException(String formatMsg, Object... objects) { + super(formatMsg, objects); + } + +} diff --git a/src/main/java/com/company/project/common/exception/DataNotFoundException.java b/src/main/java/com/company/project/common/exception/DataNotFoundException.java new file mode 100644 index 000000000..af2e3319f --- /dev/null +++ b/src/main/java/com/company/project/common/exception/DataNotFoundException.java @@ -0,0 +1,42 @@ +package com.company.project.common.exception; + + +import com.company.project.common.result.ResultCode; + +/** + * Created with IntelliJ IDEA. + * Description: + * 数据未找到 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +public class DataNotFoundException extends BusinessException { + private static final long serialVersionUID = 3721036867889297081L; + + public DataNotFoundException() { + super(); + } + + public DataNotFoundException(Object data) { + super(); + super.data = data; + } + + public DataNotFoundException(ResultCode resultCode) { + super(resultCode); + } + + public DataNotFoundException(ResultCode resultCode, Object data) { + super(resultCode, data); + } + + public DataNotFoundException(String msg) { + super(msg); + } + + public DataNotFoundException(String formatMsg, Object... objects) { + super(formatMsg, objects); + } + +} diff --git a/src/main/java/com/company/project/common/exception/ExceptionEnum.java b/src/main/java/com/company/project/common/exception/ExceptionEnum.java new file mode 100644 index 000000000..e14bb9ec9 --- /dev/null +++ b/src/main/java/com/company/project/common/exception/ExceptionEnum.java @@ -0,0 +1,126 @@ +package com.company.project.common.exception; + +import com.company.project.common.result.ResultCode; +import org.springframework.http.HttpStatus; + +/** + * Created with IntelliJ IDEA. + * Description: + * 异常、HTTP状态码、默认自定义返回码 映射类 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +public enum ExceptionEnum { + /** + * 无效参数 + */ + PARAMETER_INVALID(ParameterInvalidException.class, HttpStatus.BAD_REQUEST, ResultCode.PARAM_IS_INVALID), + + /** + * 数据未找到 + */ + NOT_FOUND(DataNotFoundException.class, HttpStatus.NOT_FOUND, ResultCode.RESULE_DATA_NONE), + + /** + * 数据已存在 + */ + CONFLICT(DataConflictException.class, HttpStatus.CONFLICT, ResultCode.DATA_ALREADY_EXISTED), + + /** + * 用户未登录 + */ + UNAUTHORIZED(UserNotLoginException.class, HttpStatus.UNAUTHORIZED, ResultCode.USER_NOT_LOGGED_IN), + + /** + * 无访问权限 + */ + FORBIDDEN(PermissionForbiddenException.class, HttpStatus.FORBIDDEN, ResultCode.PERMISSION_NO_ACCESS), + + /** + * 远程访问时错误 + */ + REMOTE_ACCESS_ERROR(RemoteAccessException.class, HttpStatus.INTERNAL_SERVER_ERROR, ResultCode.INTERFACE_OUTTER_INVOKE_ERROR), + + /** + * 系统内部错误 + */ + INTERNAL_SERVER_ERROR(InternalServerException.class, HttpStatus.INTERNAL_SERVER_ERROR, ResultCode.SYSTEM_INNER_ERROR); + + private Class eClass; + + private HttpStatus httpStatus; + + private ResultCode resultCode; + + /** + * @param eClass + * @param httpStatus + * @param resultCode + */ + ExceptionEnum(Class eClass, HttpStatus httpStatus, ResultCode resultCode) { + this.eClass = eClass; + this.httpStatus = httpStatus; + this.resultCode = resultCode; + } + + public Class getEClass() { + return eClass; + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } + + public ResultCode getResultCode() { + return resultCode; + } + + public static boolean isSupportHttpStatus(int httpStatus) { + for (ExceptionEnum exceptionEnum : ExceptionEnum.values()) { + if (exceptionEnum.httpStatus.value() == httpStatus) { + return true; + } + } + + return false; + } + + public static boolean isSupportException(Class z) { + for (ExceptionEnum exceptionEnum : ExceptionEnum.values()) { + if (exceptionEnum.eClass.equals(z)) { + return true; + } + } + + return false; + } + + public static ExceptionEnum getByHttpStatus(HttpStatus httpStatus) { + if (httpStatus == null) { + return null; + } + + for (ExceptionEnum exceptionEnum : ExceptionEnum.values()) { + if (httpStatus.equals(exceptionEnum.httpStatus)) { + return exceptionEnum; + } + } + + return null; + } + + public static ExceptionEnum getByEClass(Class eClass) { + if (eClass == null) { + return null; + } + + for (ExceptionEnum exceptionEnum : ExceptionEnum.values()) { + if (eClass.equals(exceptionEnum.eClass)) { + return exceptionEnum; + } + } + + return null; + } +} diff --git a/src/main/java/com/company/project/common/exception/GlobalExceptionHandler.java b/src/main/java/com/company/project/common/exception/GlobalExceptionHandler.java new file mode 100644 index 000000000..09ab65a3a --- /dev/null +++ b/src/main/java/com/company/project/common/exception/GlobalExceptionHandler.java @@ -0,0 +1,89 @@ +package com.company.project.common.exception; + +import com.company.project.common.result.DefaultErrorResult; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.BindException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.ConstraintViolationException; + +/** + * Created with IntelliJ IDEA. + * Description: + * 统一异常处理器 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +@RestController +@ControllerAdvice +public class GlobalExceptionHandler extends BaseGlobalExceptionHandler { + + /** + * 处理400类异常 + * @param e + * @param request + * @return + */ + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(ConstraintViolationException.class) + @Override + public DefaultErrorResult handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) { + return super.handleConstraintViolationException(e, request); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(HttpMessageNotReadableException.class) + @Override + public DefaultErrorResult handleConstraintViolationException(HttpMessageNotReadableException e, HttpServletRequest request) { + return super.handleConstraintViolationException(e, request); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(BindException.class) + @Override + public DefaultErrorResult handleBindException(BindException e, HttpServletRequest request) { + return super.handleBindException(e, request); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(MethodArgumentNotValidException.class) + @Override + public DefaultErrorResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) { + return super.handleMethodArgumentNotValidException(e, request); + } + + /** + * 处理自定义异常 + * @param e + * @param request + * @return + */ + @ExceptionHandler(BusinessException.class) + @Override + public ResponseEntity handleBusinessException(BusinessException e, HttpServletRequest request) { + return super.handleBusinessException(e, request); + } + + + /** + * 处理运行时异常 + * @param e + * @param request + * @return + */ + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler(RuntimeException.class) + @Override + public DefaultErrorResult handleRuntimeException(RuntimeException e, HttpServletRequest request) { + //TODO 可通过邮件、微信公众号等方式发送信息至开发人员、记录存档等操作 + return super.handleRuntimeException(e, request); + } +} diff --git a/src/main/java/com/company/project/common/exception/InternalServerException.java b/src/main/java/com/company/project/common/exception/InternalServerException.java new file mode 100644 index 000000000..6c072f13a --- /dev/null +++ b/src/main/java/com/company/project/common/exception/InternalServerException.java @@ -0,0 +1,34 @@ +package com.company.project.common.exception; + +/** + * Created with IntelliJ IDEA. + * Description: + * 系统内部错误 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +public class InternalServerException extends BusinessException { + private static final long serialVersionUID = 2659909836556958676L; + + public InternalServerException() { + super(); + } + + public InternalServerException(String msg, Throwable cause) { + super(msg, cause); + } + + public InternalServerException(String msg, Throwable cause, Object... objects) { + super(msg, cause, objects); + } + + public InternalServerException(String msg) { + super(msg); + } + + public InternalServerException(String formatMsg, Object... objects) { + super(formatMsg, objects); + } + +} diff --git a/src/main/java/com/company/project/common/exception/ParameterInvalidException.java b/src/main/java/com/company/project/common/exception/ParameterInvalidException.java new file mode 100644 index 000000000..dd4abd523 --- /dev/null +++ b/src/main/java/com/company/project/common/exception/ParameterInvalidException.java @@ -0,0 +1,42 @@ +package com.company.project.common.exception; + + +import com.company.project.common.result.ResultCode; + +/** + * Created with IntelliJ IDEA. + * Description: + * 参数无效异常 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +public class ParameterInvalidException extends BusinessException { + private static final long serialVersionUID = 3721036867889297081L; + + public ParameterInvalidException() { + super(); + } + + public ParameterInvalidException(Object data) { + super(); + super.data = data; + } + + public ParameterInvalidException(ResultCode resultCode) { + super(resultCode); + } + + public ParameterInvalidException(ResultCode resultCode, Object data) { + super(resultCode, data); + } + + public ParameterInvalidException(String msg) { + super(msg); + } + + public ParameterInvalidException(String formatMsg, Object... objects) { + super(formatMsg, objects); + } + +} diff --git a/src/main/java/com/company/project/common/exception/PermissionForbiddenException.java b/src/main/java/com/company/project/common/exception/PermissionForbiddenException.java new file mode 100644 index 000000000..9bc648667 --- /dev/null +++ b/src/main/java/com/company/project/common/exception/PermissionForbiddenException.java @@ -0,0 +1,41 @@ +package com.company.project.common.exception; + + +import com.company.project.common.result.ResultCode; + +/** + * Created with IntelliJ IDEA. + * Description: + * 权限不足异常 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +public class PermissionForbiddenException extends BusinessException { + private static final long serialVersionUID = 3721036867889297081L; + + public PermissionForbiddenException() { + super(); + } + + public PermissionForbiddenException(Object data) { + super.data = data; + } + + public PermissionForbiddenException(ResultCode resultCode) { + super(resultCode); + } + + public PermissionForbiddenException(ResultCode resultCode, Object data) { + super(resultCode, data); + } + + public PermissionForbiddenException(String msg) { + super(msg); + } + + public PermissionForbiddenException(String formatMsg, Object... objects) { + super(formatMsg, objects); + } + +} diff --git a/src/main/java/com/company/project/common/exception/RemoteAccessException.java b/src/main/java/com/company/project/common/exception/RemoteAccessException.java new file mode 100644 index 000000000..765902359 --- /dev/null +++ b/src/main/java/com/company/project/common/exception/RemoteAccessException.java @@ -0,0 +1,40 @@ +package com.company.project.common.exception; + +import com.company.project.common.result.ResultCode; + +/** + * Created with IntelliJ IDEA. + * Description: + * 远程访问时错误 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +public class RemoteAccessException extends BusinessException { + private static final long serialVersionUID = -832464574076215195L; + + public RemoteAccessException() { + super(); + } + + public RemoteAccessException(Object data) { + super.data = data; + } + + public RemoteAccessException(ResultCode resultCode) { + super(resultCode); + } + + public RemoteAccessException(ResultCode resultCode, Object data) { + super(resultCode, data); + } + + public RemoteAccessException(String msg) { + super(msg); + } + + public RemoteAccessException(String formatMsg, Object... objects) { + super(formatMsg, objects); + } + +} diff --git a/src/main/java/com/company/project/common/exception/UserNotLoginException.java b/src/main/java/com/company/project/common/exception/UserNotLoginException.java new file mode 100644 index 000000000..f7572a30d --- /dev/null +++ b/src/main/java/com/company/project/common/exception/UserNotLoginException.java @@ -0,0 +1,26 @@ +package com.company.project.common.exception; + +/** + * Created with IntelliJ IDEA. + * Description: + * 用户未登录 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +public class UserNotLoginException extends BusinessException { + private static final long serialVersionUID = -1879503946782379204L; + + public UserNotLoginException() { + super(); + } + + public UserNotLoginException(String msg) { + super(msg); + } + + public UserNotLoginException(String formatMsg, Object... objects) { + super(formatMsg, objects); + } + +} diff --git a/src/main/java/com/company/project/common/exception/package-info.java b/src/main/java/com/company/project/common/exception/package-info.java new file mode 100644 index 000000000..afff24da2 --- /dev/null +++ b/src/main/java/com/company/project/common/exception/package-info.java @@ -0,0 +1,9 @@ +/** + * Created with IntelliJ IDEA. + * Description: + * 全局异常处理 + * @author LErry.li + * Date: 2018-06-16 + * Time: 14:29 + */ +package com.company.project.common.exception; \ No newline at end of file diff --git a/src/main/java/com/company/project/common/interceptor/AllowCrossDomainInterceptor.java b/src/main/java/com/company/project/common/interceptor/AllowCrossDomainInterceptor.java new file mode 100644 index 000000000..20a536646 --- /dev/null +++ b/src/main/java/com/company/project/common/interceptor/AllowCrossDomainInterceptor.java @@ -0,0 +1,52 @@ +package com.company.project.common.interceptor; + +import com.company.project.EnvironmentEnum; +import com.company.project.common.util.StringUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Created with IntelliJ IDEA. + * Description: + * 允许跨域拦截器 + * @author LErry.li + * Date: 2018-06-16 + * Time: 14:36 + */ +@Component +public class AllowCrossDomainInterceptor implements HandlerInterceptor { + + @Autowired + private Environment env; + + @Override + public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { + //仅在非正式环境下生效 + if (!EnvironmentEnum.isProdEnv(env)) { + String origin = httpServletRequest.getHeader("Origin"); + httpServletResponse.setHeader("Access-Control-Allow-Origin", StringUtil.isEmpty(origin) ? "*" : origin); + httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, PATCH"); + httpServletResponse.setHeader("Access-Control-Max-Age", "0"); + httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token"); + httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); + httpServletResponse.setHeader("XDomainRequestAllowed", "1"); + } + return true; + } + + @Override + public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { + // nothing to do + } + + @Override + public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { + // nothing to do + } +} diff --git a/src/main/java/com/company/project/common/interceptor/ResponseResultInterceptor.java b/src/main/java/com/company/project/common/interceptor/ResponseResultInterceptor.java new file mode 100644 index 000000000..aa1c7ff72 --- /dev/null +++ b/src/main/java/com/company/project/common/interceptor/ResponseResultInterceptor.java @@ -0,0 +1,52 @@ +package com.company.project.common.interceptor; + +import com.company.project.common.result.ResponseResult; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Method; + +/** + * Created with IntelliJ IDEA. + * Description: + * 接口响应体控制拦截器 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +@Component +public class ResponseResultInterceptor implements HandlerInterceptor { + + public static final String RESPONSE_RESULT = "RESPONSE-RESULT"; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (handler instanceof HandlerMethod) { + final HandlerMethod handlerMethod = (HandlerMethod) handler; + final Class clazz = handlerMethod.getBeanType(); + final Method method = handlerMethod.getMethod(); + if (clazz.isAnnotationPresent(ResponseResult.class)) { + request.setAttribute(RESPONSE_RESULT, clazz.getAnnotation(ResponseResult.class)); + } else if (method.isAnnotationPresent(ResponseResult.class)) { + request.setAttribute(RESPONSE_RESULT, method.getAnnotation(ResponseResult.class)); + } + } + + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + // nothing to do + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + // nothing to do + } + +} diff --git a/src/main/java/com/company/project/common/interceptor/package-info.java b/src/main/java/com/company/project/common/interceptor/package-info.java new file mode 100644 index 000000000..1aaade2f1 --- /dev/null +++ b/src/main/java/com/company/project/common/interceptor/package-info.java @@ -0,0 +1,9 @@ +/** + * Created with IntelliJ IDEA. + * Description: + * 拦截器 + * @author LErry.li + * Date: 2018-06-16 + * Time: 14:34 + */ +package com.company.project.common.interceptor; \ No newline at end of file diff --git a/src/main/java/com/company/project/common/mapper/CrudMapper.java b/src/main/java/com/company/project/common/mapper/CrudMapper.java new file mode 100644 index 000000000..f0fd73a9f --- /dev/null +++ b/src/main/java/com/company/project/common/mapper/CrudMapper.java @@ -0,0 +1,16 @@ +package com.company.project.common.mapper; + +/** + * Created with IntelliJ IDEA. + * Description: + * 基础增删改查功能mapper + * @author LErry.li + * Date: 2018-06-16 + * Time: 12:49 + */ +public interface CrudMapper extends + InsertMapper, + DeleteMapper, + UpdateMapper, + SelectMapper { +} diff --git a/src/main/java/com/company/project/common/mapper/DeleteMapper.java b/src/main/java/com/company/project/common/mapper/DeleteMapper.java new file mode 100644 index 000000000..279a8e302 --- /dev/null +++ b/src/main/java/com/company/project/common/mapper/DeleteMapper.java @@ -0,0 +1,21 @@ +package com.company.project.common.mapper; + +import tk.mybatis.mapper.common.Marker; +import tk.mybatis.mapper.common.base.delete.DeleteByPrimaryKeyMapper; +import tk.mybatis.mapper.common.condition.DeleteByConditionMapper; +import tk.mybatis.mapper.common.ids.DeleteByIdsMapper; + +/** + * Created with IntelliJ IDEA. + * Description: + * 基础删除功能mapper + * @author LErry.li + * Date: 2018-06-16 + * Time: 12:46 + */ +public interface DeleteMapper extends Marker, + tk.mybatis.mapper.common.base.delete.DeleteMapper, + DeleteByPrimaryKeyMapper, + DeleteByConditionMapper, + DeleteByIdsMapper { +} diff --git a/src/main/java/com/company/project/common/mapper/InsertMapper.java b/src/main/java/com/company/project/common/mapper/InsertMapper.java new file mode 100644 index 000000000..dbf13de15 --- /dev/null +++ b/src/main/java/com/company/project/common/mapper/InsertMapper.java @@ -0,0 +1,19 @@ +package com.company.project.common.mapper; + +import tk.mybatis.mapper.common.Marker; +import tk.mybatis.mapper.common.MySqlMapper; +import tk.mybatis.mapper.common.base.insert.InsertSelectiveMapper; + +/** + * Created with IntelliJ IDEA. + * Description: + * 基础插入功能mapper + * @author LErry.li + * Date: 2018-06-16 + * Time: 12:44 + */ +public interface InsertMapper extends Marker, + tk.mybatis.mapper.common.base.insert.InsertMapper, + InsertSelectiveMapper, + MySqlMapper { +} diff --git a/src/main/java/com/company/project/common/mapper/SelectMapper.java b/src/main/java/com/company/project/common/mapper/SelectMapper.java new file mode 100644 index 000000000..0567fe7fe --- /dev/null +++ b/src/main/java/com/company/project/common/mapper/SelectMapper.java @@ -0,0 +1,29 @@ +package com.company.project.common.mapper; + +import tk.mybatis.mapper.common.Marker; +import tk.mybatis.mapper.common.base.select.*; +import tk.mybatis.mapper.common.condition.SelectByConditionMapper; +import tk.mybatis.mapper.common.condition.SelectCountByConditionMapper; +import tk.mybatis.mapper.common.example.SelectByExampleMapper; +import tk.mybatis.mapper.common.ids.SelectByIdsMapper; + +/** + * Created with IntelliJ IDEA. + * Description: + * 基础查询功能mapper + * @author LErry.li + * Date: 2018-06-16 + * Time: 12:48 + */ +public interface SelectMapper extends Marker, + SelectOneMapper, + tk.mybatis.mapper.common.base.select.SelectMapper, + SelectAllMapper, + SelectCountMapper, + SelectByPrimaryKeyMapper, + ExistsWithPrimaryKeyMapper, + SelectByIdsMapper, + SelectByConditionMapper, + SelectCountByConditionMapper, + SelectByExampleMapper { +} diff --git a/src/main/java/com/company/project/common/mapper/UpdateMapper.java b/src/main/java/com/company/project/common/mapper/UpdateMapper.java new file mode 100644 index 000000000..e4f66da3e --- /dev/null +++ b/src/main/java/com/company/project/common/mapper/UpdateMapper.java @@ -0,0 +1,24 @@ +package com.company.project.common.mapper; + +import tk.mybatis.mapper.common.Marker; +import tk.mybatis.mapper.common.base.update.UpdateByPrimaryKeyMapper; +import tk.mybatis.mapper.common.base.update.UpdateByPrimaryKeySelectiveMapper; +import tk.mybatis.mapper.common.condition.UpdateByConditionMapper; +import tk.mybatis.mapper.common.condition.UpdateByConditionSelectiveMapper; +import tk.mybatis.mapper.common.example.UpdateByExampleSelectiveMapper; + +/** + * Created with IntelliJ IDEA. + * Description: + * 基础更新功能mapper + * @author LErry.li + * Date: 2018-06-16 + * Time: 12:47 + */ +public interface UpdateMapper extends Marker, + UpdateByPrimaryKeyMapper, + UpdateByPrimaryKeySelectiveMapper, + UpdateByConditionMapper, + UpdateByConditionSelectiveMapper, + UpdateByExampleSelectiveMapper { +} diff --git a/src/main/java/com/company/project/common/mapper/package-info.java b/src/main/java/com/company/project/common/mapper/package-info.java new file mode 100644 index 000000000..b50d2ea5d --- /dev/null +++ b/src/main/java/com/company/project/common/mapper/package-info.java @@ -0,0 +1,9 @@ +/** + * Created with IntelliJ IDEA. + * Description: + * 通用Mapper + * @author LErry.li + * Date: 2018-06-16 + * Time: 14:29 + */ +package com.company.project.common.mapper; \ No newline at end of file diff --git a/src/main/java/com/company/project/common/result/DefaultErrorResult.java b/src/main/java/com/company/project/common/result/DefaultErrorResult.java new file mode 100644 index 000000000..e84072d44 --- /dev/null +++ b/src/main/java/com/company/project/common/result/DefaultErrorResult.java @@ -0,0 +1,104 @@ +package com.company.project.common.result; + +import cn.hutool.core.util.StrUtil; +import com.company.project.common.exception.BusinessException; +import com.company.project.common.exception.ExceptionEnum; +import com.company.project.common.util.RequestContextHolderUtil; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.http.HttpStatus; + +import java.util.Date; + + +/** + * Created with IntelliJ IDEA. + * Description: + * 默认全局错误返回结果 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class DefaultErrorResult implements Result { + private static final long serialVersionUID = 1899083570489722793L; + + /** + * HTTP响应状态码 {@link org.springframework.http.HttpStatus} + */ + private Integer status; + + /** + * HTTP响应状态码的英文提示 + */ + private String error; + + /** + * 异常堆栈的精简信息 + * + */ + private String message; + + /** + * 我们系统内部自定义的返回值编码,{@link ResultCode} 它是对错误更加详细的编码 + * + * 备注:spring boot默认返回异常时,该字段为null + */ + private Integer code; + + /** + * 调用接口路径 + */ + private String path; + + /** + * 异常的名字 + */ + private String exception; + + /** + * 异常的错误传递的数据 + */ + private Object errors; + + /** + * 时间戳 + */ + private Date timestamp; + + public static DefaultErrorResult failure(ResultCode resultCode, Throwable e, HttpStatus httpStatus, Object errors) { + DefaultErrorResult result = DefaultErrorResult.failure(resultCode, e, httpStatus); + result.setErrors(errors); + return result; + } + + public static DefaultErrorResult failure(ResultCode resultCode, Throwable e, HttpStatus httpStatus) { + DefaultErrorResult result = new DefaultErrorResult(); + result.setCode(resultCode.code()); + result.setMessage(resultCode.message()); + result.setStatus(httpStatus.value()); + result.setError(httpStatus.getReasonPhrase()); + result.setException(e.getClass().getName()); + result.setPath(RequestContextHolderUtil.getRequest().getRequestURI()); + result.setTimestamp(new Date()); + return result; + } + + public static DefaultErrorResult failure(BusinessException e) { + ExceptionEnum ee = ExceptionEnum.getByEClass(e.getClass()); + if (ee != null) { + return DefaultErrorResult.failure(ee.getResultCode(), e, ee.getHttpStatus(), e.getData()); + } + + DefaultErrorResult defaultErrorResult = DefaultErrorResult.failure(e.getResultCode() == null ? ResultCode.SUCCESS : e.getResultCode(), e, HttpStatus.OK, e.getData()); + if (StrUtil.isNotEmpty(e.getMessage())) { + defaultErrorResult.setMessage(e.getMessage()); + } + return defaultErrorResult; + } +} diff --git a/src/main/java/com/company/project/common/result/ParameterInvalidItem.java b/src/main/java/com/company/project/common/result/ParameterInvalidItem.java new file mode 100644 index 000000000..639f69436 --- /dev/null +++ b/src/main/java/com/company/project/common/result/ParameterInvalidItem.java @@ -0,0 +1,34 @@ +package com.company.project.common.result; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Created with IntelliJ IDEA. + * Description: + * 参数无效项 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +public class ParameterInvalidItem { + + /** + * 无效字段的名称 + */ + private String fieldName; + + /** + * 错误信息 + */ + private String message; + + +} \ No newline at end of file diff --git a/src/main/java/com/company/project/common/result/PlatformResult.java b/src/main/java/com/company/project/common/result/PlatformResult.java new file mode 100644 index 000000000..edcf7e4ce --- /dev/null +++ b/src/main/java/com/company/project/common/result/PlatformResult.java @@ -0,0 +1,83 @@ +package com.company.project.common.result; + + +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Created with IntelliJ IDEA. + * Description: + * 平台通用返回结果 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Data +public class PlatformResult implements Result { + private static final long serialVersionUID = 874200365941306385L; + + private Integer code; + + private String message; + + private Object data; + + public PlatformResult setCode(Integer code) { + this.code = code; + return this; + } + + public PlatformResult setMessage(String message) { + this.message = message; + return this; + } + + public static PlatformResult success() { + PlatformResult result = new PlatformResult(); + result.setResultCode(ResultCode.SUCCESS); + return result; + } + + public static PlatformResult success(Object data) { + PlatformResult result = new PlatformResult(); + result.setResultCode(ResultCode.SUCCESS); + result.setData(data); + return result; + } + + public static PlatformResult failure(ResultCode resultCode) { + PlatformResult result = new PlatformResult(); + result.setResultCode(resultCode); + return result; + } + + public static PlatformResult failure(ResultCode resultCode, Object data) { + PlatformResult result = new PlatformResult(); + result.setResultCode(resultCode); + result.setData(data); + return result; + } + + public static PlatformResult failure(String message) { + PlatformResult result = new PlatformResult(); + result.setCode(ResultCode.PARAM_IS_INVALID.code()); + result.setMessage(message); + return result; + } + + private void setResultCode(ResultCode code) { + this.code = code.code(); + this.message = code.message(); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/src/main/java/com/company/project/common/result/ResponseResult.java b/src/main/java/com/company/project/common/result/ResponseResult.java new file mode 100644 index 000000000..75ebcd5ad --- /dev/null +++ b/src/main/java/com/company/project/common/result/ResponseResult.java @@ -0,0 +1,18 @@ +package com.company.project.common.result; + +import java.lang.annotation.*; + +/** + * Created with IntelliJ IDEA. + * Description: + * 接口返回结果增强 会通过拦截器拦截后放入标记,在WebResponseBodyHandler进行结果处理 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ResponseResult { + Class value() default PlatformResult.class; +} diff --git a/src/main/java/com/company/project/common/result/ResponseResultHandler.java b/src/main/java/com/company/project/common/result/ResponseResultHandler.java new file mode 100644 index 000000000..4da1c323b --- /dev/null +++ b/src/main/java/com/company/project/common/result/ResponseResultHandler.java @@ -0,0 +1,50 @@ +package com.company.project.common.result; + +import com.company.project.common.interceptor.ResponseResultInterceptor; +import com.company.project.common.util.RequestContextHolderUtil; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +/** + * Created with IntelliJ IDEA. + * Description: + * + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +@ControllerAdvice +public class ResponseResultHandler implements ResponseBodyAdvice { + @Override + public boolean supports(MethodParameter returnType, Class> converterType) { + ResponseResult responseResultAnn = (ResponseResult) RequestContextHolderUtil.getRequest().getAttribute(ResponseResultInterceptor.RESPONSE_RESULT); + return responseResultAnn == null ? false : true; + } + + @Override + public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { + ResponseResult responseResultAnn = (ResponseResult) RequestContextHolderUtil.getRequest().getAttribute(ResponseResultInterceptor.RESPONSE_RESULT); + + Class resultClazz = responseResultAnn.value(); + + if (resultClazz.isAssignableFrom(PlatformResult.class)) { + if (body instanceof DefaultErrorResult) { + DefaultErrorResult defaultErrorResult = (DefaultErrorResult) body; + return PlatformResult.builder() + .code(defaultErrorResult.getCode()) + .message(defaultErrorResult.getMessage()) + .data(defaultErrorResult.getErrors()) + .build(); + } + + return PlatformResult.success(body); + } + + return body; + } +} diff --git a/src/main/java/com/company/project/common/result/Result.java b/src/main/java/com/company/project/common/result/Result.java new file mode 100644 index 000000000..bc0e4dd08 --- /dev/null +++ b/src/main/java/com/company/project/common/result/Result.java @@ -0,0 +1,15 @@ +package com.company.project.common.result; + +import java.io.Serializable; + +/** + * Created with IntelliJ IDEA. + * Description: + * 响应格式父接口 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:41 + */ +public interface Result extends Serializable { + +} diff --git a/src/main/java/com/company/project/common/result/ResultCode.java b/src/main/java/com/company/project/common/result/ResultCode.java new file mode 100644 index 000000000..2ea1d2456 --- /dev/null +++ b/src/main/java/com/company/project/common/result/ResultCode.java @@ -0,0 +1,108 @@ +package com.company.project.common.result; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created with IntelliJ IDEA. + * Description: + * 统一返回状态码 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:42 + */ +public enum ResultCode { + + /* 成功状态码 */ + SUCCESS(200, "成功"), + + /* 参数错误:10001-19999 */ + PARAM_IS_INVALID(10001, "参数无效"), + PARAM_IS_BLANK(10002, "参数为空"), + PARAM_TYPE_BIND_ERROR(10003, "参数类型错误"), + PARAM_NOT_COMPLETE(10004, "参数缺失"), + + /* 用户错误:20001-29999*/ + USER_NOT_LOGGED_IN(20001, "用户未登录"), + USER_LOGIN_ERROR(20002, "账号不存在或密码错误"), + USER_ACCOUNT_FORBIDDEN(20003, "账号已被禁用"), + USER_NOT_EXIST(20004, "用户不存在"), + USER_HAS_EXISTED(20005, "用户已存在"), + + /* 业务错误:30001-39999 */ + SPECIFIED_QUESTIONED_USER_NOT_EXIST(30001, "业务错误"), + + /* 系统错误:40001-49999 */ + SYSTEM_INNER_ERROR(40001, "系统繁忙,请稍后重试"), + + /* 数据错误:50001-599999 */ + RESULE_DATA_NONE(50001, "数据未找到"), + DATA_IS_WRONG(50002, "数据有误"), + DATA_ALREADY_EXISTED(50003, "数据已存在"), + + /* 接口错误:60001-69999 */ + INTERFACE_INNER_INVOKE_ERROR(60001, "内部系统接口调用异常"), + INTERFACE_OUTTER_INVOKE_ERROR(60002, "外部系统接口调用异常"), + INTERFACE_FORBID_VISIT(60003, "该接口禁止访问"), + INTERFACE_ADDRESS_INVALID(60004, "接口地址无效"), + INTERFACE_REQUEST_TIMEOUT(60005, "接口请求超时"), + INTERFACE_EXCEED_LOAD(60006, "接口负载过高"), + + /* 权限错误:70001-79999 */ + PERMISSION_NO_ACCESS(70001, "无访问权限"), + RESOURCE_EXISTED(70002, "资源已存在"), + RESOURCE_NOT_EXISTED(70003, "资源不存在"); + + private Integer code; + + private String message; + + ResultCode(Integer code, String message) { + this.code = code; + this.message = message; + } + + public Integer code() { + return this.code; + } + + public String message() { + return this.message; + } + + public static String getMessage(String name) { + for (ResultCode item : ResultCode.values()) { + if (item.name().equals(name)) { + return item.message; + } + } + return name; + } + + public static Integer getCode(String name) { + for (ResultCode item : ResultCode.values()) { + if (item.name().equals(name)) { + return item.code; + } + } + return null; + } + + @Override + public String toString() { + return this.name(); + } + + //校验重复的code值 + public static void main(String[] args) { + ResultCode[] apiResultCodes = ResultCode.values(); + List codeList = new ArrayList<>(); + for (ResultCode apiResultCode : apiResultCodes) { + if (codeList.contains(apiResultCode.code)) { + System.out.println(apiResultCode.code); + } else { + codeList.add(apiResultCode.code()); + } + } + } +} diff --git a/src/main/java/com/company/project/common/result/package-info.java b/src/main/java/com/company/project/common/result/package-info.java new file mode 100644 index 000000000..7d52addf5 --- /dev/null +++ b/src/main/java/com/company/project/common/result/package-info.java @@ -0,0 +1,9 @@ +/** + * Created with IntelliJ IDEA. + * Description: + * 接口响应体格式统一封装 + * @author LErry.li + * Date: 2018-06-16 + * Time: 14:29 + */ +package com.company.project.common.result; \ No newline at end of file diff --git a/src/main/java/com/company/project/common/util/ConvertUtil.java b/src/main/java/com/company/project/common/util/ConvertUtil.java new file mode 100644 index 000000000..7212dd30c --- /dev/null +++ b/src/main/java/com/company/project/common/util/ConvertUtil.java @@ -0,0 +1,77 @@ +package com.company.project.common.util; + +import com.alibaba.fastjson.JSON; +import com.company.project.common.result.ParameterInvalidItem; +import com.google.common.collect.Lists; +import org.springframework.util.CollectionUtils; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; + +import javax.validation.ConstraintViolation; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * Created with IntelliJ IDEA. + * Description: + * 类转化工具类 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:42 + */ +public class ConvertUtil { + + private ConvertUtil() { + + } + + public static List convertCVSetToParameterInvalidItemList(Set> cvset) { + if (CollectionUtils.isEmpty(cvset)) { + return Collections.emptyList(); + } + char ch = '.'; + List parameterInvalidItemList = Lists.newArrayList(); + for (ConstraintViolation cv : cvset) { + ParameterInvalidItem parameterInvalidItem = new ParameterInvalidItem(); + String propertyPath = cv.getPropertyPath().toString(); + if (propertyPath.indexOf(ch) != -1) { + String[] propertyPathArr = propertyPath.split("\\."); + parameterInvalidItem.setFieldName(propertyPathArr[propertyPathArr.length - 1]); + } else { + parameterInvalidItem.setFieldName(propertyPath); + } + parameterInvalidItem.setMessage(cv.getMessage()); + parameterInvalidItemList.add(parameterInvalidItem); + } + + return parameterInvalidItemList; + } + + public static List convertBindingResultToMapParameterInvalidItemList(BindingResult bindingResult) { + if (bindingResult == null) { + return Collections.emptyList(); + } + + List parameterInvalidItemList = Lists.newArrayList(); + + List fieldErrorList = bindingResult.getFieldErrors(); + for (FieldError fieldError : fieldErrorList) { + ParameterInvalidItem parameterInvalidItem = new ParameterInvalidItem(); + parameterInvalidItem.setFieldName(fieldError.getField()); + parameterInvalidItem.setMessage(fieldError.getDefaultMessage()); + parameterInvalidItemList.add(parameterInvalidItem); + } + + return parameterInvalidItemList; + } + + public static Map convertBeanToMap(Object object) { + if (object == null) { + return Collections.emptyMap(); + } + return JSON.parseObject(JSON.toJSONString(object)); + } +} \ No newline at end of file diff --git a/src/main/java/com/company/project/common/util/IpUtil.java b/src/main/java/com/company/project/common/util/IpUtil.java new file mode 100644 index 000000000..dc90cdf75 --- /dev/null +++ b/src/main/java/com/company/project/common/util/IpUtil.java @@ -0,0 +1,145 @@ +package com.company.project.common.util; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.util.Enumeration; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Created with IntelliJ IDEA. + * Description: + * IP操作工具类 + * @author LErry.li + * Date: 2018-06-16 + * Time: 16:59 + */ +public class IpUtil { + + private IpUtil() { + + } + + private static final Logger logger = LoggerFactory.getLogger(IpUtil.class); + + private static final String IP_PATTERN = "^(?:(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\.){3}(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\b"; + + private static final String UNKNOWN = "unknown"; + + private static final String LOCAL_IP = "127.0.0.1"; + + /** + * 获取请求中的ip地址:过了多级反向代理,获取的ip不是唯一的,二是包含中间代理层ip + * @param request + * @return 可能有多个,例如:192.168.1.110, 192.168.1.120 + */ + public static String getIpAddr(HttpServletRequest request) { + String ip = LOCAL_IP; + if (request != null) { + ip = request.getHeader("x-forwarded-for"); + if (StringUtil.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + + if (StringUtil.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + + if (StringUtil.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + } + return ip; + + } + + /** + * 获取客户端请求中的真实的ip地址 + * 获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的。 + * 但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实IP地址。而且,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个, + * 而是一串ip值,例如:192.168.1.110, 192.168.1.120, 192.168.1.130, 192.168.1.100。其中第一个192.168.1.110才是用户真实的ip + * @param request + * @return + */ + public static String getRealIp(HttpServletRequest request) { + String ip = LOCAL_IP; + if (request == null) { + return ip; + } + ip = request.getHeader("x-forwarded-for"); + ip = getIp(request,ip); + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + if (LOCAL_IP.equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) { + //根据网卡取本机配置的IP + InetAddress inet = null; + try { + inet = InetAddress.getLocalHost(); + ip = inet.getHostAddress(); + } catch (UnknownHostException e) { + logger.error("getRealIp occurs error, caused by: ", e); + } + } + } + String ch = ","; + if (ip != null && ip.contains(ch)) { + ip = ip.substring(0, ip.indexOf(ch)); + } + return ip; + } + + /** + * 通过各种方式获取IP + * @param request + * @param ip + * @return + */ + private static String getIp(HttpServletRequest request, String ip){ + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + return ip; + } + + /** + * 获取服务器IP + */ + public static String getServiceIp() { + Enumeration netInterfaces = null; + String ipsStr = ""; + try { + netInterfaces = NetworkInterface.getNetworkInterfaces(); + while (netInterfaces.hasMoreElements()) { + NetworkInterface ni = netInterfaces.nextElement(); + Enumeration ips = ni.getInetAddresses(); + Pattern pattern = Pattern.compile(IP_PATTERN); + while (ips.hasMoreElements()) { + String ip = ips.nextElement().getHostAddress(); + Matcher matcher = pattern.matcher(ip); + if (matcher.matches() && !LOCAL_IP.equals(ip)) { + ipsStr = ip; + } + } + } + } catch (Exception e) { + logger.error("getServiceIp occurs error, caused by: ", e); + } + + return ipsStr; + } +} diff --git a/src/main/java/com/company/project/common/util/LogAspectUtil.java b/src/main/java/com/company/project/common/util/LogAspectUtil.java new file mode 100644 index 000000000..91b7e12cb --- /dev/null +++ b/src/main/java/com/company/project/common/util/LogAspectUtil.java @@ -0,0 +1,90 @@ +package com.company.project.common.util; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * Created with IntelliJ IDEA. + * Description: + * AOP记录日志的一些共用方法 + * @author LErry.li + * Date: 2018-06-17 + * Time: 15:19 + */ +public class LogAspectUtil { + + private LogAspectUtil(){ + + } + + /** + * 获取需要记录日志方法的参数,敏感参数用*代替 + * @param joinPoint 切点 + * @return 去除敏感参数后的Json字符串 + */ + public static String getMethodParams(ProceedingJoinPoint joinPoint){ + Object[] arguments = joinPoint.getArgs(); + StringBuilder sb = new StringBuilder(); + if(arguments ==null || arguments.length <= 0){ + return sb.toString(); + } + for (Object arg : arguments) { + //移除敏感内容 + String paramStr; + if (arg instanceof HttpServletResponse) { + paramStr = HttpServletResponse.class.getSimpleName(); + } else if (arg instanceof HttpServletRequest) { + paramStr = HttpServletRequest.class.getSimpleName(); + } else if (arg instanceof MultipartFile) { + long size = ((MultipartFile) arg).getSize(); + paramStr = MultipartFile.class.getSimpleName() + " size:" + size; + } else { + paramStr = deleteSensitiveContent(arg); + } + sb.append(paramStr).append(","); + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } + + /** + * 删除参数中的敏感内容 + * @param obj 参数对象 + * @return 去除敏感内容后的参数对象 + */ + public static String deleteSensitiveContent(Object obj) { + JSONObject jsonObject = new JSONObject(); + if (obj == null || obj instanceof Exception) { + return jsonObject.toJSONString(); + } + String param = JSON.toJSONString(obj); + try { + jsonObject = JSONObject.parseObject(param); + }catch (Exception e) { + return String.valueOf(obj); + } + List sensitiveFieldList = getSensitiveFieldList(); + for (String sensitiveField : sensitiveFieldList) { + if (jsonObject.containsKey(sensitiveField)) { + jsonObject.put(sensitiveField, "******"); + } + } + return jsonObject.toJSONString(); + } + + /** + * 敏感字段列表(当然这里你可以更改为可配置的) + */ + private static List getSensitiveFieldList() { + List sensitiveFieldList = Lists.newArrayList(); + sensitiveFieldList.add("pwd"); + sensitiveFieldList.add("password"); + return sensitiveFieldList; + } +} diff --git a/src/main/java/com/company/project/common/util/RequestContextHolderUtil.java b/src/main/java/com/company/project/common/util/RequestContextHolderUtil.java new file mode 100644 index 000000000..5fbea25b4 --- /dev/null +++ b/src/main/java/com/company/project/common/util/RequestContextHolderUtil.java @@ -0,0 +1,44 @@ +package com.company.project.common.util; + +import org.springframework.web.context.ContextLoader; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +/** + * Created with IntelliJ IDEA. + * Description: + * 应用级对象获取工具类 + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:42 + */ +public class RequestContextHolderUtil { + + private RequestContextHolderUtil() { + + } + public static HttpServletRequest getRequest() { + return getRequestAttributes().getRequest(); + } + + public static HttpServletResponse getResponse() { + return getRequestAttributes().getResponse(); + } + + public static HttpSession getSession() { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() { + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()); + } + + public static ServletContext getServletContext() { + return ContextLoader.getCurrentWebApplicationContext().getServletContext(); + } +} diff --git a/src/main/java/com/company/project/common/util/StringUtil.java b/src/main/java/com/company/project/common/util/StringUtil.java new file mode 100644 index 000000000..809576f45 --- /dev/null +++ b/src/main/java/com/company/project/common/util/StringUtil.java @@ -0,0 +1,70 @@ +package com.company.project.common.util; + + +import cn.hutool.core.util.StrUtil; + +/** + * Created with IntelliJ IDEA. + * Description: + * 字符串操作工具类,扩展自 hutool.cn + * @author LErry.li + * Date: 2018-06-15 + * Time: 14:42 + */ +public class StringUtil extends StrUtil { + + /** + * 格式化字符串(替换符为%s) + */ + public static String formatIfArgs(String format, Object... args) { + if (StrUtil.isEmpty(format)) { + return format; + } + + return (args == null || args.length == 0) ? String.format(format.replaceAll("%([^n])", "%%$1")) : String.format(format, args); + } + + /** + * 格式化字符串(替换符自己指定) + */ + public static String formatIfArgs(String format, String replaceOperator, Object... args) { + if (isEmpty(format) || isEmpty(replaceOperator)) { + return format; + } + + format = replace(format, replaceOperator, "%s"); + return formatIfArgs(format, args); + } + + /** + * 替换字符串 + */ + public static String replace(String inString, String oldPattern, String newPattern) { + if (isNotEmpty(inString) && isNotEmpty(oldPattern) && newPattern != null) { + int index = inString.indexOf(oldPattern); + if (index == -1) { + return inString; + } else { + int capacity = inString.length(); + if (newPattern.length() > oldPattern.length()) { + capacity += 16; + } + + StringBuilder sb = new StringBuilder(capacity); + int pos = 0; + + for(int patLen = oldPattern.length(); index >= 0; index = inString.indexOf(oldPattern, pos)) { + sb.append(inString.substring(pos, index)); + sb.append(newPattern); + pos = index + patLen; + } + + sb.append(inString.substring(pos)); + return sb.toString(); + } + } else { + return inString; + } + } + +} diff --git a/src/main/java/com/company/project/common/util/package-info.java b/src/main/java/com/company/project/common/util/package-info.java new file mode 100644 index 000000000..e9c006a82 --- /dev/null +++ b/src/main/java/com/company/project/common/util/package-info.java @@ -0,0 +1,9 @@ +/** + * Created with IntelliJ IDEA. + * Description: + * 公共工具 + * @author LErry.li + * Date: 2018-06-16 + * Time: 14:29 + */ +package com.company.project.common.util; \ No newline at end of file diff --git a/src/main/java/com/company/project/configurer/DataSourceConfig.java b/src/main/java/com/company/project/configurer/DataSourceConfig.java new file mode 100644 index 000000000..f497735d2 --- /dev/null +++ b/src/main/java/com/company/project/configurer/DataSourceConfig.java @@ -0,0 +1,34 @@ +package com.company.project.configurer; + +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.sql.DataSource; + +/** + * Created with IntelliJ IDEA. + * Description: + * 数据连接池 + * @author LErry.li + * Date: 2018-06-14 + * Time: 15:30 + */ +@Configuration +@EnableTransactionManagement +public class DataSourceConfig { + + private Logger log = LoggerFactory.getLogger(this.getClass()); + + @Bean(name = "dataSource") + public DataSource dataSource(Environment environment) { + return DruidDataSourceBuilder + .create() + .build(environment, "spring.datasource.druid."); + } + +} diff --git a/src/main/java/com/company/project/configurer/HibernateValidateConfig.java b/src/main/java/com/company/project/configurer/HibernateValidateConfig.java new file mode 100644 index 000000000..ab426e5ad --- /dev/null +++ b/src/main/java/com/company/project/configurer/HibernateValidateConfig.java @@ -0,0 +1,38 @@ +package com.company.project.configurer; + +import org.hibernate.validator.HibernateValidator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; + +/** + * Created with IntelliJ IDEA. + * Description: + * + * @author LErry.li + * Date: 2018-06-14 + * Time: 15:34 + */ +@Configuration +public class HibernateValidateConfig { + + @Bean + public ReloadableResourceBundleMessageSource messageSource() { + ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource(); + reloadableResourceBundleMessageSource.setBasenames("classpath:message/ValidationMessages"); + reloadableResourceBundleMessageSource.setUseCodeAsDefaultMessage(true); + reloadableResourceBundleMessageSource.setDefaultEncoding("UTF-8"); + reloadableResourceBundleMessageSource.setCacheSeconds(60); + return reloadableResourceBundleMessageSource; + } + + @Bean + public LocalValidatorFactoryBean validator() { + LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); + localValidatorFactoryBean.setProviderClass(HibernateValidator.class); + localValidatorFactoryBean.setValidationMessageSource(messageSource()); + return localValidatorFactoryBean; + } + +} diff --git a/src/main/java/com/company/project/configurer/MybatisConfigurer.java b/src/main/java/com/company/project/configurer/MybatisConfigurer.java index 0d6487d86..899b2c3df 100644 --- a/src/main/java/com/company/project/configurer/MybatisConfigurer.java +++ b/src/main/java/com/company/project/configurer/MybatisConfigurer.java @@ -4,16 +4,12 @@ import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import tk.mybatis.spring.mapper.MapperScannerConfigurer; -import javax.annotation.Resource; import javax.sql.DataSource; import java.util.Properties; @@ -21,6 +17,7 @@ /** * Mybatis & Mapper & PageHelper 配置 + * @author lerry */ @Configuration public class MybatisConfigurer { @@ -34,9 +31,12 @@ public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource) throws Exc //配置分页插件,详情请查阅官方文档 PageHelper pageHelper = new PageHelper(); Properties properties = new Properties(); - properties.setProperty("pageSizeZero", "true");//分页尺寸为0时查询所有纪录不再执行分页 - properties.setProperty("reasonable", "true");//页码<=0 查询第一页,页码>=总页数查询最后一页 - properties.setProperty("supportMethodsArguments", "true");//支持通过 Mapper 接口参数来传递分页参数 + //分页尺寸为0时查询所有纪录不再执行分页 + properties.setProperty("pageSizeZero", "true"); + //页码<=0 查询第一页,页码>=总页数查询最后一页 + properties.setProperty("reasonable", "true"); + //支持通过 Mapper 接口参数来传递分页参数 + properties.setProperty("supportMethodsArguments", "true"); pageHelper.setProperties(properties); //添加插件 @@ -57,7 +57,8 @@ public MapperScannerConfigurer mapperScannerConfigurer() { //配置通用Mapper,详情请查阅官方文档 Properties properties = new Properties(); properties.setProperty("mappers", MAPPER_INTERFACE_REFERENCE); - properties.setProperty("notEmpty", "false");//insert、update是否判断字符串类型!='' 即 test="str != null"表达式内是否追加 and str != '' + //insert、update是否判断字符串类型!='' 即 test="str != null"表达式内是否追加 and str != '' + properties.setProperty("notEmpty", "false"); properties.setProperty("IDENTITY", "MYSQL"); mapperScannerConfigurer.setProperties(properties); diff --git a/src/main/java/com/company/project/configurer/WebMvcConfigurer.java b/src/main/java/com/company/project/configurer/WebMvcConfigurer.java index 5292390fa..b084cda0f 100644 --- a/src/main/java/com/company/project/configurer/WebMvcConfigurer.java +++ b/src/main/java/com/company/project/configurer/WebMvcConfigurer.java @@ -1,112 +1,92 @@ package com.company.project.configurer; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; - -import com.company.project.core.Result; -import com.company.project.core.ResultCode; -import com.company.project.core.ServiceException; +import com.company.project.common.interceptor.AllowCrossDomainInterceptor; +import com.company.project.common.interceptor.ResponseResultInterceptor; +import com.company.project.common.result.PlatformResult; +import com.company.project.common.util.IpUtil; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.HandlerExceptionResolver; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.NoHandlerFoundException; -import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** - * Spring MVC 配置 + * 全局定制化Spring Boot的MVC特性 + * + * @author lerry */ @Configuration public class WebMvcConfigurer extends WebMvcConfigurerAdapter { private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class); + + /** + * 当前激活的配置文件 + */ @Value("${spring.profiles.active}") - private String env;//当前激活的配置文件 + private String env; + + private String apiUri = "/**"; + + /** + * 响应结果控制拦截 + */ + @Autowired + private ResponseResultInterceptor responseResultInterceptor; + + /** + * 跨域配置拦截器 + */ + @Autowired + private AllowCrossDomainInterceptor allowCrossDomainInterceptor; - //使用阿里 FastJson 作为JSON MessageConverter @Override public void configureMessageConverters(List> converters) { + //使用阿里 FastJson 作为JSON MessageConverter FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); FastJsonConfig config = new FastJsonConfig(); - config.setSerializerFeatures(SerializerFeature.WriteMapNullValue);//保留空的字段 - //SerializerFeature.WriteNullStringAsEmpty,//String null -> "" - //SerializerFeature.WriteNullNumberAsZero//Number null -> 0 - // 按需配置,更多参考FastJson文档哈 - + //保留空的字段 + config.setSerializerFeatures(SerializerFeature.WriteMapNullValue); + // 按需配置,更多参考FastJson文档 converter.setFastJsonConfig(config); converter.setDefaultCharset(Charset.forName("UTF-8")); converters.add(converter); } - - //统一异常处理 - @Override - public void configureHandlerExceptionResolvers(List exceptionResolvers) { - exceptionResolvers.add(new HandlerExceptionResolver() { - public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) { - Result result = new Result(); - if (e instanceof ServiceException) {//业务失败的异常,如“账号或密码错误” - result.setCode(ResultCode.FAIL).setMessage(e.getMessage()); - logger.info(e.getMessage()); - } else if (e instanceof NoHandlerFoundException) { - result.setCode(ResultCode.NOT_FOUND).setMessage("接口 [" + request.getRequestURI() + "] 不存在"); - } else if (e instanceof ServletException) { - result.setCode(ResultCode.FAIL).setMessage(e.getMessage()); - } else { - result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员"); - String message; - if (handler instanceof HandlerMethod) { - HandlerMethod handlerMethod = (HandlerMethod) handler; - message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s", - request.getRequestURI(), - handlerMethod.getBean().getClass().getName(), - handlerMethod.getMethod().getName(), - e.getMessage()); - } else { - message = e.getMessage(); - } - logger.error(message, e); - } - responseResult(response, result); - return new ModelAndView(); - } - - }); - } - - //解决跨域问题 - @Override - public void addCorsMappings(CorsRegistry registry) { - //registry.addMapping("/**"); - } - - //添加拦截器 + /** + * 添加拦截器 + * + * @param interceptorRegistry + */ @Override - public void addInterceptors(InterceptorRegistry registry) { + public void addInterceptors(InterceptorRegistry interceptorRegistry) { + //跨域拦截 + interceptorRegistry.addInterceptor(allowCrossDomainInterceptor).addPathPatterns(apiUri); + //响应结果控制拦截 + interceptorRegistry.addInterceptor(responseResultInterceptor).addPathPatterns(apiUri); //接口签名认证拦截器,该签名认证比较简单,实际项目中可以使用Json Web Token或其他更好的方式替代。 - if (!"dev".equals(env)) { //开发环境忽略签名认证 - registry.addInterceptor(new HandlerInterceptorAdapter() { + //开发环境忽略签名认证 + if (!"dev".equals(env)) { + interceptorRegistry.addInterceptor(new HandlerInterceptorAdapter() { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //验证签名 @@ -115,10 +95,9 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons return true; } else { logger.warn("签名认证失败,请求接口:{},请求IP:{},请求参数:{}", - request.getRequestURI(), getIpAddress(request), JSON.toJSONString(request.getParameterMap())); - - Result result = new Result(); - result.setCode(ResultCode.UNAUTHORIZED).setMessage("签名认证失败"); + request.getRequestURI(), IpUtil.getRealIp(request), JSON.toJSONString(request.getParameterMap())); + PlatformResult result = new PlatformResult(); + result.setCode(HttpStatus.UNAUTHORIZED.value()).setMessage("签名认证失败"); responseResult(response, result); return false; } @@ -127,7 +106,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons } } - private void responseResult(HttpServletResponse response, Result result) { + private void responseResult(HttpServletResponse response, PlatformResult result) { response.setCharacterEncoding("UTF-8"); response.setHeader("Content-type", "application/json;charset=UTF-8"); response.setStatus(200); @@ -145,49 +124,31 @@ private void responseResult(HttpServletResponse response, Result result) { * 3. 混合密钥(secret)进行md5获得签名,与请求的签名进行比较 */ private boolean validateSign(HttpServletRequest request) { - String requestSign = request.getParameter("sign");//获得请求签名,如sign=19e907700db7ad91318424a97c54ed57 + //获得请求签名,如sign=19e907700db7ad91318424a97c54ed57 + String requestSign = request.getParameter("sign"); if (StringUtils.isEmpty(requestSign)) { return false; } - List keys = new ArrayList(request.getParameterMap().keySet()); - keys.remove("sign");//排除sign参数 - Collections.sort(keys);//排序 + List keys = new ArrayList<>(request.getParameterMap().keySet()); + //排除sign参数 + keys.remove("sign"); + //排序 + Collections.sort(keys); StringBuilder sb = new StringBuilder(); + //拼接字符串 for (String key : keys) { - sb.append(key).append("=").append(request.getParameter(key)).append("&");//拼接字符串 + sb.append(key).append("=").append(request.getParameter(key)).append("&"); } String linkString = sb.toString(); - linkString = StringUtils.substring(linkString, 0, linkString.length() - 1);//去除最后一个'&' - - String secret = "Potato";//密钥,自己修改 - String sign = DigestUtils.md5Hex(linkString + secret);//混合密钥md5 - - return StringUtils.equals(sign, requestSign);//比较 + //去除最后一个'&' + linkString = StringUtils.substring(linkString, 0, linkString.length() - 1); + //密钥,自己修改 + String secret = "Potato"; + //混合密钥md5 + String sign = DigestUtils.md5Hex(linkString + secret); + //比较 + return StringUtils.equals(sign, requestSign); } - private String getIpAddress(HttpServletRequest request) { - String ip = request.getHeader("x-forwarded-for"); - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("Proxy-Client-IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("WL-Proxy-Client-IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("HTTP_CLIENT_IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("HTTP_X_FORWARDED_FOR"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getRemoteAddr(); - } - // 如果是多级代理,那么取第一个ip为客户端ip - if (ip != null && ip.indexOf(",") != -1) { - ip = ip.substring(0, ip.indexOf(",")).trim(); - } - - return ip; - } } diff --git a/src/main/java/com/company/project/core/AbstractService.java b/src/main/java/com/company/project/core/AbstractService.java index 35dbf8201..4d3144e36 100644 --- a/src/main/java/com/company/project/core/AbstractService.java +++ b/src/main/java/com/company/project/core/AbstractService.java @@ -1,7 +1,9 @@ package com.company.project.core; -import org.apache.ibatis.exceptions.TooManyResultsException; +import com.company.project.common.annotations.ServiceLog; +import com.company.project.common.exception.BusinessException; +import com.company.project.common.mapper.CrudMapper; import org.springframework.beans.factory.annotation.Autowired; import tk.mybatis.mapper.entity.Condition; @@ -11,65 +13,89 @@ /** * 基于通用MyBatis Mapper插件的Service接口的实现 + * @author lerry */ public abstract class AbstractService implements Service { @Autowired - protected Mapper mapper; + protected CrudMapper mapper; - private Class modelClass; // 当前泛型真实类型的Class + /** + * 当前泛型真实类型的Class + */ + private Class modelClass; public AbstractService() { ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); modelClass = (Class) pt.getActualTypeArguments()[0]; } + @Override + @ServiceLog(description = "持久化") public void save(T model) { mapper.insertSelective(model); } + @Override + @ServiceLog(description = "批量持久化") public void save(List models) { mapper.insertList(models); } + @Override + @ServiceLog(description = "通过主鍵刪除") public void deleteById(Integer id) { mapper.deleteByPrimaryKey(id); } + @Override + @ServiceLog(description = "通过主鍵批量刪除") public void deleteByIds(String ids) { mapper.deleteByIds(ids); } + @Override + @ServiceLog(description = "更新") public void update(T model) { mapper.updateByPrimaryKeySelective(model); } + @Override + @ServiceLog(description = "通过ID查找") public T findById(Integer id) { return mapper.selectByPrimaryKey(id); } @Override - public T findBy(String fieldName, Object value) throws TooManyResultsException { - try { - T model = modelClass.newInstance(); - Field field = modelClass.getDeclaredField(fieldName); - field.setAccessible(true); - field.set(model, value); - return mapper.selectOne(model); - } catch (ReflectiveOperationException e) { - throw new ServiceException(e.getMessage(), e); - } - } - + @ServiceLog(description = "通过多个ID查找") public List findByIds(String ids) { return mapper.selectByIds(ids); } + @Override + @ServiceLog(description = "根据条件查找") public List findByCondition(Condition condition) { return mapper.selectByCondition(condition); } + @Override + @ServiceLog(description = "获取所有") public List findAll() { return mapper.selectAll(); } + + @Override + @ServiceLog(description = "根据自定义条件查找") + public T findBy(String fieldName, Object value) throws BusinessException { + try { + T model = modelClass.newInstance(); + Field field = modelClass.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(model, value); + return mapper.selectOne(model); + } catch (ReflectiveOperationException e) { + throw new BusinessException(e.getMessage(), e); + } + } + } diff --git a/src/main/java/com/company/project/core/Mapper.java b/src/main/java/com/company/project/core/Mapper.java deleted file mode 100644 index 0adebc2a8..000000000 --- a/src/main/java/com/company/project/core/Mapper.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.company.project.core; - -import tk.mybatis.mapper.common.BaseMapper; -import tk.mybatis.mapper.common.ConditionMapper; -import tk.mybatis.mapper.common.IdsMapper; -import tk.mybatis.mapper.common.special.InsertListMapper; - -/** - * 定制版MyBatis Mapper插件接口,如需其他接口参考官方文档自行添加。 - */ -public interface Mapper - extends - BaseMapper, - ConditionMapper, - IdsMapper, - InsertListMapper { -} diff --git a/src/main/java/com/company/project/core/ProjectConstant.java b/src/main/java/com/company/project/core/ProjectConstant.java index 76d6abe4c..faead62b8 100644 --- a/src/main/java/com/company/project/core/ProjectConstant.java +++ b/src/main/java/com/company/project/core/ProjectConstant.java @@ -2,15 +2,46 @@ /** * 项目常量 + * @author lerry */ public final class ProjectConstant { - public static final String BASE_PACKAGE = "com.company.project";//生成代码所在的基础包名称,可根据自己公司的项目修改(注意:这个配置修改之后需要手工修改src目录项目默认的包路径,使其保持一致,不然会找不到类) - public static final String MODEL_PACKAGE = BASE_PACKAGE + ".model";//生成的Model所在包 - public static final String MAPPER_PACKAGE = BASE_PACKAGE + ".dao";//生成的Mapper所在包 - public static final String SERVICE_PACKAGE = BASE_PACKAGE + ".service";//生成的Service所在包 - public static final String SERVICE_IMPL_PACKAGE = SERVICE_PACKAGE + ".impl";//生成的ServiceImpl所在包 - public static final String CONTROLLER_PACKAGE = BASE_PACKAGE + ".web";//生成的Controller所在包 + private ProjectConstant(){ - public static final String MAPPER_INTERFACE_REFERENCE = BASE_PACKAGE + ".core.Mapper";//Mapper插件基础接口的完全限定名 + } + + /** + * 生成代码所在的基础包名称,可根据自己公司的项目修改 + * 注意:这个配置修改之后需要手工修改src目录项目默认的包路径,使其保持一致,不然会找不到类 + */ + public static final String BASE_PACKAGE = "com.company.project"; + + /** + * 生成的Model所在包 + */ + public static final String MODEL_PACKAGE = BASE_PACKAGE + ".model"; + + /** + * 生成的Mapper所在包 + */ + public static final String MAPPER_PACKAGE = BASE_PACKAGE + ".dao"; + + /** + * 生成的Service所在包 + */ + public static final String SERVICE_PACKAGE = BASE_PACKAGE + ".service"; + /** + * 生成的ServiceImpl所在包 + */ + public static final String SERVICE_IMPL_PACKAGE = SERVICE_PACKAGE + ".impl"; + + /** + * 生成的Controller所在包 + */ + public static final String CONTROLLER_PACKAGE = BASE_PACKAGE + ".web"; + + /** + * Mapper插件基础接口的完全限定名 + */ + public static final String MAPPER_INTERFACE_REFERENCE = BASE_PACKAGE + ".common.mapper.CrudMapper"; } diff --git a/src/main/java/com/company/project/core/Result.java b/src/main/java/com/company/project/core/Result.java deleted file mode 100644 index 85ba3a114..000000000 --- a/src/main/java/com/company/project/core/Result.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.company.project.core; - -import com.alibaba.fastjson.JSON; - -/** - * 统一API响应结果封装 - */ -public class Result { - private int code; - private String message; - private T data; - - public Result setCode(ResultCode resultCode) { - this.code = resultCode.code(); - return this; - } - - public int getCode() { - return code; - } - - public String getMessage() { - return message; - } - - public Result setMessage(String message) { - this.message = message; - return this; - } - - public T getData() { - return data; - } - - public Result setData(T data) { - this.data = data; - return this; - } - - @Override - public String toString() { - return JSON.toJSONString(this); - } -} diff --git a/src/main/java/com/company/project/core/ResultCode.java b/src/main/java/com/company/project/core/ResultCode.java deleted file mode 100644 index 7768df9ab..000000000 --- a/src/main/java/com/company/project/core/ResultCode.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.company.project.core; - -/** - * 响应码枚举,参考HTTP状态码的语义 - */ -public enum ResultCode { - SUCCESS(200),//成功 - FAIL(400),//失败 - UNAUTHORIZED(401),//未认证(签名错误) - NOT_FOUND(404),//接口不存在 - INTERNAL_SERVER_ERROR(500);//服务器内部错误 - - private final int code; - - ResultCode(int code) { - this.code = code; - } - - public int code() { - return code; - } -} diff --git a/src/main/java/com/company/project/core/ResultGenerator.java b/src/main/java/com/company/project/core/ResultGenerator.java deleted file mode 100644 index 863d73aa6..000000000 --- a/src/main/java/com/company/project/core/ResultGenerator.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.company.project.core; - -/** - * 响应结果生成工具 - */ -public class ResultGenerator { - private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS"; - - public static Result genSuccessResult() { - return new Result() - .setCode(ResultCode.SUCCESS) - .setMessage(DEFAULT_SUCCESS_MESSAGE); - } - - public static Result genSuccessResult(T data) { - return new Result() - .setCode(ResultCode.SUCCESS) - .setMessage(DEFAULT_SUCCESS_MESSAGE) - .setData(data); - } - - public static Result genFailResult(String message) { - return new Result() - .setCode(ResultCode.FAIL) - .setMessage(message); - } -} diff --git a/src/main/java/com/company/project/core/Service.java b/src/main/java/com/company/project/core/Service.java index 5fec7ca13..ffc1786a8 100644 --- a/src/main/java/com/company/project/core/Service.java +++ b/src/main/java/com/company/project/core/Service.java @@ -7,16 +7,72 @@ /** * Service 层 基础接口,其他Service 接口 请继承该接口 + * @author lerry */ public interface Service { - void save(T model);//持久化 - void save(List models);//批量持久化 - void deleteById(Integer id);//通过主鍵刪除 - void deleteByIds(String ids);//批量刪除 eg:ids -> “1,2,3,4” - void update(T model);//更新 - T findById(Integer id);//通过ID查找 - T findBy(String fieldName, Object value) throws TooManyResultsException; //通过Model中某个成员变量名称(非数据表中column的名称)查找,value需符合unique约束 - List findByIds(String ids);//通过多个ID查找//eg:ids -> “1,2,3,4” - List findByCondition(Condition condition);//根据条件查找 - List findAll();//获取所有 + /** + * 持久化 + * @param model + */ + void save(T model); + + /** + * 批量持久化 + * @param models + */ + void save(List models); + + /** + * 通过主鍵刪除 + * @param id + */ + void deleteById(Integer id); + + /** + * 批量刪除 eg:ids -> “1,2,3,4” + * @param ids + */ + void deleteByIds(String ids); + + /** + * 更新 + * @param model + */ + void update(T model); + + /** + * 通过ID查找 + * @param id + * @return + */ + T findById(Integer id); + + /** + * 通过Model中某个成员变量名称(非数据表中column的名称)查找,value需符合unique约束 + * @param fieldName + * @param value + * @return + * @throws TooManyResultsException + */ + T findBy(String fieldName, Object value) throws TooManyResultsException; + + /** + * 通过多个ID查找//eg:ids -> “1,2,3,4” + * @param ids + * @return + */ + List findByIds(String ids); + + /** + * 根据条件查找 + * @param condition + * @return + */ + List findByCondition(Condition condition); + + /** + * 获取所有 + * @return + */ + List findAll(); } diff --git a/src/main/java/com/company/project/core/ServiceException.java b/src/main/java/com/company/project/core/ServiceException.java deleted file mode 100644 index e698bce9d..000000000 --- a/src/main/java/com/company/project/core/ServiceException.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.company.project.core; - -/** - * 服务(业务)异常如“ 账号或密码错误 ”,该异常只做INFO级别的日志记录 @see WebMvcConfigurer - */ -public class ServiceException extends RuntimeException { - public ServiceException() { - } - - public ServiceException(String message) { - super(message); - } - - public ServiceException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index 71aa3d9b0..ef98842b5 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -1,6 +1,13 @@ -# 开发环境配置 -# 数据源配置,请修改为你项目的实际配置 +# \u5F00\u53D1\u73AF\u5883\u914D\u7F6E +# \u6570\u636E\u6E90\u914D\u7F6E\uFF0C\u8BF7\u4FEE\u6539\u4E3A\u4F60\u9879\u76EE\u7684\u5B9E\u9645\u914D\u7F6E spring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username=root spring.datasource.password=123456 -spring.datasource.driver-class-name=com.mysql.jdbc.Driver +#Druid\u76D1\u63A7\u914D\u7F6E +#\u67E5\u770B\u76D1\u63A7\u7684IP\u767D\u540D\u5355 (\u6CA1\u6709\u914D\u7F6E\u6216\u8005\u4E3A\u7A7A\uFF0C\u5219\u5141\u8BB8\u6240\u6709\u8BBF\u95EE) +spring.datasource.druid.stat-view-servlet.allow=127.0.0.1,192.168.3.0,192.168.100.0 +#IP\u9ED1\u540D\u5355 (\u5B58\u5728\u5171\u540C\u65F6\uFF0Cdeny\u4F18\u5148\u4E8Eallow) +spring.datasource.druid.stat-view-servlet.deny=192.168.3.110 +#\u767B\u5F55\u67E5\u770B\u4FE1\u606F\u7684\u8D26\u53F7\u5BC6\u7801 +spring.datasource.druid.stat-view-servlet.login-username=lerry +spring.datasource.druid.stat-view-servlet.login-password=lerryadmin diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e217b7192..9b7f727fa 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,79 @@ spring.profiles.active=dev -# 所有环境通用的配置,放在这里 +# \u6240\u6709\u73AF\u5883\u901A\u7528\u7684\u914D\u7F6E\uFF0C\u653E\u5728\u8FD9\u91CC -# 404 交给异常处理器处理 +#DataSource +spring.datasource.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.type=com.alibaba.druid.pool.DruidDataSource +# \u4E0B\u9762\u4E3ADruid\u8FDE\u63A5\u6C60\u7684\u8865\u5145\u8BBE\u7F6E +# \u521D\u59CB\u8FDE\u63A5\u6570 +spring.datasource.druid.initial-size=5 +# \u6700\u5C0F\u7A7A\u95F2\u8FDE\u63A5 +spring.datasource.druid.min-idle=5 +# \u6700\u5927\u8FDE\u63A5\u6570 +spring.datasource.druid.max-active=20 +# \u914D\u7F6E\u83B7\u53D6\u8FDE\u63A5\u7B49\u5F85\u8D85\u65F6\u7684\u65F6\u95F4 +spring.datasource.druid.max-wait=60000 +# \u914D\u7F6E\u95F4\u9694\u591A\u4E45\u624D\u8FDB\u884C\u4E00\u6B21\u68C0\u6D4B\uFF0C\u68C0\u6D4B\u9700\u8981\u5173\u95ED\u7684\u7A7A\u95F2\u8FDE\u63A5\uFF0C\u5355\u4F4D\u662F\u6BEB\u79D2 +spring.datasource.druid.time-between-eviction-runs-millis=60000 +# \u914D\u7F6E\u4E00\u4E2A\u8FDE\u63A5\u5728\u6C60\u4E2D\u6700\u5C0F\u751F\u5B58\u7684\u65F6\u95F4\uFF0C\u5355\u4F4D\u662F\u6BEB\u79D2 +spring.datasource.druid.min-evictable-idle-time-millis=300000 +spring.datasource.druid.validation-query=SELECT 1 +spring.datasource.druid.validation-query-timeout=1000 +spring.datasource.druid.test-while-idle=true +spring.datasource.druid.test-on-borrow=false +spring.datasource.druid.test-on-return=false +# \u6253\u5F00PSCache\uFF0C\u5E76\u4E14\u6307\u5B9A\u6BCF\u4E2A\u8FDE\u63A5\u4E0APSCache\u7684\u5927\u5C0F +# \u5982\u679C\u7528Oracle, \u5219\u628ApoolPreparedStatements\u914D\u7F6E\u4E3Atrue, mysql 5.5\u4E4B\u540E\u5EFA\u8BAEtrue +spring.datasource.druid.pool-prepared-statements=true +spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20 +spring.datasource.druid.max-open-prepared-statements=50 +# \u914D\u7F6E\u76D1\u63A7\u7EDF\u8BA1\u62E6\u622A\u7684filters\uFF0C\u53BB\u6389\u540E\u76D1\u63A7\u754C\u9762sql\u65E0\u6CD5\u7EDF\u8BA1\uFF0C'wall'\u7528\u4E8E\u9632\u706B\u5899 +spring.datasource.druid.filters=stat,wall +#\u914D\u7F6Eweb-stat-filter +spring.datasource.druid.web-stat-filter.enabled=true +#\u8FC7\u6EE4\u89C4\u5219 +spring.datasource.druid.web-stat-filter.url-pattern=/* +#\u5FFD\u7565\u8D44\u6E90 +spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/* +#\u914D\u7F6EprincipalCookieName\uFF0C\u4F7F\u5F97druid\u80FD\u591F\u77E5\u9053\u5F53\u524D\u7684cookie\u7684\u7528\u6237\u662F\u8C01 +spring.datasource.druid.web-stat-filter.principal-cookie-name=erry +#\u914D\u7F6EprincipalSessionName\uFF0C\u4F7F\u5F97druid\u80FD\u591F\u77E5\u9053\u5F53\u524D\u7684session\u7684\u7528\u6237\u662F\u8C01 +spring.datasource.druid.web-stat-filter.principal-session-name=erry +#\u914D\u7F6EprofileEnable\u80FD\u591F\u76D1\u63A7\u5355\u4E2Aurl\u8C03\u7528\u7684sql\u5217\u8868\u3002 +spring.datasource.druid.web-stat-filter.profile-enable=true +#session\u7EDF\u8BA1\u529F\u80FD +spring.datasource.druid.web-stat-filter.session-stat-enable=true +#\u6700\u5927session\u6570 +spring.datasource.druid.web-stat-filter.session-stat-max-count=100000 +#\u914D\u7F6EStatViewServlet +spring.datasource.druid.stat-view-servlet.enabled=true +spring.datasource.druid.stat-view-servlet.url-pattern=/druid/* +#\u662F\u5426\u80FD\u591F\u91CD\u7F6E\u6570\u636E(\u7981\u7528HTML\u9875\u9762\u4E0A\u7684\u201CReset All\u201D\u529F\u80FD) +spring.datasource.druid.stat-view-servlet.reset-enable=false +#\u914D\u7F6Ewall filter +spring.datasource.druid.filter.wall.enabled=true +spring.datasource.druid.filter.wall.db-type=sqlserver +spring.datasource.druid.filter.wall.config.alter-table-allow=false +spring.datasource.druid.filter.wall.config.truncate-allow=false +spring.datasource.druid.filter.wall.config.drop-table-allow=false +#\u662F\u5426\u5141\u8BB8\u975E\u4EE5\u4E0A\u57FA\u672C\u8BED\u53E5\u7684\u5176\u4ED6\u8BED\u53E5\uFF0C\u7F3A\u7701\u5173\u95ED\uFF0C\u901A\u8FC7\u8FD9\u4E2A\u9009\u9879\u5C31\u80FD\u591F\u5C4F\u853DDDL\u3002 +spring.datasource.druid.filter.wall.config.none-base-statement-allow=false +#\u68C0\u67E5UPDATE\u8BED\u53E5\u662F\u5426\u65E0where\u6761\u4EF6\uFF0C\u8FD9\u662F\u6709\u98CE\u9669\u7684\uFF0C\u4F46\u4E0D\u662FSQL\u6CE8\u5165\u7C7B\u578B\u7684\u98CE\u9669 +spring.datasource.druid.filter.wall.config.update-where-none-check=true +#SELECT ... INTO OUTFILE \u662F\u5426\u5141\u8BB8\uFF0C\u8FD9\u4E2A\u662Fmysql\u6CE8\u5165\u653B\u51FB\u7684\u5E38\u89C1\u624B\u6BB5\uFF0C\u7F3A\u7701\u662F\u7981\u6B62\u7684 +spring.datasource.druid.filter.wall.config.select-into-outfile-allow=false +#\u662F\u5426\u5141\u8BB8\u8C03\u7528Connection.getMetadata\u65B9\u6CD5\uFF0C\u8FD9\u4E2A\u65B9\u6CD5\u8C03\u7528\u4F1A\u66B4\u9732\u6570\u636E\u5E93\u7684\u8868\u4FE1\u606F +spring.datasource.druid.filter.wall.config.metadata-allow=true +#\u5BF9\u88AB\u8BA4\u4E3A\u662F\u653B\u51FB\u7684SQL\u8FDB\u884CLOG.error\u8F93\u51FA +spring.datasource.druid.filter.wall.log-violation=true +#\u5BF9\u88AB\u8BA4\u4E3A\u662F\u653B\u51FB\u7684SQL\u629B\u51FASQLExcepton +spring.datasource.druid.filter.wall.throw-exception=true +#\u914D\u7F6Espring\u5173\u8054 +#\u8BBE\u7F6E\u4F7F\u7528Cglib\u8FDB\u884C\u4EE3\u7406\uFF0C\u56E0\u4E3A\u90E8\u5206\u9700\u8981\u4EE3\u7406\u7684\u4E0D\u662F\u63A5\u53E3\u4E0D\u9002\u7528\u4E8EJDK\u52A8\u6001\u4EE3\u7406\uFF0C\u4F1A\u62A5\u9519 +spring.aop.proxy-target-class=true +#\u914D\u7F6EDruid\u76D1\u63A7Spring\u5305\u65B9\u6CD5\u7684\u8C03\u7528 +spring.datasource.druid.aop-patterns=packages + +# 404 \u4EA4\u7ED9\u5F02\u5E38\u5904\u7406\u5668\u5904\u7406 spring.mvc.throw-exception-if-no-handler-found=true spring.resources.add-mappings=false diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt index 152e17939..a35f837f2 100644 --- a/src/main/resources/banner.txt +++ b/src/main/resources/banner.txt @@ -18,5 +18,5 @@ // ========`-.____`-.___\_____/___.-`____.-'======== // // `=---=' // // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // -// 佛祖保佑 永不宕机 永无BUG // +// 佛祖保佑 永不宕机 永无BUG // //////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/src/main/resources/message/ValidationMessages.properties b/src/main/resources/message/ValidationMessages.properties new file mode 100644 index 000000000..5646d4cd4 --- /dev/null +++ b/src/main/resources/message/ValidationMessages.properties @@ -0,0 +1,36 @@ +javax.validation.constraints.AssertFalse.message = must be false +javax.validation.constraints.AssertTrue.message = must be true +javax.validation.constraints.DecimalMax.message = must be less than ${inclusive == true ? 'or equal to ' : ''}{value} +javax.validation.constraints.DecimalMin.message = must be greater than ${inclusive == true ? 'or equal to ' : ''}{value} +javax.validation.constraints.Digits.message = numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected) +javax.validation.constraints.Future.message = must be in the future +javax.validation.constraints.Max.message = must be less than or equal to {value} +javax.validation.constraints.Min.message = must be greater than or equal to {value} +javax.validation.constraints.NotNull.message = may not be null +javax.validation.constraints.Null.message = must be null +javax.validation.constraints.Past.message = must be in the past +javax.validation.constraints.Pattern.message = must match "{regexp}" +javax.validation.constraints.Size.message = size must be between {min} and {max} + +org.hibernate.validator.constraints.CreditCardNumber.message = invalid credit card number +org.hibernate.validator.constraints.EAN.message = invalid {type} barcode +org.hibernate.validator.constraints.Email.message = not a well-formed email address +org.hibernate.validator.constraints.Length.message = length must be between {min} and {max} +org.hibernate.validator.constraints.LuhnCheck.message = The check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed +org.hibernate.validator.constraints.Mod10Check.message = The check digit for ${validatedValue} is invalid, Modulo 10 checksum failed +org.hibernate.validator.constraints.Mod11Check.message = The check digit for ${validatedValue} is invalid, Modulo 11 checksum failed +org.hibernate.validator.constraints.ModCheck.message = The check digit for ${validatedValue} is invalid, ${modType} checksum failed +org.hibernate.validator.constraints.NotBlank.message = may not be empty +org.hibernate.validator.constraints.NotEmpty.message = may not be empty +org.hibernate.validator.constraints.ParametersScriptAssert.message = script expression "{script}" didn't evaluate to true +org.hibernate.validator.constraints.Range.message = must be between {min} and {max} +org.hibernate.validator.constraints.SafeHtml.message = may have unsafe html content +org.hibernate.validator.constraints.ScriptAssert.message = script expression "{script}" didn't evaluate to true +org.hibernate.validator.constraints.URL.message = must be a valid URL + +org.hibernate.validator.constraints.br.CNPJ.message = invalid Brazilian corporate taxpayer registry number (CNPJ) +org.hibernate.validator.constraints.br.CPF.message = invalid Brazilian individual taxpayer registry number (CPF) +org.hibernate.validator.constraints.br.TituloEleitoral.message = invalid Brazilian Voter ID card number + +# custom message +custom.value.invalid=the value is invalid \ No newline at end of file diff --git a/src/main/resources/message/ValidationMessages_zh_CN.properties b/src/main/resources/message/ValidationMessages_zh_CN.properties new file mode 100644 index 000000000..98a10c39b --- /dev/null +++ b/src/main/resources/message/ValidationMessages_zh_CN.properties @@ -0,0 +1,26 @@ +javax.validation.constraints.AssertFalse.message = \u53EA\u80FD\u4E3Afalse +javax.validation.constraints.AssertTrue.message = \u53EA\u80FD\u4E3Atrue +javax.validation.constraints.DecimalMax.message = \u5FC5\u987B\u5C0F\u4E8E\u6216\u7B49\u4E8E{value} +javax.validation.constraints.DecimalMin.message = \u5FC5\u987B\u5927\u4E8E\u6216\u7B49\u4E8E{value} +javax.validation.constraints.Digits.message = \u6570\u5B57\u7684\u503C\u8D85\u51FA\u4E86\u5141\u8BB8\u8303\u56F4(\u53EA\u5141\u8BB8\u5728{integer}\u4F4D\u6574\u6570\u548C{fraction}\u4F4D\u5C0F\u6570\u8303\u56F4\u5185) +javax.validation.constraints.Future.message = \u9700\u8981\u662F\u4E00\u4E2A\u5C06\u6765\u7684\u65F6\u95F4 +javax.validation.constraints.Max.message = \u6700\u5927\u4E0D\u80FD\u8D85\u8FC7{value} +javax.validation.constraints.Min.message = \u6700\u5C0F\u4E0D\u80FD\u5C0F\u4E8E{value} +javax.validation.constraints.NotNull.message = \u4E0D\u80FD\u4E3Anull +javax.validation.constraints.Null.message = \u5FC5\u987B\u4E3Anull +javax.validation.constraints.Past.message = \u9700\u8981\u662F\u4E00\u4E2A\u8FC7\u53BB\u7684\u65F6\u95F4 +javax.validation.constraints.Pattern.message = \u9700\u8981\u5339\u914D\u6B63\u5219\u8868\u8FBE\u5F0F"{regexp}" +javax.validation.constraints.Size.message = \u4E2A\u6570\u5FC5\u987B\u5728{min}\u548C{max}\u4E4B\u95F4 + +org.hibernate.validator.constraints.CreditCardNumber.message = \u4E0D\u5408\u6CD5\u7684\u4FE1\u7528\u5361\u53F7\u7801 +org.hibernate.validator.constraints.Email.message = \u4E0D\u662F\u4E00\u4E2A\u5408\u6CD5\u7684\u7535\u5B50\u90AE\u4EF6\u5730\u5740 +org.hibernate.validator.constraints.Length.message = \u957F\u5EA6\u9700\u8981\u5728{min}\u548C{max}\u4E4B\u95F4 +org.hibernate.validator.constraints.NotBlank.message = \u4E0D\u80FD\u4E3A\u7A7A +org.hibernate.validator.constraints.NotEmpty.message = \u4E0D\u80FD\u4E3A\u7A7A +org.hibernate.validator.constraints.Range.message = \u9700\u8981\u5728{min}\u548C{max}\u4E4B\u95F4 +org.hibernate.validator.constraints.SafeHtml.message = \u53EF\u80FD\u6709\u4E0D\u5B89\u5168\u7684HTML\u5185\u5BB9 +org.hibernate.validator.constraints.ScriptAssert.message = \u6267\u884C\u811A\u672C\u8868\u8FBE\u5F0F"{script}"\u6CA1\u6709\u80FD\u591F\u5F97\u5230true +org.hibernate.validator.constraints.URL.message = \u9700\u8981\u662F\u4E00\u4E2A\u5408\u6CD5\u7684URL + +# custom message +custom.value.invalid = \u05B5\u503C\u662F\u65E0\u6548\u7684 \ No newline at end of file diff --git a/src/test/java/CodeGenerator.java b/src/test/java/CodeGenerator.java index 05928e2bf..d76853fe7 100644 --- a/src/test/java/CodeGenerator.java +++ b/src/test/java/CodeGenerator.java @@ -8,6 +8,7 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; @@ -33,11 +34,27 @@ public class CodeGenerator { private static final String PACKAGE_PATH_SERVICE_IMPL = packageConvertPath(SERVICE_IMPL_PACKAGE);//生成的Service实现存放路径 private static final String PACKAGE_PATH_CONTROLLER = packageConvertPath(CONTROLLER_PACKAGE);//生成的Controller存放路径 - private static final String AUTHOR = "CodeGenerator";//@author - private static final String DATE = new SimpleDateFormat("yyyy/MM/dd").format(new Date());//@date + private static final String CREATE_BY = "CodeGenerator";//@createBy + private static final String AUTHOR = "LErry.li";//@author + private static final String DATE = DateFormat.getDateInstance().format(new Date());//@date + private static final String TIME = DateFormat.getTimeInstance().format(new Date());//@time + + /** + * 是否启用MVC代码生成 + */ + private static final boolean ENABLE_MVC_CODE_GENERATOR = true; + /** + * 是否继承Serializable接口 + */ + private static final boolean IMPLEMENTS_SERIALIZABLE = true; + + /** + * 默认生成的Model名称,需要去掉的表名前缀,不区分大小写 + */ + private static final String REDUCE_TABLE_PREFIX = "T_"; public static void main(String[] args) { - genCode("输入表名"); + genCode("t_user"); //genCodeByCustomModelName("输入表名","输入自定义Model名称"); } @@ -46,7 +63,7 @@ public static void main(String[] args) { * 如输入表名称 "t_user_detail" 将生成 TUserDetail、TUserDetailMapper、TUserDetailService ... * @param tableNames 数据表名称... */ - public static void genCode(String... tableNames) { + private static void genCode(String... tableNames) { for (String tableName : tableNames) { genCodeByCustomModelName(tableName, null); } @@ -58,14 +75,25 @@ public static void genCode(String... tableNames) { * @param tableName 数据表名称 * @param modelName 自定义的 Model 名称 */ - public static void genCodeByCustomModelName(String tableName, String modelName) { + private static void genCodeByCustomModelName(String tableName, String modelName) { + //如果自定义的model名称为空,且需要去除表名的前缀,则去前缀后的驼峰命名为自定义的model名称 + if(StringUtils.isEmpty(modelName) && StringUtils.isNotEmpty(REDUCE_TABLE_PREFIX)){ + modelName = getUpperCamel(tableName.replaceAll(String.format("^((?i)%s)", REDUCE_TABLE_PREFIX), "")); + } genModelAndMapper(tableName, modelName); - genService(tableName, modelName); - genController(tableName, modelName); + if(ENABLE_MVC_CODE_GENERATOR){ + genService(tableName, modelName); + genController(tableName, modelName); + } } - public static void genModelAndMapper(String tableName, String modelName) { + /** + * 生成实体类和Mapper + * @param tableName + * @param modelName + */ + private static void genModelAndMapper(String tableName, String modelName) { Context context = new Context(ModelType.FLAT); context.setId("Potato"); context.setTargetRuntime("MyBatis3Simple"); @@ -79,10 +107,8 @@ public static void genModelAndMapper(String tableName, String modelName) { jdbcConnectionConfiguration.setDriverClass(JDBC_DIVER_CLASS_NAME); context.setJdbcConnectionConfiguration(jdbcConnectionConfiguration); - PluginConfiguration pluginConfiguration = new PluginConfiguration(); - pluginConfiguration.setConfigurationType("tk.mybatis.mapper.generator.MapperPlugin"); - pluginConfiguration.addProperty("mappers", MAPPER_INTERFACE_REFERENCE); - context.addPluginConfiguration(pluginConfiguration); + //添加各种插件 + addPlugin(context); JavaModelGeneratorConfiguration javaModelGeneratorConfiguration = new JavaModelGeneratorConfiguration(); javaModelGeneratorConfiguration.setTargetProject(PROJECT_PATH + JAVA_PATH); @@ -102,7 +128,9 @@ public static void genModelAndMapper(String tableName, String modelName) { TableConfiguration tableConfiguration = new TableConfiguration(context); tableConfiguration.setTableName(tableName); - if (StringUtils.isNotEmpty(modelName))tableConfiguration.setDomainObjectName(modelName); + if (StringUtils.isNotEmpty(modelName)){ + tableConfiguration.setDomainObjectName(modelName); + } tableConfiguration.setGeneratedKey(new GeneratedKey("id", "Mysql", true, null)); context.addTableConfiguration(tableConfiguration); @@ -113,9 +141,8 @@ public static void genModelAndMapper(String tableName, String modelName) { config.addContext(context); config.validate(); - boolean overwrite = true; - DefaultShellCallback callback = new DefaultShellCallback(overwrite); - warnings = new ArrayList(); + DefaultShellCallback callback = new DefaultShellCallback(true); + warnings = new ArrayList<>(); generator = new MyBatisGenerator(config, callback, warnings); generator.generate(null); } catch (Exception e) { @@ -125,22 +152,28 @@ public static void genModelAndMapper(String tableName, String modelName) { if (generator.getGeneratedJavaFiles().isEmpty() || generator.getGeneratedXmlFiles().isEmpty()) { throw new RuntimeException("生成Model和Mapper失败:" + warnings); } - if (StringUtils.isEmpty(modelName)) modelName = tableNameConvertUpperCamel(tableName); + if (StringUtils.isEmpty(modelName)){ + modelName = tableNameConvertUpperCamel(tableName); + } System.out.println(modelName + ".java 生成成功"); System.out.println(modelName + "Mapper.java 生成成功"); System.out.println(modelName + "Mapper.xml 生成成功"); } - public static void genService(String tableName, String modelName) { + /** + * 生成service 代码 + * @param tableName + * @param modelName + */ + private static void genService(String tableName, String modelName) { try { freemarker.template.Configuration cfg = getConfiguration(); Map data = new HashMap<>(); - data.put("date", DATE); - data.put("author", AUTHOR); + setFileHeader(data); String modelNameUpperCamel = StringUtils.isEmpty(modelName) ? tableNameConvertUpperCamel(tableName) : modelName; data.put("modelNameUpperCamel", modelNameUpperCamel); - data.put("modelNameLowerCamel", tableNameConvertLowerCamel(tableName)); + data.put("modelNameLowerCamel", CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, modelNameUpperCamel)); data.put("basePackage", BASE_PACKAGE); File file = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_SERVICE + modelNameUpperCamel + "Service.java"); @@ -163,13 +196,17 @@ public static void genService(String tableName, String modelName) { } } - public static void genController(String tableName, String modelName) { + /** + * 生成Controller代码 + * @param tableName + * @param modelName + */ + private static void genController(String tableName, String modelName) { try { freemarker.template.Configuration cfg = getConfiguration(); Map data = new HashMap<>(); - data.put("date", DATE); - data.put("author", AUTHOR); + setFileHeader(data); String modelNameUpperCamel = StringUtils.isEmpty(modelName) ? tableNameConvertUpperCamel(tableName) : modelName; data.put("baseRequestMapping", modelNameConvertMappingPath(modelNameUpperCamel)); data.put("modelNameUpperCamel", modelNameUpperCamel); @@ -180,7 +217,6 @@ public static void genController(String tableName, String modelName) { if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } - //cfg.getTemplate("controller-restful.ftl").process(data, new FileWriter(file)); cfg.getTemplate("controller.ftl").process(data, new FileWriter(file)); System.out.println(modelNameUpperCamel + "Controller.java 生成成功"); @@ -190,6 +226,17 @@ public static void genController(String tableName, String modelName) { } + /** + * 设置自动生成代码的文件头 + * @param data + */ + private static void setFileHeader(Map data){ + data.put("date", DATE); + data.put("author", AUTHOR); + data.put("time", TIME); + data.put("createBy", CREATE_BY); + } + private static freemarker.template.Configuration getConfiguration() throws IOException { freemarker.template.Configuration cfg = new freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_23); cfg.setDirectoryForTemplateLoading(new File(TEMPLATE_FILE_PATH)); @@ -221,4 +268,36 @@ private static String packageConvertPath(String packageName) { return String.format("/%s/", packageName.contains(".") ? packageName.replaceAll("\\.", "/") : packageName); } + /** + * 获取字符串的大骆驼峰形式 + */ + private static String getUpperCamel(String str) { + //如果全大写,且包含下划线 + if (str.replaceAll("[A-Z]+", "").equals(str) && str.contains("_")) + return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, str); + //如果不包含下划线 + if (!str.contains("_") && str.length() > 2) + return str.toUpperCase().charAt(0) + str.substring(1); + //如果不全为大写,且包含下划线 + return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, str.toLowerCase()); + } + + /** + * 添加各种插件 + * @param context + */ + private static void addPlugin(Context context){ + PluginConfiguration pluginConfiguration = new PluginConfiguration(); + //通用Mapper插件 + pluginConfiguration.setConfigurationType("tk.mybatis.mapper.generator.MapperPlugin"); + pluginConfiguration.addProperty("mappers", MAPPER_INTERFACE_REFERENCE); + context.addPluginConfiguration(pluginConfiguration); + //实现序列化接口插件 + if(IMPLEMENTS_SERIALIZABLE){ + pluginConfiguration = new PluginConfiguration(); + pluginConfiguration.setConfigurationType("org.mybatis.generator.plugins.SerializablePlugin"); + context.addPluginConfiguration(pluginConfiguration); + } + } + } diff --git a/src/test/resources/demo-user.sql b/src/test/resources/demo-user.sql index 6c16772ff..4fc6a31b4 100644 --- a/src/test/resources/demo-user.sql +++ b/src/test/resources/demo-user.sql @@ -18,28 +18,27 @@ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for user -- ---------------------------- -DROP TABLE IF EXISTS `user`; -CREATE TABLE `user` ( +DROP TABLE IF EXISTS `t_user`; +CREATE TABLE `t_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, `nick_name` varchar(255) DEFAULT NULL, `sex` int(1) DEFAULT NULL, - `register_date` datetime NOT NULL, + `register_date` datetime, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of user -- ---------------------------- -INSERT INTO `user` VALUES ('1', '89921218@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆', '1', '2017-06-23 14:24:23'); -INSERT INTO `user` VALUES ('2', '2@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-2', '1', '2017-06-23 14:24:23'); -INSERT INTO `user` VALUES ('3', '3@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-3', '1', '2017-06-23 14:24:23'); -INSERT INTO `user` VALUES ('4', '4@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-4', '1', '2017-06-23 14:24:23'); -INSERT INTO `user` VALUES ('5', '5@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-5', '1', '2017-06-23 14:24:23'); -INSERT INTO `user` VALUES ('6', '6@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-6', '1', '2017-06-23 14:24:23'); -INSERT INTO `user` VALUES ('7', '7@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-7', '1', '2017-06-23 14:24:23'); -INSERT INTO `user` VALUES ('8', '8@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-8', '1', '2017-06-23 14:24:23'); -INSERT INTO `user` VALUES ('9', '9@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-9', '1', '2017-06-23 14:24:23'); -INSERT INTO `user` VALUES ('10', '10@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-10', '1', '2017-06-23 14:24:23'); +INSERT INTO `t_user` VALUES ('1', '1@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-1', '1', '2017-06-23 14:24:23'); +INSERT INTO `t_user` VALUES ('2', '2@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-2', '1', '2017-06-23 14:24:23'); +INSERT INTO `t_user` VALUES ('3', '3@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-3', '1', '2017-06-23 14:24:23'); +INSERT INTO `t_user` VALUES ('4', '4@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-4', '1', '2017-06-23 14:24:23'); +INSERT INTO `t_user` VALUES ('5', '5@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-5', '1', '2017-06-23 14:24:23'); +INSERT INTO `t_user` VALUES ('6', '6@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-6', '1', '2017-06-23 14:24:23'); +INSERT INTO `t_user` VALUES ('7', '7@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-7', '1', '2017-06-23 14:24:23'); +INSERT INTO `t_user` VALUES ('8', '8@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-8', '1', '2017-06-23 14:24:23'); +INSERT INTO `t_user` VALUES ('9', '9@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-9', '1', '2017-06-23 14:24:23'); SET FOREIGN_KEY_CHECKS=1; diff --git a/src/test/resources/generator/template/controller-restful.ftl b/src/test/resources/generator/template/controller-restful.ftl index 76a38566d..404a21e13 100644 --- a/src/test/resources/generator/template/controller-restful.ftl +++ b/src/test/resources/generator/template/controller-restful.ftl @@ -12,8 +12,12 @@ import javax.annotation.Resource; import java.util.List; /** -* Created by ${author} on ${date}. -*/ + * Created with ${createBy} + * Description: + * @author ${author} + * Date: ${date} + * Time: ${time} + */ @RestController @RequestMapping("${baseRequestMapping}") public class ${modelNameUpperCamel}Controller { diff --git a/src/test/resources/generator/template/controller.ftl b/src/test/resources/generator/template/controller.ftl index 720cbc039..77d41191e 100644 --- a/src/test/resources/generator/template/controller.ftl +++ b/src/test/resources/generator/template/controller.ftl @@ -1,8 +1,8 @@ package ${basePackage}.web; -import ${basePackage}.core.Result; -import ${basePackage}.core.ResultGenerator; + import ${basePackage}.model.${modelNameUpperCamel}; import ${basePackage}.service.${modelNameUpperCamel}Service; +import ${basePackage}.common.result.ResponseResult; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import org.springframework.web.bind.annotation.PostMapping; @@ -14,8 +14,13 @@ import javax.annotation.Resource; import java.util.List; /** -* Created by ${author} on ${date}. -*/ + * Created with ${createBy} + * Description: + * @author ${author} + * Date: ${date} + * Time: ${time} + */ +@ResponseResult @RestController @RequestMapping("${baseRequestMapping}") public class ${modelNameUpperCamel}Controller { @@ -23,34 +28,31 @@ public class ${modelNameUpperCamel}Controller { private ${modelNameUpperCamel}Service ${modelNameLowerCamel}Service; @PostMapping("/add") - public Result add(${modelNameUpperCamel} ${modelNameLowerCamel}) { + public ${modelNameUpperCamel} add(${modelNameUpperCamel} ${modelNameLowerCamel}) { ${modelNameLowerCamel}Service.save(${modelNameLowerCamel}); - return ResultGenerator.genSuccessResult(); + return ${modelNameLowerCamel}; } @PostMapping("/delete") - public Result delete(@RequestParam Integer id) { + public void delete(@RequestParam Integer id) { ${modelNameLowerCamel}Service.deleteById(id); - return ResultGenerator.genSuccessResult(); } @PostMapping("/update") - public Result update(${modelNameUpperCamel} ${modelNameLowerCamel}) { + public ${modelNameUpperCamel} update(${modelNameUpperCamel} ${modelNameLowerCamel}) { ${modelNameLowerCamel}Service.update(${modelNameLowerCamel}); - return ResultGenerator.genSuccessResult(); + return ${modelNameLowerCamel}; } @PostMapping("/detail") - public Result detail(@RequestParam Integer id) { - ${modelNameUpperCamel} ${modelNameLowerCamel} = ${modelNameLowerCamel}Service.findById(id); - return ResultGenerator.genSuccessResult(${modelNameLowerCamel}); + public ${modelNameUpperCamel} detail(@RequestParam Integer id) { + return ${modelNameLowerCamel}Service.findById(id); } @PostMapping("/list") - public Result list(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "0") Integer size) { + public PageInfo list(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "0") Integer size) { PageHelper.startPage(page, size); List<${modelNameUpperCamel}> list = ${modelNameLowerCamel}Service.findAll(); - PageInfo pageInfo = new PageInfo(list); - return ResultGenerator.genSuccessResult(pageInfo); + return new PageInfo(list); } } diff --git a/src/test/resources/generator/template/service-impl.ftl b/src/test/resources/generator/template/service-impl.ftl index cd4764033..b5b5f165d 100644 --- a/src/test/resources/generator/template/service-impl.ftl +++ b/src/test/resources/generator/template/service-impl.ftl @@ -11,11 +11,16 @@ import javax.annotation.Resource; /** - * Created by ${author} on ${date}. + * Created with ${createBy} + * Description: + * @author ${author} + * Date: ${date} + * Time: ${time} */ @Service @Transactional public class ${modelNameUpperCamel}ServiceImpl extends AbstractService<${modelNameUpperCamel}> implements ${modelNameUpperCamel}Service { + @Resource private ${modelNameUpperCamel}Mapper ${modelNameLowerCamel}Mapper; diff --git a/src/test/resources/generator/template/service.ftl b/src/test/resources/generator/template/service.ftl index 9479e9393..face0cc79 100644 --- a/src/test/resources/generator/template/service.ftl +++ b/src/test/resources/generator/template/service.ftl @@ -4,7 +4,11 @@ import ${basePackage}.core.Service; /** - * Created by ${author} on ${date}. + * Created with ${createBy} + * Description: + * @author ${author} + * Date: ${date} + * Time: ${time} */ public interface ${modelNameUpperCamel}Service extends Service<${modelNameUpperCamel}> {