提交 | 用户 | 时间
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 }