SyncDrugReportService.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <?php
  2. namespace App\Services;
  3. use App\Models\DrugReportInfo;
  4. use Carbon\Carbon;
  5. use Illuminate\Support\Facades\Log;
  6. class SyncDrugReportService
  7. {
  8. /** @var string 查询上游企业的待签收药检报告信息 */
  9. const QUERY_DRUG_REPORT = 'querydrugreport';
  10. /** @var string 查询上传报告信息接口 */
  11. const QUERY_SEAL_DRUG_REPORT = 'querysealdrugreport';
  12. /** @var string 一张网报告操作日志 */
  13. const DRUG_REPORT_OPT_HISTORY = 'drugreport.opt.history';
  14. /** @var Log */
  15. protected $log;
  16. /** @var QueryDrugReportService */
  17. protected $queryDrugReportService;
  18. /**
  19. * 检查同步来源
  20. * @param string $source
  21. * @return bool
  22. */
  23. protected function checkSource(string $source): bool
  24. {
  25. return (in_array($source, [
  26. self::QUERY_DRUG_REPORT,
  27. self::QUERY_SEAL_DRUG_REPORT,
  28. self::DRUG_REPORT_OPT_HISTORY]));
  29. }
  30. /**
  31. * 根据来源设置日志频道
  32. * @param string $source
  33. * @return void
  34. */
  35. protected function setLog(string $source)
  36. {
  37. if (self::QUERY_SEAL_DRUG_REPORT == $source) {
  38. $this->log = Log::channel('sync_seal');
  39. } elseif (self::DRUG_REPORT_OPT_HISTORY == $source) {
  40. $this->log = Log::channel('sync_opt');
  41. } else {
  42. $this->log = Log::channel('sync');
  43. }
  44. }
  45. /**
  46. * 分批次同步数据
  47. * @param string $source
  48. * @param bool $isFull
  49. * @param string $startTime
  50. * @param string $endTime
  51. * @param int $page
  52. * @param int $pageSize
  53. * @return int
  54. */
  55. public function syncDrugReport(string $source, bool $isFull, string $startTime, string $endTime = '', int $page = 1, int $pageSize = 20): int
  56. {
  57. if (!$this->checkSource($source)) {
  58. $this->log->error("来源[source]不正确,无法进行同步", [
  59. 'source' => $source,
  60. 'params' => [
  61. 'is_full' => $isFull,
  62. 'start_time' => $startTime,
  63. 'end_time' => $endTime,
  64. 'page' => $page,
  65. 'page_size' => $pageSize,
  66. ],
  67. ]);
  68. return 0;
  69. }
  70. $this->setLog($source);
  71. $total = 0;
  72. $this->log->notice($isFull ? "开始全量同步" : "开始增量同步", [
  73. 'source' => $source,
  74. 'params' => [
  75. 'is_full' => $isFull,
  76. 'start_time' => $startTime,
  77. 'end_time' => $endTime,
  78. 'page' => $page,
  79. 'page_size' => $pageSize,
  80. ],
  81. ]);
  82. if (empty($endTime)) {
  83. $endTime = Carbon::parse($startTime)->copy()->endOfMonth()->toDateTimeString();
  84. }
  85. $this->queryDrugReportService = app(QueryDrugReportService::class);
  86. do {
  87. $month = Carbon::parse($startTime)->copy()->format('Ym');
  88. try {
  89. if (Carbon::parse($startTime)->isFuture()) {
  90. $this->log->warning("时间大于当前时间,同步终止", [
  91. 'source' => $source,
  92. 'params' => [
  93. 'is_full' => $isFull,
  94. 'start_time' => $startTime,
  95. 'end_time' => $endTime,
  96. 'page' => $page,
  97. 'page_size' => $pageSize,
  98. ]
  99. ]);
  100. break;
  101. }
  102. $this->log->info("批次 {$month}[{$page}] 开始处理", [
  103. 'source' => $source,
  104. 'params' => [
  105. 'is_full' => $isFull,
  106. 'start_time' => $startTime,
  107. 'end_time' => $endTime,
  108. 'page' => $page,
  109. 'page_size' => $pageSize,
  110. ]
  111. ]);
  112. // 分批次拉取数据
  113. $data = $this->queryDrugReport($source, $startTime, $endTime, $page, $pageSize);
  114. if (empty($data)) {
  115. $this->log->warning("批次 {$month}[{$page}] 没有数据", [
  116. 'source' => $source,
  117. 'params' => [
  118. 'is_full' => $isFull,
  119. 'start_time' => $startTime,
  120. 'end_time' => $endTime,
  121. 'page' => $page,
  122. 'page_size' => $pageSize,
  123. ]
  124. ]);
  125. if ($isFull) {
  126. $next = Carbon::parse($startTime)->copy()->addMonth();
  127. $startTime = $next->startOfMonth()->toDateTimeString();
  128. $endTime = $next->endOfMonth()->toDateTimeString();
  129. $page = 1;
  130. continue;
  131. }
  132. break;
  133. }
  134. // 处理并保存数据
  135. $saveCount = $this->saveDrugReport($source, $data);
  136. $total += $saveCount;
  137. $this->log->info("批次 {$month}[{$page}] 处理完成", [
  138. 'source' => $source,
  139. 'fetch_count' => count($data),
  140. 'save_count' => $saveCount,
  141. 'total' => $total,
  142. ]);
  143. // 检查是否还有更多数据
  144. if (count($data) <> $pageSize) {
  145. if ($isFull) {
  146. $next = Carbon::parse($startTime)->copy()->addMonth();
  147. $startTime = $next->startOfMonth()->toDateTimeString();
  148. $endTime = $next->endOfMonth()->toDateTimeString();
  149. $page = 1;
  150. continue;
  151. }
  152. break;
  153. }
  154. $page++;
  155. // 避免请求过于频繁
  156. sleep(1);
  157. } catch (\Exception $e) {
  158. $this->log->error("批次 {$month}[{$page}] 数据获取失败", [
  159. 'source' => $source,
  160. 'params' => [
  161. 'is_full' => $isFull,
  162. 'start_time' => $startTime,
  163. 'end_time' => $endTime,
  164. 'page' => $page,
  165. 'page_size' => $pageSize,
  166. ],
  167. 'error' => $e->getMessage()
  168. ]);
  169. // continue;
  170. break;
  171. }
  172. } while (true);
  173. $this->log->notice($isFull ? "全量同步完成" : "增量同步完成", [
  174. 'source' => $source,
  175. 'total' => $total
  176. ]);
  177. return $total;
  178. }
  179. /**
  180. * 查询药检报告数据
  181. * @param string $source
  182. * @param string $startTime
  183. * @param string $endTime
  184. * @param int $page
  185. * @param int $pageSize
  186. * @return array
  187. * @throws \Exception
  188. */
  189. protected function queryDrugReport(string $source, string $startTime, string $endTime = '', int $page = 1, int $pageSize = 20): array
  190. {
  191. if (self::QUERY_DRUG_REPORT == $source) {
  192. return $this->queryDrugReportService->queryDrugReport($startTime, $endTime, $page, $pageSize);
  193. }
  194. if (self::QUERY_SEAL_DRUG_REPORT == $source) {
  195. return $this->queryDrugReportService->querySealDrugReport($startTime, $endTime, $page, $pageSize);
  196. }
  197. if (self::DRUG_REPORT_OPT_HISTORY == $source) {
  198. $startDate = Carbon::parse($startTime)->copy()->toDateString();
  199. $endDate = Carbon::parse($endTime)->copy()->toDateString();
  200. return $this->queryDrugReportService->drugReportOptHistory($startDate, $endDate, $page, $pageSize);
  201. }
  202. return [];
  203. }
  204. /**
  205. * 处理并保存批次数据
  206. * @param string $source
  207. * @param array $data
  208. * @return int
  209. */
  210. protected function saveDrugReport(string $source, array $data): int
  211. {
  212. try {
  213. if (self::DRUG_REPORT_OPT_HISTORY == $source) {
  214. return $this->processDrugReportOptHistory($data);
  215. }
  216. if (self::QUERY_SEAL_DRUG_REPORT == $source) {
  217. $data = $this->correctSealDrugReport($data);
  218. } else {
  219. $data = $this->correctDrugReport($data);
  220. }
  221. return DrugReportInfo::bulkUpsert($data);
  222. } catch (\Throwable $e) {
  223. $this->log->error("保存批次数据失败", [
  224. 'data' => $data,
  225. 'error' => $e->getMessage()
  226. ]);
  227. return 0;
  228. }
  229. }
  230. /**
  231. * 处理【待签收】医药报告批次数据
  232. * @param array $data
  233. * @return array
  234. */
  235. protected function correctDrugReport(array $data): array
  236. {
  237. return array_map(function ($item) {
  238. return [
  239. 'report_id' => $item['drug_report_id'] ?? '',
  240. 'batch_no' => $item['produce_batch_no'] ?? '',
  241. 'drug_id' => $item['drug_id'] ?? '',
  242. 'drug_name' => $item['physic_name'] ?? '',
  243. 'pkg_spec' => $item['pkg_spec'] ?? '',
  244. 'prepn_spec' => $item['prepn_spec'] ?? '',
  245. 'prepn_type_desc' => $item['prepn_type_desc'] ?? '',
  246. 'bill_type' => $item['bill_type'] ?? 0,
  247. 'bill_id' => $item['bill_id'] ?? '',
  248. 'bill_detail_id' => $item['bill_detail_id'] ?? '',
  249. 'bill_time' => $item['bill_time'] ?? '',
  250. 'bill_code' => $item['bill_code'] ?? '',
  251. 'produce_date' => $item['produce_date'] ?? '',
  252. 'produce_ent_id' => $item['produce_ent_id'] ?? '',
  253. 'produce_ent_name' => $item['produce_ent_name'] ?? '',
  254. 'ass_ref_ent_id' => $item['ass_ref_ent_id'] ?? '',
  255. 'from_ref_ent_id' => $item['from_ref_ent_id'] ?? '',
  256. 'from_ent_name' => $item['from_ent_name'] ?? '',
  257. 'report_url' => $item['sealed_report_url'] ?? '',
  258. // 'file_name' => $item['file_name'] ?? '',
  259. 'report_sign_status' => $item['drug_report_sign_status'] ?? '',
  260. 'raw_data' => json_encode($item ?? [], JSON_UNESCAPED_UNICODE),
  261. ];
  262. }, $data);
  263. }
  264. /**
  265. * 处理【已签收】医药报告批次数据
  266. * @param array $data
  267. * @return array
  268. */
  269. protected function correctSealDrugReport(array $data): array
  270. {
  271. return array_map(function ($item) {
  272. return [
  273. 'report_v2_id' => $item['drug_report_v2_id'] ?? '',
  274. 'report_name' => $item['drug_report_name'] ?? '',
  275. 'report_no' => $item['report_no'] ?? '',
  276. 'report_date' => $item['report_date'] ?? '',
  277. 'batch_no' => $item['batch_no'] ?? '',
  278. 'drug_id' => $item['drug_ent_base_info_id'] ?? '',
  279. 'drug_name' => $item['drug_name'] ?? '',
  280. 'prod_code' => $item['prod_code'] ?? '',
  281. 'pkg_spec' => $item['pkg_spec'] ?? '',
  282. 'prepn_spec' => $item['prepn_spec'] ?? '',
  283. 'pkg_ratio_list' => json_encode($item['pkg_ratio_list'] ?? [], JSON_UNESCAPED_UNICODE),
  284. 'seal_report_url' => $item['sealed_report_url'] ?? '',
  285. 'seal_raw_data' => json_encode($item ?? [], JSON_UNESCAPED_UNICODE),
  286. ];
  287. }, $data);
  288. }
  289. /**
  290. * 处理医药报告操作批次数据
  291. * @param array $data
  292. * @return int
  293. */
  294. protected function processDrugReportOptHistory(array $data): int
  295. {
  296. $count = 0;
  297. foreach ($data as $item) {
  298. try {
  299. // 操作类型(insertReport:新增报告、deleteReport:删除报告、sealReport:报告签章、updateReport:报告更新)
  300. $optType = $item['opt_type'];
  301. $drugReportInfo = DrugReportInfo::query()->where([
  302. 'batch_no' => $item['batch_no'],
  303. 'drug_id' => $item['drug_id'],
  304. ])->first();
  305. if (empty($drugReportInfo)) {
  306. $drugReportInfo = DrugReportInfo::create([
  307. 'batch_no' => $item['batch_no'],
  308. 'drug_id' => $item['drug_id'],
  309. 'report_v2_id' => $item['report_v2_id'],
  310. 'report_date' => $item['report_date'],
  311. 'report_name' => $item['report_name'],
  312. 'report_no' => $item['report_no'],
  313. ]);
  314. }
  315. switch ($optType) {
  316. case "deleteReport":
  317. $drugReportInfo->delete();
  318. break;
  319. case "sealReport":
  320. $optTime = Carbon::parse($item['opt_time']);
  321. $updateTime = Carbon::parse($drugReportInfo->update_time);
  322. if ($optTime->gt($updateTime)) {
  323. $drugReportInfo->is_sign = 1;
  324. $drugReportInfo->is_seal = 1;
  325. $drugReportInfo->sync_seal_opt = 1;
  326. }
  327. break;
  328. }
  329. $drugReportInfo->opt_raw_data = json_encode($item, JSON_UNESCAPED_UNICODE);
  330. $drugReportInfo->save();
  331. $count ++;
  332. } catch (\Exception $e) {
  333. $this->log->error("保存数据项失败", [
  334. 'data' => $item,
  335. 'error' => $e->getMessage()
  336. ]);
  337. continue;
  338. }
  339. }
  340. return $count;
  341. }
  342. }