@@ -45,48 +45,109 @@ class getProductCommand extends HyperfCommand
|
||||
$this->setDescription('获取处方平台商品数据');
|
||||
}
|
||||
|
||||
protected function logInfo(string $message): void
|
||||
{
|
||||
$this->line(date('Y-m-d H:i:s') . ' [INFO] ' . $message);
|
||||
Log::getInstance('getProductCommand')->info($message);
|
||||
}
|
||||
|
||||
protected function logWarn(string $message): void
|
||||
{
|
||||
$this->warn(date('Y-m-d H:i:s') . ' [WARN] ' . $message);
|
||||
Log::getInstance('getProductCommand')->warning($message);
|
||||
}
|
||||
|
||||
protected function logError(string $message): void
|
||||
{
|
||||
$this->error(date('Y-m-d H:i:s') . ' [ERROR] ' . $message);
|
||||
Log::getInstance('getProductCommand')->error($message);
|
||||
}
|
||||
|
||||
// 主逻辑处理
|
||||
public function handle()
|
||||
{
|
||||
$this->line("商品更新开始");
|
||||
$startTime = microtime(true);
|
||||
$this->logInfo("================== [getProductCommand] 处方平台商品目录同步开始 ==================");
|
||||
|
||||
$stats = [
|
||||
'total_received' => 0,
|
||||
'created' => 0,
|
||||
'updated' => 0,
|
||||
'unchanged' => 0,
|
||||
'skipped_invalid' => 0,
|
||||
'skipped_pharmacy' => 0,
|
||||
'failed' => 0,
|
||||
];
|
||||
|
||||
try {
|
||||
$prescription = new Prescription();
|
||||
|
||||
$page = 1;
|
||||
$page_size = 100;
|
||||
$result = $prescription->getProd(1, 100);
|
||||
if (!empty($result['rows'])) {
|
||||
foreach ($result['rows'] as $item) {
|
||||
// 执行入库
|
||||
$this->handleData($item);
|
||||
// 预加载已启用的药房列表
|
||||
if (empty($this->active_pharmacies)) {
|
||||
$list = Pharmacy::where('status', 1)->get();
|
||||
foreach ($list as $p) {
|
||||
$this->active_pharmacies[$p->pharmacy_code] = $p;
|
||||
}
|
||||
}
|
||||
|
||||
$count = ceil($result['count'] / ($page * $page_size));
|
||||
if (empty($this->active_pharmacies)) {
|
||||
$this->logError("未找到任何已启用的药房配置,请先在药房管理中添加并启用药房后再执行同步");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($result['count'] > $page * $page_size) {
|
||||
for ($i = 2; $i < $count; $i++) {
|
||||
$pharmacyListStr = implode(', ', array_map(function ($p) {
|
||||
return "{$p->pharmacy_name}({$p->pharmacy_code})";
|
||||
}, array_values($this->active_pharmacies)));
|
||||
$this->logInfo("当前系统已启用药房 (" . count($this->active_pharmacies) . " 个): [{$pharmacyListStr}]");
|
||||
|
||||
$prescription = new Prescription();
|
||||
$page = 1;
|
||||
$page_size = 100;
|
||||
|
||||
$this->logInfo("正在请求第 1 页商品目录数据 (pageSize={$page_size})...");
|
||||
$result = $prescription->getProd(1, $page_size);
|
||||
|
||||
if (empty($result) || !isset($result['rows'])) {
|
||||
$this->logWarn("处方平台未返回有效的商品目录数据,任务结束");
|
||||
return;
|
||||
}
|
||||
|
||||
$totalCount = (int)($result['count'] ?? count($result['rows']));
|
||||
$totalPages = (int)ceil($totalCount / $page_size);
|
||||
$this->logInfo("处方平台商品目录查询成功: 平台总数据量={$totalCount} 条,总页数={$totalPages} 页");
|
||||
|
||||
// 处理第 1 页
|
||||
$this->logInfo(">>> 正在处理第 1/{$totalPages} 页数据 (当前页 " . count($result['rows']) . " 条)...");
|
||||
foreach ($result['rows'] as $item) {
|
||||
$stats['total_received']++;
|
||||
$this->handleData($item, $stats);
|
||||
}
|
||||
|
||||
// 处理后续页
|
||||
if ($totalPages > 1) {
|
||||
for ($i = 2; $i <= $totalPages; $i++) {
|
||||
$this->logInfo(">>> 正在请求并处理第 {$i}/{$totalPages} 页数据...");
|
||||
try {
|
||||
$result = $prescription->getProd($i, $page_size);
|
||||
if (!isset($result['rows'])) {
|
||||
$pageResult = $prescription->getProd($i, $page_size);
|
||||
if (!isset($pageResult['rows']) || empty($pageResult['rows'])) {
|
||||
$this->logWarn("第 {$i} 页返回空数据,跳过");
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($result['rows'] as $item) {
|
||||
// 执行入库
|
||||
$this->handleData($item);
|
||||
foreach ($pageResult['rows'] as $item) {
|
||||
$stats['total_received']++;
|
||||
$this->handleData($item, $stats);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->line("部分商品更新失败:" . $e->getMessage());
|
||||
} catch (\Throwable $e) {
|
||||
$this->logError("第 {$i} 页商品同步失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->line("商品更新失败:" . $e->getMessage());
|
||||
} catch (\Throwable $e) {
|
||||
$this->logError("商品同步全局异常: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$this->line("商品更新成功");
|
||||
$duration = round(microtime(true) - $startTime, 2);
|
||||
$this->logInfo("================== [getProductCommand] 处方平台商品目录同步完成 ==================");
|
||||
$this->logInfo("总耗时: {$duration}s | 平台接收: {$stats['total_received']} 条 | 新增入库: {$stats['created']} 条 | 变更更新: {$stats['updated']} 条 | 资料无变动: {$stats['unchanged']} 条 | 非本院药房忽略: {$stats['skipped_pharmacy']} 条 | 字段缺失跳过: {$stats['skipped_invalid']} 条 | 处理失败: {$stats['failed']} 条");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,27 +164,15 @@ class getProductCommand extends HyperfCommand
|
||||
/**
|
||||
* 入库
|
||||
* @param array $item
|
||||
* @param array $stats
|
||||
* @return bool
|
||||
*/
|
||||
protected function handleData(array $item): bool
|
||||
protected function handleData(array $item, array &$stats): bool
|
||||
{
|
||||
if (empty($item['drugCode'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($item['drugPrice'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($item['thirdCode'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($item['thirdDrugCode'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($item['approvalNumber'])) {
|
||||
if (empty($item['drugCode']) || empty($item['drugPrice']) || empty($item['thirdCode']) || empty($item['thirdDrugCode']) || empty($item['approvalNumber'])) {
|
||||
$drugName = $item['tradeName'] ?? '未知名';
|
||||
$this->logWarn("【跳过-字段缺失】药品 [{$drugName}] 缺少必要字段(drugCode/drugPrice/thirdCode/thirdDrugCode/approvalNumber)");
|
||||
$stats['skipped_invalid']++;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -133,6 +182,7 @@ class getProductCommand extends HyperfCommand
|
||||
$pharmacy = $this->getPharmacy($item['thirdCode']);
|
||||
if (empty($pharmacy)) {
|
||||
Db::rollBack();
|
||||
$stats['skipped_pharmacy']++;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -198,7 +248,7 @@ class getProductCommand extends HyperfCommand
|
||||
if (isset($item['packingUnit'])) {
|
||||
if ($product_platform['retail_unit'] != $item['packingUnit']) {
|
||||
$product_platform_data['retail_unit'] = $item['packingUnit'];
|
||||
$product_data['packaging_unit'] = $item['packingUnit']; // 平台返回零售包装单位为:盒,此结果适用于商品表的基本包装单位
|
||||
$product_data['packaging_unit'] = $item['packingUnit'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,20 +295,20 @@ class getProductCommand extends HyperfCommand
|
||||
Product::edit($params, $product_data);
|
||||
}
|
||||
}
|
||||
$this->logInfo("【更新药品】药房 [{$pharmacy->pharmacy_name}({$item['thirdCode']})] 药品 [{$item['tradeName']}] (ID: {$product_platform['product_platform_id']}, 价格: {$item['drugPrice']})");
|
||||
$stats['updated']++;
|
||||
} else {
|
||||
$stats['unchanged']++;
|
||||
}
|
||||
} else {
|
||||
// 不存在,创建
|
||||
$data = array();
|
||||
$data['pharmacy_code'] = $item['thirdCode'];
|
||||
// 商品名称
|
||||
if (isset($item['tradeName'])) {
|
||||
$data['product_name'] = $item['tradeName'];
|
||||
}
|
||||
|
||||
// 商品价格
|
||||
$data['product_price'] = $item['drugPrice'];
|
||||
|
||||
// 药品类型
|
||||
if (isset($item['drugClassCode'])) {
|
||||
if ($item['drugClassCode'] == 1) {
|
||||
$data['product_type'] = 1;
|
||||
@@ -269,41 +319,25 @@ class getProductCommand extends HyperfCommand
|
||||
}
|
||||
}
|
||||
|
||||
// 处方平台商品编码
|
||||
$data['product_platform_code'] = $item['drugCode'];
|
||||
|
||||
// 第三方药店商品编码
|
||||
$data['product_pharmacy_code'] = $item['thirdDrugCode'];
|
||||
|
||||
// 商品规格
|
||||
if (isset($item['specifications'])) {
|
||||
$data['product_spec'] = $item['specifications'];
|
||||
}
|
||||
|
||||
// 批准文号
|
||||
$data['license_number'] = $item['approvalNumber'];
|
||||
|
||||
// 生产厂家
|
||||
if (isset($item['manufacturer'])) {
|
||||
$data['manufacturer'] = $item['manufacturer'];
|
||||
}
|
||||
|
||||
// 单次剂量单位
|
||||
if (isset($item['defaultSingleDosageUnit'])) {
|
||||
$data['single_unit'] = $item['defaultSingleDosageUnit'];
|
||||
}
|
||||
|
||||
// 基本包装单位
|
||||
if (isset($item['basicPackingUnit'])) {
|
||||
$data['packaging_unit'] = $item['basicPackingUnit'];
|
||||
}
|
||||
|
||||
// 基本包装数量
|
||||
if (isset($item['basicPackingCount'])) {
|
||||
$data['packaging_count'] = $item['basicPackingCount'];
|
||||
}
|
||||
|
||||
// 零售包装单位
|
||||
if (isset($item['packingUnit'])) {
|
||||
$data['retail_unit'] = $item['packingUnit'];
|
||||
}
|
||||
@@ -311,14 +345,20 @@ class getProductCommand extends HyperfCommand
|
||||
$product_platform = ProductPlatform::addProductPlatform($data);
|
||||
if (empty($product_platform)) {
|
||||
Db::rollBack();
|
||||
$this->line("商品更新失败:" . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
$this->logError("【新增失败】药房 [{$pharmacy->pharmacy_name}({$item['thirdCode']})] 药品 [{$item['tradeName']}] 写入数据库失败");
|
||||
$stats['failed']++;
|
||||
return false;
|
||||
}
|
||||
$this->logInfo("【新增入库】药房 [{$pharmacy->pharmacy_name}({$item['thirdCode']})] 药品 [{$item['tradeName']}] (平台编码: {$item['drugCode']}, 批准文号: {$item['approvalNumber']}, 价格: {$item['drugPrice']})");
|
||||
$stats['created']++;
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollBack();
|
||||
$this->line("商品更新失败:" . $e->getMessage());
|
||||
$drugName = $item['tradeName'] ?? '未知名';
|
||||
$this->logError("【更新异常】药品 [{$drugName}] (thirdDrugCode: {$item['thirdDrugCode']}) 异常: " . $e->getMessage());
|
||||
$stats['failed']++;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace App\Command;
|
||||
use App\Model\Product;
|
||||
use App\Model\ProductAmountRecord;
|
||||
use App\Model\ProductPlatformAmount;
|
||||
use App\Utils\Log;
|
||||
use Extend\Prescription\Prescription;
|
||||
use Hyperf\Command\Command as HyperfCommand;
|
||||
use Hyperf\Command\Annotation\Command;
|
||||
@@ -30,127 +31,188 @@ class getProductStockCommand extends HyperfCommand
|
||||
$this->setDescription('获取处方平台商品库存数据');
|
||||
}
|
||||
|
||||
protected function logInfo(string $message): void
|
||||
{
|
||||
$this->line(date('Y-m-d H:i:s') . ' [INFO] ' . $message);
|
||||
Log::getInstance('getProductStockCommand')->info($message);
|
||||
}
|
||||
|
||||
protected function logWarn(string $message): void
|
||||
{
|
||||
$this->warn(date('Y-m-d H:i:s') . ' [WARN] ' . $message);
|
||||
Log::getInstance('getProductStockCommand')->warning($message);
|
||||
}
|
||||
|
||||
protected function logError(string $message): void
|
||||
{
|
||||
$this->error(date('Y-m-d H:i:s') . ' [ERROR] ' . $message);
|
||||
Log::getInstance('getProductStockCommand')->error($message);
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->line("商品库存更新开始");
|
||||
$startTime = microtime(true);
|
||||
$this->logInfo("================== [getProductStockCommand] 商品库存更新开始 ==================");
|
||||
|
||||
$stats = [
|
||||
'total' => 0,
|
||||
'success' => 0,
|
||||
'stock_changed' => 0,
|
||||
'stock_unchanged' => 0,
|
||||
'skipped' => 0,
|
||||
'failed' => 0,
|
||||
];
|
||||
|
||||
try {
|
||||
// 获取商品
|
||||
$pageSize = 20;
|
||||
$params = array();
|
||||
$params['product_status'] = 1;
|
||||
$product = Product::getPage($params,['*'],1,10);
|
||||
if (empty($product['data'])){
|
||||
$this->line("商品库存更新成功,无可更新库存商品");
|
||||
$product = Product::getPage($params, ['*'], 1, $pageSize);
|
||||
if (empty($product['data'])) {
|
||||
$this->logInfo("当前无可更新库存的在架商品,任务结束");
|
||||
return;
|
||||
}
|
||||
|
||||
$prescription = new Prescription();
|
||||
|
||||
foreach ($product['data'] as $item){
|
||||
if (!empty($item['product_pharmacy_code'])){
|
||||
$pharmacy_code = $item['pharmacy_code'] ?? '';
|
||||
$result = $prescription->getProdStock($item['product_pharmacy_code'], $pharmacy_code);
|
||||
$this->handleData($item['product_platform_id'],$item['product_platform_code'],$result[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($product['last_page'] > 1){
|
||||
for ($i = 2; $i <= $product['last_page']; $i++) {
|
||||
// 获取商品
|
||||
$params = array();
|
||||
$params['product_status'] = 1;
|
||||
$product = Product::getPage($params,['*'],$i,10);
|
||||
if (empty($product['data'])){
|
||||
$this->line("商品库存更新成功,无可更新库存商品");
|
||||
return;
|
||||
}
|
||||
$totalCount = (int)($product['total'] ?? count($product['data']));
|
||||
$lastPage = (int)($product['last_page'] ?? 1);
|
||||
$this->logInfo("在架商品总数: {$totalCount} 件,共 {$lastPage} 页,每页批次: {$pageSize} 件");
|
||||
|
||||
$prescription = new Prescription();
|
||||
|
||||
foreach ($product['data'] as $item){
|
||||
if (!empty($item['product_pharmacy_code'])){
|
||||
$pharmacy_code = $item['pharmacy_code'] ?? '';
|
||||
$result = $prescription->getProdStock($item['product_pharmacy_code'], $pharmacy_code);
|
||||
$this->handleData($item['product_platform_id'],$item['product_platform_code'],$result[0]);
|
||||
for ($page = 1; $page <= $lastPage; $page++) {
|
||||
if ($page > 1) {
|
||||
$product = Product::getPage($params, ['*'], $page, $pageSize);
|
||||
if (empty($product['data'])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->line("商品库存更新失败:" . $e->getMessage());
|
||||
}
|
||||
|
||||
$this->line("商品库存更新成功");
|
||||
$this->logInfo(">>> 正在处理第 {$page}/{$lastPage} 页商品库存 (当前页 " . count($product['data']) . " 件)...");
|
||||
|
||||
foreach ($product['data'] as $item) {
|
||||
$stats['total']++;
|
||||
|
||||
if (empty($item['product_pharmacy_code'])) {
|
||||
$this->logWarn("【跳过】商品 [{$item['product_name']}] (ID: {$item['product_id']}) 未配置第三方药品编码(product_pharmacy_code)");
|
||||
$stats['skipped']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$pharmacy_code = $item['pharmacy_code'] ?? '';
|
||||
try {
|
||||
$result = $prescription->getProdStock($item['product_pharmacy_code'], $pharmacy_code);
|
||||
if (empty($result) || !isset($result[0])) {
|
||||
$this->logWarn("【库存响应空】商品 [{$item['product_name']}] 药房 [{$pharmacy_code}] 平台未返回库存数据");
|
||||
$stats['failed']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$res = $this->handleData($item, $result[0], $stats);
|
||||
if ($res) {
|
||||
$stats['success']++;
|
||||
} else {
|
||||
$stats['failed']++;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->logError("【查询库存失败】商品 [{$item['product_name']}] (药店编码: {$item['product_pharmacy_code']}, 药房: {$pharmacy_code}) 异常: " . $e->getMessage());
|
||||
$stats['failed']++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->logError("商品库存更新全局异常:" . $e->getMessage());
|
||||
}
|
||||
|
||||
$duration = round(microtime(true) - $startTime, 2);
|
||||
$this->logInfo("================== [getProductStockCommand] 商品库存更新完成 ==================");
|
||||
$this->logInfo("总耗时: {$duration}s | 检查总数: {$stats['total']} | 成功: {$stats['success']} (变动: {$stats['stock_changed']}, 未变: {$stats['stock_unchanged']}) | 跳过: {$stats['skipped']} | 失败: {$stats['failed']}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 入库
|
||||
* @param string $product_platform_id
|
||||
* @param string $product_platform_code
|
||||
* @param Product|array $item
|
||||
* @param array $resultData
|
||||
* @param array $stats
|
||||
* @return bool
|
||||
*/
|
||||
public function handleData(string $product_platform_id,string $product_platform_code,array $resultData): bool
|
||||
public function handleData(Product|array $item, array $resultData, array &$stats = []): bool
|
||||
{
|
||||
if (empty($resultData['quantity'])){
|
||||
$resultData['quantity'] = 0;
|
||||
}
|
||||
$quantity = (int)($resultData['quantity'] ?? 0);
|
||||
$product_platform_id = (string)$item['product_platform_id'];
|
||||
$product_platform_code = (string)$item['product_platform_code'];
|
||||
$product_name = (string)($item['product_name'] ?? '');
|
||||
$pharmacy_code = (string)($item['pharmacy_code'] ?? '');
|
||||
|
||||
try {
|
||||
Db::beginTransaction();
|
||||
|
||||
// 当前库存数量
|
||||
$stock = 0;
|
||||
|
||||
$params = array();
|
||||
$params['product_platform_id'] = $product_platform_id;
|
||||
$params['product_platform_code'] = $product_platform_code;
|
||||
$product_platform_amount = ProductPlatformAmount::getSharedLockOne($params);
|
||||
if (empty($product_platform_amount)){
|
||||
|
||||
$old_stock = 0;
|
||||
if (empty($product_platform_amount)) {
|
||||
// 无库存数据,新增
|
||||
$data = array();
|
||||
$data['product_platform_id'] = $product_platform_id;
|
||||
$data['product_platform_code'] = $product_platform_code;
|
||||
$data['stock'] = $resultData['quantity'];
|
||||
$data['stock'] = $quantity;
|
||||
$product_platform_amount = ProductPlatformAmount::addProductPlatformAmount($data);
|
||||
if (empty($product_platform_amount)){
|
||||
if (empty($product_platform_amount)) {
|
||||
Db::rollBack();
|
||||
$this->line("商品库存更新失败,无法新增库存数据" . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
$this->logError("商品 [{$product_name}] 新增库存数据失败: " . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
$stock = $product_platform_amount['stock'];
|
||||
$this->logInfo("【初始化库存】商品 [{$product_name}] 药房 [{$pharmacy_code}] 初始库存: {$quantity}");
|
||||
$stats['stock_changed']++;
|
||||
} else {
|
||||
$old_stock = (int)$product_platform_amount['stock'];
|
||||
|
||||
// 存在库存数据,修改
|
||||
$data = array();
|
||||
$data['stock'] = $resultData['quantity'];
|
||||
$data['stock'] = $quantity;
|
||||
|
||||
$params = array();
|
||||
$params['amount_id'] = $product_platform_amount['amount_id'];
|
||||
ProductPlatformAmount::edit($params,$data);
|
||||
ProductPlatformAmount::edit($params, $data);
|
||||
|
||||
if ($old_stock !== $quantity) {
|
||||
$diff = $quantity - $old_stock;
|
||||
$diffStr = ($diff > 0 ? "+{$diff}" : "{$diff}");
|
||||
$this->logInfo("【库存变动】商品 [{$product_name}] 药房 [{$pharmacy_code}] 原库存: {$old_stock} -> 最新库存: {$quantity} (变动: {$diffStr})");
|
||||
$stats['stock_changed']++;
|
||||
} else {
|
||||
$this->logInfo("【库存无变化】商品 [{$product_name}] 药房 [{$pharmacy_code}] 当前库存: {$quantity}");
|
||||
$stats['stock_unchanged']++;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取商品数据
|
||||
// 增加库存记录(仅在库存发生变动或首次初始化时记录)
|
||||
if ($old_stock !== $quantity || empty($product_platform_amount)) {
|
||||
$params = array();
|
||||
$params['product_platform_id'] = $product_platform_id;
|
||||
$product = Product::getOne($params);
|
||||
if (!empty($product)){
|
||||
// 增加库存记录
|
||||
if (!empty($product)) {
|
||||
$data = array();
|
||||
$data['product_id'] = $product['product_id'];
|
||||
$data['change_quantity'] = $resultData['quantity'] - $stock; // 库存变动的数量 变动的库存-原库存
|
||||
$data['quantity'] = $resultData['quantity']; // 变动后库存数量
|
||||
$data['change_time'] = date('Y-m-d H:i:s',time());
|
||||
$data['remark'] = "库存同步";
|
||||
$data['change_quantity'] = $quantity - $old_stock;
|
||||
$data['quantity'] = $quantity;
|
||||
$data['change_time'] = date('Y-m-d H:i:s', time());
|
||||
$data['remark'] = "处方平台库存定时同步";
|
||||
$product_amount_record = ProductAmountRecord::addProductAmountRecord($data);
|
||||
if (empty($product_amount_record)){
|
||||
if (empty($product_amount_record)) {
|
||||
Db::rollBack();
|
||||
$this->line("商品库存更新失败,增加库存记录失败" . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
$this->logError("商品 [{$product_name}] 增加库存流水记录失败: " . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollBack();
|
||||
$this->line("商品库存更新失败:" . $e->getMessage());
|
||||
$this->logError("商品 [{$product_name}] 更新库存事务异常: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
-- ==============================================================================
|
||||
-- 数据库变更脚本:多药房独立药品与库存隔离方案
|
||||
-- 适用系统:hospital-applets-api
|
||||
-- 执行说明:
|
||||
-- 1. 本脚本默认表前缀为 `gdxz_`,若数据库未配置表前缀,请全局替换删除 `gdxz_`。
|
||||
-- 2. 请在业务低峰期执行,按顺序分批执行:第一部分(新建表)-> 第二部分(修改表)-> 第三部分(数据回填初始化)。
|
||||
-- 3. 脚本具有幂等性设计(IF NOT EXISTS / ON DUPLICATE KEY UPDATE)。
|
||||
-- ==============================================================================
|
||||
|
||||
-- ==============================================================================
|
||||
-- 第一部分:新建两张药房核心表
|
||||
-- ==============================================================================
|
||||
|
||||
-- 1.1 药房基础信息表(gdxz_pharmacy)
|
||||
CREATE TABLE IF NOT EXISTS `gdxz_pharmacy` (
|
||||
`pharmacy_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '药房ID(雪花算法ID,0表示系统默认/老数据)',
|
||||
`pharmacy_name` varchar(128) NOT NULL DEFAULT '' COMMENT '药房名称',
|
||||
`pharmacy_code` varchar(64) NOT NULL DEFAULT '' COMMENT '药房编码(对接第三方药房的thirdCode/pharmacyCode)',
|
||||
`postage` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '基础运费/邮费(单位:元)',
|
||||
`free_shipping_threshold` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '满额包邮门槛(单位:元,0表示不免邮)',
|
||||
`is_pickup` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否支持到店自提(0:不支持 1:支持)',
|
||||
`telephone` varchar(32) NOT NULL DEFAULT '' COMMENT '药房联系电话',
|
||||
`province` varchar(64) NOT NULL DEFAULT '' COMMENT '省份名称',
|
||||
`city` varchar(64) NOT NULL DEFAULT '' COMMENT '城市名称',
|
||||
`county` varchar(64) NOT NULL DEFAULT '' COMMENT '区县名称',
|
||||
`address` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址',
|
||||
`pickup_address` varchar(255) NOT NULL DEFAULT '' COMMENT '自提详细地址',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0:禁用 1:启用)',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`pharmacy_id`),
|
||||
UNIQUE KEY `uk_pharmacy_code` (`pharmacy_code`),
|
||||
KEY `idx_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='药房基础信息表';
|
||||
|
||||
-- 1.2 医生-药房关联表(gdxz_doctor_pharmacy)
|
||||
CREATE TABLE IF NOT EXISTS `gdxz_doctor_pharmacy` (
|
||||
`doctor_pharmacy_id` bigint(20) unsigned NOT NULL COMMENT '关联主键ID(雪花算法ID)',
|
||||
`doctor_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '医生ID',
|
||||
`pharmacy_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '药房ID',
|
||||
`is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否默认药房(0:否 1:是)',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0:禁用/解绑 1:正常)',
|
||||
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`doctor_pharmacy_id`),
|
||||
UNIQUE KEY `uk_doctor_pharmacy` (`doctor_id`, `pharmacy_id`),
|
||||
KEY `idx_doctor_default` (`doctor_id`, `is_default`, `status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='医生-药房绑定关联表';
|
||||
|
||||
|
||||
-- ==============================================================================
|
||||
-- 第二部分:现有业务表字段扩展(ALTER TABLE)
|
||||
-- ==============================================================================
|
||||
|
||||
-- 2.1 处方主表增加药房快照字段(gdxz_order_prescription)
|
||||
ALTER TABLE `gdxz_order_prescription`
|
||||
ADD COLUMN `pharmacy_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '开方药房ID' AFTER `doctor_advice`,
|
||||
ADD COLUMN `pharmacy_code` varchar(64) NOT NULL DEFAULT '' COMMENT '开方药房编码' AFTER `pharmacy_id`,
|
||||
ADD COLUMN `pharmacy_name` varchar(128) NOT NULL DEFAULT '' COMMENT '开方药房名称' AFTER `pharmacy_code`,
|
||||
ADD KEY `idx_pharmacy_code` (`pharmacy_code`);
|
||||
|
||||
-- 2.2 药品订单表增加药房快照及配送类型字段(gdxz_order_product)
|
||||
ALTER TABLE `gdxz_order_product`
|
||||
ADD COLUMN `pharmacy_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '所属药房ID' AFTER `logistics_fee`,
|
||||
ADD COLUMN `pharmacy_code` varchar(64) NOT NULL DEFAULT '' COMMENT '所属药房编码' AFTER `pharmacy_id`,
|
||||
ADD COLUMN `pharmacy_name` varchar(128) NOT NULL DEFAULT '' COMMENT '所属药房名称' AFTER `pharmacy_code`,
|
||||
ADD COLUMN `delivery_type` tinyint(1) NOT NULL DEFAULT '2' COMMENT '配送方式(1:到店自提 2:快递配送)' AFTER `pharmacy_name`,
|
||||
ADD KEY `idx_pharmacy_code` (`pharmacy_code`),
|
||||
ADD KEY `idx_delivery_type` (`delivery_type`);
|
||||
|
||||
-- 2.3 药品表增加所属药房隔离字段(gdxz_product)
|
||||
ALTER TABLE `gdxz_product`
|
||||
ADD COLUMN `pharmacy_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '所属药房ID' AFTER `product_platform_id`,
|
||||
ADD COLUMN `pharmacy_code` varchar(64) NOT NULL DEFAULT '' COMMENT '所属药房编码' AFTER `pharmacy_id`,
|
||||
ADD KEY `idx_pharmacy_code` (`pharmacy_code`);
|
||||
|
||||
-- 2.4 处方平台药品目录表增加第三方药房编码(gdxz_product_platform)
|
||||
ALTER TABLE `gdxz_product_platform`
|
||||
ADD COLUMN `pharmacy_code` varchar(64) NOT NULL DEFAULT '' COMMENT '第三方药房编码' AFTER `product_pharmacy_code`,
|
||||
ADD KEY `idx_pharmacy_code` (`pharmacy_code`);
|
||||
|
||||
|
||||
-- ==============================================================================
|
||||
-- 第三部分:历史数据回填与初始化(DML)
|
||||
-- ==============================================================================
|
||||
|
||||
-- 3.1 初始化系统默认药房记录(pharmacy_id = 0,编码 ZD-10198)
|
||||
INSERT INTO `gdxz_pharmacy` (
|
||||
`pharmacy_id`, `pharmacy_name`, `pharmacy_code`,
|
||||
`postage`, `free_shipping_threshold`, `is_pickup`,
|
||||
`telephone`, `province`, `city`, `county`, `address`, `pickup_address`,
|
||||
`status`, `created_at`, `updated_at`
|
||||
) VALUES (
|
||||
0, '总院默认药房', 'ZD-10198',
|
||||
7.00, 0.00, 1,
|
||||
'010-87573022', '北京市', '北京市', '海淀区', '医疗机构总院院内药房', '医疗机构总院门诊楼1层药房自提窗口',
|
||||
1, NOW(), NOW()
|
||||
) ON DUPLICATE KEY UPDATE
|
||||
`pharmacy_code` = VALUES(`pharmacy_code`),
|
||||
`pharmacy_name` = VALUES(`pharmacy_name`),
|
||||
`status` = 1;
|
||||
|
||||
-- 3.2 回填现有药品表(gdxz_product)的药房归属为默认药房
|
||||
UPDATE `gdxz_product`
|
||||
SET `pharmacy_id` = 0, `pharmacy_code` = 'ZD-10198'
|
||||
WHERE `pharmacy_code` = '' OR `pharmacy_code` IS NULL;
|
||||
|
||||
-- 3.3 回填处方平台目录表(gdxz_product_platform)的药房编码
|
||||
UPDATE `gdxz_product_platform`
|
||||
SET `pharmacy_code` = 'ZD-10198'
|
||||
WHERE `pharmacy_code` = '' OR `pharmacy_code` IS NULL;
|
||||
|
||||
-- 3.4 回填历史处方表(gdxz_order_prescription)快照数据
|
||||
UPDATE `gdxz_order_prescription`
|
||||
SET `pharmacy_id` = 0, `pharmacy_code` = 'ZD-10198', `pharmacy_name` = '总院默认药房'
|
||||
WHERE `pharmacy_code` = '' OR `pharmacy_code` IS NULL;
|
||||
|
||||
-- 3.5 回填历史药品订单表(gdxz_order_product)快照与配送方式(历史订单默认为快递=2)
|
||||
UPDATE `gdxz_order_product`
|
||||
SET `pharmacy_id` = 0, `pharmacy_code` = 'ZD-10198', `pharmacy_name` = '总院默认药房', `delivery_type` = 2
|
||||
WHERE `pharmacy_code` = '' OR `pharmacy_code` IS NULL;
|
||||
|
||||
-- 3.6 为现有所有正常在职的医生,初始化绑定默认药房(避免上线后老医生无法开方)
|
||||
INSERT IGNORE INTO `gdxz_doctor_pharmacy` (
|
||||
`doctor_pharmacy_id`, `doctor_id`, `pharmacy_id`, `is_default`, `status`, `created_at`, `updated_at`
|
||||
)
|
||||
SELECT
|
||||
-- 结合时间戳与医生ID生成唯一大整数ID
|
||||
(UNIX_TIMESTAMP(NOW()) * 1000000 + (d.doctor_id % 1000000)) AS `doctor_pharmacy_id`,
|
||||
d.doctor_id,
|
||||
0 AS `pharmacy_id`,
|
||||
1 AS `is_default`,
|
||||
1 AS `status`,
|
||||
NOW() AS `created_at`,
|
||||
NOW() AS `updated_at`
|
||||
FROM `gdxz_user_doctor` d
|
||||
WHERE d.status = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `gdxz_doctor_pharmacy` dp WHERE dp.doctor_id = d.doctor_id
|
||||
);
|
||||
Reference in New Issue
Block a user