Просмотр исходного кода

Merge branch 'redesign‌-260706' into dev

shizhongqi 1 месяц назад
Родитель
Сommit
a07d27c07a

+ 134 - 9
app-ghs/controllers/OrderController.php

@@ -611,19 +611,144 @@ class OrderController extends BaseController
         util::checkRepeatCommit($id, 3);
 
         $order = OrderClass::getById($id, true);
-        if (empty($order)) {
+        OrderClass::valid($order, $this->shopId);
+        if ($order->payStatus == 1) {
             util::success(['returnStatus' => 'SUCCESS']);
+        }else {
+            util::success(['returnStatus' => 'unPay']);
         }
-        if ($order->status == OrderClass::ORDER_STATUS_COMPLETE) {
-            util::complete('订单已付款');
+        OrderClass::valid($order, $this->shopId);
+        $cgId = $order->purchaseId ?? 0;
+        $cg = PurchaseClass::getById($cgId, true);
+        $orderSn = $cg->orderSn ?? '';
+        $totalFee = $cg->actPrice ?? 0;
+        if (empty($orderSn)) {
+            util::complete('没有找到采购单');
         }
-        if ($order->status == OrderClass::ORDER_STATUS_CANCEL) {
-            util::complete('订单已取消');
+
+        $connection = Yii::$app->db;
+        $transaction = $connection->beginTransaction();
+        try {
+
+            $capitalType = dict::getDict('capitalType', 'xhPurchase', 'id');
+
+            $shop = $this->shop;
+
+            $merchantPrivateKeyPath = Yii::getAlias("@vendor/lakala") . '/production/api_private_key.pem';
+            $lklCertificatePath = Yii::getAlias("@vendor/lakala") . '/production/lkl-apigw-v1.cer';
+            $params = [
+                'appid' => 'OP00002119',
+                'serial_no' => '018b08cfddbd',
+                'merchant_no' => $shop->lklSjNo,
+                'term_no' => $shop->lklScanTermNo,
+                'merchantPrivateKeyPath' => $merchantPrivateKeyPath,
+                'lklCertificatePath' => $lklCertificatePath,
+            ];
+            $laResource = new Lakala($params);
+            $scanParams = ['orderSn' => $orderSn,];
+            $response = $laResource->query($scanParams);
+
+            if (isset($response['code']) && $response['code'] == 'BBS00000') {
+                if (isset($response['resp_data']['trade_state']) && $response['resp_data']['trade_state'] == 'SUCCESS') {
+                    //支付成功
+                    $transactionId = $response['resp_data']['trade_no'] ?? '';
+                    $openId = '';
+                    $aliUserId = '';
+
+                    $cg->onlinePay = dict::getDict('onlinePay', 'yes');
+                    $cg->codePay = 1;
+                    $cg->payOpenId = $openId;
+                    $cg->aliUserId = $aliUserId;
+                    //自取订单
+                    $cg->getType = PurchaseClass::GET_TYPE_SELF_GET;
+                    $cg->save();
+
+                    $order->onlinePay = dict::getDict('onlinePay', 'yes');
+                    $order->codePay = 1;
+                    //自取订单
+                    $order->sendType = OrderClass::SEND_TYPE_NO;
+                    $order->save();
+
+                    $payWayType = dict::getDict('payWay', 'wxPay');
+                    $account_type = $response['resp_data']['account_type'] ?? '';
+                    if ($account_type == 'WECHAT') {
+                        $payWayType = dict::getDict('payWay', 'wxPay');
+                    }
+                    if ($account_type == 'ALIPAY') {
+                        $payWayType = dict::getDict('payWay', 'alipay');
+                    }
+                    $order->payWay = $payWayType;
+                    $order->save();
+                    $cg->payWay = $payWayType;
+                    $cg->save();
+
+                    $attach = '';
+                    payUtil::thirdPay($payWayType, $capitalType, $orderSn, $totalFee, $attach, $transactionId);
+                    $transaction->commit();
+
+                    $saleId = $order->id ?? 0;
+                    $order = OrderClass::getById($saleId, true);
+                    if (!empty($order)) {
+
+                        //解决扫码付重复打印问题
+                        $cacheKey = 'scan_pay_print_order_' . $orderSn;
+                        $has = Yii::$app->redis->executeCommand('GET', [$cacheKey]);
+                        if (!empty($has)) {
+                            return false;
+                        }
+                        Yii::$app->redis->executeCommand('SETEX', [$cacheKey, 1, 'has']);
+
+                        //订单生成时唤起在线打印
+                        if (isset($order->needPrint) && $order->needPrint == dict::getDict('needPrint', 'need')) {
+                            if ($order->status != 1 && $order->status != 5) {
+                                OrderClass::onlinePrint($order);
+                                $ext = $this->shopExt;
+                                if (isset($ext->printSn) && !empty($ext->printSn)) {
+                                    $order->printNum += 1;
+                                    $order->save();
+                                }
+                            }
+                        }
+
+                        //付款成功之后呼叫跑腿,关键词 pay_after_call_pt,多处要同步修改
+                        GhsDeliveryOrderClass::payAfter($order);
+
+                        ShopExtClass::ghsGatheringReport($order);
+                    }
+                    util::success(['returnStatus' => 'SUCCESS']);
+                } else {
+                    util::complete('未知状态..');
+                }
+            } else {
+                util::complete('未知状态....');
+            }
+        } catch (\Exception $e) {
+            $transaction->rollBack();
+            noticeUtil::push('查询订单状态失败:' . $e->getMessage(), '15280215347');
+            util::fail('支付失败');
         }
-        if ($order->status != OrderClass::ORDER_STATUS_UN_PAY) {
-            util::complete('订单不是待付款状态');
+    }
+
+    //微信和支付宝付款码支付复查 ssh 20260716
+    public function actionCodePayVerify()
+    {
+        ini_set('date.timezone', 'Asia/Shanghai');
+        header("Content-type: text/html; charset=utf-8");
+        $get = Yii::$app->request->get();
+        $id = $get['id'] ?? 0;
+
+        util::checkRepeatCommit($id, 3);
+
+        $order = OrderClass::getById($id, true);
+        if (empty($order)) {
+            util::complete('没有找到订单呢');
         }
         OrderClass::valid($order, $this->shopId);
+        if ($order->payStatus == 1) {
+            util::success(['returnStatus' => 'SUCCESS']);
+        } else {
+            util::success(['returnStatus' => 'unPay']);
+        }
         $cgId = $order->purchaseId ?? 0;
         $cg = PurchaseClass::getById($cgId, true);
         $orderSn = $cg->orderSn ?? '';
@@ -723,10 +848,10 @@ class OrderController extends BaseController
                     }
                     util::success(['returnStatus' => 'SUCCESS']);
                 } else {
-                    util::complete('未知状态..');
+                    util::success(['returnStatus' => 'PENDING'], '等待支付中...');
                 }
             } else {
-                util::complete('未知状态....');
+                util::success(['returnStatus' => 'PENDING'], '等待支付中...');
             }
         } catch (\Exception $e) {
             $transaction->rollBack();

+ 2 - 2
app-hd/controllers/NoticeController.php

@@ -226,7 +226,7 @@ class NoticeController extends PublicController
 
                                 if (getenv('YII_ENV') == 'production') {
                                     //福州我要花、花路鲜花批发、小齐鲜花、花镜打二次小票,有多处要修改搜索关键词tow_print
-                                    $needTwoPrint = [7538, 36707, 76796];
+                                    $needTwoPrint = [7538, 36707, 76796, 94589];
                                 } else {
                                     $needTwoPrint = [644];
                                 }
@@ -451,7 +451,7 @@ class NoticeController extends PublicController
 
                                 if (getenv('YII_ENV') == 'production') {
                                     //福州我要花、花路鲜花批发、花镜打二次小票,有多处要修改搜索关键词tow_print
-                                    $needTwoPrint = [7538, 36707, 76796];
+                                    $needTwoPrint = [7538, 36707, 76796, 94589];
                                 } else {
                                     $needTwoPrint = [];
                                 }

+ 103 - 11
app-hd/controllers/OrderController.php

@@ -279,8 +279,8 @@ class OrderController extends BaseController
     public function actionBatchFetch()
     {
         $where = ["status" => 2, "sendType" => 1, "shopId" => $this->shopId];
-        $orderList = OrderService::getLimitList("id",$where,500,"id desc");
-        $ids = ArrayHelper::getColumn($orderList,"id");
+        $orderList = OrderService::getLimitList("id", $where, 500, "id desc");
+        $ids = ArrayHelper::getColumn($orderList, "id");
         OrderService::updateByIds($ids, ["status" => 4]);
         util::complete($ids);
     }
@@ -351,14 +351,10 @@ class OrderController extends BaseController
         OrderClass::valid($order, $this->mainId);
         $orderSn = $order->orderSn ?? '';
         $totalFee = $order->mainPay ?? 0;
-        if ($order->status == OrderClass::ORDER_STATUS_COMPLETE) {
+        if ($order->payStatus == 1) {
             util::success(['returnStatus' => 'SUCCESS']);
-        }
-        if ($order->status == OrderClass::ORDER_STATUS_CANCEL) {
-            util::complete('订单已取消');
-        }
-        if ($order->status != OrderClass::ORDER_STATUS_UN_PAY) {
-            util::complete('订单不是待付款状态');
+        } else {
+            util::success(['returnStatus' => 'unPay']);
         }
 
         $connection = Yii::$app->db;
@@ -437,6 +433,102 @@ class OrderController extends BaseController
         }
     }
 
+    //微信和支付宝付款码支付复查 ssh 20260716
+    public function actionCodePayVerify()
+    {
+        ini_set('date.timezone', 'Asia/Shanghai');
+        header("Content-type: text/html; charset=utf-8");
+        $get = Yii::$app->request->get();
+        $id = $get['id'] ?? 0;
+
+        util::checkRepeatCommit($id, 3);
+
+        $order = OrderClass::getById($id, true);
+        OrderClass::valid($order, $this->mainId);
+        $orderSn = $order->orderSn ?? '';
+        $totalFee = $order->mainPay ?? 0;
+        if ($order->payStatus == 1) {
+            util::success(['returnStatus' => 'SUCCESS']);
+        } else {
+            util::success(['returnStatus' => 'unPay']);
+        }
+
+        $connection = Yii::$app->db;
+        $transaction = $connection->beginTransaction();
+        try {
+
+            $capitalType = dict::getDict('capitalType', 'xhOrder', 'id');
+
+            $shop = $this->shop;
+            $merchantPrivateKeyPath = Yii::getAlias("@vendor/lakala") . '/production/api_private_key.pem';
+            $lklCertificatePath = Yii::getAlias("@vendor/lakala") . '/production/lkl-apigw-v1.cer';
+            $params = [
+                'appid' => 'OP00002119',
+                'serial_no' => '018b08cfddbd',
+                'merchant_no' => $shop->lklSjNo,
+                'term_no' => $shop->lklScanTermNo,
+                'merchantPrivateKeyPath' => $merchantPrivateKeyPath,
+                'lklCertificatePath' => $lklCertificatePath,
+            ];
+            $laResource = new Lakala($params);
+            $scanParams = ['orderSn' => $orderSn,];
+            $response = $laResource->query($scanParams);
+
+            if (isset($response['code']) && $response['code'] == 'BBS00000') {
+                if (isset($response['resp_data']['trade_state']) && $response['resp_data']['trade_state'] == 'SUCCESS') {
+
+                    $payWayType = dict::getDict('payWay', 'wxPay');
+                    $account_type = $response['resp_data']['account_type'] ?? '';
+                    if ($account_type == 'WECHAT') {
+                        $payWayType = dict::getDict('payWay', 'wxPay');
+                    }
+                    if ($account_type == 'ALIPAY') {
+                        $payWayType = dict::getDict('payWay', 'alipay');
+                    }
+
+                    $transactionId = $response['resp_data']['trade_no'] ?? '';
+                    $order->onlinePay = dict::getDict('onlinePay', 'yes');
+                    $order->save();
+                    $attach = '';
+                    payUtil::thirdPay($payWayType, $capitalType, $orderSn, $totalFee, $attach, $transactionId);
+                    $transaction->commit();
+
+                    //解决重复通知
+                    $cacheKey = 'hd_shop_order_pay_check_' . $orderSn;
+                    $has = Yii::$app->redis->executeCommand('GET', [$cacheKey]);
+                    if (empty($has)) {
+                        Yii::$app->redis->executeCommand('SETEX', [$cacheKey, 300, 'has']);
+
+                        //打印小票和语音播报
+                        $newOrder = OrderClass::getById($id, true);
+                        OrderClass::onlinePrint($newOrder);
+                        ShopExtClass::hdGatheringReport($newOrder);
+
+                        //到店自取订单,并且是待取货状态,则自动完成取货,多处要同步修改,关键词 reach_shop_fetch_goods
+                        if ($newOrder->getGoods == 1 && $newOrder->sendType == 1 && $newOrder->status == 2) {
+                            $newOrder->status = 4;
+                            $newOrder->save();
+                        }
+
+                        HdDeliveryOrderClass::payAfter($newOrder);
+
+                        $shopId = $newOrder->shopId ?? 0;
+                        $shop = ShopClass::getById($shopId, true);
+                        WxMessageClass::gatheringIncomeInform($shop, $newOrder);
+                    }
+
+                    util::success(['returnStatus' => 'SUCCESS']);
+                } else {
+                    util::success(['returnStatus' => 'PENDING'], '等待支付中...');
+                }
+            }
+            util::success(['returnStatus' => 'PENDING'], '等待支付中...');
+        } catch (\Exception $e) {
+            $transaction->rollBack();
+            util::complete('支付失败');
+        }
+    }
+
     /**
      * 微信和支付宝付款码支付
      * 职责:处理商户扫描客户付款码(被扫支付)的请求,调用拉卡拉支付接口并更新本地订单状态(零售端)
@@ -870,7 +962,7 @@ class OrderController extends BaseController
                         if ($hasPay != dict::getDict('hasPay', 'balance')) {
                             util::fail('用红包要用余额支付');
                         } else {
-                            if($custom->balance < $post['modifyPrice']) {
+                            if ($custom->balance < $post['modifyPrice']) {
                                 util::fail('用红包要用余额支付(您的余额不足)');
                             }
                         }
@@ -1361,7 +1453,7 @@ class OrderController extends BaseController
         }
         $list = OrderService::getOrderList($where);
         $list['shop'] = $this->shop->attributes ?? [];
-        
+
         util::success($list);
     }
 

+ 91 - 1
app-mall/controllers/CategoryController.php

@@ -3,6 +3,8 @@
 namespace mall\controllers;
 
 use bizHd\custom\classes\CustomClass;
+use bizHd\goods\classes\GoodsUseCaseClass;
+use bizHd\goods\classes\UseCaseClass;
 use bizMall\goods\services\CategoryService;
 use Yii;
 use common\components\util;
@@ -10,7 +12,7 @@ use common\components\util;
 class CategoryController extends BaseController
 {
 
-    public $guestAccess = ['list']; // 'goods-list' ---- 为什么不用认证
+    public $guestAccess = ['list', 'use-case-list']; // 'goods-list' ---- 为什么不用认证
 
     //获取分类 ssh 2019.12.2
     public function actionList()
@@ -62,7 +64,95 @@ class CategoryController extends BaseController
         $params = [];
         //调用花店端的方法,后面再分离 (为了代码维护可以不分离,免得一处改了,另一处没修改到。--- 所以分离好后,又还原了)
         $data = \bizHd\goods\classes\GoodsCategoryClass::getGoodsList($get, $shop, $custom, $params);
+        $data['list'] = $this->appendUseCaseInfo($data['list'] ?? []);
         util::success($data);
     }
 
+    /**
+     * 场景列表(商城筛选弹窗用)
+     * 游客可访问,未登录返回空数组
+     */
+    public function actionUseCaseList()
+    {
+        $user = $this->user;
+        if (empty($user)) {
+            util::success([]);
+        }
+        $mainId = intval($this->mainId ?? 0);
+        if ($mainId <= 0) {
+            util::success([]);
+        }
+        $list = UseCaseClass::getAllByCondition(
+            ['mainId' => $mainId, 'status' => 1, 'delStatus' => 0],
+            'inTurn DESC',
+            'id,useCaseName'
+        );
+        util::success($list ?: []);
+    }
+
+    /**
+     * 批量为商品列表补充场景 ID 与名称,供列表页标签展示
+     *
+     * @param array $list
+     * @return array
+     */
+    private function appendUseCaseInfo($list)
+    {
+        if (empty($list)) {
+            return [];
+        }
+        $goodsIds = array_column($list, 'id');
+        if (empty($goodsIds)) {
+            return $list;
+        }
+
+        $relations = GoodsUseCaseClass::getAllByCondition(
+            ['goodsId' => ['in', $goodsIds]],
+            null,
+            'goodsId,useCaseId'
+        );
+        $useCaseIds = array_unique(array_column($relations ?: [], 'useCaseId'));
+        $nameMap = [];
+        if (!empty($useCaseIds)) {
+            $useCases = UseCaseClass::getAllByCondition(
+                [
+                    'id' => ['in', $useCaseIds],
+                    'mainId' => intval($this->mainId ?? 0),
+                    'delStatus' => 0,
+                ],
+                null,
+                'id,useCaseName'
+            );
+            foreach ($useCases ?: [] as $uc) {
+                $nameMap[intval($uc['id'])] = $uc['useCaseName'] ?? '';
+            }
+        }
+
+        $goodsUseCaseMap = [];
+        foreach ($relations ?: [] as $rel) {
+            $gid = intval($rel['goodsId']);
+            $uid = intval($rel['useCaseId']);
+            if (!isset($goodsUseCaseMap[$gid])) {
+                $goodsUseCaseMap[$gid] = [];
+            }
+            if (!isset($nameMap[$uid])) {
+                continue;
+            }
+            $goodsUseCaseMap[$gid][] = [
+                'id' => $uid,
+                'name' => $nameMap[$uid],
+            ];
+        }
+
+        foreach ($list as &$item) {
+            $gid = intval($item['id'] ?? 0);
+            $cases = $goodsUseCaseMap[$gid] ?? [];
+            $item['useCaseIdList'] = array_column($cases, 'id');
+            $item['useCaseNames'] = array_values(array_filter(array_column($cases, 'name')));
+        }
+        unset($item);
+
+        return $list;
+    }
+
 }

+ 20 - 0
app-mall/controllers/HomePageConfigController.php

@@ -6,6 +6,7 @@ use bizHd\homePageConfig\classes\HomePageConfigClass;
 use bizHd\homePageConfig\classes\HomePageDisplayClass;
 use bizHd\homePageConfig\classes\HomePageModuleClass;
 use common\components\util;
+use Yii;
 
 /**
  * 商城端首页配置读取接口
@@ -24,6 +25,7 @@ class HomePageConfigController extends BaseController
         'get-new',
         'get-pull-goods',
         'get-home',
+        'get-section-goods',
     ];
 
     /**
@@ -69,6 +71,24 @@ class HomePageConfigController extends BaseController
         util::success(HomePageDisplayClass::formatGoodsSection($this->requireMainId(), 'pullGoods'));
     }
 
+    /**
+     * 首页模块「更多」商品列表分页
+     * moduleKey: seckill|groupBuy|hot|new|pullGoods
+     */
+    public function actionGetSectionGoods()
+    {
+        $get = Yii::$app->request->get();
+        $moduleKey = strval($get['moduleKey'] ?? '');
+        $page = intval($get['page'] ?? 1);
+        $pageSize = intval($get['pageSize'] ?? 10);
+        util::success(HomePageDisplayClass::getSectionGoodsPage(
+            $this->requireMainId(),
+            $moduleKey,
+            $page,
+            $pageSize
+        ));
+    }
+
     private function requireMainId()
     {
         $mainId = intval($this->mainId);

+ 2 - 2
app-pt/views/main/app.php

@@ -237,8 +237,8 @@
             const downloadUrls = {
                 'xiaohuabao': 'https://api.shop.hzghd.com/1312.apk', // 销花宝APP下载链接
                 'huazhanggui': 'https://api.shop.hzghd.com/246.apk', // 花掌柜APP下载链接
-                'huazhanggui_cashier': 'https://api.shop.hzghd.com/209.apk', // 花掌柜收银下载链接
-                'xiaohuabao_cashier': 'https://api.shop.hzghd.com/168.apk' // 销花宝收银下载链接
+                'huazhanggui_cashier': 'https://api.shop.hzghd.com/215.apk', // 花掌柜收银下载链接
+                'xiaohuabao_cashier': 'https://api.shop.hzghd.com/175.apk' // 销花宝收银下载链接
             };
             const url = downloadUrls[appType];
             if (url && url !== '#') {

+ 28 - 0
biz-hd/goods/classes/GoodsCategoryClass.php

@@ -39,6 +39,13 @@ class GoodsCategoryClass extends BaseClass
             }
             $query->andWhere(['xhGoodsCategory.cId' => $cId]);
         }
+        // 多分类筛选(逗号分隔),与单个 categoryId 并存
+        if (!empty($askInfo['categoryIds'])) {
+            $categoryIds = array_filter(array_map('intval', explode(',', strval($askInfo['categoryIds']))));
+            if (!empty($categoryIds)) {
+                $query->andWhere(['xhGoodsCategory.cId' => $categoryIds]);
+            }
+        }
         if (!empty($askInfo['searchText'])) {
             if($askInfo['searchType'] == 1){
                 $query->andWhere(['xhGoods.sn' => $askInfo['searchText']]);
@@ -89,6 +96,27 @@ class GoodsCategoryClass extends BaseClass
             } elseif ($requestType == 'album') {
                 util::fail('相册已经不存在');
             }
+
+            // 场景筛选(逗号分隔的 useCaseId)
+            if (!empty($askInfo['useCaseIds'])) {
+                $useCaseIds = array_filter(array_map('intval', explode(',', strval($askInfo['useCaseIds']))));
+                if (!empty($useCaseIds)) {
+                    $query->andWhere([
+                        'xhGoods.id' => (new \yii\db\Query())
+                            ->select('goodsId')
+                            ->from('xhGoodsUseCaseRelation')
+                            ->where(['useCaseId' => $useCaseIds]),
+                    ]);
+                }
+            }
+
+            // 价格区间筛选(按商品基础价 xhGoods.price)
+            if (isset($askInfo['minPrice']) && $askInfo['minPrice'] !== '' && is_numeric($askInfo['minPrice'])) {
+                $query->andWhere(['>=', 'xhGoods.price', floatval($askInfo['minPrice'])]);
+            }
+            if (isset($askInfo['maxPrice']) && $askInfo['maxPrice'] !== '' && is_numeric($askInfo['maxPrice'])) {
+                $query->andWhere(['<=', 'xhGoods.price', floatval($askInfo['maxPrice'])]);
+            }
         }]);
 
 

+ 107 - 10
biz-hd/homePageConfig/classes/HomePageDisplayClass.php

@@ -4,6 +4,7 @@ namespace bizHd\homePageConfig\classes;
 
 use biz\shop\classes\ShopClass;
 use common\components\business;
+use common\components\util;
 
 /**
  * 门店首页"展示数据"组装类
@@ -111,19 +112,15 @@ class HomePageDisplayClass
     }
 
     /**
-     * 格式化秒杀/团购活动:只返回上架中的活动商品,并补上封面完整URL
+     * 过滤上架中的活动商品并补上封面完整URL(不做条数截断)
      *
-     * @param array $data HomePageModuleClass::getSeckill/getGroupBuy 返回值
+     * @param array $rawList
      * @return array
      */
-    public static function formatActivity($data)
+    public static function filterActivityGoods($rawList)
     {
-        if (empty($data['enabled'])) {
-            $data['goods'] = [];
-            return $data;
-        }
         $goods = [];
-        foreach ($data['goods'] as $item) {
+        foreach ($rawList as $item) {
             if (empty($item['status'])) {
                 continue;
             }
@@ -134,8 +131,32 @@ class HomePageDisplayClass
             } else {
                 $item['coverUrl'] = $cover;
             }
+            // 列表页统一用 id 字段,与普通商品列表对齐
+            $item['id'] = intval($item['goodsId'] ?? ($item['id'] ?? 0));
             $goods[] = $item;
         }
+        return $goods;
+    }
+
+    /**
+     * 格式化秒杀/团购活动:只返回上架中的活动商品,并按 displayCount 截断;附带 goodsTotal
+     *
+     * @param array $data HomePageModuleClass::getSeckill/getGroupBuy 返回值
+     * @return array
+     */
+    public static function formatActivity($data)
+    {
+        if (empty($data['enabled'])) {
+            $data['goods'] = [];
+            $data['goodsTotal'] = 0;
+            return $data;
+        }
+        $goods = self::filterActivityGoods($data['goods'] ?? []);
+        $data['goodsTotal'] = count($goods);
+        $limit = HomePageModuleClass::resolveHomeDisplayLimit($data['displayCount'] ?? 0);
+        if ($limit > 0 && count($goods) > $limit) {
+            $goods = array_slice($goods, 0, $limit);
+        }
         $data['goods'] = $goods;
         return $data;
     }
@@ -152,14 +173,90 @@ class HomePageDisplayClass
         $data = HomePageModuleClass::getGoodsSection($mainId, $moduleKey);
         if (empty($data['enabled'])) {
             $data['goods'] = [];
+            $data['goodsTotal'] = 0;
             return $data;
         }
-        $data['goods'] = HomePageModuleClass::resolveDisplayGoods(
+        $limit = HomePageModuleClass::resolveHomeDisplayLimit($data['displayCount'] ?? 0);
+        $paged = HomePageModuleClass::resolveDisplayGoodsPaged(
             $mainId,
             $data['type'],
             $data['value'],
-            $data['sort']
+            $data['sort'],
+            1,
+            $limit
         );
+        $data['goods'] = $paged['list'];
+        $data['goodsTotal'] = $paged['total'];
         return $data;
     }
+
+    /**
+     * 「更多」列表页分页入口:按模块返回全部可展示商品
+     *
+     * @param int $mainId
+     * @param string $moduleKey seckill|groupBuy|hot|new|pullGoods
+     * @param int $page
+     * @param int $pageSize
+     * @return array {list, total, totalPage, page, pageSize, moreData, title}
+     */
+    public static function getSectionGoodsPage($mainId, $moduleKey, $page = 1, $pageSize = 10)
+    {
+        $mainId = intval($mainId);
+        $moduleKey = strval($moduleKey);
+        $page = max(1, intval($page));
+        $pageSize = max(1, intval($pageSize));
+        if ($mainId <= 0) {
+            util::fail('无效门店');
+        }
+
+        $title = '';
+        $list = [];
+        $total = 0;
+
+        if ($moduleKey === 'seckill' || $moduleKey === 'groupBuy') {
+            $data = $moduleKey === 'seckill'
+                ? HomePageModuleClass::getSeckill($mainId, true)
+                : HomePageModuleClass::getGroupBuy($mainId, true);
+            $title = strval($data['title'] ?? '');
+            if ($title === '') {
+                $title = $moduleKey === 'groupBuy' ? '团购优惠' : '限时秒杀';
+            }
+            $all = empty($data['enabled']) ? [] : self::filterActivityGoods($data['goods'] ?? []);
+            $total = count($all);
+            $list = array_slice($all, ($page - 1) * $pageSize, $pageSize);
+        } elseif (in_array($moduleKey, ['hot', 'new', 'pullGoods'], true)) {
+            $data = HomePageModuleClass::getGoodsSection($mainId, $moduleKey);
+            $title = strval($data['name'] ?? '');
+            if (empty($data['enabled'])) {
+                $list = [];
+                $total = 0;
+            } else {
+                $paged = HomePageModuleClass::resolveDisplayGoodsPaged(
+                    $mainId,
+                    $data['type'],
+                    $data['value'],
+                    $data['sort'],
+                    $page,
+                    $pageSize
+                );
+                $list = $paged['list'];
+                $total = $paged['total'];
+            }
+        } else {
+            util::fail('无效模块');
+        }
+
+        $totalPage = $pageSize > 0 ? intval(ceil($total / $pageSize)) : 0;
+        $moreData = $totalPage > $page ? 1 : 0;
+
+        return [
+            'list' => $list,
+            'total' => $total,
+            'totalPage' => $totalPage,
+            'page' => $page,
+            'pageSize' => $pageSize,
+            'moreData' => $moreData,
+            'title' => $title,
+        ];
+    }
 }

+ 90 - 5
biz-hd/homePageConfig/classes/HomePageModuleClass.php

@@ -452,6 +452,7 @@ class HomePageModuleClass
             'value' => strval($saved['value'] ?? ''),
             'sort' => intval($saved['sort'] ?? self::SORT_PRODUCT),
             'layoutCols' => self::normalizeLayoutCols($saved['layoutCols'] ?? null),
+            'displayCount' => intval($saved['displayCount'] ?? 0),
         ];
     }
 
@@ -464,6 +465,7 @@ class HomePageModuleClass
         $value = trim(strval($data['value'] ?? ''));
         $sort = intval($data['sort'] ?? self::SORT_PRODUCT);
         $layoutCols = intval($data['layoutCols'] ?? self::DEFAULT_LAYOUT_COLS);
+        $displayCount = self::normalizeDisplayCount($data['displayCount'] ?? 0);
         if ($name === '') {
             util::fail('请输入首页展示名称');
         }
@@ -488,6 +490,7 @@ class HomePageModuleClass
             'value' => $value,
             'sort' => $sort,
             'layoutCols' => $layoutCols,
+            'displayCount' => $displayCount,
         ]);
         HomePageConfigClass::updateModuleEnabled($mainId, $moduleKey, !empty($data['enabled']) ? 1 : 0);
         return true;
@@ -517,6 +520,21 @@ class HomePageModuleClass
      * @return array [{id,name,price,stock,cover,coverUrl,sold}]
      */
     public static function resolveDisplayGoods($mainId, $type, $value, $sort, $limit = self::DISPLAY_GOODS_LIMIT)
+    {
+        $paged = self::resolveDisplayGoodsPaged($mainId, $type, $value, $sort, 1, $limit);
+        return $paged['list'];
+    }
+
+    /**
+     * 匹配关联配置下的全部商品行(未截断、未格式化封面URL),供分页与总数统计复用
+     *
+     * @param int $mainId
+     * @param int $type
+     * @param string $value
+     * @param int $sort
+     * @return array
+     */
+    public static function matchGoodsRows($mainId, $type, $value, $sort)
     {
         $mainId = intval($mainId);
         $type = intval($type);
@@ -588,10 +606,17 @@ class HomePageModuleClass
             $rows = $ordered;
         }
 
-        if ($limit > 0 && count($rows) > $limit) {
-            $rows = array_slice($rows, 0, $limit);
-        }
+        return $rows;
+    }
 
+    /**
+     * 将原始商品行格式化为首页/列表展示结构(含 coverUrl)
+     *
+     * @param array $rows
+     * @return array
+     */
+    public static function formatGoodsRows($rows)
+    {
         $list = [];
         foreach ($rows as $row) {
             $cover = strval($row['cover'] ?? '');
@@ -609,6 +634,65 @@ class HomePageModuleClass
         return $list;
     }
 
+    /**
+     * 分页解析关联配置下的商品列表,返回 list + total(真实总数,不受首页展示上限限制)
+     *
+     * @param int $mainId
+     * @param int $type
+     * @param string $value
+     * @param int $sort
+     * @param int $page
+     * @param int $pageSize 传 0 表示不分页返回全部
+     * @return array {list, total}
+     */
+    public static function resolveDisplayGoodsPaged($mainId, $type, $value, $sort, $page = 1, $pageSize = self::DISPLAY_GOODS_LIMIT)
+    {
+        $rows = self::matchGoodsRows($mainId, $type, $value, $sort);
+        $total = count($rows);
+        $page = max(1, intval($page));
+        $pageSize = intval($pageSize);
+        if ($pageSize > 0) {
+            $rows = array_slice($rows, ($page - 1) * $pageSize, $pageSize);
+        }
+        return [
+            'list' => self::formatGoodsRows($rows),
+            'total' => $total,
+        ];
+    }
+
+    /**
+     * 规范化首页商品展示数量:0=全部,1-10=具体数量
+     *
+     * @param mixed $value
+     * @return int
+     */
+    public static function normalizeDisplayCount($value)
+    {
+        if ($value === '' || $value === null || intval($value) === 0) {
+            return 0;
+        }
+        $count = intval($value);
+        if ($count < 1 || $count > 10) {
+            util::fail('商品展示数量范围为1-10,留空表示全部');
+        }
+        return $count;
+    }
+
+    /**
+     * 根据 displayCount 计算首页实际截断条数:1-10 用配置值,0(全部)回退默认上限
+     *
+     * @param mixed $displayCount
+     * @return int
+     */
+    public static function resolveHomeDisplayLimit($displayCount)
+    {
+        $count = intval($displayCount);
+        if ($count >= 1 && $count <= 10) {
+            return $count;
+        }
+        return self::DISPLAY_GOODS_LIMIT;
+    }
+
     public static function sectionSuffix($moduleKey)
     {
         $map = [
@@ -630,7 +714,7 @@ class HomePageModuleClass
             'title' => strval($saved['title'] ?? ''),
             'subtitle' => strval($saved['subtitle'] ?? ''),
             'showCountdown' => !empty($saved['showCountdown']) ? 1 : 0,
-            'expandHome' => !empty($saved['expandHome']) ? 1 : 0,
+            'displayCount' => intval($saved['displayCount'] ?? 0),
             'startTime' => intval($saved['startTime'] ?? 0),
             'endTime' => intval($saved['endTime'] ?? 0),
             'desc' => strval($saved['desc'] ?? ''),
@@ -646,6 +730,7 @@ class HomePageModuleClass
         $startTime = intval($data['startTime'] ?? 0);
         $endTime = intval($data['endTime'] ?? 0);
         $layoutCols = intval($data['layoutCols'] ?? self::DEFAULT_LAYOUT_COLS);
+        $displayCount = self::normalizeDisplayCount($data['displayCount'] ?? 0);
         if ($title === '') {
             util::fail('请输入活动标题');
         }
@@ -677,7 +762,7 @@ class HomePageModuleClass
             'title' => $title,
             'subtitle' => $subtitle,
             'showCountdown' => !empty($data['showCountdown']) ? 1 : 0,
-            'expandHome' => !empty($data['expandHome']) ? 1 : 0,
+            'displayCount' => $displayCount,
             'startTime' => $startTime,
             'endTime' => $endTime,
             'desc' => $desc,

+ 16 - 6
sql/20260714_goods_use_case.sql

@@ -1,11 +1,11 @@
 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,
+  `mainId` int(11) NOT NULL DEFAULT 0 COMMENT '中央id',
+  `sjId` int(11) NOT NULL DEFAULT 0 COMMENT '商家id',
+  `useCaseName` varchar(50) NOT NULL DEFAULT '' COMMENT '场景名称',
+  `inTurn` int(11) NOT NULL DEFAULT 0 COMMENT '排序',
+  `status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '状态',
+  `delStatus` tinyint(4) NOT NULL DEFAULT 0 COMMENT '删除状态',
   `addTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
   `updateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   PRIMARY KEY (`id`), KEY `mainId` (`mainId`)
@@ -18,3 +18,13 @@ CREATE TABLE `xhGoodsUseCaseRelation` (
   `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;
+
+
+
+
+ALTER TABLE xhHd
+  add COLUMN `homeRule` tinyint(4) NOT NULL DEFAULT '0' COMMENT '上门遵循规则 0遵循总设置 1遵循自定义' after `updateTime`;
+
+
+ALTER TABLE xhCustom
+  add COLUMN `homeRule` tinyint(4) NOT NULL DEFAULT '0' COMMENT '上门遵循规则 0遵循总设置 1遵循自定义' after `updateTime`;