购物车悬浮

This commit is contained in:
liu
2026-08-27 18:13:43 +08:00
parent 52dc6efadf
commit f3e190f460
9 changed files with 330 additions and 6 deletions
+37
View File
@@ -265,6 +265,43 @@ class CartTest extends ProcurementTestCase
$this->getJson('/mini/cart')->assertStatus(401);
}
/** 悬浮球汇总:种数/总数量/总金额(下架商品不计入数量与金额但仍计种数) */
public function test_cart_summary_endpoint(): void
{
$level = CustomerLevelModel::factory()->create();
$store = 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' => '3.00']);
$this->actingAsMiniStore($store);
// 空购物车返回零值结构
$this->getJson('/mini/cart/summary')->assertOk()
->assertJsonPath('data.total_count', 0)
->assertJsonPath('data.total_quantity', '0.00')
->assertJsonPath('data.total_amount', '0.00');
$this->postJson('/mini/cart', ['product_id' => $p1->id, 'quantity' => 2]);
$this->postJson('/mini/cart', ['product_id' => $p2->id, 'quantity' => 1]);
$this->getJson('/mini/cart/summary')->assertOk()
->assertJsonPath('data.total_count', 2)
->assertJsonPath('data.total_quantity', '3.00')
->assertJsonPath('data.total_amount', '13.00');
// 下架商品:数量/金额不计入(与购物车列表口径一致),种数仍计
$p2->update(['status' => ProductModel::STATUS_OFF]);
$this->getJson('/mini/cart/summary')->assertOk()
->assertJsonPath('data.total_count', 2)
->assertJsonPath('data.total_quantity', '2.00')
->assertJsonPath('data.total_amount', '10.00');
}
/** 未登录访问悬浮球汇总 → 401 */
public function test_cart_summary_requires_login(): void
{
$this->getJson('/mini/cart/summary')->assertStatus(401);
}
/** 下单成功后自动清空购物车中已下单的商品(未下单商品保留) */
public function test_place_order_clears_ordered_items_from_cart(): void
{
+82
View File
@@ -2,6 +2,7 @@
namespace Tests\Feature;
use App\Models\CartModel;
use App\Models\CustomerLevelModel;
use App\Models\ProductModel;
use App\Models\StoreModel;
@@ -103,4 +104,85 @@ class MiniProductTest extends ProcurementTestCase
$this->assertNotNull($row);
$this->assertNull($row['price']);
}
/** 商品列表:登录门店附加购物车行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']);
}
}
+73
View File
@@ -2,8 +2,10 @@
namespace Tests\Feature;
use App\Models\BillModel;
use App\Models\CustomerLevelModel;
use App\Models\ProductModel;
use App\Models\PurchaseOrderModel;
use App\Models\StoreModel;
use App\Models\StoreOrderModel;
use Modules\SystemTool\Models\SysFileModel;
@@ -112,6 +114,77 @@ class StoreOrderTest extends ProcurementTestCase
$this->assertSame(StoreOrderModel::STATUS_SUMMARIZED, $order->refresh()->status);
}
/** 造一笔指定账单日期/状态的门店账单(默认总额 100.00 未支付) */
private function makeStoreBill(StoreModel $store, string $billDate, array $attributes = []): BillModel
{
return BillModel::create(array_merge([
'bill_no' => 'ZD' . random_int(100000000000, 999999999999),
'purchase_id' => PurchaseOrderModel::factory()->create()->id,
'store_id' => $store->id,
'bill_date' => $billDate,
'total_amount' => '100.00',
'status' => BillModel::STATUS_UNPAID,
], $attributes));
}
/** 回款周期校验:存在逾期未回款账单(含审核中)时禁止下单,结清后恢复 */
public function test_place_order_blocked_by_overdue_unpaid_bill(): void
{
$level = CustomerLevelModel::factory()->create();
$store = StoreModel::factory()->paymentCycle(7)->create(['level_id' => $level->id]);
$product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '5.00']);
// 账单日 10 天前(应结算日=3 天前,已逾期)且未支付 → 拒绝
$this->makeStoreBill($store, now()->subDays(10)->toDateString());
$this->actingAsMiniStore($store);
$response = $this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]]);
$response->assertOk()->assertJsonPath('success', false);
$this->assertStringContainsString('已超过回款周期未回款', (string) $response->json('msg'));
$this->assertStringContainsString('¥100.00', (string) $response->json('msg'));
$this->assertSame(0, StoreOrderModel::count(), '拦截时不应生成订单');
// 审核中(已提交付款凭证)同样拦截
$this->makeStoreBill($store, now()->subDays(10)->toDateString(), ['payment_id' => 999]);
$blocked = $this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]]);
$blocked->assertJsonPath('success', false);
$this->assertStringContainsString('2 笔', (string) $blocked->json('msg'));
// 全部结清后恢复下单
BillModel::where('store_id', $store->id)->update(['status' => BillModel::STATUS_PAID]);
$this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]])
->assertJsonPath('success', true);
}
/** 回款周期校验:周期内账单与到期日当天均不拦截;回款周期 0 天=任何未回款账单(含当天)都拦截 */
public function test_place_order_allowed_within_payment_cycle(): void
{
$level = CustomerLevelModel::factory()->create();
$store = StoreModel::factory()->paymentCycle(7)->create(['level_id' => $level->id]);
$product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'cost_price' => '5.00']);
$this->actingAsMiniStore($store);
// 账单日 7 天前 → 应结算日=今天,尚未超过周期,允许下单
$this->makeStoreBill($store, now()->subDays(7)->toDateString());
$this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]])
->assertJsonPath('success', true);
// 回款周期 0 天:昨天的未回款账单拦截;当天的未回款账单同样拦截(立即结清口径)
$store->update(['payment_cycle_days' => 0]);
$this->makeStoreBill($store, now()->subDays(1)->toDateString());
$this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]])
->assertJsonPath('success', false);
BillModel::where('store_id', $store->id)->delete();
$this->makeStoreBill($store, now()->toDateString());
$response = $this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]]);
$response->assertJsonPath('success', false);
$this->assertStringContainsString('当日结清', (string) $response->json('msg'));
// 0 天周期下无未回款账单 → 正常下单
BillModel::where('store_id', $store->id)->update(['status' => BillModel::STATUS_PAID]);
$this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 1]]])
->assertJsonPath('success', true);
}
/** 门店数据隔离:只能查看本店订单 */
public function test_order_data_isolated_between_stores(): void
{