From 06be1c7ccc7533612e4cc7b0e730080d0dd3d2a1 Mon Sep 17 00:00:00 2001
From: quanwei <419654421@qq.com>
Date: Fri, 16 Jan 2026 11:43:36 +0800
Subject: [PATCH] 商品可以赠送优惠券 商品属性增加团购 入驻商家增加实物和团购选项 团购和实物商户分类分开 增加团购商家页面 后台添加页面增加团购组件

---
 admin/app/api/model/supplier/Supplier.php |  399 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 398 insertions(+), 1 deletions(-)

diff --git a/admin/app/api/model/supplier/Supplier.php b/admin/app/api/model/supplier/Supplier.php
index cea547d..22997d6 100644
--- a/admin/app/api/model/supplier/Supplier.php
+++ b/admin/app/api/model/supplier/Supplier.php
@@ -100,6 +100,9 @@
         if (isset($param['name']) && $param['name']) {
             $model = $model->where('name', 'like', '%' . $param['name'] . '%');
         }
+        if(!empty($param['supplier_type'])){
+            $model = $model->where('supplier_type', '=', $param['supplier_type']);
+        }
         if (isset($param['category_id']) && $param['category_id']) {
             $model = $model->where('category_id', '=', $param['category_id']);
         }
@@ -108,7 +111,7 @@
             ->where('s.is_delete', '=', '0')
             ->where('s.is_recycle', '=', 0)
             //->where('s.is_full', '=', 1)
-            ->field("s.shop_supplier_id,s.name,s.fav_count,logo_id,category_id,server_score,product_sales,address,link_phone,longitude,latitude")
+            ->field("s.shop_supplier_id,s.name,s.fav_count,logo_id,category_id,server_score,product_sales,address,link_phone,longitude,latitude,supplier_type")
             ->order($sort)
             ->paginate($param);
         $product_model = new ProductModel();
@@ -200,4 +203,398 @@
             ->where('is_recycle', '=', 0)
             ->find();
     }
+
+    /**
+     * 获取团贯商户列表(用于移动端团购组件)
+     * @param array $param 查询参数
+     * @return \think\Paginator
+     */
+    public function getGroupBuyList($param)
+    {
+        // 排序规则
+        $sort = [];
+        if (isset($param['sortType'])) {
+            if ($param['sortType'] === 'all') {
+                $sort = ['s.create_time' => 'desc'];
+            } else if ($param['sortType'] === 'sales') {
+                $sort = ['product_sales' => 'desc'];
+            } else if ($param['sortType'] === 'score') {
+                $sort = ['server_score' => 'desc'];
+            }
+        } else {
+            $sort = ['s.create_time' => 'desc'];
+        }
+        $model = $this;
+        
+        // 分类筛选
+        if (isset($param['category_id']) && $param['category_id']) {
+            $model = $model->where('category_id', '=', $param['category_id']);
+        }
+        
+        // 城市商户筛选
+        if (!empty($param['city_supplier_ids'])) {
+            $supplier_ids = is_array($param['city_supplier_ids']) ? $param['city_supplier_ids'] : explode(',', $param['city_supplier_ids']);
+            $model = $model->whereIn('shop_supplier_id', $supplier_ids);
+        }
+        
+        // 获取用户经纬度(用于计算距离)
+        $user_latitude = isset($param['latitude']) ? floatval($param['latitude']) : 0;
+        $user_longitude = isset($param['longitude']) ? floatval($param['longitude']) : 0;
+        
+        // 查询列表数据
+        $list = $model->alias('s')
+            ->with(['logo', 'category'])
+            ->where('s.is_delete', '=', '0')
+            ->where('s.is_recycle', '=', 0)
+            //->where('s.supplier_type', '=', 20)
+            ->field("s.shop_supplier_id,s.supplier_type,s.name as supplier_name,s.fav_count,logo_id,category_id,server_score,product_sales,address,link_phone,longitude,latitude,supplier_type")
+            ->order($sort)
+            ->paginate($param);
+        
+        // 计算评分排名:先查询所有符合条件商户的评分
+        $rankModel = $this;
+        if (isset($param['category_id']) && $param['category_id']) {
+            $rankModel = $rankModel->where('category_id', '=', $param['category_id']);
+        }
+        if (!empty($param['city_supplier_ids'])) {
+            $supplier_ids = is_array($param['city_supplier_ids']) ? $param['city_supplier_ids'] : explode(',', $param['city_supplier_ids']);
+            $rankModel = $rankModel->whereIn('shop_supplier_id', $supplier_ids);
+        }
+        // 获取所有商户的评分排名(按评分降序)
+        $allSuppliers = $rankModel
+            ->where('is_delete', '=', '0')
+            ->where('is_recycle', '=', 0)
+            //->where('supplier_type', '=', 20)
+            ->field('shop_supplier_id,server_score,product_sales')
+            ->order(['server_score' => 'desc', 'product_sales' => 'desc', 'create_time' => 'asc'])
+            ->select()
+            ->toArray();
+        
+        // 构建评分排名映射表
+        $rankMap = [];
+        foreach ($allSuppliers as $index => $supplier) {
+            $rankMap[$supplier['shop_supplier_id']] = $index + 1;
+        }
+        
+        // 获取商品模型
+        $product_model = new ProductModel();
+        
+        // 将集合转为数组,避免间接修改警告
+        $listData = $list->toArray();
+        
+        foreach ($listData['data'] as $key => &$v) {
+            // 获取商户的团购商品列表(最多3个)
+            $productList = $product_model->with(['image.file'])
+                ->where([
+                    'shop_supplier_id' => $v['shop_supplier_id'],
+                    'product_status' => 10,
+                    'audit_status' => 10,
+                    'is_delete' => 0
+                ])
+                ->order('product_sort asc,product_id desc')
+                ->limit($param['product_num'])
+                ->field('product_id,product_price,product_name,sales_initial,sales_actual,line_price')
+                ->select();
+            
+            // 格式化商品数据
+            $v['productList'] = [];
+            foreach ($productList as $product) {
+                // 计算该商品可用的最高优惠券减价
+                $product_reduce = $this->calculateProductReducePrice(
+                    $v['shop_supplier_id'], 
+                    $product['product_id'],
+                    $product['product_price']
+                );
+                
+                $v['productList'][] = [
+                    'product_id' => $product['product_id'],
+                    'product_name' => $product['product_name'],
+                    'product_image' => isset($product['image'][0]['file']['file_path']) ? $product['image'][0]['file']['file_path'] : '',
+                    'product_price' => $product['product_price'],
+                    'line_price' => $product['line_price'],
+                    'reduce_price' => $product_reduce, // 根据优惠券计算商品减价
+                ];
+            }
+            
+            // 计算人均消费:根据商户历史订单计算
+            $average_price = $this->calculateAverageConsumption($v['shop_supplier_id']);
+            
+            // 计算与用户的距离
+            $distance = $this->calculateDistance(
+                $user_latitude, 
+                $user_longitude, 
+                floatval($v['latitude']), 
+                floatval($v['longitude'])
+            );
+            
+            // 计算最高减价:根据商户优惠券
+            $max_reduce_price = $this->calculateMaxReducePrice($v['shop_supplier_id']);
+            
+            // 商户信息格式化
+            $v['logo_image'] = isset($v['logo']) ? $v['logo']['file_path'] : '';
+            $v['category_name'] = isset($v['category']) ? $v['category']['name'] : '';
+            $v['latitude'] = (float)$v['latitude'];
+            $v['longitude'] = (float)$v['longitude'];
+            
+            // 模拟团购数据(根据实际业务调整)
+            $v['server_score'] = $v['server_score'] ?: 4.8;
+            $v['server_score_text'] = '';
+            if($v['server_score'] >= 4.0 && $v['server_score'] < 4.5){
+                $v['server_score_text'] = '优秀';
+            } else if($v['server_score'] >= 4.5 && $v['server_score'] < 4.8){
+                $v['server_score_text'] = '非常棒';
+            } else if($v['server_score'] >= 4.8){
+                $v['server_score_text'] = ' 超赞';
+            }
+            $v['comment'] = $v['fav_count'] ?: 0;
+            $v['average_price'] = $average_price; // 人均消费,根据历史订单计算
+            $v['distance'] = $distance; // 距离,根据经纬度计算
+            $v['max_reduce_price'] = $max_reduce_price; // 最高减价,根据优惠券计算
+            // 根据评分获取排名
+            $v['ranking'] = isset($rankMap[$v['shop_supplier_id']]) ? $rankMap[$v['shop_supplier_id']] : 999;
+            
+            unset($v['logo']);
+            unset($v['category']);
+        }
+        
+        // 更新分页数据
+        $list = $listData;
+        
+        return $list;
+    }
+
+    /**
+     * 计算商户的人均消费
+     * @param int $shop_supplier_id 商户ID
+     * @return float 人均消费金额
+     */
+    private function calculateAverageConsumption($shop_supplier_id)
+    {
+        // 查询商户的已完成订单(已支付且非退款)
+        $orderModel = \app\common\model\order\Order::where('shop_supplier_id', '=', $shop_supplier_id)
+            ->where('pay_status', '=', 20) // 已支付
+            ->where('order_status', '=', 30) // 已完成
+            ->where('is_delete', '=', 0)
+            ->field('order_id, pay_price, user_id')
+            ->select();
+        
+        if ($orderModel->isEmpty()) {
+            // 没有订单,返回默认值
+            return 0;
+        }
+        
+        // 计算总金额和总人数(去重用户)
+        $totalAmount = 0;
+        $userIds = [];
+        
+        foreach ($orderModel as $order) {
+            $totalAmount += $order['pay_price'];
+            $userIds[] = $order['user_id'];
+        }
+        
+        // 去重用户数量
+        $uniqueUsers = array_unique($userIds);
+        $userCount = count($uniqueUsers);
+        
+        // 计算人均消费
+        if ($userCount > 0) {
+            $averagePrice = round($totalAmount / $userCount, 2);
+            return $averagePrice > 0 ? $averagePrice : 0;
+        }
+        
+        return 0;
+    }
+
+    /**
+     * 计算两点之间的距离(根据经纬度)
+     * @param float $lat1 用户纬度
+     * @param float $lon1 用户经度
+     * @param float $lat2 商户纬度
+     * @param float $lon2 商户经度
+     * @return string 格式化后的距离字符串(如:"1.2km" 或 "500m")
+     */
+    private function calculateDistance($lat1, $lon1, $lat2, $lon2)
+    {
+        // 如果没有用户位置或商户位置,返回默认值
+        if (empty($lat1) || empty($lon1) || empty($lat2) || empty($lon2)) {
+            return '--';
+        }
+        
+        // 地球半径(千米)
+        $earthRadius = 6371;
+        
+        // 将角度转为弧度
+        $lat1Rad = deg2rad($lat1);
+        $lon1Rad = deg2rad($lon1);
+        $lat2Rad = deg2rad($lat2);
+        $lon2Rad = deg2rad($lon2);
+        
+        // 计算纬度和经度的差值
+        $latDiff = $lat2Rad - $lat1Rad;
+        $lonDiff = $lon2Rad - $lon1Rad;
+        
+        // Haversine公式计算距离
+        $a = sin($latDiff / 2) * sin($latDiff / 2) +
+             cos($lat1Rad) * cos($lat2Rad) *
+             sin($lonDiff / 2) * sin($lonDiff / 2);
+        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
+        $distance = $earthRadius * $c;
+        
+        // 格式化距离
+        if ($distance < 1) {
+            // 小于1千米,显示为米
+            return round($distance * 1000) . 'm';
+        } else if ($distance < 10) {
+            // 1-10千米,保留一位小数
+            return round($distance, 1) . 'km';
+        } else {
+            // 大于10千米,只显示整数
+            return round($distance) . 'km';
+        }
+    }
+
+    /**
+     * 计算商户的最高减价(根据优惠券)
+     * @param int $shop_supplier_id 商户ID
+     * @return float 最高减价金额
+     */
+    private function calculateMaxReducePrice($shop_supplier_id)
+    {
+        // 查询商户的有效优惠券(未删除、未过期、未被领完)
+        $couponModel = \app\common\model\plus\coupon\Coupon::where('shop_supplier_id', 'in', [$shop_supplier_id,0])
+            ->where('is_delete', '=', 0)
+            ->where(function ($query) {
+                // 领取后生效的优惠券
+                $query->whereOr('expire_type', '=', 10);
+                // 或者固定时间且未过期的优惠券
+                $query->whereOr(function ($q) {
+                    $q->where('expire_type', '=', 20)
+                      ->where('end_time', '>=', time() - 86400);
+                });
+            })
+            ->where(function ($query) {
+                // 数量不限制或者还有剩余
+                $query->whereOr('total_num', '=', -1);
+                $query->whereOr(function ($q) {
+                    $q->whereRaw('receive_num < total_num');
+                });
+            })
+            ->field('coupon_id, coupon_type, reduce_price, discount,total_num,expire_type,receive_num,end_time')
+            ->select();
+        if ($couponModel->isEmpty()) {
+            // 没有优惠券,返回0
+            return 0;
+        }
+        
+        $maxReduce = 0;
+        
+        foreach ($couponModel as $coupon) {
+            // 满减券:直接取减免金额
+            if ($coupon['coupon_type']['value'] == 10) {
+                if ($coupon['reduce_price'] > $maxReduce) {
+                    $maxReduce = $coupon['reduce_price'];
+                }
+            }
+            // 折扣券:按照一定基准价计算(假设100元的基准)
+            else if ($coupon['coupon_type']['value'] == 20) {
+                $basePrice = 100; // 基准价格
+                $discountRate = $coupon['discount'] / 10; // 折扣率(如8.5折)
+                $reduceAmount = $basePrice * (1 - $discountRate / 10);
+                if ($reduceAmount > $maxReduce) {
+                    $maxReduce = $reduceAmount;
+                }
+            }
+        }
+        
+            
+        // 返回整数
+        return intval($maxReduce);
+    }
+
+    /**
+     * 计算商品的优惠券减价
+     * @param int $shop_supplier_id 商户ID
+     * @param int $product_id 商品ID
+     * @param float $product_price 商品价格
+     * @return float 减价金额
+     */
+    private function calculateProductReducePrice($shop_supplier_id, $product_id, $product_price)
+    {
+        // 查询商户的有效优惠券
+        $couponModel = \app\common\model\plus\coupon\Coupon::where('shop_supplier_id', 'in', [$shop_supplier_id,0])
+            ->where('is_delete', '=', 0)
+            ->where(function ($query) {
+                // 领取后生效的优惠券
+                $query->whereOr('expire_type', '=', 10);
+                // 或者固定时间且未过期的优惠券
+                $query->whereOr(function ($q) {
+                    $q->where('expire_type', '=', 20)
+                      ->where('end_time', '>=', time() - 86400);
+                });
+            })
+            ->where(function ($query) {
+                // 数量不限制或者还有剩余
+                $query->whereOr('total_num', '=', -1);
+                $query->whereOr(function ($q) {
+                    $q->whereRaw('receive_num < total_num');
+                });
+            })
+            ->field('coupon_id, coupon_type, reduce_price, discount, min_price, apply_range, product_ids, category_ids')
+            ->select();
+        
+        if ($couponModel->isEmpty()) {
+            return 0;
+        }
+        
+        $maxReduce = 0;
+        
+        foreach ($couponModel as $coupon) {
+            // 检查优惠券的适用范围
+            $isApplicable = false;
+            
+            // 10-全部商品
+            if ($coupon['apply_range'] == 10) {
+                $isApplicable = true;
+            }
+            // 20-指定商品
+            else if ($coupon['apply_range'] == 20 && !empty($coupon['product_ids'])) {
+                $productIds = json_decode($coupon['product_ids'], true);
+                if (is_array($productIds) && in_array($product_id, $productIds)) {
+                    $isApplicable = true;
+                }
+            }
+            // 30-指定分类(需要查询商品分类,这里简化处理,默认不适用)
+            else if ($coupon['apply_range'] == 30) {
+                // 可以后续扩展分类判断逻辑
+                $isApplicable = false;
+            }
+            
+            if (!$isApplicable) {
+                continue;
+            }
+            
+            // 计算减免金额
+            $reduceAmount = 0;
+            
+            // 满减券
+            if ($coupon['coupon_type']['value'] == 10) {
+                // 检查是否达到最低消费金额
+                if ($product_price >= $coupon['min_price']) {
+                    $reduceAmount = $coupon['reduce_price'];
+                }
+            }
+            // 折扣券
+            else if ($coupon['coupon_type']['value'] == 20) {
+                $discountRate = $coupon['discount'] / 10; // 折扣率
+                $reduceAmount = $product_price * (1 - $discountRate / 10);
+            }
+            
+            if ($reduceAmount > $maxReduce) {
+                $maxReduce = $reduceAmount;
+            }
+        }
+        
+        // 返回整数
+        return intval($maxReduce);
+    }
 }

--
Gitblit v1.9.2