提交 | 用户 | 时间
e7c126 1 package com.iailab.module.infra.service.codegen;
H 2
3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.StrUtil;
5 import com.iailab.framework.common.pojo.PageResult;
6 import com.iailab.framework.common.util.object.BeanUtils;
7 import com.iailab.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
8 import com.iailab.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
9 import com.iailab.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
10 import com.iailab.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
11 import com.iailab.module.infra.dal.dataobject.codegen.CodegenColumnDO;
12 import com.iailab.module.infra.dal.dataobject.codegen.CodegenTableDO;
13 import com.iailab.module.infra.dal.mysql.codegen.CodegenColumnMapper;
14 import com.iailab.module.infra.dal.mysql.codegen.CodegenTableMapper;
15 import com.iailab.module.infra.enums.codegen.CodegenSceneEnum;
16 import com.iailab.module.infra.enums.codegen.CodegenTemplateTypeEnum;
17 import com.iailab.module.infra.framework.codegen.config.CodegenProperties;
18 import com.iailab.module.infra.service.codegen.inner.CodegenBuilder;
19 import com.iailab.module.infra.service.codegen.inner.CodegenEngine;
20 import com.iailab.module.infra.service.db.DatabaseTableService;
21 import com.iailab.module.system.api.user.AdminUserApi;
22 import com.baomidou.mybatisplus.generator.config.po.TableField;
23 import com.baomidou.mybatisplus.generator.config.po.TableInfo;
24 import com.google.common.annotations.VisibleForTesting;
25 import org.springframework.stereotype.Service;
26 import org.springframework.transaction.annotation.Transactional;
27
28 import javax.annotation.Resource;
29 import java.util.*;
30 import java.util.function.BiPredicate;
31 import java.util.stream.Collectors;
32
33 import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
34 import static com.iailab.framework.common.util.collection.CollectionUtils.convertMap;
35 import static com.iailab.framework.common.util.collection.CollectionUtils.convertSet;
36 import static com.iailab.module.infra.enums.ErrorCodeConstants.*;
37
38 /**
39  * 代码生成 Service 实现类
40  *
41  * @author iailab
42  */
43 @Service
44 public class CodegenServiceImpl implements CodegenService {
45
46     @Resource
47     private DatabaseTableService databaseTableService;
48
49     @Resource
50     private CodegenTableMapper codegenTableMapper;
51     @Resource
52     private CodegenColumnMapper codegenColumnMapper;
53
54     @Resource
55     private AdminUserApi userApi;
56
57     @Resource
58     private CodegenBuilder codegenBuilder;
59     @Resource
60     private CodegenEngine codegenEngine;
61
62     @Resource
63     private CodegenProperties codegenProperties;
64
65     @Override
66     @Transactional(rollbackFor = Exception.class)
67     public List<Long> createCodegenList(Long userId, CodegenCreateListReqVO reqVO) {
68         List<Long> ids = new ArrayList<>(reqVO.getTableNames().size());
69         // 遍历添加。虽然效率会低一点,但是没必要做成完全批量,因为不会这么大量
70         reqVO.getTableNames().forEach(tableName -> ids.add(createCodegen(userId, reqVO.getDataSourceConfigId(), tableName)));
71         return ids;
72     }
73
74     private Long createCodegen(Long userId, Long dataSourceConfigId, String tableName) {
75         // 从数据库中,获得数据库表结构
76         TableInfo tableInfo = databaseTableService.getTable(dataSourceConfigId, tableName);
77         // 导入
78         return createCodegen0(userId, dataSourceConfigId, tableInfo);
79     }
80
81     private Long createCodegen0(Long userId, Long dataSourceConfigId, TableInfo tableInfo) {
82         // 校验导入的表和字段非空
83         validateTableInfo(tableInfo);
84         // 校验是否已经存在
85         if (codegenTableMapper.selectByTableNameAndDataSourceConfigId(tableInfo.getName(),
86                 dataSourceConfigId) != null) {
87             throw exception(CODEGEN_TABLE_EXISTS);
88         }
89
90         // 构建 CodegenTableDO 对象,插入到 DB 中
91         CodegenTableDO table = codegenBuilder.buildTable(tableInfo);
92         table.setDataSourceConfigId(dataSourceConfigId);
93         table.setScene(CodegenSceneEnum.ADMIN.getScene()); // 默认配置下,使用管理后台的模板
94         table.setFrontType(codegenProperties.getFrontType());
95         table.setAuthor(userApi.getUser(userId).getCheckedData().getNickname());
96         codegenTableMapper.insert(table);
97
98         // 构建 CodegenColumnDO 数组,插入到 DB 中
99         List<CodegenColumnDO> columns = codegenBuilder.buildColumns(table.getId(), tableInfo.getFields());
100         // 如果没有主键,则使用第一个字段作为主键
101         if (!tableInfo.isHavePrimaryKey()) {
102             columns.get(0).setPrimaryKey(true);
103         }
104         codegenColumnMapper.insertBatch(columns);
105         return table.getId();
106     }
107
108     @VisibleForTesting
109     void validateTableInfo(TableInfo tableInfo) {
110         if (tableInfo == null) {
111             throw exception(CODEGEN_IMPORT_TABLE_NULL);
112         }
113         if (StrUtil.isEmpty(tableInfo.getComment())) {
114             throw exception(CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL);
115         }
116         if (CollUtil.isEmpty(tableInfo.getFields())) {
117             throw exception(CODEGEN_IMPORT_COLUMNS_NULL);
118         }
119         tableInfo.getFields().forEach(field -> {
120             if (StrUtil.isEmpty(field.getComment())) {
121                 throw exception(CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL, field.getName());
122             }
123         });
124     }
125
126     @Override
127     @Transactional(rollbackFor = Exception.class)
128     public void updateCodegen(CodegenUpdateReqVO updateReqVO) {
129         // 校验是否已经存在
130         if (codegenTableMapper.selectById(updateReqVO.getTable().getId()) == null) {
131             throw exception(CODEGEN_TABLE_NOT_EXISTS);
132         }
133         // 校验主表字段存在
134         if (Objects.equals(updateReqVO.getTable().getTemplateType(), CodegenTemplateTypeEnum.SUB.getType())) {
135             if (codegenTableMapper.selectById(updateReqVO.getTable().getMasterTableId()) == null) {
136                 throw exception(CODEGEN_MASTER_TABLE_NOT_EXISTS, updateReqVO.getTable().getMasterTableId());
137             }
138             if (CollUtil.findOne(updateReqVO.getColumns(),  // 关联主表的字段不存在
139                     column -> column.getId().equals(updateReqVO.getTable().getSubJoinColumnId())) == null) {
140                 throw exception(CODEGEN_SUB_COLUMN_NOT_EXISTS, updateReqVO.getTable().getSubJoinColumnId());
141             }
142         }
143
144         // 更新 table 表定义
145         CodegenTableDO updateTableObj = BeanUtils.toBean(updateReqVO.getTable(), CodegenTableDO.class);
146         codegenTableMapper.updateById(updateTableObj);
147         // 更新 column 字段定义
148         List<CodegenColumnDO> updateColumnObjs = BeanUtils.toBean(updateReqVO.getColumns(), CodegenColumnDO.class);
149         updateColumnObjs.forEach(updateColumnObj -> codegenColumnMapper.updateById(updateColumnObj));
150     }
151
152     @Override
153     @Transactional(rollbackFor = Exception.class)
154     public void syncCodegenFromDB(Long tableId) {
155         // 校验是否已经存在
156         CodegenTableDO table = codegenTableMapper.selectById(tableId);
157         if (table == null) {
158             throw exception(CODEGEN_TABLE_NOT_EXISTS);
159         }
160         // 从数据库中,获得数据库表结构
161         TableInfo tableInfo = databaseTableService.getTable(table.getDataSourceConfigId(), table.getTableName());
162         // 执行同步
163         syncCodegen0(tableId, tableInfo);
164     }
165
166     private void syncCodegen0(Long tableId, TableInfo tableInfo) {
167         // 1. 校验导入的表和字段非空
168         validateTableInfo(tableInfo);
169         List<TableField> tableFields = tableInfo.getFields();
170
171         // 2. 构建 CodegenColumnDO 数组,只同步新增的字段
172         List<CodegenColumnDO> codegenColumns = codegenColumnMapper.selectListByTableId(tableId);
173         Set<String> codegenColumnNames = convertSet(codegenColumns, CodegenColumnDO::getColumnName);
174
175         // 3.1 计算需要【修改】的字段,插入时重新插入,删除时将原来的删除
176         Map<String, CodegenColumnDO> codegenColumnDOMap = convertMap(codegenColumns, CodegenColumnDO::getColumnName);
177         BiPredicate<TableField, CodegenColumnDO> primaryKeyPredicate =
178                 (tableField, codegenColumn) -> tableField.getMetaInfo().getJdbcType().name().equals(codegenColumn.getDataType())
179                         && tableField.getMetaInfo().isNullable() == codegenColumn.getNullable()
180                         && tableField.isKeyFlag() == codegenColumn.getPrimaryKey()
181                         && tableField.getComment().equals(codegenColumn.getColumnComment());
182         Set<String> modifyFieldNames = tableFields.stream()
183                 .filter(tableField -> codegenColumnDOMap.get(tableField.getColumnName()) != null
184                         && !primaryKeyPredicate.test(tableField, codegenColumnDOMap.get(tableField.getColumnName())))
185                 .map(TableField::getColumnName)
186                 .collect(Collectors.toSet());
187         // 3.2 计算需要【删除】的字段
188         Set<String> tableFieldNames = convertSet(tableFields, TableField::getName);
189         Set<Long> deleteColumnIds = codegenColumns.stream()
190                 .filter(column -> (!tableFieldNames.contains(column.getColumnName())) || modifyFieldNames.contains(column.getColumnName()))
191                 .map(CodegenColumnDO::getId).collect(Collectors.toSet());
192         // 移除已经存在的字段
193         tableFields.removeIf(column -> codegenColumnNames.contains(column.getColumnName()) && (!modifyFieldNames.contains(column.getColumnName())));
194         if (CollUtil.isEmpty(tableFields) && CollUtil.isEmpty(deleteColumnIds)) {
195             throw exception(CODEGEN_SYNC_NONE_CHANGE);
196         }
197
198         // 4.1 插入新增的字段
199         List<CodegenColumnDO> columns = codegenBuilder.buildColumns(tableId, tableFields);
200         codegenColumnMapper.insertBatch(columns);
201         // 4.2 删除不存在的字段
202         if (CollUtil.isNotEmpty(deleteColumnIds)) {
203             codegenColumnMapper.deleteBatchIds(deleteColumnIds);
204         }
205     }
206
207     @Override
208     @Transactional(rollbackFor = Exception.class)
209     public void deleteCodegen(Long tableId) {
210         // 校验是否已经存在
211         if (codegenTableMapper.selectById(tableId) == null) {
212             throw exception(CODEGEN_TABLE_NOT_EXISTS);
213         }
214
215         // 删除 table 表定义
216         codegenTableMapper.deleteById(tableId);
217         // 删除 column 字段定义
218         codegenColumnMapper.deleteListByTableId(tableId);
219     }
220
221     @Override
222     public List<CodegenTableDO> getCodegenTableList(Long dataSourceConfigId) {
223         return codegenTableMapper.selectListByDataSourceConfigId(dataSourceConfigId);
224     }
225
226     @Override
227     public PageResult<CodegenTableDO> getCodegenTablePage(CodegenTablePageReqVO pageReqVO) {
228         return codegenTableMapper.selectPage(pageReqVO);
229     }
230
231     @Override
232     public CodegenTableDO getCodegenTable(Long id) {
233         return codegenTableMapper.selectById(id);
234     }
235
236     @Override
237     public List<CodegenColumnDO> getCodegenColumnListByTableId(Long tableId) {
238         return codegenColumnMapper.selectListByTableId(tableId);
239     }
240
241     @Override
242     public Map<String, String> generationCodes(Long tableId) {
243         // 校验是否已经存在
244         CodegenTableDO table = codegenTableMapper.selectById(tableId);
245         if (table == null) {
246             throw exception(CODEGEN_TABLE_NOT_EXISTS);
247         }
248         List<CodegenColumnDO> columns = codegenColumnMapper.selectListByTableId(tableId);
249         if (CollUtil.isEmpty(columns)) {
250             throw exception(CODEGEN_COLUMN_NOT_EXISTS);
251         }
252
253         // 如果是主子表,则加载对应的子表信息
254         List<CodegenTableDO> subTables = null;
255         List<List<CodegenColumnDO>> subColumnsList = null;
256         if (CodegenTemplateTypeEnum.isMaster(table.getTemplateType())) {
257             // 校验子表存在
258             subTables = codegenTableMapper.selectListByTemplateTypeAndMasterTableId(
259                     CodegenTemplateTypeEnum.SUB.getType(), tableId);
260             if (CollUtil.isEmpty(subTables)) {
261                 throw exception(CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE);
262             }
263             // 校验子表的关联字段存在
264             subColumnsList = new ArrayList<>();
265             for (CodegenTableDO subTable : subTables) {
266                 List<CodegenColumnDO> subColumns = codegenColumnMapper.selectListByTableId(subTable.getId());
267                 if (CollUtil.findOne(subColumns, column -> column.getId().equals(subTable.getSubJoinColumnId())) == null) {
268                     throw exception(CODEGEN_SUB_COLUMN_NOT_EXISTS, subTable.getId());
269                 }
270                 subColumnsList.add(subColumns);
271             }
272         }
273
274         // 执行生成
275         return codegenEngine.execute(table, columns, subTables, subColumnsList);
276     }
277
278     @Override
279     public List<DatabaseTableRespVO> getDatabaseTableList(Long dataSourceConfigId, String name, String comment) {
280         List<TableInfo> tables = databaseTableService.getTableList(dataSourceConfigId, name, comment);
281         // 移除在 Codegen 中,已经存在的
282         Set<String> existsTables = convertSet(
283                 codegenTableMapper.selectListByDataSourceConfigId(dataSourceConfigId), CodegenTableDO::getTableName);
284         tables.removeIf(table -> existsTables.contains(table.getName()));
285         return BeanUtils.toBean(tables, DatabaseTableRespVO.class);
286     }
287
288 }