feat: 添加挂号自费流程,添加科室和医生排序流程,添加发送订阅消息代码

fix: 修正patient_id和patient_number的关系
master
Rmiku 1 month ago
parent bd580f4093
commit b8f34743b4
  1. 26
      app/Http/Controllers/Notify/NotifyController.php
  2. 4
      app/Http/Controllers/Patient/PatientController.php
  3. 4
      app/Http/Controllers/Registration/RecordController.php
  4. 2
      app/Http/Controllers/Registration/RegisterController.php
  5. 6
      app/Http/Controllers/Registration/ScheduleController.php
  6. 49
      app/Http/Logics/Notify/NotifyLogic.php
  7. 4
      app/Http/Logics/Outpatient/PaymentLogic.php
  8. 41
      app/Http/Logics/Patient/PatientLogic.php
  9. 35
      app/Http/Logics/Registration/RecordLogic.php
  10. 135
      app/Http/Logics/Registration/RegisterLogic.php
  11. 3
      app/Http/Logics/Registration/ScheduleLogic.php
  12. 3
      app/Http/Requests/Registration/RegisterRequest.php
  13. 2
      app/Http/Resources/Patient/PatientDetailsResource.php
  14. 2
      app/Http/Resources/Patient/PatientListsResource.php
  15. 38
      app/Http/Resources/Registration/Schedule/DeptListsResource.php
  16. 71
      app/Http/Resources/Registration/Schedule/DoctorListsResource.php
  17. 3
      app/Models/Order.php
  18. 16
      app/Models/Patient.php
  19. 4
      app/Services/HisHttp/Client.php
  20. 35
      app/Utils/Traits/RedisLockUtil.php
  21. 41
      app/Utils/Traits/SendSubscribeMessage.php
  22. 15
      app/Utils/Transfer/HisHttpClient/ClientMockHttpTransfer.php
  23. 21
      config/database.php
  24. 11
      config/logging.php
  25. 3
      database/migrations/2023_03_03_075720_create_patients_table.php

@ -49,33 +49,9 @@ class NotifyController extends Controller
// 建议是拿订单号调用微信支付查询接口,
$this->info('接收回调消息', $message->toArray());
try{
// 验证通过,业务处理
// 验证签名通过,业务处理
$app->getValidator()->validate($app->getRequest());
// 查询订单
$response = $app->getClient()->postXml(V2Api::QUERY_ORDER->value, [
'body' => [
'appid' => config('wechat.payment.app_id'),
'mch_id' => config('wechat.payment.mch_id'),
'out_trade_no' => $message->out_trade_no
]
]);
if ($response->isFailed()) {
// 查询失败
$this->warning('订单查询失败', [$message->out_trade_no]);
return $next($message);
}
// 不成功
if (
!isset($response['return_code'], $response['result_code']) ||
$response["return_code"] !== "SUCCESS" ||
$response["result_code"] !== "SUCCESS"
) {
$this->warning('订单查询支付失败', [$message->out_trade_no, $response]);
return $next($message);
}
// 业务处理
$result = $this->notify_logic->notifyHandle($message);

@ -79,7 +79,7 @@ class PatientController
/**
* 设置默认状态
* @param Request $request
* @param string $patient_id
* @param string $patient_id 此处为 patient_number
* @return JsonResponse
* @throws GeneralException
*/
@ -93,7 +93,7 @@ class PatientController
/**
* 删除
* @param Request $request
* @param string $patient_id
* @param string $patient_id 此处为 patient_number
* @return JsonResponse
* @throws GeneralException
*/

@ -28,7 +28,7 @@ class RecordController
/**
* 获取挂号记录列表
* @param Request $request
* @param string $patient_id
* @param string $patient_id 此处为 patient_number
* @return JsonResponse
* @throws GeneralException
*/
@ -51,7 +51,7 @@ class RecordController
/**
* 退号
* @param Request $request
* @param string $patient_id
* @param string $patient_id 此处为 patient_number
* @param string $serial_no
* @return JsonResponse
* @throws GeneralException

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

@ -32,10 +32,11 @@ class ScheduleController
public function dept(Request $request): JsonResponse
{
$validated = $request->validate([
'date' => 'required|date_format:Y-m-d',
'date' => 'required|date_format:Y-m-d|after_or_equal:today',
],[
'date.required' => '请传入日期',
'date.date_format' => '日期格式错误',
'date.after_or_equal' => '日期不能小于今天'
]);
$response = $this->schedule_logic->getDeptLists($validated['date']);
@ -52,11 +53,12 @@ class ScheduleController
public function doctor(Request $request): JsonResponse
{
$validated = $request->validate([
'date' => 'required|date_format:Y-m-d',
'date' => 'required|date_format:Y-m-d||after_or_equal:today',
'dept_id' => 'required|numeric'
], [
'date.required' => '请传入日期',
'date.date_format' => '日期格式错误',
'date.after_or_equal' => '日期不能小于今天',
'dept_id.required' => '请传入科室ID',
'dept_id.numeric' => '科室ID格式错误',
]);

@ -24,8 +24,10 @@ use RedisException;
use Exception;
use ReflectionException;
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;
@ -64,6 +66,13 @@ class NotifyLogic
*/
public function notifyHandle(Message $notify): bool
{
// 查询支付平台,订单是否正常
$result = $this->unifyQuery($notify->out_trade_no);
if (!$result) {
return false;
}
// 订单加锁,防止并发
$lock_id = $this->addLockOrder($notify->out_trade_no);
if (!$lock_id) {
return false;
@ -120,7 +129,6 @@ class NotifyLogic
protected function registrationOrderHandle(OrderModel $order_info, Message $notify): void
{
// 挂号确认
$patient = $order_info->patient;
$record = $order_info->registrationRecord;
$extra = json_decode($record->extra_info, true);
$pay_time = strtotime($notify->time_end);
@ -132,7 +140,7 @@ class NotifyLogic
$record->docttor_id,
$record->reg_id,
$extra['SHIFT']['RANKID'],
$record->date,
$record->visit_date,
PayType::WECHAT_PAY->hisCode(),
$order_info->order_id,
'',
@ -145,18 +153,18 @@ class NotifyLogic
$response = $this->his_client->registerConfirm(... $data);
$this->info('挂号订单出入参:'.$order_info->order_id, [$data, $response]);
if (isset($response['RESULTCODE']) && $response['RESULTCODE'] === '0') {
if (isset($response['success']) && $response['success'] === true) {
// 成功流程
$order_info->orderConfirm($order_info->order_id, $response['VISITNO'], $response);
$order_info->orderConfirm($order_info->order_id, $response['visitNo'], $response);
// 支付平台业务确认
$this->unifyConfirm($notify['out_trade_no'], $response['VISITNO'], $notify['openid'], $notify['transaction_id']);
// 推送成功
$this->sendRegistrationSuccessMessage($order_info);
} else if (isset($response['RESULTCODE'])) {
} else if (isset($response['success'])) {
// 失败流程
$this->handleOrderReverse($order_info, $response['ERRORMSG'] ?? '');
$this->handleOrderReverse($order_info, $response['msg'] ?? '');
// 推送失败
$this->sendRegistrationFailureMessage($order_info);
@ -175,7 +183,7 @@ class NotifyLogic
* @param Message $notify
* @throws GeneralException
*/
protected function outpatientOrderHandle(OrderModel $order_info, Message $notify): void
public function outpatientOrderHandle(OrderModel $order_info, Message $notify): void
{
// 挂号确认
$patient = $order_info->patient;
@ -316,6 +324,31 @@ class NotifyLogic
return $result;
}
/**
* 支付平台业务查询
* @param string $order_id
* @return bool
*/
protected function unifyQuery(string $order_id): bool
{
try {
$query_order = new QueryOrder($order_id);
$response = Unify::common(config('unify'))->order->setMockHandler([new QueryOrderHandler(true)])->query($query_order);
$this->info('支付平台查询结果', $response);
if ($response['status'] === 200 && $response['success'] === true) {
return true;
}
return false;
} catch (ReflectionException|Exception $e) {
$err_msg = "订单号:{$order_id}, 支付平台查询结果异常,错误消息:{$e->getMessage()} ON {$e->getFile()}:{$e->getLine()}";
$this->error($err_msg);
return false;
}
}
/**
* 支付平台业务确认
* @param string $order_id
@ -331,7 +364,7 @@ class NotifyLogic
$response = Unify::common(config('unify'))->order->setMockHandler([new ConfirmOrderForExHandler(true)])->confirmForEx($confirm_order);
$this->info('支付平台确认结果', $response);
if ($response['status'] === 200 || $response['success'] === true) {
if ($response['status'] === 200 && $response['success'] === true) {
return true;
}

@ -181,8 +181,8 @@ class PaymentLogic
$total_fee * 100,
0,
$this->open_id,
$patient_info['PATIENTID'],
$patient_info['NAME'],
$patient_info['patientNumber'],
$patient_info['name'],
$order_type,
SourceId::MINI_PROGRAM,
$pay_record_data

@ -51,19 +51,19 @@ class PatientLogic
/**
* 卡详情
* @param string $patient_id
* @param string $patient_number
* @return Patient
* @throws GeneralException
*/
public function getPatientDetails(string $patient_id): Patient
public function getPatientDetails(string $patient_number): Patient
{
$info = $this->patient_model->getBindPatientInfoByPatientId($this->open_id, $patient_id);
$info = $this->patient_model->getBindPatientInfoByPatientNumber($this->open_id, $patient_number);
if(empty($info)) {
throw new GeneralException('找不到该就诊卡!');
}
// 获取患者信息
$response = $this->his_client->getPatientInfo($info['patient_id'], CardType::OUTPATIENT_NO, $info['name']);
$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);
}
@ -157,16 +157,17 @@ class PatientLogic
throw new GeneralException('建档失败,失败原因:'. $response['msg'] ?? '未知错误', Response::HTTP_SERVICE_UNAVAILABLE);
}
$patient_id = $response['response']['patientNumber'];
$patient_id = $response['response']['patientId'];
$patient_number = $response['response']['patientNumber'];
// 写入数据库
$result = $this->patient_model->createPatient($this->union_id, $this->open_id, $patient_id, $data['name'], $sex);
$result = $this->patient_model->createPatient($this->union_id, $this->open_id, $patient_id, $patient_number, $data['name'], $sex);
if (!$result) {
throw new GeneralException('数据保存失败,请重试!', Response::HTTP_INTERNAL_SERVER_ERROR);
}
$this->sendBindPatientSubscribeMessage($result->id, $data['name']);
$this->sendBindPatientSubscribeMessage($result->id, $result->id, $data['name']);
return $patient_id;
}
@ -197,9 +198,11 @@ class PatientLogic
}
$patient_info = &$response['response'];
$patient_id = &$patient_info['response']['patientId'];
$patient_number = &$patient_info['response']['patientNumber'];
$sex = Sex::from((int) $patient_info['SEX']);
if ($patient_info['patientNumber'] != $data['patient_id']) {
if ($patient_info['patientNumber'] != $data['patient_number']) {
throw new GeneralException('该证件号已建档,但就诊卡号不匹配!');
}
@ -222,30 +225,30 @@ class PatientLogic
}
// 写入数据库
$result = $this->patient_model->createPatient($this->union_id, $this->open_id, $patient_info['patientNumber'], $data['name'], $sex);
$result = $this->patient_model->createPatient($this->union_id, $this->open_id, $patient_id, $patient_number, $data['name'], $sex);
if (!$result) {
throw new GeneralException('数据保存失败,请重试!', Response::HTTP_INTERNAL_SERVER_ERROR);
}
$this->sendBindPatientSubscribeMessage($this->open_id, $result->id, $data['name']);
return $patient_info['patientNumber'];
return $patient_number;
}
/**
* 设置默认就诊卡
* @param string $patient_id
* @param string $patient_number
* @return bool
* @throws GeneralException
*/
public function setDefaultPatient(string $patient_id): bool
public function setDefaultPatient(string $patient_number): bool
{
$info = $this->patient_model->getBindPatientInfoByPatientId($this->open_id, $patient_id);
$info = $this->patient_model->getBindPatientInfoByPatientNumber($this->open_id, $patient_number);
if (empty($info)) {
throw new GeneralException('该就诊卡不存在!');
}
$result = $this->patient_model->setDefaultPatient($this->open_id, $patient_id);
$result = $this->patient_model->setDefaultPatient($this->open_id, $info->patient_id);
if (!$result) {
throw new GeneralException('设置失败,请稍后再试!', Response::HTTP_INTERNAL_SERVER_ERROR);
}
@ -255,23 +258,23 @@ class PatientLogic
/**
* 解绑
* @param string $patient_id
* @param string $patient_number
* @return bool
* @throws GeneralException
*/
public function cancelBindPatient(string $patient_id): bool
public function cancelBindPatient(string $patient_number): bool
{
$info = $this->patient_model->getBindPatientInfoByPatientId($this->open_id, $patient_id);
$info = $this->patient_model->getBindPatientInfoByPatientNumber($this->open_id, $patient_number);
if (empty($info)) {
throw new GeneralException('该就诊卡不存在!');
}
$result = $this->patient_model->deletePatient($this->open_id, $patient_id);
$result = $this->patient_model->deletePatient($this->open_id, $info->patient_id);
if (!$result) {
throw new GeneralException('解绑失败,请稍后再试!', Response::HTTP_INTERNAL_SERVER_ERROR);
}
$this->sendUnbindPatientSubscribeMessage($this->open_id, $info->id, $info['name'], '', $info['patient_id']);
$this->sendUnbindPatientSubscribeMessage($this->open_id, $info->id, $info['name'], '', $patient_number);
return true;
}
}

@ -8,6 +8,7 @@ use App\Dictionary\Order\SourceId;
use App\Dictionary\Order\Type;
use App\Exceptions\GeneralException;
use App\Models\Order;
use App\Models\Patient;
use App\Models\RegistrationRecord;
use App\Services\HisHttp\Client;
use App\Utils\Traits\Logger;
@ -28,6 +29,8 @@ class RecordLogic
private RegistrationRecord $reg_record_model;
private Patient $patient_model;
private Order $order_model;
/**
@ -40,19 +43,23 @@ class RecordLogic
$this->setChannel('refund');
$this->his_client = app('HisHttpService');
$this->reg_record_model = new RegistrationRecord();
$this->patient_model = new Patient();
$this->order_model = new Order();
}
/**
* 获取挂号记录列表
* @param string $patient_id
* @param string $patient_number
* @param string $start_date
* @param string $end_date
* @return array
* @throws GeneralException
*/
public function getRecordLists(string $patient_id, string $start_date, string $end_date): array
public function getRecordLists(string $patient_number, string $start_date, string $end_date): array
{
$patient_info = $this->getPatientInfo($this->open_id, $patient_number);
$patient_id = $patient_info->patient_id;
$response = $this->his_client->getRegisterRecordLists($patient_id, $start_date, $end_date);
if (!isset($response['RESULTCODE']) || $response['RESULTCODE'] !== '0') {
@ -66,13 +73,16 @@ class RecordLogic
/**
* 退号
* @param string $patient_id
* @param string $patient_number
* @param string $reg_serial_no
* @return true
* @throws GeneralException
*/
public function refundRegisterRecord(string $patient_id, string $reg_serial_no): true
public function refundRegisterRecord(string $patient_number, string $reg_serial_no): true
{
$patient_info = $this->getPatientInfo($this->open_id, $patient_number);
$patient_id = $patient_info->patient_id;
$cache_key = 'Registration.Record.'. $this->open_id.'.'. $patient_id;
$record_info = Cache::get($cache_key);
@ -164,4 +174,21 @@ class RecordLogic
$this->order_model->reverseOrderOpera($refund_order_id, $fee, true);
return true;
}
/**
* 获取患者信息
* @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;
}
}

@ -13,7 +13,9 @@ use App\Models\Patient;
use App\Services\HisHttp\Client;
use App\Utils\Traits\Logger;
use App\Utils\Traits\MiniProgramAuth;
use App\Utils\Traits\SendSubscribeMessage;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Support\Facades\Redis;
use ReflectionException;
use Symfony\Component\HttpFoundation\Response;
use UnifyPayment\Cores\Exceptions\InvalidConfigException;
@ -26,6 +28,7 @@ class RegisterLogic
{
use Logger;
use MiniProgramAuth;
use SendSubscribeMessage;
private Client $his_client;
@ -47,7 +50,8 @@ class RegisterLogic
}
/**
* @param string $patient_id
* 挂号
* @param string $patient_number
* @param string $date
* @param string $dept_id
* @param string $doctor_id
@ -55,22 +59,25 @@ class RegisterLogic
* @return array
* @throws GeneralException
*/
public function register(string $patient_id, string $date, string $dept_id, string $doctor_id, string $reg_id): array
public function register(string $patient_number, string $date, string $dept_id, string $doctor_id, string $reg_id): array
{
// 基础信息
$patient_info = $this->getPatientInfo($patient_id, $this->open_id);
$patient_info = $this->getPatientInfo($this->open_id, $patient_number);
$schedule_info = $this->getRegisterScheduleDetails($date, $dept_id, $doctor_id, $reg_id);
// 锁号?
// 创建订单
$order_type = $date === date('Y-m-d') ? Type::TODAY_REGISTRATION : Type::APPOINTMENT_REGISTRATION;
$pay_type = PayType::WECHAT_PAY;
$order_id = $this->order_model->getOrderId($pay_type, 'M');
$reg_fee = (float)(string) $schedule_info['SHIFT']['FEE'];
$reg_fee = (float)(string) $schedule_info['scheduleInfo']['fee'];
$order = $this->createOrder($order_id, $pay_type, $reg_fee, $order_type, $patient_info, $schedule_info);
// 挂号金额为 0 元
if ($reg_fee === 0.0) {
return $this->freeRegistrationConfirm($order);
}
// 申请支付
$pay_data = $this->applyPayment($order_type, $order_id, $reg_fee, $patient_info['patientNumber'], $patient_info['name']);
@ -82,14 +89,14 @@ class RegisterLogic
/**
* 获取患者信息
* @param string $patient_id
* @param string $open_id
* @param string $patient_number
* @return mixed
* @throws GeneralException
*/
protected function getPatientInfo(string $patient_id, string $open_id): mixed
protected function getPatientInfo(string $open_id, string $patient_number): mixed
{
$info = $this->patient_model->getBindPatientInfo($open_id, $patient_id);
$info = $this->patient_model->getBindPatientInfoByPatientNumber($open_id, $patient_number);
if (empty($info)) {
throw new GeneralException('找不到患者信息,请重新再试!', Response::HTTP_BAD_REQUEST);
}
@ -121,18 +128,17 @@ class RegisterLogic
// 获取排班医生信息
$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['RESULTCODE']) || $schedule_info['RESULTCODE'] !== '0') {
throw new GeneralException($schedule_info['ERRORMSG'] ?? '找不到该号源,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE);
if (!isset($schedule_info['success']) || !$schedule_info['success']) {
throw new GeneralException($schedule_info['msg'] ?? '找不到该号源,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE);
}
// 获取号源信息
$schedule_info = xmlArrayToListByKey($schedule_info, 'ITEM');
foreach ($schedule_info['ITEM'] as $v) {
if ($v['DOCTID'] === $doctor_id) {
$v = xmlArrayToListByKey($v, 'SHIFT');
foreach ($v['SHIFT'] as $v2) {
if ($v2['REGID'] === $reg_id && $v2['FDATE'] === $date) {
$v['SHIFT'] = $v2;
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;
}
}
@ -143,21 +149,26 @@ class RegisterLogic
throw new GeneralException('找不到该号源,请重新再试!', Response::HTTP_BAD_REQUEST);
}
if (!isset($info['SHIFT']['REGCOUNT']) || $info['SHIFT']['REGCOUNT'] <= 0) {
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($schedule_info['RESULTCODE']) || $schedule_info['RESULTCODE'] !== '0') {
throw new GeneralException($schedule_info['ERRORMSG'] ?? '找不到该号源,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE);
if (!isset($dept_lists['success']) || !$dept_lists['success']) {
throw new GeneralException($schedule_info['msg'] ?? '找不到该号源,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE);
}
}
$dept_lists = xmlArrayToListByKey($dept_lists, 'ITEM');
foreach ($dept_lists['ITEM'] as $v) {
if ($v['DEPID'] === $dept_id) {
$info['dept_id'] = $v['DEPID'];
$info['dept_name'] = $v['DEPNAME'];
foreach ($dept_lists['response'] as $v) {
if ($v['typeId'] === $dept_id) {
$info['dept_id'] = $v['typeId'];
$info['dept_name'] = $v['typeName'];
}
}
@ -181,15 +192,15 @@ class RegisterLogic
// 挂号记录表
$reg_record_data = [
'relate_patient_id' => $patient_info['id'],
'reg_id' => $schedule_info['SHIFT']['REGID'],
'reg_id' => $schedule_info['scheduleInfo']['regId'],
'dept_id' => $schedule_info['dept_id'],
'dept_name' => $schedule_info['dept_name'],
'dept_location' => $schedule_info['DEPLOCATION'],
'doctor_id' => $schedule_info['DOCTID'],
'doctor_name' => $schedule_info['DOCTNAME'],
'visit_date' => date('Y-m-d', strtotime($schedule_info['SHIFT']['FDATE'])),
'begin_time' => $schedule_info['SHIFT']['STARTTIME'],
'end_time' => $schedule_info['SHIFT']['ENDTIME'],
'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, JSON_UNESCAPED_UNICODE),
];
@ -201,8 +212,8 @@ class RegisterLogic
$reg_fee * 100,
0,
$this->open_id,
$patient_info['PATIENTID'],
$patient_info['NAME'],
$patient_info['patientId'],
$patient_info['name'],
$order_type,
SourceId::MINI_PROGRAM,
$reg_record_data
@ -215,6 +226,58 @@ class RegisterLogic
return $order;
}
/**
* 免费挂号确认
* @param Order $order
* @return array
* @throws GeneralException
*/
protected function freeRegistrationConfirm(Order $order): array
{
// 挂号确认
$record = &$order->registrationRecord;
$extra = json_decode($record->extra_info, true);
$data = [
$order->patient_id,
$order->patient_name,
$record->dept_id,
$record->doctor_id,
$record->reg_id,
$extra['scheduleInfo']['rankId'],
$record->visit_date,
PayType::WECHAT_PAY->hisCode(),
$order->order_id,
'',
'',
'',
'',
'0',
''
];
$response = $this->his_client->registerConfirm(... $data);
$this->info('挂号订单出入参:'.$order->order_id, [$data, $response]);
if (isset($response['success']) && $response['success'] === true) {
// 成功流程
$order->orderConfirm($order->order_id, $response['response']['visitNo'], $response['response']);
// 推送成功
$this->sendRegistrationSuccessMessage($order);
// 返回数据
return [
'order_id' => $order->order_id,
'reg_serial_no' => $response['response']['visitNo']
];
} else {
// 推送失败
$this->sendRegistrationFailureMessage($order);
throw new GeneralException('挂号失败,失败原因:'. ($response['msg'] ?? '数据异常'). ',请重新再试!');
}
}
/**
* 申请支付
* @param Type $order_type
@ -242,7 +305,7 @@ class RegisterLogic
$response = Unify::pay(config('unify'))->mini->setMockHandler([new CreateOrderHandler(true)])->jsapi($order_obj);
$this->info('jsapi 支付参数', $response);
if (!$response['success'] || empty($response['response'])) {
if (!$response['success'] && empty($response['response'])) {
throw new GeneralException('申请支付失败,请重新再试!', Response::HTTP_SERVICE_UNAVAILABLE);
}

@ -6,6 +6,7 @@ namespace App\Http\Logics\Registration;
use App\Exceptions\GeneralException;
use App\Services\HisHttp\Client;
use App\Utils\Traits\Logger;
use Illuminate\Support\Facades\Redis;
use Symfony\Component\HttpFoundation\Response;
class ScheduleLogic
@ -36,6 +37,8 @@ class ScheduleLogic
throw new GeneralException($response['msg'] ?? '暂无科室排班!', Response::HTTP_SERVICE_UNAVAILABLE);
}
Redis::setex('departments.'. $date, 2 * 3600, json_encode($response, JSON_UNESCAPED_UNICODE));
return $response;
}

@ -28,7 +28,7 @@ class RegisterRequest extends FormRequest
public function rules(): array
{
return [
'date' => 'required|date_format:Y-m-d',
'date' => 'required|date_format:Y-m-d||after_or_equal:today',
'dept_id' => 'required',
'doctor_id' => 'required',
'reg_id' => 'required'
@ -44,6 +44,7 @@ class RegisterRequest extends FormRequest
return [
'date.required' => '必须选择挂号日期',
'date.date_format' => '必须选择挂号日期',
'date.after_or_equal' => '挂号日期不得小于今天',
'dept_id.required' => '必须选择挂号科室',
'doctor_id.required' => '必须选择挂号医生',
'reg_id.required' => '必须选择挂号时间段',

@ -15,7 +15,7 @@ class PatientDetailsResource extends JsonResource
public function toArray(Request $request = null): array
{
return [
'patient_id' => $this->resource['patient_id'],
'patient_id' => $this->resource['patient_number'],
'patient_name' => $this->resource['name'],
// 'card_no' => $this->resource['card_no'],
'sex' => $this->resource['sex'],

@ -17,7 +17,7 @@ class PatientListsResource extends JsonResource
$lists = [];
foreach ($this->resource as $v) {
$lists[] = [
'patient_id' => $v['patient_id'],
'patient_id' => $v['patient_number'],
'patient_name' => $v['name'],
'is_default' => $v['def_status']
];

@ -4,6 +4,8 @@ namespace App\Http\Resources\Registration\Schedule;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
class DeptListsResource extends JsonResource
{
@ -14,10 +16,12 @@ class DeptListsResource extends JsonResource
*/
public function toArray(Request $request = null): array
{
$this->resource = xmlArrayToListByKey($this->resource, 'ITEM');
$department_lists = $this->getDepartmentLists();
$sort = collect($department_lists)->pluck('sort', 'dept_id');
$lists = [];
foreach ($this->resource['ITEM'] as $v) {
foreach ($this->resource['response'] as $v) {
$lists[] = [
'dept_id' => $v['typeId'],
'dept_name' => $v['typeName'],
@ -25,6 +29,36 @@ class DeptListsResource extends JsonResource
];
}
// 根据科室的 sort 字段对 $lists 进行排序
usort($lists, function ($a, $b) use ($sort) {
$sort_a = $sort[$a['dept_id']] ?? 0;
$sort_b = $sort[$b['dept_id']] ?? 0;
return $sort_b <=> $sort_a; // 倒序排序
});
return $lists;
}
/**
* 获取医生列表
* @return array
*/
public function getDepartmentLists(): array
{
if (Redis::exists('department.lists')) {
$department_lists = Redis::get('doctor.lists');
$department_lists = json_decode($department_lists, true);
} else {
$db = DB::connection('mysql_admin')->table('departments');
$department_lists = $db
->where('is_enable', 1)
->orderBy('sort', 'DESC')
->orderBy('id', 'ASC')
->get();
Redis::setex('department.lists', 4 * 3600, json_encode($department_lists, JSON_UNESCAPED_UNICODE));
}
return $department_lists;
}
}

@ -4,6 +4,8 @@ namespace App\Http\Resources\Registration\Schedule;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
class DoctorListsResource extends JsonResource
{
@ -14,35 +16,66 @@ class DoctorListsResource extends JsonResource
*/
public function toArray(Request $request = null): array
{
$this->resource = xmlArrayToListByKey($this->resource, 'ITEM');
$doctor_lists = $this->getDoctorLists();
$specialty = collect($doctor_lists)->pluck('specialty', 'doctor_id');
$sort = collect($doctor_lists)->pluck('sort', 'doctor_id');
$lists = [];
foreach ($this->resource['ITEM'] as $k=>$v) {
foreach ($this->resource['response'] as $k=>$v) {
$lists[$k] = [
'doctor_id' => $v['DOCTID'],
'doctor_name' => $v['DOCTNAME'],
'doctor_title' => $v['TYPENAME'],
'doctor_intro' => $v['DEPLOCATION'],
'is_doctor' => (int) $v['ISKSDOC'],
'doctor_id' => $v['doctId'],
'doctor_name' => $v['doctName'],
'doctor_title' => $v['docTitle'],
'doctor_intro' => $specialty[$v['doctId']] ?? ($v['depLocation'] ?: '暂无介绍'),
'is_doctor' => (int) $v['isksDoc'],
];
$v = xmlArrayToListByKey($v, 'SHIFT');
foreach ($v['SHIFT'] as $k2=>$v2) {
foreach ($v['doctotVisitInfoList'] as $k2 => $v2) {
$lists[$k]['schedule_lists'][$k2] = [
'reg_id' => $v2['REGID'],
'date' => $v2['FDATE'],
'rank_id' => $v2['RANKID'],
'rank_name' => $v2['RANKNAME'],
'start_time' => $v2['STARTTIME'],
'end_time' => $v2['ENDTIME'],
'fee' => $v2['FEE'],
'fee_code' => $v2['FEECODE'],
'reg_count' => $v2['REGCOUNT'],
'no_visit_count' => $v2['JZCOUNT'],
'reg_id' => $v2['regId'],
'date' => $v2['visitDate'],
'rank_id' => $v2['rankId'],
'rank_name' => $v2['rankName'],
'start_time' => $v2['startTime'],
'end_time' => $v2['endTime'],
'fee' => (float) $v2['fee'],
'fee_code' => $v2['feeCode'],
'reg_count' => (int) $v2['regCount'],
'no_visit_count' => (int) $v2['noVisits'],
];
}
}
// 根据医生的 sort 字段对 $lists 进行排序
usort($lists, function ($a, $b) use ($sort) {
$sort_a = $sort[$a['doctor_id']] ?? 0;
$sort_b = $sort[$b['doctor_id']] ?? 0;
return $sort_b <=> $sort_a; // 倒序排序
});
return $lists;
}
/**
* 获取医生列表
* @return array
*/
public function getDoctorLists(): array
{
if (Redis::exists('doctor.lists')) {
$doctor_lists = Redis::get('doctor.lists');
$doctor_lists = json_decode($doctor_lists, true);
} else {
$db = DB::connection('mysql_admin')->table('doctors');
$doctor_lists = $db
->where('is_enable', 1)
->orderBy('sort', 'DESC')
->orderBy('id', 'ASC')
->get();
Redis::setex('doctor.lists', 4 * 3600, json_encode($doctor_lists, JSON_UNESCAPED_UNICODE));
}
return $doctor_lists;
}
}

@ -33,6 +33,7 @@ class Order extends Model
*/
protected $fillable = [
'relate_id',
'relate_patient_id',
'order_id',
'his_order_id',
'transaction_id',
@ -289,7 +290,7 @@ class Order extends Model
$extra_info = json_decode($record->extra_info, true);
$extra_info['confirm_response'] = $response;
$record->update(['extra_info' => json_encode($extra_info, JSON_UNESCAPED_UNICODE)]);
$record->update(['dept_location' => $response['depLocation'], 'extra_info' => json_encode($extra_info, JSON_UNESCAPED_UNICODE)]);
break;
case Type::OUTPATIENT_PAYMENT->value:
$record = $order->outpatientPaymentRecord;

@ -28,6 +28,7 @@ class Patient extends Model
'union_id',
'open_id',
'patient_id',
'patient_number',
'card_type',
'card_no',
'name',
@ -128,6 +129,17 @@ class Patient extends Model
return $this->where('open_id', $open_id)->where('patient_id', $patient_id)->first();
}
/**
* 获取绑定患者信息By number
* @param string $open_id
* @param string $patient_number
* @return mixed
*/
public function getBindPatientInfoByPatientNumber(string $open_id, string $patient_number): mixed
{
return $this->where('open_id', $open_id)->where('patient_number', $patient_number)->first();
}
/**
* 获取绑定患者信息By Patient Id
* @param string $patient_id
@ -144,16 +156,18 @@ class Patient extends Model
* @param string $union_id
* @param string $open_id
* @param string $patient_id
* @param string $patient_number
* @param string $name
* @param Sex $gender
* @return mixed
*/
public function createPatient(string $union_id, string $open_id, string $patient_id, string $name, Sex $gender): mixed
public function createPatient(string $union_id, string $open_id, string $patient_id, string $patient_number, string $name, Sex $gender): mixed
{
$data = [
'union_id' => $union_id,
'open_id' => $open_id,
'patient_id' => $patient_id,
'patient_number' => $patient_number,
'card_type' => 0,
'card_no' => '',
'name' => $name,

@ -58,6 +58,10 @@ class Client
*/
protected function requestHandle(string $method_name, string $request_name, array $params): mixed
{
//if ($request_name === 'PayRegTrade') {
dd(json_encode($params['json'], JSON_UNESCAPED_UNICODE));
//}
try {
return $this->service
->transferMethod($method_name, $request_name, $params)

@ -3,7 +3,9 @@ declare(strict_types = 1);
namespace App\Utils\Traits;
use RedisException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;
trait RedisLockUtil
{
@ -12,37 +14,42 @@ trait RedisLockUtil
* 加锁
* @param string $lock_str 锁字符串
* @param int $expire_time 超时时间,默认300秒
* @return false|string
* @throws RedisException
* @return bool|string
*/
public function addLock(string $lock_str, int $expire_time = 300): bool|string
{
$redis = app('redis');
try {
$unique_id = $this->getUniqueLockId();
$result = $redis->set($lock_str, $unique_id, 'ex', $expire_time, 'nx');
$result = Redis::set($lock_str, $unique_id, 'ex', $expire_time, 'nx');
return $result ? $unique_id : false;
} catch (\Exception $e) {
Log::channel('genera_error')->error("Error while acquiring lock for {$lock_str}: " . $e->getMessage());
return false;
}
}
/**
* 解锁
* @param string $lock_str 锁字符串
* @param string $unique_id 唯一锁ID
* @return bool
* @throws RedisException
*/
public function unlock(string $lock_str, string $unique_id): bool
{
$redis = app('redis');
$redis->watch($lock_str);
if ($unique_id == $redis->get($lock_str)) {
$redis->multi()->del($lock_str)->exec();
try {
Redis::watch($lock_str);
if (Redis::exists($lock_str) && $unique_id == Redis::get($lock_str)) {
Redis::multi()->del($lock_str)->exec();
return true;
}
$redis->unwatch();
Redis::unwatch();
return false;
} catch (\Exception $e) {
Log::channel('genera_error')->error("Error while releasing lock for {$lock_str}: " . $e->getMessage());
return false;
}
}
/**
* 获取唯一锁ID
@ -50,6 +57,6 @@ trait RedisLockUtil
*/
protected function getUniqueLockId(): string
{
return md5(uniqid((string)mt_rand(), true));
return Str::uuid()->toString();
}
}

@ -13,7 +13,7 @@ use App\Models\SendMessageJob;
trait SendSubscribeMessage
{
protected static SendMessageJob $message_model;
protected static ?SendMessageJob $message_model = null;
/**
* 单例获取 SendMessageJob
@ -46,7 +46,7 @@ trait SendSubscribeMessage
'time2' => ['value' => date('Y-m-d H:i')],
'thing3' => ['value' => '您已绑定成功,可以进行线上线下就医服务。'],
],
'miniprogram_state' => env('custom.mini_program_message_state')
'miniprogram_state' => config('custom.mini_program_message_state')
];
$message = self::getSendMessageJob()->insertMessageJobs(0, $relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, '');
@ -77,7 +77,7 @@ trait SendSubscribeMessage
'character_string5' => ['value' => $inpatient_id],
'character_string6' => ['value' => $patient_id],
],
'miniprogram_state' => env('custom.mini_program_message_state')
'miniprogram_state' => config('custom.mini_program_message_state')
];
$message = self::getSendMessageJob()->insertMessageJobs(0, $relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data);
@ -106,10 +106,10 @@ trait SendSubscribeMessage
'thing6' => ['value' => $record->dept_location],
'thing5' => ['value' => '请准时前往医院就诊。'],
],
'miniprogram_state' => env('custom.mini_program_message_state')
'miniprogram_state' => config('custom.mini_program_message_state')
];
$message = self::getSendMessageJob()->insertMessageJobs(0, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, '');
$message = self::getSendMessageJob()->insertMessageJobs($order->id, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, '');
SendWeChatMessageJob::dispatch($message);
}
@ -121,6 +121,8 @@ trait SendSubscribeMessage
*/
public function sendRegistrationSuccessMessage(OrderModel $order): void
{
// 重新强关联,避免缓存
$order->load('registrationRecord');
$record = &$order->registrationRecord;
$subscribe_id = SubscribeId::REGISTRATION_SUCCESS;
$visit_time = $record->visit_date . ' '. $record->begin_time . '~'. $record->end_time;
@ -135,10 +137,10 @@ trait SendSubscribeMessage
'thing19' => ['value' => $record->doctor_name],
'amount13' => ['value' => $order->fee / 100],
],
'miniprogram_state' => env('custom.mini_program_message_state')
'miniprogram_state' => config('custom.mini_program_message_state')
];
$message = self::getSendMessageJob()->insertMessageJobs(0, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, '');
$message = self::getSendMessageJob()->insertMessageJobs($order->id, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, '');
SendWeChatMessageJob::dispatch($message);
}
@ -150,6 +152,8 @@ trait SendSubscribeMessage
*/
public function sendRegistrationFailureMessage(OrderModel $order): void
{
// 重新强关联,避免缓存
$order->load('registrationRecord');
$record = &$order->registrationRecord;
$subscribe_id = SubscribeId::REGISTRATION_FAILURE;
$visit_time = $record->visit_date . ' '. $record->begin_time . '~'. $record->end_time;
@ -164,10 +168,10 @@ trait SendSubscribeMessage
'time4' => ['value' => $visit_time],
'amount13' => ['value' => $order->fee / 100],
],
'miniprogram_state' => env('custom.mini_program_message_state')
'miniprogram_state' => config('custom.mini_program_message_state')
];
$message = self::getSendMessageJob()->insertMessageJobs(0, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, '');
$message = self::getSendMessageJob()->insertMessageJobs($order->id, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, '');
SendWeChatMessageJob::dispatch($message);
}
@ -179,6 +183,8 @@ trait SendSubscribeMessage
*/
public function sendRegistrationCancelMessage(OrderModel $order): void
{
// 重新强关联,避免缓存
$order->load('registrationRecord');
$record = &$order->registrationRecord;
$subscribe_id = SubscribeId::REGISTRATION_CANCEL;
$visit_time = $record->visit_date . ' '. $record->begin_time . '~'. $record->end_time;
@ -193,7 +199,7 @@ trait SendSubscribeMessage
'time4' => ['value' => $visit_time],
'thing5' => ['value' => '已成功取消预约,如有需要请重新预约。'],
],
'miniprogram_state' => env('custom.mini_program_message_state')
'miniprogram_state' => config('custom.mini_program_message_state')
];
$message = self::getSendMessageJob()->insertMessageJobs(0, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, '');
@ -208,7 +214,9 @@ trait SendSubscribeMessage
*/
public function sendOutpatientPaymentSuccessMessage(OrderModel $order): void
{
$record = &$order->outpatientPaymentRecord;
// 重新强关联,避免缓存
// $order->load('outpatientPaymentRecord');
// $record = &$order->outpatientPaymentRecord;
$subscribe_id = SubscribeId::OUTPATIENT_PAYMENT_SUCCESS;
$data = [
'touser' => $order->open_id,
@ -221,10 +229,10 @@ trait SendSubscribeMessage
'character_string14' => ['value' => $order->patient_id],
'thing9' => ['value' => '门诊缴费'],
],
'miniprogram_state' => env('custom.mini_program_message_state')
'miniprogram_state' => config('custom.mini_program_message_state')
];
$message = self::getSendMessageJob()->insertMessageJobs(0, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, '');
$message = self::getSendMessageJob()->insertMessageJobs($order->id, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, '');
SendWeChatMessageJob::dispatch($message);
}
@ -236,7 +244,8 @@ trait SendSubscribeMessage
*/
public function sendOutpatientPaymentFailureMessage(OrderModel $order): void
{
$record = &$order->outpatientPaymentRecord;
// $order->load('outpatientPaymentRecord');
// $record = &$order->outpatientPaymentRecord;
$subscribe_id = SubscribeId::OUTPATIENT_PAYMENT_FAILURE;
$data = [
'touser' => $order->open_id,
@ -250,10 +259,10 @@ trait SendSubscribeMessage
'amount4' => ['value' => $order->fee / 100],
'date6' => ['value' => date('Y-m-d')],
],
'miniprogram_state' => env('custom.mini_program_message_state')
'miniprogram_state' => config('custom.mini_program_message_state')
];
$message = self::getSendMessageJob()->insertMessageJobs(0, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, '');
$message = self::getSendMessageJob()->insertMessageJobs($order->id, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, '');
SendWeChatMessageJob::dispatch($message);
}

File diff suppressed because one or more lines are too long

@ -62,6 +62,27 @@ return [
]) : [],
],
// 管理后台数据库
'mysql_admin' => [
'driver' => 'mysql',
'url' => env('DB_ADMIN_URL'),
'host' => env('DB_ADMIN_HOST', '127.0.0.1'),
'port' => env('DB_ADMIN_PORT', '3306'),
'database' => env('DB_ADMIN_DATABASE', 'laravel'),
'username' => env('DB_ADMIN_USERNAME', 'root'),
'password' => env('DB_ADMIN_PASSWORD', ''),
'unix_socket' => env('DB_ADMIN_SOCKET', ''),
'charset' => env('DB_ADMIN_CHARSET', 'utf8mb4'),
'collation' => env('DB_ADMIN_COLLATION', 'utf8mb4_unicode_ci'),
'prefix' => env('DB_ADMIN_PREFIX', ''),
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'mariadb' => [
'driver' => 'mariadb',
'url' => env('DB_URL'),

@ -147,6 +147,15 @@ return [
'max_files' => 30,
],
// 通用错误日志
'genera_error' => [
'driver' => 'custom',
'via' => GeneralDailyLogger::class,
'service_type' => 'GeneraErrorLog',
'level' => Level::Info,
'max_files' => 30,
],
// HisSoap
'his_soap' => [
'driver' => 'custom',
@ -174,7 +183,7 @@ return [
'max_files' => 30,
],
// 挂号日志
// 缴费日志
'outpatient' => [
'driver' => 'custom',
'via' => GeneralDailyLogger::class,

@ -15,7 +15,8 @@ return new class () extends Migration {
$table->id();
$table->string('union_id', 32)->default('')->comment(' union_id');
$table->string('open_id', 32)->index()->comment('openid');
$table->string('patient_id', 20)->index()->comment('患者门诊号');
$table->string('patient_id', 20)->index()->comment('患者ID');
$table->string('patient_number', 20)->index()->comment('患者门诊号');
$table->unsignedTinyInteger('card_type')->index()->comment('患者证件类型');
$table->string('card_no', 64)->index()->comment('患者证件号');
$table->string('name', 50)->comment('患者名称');

Loading…
Cancel
Save