LinkService.php 20 KB

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