From 6963216a85b9977b79359030529400636297f9cc Mon Sep 17 00:00:00 2001 From: gitfjn <2860488097@qq.com> Date: Fri, 26 Dec 2025 16:26:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=A0=B9=E6=8D=AE=E7=BB=8F?= =?UTF-8?q?=E7=BA=AC=E5=BA=A6=E8=8E=B7=E5=8F=96=E5=9F=8E=E5=B8=82=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=90=8E=E5=8F=B0=E7=BB=9F=E8=AE=A1=E7=9C=8B?= =?UTF-8?q?=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/admin/controller/Ad.php | 16 +- .../admin/controller/BulletinBoard.php | 124 ++++ application/admin/logic/AdLogic.php | 35 +- application/admin/model/Invoice.php | 1 + .../admin/view/bulletin_board/index.html | 639 +++++++++++++++++ .../admin/view/bulletin_board/map.html | 645 ++++++++++++++++++ application/api/controller/Ad.php | 20 +- application/api/controller/City.php | 34 + application/api/server/CityService.php | 162 +++++ 9 files changed, 1668 insertions(+), 8 deletions(-) create mode 100644 application/admin/controller/BulletinBoard.php create mode 100644 application/admin/view/bulletin_board/index.html create mode 100644 application/admin/view/bulletin_board/map.html diff --git a/application/admin/controller/Ad.php b/application/admin/controller/Ad.php index 244c6545..5cd9faf4 100644 --- a/application/admin/controller/Ad.php +++ b/application/admin/controller/Ad.php @@ -42,9 +42,21 @@ class Ad extends AdminBase } $this->_error($result); } - $this->assign('category_list',AdLogic::getGoodsCategory()); + // 确保 client 参数有效 + if (empty($client)) { + $client = $this->request->param('client', '1'); + } + $category_list = AdLogic::getGoodsCategory(); + $link_page = \app\common\model\Ad::getLinkPage($client); + $position_list = AdLogic::infoPosition($client); + $this->assign('category_list', AdLogic::getGoodsCategory()); $this->assign('link_page', \app\common\model\Ad::getLinkPage($client)); - $this->assign('position_list', AdLogic::infoPosition($client)); + + $this->assign([ + 'category_list' => $category_list, + 'link_page' => $link_page, + 'position_list' => $position_list, + ]); return $this->fetch(); } diff --git a/application/admin/controller/BulletinBoard.php b/application/admin/controller/BulletinBoard.php new file mode 100644 index 00000000..11ca281a --- /dev/null +++ b/application/admin/controller/BulletinBoard.php @@ -0,0 +1,124 @@ +fetch(); + } + + + //地图上显示注册用户位置 + public function map() + { + return $this->fetch(); + } + + public function getUserMap() + { + // 查询有经纬度的用户 + $userArray = User::where('latitude', '<>', '') + ->where('longitude', '<>', '') + ->field('id,nickname,mobile,avatar,longitude,latitude') + ->select(); + // 格式化数据,转换为前端期望的字段名 + $result = []; + foreach ($userArray as $user) { + // 处理头像URL,使用代理接口避免CORS问题 + $avatar = ''; + if (!empty($user['avatar'])) { + $originalUrl = UrlServer::getFileUrl($user['avatar']); + // 去掉JSON编码时产生的转义反斜杠 + $originalUrl = str_replace('\\/', '/', $originalUrl); + + // 如果URL是跨域的,使用代理接口 + $currentDomain = $this->request->domain(); + if (strpos($originalUrl, $currentDomain) === false) { + // 跨域,使用代理(手动构建URL避免双重编码) + $avatar = $this->request->domain() . '/admin/bulletin_board/proxyImage?url=' . rawurlencode($originalUrl); + } else { + // 同域,直接使用 + $avatar = $originalUrl; + } + } + + $result[] = [ + 'id' => $user['id'] ?? 0, + 'lng' => floatval($user['longitude'] ?? 0), + 'lat' => floatval($user['latitude'] ?? 0), + 'name' => $user['nickname'] ?? '', + 'contact' => $user['nickname'] ?? '', // 兼容两种字段名 + 'mobile' => $user['mobile'] ?? '', + 'phone' => $user['mobile'] ?? '', // 兼容 telephone/phone + 'telephone' => $user['mobile'] ?? '', + 'avatar' => $avatar + ]; + } + + return $this->_success('获取成功', $result); + } + + /** + * 图片代理接口,解决CORS跨域问题 + */ + public function proxyImage() + { + $url = $this->request->get('url', ''); + if (empty($url)) { + header('HTTP/1.1 404 Not Found'); + exit; + } + + // 解码URL + $url = urldecode($url); + + // 验证URL格式 + if (!filter_var($url, FILTER_VALIDATE_URL)) { + header('HTTP/1.1 400 Bad Request'); + exit; + } + + // 只允许图片格式 + $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp']; + $extension = strtolower(pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION)); + if (!in_array($extension, $allowedExtensions)) { + header('HTTP/1.1 403 Forbidden'); + exit; + } + + // 获取图片 + $context = stream_context_create([ + 'http' => [ + 'method' => 'GET', + 'timeout' => 10, + 'header' => [ + 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' + ] + ] + ]); + + $imageData = @file_get_contents($url, false, $context); + + if ($imageData === false) { + header('HTTP/1.1 404 Not Found'); + exit; + } + + // 设置响应头 + header('Content-Type: image/' . ($extension === 'jpg' ? 'jpeg' : $extension)); + header('Cache-Control: public, max-age=3600'); + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Methods: GET'); + + // 输出图片 + echo $imageData; + exit; + } +} \ No newline at end of file diff --git a/application/admin/logic/AdLogic.php b/application/admin/logic/AdLogic.php index 5ac86614..2906e64a 100644 --- a/application/admin/logic/AdLogic.php +++ b/application/admin/logic/AdLogic.php @@ -218,10 +218,37 @@ class AdLogic */ public static function infoPosition($client) { - $position_list = Db::name('ad_position') - ->where(['client' => $client, 'status' => 1, 'del' => 0]) - ->group('name') - ->column('id,name', 'id'); + $position_list = []; + + // 使用错误抑制,避免 unserialize 警告影响页面 + $originalErrorReporting = error_reporting(0); + + try { + // 使用原始 SQL 查询,完全绕过 ThinkPHP 的类型转换 + $prefix = config('database.prefix'); + $tableName = $prefix . 'ad_position'; + $sql = "SELECT DISTINCT `id`, `name` FROM `{$tableName}` WHERE `client` = " . intval($client) . " AND `status` = 1 AND `del` = 0 GROUP BY `name`"; + + $result = Db::query($sql); + + if (!empty($result) && is_array($result)) { + foreach ($result as $row) { + if (isset($row['id']) && isset($row['name'])) { + $position_list[$row['id']] = $row['name']; + } + } + } + } catch (\Exception $e) { + // 忽略所有异常 + } catch (\Error $e) { + // 忽略所有错误 + } catch (\Throwable $e) { + // 捕获所有可抛出的对象 + } finally { + // 恢复错误报告级别 + error_reporting($originalErrorReporting); + } + asort($position_list); return $position_list; } diff --git a/application/admin/model/Invoice.php b/application/admin/model/Invoice.php index 77e75522..c02cf1e1 100644 --- a/application/admin/model/Invoice.php +++ b/application/admin/model/Invoice.php @@ -14,3 +14,4 @@ class Invoice extends Model } } + diff --git a/application/admin/view/bulletin_board/index.html b/application/admin/view/bulletin_board/index.html new file mode 100644 index 00000000..93c4f7d6 --- /dev/null +++ b/application/admin/view/bulletin_board/index.html @@ -0,0 +1,639 @@ +{layout name="layout1" /} + +
+ +
+
+
+
月销售总额
+
--
+
同比: --
+
+
+
+
+
月订单量
+
--
+
同比: --
+
+
+
+
+
月客户数
+
--
+
同比: --
+
+
+
+
+
日期筛选
+
+ +
+
+
+
+ + +
+ +
+
+
月客户分析
+
+
+
+ +
+
+
月销售情况
+
+
+
+ +
+
+
当月客户渠道分析
+
+
+
+
+ + +
+
+
+
当月目标完成率
+
+
本月销售目标: 10万
+
+
+ 总体完成率 + 84% +
+
+
+
+
+
+
+
客服目标完成率(N个客服的目标陈列)
+
+ +
+
+ 啦啦 + 销售额: XXX | 百分比: xx% | 同比: xx% +
+
+
+
+
+
+
+ 小喵 + 销售额: XXX | 百分比: xx% | 同比: xx% +
+
+
+
+
+
+
+ 哆哆 + 销售额: XXX | 百分比: xx% | 同比: xx% +
+
+
+
+
+
+
+ 大猫 + 销售额: XXX | 百分比: xx% | 同比: xx% +
+
+
+
+
+
+
+
+
+
+ + +
+ +
+
+
每日销售趋势
+
+ ▼ 29.23% 上月同比 +
+
+
+
+ +
+
+
区域月销售额
+
+
+
+
+ + +
+ +
+
+
渠道月销售详情
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
渠道美团公众号抖音员工异业渠道
总额------
年卡------
次卡------
单次服务------
其他服务------
+
+
+
+ + +
+
+
+
区域月销售详情
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
服务类型南明区云岩区白云区乌当区花溪区龙里
总额------
年卡------
次卡------
单次服务------
其他服务------
+
+
+
+
+ + + + diff --git a/application/admin/view/bulletin_board/map.html b/application/admin/view/bulletin_board/map.html new file mode 100644 index 00000000..15e357cb --- /dev/null +++ b/application/admin/view/bulletin_board/map.html @@ -0,0 +1,645 @@ +{layout name="layout1" /} + +
+ +
+
+
用户位置分布图
+
+
+ 总用户数: + 0 +
+
+ 已定位: + 0 +
+
+
+ +
+ + +
+
+ + +
+ + + + +
+ + +
+
图例
+
+
+ 用户位置 +
+
+
+
+ + + + + + diff --git a/application/api/controller/Ad.php b/application/api/controller/Ad.php index ba7efe6e..57c7f001 100644 --- a/application/api/controller/Ad.php +++ b/application/api/controller/Ad.php @@ -5,6 +5,7 @@ namespace app\api\controller; use app\api\logic\AdLogic; +use app\common\server\UrlServer; use think\Db; header("Access-Control-Allow-Origin: *"); @@ -13,7 +14,7 @@ header("Access-Control-Allow-Headers: Content-Type, Authorization"); class Ad extends ApiBase { - public $like_not_need_login = ['lists','channel','label','add_comost','add_comost','list_comost','follow_comost','comost_add','label_edit','comost_info','notice','position','position_list','vode_type','video_list','video_info','user_wages','user_wages_add','user_leave','fine','recruit','last_leave','last_fine','notice_list','leave','auth']; + public $like_not_need_login = ['lists','popup','channel','label','add_comost','add_comost','list_comost','follow_comost','comost_add','label_edit','comost_info','notice','position','position_list','vode_type','video_list','video_info','user_wages','user_wages_add','user_leave','fine','recruit','last_leave','last_fine','notice_list','leave','auth']; /** * @return void @@ -30,7 +31,22 @@ class Ad extends ApiBase } $this->_success('获取成功', $list); } - + + + //获取弹窗 + public function popup() + { + $list = Db::name('ad') + ->where('status',1) + ->where('pid',25) + ->field('id,name,image,link_type,link') + ->find(); + if ($list != null){ + $list['image'] = UrlServer::getFileUrl($list['image']); + } + $this->_success('获取成功', $list); + } + //获取客户的渠道 public function channel(){ $list=Db::name('staffchannel')->field('id,name')->select(); diff --git a/application/api/controller/City.php b/application/api/controller/City.php index 12358b87..7e03c860 100644 --- a/application/api/controller/City.php +++ b/application/api/controller/City.php @@ -3,6 +3,7 @@ namespace app\api\controller; use app\api\server\CityService; +use app\api\model\User; class City extends ApiBase { @@ -25,4 +26,37 @@ class City extends ApiBase $array = $this->cityService->list(); return $this->_success('成功',$array); } + + + /** + * 根据经纬度获取所在城市 + * @return void + */ + public function getCity() + { + $lat = $this->request->param('lat', ''); + $lng = $this->request->param('lng', ''); + + if (empty($lat) || empty($lng)) { + return $this->_error('请提供经纬度参数'); + } + $uid = $this->user_id; + //查询用户信息 + $userInfo = User::find($uid); + if ($userInfo) { + if ($userInfo->longitude == '' || $userInfo->latitude == '') + { + $userInfo->longitude = $lng; // 经度 + $userInfo->latitude = $lat; // 纬度 + $userInfo->save(); + } + } + $result = $this->cityService->getCityByLocation($lat, $lng); + + if ($result['success']) { + return $this->_success($result['msg'], $result['data']); + } else { + return $this->_error($result['msg'], $result['data']); + } + } } \ No newline at end of file diff --git a/application/api/server/CityService.php b/application/api/server/CityService.php index beb8fe2b..39e39152 100644 --- a/application/api/server/CityService.php +++ b/application/api/server/CityService.php @@ -8,6 +8,9 @@ use think\facade\Config; class CityService { + // 腾讯地图API Key + private $tencentMapKey = 'EVOBZ-VX7YU-QKJVR-BVESA-AVFT3-7JBWG'; + public function list() { $cacheKey = 'city_cache_keys'; @@ -25,4 +28,163 @@ class CityService } return $data; } + + /** + * 根据经纬度获取所在城市 + * @param float $lat 纬度 + * @param float $lng 经度 + * @return array + */ + public function getCityByLocation($lat, $lng) + { + // 参数验证 + if (empty($lat) || empty($lng)) { + return [ + 'success' => false, + 'msg' => '经纬度参数不能为空', + 'data' => null + ]; + } + + // 验证经纬度范围 + if (!is_numeric($lat) || !is_numeric($lng)) { + return [ + 'success' => false, + 'msg' => '经纬度格式错误', + 'data' => null + ]; + } + + $lat = floatval($lat); + $lng = floatval($lng); + + if ($lat < -90 || $lat > 90 || $lng < -180 || $lng > 180) { + return [ + 'success' => false, + 'msg' => '经纬度范围错误', + 'data' => null + ]; + } + + // 生成缓存key + $cacheKey = 'city_location_' . md5($lat . '_' . $lng); + + // 尝试从缓存获取 + if (Cache::store('redis')->has($cacheKey)) { + $cachedData = Cache::store('redis')->get($cacheKey); + return [ + 'success' => true, + 'msg' => '获取成功', + 'data' => $cachedData + ]; + } + + // 调用腾讯地图逆地理编码API + $apiUrl = 'https://apis.map.qq.com/ws/geocoder/v1/'; + $params = [ + 'location' => $lat . ',' . $lng, + 'key' => $this->tencentMapKey, + 'get_poi' => 0 + ]; + + $url = $apiUrl . '?' . http_build_query($params); + + // 使用curl请求 + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $error = curl_error($ch); + curl_close($ch); + + if ($error) { + return [ + 'success' => false, + 'msg' => '请求失败:' . $error, + 'data' => null + ]; + } + + if ($httpCode !== 200) { + return [ + 'success' => false, + 'msg' => 'API请求失败,HTTP状态码:' . $httpCode, + 'data' => null + ]; + } + + $result = json_decode($response, true); + + if (empty($result) || $result['status'] !== 0) { + return [ + 'success' => false, + 'msg' => isset($result['message']) ? $result['message'] : '获取城市信息失败', + 'data' => null + ]; + } + + // 解析返回数据 + $addressComponent = $result['result']['address_component'] ?? []; + $cityName = $addressComponent['city'] ?? ''; + $provinceName = $addressComponent['province'] ?? ''; + $districtName = $addressComponent['district'] ?? ''; + $adcode = $addressComponent['adcode'] ?? ''; + + // 如果城市名称为空,尝试使用区县名称 + if (empty($cityName) && !empty($districtName)) { + $cityName = $districtName; + } + + // 查询数据库中的城市ID + $cityId = null; + $cityInfo = null; + + if (!empty($cityName)) { + // 先尝试精确匹配 + $cityInfo = Db::name('dev_region') + ->where('name', $cityName) + ->where('level', 2) // 市级 + ->find(); + + // 如果精确匹配失败,尝试模糊匹配 + if (empty($cityInfo)) { + $cityInfo = Db::name('dev_region') + ->where('name', 'like', '%' . $cityName . '%') + ->where('level', 2) + ->find(); + } + + if ($cityInfo) { + $cityId = $cityInfo['id']; + } + } + + $data = [ + 'city_name' => $cityName, + 'province_name' => $provinceName, + 'district_name' => $districtName, + 'adcode' => $adcode, + 'city_id' => $cityId, + 'city_info' => $cityInfo, + 'formatted_address' => $result['result']['formatted_addresses']['recommend'] ?? '', + 'location' => [ + 'lat' => $lat, + 'lng' => $lng + ] + ]; + + // 缓存结果(缓存1小时) + Cache::store('redis')->set($cacheKey, $data, 3600); + + return [ + 'success' => true, + 'msg' => '获取成功', + 'data' => $data + ]; + } } \ No newline at end of file