parent
d131a3702a
commit
a1ed3ea2dc
@ -0,0 +1,54 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Dictionary\Medical; |
||||||
|
|
||||||
|
/** |
||||||
|
* 支付类型 |
||||||
|
*/ |
||||||
|
enum OrderType: string |
||||||
|
{ |
||||||
|
case REG_PAY = 'RegPay'; // 挂号支付 |
||||||
|
|
||||||
|
case MED_PAY = 'MedPay'; // 药费支付 |
||||||
|
|
||||||
|
case DIAG_PAY = 'DiagPay'; // 诊间支付 |
||||||
|
|
||||||
|
case IN_HOSP_PAY = 'InHospPay'; // 住院费支付 |
||||||
|
|
||||||
|
case PHARMACY_PAY = 'PharmacyPay'; // 药店支付 |
||||||
|
|
||||||
|
case INSURANCE_PAY = 'InsurancePay'; // 保险费支付 |
||||||
|
|
||||||
|
case INT_REG_PAY = 'IntRegPay'; // 互联网医院挂号支付 |
||||||
|
|
||||||
|
case INT_RE_DIAG_PAY = 'IntReDiagPay'; // 互联网医院复诊支付 |
||||||
|
|
||||||
|
case INT_PSC_PAY = 'IntPscPay'; // 互联网医院处方支付 |
||||||
|
|
||||||
|
case COVID_EXAM_PAY = 'CovidExamPay'; // 新冠检测费用 |
||||||
|
|
||||||
|
case COVID_ANTIGEN_PAY = 'CvidAntigenPay';// 新冠抗原检测 |
||||||
|
|
||||||
|
/** |
||||||
|
* Label string |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public function label(): string |
||||||
|
{ |
||||||
|
return match($this) { |
||||||
|
self::REG_PAY => '挂号支付', |
||||||
|
self::MED_PAY => '药费支付', |
||||||
|
self::DIAG_PAY => '诊间支付', |
||||||
|
self::IN_HOSP_PAY => '住院费支付', |
||||||
|
self::PHARMACY_PAY => '药店支付', |
||||||
|
self::INSURANCE_PAY => '保险费支付', |
||||||
|
self::INT_REG_PAY => '互联网医院挂号支付', |
||||||
|
self::INT_RE_DIAG_PAY => '互联网医院复诊支付', |
||||||
|
self::INT_PSC_PAY => '互联网医院处方支付', |
||||||
|
self::COVID_EXAM_PAY => '新冠检测费用', |
||||||
|
self::COVID_ANTIGEN_PAY => '新冠抗原检测', |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Dictionary\Medical; |
||||||
|
|
||||||
|
/** |
||||||
|
* 病人性质 |
||||||
|
*/ |
||||||
|
enum PatientProperty: int |
||||||
|
{ |
||||||
|
|
||||||
|
case OUTPATIENT_PLANNING = 6089; // 门诊统筹 |
||||||
|
|
||||||
|
case OUTPATIENT_MUTUAL_AID = 6095; // 门诊共济 |
||||||
|
|
||||||
|
/** |
||||||
|
* Label string |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public function label(): string |
||||||
|
{ |
||||||
|
return match($this) { |
||||||
|
self::OUTPATIENT_PLANNING => '门诊统筹', |
||||||
|
self::OUTPATIENT_MUTUAL_AID => '门诊共济', |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Dictionary\Medical; |
||||||
|
|
||||||
|
/** |
||||||
|
* 医保支付类型 |
||||||
|
*/ |
||||||
|
enum PayType: int |
||||||
|
{ |
||||||
|
case SELF_PAY = 0; // 自费 |
||||||
|
|
||||||
|
case OFFLINE_MEDICARE = 1; // 线下医保 |
||||||
|
|
||||||
|
case ONLINE_MEDICARE = 2; // 线上医保 |
||||||
|
|
||||||
|
/** |
||||||
|
* Label string |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public function label(): string |
||||||
|
{ |
||||||
|
return match($this) { |
||||||
|
self::SELF_PAY => '自费', |
||||||
|
self::OFFLINE_MEDICARE => '线下医保', |
||||||
|
self::ONLINE_MEDICARE => '线上医保', |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Dictionary\Medical; |
||||||
|
|
||||||
|
/** |
||||||
|
* 医保预结算状态 |
||||||
|
*/ |
||||||
|
enum PreSettleStatus: int |
||||||
|
{ |
||||||
|
case NOT_NEEDED = 0; // 无需预结算 |
||||||
|
|
||||||
|
case SETTLED = 1; // 已预结算 |
||||||
|
|
||||||
|
case CANCELLED = 2; // 已取消预结算 |
||||||
|
|
||||||
|
/** |
||||||
|
* Label string |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public function label(): string |
||||||
|
{ |
||||||
|
return match($this) { |
||||||
|
self::NOT_NEEDED => '无需预结算', |
||||||
|
self::SETTLED => '已预结算', |
||||||
|
self::CANCELLED => '已取消预结算', |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Dictionary\Medical; |
||||||
|
|
||||||
|
/** |
||||||
|
* 医保预结算类型 |
||||||
|
*/ |
||||||
|
enum SettleType: int |
||||||
|
{ |
||||||
|
case CASH = 0; // 使用现金支付 |
||||||
|
|
||||||
|
case MEDICARE_ACCOUNT = 1; // 使用医保个账支付 |
||||||
|
|
||||||
|
/** |
||||||
|
* Label string |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public function label(): string |
||||||
|
{ |
||||||
|
return match($this) { |
||||||
|
self::CASH => '使用现金支付', |
||||||
|
self::MEDICARE_ACCOUNT => '使用医保个账支付', |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Dictionary\Report; |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查报告影像类型 |
||||||
|
*/ |
||||||
|
enum ImageType: string |
||||||
|
{ |
||||||
|
case STUDY_ID = 'styid'; // 查询胶片ID (参数: studyid) |
||||||
|
|
||||||
|
case IMAGE = 'img'; // 获取胶片图像 (参数: 胶片ID) |
||||||
|
|
||||||
|
case IMAGE_BASE64 = 'img64'; // 获取胶片Base64编码 (参数: 胶片ID) |
||||||
|
|
||||||
|
case PDF = 'pdf'; // 获取报告PDF文件 (参数: studyid) |
||||||
|
|
||||||
|
case PDF_BASE64 = 'pdf64'; // 获取报告PDF的Base64编码 (参数: studyid) |
||||||
|
|
||||||
|
/** |
||||||
|
* Label string |
||||||
|
*/ |
||||||
|
public function label(): string |
||||||
|
{ |
||||||
|
return match ($this) { |
||||||
|
self::STUDY_ID => '查询胶片ID', |
||||||
|
self::IMAGE => '获取胶片图像', |
||||||
|
self::IMAGE_BASE64 => '获取胶片Base64编码', |
||||||
|
self::PDF => '获取报告PDF文件', |
||||||
|
self::PDF_BASE64 => '获取报告PDF的Base64编码', |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,110 @@ |
|||||||
|
<?php |
||||||
|
declare(strict_types = 1); |
||||||
|
|
||||||
|
namespace App\Http\Controllers\Outpatient; |
||||||
|
|
||||||
|
use App\Dictionary\Medical\SettleType; |
||||||
|
use App\Exceptions\GeneralException; |
||||||
|
use App\Http\Logics\Outpatient\MedicalLogic; |
||||||
|
use App\Http\Resources\Outpatient\Medical\PrescriptionPreSettleResource; |
||||||
|
use Illuminate\Http\JsonResponse; |
||||||
|
use Illuminate\Http\Request; |
||||||
|
use Illuminate\Validation\Rules\Enum; |
||||||
|
use Symfony\Component\HttpFoundation\Response; |
||||||
|
|
||||||
|
class MedicalController |
||||||
|
{ |
||||||
|
protected MedicalLogic $medical_logic; |
||||||
|
|
||||||
|
/** |
||||||
|
* MedicalController Construct. |
||||||
|
*/ |
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$this->medical_logic = new MedicalLogic(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 跳转免密授权页面 |
||||||
|
* @param Request $request |
||||||
|
* @return JsonResponse |
||||||
|
*/ |
||||||
|
public function redirectAuth(Request $request): JsonResponse |
||||||
|
{ |
||||||
|
$redirect_url = $this->medical_logic->getMiniProgramAuthRedirectUrl(); |
||||||
|
|
||||||
|
return jsonResponse(Response::HTTP_OK, 'success', [ |
||||||
|
// 固定跳转医保小程序APP ID |
||||||
|
'app_id' => config('custom.redirect_app_id'), |
||||||
|
'url' => $redirect_url |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 查询患者授权信息 |
||||||
|
* @param Request $request |
||||||
|
* @param string $patient_id |
||||||
|
* @return JsonResponse |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function userAuthInfo(Request $request, string $patient_id): JsonResponse |
||||||
|
{ |
||||||
|
$validated = $request->validate([ |
||||||
|
'auth_code.require' => 'required', |
||||||
|
], [ |
||||||
|
'auth_code.required' => '查询数据参数错误', |
||||||
|
]); |
||||||
|
|
||||||
|
$user_info = $this->medical_logic->queryPatientMedicalInsuranceAuthInfo($patient_id, $validated['auth_code']); |
||||||
|
|
||||||
|
return jsonResponse(Response::HTTP_OK, 'success', $user_info); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处方预结算 |
||||||
|
* @param Request $request |
||||||
|
* @param string $patient_id |
||||||
|
* @param string $serial_no |
||||||
|
* @return JsonResponse |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function prescriptionPreSettle(Request $request, string $patient_id, string $serial_no): JsonResponse |
||||||
|
{ |
||||||
|
$validated = $request->validate([ |
||||||
|
'settle_type' => ['required', new Enum(SettleType::class)], |
||||||
|
], [ |
||||||
|
'settle_type.required' => '请选择结算类型', |
||||||
|
'settle_type.Illuminate\Validation\Rules\Enum' => '请选择正确的结算类型', |
||||||
|
]); |
||||||
|
|
||||||
|
$settle_type = SettleType::from((int) $validated['settle_type']); |
||||||
|
|
||||||
|
$response = $this->medical_logic->pendingPrescriptionPreSettle($patient_id, $serial_no, $settle_type); |
||||||
|
|
||||||
|
return jsonResponse(Response::HTTP_OK, 'success', PrescriptionPreSettleResource::make($response)->toArray()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 医保支付 |
||||||
|
* @param Request $request |
||||||
|
* @param string $patient_id |
||||||
|
* @param string $serial_no |
||||||
|
* @return JsonResponse |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function payment(Request $request, string $patient_id, string $serial_no): JsonResponse |
||||||
|
{ |
||||||
|
$validated = $request->validate([ |
||||||
|
'settle_type' => ['required', new Enum(SettleType::class)], |
||||||
|
], [ |
||||||
|
'settle_type.required' => '请选择结算类型', |
||||||
|
'settle_type.Illuminate\Validation\Rules\Enum' => '请选择正确的结算类型', |
||||||
|
]); |
||||||
|
|
||||||
|
$settle_type = SettleType::from((int) $validated['settle_type']); |
||||||
|
|
||||||
|
$response = $this->medical_logic->medicalPayment($patient_id, $serial_no, $settle_type); |
||||||
|
|
||||||
|
return jsonResponse(Response::HTTP_OK, 'success', $response); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
<?php |
||||||
|
declare(strict_types = 1); |
||||||
|
|
||||||
|
namespace App\Http\Controllers\Registration; |
||||||
|
|
||||||
|
use App\Dictionary\Medical\PatientProperty; |
||||||
|
use App\Dictionary\Medical\SettleType; |
||||||
|
use App\Exceptions\GeneralException; |
||||||
|
use App\Http\Logics\Registration\MedicalLogic; |
||||||
|
use App\Http\Requests\Registration\RegisterPreSettleRequest; |
||||||
|
use App\Http\Resources\Registration\Medical\RegisterPreSettleResource; |
||||||
|
use Illuminate\Http\JsonResponse; |
||||||
|
use Illuminate\Http\Request; |
||||||
|
use Symfony\Component\HttpFoundation\Response; |
||||||
|
|
||||||
|
class MedicalController |
||||||
|
{ |
||||||
|
protected MedicalLogic $medical_logic; |
||||||
|
|
||||||
|
/** |
||||||
|
* MedicalController Construct. |
||||||
|
*/ |
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$this->medical_logic = new MedicalLogic(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 挂号预结算 |
||||||
|
* @param RegisterPreSettleRequest $request |
||||||
|
* @param string $patient_id |
||||||
|
* @return JsonResponse |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function registerPreSettle(RegisterPreSettleRequest $request, string $patient_id): JsonResponse |
||||||
|
{ |
||||||
|
$patient_property = PatientProperty::from((int) $request->patient_property); |
||||||
|
$settle_type = SettleType::from((int) $request->settle_type); |
||||||
|
|
||||||
|
$response = $this->medical_logic->registerPreSettle( |
||||||
|
$patient_id, |
||||||
|
$request->date, |
||||||
|
$request->dept_id, |
||||||
|
$request->doctor_id, |
||||||
|
$request->reg_id, |
||||||
|
$patient_property, |
||||||
|
$settle_type |
||||||
|
); |
||||||
|
|
||||||
|
return jsonResponse(Response::HTTP_OK, 'success', RegisterPreSettleResource::make($response)->toArray()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 医保支付 |
||||||
|
* @param Request $request |
||||||
|
* @param string $patient_id |
||||||
|
* @return JsonResponse |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function register(Request $request, string $patient_id): JsonResponse |
||||||
|
{ |
||||||
|
$patient_property = PatientProperty::from((int) $request->patient_property); |
||||||
|
$settle_type = SettleType::from((int) $request->settle_type); |
||||||
|
|
||||||
|
$response = $this->medical_logic->medicalRegister( |
||||||
|
$patient_id, |
||||||
|
$request->date, |
||||||
|
$request->dept_id, |
||||||
|
$request->doctor_id, |
||||||
|
$request->reg_id, |
||||||
|
$patient_property, |
||||||
|
$settle_type |
||||||
|
); |
||||||
|
|
||||||
|
return jsonResponse(Response::HTTP_OK, 'success', $response); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,497 @@ |
|||||||
|
<?php |
||||||
|
declare(strict_types = 1); |
||||||
|
|
||||||
|
namespace App\Http\Logics\Outpatient; |
||||||
|
|
||||||
|
use App\Dictionary\Medical\OrderType; |
||||||
|
use App\Dictionary\Medical\PayType as MedicalPayType; |
||||||
|
use App\Dictionary\Order\PayType as OrderPayType; |
||||||
|
use App\Dictionary\Medical\PreSettleStatus; |
||||||
|
use App\Dictionary\Medical\SettleType; |
||||||
|
use App\Dictionary\Order\SourceId; |
||||||
|
use App\Dictionary\Order\Type; |
||||||
|
use App\Dictionary\Patient\CardType; |
||||||
|
use App\Exceptions\GeneralException; |
||||||
|
use App\Models\Order; |
||||||
|
use App\Models\Patient; |
||||||
|
use App\Services\HisHttp\Client as HisHttpClient; |
||||||
|
use App\Services\HisMedicalHttp\Client as HisMedicalHttpClient; |
||||||
|
use App\Services\MedicalAuth\Client as AuthClient; |
||||||
|
use App\Utils\Statics\BuildCacheKeyName; |
||||||
|
use App\Utils\Traits\Logger; |
||||||
|
use App\Utils\Traits\MiniProgramAuth; |
||||||
|
use Illuminate\Auth\AuthenticationException; |
||||||
|
use Illuminate\Support\Facades\Redis; |
||||||
|
use ReflectionException; |
||||||
|
use Symfony\Component\HttpFoundation\Response; |
||||||
|
use UnifyPayment\Cores\Exceptions\InvalidConfigException; |
||||||
|
use UnifyPayment\Cores\Exceptions\RuntimeException; |
||||||
|
use UnifyPayment\Cores\Struct\CreateMedicalInsuranceOrder; |
||||||
|
use UnifyPayment\Unify; |
||||||
|
|
||||||
|
class MedicalLogic |
||||||
|
{ |
||||||
|
use Logger; |
||||||
|
use MiniProgramAuth; |
||||||
|
|
||||||
|
private HisHttpClient $his_client; |
||||||
|
|
||||||
|
private HisMedicalHttpClient $his_medical_client; |
||||||
|
|
||||||
|
private AuthClient $auth_client; |
||||||
|
|
||||||
|
private Order $order_model; |
||||||
|
|
||||||
|
private Patient $patient_model; |
||||||
|
|
||||||
|
/** |
||||||
|
* PaymentLogic Construct |
||||||
|
* @throws AuthenticationException |
||||||
|
*/ |
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$this->authInitialize(); |
||||||
|
$this->setChannel('outpatient'); |
||||||
|
$this->his_client = app('HisHttpService'); |
||||||
|
$this->his_medical_client = app('HisMedicalHttpClient'); |
||||||
|
$this->auth_client = new AuthClient(); |
||||||
|
$this->order_model = new Order(); |
||||||
|
$this->patient_model = new Patient(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 查询患者医保授权信息 |
||||||
|
* @param string $patient_number |
||||||
|
* @param string $auth_code 授权码 |
||||||
|
* @return array |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function queryPatientMedicalInsuranceAuthInfo(string $patient_number, string $auth_code): array |
||||||
|
{ |
||||||
|
$patient_info = $this->getPatientInfo($this->open_id, $patient_number); |
||||||
|
$patient_id = &$patient_info['patientId']; |
||||||
|
|
||||||
|
// 获取用户pay_auth_no和经纬度 |
||||||
|
// $info = $this->auth_client->userQuery($auth_code, $this->open_id); |
||||||
|
$info = $this->auth_client->userQuery($auth_code, 'ozOD16zPgRmjdh_O3VvtFtKaiLP0'); |
||||||
|
if ($info['code'] !== 0) { |
||||||
|
throw new GeneralException($info['message'] ?? '获取用户医保信息异常,请稍后重新再试!', Response::HTTP_BAD_REQUEST); |
||||||
|
} |
||||||
|
|
||||||
|
$user_info = [ |
||||||
|
'pay_auth_no' => $info['pay_auth_no'], |
||||||
|
'user_longitude_latitude' => $info['user_longitude_latitude'] |
||||||
|
]; |
||||||
|
|
||||||
|
// 有效期为12小时 |
||||||
|
$cache_key = BuildCacheKeyName::getUserMedicalInsuranceAuthKey($this->open_id, $patient_id); |
||||||
|
Redis::setex($cache_key, 12 * 60 * 60 - 500, json_encode($user_info, JSON_UNESCAPED_UNICODE)); |
||||||
|
return $user_info; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 待缴费处方预结算 |
||||||
|
* @param string $patient_number |
||||||
|
* @param string $serial_no |
||||||
|
* @param SettleType $settle_type |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function pendingPrescriptionPreSettle(string $patient_number, string $serial_no, SettleType $settle_type): mixed |
||||||
|
{ |
||||||
|
// 患者信息 |
||||||
|
$patient_info = $this->getPatientInfo($this->open_id, $patient_number); |
||||||
|
$patient_id = &$patient_info['patientId']; |
||||||
|
|
||||||
|
// 医保待缴费信息 |
||||||
|
$medical_pending_lists = $this->getMedicalPendingPaymentDetails($patient_number, $serial_no); |
||||||
|
|
||||||
|
// 获取缓存里的结算信息 |
||||||
|
$settle_cache_key = BuildCacheKeyName::getOutpatientPrescriptionPreSettleInfoKey($this->open_id, $serial_no, $settle_type->value); |
||||||
|
if (Redis::exists($settle_cache_key)) { |
||||||
|
$pre_settle_info = Redis::get($settle_cache_key); |
||||||
|
$pre_settle_info = json_decode($pre_settle_info, true); |
||||||
|
|
||||||
|
$this->info('缴费预结算返回数据包(cache)', $pre_settle_info); |
||||||
|
return $pre_settle_info; |
||||||
|
} |
||||||
|
|
||||||
|
// 获取患者授权信息 |
||||||
|
$auth_info = $this->getCacheUserMedicalInsuranceAuthInfo($patient_id); |
||||||
|
// 用户经纬度 |
||||||
|
$user_lng_lat = $auth_info['user_longitude_latitude']['longitude']. ','. $auth_info['user_longitude_latitude']['latitude']; |
||||||
|
|
||||||
|
$fee_record = []; |
||||||
|
foreach ($medical_pending_lists['FeeRecords'] as $v) { |
||||||
|
$fee_record[] = [ |
||||||
|
'FeeNo' => $v['FeeNo'], |
||||||
|
'FeeTypeCode' => $v['FeeTypeCode'], |
||||||
|
]; |
||||||
|
} |
||||||
|
$this->info('缴费预结算费用处方集合', $fee_record); |
||||||
|
|
||||||
|
// 预结算 |
||||||
|
$pre_settle_info = $this->prescriptionPreSettle($patient_number, $patient_info['name'], $auth_info['pay_auth_no'], $user_lng_lat, $settle_type, $fee_record); |
||||||
|
|
||||||
|
// 预结算结果 |
||||||
|
$pre_settle_info = reset($pre_settle_info); |
||||||
|
$pre_settle_info['PreSettleAt'] = date('Y-m-d H:i:s');// 预结算时间 |
||||||
|
$this->info('缴费预结算返回数据包', $pre_settle_info); |
||||||
|
|
||||||
|
// 缓存2小时 |
||||||
|
Redis::setex($settle_cache_key, 7200, json_encode($pre_settle_info, JSON_UNESCAPED_UNICODE)); |
||||||
|
|
||||||
|
return $pre_settle_info; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 医保支付 |
||||||
|
* @param string $patient_number |
||||||
|
* @param string $serial_no |
||||||
|
* @param SettleType $settle_type |
||||||
|
* @param OrderPayType $pay_type |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function medicalPayment(string $patient_number, string $serial_no, SettleType $settle_type, OrderPayType $pay_type = OrderPayType::MEDICAL_INSURANCE_PAY): mixed |
||||||
|
{ |
||||||
|
// 患者数据 |
||||||
|
$patient_info = $this->getPatientInfo($this->open_id, $patient_number); |
||||||
|
// 医保待缴费单数据 |
||||||
|
$medical_pending_info = $this->getMedicalPendingPaymentDetails($patient_number, $serial_no); |
||||||
|
// 用户授权数据 |
||||||
|
$auth_info = $this->getCacheUserMedicalInsuranceAuthInfo($patient_info['patientId']); |
||||||
|
// 预结算数据 |
||||||
|
$pre_settle_info = $this->getPrescriptionPreSettleInfo($serial_no, $settle_type); |
||||||
|
|
||||||
|
// 创建订单数据 |
||||||
|
$order_type = Type::OUTPATIENT_PAYMENT; |
||||||
|
$order_id = $this->order_model->getOrderId($pay_type, 'M'); |
||||||
|
$total_fee = (float)(string) $pre_settle_info['TotalAmount']; |
||||||
|
$reduce_fee = (float)(string) $pre_settle_info['MedicareAmount']; |
||||||
|
$self_fee = (float)(string) ($settle_type === SettleType::MEDICARE_ACCOUNT ? $pre_settle_info['AccountAmount'] : $pre_settle_info['PayAmount']); |
||||||
|
|
||||||
|
// 创建订单 |
||||||
|
$order = $this->createMedicalOrder($order_id, $pay_type, $total_fee, $reduce_fee, $order_type, $patient_info, $medical_pending_info, $pre_settle_info); |
||||||
|
|
||||||
|
// 申请医保支付 |
||||||
|
$pay_data = $this->applyMedicalPayment($order_type, $order_id, $total_fee, $reduce_fee, $self_fee, $patient_info, $auth_info, $pre_settle_info); |
||||||
|
|
||||||
|
// 去除支付无用数据 |
||||||
|
unset($pay_data['merchantId'], $pay_data['merchantName'], $pay_data['channelId'], $pay_data['channelName']); |
||||||
|
|
||||||
|
return $pay_data; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取患者信息 |
||||||
|
* @param string $open_id |
||||||
|
* @param string $patient_number |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function getPatientInfo(string $open_id, string $patient_number): mixed |
||||||
|
{ |
||||||
|
$info = $this->patient_model->getBindPatientInfoByPatientNumber($open_id, $patient_number); |
||||||
|
if (empty($info)) { |
||||||
|
throw new GeneralException('找不到患者信息,请重新再试!', Response::HTTP_BAD_REQUEST); |
||||||
|
} |
||||||
|
|
||||||
|
$response = $this->his_client->getPatientInfo($info['patient_number'], CardType::OUTPATIENT_NO, ''); |
||||||
|
if (!isset($response['success']) || !$response['success']) { |
||||||
|
throw new GeneralException($response['msg'] ?? '找不到患者信息,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
// 添加Patient 表ID |
||||||
|
$patient_info = &$response['response']; |
||||||
|
$patient_info['id'] = $info['id']; |
||||||
|
|
||||||
|
$this->info('获取缴费患者信息', $patient_info); |
||||||
|
return $patient_info; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取待缴费医保处方信息 |
||||||
|
* @param string $patient_number |
||||||
|
* @param string $record_id |
||||||
|
* @return array|mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function getMedicalPendingPaymentDetails(string $patient_number, string $record_id): mixed |
||||||
|
{ |
||||||
|
$cache_key = BuildCacheKeyName::getOutpatientMedicalPendingDetailsKey($this->open_id, $patient_number, $record_id); |
||||||
|
|
||||||
|
// 先获取缓存里的医保处方数据 |
||||||
|
if (Redis::exists($cache_key)) { |
||||||
|
$info = Redis::get($cache_key); |
||||||
|
$info = json_decode($info, true); |
||||||
|
|
||||||
|
$this->info('获取医保待缴费处方信息(cache)', $info); |
||||||
|
return $info; |
||||||
|
} |
||||||
|
|
||||||
|
$start_date = date('Y-m-d', strtotime('-7 days')); |
||||||
|
$end_date = date('Y-m-d'); |
||||||
|
$response = $this->his_medical_client->getMedicalPendingLists($patient_number, $start_date, $end_date); |
||||||
|
|
||||||
|
if(empty($response) || empty($response['Result'])) { |
||||||
|
throw new GeneralException('暂无相关缴费记录!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
if (!isset($response['ResultCode']) || $response['ResultCode'] !== '1') { |
||||||
|
throw new GeneralException($response['ErrorMsg'] ?: '暂无相关缴费记录!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
$info = []; |
||||||
|
foreach ($response['Result'] as $k => $v) { |
||||||
|
if ($v['DiagnosisRecordId'] === $record_id) { |
||||||
|
$info = $v; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (empty($info)) { |
||||||
|
throw new GeneralException('暂无相关缴费记录!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
$this->info('获取医保待缴费处方信息', $info); |
||||||
|
|
||||||
|
// 缓存2小时 |
||||||
|
Redis::setex($cache_key, 7200, json_encode($info, JSON_UNESCAPED_UNICODE)); |
||||||
|
return $info; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 处方预结算 |
||||||
|
* @param string $patient_number |
||||||
|
* @param string $patient_name |
||||||
|
* @param string $pay_auth_no |
||||||
|
* @param string $user_lng_lat |
||||||
|
* @param SettleType $settle_type |
||||||
|
* @param array $fee_record |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function prescriptionPreSettle(string $patient_number, string $patient_name, string $pay_auth_no, string $user_lng_lat, SettleType $settle_type, array $fee_record): mixed |
||||||
|
{ |
||||||
|
try { |
||||||
|
// 缴费预结算 |
||||||
|
$response = $this->his_medical_client->outpatientPreSettle( |
||||||
|
$patient_number, |
||||||
|
$patient_name, |
||||||
|
$pay_auth_no, |
||||||
|
$user_lng_lat, |
||||||
|
$settle_type, |
||||||
|
MedicalPayType::ONLINE_MEDICARE, |
||||||
|
$fee_record |
||||||
|
); |
||||||
|
$this->info('缴费预结算返回结果', $response); |
||||||
|
|
||||||
|
} catch (\Exception $e) { |
||||||
|
$err_msg = $e->getMessage(). ' On '. $e->getFile(). ':'. $e->getLine(); |
||||||
|
$this->error('缴费预结算异常, 异常消息:'. $err_msg); |
||||||
|
|
||||||
|
throw new GeneralException('缴费预结算失败,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
// 返回 false / 没有返回预结算费用信息 |
||||||
|
if(empty($response) || empty($response['Result'])) { |
||||||
|
throw new GeneralException('缴费预结算失败,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
if (!isset($response['ResultCode']) || $response['ResultCode'] !== '1') { |
||||||
|
throw new GeneralException($response['ErrorMsg'] ?: '缴费预结算遇到未知错误,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
return $response['Result']; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取缓存里的用户医保授权信息 |
||||||
|
* @param string $patient_id |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function getCacheUserMedicalInsuranceAuthInfo(string $patient_id): mixed |
||||||
|
{ |
||||||
|
$cache_key = BuildCacheKeyName::getUserMedicalInsuranceAuthKey($this->open_id, $patient_id); |
||||||
|
|
||||||
|
// 先获取缓存里的医保处方数据 |
||||||
|
if (!Redis::exists($cache_key)) { |
||||||
|
throw new GeneralException('请先进行医保授权!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
$auth_info = Redis::get($cache_key); |
||||||
|
$auth_info = json_decode($auth_info, true); |
||||||
|
|
||||||
|
$this->info('用户医保授权信息数据包(cache)', $auth_info); |
||||||
|
return $auth_info; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取待缴费处方预结算信息 |
||||||
|
* @param string $serial_no |
||||||
|
* @param SettleType $settle_type |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function getPrescriptionPreSettleInfo(string $serial_no, SettleType $settle_type): mixed |
||||||
|
{ |
||||||
|
$settle_cache_key = BuildCacheKeyName::getOutpatientPrescriptionPreSettleInfoKey($this->open_id, $serial_no, $settle_type->value); |
||||||
|
|
||||||
|
if (!Redis::exists($settle_cache_key)) { |
||||||
|
throw new GeneralException('缴费预结算信息已过期,请重新再试!'); |
||||||
|
} |
||||||
|
|
||||||
|
$pre_settle_info = Redis::get($settle_cache_key); |
||||||
|
$pre_settle_info = json_decode($pre_settle_info, true); |
||||||
|
|
||||||
|
$this->info('缴费预结算返回数据包(cache)', $pre_settle_info); |
||||||
|
return $pre_settle_info; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 创建医保缴费订单表 |
||||||
|
* @param string $order_id |
||||||
|
* @param OrderPayType $pay_type |
||||||
|
* @param float $total_fee |
||||||
|
* @param float $reduce_fee |
||||||
|
* @param Type $order_type |
||||||
|
* @param array $patient_info |
||||||
|
* @param array $medical_pending_info |
||||||
|
* @param array $pre_settle_info |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function createMedicalOrder(string $order_id, OrderPayType $pay_type, float $total_fee, float $reduce_fee, Type $order_type, array $patient_info, array $medical_pending_info, array $pre_settle_info): mixed |
||||||
|
{ |
||||||
|
// 缴费记录表 |
||||||
|
$pay_record_data = [ |
||||||
|
'relate_patient_id' => $patient_info['id'], |
||||||
|
'dept_id' => $medical_pending_info['DeptId'], |
||||||
|
'dept_name' => $medical_pending_info['DeptName'], |
||||||
|
'doctor_id' => $medical_pending_info['DoctorId'], |
||||||
|
'doctor_name' => $medical_pending_info['DoctorName'], |
||||||
|
'visit_date' => date('Y-m-d', strtotime($medical_pending_info['DiagnosisDate'])), |
||||||
|
'total_amount' => $total_fee, |
||||||
|
'pre_settle_status' => PreSettleStatus::SETTLED->value, |
||||||
|
'pre_settle_at' => $pre_settle_info['PreSettleAt'], |
||||||
|
'extra_info' => json_encode([ |
||||||
|
... $medical_pending_info, |
||||||
|
'pre_settle_info' => $pre_settle_info, |
||||||
|
], JSON_UNESCAPED_UNICODE) |
||||||
|
]; |
||||||
|
|
||||||
|
$order = $this->order_model->createOrder( |
||||||
|
$patient_info['id'], |
||||||
|
$order_id, |
||||||
|
$pay_type, |
||||||
|
$total_fee * 100, |
||||||
|
$reduce_fee * 100, |
||||||
|
$this->open_id, |
||||||
|
$patient_info['patientId'], |
||||||
|
$patient_info['name'], |
||||||
|
$order_type, |
||||||
|
SourceId::MINI_PROGRAM, |
||||||
|
$pay_record_data |
||||||
|
); |
||||||
|
if (empty($order)) { |
||||||
|
throw new GeneralException('创建医保缴费单失败,请重新再试!'); |
||||||
|
} |
||||||
|
|
||||||
|
$this->info('创建国标医保订单订单,ID:'. $order->id); |
||||||
|
return $order; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 申请医保支付 |
||||||
|
* @param Type $order_type 订单类型 |
||||||
|
* @param string $order_id 订单号 |
||||||
|
* @param float $total_fee 总金额 |
||||||
|
* @param float $reduce_fee 减免金额 |
||||||
|
* @param float $self_fee 自费金额 |
||||||
|
* @param array $patient_info 患者信息 |
||||||
|
* @param array $auth_info 用户授权信息 |
||||||
|
* @param array $pre_settle_info 预结算信息 |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function applyMedicalPayment(Type $order_type, string $order_id, float $total_fee, float $reduce_fee, float $self_fee, array $patient_info, array $auth_info, array $pre_settle_info): mixed |
||||||
|
{ |
||||||
|
// 患者信息 |
||||||
|
$patient_id = &$patient_info['patient_id']; |
||||||
|
$patient_name = &$patient_info['name']; |
||||||
|
$id_card_no = &$patient_info['idCardNo']; |
||||||
|
// $open_id = &$this->open_id; |
||||||
|
$open_id = 'ozOD16zPgRmjdh_O3VvtFtKaiLP0'; |
||||||
|
|
||||||
|
// 医保相关参数判断 |
||||||
|
// 医保部分扣费类型 0:统筹+个账 1:个账 2:统筹 |
||||||
|
$consume_type = $reduce_fee > 0 ? 2 : 0; |
||||||
|
// 支付方式 1:现金 2:医保 3:现金+医保; cash_fee>0, paytype填3; cash_fee=0, paytype填2 |
||||||
|
$pay_type = $reduce_fee <= 0 ? 2 : 3; |
||||||
|
|
||||||
|
// 证件号码类型 |
||||||
|
$card_type = getIDCardType($id_card_no); |
||||||
|
switch ($card_type) { |
||||||
|
default: |
||||||
|
case 1: |
||||||
|
$user_card_type = 1; |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
$user_card_type = match (true) { |
||||||
|
preg_match('/^HKG\d{9}$/', $id_card_no) => 6, |
||||||
|
preg_match('/^MAC\d{9}$/', $id_card_no) => 4, |
||||||
|
preg_match('/^TWN\d{9}$/', $id_card_no) => 5, |
||||||
|
}; |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
case 4: |
||||||
|
$user_card_type = 7; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
$order_obj = new CreateMedicalInsuranceOrder( |
||||||
|
orderType: $order_type->unifyOrderType(), |
||||||
|
attach: $patient_id. '|'. $patient_name, |
||||||
|
insuranceOrderType: OrderType::DIAG_PAY->value, |
||||||
|
orderNo: $order_id, |
||||||
|
hospitalName: '珠海市香洲区第二人民医院', |
||||||
|
totalAmount: (string) $total_fee, |
||||||
|
cashAmount: (string) $self_fee, |
||||||
|
allowFeeChange: 0, |
||||||
|
spbillCreateIp: request()->ip(), |
||||||
|
openid: $open_id, |
||||||
|
notifyUrl: config('custom.payment_notify_url'), |
||||||
|
title: $order_type->label(), |
||||||
|
payType: (string) $pay_type, |
||||||
|
cityCode: config('wechat.medical.auth.city_code'), |
||||||
|
consumeType: $consume_type, |
||||||
|
insuranceAmount: (string) $reduce_fee, |
||||||
|
userCardType: $user_card_type, |
||||||
|
idCardNo: md5(strtoupper($id_card_no)), |
||||||
|
name: $patient_name, |
||||||
|
serialNo: $pre_settle_info['MedOrgOrd'], |
||||||
|
orgNo: config('wechat.medical.auth.org_codg'), |
||||||
|
requestContent: [ |
||||||
|
'payAuthNo' => $auth_info['pay_auth_no'], |
||||||
|
'payOrdId' => $pre_settle_info['PayOrdId'], |
||||||
|
'setlLatlnt' => $auth_info['user_longitude_latitude']['latitude']. ','. $auth_info['user_longitude_latitude']['longitude'], |
||||||
|
], |
||||||
|
channelNo: config('wechat.medical.auth.channel') |
||||||
|
); |
||||||
|
|
||||||
|
$response = Unify::pay(config('unifyTest'))->mini->medicalPay($order_obj); |
||||||
|
$this->info('medical Pay 支付参数', $response); |
||||||
|
|
||||||
|
if (!$response['success'] || empty($response['response'])) { |
||||||
|
throw new GeneralException('申请医保支付失败,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
return $response['response']; |
||||||
|
} catch (InvalidConfigException|RuntimeException|ReflectionException $e) { |
||||||
|
throw new GeneralException('申请医保支付失败,请重新再试!', Response::HTTP_INTERNAL_SERVER_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,504 @@ |
|||||||
|
<?php |
||||||
|
declare(strict_types = 1); |
||||||
|
|
||||||
|
namespace App\Http\Logics\Registration; |
||||||
|
|
||||||
|
use App\Dictionary\Medical\OrderType; |
||||||
|
use App\Dictionary\Medical\PatientProperty; |
||||||
|
use App\Dictionary\Medical\PayType as MedicalPayType; |
||||||
|
use App\Dictionary\Order\PayType; |
||||||
|
use App\Dictionary\Order\PayType as OrderPayType; |
||||||
|
use App\Dictionary\Medical\PreSettleStatus; |
||||||
|
use App\Dictionary\Medical\SettleType; |
||||||
|
use App\Dictionary\Order\SourceId; |
||||||
|
use App\Dictionary\Order\Type; |
||||||
|
use App\Dictionary\Patient\CardType; |
||||||
|
use App\Exceptions\GeneralException; |
||||||
|
use App\Models\Admin\Department; |
||||||
|
use App\Models\Order; |
||||||
|
use App\Models\Patient; |
||||||
|
use App\Services\HisHttp\Client as HisHttpClient; |
||||||
|
use App\Services\HisMedicalHttp\Client as HisMedicalHttpClient; |
||||||
|
use App\Services\MedicalAuth\Client as AuthClient; |
||||||
|
use App\Utils\Statics\BuildCacheKeyName; |
||||||
|
use App\Utils\Traits\Logger; |
||||||
|
use App\Utils\Traits\MiniProgramAuth; |
||||||
|
use Illuminate\Auth\AuthenticationException; |
||||||
|
use Illuminate\Support\Facades\Redis; |
||||||
|
use ReflectionException; |
||||||
|
use Symfony\Component\HttpFoundation\Response; |
||||||
|
use UnifyPayment\Cores\Exceptions\InvalidConfigException; |
||||||
|
use UnifyPayment\Cores\Exceptions\RuntimeException; |
||||||
|
use UnifyPayment\Cores\Struct\CreateMedicalInsuranceOrder; |
||||||
|
use UnifyPayment\Unify; |
||||||
|
|
||||||
|
class MedicalLogic |
||||||
|
{ |
||||||
|
use Logger; |
||||||
|
use MiniProgramAuth; |
||||||
|
|
||||||
|
private HisHttpClient $his_client; |
||||||
|
|
||||||
|
private HisMedicalHttpClient $his_medical_client; |
||||||
|
|
||||||
|
private AuthClient $auth_client; |
||||||
|
|
||||||
|
private Order $order_model; |
||||||
|
|
||||||
|
private Patient $patient_model; |
||||||
|
|
||||||
|
/** |
||||||
|
* PaymentLogic Construct |
||||||
|
* @throws AuthenticationException |
||||||
|
*/ |
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$this->authInitialize(); |
||||||
|
$this->setChannel('registration'); |
||||||
|
$this->his_client = app('HisHttpService'); |
||||||
|
$this->his_medical_client = app('HisMedicalHttpClient'); |
||||||
|
$this->auth_client = new AuthClient(); |
||||||
|
$this->order_model = new Order(); |
||||||
|
$this->patient_model = new Patient(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 挂号预结算 |
||||||
|
* @param string $patient_number |
||||||
|
* @param string $date |
||||||
|
* @param string $dept_id |
||||||
|
* @param string $doctor_id |
||||||
|
* @param string $reg_id |
||||||
|
* @param PatientProperty $patient_property |
||||||
|
* @param SettleType $settle_type |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function registerPreSettle(string $patient_number, string $date, string $dept_id, string $doctor_id, string $reg_id, PatientProperty $patient_property, SettleType $settle_type): mixed |
||||||
|
{ |
||||||
|
// 患者信息 |
||||||
|
$patient_info = $this->getPatientInfo($this->open_id, $patient_number); |
||||||
|
$patient_id = &$patient_info['patientId']; |
||||||
|
|
||||||
|
// 获取挂号号源信息 |
||||||
|
$schedule_info = $this->getRegisterScheduleDetails($date, $dept_id, $doctor_id, $reg_id); |
||||||
|
|
||||||
|
// 获取缓存里的结算信息 |
||||||
|
$settle_cache_key = BuildCacheKeyName::getRegistrationPreSettleInfoKey($this->open_id, $reg_id, $patient_property->value, $settle_type->value); |
||||||
|
if (Redis::exists($settle_cache_key)) { |
||||||
|
$pre_settle_info = Redis::get($settle_cache_key); |
||||||
|
$pre_settle_info = json_decode($pre_settle_info, true); |
||||||
|
|
||||||
|
$this->info('挂号预结算返回数据包(cache)', $pre_settle_info); |
||||||
|
return $pre_settle_info; |
||||||
|
} |
||||||
|
|
||||||
|
// 获取患者授权信息 |
||||||
|
$auth_info = $this->getCacheUserMedicalInsuranceAuthInfo($patient_id); |
||||||
|
// 用户经纬度 |
||||||
|
$user_lng_lat = $auth_info['user_longitude_latitude']['longitude']. ','. $auth_info['user_longitude_latitude']['latitude']; |
||||||
|
|
||||||
|
// 预结算 |
||||||
|
$pre_settle_info = $this->registrationPreSettle( |
||||||
|
$patient_number, |
||||||
|
$patient_info['name'], |
||||||
|
$schedule_info['scheduleInfo']['feeRecord'], |
||||||
|
$schedule_info['scheduleInfo']['feeRecord'], |
||||||
|
$reg_id, |
||||||
|
$patient_property, |
||||||
|
$auth_info['pay_auth_no'], |
||||||
|
$user_lng_lat |
||||||
|
); |
||||||
|
|
||||||
|
// 预结算结果 |
||||||
|
$pre_settle_info = reset($pre_settle_info); |
||||||
|
$pre_settle_info['PreSettleAt'] = date('Y-m-d H:i:s');// 预结算时间 |
||||||
|
$this->info('挂号预结算返回数据包', $pre_settle_info); |
||||||
|
|
||||||
|
// 缓存2小时 |
||||||
|
Redis::setex($settle_cache_key, 7200, json_encode($pre_settle_info, JSON_UNESCAPED_UNICODE)); |
||||||
|
|
||||||
|
return $pre_settle_info; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 医保支付 |
||||||
|
* @param string $patient_number |
||||||
|
* @param string $date |
||||||
|
* @param string $dept_id |
||||||
|
* @param string $doctor_id |
||||||
|
* @param string $reg_id |
||||||
|
* @param PatientProperty $patient_property |
||||||
|
* @param SettleType $settle_type |
||||||
|
* @param OrderPayType $pay_type |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function medicalRegister(string $patient_number, string $date, string $dept_id, string $doctor_id, string $reg_id, PatientProperty $patient_property, SettleType $settle_type, OrderPayType $pay_type = OrderPayType::MEDICAL_INSURANCE_PAY): mixed |
||||||
|
{ |
||||||
|
// 患者数据 |
||||||
|
$patient_info = $this->getPatientInfo($this->open_id, $patient_number); |
||||||
|
// 挂号号源信息 |
||||||
|
$medical_pending_info = $this->getRegisterScheduleDetails($date, $dept_id, $doctor_id, $reg_id); |
||||||
|
// 用户授权数据 |
||||||
|
$auth_info = $this->getCacheUserMedicalInsuranceAuthInfo($patient_info['patientId']); |
||||||
|
// 预结算数据 |
||||||
|
$pre_settle_info = $this->getRegistrationPreSettleInfo($reg_id, $patient_property, $settle_type); |
||||||
|
|
||||||
|
// 创建订单数据 |
||||||
|
$order_type = $date === date('Y-m-d') ? Type::TODAY_REGISTRATION : Type::APPOINTMENT_REGISTRATION; |
||||||
|
$order_id = $this->order_model->getOrderId($pay_type, 'M'); |
||||||
|
$total_fee = (float)(string) $pre_settle_info['TotalAmount']; |
||||||
|
$reduce_fee = (float)(string) $pre_settle_info['MedicareAmount']; |
||||||
|
$self_fee = (float)(string) ($settle_type === SettleType::MEDICARE_ACCOUNT ? $pre_settle_info['AccountAmount'] : $pre_settle_info['PayAmount']); |
||||||
|
|
||||||
|
// 创建订单 |
||||||
|
$order = $this->createMedicalOrder($order_id, $pay_type, $total_fee, $reduce_fee, $order_type, $patient_info, $medical_pending_info, $pre_settle_info); |
||||||
|
|
||||||
|
// 申请医保支付 |
||||||
|
$pay_data = $this->applyMedicalPayment($order_type, $order_id, $total_fee, $reduce_fee, $self_fee, $patient_info, $auth_info, $pre_settle_info); |
||||||
|
|
||||||
|
// 去除支付无用数据 |
||||||
|
unset($pay_data['merchantId'], $pay_data['merchantName'], $pay_data['channelId'], $pay_data['channelName']); |
||||||
|
|
||||||
|
return $pay_data; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取患者信息 |
||||||
|
* @param string $open_id |
||||||
|
* @param string $patient_number |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function getPatientInfo(string $open_id, string $patient_number): mixed |
||||||
|
{ |
||||||
|
$info = $this->patient_model->getBindPatientInfoByPatientNumber($open_id, $patient_number); |
||||||
|
if (empty($info)) { |
||||||
|
throw new GeneralException('找不到患者信息,请重新再试!', Response::HTTP_BAD_REQUEST); |
||||||
|
} |
||||||
|
|
||||||
|
$response = $this->his_client->getPatientInfo($info['patient_number'], CardType::OUTPATIENT_NO, ''); |
||||||
|
if (!isset($response['success']) || !$response['success']) { |
||||||
|
throw new GeneralException($response['msg'] ?? '找不到患者信息,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
// 添加Patient 表ID |
||||||
|
$patient_info = &$response['response']; |
||||||
|
$patient_info['id'] = $info['id']; |
||||||
|
|
||||||
|
$this->info('获取缴费患者信息', $patient_info); |
||||||
|
return $patient_info; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取挂号信息(实时,不取缓存) |
||||||
|
* @param string $date |
||||||
|
* @param string $dept_id |
||||||
|
* @param string $doctor_id |
||||||
|
* @param string $reg_id |
||||||
|
* @return array |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function getRegisterScheduleDetails(string $date, string $dept_id, string $doctor_id, string $reg_id): array |
||||||
|
{ |
||||||
|
// 获取排班医生信息 |
||||||
|
$is_today = $dept_id === date('Y-m-d') ? '3' : '1'; |
||||||
|
$schedule_info = $this->his_client->getDoctorLists($dept_id, $is_today, '', $date); |
||||||
|
if (!isset($schedule_info['success']) || !$schedule_info['success']) { |
||||||
|
throw new GeneralException($schedule_info['msg'] ?? '找不到该号源,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
// 获取号源信息 |
||||||
|
foreach ($schedule_info['response'] as $v) { |
||||||
|
if ($v['doctId'] === $doctor_id) { |
||||||
|
foreach ($v['doctotVisitInfoList'] as $v2) { |
||||||
|
if ($v2['regId'] === $reg_id && $v2['visitDate'] === $date) { |
||||||
|
$v['scheduleInfo'] = $v2; |
||||||
|
unset($v['doctotVisitInfoList']); |
||||||
|
$info = $v; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (empty($info)) { |
||||||
|
throw new GeneralException('找不到该号源,请重新再试!', Response::HTTP_BAD_REQUEST); |
||||||
|
} |
||||||
|
|
||||||
|
if (!isset($info['scheduleInfo']['regCount']) || $info['scheduleInfo']['regCount'] <= 0) { |
||||||
|
throw new GeneralException('该号源已挂完,请重新选择号源!', Response::HTTP_BAD_REQUEST); |
||||||
|
} |
||||||
|
|
||||||
|
// 获取科室名称 |
||||||
|
/*if (Redis::exists('departments.'. $date)) { |
||||||
|
$dept_lists = Redis::get('departments.'. $date); |
||||||
|
$dept_lists = json_decode($dept_lists, true); |
||||||
|
} else { |
||||||
|
$dept_lists = $this->his_client->getDepType('', '','01', $date); |
||||||
|
|
||||||
|
if (!isset($dept_lists['success']) || !$dept_lists['success']) { |
||||||
|
throw new GeneralException($schedule_info['msg'] ?? '找不到该号源,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
foreach ($dept_lists['response'] as $v) { |
||||||
|
if ($v['typeId'] === $dept_id) { |
||||||
|
$info['dept_id'] = $v['typeId']; |
||||||
|
$info['dept_name'] = $v['typeName']; |
||||||
|
} |
||||||
|
}*/ |
||||||
|
|
||||||
|
// 获取科室名称 |
||||||
|
$department_info = Department::where('dept_id', $dept_id)->where('is_enable', 1)->first(); |
||||||
|
if (!$department_info){ |
||||||
|
throw new GeneralException($schedule_info['msg'] ?? '找不到该号源,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
$info['dept_id'] = $department_info['dept_id']; |
||||||
|
$info['dept_name'] = $department_info['dept_name']; |
||||||
|
|
||||||
|
$this->info('挂号排班信息', $info); |
||||||
|
return $info; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 挂号预结算 |
||||||
|
* @param string $patient_number |
||||||
|
* @param string $patient_name |
||||||
|
* @param string $total_fee |
||||||
|
* @param string $fee_code |
||||||
|
* @param string $reg_id |
||||||
|
* @param PatientProperty $patient_property |
||||||
|
* @param string $pay_auth_no |
||||||
|
* @param string $user_lng_lat |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function registrationPreSettle(string $patient_number, string $patient_name, string $total_fee, string $fee_code, string $reg_id, PatientProperty $patient_property, string $pay_auth_no, string $user_lng_lat): mixed |
||||||
|
{ |
||||||
|
try { |
||||||
|
// 挂号预结算 |
||||||
|
$response = $this->his_medical_client->registrationPreSettle( |
||||||
|
$patient_number, |
||||||
|
$patient_name, |
||||||
|
$total_fee, |
||||||
|
$fee_code, |
||||||
|
$reg_id, |
||||||
|
$patient_property, |
||||||
|
$pay_auth_no, |
||||||
|
$user_lng_lat, |
||||||
|
); |
||||||
|
$this->info('挂号预结算返回结果', $response); |
||||||
|
|
||||||
|
} catch (\Exception $e) { |
||||||
|
$err_msg = $e->getMessage(). ' On '. $e->getFile(). ':'. $e->getLine(); |
||||||
|
$this->error('挂号预结算异常, 异常消息:'. $err_msg); |
||||||
|
|
||||||
|
throw new GeneralException('挂号预结算失败,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
// 返回 false / 没有返回预结算费用信息 |
||||||
|
if(empty($response) || empty($response['Result'])) { |
||||||
|
throw new GeneralException('挂号预结算失败,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
if (!isset($response['ResultCode']) || $response['ResultCode'] !== '1') { |
||||||
|
throw new GeneralException($response['ErrorMsg'] ?: '挂号预结算遇到未知错误,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
return $response['Result']; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取缓存里的用户医保授权信息 |
||||||
|
* @param string $patient_id |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function getCacheUserMedicalInsuranceAuthInfo(string $patient_id): mixed |
||||||
|
{ |
||||||
|
$cache_key = BuildCacheKeyName::getUserMedicalInsuranceAuthKey($this->open_id, $patient_id); |
||||||
|
|
||||||
|
// 先获取缓存里的医保处方数据 |
||||||
|
if (!Redis::exists($cache_key)) { |
||||||
|
throw new GeneralException('请先进行医保授权!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
$auth_info = Redis::get($cache_key); |
||||||
|
$auth_info = json_decode($auth_info, true); |
||||||
|
|
||||||
|
$this->info('用户医保授权信息数据包(cache)', $auth_info); |
||||||
|
return $auth_info; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取挂号预结算信息 |
||||||
|
* @param string $reg_id |
||||||
|
* @param PatientProperty $patient_property |
||||||
|
* @param SettleType $settle_type |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function getRegistrationPreSettleInfo(string $reg_id, PatientProperty $patient_property, SettleType $settle_type): mixed |
||||||
|
{ |
||||||
|
$settle_cache_key = BuildCacheKeyName::getRegistrationPreSettleInfoKey($this->open_id, $reg_id, $patient_property->value, $settle_type->value); |
||||||
|
|
||||||
|
if (!Redis::exists($settle_cache_key)) { |
||||||
|
throw new GeneralException('挂号预结算信息已过期,请重新再试!'); |
||||||
|
} |
||||||
|
|
||||||
|
$pre_settle_info = Redis::get($settle_cache_key); |
||||||
|
$pre_settle_info = json_decode($pre_settle_info, true); |
||||||
|
|
||||||
|
$this->info('挂号预结算返回数据包(cache)', $pre_settle_info); |
||||||
|
return $pre_settle_info; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建医保挂号订单表 |
||||||
|
* @param string $order_id |
||||||
|
* @param OrderPayType $pay_type |
||||||
|
* @param float $total_fee |
||||||
|
* @param float $reduce_fee |
||||||
|
* @param Type $order_type |
||||||
|
* @param array $patient_info |
||||||
|
* @param array $schedule_info |
||||||
|
* @param array $pre_settle_info |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function createMedicalOrder(string $order_id, PayType $pay_type, float $total_fee, float $reduce_fee, Type $order_type, array $patient_info, array $schedule_info, array $pre_settle_info): mixed |
||||||
|
{ |
||||||
|
// 挂号记录表 |
||||||
|
$reg_record_data = [ |
||||||
|
'relate_patient_id' => $patient_info['id'], |
||||||
|
'reg_id' => $schedule_info['scheduleInfo']['regId'], |
||||||
|
'dept_id' => $schedule_info['dept_id'], |
||||||
|
'dept_name' => $schedule_info['dept_name'], |
||||||
|
'dept_location' => '', |
||||||
|
'doctor_id' => $schedule_info['doctId'], |
||||||
|
'doctor_name' => $schedule_info['doctName'], |
||||||
|
'visit_date' => date('Y-m-d', strtotime($schedule_info['scheduleInfo']['visitDate'])), |
||||||
|
'begin_time' => $schedule_info['scheduleInfo']['startTime'], |
||||||
|
'end_time' => $schedule_info['scheduleInfo']['endTime'], |
||||||
|
'lock_status' => 0, |
||||||
|
'extra_info' => json_encode([ |
||||||
|
... $schedule_info, |
||||||
|
'pre_settle_info' => $pre_settle_info, |
||||||
|
], JSON_UNESCAPED_UNICODE) |
||||||
|
]; |
||||||
|
|
||||||
|
$order = $this->order_model->createOrder( |
||||||
|
$patient_info['id'], |
||||||
|
$order_id, |
||||||
|
$pay_type, |
||||||
|
$total_fee * 100, |
||||||
|
$reduce_fee * 100, |
||||||
|
$this->open_id, |
||||||
|
$patient_info['patientId'], |
||||||
|
$patient_info['name'], |
||||||
|
$order_type, |
||||||
|
SourceId::MINI_PROGRAM, |
||||||
|
$reg_record_data |
||||||
|
); |
||||||
|
if (empty($order)) { |
||||||
|
throw new GeneralException('创建医保挂号单失败,请重新再试!'); |
||||||
|
} |
||||||
|
|
||||||
|
$this->info('创建国标医保订单订单,ID:'. $order->id); |
||||||
|
return $order; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 申请医保支付 |
||||||
|
* @param Type $order_type 订单类型 |
||||||
|
* @param string $order_id 订单号 |
||||||
|
* @param float $total_fee 总金额 |
||||||
|
* @param float $reduce_fee 减免金额 |
||||||
|
* @param float $self_fee 自费金额 |
||||||
|
* @param array $patient_info 患者信息 |
||||||
|
* @param array $auth_info 用户授权信息 |
||||||
|
* @param array $pre_settle_info 预结算信息 |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function applyMedicalPayment(Type $order_type, string $order_id, float $total_fee, float $reduce_fee, float $self_fee, array $patient_info, array $auth_info, array $pre_settle_info): mixed |
||||||
|
{ |
||||||
|
// 患者信息 |
||||||
|
$patient_id = &$patient_info['patient_id']; |
||||||
|
$patient_name = &$patient_info['name']; |
||||||
|
$id_card_no = &$patient_info['idCardNo']; |
||||||
|
// $open_id = &$this->open_id; |
||||||
|
$open_id = 'ozOD16zPgRmjdh_O3VvtFtKaiLP0'; |
||||||
|
|
||||||
|
// 医保相关参数判断 |
||||||
|
// 医保部分扣费类型 0:统筹+个账 1:个账 2:统筹 |
||||||
|
$consume_type = $reduce_fee > 0 ? 2 : 0; |
||||||
|
// 支付方式 1:现金 2:医保 3:现金+医保; cash_fee>0, paytype填3; cash_fee=0, paytype填2 |
||||||
|
$pay_type = $reduce_fee <= 0 ? 2 : 3; |
||||||
|
|
||||||
|
// 证件号码类型 |
||||||
|
$card_type = getIDCardType($id_card_no); |
||||||
|
switch ($card_type) { |
||||||
|
default: |
||||||
|
case 1: |
||||||
|
$user_card_type = 1; |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
$user_card_type = match (true) { |
||||||
|
preg_match('/^HKG\d{9}$/', $id_card_no) => 6, |
||||||
|
preg_match('/^MAC\d{9}$/', $id_card_no) => 4, |
||||||
|
preg_match('/^TWN\d{9}$/', $id_card_no) => 5, |
||||||
|
}; |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
case 4: |
||||||
|
$user_card_type = 7; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
$order_obj = new CreateMedicalInsuranceOrder( |
||||||
|
orderType: $order_type->unifyOrderType(), |
||||||
|
attach: $patient_id. '|'. $patient_name, |
||||||
|
insuranceOrderType: OrderType::DIAG_PAY->value, |
||||||
|
orderNo: $order_id, |
||||||
|
hospitalName: '珠海市香洲区第二人民医院', |
||||||
|
totalAmount: (string) $total_fee, |
||||||
|
cashAmount: (string) $self_fee, |
||||||
|
allowFeeChange: 0, |
||||||
|
spbillCreateIp: request()->ip(), |
||||||
|
openid: $open_id, |
||||||
|
notifyUrl: config('custom.payment_notify_url'), |
||||||
|
title: $order_type->label(), |
||||||
|
payType: (string) $pay_type, |
||||||
|
cityCode: config('wechat.medical.auth.city_code'), |
||||||
|
consumeType: $consume_type, |
||||||
|
insuranceAmount: (string) $reduce_fee, |
||||||
|
userCardType: $user_card_type, |
||||||
|
idCardNo: md5(strtoupper($id_card_no)), |
||||||
|
name: $patient_name, |
||||||
|
serialNo: $pre_settle_info['MedOrgOrd'], |
||||||
|
orgNo: config('wechat.medical.auth.org_codg'), |
||||||
|
requestContent: [ |
||||||
|
'payAuthNo' => $auth_info['pay_auth_no'], |
||||||
|
'payOrdId' => $pre_settle_info['PayOrdId'], |
||||||
|
'setlLatlnt' => $auth_info['user_longitude_latitude']['latitude']. ','. $auth_info['user_longitude_latitude']['longitude'], |
||||||
|
], |
||||||
|
channelNo: config('wechat.medical.auth.channel') |
||||||
|
); |
||||||
|
|
||||||
|
$response = Unify::pay(config('unifyTest'))->mini->medicalPay($order_obj); |
||||||
|
$this->info('medical Pay 支付参数', $response); |
||||||
|
|
||||||
|
if (!$response['success'] || empty($response['response'])) { |
||||||
|
throw new GeneralException('申请医保支付失败,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
return $response['response']; |
||||||
|
} catch (InvalidConfigException|RuntimeException|ReflectionException $e) { |
||||||
|
throw new GeneralException('申请医保支付失败,请重新再试!', Response::HTTP_INTERNAL_SERVER_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Http\Requests\Registration; |
||||||
|
|
||||||
|
use App\Dictionary\Medical\PatientProperty; |
||||||
|
use App\Dictionary\Medical\SettleType; |
||||||
|
use Illuminate\Contracts\Validation\ValidationRule; |
||||||
|
use Illuminate\Foundation\Http\FormRequest; |
||||||
|
use Illuminate\Validation\Rules\Enum; |
||||||
|
|
||||||
|
class RegisterPreSettleRequest extends FormRequest |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Determine if the user is authorized to make this request. |
||||||
|
*/ |
||||||
|
public function authorize(): bool |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the validation rules that apply to the request. |
||||||
|
* |
||||||
|
* @return array<string, ValidationRule|array|string> |
||||||
|
*/ |
||||||
|
/** |
||||||
|
* 规则 |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
public function rules(): array |
||||||
|
{ |
||||||
|
return [ |
||||||
|
'date' => 'required|date_format:Y-m-d||after_or_equal:today', |
||||||
|
'dept_id' => 'required', |
||||||
|
'doctor_id' => 'required', |
||||||
|
'reg_id' => 'required', |
||||||
|
'patient_property' => ['required', new Enum(PatientProperty::class)], |
||||||
|
'settle_type' => ['required', new Enum(SettleType::class)], |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 错误提示语句 |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
public function messages(): array |
||||||
|
{ |
||||||
|
return [ |
||||||
|
'date.required' => '必须选择挂号日期', |
||||||
|
'date.date_format' => '必须选择挂号日期', |
||||||
|
'date.after_or_equal' => '挂号日期不得小于今天', |
||||||
|
'dept_id.required' => '必须选择挂号科室', |
||||||
|
'doctor_id.required' => '必须选择挂号医生', |
||||||
|
'reg_id.required' => '必须选择挂号时间段', |
||||||
|
'patient_property.required' => '请选择病人性质', |
||||||
|
'patient_property.Illuminate\Validation\Rules\Enum' => '请选择正确的病人性质', |
||||||
|
'settle_type.required' => '请选择结算类型', |
||||||
|
'settle_type.Illuminate\Validation\Rules\Enum' => '请选择正确的结算类型', |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 字段名称 |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
public function attributes(): array |
||||||
|
{ |
||||||
|
return [ |
||||||
|
'date' => '挂号日期', |
||||||
|
'dept_id' => '挂号科室', |
||||||
|
'doctor_id' => '挂号医生', |
||||||
|
'reg_id' => '挂号时间段', |
||||||
|
'patient_property' => '病人性质', |
||||||
|
'settle_type' => '结算类型', |
||||||
|
]; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
<?php |
||||||
|
declare(strict_types = 1); |
||||||
|
|
||||||
|
namespace App\Http\Resources\Outpatient\Medical; |
||||||
|
|
||||||
|
use Illuminate\Http\Request; |
||||||
|
use Illuminate\Http\Resources\Json\JsonResource; |
||||||
|
|
||||||
|
class PrescriptionPreSettleResource extends JsonResource |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Transform the resource into an array. |
||||||
|
* |
||||||
|
* @return array<string, mixed> |
||||||
|
*/ |
||||||
|
public function toArray(Request $request = null): array |
||||||
|
{ |
||||||
|
if (empty($this->resource)) { |
||||||
|
return []; |
||||||
|
} |
||||||
|
|
||||||
|
return [ |
||||||
|
// 总金额 |
||||||
|
'total_amt' => (float) $this->resource['TotalAmount'], |
||||||
|
// 减免金额 |
||||||
|
'medical_amt' => (float) $this->resource['MedicareAmount'], |
||||||
|
// 现金金额 |
||||||
|
'pay_amt' => (float) $this->resource['PayAmount'], |
||||||
|
// 个人账户支付金额 |
||||||
|
'account_amt' => (float) $this->resource['AccountAmount'], |
||||||
|
// 其他优惠金额 |
||||||
|
'other_amt' => 0, |
||||||
|
// 加收费费用描述 |
||||||
|
'extra_fee_desc' => $this->resource['ExtraFeeDesc'] ?? '' |
||||||
|
]; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
<?php |
||||||
|
declare(strict_types = 1); |
||||||
|
|
||||||
|
namespace App\Http\Resources\Registration\Medical; |
||||||
|
|
||||||
|
use Illuminate\Http\Request; |
||||||
|
use Illuminate\Http\Resources\Json\JsonResource; |
||||||
|
|
||||||
|
class RegisterPreSettleResource extends JsonResource |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Transform the resource into an array. |
||||||
|
* |
||||||
|
* @return array<string, mixed> |
||||||
|
*/ |
||||||
|
public function toArray(Request $request = null): array |
||||||
|
{ |
||||||
|
if (empty($this->resource)) { |
||||||
|
return []; |
||||||
|
} |
||||||
|
|
||||||
|
return [ |
||||||
|
// 总金额 |
||||||
|
'total_amt' => (float) $this->resource['TotalAmount'], |
||||||
|
// 减免金额 |
||||||
|
'medical_amt' => (float) $this->resource['MedicareAmount'], |
||||||
|
// 现金金额 |
||||||
|
'pay_amt' => (float) $this->resource['PayAmount'], |
||||||
|
// 个人账户支付金额 |
||||||
|
'account_amt' => (float) $this->resource['AccountAmount'], |
||||||
|
// 其他优惠金额 |
||||||
|
'other_amt' => 0, |
||||||
|
// 加收费费用描述 |
||||||
|
'extra_fee_desc' => $this->resource['ExtraFeeDesc'] ?? '' |
||||||
|
]; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,293 @@ |
|||||||
|
<?php |
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Services\HisMedicalHttp; |
||||||
|
|
||||||
|
use App\Dictionary\Medical\PatientProperty; |
||||||
|
use App\Dictionary\Medical\PayType as MedicalPayType; |
||||||
|
use App\Dictionary\Medical\SettleType; |
||||||
|
use App\Exceptions\GeneralException; |
||||||
|
use App\Utils\Traits\Logger; |
||||||
|
use App\Utils\Transfer\HisMedicalHttpClient\ClientFactory; |
||||||
|
use App\Utils\Transfer\HttpTransferAbstract; |
||||||
|
use Exception; |
||||||
|
use Symfony\Component\HttpFoundation\Response; |
||||||
|
|
||||||
|
class Client |
||||||
|
{ |
||||||
|
use Logger; |
||||||
|
|
||||||
|
// his服务 |
||||||
|
private HttpTransferAbstract $service; |
||||||
|
|
||||||
|
/** |
||||||
|
* Client Construct |
||||||
|
*/ |
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$his_name = 'his_medical_http'; |
||||||
|
$this->service = ClientFactory::getClientTransfer($his_name); |
||||||
|
$this->setChannel($his_name); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 公共入参 |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
protected function commonRequestData(): array |
||||||
|
{ |
||||||
|
return []; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 请求 |
||||||
|
* @param string $method_name |
||||||
|
* @param string $request_name |
||||||
|
* @param array $params |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
protected function requestHandle(string $method_name, string $request_name, array $params): mixed |
||||||
|
{ |
||||||
|
try { |
||||||
|
return $this->service |
||||||
|
->transferMethod($method_name, $request_name, $params) |
||||||
|
->getResult(); |
||||||
|
|
||||||
|
} catch (Exception $e) { |
||||||
|
$err_msg = "{$e->getMessage()} ON {$e->getFile()}:{$e->getLine()}"; |
||||||
|
$this->error('调用api接口失败, 错误消息:' . $err_msg, $params); |
||||||
|
|
||||||
|
throw new GeneralException($e->getMessage(), Response::HTTP_SERVICE_UNAVAILABLE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 医保定点签约 |
||||||
|
* @param string $cert_type |
||||||
|
* @param string $cert_no |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function medicalDesignated(string $cert_type, string $cert_no): mixed |
||||||
|
{ |
||||||
|
// 调用请求处理方法 |
||||||
|
return $this->requestHandle('POST', 'HospRedesignated', [ |
||||||
|
'json' => [ |
||||||
|
'ApplicationId' => '', |
||||||
|
'ApplicationSecret' => '', |
||||||
|
'Parameter' => [ |
||||||
|
'mdtrt_cert_type' => $cert_type, |
||||||
|
'mdtrt_cert_no' => $cert_no |
||||||
|
] |
||||||
|
] |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 挂号预结算 |
||||||
|
* @param string $patient_number 门诊号码 |
||||||
|
* @param string $patient_name 患者名称 |
||||||
|
* @param string $total_fee 挂号总金额 |
||||||
|
* @param string $fee_code 费用代码 |
||||||
|
* @param string $reg_id 挂号号源ID |
||||||
|
* @param PatientProperty $patient_property 病人性质 |
||||||
|
* @param string $pay_auth_no 支付授权码 |
||||||
|
* @param string $lat_lnt 经纬度信息 |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function registrationPreSettle( |
||||||
|
string $patient_number, |
||||||
|
string $patient_name, |
||||||
|
string $total_fee, |
||||||
|
string $fee_code, |
||||||
|
string $reg_id, |
||||||
|
PatientProperty $patient_property, |
||||||
|
string $pay_auth_no, |
||||||
|
string $lat_lnt, |
||||||
|
): mixed |
||||||
|
{ |
||||||
|
// 调用请求处理方法 |
||||||
|
return $this->requestHandle('POST', 'GetPrecalculatedFeeGh', [ |
||||||
|
'json' => [ |
||||||
|
'ApplicationId' => '', |
||||||
|
'ApplicationSecret' => '', |
||||||
|
'Parameter' => [ |
||||||
|
'PatientId' => $patient_number, |
||||||
|
'PatientName' => $patient_name, |
||||||
|
'TotalFee' => $total_fee, |
||||||
|
'FeeCode' => $fee_code, |
||||||
|
'RegId' => $reg_id, |
||||||
|
'Brxz' => $patient_property->value, |
||||||
|
'PayAuthNo' => $pay_auth_no, |
||||||
|
'UldLatlnt' => $lat_lnt, |
||||||
|
] |
||||||
|
// 'hospitalId' => '', |
||||||
|
// 'PatientId' => $patient_number, |
||||||
|
// 'PatientName' => $patient_name, |
||||||
|
// 'TotalFee' => '01', |
||||||
|
// 'FeeCode' => $fee_code, |
||||||
|
// 'RegId' => $reg_id, |
||||||
|
// 'Brxz' => $patient_property, |
||||||
|
// 'PayAuthNo' => $pay_auth_no, |
||||||
|
// 'UldLatlnt' => $lat_lnt, |
||||||
|
// ... $this->commonRequestData() |
||||||
|
] |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 查询就诊记录中的所有诊疗单据 |
||||||
|
* @param string $patient_number 就诊卡号 |
||||||
|
* @param string $start_date 查询开始日期 yyyy-MM-dd |
||||||
|
* @param string $end_date 查询结束日期 yyyy-MM-dd |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function getMedicalPendingLists( |
||||||
|
string $patient_number, |
||||||
|
string $start_date, |
||||||
|
string $end_date |
||||||
|
): mixed |
||||||
|
{ |
||||||
|
// 调用请求处理方法 |
||||||
|
return $this->requestHandle('POST', 'GetUnpayedList', [ |
||||||
|
'json' => [ |
||||||
|
'ApplicationId' => '', |
||||||
|
'ApplicationSecret' => '', |
||||||
|
'Parameter' => [ |
||||||
|
'BeginDate' => $start_date, |
||||||
|
'EndDate' => $end_date, |
||||||
|
'CardType' => '01', |
||||||
|
'CardId' => $patient_number |
||||||
|
] |
||||||
|
|
||||||
|
// 'hospitalId' => '', |
||||||
|
// 'beginDate' => $start_date, |
||||||
|
// 'endDate' => $end_date, |
||||||
|
// 'cardType' => '01', |
||||||
|
// 'cardId' => $patient_number, |
||||||
|
// ... $this->commonRequestData() |
||||||
|
] |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 门诊缴费预结算 |
||||||
|
* @param string $patient_number 门诊号码 |
||||||
|
* @param string $patient_name 患者名称 |
||||||
|
* @param string $pay_auth_no 支付授权码 |
||||||
|
* @param string $lat_lnt 经纬度信息 |
||||||
|
* @param SettleType $settle_type 结算类型(是否使用个账) |
||||||
|
* @param MedicalPayType $pay_type 医保支付类型 |
||||||
|
* @param array $fee_records 处方数组集合 [0 => ["FeeNo"=>"", "FeeTypeCode"=>""], 1 => ...] |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function outpatientPreSettle( |
||||||
|
string $patient_number, |
||||||
|
string $patient_name, |
||||||
|
string $pay_auth_no, |
||||||
|
string $lat_lnt, |
||||||
|
SettleType $settle_type, |
||||||
|
MedicalPayType $pay_type, |
||||||
|
array $fee_records |
||||||
|
): mixed |
||||||
|
{ |
||||||
|
// 调用请求处理方法 |
||||||
|
return $this->requestHandle('POST', 'GetPrecalculatedFee', [ |
||||||
|
'json' => [ |
||||||
|
'ApplicationId' => '', |
||||||
|
'ApplicationSecret'=> '', |
||||||
|
'Parameter'=> [ |
||||||
|
'HospitalId' => '', |
||||||
|
'PatientId'=> $patient_number, |
||||||
|
'PatientName'=> $patient_name, |
||||||
|
'PayAuthNo'=> $pay_auth_no, |
||||||
|
'UldLatlnt'=> $lat_lnt, |
||||||
|
'IsInsSelfPay'=> $settle_type->value, |
||||||
|
'YbPayType'=> $pay_type->value, |
||||||
|
'FeeRecords'=> $fee_records |
||||||
|
] |
||||||
|
// 'hospitalId' => '', |
||||||
|
// 'patientId' => $patient_number, |
||||||
|
// 'patientName' => $patient_name, |
||||||
|
// 'payAuthNo' => $pay_auth_no, |
||||||
|
// 'uldLatlnt' => $lat_lnt, |
||||||
|
// 'isInsSelfPay' => $settle_type->value, |
||||||
|
// 'ybPayType' => $pay_type->value, |
||||||
|
// 'payType' => '03', |
||||||
|
// 'feeRecord' => $fee_records, |
||||||
|
// ... $this->commonRequestData() |
||||||
|
] |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 确认医保缴费 |
||||||
|
* @param string $patient_number 门诊号码 |
||||||
|
* @param string $patient_name 患者名称 |
||||||
|
* @param string $trade_no 订单唯一标识 |
||||||
|
* @param string $precal_id 预结算ID |
||||||
|
* @param string $trade_date 支付时间 yyyy-MM-dd HH:mm:ss |
||||||
|
* @param string $total_amount 总金额 单位元 |
||||||
|
* @param string $medicare_amount 统筹金额 单位元 |
||||||
|
* @param string $pay_amount 现金金额 单位元 |
||||||
|
* @param string $account_amount 个帐金额 单位元 |
||||||
|
* @param MedicalPayType $pay_type 医保支付类型 |
||||||
|
* @param array $fee_records 处方数组集合 [0 => ['FeeNo"=>"", "FeeTypeCode"=>""], 1 => ...] |
||||||
|
* @return mixed |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function confirmMedicalOutpatient( |
||||||
|
string $patient_number, |
||||||
|
string $patient_name, |
||||||
|
string $trade_no, |
||||||
|
string $precal_id, |
||||||
|
string $trade_date, |
||||||
|
string $total_amount, |
||||||
|
string $medicare_amount, |
||||||
|
string $pay_amount, |
||||||
|
string $account_amount, |
||||||
|
MedicalPayType $pay_type, |
||||||
|
array $fee_records |
||||||
|
): mixed |
||||||
|
{ |
||||||
|
return $this->requestHandle('POST', 'NotifyPayed', [ |
||||||
|
'json' => [ |
||||||
|
'ApplicationId' => '', |
||||||
|
'ApplicationSecret'=> '', |
||||||
|
'Parameter'=> [ |
||||||
|
'HospitalId' => '', |
||||||
|
'PatientId'=> $patient_number, |
||||||
|
'PatientName'=> $patient_name, |
||||||
|
'TradeNo' => $trade_no, |
||||||
|
'PrecalId' => $precal_id, |
||||||
|
'TradeDate' => $trade_date, |
||||||
|
'TotalAmount' => $total_amount, |
||||||
|
'MedicareAmount' => $medicare_amount, |
||||||
|
'PayAmount' => $pay_amount, |
||||||
|
'AccountAmount' => $account_amount, |
||||||
|
'YbPayType' => $pay_type->value, |
||||||
|
'PayType' => '03', |
||||||
|
'FeeRecord' => $fee_records, |
||||||
|
] |
||||||
|
// 'hospitalId' => '', |
||||||
|
// 'patientId' => $patient_number, |
||||||
|
// 'patientName' => $patient_name, |
||||||
|
// 'tradeNo' => $trade_no, |
||||||
|
// 'precalId' => $precal_id, |
||||||
|
// 'tradeDate' => $trade_date, |
||||||
|
// 'totalAmount' => $total_amount, |
||||||
|
// 'medicareAmount' => $medicare_amount, |
||||||
|
// 'payAmount' => $pay_amount, |
||||||
|
// 'accountAmount' => $account_amount, |
||||||
|
// 'ybPayType' => $pay_type->value, |
||||||
|
// 'payType' => '03', |
||||||
|
// 'feeRecord' => $fee_records, |
||||||
|
// ... $this->commonRequestData() |
||||||
|
] |
||||||
|
]); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
<?php |
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Utils\Statics; |
||||||
|
|
||||||
|
class BuildCacheKeyName |
||||||
|
{ |
||||||
|
/** |
||||||
|
* 获取 检验报告列表 缓存建名 |
||||||
|
* @param string $open_id |
||||||
|
* @param string $patient_id |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function getInspectReportListsKey(string $open_id, string $patient_id): string |
||||||
|
{ |
||||||
|
return 'report.inspect.'. $open_id. '.'. $patient_id; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取 检验报告详情图片 缓存建名 |
||||||
|
* @param string $open_id |
||||||
|
* @param string $patient_id |
||||||
|
* @param string $serial_no |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function getInspectReportDetailsImagesKey(string $open_id, string $patient_id, string $serial_no): string |
||||||
|
{ |
||||||
|
return 'report.inspect.'. $open_id. '.'. $patient_id. '.images.'. $serial_no; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取 门诊待缴费列表 缓存键名 |
||||||
|
* @param string $open_id |
||||||
|
* @param string $patient_id |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function getOutpatientPendingListsKey(string $open_id, string $patient_id): string |
||||||
|
{ |
||||||
|
return 'outpatient.pending.'. $open_id. '.'. $patient_id; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取 门诊待缴费详情 缓存键名 |
||||||
|
* @param string $open_id |
||||||
|
* @param string $patient_id |
||||||
|
* @param string $serial_no |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function getOutpatientPendingDetailsKey(string $open_id, string $patient_id, string $serial_no): string |
||||||
|
{ |
||||||
|
return 'outpatient.pending.'. $open_id.'.'. $patient_id. '.'. $serial_no; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取 门诊医保待缴费详情 缓存键名 |
||||||
|
* @param string $open_id |
||||||
|
* @param string $patient_id |
||||||
|
* @param string $serial_no |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function getOutpatientMedicalPendingDetailsKey(string $open_id, string $patient_id, string $serial_no): string |
||||||
|
{ |
||||||
|
return 'outpatient.medical.pending.'. $open_id.'.'. $patient_id. '.'. $serial_no; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取用户 免密授权 缓存键名 |
||||||
|
* @param string $open_id |
||||||
|
* @param string $patient_id |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function getUserMedicalInsuranceAuthKey(string $open_id, string $patient_id): string |
||||||
|
{ |
||||||
|
return 'medical.user.info.'. $open_id.'.'. $patient_id; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取挂号预结算详情缓存键名 |
||||||
|
* @param string $open_id |
||||||
|
* @param string $reg_id |
||||||
|
* @param int $patient_property |
||||||
|
* @param int $settle_type |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function getRegistrationPreSettleInfoKey(string $open_id, string $reg_id, int $patient_property, int $settle_type): string |
||||||
|
{ |
||||||
|
return "registration.medical.pre_settle_info.{$open_id}.{$reg_id}.{$patient_property}.{$settle_type}"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取缴费预结算详情缓存键名 |
||||||
|
* @param string $open_id |
||||||
|
* @param string $record_id |
||||||
|
* @param int $settle_type 0 医保统筹 1 自费支付 |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function getOutpatientPrescriptionPreSettleInfoKey(string $open_id, string $record_id, int $settle_type): string |
||||||
|
{ |
||||||
|
return "outpatient.medical.pre_settle_info.{$open_id}.{$record_id}.{$settle_type}"; |
||||||
|
} |
||||||
|
} |
@ -1,52 +0,0 @@ |
|||||||
<?php |
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace App\Utils\Traits; |
|
||||||
|
|
||||||
trait BuildCacheKeyName |
|
||||||
{ |
|
||||||
/** |
|
||||||
* 获取 检验报告列表 缓存建名 |
|
||||||
* @param string $open_id |
|
||||||
* @param string $patient_id |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function getInspectReportListsKey(string $open_id, string $patient_id): string |
|
||||||
{ |
|
||||||
return 'report.inspect.'. $open_id. '.'. $patient_id; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取 门诊待缴费列表 缓存键名 |
|
||||||
* @param string $open_id |
|
||||||
* @param string $patient_id |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function getOutpatientPendingListsKey(string $open_id, string $patient_id): string |
|
||||||
{ |
|
||||||
return 'outpatient.pending.'. $open_id. '.'. $patient_id; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取 门诊待缴费详情 缓存键名 |
|
||||||
* @param string $open_id |
|
||||||
* @param string $patient_id |
|
||||||
* @param string $serial_no |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function getOutpatientPendingDetailsKey(string $open_id, string $patient_id, string $serial_no): string |
|
||||||
{ |
|
||||||
return 'outpatient.pending.'. $open_id.'.'. $patient_id. '.'. $serial_no; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取用户 免密授权 缓存键名 |
|
||||||
* @param string $open_id |
|
||||||
* @param string $patient_id |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function getUserMedicalInsuranceAuthKey(string $open_id, string $patient_id): string |
|
||||||
{ |
|
||||||
return 'medical.user.info.'. $open_id.'.'. $patient_id; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,25 @@ |
|||||||
|
<?php |
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Utils\Transfer\HisMedicalHttpClient; |
||||||
|
|
||||||
|
use App\Utils\Transfer\HttpTransferAbstract; |
||||||
|
|
||||||
|
class ClientFactory |
||||||
|
{ |
||||||
|
|
||||||
|
/** |
||||||
|
* Get Client Transfer Class |
||||||
|
* @param string $name |
||||||
|
* @return HttpTransferAbstract |
||||||
|
*/ |
||||||
|
public static function getClientTransfer(string $name): HttpTransferAbstract |
||||||
|
{ |
||||||
|
$is_mock = config('hisservice.'. $name. '.is_mock'); |
||||||
|
|
||||||
|
return match ($is_mock) { |
||||||
|
false => new ClientHttpTransfer($name), |
||||||
|
true => new ClientMockHttpTransfer($name), |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
<?php |
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Utils\Transfer\HisMedicalHttpClient; |
||||||
|
|
||||||
|
use App\Utils\Transfer\HttpTransferAbstract; |
||||||
|
use Exception; |
||||||
|
use JsonException; |
||||||
|
use Psr\Http\Message\ResponseInterface; |
||||||
|
|
||||||
|
class ClientHttpTransfer extends HttpTransferAbstract |
||||||
|
{ |
||||||
|
|
||||||
|
/** |
||||||
|
* MediWayTransfer Construct |
||||||
|
* @param string $his_name |
||||||
|
*/ |
||||||
|
public function __construct(string $his_name = "") |
||||||
|
{ |
||||||
|
parent::__construct($his_name); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 初始化 |
||||||
|
*/ |
||||||
|
public function initialize(): void |
||||||
|
{ |
||||||
|
parent::initialize(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置客户端选项 |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
public function ClientHeaders(): array |
||||||
|
{ |
||||||
|
return []; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 响应格式化 |
||||||
|
* @return mixed |
||||||
|
*/ |
||||||
|
public function responseFormat(): mixed |
||||||
|
{ |
||||||
|
$response = $this->transfer_response; |
||||||
|
if ($this->transfer_response instanceof ResponseInterface) { |
||||||
|
$response = $this->transfer_response->getBody()->getContents(); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
return json_decode($response, true, JSON_THROW_ON_ERROR); |
||||||
|
} catch (JsonException|Exception $e) { |
||||||
|
return [ |
||||||
|
'ResultCode' => 0, |
||||||
|
'ErrorMsg' => '解析JSON失败', |
||||||
|
'ErrorMsgDev' => $e->getMessage(), |
||||||
|
'Result' => $data |
||||||
|
]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,128 @@ |
|||||||
|
<?php |
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Utils\Transfer\HisMedicalHttpClient; |
||||||
|
|
||||||
|
use App\Exceptions\GeneralException; |
||||||
|
use App\Utils\Transfer\HttpTransferAbstract; |
||||||
|
use Exception; |
||||||
|
use Random\RandomException; |
||||||
|
|
||||||
|
class ClientMockHttpTransfer extends HttpTransferAbstract |
||||||
|
{ |
||||||
|
/** |
||||||
|
* ClientMockTransfer Construct |
||||||
|
* @param string $his_name |
||||||
|
*/ |
||||||
|
public function __construct(string $his_name = "") |
||||||
|
{ |
||||||
|
parent::__construct($his_name); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置客户端选项 |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
public function clientHeaders(): array |
||||||
|
{ |
||||||
|
return []; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $class_name |
||||||
|
* @return $this |
||||||
|
*/ |
||||||
|
public function transferClass(string $class_name): static |
||||||
|
{ |
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @throws GeneralException |
||||||
|
*/ |
||||||
|
public function transferMethod(string $method, string $request_name, array $request_data = []): self |
||||||
|
{ |
||||||
|
// 使用 match 替代 switch |
||||||
|
return match ($request_name) { |
||||||
|
'GetPrecalculatedFeeGh' => $this->mockGetPrecalculatedFeeGh($request_data), |
||||||
|
'GetUnpayedList' => $this->mockGetUnpayedList($request_data), |
||||||
|
'GetPrecalculatedFee' => $this->mockGetPrecalculatedFee($request_data), |
||||||
|
'NotifyPayed' => $this->mockNotifyPayed($request_data), |
||||||
|
default => throw new GeneralException("Method '{$request_name}' not found"), |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 响应格式化 |
||||||
|
* @return mixed |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public function responseFormat(): mixed |
||||||
|
{ |
||||||
|
try { |
||||||
|
// 此处为json格式 |
||||||
|
return json_decode((string)$this->transfer_response, true); |
||||||
|
} catch (Exception $e) { |
||||||
|
throw new Exception($e->getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取返回值 |
||||||
|
* @param bool $is_format |
||||||
|
* @return mixed |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public function getResult(bool $is_format = true): mixed |
||||||
|
{ |
||||||
|
return $this->responseFormat($this->transfer_response); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* mockGetPrecalculatedFeeGh |
||||||
|
* @param array $params |
||||||
|
* @return self |
||||||
|
*/ |
||||||
|
private function mockGetPrecalculatedFeeGh(array $params): self |
||||||
|
{ |
||||||
|
$this->transfer_response = ''; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* mockRegisterCard |
||||||
|
* @param array $params |
||||||
|
* @return self |
||||||
|
*/ |
||||||
|
private function mockGetUnpayedList(array $params): self |
||||||
|
{ |
||||||
|
$this->transfer_response = ''; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* mockGetPrecalculatedFee |
||||||
|
* @param array $params |
||||||
|
* @return self |
||||||
|
*/ |
||||||
|
private function mockGetPrecalculatedFee(array $params): self |
||||||
|
{ |
||||||
|
$this->transfer_response = ''; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* mockNotifyPayed |
||||||
|
* @param array $params |
||||||
|
* @return self |
||||||
|
*/ |
||||||
|
private function mockNotifyPayed(array $params): self |
||||||
|
{ |
||||||
|
$this->transfer_response = ''; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,113 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace UnifyPayment; |
||||||
|
|
||||||
|
use Monolog\Formatter\LineFormatter; |
||||||
|
use Monolog\Handler\RotatingFileHandler; |
||||||
|
use Monolog\Logger; |
||||||
|
|
||||||
|
return [ |
||||||
|
|
||||||
|
/* |
||||||
|
|-------------------------------------------------------------------------- |
||||||
|
| Request Platform Config |
||||||
|
|-------------------------------------------------------------------------- |
||||||
|
| domain:支付平台地址。 |
||||||
|
| apiSecret: 应用秘钥 |
||||||
|
| |
||||||
|
*/ |
||||||
|
'domain' => 'http://localhost:7878/', |
||||||
|
|
||||||
|
'apiSecret' => 'ed147e09-4e8a-4fe4-8c72-3394b97264f0', |
||||||
|
|
||||||
|
/* |
||||||
|
|-------------------------------------------------------------------------- |
||||||
|
| Payment Channels Config |
||||||
|
|-------------------------------------------------------------------------- |
||||||
|
| |
||||||
|
| 统一支付平台支付渠道配置参数 |
||||||
|
| merchantId: 商户号 |
||||||
|
| secret: 秘钥 |
||||||
|
| channelId: 支付渠道 |
||||||
|
| operatorId: [ 操作员1 , 操作员2 ] |
||||||
|
| |
||||||
|
*/ |
||||||
|
|
||||||
|
'channels' => [ |
||||||
|
|
||||||
|
'default' => [ |
||||||
|
'merchantId' => 'ZHXZEY20250228', |
||||||
|
'secret' => '2a62ff796006a7a2b2d06e40ea240de9', |
||||||
|
'channelId' => 'FD013', |
||||||
|
'operatorId' => [ |
||||||
|
'MINI001', |
||||||
|
], |
||||||
|
] |
||||||
|
|
||||||
|
], |
||||||
|
|
||||||
|
/* |
||||||
|
|-------------------------------------------------------------------------- |
||||||
|
| Logger Config |
||||||
|
|-------------------------------------------------------------------------- |
||||||
|
| |
||||||
|
| 详细使用方式见 MonoLog 文档:https://github.com/Seldaek/monolog/blob/2.x/README.md |
||||||
|
| format: |
||||||
|
| LineFormatter 字符串 |
||||||
|
| JsonFormatter Json |
||||||
|
| ... |
||||||
|
| handler: |
||||||
|
| StreamHandler 单个文件 |
||||||
|
| RotatingFileHandler 每日记录一次 |
||||||
|
| RedisHandler Redis |
||||||
|
| ... |
||||||
|
| handler_with: |
||||||
|
| RotatingFileHandler: |
||||||
|
| file 文件名称 |
||||||
|
| max_files 最大记录日志文件数量,0为无限制 |
||||||
|
| level 日志记录等级 |
||||||
|
| OtherHandler详见MonoLog文档 |
||||||
|
| ... |
||||||
|
| |
||||||
|
*/ |
||||||
|
|
||||||
|
'log' => [ |
||||||
|
// 时区 |
||||||
|
'timezone' => 'Asia/Shanghai', |
||||||
|
// 日期格式 |
||||||
|
'date_format' => 'Y-m-d H:i:s', |
||||||
|
// 格式化器 |
||||||
|
'formatter' => LineFormatter::class, |
||||||
|
// 对应格式化器构造参数 |
||||||
|
'formatter_with' => [ |
||||||
|
// 输出格式字符串 |
||||||
|
'output' => "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n\n" |
||||||
|
], |
||||||
|
// 处理器 |
||||||
|
'handler' => RotatingFileHandler::class, |
||||||
|
// 对应处理器构造参数 |
||||||
|
'handler_with' => [ |
||||||
|
// 文件 |
||||||
|
'file' => storage_path('logs/UnifyLog/payment.log'), |
||||||
|
// 最大文件 0 无限制 |
||||||
|
'max_files' => 0, |
||||||
|
// 记录等级 |
||||||
|
'level' => Logger::DEBUG |
||||||
|
] |
||||||
|
], |
||||||
|
|
||||||
|
/* |
||||||
|
|-------------------------------------------------------------------------- |
||||||
|
| Http Client Config |
||||||
|
|-------------------------------------------------------------------------- |
||||||
|
| |
||||||
|
| 详细使用方式见 Guzzle 文档:https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html |
||||||
|
| |
||||||
|
*/ |
||||||
|
'http' => [ |
||||||
|
'verify' => false, |
||||||
|
'timeout' => 20, |
||||||
|
'connect_timeout' => 5.0, |
||||||
|
] |
||||||
|
]; |
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,49 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace UnifyPayment\Mock; |
||||||
|
|
||||||
|
class CreateMedicalInsuranceOrderHandler extends ResponseHandler |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
public function success(): array |
||||||
|
{ |
||||||
|
$channelId = 'FD00' . $this->faker->randomDigit(); |
||||||
|
|
||||||
|
$data = array_merge($this->getSuccessHeaderData('Query Success.'), [ |
||||||
|
'response' => [ |
||||||
|
'payUrl' => $this->faker->url(), |
||||||
|
'medTransId' => 'M'. $this->faker->time('ymdhis'). $this->faker->regexify('[0-9]{5}'), |
||||||
|
'payAppId' => 'wx' . $this->faker->regexify('[0-9a-z]{15}'), |
||||||
|
'orderNo' => 'FD' . $channelId . $this->faker->time('YmdHis') . $this->faker->regexify('[0-9]{5}'), |
||||||
|
'merchantId' => 'STZL' . $this->faker->time('Ymd'), |
||||||
|
'merchantName' => $this->faker->company(), |
||||||
|
'channelId' => $channelId, |
||||||
|
'channelName' => '微信移动医保' |
||||||
|
] |
||||||
|
]); |
||||||
|
|
||||||
|
return [ |
||||||
|
self::SUCCESS_CODE, |
||||||
|
self::JSON_HEADER, |
||||||
|
json_encode($data, JSON_UNESCAPED_UNICODE) |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
public function failure(): array |
||||||
|
{ |
||||||
|
$data = $this->getFailureHeaderData($this->faker->sentence()); |
||||||
|
|
||||||
|
return [ |
||||||
|
self::FAILURE_CODE, |
||||||
|
self::JSON_HEADER, |
||||||
|
json_encode($data, JSON_UNESCAPED_UNICODE) |
||||||
|
]; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue