create(); $levelB = CustomerLevelModel::factory()->create(); $store = StoreModel::factory()->create(['level_id' => $levelA->id]); $product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON]); ProductPriceModel::factory()->create(['product_id' => $product->id, 'level_id' => $levelA->id, 'price' => 5.50]); ProductPriceModel::factory()->create(['product_id' => $product->id, 'level_id' => $levelB->id, 'price' => 3.00]); $this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create()); $response = $this->getJson('/mini/product/list'); $response->assertOk()->assertJsonPath('success', true); $row = collect($response->json('data.data'))->firstWhere('id', $product->id); $this->assertNotNull($row, '商品应出现在列表中'); $this->assertSame(5.50, (float) $row['price'], '应返回门店所在等级的价格'); } /** 门店未设置客户等级:仍可浏览商品,但价格不可见(price=null) */ public function test_store_without_level_sees_products_without_price(): void { $store = StoreModel::factory()->create(['level_id' => 0]); $product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON]); $this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create()); $response = $this->getJson('/mini/product/list'); $response->assertOk()->assertJsonPath('success', true); $row = collect($response->json('data.data'))->firstWhere('id', $product->id); $this->assertNotNull($row, '未设等级的门店仍应能看到商品'); $this->assertNull($row['price'], '未设等级时价格应为 null'); } /** 未绑定门店:可浏览商品与分类,但价格不可见;加购仍被拦截(购买前置校验不变) */ public function test_unbound_user_can_browse_products_without_price_but_cannot_cart(): void { $product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON]); $this->actingAsMiniUser(UserModel::factory()->create()); // type=0 待绑定 $list = $this->getJson('/mini/product/list'); $list->assertOk()->assertJsonPath('success', true); $row = collect($list->json('data.data'))->firstWhere('id', $product->id); $this->assertNotNull($row, '未绑定门店应能看到商品'); $this->assertNull($row['price'], '未绑定门店不得看到价格'); $this->getJson('/mini/product/categories') ->assertOk() ->assertJsonPath('success', true); $this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 1]) ->assertJsonPath('success', false) ->assertJsonPath('msg', '尚未绑定门店,请联系客服处理'); } /** 批量调价:事务写入 + 通知受影响门店用户(不影响无关门店) */ public function test_batch_price_updates_and_notifies_affected_stores(): void { $this->actingAsSysUser(); $level = CustomerLevelModel::factory()->create(); $affectedStore = StoreModel::factory()->create(['level_id' => $level->id]); $unaffectedStore = StoreModel::factory()->create(['level_id' => CustomerLevelModel::factory()->create()->id]); $affectedUser = UserModel::factory()->forStore($affectedStore->id)->create(); $unaffectedUser = UserModel::factory()->forStore($unaffectedStore->id)->create(); $product = ProductModel::factory()->create(); ProductPriceModel::factory()->create(['product_id' => $product->id, 'level_id' => $level->id, 'price' => 5.00]); $this->putJson('/product/goods/batchPrice', [ 'updates' => [ ['product_id' => $product->id, 'level_id' => $level->id, 'price' => 8.80], ], ])->assertOk()->assertJsonPath('success', true); $this->assertSame( 8.80, (float) ProductPriceModel::forProductLevel($product->id, $level->id)->first()->price, '等级价格应已更新' ); $this->assertSame( 1, NoticeModel::where('user_id', $affectedUser->id)->where('type', NoticeModel::TYPE_PRICE)->count(), '受影响门店用户应收到价格变更通知' ); $this->assertSame( 0, NoticeModel::where('user_id', $unaffectedUser->id)->count(), '无关门店用户不应收到通知' ); } /** 价格矩阵:行=商品 × 列=启用等级 */ public function test_price_matrix_structure(): void { $this->actingAsSysUser(); $level = CustomerLevelModel::factory()->create(); $product = ProductModel::factory()->create(); ProductPriceModel::factory()->create(['product_id' => $product->id, 'level_id' => $level->id, 'price' => 6.00]); $response = $this->getJson('/product/goods/priceMatrix'); $response->assertOk()->assertJsonPath('success', true); $this->assertNotEmpty($response->json('data.levels')); $row = collect($response->json('data.rows'))->firstWhere('id', $product->id); $this->assertNotNull($row); $this->assertSame(6.0, (float) $row['price_' . $level->id]); } }