├── .gitignore
├── LICENSE
├── README.md
├── init.sql
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── luban
│ │ └── demo
│ │ ├── LubanApplication.java
│ │ ├── common
│ │ ├── enums
│ │ │ └── UploadEnum.java
│ │ ├── exceptions
│ │ │ ├── AbstractExceptionTranslator.java
│ │ │ ├── ExceptionTranslator.java
│ │ │ ├── LubanException.java
│ │ │ └── error
│ │ │ │ ├── ErrorEnum.java
│ │ │ │ ├── ErrorVM.java
│ │ │ │ └── FieldVM.java
│ │ └── utils
│ │ │ └── EnvUtil.java
│ │ ├── config
│ │ └── Swagger2Config.java
│ │ ├── controller
│ │ ├── UploadController.java
│ │ └── WorkController.java
│ │ ├── domain
│ │ ├── Work.java
│ │ └── WorkForms.java
│ │ ├── dto
│ │ └── WorkDto.java
│ │ ├── repo
│ │ └── WorkRepo.java
│ │ ├── request
│ │ ├── WorkCreateRequest.java
│ │ ├── WorkQueryRequest.java
│ │ └── WorkUpdateRequest.java
│ │ └── service
│ │ ├── UploadService.java
│ │ ├── WorkService.java
│ │ └── impl
│ │ ├── UploadServiceImpl.java
│ │ └── WorkServiceImpl.java
└── resources
│ ├── application-dev.example.yml
│ ├── application-prod.example.yml
│ ├── application.yml
│ └── logback.xml
└── test
└── java
└── com
└── luban
└── demo
└── LubanApplicationTests.java
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**
5 | !**/src/test/**
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 |
30 | ### VS Code ###
31 | .vscode/
32 |
33 | # https://stackoverflow.com/questions/11145131/how-to-config-gitignore
34 | src/main/resources/application-dev.yml
35 | src/main/resources/application-prod.yml
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 luban-h5
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](http://makeapullrequest.com)
2 |
3 | # springboot2-jpa-api-for-luban
4 |
5 | * #!zh: 为[鲁班H5](https://github.com/ly525/luban-h5) 提供 由 [Spring Boot](https://spring.io/projects/spring-boot) 驱动的后端 API
6 | * #!zh: 现在仍然在完善中,非常欢迎 PR,如果您想参与贡献,可以直接 Pull Request。也可以和作者直接联系, [联系方式](https://github.com/ly525/luban-h5#%E4%BA%A4%E6%B5%81%E7%BE%A4)
7 |
8 | * #!en: Demo API for [Luban-H5-Editor-Module](https://github.com/ly525/luban-h5) powered by [Spring Boot + JPA](https://spring.io/projects/spring-boot)
9 | * #!en It is still working in progress, so pr is welcome!(now it's just a demo)
10 |
11 | #### 相关文档
12 | * [说明文档(WIP/完善中)](https://www.yuque.com/xpm1xa/rgf7kz/xkm4aq)
13 |
14 |
15 | ### TODO
16 | > pr is welcome!
17 |
18 | - [x] Get Work By Id
19 | - [x] Get All Works
20 | - [x] Update Work
21 | - [x] Create Work
22 | - [x] Delete Work
23 | - [ ] Upload Work Cover
24 | - [ ] Cors Proxy
25 | - [x] Set Work as Template
26 | - [ ] create Work based on Template
27 |
28 |
29 | ### Development
30 | ##### 后端
31 | 1. 使用 `init.sql` 初始化数据(不含建库语句),mysql 版本需要 >= 5.7.8
32 | 2. 修改 `src/main/resources/application-dev.example.yml` 中的 mysql 相关配置
33 | 3. 修改 `src/main/resources/application-prod.example.yml` 中的 mysql 相关配置
34 | 4. `git clone https://github.com/luban-h5/springboot2-jpa-api-for-luban` , 启动 Spring Boot 项目
35 |
36 |
37 | ##### 前端
38 | > 预备知识
39 | > * [Node、yarn 安装教程](https://github.com/ly525/luban-h5/blob/dev/docs/zh/getting-started/quick-start.md#nodeyarnnpm%E5%AE%89%E8%A3%85)
40 | > * 请使用 yarn 安装依赖,而非 npm,原因参见 [#92](https://github.com/ly525/luban-h5/issues/92) 和 [#101](https://github.com/ly525/luban-h5/issues/101)
41 | > * 安装前端项目依赖,[前端开发文档](https://github.com/ly525/luban-h5/blob/dev/docs/zh/getting-started/quick-start.md):
42 |
43 |
44 | ```bash
45 | # 重新打开一个 terminal
46 | git clone https://github.com/ly525/luban-h5
47 | cd luban-h5/front-end/h5
48 |
49 | # 安装前端依赖
50 | yarn install
51 | # #!en modify `target` in `vue.config.js`
52 | # #!zh 修改 vue.config.js 中的 target 变量,比如:const target = 'http://127.0.0.1:8888',
53 | # 其中:
54 | # 8888 为 springboot2-jpa-api-for-luban 提供服务的端口
55 | # 127.0.0.1 是本地开发的IP 或者 内网 IP 都是可以的
56 |
57 | # 修改完毕之后,运行下面的命令,即可启动前端服务进行联调
58 | # 注意是 serve 不是 server!
59 | yarn serve
60 |
61 | ```
62 |
63 | 联调开始
64 |
--------------------------------------------------------------------------------
/init.sql:
--------------------------------------------------------------------------------
1 |
2 | DROP TABLE IF EXISTS `work`;
3 | CREATE TABLE `work` (
4 | `id` bigint(20) NOT NULL AUTO_INCREMENT,
5 | `title` varchar(255) NOT NULL COMMENT '标题',
6 | `description` longtext COMMENT '描述',
7 | `cover_image_url` longtext,
8 | `pages` json DEFAULT NULL,
9 | `publish` tinyint(1) NOT NULL DEFAULT '0',
10 | `template` tinyint(1) NOT NULL DEFAULT '0',
11 | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
12 | `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
13 | PRIMARY KEY (`id`)
14 | ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
15 |
16 |
17 | DROP TABLE IF EXISTS `work_forms`;
18 | CREATE TABLE `work_forms` (
19 | `id` bigint(20) NOT NULL AUTO_INCREMENT,
20 | `form` longtext,
21 | `work_id` bigint(20) NOT NULL,
22 | PRIMARY KEY (`id`),
23 | KEY `work_id` (`work_id`),
24 | CONSTRAINT `work_forms_ibfk_1` FOREIGN KEY (`work_id`) REFERENCES `work` (`id`)
25 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
26 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.2.1.RELEASE
9 |
10 |
11 | com.luban
12 | demo
13 | 0.0.1-SNAPSHOT
14 | demo
15 | Demo project for Spring Boot
16 |
17 |
18 | 1.8
19 | 5.1.40
20 | 5.1.0.Final
21 | 0.2.7
22 | 1.16.10
23 | 2.5.1
24 | 2.9.2
25 | 1.2.49
26 | 2.0.1
27 | 2.5
28 | 3.4
29 | 3.6
30 |
31 |
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-starter-data-jpa
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-starter-mustache
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-starter-web
44 |
45 |
46 | mysql
47 | mysql-connector-java
48 | ${mysql-driver.version}
49 |
50 |
51 | com.alibaba
52 | fastjson
53 | ${fastjson.version}
54 |
55 |
56 | org.hibernate
57 | hibernate-spatial
58 | ${hibernate-spatial.version}
59 |
60 |
61 |
62 | com.zaxxer
63 | HikariCP
64 | ${HikariCP.version}
65 |
66 |
67 | org.lazyluke
68 | log4jdbc-remix
69 | ${log4jdbc.version}
70 |
71 |
72 |
73 | org.projectlombok
74 | lombok
75 | ${lombok.version}
76 | true
77 |
78 |
79 |
80 | org.junit.jupiter
81 | junit-jupiter-api
82 | RELEASE
83 | compile
84 |
85 |
86 |
87 | io.springfox
88 | springfox-swagger2
89 | ${springfox.version}
90 |
91 |
92 |
93 | io.springfox
94 | springfox-swagger-ui
95 | ${springfox.version}
96 |
97 |
98 | com.github.xiaoymin
99 | knife4j-spring-boot-starter
100 | ${knife4j.version}
101 |
102 |
103 | commons-io
104 | commons-io
105 | ${commons-io.version}
106 |
107 |
108 | org.apache.commons
109 | commons-lang3
110 | ${commons-lang3.version}
111 |
112 |
113 | commons-net
114 | commons-net
115 | ${commons-net.version}
116 |
117 |
118 |
119 | org.springframework.boot
120 | spring-boot-starter-test
121 | test
122 |
123 |
124 | org.junit.vintage
125 | junit-vintage-engine
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | org.springframework.boot
135 | spring-boot-maven-plugin
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/LubanApplication.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 | import org.springframework.core.env.Environment;
8 |
9 | import java.net.Inet4Address;
10 | import java.net.InetAddress;
11 | import java.net.NetworkInterface;
12 | import java.net.UnknownHostException;
13 | import java.util.Enumeration;
14 |
15 | /**
16 | * @author Yang Hao
17 | * @date 2019/11/17 21:07
18 | */
19 | @SpringBootApplication
20 | public class LubanApplication {
21 |
22 | private static Logger log = LoggerFactory.getLogger(LubanApplication.class);
23 |
24 | public static void main(String[] args) throws Exception {
25 |
26 | SpringApplication app = new SpringApplication(LubanApplication.class);
27 | Environment env = app.run(args).getEnvironment();
28 | String protocol = "http";
29 | if (env.getProperty("server.ssl.key-store") != null) {
30 | protocol = "https";
31 | }
32 |
33 | log.info("\n----------------------------------------------------------\n\t" +
34 | "Application '{}' is running! Access URLs:\n\t" +
35 | "Local: \t\t\t\t{}://localhost:{}\n\t" +
36 | "API 地址: \t\t\t{}://{}:{}/doc.html\n\t" +
37 | "Profile(s): \t{}\n----------------------------------------------------------",
38 | env.getProperty("spring.application.name"),
39 | protocol,
40 | env.getProperty("server.port"),
41 | protocol,
42 | getLocalHostLANAddress(),
43 | env.getProperty("server.port"),
44 | env.getActiveProfiles());
45 |
46 | String configServerStatus = env.getProperty("configserver.status");
47 | log.info("\n----------------------------------------------------------\n\t" +
48 | "Config Server: \t{}\n----------------------------------------------------------",
49 | configServerStatus == null ? "Not found or not setup for this application" : configServerStatus);
50 |
51 |
52 | }
53 |
54 | private static String getHostIp() {
55 | try {
56 | Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces();
57 | while (allNetInterfaces.hasMoreElements()) {
58 | NetworkInterface netInterface = allNetInterfaces.nextElement();
59 | Enumeration addresses = netInterface.getInetAddresses();
60 | while (addresses.hasMoreElements()) {
61 | InetAddress ip = addresses.nextElement();
62 | if (ip != null
63 | && ip instanceof Inet4Address
64 | && !ip.isLoopbackAddress() //loopback地址即本机地址,IPv4的loopback范围是127.0.0.0 ~ 127.255.255.255
65 | && ip.getHostAddress().indexOf(":") == -1) {
66 | System.out.println("本机的IP = " + ip.getHostAddress());
67 | return ip.getHostAddress();
68 | }
69 | }
70 | }
71 | } catch (Exception e) {
72 | e.printStackTrace();
73 | }
74 | return null;
75 | }
76 |
77 |
78 | // 正确的IP拿法,即优先拿site-local地址
79 | private static InetAddress getLocalHostLANAddress() throws UnknownHostException {
80 | try {
81 | InetAddress candidateAddress = null;
82 | // 遍历所有的网络接口
83 | for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); ) {
84 | NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
85 | // 在所有的接口下再遍历IP
86 | for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) {
87 | InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();
88 | if (!inetAddr.isLoopbackAddress()) {// 排除loopback类型地址
89 | if (inetAddr.isSiteLocalAddress()) {
90 | // 如果是site-local地址,就是它了
91 | return inetAddr;
92 | } else if (candidateAddress == null) {
93 | // site-local类型的地址未被发现,先记录候选地址
94 | candidateAddress = inetAddr;
95 | }
96 | }
97 | }
98 | }
99 | if (candidateAddress != null) {
100 | return candidateAddress;
101 | }
102 | // 如果没有发现 non-loopback地址.只能用最次选的方案
103 | InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
104 | if (jdkSuppliedAddress == null) {
105 | throw new UnknownHostException("The JDK InetAddress.getLocalHost() method unexpectedly returned null.");
106 | }
107 | return jdkSuppliedAddress;
108 | } catch (Exception e) {
109 | UnknownHostException unknownHostException = new UnknownHostException(
110 | "Failed to determine LAN address: " + e);
111 | unknownHostException.initCause(e);
112 | throw unknownHostException;
113 | }
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/common/enums/UploadEnum.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.common.enums;
2 |
3 | /**
4 | * @author Yang Hao
5 | * @date 2020/1/12 18:48
6 | */
7 | public enum UploadEnum {
8 | /**
9 | * 上传目录
10 | */
11 | work
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/common/exceptions/AbstractExceptionTranslator.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.common.exceptions;
2 |
3 |
4 | import com.luban.demo.common.exceptions.error.ErrorEnum;
5 | import com.luban.demo.common.exceptions.error.ErrorVM;
6 | import com.luban.demo.common.exceptions.error.FieldVM;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.core.annotation.AnnotationUtils;
10 | import org.springframework.http.HttpStatus;
11 | import org.springframework.http.ResponseEntity;
12 | import org.springframework.http.ResponseEntity.BodyBuilder;
13 | import org.springframework.validation.BindException;
14 | import org.springframework.validation.BindingResult;
15 | import org.springframework.validation.FieldError;
16 | import org.springframework.web.HttpMediaTypeNotSupportedException;
17 | import org.springframework.web.HttpRequestMethodNotSupportedException;
18 | import org.springframework.web.bind.MethodArgumentNotValidException;
19 | import org.springframework.web.bind.annotation.ExceptionHandler;
20 | import org.springframework.web.bind.annotation.ResponseBody;
21 | import org.springframework.web.bind.annotation.ResponseStatus;
22 |
23 | import javax.servlet.http.HttpServletRequest;
24 | import java.nio.file.AccessDeniedException;
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | /**
29 | * Controller advice to translate the server side exceptions to client-friendly json structures.
30 | */
31 | public abstract class AbstractExceptionTranslator {
32 | private final Logger log = LoggerFactory.getLogger(AbstractExceptionTranslator.class);
33 |
34 |
35 | /**
36 | * 处理参数校验异常,多个字段错误转换成错误数组
37 | *
38 | * @param request
39 | * @param ex
40 | * @return
41 | */
42 | @ExceptionHandler(MethodArgumentNotValidException.class)
43 | @ResponseStatus(HttpStatus.BAD_REQUEST)
44 | @ResponseBody
45 | protected ErrorVM processValidationError(HttpServletRequest request, MethodArgumentNotValidException ex) {
46 | ErrorVM error = getErrorVM(ex.getBindingResult());
47 | log.error("MethodArgumentNotValidException : [" + error.getErrorId() + "] : ", error.getError(), ex);
48 | return error;
49 | }
50 |
51 | @ExceptionHandler(AccessDeniedException.class)
52 | @ResponseStatus(HttpStatus.FORBIDDEN)
53 | @ResponseBody
54 | protected ErrorVM processAccessDeniedException(AccessDeniedException ex) {
55 | String errorCode = ErrorEnum.ACCESS_DENIED.getError();
56 | ErrorVM error = new ErrorVM(errorCode, ex.getMessage(), HttpStatus.FORBIDDEN.value());
57 | error.setCause(ex.getMessage());
58 | log.error("AccessDeniedException[" + error.getErrorId() + "]: " + ex.getMessage(), ex);
59 | return error;
60 | }
61 |
62 | @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
63 | @ResponseBody
64 | @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
65 | protected ErrorVM processMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
66 | String errorCode = ErrorEnum.ERR_METHOD_NOT_SUPPORTED.getError();
67 | ErrorVM error = new ErrorVM(errorCode, ex.getMessage(), HttpStatus.METHOD_NOT_ALLOWED.value());
68 | log.error("HttpRequestMethodNotSupportedException[" + error.getErrorId() + "]: " + ex.getMessage(), ex);
69 | return error;
70 | }
71 |
72 | @ExceptionHandler(IllegalArgumentException.class)
73 | @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
74 | protected ErrorVM handleIllegalArgumentException(IllegalArgumentException ex) {
75 | String errorCode = ErrorEnum.UN_PROCESSABLE_ENTITY.getError();
76 | ErrorVM error = new ErrorVM(errorCode, ex.getMessage(), HttpStatus.UNPROCESSABLE_ENTITY.value());
77 | log.error("IllegalArgumentException[" + error.getErrorId() + "]: " + ex.getMessage(), ex);
78 | return error;
79 | }
80 |
81 | @ExceptionHandler(LubanException.class)
82 | @ResponseStatus(HttpStatus.BAD_REQUEST)
83 | @ResponseBody
84 | protected ErrorVM processServiceError(LubanException ex) {
85 | ErrorVM error = new ErrorVM(ex.getError(), ex.getMessage(), HttpStatus.BAD_REQUEST.value());
86 | log.error("AconnException[" + error.getErrorId() + "]: " + ex.getMessage(), ex);
87 | return error;
88 | }
89 |
90 | @ExceptionHandler(BindException.class)
91 | @ResponseStatus(HttpStatus.BAD_REQUEST)
92 | @ResponseBody
93 | protected ErrorVM processBindException(BindException ex) {
94 | ErrorVM error = getErrorVM(ex.getBindingResult());
95 | log.error("BindException[" + error.getErrorId() + "]: " + ex.getMessage(), ex);
96 | return error;
97 | }
98 |
99 | @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
100 | @ResponseStatus(HttpStatus.BAD_REQUEST)
101 | protected ErrorVM handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException ex) {
102 | String errorCode = ErrorEnum.HTTP_MEDIA_TYPE_NOT_SUPPORTED.getError();
103 | ErrorVM error = new ErrorVM(errorCode,
104 | ErrorEnum.HTTP_MEDIA_TYPE_NOT_SUPPORTED.getDesc(),
105 | HttpStatus.BAD_REQUEST.value());
106 | log.error("HttpMediaTypeNotSupportedException[" + error.getErrorId() + "]: " + ex.getMessage(), ex);
107 | return error;
108 | }
109 |
110 |
111 | @ExceptionHandler(Exception.class)
112 | protected ResponseEntity processException(Exception ex) {
113 | BodyBuilder builder;
114 | ErrorVM error;
115 | ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
116 | if (responseStatus != null) {
117 | builder = ResponseEntity.status(responseStatus.value());
118 | String errorCode = "error." + responseStatus.value().value();
119 | error = new ErrorVM(errorCode, ex.getMessage(), responseStatus.reason(), responseStatus.value().value());
120 | } else {
121 | builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
122 | String errorCode = ErrorEnum.INTERNAL_SERVER_ERROR.getError();
123 | error = new ErrorVM(errorCode, ErrorEnum.INTERNAL_SERVER_ERROR.getDesc());
124 | error.setCause(ex.getMessage());
125 | }
126 | log.error("Exception[" + error.getErrorId() + "]: " + ex.getMessage(), ex);
127 | return builder.body(error);
128 | }
129 |
130 |
131 | protected ErrorVM getErrorVM(BindingResult bindingResult) {
132 | BindingResult result = bindingResult;
133 | List fieldErrors = result.getFieldErrors();
134 |
135 | List fieldVMS = new ArrayList(fieldErrors.size());
136 |
137 | for (FieldError fieldError : fieldErrors) {
138 | String objectName = fieldError.getObjectName();
139 | String field = fieldError.getField();
140 | String defaultMessage = fieldError.getDefaultMessage();
141 | String code = fieldError.getCode();
142 | fieldVMS.add(new FieldVM(objectName, field, code, defaultMessage, null));
143 | }
144 | String errorCode = ErrorEnum.METHOD_ARGUMENT_NOT_VALID.getError();
145 | return new ErrorVM(errorCode, errorCode, null, HttpStatus.BAD_REQUEST.value(), fieldVMS);
146 | }
147 |
148 |
149 | }
150 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/common/exceptions/ExceptionTranslator.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.common.exceptions;
2 |
3 |
4 | import com.luban.demo.common.exceptions.error.ErrorEnum;
5 | import com.luban.demo.common.exceptions.error.ErrorVM;
6 | import com.luban.demo.common.exceptions.error.FieldVM;
7 | import org.springframework.http.HttpStatus;
8 | import org.springframework.util.CollectionUtils;
9 | import org.springframework.validation.BindingResult;
10 | import org.springframework.validation.FieldError;
11 | import org.springframework.web.bind.annotation.ControllerAdvice;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | /**
17 | * Controller advice to translate the server side exceptions to client-friendly json structures.
18 | */
19 | @ControllerAdvice
20 | public class ExceptionTranslator extends AbstractExceptionTranslator {
21 |
22 |
23 | @Override
24 | public ErrorVM getErrorVM(BindingResult bindingResult) {
25 | BindingResult result = bindingResult;
26 | List fieldErrors = result.getFieldErrors();
27 |
28 | List fieldVMS = new ArrayList(fieldErrors.size());
29 |
30 | for (FieldError fieldError : fieldErrors) {
31 | String objectName = fieldError.getObjectName();
32 | String field = fieldError.getField();
33 | String defaultMessage = fieldError.getDefaultMessage();
34 | String code = fieldError.getCode();
35 | fieldVMS.add(new FieldVM(objectName, field, code, defaultMessage, null));
36 | }
37 | if (!CollectionUtils.isEmpty(fieldErrors)) {
38 | String errorCode = fieldErrors.get(0).getCode();
39 | String message = fieldErrors.get(0).getDefaultMessage();
40 | return new ErrorVM(errorCode, message, null, HttpStatus.BAD_REQUEST.value(), fieldVMS);
41 | }
42 | String errorCode = ErrorEnum.METHOD_ARGUMENT_NOT_VALID.getError();
43 | return new ErrorVM(errorCode, ErrorEnum.METHOD_ARGUMENT_NOT_VALID.getDesc(), null, HttpStatus.BAD_REQUEST.value(), fieldVMS);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/common/exceptions/LubanException.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.common.exceptions;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * Lu banException鲁班异常
7 | *
8 | * @author Yang Hao
9 | * @date 2020/1/12 17:40
10 | */
11 | @Data
12 | public class LubanException extends RuntimeException {
13 |
14 | private static final long serialVersionUID = -3576785908021342999L;
15 |
16 | private String error;
17 |
18 | public LubanException() {
19 | super();
20 | }
21 |
22 | public LubanException(String error) {
23 | super(error);
24 | this.error = error;
25 | }
26 |
27 | public LubanException(String error, String message) {
28 | super(message);
29 | this.error = error;
30 | }
31 |
32 | public LubanException(String message, Throwable cause) {
33 | super(message, cause);
34 | }
35 |
36 | public LubanException(Throwable cause) {
37 | super(cause);
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/common/exceptions/error/ErrorEnum.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.common.exceptions.error;
2 |
3 | public enum ErrorEnum {
4 |
5 | METHOD_ARGUMENT_NOT_VALID("error.common.argumentNotValid", "方法参数无效"),
6 | ACCESS_DENIED("error.common.accessDenied", "进入拒绝"),
7 | INTERNAL_SERVER_ERROR("error.common.internalServerError", "服务内部错误"),
8 | ERR_METHOD_NOT_SUPPORTED("error.common.methodNotSupported", "方法不支持"),
9 | UNAUTHORIZED("error.common.unauthorized", "未授权"),
10 | BAD_CREDENTIALS("error.common.badCredentials", "账号或密码错误"),
11 | HTTP_MEDIA_TYPE_NOT_SUPPORTED("error.common.mediaTypeNotSupported", "请求Content-Type不支持"),
12 | UN_PROCESSABLE_ENTITY("error.common.unProcessableEntity", "无法处理的实体类"),
13 | NAME_PASSWORD_INCORRECT("user.username.password.incorrect", "用户名或密码错误"),
14 | USER_NOT_EXIST("user.notExist", "用户不存在"),
15 | USER_LOCKED("user.lock", "用户已被锁定");
16 |
17 | private String error;
18 | private String desc;
19 |
20 | ErrorEnum(String error, String desc) {
21 | this.error = error;
22 | this.desc = desc;
23 | }
24 |
25 | public String getError() {
26 | return this.error;
27 | }
28 |
29 | public String getDesc() {
30 | return this.desc;
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/common/exceptions/error/ErrorVM.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.common.exceptions.error;
2 |
3 | import lombok.Data;
4 | import org.apache.commons.lang3.RandomStringUtils;
5 |
6 | import java.io.Serializable;
7 | import java.util.List;
8 |
9 | /**
10 | * 用于报错后错误返回
11 | */
12 | @Data
13 | public class ErrorVM implements Serializable {
14 | private String errorId;
15 | private String entityName; //错误实体
16 | private String error; //错误
17 | private String cause; //错误原因
18 | private Integer status; //错误状态码
19 | private String message; //错误信息
20 | private List fields; //错误字段
21 |
22 | public ErrorVM(String error, String description) {
23 | this(error, description, null);
24 | }
25 |
26 | public ErrorVM(String error, String description, Integer status) {
27 | this(error, description, null, status, null);
28 | }
29 |
30 | public ErrorVM(String error, String description, String cause, Integer status) {
31 | this(error, description, cause, status, null);
32 | }
33 |
34 | public ErrorVM(String error, String message, String cause, Integer status, List fields) {
35 | this.errorId = RandomStringUtils.randomNumeric(8);
36 | this.error = error;
37 | this.message = message;
38 | this.status = status;
39 | this.fields = fields;
40 | this.cause = cause;
41 | // this.entityName = this.getClass().getSimpleName();
42 | }
43 |
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/common/exceptions/error/FieldVM.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.common.exceptions.error;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 |
7 |
8 | /**
9 | * 用于校验错误字段描述
10 | *
11 | * @author Yang Hao
12 | * @date 2020/1/12 17:40
13 | */
14 | @Data
15 | public class FieldVM implements Serializable {
16 | private String objectName; //实体名称
17 | private String field; //字段
18 | private String message; //错误描述
19 | private String defaultMessage;
20 | private Object[] arguments; //参数
21 |
22 | public FieldVM(String objectName, String field, String message, String defaultMessage, Object[] arguments) {
23 | this.objectName = objectName;
24 | this.field = field;
25 | this.message = message;
26 | this.defaultMessage = defaultMessage;
27 | this.arguments = arguments;
28 | }
29 | }
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/common/utils/EnvUtil.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.common.utils;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 | import org.springframework.core.env.Environment;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | /**
10 | * 获取环境变量
11 | *
12 | * @author Yang Hao
13 | * @date 2020/1/12 18:40
14 | */
15 | public class EnvUtil {
16 |
17 | private static final Map cache = new HashMap(16);
18 |
19 | public static String getStrWithCache(Environment env, String key) {
20 | return getStrWithCache(env, key, null);
21 | }
22 |
23 | public static String getStrWithCache(Environment env, String key, String defaul) {
24 | String val = cache.get(key);
25 | if (StringUtils.isNotEmpty(val)) {
26 | return cache.get(key);
27 | }
28 |
29 | val = env.getProperty(key);
30 | if (StringUtils.isNotEmpty(val)) {
31 | cache.put(key, val);
32 | } else {
33 | val = defaul;
34 | }
35 | return val;
36 | }
37 |
38 | /**
39 | * 获取环境变量中int类型属性值
40 | *
41 | * @param env
42 | * @param key
43 | * @param defaul
44 | * @return
45 | */
46 | public static Integer getIntWithCache(Environment env, String key, Integer defaul) {
47 | Integer res = env.getProperty(key, Integer.class);
48 | return res == null ? defaul : res;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/config/Swagger2Config.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.config;
2 |
3 | import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import springfox.documentation.builders.ApiInfoBuilder;
7 | import springfox.documentation.builders.PathSelectors;
8 | import springfox.documentation.builders.RequestHandlerSelectors;
9 | import springfox.documentation.service.ApiInfo;
10 | import springfox.documentation.service.Contact;
11 | import springfox.documentation.spi.DocumentationType;
12 | import springfox.documentation.spring.web.plugins.Docket;
13 | import springfox.documentation.swagger2.annotations.EnableSwagger2;
14 |
15 | /**
16 | * @author Yang Hao
17 | * @date 2019/11/17 20:58
18 | */
19 | @Configuration
20 | @EnableSwagger2
21 | @EnableKnife4j
22 | public class Swagger2Config {
23 |
24 | @Bean
25 | public Docket petApi() {
26 |
27 | return new Docket(DocumentationType.SWAGGER_2)
28 | .apiInfo(apiInfo())
29 | .select()
30 | //当前包路径
31 | .apis(RequestHandlerSelectors.basePackage("com.luban.demo.controller"))
32 | .paths(PathSelectors.any())
33 | .build();
34 |
35 | }
36 |
37 | /**
38 | * 构建api文档的详细信息函数
39 | *
40 | * @return
41 | */
42 | private ApiInfo apiInfo() {
43 | return new ApiInfoBuilder()
44 | //页面标题
45 | .title("API 描述")
46 | //创建人
47 | .contact(new Contact("springboot2-jpa-api-for-luban", "https://github.com/luban-h5/springboot2-jpa-api-for-luban", ""))
48 | //版本号
49 | .version("1.0")
50 | //描述
51 | .description("API 描述")
52 | .termsOfServiceUrl("http://localhost/")
53 | .build();
54 | }
55 |
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/controller/UploadController.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.controller;
2 |
3 | import com.luban.demo.service.UploadService;
4 | import io.swagger.annotations.Api;
5 | import io.swagger.annotations.ApiOperation;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.springframework.web.bind.annotation.PostMapping;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RequestParam;
10 | import org.springframework.web.bind.annotation.RestController;
11 | import org.springframework.web.multipart.MultipartFile;
12 |
13 | import javax.annotation.Resource;
14 | import javax.validation.constraints.NotNull;
15 |
16 | /**
17 | * @author Yang Hao
18 | * @date 2020/1/12 17:40
19 | */
20 | @RestController
21 | @RequestMapping(value = "/upload")
22 | @Api(value = "文件上传", tags = {"文件上传"})
23 | @Slf4j
24 | public class UploadController {
25 |
26 | @Resource
27 | private UploadService uploadService;
28 |
29 | @ApiOperation(value = "文件上传")
30 | @PostMapping
31 | public void upload(@NotNull @RequestParam(value = "file", required = true) MultipartFile file) {
32 | uploadService.upload(file);
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/controller/WorkController.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.controller;
2 |
3 | import com.luban.demo.dto.WorkDto;
4 | import com.luban.demo.request.WorkCreateRequest;
5 | import com.luban.demo.request.WorkQueryRequest;
6 | import com.luban.demo.request.WorkUpdateRequest;
7 | import com.luban.demo.service.WorkService;
8 | import io.swagger.annotations.Api;
9 | import io.swagger.annotations.ApiOperation;
10 | import lombok.extern.slf4j.Slf4j;
11 | import org.springframework.beans.BeanUtils;
12 | import org.springframework.data.domain.Page;
13 | import org.springframework.data.domain.Pageable;
14 | import org.springframework.data.domain.Sort;
15 | import org.springframework.data.web.PageableDefault;
16 | import org.springframework.http.HttpStatus;
17 | import org.springframework.web.bind.annotation.*;
18 |
19 | import javax.annotation.Resource;
20 | import javax.validation.Valid;
21 | import java.util.List;
22 |
23 | /**
24 | * @author Yang Hao
25 | * @date 2019/11/17 20:42
26 | */
27 | @RestController
28 | @RequestMapping(value = "/works")
29 | @Api(value = "作品管理", tags = {"作品管理"})
30 | @Slf4j
31 | public class WorkController {
32 | @Resource
33 | private WorkService workService;
34 |
35 | @ApiOperation("根据workId查询")
36 | @RequestMapping(value = "/{id}", method = RequestMethod.GET)
37 | public WorkDto findWorkById(@PathVariable Long id) {
38 | return workService.findWorkById(id);
39 | }
40 |
41 | /**
42 | * 查询所有work
43 | *
44 | * @param request
45 | * @return
46 | */
47 | @ApiOperation("查询所有work")
48 | @RequestMapping(method = RequestMethod.GET)
49 | public List listAllWorks(@Valid @ModelAttribute WorkQueryRequest request) {
50 | WorkDto dto = new WorkDto();
51 | dto.setTemplate(Boolean.valueOf(request.getIs_template()));
52 | BeanUtils.copyProperties(request, dto);
53 | return workService.listAllWorks(dto);
54 | }
55 |
56 | /**
57 | * 分页查询works
58 | *
59 | * @param request
60 | * @return
61 | */
62 | @ApiOperation("分页查询works")
63 | @RequestMapping(value = "/pageable", method = RequestMethod.GET)
64 | public Page listWorks(@Valid @ModelAttribute WorkQueryRequest request,
65 | @PageableDefault(sort = {"createdTime"}, direction = Sort.Direction.DESC) Pageable pageable) {
66 | WorkDto dto = new WorkDto();
67 | dto.setTemplate(Boolean.valueOf(request.getIs_template()));
68 | BeanUtils.copyProperties(request, dto);
69 | return workService.listWorks(dto, pageable);
70 | }
71 |
72 | /**
73 | * ResponseStatus 和strapi.js(鲁班官方后端框架) response保持一致
74 | *
75 | * @param request
76 | * @return
77 | */
78 | @ApiOperation("创建work")
79 | @RequestMapping(method = RequestMethod.POST)
80 | @ResponseStatus(HttpStatus.OK)
81 | public WorkDto createWork(@RequestBody @Valid WorkCreateRequest request) {
82 | WorkDto workDto = new WorkDto();
83 | BeanUtils.copyProperties(request, workDto);
84 | return workService.createWork(workDto);
85 | }
86 |
87 | /**
88 | * 根据workId 修改work
89 | *
90 | * @param id
91 | * @param request
92 | * @return
93 | */
94 | @ApiOperation("更新work")
95 | @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
96 | @ResponseStatus(HttpStatus.OK)
97 | public WorkDto updateWork(@PathVariable Long id, @RequestBody @Valid WorkUpdateRequest request) {
98 | WorkDto workDto = new WorkDto();
99 | workDto.setId(id);
100 | BeanUtils.copyProperties(request, workDto);
101 | return workService.updateWork(workDto);
102 | }
103 |
104 | /**
105 | * 删除work
106 | *
107 | * @param id
108 | * @return
109 | */
110 | @ApiOperation("删除work")
111 | @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
112 | public void deleteWorkById(@PathVariable Long id) {
113 | workService.deleteWorkById(id);
114 | }
115 |
116 | /**
117 | * 设为模板
118 | *
119 | * @param id
120 | * @return
121 | */
122 | @ApiOperation("设为模板")
123 | @RequestMapping(value = "/set-as-template/{id}", method = RequestMethod.POST)
124 | @ResponseStatus(HttpStatus.OK)
125 | public WorkDto markWorkAsTemplate(@PathVariable Long id) {
126 | return workService.markWorkAsTemplate(id);
127 | }
128 |
129 | /**
130 | * 统计作品总数
131 | *
132 | * @return
133 | */
134 | @ApiOperation("统计作品总数")
135 | @RequestMapping(value = "/count", method = RequestMethod.GET)
136 | public Long countWork() {
137 | return workService.countWork();
138 | }
139 |
140 |
141 | /**
142 | * 设为模板
143 | *
144 | * @param id
145 | * @return
146 | */
147 | @ApiOperation("使用模板")
148 | @RequestMapping(value = "/use-template/{id}", method = RequestMethod.POST)
149 | @ResponseStatus(HttpStatus.OK)
150 | public WorkDto useTemplate(@PathVariable Long id) {
151 | return workService.useTemplate(id);
152 | }
153 |
154 |
155 | }
156 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/domain/Work.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.domain;
2 |
3 | import lombok.Data;
4 |
5 | import javax.persistence.*;
6 | import java.util.Date;
7 |
8 | /**
9 | * @author Yang Hao
10 | * @date 2019/11/17 19:51
11 | */
12 | @Data
13 | @Entity
14 | @Table(name = "work")
15 | public class Work implements Cloneable {
16 |
17 | @Id
18 | @GeneratedValue(strategy = GenerationType.IDENTITY)
19 | @Column(columnDefinition = "bigint(11) AUTO_INCREMENT NOT NULL COMMENT '主键'")
20 | private Long id;
21 |
22 | @Column(name = "title")
23 | private String title;
24 |
25 | @Column(name = "description")
26 | private String description;
27 |
28 | @Column(name = "cover_image_url")
29 | private String coverImageUrl;
30 |
31 | @Column(name = "pages")
32 | private String pages;
33 |
34 | @Temporal(TemporalType.TIMESTAMP)
35 | @Column(name = "create_time")
36 | private Date createTime;
37 |
38 | @Temporal(TemporalType.TIMESTAMP)
39 | @Column(name = "update_time")
40 | private Date updateTime;
41 |
42 | @Column(name = "publish")
43 | private boolean publish;
44 |
45 | @Column(name = "template")
46 | private boolean template;
47 |
48 | @Override
49 | public Work clone() throws CloneNotSupportedException {
50 | Work cloneWork = (Work) super.clone();
51 | cloneWork.setId(null);
52 | cloneWork.setTemplate(false);
53 | cloneWork.setPublish(false);
54 | return cloneWork;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/domain/WorkForms.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.domain;
2 |
3 | import lombok.Data;
4 |
5 | import javax.persistence.*;
6 |
7 | /**
8 | * @author Yang Hao
9 | * @date 2019/11/17 20:36
10 | */
11 | @Data
12 | @Entity
13 | @Table(name = "work")
14 | public class WorkForms {
15 |
16 | @Id
17 | @GeneratedValue(strategy = GenerationType.IDENTITY)
18 | @Column(columnDefinition = "bigint(20) AUTO_INCREMENT NOT NULL COMMENT '主键'")
19 | private Long id;
20 |
21 | @Column(name = "form")
22 | private String form;
23 |
24 | @Column(name = "work_id")
25 | private Long workId;
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/luban/demo/dto/WorkDto.java:
--------------------------------------------------------------------------------
1 | package com.luban.demo.dto;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.Date;
6 | import java.util.List;
7 |
8 | /**
9 | * @author Yang Hao
10 | * @date 2019/11/17 21:48
11 | */
12 | @Data
13 | public class WorkDto {
14 |
15 | private Long id;
16 |
17 | private String title;
18 |
19 | private String description;
20 |
21 | private String coverImageUrl;
22 |
23 | private List