211 lines
9.5 KiB
PHP
211 lines
9.5 KiB
PHP
<?php
|
||
|
||
namespace Tests\Feature;
|
||
|
||
use App\Models\CartModel;
|
||
use App\Models\CustomerLevelModel;
|
||
use App\Models\ProductCategoryModel;
|
||
use App\Models\ProductModel;
|
||
use App\Models\StoreModel;
|
||
|
||
/**
|
||
* 小程序商品:商品详情接口(免登录浏览,登录门店按等级上浮比例显示换算价);
|
||
* 商品列表回归:登录门店场景曾数组误用对象访问 + 价格缺失导致 500
|
||
* (门店即用户:登录主体为 StoreModel)
|
||
*/
|
||
class MiniProductTest extends ProcurementTestCase
|
||
{
|
||
/**
|
||
* 造门店 + 上架商品(成本价即售价,等级上浮 0%)
|
||
*
|
||
* @return array{0: StoreModel, 1: ProductModel, 2: CustomerLevelModel}
|
||
*/
|
||
private function makeStoreWithProduct(string $price = '5.00'): array
|
||
{
|
||
$level = CustomerLevelModel::factory()->create();
|
||
$store = StoreModel::factory()->create(['level_id' => $level->id]);
|
||
$product = ProductModel::factory()->create([
|
||
'status' => ProductModel::STATUS_ON,
|
||
'cost_price' => $price,
|
||
]);
|
||
|
||
return [$store, $product, $level];
|
||
}
|
||
|
||
/** 商品详情:未登录浏览 price=null,基础字段完整 */
|
||
public function test_product_detail_guest_sees_null_price(): void
|
||
{
|
||
[, $product] = $this->makeStoreWithProduct();
|
||
|
||
$data = $this->getJson("/mini/product/{$product->id}")->assertOk()->json('data');
|
||
|
||
$this->assertSame($product->name, $data['name']);
|
||
$this->assertSame($product->spec, $data['spec']);
|
||
$this->assertSame($product->unit, $data['unit']);
|
||
$this->assertNull($data['price'], '未登录不显示价格');
|
||
$this->assertArrayNotHasKey('cost_price', $data, '成本价不得输出到小程序端');
|
||
}
|
||
|
||
/** 商品详情:登录门店按等级显示固定价 */
|
||
public function test_product_detail_store_sees_level_price(): void
|
||
{
|
||
[$store, $product] = $this->makeStoreWithProduct('6.50');
|
||
$this->actingAsMiniStore($store);
|
||
|
||
$data = $this->getJson("/mini/product/{$product->id}")->assertOk()->json('data');
|
||
|
||
$this->assertSame('6.50', $data['price']);
|
||
}
|
||
|
||
/** 商品详情:按门店等级上浮比例换算(成本价上浮 30%) */
|
||
public function test_product_detail_percent_price_calculated_from_cost(): void
|
||
{
|
||
[$store, $product, $level] = $this->makeStoreWithProduct();
|
||
$level->update(['percent' => 30]);
|
||
$product->update(['cost_price' => '20.00']);
|
||
$this->actingAsMiniStore($store);
|
||
|
||
$data = $this->getJson("/mini/product/{$product->id}")->assertOk()->json('data');
|
||
|
||
$this->assertSame('26.00', $data['price'], '20 元上浮 30% = 26.00');
|
||
}
|
||
|
||
/** 商品详情:下架/不存在拒绝 */
|
||
public function test_product_detail_off_shelf_or_missing_rejected(): void
|
||
{
|
||
[, $product] = $this->makeStoreWithProduct();
|
||
$product->update(['status' => ProductModel::STATUS_OFF]);
|
||
|
||
$this->getJson("/mini/product/{$product->id}")->assertJsonPath('success', false);
|
||
$this->getJson('/mini/product/99999')->assertJsonPath('success', false);
|
||
}
|
||
|
||
/** 商品列表:登录门店显示等级价(回归:曾数组误用对象访问导致 500) */
|
||
public function test_product_list_store_sees_level_price(): void
|
||
{
|
||
[$store, $product] = $this->makeStoreWithProduct('7.00');
|
||
$this->actingAsMiniStore($store);
|
||
|
||
$rows = $this->getJson('/mini/product/list')->assertOk()->json('data.data');
|
||
$row = collect($rows)->firstWhere('id', $product->id);
|
||
|
||
$this->assertNotNull($row);
|
||
$this->assertSame('7.00', $row['price']);
|
||
$this->assertArrayNotHasKey('cost_price', $row, '成本价不得输出到小程序端');
|
||
}
|
||
|
||
/** 商品列表:未登录 price=null */
|
||
public function test_product_list_guest_sees_null_price(): void
|
||
{
|
||
[, $product] = $this->makeStoreWithProduct();
|
||
|
||
$rows = $this->getJson('/mini/product/list')->assertOk()->json('data.data');
|
||
$row = collect($rows)->firstWhere('id', $product->id);
|
||
|
||
$this->assertNotNull($row);
|
||
$this->assertNull($row['price']);
|
||
}
|
||
|
||
/** 商品列表:先按分类树展示顺序(深度优先),同分类内按商品 sort 升序、id 升序,未入树分类排最后 */
|
||
public function test_product_list_ordered_by_category_tree(): void
|
||
{
|
||
$rootA = ProductCategoryModel::create(['name' => '蔬菜', 'parent_id' => 0, 'sort' => 0, 'status' => 1]);
|
||
$rootB = ProductCategoryModel::create(['name' => '水果', 'parent_id' => 0, 'sort' => 1, 'status' => 1]);
|
||
// 子分类 sort 与创建顺序相反,验证按 sort 而非 id 排序
|
||
$leafA1 = ProductCategoryModel::create(['name' => '叶菜', 'parent_id' => $rootA->id, 'sort' => 1, 'status' => 1]);
|
||
$leafA2 = ProductCategoryModel::create(['name' => '根茎', 'parent_id' => $rootA->id, 'sort' => 0, 'status' => 1]);
|
||
|
||
$pB = ProductModel::factory()->create(['category_id' => $rootB->id, 'status' => 1]);
|
||
$pA1 = ProductModel::factory()->create(['category_id' => $leafA1->id, 'status' => 1]);
|
||
$pA2Low = ProductModel::factory()->create(['category_id' => $leafA2->id, 'status' => 1, 'sort' => 1]);
|
||
$pA2High = ProductModel::factory()->create(['category_id' => $leafA2->id, 'status' => 1, 'sort' => 9]);
|
||
$pNone = ProductModel::factory()->create(['category_id' => 0, 'status' => 1]);
|
||
|
||
$ids = array_column($this->getJson('/mini/product/list')->assertOk()->json('data.data'), 'id');
|
||
|
||
// 树序:rootA → leafA2 → leafA1 → rootB;leafA2 内 sort 升序;无分类排最后
|
||
$this->assertSame([$pA2Low->id, $pA2High->id, $pA1->id, $pB->id, $pNone->id], $ids);
|
||
}
|
||
|
||
/** 商品列表:登录门店附加购物车行ID与数量,响应附悬浮球汇总 */
|
||
public function test_product_list_appends_cart_quantity_and_summary(): void
|
||
{
|
||
[$store, $product] = $this->makeStoreWithProduct('5.00');
|
||
$extra = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '3.00']);
|
||
$this->actingAsMiniStore($store);
|
||
|
||
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 2]);
|
||
$this->postJson('/mini/cart', ['product_id' => $extra->id, 'quantity' => 1]);
|
||
$cartId = CartModel::where('product_id', $product->id)->first()->id;
|
||
|
||
$data = $this->getJson('/mini/product/list')->assertOk()->json('data');
|
||
$row = collect($data['data'])->firstWhere('id', $product->id);
|
||
$this->assertSame($cartId, $row['cart_id'], '在购物车的商品应附购物车行ID');
|
||
$this->assertSame('2.00', $row['cart_quantity']);
|
||
|
||
// 不在购物车的商品:cart_id=0、cart_quantity='0.00'
|
||
$other = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '1.00']);
|
||
$otherRow = collect($this->getJson('/mini/product/list')->json('data.data'))->firstWhere('id', $other->id);
|
||
$this->assertSame(0, $otherRow['cart_id']);
|
||
$this->assertSame('0.00', $otherRow['cart_quantity']);
|
||
|
||
// 悬浮球汇总:2 种商品、总数量 3、总金额 5×2+3×1=13
|
||
$this->assertSame(2, $data['cart']['total_count']);
|
||
$this->assertSame('3.00', $data['cart']['total_quantity']);
|
||
$this->assertSame('13.00', $data['cart']['total_amount']);
|
||
}
|
||
|
||
/** 商品列表/详情:未登录时购物车字段为零值结构 */
|
||
public function test_product_list_guest_sees_zero_cart_fields(): void
|
||
{
|
||
[, $product] = $this->makeStoreWithProduct();
|
||
|
||
$data = $this->getJson('/mini/product/list')->assertOk()->json('data');
|
||
$row = collect($data['data'])->firstWhere('id', $product->id);
|
||
$this->assertSame(0, $row['cart_id']);
|
||
$this->assertSame('0.00', $row['cart_quantity']);
|
||
$this->assertSame(
|
||
['total_count' => 0, 'total_quantity' => '0.00', 'total_amount' => '0.00'],
|
||
$data['cart']
|
||
);
|
||
|
||
$detail = $this->getJson("/mini/product/{$product->id}")->assertOk()->json('data');
|
||
$this->assertSame(0, $detail['cart_id']);
|
||
$this->assertSame('0.00', $detail['cart_quantity']);
|
||
}
|
||
|
||
/** 商品详情:登录门店附加购物车行ID与数量 */
|
||
public function test_product_detail_appends_cart_quantity(): void
|
||
{
|
||
[$store, $product] = $this->makeStoreWithProduct();
|
||
$this->actingAsMiniStore($store);
|
||
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 4]);
|
||
$cartId = CartModel::first()->id;
|
||
|
||
$data = $this->getJson("/mini/product/{$product->id}")->assertOk()->json('data');
|
||
$this->assertSame($cartId, $data['cart_id']);
|
||
$this->assertSame('4.00', $data['cart_quantity']);
|
||
}
|
||
|
||
/** 首页:登录门店附购物车悬浮球汇总,未登录为零值结构 */
|
||
public function test_home_appends_cart_summary(): void
|
||
{
|
||
[$store, $product] = $this->makeStoreWithProduct('5.00');
|
||
|
||
// 未登录:零值结构
|
||
$guest = $this->getJson('/mini/home')->assertOk()->json('data');
|
||
$this->assertSame(
|
||
['total_count' => 0, 'total_quantity' => '0.00', 'total_amount' => '0.00'],
|
||
$guest['cart']
|
||
);
|
||
|
||
// 登录:汇总跟随购物车
|
||
$this->actingAsMiniStore($store);
|
||
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 3]);
|
||
$data = $this->getJson('/mini/home')->assertOk()->json('data');
|
||
$this->assertSame(1, $data['cart']['total_count']);
|
||
$this->assertSame('3.00', $data['cart']['total_quantity']);
|
||
$this->assertSame('15.00', $data['cart']['total_amount']);
|
||
}
|
||
}
|