├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── goos-1.3.0.jar
├── pom.xml
├── springMVC.xml
└── src
└── main
├── java
└── me
│ └── jxy
│ └── goos
│ └── GooGoo.java
└── resources
├── CommonResult.sample
├── Controller.sample
├── LoginController.sample
├── QueryVO.sample
├── UploadController.sample
└── VO.sample
/.gitignore:
--------------------------------------------------------------------------------
1 | # Eclipse
2 | .classpath
3 | .project
4 | .settings/
5 |
6 | # Intellij
7 | .idea/
8 | *.iml
9 | *.iws
10 |
11 | # Mac
12 | .DS_Store
13 | .localized
14 |
15 | # Maven
16 | target/
17 |
18 | # other
19 | log/
20 | .svn/
21 | *.imp
22 | *.crc
23 | *.ipr
24 | dependency-reduced-pom.xml
25 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 1.3.0
2 |
3 | 配合[react-antd-admin](https://github.com/jiangxy/react-antd-admin) 1.4.0版本。
4 |
5 | # 1.2.0
6 |
7 | 配合[react-antd-admin](https://github.com/jiangxy/react-antd-admin) 1.3.0版本。
8 |
9 | # 1.1.0
10 |
11 | 配合[react-antd-admin](https://github.com/jiangxy/react-antd-admin) 1.2.0版本。
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-java-goos
2 |
3 | 配合[React通用后台](https://github.com/jiangxy/react-antd-admin)使用的一个小工具。
4 |
5 | [React通用后台](https://github.com/jiangxy/react-antd-admin)要求后端接口必须符合特定的格式,比如查询接口必须是`/api/xxx/select`、接口的返回必须是`HTTP 200`等。如果每个项目接入的时候都从头写一遍,估计要崩溃了。。。所以我在想能不能减少一些重复的工作。
6 |
7 | 如果你的后端是java的(基于Spring),这个工具可以帮你快速生成一些模版类。把生成的类copy到自己的工程中并填写自己的逻辑,就可以直接使用通用后台提供的登录/CRUD/导入/导出等功能。
8 |
9 | ## Quick Start
10 |
11 | 首先要按照[React通用后台](https://github.com/jiangxy/react-antd-admin)的要求写好querySchema和dataSchema文件,然后直接执行jar文件即可:
12 |
13 | `java -jar goos-1.1.0.jar [输入目录] [输出目录]`
14 |
15 | 输入目录大概是这种结构:
16 | ```bash
17 | foolbear:schema $ ls -lh
18 | total 48
19 | -rw-r--r-- 1 foolbear staff 377B 9 5 00:41 test.config.js // test表的配置文件
20 | -rw-r--r-- 1 foolbear staff 1.5K 9 5 00:41 test.dataSchema.js // test表的dataSchema
21 | -rw-r--r-- 1 foolbear staff 3.4K 9 5 00:41 test.querySchema.js // test表的querySchema
22 | -rw-r--r-- 1 foolbear staff 133B 9 5 00:41 testSms.config.js // testSms表的配置文件
23 | -rw-r--r-- 1 foolbear staff 547B 9 5 00:41 testSms.dataSchema.js
24 | -rw-r--r-- 1 foolbear staff 762B 9 5 00:41 testSms.querySchema.js
25 | ```
26 |
27 | 输出目录的结构:
28 | ```bash
29 | foolbear:output $ ls -lh -R
30 | total 16
31 | -rw-r--r-- 1 foolbear staff 2.1K 9 5 15:29 CommonResult.java // 通用工具类
32 | -rw-r--r-- 1 foolbear staff 1.4K 9 5 15:29 LoginController.java // 登录相关接口
33 | drwxr-xr-x 5 foolbear staff 170B 9 5 15:29 test
34 | drwxr-xr-x 5 foolbear staff 170B 9 5 15:29 testSms
35 |
36 | ./test: // test表相关的类
37 | total 32
38 | -rw-r--r-- 1 foolbear staff 7.6K 9 5 15:29 TestController.java // test表CRUD相关接口
39 | -rw-r--r-- 1 foolbear staff 726B 9 5 15:29 TestQueryVO.java
40 | -rw-r--r-- 1 foolbear staff 269B 9 5 15:29 TestVO.java
41 |
42 | ./testSms: // testSms表相关的类
43 | total 32
44 | -rw-r--r-- 1 foolbear staff 7.6K 9 5 15:29 TestSmsController.java
45 | -rw-r--r-- 1 foolbear staff 453B 9 5 15:29 TestSmsQueryVO.java
46 | -rw-r--r-- 1 foolbear staff 317B 9 5 15:29 TestSmsVO.java
47 | ```
48 |
49 | 把生成的类copy到自己的项目中,将Controller类的逻辑填写完整,前端需要的接口就完成了。
50 |
51 | 更多文档请参考[React通用后台](https://github.com/jiangxy/react-antd-admin)项目。
52 |
53 | ## 关于跨域
54 |
55 | spring 4.2之后开始支持[CORS跨域](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS),但可能有些问题,见[代码中的注释](src/main/resources/Controller.sample#L42)。
56 |
57 | 对于SpringMVC的配置,给出一个例子:[springMVC.xml](springMVC.xml),注意其中的message converter和跨域相关配置。
58 |
59 | 跨域需要先一次OPTIONS请求,再实际的GET/POST之类请求。如果你的web.xml中配置了一些filter,可能导致OPTIONS请求失败,这就需要具体情况具体分析了。
60 |
61 | 我的习惯是这边`localhost:8080`跑着tomcat,那边`localhost:4040`跑着webpack-dev-server,这样调试起来很方便。
62 |
63 | ## TIPS
64 |
65 | schema中的`int/float`会被转换为java中的`Long/Double`,为了简单,不区分`Long/Integer`和`Double/Float`了,要注意一下。
66 |
67 | 一般我们要将VO转换为其他pojo去操作,转换的时候可能用到`BeanUtils.copyProperties`之类的方法,对于同名但不同类型的字段,`copyProperties`不会生效的,比如不会把`Long id`的字段值copy给`Integer id`,要自己注意下。
68 |
69 | ## 关于goos
70 |
71 | 我在给这个项目想名字的时候,突然想起了早年玩过的一个游戏[World of Goo](https://zh.wikipedia.org/wiki/%E7%B2%98%E7%B2%98%E4%B8%96%E7%95%8C)。其中goo是一种黏黏糊糊的球状生物,可以搭建各种各样的结构,连接不同的建筑,跨越各种地形。倒是蛮切题的,这个项目也是希望**连接**React前端和java后端嘛。
72 |
73 | 能力所限,我只能给出java版本的,而且也未必是最优的。希望以后能有更多版本的goos吧。
74 |
--------------------------------------------------------------------------------
/goos-1.3.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangxy/react-java-goos/bcee92e345236424006c14a1ab7852b2900d5049/goos-1.3.0.jar
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | me.jxy.goos
5 | goos
6 | 1.3.0
7 | jar
8 |
9 |
10 | UTF-8
11 | UTF-8
12 | 1.7
13 | 1.7
14 |
15 |
16 |
17 |
18 | junit
19 | junit
20 | 4.8.2
21 | test
22 |
23 |
24 | org.mockito
25 | mockito-all
26 | 1.9.5
27 | test
28 |
29 |
30 | com.google.guava
31 | guava
32 | 15.0
33 |
34 |
35 | com.alibaba
36 | fastjson
37 | 1.2.6
38 |
39 |
40 |
41 | org.projectlombok
42 | lombok
43 | 1.14.4
44 | provided
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | org.apache.maven.plugins
53 | maven-source-plugin
54 | 2.1.2
55 |
56 |
57 | attach-sources
58 | compile
59 |
60 | jar-no-fork
61 |
62 |
63 |
64 |
65 |
66 |
67 | org.apache.maven.plugins
68 | maven-compiler-plugin
69 | 2.3.2
70 |
71 | UTF-8
72 | true
73 | true
74 |
75 |
76 |
77 |
78 | org.apache.maven.plugins
79 | maven-resources-plugin
80 | 2.5
81 |
82 | UTF-8
83 |
84 |
85 |
86 |
87 |
88 | org.apache.maven.plugins
89 | maven-shade-plugin
90 | 2.4.3
91 |
92 |
93 | package
94 |
95 | shade
96 |
97 |
98 |
99 |
100 | me.jxy.goos.GooGoo
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/springMVC.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | WriteMapNullValue
20 | DisableCircularReferenceDetect
21 | WriteDateUseDateFormat
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/main/java/me/jxy/goos/GooGoo.java:
--------------------------------------------------------------------------------
1 | package me.jxy.goos;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.BufferedWriter;
5 | import java.io.File;
6 | import java.io.FileReader;
7 | import java.io.IOException;
8 | import java.net.MalformedURLException;
9 | import java.nio.file.Files;
10 | import java.util.List;
11 | import java.util.Map;
12 | import java.util.Set;
13 | import java.util.Stack;
14 | import java.util.regex.Matcher;
15 | import java.util.regex.Pattern;
16 |
17 | import com.alibaba.fastjson.JSONArray;
18 | import com.alibaba.fastjson.JSONObject;
19 | import com.google.common.base.Charsets;
20 | import com.google.common.base.Objects;
21 | import com.google.common.collect.Lists;
22 | import com.google.common.collect.Maps;
23 | import com.google.common.collect.Sets;
24 | import com.google.common.io.LineProcessor;
25 | import com.google.common.io.Resources;
26 |
27 | /**
28 | * 生成通用后台需要的模版类
29 | *
30 | * @version 1.0
31 | * @author foolbeargm@gmail.com
32 | */
33 | public class GooGoo {
34 |
35 | // schema文件中一个特殊的key,用于自定义操作
36 | public static final String ACTION_KEY = "singleRecordActions";
37 |
38 | private static Pattern querySchemaPattern = Pattern.compile("^(.*)\\.querySchema\\.js$");
39 | private static Pattern dataSchemaPattern = Pattern.compile("^(.*)\\.dataSchema\\.js$");
40 |
41 | /**
42 | * 打印帮助信息
43 | */
44 | private static void usage() {
45 | System.err.println("Param incorrect.");
46 | System.err.println("Usage: java -jar goos.jar [inputDir] [outputDir]");
47 | }
48 |
49 | public static void main(String[] args) {
50 | // 至少要有一个参数
51 | if (args.length > 2 || args.length < 1) {
52 | usage();
53 | return;
54 | }
55 |
56 | String inputDir = "../src/schema";
57 | String outputDir = "output";
58 |
59 | if (args.length == 2) {
60 | inputDir = args[0];
61 | outputDir = args[1];
62 | }
63 |
64 | if (args.length == 1) {
65 | inputDir = args[0];
66 | }
67 |
68 | // 为了减少jar的大小,不使用logger,直接sysout了
69 | System.out.println("INFO: input directory = " + inputDir);
70 | System.out.println("INFO: output directory = " + outputDir);
71 |
72 | File f1 = new File(inputDir), f2 = new File(outputDir);
73 |
74 | if (!f1.exists()) {
75 | System.err.println("ERROR: " + inputDir + " not exist");
76 | return;
77 | }
78 |
79 | if (!f2.exists()) {
80 | f2.mkdirs();
81 | System.out.println("INFO: mkdir " + outputDir);
82 | }
83 |
84 | // 扫描inputDir下的所有文件
85 | Set allTable = Sets.newHashSet();
86 | Matcher m1, m2;
87 | for (File f : f1.listFiles()) {
88 | String fileName = f.getName();
89 | String tableName = null;
90 | m1 = querySchemaPattern.matcher(fileName);
91 | if (m1.matches()) {
92 | tableName = m1.group(1);
93 | generateQueryVO(f, outputDir, tableName);
94 | }
95 |
96 | m2 = dataSchemaPattern.matcher(fileName);
97 | if (m2.matches()) {
98 | tableName = m2.group(1);
99 | generateVO(f, outputDir, tableName);
100 | }
101 |
102 | if (tableName != null && !allTable.contains(tableName)) {
103 | generateController(outputDir, tableName);
104 | allTable.add(tableName);
105 | }
106 | }
107 |
108 | // 每个表特定的类生成完毕,开始生成一些通用的类
109 | try {
110 | // 直接copy过去就好,不用render了
111 | copyFileFromClasspath("LoginController.sample", outputDir, "LoginController.java");
112 | copyFileFromClasspath("CommonResult.sample", outputDir, "CommonResult.java");
113 | copyFileFromClasspath("UploadController.sample", outputDir, "UploadController.java");
114 | } catch (Exception e) {
115 | e.printStackTrace();
116 | }
117 |
118 | return;
119 | }
120 |
121 | /**
122 | * 根据QuerySchema生成QueryVO
123 | */
124 | private static void generateQueryVO(File schemaFile, String outputDir, String tableName) {
125 | System.out.println("INFO: generating QueryVO for " + tableName);
126 |
127 | try {
128 | // 这个render的过程借鉴了一些velocity的理念
129 | Map params = getParamMap(tableName);
130 | JSONArray schema = parseJson(schemaFile);
131 |
132 | // 开始处理schema,根据dataType和showType生成各个字段
133 | StringBuilder fields = new StringBuilder();
134 | for (int i = 0; i < schema.size(); i++) {
135 | // 一个字段处理失败不应该影响全局
136 | try {
137 | JSONObject field = schema.getJSONObject(i);
138 | String dataType = Objects.firstNonNull(field.getString("dataType"), "varchar");
139 | String showType = Objects.firstNonNull(field.getString("showType"), "normal");
140 |
141 | if ("between".equals(showType)) {
142 | fields.append(getBetweenField(dataType, field.getString("key")));
143 | } else if ("checkbox".equals(showType) || "multiSelect".equals(showType) || "cascader".equals(showType)) {
144 | fields.append(getListField(dataType, field.getString("key")));
145 | } else {
146 | fields.append(getSingleField(dataType, field.getString("key")));
147 | }
148 | } catch (Exception e) {
149 | System.err.println("ERROR: parsing field " + schema.getJSONObject(i) + ": " + e.getMessage());
150 | }
151 | }
152 |
153 | // 读取模版文件并做变量替换
154 | params.put("fields", fields.toString());
155 | List lines = renderTemplate("QueryVO.sample", params);
156 |
157 | // 将parse后的内容写入文件
158 | writeLinesToFile(lines, outputDir, tableName, "QueryVO.java");
159 | } catch (Exception e) {
160 | System.err.println("ERROR: generating QueryVO ERROR: " + schemaFile.getAbsolutePath());
161 | e.printStackTrace();
162 | }
163 | }
164 |
165 | /**
166 | * 根据dataSchema生成VO
167 | */
168 | private static void generateVO(File schemaFile, String outputDir, String tableName) {
169 | System.out.println("INFO: generating VO for " + tableName);
170 |
171 | try {
172 | Map params = getParamMap(tableName);
173 | JSONArray schema = parseJson(schemaFile);
174 |
175 | // 开始处理schema
176 | StringBuilder fields = new StringBuilder();
177 | for (int i = 0; i < schema.size(); i++) {
178 | try {
179 | JSONObject field = schema.getJSONObject(i);
180 | if (ACTION_KEY.equals(field.getString("key"))) {
181 | System.out.println("INFO: skip field " + ACTION_KEY);
182 | continue;
183 | }
184 |
185 | String dataType = Objects.firstNonNull(field.getString("dataType"), "varchar");
186 | String showType = Objects.firstNonNull(field.getString("showType"), "normal");
187 |
188 | // 对于file和image要特殊处理下
189 | if ("file".equals(showType) || "image".equals(showType)) {
190 | if (!field.containsKey("max") || field.getInteger("max") == 1) {
191 | fields.append(getSingleField(dataType, field.getString("key")));
192 | } else {
193 | fields.append(getListField(dataType, field.getString("key")));
194 | }
195 | continue;
196 | }
197 |
198 | if ("checkbox".equals(showType) || "multiSelect".equals(showType) || "cascader".equals(showType)) {
199 | fields.append(getListField(dataType, field.getString("key")));
200 | } else {
201 | fields.append(getSingleField(dataType, field.getString("key")));
202 | }
203 | } catch (Exception e) {
204 | System.err.println("ERROR: parsing field " + schema.getJSONObject(i) + ": " + e.getMessage());
205 | }
206 | }
207 |
208 | // 读取模版文件并做变量替换
209 | params.put("fields", fields.toString());
210 | List lines = renderTemplate("VO.sample", params);
211 |
212 | // 将parse后的内容写入文件
213 | writeLinesToFile(lines, outputDir, tableName, "VO.java");
214 | } catch (Exception e) {
215 | System.err.println("ERROR: generating VO ERROR: " + schemaFile.getAbsolutePath());
216 | e.printStackTrace();
217 | }
218 | }
219 |
220 | /**
221 | * 生成controller
222 | */
223 | private static void generateController(String outputDir, String tableName) {
224 | System.out.println("INFO: generating Controller for " + tableName);
225 |
226 | try {
227 | Map params = getParamMap(tableName);
228 | List lines = renderTemplate("Controller.sample", params);
229 | writeLinesToFile(lines, outputDir, tableName, "Controller.java");
230 | } catch (Exception e) {
231 | System.err.println("ERROR: generating Controller ERROR: " + tableName);
232 | e.printStackTrace();
233 | }
234 | }
235 |
236 | /**获得生成某个表的java文件时,render所需的参数*/
237 | private static Map getParamMap(String tableName) {
238 | String lowCamelName = tableName;
239 | String upCamelName = tableName.substring(0, 1).toUpperCase() + tableName.substring(1);
240 | Map params = Maps.newHashMap();
241 | params.put("lowCamelName", lowCamelName);
242 | params.put("upCamelName", upCamelName);
243 | return params;
244 | }
245 |
246 | /**生成普通的字段*/
247 | private static String getSingleField(String dataType, String key) {
248 | if ("int".equals(dataType)) {
249 | return "private Long " + key + ";\n";
250 | } else if ("float".equals(dataType)) {
251 | return "private Double " + key + ";\n";
252 | } else if ("varchar".equals(dataType)) {
253 | return "private String " + key + ";\n";
254 | } else if ("datetime".equals(dataType)) {
255 | return "private Date " + key + ";\n";
256 | } else {
257 | throw new RuntimeException("unknown dataType " + dataType);
258 | }
259 | }
260 |
261 | /**生成list字段*/
262 | private static String getListField(String dataType, String key) {
263 | if ("int".equals(dataType)) {
264 | return "private List " + key + ";\n";
265 | } else if ("varchar".equals(dataType)) {
266 | return "private List " + key + ";\n";
267 | } else if ("float".equals(dataType)) {
268 | return "private List " + key + ";\n";
269 | } else if ("datetime".equals(dataType)) {
270 | return "private List " + key + ";\n";
271 | } else {
272 | throw new RuntimeException("unknown dataType " + dataType);
273 | }
274 | }
275 |
276 | /**生成两个字段,用于范围查询,只有int/float/datetime可能出现范围查询*/
277 | private static String getBetweenField(String dataType, String key) {
278 | StringBuilder sb = new StringBuilder();
279 | if ("int".equals(dataType)) {
280 | sb.append("private Long " + key + "Begin;\n");
281 | sb.append("private Long " + key + "End;\n");
282 | } else if ("float".equals(dataType)) {
283 | sb.append("private Double " + key + "Begin;\n");
284 | sb.append("private Double " + key + "End;\n");
285 | } else if ("datetime".equals(dataType)) {
286 | sb.append("private Date " + key + "Begin;\n");
287 | sb.append("private Date " + key + "End;\n");
288 | } else {
289 | throw new RuntimeException("unknown dataType " + dataType);
290 | }
291 |
292 | return sb.toString();
293 | }
294 |
295 | /**读取schema文件并转换为json对象*/
296 | private static JSONArray parseJson(File schemaFile) throws IOException {
297 |
298 | // 这个方法其实有很多问题,可能有各种corner case,要将js的对象转换为java中的json还是有点麻烦的
299 | // 难道要写一个语法解析器么。。。太麻烦了
300 | // 先将就着用吧,这个方法不会校验schema的正确性,对json格式也有要求(以webstorm的默认的格式化为准),要注意下
301 | // 如果json格式不符合要求,即使是正确的json,解析也会出错
302 |
303 | Stack stack = new Stack();
304 | BufferedReader br = new BufferedReader(new FileReader(schemaFile));
305 | StringBuilder sb = new StringBuilder();
306 | String line = null;
307 | while ((line = br.readLine()) != null) {
308 | line = line.trim();
309 | if (line.length() == 0)
310 | continue;
311 | if (line.startsWith("//")) // 忽略注释
312 | continue;
313 | if (line.startsWith("import")) // 开始时的import语句
314 | continue;
315 | if (line.startsWith("module.exports")) // module语句
316 | continue;
317 |
318 | // 找到第一个json开始的地方
319 | if (stack.size() == 0 && !line.equals("{")) {
320 | continue;
321 | }
322 |
323 | stack.push(line);
324 |
325 | if (line.equals("},")) { // 碰到了json的结尾
326 | List list = Lists.newArrayList(); // 暂存json中间的行
327 | while (stack.size() > 0) {
328 | String lastLine = stack.pop(); // pop直到碰到{
329 | if (lastLine.equals("{")) {
330 | break;
331 | }
332 | list.add(lastLine);
333 | }
334 | // 如果stack.size=0了,说明已经是最外层的json了
335 | if (stack.size() == 0) {
336 | sb.append("}{");
337 | appendLine(sb, list);
338 | }
339 | }
340 | }
341 | br.close();
342 |
343 | // 组合最终的json字符串
344 | String tmp = "[" + sb.substring(1) + "}]";
345 |
346 | // {a:1, b:2, c:3,}这种结构,在js里能正常识别为一个对象,但fastjson会出错
347 | char[] charArray = tmp.toCharArray();
348 | for (int i = 0; i < charArray.length - 1; i++) {
349 | char thisChar = charArray[i];
350 | char nextChar = charArray[i + 1];
351 | if (thisChar == ',' && (nextChar == '}' || nextChar == ']')) {
352 | charArray[i] = ' ';
353 | }
354 | }
355 | return JSONArray.parseArray(new String(charArray));
356 | }
357 |
358 | // 转换json时的辅助方法
359 | private static void appendLine(StringBuilder sb, List list) {
360 | for (String line : list) {
361 | // 只关心特定的字段
362 | if (!line.startsWith("key:") && !line.startsWith("dataType:") && !line.startsWith("showType:") && !line.startsWith("max:")) {
363 | continue;
364 | }
365 | if (line.contains("//")) {// inline注释
366 | sb.append(line.substring(0, line.indexOf("//")));
367 | } else {
368 | sb.append(line);
369 | }
370 | }
371 | }
372 |
373 | /*
374 | *
375 | * 下面开始是一些辅助方法
376 | *
377 | */
378 |
379 | /**
380 | * 从classpath中读取某个文件,原样写入outputDir
381 | */
382 | private static void copyFileFromClasspath(String inputFile, String outputDir, String outputName) throws IOException {
383 | File target = new File(outputDir, outputName);
384 | if (target.exists()) {
385 | System.out.println("INFO: delete exist file " + target.getAbsolutePath());
386 | target.delete();
387 | }
388 | System.out.println("INFO: writing file " + target.getAbsolutePath());
389 | BufferedWriter bw = Files.newBufferedWriter(target.toPath());
390 | for (String line : Resources.readLines(Resources.getResource(inputFile), Charsets.UTF_8)) {
391 | bw.write(line);
392 | bw.newLine();
393 | }
394 | bw.close();
395 | }
396 |
397 | /**
398 | * 将parse好的行写入一个文件
399 | */
400 | private static void writeLinesToFile(List lines, String outputDir, String tableName, String fileName) throws IOException {
401 | ensureDir(outputDir, tableName);
402 | String upCamelName = tableName.substring(0, 1).toUpperCase() + tableName.substring(1);
403 | File target = new File(outputDir + "/" + tableName + "/" + upCamelName + fileName);
404 | if (target.exists()) {
405 | System.out.println("INFO: delete exist file " + target.getAbsolutePath());
406 | target.delete();
407 | }
408 | System.out.println("INFO: writing file " + target.getAbsolutePath());
409 | BufferedWriter bw = Files.newBufferedWriter(target.toPath());
410 | for (String line : lines) {
411 | bw.write(line);
412 | bw.newLine();
413 | }
414 | bw.close();
415 | }
416 |
417 | /**
418 | * 读取模版文件并做变量替换
419 | */
420 | private static List renderTemplate(String templateFileName, final Map params) throws MalformedURLException, IOException {
421 | final Pattern p = Pattern.compile("\\{(.*?)\\}");
422 | // 定义每行的处理逻辑
423 | LineProcessor> processor = new LineProcessor>() {
424 |
425 | private List result = Lists.newArrayList();
426 |
427 | @Override
428 | public boolean processLine(String line) throws IOException {
429 | String tmp = line;
430 | Matcher m = p.matcher(line);
431 | while (m.find()) {
432 | String key = m.group(1);
433 | if (params.containsKey(key)) {
434 | tmp = tmp.replaceAll("\\{" + key + "\\}", params.get(key));
435 | }
436 | }
437 |
438 | result.add(tmp);
439 | return true;
440 | }
441 |
442 | @Override
443 | public List getResult() {
444 | return result;
445 | }
446 | };
447 |
448 | return Resources.readLines(Resources.getResource(templateFileName), Charsets.UTF_8, processor);
449 | }
450 |
451 | /**
452 | * 确保某个目录存在,不存在则新建
453 | */
454 | private static void ensureDir(String parent, String child) {
455 | File f = new File(parent, child);
456 | if (!f.exists())
457 | f.mkdirs();
458 | }
459 | }
460 |
--------------------------------------------------------------------------------
/src/main/resources/CommonResult.sample:
--------------------------------------------------------------------------------
1 | package me.jxy;
2 |
3 | import java.io.Serializable;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * 包装http请求的返回
9 | *
10 | * @version 1.0
11 | * @author foolbeargm@gmail.com
12 | */
13 | @Data
14 | public class CommonResult implements Serializable {
15 |
16 | private static final long serialVersionUID = 8081585371266115332L;
17 |
18 | private boolean success = true;
19 | private Integer code;
20 | private String message;
21 | private T data;
22 | private Integer total;
23 |
24 | /**
25 | * 请求成功
26 | *
27 | * @param data 要返回的数据
28 | * @return
29 | */
30 | public static CommonResult successReturn(T data) {
31 | return successReturn(data, null, null);
32 | }
33 |
34 | /**
35 | * 请求成功
36 | *
37 | * @param data 要返回的数据
38 | * @param total 数据总数
39 | * @return
40 | */
41 | public static CommonResult successReturn(T data, Integer total) {
42 | return successReturn(data, total, null);
43 | }
44 |
45 | /**
46 | * 请求成功
47 | *
48 | * @param data 要返回的数据
49 | * @param message 要返回的信息
50 | * @return
51 | */
52 | public static CommonResult successReturn(T data, String message) {
53 | return successReturn(data, null, message);
54 | }
55 |
56 | /**
57 | * 请求成功
58 | *
59 | * @param data 要返回的数据
60 | * @param total 数据总数
61 | * @param message 要返回的信息
62 | * @return
63 | */
64 | public static CommonResult successReturn(T data, Integer total, String message) {
65 | CommonResult result = new CommonResult();
66 | result.setSuccess(true);
67 | result.setCode(0);
68 | result.setMessage(message);
69 | result.setData(data);
70 | result.setTotal(total);
71 | return result;
72 | }
73 |
74 | /**
75 | * 请求失败
76 | *
77 | * @param message 错误信息
78 | * @return
79 | */
80 | public static CommonResult errorReturn(String message) {
81 | return errorReturn(null, message);
82 | }
83 |
84 | /**
85 | * 请求失败
86 | *
87 | * @param code 错误码
88 | * @param message 错误信息
89 | * @return
90 | */
91 | public static CommonResult errorReturn(Integer code, String message) {
92 | CommonResult result = new CommonResult();
93 | result.setSuccess(false);
94 | result.setCode(code);
95 | result.setMessage(message);
96 | result.setData(null);
97 | result.setTotal(null);
98 | return result;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/resources/Controller.sample:
--------------------------------------------------------------------------------
1 | package me.jxy;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.util.List;
8 |
9 | import javax.servlet.ServletOutputStream;
10 | import javax.servlet.http.HttpServletResponse;
11 |
12 | import org.apache.commons.io.IOUtils;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 | import org.springframework.http.MediaType;
16 | import org.springframework.stereotype.Controller;
17 | import org.springframework.web.bind.annotation.RequestBody;
18 | import org.springframework.web.bind.annotation.RequestMapping;
19 | import org.springframework.web.bind.annotation.RequestParam;
20 | import org.springframework.web.bind.annotation.ResponseBody;
21 | import org.springframework.web.multipart.MultipartFile;
22 |
23 | import com.alibaba.fastjson.JSONObject;
24 | import com.google.common.collect.Lists;
25 |
26 | import static com.google.common.base.Preconditions.*;
27 |
28 | /**
29 | * 针对某个表的CRUD操作
30 | * 注意这个类中的所有方法不能抛异常,即使出错也用CommonResult包装下返回错误信息
31 | *
32 | * @version 1.0
33 | * @author foolbeargm@gmail.com
34 | */
35 | @Controller
36 | @RequestMapping("/api/{lowCamelName}")
37 | public class {upCamelName}Controller {
38 |
39 | private final static Logger logger = LoggerFactory.getLogger({upCamelName}Controller.class);
40 |
41 | /*
42 | * 如果要提供跨域的接口,需要支持CORS
43 | *
44 | * spring 4.2+开始支持CORS,有两种方式:
45 | * 1. 在controller类或方法上使用CrossOrigin注解
46 | * 2. 在spring配置文件中配置特定路径支持CORS
47 | *
48 | * 注解的方式更方便,但在jdk7下有bug,必须升级到jdk7_80或jdk8才可以
49 | */
50 |
51 | /**
52 | * 查询/select
53 | *
54 | * @param vo 封装查询条件
55 | * @return 符合查询条件的记录
56 | */
57 | @RequestMapping("select")
58 | @ResponseBody
59 | public CommonResult> select(@RequestBody {upCamelName}QueryVO vo) {
60 | try {
61 | checkNotNull(vo);
62 | List<{upCamelName}VO> result = Lists.newArrayList();
63 |
64 | // TODO: 填写自己的逻辑
65 |
66 | return CommonResult.successReturn(result, 10000);
67 | } catch (Exception e) {
68 | logger.error("select error", e);
69 | return CommonResult.errorReturn(100, "select error");
70 | }
71 | }
72 |
73 | /**
74 | * 新增一条数据/insert
75 | *
76 | * @param vo 要插入的数据
77 | * @return 完整的数据记录,补充了主键的(如果有主键的话)
78 | */
79 | @RequestMapping("insert")
80 | @ResponseBody
81 | public CommonResult<{upCamelName}VO> insert(@RequestBody {upCamelName}VO vo) {
82 | try {
83 | checkNotNull(vo);
84 |
85 | // TODO: 填写自己的逻辑
86 |
87 | return CommonResult.successReturn(vo);
88 | } catch (Exception e) {
89 | logger.error("insert error", e);
90 | return CommonResult.errorReturn(200, "insert error");
91 | }
92 | }
93 |
94 | /**
95 | * 更新/update
96 | * 可以单条更新,也可以批量更新
97 | *
98 | * @param keys 要更新的记录,逗号分隔的主键
99 | * @param vo 要更新哪些字段
100 | * @return 更新了几条记录
101 | */
102 | @RequestMapping("update")
103 | @ResponseBody
104 | public CommonResult update(@RequestParam("keys") String keys, @RequestBody {upCamelName}VO vo) {
105 | try {
106 | checkNotNull(keys);
107 | checkNotNull(vo);
108 |
109 | // 注意:主键可能是数字,也可能是字符串,要自己处理
110 | // TODO: 填写自己的逻辑
111 |
112 | return CommonResult.successReturn(100);
113 | } catch (Exception e) {
114 | logger.error("update error", e);
115 | return CommonResult.errorReturn(300, "update error");
116 | }
117 | }
118 |
119 | /**
120 | * 删除/delete
121 | * 可以单条删除,也能批量删除
122 | *
123 | * @param keys 要删除的记录,逗号分隔的主键
124 | * @return 删除了几条记录
125 | */
126 | @RequestMapping("delete")
127 | @ResponseBody
128 | public CommonResult delete(@RequestParam("keys") String keys) {
129 | try {
130 | checkNotNull(keys);
131 |
132 | // TODO: 填写自己的逻辑
133 |
134 | return CommonResult.successReturn(99);
135 | } catch (Exception e) {
136 | logger.error("delete error", e);
137 | return CommonResult.errorReturn(400, "delete error");
138 | }
139 | }
140 |
141 | /**
142 | * 从某个文件导入数据
143 | * 不限定文件的格式和处理逻辑,需要自己和运营约定
144 | *
145 | * @param file
146 | * @return 返回一个string提示信息,提示信息看运营的需求,一般要包括成功/失败的记录数
147 | */
148 | @RequestMapping("import")
149 | @ResponseBody
150 | public CommonResult importFile(MultipartFile file) {
151 | try {
152 | // 将上传的文件写到一个临时位置,为了防止文件名冲突,加上时间戳
153 | File tmpFile = new File(System.getProperty("java.io.tmpdir"), System.currentTimeMillis() + file.getOriginalFilename());
154 | logger.info("import: writing tmp file {}", tmpFile.getAbsolutePath());
155 | file.transferTo(tmpFile);
156 |
157 | // TODO: 处理tmpFile并导入数据
158 |
159 | // 最后删除这个临时文件
160 | logger.info("import: delete tmp file {}", tmpFile.getAbsolutePath());
161 | tmpFile.delete();
162 |
163 | return CommonResult.successReturn("导入成功XX条,导入失败YY条,导入失败的行:1,2,3");
164 | } catch (Exception e) {
165 | logger.error("import error", e);
166 | return CommonResult.errorReturn(500, "import error");
167 | }
168 | }
169 |
170 | /**
171 | * 将符合某个条件的所有记录导出成一个文件
172 | * 同样不限定文件格式和处理逻辑,需要自己和运营约定
173 | *
174 | * @param res
175 | * @param q 其实是queryVO的json,封装查询条件,需要手动反序列化
176 | * @return
177 | */
178 | @RequestMapping(value = "export", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
179 | @ResponseBody
180 | public void exportFile(HttpServletResponse res, String q) {
181 | try {
182 | JSONObject json = JSONObject.parseObject(checkNotNull(q));
183 | checkArgument(json.size() > 0, "empty query condition");
184 |
185 | // 注意这个方法是查询符合条件的所有记录,忽略vo中的page和pageSize
186 | {upCamelName}QueryVO vo = JSONObject.parseObject(q, {upCamelName}QueryVO.class);
187 |
188 | String fileName = "export.csv"; // 用户下载得到的文件名,暂时写死
189 | File tmpFile = new File(System.getProperty("java.io.tmpdir"), System.currentTimeMillis() + fileName);
190 | logger.info("export: writing tmp file {}", tmpFile.getAbsolutePath());
191 | // TODO: 根据QueryVO中的条件查询并将结果写入tmpFile
192 |
193 | // 返回下载请求,这段代码不是很优雅,以后要优化下
194 | res.setHeader("Content-Disposition", "attachment; filename=" + fileName);
195 | InputStream in = new FileInputStream(tmpFile);
196 | ServletOutputStream out = res.getOutputStream();
197 | IOUtils.copy(in, out);
198 | IOUtils.closeQuietly(in);
199 | IOUtils.closeQuietly(out);
200 |
201 | // 最后删除临时文件
202 | logger.info("export: delete tmp file {}", tmpFile.getAbsolutePath());
203 | tmpFile.delete();
204 | } catch (Exception e) {
205 | logger.error("export error", e);
206 | try {
207 | res.reset();
208 | res.setHeader("Content-Type", "text/plain;charset=utf-8");
209 | ServletOutputStream out = res.getOutputStream();
210 | out.write("导出失败,请联系管理员。\n".getBytes("utf-8"));
211 | out.write(("错误信息:" + e.getMessage()).getBytes("utf-8"));
212 | IOUtils.closeQuietly(out);
213 | } catch (IOException e1) {
214 | e1.printStackTrace();
215 | }
216 | }
217 | }
218 |
219 | /**
220 | * 获取服务端schema
221 | *
222 | * @return
223 | */
224 | @RequestMapping("schema")
225 | @ResponseBody
226 | public CommonResult getSchema() {
227 | try {
228 | JSONObject schema = new JSONObject();
229 |
230 | // TODO: 按自己的需要决定是否更新querySchema和dataSchema,前端会将schema合并后展示
231 | // schema.put("querySchema", new JSONArray());
232 | // schema.put("dataSchema", new JSONArray());
233 |
234 | return CommonResult.successReturn(schema);
235 | } catch (Exception e) {
236 | logger.error("get schema error", e);
237 | return CommonResult.errorReturn(600, "get schema error");
238 | }
239 | }
240 |
241 | }
242 |
--------------------------------------------------------------------------------
/src/main/resources/LoginController.sample:
--------------------------------------------------------------------------------
1 | package me.jxy;
2 |
3 | import javax.servlet.http.HttpServletResponse;
4 |
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.stereotype.Controller;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.ResponseBody;
10 |
11 | /**
12 | * LoginController例子
13 | *
14 | * @version 1.0
15 | * @author foolbeargm@gmail.com
16 | */
17 | @Controller
18 | @RequestMapping("/api")
19 | public class LoginController {
20 |
21 | private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
22 |
23 | /**
24 | * 返回当前登录用户的用户名
25 | *
26 | * @return
27 | */
28 | @RequestMapping("getCurrentUser")
29 | @ResponseBody
30 | public CommonResult getCurrentUser() {
31 | // TODO: 填写自己的逻辑
32 | return CommonResult.successReturn("guest");
33 | }
34 |
35 | /**
36 | * 校验用户信息,成功则返回当前登录的用户名
37 | *
38 | * @return
39 | */
40 | @RequestMapping("login")
41 | @ResponseBody
42 | public CommonResult login(String username, String password) {
43 | // TODO: 填写自己的逻辑
44 | logger.info("username = {}", username);
45 | return CommonResult.successReturn("guest");
46 | }
47 |
48 | /**
49 | * 退出登录,一般退出后要重定向到某个页面
50 | *
51 | * @return
52 | */
53 | @RequestMapping("logout")
54 | @ResponseBody
55 | public void logout(HttpServletResponse res) {
56 | // TODO: 填写自己的逻辑
57 | // res.sendRedirect("/sso/logout");
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/resources/QueryVO.sample:
--------------------------------------------------------------------------------
1 | package me.jxy;
2 |
3 | import java.util.Date;
4 | import java.util.List;
5 |
6 | import lombok.Data;
7 |
8 | /**
9 | * QueryVO例子
10 | *
11 | * @version 1.0
12 | * @author foolbeargm@gmail.com
13 | */
14 | @Data
15 | public class {upCamelName}QueryVO {
16 |
17 | {fields}
18 |
19 | // 这两个要有默认值
20 | private Integer page = 1;
21 | private Integer pageSize = 50;
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/resources/UploadController.sample:
--------------------------------------------------------------------------------
1 | package me.jxy;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.stereotype.Controller;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RequestParam;
8 | import org.springframework.web.bind.annotation.ResponseBody;
9 | import org.springframework.web.multipart.MultipartFile;
10 |
11 | /**
12 | * UploadController例子
13 | *
14 | * @version 1.0
15 | * @author foolbeargm@gmail.com
16 | */
17 | @Controller
18 | @RequestMapping("/api")
19 | public class UploadController {
20 |
21 | private static Logger logger = LoggerFactory.getLogger(UploadController.class);
22 |
23 | /**
24 | * 上传图片的接口
25 | *
26 | * @param file 要上传的图片
27 | * @return
28 | */
29 | @RequestMapping("uploadImage")
30 | @ResponseBody
31 | public CommonResult uploadImage(@RequestParam(value = "file", required = true) MultipartFile file) {
32 | try {
33 | // TODO: 填写自己的上传逻辑
34 | // 返回上传后的图片地址
35 | return CommonResult.successReturn("http://jxy.me/about/avatar.jpg");
36 | } catch (Exception e) {
37 | logger.error("uploadImage error ", e);
38 | return CommonResult.errorReturn("upload image error");
39 | }
40 | }
41 |
42 | /**
43 | * 上传文件的接口
44 | *
45 | * @param file 要上传的文件
46 | * @return
47 | */
48 | @RequestMapping("uploadFile")
49 | @ResponseBody
50 | public CommonResult uploadFile(@RequestParam(value = "file", required = true) MultipartFile file) {
51 | try {
52 | // TODO: 填写自己的上传逻辑
53 | // 和上传图片非常类似,返回上传后的文件地址
54 | return CommonResult.successReturn("https://static.googleusercontent.com/media/research.google.com/zh-CN//archive/mapreduce-osdi04.pdf");
55 | } catch (Exception e) {
56 | logger.error("uploadFile error ", e);
57 | return CommonResult.errorReturn("upload file error");
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/src/main/resources/VO.sample:
--------------------------------------------------------------------------------
1 | package me.jxy;
2 |
3 | import java.util.Date;
4 | import java.util.List;
5 |
6 | import lombok.Data;
7 |
8 | /**
9 | * VO例子
10 | *
11 | * @version 1.0
12 | * @author foolbeargm@gmail.com
13 | */
14 | @Data
15 | public class {upCamelName}VO {
16 | {fields}
17 | }
18 |
--------------------------------------------------------------------------------