├── .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 | --------------------------------------------------------------------------------