小程序用户改用门店登录

This commit is contained in:
liu
2026-08-20 13:46:04 +08:00
parent 6d9f02d831
commit c1d490134e
251 changed files with 689 additions and 2727 deletions
+52 -90
View File
@@ -6,17 +6,17 @@ use App\Models\CartModel;
use App\Models\CustomerLevelModel;
use App\Models\ProductModel;
use App\Models\StoreModel;
use App\Models\UserModel;
/**
* 小程序购物车:加购合并、等级上浮价/金额服务端计算、归属校验、数据隔离、清空
* (门店即用户:购物车按 store_id 归属)
*/
class CartTest extends ProcurementTestCase
{
/**
* 造一家门店 + 一个上架商品(成本价即售价,等级上浮 0%)+ 该店用户
* 造一家门店(含等级)+ 一个上架商品(成本价即售价,等级上浮 0%)
*
* @return array{0: StoreModel, 1: ProductModel, 2: UserModel}
* @return array{0: StoreModel, 1: ProductModel}
*/
private function makeStoreWithProduct(string $price = '5.00'): array
{
@@ -27,14 +27,14 @@ class CartTest extends ProcurementTestCase
'cost_price' => $price,
]);
return [$store, $product, UserModel::factory()->forStore($store->id)->create()];
return [$store, $product];
}
/** 加购成功:数量入库、返回购物车项 */
public function test_add_to_cart_succeeds(): void
{
[, $product, $user] = $this->makeStoreWithProduct('5.50');
$this->actingAsMiniUser($user);
[$store, $product] = $this->makeStoreWithProduct('5.50');
$this->actingAsMiniStore($store);
$this->postJson('/mini/cart', [
'product_id' => $product->id,
@@ -46,15 +46,15 @@ class CartTest extends ProcurementTestCase
$cart = CartModel::first();
$this->assertNotNull($cart);
$this->assertSame($user->id, $cart->user_id);
$this->assertSame($store->id, $cart->store_id);
$this->assertSame('2.50', (string) $cart->quantity);
}
/** 重复加购同一商品合并累加(同商品唯一行) */
public function test_duplicate_add_merges_quantity(): void
{
[, $product, $user] = $this->makeStoreWithProduct();
$this->actingAsMiniUser($user);
[$store, $product] = $this->makeStoreWithProduct();
$this->actingAsMiniStore($store);
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 2]);
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 3])
@@ -67,9 +67,9 @@ class CartTest extends ProcurementTestCase
/** 已下架商品拒绝加购 */
public function test_off_shelf_product_rejected(): void
{
[, $product, $user] = $this->makeStoreWithProduct();
[$store, $product] = $this->makeStoreWithProduct();
$product->update(['status' => ProductModel::STATUS_OFF]);
$this->actingAsMiniUser($user);
$this->actingAsMiniStore($store);
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 1])
->assertOk()
@@ -78,25 +78,11 @@ class CartTest extends ProcurementTestCase
$this->assertSame(0, CartModel::count());
}
/** 已软删除商品拒绝加购 */
public function test_soft_deleted_product_rejected(): void
/** 门店未设置客户等级时拒绝加购 */
public function test_store_without_level_rejected(): void
{
[, $product, $user] = $this->makeStoreWithProduct();
$product->delete();
$this->actingAsMiniUser($user);
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 1])
->assertJsonPath('success', false);
$this->assertSame(0, CartModel::count());
}
/** 门店绑定的客户等级被删除时拒绝加购 */
public function test_store_level_missing_rejected(): void
{
[$store, $product, $user] = $this->makeStoreWithProduct();
CustomerLevelModel::whereKey($store->level_id)->delete();
$this->actingAsMiniUser($user);
[, $product] = $this->makeStoreWithProduct();
$this->actingAsMiniStore(StoreModel::factory()->create(['level_id' => 0]));
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 1])
->assertJsonPath('success', false)
@@ -105,28 +91,6 @@ class CartTest extends ProcurementTestCase
$this->assertSame(0, CartModel::count());
}
/** 未绑定门店的用户拒绝加购 */
public function test_unbound_user_rejected(): void
{
[, $product] = $this->makeStoreWithProduct();
$this->actingAsMiniUser(UserModel::factory()->create());
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 1])
->assertJsonPath('success', false)
->assertJsonPath('msg', '尚未绑定门店,请联系客服处理');
}
/** 门店未设置客户等级时拒绝加购 */
public function test_store_without_level_rejected(): void
{
[, $product] = $this->makeStoreWithProduct();
$store = StoreModel::factory()->create(['level_id' => 0]);
$this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create());
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 1])
->assertJsonPath('success', false);
}
/** 列表:实时等级上浮价、服务端金额、汇总(同店两商品,新加入在前) */
public function test_list_with_prices_amounts_and_totals(): void
{
@@ -134,7 +98,7 @@ class CartTest extends ProcurementTestCase
$store = StoreModel::factory()->create(['level_id' => $level->id]);
$p1 = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '5.50']);
$p2 = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '3.00']);
$this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create());
$this->actingAsMiniStore($store);
$this->postJson('/mini/cart', ['product_id' => $p1->id, 'quantity' => 3]);
$this->postJson('/mini/cart', ['product_id' => $p2->id, 'quantity' => 2]);
@@ -163,7 +127,7 @@ class CartTest extends ProcurementTestCase
$store = StoreModel::factory()->create(['level_id' => $level->id]);
$p1 = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '5.50']);
$p2 = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '3.00']);
$this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create());
$this->actingAsMiniStore($store);
$this->postJson('/mini/cart', ['product_id' => $p1->id, 'quantity' => 3]);
$this->postJson('/mini/cart', ['product_id' => $p2->id, 'quantity' => 2]);
@@ -181,7 +145,7 @@ class CartTest extends ProcurementTestCase
public function test_empty_cart_returns_empty_items(): void
{
$store = StoreModel::factory()->create(['level_id' => CustomerLevelModel::factory()->create()->id]);
$this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create());
$this->actingAsMiniStore($store);
$this->getJson('/mini/cart')->assertOk()
->assertJsonPath('data.items', [])
@@ -192,8 +156,8 @@ class CartTest extends ProcurementTestCase
/** 修改数量 */
public function test_update_quantity(): void
{
[, $product, $user] = $this->makeStoreWithProduct();
$this->actingAsMiniUser($user);
[$store, $product] = $this->makeStoreWithProduct();
$this->actingAsMiniStore($store);
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 1]);
$cart = CartModel::first();
@@ -201,30 +165,30 @@ class CartTest extends ProcurementTestCase
->assertJsonPath('success', true)
->assertJsonPath('data.quantity', '7.00');
$this->assertSame('7.00', (string) $cart->fresh()->quantity);
$this->assertSame('7.00', (string) $cart->refresh()->quantity);
}
/** 修改他购物车项拒绝(同店另一用户) */
public function test_update_other_users_item_rejected(): void
/** 修改他购物车项拒绝 */
public function test_update_other_stores_item_rejected(): void
{
[$store, $product, $userA] = $this->makeStoreWithProduct();
$this->actingAsMiniUser($userA);
[$store, $product] = $this->makeStoreWithProduct();
$this->actingAsMiniStore($store);
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 1]);
$cart = CartModel::first();
$this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create());
$this->actingAsMiniStore(StoreModel::factory()->create());
$this->putJson("/mini/cart/{$cart->id}", ['quantity' => 9])
->assertJsonPath('success', false)
->assertJsonPath('msg', '购物车项不存在');
$this->assertSame('1.00', (string) $cart->fresh()->quantity, '他改数量不应生效');
$this->assertSame('1.00', (string) $cart->refresh()->quantity, '他改数量不应生效');
}
/** 修改不存在的购物车项拒绝 */
public function test_update_missing_item_rejected(): void
{
[$store, $product, $user] = $this->makeStoreWithProduct();
$this->actingAsMiniUser($user);
[$store, $product] = $this->makeStoreWithProduct();
$this->actingAsMiniStore($store);
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 1]);
$this->putJson('/mini/cart/999999', ['quantity' => 1])->assertJsonPath('success', false);
@@ -233,8 +197,8 @@ class CartTest extends ProcurementTestCase
/** 数量校验:0 与负值拒绝 */
public function test_invalid_quantity_rejected(): void
{
[, $product, $user] = $this->makeStoreWithProduct();
$this->actingAsMiniUser($user);
[$store, $product] = $this->makeStoreWithProduct();
$this->actingAsMiniStore($store);
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 0])
->assertOk()
@@ -247,8 +211,8 @@ class CartTest extends ProcurementTestCase
/** 删除单项 */
public function test_delete_item(): void
{
[, $product, $user] = $this->makeStoreWithProduct();
$this->actingAsMiniUser($user);
[$store, $product] = $this->makeStoreWithProduct();
$this->actingAsMiniStore($store);
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 1]);
$cart = CartModel::first();
@@ -256,15 +220,15 @@ class CartTest extends ProcurementTestCase
$this->assertSame(0, CartModel::count());
}
/** 删除他购物车项拒绝 */
public function test_delete_other_users_item_rejected(): void
/** 删除他购物车项拒绝 */
public function test_delete_other_stores_item_rejected(): void
{
[$store, $product, $userA] = $this->makeStoreWithProduct();
$this->actingAsMiniUser($userA);
[$store, $product] = $this->makeStoreWithProduct();
$this->actingAsMiniStore($store);
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 1]);
$cart = CartModel::first();
$this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create());
$this->actingAsMiniStore(StoreModel::factory()->create());
$this->deleteJson("/mini/cart/{$cart->id}")
->assertJsonPath('success', false)
->assertJsonPath('msg', '购物车项不存在');
@@ -272,28 +236,27 @@ class CartTest extends ProcurementTestCase
$this->assertSame(1, CartModel::count());
}
/** 清空购物车仅影响当前用户 */
/** 清空购物车仅影响本店 */
public function test_clear_only_own_cart(): void
{
$level = CustomerLevelModel::factory()->create();
$store = StoreModel::factory()->create(['level_id' => $level->id]);
$storeA = StoreModel::factory()->create(['level_id' => $level->id]);
$storeB = StoreModel::factory()->create(['level_id' => $level->id]);
$p1 = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '5.00']);
$p2 = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '5.00']);
$userA = UserModel::factory()->forStore($store->id)->create();
$userB = UserModel::factory()->forStore($store->id)->create();
$this->actingAsMiniUser($userA);
$this->actingAsMiniStore($storeA);
$this->postJson('/mini/cart', ['product_id' => $p1->id, 'quantity' => 1]);
$this->postJson('/mini/cart', ['product_id' => $p2->id, 'quantity' => 1]);
$this->actingAsMiniUser($userB);
$this->actingAsMiniStore($storeB);
$this->postJson('/mini/cart', ['product_id' => $p1->id, 'quantity' => 1]);
$this->actingAsMiniUser($userA);
$this->actingAsMiniStore($storeA);
$this->deleteJson('/mini/cart')->assertJsonPath('success', true);
$this->assertSame(0, CartModel::where('user_id', $userA->id)->count());
$this->assertSame(1, CartModel::where('user_id', $userB->id)->count(), '他购物车不受影响');
$this->assertSame(0, CartModel::where('store_id', $storeA->id)->count());
$this->assertSame(1, CartModel::where('store_id', $storeB->id)->count(), '他购物车不受影响');
}
/** 未登录访问购物车 → 401 */
@@ -302,19 +265,18 @@ class CartTest extends ProcurementTestCase
$this->getJson('/mini/cart')->assertStatus(401);
}
/** 跨用户列表隔离:B 看不到 A 的购物车 */
public function test_cart_isolated_between_users(): void
/** 跨门店列表隔离:B 看不到 A 的购物车 */
public function test_cart_isolated_between_stores(): void
{
$level = CustomerLevelModel::factory()->create();
$store = StoreModel::factory()->create(['level_id' => $level->id]);
$storeA = StoreModel::factory()->create(['level_id' => $level->id]);
$storeB = StoreModel::factory()->create(['level_id' => $level->id]);
$product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '5.00']);
$userA = UserModel::factory()->forStore($store->id)->create();
$userB = UserModel::factory()->forStore($store->id)->create();
$this->actingAsMiniUser($userA);
$this->actingAsMiniStore($storeA);
$this->postJson('/mini/cart', ['product_id' => $product->id, 'quantity' => 1]);
$this->actingAsMiniUser($userB);
$this->actingAsMiniStore($storeB);
$this->getJson('/mini/cart')->assertOk()
->assertJsonPath('data.items', [])
->assertJsonPath('data.total_count', 0);