Przeglądaj źródła

feat(goods): 支持应用场景与商品规格

- 新增商家管理的“应用场景”功能,并将其与商品进行关联
- 实现商品规格持久化,并强制要求买家下单时选择生效的规格
- API 层面:商品详情和创建订单接口新增对 useCaseIdList、specList 和 specGoodsId 的支持
- 数据库层面:新增“应用场景表”及“商品-应用场景关联表”
shizhongqi 1 miesiąc temu
rodzic
commit
15c50988a2

+ 16 - 2
app-hd/controllers/GoodsController.php

@@ -86,8 +86,19 @@ class GoodsController extends BaseController
         $data['flower'] = 1;
         $staff = $this->shopAdmin;
         $data['staffName'] = $staff->name ?? '';
-        $return = GoodsClass::addGoods($data);
-        util::success($return);
+        $connection = Yii::$app->db;
+        $transaction = $connection->beginTransaction();
+        try {
+            $return = GoodsClass::addGoods($data);
+            GoodsClass::syncUseCases($return->id, $this->mainId, $data['useCaseIdList'] ?? []);
+            GoodsClass::syncSpecs($return, $data);
+            $transaction->commit();
+            util::success($return);
+        } catch (\Exception $e) {
+            Yii::error('商品添加失败:' . $e->getMessage(), 'goods.add');
+            $transaction->rollBack();
+            util::fail();
+        }
     }
 
     //根据sn获取商品详情 ssh 20220505
@@ -172,6 +183,7 @@ class GoodsController extends BaseController
         $params = ['getOriginalPrice' => 1];
         $goods = GoodsClass::getGoodsInfo($id, $shop, $custom, $params);
         GoodsClass::valid($goods, $this->mainId);
+        $goods = GoodsClass::appendUseCasesAndSpecs($goods, $this->mainId);
         util::success($goods);
     }
 
@@ -261,6 +273,8 @@ class GoodsController extends BaseController
         try {
             $staff = $this->shopAdmin;
             GoodsClass::updateGoods($info, $data, $staff);
+            GoodsClass::syncUseCases($id, $this->mainId, $data['useCaseIdList'] ?? []);
+            GoodsClass::syncSpecs($info, $data);
             $transaction->commit();
             util::complete();
         } catch (\Exception $e) {

+ 24 - 0
app-hd/controllers/UseCaseController.php

@@ -0,0 +1,24 @@
+<?php
+namespace hd\controllers;
+use Yii;
+use common\components\util;
+use bizHd\goods\classes\UseCaseClass;
+
+class UseCaseController extends BaseController
+{
+    public function actionList() { util::success(UseCaseClass::getAllByCondition(['mainId'=>$this->mainId, 'delStatus'=>0], 'inTurn DESC', '*')); }
+    public function actionAdd() {
+        $post = Yii::$app->request->post(); $name = trim($post['useCaseName'] ?? '');
+        if ($name === '') util::fail('请输入使用场景名称');
+        $item = UseCaseClass::addUseCase(['mainId'=>$this->mainId,'sjId'=>$this->sjId,'useCaseName'=>$name,'inTurn'=>(int)($post['inTurn']??0),'status'=>(int)($post['status']??1),'delStatus'=>0]);
+        util::success(['id'=>$item->id]);
+    }
+    public function actionUpdate() {
+        $post=Yii::$app->request->post(); $id=(int)($post['useCaseId']??0); $item=UseCaseClass::getById($id);
+        if (!$item || $item['mainId'] != $this->mainId) util::fail('您无法操作');
+        $name=trim($post['useCaseName']??''); if ($name==='') util::fail('请输入使用场景名称');
+        UseCaseClass::updateById($id,['useCaseName'=>$name,'inTurn'=>(int)($post['inTurn']??0),'status'=>(int)($post['status']??1)]); util::complete('修改成功');
+    }
+    public function actionChangeStatus() { $id=(int)Yii::$app->request->get('useCaseId',0); $item=UseCaseClass::getById($id); if(!$item||$item['mainId']!=$this->mainId) util::fail('您无法操作'); UseCaseClass::updateById($id,['status'=>(int)Yii::$app->request->get('status',0)]); util::complete(); }
+    public function actionDelete() { $id=(int)Yii::$app->request->get('useCaseId',0); $item=UseCaseClass::getById($id); if(!$item||$item['mainId']!=$this->mainId) util::fail('您无法操作'); UseCaseClass::updateById($id,['delStatus'=>1]); util::complete('删除成功'); }
+}

+ 105 - 3
app-hd/models/goods/AddForm.php

@@ -98,6 +98,9 @@ class AddForm extends BaseForm
      * @var int
      */
     public $autoRise;
+    public $useCaseIdList;
+    public $specEnabled;
+    public $specList;
 
 
     /**
@@ -108,15 +111,21 @@ class AddForm extends BaseForm
         return [
             // 必填字段验证
             [['name'], 'required', 'message' => '请填写商品名称'],
-            [['priceType'], 'required', 'message' => '请选择价格类型'],
+            [['priceType'], 'required', 'message' => '请选择价格类型', 'when' => function($model) {
+                return (int)$model->specEnabled !== 1;
+            }],
             [['catIds'], 'required', 'message' => '请选择商品分类'],
-            ['weight', 'required', 'message' => '请填写重量'],
+            ['weight', 'required', 'message' => '请填写重量', 'when' => function($model) {
+                return (int)$model->specEnabled !== 1;
+            }],
 
             // 默认值设置
             ['price', 'default', 'value' => 0],
             ['flowerNum', 'default', 'value' => 0],
             ['sold', 'default', 'value' => 0],
             ['catIds', 'default', 'value' => []],
+            ['useCaseIdList', 'default', 'value' => []],
+            ['specEnabled', 'default', 'value' => 0],
             
             // 数据类型验证
             [['name', 'cover'], 'string'],
@@ -131,6 +140,9 @@ class AddForm extends BaseForm
             [['freightType'], 'in', 'range' => [0, 1]],
             [['status'], 'in', 'range' => [0, 1]],
             [['autoRise'], 'in', 'range' => [0, 1]],
+            [['specEnabled'], 'in', 'range' => [0, 1]],
+            [['useCaseIdList'], 'validateIdList'],
+            [['specList'], 'validateSpecList'],
 
             // 添加条件验证
 //            ['price', 'required', 'message' => '请填写商品价格', 'when' => function($model) {
@@ -157,6 +169,96 @@ class AddForm extends BaseForm
         }
     }
 
+    public function validateIdList($attribute) {
+        if (!is_array($this->$attribute)) $this->addError($attribute, '使用场景格式不正确');
+    }
+
+    public function validateSpecList($attribute)
+    {
+        if ((int)$this->specEnabled !== 1) {
+            $this->$attribute = [];
+            return;
+        }
+        $list = $this->$attribute;
+        if (is_string($list)) {
+            $list = json_decode($list, true);
+        }
+        if (!is_array($list) || empty($list)) {
+            $this->addError($attribute, '请至少填写一个规格');
+            return;
+        }
+        $formatList = [];
+        foreach ($list as $index => $spec) {
+            $name = '规格' . ($index + 1);
+            if (!is_array($spec)) {
+                $this->addError($attribute, $name . '格式错误');
+                return;
+            }
+            $specName = trim($spec['specName'] ?? '');
+            if ($specName === '') {
+                $this->addError($attribute, '请填写' . $name . '名称');
+                return;
+            }
+            $priceType = $spec['priceType'] ?? null;
+            if ($priceType === null || !in_array((int)$priceType, [0, 1], true)) {
+                $this->addError($attribute, $name . '价格类型错误');
+                return;
+            }
+            $price = $spec['price'] ?? 0;
+            if ((int)$priceType === 1 && (!is_numeric($price) || $price <= 0)) {
+                $this->addError($attribute, '请填写' . $name . '价格');
+                return;
+            }
+            $stockSet = $spec['stockSet'] ?? 0;
+            if (!in_array((int)$stockSet, [0, 1], true)) {
+                $this->addError($attribute, $name . '库存类型错误');
+                return;
+            }
+            $stock = $spec['stock'] ?? 0;
+            if ((int)$stockSet === 1 && (!is_numeric($stock) || (int)$stock < 0 || (int)$stock != $stock)) {
+                $this->addError($attribute, $name . '库存数量错误');
+                return;
+            }
+            $flowerNum = $spec['flowerNum'] ?? 0;
+            if (!is_numeric($flowerNum) || (int)$flowerNum < 0 || (int)$flowerNum != $flowerNum) {
+                $this->addError($attribute, $name . '花支数错误');
+                return;
+            }
+            $weight = $spec['weight'] ?? 1;
+            if (!is_numeric($weight) || $weight < 0) {
+                $this->addError($attribute, $name . '重量错误');
+                return;
+            }
+            $sold = $spec['sold'] ?? 0;
+            if (!is_numeric($sold) || (int)$sold < 0 || (int)$sold != $sold) {
+                $this->addError($attribute, $name . '已售数量错误');
+                return;
+            }
+            $formatList[] = [
+                'id' => (int)($spec['id'] ?? 0),
+                'specName' => $specName,
+                'priceType' => (int)$priceType,
+                'price' => (int)$priceType === 1 ? (float)$price : 0,
+                'stockSet' => (int)$stockSet,
+                'stock' => (int)$stockSet === 1 ? (int)$stock : 9999,
+                'flowerNum' => (int)$flowerNum,
+                'weight' => (float)$weight,
+                'sold' => (int)$sold,
+                'shopImg' => is_array($spec['shopImg'] ?? null) ? $spec['shopImg'] : [],
+                'cover' => trim($spec['cover'] ?? ''),
+            ];
+        }
+        $this->$attribute = $formatList;
+        $firstSpec = $formatList[0];
+        $this->priceType = $firstSpec['priceType'];
+        $this->price = $firstSpec['price'];
+        $this->stockSet = $firstSpec['stockSet'];
+        $this->stock = $firstSpec['stock'];
+        $this->flowerNum = $firstSpec['flowerNum'];
+        $this->weight = $firstSpec['weight'];
+        $this->sold = $firstSpec['sold'];
+    }
+
     /**
      * 获取属性标签
      * @return array
@@ -176,4 +278,4 @@ class AddForm extends BaseForm
             'autoRise' => '自动涨价',
         ];
     }
-} 
+}

+ 98 - 0
app-hd/models/goods/UpdateForm.php

@@ -116,6 +116,9 @@ class UpdateForm extends BaseForm
      * @var int
      */
     public $freightType;
+    public $useCaseIdList;
+    public $specEnabled;
+    public $specList;
 
     /**
      * {@inheritdoc}
@@ -156,6 +159,11 @@ class UpdateForm extends BaseForm
             ['delStatus', 'default', 'value' => 0],
             ['categoryIdList', 'default', 'value' => []],
             ['stock', 'default', 'value' => 0],
+            ['useCaseIdList', 'default', 'value' => []],
+            ['specEnabled', 'default', 'value' => 0],
+            [['specEnabled'], 'in', 'range' => [0, 1]],
+            [['useCaseIdList'], 'validateUseCaseIdList'],
+            [['specList'], 'validateSpecList'],
         ];
     }
 
@@ -183,6 +191,96 @@ class UpdateForm extends BaseForm
         }
     }
 
+    public function validateUseCaseIdList($attribute) {
+        if (!is_array($this->$attribute)) $this->addError($attribute, '使用场景格式不正确');
+    }
+
+    public function validateSpecList($attribute)
+    {
+        if ((int)$this->specEnabled !== 1) {
+            $this->$attribute = [];
+            return;
+        }
+        $list = $this->$attribute;
+        if (is_string($list)) {
+            $list = json_decode($list, true);
+        }
+        if (!is_array($list) || empty($list)) {
+            $this->addError($attribute, '请至少填写一个规格');
+            return;
+        }
+        $formatList = [];
+        foreach ($list as $index => $spec) {
+            $name = '规格' . ($index + 1);
+            if (!is_array($spec)) {
+                $this->addError($attribute, $name . '格式错误');
+                return;
+            }
+            $specName = trim($spec['specName'] ?? '');
+            if ($specName === '') {
+                $this->addError($attribute, '请填写' . $name . '名称');
+                return;
+            }
+            $priceType = $spec['priceType'] ?? null;
+            if ($priceType === null || !in_array((int)$priceType, [0, 1], true)) {
+                $this->addError($attribute, $name . '价格类型错误');
+                return;
+            }
+            $price = $spec['price'] ?? 0;
+            if ((int)$priceType === 1 && (!is_numeric($price) || $price <= 0)) {
+                $this->addError($attribute, '请填写' . $name . '价格');
+                return;
+            }
+            $stockSet = $spec['stockSet'] ?? 0;
+            if (!in_array((int)$stockSet, [0, 1], true)) {
+                $this->addError($attribute, $name . '库存类型错误');
+                return;
+            }
+            $stock = $spec['stock'] ?? 0;
+            if ((int)$stockSet === 1 && (!is_numeric($stock) || (int)$stock < 0 || (int)$stock != $stock)) {
+                $this->addError($attribute, $name . '库存数量错误');
+                return;
+            }
+            $flowerNum = $spec['flowerNum'] ?? 0;
+            if (!is_numeric($flowerNum) || (int)$flowerNum < 0 || (int)$flowerNum != $flowerNum) {
+                $this->addError($attribute, $name . '花支数错误');
+                return;
+            }
+            $weight = $spec['weight'] ?? 1;
+            if (!is_numeric($weight) || $weight < 0) {
+                $this->addError($attribute, $name . '重量错误');
+                return;
+            }
+            $sold = $spec['sold'] ?? 0;
+            if (!is_numeric($sold) || (int)$sold < 0 || (int)$sold != $sold) {
+                $this->addError($attribute, $name . '已售数量错误');
+                return;
+            }
+            $formatList[] = [
+                'id' => (int)($spec['id'] ?? 0),
+                'specName' => $specName,
+                'priceType' => (int)$priceType,
+                'price' => (int)$priceType === 1 ? (float)$price : 0,
+                'stockSet' => (int)$stockSet,
+                'stock' => (int)$stockSet === 1 ? (int)$stock : 9999,
+                'flowerNum' => (int)$flowerNum,
+                'weight' => (float)$weight,
+                'sold' => (int)$sold,
+                'shopImg' => is_array($spec['shopImg'] ?? null) ? $spec['shopImg'] : [],
+                'cover' => trim($spec['cover'] ?? ''),
+            ];
+        }
+        $this->$attribute = $formatList;
+        $firstSpec = $formatList[0];
+        $this->priceType = $firstSpec['priceType'];
+        $this->price = $firstSpec['price'];
+        $this->stockSet = $firstSpec['stockSet'];
+        $this->stock = $firstSpec['stock'];
+        $this->flowerNum = $firstSpec['flowerNum'];
+        $this->weight = $firstSpec['weight'];
+        $this->sold = $firstSpec['sold'];
+    }
+
     /**
      * 获取属性标签
      * @return array

+ 2 - 1
app-mall/controllers/GoodsController.php

@@ -36,6 +36,7 @@ class GoodsController extends BaseController
         if ($info['mainId'] != $this->mainId) {
             util::fail('不是你的商品');
         }
+        $info = \bizHd\goods\classes\GoodsClass::appendUseCasesAndSpecs($info, $this->mainId);
 
         $setting = GoodsSettingClass::getByCondition(['mainId' => $this->mainId]);
         $intro = $setting['goodsIntroduce'] ?? '';
@@ -56,4 +57,4 @@ class GoodsController extends BaseController
         util::success($info);
     }
 
-}
+}

+ 2 - 1
app-mall/controllers/HdController.php

@@ -96,10 +96,11 @@ class HdController extends BaseController
                 $custom = \bizMall\custom\classes\CustomClass::getById($customId, true);
             }
 
+            $itemType = '';
+            $itemList = [];
             // 根据 xhShop 中的 `rechargeWeal`来选择返回快捷充值选项
             if ($shop->rechargeWeal == 1) {
                 $itemType = 'money'; //送钱
-                $itemList = [];
                 $weal = RechargeSqClass::getAllByCondition(['mainId' => $shop->mainId], 'recharge asc', '*', null, true);
                 if (!empty($weal)) {
                     foreach ($weal as $key => $val) {

+ 13 - 1
app-mall/controllers/OrderController.php

@@ -577,6 +577,7 @@ class OrderController extends BaseController
         }
 
         $goodsId = $post['goodsId'] ?? 0;
+        $specGoodsId = (int)($post['specGoodsId'] ?? 0);
         $goodsNum = isset($post['goodsNum']) && $post['goodsNum'] > 0 ? $post['goodsNum'] : 1;
         $sendType = $post['sendType'] ?? dict::getDict('sendType', 'thirdSend');
         if (empty($goodsId)) {
@@ -590,6 +591,16 @@ class OrderController extends BaseController
         if ($goodsInfo->mainId != $mainId) {
             util::fail('不是你的商品');
         }
+        if ($specGoodsId > 0) {
+            $specGoods = GoodsClass::getById($specGoodsId, true);
+            if (empty($specGoods) || $specGoods->mainId != $mainId || $specGoods->masterId != $goodsId || $specGoods->delStatus != 0 || $specGoods->status != 1) {
+                util::fail('规格不可用');
+            }
+            $goodsInfo = $specGoods;
+        } elseif ((int)($goodsInfo->masterId ?? 0) === 0) {
+            $hasSpecs = GoodsClass::getByCondition(['masterId'=>$goodsId, 'delStatus'=>0], true);
+            if (!empty($hasSpecs)) util::fail('请选择规格');
+        }
         $stock = $goodsInfo->stock ?? 0;
         if ($stock <= 0) {
             util::fail('库存不足');
@@ -867,7 +878,8 @@ class OrderController extends BaseController
             $post['mainId'] = $mainId;
             $post['needPrint'] = dict::getDict('needPrint', 'need');
             $hasPay = 0;
-            $product = [['productId' => $goodsId, 'unitType' => 0, 'property' => 0, 'unitPrice' => $unitPrice, 'num' => $goodsNum]];
+            $saleGoodsId = $specGoodsId > 0 ? $specGoodsId : $goodsId;
+            $product = [['productId' => $saleGoodsId, 'unitType' => 0, 'property' => 0, 'unitPrice' => $unitPrice, 'num' => $goodsNum]];
             $post['product'] = $product;
             $return = \bizHd\order\services\OrderService::createHdOrder($post, $custom, $hasPay);
 

+ 2 - 1
biz-hd/goods/classes/GoodsCategoryClass.php

@@ -50,6 +50,7 @@ class GoodsCategoryClass extends BaseClass
             $query->andWhere(['like', 'xhGoods.name', $askInfo['search']]);
         }
         $query->joinWith(['goods' => function ($query) use ($askInfo, $requestType) {
+            $query->andWhere(['xhGoods.masterId' => 0]);
 
             if ($requestType == 'kd') {
                 //花束开单
@@ -152,4 +153,4 @@ class GoodsCategoryClass extends BaseClass
 
         return $result;
     }
-}
+}

+ 64 - 1
biz-hd/goods/classes/GoodsClass.php

@@ -29,6 +29,68 @@ class GoodsClass extends BaseClass
 
     const LOCK_STOCK_GOODS = 'lock_stock_goods_';//修改库存锁
 
+    public static function syncUseCases($goodsId, $mainId, $ids)
+    {
+        $ids = array_values(array_unique(array_filter(array_map('intval', is_array($ids) ? $ids : []))));
+        $valid = UseCaseClass::getAllByCondition(['mainId'=>$mainId, 'delStatus'=>0, 'status'=>1], null, 'id', 'id');
+        foreach ($ids as $id) if (!isset($valid[$id])) util::fail('使用场景不可用');
+        GoodsUseCaseClass::deleteByCondition(['goodsId'=>$goodsId]);
+        foreach ($ids as $id) GoodsUseCaseClass::add(['mainId'=>$mainId, 'goodsId'=>$goodsId, 'useCaseId'=>$id]);
+    }
+
+    public static function appendUseCasesAndSpecs($goods, $mainId)
+    {
+        $id = $goods['id'] ?? 0;
+        $relations = GoodsUseCaseClass::getAllByCondition(['goodsId'=>$id], null, 'useCaseId');
+        $goods['useCaseIdList'] = array_map('intval', array_column($relations, 'useCaseId'));
+        $specs = self::getAllByCondition(['masterId'=>$id, 'delStatus'=>0], 'id ASC', '*');
+        foreach ($specs as &$spec) {
+            $spec = business::formatGoodsImg($spec);
+            $shortCover = $spec['cover'] ?? '';
+            $spec['shortCover'] = $shortCover;
+            $spec['cover'] = $shortCover;
+        }
+        unset($spec);
+        $goods['specEnabled'] = empty($specs) ? 0 : 1;
+        $goods['specList'] = $specs;
+        return $goods;
+    }
+
+    public static function syncSpecs($master, $data)
+    {
+        $masterId = is_array($master) ? ($master['id'] ?? 0) : ($master->id ?? 0);
+        $enabled = (int)($data['specEnabled'] ?? 0);
+        $old = self::getAllByCondition(['masterId'=>$masterId, 'delStatus'=>0], null, '*', 'id');
+        if (!$enabled) {
+            if (!empty($old)) self::updateByCondition(['masterId'=>$masterId], ['delStatus'=>1]);
+            return;
+        }
+        $base = self::getById($masterId);
+        if (empty($base)) {
+            $base = is_array($master) ? $master : $master->attributes;
+        }
+        unset($base['id'], $base['sn']);
+        $list = $data['specList'] ?? [];
+        $kept = [];
+        foreach ($list as $spec) {
+            $id = (int)($spec['id'] ?? 0); $kept[] = $id;
+            $fields = $base;
+            unset($fields['createTime']);
+            $fields = array_merge($fields, ['specName'=>trim($spec['specName'] ?? ''),'price'=>(float)($spec['price'] ?? 0),'priceType'=>(int)($spec['priceType'] ?? 0),'stock'=>(int)($spec['stock'] ?? 0),'stockSet'=>(int)($spec['stockSet'] ?? 0),'flowerNum'=>(int)($spec['flowerNum'] ?? 0),'weight'=>(float)($spec['weight'] ?? 1),'sold'=>(int)($spec['sold'] ?? 0),'masterId'=>$masterId,'status'=>(int)($data['status'] ?? 1),'delStatus'=>0]);
+            $specImg = $spec['shopImg'] ?? [];
+            if (is_array($specImg) && !empty($specImg)) {
+                $fields['shopImg'] = json_encode($specImg);
+                $fields['cover'] = !empty($spec['cover']) && in_array($spec['cover'], $specImg, true) ? $spec['cover'] : $specImg[0];
+            }
+            if ($id && isset($old[$id])) self::updateById($id, $fields);
+            else {
+                $child = $fields; $child['sn'] = orderSn::getGoodsSn($child); $child['createTime']=date('Y-m-d H:i:s'); self::add($child, true);
+            }
+        }
+        $remove = array_diff(array_keys($old), array_filter($kept));
+        if ($remove) \bizHd\goods\models\Goods::updateAll(['delStatus'=>1], ['and', ['masterId'=>$masterId], ['in', 'id', $remove]]);
+    }
+
     public static function orderCreateGoods($data)
     {
         $name = $data['name'] ?? '';
@@ -324,6 +386,7 @@ class GoodsClass extends BaseClass
         $query = GoodsCategory::find()
             ->where($where)
             ->joinWith(['goods' => function ($query) use ($flowerNum) {
+                $query->andWhere(['xhGoods.masterId' => 0]);
                 $query->andWhere(['>', 'xhGoods.stock', 0]);
                 if (isset($flowerNum) && $flowerNum > 0) {
                     $query->andWhere(['xhGoods.flowerNum' => $flowerNum]);
@@ -908,4 +971,4 @@ class GoodsClass extends BaseClass
         return $goods;
     }
 
-}
+}

+ 4 - 0
biz-hd/goods/classes/GoodsUseCaseClass.php

@@ -0,0 +1,4 @@
+<?php
+namespace bizHd\goods\classes;
+use bizHd\base\classes\BaseClass;
+class GoodsUseCaseClass extends BaseClass { public static $baseFile = '\\bizHd\\goods\\models\\GoodsUseCase'; }

+ 13 - 0
biz-hd/goods/classes/UseCaseClass.php

@@ -0,0 +1,13 @@
+<?php
+namespace bizHd\goods\classes;
+use bizHd\base\classes\BaseClass;
+use common\components\util;
+class UseCaseClass extends BaseClass
+{
+    public static $baseFile = '\\bizHd\\goods\\models\\UseCase';
+    public static function addUseCase($data) {
+        $exists = self::getByCondition(['mainId' => $data['mainId'], 'useCaseName' => $data['useCaseName'], 'delStatus' => 0]);
+        if ($exists) util::fail('使用场景名称已经存在');
+        return self::add($data, true);
+    }
+}

+ 4 - 0
biz-hd/goods/models/GoodsUseCase.php

@@ -0,0 +1,4 @@
+<?php
+namespace bizHd\goods\models;
+use bizHd\base\models\Base;
+class GoodsUseCase extends Base { public static function tableName() { return 'xhGoodsUseCaseRelation'; } }

+ 4 - 0
biz-hd/goods/models/UseCase.php

@@ -0,0 +1,4 @@
+<?php
+namespace bizHd\goods\models;
+use bizHd\base\models\Base;
+class UseCase extends Base { public static function tableName() { return 'xhGoodsUseCase'; } }

+ 1 - 0
biz-hd/goods/services/GoodsService.php

@@ -123,6 +123,7 @@ class GoodsService extends BaseService
         // 构建基础查询
         $query = GoodsCategory::find()
             ->joinWith(['goods' => function ($query) use ($where) {
+                $query->andWhere(['xhGoods.masterId' => 0]);
                 // 商品表的筛选条件
                 if (isset($where['mainId'])) {
                     $query->andWhere(['xhGoods.mainId' => $where['mainId']]);

+ 20 - 0
sql/20260714_goods_use_case.sql

@@ -0,0 +1,20 @@
+CREATE TABLE `xhGoodsUseCase` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `mainId` int(11) NOT NULL DEFAULT 0,
+  `sjId` int(11) NOT NULL DEFAULT 0,
+  `useCaseName` varchar(50) NOT NULL DEFAULT '',
+  `inTurn` int(11) NOT NULL DEFAULT 0,
+  `status` tinyint(4) NOT NULL DEFAULT 1,
+  `delStatus` tinyint(4) NOT NULL DEFAULT 0,
+  `addTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `updateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`), KEY `mainId` (`mainId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE `xhGoodsUseCaseRelation` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `mainId` int(11) NOT NULL DEFAULT 0,
+  `goodsId` int(11) NOT NULL DEFAULT 0,
+  `useCaseId` int(11) NOT NULL DEFAULT 0,
+  PRIMARY KEY (`id`), UNIQUE KEY `goods_use_case` (`goodsId`,`useCaseId`), KEY `mainId` (`mainId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;