0, 'name' => $name, 'sort' => 1, 'status' => ProductCategoryModel::STATUS_NORMAL, ]); } /** 顶级分类下可创建子分类(二级) */ public function test_create_child_category_under_top_level(): void { $this->actingAsSysUser(); $top = $this->makeTopCategory(); $this->postJson('/product/category', [ 'parent_id' => $top->id, 'name' => '叶菜类', ])->assertOk()->assertJsonPath('success', true); $this->assertDatabaseHas('product_category', ['name' => '叶菜类', 'parent_id' => $top->id]); } /** 二级分类不可作为父级(最多二级) */ public function test_create_category_under_second_level_rejected(): void { $this->actingAsSysUser(); $top = $this->makeTopCategory(); $child = ProductCategoryModel::create([ 'parent_id' => $top->id, 'name' => '叶菜类', 'sort' => 1, 'status' => ProductCategoryModel::STATUS_NORMAL, ]); $this->postJson('/product/category', [ 'parent_id' => $child->id, 'name' => '菠菜', ])->assertOk() ->assertJsonPath('success', false) ->assertJsonPath('msg', '最多支持二级分类,不能选择子分类作为上级'); } /** 编辑:二级分类可平级移动到另一个顶级分类下 */ public function test_update_second_level_move_to_another_top(): void { $this->actingAsSysUser(); $topA = $this->makeTopCategory('蔬菜'); $topB = $this->makeTopCategory('水果'); $child = ProductCategoryModel::create([ 'parent_id' => $topA->id, 'name' => '叶菜类', 'sort' => 1, 'status' => ProductCategoryModel::STATUS_NORMAL, ]); $this->putJson('/product/category/' . $child->id, [ 'parent_id' => $topB->id, 'name' => '叶菜类', ])->assertOk()->assertJsonPath('success', true); $this->assertSame($topB->id, (int) $child->fresh()->parent_id); } /** 编辑:含子分类的分类不能调整为子分类(否则子分类变三级) */ public function test_update_parent_with_children_cannot_move_under_category(): void { $this->actingAsSysUser(); $top = $this->makeTopCategory('蔬菜'); $other = $this->makeTopCategory('水果'); ProductCategoryModel::create([ 'parent_id' => $top->id, 'name' => '叶菜类', 'sort' => 1, 'status' => ProductCategoryModel::STATUS_NORMAL, ]); $this->putJson('/product/category/' . $top->id, [ 'parent_id' => $other->id, 'name' => '蔬菜', ])->assertOk() ->assertJsonPath('success', false) ->assertJsonPath('msg', '该分类下存在子分类,不能调整为子分类'); } /** 编辑:父级不存在时提示 */ public function test_update_with_missing_parent_rejected(): void { $this->actingAsSysUser(); $top = $this->makeTopCategory(); $this->putJson('/product/category/' . $top->id, [ 'parent_id' => 99999, 'name' => '蔬菜', ])->assertOk() ->assertJsonPath('success', false) ->assertJsonPath('msg', '父级分类不存在'); } /** 商品只能挂在末级分类:选择有子分类的分类创建商品 → 验证失败 */ public function test_create_product_with_parent_category_rejected(): void { $this->actingAsSysUser(); $top = $this->makeTopCategory('蔬菜'); ProductCategoryModel::create([ 'parent_id' => $top->id, 'name' => '叶菜类', 'sort' => 1, 'status' => ProductCategoryModel::STATUS_NORMAL, ]); $this->postJson('/product/goods', [ 'category_id' => $top->id, 'name' => '大白菜', ])->assertOk() ->assertJsonPath('success', false) ->assertJsonPath('msg', '该分类下存在子分类,请选择末级分类'); } /** 商品挂在末级分类(无子分类)可正常创建 */ public function test_create_product_with_leaf_category_ok(): void { $this->actingAsSysUser(); $top = $this->makeTopCategory('蔬菜'); $leaf = ProductCategoryModel::create([ 'parent_id' => $top->id, 'name' => '叶菜类', 'sort' => 1, 'status' => ProductCategoryModel::STATUS_NORMAL, ]); $this->postJson('/product/goods', [ 'category_id' => $leaf->id, 'name' => '大白菜', 'content' => '图文详情', ])->assertOk()->assertJsonPath('success', true); } /** 小程序分类树:返回全部启用分类(含无商品的分类),停用分类不返回 */ public function test_mini_categories_returns_all_enabled_categories(): void { $top = $this->makeTopCategory('蔬菜'); $empty = $this->makeTopCategory('水产'); // 无任何商品 ProductCategoryModel::create([ 'parent_id' => $top->id, 'name' => '叶菜类', 'sort' => 1, 'status' => ProductCategoryModel::STATUS_NORMAL, ]); $disabled = ProductCategoryModel::create([ 'parent_id' => 0, 'name' => '停用分类', 'sort' => 1, 'status' => ProductCategoryModel::STATUS_DISABLED, ]); $this->actingAsMiniStore(StoreModel::factory()->create()); $response = $this->getJson('/mini/product/categories'); $response->assertOk()->assertJsonPath('success', true); $ids = collect($response->json('data'))->pluck('id'); $this->assertContains($top->id, $ids); $this->assertContains($empty->id, $ids, '无商品的分类也应返回'); $this->assertNotContains($disabled->id, $ids, '停用分类不应返回'); } }