package com.ywt.outpatient.controllers.taihe;

import com.ywt.biz.common.constant.YwtCommonRespCode;
import com.ywt.biz.common.enums.OrderTypeEnum;
import com.ywt.biz.common.enums.PaymentStatusEnum;
import com.ywt.biz.common.enums.SexEnum;
import com.ywt.biz.common.exception.YwtCommonException;
import com.ywt.biz.common.util.Checker;
import com.ywt.biz.common.util.DateUtil;
import com.ywt.biz.common.util.StringHelper;
import com.ywt.biz.common.util.serializers.JsonSerializer;
import com.ywt.gapi.Result;
import com.ywt.gapi.ResultCode;
import com.ywt.gapi.drug.*;
import com.ywt.gapi.order.*;
import com.ywt.gapi.order.OutpatientOrder;
import com.ywt.gapi.taihe.outpaitent.*;
import com.ywt.gapi.taihe.outpaitent.BaseResult;
import com.ywt.gapi.third.taihe.*;
import com.ywt.gapi.third.taihe.GetOutpatientListRequest;
import com.ywt.gapi.third.taihe.GetOutpatientListResponse;
import com.ywt.gapi.third.wxInsPay.*;
import com.ywt.gapi.wx_ins_pay.*;
import com.ywt.outpatient.api.outpatient.GetInPayListReq;
import com.ywt.outpatient.api.outpatient.InPayInfo;
import com.ywt.outpatient.api.req.*;
import com.ywt.outpatient.api.resp.*;
import com.ywt.outpatient.core.BaseResponse;
import com.ywt.outpatient.core.utils.BizUtil;
import com.ywt.outpatient.core.utils.CheckUtil;
import com.ywt.outpatient.core.utils.IdCardUtil;
import com.ywt.outpatient.core.Constant;
import com.ywt.outpatient.domain.models.enums.MedInsTypeEnum;
import com.ywt.outpatient.domain.models.enums.OutpatientOrderPayInsTypeEnum;
import com.ywt.outpatient.domain.models.enums.OutpatientStatusEnum;
import com.ywt.outpatient.service.rpc.common.ServiceResult;
import com.ywt.outpatient.service.web.MedicalCardService;
import com.ywt.outpatient.service.web.OrderCenterService;
import com.ywt.outpatient.service.web.PaymentService;
import com.ywt.outpatient.service.web.UserSrv;
import com.ywt.outpatient.core.WebAppContext;
import com.ywt.outpatient.service.web.taihe.TaiheUserService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 东华门诊缴费
 *
 * @author Walker
 * Created on 2019/12/26
 */
@SuppressWarnings("javadoc")
@RestController
@RequestMapping(value = {"/taihe/dhop"}, method = RequestMethod.POST)
public class DHOutpatientController {
    private final Logger logger = LoggerFactory.getLogger(getClass().getName());

    @DubboReference
    private TaiheService taiheServiceBlockingStub;

    @DubboReference
    private OrderService orderServiceBlockingStub;

    @Autowired
    private TaiheUserService userService;

    @Autowired
    private PaymentService paymentService;

    @Autowired
    private OrderCenterService orderCenterService;

    @Autowired
    private TaiheUserService taiheUserService;

    @DubboReference
    private TaiheOutpatientService taiheOutpatientServiceBlockingStub;

    @DubboReference
    private DrugService drugServiceBlockingStub;

    @DubboReference
    private WxInsPayService wxInsPayServiceBlockingStub;

    @Autowired
    private UserSrv userSrv;

    @Autowired
    private MedicalCardService medicalCardService;

    private static final String magicMsg = "处方费用不一致，请与开单医生确认";
    public static final int TYPE_UNPAID = 1;
    private static final int TYPE_PAID = 2;
    // No pagination for now
    private static final int PAGE_INDEX = 1;
    private static final int PAGE_SIZE = 99999;

    // 白云外转处方状态
    private static final int HIS_PRES_STATUS_UNPAID = 2;
    private static final int HIS_PRES_STATUS_PAID = 1;
    // 门诊缴费列表数据类型
    private static final String DATA_TYPE_KEY = "dataType";
    private static final int DATA_TYPE_OUTPATIENT = 0; // 门诊缴费
    private static final int DATA_TYPE_HIS_PRES = 1; // 白云外转处方

    /**
     * 获取门诊缴费列表
     * Copy from {@link OutpatientController#getPaymentList(int, int, int, String)}
     * Modify by Walker
     *
     * @return {@link BaseResponse}
     */
    @RequestMapping({"/getPaymentList"})
    public @ResponseBody
    BaseResponse getPaymentList(@RequestBody GetPaymentListReq req) {
        BaseResponse response = new BaseResponse();
        int type = Checker.getIntegerValue(req.getType());
        String hisPatientId = Checker.getStringValue(req.getHisPatientId());
        if (type != TYPE_UNPAID && type != TYPE_PAID) {
            return response.failed(BaseResponse.PARAMETER_ERROR, "查询订单类型不正确");
        }
        if (StringHelper.isNullOrWhiteSpace(hisPatientId)) {
            return response.failed(BaseResponse.PARAMETER_ERROR, "hisPatientId 不正确");
        }
        try {
            int userId = userService.getCurrentUserIdWrapped();
            int hospitalId = BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped());
            medicalCardService.verifyMedicalCard(userId, hospitalId, hisPatientId, OrderTypeEnum.OUTPATIENT.getValue());
            // 根据 his_patient_id 获取诊疗卡信息
            String pName = "";
            int pAge = 0;
            String pGender = "";
            GetMedicalCardInfoByPatientIdRequest gResp = GetMedicalCardInfoByPatientIdRequest.newBuilder()
                    .setHisPatientId(hisPatientId)
                    .setHospitalId(hospitalId)
                    .setUserId(userSrv.getCurrentUserIdWrapped())
                    .build();
            GetMedicalCardInfoByPatientIdResponse resp = taiheOutpatientServiceBlockingStub.getMedicalCardInfoByPatientId(gResp);
            if (resp.getCode() == ResultCode.SUCCEED_VALUE) {
                pName = resp.getPatientName();
                pAge = getPatientAgeSafely(resp.getIdNo(), resp.getBirthday());
                pGender = IdCardUtil.getSex(resp.getIdNo());
            }
            Map<String, Object> data = new HashMap<>();
            Map<String, Object> map;
            switch (type) {
                case TYPE_UNPAID:
                    // 待缴费列表从 HIS 获取数据
                    com.ywt.gapi.third.taihe.GetOutpatientListRequest request = com.ywt.gapi.third.taihe.GetOutpatientListRequest.newBuilder()
                            .setPatientId(hisPatientId)
                            .setType(type)
                            .setHospitalId(hospitalId)
                            .build();
                    com.ywt.gapi.third.taihe.GetOutpatientListResponse result = taiheServiceBlockingStub.getOutpatientList(request);
                    List<Map<String, Object>> lst = new LinkedList<>();
                    if (result.getCode() == ResultCode.SUCCEED_VALUE) {
                        List<PaymentItem> paymentItems = result.getPaymentList();
                        if (!Checker.isNone(paymentItems)) {
                            // 过滤处方门诊缴费单
                            GetOneKeyRegListRequest getOneKeyRegListRequest = GetOneKeyRegListRequest.newBuilder()
                                    .setUserId(userId)
                                    .build();
                            GetOneKeyRegListResponse getOneKeyRegListResponse = drugServiceBlockingStub.getOneKeyRegList(getOneKeyRegListRequest);
                            List<String> clinicCodes = getOneKeyRegListResponse.getCode() == ResultCode.SUCCEED_VALUE ? getOneKeyRegListResponse.getClinicCodeList() : new LinkedList<>();
                            List<PaymentItem> payItems = paymentItems;
                            if (!clinicCodes.isEmpty()) {
                                payItems = paymentItems.stream()
                                        .filter(paymentItem -> !clinicCodes.contains(paymentItem.getClinicCode()))
                                        .collect(Collectors.toList());
                            }
                            for (PaymentItem payItem : payItems) {
                                map = new HashMap<>();
                                map.put("presDate", payItem.getPrescriptionDate());
                                map.put("hospitalId", hospitalId);
                                map.put("hospitalName", BizUtil.getHospitalNameByIdSimply(hospitalId));
                                map.put("deptName", payItem.getDeptName());
                                map.put("doctorName", payItem.getDoctorName());
                                map.put("totalFee", payItem.getTotalFee());
                                map.put("medInsFee", payItem.getMedInsFee());
                                map.put("selfFee", payItem.getSelfFee());
                                map.put("paymentStatus", PaymentStatusEnum.Pending.getValue());
                                map.put("outpatientStatus", OutpatientStatusEnum.PENDING.getValue());
                                map.put("hisPatientId", hisPatientId);
                                map.put("payName", payItem.getPayName());
                                map.put("hisClinicCode", payItem.getClinicCode());
                                map.put("hisOrderNo", payItem.getHisOrdNum());
                                map.put("patientName", pName);
                                map.put("orderInsType", payItem.getOrderInsType());
                                map.put("patientAge", pAge);
                                map.put("patientSex", pGender);
                                map.put("doctorCode", payItem.getDoctorCode());
                                List<PayDetailItem> detailItems = payItem.getItemListList();
                                List<Map<String, Object>> items = new LinkedList<>();
                                for (PayDetailItem detailItem : detailItems) {
                                    Map<String, Object> d = new HashMap<>();
                                    d.put("name", detailItem.getItemName());
                                    d.put("price", detailItem.getItemPrice());
                                    d.put("code", detailItem.getItemCode());
                                    d.put("number", detailItem.getItemNumber());
                                    d.put("spec", detailItem.getItemSpec());
                                    d.put("unit", detailItem.getItemUnit());
                                    d.put("totalFee", detailItem.getItemTotalFee());
                                    items.add(d);
                                }
                                map.put("detailItems", items);
                                map.put(DATA_TYPE_KEY, DATA_TYPE_OUTPATIENT);
                                lst.add(map);
                                // 保存门诊缴费明细到数据库
                                SaveOutpatientOrderDetailsRequest sReq = SaveOutpatientOrderDetailsRequest.newBuilder()
                                        .setHisOrderNo(payItem.getHisOrdNum())
                                        .addAllDetail(payItem.getItemListList().stream()
                                                .map(i -> OutpatientOrderDetail.newBuilder()
                                                        .setItemName(i.getItemName())
                                                        .setItemPrice(i.getItemPrice())
                                                        .setItemCode(i.getItemCode())
                                                        .setItemNumber(i.getItemNumber())
                                                        .setItemSpec(i.getItemSpec())
                                                        .setItemUnit(i.getItemUnit())
                                                        .setItemTotalFee(i.getItemTotalFee())
                                                        .build()).collect(Collectors.toList()))
                                        .build();

                                BaseResult baseResult = taiheOutpatientServiceBlockingStub.saveOutpatientOrderDetails(sReq);
                                if (baseResult.getCode() != ResultCode.SUCCEED_VALUE) {
                                    logger.error("保存门诊缴费明细失败，hisOrderNo : {}", payItem.getHisOrdNum());
                                }
                            }
                        }
                    }
                    // 白云分院，还需要查询外转处方数据
                    if (hospitalId == Constant.NFYYBYFY_HOSPITAL_ID) {
                        GetSelfPayPresListRequest gspplReq = GetSelfPayPresListRequest.newBuilder()
                                .setPageIndex(PAGE_INDEX)
                                .setPageSize(PAGE_SIZE)
                                .setHisPatientId(hisPatientId)
                                .setHospitalId(hospitalId)
                                .setStatus(HIS_PRES_STATUS_UNPAID)
                                .build();
                        GetSelfPayPresListResponse gspplResponse = drugServiceBlockingStub.getSelfPayPresList(gspplReq);
                        if (gspplResponse.getCode() == ResultCode.SUCCEED_VALUE) {
                            for (PrescriptionInfoLess p : gspplResponse.getPrescriptionInfoList()) {
                                Map<String, Object> presDataMap = new HashMap<>();
                                presDataMap.put("type", "自费（流转处方）");
                                presDataMap.put("preBizNo", Checker.getStringValue(p.getBizNo()));
                                presDataMap.put("timestamp", DateUtil.convertTimestampToDateString(p.getCreateOn()));
                                presDataMap.put("hospital", BizUtil.getHospitalNameByIdSimply(hospitalId));
                                presDataMap.put("doctor", Checker.getStringValue(p.getDoctorName()));
                                presDataMap.put("department", Checker.getStringValue(p.getDeptName()));
                                presDataMap.put("statusStr", p.getStatusStr());
                                presDataMap.put("amountStr", String.format("%.2f 元", p.getTotalPrice() / 100d));
                                presDataMap.put("preBizNo3rd", p.getBizNo3Rd());
                                presDataMap.put("hisPatientId", p.getHisPatientId());
                                presDataMap.put("patientName", p.getPatientName());
                                presDataMap.put(DATA_TYPE_KEY, DATA_TYPE_HIS_PRES);
                                lst.add(presDataMap);
                            }
                        }
                    }
                    data.put("list", lst);
                    break;
                case TYPE_PAID:
                    switch (hospitalId) {
                        default:
                        case Constant.TAIHE_HOSPITAL_ID:
                            // 太和已缴费列表读取数据库
                            GetOutpatientOrderListRequest req2 = GetOutpatientOrderListRequest.newBuilder()
                                    .setType(type)
                                    .setHisPatientId(hisPatientId)
                                    .setPageIndex(PAGE_INDEX)
                                    .setPageSize(PAGE_SIZE)
                                    .setUserId(taiheUserService.getCurrentUserIdWrapped())
                                    .setHospitalId(hospitalId)
                                    .build();
                            GetOutpatientOrderListResponse res = orderServiceBlockingStub.getOutpatientOrderList(req2);
                            Result result2 = res.getResult();
                            if (result2.getCode() == ResultCode.SUCCEED) {
                                List<Map<String, Object>> list2 = new LinkedList<>();
                                for (OutpatientOrder order : res.getOrderList()) {
                                    map = new HashMap<>();
                                    map.put("orderId", Checker.getIntegerValue(order.getOrderId()));
                                    map.put("orderNo", Checker.getStringValue(order.getOrderNo()));
                                    map.put("createTime", DateUtil.convertTimestampToDateString(Checker.getLongValue(order.getCreateTime()), "yyyy年MM月dd日 HH:mm"));
                                    map.put("hospitalId", Checker.getIntegerValue(order.getHospitalId()));
                                    map.put("hospitalName", Checker.getStringValue(order.getHospitalName()));
                                    map.put("deptCode", Checker.getStringValue(order.getDeptCode()));
                                    map.put("deptName", Checker.getStringValue(order.getDeptName()));
                                    map.put("doctorCode", Checker.getStringValue(order.getDoctorCode()));
                                    map.put("doctorName", Checker.getStringValue(order.getDoctorName()));
                                    map.put("totalFee", Checker.getIntegerValue(order.getTotal()));
                                    map.put("medInsFee", Checker.getIntegerValue(order.getReimbursement()));
                                    map.put("selfFee", Checker.getIntegerValue(order.getIndividual()));
                                    map.put("paymentStatus", Checker.getIntegerValue(order.getPaymentStatus()));
                                    map.put("outpatientStatus", Checker.getIntegerValue(order.getOutpatientStatus()));
                                    map.put("userId", Checker.getIntegerValue(order.getUserId()));
                                    map.put("hisPatientId", Checker.getStringValue(order.getHisPatientId()));
                                    map.put("payName", Checker.getStringValue(order.getPayName()));
                                    map.put("orderInsType", order.getOrderInsType());
                                    map.put("hisOrderNo", Checker.getStringValue(order.getHisOrderNo()));
                                    map.put("presDate", DateUtil.convertTimestampToDateString(order.getPrescriptionDate()));
                                    map.put("invoiceNo", Checker.getStringValue(order.getInvoiceNo()));
                                    map.put("selfpay", order.getSelfPay());
                                    map.put("insurance", order.getInsurance());
                                    map.put("insDetail", order.getInsDetail());
                                    list2.add(map);
                                }
                                data.put("list", list2);
                            }
                            break;
                        case Constant.NFYYBYFY_HOSPITAL_ID:
                            // v1.3.7 白云改为从 HIS 接口读取数据
                            String startDate = req.getStartDate();
                            String endDate = req.getEndDate();
                            Date start = DateUtil.parseDate(startDate, DateUtil.DADE_FROMAT_YMD);
                            Date end = DateUtil.parseDate(endDate, DateUtil.DADE_FROMAT_YMD);
                            CheckUtil.ensureNotNull(start, "参数开始日期有误");
                            CheckUtil.ensureNotNull(end, "参数结束日期有误");
                            List<Map<String, Object>> list2 = new LinkedList<>();
                            com.ywt.gapi.third.taihe.GetOutpatientListRequest req3 = com.ywt.gapi.third.taihe.GetOutpatientListRequest.newBuilder()
                                    .setPatientId(hisPatientId)
                                    .setType(type)
                                    .setHospitalId(hospitalId)
                                    .setStartDate(startDate)
                                    .setEndDate(endDate)
                                    .build();
                            com.ywt.gapi.third.taihe.GetOutpatientListResponse res3 = taiheServiceBlockingStub.getOutpatientList(req3);
                            if (res3.getCode() == ResultCode.SUCCEED_VALUE) {
                                for (PaymentItem payItem : res3.getPaymentList()) {
                                    map = new HashMap<>();
                                    map.put("userId", userId);
                                    map.put("invoiceNo", Checker.getStringValue(payItem.getInvoiceNo()));
                                    map.put("presDate", payItem.getPrescriptionDate());
                                    map.put("hospitalId", hospitalId);
                                    map.put("hospitalName", BizUtil.getHospitalNameByIdSimply(hospitalId));
                                    map.put("deptName", payItem.getDeptName());
                                    map.put("doctorName", payItem.getDoctorName());
                                    map.put("totalFee", payItem.getTotalFee());
                                    map.put("medInsFee", payItem.getMedInsFee());
                                    map.put("selfFee", payItem.getSelfFee());
                                    map.put("paymentStatus", PaymentStatusEnum.Success.getValue());
                                    map.put("outpatientStatus", OutpatientStatusEnum.PAID.getValue());
                                    map.put("hisPatientId", hisPatientId);
                                    map.put("payName", payItem.getPayName());
                                    map.put("hisClinicCode", payItem.getClinicCode());
                                    map.put("hisOrderNo", payItem.getHisOrdNum());
                                    map.put("patientName", pName);
                                    map.put("orderInsType", payItem.getOrderInsType());// TODO: HIS 需要提供字段
                                    map.put("patientAge", pAge);
                                    map.put("patientSex", pGender);
                                    map.put("doctorCode", payItem.getDoctorCode());
                                    list2.add(map);
                                }
                            }
                            // 白云分院，还需要查询外转处方数据
                            GetSelfPayPresListRequest gspplReq = GetSelfPayPresListRequest.newBuilder()
                                    .setPageIndex(PAGE_INDEX)
                                    .setPageSize(PAGE_SIZE)
                                    .setHisPatientId(hisPatientId)
                                    .setHospitalId(hospitalId)
                                    .setStatus(HIS_PRES_STATUS_PAID)
                                    .build();
                            GetSelfPayPresListResponse gspplResponse = drugServiceBlockingStub.getSelfPayPresList(gspplReq);
                            if (gspplResponse.getCode() == ResultCode.SUCCEED_VALUE) {
                                for (PrescriptionInfoLess p : gspplResponse.getPrescriptionInfoList()) {
                                    Date createOnDate = DateUtil.parseDate(DateUtil.convertTimestampToDateString(p.getCreateOn(), DateUtil.DADE_FROMAT_YMD), DateUtil.DADE_FROMAT_YMD);
                                    // FIXME: 版本急着上线，先在内存过滤，接下来需要修改 drug_rpc 按日期查询
                                    if (createOnDate == null || createOnDate.before(start) || createOnDate.after(end)) continue;
                                    Map<String, Object> presDataMap = new HashMap<>();
                                    presDataMap.put("type", "自费（流转处方）");
                                    presDataMap.put("preBizNo", Checker.getStringValue(p.getBizNo()));
                                    presDataMap.put("timestamp", DateUtil.convertTimestampToDateString(p.getCreateOn()));
                                    presDataMap.put("hospital", BizUtil.getHospitalNameByIdSimply(hospitalId));
                                    presDataMap.put("doctor", Checker.getStringValue(p.getDoctorName()));
                                    presDataMap.put("department", Checker.getStringValue(p.getDeptName()));
                                    presDataMap.put("statusStr", p.getStatusStr());
                                    presDataMap.put("amountStr", String.format("%.2f 元", p.getTotalPrice() / 100d));
                                    presDataMap.put("preBizNo3rd", p.getBizNo3Rd());
                                    presDataMap.put("hisPatientId", p.getHisPatientId());
                                    presDataMap.put("patientName", p.getPatientName());
                                    presDataMap.put(DATA_TYPE_KEY, DATA_TYPE_HIS_PRES);
                                    list2.add(presDataMap);
                                }
                            }
                            data.put("list", list2);
                            break;
                    }
                    break;
            }
            return response.succeed(data);
        } catch (Exception e) {
            logger.error("DHOutpatientController#getPaymentList(type={} , hisPatientId={} ):\n {}", type, hisPatientId, e.getMessage(), e);
            return response.error(e.getMessage());
        }
    }

    /**
     * 创建门站缴费订单
     *
     * @param reqData
     * @return
     */
    @RequestMapping(value = {"/createOPOrder"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<CreateOPOrderResp> createOPOrder(@RequestBody CreateOPOrderReq reqData) {
        BaseResponse2<CreateOPOrderResp> baseResponse = new BaseResponse2<>();
        try {
            String deptName = Checker.getStringValue(reqData.getDeptName());
            String doctorName = Checker.getStringValue(reqData.getDoctorName());
            int totalFee = Checker.getIntegerValue(reqData.getTotalFee());
            int medInsFee = Checker.getIntegerValue(reqData.getMedInsFee());
            int selfFee = Checker.getIntegerValue(reqData.getSelfFee());
            String patientId = Checker.getStringValue(reqData.getPatientId());
            String hisClinicCode = Checker.getStringValue(reqData.getHisClinicCode());
            String hisOrdNum = Checker.getStringValue(reqData.getHisOrdNum());
            String payName = Checker.getStringValue(reqData.getPayName());
            String prescriptionDate = Checker.getStringValue(reqData.getPrescriptionDate());
            String patName = Checker.getStringValue(reqData.getPatName());
            int terminalId = BizUtil.getCurrentTerminalWrapped();
            int age = Checker.getIntegerValue(reqData.getAge());
            if (age < 0) {
                logger.warn("DHOutpatientController#createOPOrder(patientId={} ): 传入年龄为负数！", reqData.getPatientId());
            }
            int sex = Checker.getIntegerValue(reqData.getSex());
            String orderInsType = Checker.getStringValue(reqData.getOrderInsType());
            String invoiceNo = Checker.getStringValue(reqData.getInvoiceNo());
            String doctorCode = Checker.getStringValue(reqData.getDoctorCode());
            int payInsType = Checker.getIntegerValue(reqData.getPayInsType());
            String insuAdmDr = Checker.getStringValue(reqData.getInsuAdmDr());
            String insUploadFeeResp = Checker.getStringValue(reqData.getInsUploadFeeResp());

            CheckUtil.ensureNotEmpty(invoiceNo, "发票号不能为空");
            CheckUtil.ensureNotEmpty(doctorCode, "医生编码不能为空");
            CheckUtil.ensureNotEmpty(orderInsType, "订单费用类型不能为空");
            Date pDate = DateUtil.parseDate(prescriptionDate, "yyyy-MM-dd HH:mm:ss");
            if (pDate == null) {
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, "处方日期有误");
            }
            if (StringHelper.isNullOrWhiteSpace(patientId)) {
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, "患者ID有误");
            }
            if (StringHelper.isNullOrWhiteSpace(hisOrdNum)) {
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, "医院订单号有误");
            }
            if (StringHelper.isNullOrWhiteSpace(payName)) {
                // 目前 HIS 返回空
                payName = "";
//                throw new AppMessageException("缴费项名称有误");
            }
            if (totalFee <= 0) {
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, "缴费总额有误");
            }
            if (medInsFee + selfFee != totalFee) {
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, "医保部分金额与自费部分金额总和不等于缴费总额");
            }
            if (OutpatientOrderPayInsTypeEnum.valueOf(payInsType) == OutpatientOrderPayInsTypeEnum.MED_INSURANCE) {
                // 医保支付校验必传参数
                if (StringHelper.isNullOrEmpty(insUploadFeeResp)) {
                    throw new YwtCommonException(YwtCommonRespCode.P_ERR, "医保支付需要传入医保上传费用信息等参数");
                }
            }
            CreateOutpatientOrderRequest req;
            Result result;
            int hospitalId = BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped());
            switch (OutpatientOrderPayInsTypeEnum.valueOf(payInsType)) {
                default:
                case SELF:
                    req = CreateOutpatientOrderRequest.newBuilder()
                            .setDeptCode("")
                            .setDeptName(deptName)
                            .setDoctorCode(doctorCode)
                            .setDoctorName(doctorName)
                            .setTotal(totalFee)
                            .setReimbursement(medInsFee)
                            .setIndividual(selfFee)
                            .setHisPatientId(patientId)
                            .setHisClinicCode(hisClinicCode)
                            .setHisOrderNo(hisOrdNum)
                            .setPayName(payName)
                            .setPrescriptionDate(pDate.getTime())
                            .setPatientName(patName)
                            .setPatientAge(age)
                            .setPatientSex(sex)
                            .setOrderInsType(orderInsType)
                            .setInvoiceNo(invoiceNo)
                            .setPayInsType(payInsType)
                            .setHospitalId(hospitalId)
                            .setTerminal(terminalId)
                            .build();
                    result = orderServiceBlockingStub.createOutpatientOrder(req);

                    if (result.getCode() == ResultCode.SUCCEED) {
                        CreateOPOrderResp resp = new CreateOPOrderResp();
                        resp.setOrderNo(result.getValue());
                        return baseResponse.succeed(resp);
                    }
                    return baseResponse.failed(BaseResponse.APP_ERROR, result.getInfo());
                case MED_INSURANCE:
                    // 获取身份证
                    GetMedicalCardInfoByPatientIdRequest gResp = GetMedicalCardInfoByPatientIdRequest.newBuilder()
                            .setHisPatientId(patientId)
                            .setHospitalId(hospitalId)
                            .setUserId(userSrv.getCurrentUserIdWrapped())
                            .build();
                    GetMedicalCardInfoByPatientIdResponse resp = taiheOutpatientServiceBlockingStub.getMedicalCardInfoByPatientId(gResp);
                    if (resp.getCode() != ResultCode.SUCCEED_VALUE) {
                        return baseResponse.failedWithAppError(String.format("获取不到诊疗卡信息: %s", resp.getMsg()));
                    }
                    String userCardNo = resp.getIdNo();
                    if (StringHelper.isNullOrEmpty(userCardNo))
                        return baseResponse.failedWithAppError("用户诊疗卡身份证信息为空");
                    // 读取配置
                    GetOutPatientConfigResponse getOutPatientConfigResponse = taiheOutpatientServiceBlockingStub.getOutpatientConfigs(GetOutPatientConfigRequest.newBuilder()
                            .setHospitalId(hospitalId)
                            .build());
                    // 医保支付，先生成系统订单，然后继续使用腾讯医保接口下单
                    req = CreateOutpatientOrderRequest.newBuilder()
                            .setDeptCode("")
                            .setDeptName(deptName)
                            .setDoctorCode(doctorCode)
                            .setDoctorName(doctorName)
                            .setTotal(totalFee)
                            .setReimbursement(medInsFee)
                            .setIndividual(selfFee)
                            .setHisPatientId(patientId)
                            .setHisClinicCode(hisClinicCode)
                            .setHisOrderNo(hisOrdNum)
                            .setPayName(payName)
                            .setPrescriptionDate(pDate.getTime())
                            .setPatientName(patName)
                            .setPatientAge(age)
                            .setPatientSex(sex)
                            .setOrderInsType(orderInsType)
                            .setInvoiceNo(invoiceNo)
                            .setPayInsType(payInsType)
                            .setHospitalId(hospitalId)
                            .setInsuAdmDr(insuAdmDr)
                            .setInsUploadFeeResp(insUploadFeeResp)
                            .setTerminal(terminalId)
                            .build();
                    result = orderServiceBlockingStub.createOutpatientOrder(req);
                    if (result.getCode() != ResultCode.SUCCEED) {
                        return baseResponse.failed(BaseResponse.APP_ERROR, result.getInfo());
                    }
                    String orderNo = result.getValue();
                    String openId = userService.getCurrentOpenId();
                    int payType = selfFee > 0 ? 3 : 2; // 1：现金 2：医保 3：现金+医保
                    String notifyUrl = getOutPatientConfigResponse.getNotifyUrl();
                    String returnUrl = getOutPatientConfigResponse.getReturnUrl() + orderNo;
                    String openIdConfig = getOutPatientConfigResponse.getOpenId();
                    if (!StringHelper.isNullOrEmpty(openIdConfig)) openId = openIdConfig;
                    int userCardType = 1;// 身份证
                    int terminal = BizUtil.getCurrentTerminalWrapped();
                    Map<String, Object> am = new HashMap<>();
                    am.put("terminal", terminal);
                    am.put("hospitalId", hospitalId);
                    // 使用腾讯医保接口下单
                    CreatePayInsOrderRequest createPayInsOrderRequest = CreatePayInsOrderRequest.newBuilder()
                            .setOpenId(openId)
                            .setHospOutTradeNo(orderNo)
                            .setTotalFee(totalFee)
                            .setCashFee(selfFee)
                            .setNotifyUrl(notifyUrl)
                            .setReturnUrl(returnUrl)
                            .setPayType(payType)
                            .setInsuranceFee(medInsFee)
                            .setUserCardType(userCardType)
                            .setUserCardNo(userCardNo)
                            .setUserName(patName)
                            .setSerialNo(hisOrdNum)
                            .setTerminal(terminal)
                            .setAttach(JsonSerializer.toJson(am))
                            .setInsUploadFeeResp(insUploadFeeResp)
                            .setConsumeType(Checker.getIntegerValue(reqData.getConsumeType()))
                            .setMedInsType(getMedInsTypeByOrderInsType(orderInsType))
                            .build();
                    CreatePayInsOrderResponse createPayInsOrderResponse = wxInsPayServiceBlockingStub.createPayInsOrder(createPayInsOrderRequest);
                    if (createPayInsOrderResponse.getCode() == ResultCode.SUCCEED_VALUE) {
                        CreateOPOrderResp resp2 = new CreateOPOrderResp();
                        resp2.setOrderNo(orderNo);
                        resp2.setPayUrl(createPayInsOrderResponse.getPayUrl());
                        resp2.setPayAppid(createPayInsOrderResponse.getPayAppid());
                        return baseResponse.succeed(resp2);
                    } else {
                        MarkOpOrderDeletedResponse moodResp = wxInsPayServiceBlockingStub.markOpOrderDeleted(MarkOpOrderDeletedRequest.newBuilder()
                                .setOrderNo(orderNo)
                                .build());
                        if (moodResp.getCode() != ResultCode.SUCCEED_VALUE) {
                            logger.error("DHOutpatientController#createOPOrder():markOpOrderDeleted\n{}", moodResp.getMsg());
                        }
                        return baseResponse.failedWithAppError(createPayInsOrderResponse.getMsg());
                    }
            }
        } catch (YwtCommonException ame) {
            return baseResponse.failed(BaseResponse.PARAMETER_ERROR, ame.getMessage());
        } catch (Exception e) {
            logger.error("DHOutpatientController#createOPOrder(reqData={} ):\n {}", reqData, e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    /**
     * 根据 HIS 返回的订单医保类型，获取系统定义的医保类型值
     *
     * @param orderInsType HIS 返回的订单医保类型字符串
     * @return 系统医保类型值，{@link com.ywt.outpatient.domain.models.enums.MedInsTypeEnum}
     * Copied from rpc project
     */
    private int getMedInsTypeByOrderInsType(String orderInsType) {
        if (StringHelper.isNullOrEmpty(orderInsType)) return MedInsTypeEnum.UNKNOWN.getValue();
        // // 和 HIS 开发人员确认过，省直医保开头的，都属于省直医保。
        if (orderInsType.startsWith("省直医保")) return MedInsTypeEnum.SNZYB.getValue();
        // // 和 HIS 开发人员确认过，市直医保开头的，都属于市直医保。
        if (orderInsType.startsWith("市直医保")) return MedInsTypeEnum.SZYB.getValue();
        // 和 HIS 开发人员确认过，广州医保开头的，都属于广州医保。包括：广州医保新、广州医保门特新、广州医保门慢新、广州医保生育新等
        if (orderInsType.startsWith("广州医保")) return MedInsTypeEnum.GZYB.getValue();
        return MedInsTypeEnum.UNKNOWN.getValue();
    }

    /**
     * 获取支付信息
     * Copy from {@link PayController#getPaymentInfo(int, int, Integer)}, modify by Walker
     *
     * @param req
     * @return
     */
    @RequestMapping({"/getPaymentInfo"})
    public @ResponseBody
    BaseResponse getPaymentInfo(@RequestBody GetPaymentInfoReq req) {
        BaseResponse response = new BaseResponse();
        try {
            String orderNo = Checker.getStringValue(req.getOrderNo());
            int amount = Checker.getIntegerValue(req.getAmount());
            CheckUtil.ensureNotEmpty(orderNo, "订单号不能为空");
            CheckUtil.ensureLargerThanZero(amount, "金额需大于 0");

            String ip = WebAppContext.current().getIP();
            String openId = userService.getCurrentOpenId();
            int terminal = WebAppContext.current().getTerminal();
            int userId = userService.getCurrentUserIdWrapped();
            int orderType = orderCenterService.getOrderTypeByOrderNo(orderNo);
            int orderId = orderCenterService.getOrderIdByOrderNo(orderNo);
            CheckUtil.ensureLargerThanZero(orderId, String.format("根据 orderNo 找不到订单信息(%s)", orderNo));

            if (orderType == OrderTypeEnum.OFFLINE_CONSULT.getValue() || orderType == OrderTypeEnum.REGISTERED.getValue()) {
                //线下就诊订单、太和预约挂号订单支付走支付中心方式
                ServiceResult result = orderCenterService.wechatPayment("", ip, openId, amount, terminal, orderId, userId);
                if (result.success()) {
                    return response.succeed(result.getMsg(), result.getData());
                }
                return response.failedWithAppError(result.getMsg());
            }
            // 自费的门诊订单处理。医保的订单在中控处理。
            int insuranceType = 0;
            ServiceResult serviceResult = paymentService.getWxPaymentInfo(orderId, userId, openId, ip, terminal, amount, insuranceType);
            if (serviceResult.success()) {
                return response.succeed(serviceResult.getData());
            }
            if (magicMsg.equals(serviceResult.getMsg())) {
                return response.failed(BaseResponse.PAYMENT_AMOUNT_ERROR, serviceResult.getMsg());
            }
            return response.failed(BaseResponse.APP_ERROR, serviceResult.getMsg());
        } catch (YwtCommonException e) {
            return response.failed(BaseResponse.PARAMETER_ERROR, e.getMessage());
        } catch (Exception e) {
            logger.error("DHOutpatientController#getPaymentInfo(req={} ):\n {}", req, e.getMessage(), e);
            return response.failed(BaseResponse.APP_ERROR, e.getMessage());
        }
    }

    @RequestMapping(value = {"/opOrderDetail"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse opOrderDetail(@RequestBody OpOrderDetailReq reqData) {
        BaseResponse baseResponse = new BaseResponse();
        try {
            String hisOrderNo = reqData.getHisOrderNo();
            String invoiceNo = reqData.getInvoiceNo();
            CheckUtil.ensureNotEmpty(hisOrderNo, "hisOrderNo 不能为空");
            CheckUtil.ensureNotEmpty(invoiceNo, "invoiceNo 不能为空");
            GetOutpatientOrderByHisOrderNoRequest request = GetOutpatientOrderByHisOrderNoRequest.newBuilder()
                    .setHisOrderNo(hisOrderNo)
                    .setInvoiceNo(invoiceNo)
                    .setHospitalId(BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped()))
                    .build();
            GetOutpatientOrderByHisOrderNoResponse response = taiheOutpatientServiceBlockingStub.getOutpatientOrderByHisOrderNo(request);
            if (response.getCode() == ResultCode.SUCCEED_VALUE) {
                CreateOPOrderResp resp = new CreateOPOrderResp();
                resp.setOrderNo(response.getOrderNo());
                resp.setPayUrl(response.getPayUrl());
                resp.setPayAppid(response.getPayAppId());
                return baseResponse.succeed(resp);
            }
            return baseResponse.failed(BaseResponse.APP_ERROR, response.getMsg());
        } catch (YwtCommonException ame) {
            return baseResponse.failed(BaseResponse.PARAMETER_ERROR, ame.getMessage());
        } catch (Exception e) {
            logger.error("DHOutpatientController#opOrderDetail(reqData={} ):\n {}", reqData, e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    @RequestMapping(value = {"/prePayConfirm"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<PrePayConfirmResp> prePayConfirm(@RequestBody PrePayConfirmReq reqData) {
        BaseResponse2<PrePayConfirmResp> baseResponse = new BaseResponse2<>();
        try {
            String cardNo = Checker.getStringValue(reqData.getCardNo());
            String patientId = Checker.getStringValue(reqData.getPatientId());
            String startDate = Checker.getStringValue(reqData.getStartDate());
            String endDate = Checker.getStringValue(reqData.getEndDate());
            String clinicCode = Checker.getStringValue(reqData.getClinicCode());
            String hisOrdNum = Checker.getStringValue(reqData.getHisOrdNum());
            int orderSum = Checker.getIntegerValue(reqData.getOrderSum());
            int payInsType = Checker.getIntegerValue(reqData.getPayInsType());
            int hospitalId = BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped());
            CheckUtil.ensureNotEmpty(patientId, "HIS 患者 id 不能为空");
            CheckUtil.ensureNotEmpty(clinicCode, "就诊流水号不能为空");
            CheckUtil.ensureNotEmpty(hisOrdNum, "HIS 订单号不能为空");
            CheckUtil.ensureLargerThanZero(orderSum, "订单金额需大于 0");
            PrePayConfirmRequest req = PrePayConfirmRequest.newBuilder()
                    .setCardNo(cardNo)
                    .setPatientId(patientId)
                    .setStartDate(startDate)
                    .setEndDate(endDate)
                    .setClinicCode(clinicCode)
                    .setHisOrdNum(hisOrdNum)
                    .setOrderSum(orderSum)
                    .setHospitalId(hospitalId)
                    .build();
            PrePayConfirmResponse response = taiheServiceBlockingStub.prePayConfirm(req);
            if (response.getCode() == ResultCode.SUCCEED_VALUE) {
                if (!Checker.isNone(response.getListList())) {
                    // 预结算成功，自动取消之前生成的未支付订单，让前端可以重新创建订单
                    autoCancelPrevOrder(hisOrdNum);
                    PrePayConfirm p = response.getListList().get(0);
                    PrePayConfirmResp resp = new PrePayConfirmResp();
                    String invoiceNo = p.getInvoiceNo();
                    resp.setInvoiceNo(invoiceNo);
                    resp.setInvoiceAmt(p.getInvoiceAmt());
                    resp.setInsuFlag(p.getInsuFlag());
                    resp.setOutInsuInfo(p.getOutInsuInfo());
                    resp.setHisInsuInfo(p.getHisInsuInfo());
                    resp.setInvoiceInsuAmt(p.getInvoiceInsuAmt());
                    resp.setInvoiceSelfAmt(p.getInvoiceSelfAmt());
                    resp.setInsuOutTradeNo(p.getInsuOutTradeNo());
                    // 日志纪录第三方医保分解上传明细
                    logger.info("预结算：hisOrdNum={}, 第三方医保分解上传明细：insuOutTradeNo={}", hisOrdNum, p.getInsuOutTradeNo());
//                    // 根据 his_order_no update invoice_no
//                    UpdateOutpatientOrderRequest updateOutpatientOrderRequest = UpdateOutpatientOrderRequest.newBuilder()
//                            .setHisOrderNo(reqData.getHisOrdNum())
//                            .setInvoiceNo(Checker.getStringValue(p.getInvoiceNo()))
//                            .build();
//                    UpdateOutpatientOrderResponse updateOutpatientOrderResponse = taiheOutpatientServiceBlockingStub.updateOutpatientOrder(updateOutpatientOrderRequest);
//                    if (updateOutpatientOrderResponse.getCode() != ResultCode.SUCCEED_VALUE) {
//                        logger.error("DHOutpatientController#prePayConfirm(reqData={} ): Fail to update invoiceNo\n {}",
//                                reqData, updateOutpatientOrderResponse.getMsg());
//                    }
                    if (OutpatientOrderPayInsTypeEnum.valueOf(payInsType) == OutpatientOrderPayInsTypeEnum.MED_INSURANCE) {
                        String orderInsType = Checker.getStringValue(reqData.getOrderInsType());
                        CheckUtil.ensureNotEmpty(orderInsType, "订单费用类型不能为空");
                        // 获取身份证和姓名
                        GetMedicalCardInfoByPatientIdRequest gResp = GetMedicalCardInfoByPatientIdRequest.newBuilder()
                                .setHisPatientId(patientId)
                                .setHospitalId(hospitalId)
                                .setUserId(userSrv.getCurrentUserIdWrapped())
                                .build();
                        GetMedicalCardInfoByPatientIdResponse gmcResp = taiheOutpatientServiceBlockingStub.getMedicalCardInfoByPatientId(gResp);
                        if (gmcResp.getCode() != ResultCode.SUCCEED_VALUE) {
                            return baseResponse.failedWithAppError(String.format("获取不到诊疗卡信息: %s(%s)", gmcResp.getMsg(), patientId));
                        }
                        String idCard = gmcResp.getIdNo();
                        // 如果选择医保，需要调用 HIS 接口上传费用
                        GetInsuOPDividePreResponse getInsuOPDividePreResponse = taiheServiceBlockingStub.getInsuOPDividePre(GetInsuOPDividePreRequest.newBuilder()
                                .setHospitalId(hospitalId)
                                .setIdNo(idCard)
                                .setInvoiceNo(invoiceNo)
                                .setClinicCode(clinicCode)
                                .setOrderInsType(orderInsType)
                                .build());
                        if (getInsuOPDividePreResponse.getCode() != ResultCode.SUCCEED_VALUE) {
                            return baseResponse.failedWithAppError(String.format("医保上传费用：%s", getInsuOPDividePreResponse.getInfo()));
                        }
                        GetInsuOPDividePreResp r = new GetInsuOPDividePreResp();
                        r.setPsnNo(getInsuOPDividePreResponse.getPsnNo());
                        r.setMedType(getInsuOPDividePreResponse.getMedType());
                        r.setMdtrtId(getInsuOPDividePreResponse.getMdtrtId());
                        r.setInsuType(getInsuOPDividePreResponse.getInsuType());
                        r.setInsuAdmDr(getInsuOPDividePreResponse.getInsuAdmDr());
                        r.setRecerSysCode(getInsuOPDividePreResponse.getRecerSysCode());
                        r.setFixmedinsCode(getInsuOPDividePreResponse.getFixmedinsCode());
                        r.setFixmedinsName(getInsuOPDividePreResponse.getFixmedinsName());
                        r.setInfver(getInsuOPDividePreResponse.getInfver());
                        r.setMdtrtareaAdmvs(getInsuOPDividePreResponse.getMdtrtareaAdmvs());
                        r.setInsuplcAdmdvs(getInsuOPDividePreResponse.getInsuplcAdmdvs());
                        r.setPsnSetlway(getInsuOPDividePreResponse.getPsnSetlway());
                        r.setAcctUsedFlag(getInsuOPDividePreResponse.getAcctUsedFlag());
                        r.setChrgBchNo(getInsuOPDividePreResponse.getChrgBchNo());
                        r.setMedfeeSumamt(getInsuOPDividePreResponse.getMedfeeSumamt());
                        r.setOpterType(getInsuOPDividePreResponse.getOpterType());
                        r.setOpter(getInsuOPDividePreResponse.getOpter());
                        r.setOpterName(getInsuOPDividePreResponse.getOpterName());
                        r.setInsuAdmInfoDr(getInsuOPDividePreResponse.getInsuAdmInfoDr());
                        r.setInsuTypeCode(getInsuOPDividePreResponse.getInsuTypeCode());
                        r.setSessionId(getInsuOPDividePreResponse.getSessionId());
                        r.setCurrentSessionId(getInsuOPDividePreResponse.getCurrentSessionId());
                        r.setInsuDividePreDr(getInsuOPDividePreResponse.getInsuDividePreDr());
                        r.setInsuAdmInfoJson(getInsuOPDividePreResponse.getInsuAdmInfoJson());

                        resp.setInsUploadFeeResp(JsonSerializer.toJson(r));
                        resp.setInsuAdmDr(getInsuOPDividePreResponse.getInsuAdmDr());
                        resp.setInsuAdmInfoDr(getInsuOPDividePreResponse.getInsuAdmInfoDr());
                    }
                    return baseResponse.succeed(resp);
                }
                return baseResponse.failed(BaseResponse2.APP_ERROR, "预结算：HIS 返回列表数据为空");
            }
            return baseResponse.failed(BaseResponse.APP_ERROR, response.getMsg());
        } catch (YwtCommonException ame) {
            return baseResponse.failed(BaseResponse.PARAMETER_ERROR, ame.getMessage());
        } catch (
                Exception e) {
            logger.error("DHOutpatientController#prePayConfirm(reqData={} ):\n {}", reqData, e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    private void autoCancelPrevOrder(String hisOrderNo) {
        GetOutpatientOrderByHisOrderNoRequest request = GetOutpatientOrderByHisOrderNoRequest.newBuilder()
                .setHisOrderNo(hisOrderNo)
                .setHospitalId(BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped()))
                .build();
        GetOutpatientOrderByHisOrderNoResponse response = taiheOutpatientServiceBlockingStub.getOutpatientOrderByHisOrderNo(request);
        if (response.getCode() == ResultCode.SUCCEED_VALUE) {
            String orderNo = response.getOrderNo();
            if (!StringHelper.isNullOrEmpty(orderNo)) {
                logger.info("DHOutpatientController#autoCancelPrevOrder(): 预结算自动取消之前生成的订单 orderNo = {}", orderNo);
                MarkOpOrderDeletedResponse markOpOrderDeletedResponse = wxInsPayServiceBlockingStub.markOpOrderDeleted(
                        MarkOpOrderDeletedRequest.newBuilder()
                                .setOrderNo(orderNo)
                                .build());
                if (markOpOrderDeletedResponse.getCode() != ResultCode.SUCCEED_VALUE) {
                    logger.error("DHOutpatientController#autoCancelPrevOrder(): markOpOrderDeleted failed: {}", markOpOrderDeletedResponse.getMsg());
                }
            }
        } else {
            logger.error("DHOutpatientController#autoCancelPrevOrder(hisOrderNo={} ):\n {}", hisOrderNo, response.getMsg());
        }
    }

    /**
     * 获取已支付门诊订单明细
     *
     * @param reqData
     * @return
     */
    @RequestMapping(value = {"/getPaidDetail"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<PaidDetailResp> getPaidDetail(@RequestBody OpOrderDetailReq reqData) {
        BaseResponse2<PaidDetailResp> baseResponse = new BaseResponse2<>();
        try {
            int hospitalId = BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped());
            PaidDetailResp p = new PaidDetailResp();
            List<OpOrderDetailResp> list = new LinkedList<>();
            switch (hospitalId) {
                default:
                case Constant.TAIHE_HOSPITAL_ID:
                    // 太和从数据库读取数据
                    String hisOrderNo = reqData.getHisOrderNo();
                    CheckUtil.ensureNotEmpty(hisOrderNo, "hisOrderNo 不能为空");
                    GetOutpatientOrderDetailRequest req = GetOutpatientOrderDetailRequest.newBuilder()
                            .setHospitalId(BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped()))
                            .setHisOrderNo(hisOrderNo)
                            .build();
                    GetOutpatientOrderDetailResponse response = taiheOutpatientServiceBlockingStub.getOutpatientOrderDetail(req);
                    if (response.getCode() != ResultCode.SUCCEED_VALUE)
                        return baseResponse.failed(BaseResponse.APP_ERROR, response.getMsg());
                    if (!Checker.isNone(response.getDetailList())) {
                        list = response.getDetailList().stream()
                                .map(i -> {
                                    OpOrderDetailResp resp = new OpOrderDetailResp();
                                    resp.setName(i.getItemName());
                                    resp.setPrice(i.getItemPrice());
                                    resp.setCode(i.getItemCode());
                                    resp.setNumber(i.getItemNumber());
                                    resp.setSpec(i.getItemSpec());
                                    resp.setUnit(i.getItemUnit());
                                    resp.setTotalFee(i.getItemTotalFee());
                                    return resp;
                                })
                                .collect(Collectors.toList());
                    }
                    p.setList(list);
                    p.setPatientAge(Checker.getIntegerValue(response.getPatientAge()));
                    p.setPatientName(Checker.getStringValue(response.getPatientName()));
                    p.setPatientSex(SexEnum.getSexDisplayName(Checker.getIntegerValue(response.getPatientSex())));
                    p.setInvoiceNo(Checker.getStringValue(response.getInvoiceNo()));
                    p.setSelfPay(response.getSelfPay());
                    p.setInsurance(response.getInsurance());
                    p.setInsDetail(response.getInsDetail());
                    p.setType(response.getType());
                    break;
                case Constant.NFYYBYFY_HOSPITAL_ID:
                    // 白云从 HIS 接口获取门诊缴费详情
                    String invoiceNo = reqData.getInvoiceNo();
                    String hisPatientId = reqData.getHisPatientId();
                    CheckUtil.ensureNotEmpty(invoiceNo, "发票号不能为空");
                    CheckUtil.ensureNotEmpty(hisPatientId, "HIS 病人 id 不能为空");
                    // 根据 his_patient_id 获取诊疗卡信息
                    String pName = "";
                    int pAge = 0;
                    String pGender = "";
                    GetMedicalCardInfoByPatientIdRequest gResp = GetMedicalCardInfoByPatientIdRequest.newBuilder()
                            .setHisPatientId(hisPatientId)
                            .setHospitalId(hospitalId)
                            .build();
                    GetMedicalCardInfoByPatientIdResponse gmcResp = taiheOutpatientServiceBlockingStub.getMedicalCardInfoByPatientId(gResp);
                    if (gmcResp.getCode() == ResultCode.SUCCEED_VALUE) {
                        pName = gmcResp.getPatientName();
                        pAge = getPatientAgeSafely(gmcResp.getIdNo(), gmcResp.getBirthday());
                        pGender = IdCardUtil.getSex(gmcResp.getIdNo());
                    }
                    GetPaymentDetailResponse response2 = taiheServiceBlockingStub.getPaymentDetail(GetPaymentDetailRequest.newBuilder()
                            .setHospitalId(hospitalId)
                            .setInvoiceNo(invoiceNo)
                            .setHisPatientId(hisPatientId)
                            .setType("2")
                            .build());
                    if (response2.getCode() != ResultCode.SUCCEED_VALUE)
                        return baseResponse.failed(BaseResponse.APP_ERROR, response2.getMsg());
                    if (!Checker.isNone(response2.getDetailList())) {
                        list = response2.getDetailList().stream()
                                .map(i -> {
                                    OpOrderDetailResp resp = new OpOrderDetailResp();
                                    resp.setName(i.getItemName());
                                    resp.setPrice(i.getItemPrice());
                                    resp.setCode(i.getItemCode());
                                    resp.setNumber(String.valueOf(i.getItemNumber()));
                                    resp.setSpec(i.getItemSpec());
                                    resp.setUnit(i.getItemUnit());
                                    resp.setTotalFee(i.getItemTotalFee());
                                    return resp;
                                })
                                .collect(Collectors.toList());
                    }

                    GetOutpatientOrderDetailByInvoiceRequest requ = GetOutpatientOrderDetailByInvoiceRequest.newBuilder()
                            .setHospitalId(BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped()))
                            .setInvoiceNo(invoiceNo)
                            .build();
                    GetOutpatientOrderDetailByInvoiceResponse resp = taiheOutpatientServiceBlockingStub.getOutpatientOrderByInvoiceNo(requ);
                    if (resp.getCode() != ResultCode.SUCCEED_VALUE) {
                        return baseResponse.failed(BaseResponse.APP_ERROR, resp.getMsg());
                    }
                    String selfPay = "";
                    p.setList(list);
                    p.setPatientAge(pAge);
                    p.setPatientName(pName);
                    p.setPatientSex(pGender);
                    p.setInvoiceNo(invoiceNo);
                    p.setSelfPay(resp.getSelfPay());
                    p.setInsurance(resp.getInsurance());
                    p.setInsDetail(resp.getInsDetail());
                    p.setType(resp.getType());
                    break;
            }
            return baseResponse.succeed(p);
        } catch (YwtCommonException ame) {
            return baseResponse.failed(BaseResponse.PARAMETER_ERROR, ame.getMessage());
        } catch (Exception e) {
            logger.error("DHOutpatientController#getPaidDetail(reqData={} ):\n {}", reqData, e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    /**
     * 根据身份证或出生日期获取年龄，避免当身份证为空的情况下获取年龄会返回奇怪的值
     *
     * @param idNo     身份证
     * @param birthday 出生年月日，格式 yyyy-MM-dd
     * @return
     */
    private int getPatientAgeSafely(String idNo, String birthday) {
        if (!Checker.isNone(idNo)) {
            return IdCardUtil.getCurrentAge(idNo);
        } else {
            logger.info("DHOutpatientController#getPatientAgeSafely(birthday = {}): 身份证为空", birthday);
            return IdCardUtil.getCurrentAgeByBirthday(birthday, IdCardUtil.DATE_PATTERN2);
        }
    }

    @RequestMapping(value = {"/treatmentGuide"}, method = RequestMethod.POST)
    public BaseResponse getTreatmentGuide(@RequestBody GetTreatmentGuideReq reqData) {
        BaseResponse baseResponse = new BaseResponse();
        try {
            String invoiceNo = Checker.getStringValue(reqData.getInvoiceNo());
            CheckUtil.ensureNotEmpty(invoiceNo, "发票号不能为空");
            String hisOrderNo = reqData.getHisOrderNo();
//            CheckUtil.ensureNotEmpty(hisOrderNo, "hisOrderNo 不能为空");
            GetTreatmentGuideV2Request request = GetTreatmentGuideV2Request.newBuilder()
                    .setInvoiceNo(invoiceNo)
                    .setHospitalId(BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped()))
                    .build();
            GetTreatmentGuideV2Response response = taiheServiceBlockingStub.getTreatmentGuideV2(request);


            int hospitalId = BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped());
            String selfPay = "";
            String insurance = "";
            String insDetail = "";
            int type = 0;
            if (hospitalId == Constant.TAIHE_HOSPITAL_ID) {
                CheckUtil.ensureNotEmpty(hisOrderNo, "hisOrderNo 不能为空");
                GetOutpatientOrderDetailRequest req = GetOutpatientOrderDetailRequest.newBuilder()
                        .setHospitalId(BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped()))
                        .setHisOrderNo(hisOrderNo)
                        .build();
                GetOutpatientOrderDetailResponse res = taiheOutpatientServiceBlockingStub.getOutpatientOrderDetail(req);
                if (res.getCode() != BaseResponse.SUCCEED) {
                    return baseResponse.failed(BaseResponse.APP_ERROR, res.getMsg());
                }
                selfPay = res.getSelfPay();
                insurance = res.getInsurance();
                insDetail = res.getInsDetail();
                type = res.getType();
            } else if (hospitalId == Constant.NFYYBYFY_HOSPITAL_ID) {
                GetOutpatientOrderDetailByInvoiceRequest requ = GetOutpatientOrderDetailByInvoiceRequest.newBuilder()
                        .setHospitalId(BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped()))
                        .setInvoiceNo(invoiceNo)
                        .build();
                GetOutpatientOrderDetailByInvoiceResponse res = taiheOutpatientServiceBlockingStub.getOutpatientOrderByInvoiceNo(requ);
                if (res.getCode() != ResultCode.SUCCEED_VALUE) {
                    return baseResponse.failed(BaseResponse.APP_ERROR, res.getMsg());
                }
                selfPay = res.getSelfPay();
                insurance = res.getInsurance();
                insDetail = res.getInsDetail();
                type = res.getType();

            }
            if (response.getCode() == BaseResponse.SUCCEED) {
                GetTreatmentGuideResp resp = new GetTreatmentGuideResp();
                resp.setPatientId(response.getPatientId());
                resp.setPatientName(response.getPatientName());
                resp.setSex(response.getSex());
                resp.setAge(response.getAge());
                resp.setPayTime(response.getPayTime());
                resp.setPatType(response.getPatType());
                resp.setPayAmt(response.getPayAmt());
                resp.setDiagnosis(response.getDiagnosis());
                resp.setDoctorName(response.getDoctorName());

                resp.setSelfPay(selfPay);
                resp.setInsurance(insurance);
                resp.setInsDetail(insDetail);
                resp.setType(type);

                List<TreamentGuideItem> guideItems = response.getItemListList();
                List<GetTreatmentGuideItemResp> items = guideItems.stream().map(item -> {
                    GetTreatmentGuideItemResp treatmentGuideItemResp = new GetTreatmentGuideItemResp();
                    treatmentGuideItemResp.setPreName(item.getPreName());
                    treatmentGuideItemResp.setDeptName(item.getDeptName());
                    treatmentGuideItemResp.setAddress(item.getAddress());
                    treatmentGuideItemResp.setWindowName(item.getWindowName());
                    treatmentGuideItemResp.setRemark(item.getRemark());

                    List<TreamentGuidePreItem> guidePreItems = item.getPreItemList();
                    List<GetTreatmentGuidePreItemResp> preItems = guidePreItems.stream().map(preItem -> {
                        GetTreatmentGuidePreItemResp preItemResp = new GetTreatmentGuidePreItemResp();
                        preItemResp.setItemName(preItem.getItemName());
                        preItemResp.setItemNumber(preItem.getItemNumber());
                        preItemResp.setItemPrice(preItem.getItemPrice());
                        preItemResp.setItemUnit(preItem.getItemUnit());
                        preItemResp.setItemTotalFee(preItem.getItemTotalFee());
                        preItemResp.setItemAddress(preItem.getItemAddress());
                        return preItemResp;
                    }).collect(Collectors.toList());
                    treatmentGuideItemResp.setPreItem(preItems);
                    return treatmentGuideItemResp;
                }).collect(Collectors.toList());
                resp.setItemList(items);
                return baseResponse.succeed(resp);
            }
            return baseResponse.failed(BaseResponse.APP_ERROR, response.getMsg());
        } catch (YwtCommonException e) {
            return baseResponse.failed(BaseResponse.PARAMETER_ERROR, e.getMessage());
        } catch (Exception e) {
            logger.error("DHOutpatientController#getTreamentGuide(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }


    /**
     * 电子发票
     *
     * @param reqData
     * @return
     */
    @RequestMapping(value = {"/getEInvoice"}, method = RequestMethod.POST)
    public BaseResponse getEInvoice(@RequestBody GetEInvoiceReq reqData) {
        BaseResponse baseResponse = new BaseResponse();
        try {
            String invoiceNo = Checker.getStringValue(reqData.getInvoiceNo());
            int hospitalId = Checker.getIntegerValue(reqData.getHospitalId());
            if (hospitalId <= 0) {
                // 前端不传 hospitalId，从网关获取
                hospitalId = BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped());
            }
            CheckUtil.ensureNotEmpty(invoiceNo, "发票号不能为空");
            String invoiceType = Checker.getStringValue(reqData.getInvoiceType());

            GetEInvoiceRequest request = GetEInvoiceRequest.newBuilder().setInvoiceNo(invoiceNo).setHospitalId(hospitalId).setInvoiceType(invoiceType).build();
            GetEInvoiceResponse response = taiheServiceBlockingStub.getEInvoice(request);

            if (response.getCode() == BaseResponse.SUCCEED) {

                GetEInvoiceResp resp = new GetEInvoiceResp();
                resp.setInvoiceInfo(response.getInvoiceInfo());

                return baseResponse.succeed(resp);
            }
            return baseResponse.failed(BaseResponse.APP_ERROR, response.getInfo());
        } catch (YwtCommonException e) {
            return baseResponse.failed(BaseResponse.PARAMETER_ERROR, e.getMessage());
        } catch (Exception e) {
            logger.error("DHOutpatientController#getEInvoice(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    @RequestMapping(value = {"/getInPayList"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<ListResponse<com.ywt.outpatient.api.outpatient.InPayInfo>> getInPayList(@RequestBody GetInPayListReq reqData) {
        BaseResponse2<ListResponse<com.ywt.outpatient.api.outpatient.InPayInfo>> baseResponse = new BaseResponse2<>();
        try {
            int hospitalId = Checker.getIntegerValue(reqData.getHospitalId());
            if (hospitalId <= 0) {
                // 前端不传 hospitalId，从网关获取
                hospitalId = BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped());
            }
            String hisPatientId = reqData.getHisPatientId();
            CheckUtil.ensureNotEmpty(hisPatientId, "hisPatientId 不能为空");
            String startDate = reqData.getStartDate();
            String endDate = reqData.getEndDate();
            Date start = DateUtil.parseDate(startDate, DateUtil.DADE_FROMAT_YMD);
            Date end = DateUtil.parseDate(endDate, DateUtil.DADE_FROMAT_YMD);
            CheckUtil.ensureNotNull(start, "开始日期格式不正确");
            CheckUtil.ensureNotNull(end, "结束日期格式不正确");
            if (end.before(start)) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "结束日期不能早于开始日期");
            int userId = userService.getCurrentUserIdWrapped();
            medicalCardService.verifyMedicalCard(userId, hospitalId, hisPatientId, 0);
            GetInPayListResponse response = taiheServiceBlockingStub.getInPayList(GetInPayListRequest.newBuilder()
                    .setHisPatientId(hisPatientId)
                    .setStartDate(startDate)
                    .setEndDate(endDate)
                    .setHospitalId(hospitalId)
                    .build());
            if (response.getCode() == BaseResponse.SUCCEED) {
                ListResponse<InPayInfo> resp = new ListResponse<>();
                resp.setList(response.getInPayInfosList().stream().map(i -> {
                            com.ywt.outpatient.api.outpatient.InPayInfo info = new com.ywt.outpatient.api.outpatient.InPayInfo();
                            info.setStartDate(i.getStartDate());
                            info.setEndDate(i.getEndDate());
                            info.setDeptName(i.getDeptName());
                            info.setTotalFee(i.getTotalFee());
                            info.setChargeFlag(i.getChargeFlag());
                            info.setInvoiceNo(i.getInvoiceNo());
                            info.setInvoiceType(i.getInvoiceType());
                            info.setInvoiceTypeName(i.getInvoiceTypeName());
                            return info;
                        })
                        .collect(Collectors.toList()));
                return baseResponse.succeed(resp);
            }
            return baseResponse.failed(BaseResponse.APP_ERROR, response.getInfo());
        } catch (YwtCommonException appMessageException) {
            return baseResponse.error(appMessageException.getMessage());
        } catch (Exception e) {
            logger.error("DHOutpatientController#getInPayList(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }


    /**
     * 查询微信医保支付订单
     *
     * @param reqData
     * @return
     */
    @RequestMapping(value = {"/queryWxInsPayOrder"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<QueryWxInsPayOrderResp> queryWxInsPayOrder(@RequestBody QueryWxInsPayOrderReq reqData) {
        BaseResponse2<QueryWxInsPayOrderResp> baseResponse = new BaseResponse2<>();
        try {
            String hospOutTradeNo = Checker.getStringValue(reqData.getHospOutTradeNo()).trim();
            CheckUtil.ensureNotEmpty(hospOutTradeNo, "订单号不能为空");
            QueryPayInsOrderResponse response = wxInsPayServiceBlockingStub.queryPayInsOrder(QueryPayInsOrderRequest.newBuilder()
                    .setHospOutTradeNo(hospOutTradeNo)
                    .setTerminal(BizUtil.getCurrentTerminalWrapped())
                    .build());
            if (response.getCode() == BaseResponse.SUCCEED) {
                QueryWxInsPayOrderResp resp = new QueryWxInsPayOrderResp();
                resp.setMedTradeState(response.getMedTradeState());
                resp.setTimeEnd(response.getTimeEnd());
                return baseResponse.succeed(resp);
            }
            return baseResponse.failed(BaseResponse.APP_ERROR, response.getMsg());
        } catch (YwtCommonException appMessageException) {
            return baseResponse.error(appMessageException.getMessage());
        } catch (Exception e) {
            logger.error("DHOutpatientController#queryWxInsPayOrder(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    @RequestMapping(value = {"/invoiceList"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<ListResponse<InvoiceListResp>> invoiceList(@RequestBody InvoiceListReq reqData) {
        BaseResponse2<ListResponse<InvoiceListResp>> baseResponse = new BaseResponse2<>();
        try {
            String hisPatientId = reqData.getHisPatientId();
            CheckUtil.ensureNotEmpty(hisPatientId, "请选择诊疗卡");
            String startDate = reqData.getStartDate();
            String endDate = reqData.getEndDate();
            Date start = DateUtil.parseDate(startDate, DateUtil.DADE_FROMAT_YMD);
            CheckUtil.ensureNotNull(start, "开始日期格式不正确");
            Date end = DateUtil.parseDate(endDate, DateUtil.DADE_FROMAT_YMD);
            CheckUtil.ensureNotNull(end, "结束日期格式不正确");
            if (end.before(start)) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "结束日期不能早于开始日期");
            long interval = DateUtil.getDaySub(startDate, endDate);
            if (interval > 15) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "暂不支持查询间隔超过 15 天的时间段");
            int userId = userService.getCurrentUserIdWrapped();
            int hospitalId = BizUtil.getHospitalIdByTerminalId(userSrv.getCurrentTerminal());
            medicalCardService.verifyMedicalCard(userId, hospitalId, hisPatientId, 0);
            GetOutpatientListResponse response = taiheServiceBlockingStub.getOutpatientList(GetOutpatientListRequest.newBuilder()
                    .setPatientId(hisPatientId)
                    .setHospitalId(BizUtil.getHospitalIdByTerminalId(userSrv.getCurrentTerminal()))
                    .setStartDate(startDate)
                    .setEndDate(endDate)
                    .setType(2)
                    .build());
            if (response.getCode() == BaseResponse.SUCCEED) {
                ListResponse<InvoiceListResp> listResponse = new ListResponse<>();
                listResponse.setList(response.getPaymentList().stream()
                        .map(paymentItem -> {
                            InvoiceListResp r = new InvoiceListResp();
                            r.setInvDate(paymentItem.getInvDate());
                            r.setInvoiceNo(paymentItem.getInvoiceNo());
                            r.setTotalFeeStr(String.format("%.2f 元", BizUtil.cent2Yuan(paymentItem.getTotalFee())));
                            r.setInvoiceType(paymentItem.getInvoiceType());
                            r.setInvoiceTypeName(paymentItem.getInvoiceTypeName());
                            return r;
                        })
                        .collect(Collectors.toList()));
                return baseResponse.succeed(listResponse);
            }
            return baseResponse.failed(BaseResponse.APP_ERROR, response.getMsg());
        } catch (YwtCommonException appMessageException) {
            return baseResponse.error(appMessageException.getMessage());
        } catch (Exception e) {
            logger.error("DHOutpatientController#invoiceList(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    /**
     * 判断能否使用医保支付
     */
    @RequestMapping(value = {"/verifyMedInfo"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<VerifyMedInfoResp> verifyMedInfo(@RequestBody VerifyMedInfoReq reqData) {
        BaseResponse2<VerifyMedInfoResp> baseResponse = new BaseResponse2<>();
        try {
            String patientId = Checker.getStringValue(reqData.getPatientId());
            int hospitalId = BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped());
            String openId = userSrv.getCurrentOpenId();
            CheckUtil.ensureNotEmpty(openId, "用户 openId 参数为空");
            CheckUtil.ensureNotEmpty(patientId, "用户 patientId 参数为空");
            CheckUtil.ensureLargerThanZero(hospitalId, "医院 id 参数有误");
            // 获取身份证和姓名
            GetMedicalCardInfoByPatientIdRequest gResp = GetMedicalCardInfoByPatientIdRequest.newBuilder()
                    .setHisPatientId(patientId)
                    .setHospitalId(hospitalId)
                    .setUserId(userSrv.getCurrentUserIdWrapped())
                    .build();
            GetMedicalCardInfoByPatientIdResponse gmcResp = taiheOutpatientServiceBlockingStub.getMedicalCardInfoByPatientId(gResp);
            if (gmcResp.getCode() != ResultCode.SUCCEED_VALUE) {
                return baseResponse.failedWithAppError(String.format("获取不到诊疗卡信息: %s(%s)", gmcResp.getMsg(), patientId));
            }
            String idCard = gmcResp.getIdNo();
            String name = gmcResp.getPatientName();
            if (StringHelper.isNullOrEmpty(idCard))
                return baseResponse.failedWithAppError(String.format("用户诊疗卡身份证信息为空(%s)", patientId));
            if (StringHelper.isNullOrEmpty(name))
                return baseResponse.failedWithAppError(String.format("用户诊疗卡姓名信息为空(%s)", patientId));
            VerifyMedInfoResponse response = wxInsPayServiceBlockingStub.verifyMedInfo(VerifyMedInfoRequest.newBuilder()
                    .setIdCard(idCard)
                    .setName(name)
                    .setOpenId(openId)
                    .setTerminal(BizUtil.getCurrentTerminalWrapped())
                    .build());
            if (response.getCode() == BaseResponse.SUCCEED) {
                VerifyMedInfoResp resp = new VerifyMedInfoResp();
                boolean isValid = response.getIsValid();
                resp.setValid(isValid);
                return baseResponse.succeed(isValid ? "" : response.getMsg(), resp);
            }
            return baseResponse.failed(BaseResponse.APP_ERROR, response.getMsg());
        } catch (YwtCommonException appMessageException) {
            return baseResponse.error(appMessageException.getMessage());
        } catch (Exception e) {
            logger.error("DHOutpatientController#verifyMedInfo(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }

    /**
     * 查询当前诊疗卡是否支持医保结算
     */
    @RequestMapping(value = {"/insuPaySupport"}, method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse2<InsuPaySupportResp> insuPaySupport(@RequestBody InsuPaySupportReq reqData) {
        BaseResponse2<InsuPaySupportResp> baseResponse = new BaseResponse2<>();
        try {
            int hospitalId = Checker.getIntegerValue(reqData.getHospitalId());
            if (hospitalId <= 0)
                hospitalId = BizUtil.getHospitalIdByTerminalId(BizUtil.getCurrentTerminalWrapped());
            String hisPatientId = Checker.getStringValue(reqData.getHisPatientId());
            CheckUtil.ensureNotEmpty(hisPatientId, "hisPatientId 不能为空");
            // 获取诊疗卡信息
            GetMedicalCardInfoByPatientIdRequest gResp = GetMedicalCardInfoByPatientIdRequest.newBuilder()
                    .setHisPatientId(hisPatientId)
                    .setHospitalId(hospitalId)
                    .setUserId(userSrv.getCurrentUserIdWrapped())
                    .build();
            GetMedicalCardInfoByPatientIdResponse gmcResp = taiheOutpatientServiceBlockingStub.getMedicalCardInfoByPatientId(gResp);
            if (gmcResp.getCode() != ResultCode.SUCCEED_VALUE) {
                return baseResponse.failedWithAppError(String.format("获取不到诊疗卡信息: %s(%s)", gmcResp.getMsg(), hisPatientId));
            }
            String cardType = gmcResp.getPatientType();
            // 读取配置
            GetOutPatientConfigResponse getOutPatientConfigResponse = taiheOutpatientServiceBlockingStub.getOutpatientConfigs(GetOutPatientConfigRequest.newBuilder()
                    .setHospitalId(hospitalId)
                    .build());
            List<String> insuSupportCardType = Arrays.asList(getOutPatientConfigResponse.getInsuSupportCardType().split(","));

            InsuPaySupportResp resp = new InsuPaySupportResp();
            resp.setInsuPaySupport(insuSupportCardType.contains(cardType));
            return baseResponse.succeed(resp);
        } catch (YwtCommonException appMessageException) {
            return baseResponse.error(appMessageException.getMessage());
        } catch (Exception e) {
            logger.error("DHOutpatientController#insuPaySupport(): {}", e.getMessage(), e);
            return baseResponse.error(e.getMessage());
        }
    }
}
