| | |
| | | |
| | | namespace app\api\model\supplier; |
| | | |
| | | use app\common\model\plus\shareholder\Apply as ShareholderApplyModel; |
| | | use app\common\model\supplier\Supplier as SupplierModel; |
| | | use app\common\model\supplier\User as SupplierUserModel; |
| | | use app\api\model\user\Favorite as FavoriteModel; |
| | |
| | | $data['is_super'] = 1; |
| | | $SupplierUserModel->save($data); |
| | | (new teamApplyModel())->becomeTeamByAgent($data['referee_id'],70,$data['app_id']); |
| | | //根据团队人数判断股东 by yj |
| | | $shareholderModel = new ShareholderApplyModel; |
| | | $shareholderModel->becomeShareholderByTeam($data['referee_id'], 80, $data['app_id']); |
| | | $this->commit(); |
| | | return true; |
| | | } catch (\Exception $e) { |
| | |
| | | public function getDetail($data, $user) |
| | | { |
| | | $detail = $this->alias('s')->where(['shop_supplier_id' => $data['shop_supplier_id']]) |
| | | ->field("name as store_name,shop_supplier_id,logo_id,category_id,server_score,fav_count,user_id,product_sales,address,notice") |
| | | ->field("name as store_name,shop_supplier_id,logo_id,category_id,server_score,fav_count,user_id,product_sales,address,notice,description") |
| | | ->with(['logo', 'category']) |
| | | ->find(); |
| | | if ($detail) { |
| | |
| | | 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']); |
| | | } |
| | |
| | | ->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") |
| | | ->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(); |
| | |
| | | $v['productList'] = $productList; |
| | | $v['logos'] = isset($v['logo'])?$v['logo']['file_path']:''; |
| | | $v['category_name'] = $v['category']['name']; |
| | | $v['latitude'] = (float)$v['latitude']; |
| | | $v['longitude'] = (float)$v['longitude']; |
| | | unset($v['logo']); |
| | | unset($v['category']); |
| | | } |
| | |
| | | ->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); |
| | | } |
| | | } |