|
|
@@ -0,0 +1,334 @@
|
|
|
+package org.thyy.archive.service.archive.task;
|
|
|
+
|
|
|
+import cn.hutool.core.io.FileTypeUtil;
|
|
|
+import cn.hutool.core.io.FileUtil;
|
|
|
+import cn.hutool.core.io.file.FileNameUtil;
|
|
|
+import cn.hutool.core.thread.ExecutorBuilder;
|
|
|
+import cn.hutool.core.thread.ThreadFactoryBuilder;
|
|
|
+import cn.hutool.core.util.IdUtil;
|
|
|
+import cn.hutool.core.util.ReflectUtil;
|
|
|
+import cn.hutool.extra.spring.SpringUtil;
|
|
|
+import cn.hutool.json.JSONArray;
|
|
|
+import org.reflections.Reflections;
|
|
|
+import org.reflections.scanners.Scanners;
|
|
|
+import org.reflections.util.ConfigurationBuilder;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+import org.thyy.archive.config.envionment.ArchiveConfig;
|
|
|
+import org.thyy.archive.dao.archive.ArchiveDao;
|
|
|
+import org.thyy.archive.dao.archive.ArchiveTaskDao;
|
|
|
+import org.thyy.archive.data.Patient;
|
|
|
+import org.thyy.archive.data.UserInfo;
|
|
|
+import org.thyy.archive.data.archive.ArchiveClass;
|
|
|
+import org.thyy.archive.data.archive.ArchiveTask;
|
|
|
+import org.thyy.archive.data.archive.PatientArchive;
|
|
|
+import org.thyy.archive.data.archive.TaskPatient;
|
|
|
+import org.thyy.archive.enumtype.ArchiveType;
|
|
|
+import org.thyy.archive.utils.TokenUtil;
|
|
|
+import org.thyy.utils.exception.BizException;
|
|
|
+import org.thyy.utils.exception.ExceptionEnum;
|
|
|
+import org.thyy.utils.result.R;
|
|
|
+import org.thyy.utils.result.ResultVo;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.lang.annotation.Annotation;
|
|
|
+import java.lang.reflect.Constructor;
|
|
|
+import java.lang.reflect.Parameter;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Comparator;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Set;
|
|
|
+import java.util.concurrent.ThreadPoolExecutor;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+@Service
|
|
|
+public class ArchiveService {
|
|
|
+ private final ArchiveDao dao;
|
|
|
+ private final List<ArchiveClass> archiveMethods;
|
|
|
+ private static boolean hasTest = false;
|
|
|
+ private final ThreadPoolExecutor threadPoolExecutor;
|
|
|
+ private final ArchiveTaskDao taskDao;
|
|
|
+ private final ArchiveConfig config;
|
|
|
+
|
|
|
+ public ArchiveService(ArchiveDao dao, ArchiveTaskDao taskDao, ArchiveConfig config) {
|
|
|
+ this.dao = dao;
|
|
|
+ this.taskDao = taskDao;
|
|
|
+ this.config = config;
|
|
|
+ archiveMethods = getArchiveMethods();
|
|
|
+
|
|
|
+ threadPoolExecutor = ExecutorBuilder.create()
|
|
|
+ .setCorePoolSize(5)
|
|
|
+ .setMaxPoolSize(10)
|
|
|
+ .setKeepAliveTime(0L, TimeUnit.MILLISECONDS)
|
|
|
+ .setThreadFactory(new ThreadFactoryBuilder()
|
|
|
+ .setNamePrefix("Archive-Task-Thread-")
|
|
|
+ .build()
|
|
|
+ )
|
|
|
+ .setHandler(new ThreadPoolExecutor.AbortPolicy())
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<ArchiveClass> getArchiveMethods() {
|
|
|
+ String packageName = ArchiveService.class.getPackage().getName();
|
|
|
+ Reflections reflections = new Reflections(
|
|
|
+ new ConfigurationBuilder()
|
|
|
+ .forPackages(packageName)
|
|
|
+ .addScanners(Scanners.TypesAnnotated)
|
|
|
+ );
|
|
|
+ Class<? extends Annotation> annotationType = ArchiveComponent.class;
|
|
|
+ Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(annotationType);
|
|
|
+ List<ArchiveClass> classes = new ArrayList<>();
|
|
|
+
|
|
|
+ for (Class<?> aClass : typesAnnotatedWith) {
|
|
|
+ ArchiveComponent annotation = aClass.getAnnotation(ArchiveComponent.class);
|
|
|
+ ArchiveClass build = ArchiveClass.builder()
|
|
|
+ .sort(annotation.sort())
|
|
|
+ .clazz(aClass)
|
|
|
+ .name(annotation.name())
|
|
|
+ .test(annotation.test())
|
|
|
+ .startupParameter(annotation.startupParameter())
|
|
|
+ .build();
|
|
|
+ classes.add(build);
|
|
|
+ if (annotation.test()) {
|
|
|
+ hasTest = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ classes.sort(Comparator.comparing((o) -> o.getSort() == null ? 0 : o.getSort()));
|
|
|
+ return classes;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void submitTask(List<TaskPatient> data) {
|
|
|
+ for (TaskPatient datum : data) {
|
|
|
+ if (datum.getForceStart())
|
|
|
+ taskDao.delByTaskPatient(datum);
|
|
|
+ ArchiveTask archiveTask = taskDao.hasData(datum);
|
|
|
+ if (archiveTask == null)
|
|
|
+ submitTask(datum);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void submitTask(TaskPatient data) {
|
|
|
+ threadPoolExecutor.execute(() -> {
|
|
|
+ ArchiveLog log = new ArchiveLog(data, ArchiveService.class, "main");
|
|
|
+ Patient patInfo = dao.getPatientInfo(data.getPatNo(), data.getTimes());
|
|
|
+ if (patInfo == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ data.setPatient(patInfo);
|
|
|
+ taskDao.setData(data);
|
|
|
+ data.setOldArchives(dao.getPatientList(data.getPatNo(), data.getTimes()));
|
|
|
+ log.delimiter("生成pdf任务启动");
|
|
|
+ executePdfServer(data, log);
|
|
|
+ createSql(data, log);
|
|
|
+ log.delimiter("生成pdf任务结束");
|
|
|
+ log.sendSocketMsg("taskComplete", "");
|
|
|
+ taskDao.delByTaskPatient(data);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private void executePdfServer(TaskPatient data, ArchiveLog log) {
|
|
|
+ // 如果有注解是在测试的且不是正式环境的话就为真
|
|
|
+ boolean test = hasTest && !config.getProd();
|
|
|
+
|
|
|
+ for (ArchiveClass archiveMethod : archiveMethods) {
|
|
|
+ // 如果为真且这个方法不是测试的话就跳过这个循环,去执行测试类
|
|
|
+ if (test && !archiveMethod.getTest()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // 判断是否跳过任务
|
|
|
+ if (data.getPassTaskName() != null &&
|
|
|
+ data.getPassTaskName().contains(archiveMethod.getName())) {
|
|
|
+ log.warn("跳过任务:{}", archiveMethod.getName());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String name = archiveMethod.getName();
|
|
|
+ ArchiveLog archiveLog = new ArchiveLog(data, archiveMethod.getClazz(), name);
|
|
|
+ Object[] params = getParams(archiveMethod.getClazz(), data, archiveLog);
|
|
|
+ try {
|
|
|
+ log.info("【{}】任务启动", name);
|
|
|
+ ReflectUtil.newInstance(archiveMethod.getClazz(), params);
|
|
|
+ log.info("【{}】任务结束", name);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("【{}】任务错误\n消息:{}", name, e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void createSql(TaskPatient data, ArchiveLog log) {
|
|
|
+ try {
|
|
|
+ dao.insert(data.getArchives());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("生成目录失败:{}", "请重新执行任务。");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取构造函数的参数
|
|
|
+ *
|
|
|
+ * @param imp 类
|
|
|
+ * @param data 患者数据
|
|
|
+ * @param log 日志
|
|
|
+ * @return 参数
|
|
|
+ */
|
|
|
+ public Object[] getParams(Class<?> imp, TaskPatient data, ArchiveLog log) {
|
|
|
+ Constructor<?>[] constructors = imp.getConstructors();
|
|
|
+ // 获取第一个构造参数
|
|
|
+ Constructor<?> constructor = constructors[0];
|
|
|
+ Object[] parameters = new Object[constructor.getParameterCount()];
|
|
|
+ for (int i = 0; i < constructor.getParameterCount(); i++) {
|
|
|
+ Parameter parameter = constructor.getParameters()[i];
|
|
|
+ if (parameter.getType() == TaskPatient.class) {
|
|
|
+ parameters[i] = data;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (ArchiveLog.class == parameter.getType()) {
|
|
|
+ parameters[i] = log;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Object bean = SpringUtil.getBean(parameter.getType());
|
|
|
+ parameters[i] = bean;
|
|
|
+ }
|
|
|
+ return parameters;
|
|
|
+ }
|
|
|
+
|
|
|
+ public ArchiveLog getPatLog(String patNo, Integer times, String serverName) {
|
|
|
+ String code = TokenUtil.getInstance().getTokenUserId();
|
|
|
+ UserInfo userByCode = dao.getUserByCode(code);
|
|
|
+ String submitName = userByCode == null ? "9999" : userByCode.getName();
|
|
|
+
|
|
|
+ TaskPatient build = TaskPatient.builder()
|
|
|
+ .patNo(patNo)
|
|
|
+ .times(times)
|
|
|
+ .submitCode(TokenUtil.getInstance().getTokenUserId())
|
|
|
+ .submitName(submitName)
|
|
|
+ .build();
|
|
|
+ return new ArchiveLog(build, this.getClass(), serverName);
|
|
|
+ }
|
|
|
+
|
|
|
+ public ResultVo<PatientArchive> upload(MultipartFile file, String patNo, Integer times, String parent) {
|
|
|
+ try {
|
|
|
+ String type = FileTypeUtil.getType(file.getInputStream());
|
|
|
+ if (type == null) {
|
|
|
+ return R.fail(ExceptionEnum.LOGICAL_ERROR, "文件上传类型只能是pdf");
|
|
|
+ }
|
|
|
+ if (!type.contains("pdf")) {
|
|
|
+ return R.fail(ExceptionEnum.LOGICAL_ERROR, "文件上传类型只能是pdf");
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ return R.fail(ExceptionEnum.LOGICAL_ERROR, "文件上传类型只能是pdf");
|
|
|
+ }
|
|
|
+
|
|
|
+ ArchiveLog logger = getPatLog(patNo, times, "上传文件");
|
|
|
+
|
|
|
+ String filename = FileNameUtil.mainName(file.getOriginalFilename());
|
|
|
+ String id = IdUtil.simpleUUID();
|
|
|
+ // 上传到 upload 文件夹中
|
|
|
+ String path = config.getEmrPath() + patNo + "/" + times + "/upload/" + id + ".pdf";
|
|
|
+
|
|
|
+ PatientArchive build = PatientArchive.builder()
|
|
|
+ .id(id)
|
|
|
+ .isDir(false)
|
|
|
+ .type(ArchiveType.USER_UPLOAD.getValue())
|
|
|
+ .children(null)
|
|
|
+ .documentId(id)
|
|
|
+ .name(filename)
|
|
|
+ .parentId(parent.equals("null") ? null : parent)
|
|
|
+ .patNo(patNo)
|
|
|
+ .times(times)
|
|
|
+ .sort(null)
|
|
|
+ .path(path.replace(config.getEmrPath(), ""))
|
|
|
+ .createId(TokenUtil.getInstance().getTokenUserId())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ try {
|
|
|
+ FileUtil.writeBytes(file.getBytes(), path);
|
|
|
+ logger.fileChangeSend(build.getCreateId(), "上传文件");
|
|
|
+ logger.info("上传文件成功:文件名【{}】", build.getName());
|
|
|
+ } catch (IOException e) {
|
|
|
+ return R.fail(ExceptionEnum.ERROR_MESSAGE_BOX, e.getMessage());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ dao.insert(build);
|
|
|
+ } catch (Exception e) {
|
|
|
+ FileUtil.del(path);
|
|
|
+ logger.error("上传文件失败:文件名【{}】,错误信息:{}", build.getName(), e.getMessage());
|
|
|
+ return R.fail(ExceptionEnum.LOGICAL_ERROR, e.getMessage());
|
|
|
+ }
|
|
|
+ return R.ok(build);
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<String> getTaskNameList() {
|
|
|
+ List<String> result = new ArrayList<>();
|
|
|
+ for (ArchiveClass item : this.archiveMethods) {
|
|
|
+ if (!item.getStartupParameter()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (!item.getTest())
|
|
|
+ result.add(item.getName());
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ public ResultVo<String> addDir(PatientArchive archive) {
|
|
|
+ archive.setId(IdUtil.simpleUUID());
|
|
|
+ archive.setCreateId(TokenUtil.getInstance().getTokenUserId());
|
|
|
+ archive.setSort(null);
|
|
|
+ dirNameRepeat(archive);
|
|
|
+ dao.insert(archive);
|
|
|
+ ArchiveLog logger = getPatLog(archive.getPatNo(), archive.getTimes(), "新增文件夹");
|
|
|
+ logger.fileChangeSend(TokenUtil.getInstance().getTokenUserId(), "新增文件夹");
|
|
|
+ logger.info("名称:{}", archive.getName());
|
|
|
+ return R.ok();
|
|
|
+ }
|
|
|
+
|
|
|
+ public ResultVo<String> delFile(PatientArchive archive) {
|
|
|
+ if (dao.hasDir(archive.getPatNo(), archive.getTimes(), archive.getId())) {
|
|
|
+ return R.fail(ExceptionEnum.LOGICAL_ERROR, "文件夹下面还存在文件无法删除。");
|
|
|
+ }
|
|
|
+
|
|
|
+ ArchiveLog logger = getPatLog(archive.getPatNo(), archive.getTimes(), "删除文件");
|
|
|
+
|
|
|
+ if (!archive.getIsDir()) {
|
|
|
+ FileUtil.del(config.getEmrPath() + "/" + archive.getPath());
|
|
|
+ }
|
|
|
+ PatientArchive oldData = dao.selectById(archive.getId());
|
|
|
+ dao.deleteById(archive.getId());
|
|
|
+ logger.warn("删除{}:文件名 {}", oldData.getIsDir() ? "文件夹" : "文件", oldData.getName());
|
|
|
+ logger.fileChangeSend(TokenUtil.getInstance().getTokenUserId(), "删除文件");
|
|
|
+ return R.ok(ExceptionEnum.SUCCESS_AND_EL_MESSAGE);
|
|
|
+ }
|
|
|
+
|
|
|
+ public ResultVo<String> rename(String id, String name) {
|
|
|
+ PatientArchive oldData = dao.selectById(id);
|
|
|
+ PatientArchive build = PatientArchive.builder()
|
|
|
+ .id(id)
|
|
|
+ .name(name)
|
|
|
+ .patNo(oldData.getPatNo())
|
|
|
+ .times(oldData.getTimes())
|
|
|
+ .build();
|
|
|
+ dirNameRepeat(build);
|
|
|
+ ArchiveLog logger = getPatLog(oldData.getPatNo(), oldData.getTimes(), "重命名");
|
|
|
+ logger.info("文件id:{},原名称:【{}】,新名称:【{}】", id, oldData.getName(), name);
|
|
|
+ logger.fileChangeSend(TokenUtil.getInstance().getTokenUserId(), "重命名文件");
|
|
|
+ dao.updateById(build);
|
|
|
+ return R.ok(ExceptionEnum.SUCCESS_AND_EL_MESSAGE);
|
|
|
+ }
|
|
|
+
|
|
|
+ public JSONArray getAllLog(String patNo, Integer times) {
|
|
|
+ String path = config.getEmrPath() + "/" + patNo + "/" + times + "/archive.txt";
|
|
|
+ if (!FileUtil.exist(path)) {
|
|
|
+ return new JSONArray();
|
|
|
+ }
|
|
|
+ String data = FileUtil.readString(path, StandardCharsets.UTF_8);
|
|
|
+ return new JSONArray("[" + data + "]");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void dirNameRepeat(PatientArchive data) {
|
|
|
+ boolean repeat = dao.dirNameRepeat(data);
|
|
|
+ if (repeat) {
|
|
|
+ throw new BizException(ExceptionEnum.LOGICAL_ERROR, "文件夹名称重复请重新命名。");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|