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