540 lines
18 KiB
PHP
540 lines
18 KiB
PHP
<?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'];
|
||
}
|
||
} |