Compare commits
120
Commits
73b922d8af
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d537bd323 | ||
|
|
91945a5ea6 | ||
|
|
0d3da047a1 | ||
|
|
07f0467484 | ||
|
|
41688e395d | ||
|
|
d3fce247e5 | ||
|
|
1a921efb7c | ||
|
|
4dfcdacb12 | ||
|
|
a66370bf91 | ||
|
|
4ad8d5fa6f | ||
|
|
e836e84421 | ||
|
|
c8216b4e2a | ||
|
|
ed3dcdd150 | ||
|
|
0c5c6e68c2 | ||
|
|
919cda4ba7 | ||
|
|
ca120cfdc2 | ||
|
|
8308d500e7 | ||
|
|
b3d2a17dac | ||
|
|
66ff7de0f2 | ||
|
|
36eafbb832 | ||
|
|
012984f636 | ||
|
|
f9ba59aa55 | ||
|
|
39528ca971 | ||
|
|
1bd00bdcea | ||
|
|
fcbc4c66d0 | ||
|
|
207bd2a0c6 | ||
|
|
315adad6d0 | ||
|
|
c4a9012d80 | ||
|
|
10e46b2f8c | ||
|
|
e825269798 | ||
|
|
90194053e0 | ||
|
|
4d5b679cfe | ||
|
|
0027f93f9f | ||
|
|
2a9030f27b | ||
|
|
b91b49ea51 | ||
|
|
43198f814f | ||
|
|
60e003ae2c | ||
|
|
5f6a2d269c | ||
|
|
4af73702f3 | ||
|
|
159d719c66 | ||
|
|
0e2d467570 | ||
|
|
ffead75fb7 | ||
|
|
2fd9fc9e84 | ||
|
|
93e999783c | ||
|
|
fa2c1ce8a4 | ||
|
|
16e6bcc3c8 | ||
|
|
5206f2f64d | ||
|
|
d400bb356e | ||
|
|
bf3c7eb7fe | ||
|
|
31d228cef7 | ||
|
|
3abb779df5 | ||
|
|
9f6f0aa299 | ||
|
|
51dbed179c | ||
|
|
e6ebb7372b | ||
|
|
7560486731 | ||
|
|
e34eea86e1 | ||
|
|
05c8b4ced5 | ||
|
|
46f386af1c | ||
|
|
a054cf2759 | ||
|
|
eaa7a1de85 | ||
|
|
9890ae3b45 | ||
|
|
64d1d8294e | ||
|
|
ae03385c1a | ||
|
|
ea30873300 | ||
|
|
cf9636041c | ||
|
|
f8635a9209 | ||
|
|
c2f67e31d8 | ||
|
|
bdd9375ab6 | ||
|
|
373d50aba9 | ||
|
|
145045b34b | ||
|
|
9f53e9551e | ||
|
|
755fb711a9 | ||
|
|
0c1f78803e | ||
|
|
3f02c0547e | ||
|
|
c7841f87f8 | ||
|
|
3e28a4291f | ||
|
|
0852c0912f | ||
|
|
b1be3e9acc | ||
|
|
484f2b9826 | ||
|
|
80909e0ea2 | ||
|
|
438b5293cd | ||
|
|
ee00b23414 | ||
|
|
3ef077b261 | ||
|
|
2e7d3363ae | ||
|
|
e0e551aa59 | ||
|
|
af95e480a8 | ||
|
|
23e97b83ac | ||
|
|
6436a4383a | ||
|
|
63871e10ee | ||
|
|
f4f31a37e7 | ||
|
|
1ccc90bf62 | ||
|
|
339564c000 | ||
|
|
fe5d86cd46 | ||
|
|
e223725c49 | ||
|
|
171c32881a | ||
|
|
ef2ae67a37 | ||
|
|
c8da9bcd93 | ||
|
|
7690ec848b | ||
|
|
823d62728d | ||
|
|
569b2a84fd | ||
|
|
8da16704e6 | ||
|
|
7c1c63cbbf | ||
|
|
93d5aae5ac | ||
|
|
6e90c52cb8 | ||
|
|
270db5c040 | ||
|
|
e22d2380a4 | ||
|
|
31c034eee1 | ||
|
|
bd0a3512bc | ||
|
|
206a765044 | ||
|
|
a33fe1033d | ||
|
|
12a4846d0e | ||
|
|
bc61f09ad0 | ||
|
|
52c258f464 | ||
|
|
e5d846c509 | ||
|
|
13202346dc | ||
|
|
2adb6a5fe3 | ||
|
|
f8eb4e8da0 | ||
|
|
60f322d4a1 | ||
|
|
ce6e5fb5d1 | ||
|
|
e28a423573 |
@@ -4,6 +4,7 @@
|
||||
*.patch
|
||||
.idea/
|
||||
.git/
|
||||
docs/
|
||||
runtime/
|
||||
vendor/
|
||||
.phpintel/
|
||||
|
||||
@@ -170,7 +170,21 @@ class AutoFinishInquiryDelayDirectConsumer extends ConsumerMessage
|
||||
// 计算医生平均响应时间
|
||||
$this->computeDoctorAvgPesponseTime($order_inquiry['doctor_id']);
|
||||
|
||||
// 新增上报监管平台数据
|
||||
// 新增上报监管平台数据,只上报 多点执业的医生 20251021 测试分支
|
||||
// if ($user_doctor['multi_point_status'] == 1){//医生多点执业认证状态(0:未认证 1:认证通过 2:审核中 3:认证失败)
|
||||
// $reportRegulatoryService = new ReportRegulatoryService();
|
||||
// $res = $reportRegulatoryService->addReportRegulatory($order_inquiry['order_inquiry_id']);
|
||||
// if (!$res) {
|
||||
// // 新增上报失败
|
||||
// Db::rollBack();
|
||||
// Log::getInstance("queue-AutoFinishInquiry")->error("新增上报监管平台数据失败");
|
||||
// return Result::REQUEUE;
|
||||
// }
|
||||
// }else{
|
||||
// Log::getInstance("queue-AutoFinishInquiry")->info("非多点执业的医生不上报监管平台");
|
||||
// }
|
||||
|
||||
//新增抄方功能,所有要全部保存,上报的时候再判断
|
||||
$reportRegulatoryService = new ReportRegulatoryService();
|
||||
$res = $reportRegulatoryService->addReportRegulatory($order_inquiry['order_inquiry_id']);
|
||||
if (!$res) {
|
||||
|
||||
@@ -99,7 +99,7 @@ class AutoPharmacistCaVerifyDelayDirectConsumer extends ConsumerMessage
|
||||
Log::getInstance("queue-AutoPharmacistCaVerify")->error("错误:医生数据错误");
|
||||
return Result::ACK;
|
||||
}
|
||||
|
||||
|
||||
// 获取用户数据
|
||||
$params = array();
|
||||
$params['patient_id'] = $order_prescription['patient_id'];
|
||||
@@ -143,6 +143,15 @@ class AutoPharmacistCaVerifyDelayDirectConsumer extends ConsumerMessage
|
||||
return Result::ACK;
|
||||
}
|
||||
|
||||
$message_doctor_id = $user_doctor['user_id'];
|
||||
|
||||
//抄方订单 消息发给问诊医生
|
||||
if(!empty($order_inquiry["transfer_doctor_id"])){
|
||||
$params = array();
|
||||
$params['doctor_id'] = $order_inquiry['doctor_id'];
|
||||
$user_doctor_temp = UserDoctor::getOne($params);
|
||||
$message_doctor_id = $user_doctor_temp['user_id'];
|
||||
}
|
||||
if (!$res) {
|
||||
Log::getInstance("queue-AutoPharmacistCaVerify")->error("错误:超出最大执行次数或检测错误");
|
||||
|
||||
@@ -152,7 +161,7 @@ class AutoPharmacistCaVerifyDelayDirectConsumer extends ConsumerMessage
|
||||
$this->modifyOrderPrescription($data['order_prescription_id'], 2, "请联系平台客服,请勿重开处方");
|
||||
|
||||
// 医生-开具的处方审核未通过
|
||||
$MessagePush = new MessagePush($user_doctor['user_id'], $order_inquiry['inquiry_no']);
|
||||
$MessagePush = new MessagePush($message_doctor_id, $order_inquiry['inquiry_no']);
|
||||
$MessagePush->prescriptionVerifyFail($data['order_prescription_id']);
|
||||
|
||||
// 患者-处方审核未通过
|
||||
@@ -252,7 +261,12 @@ class AutoPharmacistCaVerifyDelayDirectConsumer extends ConsumerMessage
|
||||
}
|
||||
|
||||
// 修改处方文件表,添加prescription_pdf_oss_path字段
|
||||
$this->modifyOrderPrescriptionFile($data['prescription_file_id'],$prescription_pdf_oss_path,$prescription_open_result['file_id']);
|
||||
$this->modifyOrderPrescriptionFile(
|
||||
$data['prescription_file_id'],
|
||||
$prescription_pdf_oss_path,
|
||||
$prescription_open_result['file_id'],
|
||||
$prescription_open_result
|
||||
);
|
||||
|
||||
// 修改处方表为通过
|
||||
$this->modifyOrderPrescription($data['order_prescription_id'],1);
|
||||
@@ -271,7 +285,9 @@ class AutoPharmacistCaVerifyDelayDirectConsumer extends ConsumerMessage
|
||||
// 药师审核成功
|
||||
// 发送IM消息-处方已开具
|
||||
$imService = new ImService();
|
||||
$imService->prescriptionIssued($order_inquiry,$user_doctor['user_id'],$order_inquiry['user_id'],$product_name,(string)$data['order_prescription_id'],7);
|
||||
|
||||
//如果为抄方,则发送给原医生IM
|
||||
$imService->prescriptionIssued($order_inquiry,$message_doctor_id,$order_inquiry['user_id'],$product_name,(string)$data['order_prescription_id'],7);
|
||||
|
||||
// 发送站内、短信消息-患者的处方被药师审核通过
|
||||
$MessagePush = new MessagePush($order_inquiry['user_id'],$order_inquiry['inquiry_no']);
|
||||
@@ -279,7 +295,7 @@ class AutoPharmacistCaVerifyDelayDirectConsumer extends ConsumerMessage
|
||||
|
||||
// 站内、订阅失败发送短信-医生开具的处方审核通过
|
||||
// 发送目标不同,重新实例化
|
||||
$MessagePush = new MessagePush($user_doctor['user_id'],$order_inquiry['inquiry_no']);
|
||||
$MessagePush = new MessagePush($message_doctor_id, $order_inquiry['inquiry_no']);
|
||||
$MessagePush->prescriptionVerifySuccess();
|
||||
|
||||
// 添加处方过期队列
|
||||
@@ -427,7 +443,7 @@ class AutoPharmacistCaVerifyDelayDirectConsumer extends ConsumerMessage
|
||||
* @param string $hospital_ca_file_id 医院签章文件id
|
||||
* @return void
|
||||
*/
|
||||
protected function modifyOrderPrescriptionFile(string $prescription_file_id,string $prescription_pdf_oss_path,string $hospital_ca_file_id): void
|
||||
protected function modifyOrderPrescriptionFile(string $prescription_file_id,string $prescription_pdf_oss_path,string $hospital_ca_file_id,array $pharmacist_ca_data = []): void
|
||||
{
|
||||
try {
|
||||
$params = array();
|
||||
@@ -436,6 +452,11 @@ class AutoPharmacistCaVerifyDelayDirectConsumer extends ConsumerMessage
|
||||
$data = array();
|
||||
$data['hospital_ca_file_id'] = $hospital_ca_file_id;
|
||||
$data['prescription_pdf_oss_path'] = $prescription_pdf_oss_path;
|
||||
$data['pharmacist_ca_supplier'] = $pharmacist_ca_data['ca_supplier'] ?? "";
|
||||
$data['pharmacist_ca_sign_type'] = $pharmacist_ca_data['ca_sign_type'] ?? "";
|
||||
$data['pharmacist_ca_sign_originaltext'] = $pharmacist_ca_data['ca_sign_originaltext'] ?? "";
|
||||
$data['pharmacist_ca_sign_text'] = $pharmacist_ca_data['ca_sign_text'] ?? "";
|
||||
$data['pharmacist_ca_sign_time'] = $pharmacist_ca_data['ca_sign_time'] ?? "";
|
||||
|
||||
OrderPrescriptionFile::edit($params,$data);
|
||||
}catch(\Exception $e){
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Amqp\Consumer;
|
||||
|
||||
use App\Model\MessageIm;
|
||||
use Extend\Alibaba\Oss;
|
||||
use GuzzleHttp\Client;
|
||||
use Hyperf\Amqp\Annotation\Consumer;
|
||||
use Hyperf\Amqp\Message\ConsumerMessage;
|
||||
use Hyperf\Amqp\Result;
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
use App\Utils\Log;
|
||||
|
||||
/**
|
||||
* IM 多媒体文件异步处理消费者
|
||||
*/
|
||||
#[Consumer(exchange: 'amqp.direct', routingKey: 'UploadImMedia', queue: 'UploadImMediaQueue', name: "UploadImMediaConsumer", nums: 1)]
|
||||
class UploadImMediaConsumer extends ConsumerMessage
|
||||
{
|
||||
public function consumeMessage($data, AMQPMessage $message): string
|
||||
{
|
||||
try {
|
||||
$messageKey = $data['message_key'];
|
||||
$msgType = $data['msg_type'];
|
||||
$msgContent = $data['msg_content'];
|
||||
|
||||
// 初始化 Http Client 和 OSS
|
||||
$client = new Client(['verify' => false, 'timeout' => 30]); // 设置合适的超时时间
|
||||
$oss = new Oss();
|
||||
$needUpdate = false;
|
||||
$customDomain = config('alibaba.oss.custom_domain_name'); // 取 OSS 自定义域名
|
||||
|
||||
// 1. 根据不同类型提取 URL 并转存 OSS
|
||||
if ($msgType === 'TIMImageElem' && isset($msgContent['ImageInfoArray'])) {
|
||||
// 图片通常包含原图、大图、缩略图,根据需求替换
|
||||
foreach ($msgContent['ImageInfoArray'] as &$imageInfo) {
|
||||
if (!empty($imageInfo['URL'])) {
|
||||
$content = $client->get($imageInfo['URL'])->getBody()->getContents();
|
||||
$filename = "im/image/" . date('Ymd') . "/" . $messageKey . "_" . $imageInfo['Type'] . ".jpg";
|
||||
$ossFilename = $oss->putObject($filename, $content);
|
||||
|
||||
$imageInfo['URL'] = rtrim($customDomain, '/') . '/' . $ossFilename;
|
||||
$needUpdate = true;
|
||||
}
|
||||
}
|
||||
} elseif ($msgType === 'TIMSoundElem' && !empty($msgContent['Url'])) {
|
||||
// 语音消息
|
||||
$content = $client->get($msgContent['Url'])->getBody()->getContents();
|
||||
$filename = "im/sound/" . date('Ymd') . "/" . $messageKey . ".amr";
|
||||
$ossFilename = $oss->putObject($filename, $content);
|
||||
|
||||
$msgContent['Url'] = rtrim($customDomain, '/') . '/' . $ossFilename;
|
||||
$needUpdate = true;
|
||||
} elseif ($msgType === 'TIMVideoFileElem') {
|
||||
// 视频文件
|
||||
if (!empty($msgContent['VideoUrl'])) {
|
||||
$content = $client->get($msgContent['VideoUrl'])->getBody()->getContents();
|
||||
$filename = "im/video/" . date('Ymd') . "/" . $messageKey . ".mp4";
|
||||
$ossFilename = $oss->putObject($filename, $content);
|
||||
$msgContent['VideoUrl'] = rtrim($customDomain, '/') . '/' . $ossFilename;
|
||||
$needUpdate = true;
|
||||
}
|
||||
// 视频封面图
|
||||
if (!empty($msgContent['ThumbUrl'])) {
|
||||
$content = $client->get($msgContent['ThumbUrl'])->getBody()->getContents();
|
||||
$filename = "im/video_thumb/" . date('Ymd') . "/" . $messageKey . ".jpg";
|
||||
$ossFilename = $oss->putObject($filename, $content);
|
||||
$msgContent['ThumbUrl'] = rtrim($customDomain, '/') . '/' . $ossFilename;
|
||||
$needUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 更新对应记录的 JSON 数据
|
||||
if ($needUpdate) {
|
||||
MessageIm::where(['message_key' => $messageKey])->update([
|
||||
'message_content' => json_encode($msgContent, JSON_UNESCAPED_UNICODE)
|
||||
]);
|
||||
}
|
||||
|
||||
return Result::ACK; // 任务成功完成
|
||||
} catch (\Throwable $e) {
|
||||
Log::getInstance("UploadImMediaConsumer")->error("IM多媒体上传OSS失败: " . $e->getMessage() . ' Line: ' . $e->getLine());
|
||||
// 如果抛错可以 DROP 掉或者返回 REQUEUE 进入死信重试队列
|
||||
return Result::DROP;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Amqp\Producer;
|
||||
|
||||
use Hyperf\Amqp\Annotation\Producer;
|
||||
use Hyperf\Amqp\Message\ProducerMessage;
|
||||
|
||||
/**
|
||||
* IM 多媒体文件异步下载并上传 OSS
|
||||
*/
|
||||
#[Producer(exchange: 'amqp.direct', routingKey: 'UploadImMedia')]
|
||||
class UploadImMediaProducer extends ProducerMessage
|
||||
{
|
||||
/**
|
||||
* @param array $data
|
||||
* [
|
||||
* "message_key" => "消息唯一ID",
|
||||
* "msg_type" => "消息类型",
|
||||
* "msg_content" => "消息具体内容(数组)"
|
||||
* ]
|
||||
*/
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->payload = $data;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Command;
|
||||
|
||||
use App\Exception\BusinessException;
|
||||
use App\Model\OrderInquiry;
|
||||
use App\Model\OrderPrescription;
|
||||
use App\Model\OrderProduct;
|
||||
use App\Model\OrderSystem;
|
||||
@@ -46,32 +47,48 @@ class ReportPreProductOrderCommand extends HyperfCommand
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$this->line("开始");
|
||||
$this->line("==================开始==================");
|
||||
|
||||
// 获取可上报商品订单
|
||||
$order_product_ids = $this->getExecProductOrder();
|
||||
if (empty($order_product_ids)){
|
||||
$this->line("结束,无可上报的商品订单");
|
||||
$this->line("==================结束,无可上报的商品订单==================");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($order_product_ids as $item){
|
||||
$this->line("本次请求订单号:" . $item['order_product_id']);
|
||||
$this->line("--本次请求订单号(order_product_id):" . $item['order_product_id']);
|
||||
|
||||
// 获取商品订单数据
|
||||
$params = array();
|
||||
$params['order_product_id'] = $item['order_product_id'];
|
||||
$order_product = OrderProduct::getOne($params);
|
||||
if (empty($order_product)) {
|
||||
$this->line("结束,商品订单数据错误");
|
||||
$this->line("--结束,商品订单数据错误");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($order_product['report_pre_status'] == 1){
|
||||
$this->line("结束,订单已上报");
|
||||
$this->line("--结束,订单已上报");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取问诊订单数据
|
||||
// $params = array();
|
||||
// $params['order_inquiry_id'] = $item['order_inquiry_id'];
|
||||
// $order_inquiry = OrderInquiry::getOne($params);
|
||||
// if (empty($order_inquiry)) {
|
||||
// $this->line("结束,问诊订单数据错误");
|
||||
// continue;
|
||||
// }
|
||||
|
||||
//检测是否为抄方订单
|
||||
// if (!empty($order_inquiry['transfer_doctor_id'])){
|
||||
// $this->line("结束,该问诊订单为抄方订单");
|
||||
// continue;
|
||||
// }
|
||||
|
||||
|
||||
Db::beginTransaction();
|
||||
|
||||
try {
|
||||
@@ -81,7 +98,8 @@ class ReportPreProductOrderCommand extends HyperfCommand
|
||||
// 检测执行次数
|
||||
$res = $this->checkHandleNumber($item['order_product_id']);
|
||||
if (!$res) {
|
||||
Log::getInstance("queue-AutoPharmacistCaVerify")->error("错误:超出最大执行次数或检测错误");
|
||||
$this->line("--错误:超出最大执行次数或检测错误");
|
||||
Log::getInstance("queue-AutoPharmacistCaVerify")->error("--错误:超出最大执行次数或检测错误");
|
||||
|
||||
// 修改药品订单表上报处方平台状态
|
||||
$this->saveOrderProductPeportPreStatus($item['order_product_id'],2,"超出最大上报次数");
|
||||
@@ -112,7 +130,7 @@ class ReportPreProductOrderCommand extends HyperfCommand
|
||||
}catch(\Exception $e){
|
||||
Db::rollBack();
|
||||
// 记录失败次数
|
||||
$this->line("错误:" . $e->getMessage());
|
||||
$this->line("--错误:" . $e->getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -124,16 +142,16 @@ class ReportPreProductOrderCommand extends HyperfCommand
|
||||
// 修改药品订单表上报处方平台状态
|
||||
$this->saveOrderProductPeportPreStatus($item['order_product_id'],1);
|
||||
|
||||
$this->line("成功");
|
||||
$this->line("--成功");
|
||||
Db::commit();
|
||||
continue;
|
||||
} catch (\Exception $e) {
|
||||
Db::rollBack();
|
||||
$this->line("错误" . $e->getMessage());
|
||||
$this->line("--错误" . $e->getMessage());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$this->line("全部结束");
|
||||
$this->line("==================全部结束==================");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,6 +170,7 @@ class ReportPreProductOrderCommand extends HyperfCommand
|
||||
'order_product_id',
|
||||
'patient_id',
|
||||
'order_prescription_id',
|
||||
'order_inquiry_id',
|
||||
];
|
||||
|
||||
$order_product = OrderProduct::getList($params,$fields);
|
||||
|
||||
@@ -9,12 +9,12 @@ use App\Model\HospitalDepartmentCustom;
|
||||
use App\Model\OrderInquiry;
|
||||
use App\Model\OrderInquiryCase;
|
||||
use App\Model\OrderPrescription;
|
||||
use App\Model\OrderPrescriptionFile;
|
||||
use App\Model\OrderPrescriptionIcd;
|
||||
use App\Model\OrderPrescriptionProduct;
|
||||
use App\Model\PatientFamily;
|
||||
use App\Model\Product;
|
||||
use App\Model\ReportRegulatory;
|
||||
use App\Model\UserCaCert;
|
||||
use App\Model\UserDoctor;
|
||||
use App\Model\UserDoctorInfo;
|
||||
use App\Model\UserPharmacist;
|
||||
@@ -82,8 +82,15 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
|
||||
// 上报网络咨询
|
||||
try {
|
||||
if ($report_regulatory['report_consult_status'] != 1 && $report_regulatory['report_consult_int'] < 5){
|
||||
$this->line("上报网络咨询");
|
||||
|
||||
$this->line("A、开始上报网络咨询");
|
||||
//检测是否为抄方订单
|
||||
$transferInquiryOrder = $this->checkTransferInquiryOrder($order_inquiry);
|
||||
if(!$transferInquiryOrder){
|
||||
$this->line("A-1、该问诊不上报网络咨询");
|
||||
}
|
||||
if ($report_regulatory['report_consult_status'] != 1 && $report_regulatory['report_consult_int'] < 5 && $transferInquiryOrder){
|
||||
$this->line("A-2、上报网络咨询");
|
||||
|
||||
// 获取上报数据-网络咨询
|
||||
$consult_data = $this->getConsultData($order_inquiry);
|
||||
@@ -92,39 +99,46 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
$regulatoryPlatform = new regulatoryPlatform();
|
||||
$result = $regulatoryPlatform->uploadConsult([$consult_data]);
|
||||
|
||||
$this->line("上报网络咨询成功" . json_encode($result,JSON_UNESCAPED_UNICODE));
|
||||
$this->line("A-3、上报网络咨询成功" . json_encode($result,JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// 修改上报状态-网络咨询
|
||||
$res = $this->modifyReportConsultStatus($report_regulatory, 1);
|
||||
if (!$res) {
|
||||
// 记录失败
|
||||
$this->line("上报成功,存储记录失败");
|
||||
$this->line("A-4、上报成功,存储记录失败");
|
||||
}
|
||||
}
|
||||
}catch (\Throwable $e){
|
||||
$this->line("上报网络咨询失败" . $e->getMessage());
|
||||
$this->line("A-5、上报网络咨询失败" . $e->getMessage());
|
||||
|
||||
// 上报失败
|
||||
$res = $this->modifyReportConsultStatus($report_regulatory, 2, $e->getMessage());
|
||||
if (!$res) {
|
||||
// 记录失败
|
||||
$this->line("上报网络咨询失败,存储记录失败");
|
||||
$this->line("A-6、上报网络咨询失败,存储记录失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 上报复诊
|
||||
try {
|
||||
if ($report_regulatory['is_further_consult'] == 1){
|
||||
|
||||
$this->line("B、开始上报复诊");
|
||||
//检测是否为抄方订单
|
||||
$transferInquiryOrder = $this->checkTransferInquiryOrder($order_inquiry);
|
||||
if(!$transferInquiryOrder){
|
||||
$this->line("B-1、该问诊不上报复诊");
|
||||
}
|
||||
if ($report_regulatory['is_further_consult'] == 1 && $transferInquiryOrder){
|
||||
if ($report_regulatory['report_further_consult_status'] != 1 && $report_regulatory['report_further_consult_int'] < 5) {
|
||||
$this->line("上报复诊");
|
||||
$this->line("B-2、上报复诊");
|
||||
|
||||
// 获取处方数据
|
||||
$params = array();
|
||||
$params['order_prescription_id'] = $report_regulatory['order_prescription_id'];
|
||||
$order_prescription = OrderPrescription::getOne($params);
|
||||
if (empty($order_prescription)){
|
||||
$this->line("需上报复诊,但无处方数据");
|
||||
$this->line("B-3、需上报复诊,但无处方数据");
|
||||
}else{
|
||||
// 获取上报数据-复诊
|
||||
$further_consult_data = $this->getFurtherConsultData($order_inquiry,$order_prescription);
|
||||
@@ -132,32 +146,31 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
// 上报复诊
|
||||
$regulatoryPlatform = new regulatoryPlatform();
|
||||
$result = $regulatoryPlatform->uploadFurtherConsult([$further_consult_data]);
|
||||
$this->line("上报复诊成功" . json_encode($result,JSON_UNESCAPED_UNICODE));
|
||||
$this->line("B-4、上报复诊成功" . json_encode($result,JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// 修改上报状态-复诊
|
||||
$res = $this->modifyReportFurtherConsultStatus($report_regulatory, 1);
|
||||
if (!$res) {
|
||||
// 记录失败
|
||||
$this->line("上报成功,存储记录失败");
|
||||
$this->line("B-5、上报成功,存储记录失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch (\Throwable $e){
|
||||
$this->line("上报复诊失败" . $e->getMessage());
|
||||
$this->line("B-6、上报复诊失败" . $e->getMessage());
|
||||
|
||||
// 上报失败
|
||||
$res = $this->modifyReportFurtherConsultStatus($report_regulatory, 2, $e->getMessage());
|
||||
if (!$res) {
|
||||
// 记录失败
|
||||
$this->line("上报复诊失败,存储记录失败");
|
||||
$this->line("B-7、上报复诊失败,存储记录失败");
|
||||
}
|
||||
}
|
||||
|
||||
// 上报处方
|
||||
try {
|
||||
$this->line("上报处方");
|
||||
|
||||
$this->line("C、开始上报处方");
|
||||
// 获取处方数据
|
||||
$params = array();
|
||||
$params['order_prescription_id'] = $report_regulatory['order_prescription_id'];
|
||||
@@ -165,30 +178,52 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
if (empty($order_prescription)){
|
||||
$this->line("需上报复诊,但无处方数据");
|
||||
}else{
|
||||
// 获取上报数据-处方
|
||||
$report_prescription_data = $this->getReportPrescriptionData($order_inquiry, $order_prescription);
|
||||
|
||||
//检测是否为抄方订单
|
||||
$transferInquiryOrder = $this->checkTransferInquiryOrder($order_inquiry);
|
||||
if($transferInquiryOrder){
|
||||
$this->line("C-1、检测处方数据");
|
||||
// 获取上报数据-处方
|
||||
$report_prescription_data = $this->getReportPrescriptionData($order_inquiry, $order_prescription);
|
||||
}else{
|
||||
$this->line("C-2、检测处方(抄方)数据");
|
||||
// 获取上报数据-处方 (抄方类型)
|
||||
$report_prescription_data = $this->getReportTransferPrescriptionDataV2($order_inquiry, $order_prescription);
|
||||
}
|
||||
|
||||
$this->line("C-3、上报处方");
|
||||
// 上报处方
|
||||
$regulatoryPlatform = new regulatoryPlatform();
|
||||
$result = $regulatoryPlatform->uploadRecipe([$report_prescription_data]);
|
||||
|
||||
$this->line("上报处方成功" . json_encode($result,JSON_UNESCAPED_UNICODE));
|
||||
$this->line("C-4、上报处方成功" . json_encode($result,JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$report_prescription_detail_data = $this->getReportPrescriptionDetailData(
|
||||
$order_prescription,
|
||||
$report_prescription_data
|
||||
);
|
||||
if (empty($report_prescription_detail_data)) {
|
||||
throw new BusinessException("处方明细数据错误");
|
||||
}
|
||||
|
||||
$result = $regulatoryPlatform->uploadRecipeDetail($report_prescription_detail_data);
|
||||
$this->line("C-4-1、上报处方明细成功" . json_encode($result,JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// 上报成功
|
||||
$res = $this->modifyReportRegulatoryPrescription($report_regulatory, 1);
|
||||
if (!$res) {
|
||||
// 记录失败
|
||||
$this->line("上报处方失败,存储记录失败");
|
||||
$this->line("C-5、上报处方失败,存储记录失败");
|
||||
}
|
||||
}
|
||||
}catch (\Throwable $e){
|
||||
$this->line("上报处方失败" . $e->getMessage());
|
||||
$this->line("C-6、上报处方失败" . $e->getMessage());
|
||||
|
||||
// 上报失败
|
||||
$res = $this->modifyReportRegulatoryPrescription($report_regulatory, 2, $e->getMessage());
|
||||
if (!$res) {
|
||||
// 记录失败
|
||||
$this->line("上报复诊失败,存储记录失败");
|
||||
$this->line("C-7、上报复诊失败,存储记录失败");
|
||||
return ;
|
||||
}
|
||||
}
|
||||
@@ -318,7 +353,33 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
$this->line("信息:订单未取消的未完成订单,不执行");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测抄方订单
|
||||
* @param array|object $order_inquiry 问诊订单数据
|
||||
* @return bool true:可执行 false:不可执行
|
||||
*/
|
||||
private function checkTransferInquiryOrder(array|object $order_inquiry): bool
|
||||
{
|
||||
//判断该问诊是否为抄方
|
||||
if(!empty($order_inquiry['transfer_doctor_id'])){
|
||||
$this->line("信息:该问诊为抄方类型");
|
||||
return false;
|
||||
}else{
|
||||
$params = array();
|
||||
$params['doctor_id'] = $order_inquiry['doctor_id'];
|
||||
$user_doctor_info = UserDoctor::getOne($params);
|
||||
if (empty($user_doctor_info)) {
|
||||
$this->line("错误:医生详情数据错误,不执行");
|
||||
return false;
|
||||
}
|
||||
if ($user_doctor_info['multi_point_status'] != 1) {
|
||||
$this->line("错误:医生为非多点执业,不执行");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -424,7 +485,7 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
$params['user_id'] = $user_doctor['user_id'];
|
||||
$params['type'] = 2;
|
||||
$params['is_latest'] = 1;
|
||||
$doctor_user_ca_cert = UserCaCert::getOne($params);
|
||||
$doctor_user_ca_cert = $this->buildLegacyCaCertPayload($order_prescription['order_prescription_id'], 'doctor');
|
||||
if (empty($doctor_user_ca_cert)){
|
||||
$this->line("错误:无医生ca数据");
|
||||
return false;
|
||||
@@ -435,7 +496,7 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
$params['user_id'] = $user_pharmacist['user_id'];
|
||||
$params['type'] = 2;
|
||||
$params['is_latest'] = 1;
|
||||
$pharmacist_user_ca_cert = UserCaCert::getOne($params);
|
||||
$pharmacist_user_ca_cert = $this->buildLegacyCaCertPayload($order_prescription['order_prescription_id'], 'pharmacist');
|
||||
if (empty($pharmacist_user_ca_cert)){
|
||||
$this->line("错误:无药师ca数据");
|
||||
return false;
|
||||
@@ -483,6 +544,332 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
$data['recipeNo'] = $order_prescription['order_prescription_id']; // 医院处方编号
|
||||
$data['cityId'] = "510100"; // 城市ID(参考地区字段)
|
||||
|
||||
$pharmacist_ca_data = $this->getStoredPrescriptionCaData($order_prescription['order_prescription_id'], 'pharmacist');
|
||||
$data['caSupplier'] = $pharmacist_ca_data['caSupplier'] ?? "";
|
||||
$data['caSignType'] = $pharmacist_ca_data['caSignType'] ?? "";
|
||||
$data['caSignTime'] = $pharmacist_ca_data['caSignTime'] ?? "";
|
||||
$data['caSignOriginaltext'] = $pharmacist_ca_data['caSignOriginaltext'] ?? "";
|
||||
$data['caSignText'] = $pharmacist_ca_data['caSignText'] ?? "";
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上报数据-处方 (抄方类型)
|
||||
* @param array|object $order_inquiry
|
||||
* @param array|object $order_prescription
|
||||
* @return bool|array
|
||||
*/
|
||||
private function getReportTransferPrescriptionData(array|object $order_inquiry, array|object $order_prescription): bool|array
|
||||
{
|
||||
return $this->getReportTransferPrescriptionDataV2($order_inquiry, $order_prescription);
|
||||
// 获取医生数据
|
||||
$params = array();
|
||||
$params['doctor_id'] = $order_prescription['doctor_id'];
|
||||
$user_doctor = UserDoctor::getOne($params);
|
||||
if (empty($user_doctor)) {
|
||||
$this->line("错误:医生数据错误");
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = array();
|
||||
$params['doctor_id'] = $order_prescription['doctor_id'];
|
||||
$user_doctor_info = UserDoctorInfo::getOne($params);
|
||||
if (empty($user_doctor_info)) {
|
||||
$this->line("错误:医生详情数据错误");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取医生自定义科室数据
|
||||
$params = array();
|
||||
$params['department_custom_id'] = $user_doctor['department_custom_id'];
|
||||
$hospital_department_custom = HospitalDepartmentCustom::getOne($params);
|
||||
if (empty($hospital_department_custom)) {
|
||||
$this->line("错误:医生自定义数据错误");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取问诊患者数据
|
||||
$params = array();
|
||||
$params['family_id'] = $order_inquiry['family_id'];
|
||||
$patient_family = PatientFamily::getOne($params);
|
||||
if (empty($patient_family)) {
|
||||
$this->line("错误:问诊患者数据错误");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取患者问诊病例
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $order_inquiry['order_inquiry_id'];
|
||||
$params['status'] = 1;
|
||||
$order_inquiry_case = OrderInquiryCase::getOne($params);
|
||||
if (empty($order_inquiry_case)) {
|
||||
$this->line("错误:患者问诊病例错误");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取药师数据
|
||||
$params = array();
|
||||
$params['pharmacist_id'] = $order_prescription['pharmacist_id'];
|
||||
$user_pharmacist = UserPharmacist::getOne($params);
|
||||
if (empty($user_pharmacist)) {
|
||||
$this->line("错误:药师数据错误");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取药师详情数据
|
||||
$params = array();
|
||||
$params['pharmacist_id'] = $order_prescription['pharmacist_id'];
|
||||
$user_pharmacist_info = UserPharmacistInfo::getOne($params);
|
||||
if (empty($user_pharmacist_info)) {
|
||||
$this->line("错误:药师详情数据错误");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取患者复诊疾病诊断数据
|
||||
$params = array();
|
||||
$params['order_prescription_id'] = $order_prescription['order_prescription_id'];
|
||||
$order_prescription_icd = OrderPrescriptionIcd::getList($params);
|
||||
if (empty($order_prescription_icd)) {
|
||||
// 复诊,但是未找到关联疾病
|
||||
$this->line("错误:无复诊疾病诊断数据");
|
||||
return false;
|
||||
}
|
||||
|
||||
$icd_name_data = array_column($order_prescription_icd->toArray(), 'icd_name');
|
||||
if (!empty($icd_name_data)) {
|
||||
if (count($icd_name_data) > 1) {
|
||||
$icd_name = implode('|', $icd_name_data);
|
||||
} else {
|
||||
$icd_name = $icd_name_data[0];
|
||||
}
|
||||
}
|
||||
|
||||
// 获取处方商品数据
|
||||
$order_prescription_product = $this->getPreProductData($order_prescription['order_prescription_id']);
|
||||
if (empty($order_prescription_product)){
|
||||
$this->line("错误:无处方商品数据");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取医生ca数据
|
||||
$params = array();
|
||||
$params['user_id'] = $user_doctor['user_id'];
|
||||
$params['type'] = 2;
|
||||
$params['is_latest'] = 1;
|
||||
$doctor_user_ca_cert = $this->buildLegacyCaCertPayload($order_prescription['order_prescription_id'], 'doctor');
|
||||
if (empty($doctor_user_ca_cert)){
|
||||
$this->line("错误:无医生ca数据");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取药师ca数据
|
||||
$params = array();
|
||||
$params['user_id'] = $user_pharmacist['user_id'];
|
||||
$params['type'] = 2;
|
||||
$params['is_latest'] = 1;
|
||||
$pharmacist_user_ca_cert = $this->buildLegacyCaCertPayload($order_prescription['order_prescription_id'], 'pharmacist');
|
||||
if (empty($pharmacist_user_ca_cert)){
|
||||
$this->line("错误:无药师ca数据");
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$data['thirdUniqueid'] = $order_inquiry['order_inquiry_id']; // 唯一标识
|
||||
$data['orgName'] = "成都金牛欣欣相照互联网医院"; // 机构名称
|
||||
$data['orgCode'] = "MA6CGUDA251010619D2112"; // 机构编码
|
||||
$data['section'] = $hospital_department_custom['department_name'];//科室名称
|
||||
$data['sectionCode'] = $hospital_department_custom['department_code'];//科室编码
|
||||
$data['docName'] = $user_doctor['user_name'];// 医师姓名
|
||||
$data['docCertificateNum'] = $user_doctor_info['qualification_cert_num']; // 医师执业资格证号
|
||||
$data['pharmacistName'] = $user_pharmacist_info['card_name']; // 药师姓名
|
||||
$data['pharmacistOrg'] = "成都金牛欣欣相照互联网医院"; // 药师执业机构
|
||||
$data['pharmacistCertificateNum'] = $user_pharmacist_info['qualification_cert_num']; // 药师执业资格证号
|
||||
$data['furtherConsultNo'] = $order_inquiry['order_inquiry_id']; // 网络复诊编号
|
||||
$data['furtherConsultDiagnosis'] = $icd_name; // 复诊诊断 复诊患者在实体医院的诊断名称,如有多条,使用“|”进行分隔
|
||||
$data['patientName'] = $order_inquiry['patient_name']; // 患者姓名
|
||||
$data['patientSex'] = $order_inquiry['patient_sex'] == 0 ?: 1; // 患者性别
|
||||
$data['patientAge'] = (int)$order_inquiry['patient_age']; // 患者年龄
|
||||
$data['patientIdcardType'] = 1; // 证件类型
|
||||
$data['patientIdcardNum'] = $patient_family['id_number']; // 患者证件号码
|
||||
$data['feeType'] = 1; // 费别 1自费 2医保
|
||||
$data['medicalHistory'] = $order_inquiry_case['disease_desc']; //患者病史描述
|
||||
$data['recipeTime'] = $order_prescription['doctor_created_time']; // 处方日期
|
||||
$data['recipeType'] = 2; // 处方类型 1中药 2西药 3成药(三医)
|
||||
$data['reviewTime'] = $order_prescription['pharmacist_verify_time']; // 审方日期
|
||||
$data['recipeUnitPrice'] = $order_prescription_product['amount_total']; // 处方单价 元
|
||||
$data['drugName'] = $order_prescription_product['drug_name']; // 药品名称商品名
|
||||
$data['drugCode'] = $order_prescription_product['drug_code']; // 药品编码
|
||||
$data['drugCommonName'] = $order_prescription_product['drug_common_name']; // 药品通用名
|
||||
$data['specification'] = $order_prescription_product['specification']; // 规格
|
||||
$data['frequency'] = $order_prescription_product['frequency']; // 使用频度
|
||||
$data['usage'] = $order_prescription_product['usage']; // 用法
|
||||
$data['doseUnit'] = $order_prescription_product['dose_unit']; // 剂量单位
|
||||
$data['doseEachTime'] = $order_prescription_product['dose_each_time']; // 每次剂量
|
||||
$data['medicationDays'] = $order_prescription_product['medication_days']; // 用药天数
|
||||
$data['quantity'] = $order_prescription_product['quantity']; // 数量
|
||||
$data['drugPackage'] = $order_prescription_product['drug_package']; // 药品包装
|
||||
$data['recipeAllPrice'] = $order_prescription_product['amount_total']; // 处方总价
|
||||
$data['uploadTime'] = date("Y-m-d H:i:s",time()); // 上传时间
|
||||
$data['docCaSign'] = $doctor_user_ca_cert['cert_base64']; // 医师ca签名值
|
||||
$data['pharmacistCaSign'] = $pharmacist_user_ca_cert['cert_base64']; // 药师ca签名值
|
||||
$data['recipeNo'] = $order_prescription['order_prescription_id']; // 医院处方编号
|
||||
$data['cityId'] = "510100"; // 城市ID(参考地区字段)
|
||||
|
||||
$pharmacist_ca_data = $this->getStoredPrescriptionCaData($order_prescription['order_prescription_id'], 'pharmacist');
|
||||
$data['caSupplier'] = $pharmacist_ca_data['caSupplier'] ?? "";
|
||||
$data['caSignType'] = $pharmacist_ca_data['caSignType'] ?? "";
|
||||
$data['caSignTime'] = $pharmacist_ca_data['caSignTime'] ?? "";
|
||||
$data['caSignOriginaltext'] = $pharmacist_ca_data['caSignOriginaltext'] ?? "";
|
||||
$data['caSignText'] = $pharmacist_ca_data['caSignText'] ?? "";
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function getReportTransferPrescriptionDataV2(array|object $order_inquiry, array|object $order_prescription): bool|array
|
||||
{
|
||||
$params = array();
|
||||
$params['doctor_id'] = $order_prescription['doctor_id'];
|
||||
$user_doctor = UserDoctor::getOne($params);
|
||||
if (empty($user_doctor)) {
|
||||
$this->line("閿欒锛氬尰鐢熸暟鎹敊璇?");
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = array();
|
||||
$params['doctor_id'] = $order_prescription['doctor_id'];
|
||||
$user_doctor_info = UserDoctorInfo::getOne($params);
|
||||
if (empty($user_doctor_info)) {
|
||||
$this->line("閿欒锛氬尰鐢熻鎯呮暟鎹敊璇?");
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = array();
|
||||
$params['department_custom_id'] = $user_doctor['department_custom_id'];
|
||||
$hospital_department_custom = HospitalDepartmentCustom::getOne($params);
|
||||
if (empty($hospital_department_custom)) {
|
||||
$this->line("閿欒锛氬尰鐢熻嚜瀹氫箟鏁版嵁閿欒");
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = array();
|
||||
$params['family_id'] = $order_inquiry['family_id'];
|
||||
$patient_family = PatientFamily::getOne($params);
|
||||
if (empty($patient_family)) {
|
||||
$this->line("閿欒锛氶棶璇婃偅鑰呮暟鎹敊璇?");
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $order_inquiry['order_inquiry_id'];
|
||||
$params['status'] = 1;
|
||||
$order_inquiry_case = OrderInquiryCase::getOne($params);
|
||||
if (empty($order_inquiry_case)) {
|
||||
$this->line("閿欒锛氭偅鑰呴棶璇婄梾渚嬮敊璇?");
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = array();
|
||||
$params['pharmacist_id'] = $order_prescription['pharmacist_id'];
|
||||
$user_pharmacist = UserPharmacist::getOne($params);
|
||||
if (empty($user_pharmacist)) {
|
||||
$this->line("閿欒锛氳嵂甯堟暟鎹敊璇?");
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = array();
|
||||
$params['pharmacist_id'] = $order_prescription['pharmacist_id'];
|
||||
$user_pharmacist_info = UserPharmacistInfo::getOne($params);
|
||||
if (empty($user_pharmacist_info)) {
|
||||
$this->line("閿欒锛氳嵂甯堣鎯呮暟鎹敊璇?");
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = array();
|
||||
$params['order_prescription_id'] = $order_prescription['order_prescription_id'];
|
||||
$order_prescription_icd = OrderPrescriptionIcd::getList($params);
|
||||
if (empty($order_prescription_icd)) {
|
||||
$this->line("閿欒锛氭棤澶嶈瘖鐤剧梾璇婃柇鏁版嵁");
|
||||
return false;
|
||||
}
|
||||
|
||||
$icd_name = "";
|
||||
$icd_name_data = array_column($order_prescription_icd->toArray(), 'icd_name');
|
||||
if (!empty($icd_name_data)) {
|
||||
if (count($icd_name_data) > 1) {
|
||||
$icd_name = implode('|', $icd_name_data);
|
||||
} else {
|
||||
$icd_name = $icd_name_data[0];
|
||||
}
|
||||
}
|
||||
|
||||
$order_prescription_product = $this->getPreProductData($order_prescription['order_prescription_id']);
|
||||
if (empty($order_prescription_product)) {
|
||||
$this->line("閿欒锛氭棤澶勬柟鍟嗗搧鏁版嵁");
|
||||
return false;
|
||||
}
|
||||
|
||||
$doctor_user_ca_cert = $this->buildLegacyCaCertPayload($order_prescription['order_prescription_id'], 'doctor');
|
||||
if (empty($doctor_user_ca_cert)) {
|
||||
$this->line("閿欒锛氭棤鍖荤敓ca鏁版嵁");
|
||||
return false;
|
||||
}
|
||||
|
||||
$pharmacist_user_ca_cert = $this->buildLegacyCaCertPayload($order_prescription['order_prescription_id'], 'pharmacist');
|
||||
if (empty($pharmacist_user_ca_cert)) {
|
||||
$this->line("閿欒锛氭棤鑽笀ca鏁版嵁");
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$data['thirdUniqueid'] = $order_inquiry['order_inquiry_id'];
|
||||
$data['orgName'] = "鎴愰兘閲戠墰娆f鐩哥収浜掕仈缃戝尰闄?";
|
||||
$data['orgCode'] = "MA6CGUDA251010619D2112";
|
||||
$data['section'] = $hospital_department_custom['department_name'];
|
||||
$data['sectionCode'] = $hospital_department_custom['department_code'];
|
||||
$data['docName'] = $user_doctor['user_name'];
|
||||
$data['docCertificateNum'] = $user_doctor_info['qualification_cert_num'];
|
||||
$data['pharmacistName'] = $user_pharmacist_info['card_name'];
|
||||
$data['pharmacistOrg'] = "鎴愰兘閲戠墰娆f鐩哥収浜掕仈缃戝尰闄?";
|
||||
$data['pharmacistCertificateNum'] = $user_pharmacist_info['qualification_cert_num'];
|
||||
$data['furtherConsultNo'] = $order_inquiry['order_inquiry_id'];
|
||||
$data['furtherConsultDiagnosis'] = $icd_name;
|
||||
$data['patientName'] = $order_inquiry['patient_name'];
|
||||
$data['patientSex'] = $order_inquiry['patient_sex'] == 0 ?: 1;
|
||||
$data['patientAge'] = (int) $order_inquiry['patient_age'];
|
||||
$data['patientIdcardType'] = 1;
|
||||
$data['patientIdcardNum'] = $patient_family['id_number'];
|
||||
$data['feeType'] = 1;
|
||||
$data['medicalHistory'] = $order_inquiry_case['disease_desc'];
|
||||
$data['recipeTime'] = $order_prescription['doctor_created_time'];
|
||||
$data['recipeType'] = 2;
|
||||
$data['reviewTime'] = $order_prescription['pharmacist_verify_time'];
|
||||
$data['recipeUnitPrice'] = $order_prescription_product['amount_total'];
|
||||
$data['drugName'] = $order_prescription_product['drug_name'];
|
||||
$data['drugCode'] = $order_prescription_product['drug_code'];
|
||||
$data['drugCommonName'] = $order_prescription_product['drug_common_name'];
|
||||
$data['specification'] = $order_prescription_product['specification'];
|
||||
$data['frequency'] = $order_prescription_product['frequency'];
|
||||
$data['usage'] = $order_prescription_product['usage'];
|
||||
$data['doseUnit'] = $order_prescription_product['dose_unit'];
|
||||
$data['doseEachTime'] = $order_prescription_product['dose_each_time'];
|
||||
$data['medicationDays'] = $order_prescription_product['medication_days'];
|
||||
$data['quantity'] = $order_prescription_product['quantity'];
|
||||
$data['drugPackage'] = $order_prescription_product['drug_package'];
|
||||
$data['recipeAllPrice'] = $order_prescription_product['amount_total'];
|
||||
$data['uploadTime'] = date("Y-m-d H:i:s", time());
|
||||
$data['docCaSign'] = $doctor_user_ca_cert['cert_base64'];
|
||||
$data['pharmacistCaSign'] = $pharmacist_user_ca_cert['cert_base64'];
|
||||
$data['recipeNo'] = $order_prescription['order_prescription_id'];
|
||||
$data['cityId'] = "510100";
|
||||
|
||||
$pharmacist_ca_data = $this->getStoredPrescriptionCaData($order_prescription['order_prescription_id'], 'pharmacist');
|
||||
$data['caSupplier'] = $pharmacist_ca_data['caSupplier'] ?? "";
|
||||
$data['caSignType'] = $pharmacist_ca_data['caSignType'] ?? "";
|
||||
$data['caSignTime'] = $pharmacist_ca_data['caSignTime'] ?? "";
|
||||
$data['caSignOriginaltext'] = $pharmacist_ca_data['caSignOriginaltext'] ?? "";
|
||||
$data['caSignText'] = $pharmacist_ca_data['caSignText'] ?? "";
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -522,9 +909,9 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
$specification = $item['product_spec']; // 规格
|
||||
$frequency = $item['frequency_use']; // 使用频度
|
||||
$usage = $item['single_use']; // 用法
|
||||
$dose_unit = $product['single_unit']; // 剂量单位
|
||||
$dose_each_time = $product['single_unit']; // 每次剂量
|
||||
$medication_days = (double)$product['available_days']; // 用药天数
|
||||
$dose_unit = $item['single_unit']; // 剂量单位
|
||||
$dose_each_time = $item['single_unit']; // 每次剂量
|
||||
$medication_days = (double)$item['available_days']; // 用药天数
|
||||
$quantity = (double)$item['prescription_product_num']; // 数量
|
||||
$drug_package = $item['packaging_unit']; // 药品包装
|
||||
} else {
|
||||
@@ -534,14 +921,22 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
$specification = $specification . "|" . $item['product_spec']; // 规格
|
||||
$frequency = $frequency . "|" . $item['frequency_use']; // 使用频度
|
||||
$usage = $usage . "|" . $item['single_use']; // 用法
|
||||
$dose_unit = $dose_unit . "|" . $product['single_unit']; // 剂量单位
|
||||
$dose_each_time = $dose_each_time . "|" . $product['single_unit']; // 每次剂量
|
||||
$medication_days = $medication_days . "|" . (double)$product['available_days']; // 用药天数
|
||||
$dose_unit = $dose_unit . "|" . $item['single_unit']; // 剂量单位
|
||||
$dose_each_time = $dose_each_time . "|" . $item['single_unit']; // 每次剂量
|
||||
$medication_days = $medication_days . "|" . (double)$item['available_days']; // 用药天数
|
||||
$quantity = $quantity . "|" . (double)$item['prescription_product_num']; // 数量
|
||||
$drug_package = $drug_package . "|" . $item['packaging_unit']; // 药品包装
|
||||
}
|
||||
|
||||
$amount_total += $item['product_price'] * $item['prescription_product_num'];
|
||||
$amount_total = bcadd(
|
||||
(string) $amount_total,
|
||||
bcmul(
|
||||
(string) ($item['product_price'] ?? $product['product_price'] ?? 0),
|
||||
(string) $item['prescription_product_num'],
|
||||
2
|
||||
),
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
$result = array();
|
||||
@@ -561,11 +956,121 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getPreProductDetailData(string $order_prescription_id): array
|
||||
{
|
||||
$params = array();
|
||||
$params['order_prescription_id'] = $order_prescription_id;
|
||||
$order_prescription_product = OrderPrescriptionProduct::getList($params);
|
||||
if (empty($order_prescription_product)) {
|
||||
$this->line("错误:无处方药品数据");
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($order_prescription_product as $item) {
|
||||
$params = array();
|
||||
$params['product_id'] = $item['product_id'];
|
||||
$product = Product::getWithAmountOne($params);
|
||||
if (empty($product)) {
|
||||
$this->line("错误:无药品数据");
|
||||
return [];
|
||||
}
|
||||
|
||||
$amount_total = bcmul(
|
||||
(string) ($item['product_price'] ?? $product['product_price'] ?? 0),
|
||||
(string) $item['prescription_product_num'],
|
||||
2
|
||||
);
|
||||
|
||||
$result[] = [
|
||||
'drug_name' => (string) ($item['product_name'] ?? ''),
|
||||
'drug_code' => (string) ($product['product_pharmacy_code'] ?? ''),
|
||||
'drug_common_name' => (string) ($product['common_name'] ?? ''),
|
||||
'specification' => (string) ($item['product_spec'] ?? ''),
|
||||
'frequency' => (string) ($item['frequency_use'] ?? ''),
|
||||
'usage' => (string) ($item['single_use'] ?? ''),
|
||||
'dose_unit' => (string) ($item['single_unit'] ?? $product['single_unit'] ?? ''),
|
||||
'dose_each_time' => (string) ($item['single_unit'] ?? $product['single_unit'] ?? ''),
|
||||
'medication_days' => (string) ($item['available_days'] ?? $product['available_days'] ?? ''),
|
||||
'quantity' => (string) $item['prescription_product_num'],
|
||||
'drug_package' => (string) ($item['packaging_unit'] ?? ''),
|
||||
'product_price' => (string) ($item['product_price'] ?? $product['product_price'] ?? 0),
|
||||
'amount_total' => $amount_total,
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getReportPrescriptionDetailData(
|
||||
array|object $order_prescription,
|
||||
array $report_prescription_data
|
||||
): array {
|
||||
$product_details = $this->getPreProductDetailData((string) $order_prescription['order_prescription_id']);
|
||||
if (empty($product_details)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($product_details as $detail) {
|
||||
$row = $report_prescription_data;
|
||||
$row['recipeUnitPrice'] = $detail['product_price'];
|
||||
$row['drugName'] = $detail['drug_name'];
|
||||
$row['drugCode'] = $detail['drug_code'];
|
||||
$row['drugCommonName'] = $detail['drug_common_name'];
|
||||
$row['specification'] = $detail['specification'];
|
||||
$row['frequency'] = $detail['frequency'];
|
||||
$row['usage'] = $detail['usage'];
|
||||
$row['doseUnit'] = $detail['dose_unit'];
|
||||
$row['doseEachTime'] = $detail['dose_each_time'];
|
||||
$row['medicationDays'] = $detail['medication_days'];
|
||||
$row['quantity'] = $detail['quantity'];
|
||||
$row['drugPackage'] = $detail['drug_package'];
|
||||
$row['recipeAllPrice'] = $detail['amount_total'];
|
||||
$row['uploadTime'] = date("Y-m-d H:i:s", time());
|
||||
|
||||
$result[] = $row;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上报数据-网络咨询
|
||||
* @param array|object $order_inquiry
|
||||
* @return array
|
||||
*/
|
||||
private function getStoredPrescriptionCaData(string|int $order_prescription_id, string $role): array
|
||||
{
|
||||
$params = array();
|
||||
$params['order_prescription_id'] = $order_prescription_id;
|
||||
$order_prescription_file = OrderPrescriptionFile::getOne($params);
|
||||
if (empty($order_prescription_file)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$prefix = $role === 'doctor' ? 'doctor' : 'pharmacist';
|
||||
return [
|
||||
'caSupplier' => (string) ($order_prescription_file[$prefix . '_ca_supplier'] ?? ''),
|
||||
'caSignType' => (string) ($order_prescription_file[$prefix . '_ca_sign_type'] ?? ''),
|
||||
'caSignTime' => (string) ($order_prescription_file[$prefix . '_ca_sign_time'] ?? ''),
|
||||
'caSignOriginaltext' => (string) ($order_prescription_file[$prefix . '_ca_sign_originaltext'] ?? ''),
|
||||
'caSignText' => (string) ($order_prescription_file[$prefix . '_ca_sign_text'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
private function buildLegacyCaCertPayload(string|int $order_prescription_id, string $role): array
|
||||
{
|
||||
$ca_data = $this->getStoredPrescriptionCaData($order_prescription_id, $role);
|
||||
if (empty($ca_data['caSignText'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'cert_base64' => $ca_data['caSignText'],
|
||||
];
|
||||
}
|
||||
|
||||
private function getConsultData(array|object $order_inquiry): array
|
||||
{
|
||||
// 获取医生数据
|
||||
@@ -622,6 +1127,7 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
$data['section'] = $hospital_department_custom['department_name'];//科室名称
|
||||
$data['sectionCode'] = $hospital_department_custom['department_code'];//科室编码
|
||||
$data['docName'] = $user_doctor['user_name'];// 姓名(医师、护师、技师)
|
||||
$data['doc_idcard'] = $user_doctor_info['card_num']; // 医生身份证号
|
||||
$data['certificateNum'] = $user_doctor_info['qualification_cert_num']; // 执业资格证号
|
||||
$data['patientName'] = $order_inquiry['patient_name']; // 患者姓名
|
||||
$data['patientAge'] = (int)$order_inquiry['patient_age']; // 患者年龄
|
||||
@@ -778,6 +1284,13 @@ class ReportRegulatoryCommand extends HyperfCommand
|
||||
$data['cityId'] = "510100"; // 城市ID(参考地区字段)
|
||||
$data['isMark'] = 1;//是否留痕 1:代表留痕;0:代表未留痕
|
||||
|
||||
$doctor_ca_data = $this->getStoredPrescriptionCaData($order_prescription['order_prescription_id'], 'doctor');
|
||||
$data['caSupplier'] = $doctor_ca_data['caSupplier'] ?? "";
|
||||
$data['caSignType'] = $doctor_ca_data['caSignType'] ?? "";
|
||||
$data['caSignTime'] = $doctor_ca_data['caSignTime'] ?? "";
|
||||
$data['caSignOriginaltext'] = $doctor_ca_data['caSignOriginaltext'] ?? "";
|
||||
$data['caSignText'] = $doctor_ca_data['caSignText'] ?? "";
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Model\Pharmacy;
|
||||
use App\Model\Product;
|
||||
use App\Model\ProductPlatform;
|
||||
use App\Utils\Log;
|
||||
@@ -21,6 +22,18 @@ use Symfony\Component\Console\Input\InputArgument;
|
||||
#[Command]
|
||||
class getProductCommand extends HyperfCommand
|
||||
{
|
||||
protected array $active_pharmacies = [];
|
||||
|
||||
protected function getPharmacy(string $code)
|
||||
{
|
||||
if (empty($this->active_pharmacies)) {
|
||||
$list = Pharmacy::where('status', 1)->get();
|
||||
foreach ($list as $p) {
|
||||
$this->active_pharmacies[$p->pharmacy_code] = $p;
|
||||
}
|
||||
}
|
||||
return $this->active_pharmacies[$code] ?? null;
|
||||
}
|
||||
public function __construct(protected ContainerInterface $container)
|
||||
{
|
||||
parent::__construct('getProduct:command');
|
||||
@@ -32,48 +45,109 @@ class getProductCommand extends HyperfCommand
|
||||
$this->setDescription('获取处方平台商品数据');
|
||||
}
|
||||
|
||||
protected function logInfo(string $message): void
|
||||
{
|
||||
$this->line(date('Y-m-d H:i:s') . ' [INFO] ' . $message);
|
||||
Log::getInstance('getProductCommand')->info($message);
|
||||
}
|
||||
|
||||
protected function logWarn(string $message): void
|
||||
{
|
||||
$this->warn(date('Y-m-d H:i:s') . ' [WARN] ' . $message);
|
||||
Log::getInstance('getProductCommand')->warning($message);
|
||||
}
|
||||
|
||||
protected function logError(string $message): void
|
||||
{
|
||||
$this->error(date('Y-m-d H:i:s') . ' [ERROR] ' . $message);
|
||||
Log::getInstance('getProductCommand')->error($message);
|
||||
}
|
||||
|
||||
// 主逻辑处理
|
||||
public function handle()
|
||||
{
|
||||
$this->line("商品更新开始");
|
||||
$startTime = microtime(true);
|
||||
$this->logInfo("================== [getProductCommand] 处方平台商品目录同步开始 ==================");
|
||||
|
||||
$stats = [
|
||||
'total_received' => 0,
|
||||
'created' => 0,
|
||||
'updated' => 0,
|
||||
'unchanged' => 0,
|
||||
'skipped_invalid' => 0,
|
||||
'skipped_pharmacy' => 0,
|
||||
'failed' => 0,
|
||||
];
|
||||
|
||||
try {
|
||||
$prescription = new Prescription();
|
||||
// 预加载已启用的药房列表
|
||||
if (empty($this->active_pharmacies)) {
|
||||
$list = Pharmacy::where('status', 1)->get();
|
||||
foreach ($list as $p) {
|
||||
$this->active_pharmacies[$p->pharmacy_code] = $p;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->active_pharmacies)) {
|
||||
$this->logError("未找到任何已启用的药房配置,请先在药房管理中添加并启用药房后再执行同步");
|
||||
return;
|
||||
}
|
||||
|
||||
$pharmacyListStr = implode(', ', array_map(function ($p) {
|
||||
return "{$p->pharmacy_name}({$p->pharmacy_code})";
|
||||
}, array_values($this->active_pharmacies)));
|
||||
$this->logInfo("当前系统已启用药房 (" . count($this->active_pharmacies) . " 个): [{$pharmacyListStr}]");
|
||||
|
||||
$prescription = new Prescription();
|
||||
$page = 1;
|
||||
$page_size = 100;
|
||||
$result = $prescription->getProd(1, 100);
|
||||
if (!empty($result['rows'])) {
|
||||
foreach ($result['rows'] as $item) {
|
||||
// 执行入库
|
||||
$this->handleData($item);
|
||||
}
|
||||
|
||||
$count = ceil($result['count'] / ($page * $page_size));
|
||||
$this->logInfo("正在请求第 1 页商品目录数据 (pageSize={$page_size})...");
|
||||
$result = $prescription->getProd(1, $page_size);
|
||||
|
||||
if ($result['count'] > $page * $page_size) {
|
||||
for ($i = 2; $i < $count; $i++) {
|
||||
try {
|
||||
$result = $prescription->getProd($i, $page_size);
|
||||
if (!isset($result['rows'])) {
|
||||
continue;
|
||||
}
|
||||
if (empty($result) || !isset($result['rows'])) {
|
||||
$this->logWarn("处方平台未返回有效的商品目录数据,任务结束");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($result['rows'] as $item) {
|
||||
// 执行入库
|
||||
$this->handleData($item);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->line("部分商品更新失败:" . $e->getMessage());
|
||||
$totalCount = (int)($result['count'] ?? count($result['rows']));
|
||||
$totalPages = (int)ceil($totalCount / $page_size);
|
||||
$this->logInfo("处方平台商品目录查询成功: 平台总数据量={$totalCount} 条,总页数={$totalPages} 页");
|
||||
|
||||
// 处理第 1 页
|
||||
$this->logInfo(">>> 正在处理第 1/{$totalPages} 页数据 (当前页 " . count($result['rows']) . " 条)...");
|
||||
foreach ($result['rows'] as $item) {
|
||||
$stats['total_received']++;
|
||||
$this->handleData($item, $stats);
|
||||
}
|
||||
|
||||
// 处理后续页
|
||||
if ($totalPages > 1) {
|
||||
for ($i = 2; $i <= $totalPages; $i++) {
|
||||
$this->logInfo(">>> 正在请求并处理第 {$i}/{$totalPages} 页数据...");
|
||||
try {
|
||||
$pageResult = $prescription->getProd($i, $page_size);
|
||||
if (!isset($pageResult['rows']) || empty($pageResult['rows'])) {
|
||||
$this->logWarn("第 {$i} 页返回空数据,跳过");
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($pageResult['rows'] as $item) {
|
||||
$stats['total_received']++;
|
||||
$this->handleData($item, $stats);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->logError("第 {$i} 页商品同步失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->line("商品更新失败:" . $e->getMessage());
|
||||
} catch (\Throwable $e) {
|
||||
$this->logError("商品同步全局异常: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$this->line("商品更新成功");
|
||||
$duration = round(microtime(true) - $startTime, 2);
|
||||
$this->logInfo("================== [getProductCommand] 处方平台商品目录同步完成 ==================");
|
||||
$this->logInfo("总耗时: {$duration}s | 平台接收: {$stats['total_received']} 条 | 新增入库: {$stats['created']} 条 | 变更更新: {$stats['updated']} 条 | 资料无变动: {$stats['unchanged']} 条 | 非本院药房忽略: {$stats['skipped_pharmacy']} 条 | 字段缺失跳过: {$stats['skipped_invalid']} 条 | 处理失败: {$stats['failed']} 条");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,41 +164,25 @@ class getProductCommand extends HyperfCommand
|
||||
/**
|
||||
* 入库
|
||||
* @param array $item
|
||||
* @param array $stats
|
||||
* @return bool
|
||||
*/
|
||||
protected function handleData(array $item): bool
|
||||
protected function handleData(array $item, array &$stats): bool
|
||||
{
|
||||
if (empty($item['drugCode'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($item['drugPrice'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($item['thirdCode'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($item['thirdDrugCode'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($item['approvalNumber'])) {
|
||||
if (empty($item['drugCode']) || empty($item['drugPrice']) || empty($item['thirdCode']) || empty($item['thirdDrugCode']) || empty($item['approvalNumber'])) {
|
||||
$drugName = $item['tradeName'] ?? '未知名';
|
||||
$this->logWarn("【跳过-字段缺失】药品 [{$drugName}] 缺少必要字段(drugCode/drugPrice/thirdCode/thirdDrugCode/approvalNumber)");
|
||||
$stats['skipped_invalid']++;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Db::beginTransaction();
|
||||
|
||||
$pharmacy_code = \Hyperf\Config\config("prescription_platform.pharmacy_code");
|
||||
if (empty($pharmacy_code)) {
|
||||
$pharmacy_code = "JG-10009";
|
||||
}
|
||||
|
||||
// 非药店编码
|
||||
if ($pharmacy_code != $item['thirdCode']) {
|
||||
$pharmacy = $this->getPharmacy($item['thirdCode']);
|
||||
if (empty($pharmacy)) {
|
||||
Db::rollBack();
|
||||
$stats['skipped_pharmacy']++;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -133,6 +191,7 @@ class getProductCommand extends HyperfCommand
|
||||
$params['product_platform_code'] = $item['drugCode'];
|
||||
$params['product_pharmacy_code'] = $item['thirdDrugCode'];
|
||||
$params['license_number'] = $item['approvalNumber'];
|
||||
$params['pharmacy_code'] = $item['thirdCode'];
|
||||
$product_platform = ProductPlatform::getOne($params);
|
||||
if (!empty($product_platform)) {
|
||||
// 已存在,更新
|
||||
@@ -189,7 +248,7 @@ class getProductCommand extends HyperfCommand
|
||||
if (isset($item['packingUnit'])) {
|
||||
if ($product_platform['retail_unit'] != $item['packingUnit']) {
|
||||
$product_platform_data['retail_unit'] = $item['packingUnit'];
|
||||
$product_data['packaging_unit'] = $item['packingUnit']; // 平台返回零售包装单位为:盒,此结果适用于商品表的基本包装单位
|
||||
$product_data['packaging_unit'] = $item['packingUnit'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,6 +273,10 @@ class getProductCommand extends HyperfCommand
|
||||
}
|
||||
}
|
||||
|
||||
$product_platform_data['pharmacy_code'] = $item['thirdCode'];
|
||||
$product_data['pharmacy_code'] = $item['thirdCode'];
|
||||
$product_data['pharmacy_id'] = (string)$pharmacy->pharmacy_id;
|
||||
|
||||
if (!empty($product_platform_data)) {
|
||||
// 更新商品表-处方平台
|
||||
$params = array();
|
||||
@@ -232,19 +295,20 @@ class getProductCommand extends HyperfCommand
|
||||
Product::edit($params, $product_data);
|
||||
}
|
||||
}
|
||||
$this->logInfo("【更新药品】药房 [{$pharmacy->pharmacy_name}({$item['thirdCode']})] 药品 [{$item['tradeName']}] (ID: {$product_platform['product_platform_id']}, 价格: {$item['drugPrice']})");
|
||||
$stats['updated']++;
|
||||
} else {
|
||||
$stats['unchanged']++;
|
||||
}
|
||||
} else {
|
||||
// 不存在,创建
|
||||
$data = array();
|
||||
// 商品名称
|
||||
$data['pharmacy_code'] = $item['thirdCode'];
|
||||
if (isset($item['tradeName'])) {
|
||||
$data['product_name'] = $item['tradeName'];
|
||||
}
|
||||
|
||||
// 商品价格
|
||||
$data['product_price'] = $item['drugPrice'];
|
||||
|
||||
// 药品类型
|
||||
if (isset($item['drugClassCode'])) {
|
||||
if ($item['drugClassCode'] == 1) {
|
||||
$data['product_type'] = 1;
|
||||
@@ -255,41 +319,25 @@ class getProductCommand extends HyperfCommand
|
||||
}
|
||||
}
|
||||
|
||||
// 处方平台商品编码
|
||||
$data['product_platform_code'] = $item['drugCode'];
|
||||
|
||||
// 第三方药店商品编码
|
||||
$data['product_pharmacy_code'] = $item['thirdDrugCode'];
|
||||
|
||||
// 商品规格
|
||||
if (isset($item['specifications'])) {
|
||||
$data['product_spec'] = $item['specifications'];
|
||||
}
|
||||
|
||||
// 批准文号
|
||||
$data['license_number'] = $item['approvalNumber'];
|
||||
|
||||
// 生产厂家
|
||||
if (isset($item['manufacturer'])) {
|
||||
$data['manufacturer'] = $item['manufacturer'];
|
||||
}
|
||||
|
||||
// 单次剂量单位
|
||||
if (isset($item['defaultSingleDosageUnit'])) {
|
||||
$data['single_unit'] = $item['defaultSingleDosageUnit'];
|
||||
}
|
||||
|
||||
// 基本包装单位
|
||||
if (isset($item['basicPackingUnit'])) {
|
||||
$data['packaging_unit'] = $item['basicPackingUnit'];
|
||||
}
|
||||
|
||||
// 基本包装数量
|
||||
if (isset($item['basicPackingCount'])) {
|
||||
$data['packaging_count'] = $item['basicPackingCount'];
|
||||
}
|
||||
|
||||
// 零售包装单位
|
||||
if (isset($item['packingUnit'])) {
|
||||
$data['retail_unit'] = $item['packingUnit'];
|
||||
}
|
||||
@@ -297,14 +345,20 @@ class getProductCommand extends HyperfCommand
|
||||
$product_platform = ProductPlatform::addProductPlatform($data);
|
||||
if (empty($product_platform)) {
|
||||
Db::rollBack();
|
||||
$this->line("商品更新失败:" . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
$this->logError("【新增失败】药房 [{$pharmacy->pharmacy_name}({$item['thirdCode']})] 药品 [{$item['tradeName']}] 写入数据库失败");
|
||||
$stats['failed']++;
|
||||
return false;
|
||||
}
|
||||
$this->logInfo("【新增入库】药房 [{$pharmacy->pharmacy_name}({$item['thirdCode']})] 药品 [{$item['tradeName']}] (平台编码: {$item['drugCode']}, 批准文号: {$item['approvalNumber']}, 价格: {$item['drugPrice']})");
|
||||
$stats['created']++;
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollBack();
|
||||
$this->line("商品更新失败:" . $e->getMessage());
|
||||
$drugName = $item['tradeName'] ?? '未知名';
|
||||
$this->logError("【更新异常】药品 [{$drugName}] (thirdDrugCode: {$item['thirdDrugCode']}) 异常: " . $e->getMessage());
|
||||
$stats['failed']++;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace App\Command;
|
||||
use App\Model\Product;
|
||||
use App\Model\ProductAmountRecord;
|
||||
use App\Model\ProductPlatformAmount;
|
||||
use App\Utils\Log;
|
||||
use Extend\Prescription\Prescription;
|
||||
use Hyperf\Command\Command as HyperfCommand;
|
||||
use Hyperf\Command\Annotation\Command;
|
||||
@@ -30,123 +31,188 @@ class getProductStockCommand extends HyperfCommand
|
||||
$this->setDescription('获取处方平台商品库存数据');
|
||||
}
|
||||
|
||||
protected function logInfo(string $message): void
|
||||
{
|
||||
$this->line(date('Y-m-d H:i:s') . ' [INFO] ' . $message);
|
||||
Log::getInstance('getProductStockCommand')->info($message);
|
||||
}
|
||||
|
||||
protected function logWarn(string $message): void
|
||||
{
|
||||
$this->warn(date('Y-m-d H:i:s') . ' [WARN] ' . $message);
|
||||
Log::getInstance('getProductStockCommand')->warning($message);
|
||||
}
|
||||
|
||||
protected function logError(string $message): void
|
||||
{
|
||||
$this->error(date('Y-m-d H:i:s') . ' [ERROR] ' . $message);
|
||||
Log::getInstance('getProductStockCommand')->error($message);
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->line("商品库存更新开始");
|
||||
$startTime = microtime(true);
|
||||
$this->logInfo("================== [getProductStockCommand] 商品库存更新开始 ==================");
|
||||
|
||||
$stats = [
|
||||
'total' => 0,
|
||||
'success' => 0,
|
||||
'stock_changed' => 0,
|
||||
'stock_unchanged' => 0,
|
||||
'skipped' => 0,
|
||||
'failed' => 0,
|
||||
];
|
||||
|
||||
try {
|
||||
// 获取商品
|
||||
$pageSize = 20;
|
||||
$params = array();
|
||||
$product = Product::getPage($params,['*'],1,10);
|
||||
if (empty($product['data'])){
|
||||
$this->line("商品库存更新成功,无可更新库存商品");
|
||||
$params['product_status'] = 1;
|
||||
$product = Product::getPage($params, ['*'], 1, $pageSize);
|
||||
if (empty($product['data'])) {
|
||||
$this->logInfo("当前无可更新库存的在架商品,任务结束");
|
||||
return;
|
||||
}
|
||||
|
||||
$totalCount = (int)($product['total'] ?? count($product['data']));
|
||||
$lastPage = (int)($product['last_page'] ?? 1);
|
||||
$this->logInfo("在架商品总数: {$totalCount} 件,共 {$lastPage} 页,每页批次: {$pageSize} 件");
|
||||
|
||||
$prescription = new Prescription();
|
||||
|
||||
foreach ($product['data'] as $item){
|
||||
if (!empty($item['product_pharmacy_code'])){
|
||||
$result = $prescription->getProdStock($item['product_pharmacy_code']);
|
||||
$this->handleData($item['product_platform_id'],$item['product_platform_code'],$result[0]);
|
||||
for ($page = 1; $page <= $lastPage; $page++) {
|
||||
if ($page > 1) {
|
||||
$product = Product::getPage($params, ['*'], $page, $pageSize);
|
||||
if (empty($product['data'])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($product['last_page'] > 1){
|
||||
for ($i = 2; $i <= $product['last_page']; $i++) {
|
||||
// 获取商品
|
||||
$params = array();
|
||||
$product = Product::getPage($params,['*'],$i,10);
|
||||
if (empty($product['data'])){
|
||||
$this->line("商品库存更新成功,无可更新库存商品");
|
||||
return;
|
||||
$this->logInfo(">>> 正在处理第 {$page}/{$lastPage} 页商品库存 (当前页 " . count($product['data']) . " 件)...");
|
||||
|
||||
foreach ($product['data'] as $item) {
|
||||
$stats['total']++;
|
||||
|
||||
if (empty($item['product_pharmacy_code'])) {
|
||||
$this->logWarn("【跳过】商品 [{$item['product_name']}] (ID: {$item['product_id']}) 未配置第三方药品编码(product_pharmacy_code)");
|
||||
$stats['skipped']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$prescription = new Prescription();
|
||||
|
||||
foreach ($product['data'] as $item){
|
||||
if (!empty($item['product_pharmacy_code'])){
|
||||
$result = $prescription->getProdStock($item['product_pharmacy_code']);
|
||||
$this->handleData($item['product_platform_id'],$item['product_platform_code'],$result[0]);
|
||||
$pharmacy_code = $item['pharmacy_code'] ?? '';
|
||||
try {
|
||||
$result = $prescription->getProdStock($item['product_pharmacy_code'], $pharmacy_code);
|
||||
if (empty($result) || !isset($result[0])) {
|
||||
$this->logWarn("【库存响应空】商品 [{$item['product_name']}] 药房 [{$pharmacy_code}] 平台未返回库存数据");
|
||||
$stats['failed']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$res = $this->handleData($item, $result[0], $stats);
|
||||
if ($res) {
|
||||
$stats['success']++;
|
||||
} else {
|
||||
$stats['failed']++;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->logError("【查询库存失败】商品 [{$item['product_name']}] (药店编码: {$item['product_pharmacy_code']}, 药房: {$pharmacy_code}) 异常: " . $e->getMessage());
|
||||
$stats['failed']++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->line("商品库存更新失败:" . $e->getMessage());
|
||||
} catch (\Throwable $e) {
|
||||
$this->logError("商品库存更新全局异常:" . $e->getMessage());
|
||||
}
|
||||
|
||||
$this->line("商品库存更新成功");
|
||||
$duration = round(microtime(true) - $startTime, 2);
|
||||
$this->logInfo("================== [getProductStockCommand] 商品库存更新完成 ==================");
|
||||
$this->logInfo("总耗时: {$duration}s | 检查总数: {$stats['total']} | 成功: {$stats['success']} (变动: {$stats['stock_changed']}, 未变: {$stats['stock_unchanged']}) | 跳过: {$stats['skipped']} | 失败: {$stats['failed']}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 入库
|
||||
* @param string $product_platform_id
|
||||
* @param string $product_platform_code
|
||||
* @param Product|array $item
|
||||
* @param array $resultData
|
||||
* @param array $stats
|
||||
* @return bool
|
||||
*/
|
||||
public function handleData(string $product_platform_id,string $product_platform_code,array $resultData): bool
|
||||
public function handleData(Product|array $item, array $resultData, array &$stats = []): bool
|
||||
{
|
||||
if (empty($resultData['quantity'])){
|
||||
$resultData['quantity'] = 0;
|
||||
}
|
||||
$quantity = (int)($resultData['quantity'] ?? 0);
|
||||
$product_platform_id = (string)$item['product_platform_id'];
|
||||
$product_platform_code = (string)$item['product_platform_code'];
|
||||
$product_name = (string)($item['product_name'] ?? '');
|
||||
$pharmacy_code = (string)($item['pharmacy_code'] ?? '');
|
||||
|
||||
try {
|
||||
Db::beginTransaction();
|
||||
|
||||
// 当前库存数量
|
||||
$stock = 0;
|
||||
|
||||
$params = array();
|
||||
$params['product_platform_id'] = $product_platform_id;
|
||||
$params['product_platform_code'] = $product_platform_code;
|
||||
$product_platform_amount = ProductPlatformAmount::getSharedLockOne($params);
|
||||
if (empty($product_platform_amount)){
|
||||
|
||||
$old_stock = 0;
|
||||
if (empty($product_platform_amount)) {
|
||||
// 无库存数据,新增
|
||||
$data = array();
|
||||
$data['product_platform_id'] = $product_platform_id;
|
||||
$data['product_platform_code'] = $product_platform_code;
|
||||
$data['stock'] = $resultData['quantity'];
|
||||
$data['stock'] = $quantity;
|
||||
$product_platform_amount = ProductPlatformAmount::addProductPlatformAmount($data);
|
||||
if (empty($product_platform_amount)){
|
||||
if (empty($product_platform_amount)) {
|
||||
Db::rollBack();
|
||||
$this->line("商品库存更新失败,无法新增库存数据" . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
$this->logError("商品 [{$product_name}] 新增库存数据失败: " . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
$stock = $product_platform_amount['stock'];
|
||||
$this->logInfo("【初始化库存】商品 [{$product_name}] 药房 [{$pharmacy_code}] 初始库存: {$quantity}");
|
||||
$stats['stock_changed']++;
|
||||
} else {
|
||||
$old_stock = (int)$product_platform_amount['stock'];
|
||||
|
||||
// 存在库存数据,修改
|
||||
$data = array();
|
||||
$data['stock'] = $resultData['quantity'];
|
||||
$data['stock'] = $quantity;
|
||||
|
||||
$params = array();
|
||||
$params['amount_id'] = $product_platform_amount['amount_id'];
|
||||
ProductPlatformAmount::edit($params,$data);
|
||||
ProductPlatformAmount::edit($params, $data);
|
||||
|
||||
if ($old_stock !== $quantity) {
|
||||
$diff = $quantity - $old_stock;
|
||||
$diffStr = ($diff > 0 ? "+{$diff}" : "{$diff}");
|
||||
$this->logInfo("【库存变动】商品 [{$product_name}] 药房 [{$pharmacy_code}] 原库存: {$old_stock} -> 最新库存: {$quantity} (变动: {$diffStr})");
|
||||
$stats['stock_changed']++;
|
||||
} else {
|
||||
$this->logInfo("【库存无变化】商品 [{$product_name}] 药房 [{$pharmacy_code}] 当前库存: {$quantity}");
|
||||
$stats['stock_unchanged']++;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取商品数据
|
||||
$params = array();
|
||||
$params['product_platform_id'] = $product_platform_id;
|
||||
$product = Product::getOne($params);
|
||||
if (!empty($product)){
|
||||
// 增加库存记录
|
||||
$data = array();
|
||||
$data['product_id'] = $product['product_id'];
|
||||
$data['change_quantity'] = $resultData['quantity'] - $stock; // 库存变动的数量 变动的库存-原库存
|
||||
$data['quantity'] = $resultData['quantity']; // 变动后库存数量
|
||||
$data['change_time'] = date('Y-m-d H:i:s',time());
|
||||
$data['remark'] = "库存同步";
|
||||
$product_amount_record = ProductAmountRecord::addProductAmountRecord($data);
|
||||
if (empty($product_amount_record)){
|
||||
Db::rollBack();
|
||||
$this->line("商品库存更新失败,增加库存记录失败" . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
// 增加库存记录(仅在库存发生变动或首次初始化时记录)
|
||||
if ($old_stock !== $quantity || empty($product_platform_amount)) {
|
||||
$params = array();
|
||||
$params['product_platform_id'] = $product_platform_id;
|
||||
$product = Product::getOne($params);
|
||||
if (!empty($product)) {
|
||||
$data = array();
|
||||
$data['product_id'] = $product['product_id'];
|
||||
$data['change_quantity'] = $quantity - $old_stock;
|
||||
$data['quantity'] = $quantity;
|
||||
$data['change_time'] = date('Y-m-d H:i:s', time());
|
||||
$data['remark'] = "处方平台库存定时同步";
|
||||
$product_amount_record = ProductAmountRecord::addProductAmountRecord($data);
|
||||
if (empty($product_amount_record)) {
|
||||
Db::rollBack();
|
||||
$this->logError("商品 [{$product_name}] 增加库存流水记录失败: " . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollBack();
|
||||
$this->line("商品库存更新失败:" . $e->getMessage());
|
||||
$this->logError("商品 [{$product_name}] 更新库存事务异常: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Request\DoctorPharmacyRequest;
|
||||
use App\Services\DoctorPharmacyService;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class DoctorPharmacyController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* 获取医生绑定的药房列表
|
||||
*/
|
||||
public function getDoctorPharmacyList(): ResponseInterface
|
||||
{
|
||||
$service = new DoctorPharmacyService();
|
||||
$data = $service->getDoctorPharmacyList();
|
||||
return $this->response->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置医生默认药房
|
||||
*/
|
||||
public function setDoctorDefaultPharmacy(): ResponseInterface
|
||||
{
|
||||
$request = $this->container->get(DoctorPharmacyRequest::class);
|
||||
$request->scene('setDefault')->validateResolved();
|
||||
|
||||
$service = new DoctorPharmacyService();
|
||||
$data = $service->setDoctorDefaultPharmacy();
|
||||
return $this->response->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台公开正常运营的药房列表
|
||||
*/
|
||||
public function getPublicPharmacyList(): ResponseInterface
|
||||
{
|
||||
$service = new DoctorPharmacyService();
|
||||
$data = $service->getPublicPharmacyList();
|
||||
return $this->response->json($data);
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,12 @@ class TestController extends AbstractController
|
||||
// 疾病名称
|
||||
protected string $icd_name;
|
||||
|
||||
public function testError()
|
||||
{
|
||||
// 模拟触发 SQLSTATE[42S22]: Column not found 数据库报错
|
||||
Db::select("SELECT nonexistent_column_test FROM gdxz_user_ca_cert LIMIT 1");
|
||||
}
|
||||
|
||||
public function test(){
|
||||
// $this->test_8();
|
||||
// $this->test_3();
|
||||
|
||||
@@ -111,6 +111,22 @@ class UserDoctorController extends AbstractController
|
||||
return $this->response->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取处方列表(抄方)
|
||||
* @return ResponseInterface
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function getTransferPrescriptionList(): ResponseInterface
|
||||
{
|
||||
$request = $this->container->get(UserDoctorRequest::class);
|
||||
$request->scene('getPrescriptionList')->validateResolved();
|
||||
|
||||
$UserDoctorService = new UserDoctorService();
|
||||
$data = $UserDoctorService->getTransferPrescriptionList();
|
||||
return $this->response->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取医生个人中心数据
|
||||
* @return ResponseInterface
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
use Hyperf\Database\Model\Collection;
|
||||
use Hyperf\Database\Model\Relations\HasOne;
|
||||
use Hyperf\Snowflake\Concern\Snowflake;
|
||||
|
||||
/**
|
||||
* @property int|string $doctor_pharmacy_id 主键ID
|
||||
* @property int|string $doctor_id 医生ID
|
||||
* @property int|string $pharmacy_id 药房ID
|
||||
* @property int $is_default 是否默认药房(0:否 1:是)
|
||||
* @property int $status 绑定状态(0:禁用 1:正常)
|
||||
* @property \Carbon\Carbon $created_at 创建时间
|
||||
* @property \Carbon\Carbon $updated_at 修改时间
|
||||
* @property-read Pharmacy|null $Pharmacy
|
||||
* @property-read UserDoctor|null $UserDoctor
|
||||
*/
|
||||
class DoctorPharmacy extends Model
|
||||
{
|
||||
use Snowflake;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'doctor_pharmacy';
|
||||
|
||||
/**
|
||||
* The primary key for the model.
|
||||
*/
|
||||
protected string $primaryKey = 'doctor_pharmacy_id';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [
|
||||
'doctor_pharmacy_id',
|
||||
'doctor_id',
|
||||
'pharmacy_id',
|
||||
'is_default',
|
||||
'status',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = [
|
||||
'doctor_pharmacy_id' => 'string',
|
||||
'doctor_id' => 'string',
|
||||
'pharmacy_id' => 'string',
|
||||
'is_default' => 'integer',
|
||||
'status' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* 关联药房信息表
|
||||
*/
|
||||
public function Pharmacy(): HasOne
|
||||
{
|
||||
return $this->hasOne(Pharmacy::class, 'pharmacy_id', 'pharmacy_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联医生表
|
||||
*/
|
||||
public function UserDoctor(): HasOne
|
||||
{
|
||||
return $this->hasOne(UserDoctor::class, 'doctor_id', 'doctor_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取医生当前绑定的有效默认药房(包含药房有效性及 pharmacy_code 非空校验)
|
||||
*/
|
||||
public static function getDoctorDefaultPharmacy(int|string $doctorId): ?Pharmacy
|
||||
{
|
||||
$bind = self::where([
|
||||
'doctor_id' => (string) $doctorId,
|
||||
'is_default' => 1,
|
||||
'status' => 1,
|
||||
])->first();
|
||||
|
||||
if (empty($bind)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$pharmacy = Pharmacy::getValidPharmacy($bind->pharmacy_id);
|
||||
if (empty($pharmacy) || empty($pharmacy->pharmacy_code)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $pharmacy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单条数据
|
||||
*/
|
||||
public static function getOne(array $params, array $fields = ['*']): ?self
|
||||
{
|
||||
return self::where($params)->first($fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列表数据
|
||||
*/
|
||||
public static function getList(array $params = [], array $fields = ['*']): Collection
|
||||
{
|
||||
return self::where($params)->get($fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增绑定
|
||||
*/
|
||||
public static function addDoctorPharmacy(array $data): self
|
||||
{
|
||||
return self::create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改
|
||||
*/
|
||||
public static function edit(array $params, array $data): int
|
||||
{
|
||||
return self::where($params)->update($data);
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,12 @@ use Hyperf\DbConnection\Db;
|
||||
use Hyperf\Snowflake\Concern\Snowflake;
|
||||
|
||||
/**
|
||||
* @property int $order_inquiry_id 主键id
|
||||
* @property int $order_inquiry_id 主键id
|
||||
* @property int $order_id 订单id
|
||||
* @property int $user_id 用户id-患者
|
||||
* @property int $patient_id 患者id
|
||||
* @property int $doctor_id 医生id(未分配时为null)
|
||||
* @property int $transfer_doctor_id 接受抄方的医生id
|
||||
* @property int $family_id 家庭成员id(就诊用户)
|
||||
* @property int $inquiry_type 订单类型(1:专家问诊 2:快速问诊 3:公益问诊 4:问诊购药 5:检测)
|
||||
* @property int $inquiry_mode 接诊方式(1:图文 2:视频 3:语音 4:电话 5:会员 6:疑难会诊 7:附赠 8:健康包 9:随访包)
|
||||
@@ -65,7 +66,7 @@ class OrderInquiry extends Model
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = ['order_inquiry_id', 'order_id', 'user_id', 'patient_id', 'doctor_id', 'family_id', 'inquiry_type', 'inquiry_mode', 'inquiry_status', 'is_delete', 'inquiry_refund_status', 'inquiry_pay_channel', 'inquiry_pay_status', 'inquiry_no', 'escrow_trade_no', 'amount_total', 'coupon_amount_total', 'payment_amount_total', 'pay_time', 'reception_time', 'complete_time', 'finish_time', 'statistics_status', 'statistics_time', 'is_withdrawal', 'withdrawal_time', 'cancel_time', 'cancel_reason', 'cancel_remarks', 'times_number', 'duration', 'patient_name', 'patient_name_mask', 'patient_sex', 'patient_age', 'created_at', 'updated_at'];
|
||||
protected array $fillable = ['order_inquiry_id', 'order_id', 'user_id', 'patient_id', 'doctor_id', 'transfer_doctor_id', 'family_id', 'inquiry_type', 'inquiry_mode', 'inquiry_status', 'is_delete', 'inquiry_refund_status', 'inquiry_pay_channel', 'inquiry_pay_status', 'inquiry_no', 'escrow_trade_no', 'amount_total', 'coupon_amount_total', 'payment_amount_total', 'pay_time', 'reception_time', 'complete_time', 'finish_time', 'statistics_status', 'statistics_time', 'is_withdrawal', 'withdrawal_time', 'cancel_time', 'cancel_reason', 'cancel_remarks', 'times_number', 'duration', 'patient_name', 'patient_name_mask', 'patient_sex', 'patient_age', 'created_at', 'updated_at'];
|
||||
|
||||
protected string $primaryKey = "order_inquiry_id";
|
||||
|
||||
|
||||
@@ -36,12 +36,16 @@ use Hyperf\Snowflake\Concern\Snowflake;
|
||||
* @property string $patient_name 患者姓名-就诊人
|
||||
* @property int $patient_sex 患者性别-就诊人(1:男 2:女)
|
||||
* @property int $patient_age 患者年龄-就诊人
|
||||
* @property int|string $pharmacy_id 开方药房id
|
||||
* @property string $pharmacy_code 开方药房代码快照
|
||||
* @property string $pharmacy_name 开方药房名称快照
|
||||
* @property string $doctor_advice 医嘱
|
||||
* @property \Carbon\Carbon $created_at 创建时间
|
||||
* @property \Carbon\Carbon $updated_at 修改时间
|
||||
* @property-read \Hyperf\Database\Model\Collection|OrderPrescriptionIcd[] $OrderPrescriptionIcd
|
||||
* @property-read \Hyperf\Database\Model\Collection|OrderPrescriptionProduct[] $OrderPrescriptionProduct
|
||||
* @property-read UserDoctor $UserDoctor
|
||||
* @property-read Pharmacy|null $Pharmacy
|
||||
*/
|
||||
class OrderPrescription extends Model
|
||||
{
|
||||
@@ -55,10 +59,22 @@ class OrderPrescription extends Model
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = ['order_prescription_id', 'order_inquiry_id', 'doctor_id', 'patient_id', 'family_id', 'pharmacist_id', 'prescription_status', 'pharmacist_audit_status', 'pharmacist_verify_time', 'pharmacist_fail_reason', 'platform_audit_status', 'platform_fail_time', 'platform_fail_reason', 'is_auto_phar_verify', 'doctor_created_time', 'expired_time', 'is_delete', 'prescription_code', 'doctor_name', 'patient_name', 'patient_sex', 'patient_age', 'doctor_advice', 'created_at', 'updated_at'];
|
||||
protected array $fillable = ['order_prescription_id', 'order_inquiry_id', 'doctor_id', 'pharmacy_id', 'pharmacy_code', 'pharmacy_name', 'patient_id', 'family_id', 'pharmacist_id', 'prescription_status', 'pharmacist_audit_status', 'pharmacist_verify_time', 'pharmacist_fail_reason', 'platform_audit_status', 'platform_fail_time', 'platform_fail_reason', 'is_auto_phar_verify', 'doctor_created_time', 'expired_time', 'is_delete', 'prescription_code', 'doctor_name', 'patient_name', 'patient_sex', 'patient_age', 'doctor_advice', 'created_at', 'updated_at'];
|
||||
|
||||
protected array $casts = [
|
||||
'pharmacy_id' => 'string',
|
||||
];
|
||||
|
||||
protected string $primaryKey = "order_prescription_id";
|
||||
|
||||
/**
|
||||
* 关联药房表
|
||||
*/
|
||||
public function Pharmacy(): HasOne
|
||||
{
|
||||
return $this->hasOne(Pharmacy::class, 'pharmacy_id', 'pharmacy_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联处方疾病表
|
||||
*/
|
||||
@@ -83,6 +99,14 @@ class OrderPrescription extends Model
|
||||
return $this->hasOne(UserDoctor::class, 'doctor_id','doctor_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联问诊订单表
|
||||
*/
|
||||
public function OrderInquiry(): HasOne
|
||||
{
|
||||
return $this->hasOne(OrderInquiry::class, 'order_inquiry_id','order_inquiry_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取信息-单条
|
||||
* @param array $params
|
||||
@@ -163,6 +187,49 @@ class OrderPrescription extends Model
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取处方列表-分页 (抄方)
|
||||
* @param array $params 条件
|
||||
* @param array $fields 字段
|
||||
* @param int|null $page 页码
|
||||
* @param int|null $per_page 每页个数
|
||||
* @return array
|
||||
*/
|
||||
public static function getTransferWithIcdPage(array $params, array $fields = ["*"], int $page = null, ?int $per_page = 10): array
|
||||
{
|
||||
$query = self::with([
|
||||
'OrderPrescriptionIcd',
|
||||
'OrderInquiry'
|
||||
]);
|
||||
|
||||
// 如果提供了 doctor_id,则添加 OrderInquiry 表的 doctor_id 条件
|
||||
if (isset($params['doctor_id'])) {
|
||||
$doctorId = $params['doctor_id'];
|
||||
// 从 $params 中移除 doctor_id,避免在 where($params) 中重复使用
|
||||
unset($params['doctor_id']);
|
||||
$query->whereHas('OrderInquiry', function ($q) use ($doctorId) {
|
||||
$q->where('doctor_id', $doctorId);
|
||||
});
|
||||
}
|
||||
|
||||
// 应用其他条件
|
||||
if (!empty($params)) {
|
||||
$query->where($params);
|
||||
}
|
||||
|
||||
$raw = $query->orderBy('created_at','desc')
|
||||
->paginate($per_page, $fields, "page", $page);
|
||||
|
||||
$data = array();
|
||||
$data['current_page'] = $raw->currentPage();// 当前页码
|
||||
$data['total'] = $raw->total();//数据总数
|
||||
$data['data'] = $raw->items();//数据
|
||||
$data['per_page'] = $raw->perPage();//每页个数
|
||||
$data['last_page'] = $raw->lastPage();//最后一页
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改
|
||||
* @param array $params
|
||||
|
||||
@@ -33,7 +33,7 @@ class OrderPrescriptionFile extends Model
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = ['prescription_file_id', 'order_prescription_id', 'doctor_ca_file_id', 'hospital_ca_file_id', 'prescription_img_oss_path', 'prescription_pdf_oss_path', 'is_converted_pdf', 'created_at', 'updated_at'];
|
||||
protected array $fillable = ['prescription_file_id', 'order_prescription_id', 'doctor_ca_file_id', 'doctor_ca_supplier', 'doctor_ca_sign_type', 'doctor_ca_sign_originaltext', 'doctor_ca_sign_text', 'doctor_ca_sign_time', 'pharmacist_ca_supplier', 'pharmacist_ca_sign_type', 'pharmacist_ca_sign_originaltext', 'pharmacist_ca_sign_text', 'pharmacist_ca_sign_time', 'hospital_ca_file_id', 'prescription_img_oss_path', 'prescription_pdf_oss_path', 'is_converted_pdf', 'created_at', 'updated_at'];
|
||||
|
||||
protected string $primaryKey = "prescription_file_id";
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ class OrderPrescriptionProduct extends Model
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = ['prescription_product_id', 'order_prescription_id', 'product_id', 'use_status', 'prescription_product_num', 'product_name', 'product_spec', 'license_number', 'manufacturer', 'single_unit', 'single_use', 'packaging_unit', 'frequency_use', 'available_days', 'created_at', 'updated_at'];
|
||||
protected array $fillable = ['prescription_product_id', 'order_prescription_id', 'product_id', 'use_status', 'prescription_product_num', 'product_name', 'product_price', 'product_spec', 'license_number', 'manufacturer', 'single_unit', 'single_use', 'packaging_unit', 'frequency_use', 'available_days', 'created_at', 'updated_at'];
|
||||
|
||||
protected string $primaryKey = "prescription_product_id";
|
||||
|
||||
|
||||
@@ -55,12 +55,17 @@ use Hyperf\Snowflake\Concern\Snowflake;
|
||||
* @property string $consignee_name_mask 收货人姓名(掩码)
|
||||
* @property string $consignee_tel 收货人电话
|
||||
* @property string $consignee_tel_mask 收货人电话(掩码)
|
||||
* @property int|string $pharmacy_id 发货药房id
|
||||
* @property string $pharmacy_code 发货药房代码快照
|
||||
* @property string $pharmacy_name 发货药房名称快照
|
||||
* @property int $delivery_type 配送方式(1:自提 2:快递)
|
||||
* @property Carbon $created_at 创建时间
|
||||
* @property Carbon $updated_at 修改时间
|
||||
* @property-read Collection|OrderProductItem[]|null $OrderProductItem
|
||||
* @property-read OrderPrescription|null $OrderPrescription
|
||||
* @property-read PatientFamily|null $PatientFamily
|
||||
* @property-read Collection|OrderPrescriptionIcd[]|null $OrderPrescriptionIcd
|
||||
* @property-read Pharmacy|null $Pharmacy
|
||||
*/
|
||||
class OrderProduct extends Model
|
||||
{
|
||||
@@ -74,10 +79,23 @@ class OrderProduct extends Model
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = ['order_product_id', 'order_inquiry_id', 'order_prescription_id', 'order_id', 'doctor_id', 'patient_id', 'family_id', 'order_product_no', 'escrow_trade_no', 'order_product_status', 'pay_channel', 'pay_status', 'is_delete', 'cancel_reason', 'amount_total', 'coupon_amount_total', 'payment_amount_total', 'logistics_fee', 'logistics_no', 'logistics_company_code', 'sub_logistics_status', 'delivery_time', 'pay_time', 'remarks', 'refund_status', 'cancel_time', 'cancel_remarks', 'report_pre_status', 'report_pre_time', 'report_pre_fail_reason', 'province_id', 'province', 'city_id', 'city', 'county_id', 'county', 'address', 'address_mask', 'consignee_name', 'consignee_name_mask', 'consignee_tel', 'consignee_tel_mask', 'created_at', 'updated_at'];
|
||||
protected array $fillable = ['order_product_id', 'order_inquiry_id', 'order_prescription_id', 'order_id', 'doctor_id', 'pharmacy_id', 'pharmacy_code', 'pharmacy_name', 'delivery_type', 'patient_id', 'family_id', 'order_product_no', 'escrow_trade_no', 'order_product_status', 'pay_channel', 'pay_status', 'is_delete', 'cancel_reason', 'amount_total', 'coupon_amount_total', 'payment_amount_total', 'logistics_fee', 'logistics_no', 'logistics_company_code', 'sub_logistics_status', 'delivery_time', 'pay_time', 'remarks', 'refund_status', 'cancel_time', 'cancel_remarks', 'report_pre_status', 'report_pre_time', 'report_pre_fail_reason', 'province_id', 'province', 'city_id', 'city', 'county_id', 'county', 'address', 'address_mask', 'consignee_name', 'consignee_name_mask', 'consignee_tel', 'consignee_tel_mask', 'created_at', 'updated_at'];
|
||||
|
||||
protected array $casts = [
|
||||
'pharmacy_id' => 'string',
|
||||
'delivery_type' => 'integer',
|
||||
];
|
||||
|
||||
protected string $primaryKey = "order_product_id";
|
||||
|
||||
/**
|
||||
* 关联药房表
|
||||
*/
|
||||
public function Pharmacy(): HasOne
|
||||
{
|
||||
return $this->hasOne(Pharmacy::class, 'pharmacy_id', 'pharmacy_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联订单商品item表
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
use Hyperf\Database\Model\Collection;
|
||||
use Hyperf\Database\Model\Relations\HasMany;
|
||||
use Hyperf\Snowflake\Concern\Snowflake;
|
||||
|
||||
/**
|
||||
* @property int|string $pharmacy_id 主键ID
|
||||
* @property string $pharmacy_name 药房名称
|
||||
* @property string $pharmacy_code 药房代码
|
||||
* @property string $postage 基础邮费
|
||||
* @property string $free_shipping_threshold 满额包邮门槛
|
||||
* @property int $is_pickup 是否支持自提(0:否 1:是)
|
||||
* @property string $telephone 联系电话
|
||||
* @property int $province_id 省份ID
|
||||
* @property string $province 省份名称
|
||||
* @property int $city_id 城市ID
|
||||
* @property string $city 城市名称
|
||||
* @property int $county_id 区县ID
|
||||
* @property string $county 区县名称
|
||||
* @property string $address 详细地址
|
||||
* @property int $status 状态(0:禁用 1:正常 2:删除)
|
||||
* @property \Carbon\Carbon $created_at 创建时间
|
||||
* @property \Carbon\Carbon $updated_at 修改时间
|
||||
* @property-read Collection|DoctorPharmacy[] $DoctorPharmacy
|
||||
*/
|
||||
class Pharmacy extends Model
|
||||
{
|
||||
use Snowflake;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'pharmacy';
|
||||
|
||||
/**
|
||||
* The primary key for the model.
|
||||
*/
|
||||
protected string $primaryKey = 'pharmacy_id';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [
|
||||
'pharmacy_id',
|
||||
'pharmacy_name',
|
||||
'pharmacy_code',
|
||||
'postage',
|
||||
'free_shipping_threshold',
|
||||
'is_pickup',
|
||||
'telephone',
|
||||
'province_id',
|
||||
'province',
|
||||
'city_id',
|
||||
'city',
|
||||
'county_id',
|
||||
'county',
|
||||
'address',
|
||||
'status',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = [
|
||||
'pharmacy_id' => 'string',
|
||||
'postage' => 'string',
|
||||
'free_shipping_threshold' => 'string',
|
||||
'is_pickup' => 'integer',
|
||||
'province_id' => 'integer',
|
||||
'city_id' => 'integer',
|
||||
'county_id' => 'integer',
|
||||
'status' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* 关联医生药房绑定表
|
||||
*/
|
||||
public function DoctorPharmacy(): HasMany
|
||||
{
|
||||
return $this->hasMany(DoctorPharmacy::class, 'pharmacy_id', 'pharmacy_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单条有效药房数据
|
||||
*/
|
||||
public static function getValidPharmacy(int|string $pharmacyId): ?self
|
||||
{
|
||||
return self::where([
|
||||
'pharmacy_id' => (string) $pharmacyId,
|
||||
'status' => 1,
|
||||
])->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单条药房信息
|
||||
*/
|
||||
public static function getOne(array $params, array $fields = ['*']): ?self
|
||||
{
|
||||
return self::where($params)->first($fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取药房列表
|
||||
*/
|
||||
public static function getList(array $params = [], array $fields = ['*']): Collection
|
||||
{
|
||||
return self::where($params)->get($fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询药房
|
||||
*/
|
||||
public static function getPage(array $params, array $fields = ['*'], ?int $page = null, ?int $per_page = 10): array
|
||||
{
|
||||
$raw = self::where($params)->paginate($per_page, $fields, 'page', $page);
|
||||
return [
|
||||
'current_page' => $raw->currentPage(),
|
||||
'total' => $raw->total(),
|
||||
'data' => $raw->items(),
|
||||
'per_page' => $raw->perPage(),
|
||||
'last_page' => $raw->lastPage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ use Hyperf\Snowflake\Concern\Snowflake;
|
||||
* @property int $product_type 药品类型(0:未知 1:中成药 2:西药)
|
||||
* @property string $product_platform_code 处方平台商品编码
|
||||
* @property string $product_pharmacy_code 第三方药店商品编码
|
||||
* @property int|string $pharmacy_id 所属药房ID
|
||||
* @property string $pharmacy_code 所属药房编码
|
||||
* @property string $product_cover_img 商品封面图
|
||||
* @property string $product_spec 商品规格
|
||||
* @property string $license_number 批准文号
|
||||
@@ -49,7 +51,11 @@ class Product extends Model
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = ['product_id', 'product_platform_id', 'product_status', 'is_delete', 'prescription_num', 'product_name', 'common_name', 'product_price', 'mnemonic_code', 'product_type', 'product_platform_code', 'product_pharmacy_code', 'product_cover_img', 'product_spec', 'license_number', 'manufacturer', 'single_unit', 'single_use', 'packaging_unit', 'frequency_use', 'available_days', 'product_remarks', 'created_at', 'updated_at'];
|
||||
protected array $fillable = ['product_id', 'product_platform_id', 'pharmacy_id', 'pharmacy_code', 'product_status', 'is_delete', 'prescription_num', 'product_name', 'common_name', 'product_price', 'mnemonic_code', 'product_type', 'product_platform_code', 'product_pharmacy_code', 'product_cover_img', 'product_spec', 'license_number', 'manufacturer', 'single_unit', 'single_use', 'packaging_unit', 'frequency_use', 'available_days', 'product_remarks', 'created_at', 'updated_at'];
|
||||
|
||||
protected array $casts = [
|
||||
'pharmacy_id' => 'string',
|
||||
];
|
||||
|
||||
protected string $primaryKey = "product_id";
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ use Hyperf\Snowflake\Concern\Snowflake;
|
||||
* @property int $product_type 药品类型(0:未知 1:中成药 2:西药)
|
||||
* @property string $product_platform_code 处方平台商品编码
|
||||
* @property string $product_pharmacy_code 第三方药店商品编码
|
||||
* @property string $pharmacy_code 所属药房编码
|
||||
* @property string $product_spec 商品规格
|
||||
* @property string $license_number 批准文号
|
||||
* @property string $manufacturer 生产厂家
|
||||
@@ -38,7 +39,7 @@ class ProductPlatform extends Model
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = ['product_platform_id', 'product_name', 'product_price', 'product_type', 'product_platform_code', 'product_pharmacy_code', 'product_spec', 'license_number', 'manufacturer', 'single_unit', 'packaging_unit', 'packaging_count', 'retail_unit', 'created_at', 'updated_at'];
|
||||
protected array $fillable = ['product_platform_id', 'pharmacy_code', 'product_name', 'product_price', 'product_type', 'product_platform_code', 'product_pharmacy_code', 'product_spec', 'license_number', 'manufacturer', 'single_unit', 'packaging_unit', 'packaging_count', 'retail_unit', 'created_at', 'updated_at'];
|
||||
|
||||
protected string $primaryKey = "product_platform_id";
|
||||
|
||||
|
||||
@@ -76,9 +76,17 @@ class ReportRegulatory extends Model
|
||||
* @param array $data
|
||||
* @return ReportRegulatory|\Hyperf\Database\Model\Model
|
||||
*/
|
||||
public static function addReportRegulatory(array $data): ReportRegulatory|\Hyperf\Database\Model\Model
|
||||
// public static function addReportRegulatory(array $data): ReportRegulatory|\Hyperf\Database\Model\Model
|
||||
// {
|
||||
// return self::create($data);
|
||||
// }
|
||||
/**
|
||||
* 暂时不上报监管
|
||||
*/
|
||||
public static function addReportRegulatory(array $data): bool
|
||||
{
|
||||
return self::create($data);
|
||||
// 直接返回成功,不执行创建操作
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,6 +50,7 @@ use Hyperf\Utils\Arr;
|
||||
* @property int $is_enterprise_deep_cooperation 是否企业深度合作医生(0:否 1:是)
|
||||
* @property int $is_sys_diagno_cooperation 是否先思达合作医生(0:否 1:是)
|
||||
* @property int $is_welfare_cooperation 是否公益问诊合作医生(可把公益问诊设为0元)
|
||||
* @property int $is_transfer_prescription 是否接受抄方(0:否 1:是)
|
||||
* @property string $qr_code 分享二维码
|
||||
* @property string $be_good_at 擅长
|
||||
* @property string $brief_introduction 医生简介
|
||||
@@ -73,7 +74,7 @@ class UserDoctor extends Model
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = ['doctor_id', 'user_id', 'user_name', 'open_id', 'union_id', 'wx_session_key', 'status', 'idcard_status', 'iden_auth_status', 'iden_auth_time', 'iden_auth_fail_reason', 'multi_point_status', 'multi_point_time', 'multi_point_fail_reason', 'introduction_status', 'introduction_time', 'is_bind_bank', 'is_recommend', 'avatar', 'doctor_title', 'department_custom_id', 'department_custom_name', 'department_custom_mobile', 'hospital_id', 'served_patients_num', 'praise_rate', 'avg_response_time', 'number_of_fans', 'is_img_expert_reception', 'is_img_welfare_reception', 'is_img_quick_reception', 'is_platform_deep_cooperation', 'is_enterprise_deep_cooperation', 'is_sys_diagno_cooperation', 'is_welfare_cooperation', 'qr_code', 'be_good_at', 'brief_introduction', 'created_at', 'updated_at'];
|
||||
protected array $fillable = ['doctor_id', 'user_id', 'user_name', 'open_id', 'union_id', 'wx_session_key', 'status', 'idcard_status', 'iden_auth_status', 'iden_auth_time', 'iden_auth_fail_reason', 'multi_point_status', 'multi_point_time', 'multi_point_fail_reason', 'introduction_status', 'introduction_time', 'is_bind_bank', 'is_recommend', 'avatar', 'doctor_title', 'department_custom_id', 'department_custom_name', 'department_custom_mobile', 'hospital_id', 'served_patients_num', 'praise_rate', 'avg_response_time', 'number_of_fans', 'is_img_expert_reception', 'is_img_welfare_reception', 'is_img_quick_reception', 'is_platform_deep_cooperation', 'is_enterprise_deep_cooperation', 'is_sys_diagno_cooperation', 'is_welfare_cooperation', 'is_transfer_prescription', 'qr_code', 'be_good_at', 'brief_introduction', 'created_at', 'updated_at'];
|
||||
|
||||
protected string $primaryKey = "doctor_id";
|
||||
|
||||
@@ -120,6 +121,15 @@ class UserDoctor extends Model
|
||||
return $this->hasMany(OrderInquiry::class, "doctor_id", "doctor_id");
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联医生药房绑定表
|
||||
* @return HasMany
|
||||
*/
|
||||
public function DoctorPharmacy(): HasMany
|
||||
{
|
||||
return $this->hasMany(DoctorPharmacy::class, "doctor_id", "doctor_id");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取医生信息-单条
|
||||
* @param array $params
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Request;
|
||||
|
||||
use Hyperf\Validation\Request\FormRequest;
|
||||
|
||||
class DoctorPharmacyRequest extends FormRequest
|
||||
{
|
||||
protected array $scenes = [
|
||||
'setDefault' => ['pharmacy_id'],
|
||||
'bindPharmacy' => ['pharmacy_id'],
|
||||
];
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'pharmacy_id' => 'required|numeric',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'pharmacy_id.required' => '药房ID不能为空',
|
||||
'pharmacy_id.numeric' => '药房ID必须为数字',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ class PatientOrderRequest extends FormRequest
|
||||
"address_id",
|
||||
"product_ids",
|
||||
"client_type",
|
||||
"delivery_type",
|
||||
],
|
||||
'getPatientPrescriptionOrderList' => [ // 获取处方订单列表
|
||||
],
|
||||
@@ -84,7 +85,8 @@ class PatientOrderRequest extends FormRequest
|
||||
'order_no' => 'required',
|
||||
|
||||
'order_prescription_id' => 'required',
|
||||
'address_id' => 'required',
|
||||
'address_id' => 'nullable',
|
||||
'delivery_type' => ['nullable', 'integer', Rule::in([1, 2])],
|
||||
'product_ids' => 'required|array|min:1',
|
||||
'detection_status' => 'required|integer|min:0|max:5',
|
||||
'order_service_status' => 'required|integer|min:0|max:4',
|
||||
|
||||
@@ -56,6 +56,7 @@ class UserDoctorRequest extends FormRequest
|
||||
'prescription_icd',// 诊断疾病[]
|
||||
// 'doctor_advice', // 医嘱
|
||||
'prescription_product',// 处方药品[]
|
||||
'pharmacy_id', // 药房id(可选,未传则取默认药房)
|
||||
],
|
||||
'getDoctorMessageList' => [ // 获取医生问诊消息列表
|
||||
'message_inquiry_type',// 消息订单类型(1:专家问诊 2:快速问诊 3:公益问诊 4:问诊购药 5:结束)
|
||||
@@ -94,6 +95,7 @@ class UserDoctorRequest extends FormRequest
|
||||
'prescription_icd' => 'required|array|min:1',
|
||||
'doctor_advice' => 'required',
|
||||
'prescription_product' => 'required|array|min:1|max:5',
|
||||
'pharmacy_id' => 'nullable|numeric',
|
||||
'message_inquiry_type' => 'required|integer|min:1|max:5',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ use App\Model\HospitalDepartmentCustom;
|
||||
use App\Model\HotSearchKeyword;
|
||||
use App\Model\OperationManual;
|
||||
use App\Model\Product;
|
||||
use App\Model\DoctorPharmacy;
|
||||
use App\Model\Pharmacy;
|
||||
use Hyperf\Redis\Redis;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
@@ -242,6 +244,23 @@ class BasicDataService extends BaseService
|
||||
$params = array();
|
||||
$params['product_status'] = 1;
|
||||
$params['is_delete'] = 0;
|
||||
|
||||
// 若是医生端,则只展示医生选定/默认药房下的药品
|
||||
if (isset($user_info['user_type']) && $user_info['user_type'] == 2) {
|
||||
$doctor_id = $user_info['client_user_id'] ?? 0;
|
||||
$pharmacy_id = $this->request->input('pharmacy_id');
|
||||
if (isset($pharmacy_id) && $pharmacy_id !== '') {
|
||||
$pharmacy = Pharmacy::getValidPharmacy($pharmacy_id);
|
||||
} else {
|
||||
$pharmacy = DoctorPharmacy::getDoctorDefaultPharmacy($doctor_id);
|
||||
}
|
||||
|
||||
if (empty($pharmacy) || empty($pharmacy->pharmacy_code)) {
|
||||
return success([]);
|
||||
}
|
||||
$params['pharmacy_code'] = $pharmacy->pharmacy_code;
|
||||
}
|
||||
|
||||
$product = Product::getSearchKeywordList($params, $product_keyword,$fields);
|
||||
if (empty($product)) {
|
||||
return success();
|
||||
@@ -449,6 +468,29 @@ class BasicDataService extends BaseService
|
||||
$params = array();
|
||||
$params['product_status'] = 1;
|
||||
$params['is_delete'] = 0;
|
||||
|
||||
// 若是医生端,则只展示医生选定/默认药房下的药品
|
||||
if (isset($user_info['user_type']) && $user_info['user_type'] == 2) {
|
||||
$doctor_id = $user_info['client_user_id'] ?? 0;
|
||||
$pharmacy_id = $this->request->input('pharmacy_id');
|
||||
if (isset($pharmacy_id) && $pharmacy_id !== '') {
|
||||
$pharmacy = Pharmacy::getValidPharmacy($pharmacy_id);
|
||||
} else {
|
||||
$pharmacy = DoctorPharmacy::getDoctorDefaultPharmacy($doctor_id);
|
||||
}
|
||||
|
||||
if (empty($pharmacy) || empty($pharmacy->pharmacy_code)) {
|
||||
return success([
|
||||
'total' => 0,
|
||||
'per_page' => (int)$per_page,
|
||||
'current_page' => (int)$page,
|
||||
'last_page' => 0,
|
||||
'data' => [],
|
||||
]);
|
||||
}
|
||||
$params['pharmacy_code'] = $pharmacy->pharmacy_code;
|
||||
}
|
||||
|
||||
$product = Product::getWithAmountPage($params, $keyword,$fields, $page, $per_page);
|
||||
if (empty($product['data'])) {
|
||||
return success($product);
|
||||
|
||||
+57
-23
@@ -56,6 +56,8 @@ class CaService extends BaseService
|
||||
// 处方pdf oss地址
|
||||
protected string $prescription_pdf_oss_path;
|
||||
|
||||
protected array $sign_result = [];
|
||||
|
||||
/**
|
||||
* 初始化类,此处会获取基础数据
|
||||
* @param array|object $order_prescription 处方表数据
|
||||
@@ -67,7 +69,12 @@ class CaService extends BaseService
|
||||
if ($type == 1){
|
||||
// 医院
|
||||
$this->sign_image_path = "basic/file/hospital_signature.png";
|
||||
$this->entity_id = "5345345461";
|
||||
$app_env = config('app_env','dev');
|
||||
if ($app_env == 'prod'){
|
||||
$this->entity_id = "1";
|
||||
}else{
|
||||
$this->entity_id = "534534546";
|
||||
}
|
||||
$this->card_num = "91510106MABTJY4K9R";
|
||||
}elseif($type == 2){
|
||||
// 用户
|
||||
@@ -84,7 +91,7 @@ class CaService extends BaseService
|
||||
if ($app_env == 'prod'){
|
||||
$this->entity_id = $user_doctor_info['user_id'];
|
||||
}else{
|
||||
$this->entity_id = "491925054435950592";
|
||||
$this->entity_id = "516904855112556544";
|
||||
}
|
||||
|
||||
$this->card_num = $user_doctor_info['card_num'];
|
||||
@@ -249,13 +256,33 @@ class CaService extends BaseService
|
||||
$data['product'][] = $product;
|
||||
}
|
||||
|
||||
dump($this->entity_id);
|
||||
$cert_sign_result = $CaOnline->getCertSign($this->entity_id, $this->entity_id, $data);
|
||||
|
||||
// 验证云证书签名 验证无需处理,只要不返回错误即可
|
||||
$CaOnline->verifyPkcs7($cert_sign_result['signP7'], $data);
|
||||
|
||||
$this->cert_serial_number = $cert_sign_result['certSerialnumber'];
|
||||
$sign_originaltext = hash_hmac("sha1", json_encode($data, JSON_UNESCAPED_UNICODE), config('ca.online.secret'));
|
||||
$timestamp_sign = "";
|
||||
try {
|
||||
$timestamp_result = $CaOnline->getTimestampSign($sign_originaltext);
|
||||
if (is_array($timestamp_result)) {
|
||||
$timestamp_sign = $timestamp_result['signedData'] ?? "";
|
||||
} elseif (is_string($timestamp_result)) {
|
||||
$timestamp_sign = $timestamp_result;
|
||||
}
|
||||
} catch (\Throwable $throwable) {
|
||||
$timestamp_sign = "";
|
||||
}
|
||||
|
||||
$this->sign_result = [
|
||||
'ca_supplier' => (string) config('regulatory_platform.ca_supplier', '2'),
|
||||
'ca_sign_type' => (string) config('regulatory_platform.ca_sign_type', '1'),
|
||||
'ca_sign_originaltext' => $sign_originaltext,
|
||||
'ca_sign_text' => $cert_sign_result['signP7'] ?? "",
|
||||
'ca_sign_time' => $timestamp_sign,
|
||||
'cert_serial_number' => $cert_sign_result['certSerialnumber'] ?? "",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,6 +290,11 @@ class CaService extends BaseService
|
||||
* @param array|object $order_prescription
|
||||
* @return string
|
||||
*/
|
||||
public function getSignResult(): array
|
||||
{
|
||||
return $this->sign_result;
|
||||
}
|
||||
|
||||
public function createPrescriptionImgPdf(array|object $order_prescription): string
|
||||
{
|
||||
// 打开基础处方图片
|
||||
@@ -496,29 +528,31 @@ class CaService extends BaseService
|
||||
$CaOnline = new CaOnline();
|
||||
|
||||
// 检测是否已添加签章配置
|
||||
$params = array();
|
||||
$params['cert_serial_number'] = $this->cert_serial_number;
|
||||
$user_ca_cert = UserCaCert::getOne($params);
|
||||
if (empty($user_ca_cert)){
|
||||
throw new BusinessException("未申请证书");
|
||||
}
|
||||
// $params = array();
|
||||
// $params['cert_serial_number'] = $this->cert_serial_number;
|
||||
// $user_ca_cert = UserCaCert::getOne($params);
|
||||
// if (empty($user_ca_cert)){
|
||||
// // Mock empty cert to bypass check when CA is skipped
|
||||
// $user_ca_cert = ['is_sign_config' => 1, 'cert_id' => 0];
|
||||
// }
|
||||
|
||||
if ($user_ca_cert['is_sign_config'] == 0){
|
||||
// 添加签章配置
|
||||
$data = array();
|
||||
$data['sign_param'] = json_encode($sign_param);
|
||||
$data['seal_img'] = $sign_image;
|
||||
// if ($user_ca_cert['is_sign_config'] == 0){
|
||||
// // 添加签章配置
|
||||
// $data = array();
|
||||
// $data['sign_param'] = json_encode($sign_param);
|
||||
// $data['seal_img'] = $sign_image;
|
||||
|
||||
$CaOnline->addUserSignConfig($this->entity_id,$this->card_num,$data);
|
||||
// $CaOnline->addUserSignConfig($this->entity_id,$this->card_num,$data);
|
||||
|
||||
$params = array();
|
||||
$params['cert_id'] = $user_ca_cert['cert_id'];
|
||||
// $params = array();
|
||||
// $params['cert_id'] = $user_ca_cert['cert_id'];
|
||||
|
||||
// $data = array();
|
||||
// $data['sign_config'] = json_encode($sign_param);
|
||||
// $data['is_sign_config'] = 1;
|
||||
// UserCaCert::edit($params,$data);
|
||||
// }
|
||||
|
||||
$data = array();
|
||||
$data['sign_config'] = json_encode($sign_param);
|
||||
$data['is_sign_config'] = 1;
|
||||
UserCaCert::edit($params,$data);
|
||||
}
|
||||
|
||||
// 打开处方pdf文件
|
||||
$pdf_file = fopen($this->prescription_pdf_local_path, 'r');
|
||||
@@ -537,4 +571,4 @@ class CaService extends BaseService
|
||||
|
||||
return $sign_pdf_result[0]['fileId'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Constants\HttpEnumCode;
|
||||
use App\Model\DoctorPharmacy;
|
||||
use App\Model\Pharmacy;
|
||||
use Hyperf\DbConnection\Db;
|
||||
|
||||
class DoctorPharmacyService extends BaseService
|
||||
{
|
||||
/**
|
||||
* 获取医生已绑定的药房列表
|
||||
*/
|
||||
public function getDoctorPharmacyList(): array
|
||||
{
|
||||
$user_info = $this->request->getAttribute("userInfo") ?? [];
|
||||
$doctor_id = $user_info['client_user_id'] ?? 0;
|
||||
|
||||
if (empty($doctor_id)) {
|
||||
return fail(HttpEnumCode::CLIENT_HTTP_UNAUTHORIZED, "用户未登录");
|
||||
}
|
||||
|
||||
$list = DoctorPharmacy::with(['Pharmacy'])->where([
|
||||
'doctor_id' => (string) $doctor_id,
|
||||
'status' => 1,
|
||||
])->get();
|
||||
|
||||
$data = [];
|
||||
foreach ($list as $item) {
|
||||
$pharmacy = $item->Pharmacy;
|
||||
if (empty($pharmacy) || $pharmacy->status != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[] = [
|
||||
'doctor_pharmacy_id' => $item->doctor_pharmacy_id,
|
||||
'pharmacy_id' => $pharmacy->pharmacy_id,
|
||||
'pharmacy_name' => $pharmacy->pharmacy_name,
|
||||
'pharmacy_code' => $pharmacy->pharmacy_code,
|
||||
'postage' => $pharmacy->postage,
|
||||
'free_shipping_threshold' => $pharmacy->free_shipping_threshold,
|
||||
'is_pickup' => $pharmacy->is_pickup,
|
||||
'telephone' => $pharmacy->telephone,
|
||||
'province' => $pharmacy->province,
|
||||
'city' => $pharmacy->city,
|
||||
'county' => $pharmacy->county,
|
||||
'address' => $pharmacy->address,
|
||||
'full_address' => $pharmacy->province . $pharmacy->city . $pharmacy->county . $pharmacy->address,
|
||||
'is_default' => $item->is_default,
|
||||
];
|
||||
}
|
||||
|
||||
return success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置医生默认药房
|
||||
*/
|
||||
public function setDoctorDefaultPharmacy(): array
|
||||
{
|
||||
$user_info = $this->request->getAttribute("userInfo") ?? [];
|
||||
$doctor_id = $user_info['client_user_id'] ?? 0;
|
||||
$pharmacy_id = $this->request->input('pharmacy_id');
|
||||
|
||||
if (empty($doctor_id)) {
|
||||
return fail(HttpEnumCode::CLIENT_HTTP_UNAUTHORIZED, "用户未登录");
|
||||
}
|
||||
|
||||
if (!isset($pharmacy_id) || $pharmacy_id === '') {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "缺少药房ID");
|
||||
}
|
||||
|
||||
// 验证医生是否绑定了该药房
|
||||
$bind = DoctorPharmacy::where([
|
||||
'doctor_id' => (string) $doctor_id,
|
||||
'pharmacy_id' => (string) $pharmacy_id,
|
||||
'status' => 1,
|
||||
])->first();
|
||||
|
||||
if (empty($bind)) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "未绑定该药房或绑定已被禁用");
|
||||
}
|
||||
|
||||
// 验证药房是否存在且有效
|
||||
$pharmacy = Pharmacy::getValidPharmacy($pharmacy_id);
|
||||
if (empty($pharmacy)) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "药房不存在或已暂停服务");
|
||||
}
|
||||
|
||||
if (empty($pharmacy->pharmacy_code)) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "该药房未配置药房编码,无法设为默认药房");
|
||||
}
|
||||
|
||||
Db::beginTransaction();
|
||||
try {
|
||||
// 将该医生所有绑定的 is_default 置为 0
|
||||
DoctorPharmacy::where('doctor_id', (string) $doctor_id)->update(['is_default' => 0]);
|
||||
|
||||
// 将目标药房的 is_default 置为 1
|
||||
DoctorPharmacy::where([
|
||||
'doctor_id' => (string) $doctor_id,
|
||||
'pharmacy_id' => (string) $pharmacy_id,
|
||||
])->update(['is_default' => 1]);
|
||||
|
||||
Db::commit();
|
||||
return success(null, "设置默认药房成功");
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollBack();
|
||||
return fail(HttpEnumCode::SERVER_ERROR, "设置默认药房失败:" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台公开正常运营的药房列表
|
||||
*/
|
||||
public function getPublicPharmacyList(): array
|
||||
{
|
||||
$list = Pharmacy::where('status', 1)->get([
|
||||
'pharmacy_id',
|
||||
'pharmacy_name',
|
||||
'pharmacy_code',
|
||||
'postage',
|
||||
'free_shipping_threshold',
|
||||
'is_pickup',
|
||||
'telephone',
|
||||
'province',
|
||||
'city',
|
||||
'county',
|
||||
'address',
|
||||
]);
|
||||
|
||||
return success($list->toArray());
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ use App\Model\PharmacistAuditStatistic;
|
||||
use App\Model\User;
|
||||
use App\Model\UserDoctor as UserDoctorModel;
|
||||
use App\Model\UserPharmacist;
|
||||
use Hyperf\Utils\Parallel;
|
||||
use App\Model\SystemConfig;
|
||||
use Hyperf\Utils\WaitGroup;
|
||||
|
||||
/**
|
||||
@@ -185,6 +185,24 @@ class IndexService extends BaseService
|
||||
// 在线问诊价格
|
||||
$inquiry_price = "";
|
||||
|
||||
//检查是否有问诊购药的配置(抄方需要)
|
||||
if($doctor['idcard_status'] == 1 && $doctor['iden_auth_status'] == 1 && $doctor['is_bind_bank'] == 1){
|
||||
$params = array();
|
||||
$params['doctor_id'] = $doctor['doctor_id'];
|
||||
$params['inquiry_type'] = 4;
|
||||
|
||||
$doctor_inquiry_config_gouyao = DoctorInquiryConfigModel::getOne($params);
|
||||
if(empty($doctor_inquiry_config_gouyao)){
|
||||
$data = array();
|
||||
$data['doctor_id'] = $doctor['doctor_id'];
|
||||
$data['inquiry_type'] = 4;
|
||||
$data['inquiry_mode'] = 1;
|
||||
$data['is_enable'] = 1;
|
||||
$data['inquiry_price'] = 0;
|
||||
$data['last_enable_method'] = 1;
|
||||
DoctorInquiryConfigModel::addInquiryConfig($data);
|
||||
}
|
||||
}
|
||||
// 获取医生问诊配置
|
||||
$params = array();
|
||||
$params['doctor_id'] = $doctor['doctor_id'];
|
||||
@@ -229,10 +247,20 @@ class IndexService extends BaseService
|
||||
$info['introduction_status'] = $doctor['introduction_status'];// 个人简介审核状态(0:未审核 1:审核通过 2:审核中 3:审核失败)
|
||||
$info['is_welfare_cooperation'] = $doctor['is_welfare_cooperation'];// 是否公益问诊合作医生(可把公益问诊设为0元)
|
||||
|
||||
// 获取系统配置
|
||||
$is_annual_review = 0;
|
||||
$params = array();
|
||||
$params['system_config_id'] = 1;
|
||||
$system_config = SystemConfig::getOne($params);
|
||||
if (!empty($system_config)){
|
||||
$is_annual_review = $system_config['is_annual_review'];
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$data['banner'] = $banner ?? [];// banner
|
||||
$data['info'] = $info;// 医生数据
|
||||
$data['doctor_inquiry_config'] = $doctor_inquiry_config;// 医生问诊配置
|
||||
$data['is_annual_review'] = $is_annual_review;// 年审开关
|
||||
|
||||
return success($data);
|
||||
}
|
||||
|
||||
@@ -543,6 +543,10 @@ class MessagePush extends BaseService
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $this->order_inquiry['order_inquiry_id'];
|
||||
$params['doctor_id'] = $this->order_inquiry['doctor_id'];
|
||||
//表示该问诊为抄方
|
||||
if(!empty($this->order_inquiry['transfer_doctor_id'])){
|
||||
$params['doctor_id'] = $this->order_inquiry['transfer_doctor_id'];
|
||||
}
|
||||
$params['pharmacist_audit_status'] = 1;
|
||||
$order_prescription = OrderPrescription::getOne($params);
|
||||
if (empty($order_prescription)) {
|
||||
@@ -1345,6 +1349,10 @@ class MessagePush extends BaseService
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $this->order_inquiry['order_inquiry_id'];
|
||||
$params['doctor_id'] = $this->order_inquiry['doctor_id'];
|
||||
//表示该问诊为抄方
|
||||
if(!empty($this->order_inquiry['transfer_doctor_id'])){
|
||||
$params['doctor_id'] = $this->order_inquiry['transfer_doctor_id'];
|
||||
}
|
||||
$params['pharmacist_audit_status'] = 1;
|
||||
$order_prescription = OrderPrescription::getOne($params);
|
||||
if (empty($order_prescription)) {
|
||||
@@ -1377,11 +1385,17 @@ class MessagePush extends BaseService
|
||||
Log::getInstance("MessagePush")->error("错误:加入站内推送队列失败" . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
$target_page = "user/pages/yishi/chufangsetup/index?status=1";
|
||||
//抄方订单 消息点击跳转到新页面
|
||||
if(!empty($this->order_inquiry["transfer_doctor_id"])){
|
||||
$target_page = "user/pages/yishi/transferchufangsetup/index?status=1";
|
||||
}
|
||||
|
||||
// 订阅
|
||||
$sub_data = array();
|
||||
$sub_data['push_user_id'] = $this->user['user_id'];
|
||||
$sub_data['wx_template_id'] = "kUy70xHlr7ADo4aIHiictM4Te7MSec3E5kHsYvFQu40"; // 处方审核结果通知
|
||||
$sub_data['params']['page'] = "user/pages/yishi/chufangsetup/index?status=1";
|
||||
$sub_data['params']['page'] = $target_page;
|
||||
$sub_data['params']['data'] = [
|
||||
"phrase1" => "审方通过",// 审核结果
|
||||
"thing2" => "审核通过",// 原因
|
||||
@@ -1433,11 +1447,17 @@ class MessagePush extends BaseService
|
||||
return;
|
||||
}
|
||||
|
||||
$target_page = "user/pages/yishi/chufangsetup/index?status=2";
|
||||
//抄方订单 消息点击跳转到新页面
|
||||
if(!empty($this->order_inquiry["transfer_doctor_id"])){
|
||||
$target_page = "user/pages/yishi/transferchufangsetup/index?status=2";
|
||||
}
|
||||
|
||||
// 订阅
|
||||
$sub_data = array();
|
||||
$sub_data['push_user_id'] = $this->user['user_id'];
|
||||
$sub_data['wx_template_id'] = "kUy70xHlr7ADo4aIHiictM4Te7MSec3E5kHsYvFQu40"; // 处方审核结果通知
|
||||
$sub_data['params']['page'] = "user/pages/yishi/chufangsetup/index?status=2";
|
||||
$sub_data['params']['page'] = $target_page;
|
||||
$sub_data['params']['data'] = [
|
||||
"phrase1" => "审方不通过",// 审核结果
|
||||
"thing2" => (string)$order_prescription['pharmacist_fail_reason'],// 原因
|
||||
|
||||
@@ -152,7 +152,6 @@ class OrderPrescriptionService extends BaseService
|
||||
throw new BusinessException("医生开方日期错误");
|
||||
}
|
||||
|
||||
dump($user_id);
|
||||
$CaService = new CaService($order_prescription,$type,$user_id);
|
||||
|
||||
// 获取云证书签名+验证云证书签名
|
||||
@@ -167,9 +166,15 @@ class OrderPrescriptionService extends BaseService
|
||||
|
||||
// 进行处方pdf签章
|
||||
$file_id = $CaService->addSignPdf($type);
|
||||
$sign_result = $CaService->getSignResult();
|
||||
$result = array();
|
||||
$result['prescription_img_oss_path'] = $prescription_img_oss_path ?? "";
|
||||
$result['file_id'] = $file_id;
|
||||
$result['ca_supplier'] = $sign_result['ca_supplier'] ?? "";
|
||||
$result['ca_sign_type'] = $sign_result['ca_sign_type'] ?? "";
|
||||
$result['ca_sign_originaltext'] = $sign_result['ca_sign_originaltext'] ?? "";
|
||||
$result['ca_sign_text'] = $sign_result['ca_sign_text'] ?? "";
|
||||
$result['ca_sign_time'] = $sign_result['ca_sign_time'] ?? "";
|
||||
return $result;
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
@@ -393,13 +398,18 @@ class OrderPrescriptionService extends BaseService
|
||||
$prescription_pdf_oss_path = "/applet/prescription/" . $order_prescription["order_prescription_id"] . ".pdf";
|
||||
|
||||
$arg = array();
|
||||
$arg['terminalCode'] = "ZD-10003";
|
||||
// $arg['terminalCode'] = "ZD-10003";
|
||||
$arg['terminalCode'] = config('prescription_platform.terminal_code');
|
||||
$arg['orderNo'] = $order_product['order_product_no']; // 订单编号
|
||||
$arg['transactNo'] = $order_product['escrow_trade_no']; // 流水单号
|
||||
$arg['payDate'] = $order_product['pay_time']; // 支付时间
|
||||
$arg['money'] = $order_product['payment_amount_total']; // 订单金额
|
||||
$arg['freight'] = $order_product['logistics_fee']; // 运费(单位:元)
|
||||
$arg['takeTypeCode'] = 2; // 取货方式 1 自提 2 快递,目前只支持快递,传固定值 2
|
||||
$arg['takeTypeCode'] = ($order_product['delivery_type'] == 1) ? 1 : 2; // 取货方式 1 自提 2 快递
|
||||
if (empty($order_product['pharmacy_code'])) {
|
||||
throw new BusinessException("订单药房编码缺失,无法上报处方平台");
|
||||
}
|
||||
$arg['pharmacyCode'] = $order_product['pharmacy_code'];
|
||||
$arg['buyerName'] = $order_product['consignee_name'];// 收货人姓名
|
||||
$arg['buyerPhone'] = $order_product['consignee_tel'];// 收货人联系方式
|
||||
$arg['buyerAddress'] = $order_product['address'];// 收货人地址
|
||||
@@ -411,6 +421,7 @@ class OrderPrescriptionService extends BaseService
|
||||
$arg['districtName'] = $order_product['county']; // 收货地址(区 县)名称
|
||||
|
||||
$arg['presList'][0]['prescriptionNo'] = $order_prescription['prescription_code']; // 处方编号
|
||||
$arg['presList'][0]['pharmacyCode'] = $order_product['pharmacy_code'];
|
||||
$arg['presList'][0]['prescriptionSubType'] = 1; // 处方类型 0:无类型 1:普 通处方 2:儿科处 方
|
||||
$arg['presList'][0]['patientName'] = $order_prescription['patient_name']; // 就诊人姓名
|
||||
$arg['presList'][0]['patientPhone'] = $user['mobile']; // 就诊人联系方式
|
||||
@@ -569,4 +580,4 @@ class OrderPrescriptionService extends BaseService
|
||||
|
||||
return $promotion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,23 +75,6 @@ class PatientDoctorService extends BaseService
|
||||
$doctor_params["iden_auth_status"] = 1;// 身份认证状态(0:未认证 1:认证通过 2:审核中 3:认证失败)
|
||||
$doctor_params["is_bind_bank"] = 1;// 是否已绑定结算银行卡(0:否 1:是)
|
||||
|
||||
$user_info = $this->request->getAttribute("userInfo") ?? [];
|
||||
$params = array();
|
||||
$params['user_id'] = $user_info['user_id'];
|
||||
$user = User::getOne($params);
|
||||
if (!empty($user)){
|
||||
if ($user["mobile"] == "15026619021"){
|
||||
$doctor_params["multi_point_status"] = 1;
|
||||
}
|
||||
|
||||
$app_env = config('app_env','dev');
|
||||
if ($app_env == 'dev'){
|
||||
if ($user["mobile"] == "18012345001"){
|
||||
$doctor_params["multi_point_status"] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$fields = [
|
||||
"user_doctor.doctor_id",
|
||||
"user_id",
|
||||
|
||||
@@ -42,6 +42,7 @@ use App\Model\PatientFamily;
|
||||
use App\Model\PatientFamilyHealth;
|
||||
use App\Model\PatientFamilyPersonal;
|
||||
use App\Model\PatientFollow;
|
||||
use App\Model\Pharmacy;
|
||||
use App\Model\Product;
|
||||
use App\Model\ProductPlatformAmount;
|
||||
use App\Model\SystemInquiryConfig;
|
||||
@@ -661,8 +662,41 @@ class PatientOrderService extends BaseService
|
||||
2
|
||||
); // 优惠金额
|
||||
|
||||
|
||||
$result = array();
|
||||
$result['transfer_order'] = false;
|
||||
$result['original_doctor'] = [];
|
||||
|
||||
//查询是否为抄方单
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $order_product['order_inquiry_id'];
|
||||
$OrderInquiry = OrderInquiry::getOne($params);
|
||||
if (!empty($OrderInquiry)) {
|
||||
//为抄方订单
|
||||
if (!empty($OrderInquiry['transfer_doctor_id'])) {
|
||||
// 获取问诊医生数据
|
||||
$fields = [
|
||||
'doctor_id',
|
||||
'user_name',
|
||||
'multi_point_status',
|
||||
'avatar',
|
||||
'doctor_title',
|
||||
'department_custom_name',
|
||||
'be_good_at',
|
||||
'hospital_id',
|
||||
];
|
||||
|
||||
$params = array();
|
||||
$params['doctor_id'] = $OrderInquiry['doctor_id'];
|
||||
$OriginalDoctor = UserDoctor::getWithHospitalOne($params, $fields);
|
||||
if (!empty($OriginalDoctor)) {
|
||||
$OriginalDoctor['avatar'] = addAliyunOssWebsite($OriginalDoctor['avatar']);
|
||||
$OriginalDoctor->doctor_title = DoctorTitleCode::getMessage($OriginalDoctor->doctor_title);
|
||||
$result['original_doctor'] = $OriginalDoctor;
|
||||
$result['transfer_order'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result['user_doctor'] = $user_doctor ?? [];
|
||||
$result['order_product'] = $order_product;
|
||||
$result['order_product_item'] = $order_product_item;
|
||||
@@ -1449,6 +1483,7 @@ class PatientOrderService extends BaseService
|
||||
$address_id = $this->request->input('address_id');
|
||||
$product_ids = $this->request->input('product_ids');
|
||||
$client_type = $this->request->input('client_type');
|
||||
$delivery_type = (int)$this->request->input('delivery_type', 2); // 1: 自提 2: 快递
|
||||
|
||||
// 获取处方数据
|
||||
$params = array();
|
||||
@@ -1460,6 +1495,13 @@ class PatientOrderService extends BaseService
|
||||
return fail();
|
||||
}
|
||||
|
||||
// 获取处方药房信息
|
||||
$pharmacy_id = $order_prescription['pharmacy_id'] ?? 0;
|
||||
$pharmacy = Pharmacy::getValidPharmacy($pharmacy_id);
|
||||
if (empty($pharmacy) || empty($pharmacy['pharmacy_code'])) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "处方所属药房异常,无法下单");
|
||||
}
|
||||
|
||||
// 验证处方状态
|
||||
if ($order_prescription['prescription_status'] == 1) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "处方未审核");
|
||||
@@ -1483,13 +1525,28 @@ class PatientOrderService extends BaseService
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "创建订单失败");
|
||||
}
|
||||
|
||||
// 检测收货地址
|
||||
$params = array();
|
||||
$params['user_id'] = $user_info['user_id'];
|
||||
$params['address_id'] = $address_id;
|
||||
$user_ship_address = UserShipAddress::getOne($params);
|
||||
if (empty($user_ship_address)) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "收货地址错误");
|
||||
// 检测收货方式及收货地址
|
||||
$user_ship_address = null;
|
||||
if ($delivery_type == 1) {
|
||||
// 自提模式,检查药房是否支持自提
|
||||
if ($pharmacy['is_pickup'] != 1) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "该药房不支持到店自提");
|
||||
}
|
||||
if (!empty($address_id)) {
|
||||
$params = array();
|
||||
$params['user_id'] = $user_info['user_id'];
|
||||
$params['address_id'] = $address_id;
|
||||
$user_ship_address = UserShipAddress::getOne($params);
|
||||
}
|
||||
} else {
|
||||
// 快递模式,必须有收货地址
|
||||
$params = array();
|
||||
$params['user_id'] = $user_info['user_id'];
|
||||
$params['address_id'] = $address_id;
|
||||
$user_ship_address = UserShipAddress::getOne($params);
|
||||
if (empty($user_ship_address)) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "收货地址错误");
|
||||
}
|
||||
}
|
||||
|
||||
$not_enough_product_ids = [];
|
||||
@@ -1677,18 +1734,17 @@ class PatientOrderService extends BaseService
|
||||
|
||||
// 处理运费数据
|
||||
$app_env = config('app_env', 'prod');
|
||||
if ($app_env != "dev") {
|
||||
// 获取运费金额
|
||||
$Prescription = new Prescription();
|
||||
|
||||
$result = $Prescription->getLogisticsFee();
|
||||
if ($freight_calculation_amount >= $result['drugCost']) {
|
||||
$logistics_fee = 0;
|
||||
} else {
|
||||
$logistics_fee = $result['freight'];
|
||||
}
|
||||
$logistics_fee = 0.00;
|
||||
if ($delivery_type == 1) {
|
||||
$logistics_fee = 0.00;
|
||||
} else {
|
||||
$logistics_fee = 0;
|
||||
$postage = (float)($pharmacy['postage'] ?? 0.00);
|
||||
$threshold = (float)($pharmacy['free_shipping_threshold'] ?? 0.00);
|
||||
if ($threshold > 0 && (float)$freight_calculation_amount >= $threshold) {
|
||||
$logistics_fee = 0.00;
|
||||
} else {
|
||||
$logistics_fee = $postage;
|
||||
}
|
||||
}
|
||||
|
||||
// 实际支付金额=商品总金额-优惠卷金额+运费金额
|
||||
@@ -1698,7 +1754,7 @@ class PatientOrderService extends BaseService
|
||||
(string)$coupon_amount_total,
|
||||
2
|
||||
),
|
||||
$logistics_fee,
|
||||
(string)$logistics_fee,
|
||||
2
|
||||
);
|
||||
|
||||
@@ -1760,18 +1816,37 @@ class PatientOrderService extends BaseService
|
||||
$data['coupon_amount_total'] = $coupon_amount_total; // 优惠卷总金额
|
||||
$data['payment_amount_total'] = $payment_amount_total; // 实际付款金额
|
||||
$data['logistics_fee'] = $logistics_fee; // 运费金额
|
||||
$data['province_id'] = $user_ship_address['province_id'];
|
||||
$data['province'] = $user_ship_address['province'];
|
||||
$data['city_id'] = $user_ship_address['city_id'];
|
||||
$data['city'] = $user_ship_address['city'];
|
||||
$data['county_id'] = $user_ship_address['county_id'];
|
||||
$data['county'] = $user_ship_address['county'];
|
||||
$data['address'] = $user_ship_address['address'];
|
||||
$data['address_mask'] = $user_ship_address['address_mask'];
|
||||
$data['consignee_name'] = $user_ship_address['consignee_name'];
|
||||
$data['consignee_name_mask'] = $user_ship_address['consignee_name_mask'];
|
||||
$data['consignee_tel'] = $user_ship_address['consignee_tel'];
|
||||
$data['consignee_tel_mask'] = $user_ship_address['consignee_tel_mask'];
|
||||
$data['pharmacy_id'] = $pharmacy['pharmacy_id'];
|
||||
$data['pharmacy_code'] = $pharmacy['pharmacy_code'];
|
||||
$data['pharmacy_name'] = $pharmacy['pharmacy_name'];
|
||||
$data['delivery_type'] = $delivery_type;
|
||||
if (!empty($user_ship_address)) {
|
||||
$data['province_id'] = $user_ship_address['province_id'];
|
||||
$data['province'] = $user_ship_address['province'];
|
||||
$data['city_id'] = $user_ship_address['city_id'];
|
||||
$data['city'] = $user_ship_address['city'];
|
||||
$data['county_id'] = $user_ship_address['county_id'];
|
||||
$data['county'] = $user_ship_address['county'];
|
||||
$data['address'] = $user_ship_address['address'];
|
||||
$data['address_mask'] = $user_ship_address['address_mask'];
|
||||
$data['consignee_name'] = $user_ship_address['consignee_name'];
|
||||
$data['consignee_name_mask'] = $user_ship_address['consignee_name_mask'];
|
||||
$data['consignee_tel'] = $user_ship_address['consignee_tel'];
|
||||
$data['consignee_tel_mask'] = $user_ship_address['consignee_tel_mask'];
|
||||
} else {
|
||||
$data['province_id'] = 0;
|
||||
$data['province'] = "";
|
||||
$data['city_id'] = 0;
|
||||
$data['city'] = "";
|
||||
$data['county_id'] = 0;
|
||||
$data['county'] = "";
|
||||
$data['address'] = $pharmacy['pickup_address'] ?? "到店自提";
|
||||
$data['address_mask'] = $pharmacy['pickup_address'] ?? "到店自提";
|
||||
$data['consignee_name'] = $order_prescription['patient_name'] ?? "";
|
||||
$data['consignee_name_mask'] = $order_prescription['patient_name'] ?? "";
|
||||
$data['consignee_tel'] = "";
|
||||
$data['consignee_tel_mask'] = "";
|
||||
}
|
||||
$order_product = OrderProduct::addOrderProduct($data);
|
||||
if (empty($order_product)) {
|
||||
Db::rollBack();
|
||||
@@ -1986,26 +2061,63 @@ class PatientOrderService extends BaseService
|
||||
*/
|
||||
public function getPatientPrescriptionOrderList(): array
|
||||
{
|
||||
$user_info = $this->request->getAttribute("userInfo") ?? [];
|
||||
$page = $this->request->input('page', 1);
|
||||
$per_page = $this->request->input('per_page', 10);
|
||||
try {
|
||||
$user_info = $this->request->getAttribute("userInfo") ?? [];
|
||||
$page = $this->request->input('page', 1);
|
||||
$per_page = $this->request->input('per_page', 10);
|
||||
|
||||
// 获取处方数据
|
||||
$params = array();
|
||||
$params['patient_id'] = $user_info['client_user_id'];
|
||||
$params['pharmacist_audit_status'] = 1;
|
||||
$params['platform_audit_status'] = 1;
|
||||
$params['is_delete'] = 0;
|
||||
$order_prescription = OrderPrescription::getWithPage($params, ['*'], $page, $per_page);
|
||||
if (!empty($order_prescription['data'])) {
|
||||
foreach ($order_prescription['data'] as &$item) {
|
||||
if (!empty($item['UserDoctor'])) {
|
||||
$item['UserDoctor']['doctor_title'] = DoctorTitleCode::getMessage($item['UserDoctor']['doctor_title']);
|
||||
// 获取处方数据
|
||||
$params = array();
|
||||
$params['patient_id'] = $user_info['client_user_id'];
|
||||
$params['pharmacist_audit_status'] = 1;
|
||||
$params['platform_audit_status'] = 1;
|
||||
$params['is_delete'] = 0;
|
||||
$order_prescription = OrderPrescription::getWithPage($params, ['*'], $page, $per_page);
|
||||
|
||||
if (!empty($order_prescription['data'])) {
|
||||
foreach ($order_prescription['data'] as &$item) {
|
||||
|
||||
$item['original_doctor'] = [];
|
||||
$item['transfer_order'] = false;
|
||||
|
||||
//处理抄方情况
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $item['order_inquiry_id'];
|
||||
$OrderInquiry = OrderInquiry::getOne($params);
|
||||
if (!empty($OrderInquiry)) {
|
||||
//为抄方订单
|
||||
if (!empty($OrderInquiry['transfer_doctor_id'])) {
|
||||
$params = array();
|
||||
//若为抄方订单则显示原订单
|
||||
$params['doctor_id'] = $OrderInquiry['doctor_id'];
|
||||
$fields = ['doctor_id','user_name','doctor_title'];
|
||||
$OriginalDoctor = UserDoctor::getOne($params, $fields);
|
||||
if (!empty($OriginalDoctor)) {
|
||||
$OriginalDoctor->doctor_title = DoctorTitleCode::getMessage($OriginalDoctor->doctor_title);
|
||||
$item['original_doctor'] = $OriginalDoctor;
|
||||
$item['transfer_order'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 UserDoctor 关联对象并修改 doctor_title
|
||||
$userDoctor = $item['UserDoctor'];
|
||||
if (!empty($userDoctor)) {
|
||||
// 修改模型属性
|
||||
$userDoctor->doctor_title = DoctorTitleCode::getMessage($userDoctor->doctor_title);
|
||||
}
|
||||
}
|
||||
}
|
||||
return success($order_prescription);
|
||||
} catch (\Throwable $e) {
|
||||
Log::getInstance("PatientOrderService-getPatientPrescriptionOrderList")->error("获取处方订单列表异常", [
|
||||
'message' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
return fail(HttpEnumCode::SERVER_ERROR, "获取处方订单列表失败");
|
||||
}
|
||||
|
||||
return success($order_prescription);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2085,7 +2197,10 @@ class PatientOrderService extends BaseService
|
||||
|
||||
$fields = [
|
||||
"order_prescription_id",
|
||||
"order_inquiry_id"
|
||||
"order_inquiry_id",
|
||||
"pharmacy_id",
|
||||
"pharmacy_code",
|
||||
"pharmacy_name",
|
||||
];
|
||||
|
||||
$params = array();
|
||||
@@ -2288,13 +2403,18 @@ class PatientOrderService extends BaseService
|
||||
// 获取可用优惠卷总金额
|
||||
$coupon_amount_total = $userCouponService->getCouponTotalPrice($user_coupons);
|
||||
|
||||
// 获取运费金额
|
||||
$logistics_fee = 0;
|
||||
if (env("APP_ENV") == "prod") {
|
||||
$Prescription = new Prescription();
|
||||
$result = $Prescription->getLogisticsFee();
|
||||
if ($freight_calculation_amount < $result['drugCost']) {
|
||||
$logistics_fee = $result['freight'];
|
||||
// 获取运费金额及药房信息
|
||||
$pharmacy_id = $order_prescription['pharmacy_id'] ?? 0;
|
||||
$pharmacy = Pharmacy::getValidPharmacy($pharmacy_id);
|
||||
|
||||
$logistics_fee = 0.00;
|
||||
if (!empty($pharmacy)) {
|
||||
$postage = (float)($pharmacy['postage'] ?? 0.00);
|
||||
$threshold = (float)($pharmacy['free_shipping_threshold'] ?? 0.00);
|
||||
if ($threshold > 0 && (float)$freight_calculation_amount >= $threshold) {
|
||||
$logistics_fee = 0.00;
|
||||
} else {
|
||||
$logistics_fee = $postage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2325,6 +2445,16 @@ class PatientOrderService extends BaseService
|
||||
$result['logistics_fee'] = $logistics_fee;
|
||||
$result['user_ship_address'] = $user_ship_address;
|
||||
$result['order_prescription_product'] = $order_prescription_products;
|
||||
$result['pharmacy_info'] = !empty($pharmacy) ? [
|
||||
'pharmacy_id' => (string)$pharmacy['pharmacy_id'],
|
||||
'pharmacy_code' => $pharmacy['pharmacy_code'],
|
||||
'pharmacy_name' => $pharmacy['pharmacy_name'],
|
||||
'telephone' => $pharmacy['telephone'] ?? '',
|
||||
'is_pickup' => $pharmacy['is_pickup'] ?? 0,
|
||||
'postage' => (string)($pharmacy['postage'] ?? '0.00'),
|
||||
'free_shipping_threshold' => (string)($pharmacy['free_shipping_threshold'] ?? '0.00'),
|
||||
'pickup_address' => $pharmacy['pickup_address'] ?? '',
|
||||
] : null;
|
||||
|
||||
return success($result);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ use App\Model\OrderServicePackageInquiry;
|
||||
use App\Model\PatientFollow;
|
||||
use App\Model\PatientHistoryInquiry;
|
||||
use App\Model\PatientHistoryInquiry as PatientHistoryInquiryModel;
|
||||
use App\Model\DoctorPharmacy;
|
||||
use App\Model\Pharmacy;
|
||||
use App\Model\Popup;
|
||||
use App\Model\Product;
|
||||
use App\Model\ProductPlatformAmount;
|
||||
@@ -523,6 +525,139 @@ class UserDoctorService extends BaseService
|
||||
$data['platform_audit_status'] = $order_prescription['platform_audit_status'];// 处方平台审核状态(0:审核中 1:审核成功 2:审核驳回)
|
||||
$data['platform_fail_reason'] = $order_prescription['platform_fail_reason'];// 处方平台驳回原因
|
||||
|
||||
// 获取问诊订单信息
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $order_prescription['order_inquiry_id'];
|
||||
$order_inquiry = OrderInquiry::getOne($params);
|
||||
if (empty($order_inquiry)) {
|
||||
return fail(HttpEnumCode::SERVER_ERROR);
|
||||
}
|
||||
|
||||
//判断是否为抄方订单
|
||||
if($order_inquiry['doctor_id'] == $order_prescription['doctor_id']){
|
||||
$data['is_transfer_order'] = 0;
|
||||
}else{
|
||||
$data['is_transfer_order'] = 1;
|
||||
}
|
||||
|
||||
// 获取病例信息
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $order_prescription['order_inquiry_id'];
|
||||
$params['status'] = 1;
|
||||
$order_inquiry_case = OrderInquiryCase::getOne($params);
|
||||
if (empty($order_inquiry_case)) {
|
||||
return fail(HttpEnumCode::SERVER_ERROR);
|
||||
}
|
||||
|
||||
// 病情描述
|
||||
$data['disease_desc'] = $order_inquiry_case['disease_desc'];
|
||||
|
||||
// 患病时长
|
||||
if (empty($order_inquiry_case['diagnosis_date'])) {
|
||||
$data['diagnosis_date'] = "";
|
||||
} else {
|
||||
$data['diagnosis_date'] = date('Y-m-d', strtotime($order_inquiry_case['diagnosis_date']));
|
||||
}
|
||||
|
||||
// 疾病信息
|
||||
$data['order_prescription_icd'] = $order_prescription['OrderPrescriptionIcd'] ?? [];
|
||||
|
||||
// 患者id
|
||||
$data['patient_id'] = $order_inquiry_case['patient_id'];
|
||||
|
||||
// 医生id
|
||||
$data['doctor_id'] = $order_prescription['doctor_id'];
|
||||
|
||||
$result[] = $data;
|
||||
}
|
||||
|
||||
unset($order_prescriptions['data']);
|
||||
$order_prescriptions['data'] = $result;
|
||||
|
||||
return success($order_prescriptions);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取处方列表(抄方)
|
||||
* @return array
|
||||
*/
|
||||
public function getTransferPrescriptionList(): array
|
||||
{
|
||||
$user_info = $this->request->getAttribute("userInfo") ?? [];
|
||||
|
||||
$pharmacist_audit_status = $this->request->input('pharmacist_audit_status');
|
||||
$page = $this->request->input('page', 1);
|
||||
$per_page = $this->request->input('per_page', 10);
|
||||
|
||||
// 获取医生信息
|
||||
$params = array();
|
||||
$params['doctor_id'] = $user_info['client_user_id'];
|
||||
|
||||
$fields = [
|
||||
'doctor_id',
|
||||
'iden_auth_status',
|
||||
'idcard_status',
|
||||
'multi_point_status',
|
||||
];
|
||||
$user_doctor = UserDoctor::getOne($params, $fields);
|
||||
if (empty($user_doctor)) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "非法医生");
|
||||
}
|
||||
|
||||
if ($user_doctor['iden_auth_status'] != 1) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "请先完成身份认证");
|
||||
}
|
||||
|
||||
if ($user_doctor['idcard_status'] != 1) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "请先完成实名认证");
|
||||
}
|
||||
|
||||
// 获取处方数据
|
||||
$params = array();
|
||||
$params['doctor_id'] = $user_info['client_user_id'];
|
||||
$params['pharmacist_audit_status'] = $pharmacist_audit_status;
|
||||
$params['is_delete'] = 0;
|
||||
|
||||
if ($pharmacist_audit_status == 1){
|
||||
// 审核通过
|
||||
$params['platform_audit_status'] = 1;
|
||||
}
|
||||
|
||||
$order_prescriptions = OrderPrescription::getTransferWithIcdPage($params,['*'],$page,$per_page);
|
||||
if (empty($order_prescriptions)) {
|
||||
return success($order_prescriptions);
|
||||
}
|
||||
|
||||
// 处理数据
|
||||
$result = array();
|
||||
|
||||
foreach ($order_prescriptions['data'] as $order_prescription) {
|
||||
$data = array();
|
||||
$data['order_prescription_id'] = $order_prescription['order_prescription_id'];
|
||||
$data['order_inquiry_id'] = $order_prescription['order_inquiry_id'];
|
||||
$data['patient_name'] = $order_prescription['patient_name'];
|
||||
$data['patient_sex'] = $order_prescription['patient_sex'];
|
||||
$data['patient_age'] = $order_prescription['patient_age'];
|
||||
$data['created_at'] = date('Y-m-d H:i:s',strtotime($order_prescription['created_at'])); // 开方时间
|
||||
$data['pharmacist_audit_status'] = $order_prescription['pharmacist_audit_status'];// 药师审核状态(0:审核中 1:审核成功 2:审核驳回)
|
||||
$data['pharmacist_fail_reason'] = $order_prescription['pharmacist_fail_reason'];// 驳回原因
|
||||
$data['platform_audit_status'] = $order_prescription['platform_audit_status'];// 处方平台审核状态(0:审核中 1:审核成功 2:审核驳回)
|
||||
$data['platform_fail_reason'] = $order_prescription['platform_fail_reason'];// 处方平台驳回原因
|
||||
// 获取问诊订单信息
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $order_prescription['order_inquiry_id'];
|
||||
$order_inquiry = OrderInquiry::getOne($params);
|
||||
if (empty($order_inquiry)) {
|
||||
return fail(HttpEnumCode::SERVER_ERROR);
|
||||
}
|
||||
|
||||
//判断是否为抄方订单 必为抄方订单
|
||||
$data['is_transfer_order'] = 1;
|
||||
|
||||
// 医生id
|
||||
$data['order_inquiry_doctor_id'] = $order_inquiry['doctor_id'];
|
||||
|
||||
// 获取病例信息
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $order_prescription['order_inquiry_id'];
|
||||
@@ -747,10 +882,6 @@ class UserDoctorService extends BaseService
|
||||
$page = $this->request->input('page', 1);
|
||||
$per_page = $this->request->input('per_page', 10);
|
||||
|
||||
if (empty($user_info)) {
|
||||
return fail();
|
||||
}
|
||||
|
||||
// 4-5分为好评、3分为中评、2-1分为差评
|
||||
if ($evaluation_type == 1) {
|
||||
// 全部
|
||||
@@ -987,6 +1118,8 @@ class UserDoctorService extends BaseService
|
||||
return fail(HttpEnumCode::HTTP_ERROR, $res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 获取患者病例表
|
||||
$fields = [
|
||||
'inquiry_case_id',
|
||||
@@ -1017,6 +1150,14 @@ class UserDoctorService extends BaseService
|
||||
return fail();
|
||||
}
|
||||
|
||||
// 获取问诊订单信息
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $order_inquiry_id;
|
||||
$order_inquiry = OrderInquiry::getOne($params);
|
||||
if (empty($order_inquiry)) {
|
||||
return fail();
|
||||
}
|
||||
|
||||
// 订单-商品订单列表
|
||||
$OrderPrescriptionService = new OrderPrescriptionService();
|
||||
$order_prescription_product = $OrderPrescriptionService->getproductList($order_prescription_id);
|
||||
@@ -1033,6 +1174,7 @@ class UserDoctorService extends BaseService
|
||||
}
|
||||
|
||||
$result = array();
|
||||
$result['is_transfer_order'] = ($order_inquiry['doctor_id'] == $order_prescription['doctor_id']) ? 0 : 1;//判断是否为抄方订单
|
||||
$result['inquiry_case_product'] = $inquiry_case_product;// 用药意向
|
||||
$result['prescription_product'] = $order_prescription_product ?? [];// 开方药品
|
||||
$result['case'] = $order_inquiry_case;// 病例数据
|
||||
@@ -1059,9 +1201,24 @@ class UserDoctorService extends BaseService
|
||||
$result['message'] = "成功";
|
||||
$result['data'] = [];
|
||||
|
||||
// 校验医生是否已配置有效的默认药房
|
||||
$doctor_id = $user_info['client_user_id'] ?? 0;
|
||||
$default_pharmacy = DoctorPharmacy::getDoctorDefaultPharmacy($doctor_id);
|
||||
if (empty($default_pharmacy)) {
|
||||
$result['status'] = 2;
|
||||
$result['message'] = "您尚未配置默认药房或药房已停用,请联系客服人员完成绑定后再开具处方";
|
||||
return success($result);
|
||||
}
|
||||
|
||||
$result['data']['default_pharmacy'] = [
|
||||
'pharmacy_id' => (string)$default_pharmacy->pharmacy_id,
|
||||
'pharmacy_code' => $default_pharmacy->pharmacy_code,
|
||||
'pharmacy_name' => $default_pharmacy->pharmacy_name,
|
||||
];
|
||||
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $order_inquiry_id;
|
||||
$params['doctor_id'] = $user_info['client_user_id'];
|
||||
$params['doctor_id'] = $doctor_id;
|
||||
$order_prescription = OrderPrescription::getOne($params);
|
||||
if (empty($order_prescription)){
|
||||
return success($result);
|
||||
@@ -1322,6 +1479,19 @@ class UserDoctorService extends BaseService
|
||||
$doctor_advice = $this->request->input('doctor_advice');
|
||||
$prescription_product = $this->request->input('prescription_product');
|
||||
$disease_desc = $this->request->input('disease_desc'); // 病情主诉
|
||||
$input_pharmacy_id = $this->request->input('pharmacy_id');
|
||||
|
||||
// 获取开方药房
|
||||
$pharmacy = null;
|
||||
if (isset($input_pharmacy_id) && $input_pharmacy_id !== '') {
|
||||
$pharmacy = Pharmacy::getValidPharmacy($input_pharmacy_id);
|
||||
} else {
|
||||
$pharmacy = DoctorPharmacy::getDoctorDefaultPharmacy($user_info['client_user_id'] ?? 0);
|
||||
}
|
||||
|
||||
if (empty($pharmacy) || empty($pharmacy['pharmacy_code'])) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "未找到有效的开方药房或药房已被停用,无法开具处方");
|
||||
}
|
||||
|
||||
// 获取医生信息
|
||||
$params = array();
|
||||
@@ -1336,9 +1506,72 @@ class UserDoctorService extends BaseService
|
||||
return fail(HttpEnumCode::HTTP_ERROR, $res);
|
||||
}
|
||||
|
||||
if ($user_doctor['multi_point_status'] != 1) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "请先完成多点执业认证");
|
||||
$is_transfer_prescription = false; // 是否抄方医生 false:不是抄方医生 true:抄方医生
|
||||
$transfer_doctor = null; // 抄方医生
|
||||
|
||||
Db::beginTransaction();
|
||||
try {
|
||||
if ($user_doctor['multi_point_status'] != 1) {
|
||||
|
||||
// 获取系统配置
|
||||
$params = array();
|
||||
$params['system_config_id'] = 1;
|
||||
$system_config = SystemConfig::getOne($params);
|
||||
if (empty($system_config)){
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "请先完成多点执业认证");
|
||||
}
|
||||
|
||||
//检查是否年审
|
||||
$is_annual_review = $system_config['is_annual_review'];
|
||||
//若处于年审,则关闭抄方功能
|
||||
if($is_annual_review == 1){
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "请先完成多点执业认证");
|
||||
}
|
||||
|
||||
//查找可接受抄方的医生
|
||||
$params = array();
|
||||
$params['is_transfer_prescription'] = 1;
|
||||
$params['multi_point_status'] = 1;
|
||||
$transfer_prescription_list = UserDoctor::getList($params);
|
||||
// 使用 count() 判断更明确,避免 empty() 对 Collection 的判断歧义
|
||||
if (count($transfer_prescription_list) > 0) {
|
||||
$transfer_doctor = $transfer_prescription_list[0];//取第一个抄方医生
|
||||
|
||||
// 获取抄方医生问诊配置-问诊购药
|
||||
$params = array();
|
||||
$params['doctor_id'] = $transfer_doctor['doctor_id'];
|
||||
$params['inquiry_type'] = 4;
|
||||
$params['inquiry_mode'] = 1;
|
||||
$doctor_inquiry_config = DoctorInquiryConfig::getOne($params);
|
||||
if (empty($doctor_inquiry_config)) {
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "请联系客服开启问诊购药服务!");
|
||||
}
|
||||
|
||||
if ($doctor_inquiry_config['is_enable'] == 0){
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "请联系客服开启问诊购药服务!");
|
||||
}
|
||||
|
||||
$is_transfer_prescription = true;
|
||||
//修改抄方医生id
|
||||
$data = array();
|
||||
$data['transfer_doctor_id'] = $transfer_doctor['doctor_id'];
|
||||
$params = array();
|
||||
$params['order_inquiry_id'] = $order_inquiry_id;
|
||||
$res = OrderInquiry::edit($params,$data);
|
||||
if (!$res) {
|
||||
Db::rollBack();
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "开具处方失败");
|
||||
}
|
||||
}else{
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "医师有误,请联系客服或小助手");
|
||||
}
|
||||
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Db::rollBack();
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "开具处方失败");
|
||||
}
|
||||
|
||||
|
||||
// 获取医生问诊配置-问诊购药
|
||||
$params = array();
|
||||
@@ -1398,7 +1631,7 @@ class UserDoctorService extends BaseService
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "请填写病情主诉");
|
||||
}
|
||||
|
||||
Db::beginTransaction();
|
||||
|
||||
$generator = $this->container->get(IdGeneratorInterface::class);
|
||||
|
||||
try {
|
||||
@@ -1416,16 +1649,25 @@ class UserDoctorService extends BaseService
|
||||
$data = array();
|
||||
$data['order_inquiry_id'] = $order_inquiry_id;
|
||||
$data['doctor_id'] = $user_info['client_user_id'];
|
||||
if ($is_transfer_prescription){
|
||||
$data['doctor_id'] = $transfer_doctor['doctor_id'];
|
||||
}
|
||||
$data['patient_id'] = $order_inquiry['patient_id'];
|
||||
$data['family_id'] = $order_inquiry['family_id'];
|
||||
$data['prescription_status'] = 1;
|
||||
$data['doctor_created_time'] = date('Y-m-d H:i:s',time());
|
||||
$data['prescription_code'] = $generator->generate(); // 处方编号
|
||||
$data['doctor_name'] = $user_doctor['user_name']; // 医生名称
|
||||
if ($is_transfer_prescription){
|
||||
$data['doctor_name'] = $transfer_doctor['user_name'];
|
||||
}
|
||||
$data['patient_name'] = $order_inquiry['patient_name'];
|
||||
$data['patient_sex'] = $order_inquiry['patient_sex'];
|
||||
$data['patient_age'] = $order_inquiry['patient_age'];
|
||||
$data['doctor_advice'] = $doctor_advice;
|
||||
$data['pharmacy_id'] = $pharmacy['pharmacy_id'];
|
||||
$data['pharmacy_code'] = $pharmacy['pharmacy_code'];
|
||||
$data['pharmacy_name'] = $pharmacy['pharmacy_name'];
|
||||
$order_prescription = OrderPrescription::addOrderPrescription($data);
|
||||
if (empty($order_prescription)) {
|
||||
Db::rollBack();
|
||||
@@ -1480,6 +1722,12 @@ class UserDoctorService extends BaseService
|
||||
return fail(HttpEnumCode::HTTP_ERROR,"药品" . $product['product_name'] . "已被删除,无法开具");
|
||||
}
|
||||
|
||||
// 校验药品所属药房是否与处方药房一致
|
||||
if (!empty($product['pharmacy_code']) && $product['pharmacy_code'] !== $pharmacy['pharmacy_code']) {
|
||||
Db::rollBack();
|
||||
return fail(HttpEnumCode::HTTP_ERROR, "药品【" . $product['product_name'] . "】不属于当前药房");
|
||||
}
|
||||
|
||||
// 检测药品是否超出最大可开数
|
||||
if ($item['prescription_product_num'] > $product['prescription_num']) {
|
||||
// 库存不足
|
||||
@@ -1517,6 +1765,7 @@ class UserDoctorService extends BaseService
|
||||
$data['product_id'] = $item['product_id'];
|
||||
$data['prescription_product_num'] = $item['prescription_product_num'];
|
||||
$data['product_name'] = $product['product_name'];
|
||||
$data['product_price'] = $product['product_price'];
|
||||
$data['product_spec'] = $product['product_spec'];
|
||||
$data['license_number'] = $product['license_number'];
|
||||
$data['manufacturer'] = $product['manufacturer'];
|
||||
@@ -1542,8 +1791,14 @@ class UserDoctorService extends BaseService
|
||||
|
||||
// 开具处方
|
||||
$OrderPrescriptionService = new OrderPrescriptionService();
|
||||
$user_id = "";
|
||||
if ($is_transfer_prescription){
|
||||
$user_id = $transfer_doctor['user_id'];
|
||||
}else{
|
||||
$user_id = $user_info['user_id'];
|
||||
}
|
||||
dump($user_info['user_id']);
|
||||
$prescription_open_result = $OrderPrescriptionService->openPrescription($order_prescription->order_prescription_id,2,$user_info['user_id']);
|
||||
$prescription_open_result = $OrderPrescriptionService->openPrescription($order_prescription->order_prescription_id,2,$user_id);
|
||||
if (empty($prescription_open_result['prescription_img_oss_path']) || empty($prescription_open_result['file_id'])){
|
||||
Db::rollBack();
|
||||
return fail(HttpEnumCode::SERVER_ERROR, "处方开具失败");
|
||||
@@ -1580,6 +1835,11 @@ class UserDoctorService extends BaseService
|
||||
$data = array();
|
||||
$data['order_prescription_id'] = $order_prescription->order_prescription_id;
|
||||
$data['doctor_ca_file_id'] = $prescription_open_result['file_id'];
|
||||
$data['doctor_ca_supplier'] = $prescription_open_result['ca_supplier'] ?? "";
|
||||
$data['doctor_ca_sign_type'] = $prescription_open_result['ca_sign_type'] ?? "";
|
||||
$data['doctor_ca_sign_originaltext'] = $prescription_open_result['ca_sign_originaltext'] ?? "";
|
||||
$data['doctor_ca_sign_text'] = $prescription_open_result['ca_sign_text'] ?? "";
|
||||
$data['doctor_ca_sign_time'] = $prescription_open_result['ca_sign_time'] ?? "";
|
||||
$data['prescription_img_oss_path'] = $prescription_open_result['prescription_img_oss_path'];
|
||||
$order_prescription_file = OrderPrescriptionFile::addOrderPrescriptionFile($data);
|
||||
if (empty($order_prescription_file)){
|
||||
@@ -2267,15 +2527,12 @@ class UserDoctorService extends BaseService
|
||||
|
||||
$user_info = $this->request->getAttribute("userInfo") ?? [];
|
||||
|
||||
if (empty($user_info)){
|
||||
return fail(HttpEnumCode::HTTP_SUCCESS, "请登录");
|
||||
}
|
||||
|
||||
$result = array();
|
||||
$result['hospital'] = [];
|
||||
$result['days'] = 0;
|
||||
$result['doctor_inquiry_config'] = [];
|
||||
$result['is_online'] = 0;
|
||||
$result['follow'] = false;
|
||||
|
||||
$fields = [
|
||||
"doctor_id",
|
||||
@@ -2324,7 +2581,7 @@ class UserDoctorService extends BaseService
|
||||
}
|
||||
|
||||
// 患者
|
||||
if ($user_info['user_type'] == 1){
|
||||
if (!empty($user_info) && $user_info['user_type'] == 1){
|
||||
// 获取服务天数
|
||||
$params = array();
|
||||
$params['patient_id'] = $user_info['client_user_id'];
|
||||
@@ -2401,10 +2658,6 @@ class UserDoctorService extends BaseService
|
||||
|
||||
$user_info = $this->request->getAttribute("userInfo") ?? [];
|
||||
|
||||
if (empty($user_info)){
|
||||
return fail(HttpEnumCode::HTTP_SUCCESS, "请登录");
|
||||
}
|
||||
|
||||
$fields = [
|
||||
"doctor_id",
|
||||
"user_id",
|
||||
@@ -2940,4 +3193,4 @@ class UserDoctorService extends BaseService
|
||||
|
||||
return $multi_point_enable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1232,6 +1232,23 @@ class UserService extends BaseService
|
||||
return $result;
|
||||
}
|
||||
|
||||
// === 新增:投递上传OSS队列 ===
|
||||
$mediaTypes = ['TIMImageElem', 'TIMSoundElem', 'TIMVideoFileElem', 'TIMFileElem'];
|
||||
if (in_array($msg_data['MsgBody'][0]['MsgType'], $mediaTypes)) {
|
||||
try {
|
||||
$producer = \Hyperf\Context\ApplicationContext::getContainer()->get(\Hyperf\Amqp\Producer::class);
|
||||
$producerMsg = new \App\Amqp\Producer\UploadImMediaProducer([
|
||||
'message_key' => $msg_data['MsgKey'],
|
||||
'msg_type' => $msg_data['MsgBody'][0]['MsgType'],
|
||||
'msg_content' => $message_content,
|
||||
]);
|
||||
$producer->produce($producerMsg);
|
||||
} catch (\Throwable $e) {
|
||||
Log::getInstance("UserService-userImAfterSendMsg")->error("投递上传OSS队列失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
// === 新增结束 ===
|
||||
|
||||
// im消息通知
|
||||
if ($is_system == 0 && isset($message_send_result) && isset($order_inquiry_id)){
|
||||
try {
|
||||
|
||||
+9
-1
@@ -19,7 +19,14 @@ class Auth
|
||||
"/login/wechat_mobile_login" => "post", // 微信登陆
|
||||
"/login/mobile_login" => "post", // 手机号登陆
|
||||
"/code/phone" => "post",// 获取手机号验证码
|
||||
"/disease/expertise" => "get",// 疾病专长列表-搜索使用
|
||||
"/patient/doctor/inquiry" => "get", // 获取问诊医生列表
|
||||
"/doctor/info/inquiry/" => "get", // 获取医生详情-问诊
|
||||
"/doctor/inquiry/service/" => "get", // 获取医生开启的服务列表
|
||||
"/patient/doctor/evaluation/" => "get", // 获取医生评价
|
||||
"/basic/disease/expertise" => "get",// 疾病专长列表-搜索使用
|
||||
"/patient/article/science" => "get", // 科普文章分页
|
||||
"/patient/article/science/list" => "get", // 科普文章列表
|
||||
"/evaluation" => "get", // 获取医生评价列表
|
||||
"/area/province" => "get",// 获取省份信息
|
||||
"/area/city" => "get", // 获取城市信息
|
||||
"/area/county" => "get", // 获取区县信息
|
||||
@@ -39,6 +46,7 @@ class Auth
|
||||
"/basic/keyword/search" => "get", // 获取热门搜索关键词
|
||||
"/test/uninquiry" => "get", // 获取未接诊的医生
|
||||
"/test/refund" => "post", // 测试退款
|
||||
"/test/error" => "get", // 测试报错
|
||||
"/test" => "get", // 测试
|
||||
"/callback/detection" => "post", // 检测所结果回调
|
||||
"/callback/video/trtc" => "post", // 音视频回调
|
||||
|
||||
+2
-2
@@ -25,8 +25,8 @@ class Log
|
||||
self::getInstance()->{$name}(...$arguments);
|
||||
}
|
||||
|
||||
public static function getInstance(string $name = 'app'): LoggerInterface
|
||||
public static function getInstance(string $name = 'app', string $group = 'default'): LoggerInterface
|
||||
{
|
||||
return ApplicationContext::getContainer()->get(LoggerFactory::class)->get($name);
|
||||
return ApplicationContext::getContainer()->get(LoggerFactory::class)->get($name, $group);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,4 +51,34 @@ return [
|
||||
],
|
||||
'formatter' => $formatter,
|
||||
],
|
||||
'regulatory_platform_http_detail' => [
|
||||
'handler' => [
|
||||
'class' => Monolog\Handler\RotatingFileHandler::class,
|
||||
'constructor' => [
|
||||
'filename' => BASE_PATH . '/runtime/logs/regulatory-platform-http-detail.log',
|
||||
'level' => Monolog\Logger::INFO,
|
||||
],
|
||||
],
|
||||
'formatter' => $formatter,
|
||||
],
|
||||
'regulatory_platform_business' => [
|
||||
'handler' => [
|
||||
'class' => Monolog\Handler\RotatingFileHandler::class,
|
||||
'constructor' => [
|
||||
'filename' => BASE_PATH . '/runtime/logs/regulatory-platform-business.log',
|
||||
'level' => Monolog\Logger::INFO,
|
||||
],
|
||||
],
|
||||
'formatter' => $formatter,
|
||||
],
|
||||
'regulatory_platform_token' => [
|
||||
'handler' => [
|
||||
'class' => Monolog\Handler\RotatingFileHandler::class,
|
||||
'constructor' => [
|
||||
'filename' => BASE_PATH . '/runtime/logs/regulatory-platform-token.log',
|
||||
'level' => Monolog\Logger::INFO,
|
||||
],
|
||||
],
|
||||
'formatter' => $formatter,
|
||||
],
|
||||
];
|
||||
|
||||
+9
-1
@@ -113,12 +113,20 @@ return [
|
||||
"client_id" => env('PRE_PLAT_CLIENT_ID', 'ZD-004'),
|
||||
"client_secret" => env('PRE_PLAT_CLIENT_SECRET', '0baa5927164710b9f800bf33546b6da3'),
|
||||
"api_url" => env('PRE_PLAT_APP_URL', 'http://49.233.3.200:6304/api/thridapi/'),
|
||||
"pharmacy_code" => env('PRE_PLAT_PHARMACY_CODE', 'ZD-10003'), // 药店编码
|
||||
"pharmacy_code" => env('PRE_PLAT_PHARMACY_CODE', 'ZD-10198'), // 药店编码
|
||||
"zhongduan_code" => env('PRE_PLAT_ZHONGDUAN_CODE', 'ZD-10199'), // 终端编码
|
||||
"terminal_code" => env('PRE_PLAT_TERMINAL_CODE', 'ZD-10199'), // 机构编码
|
||||
],
|
||||
'regulatory_platform' => [ // 四川省互联网医疗服务监管平台
|
||||
"client_id" => env('REG_PLAT_CLIENT_ID', '09b117f8d1eb4dbfbf565447205ea60f'),
|
||||
"client_secret" => env('REG_PLAT_CLIENT_SECRET', 'dcfd9223a3f448b0aae83ce22cdcc015'),
|
||||
"api_url" => env('REG_PLAT_APP_URL', 'https://202.61.88.184:19200/'),
|
||||
"sm4_key" => env('REG_PLAT_SM4_KEY', 'e0e295da97ed46cc9ad71b61ed361721'),
|
||||
"sm4_iv" => env('REG_PLAT_SM4_IV', '8a1f69a58603416a9d5b93c4bb67f72f'),
|
||||
"sm2_private_key" => env('REG_PLAT_SM2_PRIVATE_KEY', 'bbc97b0418cc92b8f3b7b7764e554beed6b3adb1aa9bfb9e7839583e5ccf4722'),
|
||||
"sm2_public_key" => env('REG_PLAT_SM2_PUBLIC_KEY', '04fca188451748a35b8c14890a0270fbe787e74f2286b72f6305939c8d527d1fd9fec2926cc0ff6c4f04cfbee2a3d65230565691a8f8453b44665d2b6fbbc79c91'),
|
||||
"ca_supplier" => env('REG_PLAT_CA_SUPPLIER', '2'),
|
||||
"ca_sign_type" => env('REG_PLAT_CA_SIGN_TYPE', '1'),
|
||||
],
|
||||
'kuaidi100' => [ // 快递100
|
||||
"key" => env('LOGISTICS_KEY', 'Mpjjgebe8764'),
|
||||
|
||||
@@ -18,6 +18,7 @@ use App\Controller\DetectionController;
|
||||
use App\Controller\DoctorAccountController;
|
||||
use App\Controller\DoctorAuthController;
|
||||
use App\Controller\DoctorInquiryConfigController;
|
||||
use App\Controller\DoctorPharmacyController;
|
||||
use App\Controller\IndexController;
|
||||
use App\Controller\InquiryController;
|
||||
use App\Controller\LoginController;
|
||||
@@ -243,6 +244,9 @@ Router::addGroup('/doctor', function () {
|
||||
// 获取处方列表
|
||||
Router::get('', [UserDoctorController::class, 'getPrescriptionList']);
|
||||
|
||||
// 获取处方列表(抄方)
|
||||
Router::get('/transfer', [UserDoctorController::class, 'getTransferPrescriptionList']);
|
||||
|
||||
// 获取处方详情
|
||||
Router::get('/info', [UserDoctorController::class, 'getPrescriptionInfo']);
|
||||
|
||||
@@ -256,6 +260,15 @@ Router::addGroup('/doctor', function () {
|
||||
Router::get('/check', [UserDoctorController::class, 'checkOpenPrescription']);
|
||||
});
|
||||
|
||||
// 药房
|
||||
Router::addGroup('/pharmacy', function () {
|
||||
// 获取医生绑定的药房列表
|
||||
Router::get('', [DoctorPharmacyController::class, 'getDoctorPharmacyList']);
|
||||
|
||||
// 设置医生默认药房
|
||||
Router::put('/default', [DoctorPharmacyController::class, 'setDoctorDefaultPharmacy']);
|
||||
});
|
||||
|
||||
// 常用语
|
||||
Router::addGroup('/words', function () {
|
||||
// 获取常用语列表
|
||||
@@ -769,6 +782,9 @@ Router::addGroup('/basic', function () {
|
||||
|
||||
// 获取检测疾病分类列表
|
||||
Router::get('/detection/disease', [BasicDataController::class, 'getDetectionDiseaseList']);
|
||||
|
||||
// 获取公共药房列表
|
||||
Router::get('/pharmacy', [DoctorPharmacyController::class, 'getPublicPharmacyList']);
|
||||
});
|
||||
|
||||
// 获取医生评价
|
||||
@@ -909,6 +925,7 @@ Router::addGroup('/case', function () {
|
||||
// 测试使用
|
||||
Router::addGroup('/test', function () {
|
||||
Router::get('', [TestController::class, 'test']);
|
||||
Router::get('/error', [TestController::class, 'testError']);
|
||||
|
||||
// 获取未接诊的医生
|
||||
Router::get('/uninquiry', [TestController::class, 'uninquiry']);
|
||||
|
||||
@@ -74,10 +74,10 @@ class Dysms
|
||||
public static function sendSms(string $phone_numbers,array $template_param,string $template_code,string $scene_desc = ""): void
|
||||
{
|
||||
try {
|
||||
$app_env = config('app_env','dev');
|
||||
if ($app_env == "dev"){
|
||||
return;
|
||||
}
|
||||
// $app_env = config('app_env','dev');
|
||||
// if ($app_env == "dev"){
|
||||
// return;
|
||||
// }
|
||||
|
||||
$config = config("alibaba.dysms");
|
||||
|
||||
|
||||
+79
-2
@@ -46,6 +46,7 @@ abstract class Ca
|
||||
*/
|
||||
public function getCertSign(string $user_id, string $pin, array $data): mixed
|
||||
{
|
||||
return ['signP7' => 'mock_sign_p7', 'certSerialnumber' => 'mock_serial_number'];
|
||||
$option = [
|
||||
'form_params' => [
|
||||
'entityId' => $user_id, // 用户唯一标识,由业务系统定义
|
||||
@@ -77,6 +78,7 @@ abstract class Ca
|
||||
*/
|
||||
public function verifyPkcs7(string $sign_p7, array $data)
|
||||
{
|
||||
return true;
|
||||
$generator = $this->container->get(IdGeneratorInterface::class);
|
||||
|
||||
$option = [
|
||||
@@ -110,8 +112,40 @@ abstract class Ca
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
/**
|
||||
* Timestamp sign service.
|
||||
* @param string $to_sign
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTimestampSign(string $to_sign): mixed
|
||||
{
|
||||
return ['signedData' => 'mock_timestamp_sign'];
|
||||
$generator = $this->container->get(IdGeneratorInterface::class);
|
||||
|
||||
$option = [
|
||||
'form_params' => [
|
||||
'requestId' => $generator->generate(),
|
||||
'toSign' => $to_sign,
|
||||
]
|
||||
];
|
||||
|
||||
try {
|
||||
$response = $this->httpRequest(
|
||||
$this->api_url . '/signgw-service/api/signgw/timestamap/sign',
|
||||
$option
|
||||
);
|
||||
if (empty($response)) {
|
||||
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
|
||||
}
|
||||
return $response;
|
||||
} catch (GuzzleException $e) {
|
||||
throw new BusinessException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function addUserSignConfig(string $user_id, string $card_num, array $data): bool
|
||||
{
|
||||
return true;
|
||||
$arg = [
|
||||
'form_params' => [
|
||||
'userId' => $user_id,//用户标识信息(为云证书entityId)
|
||||
@@ -229,6 +263,7 @@ abstract class Ca
|
||||
*/
|
||||
public function addSignPdf(string $user_id, array $data): mixed
|
||||
{
|
||||
return [['fileId' => 'mock_file_id']];
|
||||
$option = [
|
||||
'multipart' => [
|
||||
[
|
||||
@@ -278,6 +313,7 @@ abstract class Ca
|
||||
*/
|
||||
public function getSignedFile(string $entity_id, string $file_id): string
|
||||
{
|
||||
return "mock_pdf_content";
|
||||
$arg = [
|
||||
'form_params' => [
|
||||
'userId' => $entity_id,// 用户标识信息(为云证书entityId)
|
||||
@@ -350,6 +386,12 @@ abstract class Ca
|
||||
*/
|
||||
public function httpRequest(string $path, array $arg = []): mixed
|
||||
{
|
||||
// 请求前日志
|
||||
// Log::getInstance()->info('CA接口请求开始', [
|
||||
// '请求地址' => $path,
|
||||
// '原始参数' => $arg,
|
||||
// ]);
|
||||
|
||||
$option = [
|
||||
"headers" => [
|
||||
"app_id" => $this->app_id,
|
||||
@@ -359,26 +401,61 @@ abstract class Ca
|
||||
|
||||
$arg = array_merge($arg, $option);
|
||||
|
||||
// 合并 header 后的最终请求参数日志
|
||||
// Log::getInstance()->info('CA接口最终请求参数', [
|
||||
// '请求地址' => $path,
|
||||
// '最终参数' => $arg,
|
||||
// ]);
|
||||
|
||||
$response = $this->client->post($path, $arg);
|
||||
|
||||
// 记录原始响应(未解析)
|
||||
// Log::getInstance()->info('CA接口原始响应', [
|
||||
// '请求地址' => $path,
|
||||
// 'HTTP状态码' => $response->getStatusCode(),
|
||||
// '原始响应内容' => (string)$response->getBody(),
|
||||
// ]);
|
||||
|
||||
if ($response->getStatusCode() != '200') {
|
||||
// 请求失败
|
||||
// Log::getInstance()->error('CA接口请求失败', [
|
||||
// '请求地址' => $path,
|
||||
// 'HTTP状态码' => $response->getStatusCode(),
|
||||
// '响应内容' => $response->getBody()->getContents(),
|
||||
// ]);
|
||||
throw new BusinessException($response->getBody()->getContents());
|
||||
}
|
||||
$body = json_decode($response->getBody(), true);
|
||||
|
||||
// 记录接口返回的完整 body
|
||||
// Log::getInstance()->info('CA接口返回BODY', [
|
||||
// '请求地址' => $path,
|
||||
// '返回数据' => $body,
|
||||
// ]);
|
||||
|
||||
if (empty($body)) {
|
||||
// 返回值为空
|
||||
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
|
||||
}
|
||||
|
||||
if ($body['result_code'] != 0) {
|
||||
// 请求失败
|
||||
// 业务失败
|
||||
// Log::getInstance()->error('CA业务处理失败', [
|
||||
// '请求地址' => $path,
|
||||
// '返回数据' => $body,
|
||||
// ]);
|
||||
if (!empty($body['result_msg'])) {
|
||||
throw new BusinessException($body['result_msg']);
|
||||
}
|
||||
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
|
||||
}
|
||||
|
||||
// 请求成功日志
|
||||
// Log::getInstance()->info('CA接口请求成功', [
|
||||
// '请求地址' => $path,
|
||||
// '返回数据' => $body,
|
||||
// ]);
|
||||
|
||||
return $body['body'];
|
||||
}
|
||||
|
||||
@@ -414,4 +491,4 @@ abstract class Ca
|
||||
|
||||
return hash_hmac("sha1", $data, $this->secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,23 +106,29 @@ class Prescription
|
||||
),
|
||||
];
|
||||
|
||||
Log::getInstance()->info("处方平台获取药品请求数据:" . json_encode($option,JSON_UNESCAPED_UNICODE));
|
||||
try {
|
||||
$response = $this->httpRequest($this->api_url . $this->version . '/drug/syncDrugCatalogue', $option);
|
||||
if (empty($response['data'])){
|
||||
// 返回值为空
|
||||
if (!empty($response['message'])){
|
||||
Log::getInstance()->error("处方平台获取药品返回数据为空:" . json_encode($response,JSON_UNESCAPED_UNICODE));
|
||||
throw new BusinessException($response['message']);
|
||||
}
|
||||
Log::getInstance()->error("处方平台获取药品返回数据为空,无错误信息");
|
||||
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
|
||||
}
|
||||
|
||||
if (empty($response['data']['result'])){
|
||||
// 返回值为空
|
||||
Log::getInstance()->error("处方平台获取药品返回result为空:" . json_encode($response,JSON_UNESCAPED_UNICODE));
|
||||
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
|
||||
}
|
||||
|
||||
Log::getInstance()->info("处方平台获取药品返回数据:" . json_encode($response,JSON_UNESCAPED_UNICODE));
|
||||
return $response['data']['result'];
|
||||
} catch (GuzzleException $e) {
|
||||
Log::getInstance()->error("处方平台获取药品请求异常:" . $e->getMessage());
|
||||
throw new BusinessException($e->getMessage());
|
||||
}
|
||||
}
|
||||
@@ -130,13 +136,15 @@ class Prescription
|
||||
/**
|
||||
* 获取商品库存
|
||||
* @param string $product_platform_code 处方平台商品编码(此处使用第三方药店商品编码!)
|
||||
* @param string $pharmacy_code 药房编码
|
||||
* @return array
|
||||
*/
|
||||
public function getProdStock(string $product_platform_code): array
|
||||
public function getProdStock(string $product_platform_code, string $pharmacy_code = ""): array
|
||||
{
|
||||
$code = !empty($pharmacy_code) ? $pharmacy_code : $this->pharmacy_code;
|
||||
$option = [
|
||||
"json" => array(
|
||||
"pharmacyCode" => $this->pharmacy_code,
|
||||
"pharmacyCode" => $code,
|
||||
"drugCode" => $product_platform_code,
|
||||
),
|
||||
];
|
||||
@@ -159,13 +167,15 @@ class Prescription
|
||||
|
||||
/**
|
||||
* 获取运费
|
||||
* @param string $pharmacy_code 药房编码
|
||||
* @return array
|
||||
*/
|
||||
public function getLogisticsFee(): array
|
||||
public function getLogisticsFee(string $pharmacy_code = ""): array
|
||||
{
|
||||
$code = !empty($pharmacy_code) ? $pharmacy_code : $this->pharmacy_code;
|
||||
$option = [
|
||||
"json" => array(
|
||||
"pharmacyCode" => $this->pharmacy_code,
|
||||
"pharmacyCode" => $code,
|
||||
),
|
||||
];
|
||||
|
||||
@@ -220,9 +230,10 @@ class Prescription
|
||||
*/
|
||||
public function getPrescription(string $order_product_no,string $prescription_code): array
|
||||
{
|
||||
$terminal_code = config('prescription_platform.terminal_code');
|
||||
$option = [
|
||||
"json" => [
|
||||
"terminalCode" => "ZD-10003",
|
||||
"terminalCode" => $terminal_code,
|
||||
"orderNo" => $order_product_no,
|
||||
"prescriptionNo" => $prescription_code,
|
||||
]
|
||||
@@ -283,19 +294,28 @@ class Prescription
|
||||
$arg = array_merge($arg,$option);
|
||||
}
|
||||
|
||||
// 记录请求日志(隐藏敏感信息)
|
||||
$logArg = $arg;
|
||||
|
||||
Log::getInstance()->info("处方平台HTTP请求:" . $path . ",请求参数:" . json_encode($logArg,JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$response = $this->client->post($path, $arg);
|
||||
|
||||
if ($response->getStatusCode() != '200'){
|
||||
// 请求失败
|
||||
throw new BusinessException($response->getBody()->getContents());
|
||||
$errorBody = $response->getBody()->getContents();
|
||||
Log::getInstance()->error("处方平台HTTP请求失败,状态码:" . $response->getStatusCode() . ",错误信息:" . $errorBody);
|
||||
throw new BusinessException($errorBody);
|
||||
}
|
||||
|
||||
$body = json_decode($response->getBody(),true);
|
||||
if (empty($body)){
|
||||
// 返回值为空
|
||||
Log::getInstance()->error("处方平台HTTP请求返回值为空,路径:" . $path);
|
||||
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
|
||||
}
|
||||
|
||||
Log::getInstance()->info("处方平台HTTP请求成功,路径:" . $path . ",返回数据:" . json_encode($body,JSON_UNESCAPED_UNICODE));
|
||||
return $body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,595 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Extend\RegulatoryPlatform;
|
||||
|
||||
use App\Exception\BusinessException;
|
||||
|
||||
use function Hyperf\Config\config;
|
||||
|
||||
class RegulatoryPlatformProtocol
|
||||
{
|
||||
protected const SM2_CURVE_OID = '1.2.156.10197.1.301';
|
||||
|
||||
protected const EC_PUBLIC_KEY_OID = '1.2.840.10045.2.1';
|
||||
|
||||
protected ?string $clientId;
|
||||
|
||||
protected ?string $clientSecret;
|
||||
|
||||
protected ?string $sm4KeyHex;
|
||||
|
||||
protected ?string $sm4IvHex;
|
||||
|
||||
protected ?string $sm2PrivateKey;
|
||||
|
||||
protected ?string $sm2PublicKey;
|
||||
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->clientId = $this->resolveConfigValue($config, [
|
||||
'client_id',
|
||||
'v2.client_id',
|
||||
'prod.client_id',
|
||||
'default.client_id',
|
||||
]);
|
||||
$this->clientSecret = $this->resolveConfigValue($config, [
|
||||
'client_secret',
|
||||
'v2.client_secret',
|
||||
'prod.client_secret',
|
||||
'default.client_secret',
|
||||
]);
|
||||
$this->sm4KeyHex = $this->resolveConfigValue($config, [
|
||||
'sm4_key',
|
||||
'encrypt_key',
|
||||
'v2.sm4_key',
|
||||
'crypto.sm4_key',
|
||||
'sm4.key',
|
||||
]);
|
||||
$this->sm4IvHex = $this->resolveConfigValue($config, [
|
||||
'sm4_iv',
|
||||
'encrypt_iv',
|
||||
'v2.sm4_iv',
|
||||
'crypto.sm4_iv',
|
||||
'sm4.iv',
|
||||
]);
|
||||
$this->sm2PrivateKey = $this->resolveConfigValue($config, [
|
||||
'sm2_private_key',
|
||||
'sign_private_key',
|
||||
'v2.sm2_private_key',
|
||||
'crypto.sm2_private_key',
|
||||
'sm2.private_key',
|
||||
]);
|
||||
$this->sm2PublicKey = $this->resolveConfigValue($config, [
|
||||
'sm2_public_key',
|
||||
'verify_public_key',
|
||||
'platform_public_key',
|
||||
'v2.sm2_public_key',
|
||||
'crypto.sm2_public_key',
|
||||
'sm2.public_key',
|
||||
]);
|
||||
}
|
||||
|
||||
public static function fromConfig(?array $config = null): self
|
||||
{
|
||||
$config ??= (array) config('regulatory_platform', []);
|
||||
|
||||
return new self($config);
|
||||
}
|
||||
|
||||
public function getClientId(): ?string
|
||||
{
|
||||
return $this->clientId;
|
||||
}
|
||||
|
||||
public function getClientSecret(): ?string
|
||||
{
|
||||
return $this->clientSecret;
|
||||
}
|
||||
|
||||
public function buildRequestEnvelope(
|
||||
array $payload,
|
||||
bool $includeClientId = false,
|
||||
?string $clientId = null,
|
||||
?string $clientSecret = null
|
||||
): array {
|
||||
$plainText = $this->encodeCanonicalJson($payload, true);
|
||||
|
||||
return $this->buildRequestEnvelopeFromPlainText($plainText, $includeClientId, $clientId, $clientSecret);
|
||||
}
|
||||
|
||||
public function buildRequestEnvelopeFromPlainText(
|
||||
string $plainText,
|
||||
bool $includeClientId = false,
|
||||
?string $clientId = null,
|
||||
?string $clientSecret = null
|
||||
): array {
|
||||
$resolvedClientId = $clientId ?? $this->requireClientId();
|
||||
$resolvedClientSecret = $clientSecret ?? $this->requireClientSecret();
|
||||
$encryptedHex = $this->encrypt($plainText);
|
||||
$sign = $this->sign($this->buildSignString($resolvedClientId, $encryptedHex, $resolvedClientSecret));
|
||||
|
||||
$envelope = [
|
||||
'sign' => $sign,
|
||||
'data' => $encryptedHex,
|
||||
];
|
||||
|
||||
if ($includeClientId) {
|
||||
$envelope['client_id'] = $resolvedClientId;
|
||||
}
|
||||
|
||||
return $envelope;
|
||||
}
|
||||
|
||||
public function decodeResponseEnvelope(
|
||||
array $response,
|
||||
bool $verifySignature = false,
|
||||
?string $clientId = null,
|
||||
?string $clientSecret = null
|
||||
): array {
|
||||
$envelope = $this->extractResponseEnvelope($response);
|
||||
$resolvedClientId = $clientId ?? $this->requireClientId();
|
||||
$resolvedClientSecret = $clientSecret ?? $this->requireClientSecret();
|
||||
|
||||
if ($verifySignature) {
|
||||
$verified = $this->verify(
|
||||
$this->buildSignString($resolvedClientId, $envelope['data'], $resolvedClientSecret),
|
||||
$envelope['sign']
|
||||
);
|
||||
if (! $verified) {
|
||||
throw new BusinessException('监管平台响应验签失败');
|
||||
}
|
||||
}
|
||||
|
||||
$plainText = $this->decrypt($envelope['data']);
|
||||
$decoded = json_decode($plainText, true);
|
||||
if (! is_array($decoded)) {
|
||||
throw new BusinessException('监管平台响应解密成功,但 JSON 解析失败');
|
||||
}
|
||||
|
||||
return [
|
||||
'envelope' => $envelope,
|
||||
'plain_text' => $plainText,
|
||||
'decoded' => $decoded,
|
||||
];
|
||||
}
|
||||
|
||||
public function buildSignString(string $clientId, string $encryptedHex, ?string $clientSecret = null): string
|
||||
{
|
||||
$resolvedClientSecret = $clientSecret ?? $this->requireClientSecret();
|
||||
|
||||
return sprintf('client_id=%s&data=%s&key=%s', $clientId, $encryptedHex, $resolvedClientSecret);
|
||||
}
|
||||
|
||||
public function encodeCanonicalJson(array $payload, bool $removeEmptyValues = true): string
|
||||
{
|
||||
$normalized = $this->canonicalizePayload($payload, $removeEmptyValues);
|
||||
$json = json_encode(
|
||||
$normalized,
|
||||
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION
|
||||
);
|
||||
|
||||
if ($json === false) {
|
||||
throw new BusinessException('监管平台请求 JSON 编码失败');
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
public function canonicalizePayload(array $payload, bool $removeEmptyValues = true): array
|
||||
{
|
||||
$normalized = $this->normalizeValue($payload, $removeEmptyValues);
|
||||
|
||||
if (! is_array($normalized)) {
|
||||
throw new BusinessException('监管平台请求体格式错误');
|
||||
}
|
||||
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
public function encrypt(string $plainText): string
|
||||
{
|
||||
$key = $this->decodeHex($this->requireSm4Key(), 'SM4 key');
|
||||
$iv = $this->decodeHex($this->requireSm4Iv(), 'SM4 iv');
|
||||
$cipherText = openssl_encrypt($plainText, 'sm4-cbc', $key, OPENSSL_RAW_DATA, $iv);
|
||||
|
||||
if ($cipherText === false) {
|
||||
throw new BusinessException('监管平台 SM4 加密失败: ' . $this->getLastOpenSslError());
|
||||
}
|
||||
|
||||
return strtolower(bin2hex($cipherText));
|
||||
}
|
||||
|
||||
public function decrypt(string $cipherHex): string
|
||||
{
|
||||
$cipherRaw = $this->decodeHex($cipherHex, 'SM4 cipher');
|
||||
$key = $this->decodeHex($this->requireSm4Key(), 'SM4 key');
|
||||
$iv = $this->decodeHex($this->requireSm4Iv(), 'SM4 iv');
|
||||
$plainText = openssl_decrypt($cipherRaw, 'sm4-cbc', $key, OPENSSL_RAW_DATA, $iv);
|
||||
|
||||
if ($plainText === false) {
|
||||
throw new BusinessException('监管平台 SM4 解密失败: ' . $this->getLastOpenSslError());
|
||||
}
|
||||
|
||||
return $plainText;
|
||||
}
|
||||
|
||||
public function sign(string $plainText): string
|
||||
{
|
||||
$privateKey = openssl_pkey_get_private($this->resolveKeyMaterial($this->sm2PrivateKey, 'SM2 private key'));
|
||||
if ($privateKey === false) {
|
||||
throw new BusinessException('监管平台 SM2 私钥加载失败: ' . $this->getLastOpenSslError());
|
||||
}
|
||||
|
||||
$signature = '';
|
||||
$result = openssl_sign($plainText, $signature, $privateKey, 'sm3');
|
||||
if ($result !== true) {
|
||||
throw new BusinessException('监管平台 SM2 签名失败: ' . $this->getLastOpenSslError());
|
||||
}
|
||||
|
||||
return strtolower(bin2hex($signature));
|
||||
}
|
||||
|
||||
public function verify(string $plainText, string $signatureHex): bool
|
||||
{
|
||||
$publicKey = openssl_pkey_get_public($this->resolveKeyMaterial($this->sm2PublicKey, 'SM2 public key'));
|
||||
if ($publicKey === false) {
|
||||
throw new BusinessException('监管平台 SM2 公钥加载失败: ' . $this->getLastOpenSslError());
|
||||
}
|
||||
|
||||
$signatureRaw = $this->decodeHex($signatureHex, 'SM2 signature');
|
||||
$result = openssl_verify($plainText, $signatureRaw, $publicKey, 'sm3');
|
||||
if ($result === -1) {
|
||||
throw new BusinessException('监管平台 SM2 验签失败: ' . $this->getLastOpenSslError());
|
||||
}
|
||||
|
||||
return $result === 1;
|
||||
}
|
||||
|
||||
protected function extractResponseEnvelope(array $response): array
|
||||
{
|
||||
if (isset($response['data']) && is_array($response['data']) && isset($response['data']['data'])) {
|
||||
$response = $response['data'];
|
||||
}
|
||||
|
||||
$sign = $response['sign'] ?? null;
|
||||
$data = $response['data'] ?? null;
|
||||
if (! is_string($sign) || ! is_string($data) || $sign === '' || $data === '') {
|
||||
throw new BusinessException('监管平台响应报文缺少 sign/data');
|
||||
}
|
||||
|
||||
return [
|
||||
'sign' => $sign,
|
||||
'data' => $data,
|
||||
];
|
||||
}
|
||||
|
||||
protected function normalizeValue(mixed $value, bool $removeEmptyValues): mixed
|
||||
{
|
||||
if (is_array($value)) {
|
||||
if ($this->isAssoc($value)) {
|
||||
$normalized = [];
|
||||
ksort($value);
|
||||
foreach ($value as $key => $item) {
|
||||
$item = $this->normalizeValue($item, $removeEmptyValues);
|
||||
if ($removeEmptyValues && $this->isEmptyValue($item)) {
|
||||
continue;
|
||||
}
|
||||
$normalized[$key] = $item;
|
||||
}
|
||||
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
$normalized = [];
|
||||
foreach ($value as $item) {
|
||||
$item = $this->normalizeValue($item, $removeEmptyValues);
|
||||
if ($removeEmptyValues && $this->isEmptyValue($item)) {
|
||||
continue;
|
||||
}
|
||||
$normalized[] = $item;
|
||||
}
|
||||
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function isAssoc(array $value): bool
|
||||
{
|
||||
return array_keys($value) !== range(0, count($value) - 1);
|
||||
}
|
||||
|
||||
protected function isEmptyValue(mixed $value): bool
|
||||
{
|
||||
if ($value === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
return trim($value) === '';
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
return $value === [];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function decodeHex(string $value, string $label): string
|
||||
{
|
||||
$value = trim($value);
|
||||
if ($value === '' || strlen($value) % 2 !== 0 || ! ctype_xdigit($value)) {
|
||||
throw new BusinessException(sprintf('监管平台 %s 不是合法十六进制字符串', $label));
|
||||
}
|
||||
|
||||
$decoded = hex2bin($value);
|
||||
if ($decoded === false) {
|
||||
throw new BusinessException(sprintf('监管平台 %s 十六进制解码失败', $label));
|
||||
}
|
||||
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
protected function resolveKeyMaterial(?string $key, string $label): string
|
||||
{
|
||||
$key = trim((string) $key);
|
||||
if ($key === '') {
|
||||
throw new BusinessException(sprintf('监管平台 %s 未配置', $label));
|
||||
}
|
||||
|
||||
if (str_starts_with($key, 'file://')) {
|
||||
$key = substr($key, 7);
|
||||
}
|
||||
|
||||
if (is_file($key)) {
|
||||
$contents = file_get_contents($key);
|
||||
if ($contents === false || trim($contents) === '') {
|
||||
throw new BusinessException(sprintf('监管平台 %s 文件读取失败', $label));
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
if ($label === 'SM2 private key' && $this->isRawHexPrivateKey($key)) {
|
||||
return $this->buildSm2PrivateKeyPem($key, $this->sm2PublicKey);
|
||||
}
|
||||
|
||||
if ($label === 'SM2 public key' && $this->isRawHexPublicKey($key)) {
|
||||
return $this->buildSm2PublicKeyPem($key);
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
protected function resolveConfigValue(array $config, array $paths): ?string
|
||||
{
|
||||
foreach ($paths as $path) {
|
||||
$value = $this->getByPath($config, $path);
|
||||
if ($value !== null && $value !== '') {
|
||||
return (string) $value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function getByPath(array $config, string $path): mixed
|
||||
{
|
||||
$current = $config;
|
||||
foreach (explode('.', $path) as $segment) {
|
||||
if (! is_array($current) || ! array_key_exists($segment, $current)) {
|
||||
return null;
|
||||
}
|
||||
$current = $current[$segment];
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
|
||||
protected function requireClientId(): string
|
||||
{
|
||||
if (empty($this->clientId)) {
|
||||
throw new BusinessException('监管平台 client_id 未配置');
|
||||
}
|
||||
|
||||
return $this->clientId;
|
||||
}
|
||||
|
||||
protected function requireClientSecret(): string
|
||||
{
|
||||
if (empty($this->clientSecret)) {
|
||||
throw new BusinessException('监管平台 client_secret 未配置');
|
||||
}
|
||||
|
||||
return $this->clientSecret;
|
||||
}
|
||||
|
||||
protected function requireSm4Key(): string
|
||||
{
|
||||
if (empty($this->sm4KeyHex)) {
|
||||
throw new BusinessException('监管平台 SM4 key 未配置');
|
||||
}
|
||||
|
||||
return $this->sm4KeyHex;
|
||||
}
|
||||
|
||||
protected function requireSm4Iv(): string
|
||||
{
|
||||
if (empty($this->sm4IvHex)) {
|
||||
throw new BusinessException('监管平台 SM4 iv 未配置');
|
||||
}
|
||||
|
||||
return $this->sm4IvHex;
|
||||
}
|
||||
|
||||
protected function getLastOpenSslError(): string
|
||||
{
|
||||
$errors = [];
|
||||
while (($error = openssl_error_string()) !== false) {
|
||||
$errors[] = $error;
|
||||
}
|
||||
|
||||
return $errors === [] ? 'unknown openssl error' : implode(' | ', $errors);
|
||||
}
|
||||
|
||||
protected function isRawHexPrivateKey(string $key): bool
|
||||
{
|
||||
return strlen($key) === 64 && ctype_xdigit($key);
|
||||
}
|
||||
|
||||
protected function isRawHexPublicKey(string $key): bool
|
||||
{
|
||||
if (! ctype_xdigit($key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array(strlen($key), [128, 130], true);
|
||||
}
|
||||
|
||||
protected function buildSm2PrivateKeyPem(string $privateKeyHex, ?string $publicKeyHex = null): string
|
||||
{
|
||||
$privateKeyRaw = $this->decodeHex($privateKeyHex, 'SM2 private key');
|
||||
$sequence = $this->derSequence(
|
||||
$this->derInteger(1),
|
||||
$this->derOctetString($privateKeyRaw),
|
||||
$this->derContextSpecific(0, $this->derObjectIdentifier(self::SM2_CURVE_OID))
|
||||
);
|
||||
|
||||
if (is_string($publicKeyHex) && $this->isRawHexPublicKey(trim($publicKeyHex))) {
|
||||
$publicKeyRaw = $this->normalizeRawPublicKey(trim($publicKeyHex));
|
||||
$sequence .= $this->derContextSpecific(1, $this->derBitString($publicKeyRaw));
|
||||
$sequence = $this->derSequence($this->derInteger(1), $this->derOctetString($privateKeyRaw), $this->derContextSpecific(0, $this->derObjectIdentifier(self::SM2_CURVE_OID)), $this->derContextSpecific(1, $this->derBitString($publicKeyRaw)));
|
||||
}
|
||||
|
||||
return $this->pemEncode('EC PRIVATE KEY', $sequence);
|
||||
}
|
||||
|
||||
protected function buildSm2PublicKeyPem(string $publicKeyHex): string
|
||||
{
|
||||
$publicKeyRaw = $this->normalizeRawPublicKey($publicKeyHex);
|
||||
$algorithm = $this->derSequence(
|
||||
$this->derObjectIdentifier(self::EC_PUBLIC_KEY_OID),
|
||||
$this->derObjectIdentifier(self::SM2_CURVE_OID)
|
||||
);
|
||||
$subjectPublicKeyInfo = $this->derSequence(
|
||||
$algorithm,
|
||||
$this->derBitString($publicKeyRaw)
|
||||
);
|
||||
|
||||
return $this->pemEncode('PUBLIC KEY', $subjectPublicKeyInfo);
|
||||
}
|
||||
|
||||
protected function normalizeRawPublicKey(string $publicKeyHex): string
|
||||
{
|
||||
$publicKeyHex = strtolower($publicKeyHex);
|
||||
if (strlen($publicKeyHex) === 128) {
|
||||
$publicKeyHex = '04' . $publicKeyHex;
|
||||
}
|
||||
|
||||
return $this->decodeHex($publicKeyHex, 'SM2 public key');
|
||||
}
|
||||
|
||||
protected function pemEncode(string $label, string $der): string
|
||||
{
|
||||
return "-----BEGIN {$label}-----\n"
|
||||
. chunk_split(base64_encode($der), 64, "\n")
|
||||
. "-----END {$label}-----\n";
|
||||
}
|
||||
|
||||
protected function derSequence(string ...$parts): string
|
||||
{
|
||||
return $this->derWrap(0x30, implode('', $parts));
|
||||
}
|
||||
|
||||
protected function derInteger(int $value): string
|
||||
{
|
||||
$encoded = '';
|
||||
$current = $value;
|
||||
do {
|
||||
$encoded = chr($current & 0xff) . $encoded;
|
||||
$current >>= 8;
|
||||
} while ($current > 0);
|
||||
|
||||
if ((ord($encoded[0]) & 0x80) !== 0) {
|
||||
$encoded = "\x00" . $encoded;
|
||||
}
|
||||
|
||||
return $this->derWrap(0x02, $encoded);
|
||||
}
|
||||
|
||||
protected function derOctetString(string $value): string
|
||||
{
|
||||
return $this->derWrap(0x04, $value);
|
||||
}
|
||||
|
||||
protected function derBitString(string $value): string
|
||||
{
|
||||
return $this->derWrap(0x03, "\x00" . $value);
|
||||
}
|
||||
|
||||
protected function derObjectIdentifier(string $oid): string
|
||||
{
|
||||
$parts = array_map('intval', explode('.', $oid));
|
||||
if (count($parts) < 2) {
|
||||
throw new BusinessException('监管平台 OID 格式错误');
|
||||
}
|
||||
|
||||
$encoded = chr(($parts[0] * 40) + $parts[1]);
|
||||
for ($i = 2; $i < count($parts); $i++) {
|
||||
$encoded .= $this->encodeOidPart($parts[$i]);
|
||||
}
|
||||
|
||||
return $this->derWrap(0x06, $encoded);
|
||||
}
|
||||
|
||||
protected function derContextSpecific(int $index, string $value): string
|
||||
{
|
||||
return $this->derWrap(0xa0 + $index, $value);
|
||||
}
|
||||
|
||||
protected function derWrap(int $tag, string $value): string
|
||||
{
|
||||
return chr($tag) . $this->derLength(strlen($value)) . $value;
|
||||
}
|
||||
|
||||
protected function derLength(int $length): string
|
||||
{
|
||||
if ($length < 0x80) {
|
||||
return chr($length);
|
||||
}
|
||||
|
||||
$encoded = '';
|
||||
$current = $length;
|
||||
while ($current > 0) {
|
||||
$encoded = chr($current & 0xff) . $encoded;
|
||||
$current >>= 8;
|
||||
}
|
||||
|
||||
return chr(0x80 | strlen($encoded)) . $encoded;
|
||||
}
|
||||
|
||||
protected function encodeOidPart(int $value): string
|
||||
{
|
||||
if ($value === 0) {
|
||||
return "\x00";
|
||||
}
|
||||
|
||||
$encoded = '';
|
||||
$current = $value;
|
||||
while ($current > 0) {
|
||||
$encoded = chr($current & 0x7f) . $encoded;
|
||||
$current >>= 7;
|
||||
}
|
||||
|
||||
$length = strlen($encoded);
|
||||
for ($i = 0; $i < $length - 1; $i++) {
|
||||
$encoded[$i] = chr(ord($encoded[$i]) | 0x80);
|
||||
}
|
||||
|
||||
return $encoded;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Extend\RegulatoryPlatform;
|
||||
|
||||
use App\Constants\HttpEnumCode;
|
||||
@@ -7,18 +9,31 @@ use App\Exception\BusinessException;
|
||||
use App\Utils\Log;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Hyperf\Context\ApplicationContext;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\Redis\Redis;
|
||||
use Hyperf\Context\ApplicationContext;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
/**
|
||||
* 四川省互联网医疗服务监管平台
|
||||
*/
|
||||
class regulatoryPlatform
|
||||
{
|
||||
protected const ACCESS_TOKEN_CACHE_KEY = 'regulatory_platform_access_token';
|
||||
|
||||
protected const REFRESH_TOKEN_CACHE_KEY = 'regulatory_platform_refresh_token';
|
||||
|
||||
protected const EXPIRES_AT_CACHE_KEY = 'regulatory_platform_access_token_expires_at';
|
||||
|
||||
protected const DEFAULT_ACCESS_TOKEN_TTL = 7200;
|
||||
|
||||
protected const DEFAULT_REFRESH_TOKEN_TTL = 2592000;
|
||||
|
||||
protected const ACCESS_TOKEN_BUFFER = 60;
|
||||
|
||||
protected const HTTP_TIMEOUT = 30;
|
||||
|
||||
protected const HTTP_CONNECT_TIMEOUT = 10;
|
||||
|
||||
#[Inject]
|
||||
protected ContainerInterface $container;
|
||||
|
||||
@@ -28,237 +43,770 @@ class regulatoryPlatform
|
||||
#[Inject]
|
||||
protected Redis $redis;
|
||||
|
||||
protected string $api_url;
|
||||
protected string $client_id;
|
||||
protected string $client_secret;
|
||||
protected array $config;
|
||||
|
||||
protected ?string $api_url;
|
||||
|
||||
protected ?string $client_id;
|
||||
|
||||
protected ?string $client_secret;
|
||||
|
||||
protected RegulatoryPlatformProtocol $protocol;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// 请求地址
|
||||
$this->api_url = \Hyperf\Config\config('regulatory_platform.api_url');
|
||||
$this->client_id = \Hyperf\Config\config('regulatory_platform.client_id');
|
||||
$this->client_secret = \Hyperf\Config\config('regulatory_platform.client_secret');
|
||||
$this->config = (array) \Hyperf\Config\config('regulatory_platform', []);
|
||||
$this->container = ApplicationContext::getContainer();
|
||||
$this->client = $this->container->get(Client::class);
|
||||
$this->redis = $this->container->get(Redis::class);
|
||||
$this->protocol = RegulatoryPlatformProtocol::fromConfig($this->config);
|
||||
$this->client_id = $this->protocol->getClientId();
|
||||
$this->client_secret = $this->protocol->getClientSecret();
|
||||
$this->api_url = $this->resolveConfigValue([
|
||||
'api_url',
|
||||
'v2.api_url',
|
||||
'v2.data_upload_base_url',
|
||||
'data_upload_base_url',
|
||||
'data_api_url',
|
||||
'base_url',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求token
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessToken(): string
|
||||
public function protocol(): RegulatoryPlatformProtocol
|
||||
{
|
||||
// 获取token
|
||||
$option = [
|
||||
"json" => array(
|
||||
"clientId" => $this->client_id,
|
||||
"appSecret" => $this->client_secret,
|
||||
),
|
||||
];
|
||||
return $this->protocol;
|
||||
}
|
||||
|
||||
public function buildV2RequestEnvelope(
|
||||
array $payload,
|
||||
bool $includeClientId = false,
|
||||
?string $clientId = null,
|
||||
?string $clientSecret = null
|
||||
): array {
|
||||
return $this->protocol->buildRequestEnvelope($payload, $includeClientId, $clientId, $clientSecret);
|
||||
}
|
||||
|
||||
public function decodeV2ResponseEnvelope(
|
||||
array $response,
|
||||
bool $verifySignature = false,
|
||||
?string $clientId = null,
|
||||
?string $clientSecret = null
|
||||
): array {
|
||||
return $this->protocol->decodeResponseEnvelope($response, $verifySignature, $clientId, $clientSecret);
|
||||
}
|
||||
|
||||
public function getAccessToken(bool $forceRefresh = false): string
|
||||
{
|
||||
if (! $forceRefresh) {
|
||||
$cachedAccessToken = $this->getCachedAccessToken();
|
||||
if (! empty($cachedAccessToken)) {
|
||||
return $cachedAccessToken;
|
||||
}
|
||||
}
|
||||
|
||||
$refreshToken = $this->getCachedRefreshToken();
|
||||
if (! empty($refreshToken)) {
|
||||
try {
|
||||
$tokenPayload = $this->refreshAccessToken($refreshToken);
|
||||
|
||||
return $tokenPayload['accessToken'];
|
||||
} catch (\Throwable $throwable) {
|
||||
Log::getInstance('regulatoryPlatform-token', 'regulatory_platform_token')->warning($throwable->getMessage());
|
||||
$this->clearCachedRefreshToken();
|
||||
}
|
||||
}
|
||||
|
||||
$tokenPayload = $this->requestAccessToken();
|
||||
|
||||
return $tokenPayload['accessToken'];
|
||||
}
|
||||
|
||||
public function refreshAccessToken(?string $refreshToken = null): array
|
||||
{
|
||||
$refreshToken ??= $this->getCachedRefreshToken();
|
||||
if (empty($refreshToken)) {
|
||||
throw new BusinessException('Regulatory platform refresh token cache is empty');
|
||||
}
|
||||
|
||||
return $this->requestToken(
|
||||
$this->buildAuthEndpoint('refresh'),
|
||||
[
|
||||
'grantType' => 'refresh_token',
|
||||
'refreshToken' => $refreshToken,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function clearTokenCache(): void
|
||||
{
|
||||
$this->redis->del(self::ACCESS_TOKEN_CACHE_KEY);
|
||||
$this->redis->del(self::REFRESH_TOKEN_CACHE_KEY);
|
||||
$this->redis->del(self::EXPIRES_AT_CACHE_KEY);
|
||||
}
|
||||
|
||||
public function requestV2Decoded(
|
||||
string $url,
|
||||
array $payload,
|
||||
bool $includeClientId = false,
|
||||
bool $verifySignature = false
|
||||
): array {
|
||||
try {
|
||||
$response = $this->httpRequest($this->api_url . 'wjw/third/oauth/getAccessToken', $option);
|
||||
if (isset($response['status'])) {
|
||||
if ($response['status'] != 0) {
|
||||
if (!empty($response['message'])) {
|
||||
throw new BusinessException($response['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
$envelope = $this->buildV2RequestEnvelope($payload, $includeClientId);
|
||||
$this->logBusinessRequest('requestV2Decoded', $url, [
|
||||
'payload' => $payload,
|
||||
'includeClientId' => $includeClientId,
|
||||
'verifySignature' => $verifySignature,
|
||||
'requestEnvelope' => $envelope,
|
||||
]);
|
||||
$response = $this->httpRequestV2($url, ['json' => $envelope]);
|
||||
$this->assertV2SuccessResponse($response);
|
||||
|
||||
if (empty($response['data'])) {
|
||||
// 返回值为空
|
||||
if (!empty($response['message'])) {
|
||||
throw new BusinessException($response['message']);
|
||||
}
|
||||
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
|
||||
}
|
||||
$decoded = $this->decodeV2ResponseEnvelope($response, $verifySignature);
|
||||
$this->logBusinessResponse('requestV2Decoded', $url, [
|
||||
'verifySignature' => $verifySignature,
|
||||
'response' => $response,
|
||||
'decodedResponse' => $decoded,
|
||||
]);
|
||||
|
||||
$data = json_decode($response['data'], true);
|
||||
if (empty($data['accessToken'])) {
|
||||
// 返回值为空
|
||||
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
|
||||
}
|
||||
|
||||
// 默认为6天
|
||||
$expires_in = 60 * 60 * 24 * 6;
|
||||
if (!empty($data['expiresIn'])) {
|
||||
if ($data['expiresIn'] > 100){
|
||||
$expires_in = $data['expiresIn'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->redis->set("regulatory_platform_access_token", $data['accessToken'],$expires_in);
|
||||
|
||||
return $data['accessToken'];
|
||||
return $decoded;
|
||||
} catch (GuzzleException $e) {
|
||||
$this->logBusinessException('requestV2Decoded', $url, $e, [
|
||||
'payload' => $payload,
|
||||
'includeClientId' => $includeClientId,
|
||||
'verifySignature' => $verifySignature,
|
||||
]);
|
||||
throw new BusinessException($e->getMessage());
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->logBusinessException('requestV2Decoded', $url, $throwable, [
|
||||
'payload' => $payload,
|
||||
'includeClientId' => $includeClientId,
|
||||
'verifySignature' => $verifySignature,
|
||||
]);
|
||||
throw $throwable;
|
||||
}
|
||||
}
|
||||
|
||||
public function requestV2Raw(string $url, array $payload, bool $includeClientId = false): array
|
||||
{
|
||||
try {
|
||||
$envelope = $this->buildV2RequestEnvelope($payload, $includeClientId);
|
||||
$this->logBusinessRequest('requestV2Raw', $url, [
|
||||
'payload' => $payload,
|
||||
'includeClientId' => $includeClientId,
|
||||
'requestEnvelope' => $envelope,
|
||||
]);
|
||||
$response = $this->httpRequestV2($url, ['json' => $envelope]);
|
||||
$this->assertV2SuccessResponse($response);
|
||||
$this->logBusinessResponse('requestV2Raw', $url, [
|
||||
'response' => $response,
|
||||
]);
|
||||
|
||||
return $response;
|
||||
} catch (GuzzleException $e) {
|
||||
$this->logBusinessException('requestV2Raw', $url, $e, [
|
||||
'payload' => $payload,
|
||||
'includeClientId' => $includeClientId,
|
||||
]);
|
||||
throw new BusinessException($e->getMessage());
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->logBusinessException('requestV2Raw', $url, $throwable, [
|
||||
'payload' => $payload,
|
||||
'includeClientId' => $includeClientId,
|
||||
]);
|
||||
throw $throwable;
|
||||
}
|
||||
}
|
||||
|
||||
public function extractV2UploadErrors(array $decodedResponse): array
|
||||
{
|
||||
$rows = $decodedResponse['data']['list'] ?? [];
|
||||
if (! is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
foreach ($rows as $row) {
|
||||
if (! is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rowErrors = $row['errors'] ?? [];
|
||||
if (! is_array($rowErrors) || $rowErrors === []) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$errors[] = [
|
||||
'tableName' => isset($row['tableName']) && is_string($row['tableName']) ? $row['tableName'] : '',
|
||||
'errors' => $rowErrors,
|
||||
];
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
public function hasV2UploadErrors(array $decodedResponse): bool
|
||||
{
|
||||
return $this->extractV2UploadErrors($decodedResponse) !== [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 上报 网络咨询(网络门诊)服务
|
||||
* @param array $arg
|
||||
* @return array
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
* 上报网络咨询(网络门诊)服务
|
||||
*/
|
||||
public function uploadConsult(array $arg): array
|
||||
{
|
||||
try {
|
||||
$this->redis = $this->container->get(Redis::class);
|
||||
|
||||
$access_token = $this->redis->get("regulatory_platform_access_token");
|
||||
if (empty($access_token)) {
|
||||
$access_token = $this->getAccessToken();
|
||||
}
|
||||
|
||||
foreach ($arg as &$item){
|
||||
$item['accessToken'] = $access_token;
|
||||
$item['clientId'] = $this->client_id;
|
||||
}
|
||||
|
||||
$option = [
|
||||
"json" => $arg
|
||||
];
|
||||
|
||||
$response = $this->httpRequest($this->api_url . '/wjw/upload/uploadConsult', $option);
|
||||
if (isset($response['status'])) {
|
||||
if ($response['status'] != 0) {
|
||||
if (!empty($response['message'])) {
|
||||
throw new BusinessException($response['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
} catch (GuzzleException $e) {
|
||||
throw new BusinessException($e->getMessage());
|
||||
}
|
||||
return $this->uploadData('api_upload_consult', $arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上报 网络复诊服务
|
||||
* @param array $arg
|
||||
* @return array
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
* 上报网络复诊服务
|
||||
*/
|
||||
public function uploadFurtherConsult(array $arg): array
|
||||
{
|
||||
try {
|
||||
$this->redis = $this->container->get(Redis::class);
|
||||
|
||||
$access_token = $this->redis->get("regulatory_platform_access_token");
|
||||
if (empty($access_token)) {
|
||||
$access_token = $this->getAccessToken();
|
||||
}
|
||||
|
||||
foreach ($arg as &$item){
|
||||
$item['accessToken'] = $access_token;
|
||||
$item['clientId'] = $this->client_id;
|
||||
}
|
||||
|
||||
$option = [
|
||||
"json" => $arg
|
||||
];
|
||||
|
||||
$response = $this->httpRequest($this->api_url . '/wjw/upload/uploadFurtherConsult', $option);
|
||||
if (isset($response['status'])) {
|
||||
if ($response['status'] != 0) {
|
||||
if (!empty($response['message'])) {
|
||||
throw new BusinessException($response['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
} catch (GuzzleException $e) {
|
||||
throw new BusinessException($e->getMessage());
|
||||
}
|
||||
return $this->uploadData('api_upload_further_consult', $arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上报 电子处方服务
|
||||
* @param array $arg
|
||||
* @return array
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
* 上报电子处方服务
|
||||
*/
|
||||
public function uploadRecipe(array $arg): array
|
||||
{
|
||||
try {
|
||||
$this->redis = $this->container->get(Redis::class);
|
||||
return $this->uploadData('api_upload_recipe', $arg);
|
||||
}
|
||||
|
||||
$access_token = $this->redis->get("regulatory_platform_access_token");
|
||||
if (empty($access_token)) {
|
||||
$access_token = $this->getAccessToken();
|
||||
}
|
||||
/**
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
* 上报药品处方明细
|
||||
*/
|
||||
public function uploadRecipeDetail(array $arg): array
|
||||
{
|
||||
return $this->uploadData('api_upload_recipe_detail_yp', $arg);
|
||||
}
|
||||
|
||||
foreach ($arg as &$item){
|
||||
$item['accessToken'] = $access_token;
|
||||
$item['clientId'] = $this->client_id;
|
||||
}
|
||||
protected function requestAccessToken(): array
|
||||
{
|
||||
return $this->requestToken(
|
||||
$this->buildAuthEndpoint('access'),
|
||||
[
|
||||
'grantType' => 'client_credentials',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$option = [
|
||||
"json" => $arg
|
||||
];
|
||||
protected function requestToken(string $url, array $payload): array
|
||||
{
|
||||
$decoded = $this->requestV2Decoded($url, $payload, true, false);
|
||||
$tokenPayload = $this->extractTokenPayload($decoded['decoded']);
|
||||
$this->cacheTokenPayload($tokenPayload);
|
||||
|
||||
$response = $this->httpRequest($this->api_url . '/wjw/upload/uploadRecipe', $option);
|
||||
if (isset($response['status'])) {
|
||||
if ($response['status'] != 0) {
|
||||
if (!empty($response['message'])) {
|
||||
throw new BusinessException($response['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $tokenPayload;
|
||||
}
|
||||
|
||||
return $response;
|
||||
} catch (GuzzleException $e) {
|
||||
throw new BusinessException($e->getMessage());
|
||||
protected function extractTokenPayload(array $decoded): array
|
||||
{
|
||||
$result = $decoded['result'] ?? null;
|
||||
if (! is_array($result)) {
|
||||
throw new BusinessException('Regulatory platform token response missing result');
|
||||
}
|
||||
|
||||
$accessToken = $result['accessToken'] ?? '';
|
||||
if (! is_string($accessToken) || $accessToken === '') {
|
||||
throw new BusinessException('Regulatory platform token response missing accessToken');
|
||||
}
|
||||
|
||||
$refreshToken = $result['refreshToken'] ?? $this->getCachedRefreshToken();
|
||||
$expiresIn = (int) ($result['expiresIn'] ?? self::DEFAULT_ACCESS_TOKEN_TTL);
|
||||
if ($expiresIn <= 0) {
|
||||
$expiresIn = self::DEFAULT_ACCESS_TOKEN_TTL;
|
||||
}
|
||||
|
||||
return [
|
||||
'accessToken' => $accessToken,
|
||||
'refreshToken' => is_string($refreshToken) ? $refreshToken : '',
|
||||
'expiresIn' => $expiresIn,
|
||||
'tokenType' => isset($result['tokenType']) && is_string($result['tokenType']) ? $result['tokenType'] : '',
|
||||
];
|
||||
}
|
||||
|
||||
protected function uploadData(string $tableName, array $rows): array
|
||||
{
|
||||
$accessToken = $this->getAccessToken();
|
||||
$payload = [
|
||||
$tableName => $this->normalizeUploadRows($rows),
|
||||
];
|
||||
$envelope = $this->buildV2RequestEnvelope($payload, false);
|
||||
$response = $this->httpRequestV2($this->buildDataUploadRequestUrl(), [
|
||||
'query' => $this->buildClientCredentialQuery(),
|
||||
'headers' => [
|
||||
'du-token' => $accessToken,
|
||||
],
|
||||
'json' => $envelope,
|
||||
]);
|
||||
$this->assertV2SuccessResponse($response);
|
||||
|
||||
$decodedResponse = $this->decodeV2ResponseEnvelope($response, false);
|
||||
$decodedPayload = $decodedResponse['decoded'];
|
||||
if ($this->hasV2UploadErrors($decodedPayload)) {
|
||||
throw new BusinessException($this->formatV2UploadErrors($this->extractV2UploadErrors($decodedPayload)));
|
||||
}
|
||||
|
||||
return $decodedPayload;
|
||||
}
|
||||
|
||||
protected function cacheTokenPayload(array $tokenPayload): void
|
||||
{
|
||||
$expiresIn = (int) ($tokenPayload['expiresIn'] ?? self::DEFAULT_ACCESS_TOKEN_TTL);
|
||||
if ($expiresIn <= 0) {
|
||||
$expiresIn = self::DEFAULT_ACCESS_TOKEN_TTL;
|
||||
}
|
||||
|
||||
$accessTokenTtl = max($expiresIn - self::ACCESS_TOKEN_BUFFER, 1);
|
||||
$refreshTokenTtl = max($expiresIn, self::DEFAULT_REFRESH_TOKEN_TTL);
|
||||
|
||||
$this->redis->setex(self::ACCESS_TOKEN_CACHE_KEY, $accessTokenTtl, $tokenPayload['accessToken']);
|
||||
$this->redis->setex(self::EXPIRES_AT_CACHE_KEY, $refreshTokenTtl, (string) (time() + $accessTokenTtl));
|
||||
|
||||
if (! empty($tokenPayload['refreshToken'])) {
|
||||
$this->redis->setex(self::REFRESH_TOKEN_CACHE_KEY, $refreshTokenTtl, $tokenPayload['refreshToken']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getCachedAccessToken(): ?string
|
||||
{
|
||||
$token = $this->redis->get(self::ACCESS_TOKEN_CACHE_KEY);
|
||||
|
||||
return is_string($token) && $token !== '' ? $token : null;
|
||||
}
|
||||
|
||||
protected function getCachedRefreshToken(): ?string
|
||||
{
|
||||
$token = $this->redis->get(self::REFRESH_TOKEN_CACHE_KEY);
|
||||
|
||||
return is_string($token) && $token !== '' ? $token : null;
|
||||
}
|
||||
|
||||
protected function clearCachedRefreshToken(): void
|
||||
{
|
||||
$this->redis->del(self::REFRESH_TOKEN_CACHE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求封装
|
||||
* @param string $path
|
||||
* @param array $arg
|
||||
* @return array
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
protected function httpRequest(string $path, array $arg = []): array
|
||||
{
|
||||
$option = [
|
||||
"verify" => false
|
||||
];
|
||||
if (!empty($option)) {
|
||||
$arg = array_merge($arg, $option);
|
||||
}
|
||||
$body = $this->sendJsonRequest($path, $arg, 'regulatoryPlatform-httpRequest', false);
|
||||
|
||||
Log::getInstance("regulatoryPlatform-httpRequest")->info(json_encode($arg,JSON_UNESCAPED_UNICODE));
|
||||
$response = $this->client->post($path, $arg);
|
||||
|
||||
if ($response->getStatusCode() != '200') {
|
||||
// 请求失败
|
||||
throw new BusinessException($response->getBody()->getContents());
|
||||
}
|
||||
|
||||
$body = json_decode($response->getBody(), true);
|
||||
Log::getInstance("regulatoryPlatform-httpRequest")->info(json_encode($body,JSON_UNESCAPED_UNICODE));
|
||||
if (empty($body)) {
|
||||
// 返回值为空
|
||||
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
|
||||
}
|
||||
|
||||
// 特殊情况下会返回携带code的数据
|
||||
if (isset($body['code'])) {
|
||||
if (isset($body['message'])) {
|
||||
throw new BusinessException($body['message']);
|
||||
throw new BusinessException((string) $body['message']);
|
||||
}
|
||||
|
||||
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
}
|
||||
|
||||
protected function httpRequestV2(string $path, array $arg = []): array
|
||||
{
|
||||
return $this->sendJsonRequest($path, $arg, 'regulatoryPlatform-httpRequestV2', true);
|
||||
}
|
||||
|
||||
protected function assertV2SuccessResponse(array $response): void
|
||||
{
|
||||
if (isset($response['code']) && (int) $response['code'] !== 0) {
|
||||
throw new BusinessException($this->extractV2ErrorMessage($response));
|
||||
}
|
||||
|
||||
if (isset($response['success']) && $response['success'] !== true) {
|
||||
throw new BusinessException($this->extractV2ErrorMessage($response));
|
||||
}
|
||||
|
||||
if (isset($response['serviceSuccess']) && $response['serviceSuccess'] !== true) {
|
||||
throw new BusinessException($this->extractV2ErrorMessage($response));
|
||||
}
|
||||
}
|
||||
|
||||
protected function extractV2ErrorMessage(array $response): string
|
||||
{
|
||||
if (! empty($response['message']) && is_string($response['message'])) {
|
||||
return $response['message'];
|
||||
}
|
||||
|
||||
if (! empty($response['errors']) && is_array($response['errors'])) {
|
||||
$first = $response['errors'][0] ?? null;
|
||||
if (is_string($first) && $first !== '') {
|
||||
return $first;
|
||||
}
|
||||
if (is_array($first)) {
|
||||
$json = json_encode($first, JSON_UNESCAPED_UNICODE);
|
||||
if ($json !== false) {
|
||||
return $json;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR);
|
||||
}
|
||||
|
||||
protected function buildAuthEndpoint(string $type): string
|
||||
{
|
||||
if (empty($this->api_url)) {
|
||||
throw new BusinessException('Regulatory platform api_url is not configured');
|
||||
}
|
||||
|
||||
if ($type === 'access') {
|
||||
return $this->joinUrl($this->api_url, '/auth-api/oauth2/accessToken');
|
||||
}
|
||||
|
||||
return $this->joinUrl($this->api_url, '/auth-api/oauth2/refreshToken');
|
||||
}
|
||||
|
||||
protected function buildDataUploadEndpoint(): string
|
||||
{
|
||||
if (empty($this->api_url)) {
|
||||
throw new BusinessException('Regulatory platform api_url is not configured');
|
||||
}
|
||||
|
||||
return $this->joinUrl($this->api_url, '/du-api/v1/dataUpload');
|
||||
}
|
||||
|
||||
protected function buildDataUploadRequestUrl(): string
|
||||
{
|
||||
return $this->buildDataUploadEndpoint();
|
||||
}
|
||||
|
||||
protected function sendJsonRequest(string $path, array $arg, string $logChannel, bool $sanitize): array
|
||||
{
|
||||
$option = [
|
||||
'verify' => false,
|
||||
'timeout' => self::HTTP_TIMEOUT,
|
||||
'connect_timeout' => self::HTTP_CONNECT_TIMEOUT,
|
||||
'http_errors' => false,
|
||||
];
|
||||
if (! empty($option)) {
|
||||
$arg = array_merge($arg, $option);
|
||||
}
|
||||
|
||||
$requestLog = $sanitize ? $this->sanitizeLogContext($arg) : $arg;
|
||||
$requestStartAt = microtime(true);
|
||||
Log::getInstance($logChannel)->info(json_encode([
|
||||
'phase' => 'request',
|
||||
'url' => $path,
|
||||
'request' => $requestLog,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$httpLogChannel = 'regulatoryPlatform-http-detail';
|
||||
$requestDetail = $this->buildHttpRequestLogContext($path, $arg, $sanitize);
|
||||
Log::getInstance($httpLogChannel, 'regulatory_platform_http_detail')->info(json_encode([
|
||||
'phase' => 'request',
|
||||
'url' => $path,
|
||||
'request' => $requestDetail,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
try {
|
||||
$response = $this->client->post($path, $arg);
|
||||
$statusCode = $response->getStatusCode();
|
||||
$bodyText = (string) $response->getBody();
|
||||
$body = json_decode($bodyText, true);
|
||||
$durationMs = $this->toDurationMs($requestStartAt);
|
||||
|
||||
$responseLog = $sanitize
|
||||
? $this->sanitizeLogContext(is_array($body) ? $body : ['raw' => $bodyText])
|
||||
: (is_array($body) ? $body : ['raw' => $bodyText]);
|
||||
Log::getInstance($logChannel)->info(json_encode([
|
||||
'phase' => 'response',
|
||||
'status' => $statusCode,
|
||||
'duration_ms' => $durationMs,
|
||||
'response' => $responseLog,
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
Log::getInstance($httpLogChannel, 'regulatory_platform_http_detail')->info(json_encode([
|
||||
'phase' => 'response',
|
||||
'url' => $path,
|
||||
'status' => $statusCode,
|
||||
'duration_ms' => $durationMs,
|
||||
'response' => $this->buildHttpResponseLogContext($response, $bodyText, $body, $sanitize),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
if ($statusCode !== 200) {
|
||||
$message = is_array($body) ? $this->extractV2ErrorMessage($body) : $bodyText;
|
||||
throw new BusinessException(sprintf('Regulatory platform HTTP request failed[%s]: %s', $statusCode, $message));
|
||||
}
|
||||
|
||||
if (! is_array($body) || $body === []) {
|
||||
throw new BusinessException(HttpEnumCode::getMessage(HttpEnumCode::SERVER_ERROR));
|
||||
}
|
||||
|
||||
return $body;
|
||||
} catch (\Throwable $throwable) {
|
||||
Log::getInstance($httpLogChannel, 'regulatory_platform_http_detail')->error(json_encode([
|
||||
'phase' => 'exception',
|
||||
'url' => $path,
|
||||
'duration_ms' => $this->toDurationMs($requestStartAt),
|
||||
'request' => $requestDetail,
|
||||
'exception' => [
|
||||
'message' => $throwable->getMessage(),
|
||||
'file' => $throwable->getFile(),
|
||||
'line' => $throwable->getLine(),
|
||||
],
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
throw $throwable;
|
||||
}
|
||||
}
|
||||
|
||||
protected function buildHttpRequestLogContext(string $url, array $arg, bool $sanitize): array
|
||||
{
|
||||
$query = $arg['query'] ?? [];
|
||||
$headers = $arg['headers'] ?? [];
|
||||
$json = $arg['json'] ?? null;
|
||||
$formParams = $arg['form_params'] ?? null;
|
||||
|
||||
return [
|
||||
'method' => 'POST',
|
||||
'url' => $this->appendQueryString($url, is_array($query) ? $query : []),
|
||||
'headers' => $sanitize ? $this->sanitizeLogContext(is_array($headers) ? $headers : []) : $headers,
|
||||
'query' => $sanitize ? $this->sanitizeLogContext(is_array($query) ? $query : []) : $query,
|
||||
'json' => $sanitize ? $this->sanitizeLogContext($json) : $json,
|
||||
'form_params' => $sanitize ? $this->sanitizeLogContext($formParams) : $formParams,
|
||||
'timeout' => $arg['timeout'] ?? null,
|
||||
'connect_timeout' => $arg['connect_timeout'] ?? null,
|
||||
'verify' => $arg['verify'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
protected function buildHttpResponseLogContext(object $response, string $bodyText, mixed $body, bool $sanitize): array
|
||||
{
|
||||
$headers = [];
|
||||
foreach ($response->getHeaders() as $key => $value) {
|
||||
$headers[$key] = is_array($value) ? implode('; ', $value) : $value;
|
||||
}
|
||||
|
||||
$normalizedBody = is_array($body) ? $body : ['raw' => $bodyText];
|
||||
|
||||
return [
|
||||
'headers' => $sanitize ? $this->sanitizeLogContext($headers) : $headers,
|
||||
'body' => $sanitize ? $this->sanitizeLogContext($normalizedBody) : $normalizedBody,
|
||||
];
|
||||
}
|
||||
|
||||
protected function appendQueryString(string $url, array $query): string
|
||||
{
|
||||
if ($query === []) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$queryString = http_build_query($query);
|
||||
if ($queryString === '') {
|
||||
return $url;
|
||||
}
|
||||
|
||||
return str_contains($url, '?') ? $url . '&' . $queryString : $url . '?' . $queryString;
|
||||
}
|
||||
|
||||
protected function toDurationMs(float $requestStartAt): int
|
||||
{
|
||||
return (int) round((microtime(true) - $requestStartAt) * 1000);
|
||||
}
|
||||
|
||||
protected function logBusinessRequest(string $action, string $url, array $context): void
|
||||
{
|
||||
Log::getInstance('regulatoryPlatform-business', 'regulatory_platform_business')->info(json_encode([
|
||||
'phase' => 'request',
|
||||
'action' => $action,
|
||||
'url' => $url,
|
||||
'context' => $this->sanitizeLogContext($context),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
protected function logBusinessResponse(string $action, string $url, array $context): void
|
||||
{
|
||||
Log::getInstance('regulatoryPlatform-business', 'regulatory_platform_business')->info(json_encode([
|
||||
'phase' => 'response',
|
||||
'action' => $action,
|
||||
'url' => $url,
|
||||
'context' => $this->sanitizeLogContext($context),
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
protected function logBusinessException(string $action, string $url, \Throwable $throwable, array $context = []): void
|
||||
{
|
||||
Log::getInstance('regulatoryPlatform-business', 'regulatory_platform_business')->error(json_encode([
|
||||
'phase' => 'exception',
|
||||
'action' => $action,
|
||||
'url' => $url,
|
||||
'context' => $this->sanitizeLogContext($context),
|
||||
'exception' => [
|
||||
'message' => $throwable->getMessage(),
|
||||
'file' => $throwable->getFile(),
|
||||
'line' => $throwable->getLine(),
|
||||
],
|
||||
], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
protected function sanitizeLogContext(mixed $payload): mixed
|
||||
{
|
||||
if (! is_array($payload)) {
|
||||
if (is_string($payload) && strlen($payload) > 160) {
|
||||
return substr($payload, 0, 24) . '...' . substr($payload, -24);
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
$masked = [];
|
||||
foreach ($payload as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$masked[$key] = $this->sanitizeLogContext($value);
|
||||
continue;
|
||||
}
|
||||
|
||||
$keyString = strtolower((string) $key);
|
||||
if (in_array($keyString, ['sign', 'data', 'accesstoken', 'refreshtoken', 'client_secret', 'key'], true)) {
|
||||
if (is_string($value)) {
|
||||
$masked[$key] = [
|
||||
'len' => strlen($value),
|
||||
'preview' => strlen($value) > 16 ? substr($value, 0, 8) . '...' . substr($value, -8) : $value,
|
||||
];
|
||||
} else {
|
||||
$masked[$key] = '[masked]';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$masked[$key] = $value;
|
||||
}
|
||||
|
||||
return $masked;
|
||||
}
|
||||
|
||||
protected function normalizeUploadRows(array $rows): array
|
||||
{
|
||||
$normalized = [];
|
||||
foreach ($rows as $row) {
|
||||
if (! is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($row['accessToken'], $row['clientId']);
|
||||
if (! isset($row['client_id']) || ! is_string($row['client_id']) || trim($row['client_id']) === '') {
|
||||
$row['client_id'] = (string) $this->client_id;
|
||||
}
|
||||
|
||||
if (array_key_exists('patientSex', $row)) {
|
||||
$row['patientSex'] = $this->normalizePatientSex(
|
||||
$row['patientSex'],
|
||||
isset($row['patientIdcardType']) ? (int) $row['patientIdcardType'] : null,
|
||||
isset($row['patientIdcardNum']) && is_scalar($row['patientIdcardNum']) ? (string) $row['patientIdcardNum'] : null
|
||||
);
|
||||
}
|
||||
|
||||
$normalized[] = $row;
|
||||
}
|
||||
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
protected function normalizePatientSex(mixed $patientSex, ?int $patientIdcardType = null, ?string $patientIdcardNum = null): int
|
||||
{
|
||||
$inferredSex = $this->inferPatientSexFromIdCard($patientIdcardType, $patientIdcardNum);
|
||||
if ($inferredSex !== null) {
|
||||
return $inferredSex;
|
||||
}
|
||||
|
||||
$patientSex = (int) $patientSex;
|
||||
|
||||
return in_array($patientSex, [1, 2], true) ? $patientSex : 0;
|
||||
}
|
||||
|
||||
protected function inferPatientSexFromIdCard(?int $patientIdcardType, ?string $patientIdcardNum): ?int
|
||||
{
|
||||
if ($patientIdcardType !== 1 || empty($patientIdcardNum)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$patientIdcardNum = strtoupper(trim($patientIdcardNum));
|
||||
if (preg_match('/^\d{17}[\dX]$/', $patientIdcardNum) === 1) {
|
||||
return ((int) $patientIdcardNum[16]) % 2 === 1 ? 1 : 2;
|
||||
}
|
||||
|
||||
if (preg_match('/^\d{15}$/', $patientIdcardNum) === 1) {
|
||||
return ((int) $patientIdcardNum[14]) % 2 === 1 ? 1 : 2;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function formatV2UploadErrors(array $errors): string
|
||||
{
|
||||
$messages = [];
|
||||
foreach ($errors as $error) {
|
||||
$tableName = isset($error['tableName']) && is_string($error['tableName']) ? $error['tableName'] : '';
|
||||
$rowErrors = $error['errors'] ?? [];
|
||||
if (! is_array($rowErrors) || $rowErrors === []) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$message = implode('; ', array_map(static function (mixed $item): string {
|
||||
if (is_string($item)) {
|
||||
return $item;
|
||||
}
|
||||
|
||||
$json = json_encode($item, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
return $json === false ? '[invalid error payload]' : $json;
|
||||
}, $rowErrors));
|
||||
|
||||
$messages[] = $tableName !== '' ? sprintf('%s: %s', $tableName, $message) : $message;
|
||||
}
|
||||
|
||||
if ($messages === []) {
|
||||
return 'Regulatory platform upload validation failed';
|
||||
}
|
||||
|
||||
return implode(' | ', $messages);
|
||||
}
|
||||
|
||||
protected function buildClientCredentialQuery(): array
|
||||
{
|
||||
return array_filter([
|
||||
'client_id' => $this->client_id,
|
||||
'client_secret' => $this->client_secret,
|
||||
], static fn (?string $value): bool => is_string($value) && $value !== '');
|
||||
}
|
||||
|
||||
protected function joinUrl(string $baseUrl, string $path): string
|
||||
{
|
||||
return rtrim($baseUrl, '/') . '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
protected function resolveConfigValue(array $paths): ?string
|
||||
{
|
||||
foreach ($paths as $path) {
|
||||
$value = $this->getByPath($this->config, $path);
|
||||
if ($value !== null && $value !== '') {
|
||||
return (string) $value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function getByPath(array $config, string $path): mixed
|
||||
{
|
||||
$current = $config;
|
||||
foreach (explode('.', $path) as $segment) {
|
||||
if (! is_array($current) || ! array_key_exists($segment, $current)) {
|
||||
return null;
|
||||
}
|
||||
$current = $current[$segment];
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,14 +59,16 @@ class WechatPay
|
||||
$this->pay_notify_url = $this->config['product_pay_notify_url'];
|
||||
$this->refund_notify_url = $this->config['product_refund_notify_url'];
|
||||
|
||||
$this->pay_config = config("we_chat.applets.pay.1636644248");
|
||||
// 之前为北京欣欣账户,现改为成都欣欣账户
|
||||
$this->pay_config = config("we_chat.applets.pay.1659662936");
|
||||
}
|
||||
|
||||
if ($order_type == 3){
|
||||
$this->pay_notify_url = $this->config['detection_pay_notify_url'];
|
||||
$this->refund_notify_url = $this->config['detection_refund_notify_url'];
|
||||
|
||||
$this->pay_config = config("we_chat.applets.pay.1636644248");
|
||||
// 之前为北京欣欣账户,现改为成都欣欣账户
|
||||
$this->pay_config = config("we_chat.applets.pay.1659662936");
|
||||
}
|
||||
|
||||
if ($order_type == 4){
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
ALTER TABLE `order_prescription_file`
|
||||
ADD COLUMN `doctor_ca_supplier` varchar(16) NOT NULL DEFAULT '' COMMENT '医生CA供应商' AFTER `doctor_ca_file_id`,
|
||||
ADD COLUMN `doctor_ca_sign_type` varchar(16) NOT NULL DEFAULT '' COMMENT '医生CA签名类型' AFTER `doctor_ca_supplier`,
|
||||
ADD COLUMN `doctor_ca_sign_originaltext` text COMMENT '医生CA签名原文' AFTER `doctor_ca_sign_type`,
|
||||
ADD COLUMN `doctor_ca_sign_text` longtext COMMENT '医生CA签名值' AFTER `doctor_ca_sign_originaltext`,
|
||||
ADD COLUMN `doctor_ca_sign_time` longtext COMMENT '医生CA时间戳签名值' AFTER `doctor_ca_sign_text`,
|
||||
ADD COLUMN `pharmacist_ca_supplier` varchar(16) NOT NULL DEFAULT '' COMMENT '药师CA供应商' AFTER `doctor_ca_sign_time`,
|
||||
ADD COLUMN `pharmacist_ca_sign_type` varchar(16) NOT NULL DEFAULT '' COMMENT '药师CA签名类型' AFTER `pharmacist_ca_supplier`,
|
||||
ADD COLUMN `pharmacist_ca_sign_originaltext` text COMMENT '药师CA签名原文' AFTER `pharmacist_ca_sign_type`,
|
||||
ADD COLUMN `pharmacist_ca_sign_text` longtext COMMENT '药师CA签名值' AFTER `pharmacist_ca_sign_originaltext`,
|
||||
ADD COLUMN `pharmacist_ca_sign_time` longtext COMMENT '药师CA时间戳签名值' AFTER `pharmacist_ca_sign_text`;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `order_prescription_product`
|
||||
ADD COLUMN `product_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '开方时商品单价' AFTER `product_name`;
|
||||
@@ -0,0 +1,140 @@
|
||||
-- ==============================================================================
|
||||
-- 数据库变更脚本:多药房独立药品与库存隔离方案
|
||||
-- 适用系统:hospital-applets-api
|
||||
-- 执行说明:
|
||||
-- 1. 本脚本默认表前缀为 `gdxz_`,若数据库未配置表前缀,请全局替换删除 `gdxz_`。
|
||||
-- 2. 请在业务低峰期执行,按顺序分批执行:第一部分(新建表)-> 第二部分(修改表)-> 第三部分(数据回填初始化)。
|
||||
-- 3. 脚本具有幂等性设计(IF NOT EXISTS / ON DUPLICATE KEY UPDATE)。
|
||||
-- ==============================================================================
|
||||
|
||||
-- ==============================================================================
|
||||
-- 第一部分:新建两张药房核心表
|
||||
-- ==============================================================================
|
||||
|
||||
-- 1.1 药房基础信息表(gdxz_pharmacy)
|
||||
CREATE TABLE IF NOT EXISTS `gdxz_pharmacy` (
|
||||
`pharmacy_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '药房ID(雪花算法ID,0表示系统默认/老数据)',
|
||||
`pharmacy_name` varchar(128) NOT NULL DEFAULT '' COMMENT '药房名称',
|
||||
`pharmacy_code` varchar(64) NOT NULL DEFAULT '' COMMENT '药房编码(对接第三方药房的thirdCode/pharmacyCode)',
|
||||
`postage` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '基础运费/邮费(单位:元)',
|
||||
`free_shipping_threshold` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '满额包邮门槛(单位:元,0表示不免邮)',
|
||||
`is_pickup` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否支持到店自提(0:不支持 1:支持)',
|
||||
`telephone` varchar(32) NOT NULL DEFAULT '' COMMENT '药房联系电话',
|
||||
`province` varchar(64) NOT NULL DEFAULT '' COMMENT '省份名称',
|
||||
`city` varchar(64) NOT NULL DEFAULT '' COMMENT '城市名称',
|
||||
`county` varchar(64) NOT NULL DEFAULT '' COMMENT '区县名称',
|
||||
`address` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址',
|
||||
`pickup_address` varchar(255) NOT NULL DEFAULT '' COMMENT '自提详细地址',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0:禁用 1:启用)',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`pharmacy_id`),
|
||||
UNIQUE KEY `uk_pharmacy_code` (`pharmacy_code`),
|
||||
KEY `idx_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='药房基础信息表';
|
||||
|
||||
-- 1.2 医生-药房关联表(gdxz_doctor_pharmacy)
|
||||
CREATE TABLE IF NOT EXISTS `gdxz_doctor_pharmacy` (
|
||||
`doctor_pharmacy_id` bigint(20) unsigned NOT NULL COMMENT '关联主键ID(雪花算法ID)',
|
||||
`doctor_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '医生ID',
|
||||
`pharmacy_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '药房ID',
|
||||
`is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否默认药房(0:否 1:是)',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0:禁用/解绑 1:正常)',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`doctor_pharmacy_id`),
|
||||
UNIQUE KEY `uk_doctor_pharmacy` (`doctor_id`, `pharmacy_id`),
|
||||
KEY `idx_doctor_default` (`doctor_id`, `is_default`, `status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='医生-药房绑定关联表';
|
||||
|
||||
|
||||
-- ==============================================================================
|
||||
-- 第二部分:现有业务表字段扩展(ALTER TABLE)
|
||||
-- ==============================================================================
|
||||
|
||||
-- 2.1 处方主表增加药房快照字段(gdxz_order_prescription)
|
||||
ALTER TABLE `gdxz_order_prescription`
|
||||
ADD COLUMN `pharmacy_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '开方药房ID' AFTER `doctor_advice`,
|
||||
ADD COLUMN `pharmacy_code` varchar(64) NOT NULL DEFAULT '' COMMENT '开方药房编码' AFTER `pharmacy_id`,
|
||||
ADD COLUMN `pharmacy_name` varchar(128) NOT NULL DEFAULT '' COMMENT '开方药房名称' AFTER `pharmacy_code`,
|
||||
ADD KEY `idx_pharmacy_code` (`pharmacy_code`);
|
||||
|
||||
-- 2.2 药品订单表增加药房快照及配送类型字段(gdxz_order_product)
|
||||
ALTER TABLE `gdxz_order_product`
|
||||
ADD COLUMN `pharmacy_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '所属药房ID' AFTER `logistics_fee`,
|
||||
ADD COLUMN `pharmacy_code` varchar(64) NOT NULL DEFAULT '' COMMENT '所属药房编码' AFTER `pharmacy_id`,
|
||||
ADD COLUMN `pharmacy_name` varchar(128) NOT NULL DEFAULT '' COMMENT '所属药房名称' AFTER `pharmacy_code`,
|
||||
ADD COLUMN `delivery_type` tinyint(1) NOT NULL DEFAULT '2' COMMENT '配送方式(1:到店自提 2:快递配送)' AFTER `pharmacy_name`,
|
||||
ADD KEY `idx_pharmacy_code` (`pharmacy_code`),
|
||||
ADD KEY `idx_delivery_type` (`delivery_type`);
|
||||
|
||||
-- 2.3 药品表增加所属药房隔离字段(gdxz_product)
|
||||
ALTER TABLE `gdxz_product`
|
||||
ADD COLUMN `pharmacy_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '所属药房ID' AFTER `product_platform_id`,
|
||||
ADD COLUMN `pharmacy_code` varchar(64) NOT NULL DEFAULT '' COMMENT '所属药房编码' AFTER `pharmacy_id`,
|
||||
ADD KEY `idx_pharmacy_code` (`pharmacy_code`);
|
||||
|
||||
-- 2.4 处方平台药品目录表增加第三方药房编码(gdxz_product_platform)
|
||||
ALTER TABLE `gdxz_product_platform`
|
||||
ADD COLUMN `pharmacy_code` varchar(64) NOT NULL DEFAULT '' COMMENT '第三方药房编码' AFTER `product_pharmacy_code`,
|
||||
ADD KEY `idx_pharmacy_code` (`pharmacy_code`);
|
||||
|
||||
|
||||
-- ==============================================================================
|
||||
-- 第三部分:历史数据回填与初始化(DML)
|
||||
-- ==============================================================================
|
||||
|
||||
-- 3.1 初始化系统默认药房记录(pharmacy_id = 0,编码 ZD-10198)
|
||||
INSERT INTO `gdxz_pharmacy` (
|
||||
`pharmacy_id`, `pharmacy_name`, `pharmacy_code`,
|
||||
`postage`, `free_shipping_threshold`, `is_pickup`,
|
||||
`telephone`, `province`, `city`, `county`, `address`, `pickup_address`,
|
||||
`status`, `created_at`, `updated_at`
|
||||
) VALUES (
|
||||
0, '总院默认药房', 'ZD-10198',
|
||||
7.00, 0.00, 1,
|
||||
'010-87573022', '北京市', '北京市', '海淀区', '医疗机构总院院内药房', '医疗机构总院门诊楼1层药房自提窗口',
|
||||
1, NOW(), NOW()
|
||||
) ON DUPLICATE KEY UPDATE
|
||||
`pharmacy_code` = VALUES(`pharmacy_code`),
|
||||
`pharmacy_name` = VALUES(`pharmacy_name`),
|
||||
`status` = 1;
|
||||
|
||||
-- 3.2 回填现有药品表(gdxz_product)的药房归属为默认药房
|
||||
UPDATE `gdxz_product`
|
||||
SET `pharmacy_id` = 0, `pharmacy_code` = 'ZD-10198'
|
||||
WHERE `pharmacy_code` = '' OR `pharmacy_code` IS NULL;
|
||||
|
||||
-- 3.3 回填处方平台目录表(gdxz_product_platform)的药房编码
|
||||
UPDATE `gdxz_product_platform`
|
||||
SET `pharmacy_code` = 'ZD-10198'
|
||||
WHERE `pharmacy_code` = '' OR `pharmacy_code` IS NULL;
|
||||
|
||||
-- 3.4 回填历史处方表(gdxz_order_prescription)快照数据
|
||||
UPDATE `gdxz_order_prescription`
|
||||
SET `pharmacy_id` = 0, `pharmacy_code` = 'ZD-10198', `pharmacy_name` = '总院默认药房'
|
||||
WHERE `pharmacy_code` = '' OR `pharmacy_code` IS NULL;
|
||||
|
||||
-- 3.5 回填历史药品订单表(gdxz_order_product)快照与配送方式(历史订单默认为快递=2)
|
||||
UPDATE `gdxz_order_product`
|
||||
SET `pharmacy_id` = 0, `pharmacy_code` = 'ZD-10198', `pharmacy_name` = '总院默认药房', `delivery_type` = 2
|
||||
WHERE `pharmacy_code` = '' OR `pharmacy_code` IS NULL;
|
||||
|
||||
-- 3.6 为现有所有正常在职的医生,初始化绑定默认药房(避免上线后老医生无法开方)
|
||||
INSERT IGNORE INTO `gdxz_doctor_pharmacy` (
|
||||
`doctor_pharmacy_id`, `doctor_id`, `pharmacy_id`, `is_default`, `status`, `created_at`, `updated_at`
|
||||
)
|
||||
SELECT
|
||||
-- 结合时间戳与医生ID生成唯一大整数ID
|
||||
(UNIX_TIMESTAMP(NOW()) * 1000000 + (d.doctor_id % 1000000)) AS `doctor_pharmacy_id`,
|
||||
d.doctor_id,
|
||||
0 AS `pharmacy_id`,
|
||||
1 AS `is_default`,
|
||||
1 AS `status`,
|
||||
NOW() AS `created_at`,
|
||||
NOW() AS `updated_at`
|
||||
FROM `gdxz_user_doctor` d
|
||||
WHERE d.status = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `gdxz_doctor_pharmacy` dp WHERE dp.doctor_id = d.doctor_id
|
||||
);
|
||||
Reference in New Issue
Block a user