LinkService.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. <?php
  2. namespace app\api\servies\link;
  3. use app\api\dao\link\LinkDao;
  4. use app\api\servies\ZskkDefaultService;
  5. use app\api\utils\UUIDUtils;
  6. use think\facade\Cache;
  7. use think\facade\Config;
  8. use think\facade\Log;
  9. use think\Db;
  10. use think\db\exception;
  11. /**
  12. * 后台控制器基类
  13. * 接口方法权限 必传参数 接口返回 错误抛出 通用参数处理
  14. */
  15. class LinkService extends ZskkDefaultService {
  16. protected $logName = "LinkService";
  17. private $linkDao = null;
  18. public function __construct(LinkDao $linkDao) {
  19. parent::__construct();
  20. $this->linkDao = $linkDao;
  21. }
  22. public function getPacsInstitutionExam()
  23. {
  24. $institution = $this->linkDao->getAllIns();
  25. $allIns = [];
  26. foreach ($institution as $k=>$v)
  27. {
  28. $allIns[$v['id']] = $v['name'];
  29. }
  30. $exam = $this->linkDao->getinsExam();
  31. $date = '20260420';
  32. $today = strtotime(date('Ymd',time()));
  33. $target = strtotime($date);
  34. $diffDays = ($today - $target) / (60 * 60 * 24);
  35. $exam[] = ['num'=>$diffDays*157,'institution_id'=>'06700001'];
  36. rsort($exam);
  37. $arr = [];
  38. foreach ($exam as $k=>$v)
  39. {
  40. if(empty($allIns[$v['institution_id']] ?? ''))
  41. {
  42. continue;
  43. }
  44. $arr[] = ['name'=>$allIns[$v['institution_id']] ,'num'=> $v['num']];
  45. }
  46. return $arr;
  47. }
  48. public function getPacsAgeExam()
  49. {
  50. $data = $this->linkDao->getPacsAgeExam();
  51. $arr = $this->statisticsAge($data);
  52. return $arr;
  53. }
  54. public function getPacsDateExam()
  55. {
  56. $data = $this->linkDao->getPacsDateExam();
  57. return $data;
  58. }
  59. /**
  60. * 统计年龄分布
  61. */
  62. function statisticsAge($data) {
  63. $ageStats = [];
  64. foreach ($data as $row) {
  65. $age = $this->normalizeAge($row['age']);
  66. $count = (int)$row['row_cnt'];
  67. // 跳过无效年龄
  68. if ($age === null) {
  69. continue;
  70. }
  71. // 累加相同年龄的数量
  72. if (!isset($ageStats[$age])) {
  73. $ageStats[$age] = 0;
  74. }
  75. $ageStats[$age] += $count;
  76. }
  77. // 转换为所需格式并按年龄排序
  78. $result = [];
  79. ksort($ageStats);
  80. foreach ($ageStats as $age => $count) {
  81. $result[] = [
  82. 'age' => $age,
  83. 'count' => $count
  84. ];
  85. }
  86. return $result;
  87. }
  88. function normalizeAge($ageStr) {
  89. if (empty($ageStr) || $ageStr === 'NULL' || $ageStr === '不详') {
  90. return null;
  91. }
  92. $ageStr = trim($ageStr);
  93. // 处理 Y 结尾的年龄(如 27Y, 83Y)
  94. if (preg_match('/^(\d+)Y$/i', $ageStr, $matches)) {
  95. return (int)$matches[1];
  96. }
  97. // 处理"岁"结尾的年龄(如 31岁, 72岁)
  98. if (preg_match('/^(\d+)岁/', $ageStr, $matches)) {
  99. return (int)$matches[1];
  100. }
  101. // 处理小数格式(如 74.00, 64.00)
  102. if (preg_match('/^(\d+)\.?\d*$/', $ageStr, $matches)) {
  103. return (int)$matches[1];
  104. }
  105. // 处理纯数字(如 48, 179)
  106. if (is_numeric($ageStr)) {
  107. return (int)$ageStr;
  108. }
  109. // 处理月份(如 3月28天, 10M)- 转换为 0 岁
  110. if (preg_match('/月|M$/i', $ageStr)) {
  111. return 0;
  112. }
  113. // 处理天数或小时(如 19天, 0小时19分钟)- 转换为 0 岁
  114. if (preg_match('/天|小时|分钟|D$/i', $ageStr)) {
  115. return 0;
  116. }
  117. // 其他无法识别的格式返回 null
  118. return null;
  119. }
  120. public function getPacsCount()
  121. {
  122. $info = Db::query("SELECT TRUNC(createdAt,'MM') AS month_begin,COUNT(*) AS cnt FROM PACSONLINE_PRO.query_study_logs GROUP BY TRUNC(createdAt,'MM') ORDER BY month_begin");
  123. $all = 0;
  124. foreach ($info as $k=>$v)
  125. {
  126. $info[$k]['month_begin'] = (explode(' ',$v['month_begin'])[0] ?? '');
  127. $all += $v['cnt'];
  128. }
  129. $project = $this->linkDao->countExam();
  130. $data = ['image'=>$all,'project'=>$project,'list'=>$info];
  131. return $data;
  132. }
  133. public function getPacsResult()
  134. {
  135. $arr = $this->linkDao->getReportResult();
  136. $data = ['阴性'=>0,'阳性'=>0,'其他'=>0];
  137. foreach ($arr as $k=>$v)
  138. {
  139. if($v['report_result'] == '1')
  140. {
  141. $data['阴性'] += $v['cnt'];
  142. }elseif ($v['report_result'] == '2')
  143. {
  144. $data['阳性'] += $v['cnt'];
  145. }else{
  146. $data['其他'] += $v['cnt'];
  147. }
  148. }
  149. $return = $this->makeData($data);
  150. return $return;
  151. }
  152. public function makeData($arr)
  153. {
  154. $data = [];
  155. foreach ($arr as $k=>$v)
  156. {
  157. $data[] = ['name'=>$k,'value'=>$v];
  158. }
  159. return $data;
  160. }
  161. public function getPacsSex()
  162. {
  163. $arr = $this->linkDao->getSexData();
  164. $data = ['男'=>0,'女'=>0];
  165. foreach ($arr as $k=>$v)
  166. {
  167. if($v['sex'] == 'M' || $v['sex'] == '男' )
  168. {
  169. $data['男'] += $v['cnt'];
  170. }elseif ($v['sex'] == 'F' || $v['sex'] == '女')
  171. {
  172. $data['女'] += $v['cnt'];
  173. }
  174. }
  175. $return = $this->makeData($data);
  176. return $return;
  177. }
  178. public function getMiddleProjectList()
  179. {
  180. $list = $this->linkDao->getMiddleProjectList();
  181. foreach ($list as $k=>$v)
  182. {
  183. if(!empty($v['name']))
  184. {
  185. $name = $v['name'];
  186. $nameLen = mb_strlen($name, 'UTF-8');
  187. if ($nameLen > 1) {
  188. $list[$k]['name'] = mb_substr($name, 0, 1, 'UTF-8') . str_repeat('*', $nameLen - 1);
  189. }
  190. }
  191. }
  192. return $list;
  193. }
  194. public function getPacsMapExam()
  195. {
  196. $institution = $this->linkDao->getAllIns();
  197. $allIns = [];
  198. foreach ($institution as $k=>$v)
  199. {
  200. $allIns[$v['id']] = $v;
  201. }
  202. $exam = $this->linkDao->getInsExam();
  203. $date = '20260420';
  204. $today = strtotime(date('Ymd',time()));
  205. $target = strtotime($date);
  206. $diffDays = ($today - $target) / (60 * 60 * 24);
  207. $exam[] = ['num'=>$diffDays*157,'institution_id'=>'06700001'];
  208. rsort($exam);
  209. $arr = [];
  210. foreach ($exam as $k=>$v)
  211. {
  212. if(empty($allIns[$v['institution_id']] ?? ''))
  213. {
  214. continue;
  215. }
  216. $arr[] = ['num'=>$v['num'],'lng'=>$allIns[$v['institution_id']]['lng'],'lat'=>$allIns[$v['institution_id']]['lat'],'NAME'=>$allIns[$v['institution_id']]['name']];
  217. }
  218. return $arr;
  219. }
  220. public function getShareToken($param)
  221. {
  222. if(empty($param['B']))
  223. {
  224. $this->throwError('证件号不能为空','-1');
  225. }
  226. $token = UUIDUtils::uuid();
  227. $this->setCache($token,$param,32400);
  228. return $token;
  229. }
  230. public function getViewDetail($info)
  231. {
  232. $cardNum = $info['B'];
  233. $cardWhere = ['card_num'=>$cardNum];
  234. $where = [];
  235. if($info['H'] == 1)
  236. {
  237. $where['institution_id'] = $info['C'];
  238. }
  239. $data = $this->linkDao->getExamData($where,$cardWhere);
  240. if(!empty($data))
  241. {
  242. $patient = [];
  243. $report = [];
  244. $examReport = [];
  245. foreach ($data as $k=>$v)
  246. {
  247. switch ($v['sex'])
  248. {
  249. case 'M':
  250. $sex = '男';
  251. break;
  252. case 'F':
  253. $sex = '女';
  254. break;
  255. default:
  256. $sex = '未知';
  257. }
  258. $patient = [
  259. 'BIRTH_DATE'=>$v['birthday'],
  260. 'CARDNUM'=>$v['card_num'],
  261. 'GENDER'=>$sex,
  262. 'ID_CARDNUM'=>$v['card_num'],
  263. 'MPI'=>$v['accession_num'],
  264. 'NAME'=>$v['name'],
  265. 'PATIENTNAME'=>$v['name'],
  266. 'AGE'=>$v['age']
  267. ];
  268. $report = $this->linkDao->getReportData(['exam_id'=>$v['id']]);
  269. $info = [];
  270. $institution = $this->linkDao->getInsData(['id'=>$v['institution_id']]);
  271. switch ($report['report_result'] ?? '')
  272. {
  273. case '1':
  274. $result = '阴性';
  275. break;
  276. case '2':
  277. $result = '阳性';
  278. break;
  279. default:
  280. $result = '未知';
  281. }
  282. $info['EXAM_REPORT'][] = [
  283. 'ACCESSION_NUM'=>$v['accession_num'],
  284. 'AUDITNAME'=>$report['review_doctor_name'] ?? '',
  285. 'BODYSITE'=>$v['body_part'],
  286. 'DEPTNAME'=>$v['application_department'],
  287. 'DEVICETYPE_CODE'=>$v['exam_class'],
  288. 'EXAM_ITEMNAME'=>$v['exam_project'],
  289. 'OBSERVATIONS_COMMENT'=>$report['description'] ?? '',
  290. 'OBSERVATIONS_RESULT'=>$report['impression'] ?? '',
  291. 'ORGNAME'=>$institution['name'],
  292. 'REPORTTIME'=>$report['report_datetime'] ?? '',
  293. 'RESULT_STATUS'=>$result,
  294. 'STUDYTIME'=>$v['exam_datetime'],
  295. 'STUDY_ID'=>$v['study_id'],
  296. ];
  297. //患者来源
  298. //'1'=>'急诊号 '2'=>住院号', '3'=>'门诊号' '4'=>'体检号'
  299. switch ($v['patient_source'])
  300. {
  301. case '1':
  302. $source = '急诊';
  303. break;
  304. case '2':
  305. $source = '住院';
  306. break;
  307. case '3':
  308. $source = '门诊';
  309. break;
  310. case '4':
  311. $source = '体检';
  312. break;
  313. default:
  314. $source = '';
  315. }
  316. $info['MEDICAL_INFORMATION'] = [
  317. 'AGE'=>$v['age'],
  318. 'CHIEFCOMPLAINT'=>$v['clin_diag'],
  319. 'CLASS'=>$source,
  320. 'DEPTNAME'=>$v['application_department'],
  321. 'DIAGNOSENAME'=>$v['clin_diag'],
  322. 'DOCTORNAME'=>$v['application_doctor'],
  323. 'ENCOUNTER_DATE'=>$v['exam_datetime'],
  324. 'GENDER'=>$sex,
  325. 'NAME'=>$v['name'],
  326. 'ORGCODE'=>$v['institution_id'],
  327. 'ORGNAME'=>$institution['name'],
  328. 'PATIENT_CODE'=>$v['accession_num']
  329. ];
  330. $examReport[] = $info;
  331. }
  332. $return = ['data'=>$examReport,'patient'=>$patient];
  333. return $return;
  334. }else{
  335. return [];
  336. }
  337. }
  338. public function getPatientList($param)
  339. {
  340. $param['code'] = openssl_decrypt(base64_decode($param['code']), 'AES-128-ECB', Config::get('chengde')['key']);
  341. // $param['institution_id'] = openssl_decrypt(base64_decode($param['institution_id']), 'AES-128-ECB', Config::get('chengde')['key']);
  342. if(empty($param['code']))
  343. {
  344. $this->throwError('数据解密失败','1210');
  345. }
  346. if(empty($param['type']) || empty($param['code']))
  347. {
  348. $this->throwError('没有相应的类型','1211');
  349. }
  350. // $insIds = explode(',',$param['institution_id']);
  351. $type = explode(',',$param['type']);
  352. $code = explode(',',$param['code']);
  353. $where = [];
  354. $institution = [];
  355. // $institution[] = ['institution_id','in',$insIds];
  356. $whereTime = [];
  357. if(count($type) == 1&& count($code) !== 1)
  358. {
  359. //1个type多个code
  360. if(!(Config::get('institution_docking')[$type[0]] ?? null))
  361. {
  362. $this->throwError('没有相应的类型','1211');
  363. }
  364. $field = Config::get('institution_docking')[$type[0]];
  365. if($field == 'name')
  366. {
  367. $field = 'e.name';
  368. }
  369. $where[] = [$field,'in',$code];
  370. }else{
  371. foreach ($type as $k=>$v)
  372. {
  373. $field = Config::get('institution_docking')[$v];
  374. if(empty($field) /*|| empty($code[$k])*/){
  375. continue;
  376. }
  377. if($field == 'name')
  378. {
  379. $field = 'e.name';
  380. }
  381. if($field == 'exam_datetime')
  382. {
  383. $whereTime['exam_datetime'] = $code[$k];
  384. }else{
  385. if(empty($code[$k]))
  386. {
  387. if(!empty($code[0]))
  388. {
  389. $where[$field] = $code[0];
  390. }
  391. }else{
  392. $where[$field] = $code[$k];
  393. }
  394. }
  395. }
  396. }
  397. if(empty($where) && empty($whereTime))
  398. {
  399. return [];
  400. }
  401. if(($param['check'] ?? null))
  402. {
  403. switch ($param['check'])
  404. {
  405. case 1:
  406. $value = 'card_num';
  407. break;
  408. case 2:
  409. $value = 'phone';
  410. break;
  411. default:
  412. $value = '';
  413. $this->throwError('验证错误','1223');
  414. break;
  415. }
  416. $data = $this->linkDao->getExamsCheck($where,$institution,$value);
  417. if(empty($data))
  418. {
  419. $this->throwError('验证错误','1223');
  420. }
  421. $str = substr($data,-4);
  422. if($param['checkcode'] !== $str)
  423. {
  424. $this->throwError('验证码错误','1224');
  425. }
  426. }
  427. $info = $this->linkDao->getPatientReportList($where,$institution,$whereTime,[]);
  428. foreach ($info as $k=>$v)
  429. {
  430. $info[$k]['insConfig'] = '';
  431. $info[$k]['config_number'] = '';
  432. $config = Config::get('hospital_config');
  433. foreach ($config as $key=>$value)
  434. {
  435. if(isset($v[$key]) && !empty($v[$key]))
  436. {
  437. $info[$k]['insConfig'] = $value;
  438. $info[$k]['config_number'] = $v[$key];
  439. }
  440. }
  441. if(!empty($v['patient_source']))
  442. {
  443. $sourceArr = Config::get('patient_source');
  444. $sourceInfo = $sourceArr[$v['patient_source']] ?? [];
  445. if(!empty($sourceInfo))
  446. {
  447. $info[$k]['insConfig'] = $sourceInfo['name'];
  448. $info[$k]['config_number'] = $v[$sourceInfo['field']];
  449. }
  450. }
  451. }
  452. return $info;
  453. }
  454. public function getAnotherPatientList($param)
  455. {
  456. if(empty($param['type']) || empty($param['code']))
  457. {
  458. $this->throwError('没有相应的类型','1211');
  459. }
  460. // $insIds = explode(',',$param['institution_id']);
  461. $type = explode(',',$param['type']);
  462. $code = explode(',',$param['code']);
  463. $where = [];
  464. $institution = [];
  465. // $institution[] = ['institution_id','in',$insIds];
  466. $whereTime = [];
  467. if(count($type) == 1&& count($code) !== 1)
  468. {
  469. //1个type多个code
  470. if(!(Config::get('institution_docking')[$type[0]] ?? null))
  471. {
  472. $this->throwError('没有相应的类型','1211');
  473. }
  474. $field = Config::get('institution_docking')[$type[0]];
  475. if($field == 'name')
  476. {
  477. $field = 'e.name';
  478. }
  479. $where[] = [$field,'in',$code];
  480. }else{
  481. foreach ($type as $k=>$v)
  482. {
  483. $field = Config::get('institution_docking')[$v];
  484. if(empty($field) /*|| empty($code[$k])*/){
  485. continue;
  486. }
  487. if($field == 'name')
  488. {
  489. $field = 'e.name';
  490. }
  491. if($field == 'exam_datetime')
  492. {
  493. $whereTime['exam_datetime'] = $code[$k];
  494. }else{
  495. if(empty($code[$k]))
  496. {
  497. if(!empty($code[0]))
  498. {
  499. $where[$field] = $code[0];
  500. }
  501. }else{
  502. $where[$field] = $code[$k];
  503. }
  504. }
  505. }
  506. }
  507. if(empty($where) && empty($whereTime))
  508. {
  509. return [];
  510. }
  511. if(($param['check'] ?? null))
  512. {
  513. switch ($param['check'])
  514. {
  515. case 1:
  516. $value = 'card_num';
  517. break;
  518. case 2:
  519. $value = 'phone';
  520. break;
  521. default:
  522. $value = '';
  523. $this->throwError('验证错误','1223');
  524. break;
  525. }
  526. $data = $this->linkDao->getExamsCheck($where,$institution,$value);
  527. if(empty($data))
  528. {
  529. $this->throwError('验证错误','1223');
  530. }
  531. $str = substr($data,-4);
  532. if($param['checkcode'] !== $str)
  533. {
  534. $this->throwError('验证码错误','1224');
  535. }
  536. }
  537. $must_where = [];
  538. if(isset($param['must']) && !empty($param['must']))
  539. {
  540. $must = $param['must'];
  541. $must_type = Config::get('institution_docking')[$must['key']];
  542. if($must_type == 'name')
  543. {
  544. $must_type = 'e.name';
  545. }
  546. $must_code = $must['value'];
  547. $must_where = [$must_type=>$must_code];
  548. }
  549. $info = $this->linkDao->getPatientReportList($where,$institution,$whereTime,$must_where);
  550. foreach ($info as $k=>$v)
  551. {
  552. $info[$k]['insConfig'] = '';
  553. $info[$k]['config_number'] = '';
  554. $config = Config::get('hospital_config');
  555. foreach ($config as $key=>$value)
  556. {
  557. if(isset($v[$key]) && !empty($v[$key]))
  558. {
  559. $info[$k]['insConfig'] = $value;
  560. $info[$k]['config_number'] = $v[$key];
  561. }
  562. }
  563. if(!empty($v['patient_source']))
  564. {
  565. $sourceArr = Config::get('patient_source');
  566. $sourceInfo = $sourceArr[$v['patient_source']] ?? [];
  567. if(!empty($sourceInfo))
  568. {
  569. $info[$k]['insConfig'] = $sourceInfo['name'];
  570. $info[$k]['config_number'] = $v[$sourceInfo['field']];
  571. }
  572. }
  573. }
  574. return $info;
  575. }
  576. public function getPatientInfo($params)
  577. {
  578. $info = $this->linkDao->getConfirmReportInfo($params['exam_id']);
  579. foreach ($info as $k=>$v)
  580. {
  581. $info[$k]['insConfig'] = '';
  582. $info[$k]['config_number'] = '';
  583. $config = Config::get('hospital_config');
  584. foreach ($config as $key=>$value)
  585. {
  586. if(isset($v[$key]) && !empty($v[$key]))
  587. {
  588. $info[$k]['insConfig'] = $value;
  589. $info[$k]['config_number'] = $v[$key];
  590. }
  591. if(!empty($v['age']))
  592. {
  593. $info[$k]['age'] = str_replace('W','周',str_replace('D','天',str_replace('M','月',str_replace('Y','岁',ltrim($info[$k]['age'],'0')))));
  594. }
  595. }
  596. }
  597. return $info;
  598. }
  599. public function getJm($param)
  600. {
  601. return base64_encode(openssl_encrypt($param['code'], 'AES-128-ECB', Config::get('chengde')['key']));
  602. }
  603. }