|
|
@@ -1,10 +1,16 @@
|
|
|
<?php
|
|
|
|
|
|
+/**
|
|
|
+ * 商品-分类关联业务
|
|
|
+ * 谁用:hd 分类商品列表、mallApp /category/goods-list
|
|
|
+ * 解决问题:按分类/搜索取商品;同一商品挂多个分类时按唯一商品分页,避免本页去重后条数不足导致前端无法触底加载
|
|
|
+ */
|
|
|
namespace bizHd\goods\classes;
|
|
|
|
|
|
use bizHd\goods\models\GoodsCategory;
|
|
|
use common\components\util;
|
|
|
use bizHd\base\classes\BaseClass;
|
|
|
+use Yii;
|
|
|
|
|
|
class GoodsCategoryClass extends BaseClass
|
|
|
{
|
|
|
@@ -12,11 +18,22 @@ class GoodsCategoryClass extends BaseClass
|
|
|
public static $baseFile = '\bizHd\goods\models\GoodsCategory';
|
|
|
|
|
|
//$params 必须预留字段,为后面做准备
|
|
|
+ /**
|
|
|
+ * 分类/搜索商品列表
|
|
|
+ * 分页口径是唯一商品,不是分类关联行:搜索或多分类时同一商品会对应多行 xhGoodsCategory,
|
|
|
+ * 若先 LIMIT 再 PHP 去重,本页可能只剩几条,列表页撑不满屏、onReachBottom 不会触发。
|
|
|
+ *
|
|
|
+ * @param array $askInfo 请求参数(page/pageSize/requestType/筛选条件)
|
|
|
+ * @param mixed $shop 店铺
|
|
|
+ * @param mixed $custom 客户(散客价/会员价)
|
|
|
+ * @param array $params 预留扩展
|
|
|
+ * @return array{list:array,total:int,page:int,pageSize:int,moreData:int}
|
|
|
+ */
|
|
|
public static function getGoodsList($askInfo, $shop, $custom, $params = [])
|
|
|
{
|
|
|
$requestType = $askInfo['requestType'] ?? '';
|
|
|
- $page = $askInfo['page'] ?? 1;
|
|
|
- $pageSize = $askInfo['pageSize'] ?? 20;
|
|
|
+ $page = max(1, intval($askInfo['page'] ?? 1));
|
|
|
+ $pageSize = max(1, intval($askInfo['pageSize'] ?? 20));
|
|
|
if (empty($requestType)) {
|
|
|
util::fail('没有请求类型');
|
|
|
}
|
|
|
@@ -56,7 +73,8 @@ class GoodsCategoryClass extends BaseClass
|
|
|
if (!empty($askInfo['search'])) {
|
|
|
$query->andWhere(['like', 'xhGoods.name', $askInfo['search']]);
|
|
|
}
|
|
|
- $query->joinWith(['goods' => function ($query) use ($askInfo, $requestType) {
|
|
|
+ // INNER JOIN:不符合商城/开单条件的商品不占分页名额,避免 LIMIT 后再被 PHP 丢掉
|
|
|
+ $query->innerJoinWith(['goods' => function ($query) use ($askInfo, $requestType) {
|
|
|
$query->andWhere(['xhGoods.masterId' => 0]);
|
|
|
|
|
|
if ($requestType == 'kd') {
|
|
|
@@ -65,7 +83,6 @@ class GoodsCategoryClass extends BaseClass
|
|
|
$query->andWhere(['>', 'xhGoods.stock', 0]);
|
|
|
} elseif ($requestType == 'goodsList') {
|
|
|
//花束管理列表
|
|
|
-
|
|
|
$query->andWhere(['xhGoods.delStatus' => 0]);
|
|
|
|
|
|
$priceType = isset($askInfo['priceType']) && is_numeric($askInfo['priceType']) ? $askInfo['priceType'] : -1;
|
|
|
@@ -136,20 +153,21 @@ class GoodsCategoryClass extends BaseClass
|
|
|
} else {
|
|
|
$query->orderBy(['inTurn' => SORT_DESC]);
|
|
|
}
|
|
|
- // 获取总数
|
|
|
- $total = $query->count();
|
|
|
- // 分页查询
|
|
|
- $goodsCategoryInfoList = $query->offset(($page - 1) * $pageSize)
|
|
|
- ->limit($pageSize)
|
|
|
- ->all();
|
|
|
- // 整理结果,只要有库存的商品,并去重
|
|
|
+ // 按唯一商品分页:total / moreData / 本页 list 都以去重后的商品为准
|
|
|
+ $pageInfo = self::getUniqueGoodsPage($query, $page, $pageSize, $goodsIdsForOrder);
|
|
|
+ $total = $pageInfo['total'];
|
|
|
+ $pagedGoodsIds = $pageInfo['ids'];
|
|
|
+
|
|
|
$list = [];
|
|
|
- $addedGoodsIds = []; // 用于记录已添加的商品ID,避免重复,一个商品在多个分类下就会出现重复,搜索的情况下
|
|
|
- foreach ($goodsCategoryInfoList as $info) {
|
|
|
- if ($info->goods && !in_array($info->goods->id, $addedGoodsIds)) {
|
|
|
- // 只要有关联且有库存的商品,且未添加过
|
|
|
- $list[] = $info->goods->attributes;
|
|
|
- $addedGoodsIds[] = $info->goods->id; // 记录已添加的商品ID
|
|
|
+ $addedGoodsIds = []; // 用于记录已添加的商品ID,避免重复
|
|
|
+ if (!empty($pagedGoodsIds)) {
|
|
|
+ $goodsMap = GoodsClass::getByIds($pagedGoodsIds, null, 'id');
|
|
|
+ foreach ($pagedGoodsIds as $gid) {
|
|
|
+ if (empty($goodsMap[$gid]) || in_array($gid, $addedGoodsIds, true)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $list[] = $goodsMap[$gid];
|
|
|
+ $addedGoodsIds[] = $gid;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -167,6 +185,47 @@ class GoodsCategoryClass extends BaseClass
|
|
|
];
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 取出去重后的商品总数与本页 goodsId
|
|
|
+ * 先按 xhGoods.id 分组再 LIMIT,保证每页都是 pageSize 条唯一商品(末页除外)
|
|
|
+ *
|
|
|
+ * @param \yii\db\ActiveQuery $query 已带商品 INNER JOIN 与筛选、排序条件
|
|
|
+ * @param int $page 页码,从 1 起
|
|
|
+ * @param int $pageSize 每页唯一商品数
|
|
|
+ * @param int[] $goodsIdsForOrder 运营指定顺序时按 FIELD 排,否则按分类 inTurn 最大值
|
|
|
+ * @return array{total:int,ids:int[]}
|
|
|
+ */
|
|
|
+ public static function getUniqueGoodsPage($query, $page, $pageSize, $goodsIdsForOrder = [])
|
|
|
+ {
|
|
|
+ $countQuery = clone $query;
|
|
|
+ $total = (int)$countQuery->count('DISTINCT xhGoods.id');
|
|
|
+ if ($total <= 0) {
|
|
|
+ return ['total' => 0, 'ids' => []];
|
|
|
+ }
|
|
|
+
|
|
|
+ $idQuery = clone $query;
|
|
|
+ if (!empty($goodsIdsForOrder)) {
|
|
|
+ // FIELD 已按商品唯一,再加 id 保证顺序稳定,避免 OFFSET 翻页交叉重复
|
|
|
+ $idQuery->select(['xhGoods.id'])
|
|
|
+ ->groupBy(['xhGoods.id'])
|
|
|
+ ->orderBy(new \yii\db\Expression('FIELD(xhGoods.id, ' . implode(',', $goodsIdsForOrder) . '), xhGoods.id DESC'));
|
|
|
+ } else {
|
|
|
+ // 同一商品多分类取最大 inTurn;inTurn 相同再按商品 id,避免无第二排序时翻页重复/遗漏
|
|
|
+ $idQuery->select(['xhGoods.id'])
|
|
|
+ ->groupBy(['xhGoods.id'])
|
|
|
+ ->orderBy(new \yii\db\Expression('MAX(xhGoodsCategory.inTurn) DESC, xhGoods.id DESC'));
|
|
|
+ }
|
|
|
+
|
|
|
+ $ids = $idQuery->offset(($page - 1) * $pageSize)
|
|
|
+ ->limit($pageSize)
|
|
|
+ ->column();
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'total' => $total,
|
|
|
+ 'ids' => array_values(array_unique(array_filter(array_map('intval', $ids)))),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
//删除商品的分类
|
|
|
public static function delGoodsCategory($mainId, $categoryId, $goodsId)
|
|
|
{
|