add update prox

This commit is contained in:
unknown
2025-08-25 18:44:35 +08:00
parent aa6695e93f
commit 48099a884b
5 changed files with 228 additions and 12 deletions

View File

@ -41,7 +41,67 @@ def set_default_routes(value='youDao'):
)
def ensure_use_personal_config_column(table_name='translate_config'):
"""
确保 translate_config 表存在 usePersonalConfig 字段,默认值为 'true'
"""
try:
from django.db import connection
with connection.cursor() as cursor:
# 当前数据库名
cursor.execute("SELECT DATABASE()")
db_name = cursor.fetchone()[0]
# 检测列是否存在
cursor.execute(
"""
SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA=%s AND TABLE_NAME=%s AND COLUMN_NAME=%s
""",
[db_name, table_name, 'usePersonalConfig']
)
exists = cursor.fetchone()[0] > 0
if not exists:
cursor.execute(
"""
ALTER TABLE translate_config
ADD COLUMN usePersonalConfig VARCHAR(8) NOT NULL DEFAULT 'true'
COMMENT '是否启用当前联系人配置true/false'
"""
)
print('[OK] Added column usePersonalConfig to translate_config')
else:
print('[SKIP] Column usePersonalConfig already exists')
except Exception as e:
print('[WARN] ensure_use_personal_config_column failed:', e)
def repair_use_personal_config_values():
"""
将已有数据中 NULL 或 空字符串 的 usePersonalConfig 修复为 'true'
"""
try:
from django.db import connection
with connection.cursor() as cursor:
cursor.execute(
"""
UPDATE translate_config
SET usePersonalConfig = 'true'
WHERE usePersonalConfig IS NULL OR usePersonalConfig = ''
"""
)
print('[OK] Repaired translate_config.usePersonalConfig NULL/empty -> true')
except Exception as e:
print('[WARN] repair_use_personal_config_values failed:', e)
if __name__ == '__main__':
real, hist = set_default_routes('youDao')
print('OK set defaults: ', real, hist)
# 确保字段存在并修复数据
ensure_use_personal_config_column()
repair_use_personal_config_values()
print('OK ensure/repair usePersonalConfig complete')

View File

@ -257,14 +257,14 @@ def translate_api(request):
target_user = user
if not safe_deduct_character_count(username=target_user.username, key=target_user.user_api_key, deduction=-char_count):
return Response(
{"error": f"账户可用字符不足,请充值"},
status=status.HTTP_400_BAD_REQUEST
)
# 设备字符增加
if not safe_deduct_character_add(device=device, user_id=user.id, deduction=char_count):
return Response(
{"error": f"设备字符添加失败"},
status=status.HTTP_400_BAD_REQUEST
@ -287,24 +287,24 @@ def translate_api(request):
# contact_name=contact_name,
# )
# save_record.save()
return Response(
{"translate_text": translate_text},
)
except Exception as e:
return Response(
{"error": f"翻译服务调用失败,请检查:{e}"},
status=status.HTTP_400_BAD_REQUEST
)
except Exception as e:
return Response(
{"error": f"查询翻译服务或者编码出错:{e} "},
status=status.HTTP_400_BAD_REQUEST
)
except Exception as e:
return Response(
{"error": f"翻译失败,请重试或更换翻译服务!"},
status=status.HTTP_400_BAD_REQUEST
@ -592,7 +592,7 @@ def user_login(request):
username = data['username']
password = data['password']
device = data['device']
# 用户认证 - 使用Django的认证系统
user = authenticate(request, username=username, password=password)
if user is None or not user.is_active:
@ -652,7 +652,7 @@ def user_login(request):
"message": "账户已禁用该设备"
}
)
# 判断是否是子账户,如果不是,剩余字符数返回父账户的
if user.parent_id is not None:
parent_user = Users.objects.get(id=user.parent_id)
@ -714,7 +714,7 @@ def user_register(request):
"message": "请求参数缺少或者错误"
}
)
username = data['username']
password = data['password']
# email = data['email']
@ -780,7 +780,7 @@ def user_register(request):
"message": "注册失败,请稍后重试"
}
)
@api_view(['GET'])
@permission_classes([AllowAny])
@renderer_classes([JSONRenderer])
@ -800,4 +800,113 @@ def get_system_config(request):
"message": "success",
"config_value": config_value
}
)
)
# 语言检测(后端统一接口)
from rest_framework.decorators import api_view, permission_classes, renderer_classes
from rest_framework.permissions import AllowAny
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
def _heuristic_detect(text: str):
import re
s = text or ""
if not isinstance(s, str):
s = str(s)
s = s.strip()
if not s:
return {"lang": "und", "prob": 0.0, "reliable": False}
# Unicode 脚本特征
if re.search(r"[\u4e00-\u9fff]", s):
return {"lang": "zh", "prob": 0.99, "reliable": True}
if re.search(r"[\u3040-\u30ff]", s):
return {"lang": "ja", "prob": 0.95, "reliable": True}
if re.search(r"[\uac00-\ud7af]", s):
return {"lang": "ko", "prob": 0.95, "reliable": True}
if re.search(r"[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF]", s):
return {"lang": "ar", "prob": 0.9, "reliable": True}
if re.search(r"[\u0400-\u04FF\u0500-\u052F]", s):
return {"lang": "ru", "prob": 0.9, "reliable": True}
# 英文:含英文字母,且不含其它显著脚本
if re.search(r"[A-Za-z]", s):
if not re.search(r"[^\x00-\x7F]", s):
return {"lang": "en", "prob": 0.85, "reliable": True}
# 允许表情/全角符号等
if not (re.search(r"[\u4e00-\u9fff]", s) or re.search(r"[\u3040-\u30ff]", s) or re.search(r"[\uac00-\ud7af]", s)):
return {"lang": "en", "prob": 0.7, "reliable": False}
# 拉丁扩展粗略判断
if re.search(r"[àâäæçéèêëîïôœùûüÿñ¡¿]", s, flags=re.I):
if re.search(r"ñ|¡|¿|á|é|í|ó|ú", s, flags=re.I):
return {"lang": "es", "prob": 0.6, "reliable": False}
if re.search(r"ç|à|â|ä|é|è|ê|ë|î|ï|ô|œ|ù|û|ü|ÿ", s, flags=re.I):
return {"lang": "fr", "prob": 0.6, "reliable": False}
if re.search(r"ã|õ|á|é|í|ó|ú", s, flags=re.I):
return {"lang": "pt", "prob": 0.6, "reliable": False}
return {"lang": "und", "prob": 0.0, "reliable": False}
@api_view(['GET', 'POST'])
@permission_classes([AllowAny])
@renderer_classes([JSONRenderer])
def detect_language(request):
"""
统一语言检测接口:
- GET: /api/detect_language?text=...
- POST: {"text": "..."} 或 {"texts": ["...","..."]}
返回code=2000, data={ single | list }, message
未知/无法判断时返回 lang=undprob=0.0reliable=false按你的策略前端放行
"""
import json
# 尝试使用可选依赖(未安装则自动回退)
pycld3 = None
langdetect = None
try:
from pycld3 import NNetLanguageIdentifier # type: ignore
pycld3 = NNetLanguageIdentifier(min_num_bytes=0, max_num_bytes=1000)
except Exception:
pycld3 = None
try:
import langdetect as _ld # type: ignore
langdetect = _ld
except Exception:
langdetect = None
def detect_one(text: str):
if pycld3:
try:
r = pycld3.FindLanguage(text or "")
if r and r.language:
lang = r.language if r.language != 'unknown' else 'und'
return {"lang": lang, "prob": float(getattr(r, 'probability', 0.0) or 0.0), "reliable": bool(getattr(r, 'is_reliable', False))}
except Exception:
pass
if langdetect:
try:
from langdetect import detect_langs # type: ignore
cand = detect_langs(text or "")
if cand:
c0 = cand[0]
return {"lang": getattr(c0, 'lang', 'und') or 'und', "prob": float(getattr(c0, 'prob', 0.0) or 0.0), "reliable": float(getattr(c0, 'prob', 0.0) or 0.0) >= 0.85}
except Exception:
pass
return _heuristic_detect(text)
try:
if request.method == 'GET':
text = request.GET.get('text', '')
data = detect_one(text)
return Response({"code": 2000, "message": "success", "data": data})
# POST
body = request.data if hasattr(request, 'data') else {}
text = body.get('text')
texts = body.get('texts')
if text is not None:
data = detect_one(text)
return Response({"code": 2000, "message": "success", "data": data})
if isinstance(texts, list):
out = [detect_one(x) for x in texts]
return Response({"code": 2000, "message": "success", "data": out})
return Response({"code": 401, "message": "参数缺失text 或 texts"})
except Exception as e:
return Response({"code": 5000, "message": f"detect failed: {e}"})

View File

@ -4,3 +4,30 @@ from django.apps import AppConfig
class TranslateConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'translate'
def ready(self):
# 在 migrate 之后自动确保/修复 usePersonalConfig 字段(幂等)
import os
from django.db.models.signals import post_migrate
def _init_after_migrate(sender, **kwargs):
# 允许通过环境变量关闭,默认开启
if os.environ.get('RUN_TRANSLATE_DEFAULTS', '1') != '1':
return
try:
from set_translate_defaults import (
set_default_routes,
ensure_use_personal_config_column,
repair_use_personal_config_values,
)
# 设置默认翻译线路,确保系统配置完整
set_default_routes('youDao')
# 保证列存在并修复历史数据
ensure_use_personal_config_column()
repair_use_personal_config_values()
print('[OK] post_migrate: translate defaults and usePersonalConfig ensured')
except Exception as e:
print('[WARN] post_migrate init failed:', e)
# 仅绑定当前应用,避免重复
post_migrate.connect(_init_after_migrate, sender=self)

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = 'Ensure translate default routes and translate_config.usePersonalConfig (create/repair)'
def handle(self, *args, **options):
from set_translate_defaults import (
set_default_routes,
ensure_use_personal_config_column,
repair_use_personal_config_values,
)
real, hist = set_default_routes('youDao')
self.stdout.write(self.style.SUCCESS(f'Set default routes: {real}, {hist}'))
ensure_use_personal_config_column()
repair_use_personal_config_values()
self.stdout.write(self.style.SUCCESS('Ensured/repair usePersonalConfig complete'))

View File

@ -6,7 +6,7 @@ from .views import TranslationServiceModelViewSet, DeviceModelViewSet, DeviceCod
BillingRecordsModelViewSet
from .views import StatusCodeModelViewSet
from .views import TranslationRecordModelViewSet
from .api import translate_api, check_login, consumption, renewal, user_login, user_register, get_system_config
from .api import translate_api, check_login, consumption, renewal, user_login, user_register, get_system_config, detect_language
from django.urls import path
from dvadmin.system.views.user import create_sub, list_sub, delete_sub, close_global_proxy_password_verification, update_sub, change_password, list_route, list_note, create_note, update_note, delete_note, get_default_translate_route_config
@ -28,6 +28,7 @@ urlpatterns = [
path("api/user_login", user_login, name='user_login'),
path("api/user_register", user_register, name='user_register'),
path("api/get_system_config", get_system_config, name='get_system_config'),
path("api/detect_language", detect_language, name='detect_language'),
# 字符套餐购买
path("api/consumption/", consumption, name='consumption'),
# 设备续期