From e4f755a9a2f68dac25a07b148eef3d37782609b1 Mon Sep 17 00:00:00 2001 From: longbao <1923439579@qq.com> Date: Tue, 5 Nov 2024 22:26:33 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E6=B5=81=E7=A8=8B=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8C=96=E3=80=91=20=E6=95=B0=E5=AD=97=E4=BA=BA=E7=94=9F?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/supervision/util/OkHttpUtils.java | 401 ++++++++++++++++++ .../manage/dto/HumanGenerateDTO.java | 15 + .../manage/human/HumanManageController.java | 31 ++ .../manage/service/HumanManageService.java | 33 ++ .../service/impl/HumanManageServiceImpl.java | 279 ++++++++++++ .../src/main/resources/bootstrap-dev.yml | 7 +- .../src/main/resources/bootstrap-prod.yml | 8 +- .../src/main/resources/bootstrap-test.yml | 8 +- .../com/supervision/enums/EnumMedicalRec.java | 3 +- .../mapper/AskPatientAnswerMapper.java | 14 +- .../com/supervision/mapper/HumanMapper.java | 12 + .../java/com/supervision/model/Human.java | 89 ++++ .../com/supervision/model/MedicalRec.java | 4 + .../service/AskPatientAnswerService.java | 10 + .../com/supervision/service/HumanService.java | 13 + .../impl/AskPatientAnswerServiceImpl.java | 24 +- .../service/impl/HumanServiceImpl.java | 25 ++ .../vo/manage/HumanCreateReqVo.java | 17 + .../com/supervision/vo/manage/HumanReqVo.java | 20 + .../mapper/AskPatientAnswerMapper.xml | 44 +- 20 files changed, 1029 insertions(+), 28 deletions(-) create mode 100644 virtual-patient-common/src/main/java/com/supervision/util/OkHttpUtils.java create mode 100644 virtual-patient-manage/src/main/java/com/supervision/manage/dto/HumanGenerateDTO.java create mode 100644 virtual-patient-manage/src/main/java/com/supervision/manage/human/HumanManageController.java create mode 100644 virtual-patient-manage/src/main/java/com/supervision/manage/service/HumanManageService.java create mode 100644 virtual-patient-manage/src/main/java/com/supervision/manage/service/impl/HumanManageServiceImpl.java create mode 100644 virtual-patient-model/src/main/java/com/supervision/mapper/HumanMapper.java create mode 100644 virtual-patient-model/src/main/java/com/supervision/model/Human.java create mode 100644 virtual-patient-model/src/main/java/com/supervision/service/HumanService.java create mode 100644 virtual-patient-model/src/main/java/com/supervision/service/impl/HumanServiceImpl.java create mode 100644 virtual-patient-model/src/main/java/com/supervision/vo/manage/HumanCreateReqVo.java create mode 100644 virtual-patient-model/src/main/java/com/supervision/vo/manage/HumanReqVo.java diff --git a/virtual-patient-common/src/main/java/com/supervision/util/OkHttpUtils.java b/virtual-patient-common/src/main/java/com/supervision/util/OkHttpUtils.java new file mode 100644 index 00000000..a28221ee --- /dev/null +++ b/virtual-patient-common/src/main/java/com/supervision/util/OkHttpUtils.java @@ -0,0 +1,401 @@ +package com.supervision.util; + +import okhttp3.*; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.net.URLEncoder; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +import com.alibaba.fastjson.JSON; + + +/** + * @Author:longbao + * @Date:2024/11/05 18:06 + * @Description: + */ +public class OkHttpUtils { + private static volatile OkHttpClient okHttpClient = null; + private static volatile Semaphore semaphore = null; + private Map headerMap; + private Map paramMap; + private Map paramMapObj; + private String url; + private Request.Builder request; + + /** + * 初始化okHttpClient,并且允许https访问 + */ + private OkHttpUtils() { + if (okHttpClient == null) { + synchronized (OkHttpUtils.class) { + if (okHttpClient == null) { + TrustManager[] trustManagers = buildTrustManagers(); + okHttpClient = new OkHttpClient.Builder() + .connectTimeout(1500, TimeUnit.SECONDS) + .writeTimeout(300, TimeUnit.SECONDS) + .readTimeout(300, TimeUnit.SECONDS) + .sslSocketFactory(createSSLSocketFactory(trustManagers), (X509TrustManager) trustManagers[0]) + .hostnameVerifier((hostName, session) -> true) + .retryOnConnectionFailure(true) + .build(); + addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"); + } + } + } + } + + /** + * 用于异步请求时,控制访问线程数,返回结果 + * + * @return + */ + private static Semaphore getSemaphoreInstance() { + //只能1个线程同时访问 + synchronized (OkHttpUtils.class) { + if (semaphore == null) { + semaphore = new Semaphore(0); + } + } + return semaphore; + } + + /** + * 创建OkHttpUtils + * + * @return + */ + public static OkHttpUtils builder() { + return new OkHttpUtils(); + } + + /** + * 生成安全套接字工厂,用于https请求的证书跳过 + * + * @return + */ + private static SSLSocketFactory createSSLSocketFactory(TrustManager[] trustAllCerts) { + SSLSocketFactory ssfFactory = null; + try { + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new SecureRandom()); + ssfFactory = sc.getSocketFactory(); + } catch (Exception e) { + e.printStackTrace(); + } + return ssfFactory; + } + + private static TrustManager[] buildTrustManagers() { + return new TrustManager[]{ + new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + } + }; + } + + public static void main(String[] args) { + // get请求,方法顺序按照这种方式,切记选择post/get一定要放在倒数第二,同步或者异步倒数第一,才会正确执行 + OkHttpUtils.builder().url("请求地址,http/https都可以") + // 有参数的话添加参数,可多个 + .addParam("参数名", "参数值") + .addParam("参数名", "参数值") + // 也可以添加多个 + .addHeader("Content-Type", "application/json; charset=utf-8") + .get() + // 可选择是同步请求还是异步请求 + //.async(); + .sync(); + + // post请求,分为两种,一种是普通表单提交,一种是json提交 + OkHttpUtils.builder().url("请求地址,http/https都可以") + // 有参数的话添加参数,可多个 + .addParam("参数名", "参数值") + .addParam("参数名", "参数值") + // 也可以添加多个 + .addHeader("Content-Type", "application/json; charset=utf-8") + // 如果是true的话,会类似于postman中post提交方式的raw,用json的方式提交,不是表单 + // 如果是false的话传统的表单提交 + .post(true) + .sync(); + + // 选择异步有两个方法,一个是带回调接口,一个是直接返回结果 + OkHttpUtils.builder().url("") + .post(false) + .async(); + + OkHttpUtils.builder().url("").post(false).async(new ICallBack() { + @Override + public void onSuccessful(Call call, String data) { + // 请求成功后的处理 + } + + @Override + public void onFailure(Call call, String errorMsg) { + // 请求失败后的处理 + } + }); + } + + /** + * 添加url + * + * @param url + * @return + */ + public OkHttpUtils url(String url) { + this.url = url; + return this; + } + + /** + * 添加参数 + * + * @param key 参数名 + * @param value 参数值 + * @return + */ + public OkHttpUtils addParam(String key, String value) { + if (paramMap == null) { + paramMap = new LinkedHashMap<>(16); + } + paramMap.put(key, value); + return this; + } + + public OkHttpUtils addParamObj(String key, Object value) { + if (paramMapObj == null) { + paramMapObj = new LinkedHashMap<>(16); + } + paramMapObj.put(key, value); + return this; + } + + public OkHttpUtils addParamMap(Map requestMap) { + if (paramMap == null) { + paramMap = new LinkedHashMap<>(16); + } + paramMap.putAll(requestMap); + return this; + } + + public OkHttpUtils addParamMapToObj(Map requestMap) { + if (paramMapObj == null) { + paramMapObj = new LinkedHashMap<>(16); + } + paramMapObj.putAll(requestMap); + return this; + } + + /** + * 添加请求头 + * + * @param key 参数名 + * @param value 参数值 + * @return + */ + public OkHttpUtils addHeader(String key, String value) { + if (headerMap == null) { + headerMap = new LinkedHashMap<>(16); + } + headerMap.put(key, value); + return this; + } + + /** + * 初始化get方法 + * + * @return + */ + public OkHttpUtils get() { + request = new Request.Builder().get(); + StringBuilder urlBuilder = new StringBuilder(url); + if (paramMap != null) { + urlBuilder.append("?"); + try { + for (Map.Entry entry : paramMap.entrySet()) { + urlBuilder.append(URLEncoder.encode(entry.getKey(), "utf-8")). + append("="). + append(URLEncoder.encode(entry.getValue(), "utf-8")). + append("&"); + } + } catch (Exception e) { + e.printStackTrace(); + } + urlBuilder.deleteCharAt(urlBuilder.length() - 1); + } + request.url(urlBuilder.toString()); + return this; + } + + /** + * 初始化post方法 + * + * @param isJsonPost true等于json的方式提交数据,类似postman里post方法的raw + * false等于普通的表单提交 + * @return + */ + public OkHttpUtils post(boolean isJsonPost) { + RequestBody requestBody; + if (isJsonPost) { + String json = ""; + if (paramMap != null) { + json = JSON.toJSONString(paramMap); + } + requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json); + } else { + FormBody.Builder formBody = new FormBody.Builder(); + if (paramMap != null) { + paramMap.forEach(formBody::add); + } + requestBody = formBody.build(); + } + request = new Request.Builder().post(requestBody).url(url); + return this; + } + + /** + * 初始化post方法 + * + * @param isJsonPost true等于json的方式提交数据,类似postman里post方法的raw + * false等于普通的表单提交 + * @return + */ + public OkHttpUtils put(boolean isJsonPost) { + RequestBody requestBody; + if (isJsonPost) { + String json = ""; + if (paramMap != null) { + json = JSON.toJSONString(paramMap); + } + requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json); + } else { + FormBody.Builder formBody = new FormBody.Builder(); + if (paramMap != null) { + paramMap.forEach(formBody::add); + } + requestBody = formBody.build(); + } + request = new Request.Builder().put(requestBody).url(url); + return this; + } + + /** + * 同步请求 + * + * @return + */ + public String sync() { + setHeader(request); + try { + Response response = okHttpClient.newCall(request.build()).execute(); + assert response.body() != null; + return response.body().string(); + } catch (IOException e) { + e.printStackTrace(); + return "请求失败:" + e.getMessage(); + } + } + + /** + * 异步请求,有返回值 + */ + public String async() { + StringBuilder buffer = new StringBuilder(""); + setHeader(request); + okHttpClient.newCall(request.build()).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + buffer.append("请求出错:").append(e.getMessage()); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + assert response.body() != null; + buffer.append(response.body().string()); + getSemaphoreInstance().release(); + } + }); + try { + getSemaphoreInstance().acquire(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return buffer.toString(); + } + + /** + * 异步请求,带有接口回调 + * + * @param callBack + */ + public void async(ICallBack callBack) { + setHeader(request); + okHttpClient.newCall(request.build()).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + callBack.onFailure(call, e.getMessage()); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + assert response.body() != null; + callBack.onSuccessful(call, response.body().string()); + } + }); + } + + /** + * 为request添加请求头 + * + * @param request + */ + private void setHeader(Request.Builder request) { + if (headerMap != null) { + try { + for (Map.Entry entry : headerMap.entrySet()) { + request.addHeader(entry.getKey(), entry.getValue()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public OkHttpUtils postJson(String jsonStr) { + RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonStr); + request = new Request.Builder().post(requestBody).url(url); + return this; + } + + /** + * 自定义一个接口回调 + */ + public interface ICallBack { + + void onSuccessful(Call call, String data); + + void onFailure(Call call, String errorMsg); + + } +} \ No newline at end of file diff --git a/virtual-patient-manage/src/main/java/com/supervision/manage/dto/HumanGenerateDTO.java b/virtual-patient-manage/src/main/java/com/supervision/manage/dto/HumanGenerateDTO.java new file mode 100644 index 00000000..8971d0c7 --- /dev/null +++ b/virtual-patient-manage/src/main/java/com/supervision/manage/dto/HumanGenerateDTO.java @@ -0,0 +1,15 @@ +package com.supervision.manage.dto; + +import lombok.Data; + +@Data +public class HumanGenerateDTO { + + private Integer code; + + private String message; + + private String uid; + + private String video; +} diff --git a/virtual-patient-manage/src/main/java/com/supervision/manage/human/HumanManageController.java b/virtual-patient-manage/src/main/java/com/supervision/manage/human/HumanManageController.java new file mode 100644 index 00000000..960bd342 --- /dev/null +++ b/virtual-patient-manage/src/main/java/com/supervision/manage/human/HumanManageController.java @@ -0,0 +1,31 @@ +package com.supervision.manage.human; + +import com.supervision.manage.service.HumanManageService; +import com.supervision.model.Human; +import com.supervision.vo.manage.HumanCreateReqVo; +import com.supervision.vo.manage.HumanReqVo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.FileInputStream; +import java.util.List; + +@RestController +@RequiredArgsConstructor +@Tag(name = "数字人管理") +@RequestMapping("/humanManage") +public class HumanManageController { + + private final HumanManageService humanManageService; + + + @Operation(summary = "新增数字人") + @PostMapping(value = "/createHuman",consumes = "multipart/form-data") + public boolean createHuman(@RequestParam("file") MultipartFile imageFile, String medicalId) { + return humanManageService.createHuman(imageFile, medicalId); + } + +} diff --git a/virtual-patient-manage/src/main/java/com/supervision/manage/service/HumanManageService.java b/virtual-patient-manage/src/main/java/com/supervision/manage/service/HumanManageService.java new file mode 100644 index 00000000..2c7a01b1 --- /dev/null +++ b/virtual-patient-manage/src/main/java/com/supervision/manage/service/HumanManageService.java @@ -0,0 +1,33 @@ +package com.supervision.manage.service; + +import com.supervision.model.Human; +import com.supervision.vo.manage.HumanReqVo; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +public interface HumanManageService { + + void generateHuman(String humanId, String medicalId) throws Exception; + + /** + * 查询数字人列表 + * @return + */ + List queryHumanList(); + + /** + * 新增数字人 + * @param humanReqVo 新增数字人请求 + * @return + */ + boolean createHuman(HumanReqVo humanReqVo); + + + boolean createHuman(MultipartFile imageFile, String medicalId); + + + boolean updateHuman(HumanReqVo human); + + boolean deleteHuman(String id); +} diff --git a/virtual-patient-manage/src/main/java/com/supervision/manage/service/impl/HumanManageServiceImpl.java b/virtual-patient-manage/src/main/java/com/supervision/manage/service/impl/HumanManageServiceImpl.java new file mode 100644 index 00000000..687ccf0c --- /dev/null +++ b/virtual-patient-manage/src/main/java/com/supervision/manage/service/impl/HumanManageServiceImpl.java @@ -0,0 +1,279 @@ +package com.supervision.manage.service.impl; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.supervision.enums.EnumMedicalRec; +import com.supervision.exception.BusinessException; +import com.supervision.manage.dto.HumanGenerateDTO; +import com.supervision.manage.service.FileManageService; +import com.supervision.manage.service.HumanManageService; +import com.supervision.model.AskPatientAnswer; +import com.supervision.model.FileResource; +import com.supervision.model.Human; +import com.supervision.model.MedicalRec; +import com.supervision.service.AskPatientAnswerService; +import com.supervision.service.HumanService; +import com.supervision.service.MedicalRecService; +import com.supervision.util.UserUtil; +import com.supervision.vo.manage.HumanReqVo; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Service +@RequiredArgsConstructor +public class HumanManageServiceImpl implements HumanManageService { + + + private final HumanService humanService; + private final FileManageService fileManageService; + private final MedicalRecService medicalRecService; + private final AskPatientAnswerService askPatientAnswerService; + /** + * 创建一个线程池,本意是用来异步调用数字人使用的 + * 核心线程数:2 + * 最大线程数:5 + * 队列长度:20 + */ + ThreadPoolExecutor humanExecutor = new ThreadPoolExecutor(2, 5, + 60L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(20), ThreadUtil.newNamedThreadFactory("human", true), + new ThreadPoolExecutor.CallerRunsPolicy()); + @Value("${humanGenerate.baseUrl}") + private String humanBaseUrl; + @Value("${humanGenerate.silent}") + private String silent; + @Value("${humanGenerate.dynamic}") + private String dynamic; + + @Override + @Transactional(rollbackFor = Exception.class) + public void generateHuman(String humanId, String medicalId) throws Exception { + Human human = humanService.getOptById(humanId).orElseThrow(() -> new BusinessException("数字人形象不存在")); + + File imageFile = fileManageService.downloadFile(human.getImageFileId()); + // 创建一个图片临时文件 + log.info("提交文件生成,文件名:{}", FileUtil.getName(imageFile)); + generateSilent(human, imageFile, medicalId); + generateDynamic(human, imageFile, medicalId); + + humanService.updateById(human); + } + + + @Override + public List queryHumanList() { + return humanService.lambdaQuery().orderBy(true, false, Human::getCreateTime).list(); + } + + @Override + public boolean createHuman(HumanReqVo humanReqVo) { + + Assert.notEmpty(humanReqVo.getImageFileId(), "图片不能为空"); + Human human = new Human(); + human.setImageName(humanReqVo.getImageName()); + human.setImageFileId(humanReqVo.getImageFileId()); + human.setDescription(humanReqVo.getDescription()); + humanService.save(human); + + // 异步调用生成数字人 + humanExecutor.submit(() -> { + try { + generateHuman(human.getId(), null); + } catch (Exception e) { + log.error("createHuman:生成数字人异常", e); + throw new BusinessException("生成数字人异常"); + } + }); + + return true; + } + + @Override + public boolean updateHuman(HumanReqVo human) { + Assert.notEmpty(human.getId(), "数字人id不能为空"); + Assert.notEmpty(human.getImageFileId(), "图片不能为空"); + + Human dbHuman = humanService.getById(human.getId()); + Assert.notEmpty(dbHuman.getId(), "数字人不存在"); + if (StrUtil.equals(dbHuman.getImageFileId(), human.getImageFileId())) { + // 只更新描述信息 + humanService.lambdaUpdate().eq(Human::getId, human.getId()) + .set(Human::getDescription, human.getDescription()) + .set(Human::getImageName, human.getImageName()) + .update(); + return true; + } + // 更新数字人状态 + dbHuman.setImageFileId(human.getImageFileId()); + dbHuman.setSilentTaskUid(null); + dbHuman.setDynamicTaskUid(null); + dbHuman.setDynamicVideoFileId(null); + dbHuman.setSilentVideoFileId(null); + dbHuman.setStatus(2); + dbHuman.setDescription(human.getDescription()); + humanService.updateById(dbHuman); + // 异步调用生成数字人 + humanExecutor.submit(() -> { + try { + generateHuman(human.getId(), null); + } catch (Exception e) { + log.error("createHuman:生成数字人异常", e); + throw new BusinessException("生成数字人异常"); + } + }); + + return true; + } + + @Override + public boolean deleteHuman(String id) { + Assert.notEmpty(id, "数字人id不能为空"); + Long count = medicalRecService.lambdaQuery().eq(MedicalRec::getHumanId, id).count(); + Assert.isTrue(count == 0, "该数字已被使用,不能删除"); + return humanService.removeById(id); + } + + private void generateSilent(Human human, File imageFile, String medicalId) throws Exception { + if (StrUtil.isAllBlank(human.getSilentVideoFileId(), human.getSilentTaskUid())) { + HumanGenerateDTO silentDTO; + try { + String post = HttpUtil.post(humanBaseUrl + silent, Map.of("image", imageFile)); + silentDTO = JSONUtil.toBean(post, HumanGenerateDTO.class); + } catch (Exception e) { + throw new BusinessException("生成数字人静态视频异常"); + } + // 保存静态视频 + if (silentDTO.getCode() == 200) { + if (StrUtil.isNotBlank(silentDTO.getVideo())) { + // 如果已经生成了视频,那么直接拿到这个视频 + FileResource fileResource = convertBase64ToFile("silent", FileUtil.getName(imageFile), silentDTO.getVideo()); + human.setSilentVideoFileId(fileResource.getId()); + human.setImageName(fileResource.getFileName()); + + // 诊断闻讯绑定静态视频 + AskPatientAnswer askPatientAnswer = askPatientAnswerService.queryAnswerIdByMedicalId(medicalId, "静态视频"); + AskPatientAnswer patientAnswer = new AskPatientAnswer(); + patientAnswer.setId(askPatientAnswer.getId()); + patientAnswer.setAnswerResourceId(fileResource.getId()); + askPatientAnswerService.updateById(patientAnswer); + // 完成 + MedicalRec medicalRec1 = new MedicalRec(); + medicalRec1.setMedicalStatus(EnumMedicalRec.ESTABLISHED_RECORD.getCode()); + medicalRec1.setId(medicalId); + medicalRecService.updateById(medicalRec1); + } + human.setSilentTaskUid(silentDTO.getUid()); + + } + } + + + } + + private void generateDynamic(Human human, File imageFile, String medicalId) throws Exception { + if (StrUtil.isAllBlank(human.getDynamicVideoFileId(), human.getDynamicTaskUid())) { + HumanGenerateDTO dynamicDTO; + try { + String post = HttpUtil.post(humanBaseUrl + dynamic, Map.of("image", imageFile)); + dynamicDTO = JSONUtil.toBean(post, HumanGenerateDTO.class); + } catch (Exception e) { + throw new BusinessException("生成数字人动态视频异常"); + } + + + if (dynamicDTO.getCode() == 200) { + if (StrUtil.isNotBlank(dynamicDTO.getVideo())) { + // 如果已经生成了视频,那么直接拿到这个视频 + FileResource fileResource = convertBase64ToFile("dynamic", FileUtil.getName(imageFile), dynamicDTO.getVideo()); + human.setDynamicVideoFileId(fileResource.getId()); + human.setImageName(fileResource.getFileName()); + + // 诊断闻讯绑定动态视频 + AskPatientAnswer askPatientAnswer = askPatientAnswerService.queryAnswerIdByMedicalId(medicalId, "动态视频"); + AskPatientAnswer patientAnswer = new AskPatientAnswer(); + patientAnswer.setId(askPatientAnswer.getId()); + patientAnswer.setAnswerResourceId(fileResource.getId()); + askPatientAnswerService.updateById(patientAnswer); + + // 完成 + MedicalRec medicalRec1 = new MedicalRec(); + medicalRec1.setMedicalStatus(EnumMedicalRec.COMPLETE.getCode()); + medicalRec1.setId(medicalId); + medicalRecService.updateById(medicalRec1); + } + human.setDynamicTaskUid(dynamicDTO.getUid()); + } + } + + } + + private FileResource convertBase64ToFile(String prefixName, String imageFileName, String base64) throws Exception { + String prefix = FileUtil.getPrefix(imageFileName); + + // 创建一个图片临时文件 + File tempFile = FileUtil.createTempFile(prefixName + "_" + prefix, ".mp4", true); + + Base64.decodeToFile(base64, tempFile); + return fileManageService.uploadFile(tempFile); + } + + + @SneakyThrows + @Override + @Transactional + public boolean createHuman(MultipartFile imageFile, String medicalId) { + // 查询病例状态 + MedicalRec medicalRec = medicalRecService.lambdaQuery() + .eq(MedicalRec::getMedicalStatus, EnumMedicalRec.DIGITAL_HUMAN_CREATED.getCode()) + .eq(MedicalRec::getId, medicalId) + .one(); + Assert.isTrue(null == medicalRec, "请先导入病例后再操作"); + + // 上传图片 + FileResource fileResource = fileManageService.uploadFile(imageFile, imageFile.getContentType()); + + // 生成数字人 + Human human = new Human(); + human.setImageFileId(fileResource.getId()); + human.setImageName(fileResource.getFileName()); + human.setCreateUserId(UserUtil.getUser().getId()); + human.setUpdateUserId(UserUtil.getUser().getId()); + humanService.save(human); + + // 异步调用生成数字人 + humanExecutor.submit(() -> { + try { + generateHuman(human.getId(), medicalId); + } catch (Exception e) { + log.error("createHuman:生成数字人异常", e); + throw new BusinessException("生成数字人异常"); + } + }); + + // 绑定病例 + MedicalRec medicalRec1 = new MedicalRec(); + medicalRec1.setMedicalStatus(EnumMedicalRec.ESTABLISHED_RECORD.getCode()); + medicalRec1.setHumanId(human.getId()); + medicalRec1.setId(medicalId); + medicalRecService.updateById(medicalRec1); + return true; + } +} diff --git a/virtual-patient-manage/src/main/resources/bootstrap-dev.yml b/virtual-patient-manage/src/main/resources/bootstrap-dev.yml index e0010333..138b0886 100644 --- a/virtual-patient-manage/src/main/resources/bootstrap-dev.yml +++ b/virtual-patient-manage/src/main/resources/bootstrap-dev.yml @@ -7,4 +7,9 @@ spring: namespace: ef0e6889-ac5f-4327-b7ac-ff3105318f58 discovery: server-addr: 192.168.1.101:18498 - namespace: ef0e6889-ac5f-4327-b7ac-ff3105318f58 \ No newline at end of file + namespace: ef0e6889-ac5f-4327-b7ac-ff3105318f58 +humanGenerate: + baseUrl: http://192.168.1.101:6711 + silent: /silent-video + dynamic: /dynamic-video + status: /status \ No newline at end of file diff --git a/virtual-patient-manage/src/main/resources/bootstrap-prod.yml b/virtual-patient-manage/src/main/resources/bootstrap-prod.yml index e76e0f54..97892282 100644 --- a/virtual-patient-manage/src/main/resources/bootstrap-prod.yml +++ b/virtual-patient-manage/src/main/resources/bootstrap-prod.yml @@ -7,4 +7,10 @@ spring: namespace: prod discovery: server-addr: 192.168.1.101:18498 - namespace: prod \ No newline at end of file + namespace: prod + +humanGenerate: + baseUrl: http://192.168.1.101:6711 + silent: /silent-video + dynamic: /dynamic-video + status: /status \ No newline at end of file diff --git a/virtual-patient-manage/src/main/resources/bootstrap-test.yml b/virtual-patient-manage/src/main/resources/bootstrap-test.yml index e0010333..c3c530cf 100644 --- a/virtual-patient-manage/src/main/resources/bootstrap-test.yml +++ b/virtual-patient-manage/src/main/resources/bootstrap-test.yml @@ -7,4 +7,10 @@ spring: namespace: ef0e6889-ac5f-4327-b7ac-ff3105318f58 discovery: server-addr: 192.168.1.101:18498 - namespace: ef0e6889-ac5f-4327-b7ac-ff3105318f58 \ No newline at end of file + namespace: ef0e6889-ac5f-4327-b7ac-ff3105318f58 + +humanGenerate: + baseUrl: http://192.168.1.101:6711 + silent: /silent-video + dynamic: /dynamic-video + status: /status \ No newline at end of file diff --git a/virtual-patient-model/src/main/java/com/supervision/enums/EnumMedicalRec.java b/virtual-patient-model/src/main/java/com/supervision/enums/EnumMedicalRec.java index 5a23d91d..1329f6d8 100644 --- a/virtual-patient-model/src/main/java/com/supervision/enums/EnumMedicalRec.java +++ b/virtual-patient-model/src/main/java/com/supervision/enums/EnumMedicalRec.java @@ -9,8 +9,7 @@ public enum EnumMedicalRec { DIGITAL_HUMAN_CREATED("已创建病例", "00"), ESTABLISHED_RECORD("数字人生成中", "01"), - MEDICAL_RECORD_ESTABLISHED("已生成数字人", "02"), - COMPLETE("完成", "03"), + COMPLETE("完成", "02"), ; private String description; diff --git a/virtual-patient-model/src/main/java/com/supervision/mapper/AskPatientAnswerMapper.java b/virtual-patient-model/src/main/java/com/supervision/mapper/AskPatientAnswerMapper.java index fe7a172b..e179a908 100644 --- a/virtual-patient-model/src/main/java/com/supervision/mapper/AskPatientAnswerMapper.java +++ b/virtual-patient-model/src/main/java/com/supervision/mapper/AskPatientAnswerMapper.java @@ -3,14 +3,18 @@ package com.supervision.mapper; import com.supervision.model.AskPatientAnswer; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import java.util.List; + /** -* @author flevance -* @description 针对表【vp_ask_patient_answer(诊断问询-针对患者的回复)】的数据库操作Mapper -* @createDate 2023-11-03 11:13:26 -* @Entity com.supervision.model.AskPatientAnswer -*/ + * @author flevance + * @description 针对表【vp_ask_patient_answer(诊断问询-针对患者的回复)】的数据库操作Mapper + * @createDate 2023-11-03 11:13:26 + * @Entity com.supervision.model.AskPatientAnswer + */ public interface AskPatientAnswerMapper extends BaseMapper { + + AskPatientAnswer queryAnswerIdByMedicalId(String medicalId, String vidioType); } diff --git a/virtual-patient-model/src/main/java/com/supervision/mapper/HumanMapper.java b/virtual-patient-model/src/main/java/com/supervision/mapper/HumanMapper.java new file mode 100644 index 00000000..2b9134c4 --- /dev/null +++ b/virtual-patient-model/src/main/java/com/supervision/mapper/HumanMapper.java @@ -0,0 +1,12 @@ +package com.supervision.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.supervision.model.Human; + +/** + * @Author:longbao + * @Date:2024/11/5 21:56 + * @Description: + */ +public interface HumanMapper extends BaseMapper { +} diff --git a/virtual-patient-model/src/main/java/com/supervision/model/Human.java b/virtual-patient-model/src/main/java/com/supervision/model/Human.java new file mode 100644 index 00000000..b9ee776d --- /dev/null +++ b/virtual-patient-model/src/main/java/com/supervision/model/Human.java @@ -0,0 +1,89 @@ +package com.supervision.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Date; + +/** + * 数字人表 + * @TableName vp_human + */ +@Data +@TableName(value = "vp_human") +public class Human extends Model implements Serializable { + /** + * 主键 + */ + @TableId + private String id; + + /** + * 数字人的状态,只有1才能被使用 1就绪 2生成中 + */ + private Integer status; + + /** + * 静态图片ID + */ + public String imageFileId; + + /** + * 静态视频ID + */ + private String silentVideoFileId; + + /** + * 提交生成的UID标识 + */ + private String silentTaskUid; + + /** + * 动态视频ID + */ + private String dynamicVideoFileId; + + /** + * 动态视频生成UID标识 + */ + private String dynamicTaskUid; + + /** + * 图片名称 + */ + private String imageName; + + /** + * 描述 + */ + private String description; + + /** + * 创建人ID + */ + private String createUserId; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新人ID + */ + private String updateUserId; + + /** + * 更新时间 + */ + private Date updateTime; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + +} \ No newline at end of file diff --git a/virtual-patient-model/src/main/java/com/supervision/model/MedicalRec.java b/virtual-patient-model/src/main/java/com/supervision/model/MedicalRec.java index 0811e837..6af564ac 100644 --- a/virtual-patient-model/src/main/java/com/supervision/model/MedicalRec.java +++ b/virtual-patient-model/src/main/java/com/supervision/model/MedicalRec.java @@ -30,6 +30,10 @@ public class MedicalRec extends Model implements Serializable { */ private String no; + + @Schema(description = "数字人id") + private String humanId; + /** * 病人头像 */ diff --git a/virtual-patient-model/src/main/java/com/supervision/service/AskPatientAnswerService.java b/virtual-patient-model/src/main/java/com/supervision/service/AskPatientAnswerService.java index 1f2f978b..90e762ff 100644 --- a/virtual-patient-model/src/main/java/com/supervision/service/AskPatientAnswerService.java +++ b/virtual-patient-model/src/main/java/com/supervision/service/AskPatientAnswerService.java @@ -3,6 +3,8 @@ package com.supervision.service; import com.supervision.model.AskPatientAnswer; import com.baomidou.mybatisplus.extension.service.IService; +import java.util.List; + /** * @author flevance * @description 针对表【vp_ask_patient_answer(诊断问询-针对患者的回复)】的数据库操作Service @@ -10,4 +12,12 @@ import com.baomidou.mybatisplus.extension.service.IService; */ public interface AskPatientAnswerService extends IService { + + /** + * 断闻讯绑定静动态视频 + * + * @param medicalId 病历id + * @param vidioType + */ + AskPatientAnswer queryAnswerIdByMedicalId(String medicalId, String vidioType); } diff --git a/virtual-patient-model/src/main/java/com/supervision/service/HumanService.java b/virtual-patient-model/src/main/java/com/supervision/service/HumanService.java new file mode 100644 index 00000000..6dabe15b --- /dev/null +++ b/virtual-patient-model/src/main/java/com/supervision/service/HumanService.java @@ -0,0 +1,13 @@ +package com.supervision.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.supervision.model.Human; + +/** +* @author Administrator +* @description 针对表【vp_human(数字人表)】的数据库操作Service +* @createDate 2024-06-20 15:51:59 +*/ +public interface HumanService extends IService { + +} diff --git a/virtual-patient-model/src/main/java/com/supervision/service/impl/AskPatientAnswerServiceImpl.java b/virtual-patient-model/src/main/java/com/supervision/service/impl/AskPatientAnswerServiceImpl.java index 83f66d36..d879b202 100644 --- a/virtual-patient-model/src/main/java/com/supervision/service/impl/AskPatientAnswerServiceImpl.java +++ b/virtual-patient-model/src/main/java/com/supervision/service/impl/AskPatientAnswerServiceImpl.java @@ -4,17 +4,31 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.supervision.model.AskPatientAnswer; import com.supervision.service.AskPatientAnswerService; import com.supervision.mapper.AskPatientAnswerMapper; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.List; + /** -* @author flevance -* @description 针对表【vp_ask_patient_answer(诊断问询-针对患者的回复)】的数据库操作Service实现 -* @createDate 2023-11-03 11:13:26 -*/ + * @author flevance + * @description 针对表【vp_ask_patient_answer(诊断问询-针对患者的回复)】的数据库操作Service实现 + * @createDate 2023-11-03 11:13:26 + */ @Service +@Slf4j public class AskPatientAnswerServiceImpl extends ServiceImpl - implements AskPatientAnswerService{ + implements AskPatientAnswerService { + /** + * 断闻讯绑定静动态视频 + * + * @param medicalId 病历id + * @param vidioType + */ + @Override + public AskPatientAnswer queryAnswerIdByMedicalId(String medicalId, String vidioType) { + return this.baseMapper.queryAnswerIdByMedicalId(medicalId, vidioType); + } } diff --git a/virtual-patient-model/src/main/java/com/supervision/service/impl/HumanServiceImpl.java b/virtual-patient-model/src/main/java/com/supervision/service/impl/HumanServiceImpl.java new file mode 100644 index 00000000..2b5c7747 --- /dev/null +++ b/virtual-patient-model/src/main/java/com/supervision/service/impl/HumanServiceImpl.java @@ -0,0 +1,25 @@ +package com.supervision.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.supervision.mapper.AskCirculationDetailMapper; +import com.supervision.mapper.HumanMapper; +import com.supervision.model.AskCirculationDetail; +import com.supervision.model.Human; +import com.supervision.service.AskCirculationDetailService; +import com.supervision.service.HumanService; +import org.springframework.stereotype.Service; + +/** + * @Author:longbao + * @Date:2024/11/5 21:56 + * @Description: + */ +@Service +public class HumanServiceImpl extends ServiceImpl + implements HumanService { + +} + + + + diff --git a/virtual-patient-model/src/main/java/com/supervision/vo/manage/HumanCreateReqVo.java b/virtual-patient-model/src/main/java/com/supervision/vo/manage/HumanCreateReqVo.java new file mode 100644 index 00000000..2d526cb8 --- /dev/null +++ b/virtual-patient-model/src/main/java/com/supervision/vo/manage/HumanCreateReqVo.java @@ -0,0 +1,17 @@ +package com.supervision.vo.manage; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +import java.io.FileInputStream; + +@Data +public class HumanCreateReqVo { + + @Schema(description = "静态图片") + private FileInputStream imageFile; + + @Schema(description = "病例id") + private String medicalId; +} diff --git a/virtual-patient-model/src/main/java/com/supervision/vo/manage/HumanReqVo.java b/virtual-patient-model/src/main/java/com/supervision/vo/manage/HumanReqVo.java new file mode 100644 index 00000000..c868d0a5 --- /dev/null +++ b/virtual-patient-model/src/main/java/com/supervision/vo/manage/HumanReqVo.java @@ -0,0 +1,20 @@ +package com.supervision.vo.manage; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class HumanReqVo { + + @Schema(description = "数字人ID") + private String id; + + @Schema(description = "数字人名") + private String imageName; + + @Schema(description = "静态图片ID") + private String imageFileId; + + @Schema(description = "描述") + private String description; +} diff --git a/virtual-patient-model/src/main/resources/mapper/AskPatientAnswerMapper.xml b/virtual-patient-model/src/main/resources/mapper/AskPatientAnswerMapper.xml index e26a7cd5..4ee81587 100644 --- a/virtual-patient-model/src/main/resources/mapper/AskPatientAnswerMapper.xml +++ b/virtual-patient-model/src/main/resources/mapper/AskPatientAnswerMapper.xml @@ -5,23 +5,41 @@ - - - - - - - - - - - - + + + + + + + + + + + + - id,medical_id,library_question_id,code,question, + id + ,medical_id,library_question_id,code,question, answer,answer_resources_id,answer_type,create_user_id, create_time,update_user_id,update_time + + +