diff --git a/backend/Dockerfile b/backend/Dockerfile index 608b883..1b44c28 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -16,7 +16,7 @@ COPY . . RUN pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple # 入口命令由 docker-compose 控制 # 数据库迁移 -#RUN python manage.py makemigrations && python manage.py migrate +RUN python manage.py migrate # 收集静态文件 RUN python manage.py collectstatic --noinput diff --git a/backend/requirements.txt b/backend/requirements.txt index 0f5b931..b683396 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -14,4 +14,5 @@ goofish_api==0.0.6 flower==2.0.1 gunicorn==23.0.0 django_redis==6.0.0 -django-ninja==1.4.3 \ No newline at end of file +django-ninja==1.4.3 +ip2geotools==0.1.6 \ No newline at end of file diff --git a/backend/system/migrations/0003_loginlog_location.py b/backend/system/migrations/0003_loginlog_location.py new file mode 100644 index 0000000..3d5f512 --- /dev/null +++ b/backend/system/migrations/0003_loginlog_location.py @@ -0,0 +1,20 @@ +# Generated by Django 5.2.1 on 2025-08-13 03:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("system", "0002_alter_dept_creator_alter_dept_is_deleted_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="loginlog", + name="location", + field=models.CharField( + blank=True, db_comment=" 地理位置", default="", max_length=200 + ), + ), + ] diff --git a/backend/system/models.py b/backend/system/models.py index 35299d4..58e9ca1 100644 --- a/backend/system/models.py +++ b/backend/system/models.py @@ -296,6 +296,7 @@ class LoginLog(CoreModel): result = models.IntegerField(choices=LoginResult.choices, default=LoginResult.SUCCESS, db_comment='登录结果') user_ip = models.CharField(max_length=50, db_comment='用户 IP') user_agent = models.CharField(max_length=512, db_comment='浏览器 UA') + location = models.CharField(max_length=200, db_comment=' 地理位置', blank=True, default='') class Meta: db_table = 'system_login_log' diff --git a/backend/system/views/user.py b/backend/system/views/user.py index 7b8bbbf..280cd6e 100644 --- a/backend/system/views/user.py +++ b/backend/system/views/user.py @@ -8,6 +8,7 @@ from rest_framework.views import APIView from django.contrib.auth.hashers import make_password from rest_framework.permissions import IsAuthenticated from django_filters import rest_framework as filters +from ip2geotools.databases.noncommercial import DbIpCity from system.models import User, Menu, LoginLog, Dept from utils.ip_utils import get_client_ip @@ -59,7 +60,10 @@ class UserLogin(ObtainAuthToken): # 获取真实IP地址 client_ip = get_client_ip(request) - + + # 获取IP地址的地理位置信息 + location_info = self.get_location_from_ip(client_ip) + # 更新登录IP和登录时间 user.login_ip = client_ip user.last_login = timezone.now() @@ -70,6 +74,7 @@ class UserLogin(ObtainAuthToken): username=user.username, result=LoginLog.LoginResult.SUCCESS, user_ip=client_ip, + location=location_info, user_agent=request.META.get('HTTP_USER_AGENT', '') ) # 在序列化后的数据中加入 accessToken @@ -81,6 +86,33 @@ class UserLogin(ObtainAuthToken): "message": "ok" }) + def get_location_from_ip(self, ip_address): + """根据IP地址获取地理位置信息""" + try: + # 对于本地IP地址,返回默认值 + if ip_address in ['127.0.0.1', 'localhost']: + return "本地网络" + + # 查询IP地址信息 + response = DbIpCity.get(ip_address, api_key='free') + + # 构建地区信息字符串 + location_parts = [] + if response.city: + location_parts.append(response.city) + if response.region: + location_parts.append(response.region) + if response.country: + location_parts.append(response.country) + + return ', '.join(location_parts) if location_parts else "未知位置" + + except Exception as e: + # 记录错误日志 + import logging + logger = logging.getLogger(__name__) + logger.error(f"获取IP地址位置信息失败: {str(e)}") + return "位置获取失败" class UserInfo(APIView): diff --git a/web/apps/web-antd/src/views/system/login_log/data.ts b/web/apps/web-antd/src/views/system/login_log/data.ts index e6d3f0f..e7c1ca8 100644 --- a/web/apps/web-antd/src/views/system/login_log/data.ts +++ b/web/apps/web-antd/src/views/system/login_log/data.ts @@ -79,7 +79,12 @@ export function useColumns(): VxeTableGridOptions