diff --git a/.env.example b/.env.example index adc7656..98c9ff7 100755 --- a/.env.example +++ b/.env.example @@ -3,6 +3,7 @@ APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=http://localhost +API_TOKEN= NOTICE_URL= GOODS_URL= LOG_CHANNEL=stack diff --git a/app/Console/Commands/HandleNotice.php b/app/Console/Commands/HandleNotice.php index e22bffe..e5452db 100644 --- a/app/Console/Commands/HandleNotice.php +++ b/app/Console/Commands/HandleNotice.php @@ -15,7 +15,7 @@ class HandleNotice extends Command implements Isolatable * * @var string */ - protected $signature = 'handle:notice'; + protected $signature = 'handle:notice {chunk=1000 : 分片}'; /** * The console command description. @@ -42,15 +42,19 @@ public function __construct() */ public function handle(): int { + $chunk = $this->argument('chunk'); $this->line('开始消息队列'); $url = env('NOTICE_URL'); - $res = apiRequest::request($url); + $res = apiRequest::request($url, [ + 'token' => env('API_TOKEN'), + ]); if (!$res['result'] || $res['errCode'] != '0000') { $this->error('request err: ' . $res['desc'] ?? ''); return 1; } $arr = $res['data']['notice_list'] ?? []; - while ($items = array_splice($arr, 0, 500)) { + $typeIds = array_flip(range(1, 5)); + while ($items = array_splice($arr, 0, $chunk)) { $ids = array_column($items, 'notice_id'); if (!$ids) { continue; @@ -74,7 +78,10 @@ public function handle(): int 'raw_content' => json_encode($data[$id]), 'created_at' => $time, ]; - $queue[$data[$id]['type']][] = $data[$id]; + if (isset($typeIds[$data[$id]['type']])) { + $queue[$data[$id]['type']][] = $data[$id]; + } + } try { Notice::insert($notices); @@ -85,8 +92,6 @@ public function handle(): int foreach ($queue as $type => $item) { \App\Jobs\notice::dispatch($type, $item); } - - } return 0; } diff --git a/app/Jobs/notice.php b/app/Jobs/notice.php index f7177a1..c926e6a 100644 --- a/app/Jobs/notice.php +++ b/app/Jobs/notice.php @@ -5,6 +5,8 @@ use App\helpers\apiRequest; use App\helpers\FormType; use App\Models\Good; +use App\Models\GoodsItem; +use Exception; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; use Illuminate\Bus\Queueable; @@ -12,25 +14,29 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\DB; +use Throwable; class notice implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public Client $client; + /** * Create a new job instance. */ - public function __construct(public int $type,public mixed $data) + public function __construct(public int $type, public mixed $data) { - $this->goodsUrl=env('GOODS_URL'); + $this->goodsUrl = env('GOODS_URL'); } - public string $goodsUrl ; + public string $goodsUrl; /** * Execute the job. * @throws GuzzleException + * @throws Throwable */ public function handle(): void { @@ -39,34 +45,111 @@ public function handle(): void $this->addGoods(); break; case 2: + case 3: + $this->changeProductState(); + break; case 4: case 5: + $this->changePrice(); + break; } } /** * @throws GuzzleException - * @throws \Exception + * @throws Exception + * @throws Throwable */ public function addGoods(): void { - $goods = []; + $goods = $goodsItems = $noticeIds = []; $this->client = new Client(); + $apiToken = env('API_TOKEN'); foreach ($this->data as $item) { - $product = apiRequest::requests($this->client,$this->goodsUrl,[ - 'itemId' =>$item['result']['itemId'], - ],FormType::json); - if($product['errCode']!='0000'){ - throw new \Exception('request item '. $item['item_id'].' err:' .$product['desc'] ?? ''); + $product = apiRequest::requests($this->client, $this->goodsUrl, [ + 'token' => $apiToken, + 'itemId' => $item['result']['itemId'], + ], FormType::json); + if ($product['errCode'] != '0000') { + throw new Exception('request item ' . $item['item_id'] . ' err:' . $product['desc'] ?? ''); } + $noticeIds[] = $item['notice_id']; $product = $product['data']['product']; $product['itemid'] = $product['itemId']; $good = new Good(); $good->fill($product); - $goods[]=$good->toArray(); + $goods[] = $good->toArray(); + $goodsItem = new GoodsItem(); + $product['item_id'] = $product['itemid']; + $product['specifications'] = json_encode($product['specifications']); + $goodsItem->fill($product); + $goodsItems[] = $goodsItem->toArray(); } - Good::insert($goods); + try { + DB::transaction(function () use ($goods, $goodsItems, $noticeIds) { + if (!Good::insert($goods) || !GoodsItem::insert($goodsItems) || !\App\Models\Notice::query()->whereIn('notice_id', $noticeIds)->update(['state' => 3])) { + throw new Exception('insert failed'); + }; + }); + } catch (Throwable $throwable) { + \App\Models\Notice::query()->whereIn('notice_id', $noticeIds) + ->update(['err_message' => $throwable->getMessage()]); + throw new $throwable; + } + } + + /** + * @throws Throwable + */ + public function changeProductState(): void + { + $ids = $idss = []; + foreach ($this->data as $item) { + $idss[] = $item['notice_id']; + $ids[$item['result']['state']][] = $item['result']['itemId']; + } + try { + DB::transaction(function () use ($ids, $idss) { + foreach ($ids as $state => $item) { + Good::query()->whereIn('itemid', $item)->update(['state' => $state]); + } + \App\Models\Notice::query()->whereIn('notice_id', $idss) + ->update(['state' => 3]); + }); + } catch (Throwable $throwable) { + \App\Models\Notice::query()->whereIn('notice_id', $idss) + ->update(['err_message' => $throwable->getMessage()]); + throw $throwable; + } + } + + /** + * @throws Throwable + */ + public function changePrice(): void + { + $updateData = array_column($this->data, 'result'); + $priceType = [4 => 'sell_price', 5 => 'settle_price']; + $tablePriceType = [4 => 'market_price', 5 => 'settlement']; + $updateData = array_map(function ($item) use ($priceType, $tablePriceType) { + $item[$tablePriceType[$this->type]] = $item[$priceType[$this->type]]; + unset($item[$priceType[$this->type]]); + return $item; + }, $updateData); + $ids = array_column($this->data, 'notice_id'); + try { + DB::transaction(function () use ($updateData, $ids) { + Good::updateBatch($updateData); + \App\Models\Notice::query()->whereIn('notice_id', $ids) + ->update(['state' => 3]); + }); + } catch (Throwable $throwable) { + \App\Models\Notice::query()->whereIn('notice_id', $updateData) + ->update(['err_message' => $throwable->getMessage()]); + throw $throwable; + } + } } diff --git a/app/Models/Base.php b/app/Models/Base.php index 98b080c..00a26a2 100644 --- a/app/Models/Base.php +++ b/app/Models/Base.php @@ -3,8 +3,49 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\DB; +/** + * @method static int update(array $values) + * @method static bool updateOrInsert(array $attributes, array $values = []) Insert or update a record matching the attributes, and fill it with values. + * @method static bool upsert(array $values, $uniqueBy, $update = null) insert new records or update the existing ones. + * @method static bool insert(array $values) + * @method static int insertOrIgnore(array $values) + * @method static int insertGetId(array $values, null|string $sequence) + * @method static int delete(mixed $id = null) + */ class Base extends Model { public $timestamps = true; + + public static function updateBatch($multipleData = []): int + { + $tableName = (new static())->getTable(); // 表名 + $firstRow = current($multipleData); + $updateColumn = array_keys($firstRow); + // 默认以id为条件更新,如果没有ID则以第一个字段为条件 + $referenceColumn = isset($firstRow['id']) ? 'id' : current($updateColumn); + unset($updateColumn[0]); + // 拼接sql语句 + $updateSql = "UPDATE " . $tableName . " SET "; + $sets = []; + $bindings = []; + foreach ($updateColumn as $uColumn) { + $setSql = "`" . $uColumn . "` = CASE "; + foreach ($multipleData as $data) { + $setSql .= "WHEN `" . $referenceColumn . "` = ? THEN ? "; + $bindings[] = $data[$referenceColumn]; + $bindings[] = $data[$uColumn]; + } + $setSql .= "ELSE `" . $uColumn . "` END "; + $sets[] = $setSql; + } + $updateSql .= implode(', ', $sets); + $whereIn = array_column($multipleData, $referenceColumn); + $bindings = array_merge($bindings, $whereIn); + $whereIn = rtrim(str_repeat('?,', count($whereIn)), ','); + $updateSql = rtrim($updateSql, ", ") . " WHERE `" . $referenceColumn . "` IN (" . $whereIn . ")"; + // 传入预处理sql语句和对应绑定数据 + return DB::update($updateSql, $bindings); + } } diff --git a/app/Models/Good.php b/app/Models/Good.php index b317bde..4e54431 100644 --- a/app/Models/Good.php +++ b/app/Models/Good.php @@ -2,12 +2,10 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasOne; /** - * @property GoodItem $goodItem + * @property GoodsItem $good_item */ class Good extends Base { @@ -15,7 +13,9 @@ class Good extends Base 'itemid','typeid','product_name','product_code','product_group', 'market_price','settlement','category_id', 'category_name' ]; - public function GoodItem() :HasOne { - return $this->hasOne(GoodItem::class,'item_id'); + + public function goodItem(): HasOne + { + return $this->hasOne(GoodsItem::class, 'item_id', 'itemid'); } } diff --git a/app/Models/GoodItem.php b/app/Models/GoodItem.php deleted file mode 100644 index d41fcbc..0000000 --- a/app/Models/GoodItem.php +++ /dev/null @@ -1,18 +0,0 @@ -belongsTo(Good::class,'item_id'); - } -} diff --git a/app/Models/GoodsItem.php b/app/Models/GoodsItem.php new file mode 100644 index 0000000..467e045 --- /dev/null +++ b/app/Models/GoodsItem.php @@ -0,0 +1,20 @@ +belongsTo(Good::class, 'item_id', 'itemid'); + } +} diff --git a/database/migrations/2024_05_24_033401_create_notices_table.php b/database/migrations/2024_05_24_033401_create_notices_table.php index 6137e01..8bb488d 100644 --- a/database/migrations/2024_05_24_033401_create_notices_table.php +++ b/database/migrations/2024_05_24_033401_create_notices_table.php @@ -15,6 +15,8 @@ public function up() { Schema::create('notices', function (Blueprint $table) { $table->id(); + $table->integer('notice_id')->unique()->comment('通知id'); + $table->integer('notice_type')->comment('通知type'); $table->text('raw_content')->comment('原始消息内容'); $table->string('err_message')->default('')->comment('处理时错误信息'); $table->tinyInteger('state')->default(1)->comment('消息状态,1待消费,2消费进行中,3处理完成'); diff --git a/database/migrations/2024_05_24_034644_create_goods_table.php b/database/migrations/2024_05_24_034644_create_goods_table.php index 669c21a..63cfa14 100644 --- a/database/migrations/2024_05_24_034644_create_goods_table.php +++ b/database/migrations/2024_05_24_034644_create_goods_table.php @@ -14,7 +14,8 @@ class CreateGoodsTable extends Migration public function up() { Schema::create('goods', function (Blueprint $table) { - $table->id('itemid')->comment('商品id'); + $table->id(); + $table->integer('itemid')->comment('商品id'); $table->integer('typeid')->comment(''); $table->string('product_name')->comment('商品名'); $table->string('product_code')->index()->comment('商品code'); @@ -23,7 +24,7 @@ public function up() $table->decimal('settlement')->comment('结算价'); $table->integer('category_id')->index()->comment('分类id'); $table->string('category_name')->comment('分类名'); - $table->tinyInteger('state')->default(10)->comment('状态 1正常 2 下架'); + $table->tinyInteger('state')->default(1)->comment('状态 1上架 0 下架'); $table->timestamps(); }); } diff --git a/database/migrations/2024_05_24_034726_create_goods_items_table.php b/database/migrations/2024_05_24_034726_create_goods_items_table.php index 8d85f62..f53cb27 100644 --- a/database/migrations/2024_05_24_034726_create_goods_items_table.php +++ b/database/migrations/2024_05_24_034726_create_goods_items_table.php @@ -18,13 +18,13 @@ public function up() $table->integer('item_id')->index(); $table->string('brand')->default('')->comment('品牌'); $table->string('color_name')->default('')->comment('颜色'); - $table->string('sell_point')->default('')->comment('卖点'); + $table->text('sell_point')->comment('卖点'); $table->string('specifications')->comment('规格'); $table->string('shop_url')->comment('链接'); $table->string('product_images')->default('')->comment('商品主图'); $table->string('images')->default('')->comment('主图列表'); - $table->string('product_infos')->default('')->comment('图文信息'); - $table->string('app_introduce')->default('')->comment('app信息'); + $table->text('product_infos')->comment('图文信息'); + $table->text('app_introduce')->comment('app图文信息'); $table->string('applet_introduce')->default('详情图片地址'); $table->timestamps(); }); diff --git a/database/migrations/2024_05_24_095735_modify_notices_table.php b/database/migrations/2024_05_24_095735_modify_notices_table.php deleted file mode 100644 index 4dd6d8c..0000000 --- a/database/migrations/2024_05_24_095735_modify_notices_table.php +++ /dev/null @@ -1,29 +0,0 @@ -integer('notice_id')->unique()->comment('通知id') - ->after('id'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::table('notices', function (Blueprint $table) { - $table->dropColumn('notice_id'); - }); - } -}; diff --git a/database/migrations/2024_05_25_120049_modify_table_notice.php b/database/migrations/2024_05_25_120049_modify_table_notice.php deleted file mode 100644 index b978232..0000000 --- a/database/migrations/2024_05_25_120049_modify_table_notice.php +++ /dev/null @@ -1,28 +0,0 @@ -integer('notice_type')->comment('通知type')->after('notice_id'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::table('notices', function (Blueprint $table) { - $table->dropColumn('notice_type'); - }); - } -}; diff --git a/database/migrations/2024_05_25_135703_modify_goods_table.php b/database/migrations/2024_05_25_135703_modify_goods_table.php deleted file mode 100644 index 2f25bdd..0000000 --- a/database/migrations/2024_05_25_135703_modify_goods_table.php +++ /dev/null @@ -1,28 +0,0 @@ -renameColumn('itemid','id'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::table('goods', function (Blueprint $table) { - $table->renameColumn('id','itemid'); - }); - } -}; diff --git a/database/migrations/2024_05_25_140215_modify_goods_table_add_itemid.php b/database/migrations/2024_05_25_140215_modify_goods_table_add_itemid.php deleted file mode 100644 index 7366137..0000000 --- a/database/migrations/2024_05_25_140215_modify_goods_table_add_itemid.php +++ /dev/null @@ -1,28 +0,0 @@ -integer('itemId')->after('id')->index()->comment('商品id'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::table('goods', function (Blueprint $table) { - $table->dropColumn('itemId'); - }); - } -};