# Conflicts: # dlp-drugtesting-biz/src/main/java/digital/laboratory/platform/inspection/service/impl/InspectRecordServiceImpl.javamaster
						commit
						1d6eb3fcd5
					
				@ -0,0 +1,63 @@ | 
				
			||||
package digital.laboratory.platform.inspection.config; | 
				
			||||
 | 
				
			||||
import org.springframework.boot.context.properties.ConfigurationProperties; | 
				
			||||
import org.springframework.stereotype.Component; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * 贵阳禁毒-情报平台推送数据配置 | 
				
			||||
 */ | 
				
			||||
@Component | 
				
			||||
@ConfigurationProperties(prefix = "gyjd.labscare.api") | 
				
			||||
public class ApiPathProperties { | 
				
			||||
    /** | 
				
			||||
     * api的ip | 
				
			||||
     */ | 
				
			||||
    private String host; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 委托书推送的接口 | 
				
			||||
     */ | 
				
			||||
    private String entrustLetter; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 确认书推送接口 | 
				
			||||
     */ | 
				
			||||
    private String confirmLetter; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 生物检材定性记录 | 
				
			||||
     */ | 
				
			||||
    private String biologyQualitativeRecord; | 
				
			||||
 | 
				
			||||
    public String getHost() { | 
				
			||||
        return host; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void setHost(String host) { | 
				
			||||
        this.host = host; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public String getEntrustLetter() { | 
				
			||||
        return host + entrustLetter; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void setEntrustLetter(String entrustLetter) { | 
				
			||||
        this.entrustLetter = entrustLetter; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public String getConfirmLetter() { | 
				
			||||
        return host + confirmLetter; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void setConfirmLetter(String confirmLetter) { | 
				
			||||
        this.confirmLetter = confirmLetter; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public String getBiologyQualitativeRecord() { | 
				
			||||
        return host + biologyQualitativeRecord; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void setBiologyQualitativeRecord(String biologyQualitativeRecord) { | 
				
			||||
        this.biologyQualitativeRecord = biologyQualitativeRecord; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,37 @@ | 
				
			||||
package digital.laboratory.platform.inspection.event; | 
				
			||||
 | 
				
			||||
import lombok.Getter; | 
				
			||||
import org.springframework.context.ApplicationEvent; | 
				
			||||
 | 
				
			||||
import java.time.Clock; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * 推送数据到LabsCare 数据平台上的spring 事件 | 
				
			||||
 */ | 
				
			||||
@Getter | 
				
			||||
public class PushDataToLabsCareEvent extends ApplicationEvent { | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 实验id | 
				
			||||
     */ | 
				
			||||
    private final String testId; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 推送的数据类型, 1 鉴定委托书数据 | 2 鉴定事项确认书数据 |  3 生物检材定性记录报告数据 | 
				
			||||
     */ | 
				
			||||
    private final Integer pushType; | 
				
			||||
 | 
				
			||||
    public PushDataToLabsCareEvent(Object source, String testId, Integer pushType) { | 
				
			||||
        super(source); | 
				
			||||
        this.testId = testId; | 
				
			||||
        this.pushType = pushType; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public PushDataToLabsCareEvent(Object source, Clock clock, String testId, Integer pushType) { | 
				
			||||
        super(source, clock); | 
				
			||||
        this.testId = testId; | 
				
			||||
        this.pushType = pushType; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
 | 
				
			||||
} | 
				
			||||
@ -0,0 +1,51 @@ | 
				
			||||
package digital.laboratory.platform.inspection.listener; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import digital.laboratory.platform.inspection.event.PushDataToLabsCareEvent; | 
				
			||||
import digital.laboratory.platform.inspection.service.PushDataToLabsCareService; | 
				
			||||
import digital.laboratory.platform.inspection.threadpool.GlobalThreadPool; | 
				
			||||
import lombok.extern.slf4j.Slf4j; | 
				
			||||
import org.springframework.context.ApplicationListener; | 
				
			||||
import org.springframework.stereotype.Component; | 
				
			||||
 | 
				
			||||
import javax.annotation.Resource; | 
				
			||||
import java.util.concurrent.CompletableFuture; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * 监听有关推送数据到LabsCare 平台的事件, 采用异步 | 
				
			||||
 */ | 
				
			||||
@Slf4j | 
				
			||||
@Component | 
				
			||||
public class PushDataToLabsCareEventListener implements ApplicationListener<PushDataToLabsCareEvent> { | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private PushDataToLabsCareService pushDataToLabsCareService; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 处理委托数据推送事件 | 
				
			||||
     * | 
				
			||||
     * @param event 委托数据推送事件对象 | 
				
			||||
     */ | 
				
			||||
    @Override | 
				
			||||
    public void onApplicationEvent(PushDataToLabsCareEvent event) { | 
				
			||||
        CompletableFuture.runAsync(() -> { | 
				
			||||
            // 处理不同的推送类型
 | 
				
			||||
            switch (event.getPushType()) { | 
				
			||||
                case 3: // 推送委托书数据
 | 
				
			||||
                    try { | 
				
			||||
                        pushDataToLabsCareService.pushBiologyQualitativeRecordData(event.getTestId()); | 
				
			||||
                    } catch (Exception e) { | 
				
			||||
                        log.error("实验id为 {} 的生物定性检验记录数据推送失败!", event.getTestId(), e); | 
				
			||||
                    } | 
				
			||||
                    break; | 
				
			||||
                default: | 
				
			||||
                    log.warn("未知的推送类型: {}", event.getPushType()); | 
				
			||||
                    break; | 
				
			||||
            } | 
				
			||||
        }, GlobalThreadPool.getInstance()).exceptionally(e -> { | 
				
			||||
            log.error("推送数据到 LabsCare 失败", e); | 
				
			||||
            return null; | 
				
			||||
        }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
} | 
				
			||||
@ -0,0 +1,99 @@ | 
				
			||||
package digital.laboratory.platform.inspection.service; | 
				
			||||
 | 
				
			||||
import com.deepoove.poi.XWPFTemplate; | 
				
			||||
import digital.laboratory.platform.sys.entity.Area; | 
				
			||||
import digital.laboratory.platform.sys.entity.SysOrg; | 
				
			||||
import digital.laboratory.platform.sys.entity.SysUser; | 
				
			||||
import org.springframework.web.multipart.MultipartFile; | 
				
			||||
 | 
				
			||||
import javax.servlet.http.HttpServletResponse; | 
				
			||||
import java.io.ByteArrayInputStream; | 
				
			||||
import java.util.List; | 
				
			||||
import java.util.Map; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * 通用的feign请求封装接口服务层接口 | 
				
			||||
 */ | 
				
			||||
public interface CommonFeignService { | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用获取用户机构 | 
				
			||||
     * @param orgId | 
				
			||||
     * @return | 
				
			||||
     */ | 
				
			||||
    SysOrg remoteGetSysOrg(String orgId); | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 根据权限和机构远程获取用户列表 | 
				
			||||
     * | 
				
			||||
     * @param orgId    组织ID | 
				
			||||
     * @param permission 权限列表 | 
				
			||||
     * @return 用户列表 | 
				
			||||
     */ | 
				
			||||
    List<SysUser> remoteGetUsersByPermission(String orgId, List<String> permission); | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 通过机构ID远程获取机构所在省市信息 | 
				
			||||
     * | 
				
			||||
     * @param orgId 机构ID | 
				
			||||
     * @return 包含机构所在省市信息的Area对象列表 | 
				
			||||
     * @throws RuntimeException 当根据机构ID获取机构所在省市信息失败时抛出 | 
				
			||||
     */ | 
				
			||||
    List<Area> remoteGetProvinceCityInfo(String orgId); | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用获取用户信息 | 
				
			||||
     * @param username | 
				
			||||
     * @return | 
				
			||||
     */ | 
				
			||||
    SysUser remoteGetUserByUsername(String username); | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用获取用户信息 | 
				
			||||
     * @param userId | 
				
			||||
     * @return | 
				
			||||
     */ | 
				
			||||
    SysUser remoteGetUserById(String userId); | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用生成word,并转成pdf | 
				
			||||
     * | 
				
			||||
     * @param template | 
				
			||||
     * @param originalFilename 文件名 | 
				
			||||
     * @param savePath         保存到minio路径 | 
				
			||||
     * @return | 
				
			||||
     * @throws Exception | 
				
			||||
     */ | 
				
			||||
    boolean remoteGenerateWord2PDF(XWPFTemplate template, String originalFilename, String savePath) throws Exception; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用根据文件路径获取文件 | 
				
			||||
     * @param filePath minio上的文件路径 | 
				
			||||
     * @return | 
				
			||||
     * @throws Exception | 
				
			||||
     */ | 
				
			||||
    ByteArrayInputStream remoteGetFile(String filePath) throws Exception; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用根据文件路径获取文件 | 
				
			||||
     * @param filePath minio上的文件路径 | 
				
			||||
     * @param fileName 名称 | 
				
			||||
     * @param httpServletResponse | 
				
			||||
     * @throws Exception | 
				
			||||
     */ | 
				
			||||
    void remoteGetFile(String filePath, String fileName, HttpServletResponse httpServletResponse) throws Exception; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用根据文件路径获取文件列表 | 
				
			||||
     * @param filePath minio上的文件路径 | 
				
			||||
     */ | 
				
			||||
    List<String> remoteGetFileList(String filePath); | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用-上传文件 | 
				
			||||
     * @param file 上传的文件对象 | 
				
			||||
     * @param path 上传到minio的位置 | 
				
			||||
     * @return | 
				
			||||
     */ | 
				
			||||
    Map<String, String> remoteUploadFile(MultipartFile file, String path); | 
				
			||||
} | 
				
			||||
@ -0,0 +1,17 @@ | 
				
			||||
package digital.laboratory.platform.inspection.service; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * @author ChenJiangBao | 
				
			||||
 * @version 1.0 | 
				
			||||
 * @description: 推送数据到labscare平台接口 | 
				
			||||
 * @date 2025/3/31 9:28 | 
				
			||||
 */ | 
				
			||||
public interface PushDataToLabsCareService { | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 推送生物检材定性记录 | 
				
			||||
     * @param testId 实验id | 
				
			||||
     */ | 
				
			||||
    void pushBiologyQualitativeRecordData(String testId); | 
				
			||||
 | 
				
			||||
} | 
				
			||||
@ -0,0 +1,262 @@ | 
				
			||||
package digital.laboratory.platform.inspection.service.impl; | 
				
			||||
 | 
				
			||||
import cn.hutool.core.io.IoUtil; | 
				
			||||
import cn.hutool.core.io.file.FileNameUtil; | 
				
			||||
import cn.hutool.core.util.StrUtil; | 
				
			||||
import com.deepoove.poi.XWPFTemplate; | 
				
			||||
import digital.laboratory.platform.common.core.constant.CommonConstants; | 
				
			||||
import digital.laboratory.platform.common.core.util.R; | 
				
			||||
import digital.laboratory.platform.common.feign.RemoteWord2PDFService; | 
				
			||||
import digital.laboratory.platform.common.oss.service.OssFile; | 
				
			||||
import digital.laboratory.platform.inspection.service.CommonFeignService; | 
				
			||||
import digital.laboratory.platform.sys.dto.UserInfo; | 
				
			||||
import digital.laboratory.platform.sys.entity.Area; | 
				
			||||
import digital.laboratory.platform.sys.entity.SysOrg; | 
				
			||||
import digital.laboratory.platform.sys.entity.SysUser; | 
				
			||||
import digital.laboratory.platform.sys.feign.RemoteOrgService; | 
				
			||||
import digital.laboratory.platform.sys.feign.RemoteUserService; | 
				
			||||
import feign.Response; | 
				
			||||
import lombok.extern.slf4j.Slf4j; | 
				
			||||
import org.apache.commons.io.output.ByteArrayOutputStream; | 
				
			||||
import org.springframework.mock.web.MockMultipartFile; | 
				
			||||
import org.springframework.stereotype.Service; | 
				
			||||
import org.springframework.web.multipart.MultipartFile; | 
				
			||||
 | 
				
			||||
import javax.annotation.Resource; | 
				
			||||
import javax.servlet.http.HttpServletResponse; | 
				
			||||
import java.io.ByteArrayInputStream; | 
				
			||||
import java.util.Base64; | 
				
			||||
import java.util.HashMap; | 
				
			||||
import java.util.List; | 
				
			||||
import java.util.Map; | 
				
			||||
import java.util.stream.Collectors; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * 通用的feign请求封装接口服务层接口 实现类 | 
				
			||||
 */ | 
				
			||||
@Slf4j | 
				
			||||
@Service | 
				
			||||
public class CommonFeignServiceImpl implements CommonFeignService { | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private RemoteOrgService remoteOrgService; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private RemoteUserService remoteUserService; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private RemoteWord2PDFService remoteWord2PDFService; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private OssFile ossFile; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 根据用户获取远程系统机构信息 | 
				
			||||
     * | 
				
			||||
     * @param orgId 用户信息 | 
				
			||||
     * @return 对应的远程系统机构信息 | 
				
			||||
     * @throws RuntimeException 如果未找到对应的机构信息,则抛出运行时异常 | 
				
			||||
     */ | 
				
			||||
    @Override | 
				
			||||
    public SysOrg remoteGetSysOrg(String orgId) { | 
				
			||||
        SysOrg sysOrg = null; | 
				
			||||
        R<SysOrg> r = remoteOrgService.getByIdWithoutToken(orgId); | 
				
			||||
        if (r != null && r.getCode() == CommonConstants.SUCCESS) { | 
				
			||||
            sysOrg = r.getData(); | 
				
			||||
        } else { | 
				
			||||
            throw new RuntimeException(String.format("没有找到 orgId 为 %s 的机构, 请确认用户所属机构的正确性!", orgId)); | 
				
			||||
        } | 
				
			||||
        return sysOrg; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 取指定环节可用的用户列表 | 
				
			||||
     * 当流程进行到某个环节的时候, 需要某个用户对这个环节进行处理(通过或不通过)。 | 
				
			||||
     * 这里取可用的用户列表 | 
				
			||||
     * <p> | 
				
			||||
     * 涉及到的环境有以下几个: | 
				
			||||
     * 1、创建委托及提交 | 
				
			||||
     * 委托的提交者就是委托的创建者。不存在不通过的可能。 | 
				
			||||
     * 2、审核 | 
				
			||||
     * 审核者有几个条件: (1)必须是鉴定中心的工作人员 (2)必须拥有委托审核权限 | 
				
			||||
     * 3、审批 | 
				
			||||
     * 审核者有几个条件: (1)必须是鉴定中心的工作人员 (2)必须拥有委托审批权限 | 
				
			||||
     * 4、送检确认 | 
				
			||||
     * 送检确认者有几个条件: (1)必须与委托的创建者是同一个机构, 或上级机构的人 (2)必须拥有委托送检确认权限 | 
				
			||||
     * 5、受理 | 
				
			||||
     * 受理者有几个条件: (1)必须是鉴定中心的工作人员 (2)必须拥有委托受理权限 | 
				
			||||
     * | 
				
			||||
     * @return | 
				
			||||
     */ | 
				
			||||
    /** | 
				
			||||
     * 根据权限和机构远程获取用户列表 | 
				
			||||
     * | 
				
			||||
     * @param orgId    组织ID | 
				
			||||
     * @param permission 权限列表 | 
				
			||||
     * @return 用户列表 | 
				
			||||
     */ | 
				
			||||
    @Override | 
				
			||||
    public List<SysUser> remoteGetUsersByPermission(String orgId, List<String> permission) { | 
				
			||||
        R<List<SysUser>> r = remoteUserService.innerGetUsersByPermission(orgId, permission); | 
				
			||||
        if (r != null && r.getCode() == CommonConstants.SUCCESS) { | 
				
			||||
            return r.getData(); | 
				
			||||
        } else { | 
				
			||||
            throw new RuntimeException(String.format("根据权限 [%s] 和 机构id [%s] 远程获取用户列表!", permission, orgId)); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 通过机构ID远程获取机构所在省市信息 | 
				
			||||
     * | 
				
			||||
     * @param orgId 机构ID | 
				
			||||
     * @return 包含机构所在省市信息的Area对象列表 | 
				
			||||
     * @throws RuntimeException 当根据机构ID获取机构所在省市信息失败时抛出 | 
				
			||||
     */ | 
				
			||||
    @Override | 
				
			||||
    public List<Area> remoteGetProvinceCityInfo(String orgId) { | 
				
			||||
        List<Area> result = null; | 
				
			||||
        R<List<Area>> r = remoteOrgService.fetchProvinceCityInfoByOrgId(orgId); | 
				
			||||
        if (r != null && r.getCode() == CommonConstants.SUCCESS) { | 
				
			||||
            result = r.getData(); | 
				
			||||
        } else { | 
				
			||||
            throw new RuntimeException("根据机构id获取机构所在省市信息失败!"); | 
				
			||||
        } | 
				
			||||
        return result; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用或者用户信息 | 
				
			||||
     * @param username | 
				
			||||
     * @return | 
				
			||||
     */ | 
				
			||||
    @Override | 
				
			||||
    public SysUser remoteGetUserByUsername(String username){ | 
				
			||||
        R<UserInfo> info = remoteUserService.innerGetUserInfoByUsername(username); | 
				
			||||
        if (info != null && info.getCode() == CommonConstants.FAIL) { | 
				
			||||
            throw new RuntimeException(String.format("获取用户名为 %s 的用户信息失败!", username)); | 
				
			||||
        } | 
				
			||||
        return info.getData().getSysUser(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用获取用户信息 | 
				
			||||
     * @param userId | 
				
			||||
     * @return | 
				
			||||
     */ | 
				
			||||
    @Override | 
				
			||||
    public SysUser remoteGetUserById(String userId){ | 
				
			||||
        R<SysUser> info = remoteUserService.innerGetById(userId); | 
				
			||||
        if (info != null && info.getCode() == CommonConstants.FAIL) { | 
				
			||||
            throw new RuntimeException(String.format("获取用户名id为 %s 的用户信息失败!", userId)); | 
				
			||||
        } | 
				
			||||
        return info.getData(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    public boolean remoteGenerateWord2PDF(XWPFTemplate template, String originalFilename, String savePath) throws Exception{ | 
				
			||||
        ByteArrayOutputStream fosWord = new ByteArrayOutputStream(); | 
				
			||||
        template.write(fosWord); | 
				
			||||
        template.close(); | 
				
			||||
 | 
				
			||||
        //------------
 | 
				
			||||
        ByteArrayInputStream fisWord = new ByteArrayInputStream(fosWord.toByteArray()); | 
				
			||||
        fosWord.close(); | 
				
			||||
 | 
				
			||||
        MockMultipartFile mockMultipartFile = new MockMultipartFile("file", originalFilename + ".docx", "image/jpg", fisWord); | 
				
			||||
        Response response = remoteWord2PDFService.word2pdf(mockMultipartFile); | 
				
			||||
        fisWord.close(); | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        ByteArrayOutputStream outPDF = new ByteArrayOutputStream(); | 
				
			||||
        IoUtil.copy(response.body().asInputStream(), outPDF, IoUtil.DEFAULT_MIDDLE_BUFFER_SIZE); | 
				
			||||
        ByteArrayInputStream isPDF = new ByteArrayInputStream(outPDF.toByteArray()); | 
				
			||||
        outPDF.close(); | 
				
			||||
 | 
				
			||||
        boolean b = ossFile.fileSave(savePath, isPDF); | 
				
			||||
 | 
				
			||||
        isPDF.close(); | 
				
			||||
 | 
				
			||||
        log.info("转换为 PDF 结束"); | 
				
			||||
        return b; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用根据文件路径获取文件 | 
				
			||||
     * @param filePath minio上的文件路径 | 
				
			||||
     * @return | 
				
			||||
     * @throws Exception | 
				
			||||
     */ | 
				
			||||
    @Override | 
				
			||||
    public ByteArrayInputStream remoteGetFile(String filePath) throws Exception { | 
				
			||||
        ByteArrayOutputStream bos = new ByteArrayOutputStream(); | 
				
			||||
        ossFile.fileGet(filePath, bos); | 
				
			||||
 | 
				
			||||
        byte[] templateArray = bos.toByteArray(); | 
				
			||||
 | 
				
			||||
        ByteArrayInputStream bis = new ByteArrayInputStream(templateArray); | 
				
			||||
        bos.close(); | 
				
			||||
        return bis; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用根据文件路径获取文件 | 
				
			||||
     * @param filePath minio上的文件路径 | 
				
			||||
     * @param fileName 名称 | 
				
			||||
     * @param httpServletResponse | 
				
			||||
     * @throws Exception | 
				
			||||
     */ | 
				
			||||
    @Override | 
				
			||||
    public void remoteGetFile(String filePath, String fileName, HttpServletResponse httpServletResponse) throws Exception { | 
				
			||||
        ossFile.fileGet(filePath, httpServletResponse.getOutputStream()); | 
				
			||||
        if (StrUtil.isNotBlank(fileName)) { | 
				
			||||
            httpServletResponse.setContentType(fileName); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用根据文件路径获取文件列表 | 
				
			||||
     * @param filePath minio上的文件路径 | 
				
			||||
     */ | 
				
			||||
    @Override | 
				
			||||
    public List<String> remoteGetFileList(String filePath) { | 
				
			||||
        if (StrUtil.isNotBlank(filePath)) { | 
				
			||||
            List<String> fileNameList = ossFile.fileList(filePath); | 
				
			||||
            List<String> fileList = fileNameList.stream().map(fileName -> { | 
				
			||||
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); | 
				
			||||
                try { | 
				
			||||
                    ossFile.fileGet(filePath + "/" + fileName, byteArrayOutputStream); | 
				
			||||
                    return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()); | 
				
			||||
                } catch (Exception e) { | 
				
			||||
                    e.printStackTrace(); | 
				
			||||
                    log.error("文件获取失败, 文件路径为: {}", filePath + "/" + fileName); | 
				
			||||
                } | 
				
			||||
                return null; | 
				
			||||
            }).collect(Collectors.toList()); | 
				
			||||
            return fileList; | 
				
			||||
        } else { | 
				
			||||
            throw new RuntimeException("文件路径不能为空!"); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 远程调用-上传文件 | 
				
			||||
     * @param file 上传的文件对象 | 
				
			||||
     * @param path 上传到minio的位置 | 
				
			||||
     * @return | 
				
			||||
     */ | 
				
			||||
    @Override | 
				
			||||
    public Map<String, String> remoteUploadFile(MultipartFile file, String path) { | 
				
			||||
        boolean r = ossFile.fileUpload(file, path); | 
				
			||||
        if (r) { | 
				
			||||
            HashMap<String, String> resultData = new HashMap<>(); | 
				
			||||
            resultData.put("fileName", FileNameUtil.getName(file.getOriginalFilename())); | 
				
			||||
            resultData.put("path", path); | 
				
			||||
            log.info("文件上传成功!"); | 
				
			||||
            return resultData; | 
				
			||||
        } else { | 
				
			||||
            String failMsg = String.format("文件名为 %s 的文件上传失败!", file.getOriginalFilename()); | 
				
			||||
            log.error(failMsg); | 
				
			||||
            throw new RuntimeException(failMsg); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,356 @@ | 
				
			||||
package digital.laboratory.platform.inspection.service.impl; | 
				
			||||
 | 
				
			||||
import cn.hutool.core.collection.CollUtil; | 
				
			||||
import cn.hutool.core.util.StrUtil; | 
				
			||||
import cn.hutool.json.JSONObject; | 
				
			||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; | 
				
			||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | 
				
			||||
import digital.laboratory.platform.inspection.config.ApiPathProperties; | 
				
			||||
import digital.laboratory.platform.inspection.constant.TestRecordSampleDataConstant; | 
				
			||||
import digital.laboratory.platform.inspection.convert.TestRecordSampleDataConverter; | 
				
			||||
import digital.laboratory.platform.inspection.entity.TestRecordInstrument; | 
				
			||||
import digital.laboratory.platform.inspection.entity.TestRecordMethod; | 
				
			||||
import digital.laboratory.platform.inspection.entity.TestRecordReagent; | 
				
			||||
import digital.laboratory.platform.inspection.entity.TestRecordSampleData; | 
				
			||||
import digital.laboratory.platform.inspection.mapper.TestRecordSampleDataMapper; | 
				
			||||
import digital.laboratory.platform.inspection.service.*; | 
				
			||||
import digital.laboratory.platform.inspection.vo.TestRecordReagentVO; | 
				
			||||
import digital.laboratory.platform.inspection.vo.TestRecordSampleDataExpandVO; | 
				
			||||
import digital.laboratory.platform.inspection.vo.TestRecordSampleDataVO; | 
				
			||||
import digital.laboratory.platform.inspetion.api.entity.EntrustInfo; | 
				
			||||
import digital.laboratory.platform.inspetion.api.entity.SampleInfo; | 
				
			||||
import digital.laboratory.platform.inspetion.api.entity.TestRecord; | 
				
			||||
import digital.laboratory.platform.othersys.utils.HttpsUtils; | 
				
			||||
import digital.laboratory.platform.sys.entity.Drug; | 
				
			||||
import digital.laboratory.platform.sys.entity.SysUser; | 
				
			||||
import lombok.extern.slf4j.Slf4j; | 
				
			||||
import org.springframework.http.*; | 
				
			||||
import org.springframework.scheduling.annotation.Scheduled; | 
				
			||||
import org.springframework.stereotype.Service; | 
				
			||||
import org.springframework.web.client.RestClientException; | 
				
			||||
 | 
				
			||||
import javax.annotation.Resource; | 
				
			||||
import java.time.format.DateTimeFormatter; | 
				
			||||
import java.util.*; | 
				
			||||
import java.util.function.Function; | 
				
			||||
import java.util.stream.Collectors; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * @author ChenJiangBao | 
				
			||||
 * @version 1.0 | 
				
			||||
 * @description: 推送数据到labscare平台接口 | 
				
			||||
 * @date 2025/3/31 9:31 | 
				
			||||
 */ | 
				
			||||
@Slf4j | 
				
			||||
@Service | 
				
			||||
public class PushDataToLabsCareServiceImpl implements PushDataToLabsCareService { | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private TestRecordService testRecordService; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private EntrustInfoService entrustInfoService; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private SampleInfoService sampleInfoService; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private TestRecordReagentService testRecordReagentService; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private TestRecordSampleDataMapper testRecordSampleDataMapper; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private TestRecordInstrumentService testRecordInstrumentService; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private TestRecordMethodService testRecordMethodService; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private InspectRecordService inspectRecordService; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private ProcessInspectDataService processInspectDataService; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private CommonFeignService commonFeignService; | 
				
			||||
 | 
				
			||||
    @Resource | 
				
			||||
    private ApiPathProperties apiPathProperties; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    private final static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日"); | 
				
			||||
 | 
				
			||||
    // "#NULL#" 作为空数据的占位符, 对于系统中没有的信息以占位符代替
 | 
				
			||||
    public final static String NULL_PLACEHOLDER = "#NULL#"; | 
				
			||||
 | 
				
			||||
    private final static String BIOLOGY_QUALITATIVE_RECORD = "BiologyQualitativeRecord"; | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 做一个定时推送,对于推送失败的委托进行重新推送, 每天凌晨1点推送 | 
				
			||||
     */ | 
				
			||||
//    @Scheduled(cron = "30 * * * * ?") // 测试
 | 
				
			||||
    @Scheduled(cron = "0 0 2 * * ?")  // 每天凌晨 2 点执行
 | 
				
			||||
    public void timingPushDataToLabsCare() { | 
				
			||||
        // 查询检验鉴定推送失败的委托
 | 
				
			||||
        List<EntrustInfo> entrustList = entrustInfoService.list( | 
				
			||||
                Wrappers.<EntrustInfo>lambdaQuery() | 
				
			||||
                        .and(wrapper -> wrapper | 
				
			||||
                                .eq(EntrustInfo::getPushFlag, "") | 
				
			||||
                                .or() | 
				
			||||
                                .isNull(EntrustInfo::getPushFlag) | 
				
			||||
                                .or() | 
				
			||||
                                .like(EntrustInfo::getPushFlag, "false") | 
				
			||||
                        ) | 
				
			||||
        ); | 
				
			||||
        if (CollUtil.isEmpty(entrustList)) { | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
        Map<String, EntrustInfo> entrustInfoMap = entrustList.stream().collect(Collectors.toMap(EntrustInfo::getId, Function.identity())); | 
				
			||||
        // 获取所有完成实验的数据
 | 
				
			||||
        List<TestRecord> testRecordList = testRecordService.list(Wrappers.<TestRecord>lambdaQuery() | 
				
			||||
                .eq(TestRecord::getStatus, 5).in(TestRecord::getBusinessId, entrustList.stream() | 
				
			||||
                        .map(EntrustInfo::getId).collect(Collectors.toList())) | 
				
			||||
        ); | 
				
			||||
        if (CollUtil.isEmpty(testRecordList)) { | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        for (TestRecord testRecord : testRecordList) { | 
				
			||||
            EntrustInfo entrustInfo = entrustInfoMap.get(testRecord.getBusinessId()); | 
				
			||||
            String pushFlag = entrustInfo.getPushFlag(); | 
				
			||||
            if (StrUtil.isBlank(pushFlag)) { | 
				
			||||
                pushBiologyQualitativeRecordData(testRecord.getId()); | 
				
			||||
            } else { | 
				
			||||
                List<String> flagList = StrUtil.split(pushFlag, StrUtil.COMMA).stream().filter(str -> str.contains("false")).collect(Collectors.toList()); | 
				
			||||
                for (String flag : flagList) { | 
				
			||||
                    if (flag.contains(BIOLOGY_QUALITATIVE_RECORD)) { | 
				
			||||
                        pushBiologyQualitativeRecordData(testRecord.getId()); | 
				
			||||
                    } else { | 
				
			||||
 | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 推送生物检材定性记录 | 
				
			||||
     * @param testId 实验id | 
				
			||||
     */ | 
				
			||||
    @Override | 
				
			||||
    public void pushBiologyQualitativeRecordData(String testId) { | 
				
			||||
        TestRecord testRecord = testRecordService.getById(testId); | 
				
			||||
        if (testRecord == null) { | 
				
			||||
            log.error("实验id为 [{}] 的实验信息查询为空!", testId); | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
        // 获取委托信息
 | 
				
			||||
        EntrustInfo entrustInfo = entrustInfoService.getById(testRecord.getBusinessId()); | 
				
			||||
 | 
				
			||||
        JSONObject recordJsonPayload = buildBiologyQualitativeRecordJsonPayload(entrustInfo, testRecord); | 
				
			||||
 | 
				
			||||
        String successFlag = BIOLOGY_QUALITATIVE_RECORD + ":true"; | 
				
			||||
        String failureFlag = BIOLOGY_QUALITATIVE_RECORD + ":false"; | 
				
			||||
        try { | 
				
			||||
            ResponseEntity<String> response = exchangeRemoteApi(recordJsonPayload, apiPathProperties.getBiologyQualitativeRecord()); | 
				
			||||
 | 
				
			||||
            if (response.getStatusCode().is2xxSuccessful() && response.getBody().contains("\"status\":200")) { | 
				
			||||
                updatePushFlag(entrustInfo, BIOLOGY_QUALITATIVE_RECORD, successFlag); | 
				
			||||
                log.info("推送生物定性检验记录数据成功, 受理编号: {}, 响应: {}", entrustInfo.getAcceptNo(), response.getBody()); | 
				
			||||
            } else { | 
				
			||||
                updatePushFlag(entrustInfo, BIOLOGY_QUALITATIVE_RECORD, failureFlag); | 
				
			||||
                log.warn("推送生物定性检验记录数据失败, 受理编号: {}, 状态码: {}, 响应: {}", | 
				
			||||
                        entrustInfo.getAcceptNo(), response.getStatusCode(), response.getBody()); | 
				
			||||
            } | 
				
			||||
        } catch (RestClientException e) { | 
				
			||||
            log.error("推送生物定性检验记录数据到 LabsCare 失败, 受理编号: {}", entrustInfo.getAcceptNo(), e); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private JSONObject buildBiologyQualitativeRecordJsonPayload(EntrustInfo entrustInfo, TestRecord testRecord) { | 
				
			||||
        // 查询封装需要用到的数据
 | 
				
			||||
        List<SampleInfo> sampleInfoList = sampleInfoService | 
				
			||||
                .lambdaQuery() | 
				
			||||
                .in(SampleInfo::getId, testRecord.getSampleTestList()) | 
				
			||||
                .list(); | 
				
			||||
        // 检材转map
 | 
				
			||||
        Map<String, SampleInfo> sampleInfoMap = sampleInfoList.stream().collect(Collectors.toMap(SampleInfo::getAcceptNo, Function.identity())); | 
				
			||||
        // 对受理编号进行分隔
 | 
				
			||||
        List<String> splitAcceptNo = StrUtil.split(entrustInfo.getAcceptNo(), "-"); | 
				
			||||
 | 
				
			||||
        // 设置使用的标准物质和试剂
 | 
				
			||||
        List<TestRecordReagentVO> references = testRecordReagentService.voListByWrapper(Wrappers.<TestRecordReagent>lambdaQuery() | 
				
			||||
                .in(TestRecordReagent::getId, testRecord.getReagentConsumablesList()) | 
				
			||||
                .eq(TestRecordReagent::getCategory, "标准物质")); | 
				
			||||
        // 本次实验使用的标准品
 | 
				
			||||
        String stdStr = references.stream().map(TestRecordReagentVO::getReagentConsumableName).collect(Collectors.joining("、")); | 
				
			||||
 | 
				
			||||
        // 获取标准物质对应的毒品成分
 | 
				
			||||
        Map<String, Drug> drugMap = references.stream().map(TestRecordReagentVO::getDrug).collect(Collectors.toMap(Drug::getEnglishName, Function.identity())); | 
				
			||||
 | 
				
			||||
        // 获取检测人用户信息
 | 
				
			||||
        SysUser testUser = commonFeignService.remoteGetUserById(testRecord.getTestUserId()); | 
				
			||||
 | 
				
			||||
        // 检测数据信息
 | 
				
			||||
        Map<String, List<TestRecordSampleDataVO>> testDataGroupBySampleNO = testRecordSampleDataMapper | 
				
			||||
                .queryTestRecordSampleDataVOList(Wrappers.<TestRecordSampleData>lambdaQuery() | 
				
			||||
                        .eq(TestRecordSampleData::getTestId, testRecord.getId()) | 
				
			||||
                        .eq(TestRecordSampleData::getSampleType, TestRecordSampleDataConstant.SAMPLE_TYPE_ANALYTE) | 
				
			||||
                ) // 数据库查询, 这个是非标准物质的检测数据
 | 
				
			||||
                .stream().collect(Collectors.groupingBy(TestRecordSampleDataVO::getSampleNo)); // 根据查询出来的结果进行分组
 | 
				
			||||
 | 
				
			||||
        Map<String, TestRecordSampleDataVO> stdTestDataMap = testRecordSampleDataMapper | 
				
			||||
                .queryTestRecordSampleDataVOList(Wrappers.<TestRecordSampleData>lambdaQuery() | 
				
			||||
                        .eq(TestRecordSampleData::getTestId, testRecord.getId()) | 
				
			||||
                        .eq(TestRecordSampleData::getSampleType, TestRecordSampleDataConstant.SAMPLE_TYPE_STD) | 
				
			||||
                ) // 数据库查询, 这个是非标准物质的检测数据
 | 
				
			||||
                .stream().collect(Collectors.toMap(TestRecordSampleDataVO::getCompoundName, Function.identity())); // 根据查询出来的结果进行分组
 | 
				
			||||
 | 
				
			||||
        // 查询使用的仪器
 | 
				
			||||
        List<String> deviceIdList = testRecord.getDeviceIdList(); | 
				
			||||
        List<TestRecordInstrument> instruments = CollectionUtils.isEmpty(deviceIdList) | 
				
			||||
                ? Collections.emptyList() | 
				
			||||
                : testRecordInstrumentService.list(Wrappers.<TestRecordInstrument>lambdaQuery() | 
				
			||||
                .in(TestRecordInstrument::getId, deviceIdList)); | 
				
			||||
        String useInstrumentNameStr = CollectionUtils.isEmpty(instruments) | 
				
			||||
                ? NULL_PLACEHOLDER | 
				
			||||
                : instruments.stream() | 
				
			||||
                .map(TestRecordInstrument::getInstrumentName) | 
				
			||||
                .collect(Collectors.joining(",")); | 
				
			||||
 | 
				
			||||
        // 使用的方法
 | 
				
			||||
        List<String> testMethodList = testRecord.getTestMethodList(); | 
				
			||||
        List<TestRecordMethod> testRecordMethodList = CollectionUtils.isEmpty(testMethodList) | 
				
			||||
                ? Collections.emptyList() | 
				
			||||
                : testRecordMethodService.list(Wrappers.<TestRecordMethod>lambdaQuery() | 
				
			||||
                .in(TestRecordMethod::getId, testMethodList)); | 
				
			||||
        String useMethodNameStr = CollectionUtils.isEmpty(instruments) | 
				
			||||
                ? NULL_PLACEHOLDER | 
				
			||||
                : testRecordMethodList.stream() | 
				
			||||
                .map(TestRecordMethod::getMethodName) | 
				
			||||
                .collect(Collectors.joining(",")); | 
				
			||||
 | 
				
			||||
        // 开始封装数据参数
 | 
				
			||||
        JSONObject jsonObject = new JSONObject(); | 
				
			||||
        jsonObject.set("jcxz", inspectRecordService.buildMaterialCharacterDesc(sampleInfoList)); // 检材性状描述
 | 
				
			||||
        jsonObject.set("jdwsh", String.format("[%s]%s号", splitAcceptNo.get(0), splitAcceptNo.get(1))); // 鉴定文书号
 | 
				
			||||
        jsonObject.set("jysj", testRecord.getTestStartDate().format(formatter)); // 检验时间
 | 
				
			||||
        jsonObject.set("jyy1qm", testUser.getName()); // 检测人1
 | 
				
			||||
        jsonObject.set( | 
				
			||||
                "jyyq", | 
				
			||||
                String.format("对所送检材中是否含有 %s 成分进行定性检测", stdStr) | 
				
			||||
        ); // 检验要求,根据模板上的值来生成
 | 
				
			||||
        jsonObject.set("sjr1", entrustInfo.getDeliver1Name()); // 送检人1
 | 
				
			||||
        jsonObject.set("sjr2", entrustInfo.getDeliver2Name()); // 送检人2
 | 
				
			||||
        jsonObject.set("slrq", entrustInfo.getAcceptDate().format(formatter)); // 受理日期
 | 
				
			||||
        jsonObject.set("sprqm", testUser.getName()); // 检测人2
 | 
				
			||||
        jsonObject.set("swmbw", stdStr); // 生物检测目标物
 | 
				
			||||
        jsonObject.set("tpfj", NULL_PLACEHOLDER); // 图谱
 | 
				
			||||
        jsonObject.set("wtdw", entrustInfo.getEntrustDepartment()); | 
				
			||||
 | 
				
			||||
        // 生物检材定性记录样品信息
 | 
				
			||||
        List<JSONObject> sampleJsonList = new ArrayList<>(); | 
				
			||||
        testDataGroupBySampleNO.forEach((sampleNo,  testDataList) -> { | 
				
			||||
            JSONObject sampleJson = new JSONObject(); | 
				
			||||
            sampleJson.set("caseName", sampleNo); // 样品编号
 | 
				
			||||
 | 
				
			||||
            // 该样品编号对应的检测数据信息
 | 
				
			||||
            List<JSONObject> gauging = testDataList.stream().map(data -> { | 
				
			||||
                JSONObject testDataJson = new JSONObject(); | 
				
			||||
                testDataJson.set("blsjxdwc", data.getRtTimeError()); // 保留时间相对误差(%)
 | 
				
			||||
                TestRecordSampleDataVO stdData = stdTestDataMap.get(data.getCompoundName()); | 
				
			||||
                testDataJson.set("bzwzblsj", stdData.getTargetRtTime()); // 标准物质保留时间(min)
 | 
				
			||||
                testDataJson.set("bzwzfdb", stdData.getIonAbundanceRatio()); // 标准物质离子对相对丰度比
 | 
				
			||||
                testDataJson.set("bzwzwb", stdData.getCompoundName()); // 标准物质名称
 | 
				
			||||
                testDataJson.set("dcypfdb", data.getIonAbundanceRatio()); // 待测样品离子对相对丰度比
 | 
				
			||||
                testDataJson.set("dxlzd", data.getExpandList().stream().map(TestRecordSampleDataExpandVO::getQualitativeIonPair).collect(Collectors.joining(","))); // 定性离子对
 | 
				
			||||
                testDataJson.set("dxyq", useInstrumentNameStr); // 使用仪器
 | 
				
			||||
                testDataJson.set("fdbwc", data.getIonAbundanceRatioError()); // 丰度比相对偏差
 | 
				
			||||
                testDataJson.set("ffStr", useMethodNameStr); // 所用的方法
 | 
				
			||||
                testDataJson.set("gradientA", NULL_PLACEHOLDER); // 梯度洗脱参考条件-A(%)
 | 
				
			||||
                testDataJson.set("gradientB", NULL_PLACEHOLDER); // 梯度洗脱参考条件-B(%)
 | 
				
			||||
                testDataJson.set("gradientTime", NULL_PLACEHOLDER); // 梯度洗脱参考条件-时间(min)
 | 
				
			||||
                testDataJson.set("jcxz", sampleInfoMap.get(data.getSampleNo()).getForm()); // 检材性状描述
 | 
				
			||||
                testDataJson.set("jdwsh", String.format("[%s]%s号", splitAcceptNo.get(0), splitAcceptNo.get(1))); // 鉴定文书号
 | 
				
			||||
                testDataJson.set("jdyj", inspectRecordService.buildInspectOpinion(Collections.singletonList(TestRecordSampleDataConverter.voToEntity(data)))); | 
				
			||||
                testDataJson.set("jgpd", data.getWhetherCheckOut()); // 结果判定
 | 
				
			||||
                testDataJson.set("jysj", testRecord.getTestStartDate().format(formatter)); // 检验时间
 | 
				
			||||
                testDataJson.set("jyyq", String.format("对所送检材中是否含有 %s 成分进行定性检测", stdData.getCompoundCnName())); // 检验要求
 | 
				
			||||
                testDataJson.set("mbwblsj", data.getTargetRtTime()); // 目标物保留时间(min)
 | 
				
			||||
                testDataJson.set("projectName", "定性检测"); // 化合物名称, 对方公司并未标明该字段信息
 | 
				
			||||
                // 获取对应的化合物信息
 | 
				
			||||
                Drug drug = drugMap.get(data.getCompoundName()); | 
				
			||||
                testDataJson.set("pzdy", StrUtil.join(",", drug.getMainCollisionEnergy(), drug.getMinorCollisionEnergy())); // 碰撞电压
 | 
				
			||||
                testDataJson.set("rjwb", NULL_PLACEHOLDER); // 溶剂
 | 
				
			||||
                testDataJson.set("sjr1", entrustInfo.getDeliver1Name()); // 送检人1
 | 
				
			||||
                testDataJson.set("sjr2", entrustInfo.getDeliver2Name()); // 送检人2
 | 
				
			||||
                testDataJson.set("slrq", entrustInfo.getAcceptDate().format(formatter)); // 受理日期
 | 
				
			||||
                testDataJson.set("swbzgzyzb", NULL_PLACEHOLDER); // 标准工作溶液制备
 | 
				
			||||
                testDataJson.set("swdcypzb", NULL_PLACEHOLDER);  // 待测样品制备
 | 
				
			||||
                testDataJson.set("swmbw", drug.getName()); // 目标物
 | 
				
			||||
                testDataJson.set("swsj", NULL_PLACEHOLDER); // 上机
 | 
				
			||||
                testDataJson.set("swyxtj", NULL_PLACEHOLDER); // 	液相条件
 | 
				
			||||
                testDataJson.set("swzptj", NULL_PLACEHOLDER); // 质谱条件
 | 
				
			||||
                testDataJson.set("wjcjgpd", "±" + processInspectDataService.getHairCaseIonAbundanceRatioWithinErrorRange(stdData.getIonAbundanceRatio().doubleValue())); //相对离子丰度比的最大允许相对误差(%)
 | 
				
			||||
                testDataJson.set("wtdw", entrustInfo.getEntrustDepartment()); | 
				
			||||
                testDataJson.set("wzdy", StrUtil.join(",", drug.getMainDeclusteringPotential(), drug.getMinorDeclusteringPotential())); | 
				
			||||
                return testDataJson; | 
				
			||||
            }).collect(Collectors.toList()); | 
				
			||||
            sampleJson.set("gauging", gauging); // 生物检材定性记录检测信息
 | 
				
			||||
            sampleJsonList.add(sampleJson); | 
				
			||||
        }); | 
				
			||||
        jsonObject.set("sample", sampleJsonList); | 
				
			||||
        return jsonObject; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 通过POST请求与远程API进行交互 | 
				
			||||
     * | 
				
			||||
     * @param jsonObject 包含请求数据的JSONObject对象 | 
				
			||||
     * @param url        远程API的URL | 
				
			||||
     * @return ResponseEntity<String> 包含远程API响应数据的ResponseEntity对象 | 
				
			||||
     */ | 
				
			||||
    private ResponseEntity<String> exchangeRemoteApi(JSONObject jsonObject, String url) { | 
				
			||||
        HttpHeaders headers = new HttpHeaders(); | 
				
			||||
        headers.setContentType(MediaType.APPLICATION_JSON);  // 设置为 application/json
 | 
				
			||||
        HttpEntity<JSONObject> requestEntity = new HttpEntity<>(jsonObject, headers); | 
				
			||||
        ResponseEntity<String> response = HttpsUtils | 
				
			||||
                .genRestTemplate() | 
				
			||||
                .exchange( | 
				
			||||
                        url, | 
				
			||||
                        HttpMethod.POST, | 
				
			||||
                        requestEntity, | 
				
			||||
                        String.class | 
				
			||||
                ); | 
				
			||||
        return response; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * 更新推送标志 | 
				
			||||
     * | 
				
			||||
     * @param entrust   委托对象 | 
				
			||||
     * @param pushType    推送类型 EntrustLetter 委托书 | ItemConfirmLetter 事项确认书 | 
				
			||||
     * @param newFlag     新的推送标志 | 
				
			||||
     */ | 
				
			||||
    private void updatePushFlag(EntrustInfo entrust, String pushType, String newFlag) { | 
				
			||||
        List<String> flagList = Optional.ofNullable(StrUtil.split(entrust.getPushFlag(), StrUtil.COMMA)) | 
				
			||||
                .orElse(new ArrayList<>()); | 
				
			||||
 | 
				
			||||
        // 判断是否已有 指定的推送类型,如果有就替换,否则添加
 | 
				
			||||
        boolean exists = flagList.stream().anyMatch(flag -> flag.contains(pushType)); | 
				
			||||
        if (exists) { | 
				
			||||
            flagList.replaceAll(flag -> flag.contains(pushType) ? newFlag : flag); | 
				
			||||
        } else { | 
				
			||||
            flagList.add(newFlag); | 
				
			||||
        } | 
				
			||||
        // 更新标识, 防止定时任务那里连续推送数据时推送标识更新出错
 | 
				
			||||
        entrust.setPushFlag(String.join(",", flagList)); | 
				
			||||
        // 更新数据库
 | 
				
			||||
        entrustInfoService.update(Wrappers.<EntrustInfo>lambdaUpdate() | 
				
			||||
                .eq(EntrustInfo::getId, entrust.getId()) | 
				
			||||
                .set(EntrustInfo::getPushFlag, String.join(",", flagList))); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
} | 
				
			||||
@ -0,0 +1,52 @@ | 
				
			||||
package digital.laboratory.platform.inspection.threadpool; | 
				
			||||
 | 
				
			||||
import lombok.extern.slf4j.Slf4j; | 
				
			||||
import org.springframework.stereotype.Component; | 
				
			||||
 | 
				
			||||
import javax.annotation.PreDestroy; | 
				
			||||
import java.util.concurrent.*; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * 定义全局线程池 | 
				
			||||
 */ | 
				
			||||
@Slf4j | 
				
			||||
@Component | 
				
			||||
public class GlobalThreadPool { | 
				
			||||
 | 
				
			||||
    // 定义全局线程池,使用单列模式
 | 
				
			||||
    private static final ExecutorService THREAD_POOL = new ThreadPoolExecutor( | 
				
			||||
            10, // 核心线程数
 | 
				
			||||
            50, // 最大线程数
 | 
				
			||||
            60L, TimeUnit.SECONDS, // 空闲线程存活时间
 | 
				
			||||
            new LinkedBlockingQueue<Runnable>(100), // 任务队列
 | 
				
			||||
            Executors.defaultThreadFactory(), // 线程工厂
 | 
				
			||||
            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
 | 
				
			||||
    ); | 
				
			||||
 | 
				
			||||
    // 私有化构造方法,防止外部实例化
 | 
				
			||||
    private GlobalThreadPool() {} | 
				
			||||
 | 
				
			||||
    // 获取全局线程池实列
 | 
				
			||||
    public static ExecutorService getInstance() { | 
				
			||||
        return THREAD_POOL; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // 关闭线程池
 | 
				
			||||
    public static void shutdown() { | 
				
			||||
        THREAD_POOL.shutdown(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // 在应用关闭前执行
 | 
				
			||||
    @PreDestroy | 
				
			||||
    public void destroy() { | 
				
			||||
        log.info("Spring 应用关闭,正在关闭线程池..."); | 
				
			||||
        shutdown(); | 
				
			||||
        try { | 
				
			||||
            if (!THREAD_POOL.awaitTermination(60, TimeUnit.SECONDS)) { | 
				
			||||
                THREAD_POOL.shutdownNow(); | 
				
			||||
            } | 
				
			||||
        } catch (InterruptedException e) { | 
				
			||||
            THREAD_POOL.shutdownNow(); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue