/*
 * Copyright ©2023 CJB-CNIT Team. All rights reserved
 */
package com.openhis.web.inhospitalnursestation.appservice.impl;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.core.common.exception.ServiceException;
import com.core.common.utils.*;
import com.core.common.utils.bean.BeanUtils;
import com.openhis.administration.domain.ChargeItem;
import com.openhis.administration.service.IChargeItemService;
import com.openhis.administration.service.IEncounterService;
import com.openhis.clinical.domain.Procedure;
import com.openhis.clinical.service.IProcedureService;
import com.openhis.common.constant.CommonConstants;
import com.openhis.common.constant.PromptMsgConstant;
import com.openhis.common.enums.*;
import com.openhis.common.utils.EnumUtils;
import com.openhis.common.utils.HisQueryUtils;
import com.openhis.medication.domain.MedicationDefinition;
import com.openhis.medication.domain.MedicationDispense;
import com.openhis.medication.domain.MedicationRequest;
import com.openhis.medication.service.IMedicationDefinitionService;
import com.openhis.medication.service.IMedicationDispenseService;
import com.openhis.medication.service.IMedicationRequestService;
import com.openhis.web.common.dto.PerformInfoDto;
import com.openhis.web.common.dto.PerformRecordDto;
import com.openhis.web.doctorstation.appservice.IDoctorStationAdviceAppService;
import com.openhis.web.doctorstation.dto.ActivityBindDeviceDetailDto;
import com.openhis.web.doctorstation.dto.AdviceBaseDto;
import com.openhis.web.doctorstation.dto.AdviceInventoryDto;
import com.openhis.web.doctorstation.dto.AdvicePriceDto;
import com.openhis.web.doctorstation.mapper.DoctorStationAdviceAppMapper;
import com.openhis.web.doctorstation.utils.AdviceUtils;
import com.openhis.web.inhospitalnursestation.appservice.IAdviceProcessAppService;
import com.openhis.web.inhospitalnursestation.dto.*;
import com.openhis.web.inhospitalnursestation.mapper.AdviceProcessAppMapper;
import com.openhis.web.outpatientmanage.mapper.OutpatientTreatmentAppMapper;
import com.openhis.workflow.domain.DeviceDispense;
import com.openhis.workflow.domain.DeviceRequest;
import com.openhis.workflow.domain.ServiceRequest;
import com.openhis.workflow.service.*;

/**
 * 护士站医嘱处理实现类
 *
 * @author zwh
 * @date 2025-08-07
 */
@Service
public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {

    @Resource
    AssignSeqUtil assignSeqUtil;

    @Resource
    private AdviceProcessAppMapper adviceProcessAppMapper;

    @Resource
    private DoctorStationAdviceAppMapper doctorStationAdviceAppMapper;

    @Resource
    private IProcedureService procedureService;

    @Resource
    private OutpatientTreatmentAppMapper outpatientTreatmentAppMapper;

    @Resource
    private IMedicationRequestService medicationRequestService;

    @Resource
    private IServiceRequestService serviceRequestService;

    @Resource
    private IMedicationDispenseService medicationDispenseService;

    @Resource
    private IDeviceDispenseService deviceDispenseService;

    @Resource
    private IDeviceRequestService deviceRequestService;

    @Resource
    private IEncounterService encounterService;

    @Resource
    private IMedicationDefinitionService medicationDefinitionService;

    @Resource
    private IChargeItemService chargeItemService;

    @Resource
    private AdviceUtils adviceUtils;

    @Resource
    private IDoctorStationAdviceAppService doctorStationAdviceAppService;

    @Resource
    private ISupplyRequestService supplyRequestService;

    @Resource
    private ISupplyDeliveryService supplyDeliveryService;

    /**
     * 住院患者分页列表
     *
     * @param admissionPageParam 查询条件
     * @param pageNo 当前页码
     * @param pageSize 查询条数
     * @param searchKey 模糊查询关键字
     * @param request 请求数据
     * @return 住院患者分页列表
     */
    @Override
    public R<?> getInpatientPage(AdmissionPageParam admissionPageParam, Integer pageNo, Integer pageSize,
        String searchKey, HttpServletRequest request) {
        // 构建查询条件
        QueryWrapper<AdmissionPageParam> queryWrapper = HisQueryUtils.buildQueryWrapper(admissionPageParam, searchKey,
            new HashSet<>(Arrays.asList(CommonConstants.FieldName.PatientWbStr, CommonConstants.FieldName.PatientPyStr,
                CommonConstants.FieldName.PatientName, CommonConstants.FieldName.BusNo)),
            request);
        // 入院患者分页列表
        Page<AdmissionPatientPageDto> admissionPatientPage =
            adviceProcessAppMapper.selectInpatientPage(new Page<>(pageNo, pageSize), queryWrapper,
                EncounterClass.IMP.getValue(), EncounterZyStatus.TO_BE_REGISTERED.getValue(),
                EncounterZyStatus.REGISTERED.getValue(), EncounterActivityStatus.ACTIVE.getValue(),
                LocationForm.WARD.getValue(), LocationForm.HOUSE.getValue(), LocationForm.BED.getValue());
        admissionPatientPage.getRecords().forEach(e -> {
            // 性别枚举
            e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
            // 病情
            e.setPriorityEnum_enumText(EnumUtils.getInfoByValue(PriorityLevel.class, e.getPriorityEnum()));
            // 计算年龄
            if (e.getBirthDate() != null) {
                e.setAge(AgeCalculatorUtil.getAge(e.getBirthDate()));
            }
            // 住院状态
            e.setEncounterStatus_enumText(EnumUtils.getInfoByValue(EncounterZyStatus.class, e.getEncounterStatus()));
        });
        return R.ok(admissionPatientPage);
    }

    /**
     * 住院患者医嘱查询
     *
     * @param inpatientAdviceParam 查询条件
     * @param pageNo 当前页码
     * @param pageSize 查询条数
     * @return 住院患者医
     */
    @Override
    public R<?> getInpatientAdvicePage(InpatientAdviceParam inpatientAdviceParam, Integer pageNo, Integer pageSize) {
        // 初始化查询参数
        String encounterIds = inpatientAdviceParam.getEncounterIds();
        inpatientAdviceParam.setEncounterIds(null);
        Integer exeStatus = inpatientAdviceParam.getExeStatus();
        inpatientAdviceParam.setExeStatus(null);
        // 构建查询条件
        QueryWrapper<InpatientAdviceParam> queryWrapper =
            HisQueryUtils.buildQueryWrapper(inpatientAdviceParam, null, null, null);

        // 手动拼接住院患者id条件
        if (encounterIds != null && !encounterIds.isEmpty()) {
            List<Long> encounterIdList =
                Arrays.stream(encounterIds.split(CommonConstants.Common.COMMA)).map(Long::parseLong).toList();
            queryWrapper.in(CommonConstants.FieldName.EncounterId, encounterIdList);
        }
        // 患者医嘱分页列表
        Page<InpatientAdviceDto> inpatientAdvicePage =
            adviceProcessAppMapper.selectInpatientAdvicePage(new Page<>(pageNo, pageSize), queryWrapper,
                CommonConstants.TableName.MED_MEDICATION_REQUEST, CommonConstants.TableName.WOR_SERVICE_REQUEST,
                RequestStatus.DRAFT.getValue(), EncounterActivityStatus.ACTIVE.getValue(), LocationForm.BED.getValue(),
                ParticipantType.ADMITTING_DOCTOR.getCode(), AccountType.PERSONAL_CASH_ACCOUNT.getCode(),
                ChargeItemStatus.BILLABLE.getValue(), ChargeItemStatus.BILLED.getValue(),
                ChargeItemStatus.REFUNDED.getValue(), EncounterClass.IMP.getValue());
        inpatientAdvicePage.getRecords().forEach(e -> {
            // 是否皮试
            e.setSkinTestFlag_enumText(EnumUtils.getInfoByValue(Whether.class, e.getSkinTestFlag()));
            // 是否为注射药物
            e.setInjectFlag_enumText(EnumUtils.getInfoByValue(Whether.class, e.getInjectFlag()));
            // 医嘱类型
            e.setTherapyEnum_enumText(EnumUtils.getInfoByValue(TherapyTimeType.class, e.getTherapyEnum()));
            // 请求状态
            e.setRequestStatus_enumText(EnumUtils.getInfoByValue(RequestStatus.class, e.getRequestStatus()));
            // 性别枚举
            e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
            // 计算年龄
            if (e.getBirthDate() != null) {
                e.setAge(AgeCalculatorUtil.getAge(e.getBirthDate()));
            }
        });

        // 获取医嘱列表
        List<InpatientAdviceDto> inpatientAdviceList = inpatientAdvicePage.getRecords();
        // 获取请求ids
        List<Long> reqIds = inpatientAdviceList.stream().map(InpatientAdviceDto::getRequestId).toList();
        Map<Long, List<PerformRecordDto>> procedureRecordGroup = new HashMap<>();
        if (!reqIds.isEmpty()) {
            // 查询执行记录
            List<PerformRecordDto> performRecordList = outpatientTreatmentAppMapper.selectPerformRecordList(reqIds);
            if (performRecordList != null && !performRecordList.isEmpty()) {
                // 根据服务请求id分组
                procedureRecordGroup = performRecordList.stream()
                    .collect(Collectors.groupingBy(PerformRecordDto::getRequestId, Collectors.toList()));
            }
        }

        // 手动处理前端传来的执行条件
        if (exeStatus != null) {
            // 处理执行记录状态
            for (InpatientAdviceDto inpatientAdvice : inpatientAdviceList) {
                List<PerformRecordDto> performRecordList = procedureRecordGroup.get(inpatientAdvice.getRequestId());
                List<PerformRecordDto> exePerformRecordList = new ArrayList<>();
                List<PerformRecordDto> cancelPerformRecordList = new ArrayList<>();
                List<PerformRecordDto> stopPerformRecordList = new ArrayList<>();
                if (performRecordList != null && !performRecordList.isEmpty()) {
                    // 按时间分组，处理每个时间点的多条记录
                    Map<String, List<PerformRecordDto>> recordsByTime = performRecordList.stream()
                        .collect(Collectors.groupingBy(record -> record.getOccurrenceTime().toString()));
                    for (Map.Entry<String, List<PerformRecordDto>> entry : recordsByTime.entrySet()) {
                        List<PerformRecordDto> records = entry.getValue();
                        // 按操作顺序排序
                        records.sort(Comparator.comparing(PerformRecordDto::getCreateTime));
                        // 取最后一条记录,最终状态由最后一次操作决定
                        PerformRecordDto lastRecord = records.get(records.size() - 1);
                        if (EventStatus.COMPLETED.getValue().equals(lastRecord.getStatusEnum())) {
                            exePerformRecordList.add(lastRecord);
                        } else if (EventStatus.CANCEL.getValue().equals(lastRecord.getStatusEnum())) {
                            cancelPerformRecordList.add(lastRecord);
                        } else if (EventStatus.STOPPED.getValue().equals(lastRecord.getStatusEnum())) {
                            stopPerformRecordList.add(lastRecord);
                        }
                    }
                }
                inpatientAdvice.setExePerformRecordList(exePerformRecordList)
                    .setCancelPerformRecordList(cancelPerformRecordList)
                    .setStopPerformRecordList(stopPerformRecordList);
            }

            // 根据执行状态过滤医嘱列表
            List<InpatientAdviceDto> filteredList = new ArrayList<>();
            if (EventStatus.COMPLETED.getValue().equals(exeStatus)) {
                // 已执行 - 过滤出有执行记录的医嘱
                filteredList = inpatientAdviceList.stream().filter(
                    advice -> advice.getExePerformRecordList() != null && !advice.getExePerformRecordList().isEmpty())
                    .collect(Collectors.toList());
                // 更新分页数据
                inpatientAdvicePage.setRecords(filteredList);
                inpatientAdvicePage.setTotal(filteredList.size());
            } else if (EventStatus.CANCEL.getValue().equals(exeStatus)) {
                // 取消执行 - 过滤出有取消记录的医嘱
                filteredList = inpatientAdviceList.stream().filter(advice -> advice.getCancelPerformRecordList() != null
                    && !advice.getCancelPerformRecordList().isEmpty()).collect(Collectors.toList());
                // 更新分页数据
                inpatientAdvicePage.setRecords(filteredList);
                inpatientAdvicePage.setTotal(filteredList.size());
            } else if (EventStatus.STOPPED.getValue().equals(exeStatus)) {
                // 不执行 - 过滤出有停止记录的医嘱
                filteredList = inpatientAdviceList.stream().filter(
                    advice -> advice.getStopPerformRecordList() != null && !advice.getStopPerformRecordList().isEmpty())
                    .collect(Collectors.toList());
                // 更新分页数据
                inpatientAdvicePage.setRecords(filteredList);
                inpatientAdvicePage.setTotal(filteredList.size());
            }
        }
        return R.ok(inpatientAdvicePage);
    }

    /**
     * 医嘱校对通过
     *
     * @param performInfoList 医嘱信息集合
     * @return 操作结果
     */
    @Override
    public R<?> adviceVerify(List<PerformInfoDto> performInfoList) {
        // 分别创建两个列表来存储不同类型的请求
        List<PerformInfoDto> serviceRequestList = new ArrayList<>();
        List<PerformInfoDto> medRequestList = new ArrayList<>();
        for (PerformInfoDto item : performInfoList) {
            if (CommonConstants.TableName.WOR_SERVICE_REQUEST.equals(item.getRequestTable())) {
                serviceRequestList.add(item);
            } else if (CommonConstants.TableName.MED_MEDICATION_REQUEST.equals(item.getRequestTable())) {
                medRequestList.add(item);
            }
        }
        Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
        Date checkDate = new Date();
        if (!serviceRequestList.isEmpty()) {
            // 更新服务请求状态已完成
            serviceRequestService.updateCompleteRequestStatus(
                serviceRequestList.stream().map(PerformInfoDto::getRequestId).toList(), practitionerId, checkDate);
            List<ServiceRequest> serviceRequests = serviceRequestService
                .listByIds(serviceRequestList.stream().map(PerformInfoDto::getRequestId).collect(Collectors.toList()));
            for (ServiceRequest serviceRequest : serviceRequests) {
                // 判断医嘱类型
                if (ActivityDefCategory.TRANSFER.getValue().equals(serviceRequest.getCategoryEnum())) {
                    // 更新患者状态 待转科
                    encounterService.updateEncounterStatus(serviceRequest.getEncounterId(),
                        EncounterZyStatus.PENDING_TRANSFER.getValue());
                } else if (ActivityDefCategory.DISCHARGE.getValue().equals(serviceRequest.getCategoryEnum())) {
                    // 更新患者状态 待出院
                    encounterService.updateEncounterStatus(serviceRequest.getEncounterId(),
                        EncounterZyStatus.AWAITING_DISCHARGE.getValue());
                }
            }
        }
        if (!medRequestList.isEmpty()) {
            // 更新药品请求状态已完成
            medicationRequestService.updateCompletedStatusBatch(
                medRequestList.stream().map(PerformInfoDto::getRequestId).toList(), practitionerId, checkDate);
        }
        return R.ok(null, "校对成功");
    }

    /**
     * 医嘱退回
     *
     * @param performInfoList 医嘱信息集合
     * @return 操作结果
     */
    @Override
    public R<?> adviceReject(List<PerformInfoDto> performInfoList) {
        // 分别创建两个列表来存储不同类型的请求
        List<PerformInfoDto> serviceRequestList = new ArrayList<>();
        List<PerformInfoDto> medRequestList = new ArrayList<>();
        for (PerformInfoDto item : performInfoList) {
            if (CommonConstants.TableName.WOR_SERVICE_REQUEST.equals(item.getRequestTable())) {
                serviceRequestList.add(item);
            } else if (CommonConstants.TableName.MED_MEDICATION_REQUEST.equals(item.getRequestTable())) {
                medRequestList.add(item);
            }
        }
        Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
        Date checkDate = new Date();
        if (!serviceRequestList.isEmpty()) {
            // 更新服务请求状态待发送
            serviceRequestService.updateDraftStatus(
                serviceRequestList.stream().map(PerformInfoDto::getRequestId).toList(), practitionerId, checkDate);
        }
        if (!medRequestList.isEmpty()) {
            // 更新药品请求状态待发送
            medicationRequestService.updateDraftStatusBatch(
                medRequestList.stream().map(PerformInfoDto::getRequestId).toList(), practitionerId, checkDate);
        }
        return R.ok(null, "退回成功");
    }

    /**
     * 医嘱执行
     *
     * @param adviceExecuteParam 医嘱执行参数
     * @return 操作结果
     */
    @Override
    public R<?> adviceExecute(AdviceExecuteParam adviceExecuteParam) {
        // 医嘱集合
        List<AdviceExecuteDetailParam> adviceExecuteDetailList = adviceExecuteParam.getAdviceExecuteDetailList();
        // 药品
        List<AdviceExecuteDetailParam> medicineList = adviceExecuteDetailList.stream()
            .filter(e -> CommonConstants.TableName.MED_MEDICATION_REQUEST.equals(e.getAdviceTable())).toList();
        // 诊疗
        List<AdviceExecuteDetailParam> activityList = adviceExecuteDetailList.stream()
            .filter(e -> CommonConstants.TableName.WOR_SERVICE_REQUEST.equals(e.getAdviceTable())).toList();
        // -------------------------------------------药品
        if (!medicineList.isEmpty()) {
            // 组装药品请求用于执行的数据结构
            List<MedicationRequestUseExe> medUseExeList = this.assemblyMedication(medicineList);
            // 校验药品库存
            if (!medUseExeList.isEmpty()) {
                String tipRes = adviceUtils.checkExeMedInventory(medUseExeList);
                if (tipRes != null) {
                    return R.fail(null, tipRes);
                }
            }
            // 处理药品执行
            this.exeMedication(medUseExeList);
        }
        // -------------------------------------------诊疗
        if (!activityList.isEmpty()) {
            // 组装诊疗请求用于执行的数据结构
            List<ServiceRequestUseExe> actUseExeList = this.assemblyActivity(activityList);
            // 处理诊疗执行
            this.exeActivity(actUseExeList);
        }

        return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] {"医嘱执行"}));
    }

    /**
     * 医嘱取消执行
     *
     * @param adviceExecuteParam 取消执行参数
     * @return 操作结果
     */
    @Override
    public R<?> adviceCancel(AdviceExecuteParam adviceExecuteParam) {

        // 长期医嘱执行id
        List<Long> longProcIdList = adviceExecuteParam.getAdviceExecuteDetailList().stream()
            .filter(x -> x.getTherapyEnum().equals(TherapyTimeType.LONG_TERM.getValue()))
            .map(AdviceExecuteDetailParam::getProcedureId).distinct().toList();
        // 药品临时医嘱执行id
        List<Long> tempProcIdList = adviceExecuteParam.getAdviceExecuteDetailList().stream()
            .filter(x -> x.getTherapyEnum().equals(TherapyTimeType.TEMPORARY.getValue()))
            .map(AdviceExecuteDetailParam::getProcedureId).distinct().toList();

        // 合并长期和临时的执行记录
        List<Long> procedureIdList = new ArrayList<>(longProcIdList);
        procedureIdList.addAll(tempProcIdList);
        // 查询执行记录
        List<Procedure> procedureList = procedureService.listByIds(procedureIdList);
        Map<Long, Long> procedureIdMap = new HashMap<>();
        if (procedureList != null && !procedureList.isEmpty()) {
            for (Procedure procedure : procedureList) {
                // 根据执行记录新增取消执行记录
                Long newProcedureId = procedureService.addProcedureRecord(procedure.getEncounterId(),
                    procedure.getPatientId(), procedure.getRequestId(), procedure.getRequestTable(), EventStatus.CANCEL,
                    ProcedureCategory.INPATIENT_ADVICE, null, procedure.getOccurrenceTime(), procedure.getGroupId(),
                    procedure.getId());
                // 将旧的执行Id和新的执行Id存入map
                procedureIdMap.put(procedure.getId(), newProcedureId);
            }
        }
        // 分别处理长期和临时的医嘱
        // 长期已发放药品
        List<MedicationDispense> longMedDispensedList = new ArrayList<>();
        // 长期未发放药品
        List<MedicationDispense> longMedUndispenseList = new ArrayList<>();
        // 处理长期医嘱
        if (!longProcIdList.isEmpty()) {
            // 长期药品
            List<MedicationDispense> longMedDispenseList =
                medicationDispenseService.getMedDispenseByProcedureId(longProcIdList);
            // 分离已发药发药单和未发药发药单
            for (MedicationDispense medicationDispense : longMedDispenseList) {
                if (DispenseStatus.COMPLETED.getValue().equals(medicationDispense.getStatusEnum())) {
                    longMedDispensedList.add(medicationDispense);
                } else if (DispenseStatus.PREPARATION.getValue().equals(medicationDispense.getStatusEnum())) {
                    longMedUndispenseList.add(medicationDispense);
                }
            }
            // 查询账单
            List<ChargeItem> chargeItemList = chargeItemService.getChargeItemByProcedureId(longProcIdList);
            if (chargeItemList != null && !chargeItemList.isEmpty()) {
                for (ChargeItem chargeItem : chargeItemList) {
                    // 因执行医嘱时创建了账单，取消执行时需要软删除账单
                    chargeItemService.removeById(chargeItem);
                }
            }
        }

        // 临时已发放药品
        List<MedicationDispense> tempMedDispensedList = new ArrayList<>();
        // 临时未发放药品
        List<MedicationDispense> tempMedUndispenseList = new ArrayList<>();
        // 处理临时医嘱
        if (!tempProcIdList.isEmpty()) {
            // 临时药品
            List<MedicationDispense> tempMedDispenseList =
                medicationDispenseService.getMedDispenseByProcedureId(tempProcIdList);
            // 分离已发药发药单和未发药发药单
            for (MedicationDispense medicationDispense : tempMedDispenseList) {
                if (DispenseStatus.COMPLETED.getValue().equals(medicationDispense.getStatusEnum())) {
                    tempMedDispensedList.add(medicationDispense);
                } else if (DispenseStatus.PREPARATION.getValue().equals(medicationDispense.getStatusEnum())) {
                    tempMedUndispenseList.add(medicationDispense);
                }
            }
            // 查询账单
            List<ChargeItem> chargeItemList = chargeItemService.getChargeItemByProcedureId(tempProcIdList);
            if (chargeItemList != null && !chargeItemList.isEmpty()) {
                // 临时医嘱取消执行时，将临时医嘱的收费项目状态改为待收费
                chargeItemService
                    .updatePlannedChargeStatus(chargeItemList.stream().map(ChargeItem::getProcedureId).toList());
            }
        }
        // 处理长期已发放的药品
        if (!longMedDispensedList.isEmpty()) {
            // 生成退药单
            this.creatRefundMedicationList(tempMedDispensedList, procedureIdMap);
        }
        // 处理临时已发放药品
        if (!tempMedDispensedList.isEmpty()) {
            // 生成退药单
            this.creatRefundMedicationList(tempMedDispensedList, procedureIdMap);
            // 药品退药请求状态变更(待退药)
            medicationRequestService.updateCancelledStatusBatch(
                tempMedDispensedList.stream().map(MedicationDispense::getMedReqId).toList(), null, null);
        }
        // 未发放药品需先判断是否申请汇总
        List<MedicationDispense> medUndispenseList = new ArrayList<>(tempMedUndispenseList);
        medUndispenseList.addAll(longMedUndispenseList);
        // 重新生成汇总发药单
        if (!medUndispenseList.isEmpty()) {
            // 查询所有汇总单号
            List<String> summaryNoList =
                medUndispenseList.stream().filter(x -> x.getSummaryNo() != null && !x.getSummaryNo().isEmpty())
                    .map(MedicationDispense::getSummaryNo).distinct().toList();
            if (!summaryNoList.isEmpty()) {
                // 查询所有被汇总的药品发放
                List<MedicationDispense> medSummaryList =
                    medicationDispenseService.getMedDispenseBySummaryNo(summaryNoList);
                // 被汇总的发药单
                Map<String, List<MedicationDispense>> medSummaryMap =
                    medSummaryList.stream().filter(x -> x.getSummaryNo() != null)
                        .collect(Collectors.groupingBy(MedicationDispense::getSummaryNo));
                // 被取消汇总的发药单
                Map<String, List<MedicationDispense>> medSummaryCancelMap =
                    medUndispenseList.stream().filter(x -> x.getSummaryNo() != null)
                        .collect(Collectors.groupingBy(MedicationDispense::getSummaryNo));
                // 过滤掉被取消汇总的发药单,重新做成汇总单
                Map<String, List<MedicationDispense>> reSummaryMap = new HashMap<>();
                // 遍历所有被汇总的发药单
                for (Map.Entry<String, List<MedicationDispense>> entry : medSummaryMap.entrySet()) {
                    String summaryNo = entry.getKey();
                    List<MedicationDispense> allDispenses = entry.getValue();
                    // 检查是否存在对应的取消汇总记录
                    if (medSummaryCancelMap.containsKey(summaryNo)) {
                        // 如果存在取消记录，需要过滤掉被取消的发药单
                        List<MedicationDispense> cancelDispenses = medSummaryCancelMap.get(summaryNo);
                        // 创建被取消的发药单ID集合，便于快速查找
                        Set<Long> cancelIds =
                            cancelDispenses.stream().map(MedicationDispense::getId).collect(Collectors.toSet());
                        // 过滤出未被取消的发药单
                        List<MedicationDispense> remainingDispenses = allDispenses.stream()
                            .filter(dispense -> !cancelIds.contains(dispense.getId())).collect(Collectors.toList());
                        // 只有当还有剩余的发药单时才加入结果
                        if (!remainingDispenses.isEmpty()) {
                            reSummaryMap.put(summaryNo, remainingDispenses);
                        }
                    }
                }
                if (!reSummaryMap.isEmpty()) {
                    for (Map.Entry<String, List<MedicationDispense>> entry : reSummaryMap.entrySet()) {
                        List<MedicationDispense> medicationDispenses = entry.getValue();
                        // 查询药品信息
                        List<MedicationDefinition> medicationDefinitions = medicationDefinitionService
                            .listByIds(medicationDispenses.stream().map(MedicationDispense::getMedicationId).toList());
                        // 汇总单据号
                        String busNo = assignSeqUtil.getSeqByDay(AssignSeqEnum.MEDICINE_SUMMARY_NO.getPrefix(), 4);
                        // 根据相同批号的同一药品进行汇总
                        Map<String, List<MedicationDispense>> medicationSummaryInfoMap =
                            medicationDispenses.stream().collect(Collectors
                                .groupingBy(x -> x.getMedicationId() + CommonConstants.Common.DASH + x.getLotNumber()));
                        for (Map.Entry<String,
                            List<MedicationDispense>> medicationSummaryInfoList : medicationSummaryInfoMap.entrySet()) {
                            List<MedicationDispense> medicationSummaryList = medicationSummaryInfoList.getValue();
                            MedicationDispense medicationDispense = medicationSummaryList.get(0);
                            Long medicationId = medicationDispense.getMedicationId();
                            Long locationId = medicationDispense.getLocationId();
                            String lotNumber = medicationDispense.getLotNumber();
                            Long patientId = medicationDispense.getPatientId();
                            // 拆零比例
                            BigDecimal partPercent = BigDecimal.ONE;
                            // 最小单位
                            String minUnitCode = null;
                            // 单位
                            String unitCode = null;
                            // 根据药品id获取对应的药品信息
                            Optional<MedicationDefinition> medicationDefinition =
                                medicationDefinitions.stream().filter(x -> x.getId().equals(medicationId)).findFirst();
                            if (medicationDefinition.isPresent()) {
                                partPercent = medicationDefinition.get().getPartPercent();
                                minUnitCode = medicationDefinition.get().getMinUnitCode();
                                unitCode = medicationDefinition.get().getUnitCode();
                            }
                            if (minUnitCode == null || unitCode == null || partPercent == null) {
                                throw new ServiceException("药品信息有误,请联系管理员");
                            }
                            // 最小单位数量
                            BigDecimal minQuantity = BigDecimal.ZERO;
                            for (MedicationDispense medicationSummaryInfo : medicationSummaryList) {
                                BigDecimal quantity = medicationSummaryInfo.getQuantity();
                                if (unitCode.equals(medicationDispense.getUnitCode())) {
                                    // 转换为小单位进行累加
                                    quantity = medicationSummaryInfo.getQuantity().multiply(partPercent);
                                }
                                minQuantity = minQuantity.add(quantity);
                            }
                            Long supplyRequestId = supplyRequestService.createSummarySupplyRequest(medicationId,
                                locationId, lotNumber, patientId, minQuantity, minUnitCode, null, null, busNo);
                            supplyDeliveryService.createSummarySupplyDelivery(medicationId, lotNumber, patientId,
                                minQuantity, minUnitCode, null, supplyRequestId);
                        }
                    }
                    // 删除原汇总单
                    List<String> delSummaryNoList = reSummaryMap.keySet().stream().toList();
                    // 取消汇总申请（软删除）
                    List<Long> requestIdList = supplyRequestService.cancelSummarySupplyRequest(delSummaryNoList);
                    // 软删除汇总发放
                    supplyDeliveryService.deleteSupplyDeliveryByReqId(requestIdList);
                    // 取消药品汇总
                    medicationDispenseService.cancelMedicationSummary(delSummaryNoList);
                }
            }
        }
        // 处理临时未发放的药品
        if (!tempMedUndispenseList.isEmpty()) {
            // 排除已汇总的药品
            List<MedicationDispense> medicationDispenseList =
                tempMedUndispenseList.stream().filter(x -> x.getSummaryNo() == null).toList();
            // 更新状态为草稿
            medicationDispenseService.updateDispenseStatus(
                medicationDispenseList.stream().map(MedicationDispense::getId).toList(),
                DispenseStatus.DRAFT.getValue());
        }
        // 处理长期未发的药品
        if (!longMedUndispenseList.isEmpty()) {
            // 排除已汇总的药品
            List<MedicationDispense> medicationDispenseList =
                tempMedUndispenseList.stream().filter(x -> x.getSummaryNo() == null).toList();
            medicationDispenseService
                .removeByIds(medicationDispenseList.stream().map(MedicationDispense::getId).toList());
        }
        // 根据执行记录查询生成的耗材发放单
        List<DeviceDispense> deviceDispenseList = deviceDispenseService.getDevDispenseByProcedureId(procedureIdList);
        if (deviceDispenseList != null && !deviceDispenseList.isEmpty()) {
            // 软删除对应的耗材申请，耗材发放
            deviceDispenseService.removeByIds(deviceDispenseList.stream().map(DeviceDispense::getId).toList());
            deviceRequestService.removeByIds(deviceDispenseList.stream().map(DeviceDispense::getDeviceReqId).toList());
        }
        return R.ok("取消执行成功,相关汇总领药单已重新生成");
    }

    /**
     * 医嘱不执行
     *
     * @param adviceExecuteParam 不执行参数
     * @return 操作结果
     */
    @Override
    public R<?> adviceVoid(AdviceExecuteParam adviceExecuteParam) {
        List<AdviceExecuteDetailParam> adviceExecuteDetailList = adviceExecuteParam.getAdviceExecuteDetailList();

        // 判断是否有空的pID
        for (AdviceExecuteDetailParam adviceExecuteDetailParam : adviceExecuteDetailList)
            if (adviceExecuteDetailParam.getProcedureId() != null)
                return R.fail("执行失败");

        for (AdviceExecuteDetailParam adviceExecuteDetailParam : adviceExecuteDetailList) {
            for (String executeTime : adviceExecuteDetailParam.getExecuteTimes()) {
                // 生成执行记录
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                // 转换为 LocalDateTime
                LocalDateTime localDateTime = LocalDateTime.parse(executeTime, formatter);
                // 转换为 Date
                Date exeDate = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
                // 根据执行记录新增不执行记录
                procedureService.addProcedureRecord(adviceExecuteDetailParam.getEncounterId(),
                    adviceExecuteDetailParam.getPatientId(), adviceExecuteDetailParam.getRequestId(),
                    adviceExecuteDetailParam.getAdviceTable(), EventStatus.STOPPED, ProcedureCategory.INPATIENT_ADVICE,
                    null, exeDate, adviceExecuteDetailParam.getGroupId(), null);
            }
        }
        return R.ok("不执行成功");
    }

    /**
     * 执行药品医嘱
     *
     * @param medUseExeList 药品医嘱
     */
    private void exeMedication(List<MedicationRequestUseExe> medUseExeList) {
        // 长期医嘱
        List<MedicationRequestUseExe> longMedication = medUseExeList.stream()
            .filter(e -> TherapyTimeType.LONG_TERM.getValue().equals(e.getTherapyEnum())).toList();
        // 临时医嘱
        List<MedicationRequestUseExe> tempMedication = medUseExeList.stream()
            .filter(e -> TherapyTimeType.TEMPORARY.getValue().equals(e.getTherapyEnum())).toList();
        // 药品定义id集合
        List<Long> medicationDefinitionIdList =
            medUseExeList.stream().map(MedicationRequestUseExe::getMedicationId).collect(Collectors.toList());
        // 医嘱详细信息
        List<AdviceBaseDto> medicationInfos = doctorStationAdviceAppService.getAdviceBaseInfo(null, null, null,
            medicationDefinitionIdList, 0L, 1, 500, Whether.NO.getValue(), List.of(1)).getRecords();

        // 当前时间
        Date curDate = new Date();
        // 参与者id
        Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
        // 当前登录账号的科室id
        Long orgId = SecurityUtils.getLoginUser().getOrgId();

        // 长期
        MedicationRequest longMedicationRequest;
        ChargeItem chargeItem;
        for (MedicationRequestUseExe medicationRequestUseExe : longMedication) {
            for (String executeTime : medicationRequestUseExe.getExecuteTimes()) {
                longMedicationRequest = new MedicationRequest();
                BeanUtils.copyProperties(medicationRequestUseExe, longMedicationRequest);

                // 生成执行记录
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                // 转换为 LocalDateTime
                LocalDateTime localDateTime = LocalDateTime.parse(executeTime, formatter);
                // 转换为 Date
                Date exeDate = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
                // 执行记录id
                Long procedureId = procedureService.addProcedureRecord(longMedicationRequest.getEncounterId(),
                    longMedicationRequest.getPatientId(), longMedicationRequest.getId(),
                    CommonConstants.TableName.MED_MEDICATION_REQUEST, EventStatus.COMPLETED,
                    ProcedureCategory.INPATIENT_ADVICE, null, exeDate, longMedicationRequest.getGroupId(), null);

                // 生成药品发放
                Long dispenseId =
                    medicationDispenseService.generateMedicationDispense(longMedicationRequest, procedureId, exeDate);

                // 生成账单
                chargeItem = new ChargeItem();
                chargeItem.setStatusEnum(ChargeItemStatus.BILLABLE.getValue()); // 收费状态
                chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(longMedicationRequest.getBusNo()));
                chargeItem.setGenerateSourceEnum(GenerateSource.ORDER_EXECUTE.getValue()); // 生成来源
                chargeItem.setPrescriptionNo(longMedicationRequest.getPrescriptionNo()); // 处方号
                chargeItem.setPatientId(longMedicationRequest.getPatientId()); // 患者
                chargeItem.setContextEnum(ChargeItemContext.MEDICATION.getValue()); // 类型
                chargeItem.setEncounterId(longMedicationRequest.getEncounterId()); // 就诊id
                chargeItem.setEntererId(practitionerId);// 开立人ID
                chargeItem.setRequestingOrgId(orgId); // 开立科室
                chargeItem.setEnteredDate(curDate); // 开立时间
                chargeItem.setServiceTable(CommonConstants.TableName.MED_MEDICATION_REQUEST);// 医疗服务类型
                chargeItem.setServiceId(longMedicationRequest.getId()); // 医疗服务ID
                chargeItem.setProductTable(CommonConstants.TableName.MED_MEDICATION_DEFINITION);// 产品所在表
                chargeItem.setProductId(longMedicationRequest.getMedicationId());// 收费项id
                chargeItem.setAccountId(medicationRequestUseExe.getAccountId());// 关联账户ID
                chargeItem.setConditionId(longMedicationRequest.getConditionId()); // 诊断id
                chargeItem.setEncounterDiagnosisId(longMedicationRequest.getEncounterDiagnosisId()); // 就诊诊断id
                chargeItem.setProcedureId(procedureId); // 执行id
                chargeItem.setDispenseTable(CommonConstants.TableName.MED_MEDICATION_DISPENSE); // 发放表名
                // chargeItem.setDispenseId(dispenseId); // 发放ID

                // ------------------------------ 匹配定价信息
                // 根据 medicationId 查找对应的医嘱信息
                MedicationRequest finalLongMedicationRequest = longMedicationRequest;
                Optional<AdviceBaseDto> matchedAdvice = medicationInfos.stream()
                    .filter(
                        advice -> finalLongMedicationRequest.getMedicationId().equals(advice.getAdviceDefinitionId()))
                    .findFirst();
                if (matchedAdvice.isEmpty()) {
                    throw new RuntimeException(
                        "未找到对应的医嘱信息，medicationId: " + finalLongMedicationRequest.getMedicationId());
                }
                AdviceBaseDto advice = matchedAdvice.get();
                // 获取拆零比
                BigDecimal partPercent = advice.getPartPercent();

                // 在 priceList 中查找匹配的定价信息
                Optional<AdvicePriceDto> matchedPrice = advice.getPriceList().stream()
                    .filter(price -> finalLongMedicationRequest.getLotNumber().equals(price.getConditionValue()))
                    .findFirst();
                if (matchedPrice.isEmpty()) {
                    throw new RuntimeException("未找到匹配的定价信息，lotNumber: " + finalLongMedicationRequest.getLotNumber());
                }
                AdvicePriceDto priceDto = matchedPrice.get();
                // 计算价格
                BigDecimal price;
                if (finalLongMedicationRequest.getUnitCode().equals(priceDto.getUnitCode())) {
                    // (大单位)unitCode 匹配，直接取 price
                    price = priceDto.getPrice();
                } else {
                    // (小单位)unitCode 不匹配，计算 price / partPercent
                    price = priceDto.getPrice().divide(partPercent, 6, RoundingMode.HALF_UP);
                }
                chargeItem.setDefinitionId(priceDto.getDefinitionId()); // 费用定价ID
                chargeItem.setDefDetailId(priceDto.getDefinitionDetailId()); // 定价子表主键
                chargeItem.setQuantityValue(longMedicationRequest.getQuantity()); // 数量
                chargeItem.setQuantityUnit(longMedicationRequest.getUnitCode()); // 单位
                chargeItem.setUnitPrice(price); // 单价
                chargeItem.setTotalPrice(
                    longMedicationRequest.getQuantity().multiply(price).setScale(6, RoundingMode.HALF_UP)); // 总价

                chargeItemService.saveOrUpdate(chargeItem);
            }
        }

        // 临时
        MedicationRequest tempMedicationRequest;
        for (MedicationRequestUseExe medicationRequestUseExe : tempMedication) {
            for (String executeTime : medicationRequestUseExe.getExecuteTimes()) {
                tempMedicationRequest = new MedicationRequest();
                BeanUtils.copyProperties(medicationRequestUseExe, tempMedicationRequest);

                // 生成执行记录
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                // 转换为 LocalDateTime
                LocalDateTime localDateTime = LocalDateTime.parse(executeTime, formatter);
                // 转换为 Date
                Date exeDate = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
                // 执行记录id
                Long procedureId = procedureService.addProcedureRecord(tempMedicationRequest.getEncounterId(),
                    tempMedicationRequest.getPatientId(), tempMedicationRequest.getId(),
                    CommonConstants.TableName.MED_MEDICATION_REQUEST, EventStatus.COMPLETED,
                    ProcedureCategory.INPATIENT_ADVICE, null, exeDate, tempMedicationRequest.getGroupId(), null);

                // 更新药品放发状态
                medicationDispenseService.update(new LambdaUpdateWrapper<MedicationDispense>()
                    .eq(MedicationDispense::getMedReqId, tempMedicationRequest.getId())
                    .set(MedicationDispense::getStatusEnum, DispenseStatus.PREPARATION.getValue()));

                // 更新账单状态
                chargeItemService.update(
                    new LambdaUpdateWrapper<ChargeItem>().eq(ChargeItem::getServiceId, tempMedicationRequest.getId())
                        .set(ChargeItem::getProcedureId, procedureId)
                        .set(ChargeItem::getStatusEnum, ChargeItemStatus.BILLABLE.getValue()));
            }
        }

    }

    /**
     * 执行诊疗医嘱
     *
     * @param actUseExeList 诊疗医嘱
     */
    private void exeActivity(List<ServiceRequestUseExe> actUseExeList) {
        // 长期医嘱
        List<ServiceRequestUseExe> longActivity = actUseExeList.stream()
            .filter(e -> TherapyTimeType.LONG_TERM.getValue().equals(e.getTherapyEnum())).toList();
        // 临时医嘱
        List<ServiceRequestUseExe> tempActivity = actUseExeList.stream()
            .filter(e -> TherapyTimeType.TEMPORARY.getValue().equals(e.getTherapyEnum())).toList();
        // 诊疗定义id集合
        List<Long> activityDefinitionIdList =
            actUseExeList.stream().map(ServiceRequestUseExe::getActivityId).collect(Collectors.toList());
        // 医嘱详细信息
        List<AdviceBaseDto> activityInfos = doctorStationAdviceAppService.getAdviceBaseInfo(null, null, null,
            activityDefinitionIdList, 0L, 1, 500, Whether.NO.getValue(), List.of(3)).getRecords();

        // 当前时间
        Date curDate = new Date();
        // 参与者id
        Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
        // 当前登录账号的科室id
        Long orgId = SecurityUtils.getLoginUser().getOrgId();

        Date clickDate = new Date();

        // 长期
        ServiceRequest longServiceRequest;
        ChargeItem chargeItem;
        for (ServiceRequestUseExe serviceRequestUseExe : longActivity) {
            // 查询耗材的取药科室
            Long takeDeviceLocationId = doctorStationAdviceAppMapper
                .getTakeDeviceLocationId(serviceRequestUseExe.getEncounterId(), ItemCategoryCode.DEVICE.getCode());
            for (String executeTime : serviceRequestUseExe.getExecuteTimes()) {
                longServiceRequest = new ServiceRequest();
                BeanUtils.copyProperties(serviceRequestUseExe, longServiceRequest);

                // 生成执行记录
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                // 转换为 LocalDateTime
                LocalDateTime localDateTime = LocalDateTime.parse(executeTime, formatter);
                // 转换为 Date
                Date exeDate = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
                // 执行记录id
                Long procedureId = procedureService.addProcedureRecord(longServiceRequest.getEncounterId(),
                    longServiceRequest.getPatientId(), longServiceRequest.getId(),
                    CommonConstants.TableName.WOR_SERVICE_REQUEST, EventStatus.COMPLETED,
                    ProcedureCategory.INPATIENT_ADVICE, null, exeDate, null, null);

                // 生成账单
                chargeItem = new ChargeItem();
                chargeItem.setStatusEnum(ChargeItemStatus.BILLABLE.getValue()); // 收费状态
                chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(longServiceRequest.getBusNo()));
                chargeItem.setGenerateSourceEnum(GenerateSource.ORDER_EXECUTE.getValue()); // 生成来源
                chargeItem.setPatientId(longServiceRequest.getPatientId()); // 患者
                chargeItem.setContextEnum(ChargeItemContext.ACTIVITY.getValue()); // 类型
                chargeItem.setEncounterId(longServiceRequest.getEncounterId()); // 就诊id
                chargeItem.setEntererId(practitionerId);// 开立人ID
                chargeItem.setRequestingOrgId(orgId); // 开立科室
                chargeItem.setEnteredDate(curDate); // 开立时间
                chargeItem.setServiceTable(CommonConstants.TableName.WOR_SERVICE_REQUEST);// 医疗服务类型
                chargeItem.setServiceId(longServiceRequest.getId()); // 医疗服务ID
                chargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION);// 产品所在表
                chargeItem.setProductId(longServiceRequest.getActivityId());// 收费项id
                chargeItem.setAccountId(serviceRequestUseExe.getAccountId());// 关联账户ID
                chargeItem.setConditionId(longServiceRequest.getConditionId()); // 诊断id
                chargeItem.setEncounterDiagnosisId(longServiceRequest.getEncounterDiagnosisId()); // 就诊诊断id
                chargeItem.setProcedureId(procedureId);
                // ------------------------------ 匹配定价信息
                // 根据 activityId 查找对应的医嘱信息
                ServiceRequest finalLongServiceRequest = longServiceRequest;
                Optional<AdviceBaseDto> matchedAdvice = activityInfos.stream()
                    .filter(advice -> finalLongServiceRequest.getActivityId().equals(advice.getAdviceDefinitionId()))
                    .findFirst();
                if (matchedAdvice.isEmpty()) {
                    throw new RuntimeException("未找到对应的医嘱信息，activityId: " + finalLongServiceRequest.getActivityId());
                }
                // 医嘱信息
                AdviceBaseDto advice = matchedAdvice.get();
                // 定价信息
                AdvicePriceDto priceDto = advice.getPriceList().get(0);
                // 计算价格
                BigDecimal price = priceDto.getPrice();

                chargeItem.setDefinitionId(priceDto.getDefinitionId()); // 费用定价ID
                chargeItem.setQuantityValue(longServiceRequest.getQuantity()); // 数量
                chargeItem.setQuantityUnit(longServiceRequest.getUnitCode()); // 单位
                chargeItem.setUnitPrice(price); // 单价
                chargeItem
                    .setTotalPrice(longServiceRequest.getQuantity().multiply(price).setScale(6, RoundingMode.HALF_UP)); // 总价
                chargeItemService.saveOrUpdate(chargeItem);

                // 处理诊疗绑定耗材
                this.handleBindDevice(longServiceRequest, procedureId, takeDeviceLocationId, curDate, practitionerId,
                    orgId, serviceRequestUseExe.getAccountId(), exeDate, clickDate);
            }
        }

        // 临时
        ServiceRequest tempServiceRequest;
        for (ServiceRequestUseExe serviceRequestUseExe : tempActivity) {
            // 查询耗材的取药科室
            Long takeDeviceLocationId = doctorStationAdviceAppMapper
                .getTakeDeviceLocationId(serviceRequestUseExe.getEncounterId(), ItemCategoryCode.DEVICE.getCode());
            for (String executeTime : serviceRequestUseExe.getExecuteTimes()) {
                tempServiceRequest = new ServiceRequest();
                BeanUtils.copyProperties(serviceRequestUseExe, tempServiceRequest);

                // 生成执行记录
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                // 转换为 LocalDateTime
                LocalDateTime localDateTime = LocalDateTime.parse(executeTime, formatter);
                // 转换为 Date
                Date exeDate = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
                // 执行记录id
                Long procedureId = procedureService.addProcedureRecord(tempServiceRequest.getEncounterId(),
                    tempServiceRequest.getPatientId(), tempServiceRequest.getId(),
                    CommonConstants.TableName.WOR_SERVICE_REQUEST, EventStatus.COMPLETED,
                    ProcedureCategory.INPATIENT_ADVICE, null, exeDate, null, null);

                // 更新账单状态
                chargeItemService.update(
                    new LambdaUpdateWrapper<ChargeItem>().eq(ChargeItem::getServiceId, tempServiceRequest.getId())
                        .set(ChargeItem::getProcedureId, procedureId)
                        .set(ChargeItem::getStatusEnum, ChargeItemStatus.BILLABLE.getValue()));

                // 处理诊疗绑定耗材
                this.handleBindDevice(tempServiceRequest, procedureId, takeDeviceLocationId, curDate, practitionerId,
                    orgId, serviceRequestUseExe.getAccountId(), exeDate, clickDate);
            }
        }

    }

    /**
     * 处理诊疗绑定耗材
     *
     * @param serviceRequest 诊疗请求
     * @param procedureId 执行id
     * @param locationId 取耗材的库房id
     * @param curDate 时间
     * @param practitionerId 参与者id
     * @param orgId 当前登录账号的科室id
     * @param accountId 支付账号ID
     * @param exeDate 执行时间(页面传的)
     * @param clickDate 执行时间(实际点击时间)
     */
    private void handleBindDevice(ServiceRequest serviceRequest, Long procedureId, Long locationId, Date curDate,
        Long practitionerId, Long orgId, Long accountId, Date exeDate, Date clickDate) {
        // 查询诊疗绑定耗材的信息
        List<ActivityBindDeviceDetailDto> activityBindDevice =
            doctorStationAdviceAppMapper.getActivityBindDevice(serviceRequest.getActivityId() + "",
                PublicationStatus.ACTIVE.getValue(), CommonConstants.TableName.ADM_DEVICE_DEFINITION);
        // 满足此条件,说明诊疗绑了耗材
        if (!activityBindDevice.isEmpty()) {
            if (locationId == null) {
                throw new RuntimeException("【" + serviceRequest.getBusNo() + "】诊疗请求,绑定的耗材未配置发放耗材房");
            }
            // 耗材定义id集合
            List<Long> deviceIds =
                activityBindDevice.stream().map(ActivityBindDeviceDetailDto::getDeviceId).collect(Collectors.toList());
            // 耗材库存集合
            List<AdviceInventoryDto> adviceInventoryList = doctorStationAdviceAppMapper.getAdviceInventory(locationId,
                deviceIds, CommonConstants.SqlCondition.ABOUT_INVENTORY_TABLE_STR, PublicationStatus.ACTIVE.getValue());
            // 待发放个数信息
            List<AdviceInventoryDto> adviceDraftInventoryList = doctorStationAdviceAppMapper.getAdviceDraftInventory(
                CommonConstants.TableName.MED_MEDICATION_DEFINITION, CommonConstants.TableName.ADM_DEVICE_DEFINITION,
                DispenseStatus.DRAFT.getValue(), DispenseStatus.PREPARATION.getValue());
            // 预减库存
            List<AdviceInventoryDto> adviceInventory =
                adviceUtils.subtractInventory(adviceInventoryList, adviceDraftInventoryList);

            // 耗材医嘱详细信息
            List<AdviceBaseDto> deviceInfos = doctorStationAdviceAppService
                .getAdviceBaseInfo(null, null, null, deviceIds, 0L, 1, 500, Whether.NO.getValue(), List.of(2))
                .getRecords();

            DeviceRequest deviceRequest;
            BigDecimal minUnitCodeQuantity;
            ChargeItem chargeItem;
            for (ActivityBindDeviceDetailDto activityBindDeviceDetailDto : activityBindDevice) {
                Optional<AdviceInventoryDto> matchedInventory = adviceInventory.stream()
                    .filter(inventoryDto -> activityBindDeviceDetailDto.getDeviceId().equals(inventoryDto.getItemId())
                        && CommonConstants.TableName.ADM_DEVICE_DEFINITION.equals(inventoryDto.getItemTable())
                        && locationId.equals(inventoryDto.getLocationId()))
                    .findFirst();
                // 匹配到库存信息,并校验
                if (matchedInventory.isPresent()) {
                    AdviceInventoryDto inventoryDto = matchedInventory.get();
                    if (activityBindDeviceDetailDto.getMinUnitCodeQuantity()
                        .compareTo(inventoryDto.getQuantity()) > 0) {
                        throw new RuntimeException("【" + serviceRequest.getBusNo() + "】诊疗请求,绑定的耗材数量不足");
                    }
                } else {
                    throw new RuntimeException("【" + serviceRequest.getBusNo() + "】诊疗请求,绑定的耗材未匹配到库存信息");
                }
                // 根据 deviceId 查找对应的医嘱信息
                Optional<AdviceBaseDto> deviceAdvice = deviceInfos.stream()
                    .filter(advice -> activityBindDeviceDetailDto.getDeviceId().equals(advice.getAdviceDefinitionId()))
                    .findFirst();
                if (deviceAdvice.isEmpty()) {
                    throw new RuntimeException("未找到对应的医嘱信息，deviceId: " + activityBindDeviceDetailDto.getDeviceId());
                }
                AdviceBaseDto advice = deviceAdvice.get();

                // 生成耗材请求
                deviceRequest = new DeviceRequest();
                deviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.DEVICE_RES_NO.getPrefix(), 4)); // 耗材请求编码
                deviceRequest.setGenerateSourceEnum(GenerateSource.ORDER_EXECUTE.getValue()); // 生成来源
                deviceRequest.setStatusEnum(RequestStatus.COMPLETED.getValue()); // 请求状态
                deviceRequest.setBasedOnId(serviceRequest.getId());
                deviceRequest.setBasedOnTable(CommonConstants.TableName.WOR_SERVICE_REQUEST);
                deviceRequest.setQuantity(activityBindDeviceDetailDto.getQuantity()); // 请求数量
                deviceRequest.setUnitCode(activityBindDeviceDetailDto.getUnitCode()); // 请求单位编码
                // deviceRequest.setLotNumber();// 产品批号
                deviceRequest.setCategoryEnum(Integer.valueOf(advice.getCategoryCode())); // 请求类型
                deviceRequest.setDeviceDefId(activityBindDeviceDetailDto.getDeviceId());// 耗材定义id
                deviceRequest.setPatientId(serviceRequest.getPatientId()); // 患者
                deviceRequest.setRequesterId(practitionerId); // 开方人
                deviceRequest.setOrgId(orgId);// 开方人科室
                deviceRequest.setReqAuthoredTime(clickDate); // 请求开始时间
                deviceRequest.setPerformLocation(locationId); // 发放耗材房
                deviceRequest.setEncounterId(serviceRequest.getEncounterId()); // 就诊id
                deviceRequest.setConditionId(serviceRequest.getConditionId()); // 诊断id
                deviceRequest.setEncounterDiagnosisId(serviceRequest.getEncounterDiagnosisId()); // 就诊诊断id

                deviceRequestService.save(deviceRequest);

                // 需要的耗材数量(小单位)
                minUnitCodeQuantity = activityBindDeviceDetailDto.getMinUnitCodeQuantity();
                // 单位(小单位)
                // String minUnitCode = activityBindDeviceDetailDto.getMinUnitCode();
                // 库存集合
                List<AdviceInventoryDto> inventoryList = advice.getInventoryList();
                // 价格集合
                List<AdvicePriceDto> priceList = advice.getPriceList();
                // 剩余需要分配的数量
                BigDecimal remainingQuantity = minUnitCodeQuantity;
                // 按批次循环分配数量
                for (AdviceInventoryDto inventory : inventoryList) {
                    if (remainingQuantity.compareTo(BigDecimal.ZERO) <= 0) {
                        break;
                    }
                    // 可用量
                    BigDecimal availableQuantity = inventory.getQuantity();
                    // 实际供应量
                    BigDecimal actualQuantity;
                    if (availableQuantity.compareTo(remainingQuantity) >= 0) {
                        // 当前批次足够满足剩余需求
                        actualQuantity = remainingQuantity;
                        remainingQuantity = BigDecimal.ZERO;
                    } else {
                        // 当前批次全部用完
                        actualQuantity = availableQuantity;
                        remainingQuantity = remainingQuantity.subtract(availableQuantity);
                    }
                    // 批次号
                    String lotNumber = inventory.getLotNumber();
                    // 查找对应批次的价格
                    AdvicePriceDto priceInfos = findPriceByLotNumber(inventory.getLotNumber(), priceList);

                    // 根据批次库存量,生成耗材发放
                    deviceRequest.setQuantity(actualQuantity);
                    deviceRequest.setLotNumber(lotNumber);
                    // 发放ID
                    Long dispenseId =
                        deviceDispenseService.generateDeviceDispense(deviceRequest, procedureId, locationId, exeDate);

                    // 根据批次库存量,生成账单
                    chargeItem = new ChargeItem();
                    chargeItem.setStatusEnum(ChargeItemStatus.BILLABLE.getValue()); // 收费状态
                    chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(deviceRequest.getBusNo()));
                    chargeItem.setGenerateSourceEnum(GenerateSource.ORDER_EXECUTE.getValue()); // 生成来源
                    chargeItem.setPatientId(deviceRequest.getPatientId()); // 患者
                    chargeItem.setContextEnum(ChargeItemContext.DEVICE.getValue()); // 类型
                    chargeItem.setEncounterId(deviceRequest.getEncounterId()); // 就诊id
                    chargeItem.setDefinitionId(priceInfos.getDefinitionId()); // 费用定价ID
                    chargeItem.setDefDetailId(priceInfos.getDefinitionDetailId()); // 定价子表主键
                    chargeItem.setEntererId(practitionerId);// 开立人ID
                    chargeItem.setRequestingOrgId(orgId); // 开立科室
                    chargeItem.setEnteredDate(curDate); // 开立时间
                    chargeItem.setServiceTable(CommonConstants.TableName.WOR_DEVICE_REQUEST);// 医疗服务类型
                    chargeItem.setServiceId(deviceRequest.getId()); // 医疗服务ID
                    chargeItem.setProductTable(CommonConstants.TableName.ADM_DEVICE_DEFINITION);// 产品所在表
                    chargeItem.setProductId(activityBindDeviceDetailDto.getDeviceId());// 收费项id
                    chargeItem.setAccountId(accountId);// 关联账户ID
                    chargeItem.setConditionId(deviceRequest.getConditionId()); // 诊断id
                    chargeItem.setEncounterDiagnosisId(deviceRequest.getEncounterDiagnosisId()); // 就诊诊断id
                    chargeItem.setDispenseTable(CommonConstants.TableName.WOR_DEVICE_DISPENSE); // 发放表名
                    // chargeItem.setDispenseId(dispenseId); // 发放ID

                    chargeItem.setQuantityValue(actualQuantity); // 数量
                    chargeItem.setQuantityUnit(priceInfos.getUnitCode()); // 单位
                    chargeItem.setUnitPrice(priceInfos.getPrice()); // 单价
                    chargeItem.setTotalPrice(
                        actualQuantity.multiply(priceInfos.getPrice()).setScale(6, RoundingMode.HALF_UP)); // 总价

                    chargeItemService.save(chargeItem);

                }
            }
        }
    }

    /**
     * 根据批次号查找价格
     *
     * @param lotNumber 批次号
     * @param priceList 价格列表
     * @return 价格信息，如果找不到则返回null
     */
    private AdvicePriceDto findPriceByLotNumber(String lotNumber, List<AdvicePriceDto> priceList) {
        if (priceList == null || priceList.isEmpty()) {
            return null;
        }
        return priceList.stream().filter(price -> Objects.equals(price.getConditionValue(), lotNumber)).findFirst()
            .get();

    }

    /**
     * 组装药品请求用于执行的数据结构
     *
     * @param medicineList 药品执行参数
     * @return 用于执行的药品请求
     */
    private List<MedicationRequestUseExe> assemblyMedication(List<AdviceExecuteDetailParam> medicineList) {
        // 药品请求id集合
        List<Long> medicineRequestIdList =
            medicineList.stream().map(AdviceExecuteDetailParam::getRequestId).collect(Collectors.toList());
        // 药品请求集合
        List<MedicationRequest> medicationRequestList = medicationRequestService
            .list(new LambdaQueryWrapper<MedicationRequest>().in(MedicationRequest::getId, medicineRequestIdList));
        // 药品定义id集合
        List<Long> medicationDefinitionIdList =
            medicationRequestList.stream().map(MedicationRequest::getMedicationId).collect(Collectors.toList());
        // 药品定义集合
        List<MedicationDefinition> medicationDefinitionList = medicationDefinitionService.list(
            new LambdaQueryWrapper<MedicationDefinition>().in(MedicationDefinition::getId, medicationDefinitionIdList));
        // 组装MedicationRequestUseExe数据结构
        List<MedicationRequestUseExe> medUseExeList = medicationRequestList.stream().map(medicationRequest -> {
            // 创建 MedicationRequestUseExe 对象，并复制 MedicationRequest 的属性
            MedicationRequestUseExe useExe = new MedicationRequestUseExe();
            // 账号id赋值
            AdviceExecuteDetailParam adviceExecuteDetailParam = medicineList.stream()
                .filter(param -> medicationRequest.getId().equals(param.getRequestId())).findFirst().orElse(null);
            if (adviceExecuteDetailParam != null) {
                useExe.setAccountId(adviceExecuteDetailParam.getAccountId());
            }
            BeanUtils.copyProperties(medicationRequest, useExe);
            // 匹配 executeTimes：根据 medicationRequest 的 id 在 medicineList 中查找匹配的 executeTimes
            List<String> matchedExecuteTimes =
                medicineList.stream().filter(medicine -> medicine.getRequestId().equals(medicationRequest.getId()))
                    .findFirst().map(AdviceExecuteDetailParam::getExecuteTimes).orElse(null);
            useExe.setExecuteTimes(matchedExecuteTimes);
            // 计算 executeTimesNum：executeTimes 的集合大小，转换为 BigDecimal
            if (matchedExecuteTimes != null) {
                useExe.setExecuteTimesNum(BigDecimal.valueOf(matchedExecuteTimes.size()));
            } else {
                useExe.setExecuteTimesNum(BigDecimal.ZERO);
            }
            // 匹配 minUnitQuantity：根据 medicationId 在 medicationDefinitionList 中查找匹配的药品定义
            MedicationDefinition matchedDefinition = medicationDefinitionList.stream()
                .filter(definition -> definition.getId().equals(medicationRequest.getMedicationId())).findFirst()
                .orElse(null);
            if (matchedDefinition != null) {
                // 判断 minUnitCode 是否等于 unitCode
                if (matchedDefinition.getMinUnitCode().equals(medicationRequest.getUnitCode())) {
                    useExe.setMinUnitQuantity(medicationRequest.getQuantity());
                } else {
                    // 若不相等，则计算 quantity * partPercent
                    BigDecimal partPercent = matchedDefinition.getPartPercent();
                    if (partPercent == null) {
                        partPercent = BigDecimal.ONE; // 避免空指针，默认设为1
                    }
                    useExe.setMinUnitQuantity(medicationRequest.getQuantity().multiply(partPercent));
                }
            } else {
                // 若未找到匹配的药品定义，设为 null 或默认值
                useExe.setMinUnitQuantity(null);
            }
            return useExe;
        }).toList();
        return medUseExeList;
    }

    /**
     * 组装诊疗请求用于执行的数据结构
     *
     * @param activityList 诊疗执行参数
     * @return 用于执行的诊疗请求
     */
    private List<ServiceRequestUseExe> assemblyActivity(List<AdviceExecuteDetailParam> activityList) {
        // 诊疗请求id集合
        List<Long> activityRequestIdList =
            activityList.stream().map(AdviceExecuteDetailParam::getRequestId).collect(Collectors.toList());
        // 诊疗请求集合
        List<ServiceRequest> activityRequestList = serviceRequestService
            .list(new LambdaQueryWrapper<ServiceRequest>().in(ServiceRequest::getId, activityRequestIdList));
        // 组装ServiceRequestUseExe数据结构
        List<ServiceRequestUseExe> actUseExeList = activityRequestList.stream().map(serviceRequest -> {
            // 创建 ServiceRequestUseExe 对象，并复制 ServiceRequest 的属性
            ServiceRequestUseExe useExe = new ServiceRequestUseExe();
            // 账号id赋值
            AdviceExecuteDetailParam adviceExecuteDetailParam = activityList.stream()
                .filter(param -> serviceRequest.getId().equals(param.getRequestId())).findFirst().orElse(null);
            if (adviceExecuteDetailParam != null) {
                useExe.setAccountId(adviceExecuteDetailParam.getAccountId());
            }
            BeanUtils.copyProperties(serviceRequest, useExe);
            // 匹配 executeTimes：根据 serviceRequest 的 id 在 activityList 中查找匹配的 executeTimes
            List<String> matchedExecuteTimes =
                activityList.stream().filter(activity -> activity.getRequestId().equals(serviceRequest.getId()))
                    .findFirst().map(AdviceExecuteDetailParam::getExecuteTimes).orElse(null);
            useExe.setExecuteTimes(matchedExecuteTimes);
            // 计算 executeTimesNum：executeTimes 的集合大小，转换为 BigDecimal
            if (matchedExecuteTimes != null) {
                useExe.setExecuteTimesNum(BigDecimal.valueOf(matchedExecuteTimes.size()));
            } else {
                useExe.setExecuteTimesNum(BigDecimal.ZERO);
            }
            return useExe;
        }).toList();
        return actUseExeList;
    }

    /**
     * 生成退药单
     *
     * @param longMedDispensedList 已发放的药品列表
     * @param procedureIdMap 执行idMap
     */
    private void creatRefundMedicationList(List<MedicationDispense> longMedDispensedList,
        Map<Long, Long> procedureIdMap) {
        List<MedicationDispense> medicationRefundList = new ArrayList<>();
        // 根据发药单对应生成退药单
        for (MedicationDispense medicationDispense : longMedDispensedList) {
            // 药品发放编码
            medicationDispense.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.MEDICATION_DIS_NO.getPrefix(), 4));
            // 退药状态
            medicationDispense.setStatusEnum(DispenseStatus.PREPARATION.getValue());
            // 状态变更时间
            medicationDispense.setStatusChangedTime(DateUtils.getNowDate());
            // 取消执行id
            Long newProcedureId = procedureIdMap.get(medicationDispense.getProcedureId());
            if (newProcedureId != null) {
                medicationDispense.setProcedureId(newProcedureId);
            }
            medicationDispense.setId(null);
            medicationRefundList.add(medicationDispense);
        }
        medicationDispenseService.saveBatch(medicationRefundList);
    }
}
