|
|
@@ -140,7 +140,147 @@ class Merchant extends BaseController
|
|
140
|
140
|
$where['cate_id'] = $params['mer_cate_id'];
|
|
141
|
141
|
}
|
|
142
|
142
|
|
|
143
|
|
- return app('json')->success($this->repository->productList($id,$where, $page, $limit,$this->userInfo,$type439,$is_commercetrade));
|
|
|
143
|
+ $result = $this->repository->productList($id,$where, $page, $limit,$this->userInfo,$type439,$is_commercetrade);
|
|
|
144
|
+
|
|
|
145
|
+ // ===== sort 相同则随机打乱顺序 =====
|
|
|
146
|
+ // 默认排序为 sort DESC, create_time DESC。当多条商品 sort 值相同时,
|
|
|
147
|
+ // 按 sort 分组,同组内随机打乱,使相同权重的商品每次展示顺序不同
|
|
|
148
|
+ if (!empty($result['list'])) {
|
|
|
149
|
+ try {
|
|
|
150
|
+ $grouped = []; // 按 sort 值分组
|
|
|
151
|
+ foreach ($result['list'] as $item) {
|
|
|
152
|
+ $sortVal = $item['sort'] ?? 0;
|
|
|
153
|
+ $grouped[$sortVal][] = $item;
|
|
|
154
|
+ }
|
|
|
155
|
+ // 同 sort 组内随机打乱
|
|
|
156
|
+ $shuffled = [];
|
|
|
157
|
+ foreach ($grouped as $sortVal => $group) {
|
|
|
158
|
+ if (count($group) > 1) {
|
|
|
159
|
+ shuffle($group);
|
|
|
160
|
+ }
|
|
|
161
|
+ $shuffled = array_merge($shuffled, $group);
|
|
|
162
|
+ }
|
|
|
163
|
+ $result['list'] = $shuffled;
|
|
|
164
|
+ } catch (\Throwable $e) {
|
|
|
165
|
+ Log::info('productTypeList sort 随机打乱失败: ' . $e->getMessage());
|
|
|
166
|
+ }
|
|
|
167
|
+ }
|
|
|
168
|
+ // ===== sort 随机打乱结束 =====
|
|
|
169
|
+
|
|
|
170
|
+ // ===== 混入推荐商品逻辑(每页3-5个,跨页不重复,随机插入) =====
|
|
|
171
|
+ // 用户已登录时,将搜索关键词匹配的商品随机混入列表,保持每页商品数量不变
|
|
|
172
|
+ // 跨页去重方案:一次性查询所有匹配商品形成"推荐池",按页码切片取不同子集
|
|
|
173
|
+ if (!is_null($this->userInfo) && !empty($result['list'])) {
|
|
|
174
|
+ try {
|
|
|
175
|
+ // 1. 获取用户最近的搜索关键词(去重)
|
|
|
176
|
+ $searchKeywords = Db::name('user_visit')
|
|
|
177
|
+ ->where('uid', $this->userInfo['uid'])
|
|
|
178
|
+ ->where('type', 'searchProduct')
|
|
|
179
|
+ ->where('content', '<>', '')
|
|
|
180
|
+ ->order('create_time', 'DESC')
|
|
|
181
|
+ ->limit(5)
|
|
|
182
|
+ ->column('content');
|
|
|
183
|
+ $searchKeywords = array_unique(array_filter($searchKeywords));
|
|
|
184
|
+
|
|
|
185
|
+ if (!empty($searchKeywords)) {
|
|
|
186
|
+ // 2. 查询所有匹配的商品ID(不 limit,形成推荐池),排除已在当前列表中的
|
|
|
187
|
+ $existIds = array_column($result['list'], 'product_id');
|
|
|
188
|
+ $matchQuery = Db::name('store_product')
|
|
|
189
|
+ ->where('mer_id', $id)
|
|
|
190
|
+ ->where('is_show', 1)
|
|
|
191
|
+ ->where('status', 1)
|
|
|
192
|
+ ->where('is_del', 0)
|
|
|
193
|
+ ->where('is_gift_bag', 0)
|
|
|
194
|
+ ->where('product_type', 0)
|
|
|
195
|
+ ->where('price', '>', 0);
|
|
|
196
|
+
|
|
|
197
|
+ // 关键词 LIKE 条件
|
|
|
198
|
+ $matchQuery->where(function ($q) use ($searchKeywords) {
|
|
|
199
|
+ foreach ($searchKeywords as $kw) {
|
|
|
200
|
+ $q->whereOr('store_name', 'like', "%{$kw}%");
|
|
|
201
|
+ }
|
|
|
202
|
+ });
|
|
|
203
|
+
|
|
|
204
|
+ if (!empty($existIds)) {
|
|
|
205
|
+ $matchQuery->whereNotIn('product_id', $existIds);
|
|
|
206
|
+ }
|
|
|
207
|
+
|
|
|
208
|
+ // 查出所有匹配商品ID(推荐池),按 ID 排序保证跨页顺序一致
|
|
|
209
|
+ $allMatchProductIds = $matchQuery->order('product_id', 'ASC')->column('product_id');
|
|
|
210
|
+
|
|
|
211
|
+ if (!empty($allMatchProductIds)) {
|
|
|
212
|
+ // 3. 根据页码切片:每页取 mixCount 个,不同页取不同区间
|
|
|
213
|
+ $mixCount = min(5, $limit); // 每页混入3-5个,取上限5
|
|
|
214
|
+ $totalPool = count($allMatchProductIds);
|
|
|
215
|
+
|
|
|
216
|
+ // 如果推荐池足够大,按页码切片;否则循环复用但保证同一页内不重复
|
|
|
217
|
+ if ($totalPool <= $mixCount) {
|
|
|
218
|
+ // 池子太小:全部使用,但不同页之间可能重复(资源有限)
|
|
|
219
|
+ $pageMatchIds = $allMatchProductIds;
|
|
|
220
|
+ } else {
|
|
|
221
|
+ // 池子足够:按页码取不同切片
|
|
|
222
|
+ // 计算当前页的起始偏移量,确保不同页取不同区间
|
|
|
223
|
+ $offset = (($page - 1) * $mixCount) % $totalPool;
|
|
|
224
|
+ $pageMatchIds = array_slice($allMatchProductIds, $offset, $mixCount);
|
|
|
225
|
+ // 如果切片不够 mixCount,从头部补足(环形取)
|
|
|
226
|
+ if (count($pageMatchIds) < $mixCount) {
|
|
|
227
|
+ $remainder = array_slice($allMatchProductIds, 0, $mixCount - count($pageMatchIds));
|
|
|
228
|
+ $pageMatchIds = array_merge($pageMatchIds, $remainder);
|
|
|
229
|
+ }
|
|
|
230
|
+ }
|
|
|
231
|
+
|
|
|
232
|
+ if (!empty($pageMatchIds)) {
|
|
|
233
|
+ // 4. 通过 productList 获取完整格式的推荐商品数据
|
|
|
234
|
+ $recommendWhere = $where;
|
|
|
235
|
+ unset($recommendWhere['keyword'], $recommendWhere['order'], $recommendWhere['mer_cate_id'], $recommendWhere['cate_id'], $recommendWhere['price_on'], $recommendWhere['price_off'], $recommendWhere['brand_id']);
|
|
|
236
|
+ $recommendResult = $this->repository->productList($id, array_merge($recommendWhere, [
|
|
|
237
|
+ 'is_gift_bag' => 0,
|
|
|
238
|
+ 'is_used' => 1,
|
|
|
239
|
+ 'product_type' => 0,
|
|
|
240
|
+ 'is_hide' => 0,
|
|
|
241
|
+ ]), 1, count($pageMatchIds), $this->userInfo, $type439, $is_commercetrade);
|
|
|
242
|
+
|
|
|
243
|
+ $matchList = [];
|
|
|
244
|
+ if (!empty($recommendResult['list'])) {
|
|
|
245
|
+ foreach ($recommendResult['list'] as $rp) {
|
|
|
246
|
+ if (in_array($rp['product_id'], $pageMatchIds) && !in_array($rp['product_id'], $existIds)) {
|
|
|
247
|
+ $matchList[] = $rp;
|
|
|
248
|
+ }
|
|
|
249
|
+ }
|
|
|
250
|
+ }
|
|
|
251
|
+
|
|
|
252
|
+ // 5. 随机混入:从原列表中随机移除同等数量商品,再随机插入推荐商品
|
|
|
253
|
+ $actualMixCount = min(count($matchList), $mixCount, $limit);
|
|
|
254
|
+ if ($actualMixCount > 0) {
|
|
|
255
|
+ $matchList = array_slice($matchList, 0, $actualMixCount);
|
|
|
256
|
+
|
|
|
257
|
+ // 随机选择要移除的商品位置
|
|
|
258
|
+ $removeKeys = array_rand($result['list'], $actualMixCount);
|
|
|
259
|
+ if (!is_array($removeKeys)) {
|
|
|
260
|
+ $removeKeys = [$removeKeys];
|
|
|
261
|
+ }
|
|
|
262
|
+ // 从大到小排序,避免删除时索引错乱
|
|
|
263
|
+ rsort($removeKeys);
|
|
|
264
|
+ foreach ($removeKeys as $rk) {
|
|
|
265
|
+ array_splice($result['list'], $rk, 1);
|
|
|
266
|
+ }
|
|
|
267
|
+
|
|
|
268
|
+ // 随机插入推荐商品
|
|
|
269
|
+ foreach ($matchList as $ml) {
|
|
|
270
|
+ $insertPos = rand(0, count($result['list']));
|
|
|
271
|
+ array_splice($result['list'], $insertPos, 0, [$ml]);
|
|
|
272
|
+ }
|
|
|
273
|
+ }
|
|
|
274
|
+ }
|
|
|
275
|
+ }
|
|
|
276
|
+ }
|
|
|
277
|
+ } catch (\Throwable $e) {
|
|
|
278
|
+ Log::info('productTypeList 混入推荐商品失败: ' . $e->getMessage());
|
|
|
279
|
+ }
|
|
|
280
|
+ }
|
|
|
281
|
+ // ===== 混入推荐商品逻辑结束 =====
|
|
|
282
|
+
|
|
|
283
|
+ return app('json')->success($result);
|
|
144
|
284
|
}
|
|
145
|
285
|
|
|
146
|
286
|
/**
|