Kaynağa Gözat

ip解析,获取天气兼容GeoIP2和OpenWeatherMap服务

locky 1 yıl önce
ebeveyn
işleme
da4e5a22c1

+ 1 - 1
Ansjer/Config/gatewaySensorConfig.py

@@ -37,7 +37,7 @@ EVENT_TYPE = {
     # 门磁
     'door_magnet_opening': 2150,
     'door_magnet_closure': 2151,
-    'door_magnet_removal': 2152,
+    'door_magnet_removal': 2156,
     'door_magnet_low_power': 2153,
     'door_magnet_start_detection': 2154,
     'door_magnet_closure_detection': 2155,

+ 2 - 2
Controller/DeviceConfirmRegion.py

@@ -26,7 +26,7 @@ class ConfirmRegion(View):
             device_domain_qs = DeviceDomainModel.objects.filter(ip=ip)
 
             # 获取国家编码
-            ip_addr_qs = IPAddr.objects.filter(ip=ip).values('country_code')
+            ip_addr_qs = IPAddr.objects.filter(ip=ip, is_geoip2=False).values('country_code')
             if ip_addr_qs.exists():
                 country_code = ip_addr_qs[0]['country_code']
             else:
@@ -93,7 +93,7 @@ class ConfirmRegionV2(View):
             data_dict['ip'] = ip
 
             # 获取国家编码
-            ip_addr_qs = IPAddr.objects.filter(ip=ip).values('country_code')
+            ip_addr_qs = IPAddr.objects.filter(ip=ip, is_geoip2=False).values('country_code')
             if ip_addr_qs.exists():
                 country_code = ip_addr_qs[0]['country_code']
             else:

+ 55 - 22
Controller/ShadowController.py

@@ -3,12 +3,12 @@ import time
 
 from django.http import JsonResponse
 
-from Ansjer.config import SERVER_TYPE, LOGGER
-from Model.models import Device_Info, UidSetModel, UID_Preview, VoicePromptModel, UID_Bucket, UidChannelSetModel, \
+from Ansjer.config import SERVER_TYPE, LOGGER, CONFIG_INFO, CONFIG_CN
+from Model.models import Device_Info, UidSetModel, UID_Preview, VoicePromptModel, UidChannelSetModel, \
     AiService, CountryModel, CityInformation, IPAddr
 from Object.ETkObject import ETkObject
 from Service.CommonService import CommonService
-from Object.IPWeatherObject import IPQuery
+from Object.IPWeatherObject import IPQuery, GeoIP2
 
 
 # 更新设备影子
@@ -66,17 +66,8 @@ def update_device_shadow(request):
             AiService.objects.filter(uid=uid, use_status=1).update(detect_status=0, detect_group='')
             LOGGER.info('{}v2重置成功'.format(uid))
 
-            # 和新ip的city_id不一致,更新tb_city_information_id
-            uid_set_qs = UidSetModel.objects.filter(uid=uid).values('ip', 'tb_city_information_id')
-            if uid_set_qs.exists():
-                # 查询ip区级信息
-                district = get_district(ip)
-
-                city_information_qs = CityInformation.objects.filter(district=district).values('city_id')
-                if city_information_qs.exists():
-                    city_id = city_information_qs[0]['city_id']
-                    if uid_set_qs[0]['tb_city_information_id'] != city_id:
-                        uid_set_qs.update(tb_city_information_id=city_id)
+            # 更新ip信息
+            update_ip_info(ip, uid)
 
         ucode = request_dict.get('ucode', None)
         version = request_dict.get('version', None)
@@ -144,13 +135,7 @@ def update_device_shadow(request):
             qs_dict['tb_country'] = country
         LOGGER.info('{} qs_dict: {}'.format(uid, qs_dict))
 
-        # 查询ip区级信息
-        district = get_district(ip)
-
-        city_information_qs = CityInformation.objects.filter(district=district).values('city_id')
-        if city_information_qs.exists():
-            city_id = city_information_qs[0]['city_id']
-            qs_dict['tb_city_information_id'] = city_id
+        save_ip_info(ip, qs_dict)
 
         us_qs = UidSetModel.objects.filter(uid=uid)
         if us_qs.exists():
@@ -171,8 +156,56 @@ def update_device_shadow(request):
         return JsonResponse(status=200, data={'code': 1000001, 'update_shadow_error': repr(e)})
 
 
+def save_ip_info(ip, qs_dict):
+    """
+    保存ip信息
+    根据服务器配置使用不同的服务, 国内:阿里云, 测试,国外:geoip2
+    @param ip:
+    @param qs_dict:
+    @return:
+    """
+    if CONFIG_INFO == CONFIG_CN:
+        # 查询ip区级信息
+        district = get_district(ip)
+
+        city_information_qs = CityInformation.objects.filter(district=district).values('city_id')
+        if city_information_qs.exists():
+            city_id = city_information_qs[0]['city_id']
+            qs_dict['tb_city_information_id'] = city_id
+    else:
+        ip_addr_qs = IPAddr.objects.filter(ip=ip, is_geoip2=True)
+        if not ip_addr_qs.exists():
+            GeoIP2(ip)
+
+
+def update_ip_info(ip, uid):
+    """
+    更新ip信息
+    根据服务器配置使用不同的服务, 国内:阿里云, 测试,国外:geoip2
+    @param ip:
+    @param uid:
+    @return:
+    """
+    if CONFIG_INFO == CONFIG_CN:
+        # 和新ip的city_id不一致,更新tb_city_information_id
+        uid_set_qs = UidSetModel.objects.filter(uid=uid).values('ip', 'tb_city_information_id')
+        if uid_set_qs.exists():
+            # 查询ip区级信息
+            district = get_district(ip)
+
+            city_information_qs = CityInformation.objects.filter(district=district).values('city_id')
+            if city_information_qs.exists():
+                city_id = city_information_qs[0]['city_id']
+                if uid_set_qs[0]['tb_city_information_id'] != city_id:
+                    uid_set_qs.update(tb_city_information_id=city_id)
+    else:
+        ip_addr_qs = IPAddr.objects.filter(ip=ip, is_geoip2=True)
+        if not ip_addr_qs.exists():
+            GeoIP2(ip)
+
+
 def get_district(ip):
-    ip_addr_qs = IPAddr.objects.filter(ip=ip).values('district', 'city')
+    ip_addr_qs = IPAddr.objects.filter(ip=ip, is_geoip2=False).values('district', 'city')
     if ip_addr_qs.exists():
         district = ip_addr_qs[0]['district']
         if district == '':

+ 38 - 25
Controller/WeatherControl.py

@@ -6,12 +6,10 @@
 """
 import datetime
 
-from Ansjer.config import LOGGER
-import time
-
+from Ansjer.config import CONFIG_INFO, CONFIG_CN
 from django.views import View
-from Object.IPWeatherObject import WeatherInfo
-from Model.models import UidSetModel, CityInformation
+from Object.IPWeatherObject import WeatherInfo, OpenWeatherMap
+from Model.models import UidSetModel, IPAddr
 from Object.ResponseObject import ResponseObject
 from Object.RedisObject import RedisObject
 from Service.CommonService import CommonService
@@ -21,14 +19,14 @@ class WeatherView(View):
     def get(self, request, *args, **kwargs):
         request.encoding = 'utf-8'
         operation = kwargs.get('operation')
-        return self.validation(request.GET, operation, request)
+        return self.validation(request.GET, operation)
 
     def post(self, request, *args, **kwargs):
         request.encoding = 'utf-8'
         operation = kwargs.get('operation')
-        return self.validation(request.POST, operation, request)
+        return self.validation(request.POST, operation)
 
-    def validation(self, request_dict, operation, request):
+    def validation(self, request_dict, operation):
         lang = request_dict.get('lang', 'en')
         response = ResponseObject(lang)
         if operation == 'get':  # 获取天气
@@ -48,27 +46,42 @@ class WeatherView(View):
         uid = request_dict.get('uid')
         if not uid:
             return response.json(444, 'uid')
-        uid_set_qs = UidSetModel.objects.filter(uid=uid).values('tb_city_information_id')
+
+        uid_set_qs = UidSetModel.objects.filter(uid=uid).values('tb_city_information_id', 'ip')
         if not uid_set_qs.exists():
             return response.json(173)
-        redis_obj = RedisObject()
-        city_id = uid_set_qs[0]['tb_city_information_id']
-        if city_id == 0:
-            return response.json(10, '请更新设备影子地区信息')
-        today = datetime.datetime.today()
-        now_time = datetime.datetime(today.year, today.month, today.day, today.hour)
-        time_stamp = CommonService.str_to_timestamp(now_time.strftime('%Y-%m-%d %H:%M:%S'))
+
         try:
-            weather = redis_obj.get_data('city_id_{}_{}_weather'.format(city_id, time_stamp))
-            if weather:
-                temp, humidity = weather.split('/')
+            # 根据服务器配置使用不同的服务, 国内:阿里云墨迹天气, 测试,国外:OpenWeatherMap
+            if CONFIG_INFO == CONFIG_CN:
+                city_id = uid_set_qs[0]['tb_city_information_id']
+                if city_id == 0:
+                    return response.json(10, '请更新设备影子地区信息')
+
+                today = datetime.datetime.today()
+                now_time = datetime.datetime(today.year, today.month, today.day, today.hour)
+                time_stamp = CommonService.str_to_timestamp(now_time.strftime('%Y-%m-%d %H:%M:%S'))
+                key = 'city_id_{}_{}_weather'.format(city_id, time_stamp)
+
+                redis_obj = RedisObject()
+                weather = redis_obj.get_data(key)
+                if weather:
+                    temp, humidity = weather.split('/')
+                else:
+                    city_obj = WeatherInfo(city_id)
+                    temp, humidity = city_obj.get_city_weather()
+                    if temp and humidity:
+                        redis_obj.set_ex_data(key, '{}/{}'.format(temp, humidity), 3600)
             else:
-                city_obj = WeatherInfo(city_id)
-                temp, humidity = city_obj.get_city_weather()
-                if temp and humidity:
-                    redis_obj.set_ex_data('city_id_{}_{}_weather'.format(city_id, time_stamp),
-                                          '{}/{}'.format(temp, humidity), 3600)
-            if not all([temp, humidity]):
+                ip = uid_set_qs[0]['ip']
+                ip_addr_qs = IPAddr.objects.filter(ip=ip, is_geoip2=True).values('lat', 'lon')
+                if not ip_addr_qs.exists():
+                    return response.json(173)
+                lat, lon = ip_addr_qs[0]['lat'], ip_addr_qs[0]['lon']
+                open_weather_map = OpenWeatherMap(lat, lon)
+                temp, humidity = open_weather_map.get_weather()
+
+            if temp is None and humidity is None:
                 return response.json(10, '获取天气失败')
             return response.json(0, {'temp': temp, 'humidity': humidity})
         except Exception as e:

+ 13 - 0
Model/models.py

@@ -4246,12 +4246,25 @@ class IPAddr(models.Model):
     city = models.CharField(default='', max_length=32, verbose_name='市')
     region = models.CharField(default='', max_length=32, verbose_name='省/州')
     country_code = models.CharField(default='', max_length=8, verbose_name='国家编码')
+    lat = models.FloatField(default=0, verbose_name='经度')
+    lon = models.FloatField(default=0, verbose_name='纬度')
+    is_geoip2 = models.BooleanField(default=False, verbose_name='是否为geoip2解析')
 
     class Meta:
         db_table = 'ip_addr'
         verbose_name = 'ip地址信息'
 
 
+class OpenWeatherMapCallCount(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='主键')
+    month = models.CharField(default='', max_length=20, verbose_name='月份')
+    count = models.IntegerField(default=1, verbose_name='api调用次数')
+
+    class Meta:
+        db_table = 'open_weather_map_call_count'
+        verbose_name = 'OpenWeatherMap调用次数'
+
+
 class DeviceLiveRestrict(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='主键')
     device_type = models.IntegerField(default=0, verbose_name='设备类型id')

+ 137 - 2
Object/IPWeatherObject.py

@@ -1,9 +1,15 @@
 # @Author    : Rocky
 # @File      : IPWeatherObject.py
 # @Time      : 2023/8/16 8:56
+import datetime
+
+import geoip2.webservice
 import requests
 
-from Model.models import IPAddr
+from Model.models import IPAddr, OpenWeatherMapCallCount
+from Object.RedisObject import RedisObject
+from Service.CommonService import CommonService
+from Ansjer.config import LOGGER
 
 
 class IPQuery:
@@ -42,7 +48,7 @@ class IPQuery:
                     city += '市'
 
                 # ip地址信息存表或更新
-                ip_addr_qs = IPAddr.objects.filter(ip=ip)
+                ip_addr_qs = IPAddr.objects.filter(ip=ip, is_geoip2=False)
                 if ip_addr_qs.exists():
                     ip_addr_qs.update(district=district, city=city, region=region, country_code=country_id)
                 else:
@@ -58,6 +64,10 @@ class IPQuery:
 
 
 class WeatherInfo:
+    """
+    阿里云墨迹天气服务
+    https://market.aliyun.com/products/57096001/cmapi013828.html?spm=5176.2020520132.101.19.2b8f7218NuiGPd#sku=yuncode782800000
+    """
     def __init__(self, city_id):
         self.appcode = 'd7d63b34b1d54214be446608a57ff0a2'
         self.headers = {'Authorization': 'APPCODE ' + self.appcode,
@@ -85,3 +95,128 @@ class WeatherInfo:
                 # 返回天气列表
                 return result['data']['hourly']
         return None
+
+
+class GeoIP2:
+    """
+    MaxMind GeoIP2查询国外ip
+    同时保存ip信息
+    https://www.maxmind.com/
+    """
+
+    def __init__(self, ip):
+        self.account_id = 938644
+        self.license_key = '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'
+        self.license_key_sandbox = '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'
+
+        try:
+            with geoip2.webservice.Client(self.account_id, self.license_key) as client:
+                # You can also use `client.city` or `client.insights`
+                # `client.insights` is not available to GeoLite2 users
+                response = client.city(ip)
+
+                # 经纬度精确到小数点两位
+                lat = round(response.location.latitude, 2)
+                lon = round(response.location.longitude, 2)
+
+                # 获取中文或英文城市名,省/州
+                city = ''
+                city_names = response.city.names
+                city_cn = city_names.get('zh-CN')
+                if city_cn:
+                    city = city_cn
+                elif city_names.get('en'):
+                    city = city_names['en']
+
+                region = ''
+                subdivisions_names = response.subdivisions[0].names
+                region_cn = subdivisions_names.get('zh-CN')
+                if region_cn:
+                    region = region_cn
+                elif subdivisions_names.get('en'):
+                    region = subdivisions_names['en']
+
+                country_code = response.country.iso_code
+
+                # 保存ip信息
+                ip_addr_data = {
+                    'ip': ip,
+                    'lat': lat,
+                    'lon': lon,
+                    'city': city,
+                    'region': region,
+                    'country_code': country_code,
+                    'is_geoip2': True
+                }
+                IPAddr.objects.create(**ip_addr_data)
+
+        except Exception as e:
+            LOGGER.info('GeoIP2解析ip异常:error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+
+class OpenWeatherMap:
+    """
+    OpenWeatherMap查询国外天气服务
+    https://openweathermap.org/
+    """
+
+    def __init__(self, lat, lon):
+        self.appid = '7a6cd7dfeb034ededa451ed575788857'
+        self.lat = lat  # 纬度
+        self.lon = lon  # 经度
+
+    def get_weather(self):
+        """
+        从缓存查询天气数据
+        或者查询当前天气,并缓存数据
+        @return: temp, humidity
+        """
+        # 查询缓存数据
+        today = datetime.datetime.today()
+        now_time = datetime.datetime(today.year, today.month, today.day, today.hour)
+        str_time = now_time.strftime('%Y-%m-%d %H:%M:%S')
+        time_stamp = CommonService.str_to_timestamp(str_time)
+        key = 'weather:lat:{}_lon:{}_time_stamp:{}'.format(self.lat, self.lon, time_stamp)
+
+        redis_obj = RedisObject()
+        weather = redis_obj.get_data(key)
+        if weather:
+            temp, humidity = weather.split('/')
+        else:
+            temp, humidity = self.get_current_weather(str_time[:7])
+            if temp is not None and humidity is not None:
+                key = 'weather:lat:{}_lon:{}_time_stamp:{}'.format(self.lat, self.lon, time_stamp)
+                redis_obj.set_ex_data(key, '{}/{}'.format(temp, humidity), 3600)
+        return temp, humidity
+
+    def get_current_weather(self, month):
+        """
+        根据经纬度获取当前天气
+        @param month: 年月份
+        @return: temp, humidity
+        """
+        url = 'https://api.openweathermap.org/data/2.5/weather'
+        params = {
+            'lat': self.lat,
+            'lon': self.lon,
+            'appid': self.appid,
+            'units': 'metric'   # 公制单位,温度单位:摄氏度
+        }
+        res = requests.get(url=url, params=params, timeout=10)
+
+        # 记录调用次数
+        open_weather_map_call_count_qs = OpenWeatherMapCallCount.objects.filter(month=month).values('count')
+        if not open_weather_map_call_count_qs.exists():
+            OpenWeatherMapCallCount.objects.create(month=month)
+        else:
+            count = open_weather_map_call_count_qs[0]['count'] + 1
+            open_weather_map_call_count_qs.update(count=count)
+
+        if res.status_code != 200:
+            return None, None
+        res_data = eval(res.text)
+        if res_data['cod'] != 200:
+            return None, None
+        temp = int(res_data['main']['temp'])
+        humidity = int(res_data['main']['humidity'])
+        return temp, humidity