潘志宝
2024-12-11 b9a19ac538b7750fe6a042a99e57e23fa4c9580d
提交 | 用户 | 时间
449017 1 package com.iailab.module.model.mpk.service.impl;
D 2
3 import cn.hutool.core.io.FileUtil;
4 import cn.hutool.core.util.RuntimeUtil;
5 import cn.hutool.core.util.ZipUtil;
6 import com.alibaba.fastjson.JSON;
8b3ee3 7 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
449017 8 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
D 9 import com.baomidou.mybatisplus.core.metadata.IPage;
10 import com.iailab.framework.common.page.PageData;
c12dae 11 import com.iailab.framework.common.pojo.CommonResult;
449017 12 import com.iailab.framework.common.service.impl.BaseServiceImpl;
D 13 import com.iailab.framework.common.util.date.DateUtils;
14 import com.iailab.framework.common.util.object.ConvertUtils;
15 import com.iailab.framework.security.core.util.SecurityFrameworkUtils;
558ffc 16 import com.iailab.framework.tenant.core.context.TenantContextHolder;
449017 17 import com.iailab.module.infra.api.config.ConfigApi;
D 18 import com.iailab.module.model.mpk.common.MdkConstant;
c12dae 19 import com.iailab.module.model.mpk.common.utils.DllUtils;
449017 20 import com.iailab.module.model.mpk.common.utils.GenUtils;
D 21 import com.iailab.module.model.mpk.dao.MpkFileDao;
1fea3e 22 import com.iailab.module.model.mpk.dto.GeneratorCodeHistoryDTO;
0425a3 23 import com.iailab.module.model.mpk.dto.MethodSettingDTO;
1fea3e 24 import com.iailab.module.model.mpk.dto.ModelMethodDTO;
D 25 import com.iailab.module.model.mpk.dto.MpkFileDTO;
26 import com.iailab.module.model.mpk.entity.GeneratorCodeHistoryEntity;
27 import com.iailab.module.model.mpk.entity.MpkFileEntity;
558ffc 28 import com.iailab.module.model.mpk.entity.ProjectPackageHistoryEntity;
1fea3e 29 import com.iailab.module.model.mpk.entity.ProjectPackageHistoryModelEntity;
449017 30 import com.iailab.module.model.mpk.service.*;
D 31 import lombok.extern.slf4j.Slf4j;
32 import org.apache.commons.io.FileUtils;
33 import org.apache.commons.lang3.StringUtils;
34 import org.apache.velocity.VelocityContext;
35 import org.apache.velocity.app.Velocity;
36 import org.springframework.beans.factory.annotation.Autowired;
a27351 37 import org.springframework.beans.factory.annotation.Value;
449017 38 import org.springframework.stereotype.Service;
D 39 import org.springframework.util.CollectionUtils;
40 import org.springframework.web.multipart.MultipartFile;
41
f7932f 42 import java.io.*;
c12dae 43 import java.net.URLClassLoader;
912a1e 44 import java.nio.file.Files;
449017 45 import java.util.*;
D 46 import java.util.stream.Collectors;
47
48 /**
49  * @author PanZhibao
50  * @Description
51  * @createTime 2024年08月14日
52  */
53 @Slf4j
54 @Service
0255ea 55 public class MpkFileServiceImpl extends BaseServiceImpl<MpkFileDao, MpkFileEntity> implements MpkFileService {
449017 56
D 57     @Autowired
58     private GeneratorCodeHistoryService generatorCodeHistoryService;
8b3ee3 59
449017 60     @Autowired
D 61     private ProjectPackageHistoryService projectPackageHistoryService;
8b3ee3 62
449017 63     @Autowired
D 64     private ModelMethodService modelMethodService;
8b3ee3 65
449017 66     @Autowired
D 67     private ProjectPackageHistoryModelService projectPackageHistoryModelService;
68
69     @Autowired
70     private ConfigApi configApi;
71
1fea3e 72     @Value("${mpk.bak-file-path}")
a27351 73     private String mpkBakFilePath;
1fea3e 74
D 75     @Value("${mpk.bak-resources}")
76     private String mpkResources;
449017 77
D 78     @Override
79     public PageData<MpkFileDTO> page(Map<String, Object> params) {
80         IPage<MpkFileEntity> page = baseDao.selectPage(
81                 getPage(params, "create_date", false),
82                 getWrapper(params)
83         );
84
85         return getPageData(page, MpkFileDTO.class);
86     }
87
88     @Override
89     public List<MpkFileDTO> list(Map<String, Object> params) {
4f4b05 90 //        List<MpkFileEntity> entityList = baseDao.selectList(getWrapper(params).orderByDesc("create_date"));
D 91         List<MpkFileDTO> list = baseDao.list(params);
449017 92
4f4b05 93         return list;
449017 94     }
D 95
8b3ee3 96     private QueryWrapper<MpkFileEntity> getWrapper(Map<String, Object> params) {
2c7af4 97         String pyChineseName = (String) params.get("pyChineseName");
449017 98         String pyName = (String) params.get("pyName");
D 99         String pyType = (String) params.get("pyType");
100         String remark = (String) params.get("remark");
d8db4b 101         String label = (String) params.get("label");
449017 102
D 103         QueryWrapper<MpkFileEntity> wrapper = new QueryWrapper<>();
2c7af4 104         wrapper.like(StringUtils.isNotBlank(pyChineseName), "py_chinese_name", pyChineseName)
105                 .like(StringUtils.isNotBlank(pyName), "py_name", pyName)
449017 106                 .eq(StringUtils.isNotBlank(pyType), "py_type", pyType)
D 107                 .like(StringUtils.isNotBlank(remark), "remark", remark);
d8db4b 108
109         if (StringUtils.isNotBlank(label)) {
110             wrapper.and(w -> {
111                 w.eq(StringUtils.isNotBlank(label),"menu_name", label)
112                         .or().eq(StringUtils.isNotBlank(label),"group_name", label);
113             });
114         }
449017 115         return wrapper;
D 116     }
117
118     @Override
119     public MpkFileDTO get(String id) {
120         MpkFileDTO entity = baseDao.get(id);
121
122         return entity;
123     }
124
125     @Override
8b3ee3 126     @DSTransactional(rollbackFor = Exception.class)
449017 127     public void save(MpkFileDTO dto) {
D 128         MpkFileEntity entity = ConvertUtils.sourceToTarget(dto, MpkFileEntity.class);
8b3ee3 129         entity.setId(UUID.randomUUID().toString());
ed8ba3 130         entity.setPkgName(dto.getPkgName().trim());
131         entity.setFilePath(dto.getFilePath().trim());
449017 132         entity.setCreator(SecurityFrameworkUtils.getLoginUserId());
D 133         entity.setCreateDate(new Date());
134         insert(entity);
bb0155 135
0425a3 136         // 将备份的pyd文件,转移到MDK_PKGS环境变量下,并添加方法的默认参数(pyFile,模型路径)
D 137         String mdkPkgs = System.getenv("MDK_PKGS");
138         String fileName = entity.getFilePath().substring(entity.getFilePath().lastIndexOf("\\") + 1,entity.getFilePath().lastIndexOf(".pyd"));
139
140         String pyFilePath = mdkPkgs + File.separator + entity.getPyModule().replace(".", File.separator) + File.separator + fileName + ".pyd";
141         FileUtil.mkParentDirs(pyFilePath);
142         FileUtil.copy(entity.getFilePath(), pyFilePath, true);
143
144         // 添加参数
145         for (ModelMethodDTO method : dto.getModelMethods()) {
146             List<MethodSettingDTO> methodSettings = method.getMethodSettings();
147             if (methodSettings.stream().anyMatch(e -> e.getSettingKey().equals(MdkConstant.PY_FILE_KEY))) {
148                 methodSettings.forEach(e -> {
149                     if (e.getSettingKey().equals(MdkConstant.PY_FILE_KEY)) {
150                         e.setValue(entity.getPyModule() + "." + fileName);
151                     }
152                 });
153             }else {
154                 MethodSettingDTO setting = new MethodSettingDTO();
155                 setting.setId(UUID.randomUUID().toString());
156                 setting.setMethodId(method.getId());
157                 setting.setSettingKey(MdkConstant.PY_FILE_KEY);
158                 setting.setValue(entity.getPyModule() + "." + fileName);
159                 setting.setName("模型路径");
160                 setting.setType("input");
161                 setting.setValueType("string");
162                 methodSettings.add(setting);
163             }
164         }
165
166         modelMethodService.insertList(dto.getModelMethods(), entity.getId());
449017 167     }
D 168
169     @Override
8b3ee3 170     @DSTransactional(rollbackFor = Exception.class)
449017 171     public void update(MpkFileDTO dto) {
784ce3 172         // 添加方法的默认参数(pyFile,模型路径)
D 173         String mdkPkgs = System.getenv("MDK_PKGS");
174         String fileName = dto.getFilePath().substring(dto.getFilePath().lastIndexOf("\\") + 1,dto.getFilePath().lastIndexOf(".pyd"));
175         String pyFilePath = mdkPkgs + File.separator + dto.getPyModule().replace(".", File.separator) + File.separator + fileName + ".pyd";
176         // 判断文件是否存在,不存在的话将备份的pyd文件,转移到MDK_PKGS环境变量下
177         File pyFile = new File(pyFilePath);
178         if (!pyFile.exists()) {
0425a3 179             FileUtil.mkParentDirs(pyFilePath);
D 180             FileUtil.copy(dto.getFilePath(), pyFilePath, true);
784ce3 181         }
0425a3 182
784ce3 183         // 添加/修改参数
D 184         for (ModelMethodDTO method : dto.getModelMethods()) {
185             List<MethodSettingDTO> methodSettings = method.getMethodSettings();
186             if (methodSettings.stream().anyMatch(e -> e.getSettingKey().equals(MdkConstant.PY_FILE_KEY))) {
187                 methodSettings.forEach(e -> {
188                     if (e.getSettingKey().equals(MdkConstant.PY_FILE_KEY)) {
189                         e.setValue(dto.getPyModule() + "." + fileName);
190                     }
191                 });
192             }else {
193                 MethodSettingDTO setting = new MethodSettingDTO();
194                 setting.setId(UUID.randomUUID().toString());
195                 setting.setMethodId(method.getId());
196                 setting.setSettingKey(MdkConstant.PY_FILE_KEY);
197                 setting.setValue(dto.getPyModule() + "." + fileName);
198                 setting.setName("模型路径");
199                 setting.setType("input");
200                 setting.setValueType("string");
201                 methodSettings.add(setting);
0425a3 202             }
D 203         }
204
449017 205         MpkFileEntity entity = ConvertUtils.sourceToTarget(dto, MpkFileEntity.class);
D 206         entity.setUpdater(SecurityFrameworkUtils.getLoginUserId());
207         entity.setUpdateDate(new Date());
208         updateById(entity);
8b3ee3 209         modelMethodService.deleteModelMethod(entity.getId());
210         modelMethodService.insertList(dto.getModelMethods(), entity.getId());
449017 211     }
D 212
213     @Override
8b3ee3 214     @DSTransactional(rollbackFor = Exception.class)
449017 215     public void delete(String id) {
D 216
217         //删除源文件
218         MpkFileEntity mpkFileEntity = selectById(id);
219         if (StringUtils.isNoneBlank(mpkFileEntity.getFilePath())) {
220             File mpkFile = new File(mpkFileEntity.getFilePath());
221             if (mpkFile.exists()) {
222                 mpkFile.delete();
223                 log.info("删除源文件备份文件:" + mpkFileEntity.getFilePath());
224             }
225         }
226
227         //删除备份文件
8b3ee3 228         Map<String, Object> map1 = new HashMap<>();
229         map1.put("mdkId", id);
449017 230         List<GeneratorCodeHistoryDTO> list = generatorCodeHistoryService.list(map1);
D 231         list.forEach(e -> {
232             File file = new File(e.getFilePath());
233             if (file.exists()) {
234                 file.delete();
235                 log.info("删除生成代码备份文件:" + e.getFilePath());
236             }
237         });
238
0255ea 239         //删除 会级联删除掉关联表
D 240         deleteById(id);
449017 241     }
D 242
243     @Override
244     public byte[] generatorCode(String id, String remark, String zipFileName) {
558ffc 245         Long tenantId = TenantContextHolder.getTenantId();
D 246         // 备份文件 租户隔离
247         String mpkTenantBakFilePath = mpkBakFilePath + File.separator + tenantId;
248
449017 249         MpkFileDTO entity = baseDao.get(id);
D 250         //生成代码
1fea3e 251         //设置velocity资源加载器
D 252         Properties prop = new Properties();
253         prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
254         Velocity.init(prop);
449017 255
1fea3e 256         //封装模板数据
D 257         Map<String, Object> map = new HashMap<>();
258         map.put("pkgName",entity.getPkgName());
259         map.put("modelMethods",entity.getModelMethods());
260         map.put("pyName",entity.getPyName());
261         map.put("pyModule",entity.getPyModule());
262
263         VelocityContext dataContext = new VelocityContext(map);
264
265         //临时文件夹
912a1e 266         File dirPath = null;
D 267         try {
268             dirPath = Files.createTempDirectory("generatorCodeTmp").toFile();
269             log.info("生成临时文件夹," + dirPath.getAbsolutePath());
270         } catch (IOException e) {
271             throw new RuntimeException("创建临时文件夹异常",e);
272         }
1fea3e 273
D 274         List<String> javaFilePaths = new ArrayList<>();
275         List<String> cppFilePaths = new ArrayList<>();
276
277         //生成java文件
278         File javaFile = new File(dirPath.getAbsolutePath() + File.separator + "IAILMDK" + File.separator + entity.getPkgName().replace(".", File.separator) + File.separator + entity.getPyName() + ".java");
279         GenUtils.drawTemplate("abstract.java.vm", dataContext, javaFile);
280         javaFilePaths.add(javaFile.getAbsolutePath());
281
282         //生成Impl.java文件
283         File implJavaFile = new File(dirPath.getAbsolutePath() + File.separator + "IAILMDK" + File.separator + entity.getPkgName().replace(".", File.separator) + File.separator + MdkConstant.IMPL + File.separator + entity.getPyName() + "Impl.java");
284         GenUtils.drawTemplate("impl.java.vm", dataContext, implJavaFile);
285         javaFilePaths.add(implJavaFile.getAbsolutePath());
286
287         //生成.cpp文件
288         File cppFile = new File(dirPath.getAbsolutePath() + File.separator + MdkConstant.C + File.separator + entity.getPyName() + ".cpp");
289         GenUtils.drawTemplate("cpp.vm", dataContext, cppFile);
290         cppFilePaths.add(cppFile.getAbsolutePath());
291
292         //生成Jni.cpp文件
293         File jniCppFile = new File(dirPath.getAbsolutePath() + File.separator + MdkConstant.C + File.separator + entity.getPyName() + "Jni.cpp");
294         GenUtils.drawTemplate("Jni.cpp.vm", dataContext, jniCppFile);
295         cppFilePaths.add(jniCppFile.getAbsolutePath());
296
297         //生成.h文件
298         File hFile = new File(dirPath.getAbsolutePath() + File.separator + MdkConstant.C + File.separator + entity.getPyName() + ".h");
299         GenUtils.drawTemplate("h.vm", dataContext, hFile);
300
301         //生成Jni.h文件
302         File jniHFile = new File(dirPath.getAbsolutePath() + File.separator + MdkConstant.C + File.separator + entity.getPyName() + "Jni.h");
303         GenUtils.drawTemplate("Jni.h.vm", dataContext, jniHFile);
a8c6a6 304
D 305         // 添加python源文件
8c86a9 306         String pyFilePath = dirPath.getAbsolutePath() + File.separator + MdkConstant.ALGS + File.separator + entity.getPyModule().replace(".", File.separator) + File.separator + entity.getFilePath().substring(entity.getFilePath().lastIndexOf("\\"));
a8c6a6 307         FileUtil.mkParentDirs(pyFilePath);
D 308         FileUtil.copy(entity.getFilePath(), pyFilePath, true);
1fea3e 309
D 310         // 资源文件
311         FileUtil.copy(mpkResources + File.separator + "libs", dirPath.getAbsolutePath(), true);
312
313         //生成dll文件
314         String dllSavePath = dirPath.getAbsolutePath() + File.separator + MdkConstant.LIBS + File.separator + "IAIL.MDK.Mid.Jni.dll";
315         createDllFile(dirPath.getAbsolutePath(),cppFilePaths,dllSavePath);
316         //备份dll文件,用于后续运行
558ffc 317         String dllBakPath = mpkTenantBakFilePath + File.separator + MdkConstant.DLL + File.separator + entity.getPyName() + ".dll";
1fea3e 318         FileUtil.mkParentDirs(dllBakPath);
D 319         FileUtil.copy(dllSavePath, dllBakPath, true);
320
321         //utils + env java文件
322         File utilsJavaFile = new File(dirPath.getAbsolutePath() + File.separator + "IAILMDK" + File.separator + "iail" + File.separator + "mdk" + File.separator + "model" + File.separator + "utils" + File.separator + "AlgsUtils.java");
323         FileUtil.mkParentDirs(utilsJavaFile);
324         FileUtil.copy(mpkResources + File.separator +"IAILMDK/utils/AlgsUtils.java", utilsJavaFile.getAbsolutePath(), true);
325         javaFilePaths.add(utilsJavaFile.getAbsolutePath());
326         File envJavaFile = new File(dirPath.getAbsolutePath() + File.separator + "IAILMDK" + File.separator + "iail" + File.separator + "mdk" + File.separator + "model" + File.separator + "common" + File.separator + "Environment.java");
327         FileUtil.mkParentDirs(envJavaFile);
328         FileUtil.copy(mpkResources + File.separator +"IAILMDK/common/Environment.java", envJavaFile.getAbsolutePath(), true);
329         javaFilePaths.add(envJavaFile.getAbsolutePath());
330         // 生成class文件
331         createClassFile(javaFilePaths);
332         // 删除java源文件
333         deleteJavaFile(javaFilePaths);
334         // 打jar包
335         String jarSavePath = pkgJar(dirPath.getAbsolutePath());
336         //备份jar文件,用于后续运行
558ffc 337         String jarBakPath = mpkTenantBakFilePath + File.separator + MdkConstant.JAR + File.separator + entity.getPyName() + ".jar";
1fea3e 338         FileUtil.mkParentDirs(dllBakPath);
D 339         FileUtil.copy(jarSavePath, jarBakPath, true);
340         // 打zip包
558ffc 341         String zipPath = mpkTenantBakFilePath + File.separator + zipFileName;
1fea3e 342         ZipUtil.zip(dirPath.getAbsolutePath(), zipPath);
D 343         byte[] bytes = FileUtil.readBytes(zipPath);
449017 344
D 345         //代码生成历史记录
346         GeneratorCodeHistoryEntity historyEntity = new GeneratorCodeHistoryEntity();
347         historyEntity.setId(UUID.randomUUID().toString());
348         historyEntity.setMdkId(id);
1fea3e 349         historyEntity.setFileName(zipFileName);
D 350         historyEntity.setFilePath(zipPath);
449017 351         historyEntity.setRemark(remark);
D 352         historyEntity.setCreateTime(new Date());
353         generatorCodeHistoryService.insert(historyEntity);
354
1fea3e 355         // 删除临时文件
3009e2 356         try {
D 357             FileUtils.deleteDirectory(dirPath);
358         } catch (IOException e) {
912a1e 359             throw new RuntimeException("删除临时文件异常",e);
3009e2 360         }
1fea3e 361         return bytes;
449017 362     }
D 363
364     @Override
8b3ee3 365     @DSTransactional(rollbackFor = Exception.class)
f7932f 366     public byte[] packageModel(List<String> ids, String projectId, String projectName, String zipFileName, String logs, String version) throws IOException, InterruptedException {
558ffc 367         Long tenantId = TenantContextHolder.getTenantId();
D 368         // 备份文件 租户隔离
369         String mpkTenantBakFilePath = mpkBakFilePath + File.separator + tenantId;
370
449017 371         List<MpkFileDTO> entities = baseDao.selectByIds(ids);
D 372
373         //模板数据
1fea3e 374 //        Map<String, Object> map = new HashMap<>();
D 375 //        map.put("entities", entities);
376 //        VelocityContext context = new VelocityContext(map);
449017 377
D 378         //临时文件夹
912a1e 379         File dirPath = null;
D 380         try {
381             dirPath = Files.createTempDirectory("packageModelTmp").toFile();
382             log.info("生成临时文件夹," + dirPath.getAbsolutePath());
383         } catch (IOException e) {
384             throw new RuntimeException("创建临时文件夹异常",e);
385         }
449017 386
D 387         //设置velocity资源加载器
388         Properties prop = new Properties();
389         prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
390         Velocity.init(prop);
391
a27351 392         //生成menu.xml文件
2cc235 393         LinkedHashMap<String, LinkedHashMap<String, List<MpkFileDTO>>> collect = entities.stream().collect(Collectors.groupingBy(MpkFileDTO::getMenuName, LinkedHashMap::new, Collectors.groupingBy(e -> StringUtils.isNotBlank(e.getGroupName()) ? e.getGroupName() : "default_group",LinkedHashMap::new,Collectors.toList())));
a27351 394         Map<String, Object> map1 = new HashMap<>();
8b3ee3 395         map1.put("collects", collect);
c7009e 396         File xmlFile = new File(dirPath.getAbsolutePath() + File.separator + MdkConstant.LIBS + File.separator + "menu.xml");
8b3ee3 397         GenUtils.drawTemplate("menu.xml.vm", new VelocityContext(map1), xmlFile);
449017 398
D 399         List<String> javaFilePaths = new ArrayList<>();
f7932f 400         List<String> cppFilePaths = new ArrayList<>();
449017 401
D 402         //生成java文件
403         for (MpkFileDTO entity : entities) {
404             //封装模板数据
405             Map<String, Object> data = new HashMap<>();
8b3ee3 406             data.put("pkgName", entity.getPkgName());
407             data.put("modelMethods", entity.getModelMethods());
408             data.put("pyName", entity.getPyName());
409             data.put("pyModule", entity.getPyModule());
449017 410             VelocityContext dataContext = new VelocityContext(data);
D 411             //生成java文件
412             File javaFile = new File(dirPath.getAbsolutePath() + File.separator + "IAILMDK" + File.separator + entity.getPkgName().replace(".", File.separator) + File.separator + entity.getPyName() + ".java");
8b3ee3 413             GenUtils.drawTemplate("abstract.java.vm", dataContext, javaFile);
449017 414             javaFilePaths.add(javaFile.getAbsolutePath());
D 415
416             //生成Impl.java文件
417             File implJavaFile = new File(dirPath.getAbsolutePath() + File.separator + "IAILMDK" + File.separator + entity.getPkgName().replace(".", File.separator) + File.separator + MdkConstant.IMPL + File.separator + entity.getPyName() + "Impl.java");
8b3ee3 418             GenUtils.drawTemplate("impl.java.vm", dataContext, implJavaFile);
449017 419             javaFilePaths.add(implJavaFile.getAbsolutePath());
D 420
f7932f 421             //生成.cpp文件
D 422             File cppFile = new File(dirPath.getAbsolutePath() + File.separator + MdkConstant.C + File.separator + entity.getPyName() + ".cpp");
423             GenUtils.drawTemplate("cpp.vm", dataContext, cppFile);
424             cppFilePaths.add(cppFile.getAbsolutePath());
425
426             //生成Jni.cpp文件
427             File jniCppFile = new File(dirPath.getAbsolutePath() + File.separator + MdkConstant.C + File.separator + entity.getPyName() + "Jni.cpp");
428             GenUtils.drawTemplate("Jni.cpp.vm", dataContext, jniCppFile);
429             cppFilePaths.add(jniCppFile.getAbsolutePath());
430
431             //生成.h文件
432             File hFile = new File(dirPath.getAbsolutePath() + File.separator + MdkConstant.C + File.separator + entity.getPyName() + ".h");
433             GenUtils.drawTemplate("h.vm", dataContext, hFile);
434
435             //生成Jni.h文件
436             File jniHFile = new File(dirPath.getAbsolutePath() + File.separator + MdkConstant.C + File.separator + entity.getPyName() + "Jni.h");
437             GenUtils.drawTemplate("Jni.h.vm", dataContext, jniHFile);
438
449017 439             // 添加python源文件
8c86a9 440             String pyFilePath = dirPath.getAbsolutePath() + File.separator + MdkConstant.ALGS + File.separator + entity.getPyModule().replace(".", File.separator) + File.separator + entity.getFilePath().substring(entity.getFilePath().lastIndexOf("\\"));
449017 441             FileUtil.mkParentDirs(pyFilePath);
8b3ee3 442             FileUtil.copy(entity.getFilePath(), pyFilePath, true);
449017 443         }
D 444
1fea3e 445         // 资源文件
D 446         FileUtil.copy(mpkResources + File.separator + "libs", dirPath.getAbsolutePath(), true);
c7009e 447
f7932f 448         //生成dll文件
c7009e 449         String dllSavePath = dirPath.getAbsolutePath() + File.separator + MdkConstant.LIBS + File.separator + "IAIL.MDK.Mid.Jni.dll";
f7932f 450         createDllFile(dirPath.getAbsolutePath(),cppFilePaths,dllSavePath);
c12dae 451         // 打包历史id
D 452         String historyId = UUID.randomUUID().toString();
453         //备份dll,发布时使用
454         File dllFile = new File(dllSavePath);
455         if (dllFile.exists()) {
558ffc 456             File dllBakFile = new File(mpkBakFilePath + File.separator + MdkConstant.PROJECT_UNPUBLISH + File.separator + projectId + MdkConstant.SPLIT + historyId + ".dll");
c12dae 457             FileUtil.copy(dllFile, dllBakFile, true);
D 458         }else {
459             log.error("dll文件不存在,无法备份。" + dllSavePath);
460         }
f7932f 461
449017 462         //utils + env java文件
c7009e 463         File utilsJavaFile = new File(dirPath.getAbsolutePath() + File.separator + "IAILMDK" + File.separator + "iail" + File.separator + "mdk" + File.separator + "model" + File.separator + "utils" + File.separator + "AlgsUtils.java");
449017 464         FileUtil.mkParentDirs(utilsJavaFile);
1fea3e 465         FileUtil.copy(mpkResources + File.separator +"IAILMDK/utils/AlgsUtils.java", utilsJavaFile.getAbsolutePath(), true);
449017 466         javaFilePaths.add(utilsJavaFile.getAbsolutePath());
c7009e 467         File envJavaFile = new File(dirPath.getAbsolutePath() + File.separator + "IAILMDK" + File.separator + "iail" + File.separator + "mdk" + File.separator + "model" + File.separator + "common" + File.separator + "Environment.java");
449017 468         FileUtil.mkParentDirs(envJavaFile);
1fea3e 469         FileUtil.copy(mpkResources + File.separator +"IAILMDK/common/Environment.java", envJavaFile.getAbsolutePath(), true);
449017 470         javaFilePaths.add(envJavaFile.getAbsolutePath());
D 471         // 生成class文件
472         createClassFile(javaFilePaths);
473         // 删除java源文件
474         deleteJavaFile(javaFilePaths);
475         // 打jar包
c12dae 476         String jarSavePath = pkgJar(dirPath.getAbsolutePath());
D 477         //备份jar包,发布时使用
478         File jarFile = new File(jarSavePath);
479         if (jarFile.exists()) {
558ffc 480             File jarBakFile = new File(mpkBakFilePath + File.separator + MdkConstant.PROJECT_UNPUBLISH + File.separator + projectId + MdkConstant.SPLIT + historyId + ".jar");
c12dae 481             FileUtil.copy(jarFile, jarBakFile, true);
D 482         }else {
483             log.error("jar文件不存在,无法备份。" + jarSavePath);
484         }
485
449017 486         // 本次更新日志
558ffc 487         ProjectPackageHistoryEntity projectPackageHistoryEntity = new ProjectPackageHistoryEntity();
D 488         projectPackageHistoryEntity.setId(historyId);
489         projectPackageHistoryEntity.setProjectId(projectId);
490         projectPackageHistoryEntity.setFileName(zipFileName);
491         projectPackageHistoryEntity.setLog(logs);
492         projectPackageHistoryEntity.setVersion(version);
493         projectPackageHistoryEntity.setModelNames(entities.stream().map(MpkFileDTO::getPyName).collect(Collectors.joining(",")));
494         projectPackageHistoryEntity.setCreateTime(new Date());
449017 495         // 生成更新日志
558ffc 496         createLog(projectId, projectName, dirPath.getAbsolutePath(), projectPackageHistoryEntity, version);
449017 497         // 打zip包
558ffc 498         String zipPath = mpkTenantBakFilePath + File.separator + zipFileName;
8b3ee3 499         ZipUtil.zip(dirPath.getAbsolutePath(), zipPath);
449017 500         byte[] bytes = FileUtil.readBytes(zipPath);
D 501         // 记录打包日志
558ffc 502         projectPackageHistoryEntity.setFilePath(zipPath);
D 503         projectPackageHistoryService.insert(projectPackageHistoryEntity);
449017 504         // 插入打包历史-模型关联表
D 505         List<ProjectPackageHistoryModelEntity> historyModelList = new ArrayList<>(entities.size());
506         entities.forEach(e -> {
507             ProjectPackageHistoryModelEntity entity = new ProjectPackageHistoryModelEntity();
508             entity.setId(UUID.randomUUID().toString());
509             entity.setProjectId(projectId);
510             entity.setPackageHistoryId(historyId);
511             entity.setPyName(e.getPyName());
067d7a 512             entity.setPyChineseName(e.getPyChineseName());
449017 513             entity.setPkgName(e.getPkgName());
D 514             entity.setPyModule(e.getPyModule());
515             entity.setRemark(e.getRemark());
0255ea 516             List<ModelMethodDTO> methods = e.getModelMethods();
449017 517             if (!CollectionUtils.isEmpty(methods)) {
D 518                 entity.setMethodInfo(JSON.toJSONString(methods));
519             }
520             historyModelList.add(entity);
521         });
558ffc 522         projectPackageHistoryModelService.insertList(historyModelList);
449017 523         // 删除临时文件
D 524         FileUtils.deleteDirectory(dirPath);
525         return bytes;
526     }
527
1fea3e 528     private void createDllFile(String dirPath, List<String> cppFilePaths, String dllSavePath) {
D 529         try {
530             String paths = cppFilePaths.stream().collect(Collectors.joining(" "));
531             String command = "cl /LD " + paths + " /EHsc /o " + dllSavePath + " /link " + "IAIL.MDK.Mid.Windows.lib";
532             log.info("执行cmd命令生成dll:" + command);
533             ProcessBuilder builder = new ProcessBuilder("cmd", "/c", command);
534             builder.directory(new File(dirPath + File.separator + MdkConstant.LIBS));
535             Process process = builder.start();
536             // 获取命令输出结果
537             InputStream inputStream = process.getInputStream();
538             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "GBK")); // 设置编码为GBK
539             String line;
540             while ((line = reader.readLine()) != null) {
541                 log.info(line);
542             }
543             // 等待命令执行完成
544             process.waitFor();
545         } catch (Exception e) {
912a1e 546             throw new RuntimeException("执行cmd命令生成dll异常",e);
f7932f 547         }
D 548     }
549
449017 550     @Override
8b3ee3 551     public Map<String, String> savePyFile(MultipartFile file) throws IOException {
558ffc 552         Long tenantId = TenantContextHolder.getTenantId();
D 553         // 备份文件 租户隔离
554         String mpkTenantBakFilePath = mpkBakFilePath + File.separator + tenantId;
a8c6a6 555         File bakDir = new File(mpkTenantBakFilePath);
D 556         if (!bakDir.exists()) {
557             bakDir.mkdirs();
558         }
559         //临时文件夹
560         File tempDir = null;
561         try {
562             tempDir = Files.createTempDirectory("pyFileTmp").toFile();
563             log.info("生成临时文件夹," + tempDir.getAbsolutePath());
564         } catch (IOException e) {
565             throw new RuntimeException("创建临时文件夹异常",e);
449017 566         }
D 567         String fileName = file.getOriginalFilename();
a8c6a6 568         String pyName = fileName.substring(0, fileName.lastIndexOf("."));
0425a3 569         String pyName_uuTime = pyName + "_" + new Date().getTime();
D 570
571         File pydBakFile = new File(bakDir.getAbsolutePath() + File.separator + pyName_uuTime + ".pyd");
a8c6a6 572         try {
D 573             // py文件存入临时文件夹
574             File saveFile = new File(tempDir.getAbsolutePath() + File.separator + fileName);
575             saveFile.createNewFile();
576             file.transferTo(saveFile);
577
578             // 临时文件夹中生成pyd文件
579             // 生成setup.py文件
0425a3 580             createSetUpFile(tempDir.getAbsolutePath(),fileName,pyName_uuTime);
a8c6a6 581             // 执行cmd命令编译pyd文件
D 582             createPydFile(tempDir.getAbsolutePath());
583
584             // 临时文件夹中pyd文件移到dir下,修改为uuid.pyd
0425a3 585             File pydFile = new File(tempDir.getAbsolutePath() + File.separator + pyName_uuTime + MdkConstant.PYD_SUFFIX);
a8c6a6 586             if (!pydFile.exists()) {
D 587                 throw new RuntimeException("编译pyd文件失败!");
588             }
589             log.info("备份pyd文件," + pydBakFile.getAbsolutePath());
590             FileUtil.copy(pydFile,pydBakFile,true);
591         } catch (Exception e) {
592             throw new RuntimeException("编译pyd文件异常");
593         } finally {
594             if (tempDir.exists()) {
595                 tempDir.delete();
596             }
597         }
449017 598
8b3ee3 599         Map<String, String> result = new HashMap<>(2);
a8c6a6 600         result.put("filePath", pydBakFile.getAbsolutePath());
D 601         result.put("fileName", pyName);
449017 602
D 603         return result;
604     }
605
a8c6a6 606     private void createPydFile(String dir) {
D 607         try {
608             String command = "python setup.py build_ext --inplace";
609             log.info("执行cmd命令编译pyd:" + command);
610             ProcessBuilder builder = new ProcessBuilder("cmd", "/c", command);
611             builder.directory(new File(dir));  // 设置工作目录
612             builder.redirectErrorStream(true);  // 将错误输出和标准输出合并
613             Process process = builder.start();
614             // 获取命令输出结果
615             InputStream inputStream = process.getInputStream();
616             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "GBK")); // 设置编码为GBK
617             String line;
618             while ((line = reader.readLine()) != null) {
619                 log.info(line);
620             }
621             // 等待命令执行完成
622             process.waitFor();
623         } catch (Exception e) {
624             throw new RuntimeException("执行cmd命令生成dll异常", e);
625         }
626     }
627
628     private void createSetUpFile(String dir, String pyFileName, String pyName) {
629         // 生成setup.py文件
630         HashMap<String,Object> map = new HashMap<>();
631         map.put("fileName",pyFileName);
632         map.put("pyName",pyName);
633         GenUtils.drawTemplate("setup.py.vm",map,new File(dir + File.separator + MdkConstant.SETUP_PY));
634     }
635
c12dae 636     @Override
D 637     public CommonResult<String> publish(Map<String, Object> params) {
638         String historyId = (String) params.get("historyId");
639         String projectId = (String) params.get("projectId");
640
641         // 判断新的dll和jar文件是否存在
558ffc 642         String jarFileBakPath = mpkBakFilePath + File.separator + MdkConstant.PROJECT_UNPUBLISH + File.separator + projectId + MdkConstant.SPLIT + historyId + ".jar";
D 643         String dllFileBakPath = mpkBakFilePath + File.separator + MdkConstant.PROJECT_UNPUBLISH + File.separator + projectId + MdkConstant.SPLIT + historyId + ".dll";
c12dae 644         if (!FileUtil.exist(jarFileBakPath)) {
D 645             throw new RuntimeException("jar文件不存在," + jarFileBakPath);
646         }
647         if (!FileUtil.exist(dllFileBakPath)) {
648             throw new RuntimeException("dll文件不存在" + dllFileBakPath);
649         }
650
651         try {
652             // 卸载之前的dll和jar
653             DllUtils.removeClassLoaderCache(projectId);
654             // 删除之前的dll和jar备份文件
655             DllUtils.removeOldFile(mpkBakFilePath + File.separator + MdkConstant.PROJECT_PUBLISH,projectId);
656         } catch (Exception e) {
657             throw new RuntimeException("卸载之前的dll和jar异常",e);
658         }
659
660         // 备份新的dll和jar
661         String jarFilePath = mpkBakFilePath + File.separator + MdkConstant.PROJECT_PUBLISH + File.separator + projectId + MdkConstant.SPLIT + historyId + ".jar";
662         String dllFilePath = mpkBakFilePath + File.separator + MdkConstant.PROJECT_PUBLISH + File.separator + projectId + MdkConstant.SPLIT + historyId + ".dll";
663         FileUtil.copy(jarFileBakPath,jarFilePath,true);
664         FileUtil.copy(dllFileBakPath,dllFilePath,true);
665
666         URLClassLoader urlClassLoader = null;
667         try {
668             // 加载新的jar
669             urlClassLoader = DllUtils.loadJar(jarFilePath);
670         } catch (Exception e) {
671             throw new RuntimeException("加载新的jar异常",e);
672         }
673
674         try {
675             // 加载新的dll
676             DllUtils.loadDll(urlClassLoader.loadClass("iail.mdk.model.common.Environment"),dllFilePath);
677         } catch (Exception e) {
678             DllUtils.unloadJar(urlClassLoader);
679             throw new RuntimeException("加载新的dll异常",e);
680         }
681         // 都加载成功后加入缓存
682         DllUtils.addClassLoaderCache(projectId,urlClassLoader);
683
684         return CommonResult.success();
685     }
686
558ffc 687     private void createLog(String projectId, String projectName, String dirPath, ProjectPackageHistoryEntity projectPackageHistoryEntity, String version) {
8b3ee3 688         Map<String, Object> map = new HashMap<>();
689         map.put("projectId", projectId);
558ffc 690         List<ProjectPackageHistoryEntity> list = projectPackageHistoryService.list(map);
D 691         list.add(projectPackageHistoryEntity);
449017 692         // 按照日期分组再排序
558ffc 693         HashMap<String, List<ProjectPackageHistoryEntity>> dataMap = list.stream().collect(
449017 694                 Collectors.groupingBy(e -> DateUtils.format(e.getCreateTime(), DateUtils.DATE_PATTERN_POINT),
8b3ee3 695                         LinkedHashMap::new,
558ffc 696                         Collectors.collectingAndThen(Collectors.toList(), e -> e.stream().sorted(Comparator.comparing(ProjectPackageHistoryEntity::getCreateTime)).collect(Collectors.toList()))));
449017 697
D 698         Map<String, Object> data = new HashMap<>();
8b3ee3 699         data.put("dataMap", dataMap);
700         data.put("projectName", projectName);
701         data.put("version", version);
702         data.put("now", DateUtils.format(new Date(), DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND));
449017 703
D 704         File logFile = new File(dirPath + File.separator + "更新日志.txt");
8b3ee3 705         GenUtils.drawTemplate("log.txt.vm", data, logFile);
449017 706     }
D 707
3009e2 708     private String pkgJar(String dirPath) {
D 709         try {
710             String jarSavePath = dirPath + File.separator + MdkConstant.LIBS + File.separator + "IAILMDK.jar";
711             StringBuilder sb = new StringBuilder();
712             sb.append("jar -cvf");
713             sb.append(" ").append(jarSavePath);
714             sb.append(" -C ").append(dirPath).append(File.separator).append("IAILMDK").append(File.separator).append(" .");
715             log.info("执行cmd命令打jar包:" + sb);
716             Process process = RuntimeUtil.exec(sb.toString());
717             process.waitFor();
718             return jarSavePath;
719         } catch (InterruptedException e) {
912a1e 720             throw new RuntimeException("执行cmd命令打jar包异常",e);
3009e2 721         }
449017 722     }
D 723
724     private void deleteJavaFile(List<String> javaFilePaths) {
725         for (String javaFilePath : javaFilePaths) {
726             FileUtil.del(javaFilePath);
727         }
728     }
729
3009e2 730     private void createClassFile(List<String> javaFilePaths){
D 731         try {
732             StringBuilder sb = new StringBuilder();
733             sb.append("javac -encoding utf-8");
734             for (String path : javaFilePaths) {
735                 sb.append(" ").append(path);
736             }
737             log.info("执行cmd命令生成class:" + sb);
738             Process process = RuntimeUtil.exec(sb.toString());
739             process.waitFor();
740         } catch (InterruptedException e) {
912a1e 741             throw new RuntimeException("执行cmd命令生成class异常",e);
449017 742         }
D 743     }
744 }