提交 | 用户 | 时间
|
e7c126
|
1 |
package com.iailab.module.infra.service.codegen.inner; |
H |
2 |
|
|
3 |
import cn.hutool.core.collection.CollUtil; |
|
4 |
import cn.hutool.core.map.MapUtil; |
|
5 |
import cn.hutool.core.util.ObjectUtil; |
|
6 |
import cn.hutool.core.util.StrUtil; |
|
7 |
import cn.hutool.extra.template.TemplateConfig; |
|
8 |
import cn.hutool.extra.template.TemplateEngine; |
|
9 |
import cn.hutool.extra.template.engine.velocity.VelocityEngine; |
|
10 |
import cn.hutool.system.SystemUtil; |
|
11 |
import com.iailab.framework.apilog.core.annotation.ApiAccessLog; |
|
12 |
import com.iailab.framework.apilog.core.enums.OperateTypeEnum; |
|
13 |
import com.iailab.framework.common.exception.util.ServiceExceptionUtil; |
|
14 |
import com.iailab.framework.common.pojo.CommonResult; |
|
15 |
import com.iailab.framework.common.pojo.PageParam; |
|
16 |
import com.iailab.framework.common.pojo.PageResult; |
|
17 |
import com.iailab.framework.common.util.collection.CollectionUtils; |
|
18 |
import com.iailab.framework.common.util.date.DateUtils; |
|
19 |
import com.iailab.framework.common.util.date.LocalDateTimeUtils; |
|
20 |
import com.iailab.framework.common.util.object.BeanUtils; |
|
21 |
import com.iailab.framework.common.util.object.ObjectUtils; |
|
22 |
import com.iailab.framework.common.util.string.StrUtils; |
|
23 |
import com.iailab.framework.excel.core.annotations.DictFormat; |
|
24 |
import com.iailab.framework.excel.core.convert.DictConvert; |
|
25 |
import com.iailab.framework.excel.core.util.ExcelUtils; |
|
26 |
import com.iailab.framework.mybatis.core.dataobject.BaseDO; |
|
27 |
import com.iailab.framework.mybatis.core.mapper.BaseMapperX; |
|
28 |
import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX; |
|
29 |
import com.iailab.module.infra.dal.dataobject.codegen.CodegenColumnDO; |
|
30 |
import com.iailab.module.infra.dal.dataobject.codegen.CodegenTableDO; |
|
31 |
import com.iailab.module.infra.enums.codegen.CodegenFrontTypeEnum; |
|
32 |
import com.iailab.module.infra.enums.codegen.CodegenSceneEnum; |
|
33 |
import com.iailab.module.infra.enums.codegen.CodegenTemplateTypeEnum; |
|
34 |
import com.iailab.module.infra.framework.codegen.config.CodegenProperties; |
|
35 |
import com.google.common.annotations.VisibleForTesting; |
|
36 |
import com.google.common.collect.ImmutableTable; |
|
37 |
import com.google.common.collect.Maps; |
|
38 |
import com.google.common.collect.Table; |
|
39 |
import lombok.Setter; |
|
40 |
import org.springframework.stereotype.Component; |
|
41 |
|
|
42 |
import javax.annotation.PostConstruct; |
|
43 |
import javax.annotation.Resource; |
|
44 |
import java.util.*; |
|
45 |
|
|
46 |
import static cn.hutool.core.map.MapUtil.getStr; |
|
47 |
import static cn.hutool.core.text.CharSequenceUtil.*; |
|
48 |
|
|
49 |
/** |
|
50 |
* 代码生成的引擎,用于具体生成代码 |
|
51 |
* 目前基于 {@link org.apache.velocity.app.Velocity} 模板引擎实现 |
|
52 |
* |
|
53 |
* 考虑到 Java 模板引擎的框架非常多,Freemarker、Velocity、Thymeleaf 等等,所以我们采用 hutool 封装的 {@link cn.hutool.extra.template.Template} 抽象 |
|
54 |
* |
|
55 |
* @author iailab |
|
56 |
*/ |
|
57 |
@Component |
|
58 |
public class CodegenEngine { |
|
59 |
|
|
60 |
/** |
|
61 |
* 后端的模板配置 |
|
62 |
* |
|
63 |
* key:模板在 resources 的地址 |
|
64 |
* value:生成的路径 |
|
65 |
*/ |
|
66 |
private static final Map<String, String> SERVER_TEMPLATES = MapUtil.<String, String>builder(new LinkedHashMap<>()) // 有序 |
|
67 |
// Java module-biz Main |
|
68 |
.put(javaTemplatePath("controller/vo/pageReqVO"), javaModuleImplVOFilePath("PageReqVO")) |
|
69 |
.put(javaTemplatePath("controller/vo/listReqVO"), javaModuleImplVOFilePath("ListReqVO")) |
|
70 |
.put(javaTemplatePath("controller/vo/respVO"), javaModuleImplVOFilePath("RespVO")) |
|
71 |
.put(javaTemplatePath("controller/vo/saveReqVO"), javaModuleImplVOFilePath("SaveReqVO")) |
|
72 |
.put(javaTemplatePath("controller/controller"), javaModuleImplControllerFilePath()) |
|
73 |
.put(javaTemplatePath("dal/do"), |
|
74 |
javaModuleImplMainFilePath("dal/dataobject/${table.businessName}/${table.className}DO")) |
|
75 |
.put(javaTemplatePath("dal/do_sub"), // 特殊:主子表专属逻辑 |
|
76 |
javaModuleImplMainFilePath("dal/dataobject/${table.businessName}/${subTable.className}DO")) |
|
77 |
.put(javaTemplatePath("dal/mapper"), |
|
78 |
javaModuleImplMainFilePath("dal/mysql/${table.businessName}/${table.className}Mapper")) |
|
79 |
.put(javaTemplatePath("dal/mapper_sub"), // 特殊:主子表专属逻辑 |
|
80 |
javaModuleImplMainFilePath("dal/mysql/${table.businessName}/${subTable.className}Mapper")) |
|
81 |
.put(javaTemplatePath("dal/mapper.xml"), mapperXmlFilePath()) |
|
82 |
.put(javaTemplatePath("service/serviceImpl"), |
|
83 |
javaModuleImplMainFilePath("service/${table.businessName}/${table.className}ServiceImpl")) |
|
84 |
.put(javaTemplatePath("service/service"), |
|
85 |
javaModuleImplMainFilePath("service/${table.businessName}/${table.className}Service")) |
|
86 |
// Java module-biz Test |
|
87 |
.put(javaTemplatePath("test/serviceTest"), |
|
88 |
javaModuleImplTestFilePath("service/${table.businessName}/${table.className}ServiceImplTest")) |
|
89 |
// Java module-api Main |
|
90 |
.put(javaTemplatePath("enums/errorcode"), javaModuleApiMainFilePath("enums/ErrorCodeConstants_手动操作")) |
|
91 |
// SQL |
|
92 |
.put("codegen/sql/sql.vm", "sql/sql.sql") |
|
93 |
.put("codegen/sql/h2.vm", "sql/h2.sql") |
|
94 |
.build(); |
|
95 |
|
|
96 |
/** |
|
97 |
* 后端的配置模版 |
|
98 |
* |
|
99 |
* key1:UI 模版的类型 {@link CodegenFrontTypeEnum#getType()} |
|
100 |
* key2:模板在 resources 的地址 |
|
101 |
* value:生成的路径 |
|
102 |
*/ |
|
103 |
private static final Table<Integer, String, String> FRONT_TEMPLATES = ImmutableTable.<Integer, String, String>builder() |
|
104 |
// Vue2 标准模版 |
|
105 |
.put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/index.vue"), |
|
106 |
vueFilePath("views/${table.moduleName}/${table.businessName}/index.vue")) |
|
107 |
.put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("api/api.js"), |
|
108 |
vueFilePath("api/${table.moduleName}/${table.businessName}/index.js")) |
|
109 |
.put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/form.vue"), |
|
110 |
vueFilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue")) |
|
111 |
.put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/components/form_sub_normal.vue"), // 特殊:主子表专属逻辑 |
|
112 |
vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue")) |
|
113 |
.put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/components/form_sub_inner.vue"), // 特殊:主子表专属逻辑 |
|
114 |
vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue")) |
|
115 |
.put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/components/form_sub_erp.vue"), // 特殊:主子表专属逻辑 |
|
116 |
vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue")) |
|
117 |
.put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/components/list_sub_inner.vue"), // 特殊:主子表专属逻辑 |
|
118 |
vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue")) |
|
119 |
.put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/components/list_sub_erp.vue"), // 特殊:主子表专属逻辑 |
|
120 |
vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue")) |
|
121 |
// Vue3 标准模版 |
|
122 |
.put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/index.vue"), |
|
123 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue")) |
|
124 |
.put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/form.vue"), |
|
125 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue")) |
|
126 |
.put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/form_sub_normal.vue"), // 特殊:主子表专属逻辑 |
|
127 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue")) |
|
128 |
.put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/form_sub_inner.vue"), // 特殊:主子表专属逻辑 |
|
129 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue")) |
|
130 |
.put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/form_sub_erp.vue"), // 特殊:主子表专属逻辑 |
|
131 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue")) |
|
132 |
.put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/list_sub_inner.vue"), // 特殊:主子表专属逻辑 |
|
133 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue")) |
|
134 |
.put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/list_sub_erp.vue"), // 特殊:主子表专属逻辑 |
|
135 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue")) |
|
136 |
.put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("api/api.ts"), |
|
137 |
vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts")) |
|
138 |
// Vue3 Schema 模版 |
|
139 |
.put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("views/data.ts"), |
|
140 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/${classNameVar}.data.ts")) |
|
141 |
.put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("views/index.vue"), |
|
142 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue")) |
|
143 |
.put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("views/form.vue"), |
|
144 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue")) |
|
145 |
.put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("api/api.ts"), |
|
146 |
vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts")) |
|
147 |
// Vue3 vben 模版 |
|
148 |
.put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("views/data.ts"), |
|
149 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/${classNameVar}.data.ts")) |
|
150 |
.put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("views/index.vue"), |
|
151 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue")) |
|
152 |
.put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("views/form.vue"), |
|
153 |
vue3FilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Modal.vue")) |
|
154 |
.put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("api/api.ts"), |
|
155 |
vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts")) |
|
156 |
.build(); |
|
157 |
|
|
158 |
@Resource |
|
159 |
private CodegenProperties codegenProperties; |
|
160 |
|
|
161 |
/** |
|
162 |
* 是否使用 jakarta 包,用于解决 Spring Boot 2.X 和 3.X 的兼容性问题 |
|
163 |
* |
|
164 |
* true - 使用 jakarta.validation.constraints.* |
|
165 |
* false - 使用 javax.validation.constraints.* |
|
166 |
*/ |
|
167 |
@Setter // 允许设置的原因,是因为单测需要手动改变 |
|
168 |
private Boolean jakartaEnable; |
|
169 |
|
|
170 |
/** |
|
171 |
* 模板引擎,由 hutool 实现 |
|
172 |
*/ |
|
173 |
private final TemplateEngine templateEngine; |
|
174 |
/** |
|
175 |
* 全局通用变量映射 |
|
176 |
*/ |
|
177 |
private final Map<String, Object> globalBindingMap = new HashMap<>(); |
|
178 |
|
|
179 |
public CodegenEngine() { |
|
180 |
// 初始化 TemplateEngine 属性 |
|
181 |
TemplateConfig config = new TemplateConfig(); |
|
182 |
config.setResourceMode(TemplateConfig.ResourceMode.CLASSPATH); |
|
183 |
this.templateEngine = new VelocityEngine(config); |
|
184 |
// 设置 javaxEnable,按照是否使用 JDK17 来判断 |
|
185 |
this.jakartaEnable = SystemUtil.getJavaInfo().isJavaVersionAtLeast(1700); // 17.00 * 100 |
|
186 |
} |
|
187 |
|
|
188 |
@PostConstruct |
|
189 |
@VisibleForTesting |
|
190 |
void initGlobalBindingMap() { |
|
191 |
// 全局配置 |
|
192 |
globalBindingMap.put("basePackage", codegenProperties.getBasePackage()); |
|
193 |
globalBindingMap.put("baseFrameworkPackage", codegenProperties.getBasePackage() |
|
194 |
+ '.' + "framework"); // 用于后续获取测试类的 package 地址 |
|
195 |
globalBindingMap.put("jakartaPackage", jakartaEnable ? "jakarta" : "javax"); |
|
196 |
// 全局 Java Bean |
|
197 |
globalBindingMap.put("CommonResultClassName", CommonResult.class.getName()); |
|
198 |
globalBindingMap.put("PageResultClassName", PageResult.class.getName()); |
|
199 |
// VO 类,独有字段 |
|
200 |
globalBindingMap.put("PageParamClassName", PageParam.class.getName()); |
|
201 |
globalBindingMap.put("DictFormatClassName", DictFormat.class.getName()); |
|
202 |
// DO 类,独有字段 |
|
203 |
globalBindingMap.put("BaseDOClassName", BaseDO.class.getName()); |
|
204 |
globalBindingMap.put("baseDOFields", CodegenBuilder.BASE_DO_FIELDS); |
|
205 |
globalBindingMap.put("QueryWrapperClassName", LambdaQueryWrapperX.class.getName()); |
|
206 |
globalBindingMap.put("BaseMapperClassName", BaseMapperX.class.getName()); |
|
207 |
// Util 工具类 |
|
208 |
globalBindingMap.put("ServiceExceptionUtilClassName", ServiceExceptionUtil.class.getName()); |
|
209 |
globalBindingMap.put("DateUtilsClassName", DateUtils.class.getName()); |
|
210 |
globalBindingMap.put("ExcelUtilsClassName", ExcelUtils.class.getName()); |
|
211 |
globalBindingMap.put("LocalDateTimeUtilsClassName", LocalDateTimeUtils.class.getName()); |
|
212 |
globalBindingMap.put("ObjectUtilsClassName", ObjectUtils.class.getName()); |
|
213 |
globalBindingMap.put("DictConvertClassName", DictConvert.class.getName()); |
|
214 |
globalBindingMap.put("ApiAccessLogClassName", ApiAccessLog.class.getName()); |
|
215 |
globalBindingMap.put("OperateTypeEnumClassName", OperateTypeEnum.class.getName()); |
|
216 |
globalBindingMap.put("BeanUtils", BeanUtils.class.getName()); |
|
217 |
} |
|
218 |
|
|
219 |
/** |
|
220 |
* 生成代码 |
|
221 |
* |
|
222 |
* @param table 表定义 |
|
223 |
* @param columns table 的字段定义数组 |
|
224 |
* @param subTables 子表数组,当且仅当主子表时使用 |
|
225 |
* @param subColumnsList subTables 的字段定义数组 |
|
226 |
* @return 生成的代码,key 是路径,value 是对应代码 |
|
227 |
*/ |
|
228 |
public Map<String, String> execute(CodegenTableDO table, List<CodegenColumnDO> columns, |
|
229 |
List<CodegenTableDO> subTables, List<List<CodegenColumnDO>> subColumnsList) { |
|
230 |
// 1.1 初始化 bindMap 上下文 |
|
231 |
Map<String, Object> bindingMap = initBindingMap(table, columns, subTables, subColumnsList); |
|
232 |
// 1.2 获得模版 |
|
233 |
Map<String, String> templates = getTemplates(table.getFrontType()); |
|
234 |
|
|
235 |
// 2. 执行生成 |
|
236 |
Map<String, String> result = Maps.newLinkedHashMapWithExpectedSize(templates.size()); // 有序 |
|
237 |
templates.forEach((vmPath, filePath) -> { |
|
238 |
// 2.1 特殊:主子表专属逻辑 |
|
239 |
if (isSubTemplate(vmPath)) { |
|
240 |
generateSubCode(table, subTables, result, vmPath, filePath, bindingMap); |
|
241 |
return; |
|
242 |
// 2.2 特殊:树表专属逻辑 |
|
243 |
} else if (isPageReqVOTemplate(vmPath)) { |
|
244 |
// 减少多余的类生成,例如说 PageVO.java 类 |
|
245 |
if (CodegenTemplateTypeEnum.isTree(table.getTemplateType())) { |
|
246 |
return; |
|
247 |
} |
|
248 |
} else if (isListReqVOTemplate(vmPath)) { |
|
249 |
// 减少多余的类生成,例如说 ListVO.java 类 |
|
250 |
if (!CodegenTemplateTypeEnum.isTree(table.getTemplateType())) { |
|
251 |
return; |
|
252 |
} |
|
253 |
} |
|
254 |
// 2.3 默认生成 |
|
255 |
generateCode(result, vmPath, filePath, bindingMap); |
|
256 |
}); |
|
257 |
return result; |
|
258 |
} |
|
259 |
|
|
260 |
private void generateCode(Map<String, String> result, String vmPath, |
|
261 |
String filePath, Map<String, Object> bindingMap) { |
|
262 |
filePath = formatFilePath(filePath, bindingMap); |
|
263 |
String content = templateEngine.getTemplate(vmPath).render(bindingMap); |
|
264 |
// 格式化代码 |
|
265 |
content = prettyCode(content); |
|
266 |
result.put(filePath, content); |
|
267 |
} |
|
268 |
|
|
269 |
private void generateSubCode(CodegenTableDO table, List<CodegenTableDO> subTables, |
|
270 |
Map<String, String> result, String vmPath, |
|
271 |
String filePath, Map<String, Object> bindingMap) { |
|
272 |
// 没有子表,所以不生成 |
|
273 |
if (CollUtil.isEmpty(subTables)) { |
|
274 |
return; |
|
275 |
} |
|
276 |
// 主子表的模式匹配。目的:过滤掉个性化的模版 |
|
277 |
if (vmPath.contains("_normal") |
|
278 |
&& ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_NORMAL.getType())) { |
|
279 |
return; |
|
280 |
} |
|
281 |
if (vmPath.contains("_erp") |
|
282 |
&& ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_ERP.getType())) { |
|
283 |
return; |
|
284 |
} |
|
285 |
if (vmPath.contains("_inner") |
|
286 |
&& ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_INNER.getType())) { |
|
287 |
return; |
|
288 |
} |
|
289 |
|
|
290 |
// 逐个生成 |
|
291 |
for (int i = 0; i < subTables.size(); i++) { |
|
292 |
bindingMap.put("subIndex", i); |
|
293 |
generateCode(result, vmPath, filePath, bindingMap); |
|
294 |
} |
|
295 |
bindingMap.remove("subIndex"); |
|
296 |
} |
|
297 |
|
|
298 |
/** |
|
299 |
* 格式化生成后的代码 |
|
300 |
* |
|
301 |
* 因为尽量让 vm 模版简单,所以统一的处理都在这个方法。 |
|
302 |
* 如果不处理,Vue 的 Pretty 格式校验可能会报错 |
|
303 |
* |
|
304 |
* @param content 格式化前的代码 |
|
305 |
* @return 格式化后的代码 |
|
306 |
*/ |
|
307 |
private String prettyCode(String content) { |
|
308 |
// Vue 界面:去除字段后面多余的 , 逗号,解决前端的 Pretty 代码格式检查的报错 |
|
309 |
content = content.replaceAll(",\n}", "\n}").replaceAll(",\n }", "\n }"); |
|
310 |
// Vue 界面:去除多的 dateFormatter,只有一个的情况下,说明没使用到 |
|
311 |
if (StrUtil.count(content, "dateFormatter") == 1) { |
|
312 |
content = StrUtils.removeLineContains(content, "dateFormatter"); |
|
313 |
} |
|
314 |
// Vue2 界面:修正 $refs |
|
315 |
if (StrUtil.count(content, "this.refs") >= 1) { |
|
316 |
content = content.replace("this.refs", "this.$refs"); |
|
317 |
} |
|
318 |
// Vue 界面:去除多的 dict 相关,只有一个的情况下,说明没使用到 |
|
319 |
if (StrUtil.count(content, "getIntDictOptions") == 1) { |
|
320 |
content = content.replace("getIntDictOptions, ", ""); |
|
321 |
} |
|
322 |
if (StrUtil.count(content, "getStrDictOptions") == 1) { |
|
323 |
content = content.replace("getStrDictOptions, ", ""); |
|
324 |
} |
|
325 |
if (StrUtil.count(content, "getBoolDictOptions") == 1) { |
|
326 |
content = content.replace("getBoolDictOptions, ", ""); |
|
327 |
} |
|
328 |
if (StrUtil.count(content, "DICT_TYPE.") == 0) { |
|
329 |
content = StrUtils.removeLineContains(content, "DICT_TYPE"); |
|
330 |
} |
|
331 |
return content; |
|
332 |
} |
|
333 |
|
|
334 |
private Map<String, Object> initBindingMap(CodegenTableDO table, List<CodegenColumnDO> columns, |
|
335 |
List<CodegenTableDO> subTables, List<List<CodegenColumnDO>> subColumnsList) { |
|
336 |
// 创建 bindingMap |
|
337 |
Map<String, Object> bindingMap = new HashMap<>(globalBindingMap); |
|
338 |
bindingMap.put("table", table); |
|
339 |
bindingMap.put("columns", columns); |
|
340 |
bindingMap.put("primaryColumn", CollectionUtils.findFirst(columns, CodegenColumnDO::getPrimaryKey)); // 主键字段 |
|
341 |
bindingMap.put("sceneEnum", CodegenSceneEnum.valueOf(table.getScene())); |
|
342 |
|
|
343 |
// className 相关 |
|
344 |
// 去掉指定前缀,将 TestDictType 转换成 DictType. 因为在 create 等方法后,不需要带上 Test 前缀 |
|
345 |
String simpleClassName = removePrefix(table.getClassName(), upperFirst(table.getModuleName())); |
|
346 |
bindingMap.put("simpleClassName", simpleClassName); |
|
347 |
bindingMap.put("simpleClassName_underlineCase", toUnderlineCase(simpleClassName)); // 将 DictType 转换成 dict_type |
|
348 |
bindingMap.put("classNameVar", lowerFirst(simpleClassName)); // 将 DictType 转换成 dictType,用于变量 |
|
349 |
// 将 DictType 转换成 dict-type |
|
350 |
String simpleClassNameStrikeCase = toSymbolCase(simpleClassName, '-'); |
|
351 |
bindingMap.put("simpleClassName_strikeCase", simpleClassNameStrikeCase); |
|
352 |
// permission 前缀 |
|
353 |
bindingMap.put("permissionPrefix", table.getModuleName() + ":" + simpleClassNameStrikeCase); |
|
354 |
|
|
355 |
// 特殊:树表专属逻辑 |
|
356 |
if (CodegenTemplateTypeEnum.isTree(table.getTemplateType())) { |
|
357 |
CodegenColumnDO treeParentColumn = CollUtil.findOne(columns, |
|
358 |
column -> Objects.equals(column.getId(), table.getTreeParentColumnId())); |
|
359 |
bindingMap.put("treeParentColumn", treeParentColumn); |
|
360 |
bindingMap.put("treeParentColumn_javaField_underlineCase", toUnderlineCase(treeParentColumn.getJavaField())); |
|
361 |
CodegenColumnDO treeNameColumn = CollUtil.findOne(columns, |
|
362 |
column -> Objects.equals(column.getId(), table.getTreeNameColumnId())); |
|
363 |
bindingMap.put("treeNameColumn", treeNameColumn); |
|
364 |
bindingMap.put("treeNameColumn_javaField_underlineCase", toUnderlineCase(treeNameColumn.getJavaField())); |
|
365 |
} |
|
366 |
|
|
367 |
// 特殊:主子表专属逻辑 |
|
368 |
if (CollUtil.isNotEmpty(subTables)) { |
|
369 |
// 创建 bindingMap |
|
370 |
bindingMap.put("subTables", subTables); |
|
371 |
bindingMap.put("subColumnsList", subColumnsList); |
|
372 |
List<CodegenColumnDO> subPrimaryColumns = new ArrayList<>(); |
|
373 |
List<CodegenColumnDO> subJoinColumns = new ArrayList<>(); |
|
374 |
List<String> subJoinColumnStrikeCases = new ArrayList<>(); |
|
375 |
List<String> subSimpleClassNames = new ArrayList<>(); |
|
376 |
List<String> subClassNameVars = new ArrayList<>(); |
|
377 |
List<String> simpleClassNameUnderlineCases = new ArrayList<>(); |
|
378 |
List<String> subSimpleClassNameStrikeCases = new ArrayList<>(); |
|
379 |
for (int i = 0; i < subTables.size(); i++) { |
|
380 |
CodegenTableDO subTable = subTables.get(i); |
|
381 |
List<CodegenColumnDO> subColumns = subColumnsList.get(i); |
|
382 |
subPrimaryColumns.add(CollectionUtils.findFirst(subColumns, CodegenColumnDO::getPrimaryKey)); // |
|
383 |
CodegenColumnDO subColumn = CollectionUtils.findFirst(subColumns, // 关联的字段 |
|
384 |
column -> Objects.equals(column.getId(), subTable.getSubJoinColumnId())); |
|
385 |
subJoinColumns.add(subColumn); |
|
386 |
subJoinColumnStrikeCases.add(toSymbolCase(subColumn.getJavaField(), '-')); // 将 DictType 转换成 dict-type |
|
387 |
// className 相关 |
|
388 |
String subSimpleClassName = removePrefix(subTable.getClassName(), upperFirst(subTable.getModuleName())); |
|
389 |
subSimpleClassNames.add(subSimpleClassName); |
|
390 |
simpleClassNameUnderlineCases.add(toUnderlineCase(subSimpleClassName)); // 将 DictType 转换成 dict_type |
|
391 |
subClassNameVars.add(lowerFirst(subSimpleClassName)); // 将 DictType 转换成 dictType,用于变量 |
|
392 |
subSimpleClassNameStrikeCases.add(toSymbolCase(subSimpleClassName, '-')); // 将 DictType 转换成 dict-type |
|
393 |
} |
|
394 |
bindingMap.put("subPrimaryColumns", subPrimaryColumns); |
|
395 |
bindingMap.put("subJoinColumns", subJoinColumns); |
|
396 |
bindingMap.put("subJoinColumn_strikeCases", subJoinColumnStrikeCases); |
|
397 |
bindingMap.put("subSimpleClassNames", subSimpleClassNames); |
|
398 |
bindingMap.put("simpleClassNameUnderlineCases", simpleClassNameUnderlineCases); |
|
399 |
bindingMap.put("subClassNameVars", subClassNameVars); |
|
400 |
bindingMap.put("subSimpleClassName_strikeCases", subSimpleClassNameStrikeCases); |
|
401 |
} |
|
402 |
return bindingMap; |
|
403 |
} |
|
404 |
|
|
405 |
private Map<String, String> getTemplates(Integer frontType) { |
|
406 |
Map<String, String> templates = new LinkedHashMap<>(); |
|
407 |
templates.putAll(SERVER_TEMPLATES); |
|
408 |
templates.putAll(FRONT_TEMPLATES.row(frontType)); |
|
409 |
return templates; |
|
410 |
} |
|
411 |
|
|
412 |
@SuppressWarnings("unchecked") |
|
413 |
private String formatFilePath(String filePath, Map<String, Object> bindingMap) { |
|
414 |
filePath = StrUtil.replace(filePath, "${basePackage}", |
|
415 |
getStr(bindingMap, "basePackage").replaceAll("\\.", "/")); |
|
416 |
filePath = StrUtil.replace(filePath, "${classNameVar}", |
|
417 |
getStr(bindingMap, "classNameVar")); |
|
418 |
filePath = StrUtil.replace(filePath, "${simpleClassName}", |
|
419 |
getStr(bindingMap, "simpleClassName")); |
|
420 |
// sceneEnum 包含的字段 |
|
421 |
CodegenSceneEnum sceneEnum = (CodegenSceneEnum) bindingMap.get("sceneEnum"); |
|
422 |
filePath = StrUtil.replace(filePath, "${sceneEnum.prefixClass}", sceneEnum.getPrefixClass()); |
|
423 |
filePath = StrUtil.replace(filePath, "${sceneEnum.basePackage}", sceneEnum.getBasePackage()); |
|
424 |
// table 包含的字段 |
|
425 |
CodegenTableDO table = (CodegenTableDO) bindingMap.get("table"); |
|
426 |
filePath = StrUtil.replace(filePath, "${table.moduleName}", table.getModuleName()); |
|
427 |
filePath = StrUtil.replace(filePath, "${table.businessName}", table.getBusinessName()); |
|
428 |
filePath = StrUtil.replace(filePath, "${table.className}", table.getClassName()); |
|
429 |
// 特殊:主子表专属逻辑 |
|
430 |
Integer subIndex = (Integer) bindingMap.get("subIndex"); |
|
431 |
if (subIndex != null) { |
|
432 |
CodegenTableDO subTable = ((List<CodegenTableDO>) bindingMap.get("subTables")).get(subIndex); |
|
433 |
filePath = StrUtil.replace(filePath, "${subTable.moduleName}", subTable.getModuleName()); |
|
434 |
filePath = StrUtil.replace(filePath, "${subTable.businessName}", subTable.getBusinessName()); |
|
435 |
filePath = StrUtil.replace(filePath, "${subTable.className}", subTable.getClassName()); |
|
436 |
filePath = StrUtil.replace(filePath, "${subSimpleClassName}", |
|
437 |
((List<String>) bindingMap.get("subSimpleClassNames")).get(subIndex)); |
|
438 |
} |
|
439 |
return filePath; |
|
440 |
} |
|
441 |
|
|
442 |
private static String javaTemplatePath(String path) { |
|
443 |
return "codegen/java/" + path + ".vm"; |
|
444 |
} |
|
445 |
|
|
446 |
private static String javaModuleImplVOFilePath(String path) { |
|
447 |
return javaModuleFilePath("controller/${sceneEnum.basePackage}/${table.businessName}/" + |
|
448 |
"vo/${sceneEnum.prefixClass}${table.className}" + path, "biz", "main"); |
|
449 |
} |
|
450 |
|
|
451 |
private static String javaModuleImplControllerFilePath() { |
|
452 |
return javaModuleFilePath("controller/${sceneEnum.basePackage}/${table.businessName}/" + |
|
453 |
"${sceneEnum.prefixClass}${table.className}Controller", "biz", "main"); |
|
454 |
} |
|
455 |
|
|
456 |
private static String javaModuleImplMainFilePath(String path) { |
|
457 |
return javaModuleFilePath(path, "biz", "main"); |
|
458 |
} |
|
459 |
|
|
460 |
private static String javaModuleApiMainFilePath(String path) { |
|
461 |
return javaModuleFilePath(path, "api", "main"); |
|
462 |
} |
|
463 |
|
|
464 |
private static String javaModuleImplTestFilePath(String path) { |
|
465 |
return javaModuleFilePath(path, "biz", "test"); |
|
466 |
} |
|
467 |
|
|
468 |
private static String javaModuleFilePath(String path, String module, String src) { |
|
469 |
return "iailab-module-${table.moduleName}/" + // 顶级模块 |
|
470 |
"iailab-module-${table.moduleName}-" + module + "/" + // 子模块 |
|
471 |
"src/" + src + "/java/${basePackage}/module/${table.moduleName}/" + path + ".java"; |
|
472 |
} |
|
473 |
|
|
474 |
private static String mapperXmlFilePath() { |
|
475 |
return "iailab-module-${table.moduleName}/" + // 顶级模块 |
|
476 |
"iailab-module-${table.moduleName}-biz/" + // 子模块 |
|
477 |
"src/main/resources/mapper/${table.businessName}/${table.className}Mapper.xml"; |
|
478 |
} |
|
479 |
|
|
480 |
private static String vueTemplatePath(String path) { |
|
481 |
return "codegen/vue/" + path + ".vm"; |
|
482 |
} |
|
483 |
|
|
484 |
private static String vueFilePath(String path) { |
|
485 |
return "iailab-ui-${sceneEnum.basePackage}-vue2/" + // 顶级目录 |
|
486 |
"src/" + path; |
|
487 |
} |
|
488 |
|
|
489 |
private static String vue3TemplatePath(String path) { |
|
490 |
return "codegen/vue3/" + path + ".vm"; |
|
491 |
} |
|
492 |
|
|
493 |
private static String vue3FilePath(String path) { |
|
494 |
return "iailab-ui-${sceneEnum.basePackage}-vue3/" + // 顶级目录 |
|
495 |
"src/" + path; |
|
496 |
} |
|
497 |
|
|
498 |
private static String vue3SchemaTemplatePath(String path) { |
|
499 |
return "codegen/vue3_schema/" + path + ".vm"; |
|
500 |
} |
|
501 |
|
|
502 |
private static String vue3VbenTemplatePath(String path) { |
|
503 |
return "codegen/vue3_vben/" + path + ".vm"; |
|
504 |
} |
|
505 |
|
|
506 |
private static boolean isSubTemplate(String path) { |
|
507 |
return path.contains("_sub"); |
|
508 |
} |
|
509 |
|
|
510 |
private static boolean isPageReqVOTemplate(String path) { |
|
511 |
return path.contains("pageReqVO"); |
|
512 |
} |
|
513 |
|
|
514 |
private static boolean isListReqVOTemplate(String path) { |
|
515 |
return path.contains("listReqVO"); |
|
516 |
} |
|
517 |
|
|
518 |
} |