Compare commits

..

No commits in common. 'medical' and 'master' have entirely different histories.

  1. 4
      app/Console/Commands/SendAppointmentReminders.php
  2. 54
      app/Dictionary/Medical/OrderType.php
  3. 28
      app/Dictionary/Medical/PatientProperty.php
  4. 30
      app/Dictionary/Medical/PayType.php
  5. 30
      app/Dictionary/Medical/PreSettleStatus.php
  6. 27
      app/Dictionary/Medical/SettleType.php
  7. 35
      app/Dictionary/Report/ImageType.php
  8. 110
      app/Http/Controllers/Outpatient/MedicalController.php
  9. 14
      app/Http/Controllers/Patient/PatientController.php
  10. 77
      app/Http/Controllers/Registration/MedicalController.php
  11. 2
      app/Http/Controllers/Registration/RegisterController.php
  12. 92
      app/Http/Controllers/Report/InspectController.php
  13. 55
      app/Http/Controllers/Test/TestController.php
  14. 81
      app/Http/Logics/Notify/NotifyLogic.php
  15. 497
      app/Http/Logics/Outpatient/MedicalLogic.php
  16. 9
      app/Http/Logics/Outpatient/PaymentLogic.php
  17. 9
      app/Http/Logics/Outpatient/PendingLogic.php
  18. 7
      app/Http/Logics/Outpatient/RecordLogic.php
  19. 32
      app/Http/Logics/Patient/PatientLogic.php
  20. 504
      app/Http/Logics/Registration/MedicalLogic.php
  21. 164
      app/Http/Logics/Report/InspectLogic.php
  22. 1
      app/Http/Requests/Patient/BindPatientRequest.php
  23. 77
      app/Http/Requests/Registration/RegisterPreSettleRequest.php
  24. 37
      app/Http/Resources/Outpatient/Medical/PrescriptionPreSettleResource.php
  25. 37
      app/Http/Resources/Registration/Medical/RegisterPreSettleResource.php
  26. 64
      app/Http/Resources/Report/Inspect/DetailsResource.php
  27. 51
      app/Http/Resources/Report/Inspect/ListsResource.php
  28. 6
      app/Jobs/SendWeChatMessageJob.php
  29. 2
      app/Models/Order.php
  30. 6
      app/Providers/AppServiceProvider.php
  31. 3
      app/Services/HealthRecordAuth/Client.php
  32. 54
      app/Services/HisHttp/Client.php
  33. 293
      app/Services/HisMedicalHttp/Client.php
  34. 181
      app/Services/MedicalAuth/Client.php
  35. 3
      app/Utils/Helpers.php
  36. 101
      app/Utils/Statics/BuildCacheKeyName.php
  37. 30
      app/Utils/Traits/BuildCacheKeyName.php
  38. 2
      app/Utils/Traits/SendSubscribeMessage.php
  39. 1
      app/Utils/Traits/UniversalEncryption.php
  40. 25
      app/Utils/Transfer/HisMedicalHttpClient/ClientFactory.php
  41. 62
      app/Utils/Transfer/HisMedicalHttpClient/ClientHttpTransfer.php
  42. 128
      app/Utils/Transfer/HisMedicalHttpClient/ClientMockHttpTransfer.php
  43. 4
      config/custom.php
  44. 19
      config/hisservice.php
  45. 13
      config/logging.php
  46. 10
      config/unify.php
  47. 113
      config/unifytest.php
  48. 24
      config/wechat.php
  49. 2
      packagist/unify_payment/src/Cores/BasicClient.php
  50. 1074
      packagist/unify_payment/src/Cores/Struct/CreateMedicalInsuranceOrder.php
  51. 49
      packagist/unify_payment/src/Mock/CreateMedicalInsuranceOrderHandler.php
  52. 21
      packagist/unify_payment/src/Modules/Pay/MiniProgramClient.php
  53. 22
      packagist/unify_payment/src/Modules/Pay/OfficialAccountClient.php
  54. 42
      packagist/unify_payment/tests/PayTest.php
  55. 29
      routes/api.php

@ -41,8 +41,8 @@ class SendAppointmentReminders extends Command
$this->info('Starting to send appointment reminders...');
// 查询即将到期的预约记录(8小时后的预约)
$appointments = RegistrationRecord::where('visit_date', now()->toDateString())
->where('begin_time', '>=', now()->addHours(8)->toTimeString())
$appointments = RegistrationRecord::where('visit_date', now()->toDate())
->where('begin_time', now()->subHours(8))
->where('reminder_sent', 0)
->get();

@ -1,54 +0,0 @@
<?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 => '新冠抗原检测',
};
}
}

@ -1,28 +0,0 @@
<?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 => '门诊共济',
};
}
}

@ -1,30 +0,0 @@
<?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 => '线上医保',
};
}
}

@ -1,30 +0,0 @@
<?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 => '已取消预结算',
};
}
}

@ -1,27 +0,0 @@
<?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 => '使用医保个账支付',
};
}
}

@ -1,35 +0,0 @@
<?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编码',
};
}
}

@ -1,110 +0,0 @@
<?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);
}
}

@ -122,18 +122,4 @@ class PatientController
return jsonResponse(Response::HTTP_OK, 'success', ['phone_info' => $phone_info]);
}
/**
* 医保定点签约
* @param Request $request
* @param string $patient_id
* @return JsonResponse
* @throws GeneralException
*/
public function designated(Request $request, string $patient_id): JsonResponse
{
$this->patient_logic->medicalDesignated($patient_id);
return jsonResponse(Response::HTTP_OK, '签约成功!');
}
}

@ -1,77 +0,0 @@
<?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);
}
}

@ -24,7 +24,7 @@ class RegisterController
}
/**
* 挂号
* 获取挂号记录列表
* @param RegisterRequest $request
* @param string $patient_id 此处为 patient_number
* @return JsonResponse

@ -1,92 +0,0 @@
<?php
declare(strict_types = 1);
namespace App\Http\Controllers\Report;
use App\Exceptions\GeneralException;
use App\Http\Logics\Report\InspectLogic;
use App\Http\Resources\Report\Inspect\DetailsResource;
use App\Http\Resources\Report\Inspect\ListsResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Symfony\Component\HttpFoundation\Response;
class InspectController
{
protected InspectLogic $inspect_logic;
/**
* InspectController Construct.
*/
public function __construct()
{
$this->inspect_logic = new InspectLogic();
}
/**
* 获取报告提示
* @return JsonResponse
*/
public function tips()
{
if (Redis::exists('report.tips')) {
$tips_lists = Redis::get('report.tips');
$tips_lists = json_decode($tips_lists, true);
} else {
$tips_lists = DB::connection('mysql_admin')->table('department_tips')
->select(DB::raw('ABS(dept_id) as type_id'), 'title', 'message')
->whereIn('dept_id', [-2, -3])
->where('is_enable', 1)
->get()
->toArray();
Redis::setex('report.tips', 4 * 3600, json_encode($tips_lists, JSON_UNESCAPED_UNICODE));
}
return jsonResponse(Response::HTTP_OK, 'success', $tips_lists);
}
/**
* 获取检查报告列表
* @param Request $request
* @param string $patient_id
* @return JsonResponse
* @throws GeneralException
*/
public function lists(Request $request, string $patient_id): JsonResponse
{
$validated = $request->validate([
'start_date' => 'required|date_format:Y-m-d',
'end_date' => 'required|date_format:Y-m-d|after:start_date',
], [
'start_date.required' => '请选择查询开始日期',
'end_date.required' => '请选择查询结束日期',
'start_date.date_format' => '日期格式错误',
'end_date.date_format' => '日期格式错误',
'end_date.after' => '查询日期错误',
]);
// 日期必须在3个月之内
$response = $this->inspect_logic->lists($patient_id, $validated['start_date'], $validated['end_date']);
return jsonResponse(Response::HTTP_OK, 'success', ListsResource::make($response)->toArray());
}
/**
* 获取缴费记录详情
* @param Request $request
* @param string $patient_id
* @param string $serial_no
* @return JsonResponse
* @throws GeneralException
*/
public function details(Request $request, string $patient_id, string $serial_no): JsonResponse
{
$response = $this->inspect_logic->details($patient_id, $serial_no);
return jsonResponse(Response::HTTP_OK, 'success.', DetailsResource::make($response)->toArray());
}
}

@ -5,67 +5,24 @@ namespace App\Http\Controllers\Test;
use App\Dictionary\WeChat\MiniProgram\OpenApi;
use App\Models\Order;
use App\Models\RegistrationRecord;
use App\Utils\Traits\HttpRequest;
use App\Utils\Traits\SendSubscribeMessage;
use App\Utils\Traits\UniversalEncryption;
use Illuminate\Support\Facades\Redis;
use UnifyPayment\Cores\Struct\RefundOrder;
use UnifyPayment\Unify;
use function Symfony\Component\Translation\t;
class TestController
{
use HttpRequest;
use UniversalEncryption;
use SendSubscribeMessage;
public function test(): void
{
// $json = [
// 'ApplicationId' => '',
// 'ApplicationSecret' => '',
// 'Parameter' => [
// 'BeginDate' => '2025-04-27',
// 'EndDate' => '2025-04-28',
// 'CardType' => '01',
// 'CardNo' => '65284656',
// ]
// ];
$json = [
"ApplicationId" => "",
"ApplicationSecret"=> "",
"Parameter"=> [
"HospitalId" => "",
"PatientId"=> "2226817",
"PatientName"=> "李欣茹",
"PayAuthNo"=> "",
"UldLatlnt"=> "",
"IsInsSelfPay"=> "0",
"YbPayType"=> "2",
"FeeRecords"=> [
[
"FeeNo" => "-210360897",
"FeeTypeCode" => "51"
]
]
]
];
$start_time = microtime(true);
// $result = $this->request('POST', 'http://192.168.61.44:8010/api/his/GetPatientInfo', [
// 'json' => $json
// ]);
$end_time = microtime(true);
$use_time = sprintf("%.6f", ($start_time - $end_time));
recordLog('MedicalHis', json_encode([
'json' => $json,
'result' => $result,
'use_time' => $use_time
], JSON_UNESCAPED_UNICODE));
// dd($result);
$mini = getWeChatMiniProgramApp();
$response = $mini->getClient()->postJson(OpenApi::CREATE_QR_CODE->value, [
'path' => 'pagesA/register/notice?ed=show',
]);
dd($response->toArray());
//dd(json_decode('', true));
// $response = '1';
// $order = Order::where('order_id', 'WXM20250207151636790')->first();
// $record = $order->outpatientPaymentRecord;

@ -6,7 +6,6 @@ namespace App\Http\Logics\Notify;
use App\Dictionary\Order\NotifyStatus;
use App\Dictionary\Order\PayType;
use App\Dictionary\Medical\PayType as MedicalPayType;
use App\Dictionary\Order\SourceId;
use App\Dictionary\Order\Status;
use App\Dictionary\Order\Type;
@ -28,6 +27,8 @@ use UnifyPayment\Cores\Struct\ConfirmOrderForEx;
use UnifyPayment\Cores\Struct\QueryOrder;
use UnifyPayment\Cores\Struct\RefundOrder;
use UnifyPayment\Mock\ConfirmOrderForExHandler;
use UnifyPayment\Mock\QueryOrderHandler;
use UnifyPayment\Mock\RefundOrderHandler;
use UnifyPayment\Unify;
class NotifyLogic
@ -38,8 +39,6 @@ class NotifyLogic
protected Client $his_client;
protected MedicalClient $his_medical_client;
protected PaymentApplication $payment_app;
protected PatientModel $patient_model;
@ -105,12 +104,7 @@ class NotifyLogic
$this->registrationOrderHandle($order_info, $notify);
break;
case Type::OUTPATIENT_PAYMENT->value:
if ($order_info->pay_type === PayType::MEDICAL_INSURANCE_PAY) {
// 医保支付
$this->outpatientMedicalOrderHandle($order_info, $notify);
} else {
$this->outpatientOrderHandle($order_info, $notify);
}
$this->outpatientOrderHandle($order_info, $notify);
break;
default:
break;
@ -244,75 +238,6 @@ class NotifyLogic
}
}
/**
* 门诊缴费医保订单操作
* @param OrderModel $order_info
* @param Message $notify
* @return void
* @throws GeneralException
*/
public function outpatientMedicalOrderHandle(OrderModel $order_info, Message $notify): void
{
// 缴费确认
$record = $order_info->outpatientPaymentRecord;
$extra_info = json_decode($record->extra_info, true);
$time_end = date('Y-m-d H:i:s', strtotime($notify['time_end']));
$totalAmount = $extra_info['pre_settle_info']['TotalAmount']; // 总金额
$medicare_amount = bcdiv($notify['insurance_fund_fee'], '100'); // 减免金额
$pay_amount = bcdiv($notify['cash_fee'], '100'); // 现金金额
$account_amount = bcdiv($notify['insurance_self_fee'], '100'); // 个账支付金额
$fee_record = [];
foreach ($extra_info['']['feeRecords']['feeRecord'] as $v) {
$fee_record[] = [
'FeeNo' => $v['feeNo'],
'FeeTypeCode' => $v['feeTypeCode'],
];
}
$data = [
$order_info->patient_id,
$order_info->patient_name,
$order_info->order_id,
$extra_info['pre_settle_info']['PrecalId'],
$time_end,
$totalAmount,
$medicare_amount,
$pay_amount,
$account_amount,
MedicalPayType::ONLINE_MEDICARE,
'03',
$fee_record
];
$response = $this->his_client->confirmMedicalOutpatient(... $data);
$this->info('医保缴费订单出入参:'.$order_info->order_id, [$data, $response]);
// 保存返回信息
if (isset($response['success']) && $response['success'] === true) {
// 成功流程
$order_info->orderConfirm($order_info->order_id, $response['response']['ResultId'], $response['response']);
// 支付平台业务确认
$this->unifyConfirm($notify['out_trade_no'], $response['response']['ResultId'], $notify['openid'], $notify['transaction_id']);
// 推送成功
$this->sendOutpatientPaymentSuccessMessage($order_info);
} else if (isset($response['success']) && $response['success'] === false && $response['msg'] !== '服务异常') {
// 失败流程
$this->handleOrderReverse($order_info, $response['msg']);
// 推送失败
$this->sendOutpatientPaymentFailureMessage($order_info);
} else {
// 异常流程
$order_info->abnormalOrderOpera($order_info->id);
// 推送异常
$this->sendOutpatientPaymentFailureMessage($order_info);
}
}
/**
* 退款
* @param string $order_id

@ -1,497 +0,0 @@
<?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);
}
}
}

@ -8,10 +8,11 @@ use App\Dictionary\Order\SourceId;
use App\Dictionary\Order\Type;
use App\Dictionary\Patient\CardType;
use App\Exceptions\GeneralException;
use App\Http\Resources\Outpatient\Pending\PendingListsResource;
use App\Models\Order;
use App\Models\Patient;
use App\Services\HisHttp\Client;
use App\Utils\Statics\BuildCacheKeyName;
use App\Utils\Traits\BuildCacheKeyName;
use App\Utils\Traits\Logger;
use App\Utils\Traits\MiniProgramAuth;
use Illuminate\Auth\AuthenticationException;
@ -21,12 +22,14 @@ use Symfony\Component\HttpFoundation\Response;
use UnifyPayment\Cores\Exceptions\InvalidConfigException;
use UnifyPayment\Cores\Exceptions\RuntimeException;
use UnifyPayment\Cores\Struct\CreateOrder;
use UnifyPayment\Mock\CreateOrderHandler;
use UnifyPayment\Unify;
class PaymentLogic
{
use Logger;
use MiniProgramAuth;
use BuildCacheKeyName;
private Client $his_client;
@ -116,8 +119,8 @@ class PaymentLogic
protected function getPendingPrescriptionDetails(string $patient_id, string $serial_no, string $prescription_ids): array
{
// 缓存键值
$pending_lists_cache_key = BuildCacheKeyName::getOutpatientPendingListsKey($this->open_id, $patient_id);
$pending_details_cache_key = BuildCacheKeyName::getOutpatientPendingDetailsKey($this->open_id, $patient_id, $serial_no);
$pending_lists_cache_key = $this->getOutpatientPendingListsKey($this->open_id, $patient_id);
$pending_details_cache_key = $this->getOutpatientPendingDetailsKey($this->open_id, $patient_id, $serial_no);
// 获取缓存
if (Redis::exists($pending_lists_cache_key)) {

@ -6,7 +6,7 @@ namespace App\Http\Logics\Outpatient;
use App\Exceptions\GeneralException;
use App\Models\Patient;
use App\Services\HisHttp\Client;
use App\Utils\Statics\BuildCacheKeyName;
use App\Utils\Traits\BuildCacheKeyName;
use App\Utils\Traits\Logger;
use App\Utils\Traits\MiniProgramAuth;
use Illuminate\Auth\AuthenticationException;
@ -18,6 +18,7 @@ class PendingLogic
{
use Logger;
use MiniProgramAuth;
use BuildCacheKeyName;
private Client $his_client;
@ -57,7 +58,7 @@ class PendingLogic
}
// 缓存2小时
Redis::setex(BuildCacheKeyName::getOutpatientPendingListsKey($this->open_id, $patient_id), 7200, json_encode($response, JSON_UNESCAPED_UNICODE));
Redis::setnx($this->getOutpatientPendingListsKey($this->open_id, $patient_id), 7200, json_encode($response, JSON_UNESCAPED_UNICODE));
return $response;
}
@ -84,7 +85,7 @@ class PendingLogic
}
// 缓存2小时
Redis::setex(BuildCacheKeyName::getOutpatientPendingDetailsKey($this->open_id, $patient_id, $serial_no), 7200, json_encode($response, JSON_UNESCAPED_UNICODE));
Redis::setnx($this->getOutpatientPendingDetailsKey($this->open_id, $patient_id, $serial_no), 7200, json_encode($response, JSON_UNESCAPED_UNICODE));
return $response;
}
@ -98,7 +99,7 @@ class PendingLogic
protected function getCachePendingLists(string $patient_id, string $serial_no): mixed
{
// 缓存键值
$cache_key = BuildCacheKeyName::getOutpatientPendingListsKey($this->open_id, $patient_id);
$cache_key = $this->getOutpatientPendingListsKey($this->open_id, $patient_id);
// 获取缓存
if (Redis::exists($cache_key)) {

@ -6,7 +6,7 @@ namespace App\Http\Logics\Outpatient;
use App\Exceptions\GeneralException;
use App\Models\Patient;
use App\Services\HisHttp\Client;
use App\Utils\Statics\BuildCacheKeyName;
use App\Utils\Traits\BuildCacheKeyName;
use App\Utils\Traits\Logger;
use App\Utils\Traits\MiniProgramAuth;
use Illuminate\Auth\AuthenticationException;
@ -17,6 +17,7 @@ class RecordLogic
{
use Logger;
use MiniProgramAuth;
use BuildCacheKeyName;
private Patient $patient_model;
@ -65,7 +66,7 @@ class RecordLogic
}
// 缓存2小时
$cache_key = BuildCacheKeyName::getOutpatientPendingListsKey($this->open_id, $patient_id);
$cache_key = $this->getOutpatientPendingListsKey($this->open_id, $patient_id);
Redis::setex($cache_key, 2 * 60 * 60, json_encode($response, JSON_UNESCAPED_UNICODE));
return $response;
}
@ -102,7 +103,7 @@ class RecordLogic
*/
protected function getCacheRecordInfo(string $patient_id, string $serial_no): mixed
{
$cache_key = BuildCacheKeyName::getOutpatientPendingDetailsKey($this->open_id, $patient_id, $serial_no);
$cache_key = $this->getOutpatientPendingDetailsKey($this->open_id, $patient_id, $serial_no);
$record_info = Redis::get($cache_key);
if (empty($record_info)) {

@ -9,7 +9,6 @@ use App\Dictionary\WeChat\MiniProgram\OpenApi;
use App\Exceptions\GeneralException;
use App\Models\Patient;
use App\Services\HisHttp\Client;
use App\Services\HisMedicalHttp\Client as MedicalClient;
use App\Utils\Traits\Logger;
use App\Utils\Traits\MiniProgramAuth;
use App\Utils\Traits\SendSubscribeMessage;
@ -28,8 +27,6 @@ class PatientLogic
private Client $his_client;
private MedicalClient $his_medical_client;
private Patient $patient_model;
/**
@ -41,7 +38,6 @@ class PatientLogic
$this->authInitialize();
$this->patient_model = new Patient();
$this->his_client = app('HisHttpService');
$this->his_medical_client = app('HisMedicalHttpClient');
}
/**
@ -303,32 +299,4 @@ class PatientLogic
throw new GeneralException('获取手机号码失败,请稍后再试!', Response::HTTP_BAD_REQUEST);
}
}
/**
* 医保定点签约
* @param string $patient_number
* @return bool
* @throws GeneralException
*/
public function medicalDesignated(string $patient_number): bool
{
$info = $this->patient_model->getBindPatientInfoByPatientNumber($this->open_id, $patient_number);
if (empty($info)) {
throw new GeneralException('该就诊卡不存在!');
}
// 获取患者信息
$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_info = &$response['response'];
$result = $this->his_medical_client->medicalDesignated('02', $patient_info['iDCardNo']);
if (!isset($result['ResultCode']) || $result['ResultCode'] !== '1') {
throw new GeneralException($result['ErrorMsg'] ?? '医保定点签约失败,请稍后再试!', Response::HTTP_SERVICE_UNAVAILABLE);
}
return true;
}
}

@ -1,504 +0,0 @@
<?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);
}
}
}

@ -1,164 +0,0 @@
<?php
declare(strict_types = 1);
namespace App\Http\Logics\Report;
use App\Dictionary\Order\PayType;
use App\Dictionary\Order\SourceId;
use App\Dictionary\Order\Status;
use App\Dictionary\Order\Type;
use App\Dictionary\Patient\CardType;
use App\Dictionary\Report\ImageType;
use App\Exceptions\GeneralException;
use App\Models\Order;
use App\Models\Patient;
use App\Models\RegistrationRecord;
use App\Services\HisHttp\Client;
use App\Utils\Statics\BuildCacheKeyName;
use App\Utils\Traits\Logger;
use App\Utils\Traits\MiniProgramAuth;
use App\Utils\Traits\SendSubscribeMessage;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;
use ReflectionException;
use Symfony\Component\HttpFoundation\Response;
use UnifyPayment\Cores\Struct\RefundOrder;
use UnifyPayment\Unify;
class InspectLogic
{
use Logger;
use MiniProgramAuth;
private Client $his_client;
private Patient $patient_model;
private Order $order_model;
/**
* RecordLogic Construct
* @throws AuthenticationException
*/
public function __construct()
{
$this->authInitialize();
$this->setChannel('refund');
$this->his_client = app('HisHttpService');
$this->patient_model = new Patient();
}
/**
* 获取报告列表
* @param string $patient_number
* @param string $start_date
* @param string $end_date
* @return mixed
* @throws GeneralException
*/
public function lists(string $patient_number, string $start_date, string $end_date): mixed
{
$patient_info = $this->getHisPatientInfo($this->open_id, $patient_number);
$response = $this->his_client->getInspectReportLists($patient_info['name'], $patient_info['idCardNo'], $start_date, $end_date);
if (!isset($response['success']) || !$response['success']) {
throw new GeneralException($response['msg'] ?? '找不到该就诊卡!', Response::HTTP_SERVICE_UNAVAILABLE);
}
Redis::setex(BuildCacheKeyName::getInspectReportListsKey($this->open_id, $patient_info['patient_id']), 3600, json_encode($response['response'], JSON_UNESCAPED_UNICODE));
return $response['response'];
}
/**
* 获取报告详情
* @param string $patient_number
* @param string $serial_no
* @return array
* @throws GeneralException
*/
public function details(string $patient_number, string $serial_no)
{
$patient_info = $this->getPatientInfo($this->open_id, $patient_number);
// 缓存键名
$report_lists_cache_key = BuildCacheKeyName::getInspectReportListsKey($this->open_id, $patient_info['patient_id']);
$report_images_cache_key = BuildCacheKeyName::getInspectReportDetailsImagesKey($this->open_id, $patient_info['patient_id'], $serial_no);
if (!Redis::exists($report_lists_cache_key)) {
throw new GeneralException('找不到报告详情,请重新再试!', Response::HTTP_BAD_REQUEST);
}
$lists = Redis::get($report_lists_cache_key);
$lists = json_decode($lists, true);
$info = [];
foreach ($lists['body'] as $v) {
if ($serial_no === $v['sourceId']) {
$info = $v;
break;
}
}
if (empty($info)) {
throw new GeneralException('找不到报告详情,请重新再试!', Response::HTTP_BAD_REQUEST);
}
$images = [];
if (!Redis::exists($report_images_cache_key)) {
$image_response = $this->his_client->getInspectReportImages(ImageType::IMAGE,$serial_no);
if (isset($image_response['success']) && $image_response['success']) {
$images = $image_response['response']['fileBase64'];
}
// 缓存图片base64进去
if (!empty($images)) {
Redis::setex($report_images_cache_key, 3600, json_encode($images, JSON_UNESCAPED_UNICODE));
}
} else {
$images = Redis::get($report_images_cache_key);
$images = json_decode($images, true);
}
$info['pacs'] = $images;
return $info;
}
/**
* 获取数据库里的患者信息
* @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);
}
return $info;
}
/**
* 获取HIS里的患者信息
* @param string $open_id
* @param string $patient_number
* @return mixed
* @throws GeneralException
*/
protected function getHisPatientInfo(string $open_id, string $patient_number): mixed
{
$info = $this->getPatientInfo($open_id, $patient_number);
$response = $this->his_client->getPatientInfo($info['patient_number'], CardType::OUTPATIENT_NO, $info['name']);
if (!isset($response['success']) || !$response['success']) {
throw new GeneralException($response['msg'] ?? '找不到该就诊卡!', Response::HTTP_SERVICE_UNAVAILABLE);
}
return $response['response'];
}
}

@ -3,7 +3,6 @@
namespace App\Http\Requests\Patient;
use App\Dictionary\Patient\IdentifyCardType;
use Dflydev\DotAccessData\Data;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;

@ -1,77 +0,0 @@
<?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' => '结算类型',
];
}
}

@ -1,37 +0,0 @@
<?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'] ?? ''
];
}
}

@ -1,37 +0,0 @@
<?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'] ?? ''
];
}
}

@ -1,64 +0,0 @@
<?php
namespace App\Http\Resources\Report\Inspect;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class DetailsResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request = null): array
{
if (empty($this->resource)) {
return [];
}
$info = [
'clinic_no' => $this->resource['clinicNo'],
'serial_no' => $this->resource['sourceId'],
'source_form' => $this->resource['sourceFrom'],
'dept_id' => $this->resource['dept']['localCode'],
'dept_name' => $this->resource['dept']['localText'],
'doctor_id' => $this->resource['doctor']['localText'],
'doctor_name' => $this->resource['doctor']['localText'],
'exam_type' => $this->resource['examMethod'],
'exam_name' => $this->resource['examName'],
'exam_purpose' => $this->resource['examPurpose'],
'diagnose' => $this->resource['diagnose'],
'exam_view' => $this->resource['examView'],
'diagnose_opinion' =>$this->resource['diagnoseOpinion'],
'created_time' => $this->resource['createDt'],
'check_time' => $this->resource['checkDt'],
'report_time' => $this->resource['reportDt'],
'comment' => $this->resource['comment'] ?: '',
// 患者信息
'patient_name' => $this->resource['patient']['name'],
'patient_gender' => (int)$this->resource['patient']['sex'],
'patient_birthday' => $this->resource['patient']['dob'],
// 冗余信息
'org_id' => $this->resource['org']['localCode'],
'org_name' => $this->resource['org']['localText'],
'equipment_name' => $this->resource['equipmentName'],
'equipment_no' => $this->resource['equipmentNameNo'] ?: '',
'report_doctor_id' => $this->resource['rpDoctor']['localCode'],
'report_doctor_name' => $this->resource['rpDoctor']['localText'],
'report_org_id' => $this->resource['rpOrg']['localCode'],
'report_org_name' => $this->resource['rpOrg']['localText'],
'crisis_flag' => (int) ($this->resource['crisisFlag'] ?: 0),
'crisis_desc' => $this->resource['crisisDesc'] ?: '',
'crisis_content' => $this->resource['crisisContent'] ?: '',
'unusual_flag' => (int) ($this->resource['unusualFlag'] ?: 0),
'deal_flag' => (int) ($this->resource['dealFlag'] ?: 0),
'need_deal_flag' => (int) ($this->resource['needDealFlag'] ?: 0),
// pacs 图片数组一般为空
'pacs_images' => (array) ($this->resource['pacs'] ?? [])
];
return $info;
}
}

@ -1,51 +0,0 @@
<?php
namespace App\Http\Resources\Report\Inspect;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class ListsResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request = null): array
{
if (empty($this->resource)) {
return [];
}
$lists = [];
foreach ($this->resource['response']['body'] as $k => $v) {
// 过滤掉诊断为接口调用正常的第一条数据
if ($v['diagnose'] === '医院接口调用正常') {
continue;
}
$lists[] = [
'clinic_no' => $v['clinicNo'],
'serial_no' => $v['sourceId'],
'source_form' => $v['sourceFrom'],
'dept_id' => $v['dept']['localCode'],
'dept_name' => $v['dept']['localText'],
'doctor_id' => $v['doctor']['localText'],
'doctor_name' => $v['doctor']['localText'],
'exam_type' => $v['examMethod'],
'exam_name' => $v['examName'],
'exam_purpose' => $v['examPurpose'],
'diagnose' => $v['diagnose'],
'exam_view' => $v['examView'],
'diagnose_opinion' =>$v['diagnoseOpinion'],
'created_time' => $v['createDt'],
'check_time' => $v['checkDt'],
'report_time' => $v['reportDt'],
'comment' => $v['comment'] ?: '',
];
}
return $lists;
}
}

@ -152,12 +152,6 @@ class SendWeChatMessageJob implements ShouldQueue, ShouldBeUnique
*/
public function failed(string $fail_reason): void
{
// 错误信息截断
if (!empty($fail_reason) && strlen($fail_reason) >= 180) {
$fail_reason = mb_substr($fail_reason, 0, 180);
$fail_reason .= '...';
}
$this->message->status = MessageStatus::FAILURE->value;
$this->message->fail_reason = $fail_reason;
$this->message->save();

@ -112,7 +112,7 @@ class Order extends Model
/**
* 获取订单ID
* @param PayType $pay_type 支付类型
* @param string $user W 微信 M 小程序 D 大机器 X 小机器 H his
* @param string $user W 微信 D 大机器 X 小机器 H his
* @return string
*/
public function getOrderId(PayType $pay_type, string $user = 'D'): string

@ -4,7 +4,6 @@ namespace App\Providers;
use App\Services\HisSoap\Client as HisSoapClient;
use App\Services\HisHttp\Client as HisHttpClient;
use App\Services\HisMedicalHttp\Client as HisMedicalHttpClient;
use Illuminate\Support\Facades\Route;
use Illuminate\Routing\UrlGenerator;
use Illuminate\Support\Facades\Schema;
@ -65,10 +64,5 @@ class AppServiceProvider extends ServiceProvider
$this->app->singleton('HisHttpService', function () {
return new HisHttpClient();
});
// His平台服务 - medical http
$this->app->singleton('HisMedicalHttpClient', function () {
return new HisMedicalHttpClient();
});
}
}

@ -7,9 +7,6 @@ use App\Utils\SM4;
use App\Utils\Traits\HttpRequest;
use Exception;
/**
* 广东省健康档案授权
*/
class Client
{
use HttpRequest;

@ -3,11 +3,8 @@ declare(strict_types=1);
namespace App\Services\HisHttp;
use App\Dictionary\Medical\PayType as MedicalPayType;
use App\Dictionary\Medical\SettleType;
use App\Dictionary\Patient\CardType;
use App\Dictionary\Patient\Sex;
use App\Dictionary\Report\ImageType;
use App\Exceptions\GeneralException;
use App\Utils\Traits\Logger;
use App\Utils\Transfer\HisHttpClient\ClientFactory;
@ -27,7 +24,7 @@ class Client
*/
public function __construct()
{
$his_name = 'his_http';
$his_name = 'his_api';
$this->service = ClientFactory::getClientTransfer($his_name);
$this->setChannel($his_name);
}
@ -648,53 +645,4 @@ class Client
}
// Dictionary Module End
// Inspect/Check Module Start
/**
* 获取检查报告列表
* @param string $patient_name 患者姓名
* @param string $card_no 证件号,居民注册时的证件号,不区分证件类型,一般为身份证
* @param string $start_date 查询起始日期,查询时间跨度必须小于3个月
* @param string $end_date 查询截止日期
* @param string $bar_code 条码,由患者直接录入或者扫描线下单据(可能是发票,处方等)获得
* @param string $source_id 报告标识,机构内部唯一标识
* @return mixed
* @throws GeneralException
*/
public function getInspectReportLists(string $patient_name, string $card_no, string $start_date, string $end_date, string $bar_code = '', string $source_id = ''): mixed
{
return $this->requestHandle('POST', 'orientQueryExamReport', [
'json' => [
'patientName' => $patient_name,
'orgId' => '45592623X44040211A1001', // 机构ID 珠海市香洲区第二人民医院
'barCode' => $bar_code,
'SourceId' => $source_id,
'certificateNo' => $card_no,
'startDt' => $start_date,
'endDt' => $end_date,
... $this->commonRequestData()
]
]);
}
/**
* 获取检查报告影像结果
* @param ImageType $type 选择查询的类型, styid=查询胶片id,入参(studyid); img=获取胶片,入参(胶片id); img64=获取胶片base64编码,入参(胶片id); pdf=获取报告pdf文件,入参(studyid); pdf64=获取报告pdf的base64编码,入参(studyid)
* @param string $report_id 根据类型字段选择入参 styid,pdf,pdf64入参为studyid;img,img64为胶片id;
* @return mixed
* @throws GeneralException
*/
public function getInspectReportImages(ImageType $type, string $report_id): mixed
{
return $this->requestHandle('POST', 'QueryImageReport', [
'json' => [
'fileType' => $type,
'reportId' => $report_id,
... $this->commonRequestData()
]
]);
}
// Inspect/Check Module End
}

@ -1,293 +0,0 @@
<?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()
]
]);
}
}

@ -1,181 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Services\MedicalAuth;
use App\Utils\Traits\HttpRequest;
use App\Utils\Traits\Logger;
use Exception;
// use JetBrains\PhpStorm\ArrayShape;
/**
* 医保移动支付免密授权 - 微信端
*
* @link https://yb.qq.com/yibao-payment/doc?nodeId=83679977515675648
*/
class Client
{
use Logger;
use HttpRequest;
/**
* 配置数组
* @var string[]
*/
/*#[ArrayShape([
'app_id' => "string",
'partner_id' => "string",
'partner_secret' => "string",
'channel' => "string",
'org_chnl_crtf_codg' => "string",
'org_codg' => "string",
'org_app_id' => "string",
'auth_return_url' => "string",
])]*/
private array $config;
/**
* 城市编码 441721 香洲区
* @var string
*/
private string $city_code = '441721';
/**
* 授权查询host地址
* @var string
*/
private string $host = 'https://test-receiver.wecity.qq.com';
// private string $host = 'https://mip-receiver.tengmed.com';
/**
* 免密授权host地址
* @var string
*/
private string $auth_host = 'https://mitest.wecity.qq.com';
// private string $auth_host = 'https://card.wecity.qq.com';
/**
* Authorization Class Construct.
*/
public function __construct()
{
$this->config = config('wechat.medical.auth');
}
/**
* 获取签名
* @param string $timestamp
* @return string
*/
private function getSign(string $timestamp): string
{
return hash_hmac("sha256", $this->config['partner_id'] . $timestamp, $this->config['partner_secret']);
}
/**
* 获取RequestId
* @return string
*/
private function getRequestId(): string
{
$char_id = strtoupper(md5(uniqid((string)mt_rand(), true)));
return substr($char_id, 0, 8)
. substr($char_id, 8, 4)
. substr($char_id, 12, 4)
. substr($char_id, 16, 4)
. substr($char_id, 20, 12);
}
/**
* 获取毫秒
* @return int
*/
private function getMillisecond(): int
{
[$t1, $t2] = explode(' ', microtime());
return (int)sprintf('%.0f', ((float)$t1 + (float)$t2) * 1000);
}
/**
* @param string $endpoint
* @param array $params
* @param string $method
* @param array $options
* @return array|mixed
*/
protected function requestHandle(string $endpoint, array $params = [], string $method = 'post', array $options = []): mixed
{
$timestamp = $this->getMillisecond();
$params = array_merge([
'base_uri' => $this->host,
'headers' => [
'god-portal-timestamp' => $timestamp,
'god-portal-request-id' => $this->getRequestId(),
'god-portal-signature' => $this->getSign((string)$timestamp),
],
'json' => $params
], $options);
try {
$response = $this->request($method, $endpoint, $params);
$this->writerLog($endpoint, $params, $response);
return $response;
} catch (Exception $e) {
$err_msg = "{$e->getMessage()} ON {$e->getFile()}:{$e->getLine()}";
$this->info($endpoint, $params, $err_msg);
return [
'code' => -1,
//'message' => $err_msg
'message' => $e->getMessage()
];
}
}
/**
* 获取小程序授权跳转地址
* @return string
*/
public function getMiniProgramAuthRedirectUrl(): string
{
$url = 'auth/pages/bindcard/auth/index';
$data = [
'openType' => 'getAuthCode',
'cityCode' => $this->config['city_code'],
'channel' => $this->config['channel'],
'orgChnlCrtfCodg' => $this->config['org_chnl_crtf_codg'],
'orgCodg' => $this->config['org_codg'],
'bizType' => '04107',
'orgAppId' => $this->config['org_app_id'],
];
$redirect_url = $url . '?' . http_build_query($data);
$this->writerLog($url, $data, '');
return $redirect_url;
}
/**
* 查询用户授权
* @param string $auth_code
* @param string $open_id
* @return mixed
*/
public function userQuery(string $auth_code, string $open_id): mixed
{
$uri = '/api/mipuserquery/userQuery/' . $this->config['partner_id'];
$param = [
'qrcode' => $auth_code,
'openid' => $open_id,
];
return $this->requestHandle($uri, $param);
}
}

@ -1,6 +1,5 @@
<?php
use App\Dictionary\Patient\IdentifyCardType;
use EasyWeChat\MiniApp\Application as MiniApplication;
use EasyWeChat\Pay\Application as PayApplication;
use Illuminate\Http\JsonResponse;
@ -208,7 +207,7 @@ if (!function_exists('getIDCardType')) {
}
// 港澳台 社保卡号
if (preg_match("/^(HKG|MAC|TWN)[0-9]{9}$/", $id_card)) {
if (preg_match("/^[HKG|MAC|TWN][0-9]{9}$/", $id_card)) {
return 2;
}

@ -1,101 +0,0 @@
<?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}";
}
}

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace App\Utils\Traits;
trait BuildCacheKeyName
{
/**
* 获取 门诊待缴费列表 缓存键名
* @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;
}
}

@ -221,7 +221,7 @@ trait SendSubscribeMessage
// 获取取药或执行地址
$extra_info = json_decode($record->extra_info, true);
$pay_address = $extra_info['confirm_response']['phyAddress'] ?? ' ';
$pay_address = $extra_info['confirm_response']['phyAddress'] ?? '';
$subscribe_id = SubscribeId::OUTPATIENT_PAYMENT_SUCCESS;
$data = [
'touser' => $order->open_id,

@ -3,7 +3,6 @@
declare(strict_types=1);
namespace App\Utils\Traits;
trait UniversalEncryption
{
private string $key;

@ -1,25 +0,0 @@
<?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),
};
}
}

@ -1,62 +0,0 @@
<?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
];
}
}
}

@ -1,128 +0,0 @@
<?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;
}
}

@ -10,7 +10,5 @@ return [
// 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
'mini_program_message_state' => 'developer',
// 支付通知地址
'payment_notify_url' => env('APP_URL'). '/Api/notify',
// 免密授权跳转小程序APP ID
'redirect_app_id' => 'wxe183cd55df4b4369'
'payment_notify_url' => env('APP_URL'). '/Api/notify'
];

@ -12,8 +12,8 @@ return [
// 是否模拟数据
'is_mock' => true,
],
// HIS HTTP服务 -- 方鼎中间件转发
'his_http' => [
// HIS API服务 -- 方鼎中间件转发
'his_api' => [
// 测试地址
'url' => 'http://192.168.61.45:8809/api/WeChatDomain/',
// 不记录日志的请求数组
@ -24,18 +24,5 @@ return [
'options' => [
'timeout' => 60
]
],
// HIS Medical HTTP服务
'his_medical_http' => [
// 测试地址
'url' => 'http://192.168.61.44:8010/api/his/',
// 不记录日志的请求数组
'not_log_arr' => [''],
// 是否模拟数据
'is_mock' => true,
// guzzle/client options
'options' => [
'timeout' => 60
]
],
]
];

@ -174,15 +174,6 @@ return [
'max_files' => 360,
],
// HisMedicalApi
'his_medical_http' => [
'driver' => 'custom',
'via' => GeneralDailyLogger::class,
'service_type' => 'HisMedicalHttpLog',
'level' => Level::Info,
'max_files' => 360,
],
// 挂号日志
'registration' => [
'driver' => 'custom',
@ -226,9 +217,7 @@ return [
'service_type' => 'SendWeChatMessage',
'level' => Level::Info,
'max_files' => 0,
],
//
]
],
];

@ -16,9 +16,9 @@ return [
| apiSecret: 应用秘钥
|
*/
'domain' => 'http://localhost:9040/',
'domain' => 'http://fangding.picp.vip:7878/',
'apiSecret' => 'a2800bac-3384-4ad3-9002-89af6c44a3ab',
'apiSecret' => '16f62721-c156-4514-8743-bd969bed32af',
/*
|--------------------------------------------------------------------------
@ -36,11 +36,11 @@ return [
'channels' => [
'default' => [
'merchantId' => 'ZHXZEY20250228',
'secret' => '2a62ff796006a7a2b2d06e40ea240de9',
'merchantId' => 'ZHXZEY20250124',
'secret' => '94aaadd840a3b97c6bbee4f440b986ce',
'channelId' => 'FD001',
'operatorId' => [
'MINI001',
'MN001',
],
]

@ -1,113 +0,0 @@
<?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,
]
];

@ -155,27 +155,5 @@ return [
'timeout' => 5.0,
// 'base_uri' => 'https://api.mch.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri
],
],
'medical' => [
/*
|--------------------------------------------------------------------------
| Medical Auth 配置
|--------------------------------------------------------------------------
|
| 医保免密授权对应配置数据
| 详见:https://yb.qq.com/yibao-payment/doc?nodeId=83679977515675648
|
*/
'auth' =>[
'app_id' => env('WECHAT_MEDICAL_AUTH_APP_ID', ''),// AppID
'partner_id' => env('WECHAT_MEDICAL_AUTH_PARTNER_ID', ''), // 合作方ID
'partner_secret' => env('WECHAT_MEDICAL_AUTH_PARTNER_SECRET', ''), // 合作方秘钥
'city_code' => env('WECHAT_MEDICAL_AUTH_CITY_CODE', ''), // 城市ID 香洲区
'channel' => env('WECHAT_MEDICAL_AUTH_CHANNEL', ''), // 渠道号(微信医保平台分配)
'org_chnl_crtf_codg' => env('WECHAT_MEDICAL_AUTH_ORG_CHNL_CRTF_CODG', ''), // 机构渠道认证编码
'org_codg' => env('WECHAT_MEDICAL_AUTH_ORG_CODG', ''), // 定点医药机构编码
'org_app_id' => env('WECHAT_MEDICAL_AUTH_ORG_APP_ID', ''), // 定点医药机构应用ID
],
],
]
];

@ -243,8 +243,6 @@ class BasicClient
throw new RuntimeException('Generate Sign Error:' . $e->getMessage());
}
Log::debug('Generate Sign Content', [$data, $buff]);
return trim($buff);
}

@ -1,49 +0,0 @@
<?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)
];
}
}

@ -8,7 +8,6 @@ use ReflectionException;
use UnifyPayment\Cores\BasicClient;
use UnifyPayment\Cores\Exceptions\InvalidConfigException;
use UnifyPayment\Cores\Exceptions\RuntimeException;
use UnifyPayment\Cores\Struct\CreateMedicalInsuranceOrder;
use UnifyPayment\Cores\Struct\CreateOrder;
class MiniProgramClient extends BasicClient
@ -25,7 +24,7 @@ class MiniProgramClient extends BasicClient
* @throws InvalidConfigException
* @throws RuntimeException
*/
public function jsapi(CreateOrder $order): array|string
public function jsapi(CreateOrder $order)
{
$this->payload = array_merge($this->payload, $order->toArray());
$this->payload['sign'] = $this->generateSign($this->payload);
@ -33,22 +32,4 @@ class MiniProgramClient extends BasicClient
return $this->request('post', '/index.php/api/pay/miniapp');
}
/**
* WeChat Mini Program Medical Insurance Payment
*
* @param CreateMedicalInsuranceOrder $order
*
* @return array|string
*
* @throws InvalidConfigException
* @throws ReflectionException
* @throws RuntimeException
*/
public function medicalPay(CreateMedicalInsuranceOrder $order): array|string
{
$this->payload = array_merge($this->payload, $order->toArray());
$this->payload['sign'] = $this->generateSign($this->payload);
return $this->request('post', '/index.php/api/pay/mpInsurance');
}
}

@ -8,7 +8,6 @@ use ReflectionException;
use UnifyPayment\Cores\BasicClient;
use UnifyPayment\Cores\Exceptions\InvalidConfigException;
use UnifyPayment\Cores\Exceptions\RuntimeException;
use UnifyPayment\Cores\Struct\CreateMedicalInsuranceOrder;
use UnifyPayment\Cores\Struct\CreateOrder;
class OfficialAccountClient extends BasicClient
@ -25,30 +24,11 @@ class OfficialAccountClient extends BasicClient
* @throws InvalidConfigException
* @throws RuntimeException
*/
public function jsapi(CreateOrder $order): array|string
public function jsapi(CreateOrder $order)
{
$this->payload = array_merge($this->payload, $order->toArray());
$this->payload['sign'] = $this->generateSign($this->payload);
return $this->request('post', '/index.php/api/pay/mp');
}
/**
* WeChat Official Account Medical Insurance Payment
*
* @param CreateMedicalInsuranceOrder $order
*
* @return array|string
*
* @throws InvalidConfigException
* @throws ReflectionException
* @throws RuntimeException
*/
public function medicalPay(CreateMedicalInsuranceOrder $order): array|string
{
$this->payload = array_merge($this->payload, $order->toArray());
$this->payload['sign'] = $this->generateSign($this->payload);
return $this->request('post', '/index.php/api/pay/mpInsurance');
}
}

@ -1,9 +1,7 @@
<?php
use UnifyPayment\Cores\Struct\CreateMedicalInsuranceOrder;
use UnifyPayment\Cores\Struct\CreateOrder;
use UnifyPayment\Factory;
use UnifyPayment\Mock\CreateMedicalInsuranceOrderHandler;
use UnifyPayment\Mock\CreateOrderHandler;
class PayTest extends TestCase
@ -30,26 +28,6 @@ class PayTest extends TestCase
], array_keys($response['response']));
}
public function testMiniProgramMedicalPay()
{
$order = new CreateMedicalInsuranceOrder('DiagPay', 'ow_gnt5QQQ-Qby1234567890', '202204022005169952975171534816', '某某附属医院', '70', '1', 'https://kyey.test.com/pay/notify/wechatINSUNotify', '门诊缴费', 'https://kyey.test.com/wehospital/payment/paymentindex', '2', 'H53011234567', '20220402200530', '530100', '202204022005169952975171534816', '1', '0ef5c63f34237aeba58d0a6f97f66c13', '姓x', '4', '70', '0', '{"payAuthNo":"AUTH530100202204022006310000034","payOrdId":"ORD530100202204022006350000021","setlLatlnt":"102.682296,25.054260"}', 'AAGN9uhZc5EGyRdairKW7Qnu', 'bd787ae69021fbd7d0b86d080b007bad', '127.0.0.1');
$mockHandler = new CreateMedicalInsuranceOrderHandler(true);
$response = Factory::pay($this->getConfig())->mini->setMockHandler([$mockHandler])->medicalPay($order);
$this->assertArrayHasKey('response', $response);
$this->assertEquals([
'payUrl',
'medTransId',
'payAppId',
'orderNo',
'merchantId',
'merchantName',
'channelId',
'channelName'
], array_keys($response['response']));
}
public function testOfficialAccountJsapi()
{
$order = new CreateOrder('当天挂号', 'FD2022080310000055123451223', 0.01, '1', '002197|公众号', 'A', 'ovIGQwOy1e-Zptyug20l5hqI0P5Q', 'https://www.baidu.com');
@ -71,24 +49,4 @@ class PayTest extends TestCase
'channelName'
], array_keys($response['response']));
}
public function testOfficialAccountMedicalPay()
{
$order = new CreateMedicalInsuranceOrder('DiagPay', 'ow_gnt5QQQ-Qby1234567890', '202204022005169952975171534816', '某某附属医院', '70', '1', 'https://kyey.test.com/pay/notify/wechatINSUNotify', '门诊缴费', 'https://kyey.test.com/wehospital/payment/paymentindex', '2', 'H53011234567', '20220402200530', '530100', '202204022005169952975171534816', '1', '0ef5c63f34237aeba58d0a6f97f66c13', '姓x', '4', '70', '0', '{"payAuthNo":"AUTH530100202204022006310000034","payOrdId":"ORD530100202204022006350000021","setlLatlnt":"102.682296,25.054260"}', 'AAGN9uhZc5EGyRdairKW7Qnu', 'bd787ae69021fbd7d0b86d080b007bad', '127.0.0.1');
$mockHandler = new CreateMedicalInsuranceOrderHandler(true);
$response = Factory::pay($this->getConfig())->official->setMockHandler([$mockHandler])->medicalPay($order);
$this->assertArrayHasKey('response', $response);
$this->assertEquals([
'payUrl',
'medTransId',
'payAppId',
'orderNo',
'merchantId',
'merchantName',
'channelId',
'channelName'
], array_keys($response['response']));
}
}

@ -4,17 +4,14 @@ use App\Http\Controllers\Auth\AuthController;
use App\Http\Controllers\Hospital\IntroduceController;
use App\Http\Controllers\Message\TriggerController;
use App\Http\Controllers\Notify\NotifyController;
use App\Http\Controllers\Outpatient\MedicalController as OutpatientMedicalController;
use App\Http\Controllers\Outpatient\PaymentController;
use App\Http\Controllers\Outpatient\PendingController;
use App\Http\Controllers\Outpatient\RecordController as OutpatientRecordController;
use App\Http\Controllers\Patient\PatientController;
use App\Http\Controllers\Registration\MedicalController as RegistrationMedicalController;
use App\Http\Controllers\Registration\RecordController as RegistrationRecordController;
use App\Http\Controllers\Registration\RegisterController;
use App\Http\Controllers\Registration\ScheduleController;
use App\Http\Controllers\Dictionary\ItemController;
use App\Http\Controllers\Report\InspectController;
use App\Http\Controllers\Test\TestController;
use Illuminate\Support\Facades\Route;
@ -41,9 +38,6 @@ Route::middleware(['apiLog'])->group(function() {
// 获取手机号码
Route::get('/phone', [PatientController::class, 'getPhoneNumber']);
// 定点签约
Route::post('/{patient_id}/designate', [PatientController::class, 'designated']);
});
// 挂号模块
@ -53,32 +47,16 @@ Route::middleware(['apiLog'])->group(function() {
Route::get('/doctor', [ScheduleController::class, 'doctor']);
Route::post('/{patient_id}/register', [RegisterController::class, 'register']);
// 医保预结算
Route::post('/{patient_id}/medical/settle', [RegistrationMedicalController::class, 'registerPreSettle']);
// 医保支付
Route::post('/{patient_id}/medical/payment', [RegistrationMedicalController::class, 'register']);
Route::get('/{patient_id}/record', [RegistrationRecordController::class, 'lists']);
Route::post('/{patient_id}/record/{serial_no}/refund', [RegistrationRecordController::class, 'refund']);
});
// 医保模块
Route::prefix('Medical')->group(function () {
Route::get('/auth/path', [OutpatientMedicalController::class, 'redirectAuth']);
Route::post('/auth/{patient_id}/info', [OutpatientMedicalController::class, 'userAuthInfo']);
});
// 缴费模块
Route::prefix('Outpatient')->group(function () {
Route::get('/{patient_id}/pending', [PendingController::class, 'lists']);
Route::get('/{patient_id}/pending/{serial_no}/', [PendingController::class, 'details']);
Route::post('/{patient_id}/pending/{serial_no}/payment', [PaymentController::class, 'payment']);
// 医保预结算
Route::post('/{patient_id}/pending/{serial_no}/medical/settle', [OutpatientMedicalController::class, 'prescriptionPreSettle']);
// 医保支付
Route::post('/{patient_id}/pending/{serial_no}/medical/payment', [OutpatientMedicalController::class, 'payment']);
Route::get('/{patient_id}/record', [OutpatientRecordController::class, 'lists']);
Route::get('/{patient_id}/record/{serial_no}/', [OutpatientRecordController::class, 'details']);
});
@ -88,13 +66,6 @@ Route::middleware(['apiLog'])->group(function() {
Route::get('/', [ItemController::class, 'lists']);
Route::get('/{type_id}', [ItemController::class, 'details'])->where('type_id', '[0-9]+');
});
// 报告相关模块
Route::prefix('Report')->group(function () {
Route::get('/tips', [InspectController::class, 'tips']);
Route::get('/{patient_id}/inspect', [InspectController::class, 'lists']);
Route::get('/{patient_id}/inspect/{serial_no}/', [InspectController::class, 'details']);
});
});
// 医院详情相关项目

Loading…
Cancel
Save