Răsfoiți Sursa

feat(home-page): 支持首页模块商品分页

- 新增商城端首页模块更多商品分页接口,返回列表、总数和分页信息
- 支持首页模块按 displayCount 控制展示数量,并保留可分页的完整商品总数
- 为商品场景 SQL 补充字段注释并修正默认值语法

API: 新增 app-mall /home-page-config/get-section-goods,支持 moduleKey、page、pageSize 查询参数
Database: 仅补充 xhGoodsUseCase 建表字段注释,不改变字段类型和默认值
shizhongqi 2 săptămâni în urmă
părinte
comite
400bc33dce

+ 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);

+ 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,

+ 6 - 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`)