diff --git a/backend/app/api/controller/v1/product/CenterProductController.php b/backend/app/api/controller/v1/product/CenterProductController.php index 2473e4e8a..b583b3467 100644 --- a/backend/app/api/controller/v1/product/CenterProductController.php +++ b/backend/app/api/controller/v1/product/CenterProductController.php @@ -63,7 +63,7 @@ class CenterProductController } else { $where['cate_id'] = []; } - $list = $services->getUserList($where, $page, $limit); + $list = $services->getUserList($where, $page, $limit, (int)$request->uid()); // 为每个商品附加 type_info(通过 Handler 动态获取) $registry = app()->make(\crmeb\trade\product\ProductTypeRegistry::class); diff --git a/backend/app/api/controller/v1/user/UserController.php b/backend/app/api/controller/v1/user/UserController.php index 5e7ebf5e4..378b15369 100644 --- a/backend/app/api/controller/v1/user/UserController.php +++ b/backend/app/api/controller/v1/user/UserController.php @@ -227,12 +227,19 @@ class UserController */ public function visitList(Request $request, StoreProductLogServices $services) { - $where['uid'] = (int)$request->uid(); + $uid = (int)$request->uid(); + $where['uid'] = $uid; $where['type'] = 'visit'; $result = $services->getList($where, 'product_id', 'id,product_id,max(add_time) as add_time'); $time_data = []; if ($result['list']) { + // 非付费会员浏览记录不展示「仅会员可见」商品 + $vipUserLevel = app()->make(\app\services\product\CenterProductServices::class)->resolveVipUserLevel($uid); foreach ($result['list'] as $key => &$item) { + if ($vipUserLevel <= 0 && (int)($item['vip_product'] ?? 0) === 1) { + unset($result['list'][$key]); + continue; + } $add_time = strtotime($item['add_time']); if (date('Y') == date('Y', $add_time)) {//今年 $item['time_key'] = date('m-d', $add_time); @@ -240,6 +247,9 @@ class UserController $item['time_key'] = date('Y-m-d', $add_time); } } + unset($item); + $result['list'] = array_values($result['list']); + $result['count'] = count($result['list']); $time_data = array_merge(array_unique(array_column($result['list'], 'time_key'))); } $result['time'] = $time_data; diff --git a/backend/app/model/product/CenterProductModel.php b/backend/app/model/product/CenterProductModel.php index c4b60ba5f..335d8c958 100644 --- a/backend/app/model/product/CenterProductModel.php +++ b/backend/app/model/product/CenterProductModel.php @@ -205,14 +205,17 @@ class CenterProductModel extends BaseModel /** * 会员专属商品搜索器 + * 传入当前用户 is_money_level(未登录/非会员为 0):隐藏 vip_product=1(仅会员可见可买) + * vip_product=2(所有人可见仅会员可买)仍展示 * @param Model $query - * @param int $value + * @param mixed $value */ public function searchVipUserAttr($query, $value) { - // 非付费会员只能看到不限制(0)或所有人可见仅会员可买(2)的商品 - if ($value === 0) { - $query->where('vip_product', 0)->whereOr('vip_product', 2); + // 未登录或非付费会员:仅保留不限制(0)与所有人可见仅会员可买(2) + // 必须用 whereIn,禁止 whereOr 破坏外层 is_show/is_del 等条件 + if ((int)$value <= 0) { + $query->whereIn('vip_product', [0, 2]); } } diff --git a/backend/app/model/product/product/StoreProductLog.php b/backend/app/model/product/product/StoreProductLog.php index 623f1de0a..89ae16d0b 100644 --- a/backend/app/model/product/product/StoreProductLog.php +++ b/backend/app/model/product/product/StoreProductLog.php @@ -58,6 +58,7 @@ class StoreProductLog extends BaseModel 'product_price' => 'price', 'stock', 'is_show', + 'vip_product', ]); } diff --git a/backend/app/services/product/CenterProductServices.php b/backend/app/services/product/CenterProductServices.php index 63635e09e..e95b5a43c 100644 --- a/backend/app/services/product/CenterProductServices.php +++ b/backend/app/services/product/CenterProductServices.php @@ -609,15 +609,11 @@ class CenterProductServices extends BaseService $siteUrl = sys_config('site_url'); $productType = $info['product_type'] ?? 'normal'; - // 会员商品可见性校验:vip_product=1 仅会员可见可买,非会员直接拦截详情页访问(防分享链接绕过列表过滤) + // 会员商品可见性校验:vip_product=1 仅会员可见可买,未登录/非会员直接拦截(防列表或分享链接绕过) $vipProduct = (int)($info['vip_product'] ?? 0); - $isMoneyLevel = 0; - if (in_array($vipProduct, [1, 2], true) && $uid > 0) { - $isMoneyLevel = (int)app()->make(UserServices::class)->value(['uid' => $uid], 'is_money_level'); - // vip_product=1:仅会员可见可买,非会员直接拦截(列表已过滤,此处防分享链接直达) - if ($vipProduct === 1 && $isMoneyLevel === 0) { - throw new ApiException('此商品为会员专享,开通会员后可查看'); - } + $isMoneyLevel = $this->resolveVipUserLevel($uid); + if ($vipProduct === 1 && $isMoneyLevel <= 0) { + throw new ApiException('此商品为会员专享,开通会员后可查看'); } // 通过 Handler 获取产品类型处理器 @@ -808,13 +804,7 @@ class CenterProductServices extends BaseService $storeInfo['small_image'] = $storeInfo['image']; $storeInfo = get_thumb_water($storeInfo, 'small', ['small_image']); - // ========== 6. 分销价标识 ========== - $priceName = 0; - if ($uid && (int)sys_config('brokerage_window_switch', 1)) { - $priceName = $this->getPacketPrice($storeInfo, $productValue, $uid); - } - - // ========== 7. 预售状态 ========== + // ========== 6. 预售状态 ========== if ($storeInfo['presale']) { if ($storeInfo['presale_start_time'] > time()) { $storeInfo['presale_pay_status'] = 1; @@ -860,7 +850,7 @@ class CenterProductServices extends BaseService : array_map('intval', explode(',', (string)$recommendRaw)); try { $goodList = $recommendIds - ? $this->getProducts(['ids' => $recommendIds, 'is_del' => 0, 'is_show' => 1], 12) + ? $this->getProducts(['ids' => $recommendIds, 'is_del' => 0, 'is_show' => 1], 12, $uid) : []; } catch (\Throwable $e) {} } @@ -955,7 +945,6 @@ class CenterProductServices extends BaseService 'storeInfo' => $storeInfo, 'svip_open' => $svipOpen, 'svip_price_open' => $svipPriceOpen, - 'priceName' => $priceName, 'reply' => $reply, 'replyChance' => $replyChance, 'replyCount' => $replyCount, @@ -2439,7 +2428,7 @@ class CenterProductServices extends BaseService app()->make(UserSearchServices::class)->saveUserSearch($uid, $where['store_name'], [$where['store_name']], []); } [$page, $limit] = $this->getPageValue(); - $where['vip_user'] = $uid ? app()->make(UserServices::class)->value(['uid' => $uid], 'is_money_level') : 0; + $where['vip_user'] = $this->resolveVipUserLevel($uid); $list = $this->dao->getSearchList($where, $page, $limit, ['id,name as store_name,cate_id,image,IFNULL(sales, 0) + IFNULL(virtual_sales, 0) as sales,price,stock,original_price as ot_price,spec_type,recommend_image,unit_name,is_vip,vip_price,presale,custom_form,product_type,min_qty,label_list']); /** @var MemberCardServices $memberCardService */ $memberCardService = app()->make(MemberCardServices::class); @@ -2641,13 +2630,17 @@ class CenterProductServices extends BaseService * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ - public function getProducts(array $where, int $num = 0) + public function getProducts(array $where, int $num = 0, int $uid = 0) { [$page, $limit] = $this->getPageValue(); if ($num) { $page = 1; $limit = $num; } + // 未显式传入时按当前用户会员身份过滤仅会员可见商品 + if (!array_key_exists('vip_user', $where)) { + $where['vip_user'] = $this->resolveVipUserLevel($uid); + } $list = $this->dao->getSearchList($where, $page, $limit, ['id,name as store_name,cate_id,image,IFNULL(sales, 0) + IFNULL(virtual_sales, 0) as sales,price,stock,unit_name,presale,is_vip,vip_price']); // eb_product_center 无 activity 列,补默认值兼容 getActivityList foreach ($list as &$item) { @@ -2791,31 +2784,8 @@ class CenterProductServices extends BaseService if (!$this->vipIsOpen(!!$storeInfo['is_vip'], $vipStatus)) { $data['storeInfo']['vip_price'] = 0; } - $data['priceName'] = 0; - if ($uid && (int)sys_config('brokerage_window_switch', 1)) { - $user = $request->user(); - $agentProfile = event('DistributionServiceProxy', [ - 'service' => 'DistributionUserServices', - 'action' => 'profile', - ]); - $isPromoter = (int)($agentProfile[0]['is_promoter'] ?? $user->is_promoter); - if (!$isPromoter) { - /** @var OrderCenterDao $storeOrderService */ - $storeOrderService = app()->make(OrderCenterDao::class); - $price = $storeOrderService->sum(['pay_status' => 1, 'refund_status' => 0, 'uid' => $uid], 'pay_price'); - $status = is_brokerage_statu($price); - if ($status) { - $handled = event('DistributionServiceProxy', [ - 'service' => 'DistributionUserServices', - 'action' => 'saveProfile', - 'args' => [(int)$uid, ['is_promoter' => 1, 'spread_open' => 1]], - ]); - if (!is_array($handled) || ($handled[0] ?? null) === null) { - app()->make(UserServices::class)->update($uid, ['is_promoter' => 1]); - } - } - } - $data['priceName'] = $this->getPacketPrice($storeInfo, $attrValue, $uid); + // 佣金窗口已随分销佣金功能下线,不再返回 priceName 与推广员升级逻辑 + if ($uid) { //用户访问事件 event('UserVisit', [$uid, $id, 'product', $storeInfo['cate_id'], 'view']); } @@ -2827,11 +2797,10 @@ class CenterProductServices extends BaseService $data['replyChance'] = $replyChance; $data['replyCount'] = $replyCount; $data['mer_id'] = 0; - $vip_user = $uid ? app()->make(UserServices::class)->value(['uid' => $uid], 'is_money_level') : 0; // 商品详情关联推荐仅使用 recommend_list,is_good 字段已删除 if ($storeInfo['recommend_list'] !== '') { $recommend_list = explode(',', $storeInfo['recommend_list']); - $data['good_list'] = $this->getProducts(['ids' => $recommend_list, 'is_del' => 0, 'is_show' => 1], 12); + $data['good_list'] = $this->getProducts(['ids' => $recommend_list, 'is_del' => 0, 'is_show' => 1], 12, $uid); } else { $data['good_list'] = []; } @@ -2865,46 +2834,17 @@ class CenterProductServices extends BaseService } /** - * 获取商品分销佣金最低和最高 - * @param $storeInfo - * @param $productValue + * 解析用户付费会员等级(用于列表 vip_user 搜索器) + * 未登录或非付费会员返回 0,付费会员返回 is_money_level * @param int $uid - * @return int|string + * @return int */ - public function getPacketPrice($storeInfo, $productValue, int $uid) + public function resolveVipUserLevel(int $uid): int { - /** @var UserServices $userServices */ - $userServices = app()->make(UserServices::class); - if (!$userServices->checkUserPromoter($uid)) { + if ($uid <= 0) { return 0; } - if (!count($productValue)) { - return 0; - } - if (!sys_config('brokerage_func_status')) { - return 0; - } - // 未开启分销时不计算返佣 - if (empty($storeInfo['is_sub'])) { - return 0; - } - $store_brokerage_ratio = sys_config('store_brokerage_ratio'); - $store_brokerage_ratio = bcdiv((string)$store_brokerage_ratio, '100', 2); - $maxPrice = max(array_column($productValue, 'price')); - $minPrice = min(array_column($productValue, 'price')); - $maxPrice = bcmul((string)$store_brokerage_ratio, (string)$maxPrice, 0); - $minPrice = bcmul((string)$store_brokerage_ratio, (string)$minPrice, 0); - if ($minPrice == 0 && $maxPrice == 0) { - $priceName = 0; - } else if ($minPrice == 0 && $maxPrice) - $priceName = $maxPrice; - else if ($maxPrice == 0 && $minPrice) - $priceName = $minPrice; - else if ($maxPrice == $minPrice && $minPrice) - $priceName = $maxPrice; - else - $priceName = $minPrice . '~' . $maxPrice; - return strlen(trim($priceName)) ? $priceName : 0; + return (int)(app()->make(UserServices::class)->value(['uid' => $uid], 'is_money_level') ?: 0); } /** @@ -3180,8 +3120,7 @@ class CenterProductServices extends BaseService } // 会员商品推荐保留(is_vip 字段未删除) - $vip_user = $uid ? app()->make(UserServices::class)->value(['uid' => $uid], 'is_money_level') : 0; - $where = ['is_show' => 1, 'is_del' => 0, 'vip_user' => $vip_user]; + $where = ['is_show' => 1, 'is_del' => 0, 'vip_user' => $this->resolveVipUserLevel($uid)]; if (is_array($field)) { $where = array_merge($where, $field); } elseif ($field === 'is_vip') { @@ -3337,7 +3276,7 @@ class CenterProductServices extends BaseService if ($userType == 'routine') { /** @var QrcodeServices $qrcodeService */ $qrcodeService = app()->make(QrcodeServices::class); - $url = $qrcodeService->getRoutineQrcodePath($id, $user['uid'], 0, ['is_promoter' => $user['is_promoter']]); + $url = $qrcodeService->getRoutineQrcodePath($id, $user['uid'], 0); if (!$url) { throw new ApiException('二维码生成失败'); } else { @@ -4024,10 +3963,12 @@ class CenterProductServices extends BaseService * @param int $limit * @return array */ - public function getUserList(array $where, int $page, int $limit): array + public function getUserList(array $where, int $page, int $limit, int $uid = 0): array { $where['is_show'] = 1; $where['is_del'] = 0; + // 移动端商品列表:未登录/非付费会员过滤 vip_product=1(仅会员可见可买) + $where['vip_user'] = $this->resolveVipUserLevel($uid); // 查询字段:中台列 → 前端需的字段 $field = [ diff --git a/backend/app/services/product/product/StoreProductRelationServices.php b/backend/app/services/product/product/StoreProductRelationServices.php index b7f1f6cc6..694027748 100644 --- a/backend/app/services/product/product/StoreProductRelationServices.php +++ b/backend/app/services/product/product/StoreProductRelationServices.php @@ -82,8 +82,14 @@ class StoreProductRelationServices extends BaseService $where['type'] = 'collect'; [$page, $limit] = $this->getPageValue(); $list = $this->dao->getList($where, 'product_id,category', $page, $limit); + // 收藏列表同样隐藏「仅会员可见」商品(未登录不可能到此;非付费会员需过滤) + $vipUserLevel = app()->make(\app\services\product\CenterProductServices::class)->resolveVipUserLevel($uid); foreach ($list as $k => $product) { if ($product['product'] && isset($product['product']['id'])) { + if ($vipUserLevel <= 0 && (int)($product['product']['vip_product'] ?? 0) === 1) { + unset($list[$k]); + continue; + } // 中台商品表 eb_product_center 无 store_name/ot_price 列,名称/划线价字段为 name/original_price, // 此处映射为移动端收藏列表的旧字段契约(CenterProductServices 同款映射) $list[$k]['pid'] = $product['product']['id'] ?? 0; @@ -99,6 +105,7 @@ class StoreProductRelationServices extends BaseService unset($list[$k]); } } + $list = array_values($list); $count = $this->dao->count($where); return compact('list', 'count'); }