wucongxing8150 b72110fd2a
Some checks failed
Build Docker / build (push) Has been cancelled
11133
2025-08-15 14:07:04 +08:00

540 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Services;
use App\Exception\BusinessException;
use App\Model\HospitalDepartmentCustom;
use App\Model\OrderInquiryCase;
use App\Model\OrderPrescriptionIcd;
use App\Model\OrderPrescriptionProduct;
use App\Model\UserCaCert;
use App\Model\UserDoctor;
use App\Model\UserDoctorInfo;
use App\Model\UserPharmacistInfo;
use Extend\Alibaba\Oss;
use Extend\Ca\CaOnline;
use Hyperf\Utils\WaitGroup;
use Intervention\Image\ImageManager;
use Swoole\Coroutine\Channel;
use TCPDF;
/**
* CA平台
*/
class CaService extends BaseService
{
// 疾病名称
protected string $icd_name;
// 处方关联商品
protected array $order_prescription_product;
// 获取医生自定义科室名称
protected string $department_name;
// 过敏史
protected string $allergy_history;
// 是否已添加签章配置(第一次需申请)
protected string $is_sign_config;
// 签名图片地址
public string $sign_image_path;
// 唯一标识
protected string $entity_id;
// 证书序列号
protected string $cert_serial_number;
// 身份证号/信用代码
protected string $card_num;
// 处方pdf本地地址
protected string $prescription_pdf_local_path;
// 处方pdf oss地址
protected string $prescription_pdf_oss_path;
/**
* 初始化类,此处会获取基础数据
* @param array|object $order_prescription 处方表数据
* @param string|int $user_id 用户id
* @param int $type 类型 1医院 2医生 3药师
*/
public function __construct(array|object $order_prescription,int $type,string|int $user_id = ""){
// 获取用户、医院签名图片地址、用户标识信息
if ($type == 1){
// 医院
$this->sign_image_path = "basic/file/hospital_signature.png";
$this->entity_id = "5345345461";
$this->card_num = "91510106MABTJY4K9R";
}elseif($type == 2){
// 用户
$params = array();
$params['user_id'] = $user_id;
$user_doctor_info = UserDoctorInfo::getOne($params);
if (empty($user_doctor_info)) {
throw new BusinessException("用户数据错误");
}
$this->sign_image_path = substr($user_doctor_info['sign_image'], 1, strlen($user_doctor_info['sign_image']) - 1);
$app_env = config('app_env','dev');
if ($app_env == 'prod'){
$this->entity_id = $user_doctor_info['user_id'];
}else{
$this->entity_id = "491925054435950592";
}
$this->card_num = $user_doctor_info['card_num'];
} elseif ($type == 3){
$params = array();
$params['user_id'] = $user_id;
$user_pharmacist_info = UserPharmacistInfo::getOne($params);
if (empty($user_pharmacist_info)) {
throw new BusinessException("用户数据错误");
}
// 去除用户签名图片第一个/ oss不识别
$this->sign_image_path = substr($user_pharmacist_info['sign_image'], 1, strlen($user_pharmacist_info['sign_image']) - 1);
$this->entity_id = $user_id;
$this->card_num = $user_pharmacist_info['card_num'];
} else{
throw new BusinessException("用户类型错误");
}
if (empty($this->sign_image_path) || empty($this->entity_id)) {
throw new BusinessException("无签名图片/用户唯一标识");
}
$order_prescription_id = $order_prescription['order_prescription_id'];
// 创建协程客户端
$wg = new WaitGroup();
// 创建通道
$channel = new Channel();
$wg->add(2);
// 获取医生自定义科室名称
$doctor_id = $order_prescription['doctor_id'];
co(function () use ($wg,$channel,$doctor_id) {
defer(function() use ($wg) {
$wg->done();
});
// 获取医生数据
$params = array();
$params['doctor_id'] = $doctor_id;
$user_doctor = UserDoctor::getOne($params);
if (empty($user_doctor)) {
$channel->push('医生数据错误');
return;
}
// 获取医生自定义科室数据
$params = array();
$params['department_custom_id'] = $user_doctor['department_custom_id'];
$hospital_department_custom = HospitalDepartmentCustom::getOne($params);
if (empty($hospital_department_custom)) {
$channel->push('医生自定义数据错误');
return;
}
$this->department_name = $hospital_department_custom['department_name'] ?: "";
});
// 获取过敏史
$order_inquiry_id = $order_prescription['order_inquiry_id'];
co(function () use ($wg,$channel,$order_inquiry_id) {
defer(function() use ($wg) {
$wg->done();
});
$params = array();
$params['order_inquiry_id'] = $order_inquiry_id;
$order_inquiry_case = OrderInquiryCase::getOne($params);
if (empty($order_inquiry_case)) {
$channel->push('处方病例数据错误');
return;
}
$this->allergy_history = $order_inquiry_case['allergy_history'] ?: "";
});
// 结束
$wg->wait();
// 判断通道是否存在异常数据
$res = $channel->isEmpty();
if (!$res){
// 读取通道数据
$data = $channel->pop();
// 关闭channel通道
$channel->close();
throw new BusinessException($data);
}else{
// 关闭channel通道
$channel->close();
}
// 获取处方关联疾病名称
$params = array();
$params['order_prescription_id'] = $order_prescription_id;
$order_prescription_icd = OrderPrescriptionIcd::getList($params);
if (empty($order_prescription_icd)) {
throw new BusinessException("处方疾病数据错误");
}
$this->icd_name = "";
$icd_name = array_column($order_prescription_icd->toArray(), 'icd_name');
if (!empty($icd_name)) {
if (count($icd_name) > 1) {
$this->icd_name = implode(';', $icd_name);
} else {
$this->icd_name = $icd_name[0];
}
}
// 获取处方药品数据
$params = array();
$params['order_prescription_id'] = $order_prescription_id;
$order_prescription_product = OrderPrescriptionProduct::getList($params);
if (empty($order_prescription_product)) {
throw new BusinessException("处方药品数据错误");
}
$this->order_prescription_product = $order_prescription_product->toArray();
// 处方pdf本地地址
$this->prescription_pdf_local_path = "./runtime/file/prescription/" . $order_prescription_id . '.pdf';
// 处方pdf oss地址
$this->prescription_pdf_oss_path = "applet/prescription/" . $order_prescription_id. '.pdf';
}
/**
* 获取云证书签名+验证云证书签名
* @param array|object $order_prescription
*/
public function getVerifyCertSign(array|object $order_prescription)
{
$CaOnline = new CaOnline();
// 获取云证书签名
$data = array();
$data['created_at'] = $order_prescription['doctor_created_time'];
$data['department_custom_name'] = $this->department_name;
$data['user_name'] = $order_prescription['patient_name'];
$data['sex'] = sexToStringSex($order_prescription['patient_sex']);
$data['age'] = $order_prescription['patient_age'];
$data['allergy_history'] = $this->allergy_history;
$data['icd_name'] = $this->icd_name;
$data['doctor_advice'] = $order_prescription['doctor_advice'] ?: "";
// 商品数据
$data['product'] = array();
foreach ($this->order_prescription_product as $item) {
$product = array();
$product['product_name'] = $item['product_name'] . "(" . $item['product_spec'] . ")"; // 商品名称+商品规格
$product['single_unit'] = $item['single_unit'] ?: ""; // 单次剂量1次1包
$product['frequency_use'] = $item['frequency_use'] ?: ""; // 使用频率(例1天3次)
$product['single_use'] = $item['single_use'] ?: ""; // 单次用法(例:口服)
$product['prescription_product_num'] = $item['prescription_product_num'] . $item['packaging_unit']; // 商品数量 + 基本包装单位(例:盒/瓶)
$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'];
}
/**
* 生成处方图片+处方图片生成pdf
* @param array|object $order_prescription
* @return string
*/
public function createPrescriptionImgPdf(array|object $order_prescription): string
{
// 打开基础处方图片
$prescription_image = fopen("./extend/Ca/prescription.jpg", 'r+');
$manager = new ImageManager();
$image = $manager->make($prescription_image);
$fontPath = './extend/Ca/msyh.ttf';
// 处方号
$image->text($order_prescription['prescription_code'], 1480, 540, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(60);
$font->align('left');
});
// 日期
$image->text(date('Y-m-d', strtotime($order_prescription['doctor_created_time'])), 354, 675, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(60);
$font->align('left');
});
// 科室
$image->text($this->department_name ?: "", 1385, 675, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(60);
$font->align('left');
});
// 姓名
$image->text($order_prescription['patient_name'], 354, 795, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(60);
$font->align('left');
});
// 性别
$image->text(sexToStringSex($order_prescription['patient_sex']), 1385, 790, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(60);
$font->align('left');
});
// 年龄
$image->text($order_prescription['patient_age'], 354, 900, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(60);
$font->align('left');
});
// 过敏史
$image->text($this->allergy_history ?: "", 405, 1030, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(60);
$font->align('left');
});
// 初步诊断
$image->text($this->icd_name, 445, 1145, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(60);
$font->align('left');
});
// 医生建议
$image->text($order_prescription['doctor_advice'], 445, 1252, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(60);
$font->align('left');
});
// 商品数据
foreach ($this->order_prescription_product as $key => $item) {
$x_axis = 229;
$y_axis = 1600 + $key * 250;
$x_axis_num = 1900;// 数量使用
// 商品名称
$image->text($item['product_name'], $x_axis, $y_axis, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(50);
$font->align('left');
});
$image->text("X" . $item['prescription_product_num'] . $item['packaging_unit'], $x_axis_num, $y_axis, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(50);
$font->align('left');
});
// 用量
$image->text("用量:" . $item['single_unit'] . " " . $item['frequency_use'], $x_axis, $y_axis + 70, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(50);
$font->align('left');
});
// 用法
$image->text("用法:" . $item['single_use'], $x_axis, $y_axis + 140, function ($font) use ($fontPath) {
$font->file($fontPath);
$font->size(50);
$font->align('left');
});
}
// 生成图片
$img_result = (string)$image->encode('png', 75);
// 上传处方图片至oss
$oss = new Oss();
$prescription_img_oss_filename = "applet/prescription/" . $order_prescription['order_prescription_id'] . '.' . 'jpg';
$prescription_img_oss_path = $oss->putObject($prescription_img_oss_filename, $img_result);
$prescription_img_oss_path = '/' . $prescription_img_oss_path;
// 图片生成pdf
$pdf = new TCPDF('P', 'mm', 'A4', true, 'UTF-8', false);
$pdf->AddPage();
$pdf->Image('@' . $img_result, 10, 10, 0, 0, '', '', '', false, 300, '', false, false, 0, false, false, false);
$prescription_pdf_local_filename = $order_prescription['order_prescription_id'] . '.' . 'pdf';
$prescription_pdf_local_path = BASE_PATH . "/runtime/file/prescription/";
$res = mkdirs($prescription_pdf_local_path);
if (!$res){
throw new BusinessException("处方临时目录创建失败");
}
// 图片生成的处方pdf存储为本地文件 runtime目录下
$pdf->Output($prescription_pdf_local_path . $prescription_pdf_local_filename, "F");
return $prescription_img_oss_path;
}
/**
* 下载Oss中处方pdf
* @return void
*/
public function downOssPdfToLocal(): void
{
$oss = new Oss();
$oss->getObjectToLocal($this->prescription_pdf_oss_path, $this->prescription_pdf_local_path);
}
/**
* 下载CA中pdf至本地文件
* @param string $file_id
* @return void
*/
public function downCaPdfToLocal(string $file_id): void
{
$CaOnline = new CaOnline();
$prescription_pdf_result = $CaOnline->getSignedFile($this->entity_id, $file_id);
$file = fopen($this->prescription_pdf_local_path, "w");
fwrite($file, $prescription_pdf_result);
fclose($file);
}
/**
* 下载CA中的pdf并上传至oss
* @param string $file_id
* @return string oss地址
*/
public function downCaPdfToOss(string $file_id): string
{
$CaOnline = new CaOnline();
$prescription_pdf_result = $CaOnline->getSignedFile($this->entity_id, $file_id);
// 上传oss
$oss = new Oss();
return $oss->putObject($this->prescription_pdf_oss_path, $prescription_pdf_result);
}
/**
* 进行处方pdf签章
* @param string|int $type 类型 1医院 2医生 3药师
* @return string 文件id
*/
public function addSignPdf(string|int $type): string
{
$oss = new Oss();
if ($type == 1){
$style = "image/resize,w_300,h_300";
}else{
$style = "image/resize,m_lfit,w_100,h_350";
}
$sign_image = $oss->getCusTomObjectToRAM($this->sign_image_path, $style);
$sign_image = base64_encode($sign_image);
if (!$sign_image) {
throw new BusinessException("签名图片下载失败");
}
if ($type == 1){
// 医院
$llx = "370"; // 左边底部X坐标 控制左右(越小越左)
$lly = "210"; // 左边底部Y坐标 控制上下(越小越下)
$urx = "520"; // 右边上部x坐标
$ury = "360"; // 右边上部y坐标
} elseif ($type == 2){
// 医生端
$llx = "120"; // 左边底部X坐标
$lly = "190"; // 左边底部Y坐标
$urx = "190"; // 右边上部x坐标
$ury = "140"; // 右边上部y坐标
}else{
// 药师端
$llx = "350"; // 左边底部X坐标
$lly = "190"; // 左边底部Y坐标
$urx = "440"; // 右边上部x坐标
$ury = "140"; // 右边上部y坐标
}
$sign_param = [
[
"llx" => $llx, // 左边底部X坐标
"lly" => $lly, // 左边底部Y坐标
"urx" => $urx, // 右边上部x坐标
"ury" => $ury, // 右边上部y坐标
"pageList" => [1],
"sealImg" => $sign_image
],
];
$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("未申请证书");
}
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);
$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);
}
// 打开处方pdf文件
$pdf_file = fopen($this->prescription_pdf_local_path, 'r');
if (!$pdf_file) {
throw new BusinessException("处方pdf打开失败");
}
// 处方pdf进行签章
$data = array();
$data['sign_param'] = json_encode($sign_param);
$data['pdf_file'] = $pdf_file;
$sign_pdf_result = $CaOnline->addSignPdf($this->entity_id, $data);
if (empty($sign_pdf_result[0]['fileId'])) {
throw new BusinessException("处方签章失败");
}
return $sign_pdf_result[0]['fileId'];
}
}