提交 | 用户 | 时间
|
e7c126
|
1 |
package com.iailab.module.infra.service.codegen.inner; |
H |
2 |
|
|
3 |
import cn.hutool.core.map.MapUtil; |
|
4 |
import cn.hutool.core.util.ReflectUtil; |
|
5 |
import cn.hutool.core.util.StrUtil; |
|
6 |
import com.iailab.framework.mybatis.core.dataobject.BaseDO; |
|
7 |
import com.iailab.module.infra.convert.codegen.CodegenConvert; |
|
8 |
import com.iailab.module.infra.dal.dataobject.codegen.CodegenColumnDO; |
|
9 |
import com.iailab.module.infra.dal.dataobject.codegen.CodegenTableDO; |
|
10 |
import com.iailab.module.infra.enums.codegen.CodegenColumnHtmlTypeEnum; |
|
11 |
import com.iailab.module.infra.enums.codegen.CodegenColumnListConditionEnum; |
|
12 |
import com.iailab.module.infra.enums.codegen.CodegenTemplateTypeEnum; |
|
13 |
import com.baomidou.mybatisplus.generator.config.po.TableField; |
|
14 |
import com.baomidou.mybatisplus.generator.config.po.TableInfo; |
|
15 |
import com.google.common.collect.Sets; |
|
16 |
import org.springframework.stereotype.Component; |
|
17 |
|
|
18 |
import java.time.LocalDateTime; |
|
19 |
import java.util.*; |
|
20 |
|
|
21 |
import static cn.hutool.core.text.CharSequenceUtil.*; |
|
22 |
import static cn.hutool.core.util.RandomUtil.randomEle; |
|
23 |
import static cn.hutool.core.util.RandomUtil.randomInt; |
|
24 |
|
|
25 |
/** |
|
26 |
* 代码生成器的 Builder,负责: |
|
27 |
* 1. 将数据库的表 {@link TableInfo} 定义,构建成 {@link CodegenTableDO} |
|
28 |
* 2. 将数据库的列 {@link TableField} 构定义,建成 {@link CodegenColumnDO} |
|
29 |
*/ |
|
30 |
@Component |
|
31 |
public class CodegenBuilder { |
|
32 |
|
|
33 |
/** |
|
34 |
* 字段名与 {@link CodegenColumnListConditionEnum} 的默认映射 |
|
35 |
* 注意,字段的匹配以后缀的方式 |
|
36 |
*/ |
|
37 |
private static final Map<String, CodegenColumnListConditionEnum> COLUMN_LIST_OPERATION_CONDITION_MAPPINGS = |
|
38 |
MapUtil.<String, CodegenColumnListConditionEnum>builder() |
|
39 |
.put("name", CodegenColumnListConditionEnum.LIKE) |
|
40 |
.put("time", CodegenColumnListConditionEnum.BETWEEN) |
|
41 |
.put("date", CodegenColumnListConditionEnum.BETWEEN) |
|
42 |
.build(); |
|
43 |
|
|
44 |
/** |
|
45 |
* 字段名与 {@link CodegenColumnHtmlTypeEnum} 的默认映射 |
|
46 |
* 注意,字段的匹配以后缀的方式 |
|
47 |
*/ |
|
48 |
private static final Map<String, CodegenColumnHtmlTypeEnum> COLUMN_HTML_TYPE_MAPPINGS = |
|
49 |
MapUtil.<String, CodegenColumnHtmlTypeEnum>builder() |
|
50 |
.put("status", CodegenColumnHtmlTypeEnum.RADIO) |
|
51 |
.put("sex", CodegenColumnHtmlTypeEnum.RADIO) |
|
52 |
.put("type", CodegenColumnHtmlTypeEnum.SELECT) |
|
53 |
.put("image", CodegenColumnHtmlTypeEnum.IMAGE_UPLOAD) |
|
54 |
.put("file", CodegenColumnHtmlTypeEnum.FILE_UPLOAD) |
|
55 |
.put("content", CodegenColumnHtmlTypeEnum.EDITOR) |
|
56 |
.put("description", CodegenColumnHtmlTypeEnum.EDITOR) |
|
57 |
.put("demo", CodegenColumnHtmlTypeEnum.EDITOR) |
|
58 |
.put("time", CodegenColumnHtmlTypeEnum.DATETIME) |
|
59 |
.put("date", CodegenColumnHtmlTypeEnum.DATETIME) |
|
60 |
.build(); |
|
61 |
|
|
62 |
/** |
|
63 |
* 多租户编号的字段名 |
|
64 |
*/ |
|
65 |
public static final String TENANT_ID_FIELD = "tenantId"; |
|
66 |
/** |
|
67 |
* {@link com.iailab.framework.mybatis.core.dataobject.BaseDO} 的字段 |
|
68 |
*/ |
|
69 |
public static final Set<String> BASE_DO_FIELDS = new HashSet<>(); |
|
70 |
/** |
|
71 |
* 新增操作,不需要传递的字段 |
|
72 |
*/ |
|
73 |
private static final Set<String> CREATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id"); |
|
74 |
/** |
|
75 |
* 修改操作,不需要传递的字段 |
|
76 |
*/ |
|
77 |
private static final Set<String> UPDATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet(); |
|
78 |
/** |
|
79 |
* 列表操作的条件,不需要传递的字段 |
|
80 |
*/ |
|
81 |
private static final Set<String> LIST_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id"); |
|
82 |
/** |
|
83 |
* 列表操作的结果,不需要返回的字段 |
|
84 |
*/ |
|
85 |
private static final Set<String> LIST_OPERATION_RESULT_EXCLUDE_COLUMN = Sets.newHashSet(); |
|
86 |
|
|
87 |
static { |
|
88 |
Arrays.stream(ReflectUtil.getFields(BaseDO.class)).forEach(field -> BASE_DO_FIELDS.add(field.getName())); |
|
89 |
BASE_DO_FIELDS.add(TENANT_ID_FIELD); |
|
90 |
// 处理 OPERATION 相关的字段 |
|
91 |
CREATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS); |
|
92 |
UPDATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS); |
|
93 |
LIST_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS); |
|
94 |
LIST_OPERATION_EXCLUDE_COLUMN.remove("createTime"); // 创建时间,还是可能需要传递的 |
|
95 |
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS); |
|
96 |
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.remove("createTime"); // 创建时间,还是需要返回的 |
|
97 |
} |
|
98 |
|
|
99 |
public CodegenTableDO buildTable(TableInfo tableInfo) { |
|
100 |
CodegenTableDO table = CodegenConvert.INSTANCE.convert(tableInfo); |
|
101 |
initTableDefault(table); |
|
102 |
return table; |
|
103 |
} |
|
104 |
|
|
105 |
/** |
|
106 |
* 初始化 Table 表的默认字段 |
|
107 |
* |
|
108 |
* @param table 表定义 |
|
109 |
*/ |
|
110 |
private void initTableDefault(CodegenTableDO table) { |
|
111 |
// 以 system_dept 举例子。moduleName 为 system、businessName 为 dept、className 为 Dept |
|
112 |
// 如果希望以 System 前缀,则可以手动在【代码生成 - 修改生成配置 - 基本信息】,将实体类名称改为 SystemDept 即可 |
|
113 |
String tableName = table.getTableName().toLowerCase(); |
|
114 |
// 第一步,_ 前缀的前面,作为 module 名字;第二步,moduleName 必须小写; |
|
115 |
table.setModuleName(subBefore(tableName, '_', false).toLowerCase()); |
|
116 |
// 第一步,第一个 _ 前缀的后面,作为 module 名字; 第二步,可能存在多个 _ 的情况,转换成驼峰; 第三步,businessName 必须小写; |
|
117 |
table.setBusinessName(toCamelCase(subAfter(tableName, '_', false)).toLowerCase()); |
|
118 |
// 驼峰 + 首字母大写;第一步,第一个 _ 前缀的后面,作为 class 名字;第二步,驼峰命名 |
|
119 |
table.setClassName(upperFirst(toCamelCase(subAfter(tableName, '_', false)))); |
|
120 |
// 去除结尾的表,作为类描述 |
|
121 |
table.setClassComment(StrUtil.removeSuffixIgnoreCase(table.getTableComment(), "表")); |
|
122 |
table.setTemplateType(CodegenTemplateTypeEnum.ONE.getType()); |
|
123 |
} |
|
124 |
|
|
125 |
public List<CodegenColumnDO> buildColumns(Long tableId, List<TableField> tableFields) { |
|
126 |
List<CodegenColumnDO> columns = CodegenConvert.INSTANCE.convertList(tableFields); |
|
127 |
int index = 1; |
|
128 |
for (CodegenColumnDO column : columns) { |
|
129 |
column.setTableId(tableId); |
|
130 |
column.setOrdinalPosition(index++); |
|
131 |
// 特殊处理:Byte => Integer |
|
132 |
if (Byte.class.getSimpleName().equals(column.getJavaType())) { |
|
133 |
column.setJavaType(Integer.class.getSimpleName()); |
|
134 |
} |
|
135 |
// 初始化 Column 列的默认字段 |
|
136 |
processColumnOperation(column); // 处理 CRUD 相关的字段的默认值 |
|
137 |
processColumnUI(column); // 处理 UI 相关的字段的默认值 |
|
138 |
processColumnExample(column); // 处理字段的 swagger example 示例 |
|
139 |
} |
|
140 |
return columns; |
|
141 |
} |
|
142 |
|
|
143 |
private void processColumnOperation(CodegenColumnDO column) { |
|
144 |
// 处理 createOperation 字段 |
|
145 |
column.setCreateOperation(!CREATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField()) |
|
146 |
&& !column.getPrimaryKey()); // 对于主键,创建时无需传递 |
|
147 |
// 处理 updateOperation 字段 |
|
148 |
column.setUpdateOperation(!UPDATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField()) |
|
149 |
|| column.getPrimaryKey()); // 对于主键,更新时需要传递 |
|
150 |
// 处理 listOperation 字段 |
|
151 |
column.setListOperation(!LIST_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField()) |
|
152 |
&& !column.getPrimaryKey()); // 对于主键,列表过滤不需要传递 |
|
153 |
// 处理 listOperationCondition 字段 |
|
154 |
COLUMN_LIST_OPERATION_CONDITION_MAPPINGS.entrySet().stream() |
|
155 |
.filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey())) |
|
156 |
.findFirst().ifPresent(entry -> column.setListOperationCondition(entry.getValue().getCondition())); |
|
157 |
if (column.getListOperationCondition() == null) { |
|
158 |
column.setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition()); |
|
159 |
} |
|
160 |
// 处理 listOperationResult 字段 |
|
161 |
column.setListOperationResult(!LIST_OPERATION_RESULT_EXCLUDE_COLUMN.contains(column.getJavaField())); |
|
162 |
} |
|
163 |
|
|
164 |
private void processColumnUI(CodegenColumnDO column) { |
|
165 |
// 基于后缀进行匹配 |
|
166 |
COLUMN_HTML_TYPE_MAPPINGS.entrySet().stream() |
|
167 |
.filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey())) |
|
168 |
.findFirst().ifPresent(entry -> column.setHtmlType(entry.getValue().getType())); |
|
169 |
// 如果是 Boolean 类型时,设置为 radio 类型. |
|
170 |
if (Boolean.class.getSimpleName().equals(column.getJavaType())) { |
|
171 |
column.setHtmlType(CodegenColumnHtmlTypeEnum.RADIO.getType()); |
|
172 |
} |
|
173 |
// 如果是 LocalDateTime 类型,则设置为 datetime 类型 |
|
174 |
if (LocalDateTime.class.getSimpleName().equals(column.getJavaType())) { |
|
175 |
column.setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType()); |
|
176 |
} |
|
177 |
// 兜底,设置默认为 input 类型 |
|
178 |
if (column.getHtmlType() == null) { |
|
179 |
column.setHtmlType(CodegenColumnHtmlTypeEnum.INPUT.getType()); |
|
180 |
} |
|
181 |
} |
|
182 |
|
|
183 |
/** |
|
184 |
* 处理字段的 swagger example 示例 |
|
185 |
* |
|
186 |
* @param column 字段 |
|
187 |
*/ |
|
188 |
private void processColumnExample(CodegenColumnDO column) { |
|
189 |
// id、price、count 等可能是整数的后缀 |
|
190 |
if (StrUtil.endWithAnyIgnoreCase(column.getJavaField(), "id", "price", "count")) { |
|
191 |
column.setExample(String.valueOf(randomInt(1, Short.MAX_VALUE))); |
|
192 |
return; |
|
193 |
} |
|
194 |
// name |
|
195 |
if (StrUtil.endWithIgnoreCase(column.getJavaField(), "name")) { |
|
196 |
column.setExample(randomEle(new String[]{"张三", "李四", "王五", "赵六", "iailab"})); |
|
197 |
return; |
|
198 |
} |
|
199 |
// status |
|
200 |
if (StrUtil.endWithAnyIgnoreCase(column.getJavaField(), "status", "type")) { |
|
201 |
column.setExample(randomEle(new String[]{"1", "2"})); |
|
202 |
return; |
|
203 |
} |
|
204 |
// url |
|
205 |
if (StrUtil.endWithIgnoreCase(column.getColumnName(), "url")) { |
|
206 |
column.setExample("https://www.baidu.com"); |
|
207 |
return; |
|
208 |
} |
|
209 |
// reason |
|
210 |
if (StrUtil.endWithIgnoreCase(column.getColumnName(), "reason")) { |
|
211 |
column.setExample(randomEle(new String[]{"不喜欢", "不对", "不好", "不香"})); |
|
212 |
return; |
|
213 |
} |
|
214 |
// description、memo、remark |
|
215 |
if (StrUtil.endWithAnyIgnoreCase(column.getColumnName(), "description", "memo", "remark")) { |
|
216 |
column.setExample(randomEle(new String[]{"你猜", "随便", "你说的对"})); |
|
217 |
return; |
|
218 |
} |
|
219 |
} |
|
220 |
|
|
221 |
} |