diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index fb63dbe..d456bda 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,11 +5,22 @@
-
+
+
+
+
+
+
+
+
-
+
+
+
+
+
@@ -41,40 +52,40 @@
- {
+ "keyToString": {
+ "Django 服务器.chunyu_project.executor": "Run",
+ "ModuleVcsDetector.initialDetectionPerformed": "true",
+ "Notification.DisplayName-DoNotAsk-DatabaseConfigFileWatcher.found": "发现新的/更新的数据库配置",
+ "Notification.DoNotAsk-DatabaseConfigFileWatcher.found": "true",
+ "Python.tests.executor": "Run",
+ "RunOnceActivity.MCP Project settings loaded": "true",
+ "RunOnceActivity.OpenDjangoStructureViewOnStart": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
+ "RunOnceActivity.git.unshallow": "true",
+ "RunOnceActivity.typescript.service.memoryLimit.init": "true",
+ "git-widget-placeholder": "main",
+ "last_opened_file_path": "C:/Users/12914/PycharmProjects/chunyu_project/api",
+ "node.js.detected.package.eslint": "true",
+ "node.js.detected.package.tslint": "true",
+ "node.js.selected.package.eslint": "(autodetect)",
+ "node.js.selected.package.tslint": "(autodetect)",
+ "nodejs_package_manager_path": "npm",
+ "settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
+ "vue.rearranger.settings.migration": "true"
},
- "keyToStringList": {
- "DatabaseDriversLRU": [
- "sqlite"
+ "keyToStringList": {
+ "DatabaseDriversLRU": [
+ "sqlite"
]
}
-}]]>
+}
+
-
@@ -134,7 +145,12 @@
1774436691939
-
+
+
+
+
+
+
@@ -160,7 +176,23 @@
1774565193807
-
+
+
+ 1774572888898
+
+
+
+ 1774572888898
+
+
+
+ 1774573186526
+
+
+
+ 1774573186526
+
+
@@ -171,6 +203,6 @@
-
+
\ No newline at end of file
diff --git a/api/4c9da042-07f7-4b23-8e8e-8db67cdad1a8.png b/api/4c9da042-07f7-4b23-8e8e-8db67cdad1a8.png
new file mode 100644
index 0000000..f860599
Binary files /dev/null and b/api/4c9da042-07f7-4b23-8e8e-8db67cdad1a8.png differ
diff --git a/api/__pycache__/__init__.cpython-312.pyc b/api/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..54c9d71
Binary files /dev/null and b/api/__pycache__/__init__.cpython-312.pyc differ
diff --git a/api/__pycache__/admin.cpython-312.pyc b/api/__pycache__/admin.cpython-312.pyc
new file mode 100644
index 0000000..e4b6154
Binary files /dev/null and b/api/__pycache__/admin.cpython-312.pyc differ
diff --git a/api/__pycache__/apps.cpython-312.pyc b/api/__pycache__/apps.cpython-312.pyc
new file mode 100644
index 0000000..716b080
Binary files /dev/null and b/api/__pycache__/apps.cpython-312.pyc differ
diff --git a/api/__pycache__/models.cpython-312.pyc b/api/__pycache__/models.cpython-312.pyc
new file mode 100644
index 0000000..a772236
Binary files /dev/null and b/api/__pycache__/models.cpython-312.pyc differ
diff --git a/api/__pycache__/urls.cpython-312.pyc b/api/__pycache__/urls.cpython-312.pyc
new file mode 100644
index 0000000..a04b3d5
Binary files /dev/null and b/api/__pycache__/urls.cpython-312.pyc differ
diff --git a/api/migrations/__pycache__/__init__.cpython-312.pyc b/api/migrations/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..8d9939e
Binary files /dev/null and b/api/migrations/__pycache__/__init__.cpython-312.pyc differ
diff --git a/api/tests.py b/api/tests.py
index 7ce503c..8f6ea3d 100644
--- a/api/tests.py
+++ b/api/tests.py
@@ -1,3 +1,19 @@
-from django.test import TestCase
+# -*- coding=utf-8
+from qcloud_cos import CosConfig
+from qcloud_cos import CosS3Client
+import sys
+import logging
-# Create your tests here.
+# 正常情况日志级别使用 INFO,需要定位时可以修改为 DEBUG,此时 SDK 会打印和服务端的通信信息
+logging.basicConfig(level=logging.INFO, stream=sys.stdout)
+
+# 1. 设置用户属性, 包括 secret_id, secret_key, region 等。Appid 已在 CosConfig 中移除,请在参数 Bucket 中带上 Appid。Bucket 由 BucketName-Appid 组成
+secret_id = 'SecretId' # 替换为用户的 SecretId,请登录访问管理控制台进行查看和管理,https://console.cloud.tencent.com/cam/capi
+secret_key = 'SecretKey' # 替换为用户的 SecretKey,请登录访问管理控制台进行查看和管理,https://console.cloud.tencent.com/cam/capi
+region = 'ap-beijing' # 替换为用户的 region,已创建桶归属的 region 可以在控制台查看,https://console.cloud.tencent.com/cos5/bucket
+ # COS支持的所有 region 列表参见 https://cloud.tencent.com/document/product/436/6224
+token = None # 如果使用永久密钥不需要填入token,如果使用临时密钥需要填入,临时密钥生成和使用指引参见 https://cloud.tencent.com/document/product/436/14048
+scheme = 'https' # 指定使用 http/https 协议来访问 COS,默认为 https,可不填
+
+config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token, Scheme=scheme)
+client = CosS3Client(config)
\ No newline at end of file
diff --git a/api/urls.py b/api/urls.py
index f45bf9e..02bea08 100644
--- a/api/urls.py
+++ b/api/urls.py
@@ -1,8 +1,28 @@
+
from django.contrib import admin
+
from django.urls import path, include
+
from .views.GetIPDataView import GetIPDataView
-from .views.BaiduFanyiView import BaiduFanyiView
+
+from .views.BaiduFanyiView import BaiduFanyiView, AllLangTypeViews, AutoLangTypeViews, RecognizeLangTypeViews, PictureRecognizeViews, SpeechRecognitionView
+
urlpatterns = [
+
path("getIpData/", GetIPDataView.as_view()),
+
path("baiduFanyi/", BaiduFanyiView.as_view()),
+
+ path("getAllLangType/", AllLangTypeViews.as_view()),
+
+ path("getAutoLangType/", AutoLangTypeViews.as_view()),
+
+ path("recognizeLangType/", RecognizeLangTypeViews.as_view()),
+
+ path("pictureRecognize/", PictureRecognizeViews.as_view()),
+
+ path("speechRecognition/", SpeechRecognitionView.as_view()),
+
+
+
]
\ No newline at end of file
diff --git a/api/views/BaiduFanyiView.py b/api/views/BaiduFanyiView.py
index a56a71c..fc6ce50 100644
--- a/api/views/BaiduFanyiView.py
+++ b/api/views/BaiduFanyiView.py
@@ -1,4 +1,8 @@
+from symtable import Class
+
+from django.core.signing import base64_hmac
from rest_framework.decorators import permission_classes
+from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.permissions import AllowAny
from rest_framework.views import APIView
@@ -13,6 +17,12 @@ import random
import json
from hashlib import md5
+from twisted.scripts.htmlizer import header
+
+from .info.baidu_lang_info import languages, auto_lang
+
+from .info.baidu_fanyi_appid import appid, appkey, endpoint
+
def make_md5(s, encoding='utf-8'):
return md5(s.encode(encoding)).hexdigest()
@@ -20,21 +30,48 @@ def make_md5(s, encoding='utf-8'):
@permission_classes([AllowAny])
class BaiduFanyiView(APIView):
def post(self, request):
+ """
+
+ 请求参数为 q 翻译内容 文本长度限制3000字以下
+ from_lang 原语言
+ to_lang 翻译语言 (支持 auto 为自动识别)
+
+
+ :param request:
+ :return:
+ """
print(request.data)
# Set your own appid/appkey.
- appid = '20220718001275847'
- appkey = 'CJHmvf7qGs3szQy32cMg'
- from_lang = 'en'
- to_lang = 'zh'
- endpoint = 'http://api.fanyi.baidu.com'
+ from_lang = request.data.get('from_lang')
+ to_lang = request.data.get('to_lang')
+
+ if (from_lang not in languages) or (to_lang not in languages) or (to_lang == "auto"):
+ return Response(
+ {
+ "message": "翻译语种异常"
+ },
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ if from_lang == to_lang:
+ return Response(
+ {
+ "message": "翻译数据异常"
+ }
+ )
+
path = '/api/trans/vip/translate'
url = endpoint + path
query = request.data['q']
+ if len(query) > 3000:
+ return Response({"请求数据长度过长已经超过3000."}, status=status.HTTP_400_BAD_REQUEST)
+
+
salt = random.randint(32768, 65536)
sign = make_md5(appid + query + str(salt) + appkey)
@@ -44,5 +81,169 @@ class BaiduFanyiView(APIView):
r = requests.post(url, params=payload, headers=headers)
result = r.json()
+ return Response(result, status=status.HTTP_200_OK)
+@permission_classes([AllowAny])
+class AutoLangTypeViews(APIView): # 获取可用自动识别的所有语言类型
+ def get(self, request):
+ return Response(auto_lang, status=status.HTTP_200_OK)
+
+@permission_classes([AllowAny])
+class AllLangTypeViews(APIView): # 获取可用自动识别的所有语言类型
+ def get(self, request):
+ return Response(languages, status=status.HTTP_200_OK)
+
+
+@permission_classes([AllowAny])
+class RecognizeLangTypeViews(APIView): # 文本语种识别
+ """
+
+ 请求参数为 q 翻译内容 文本长度限制3000字以下
+
+ :param request:
+ :return:
+ """
+ def post(self, request):
+
+ path = '/api/trans/vip/language'
+ url = endpoint + path
+
+ query = request.data.get("q")
+
+ if len(query) > 3000:
+ return Response({"请求数据长度过长已经超过3000."}, status=status.HTTP_400_BAD_REQUEST)
+
+ salt = random.randint(32768, 65536)
+ sign = make_md5(appid + query + str(salt) + appkey)
+
+ headers = {'Content-Type': 'application/x-www-form-urlencoded'}
+ payload = {'appid': appid, 'q': query, 'salt': salt, 'sign': sign}
+
+ r = requests.post(url, params=payload, headers=headers)
+ result = r.json()
+
+ code = result.get('error_code')
+
+ if code == 0:
+ return Response(result, status=status.HTTP_200_OK)
+ elif code == 54009:
+ return Response({
+ "message": "不在课识别语种范围."
+ }, status=status.HTTP_400_BAD_REQUEST)
+ else :
+ return Response({
+ "message": "其他问题请联系管理员."
+ }, status=status.HTTP_400_BAD_REQUEST)
+
+import requests
+import random
+from hashlib import md5
+
+from .info.baidu_fanyi_appid import appid as app_id, appkey as app_key
+
+from .info.baidu_lang_info import cuid, mac, p_code, p_lang_type
+
+@permission_classes([AllowAny])
+class PictureRecognizeViews(APIView): # 图片翻译
+
+ parser_classes = [MultiPartParser, FormParser]
+
+ def post(self, request):
+
+ file_data = request.FILES['file'].read()
+
+ path = '/api/trans/sdk/picture'
+ url = endpoint + path
+
+ from_lang = request.GET.get("from_lang")
+ to_lang = request.GET.get("to_lang")
+ picture_type = request.GET.get("picture")
+
+ if (from_lang == to_lang) or (to_lang == "auto"):
+ return Response({"message": "翻译数据错误."}, status=status.HTTP_400_BAD_REQUEST)
+ if (from_lang not in p_lang_type.keys()) or (to_lang not in p_lang_type.keys()) :
+ return Response({"message": "翻译语种异常."}, status=status.HTTP_400_BAD_REQUEST)
+
+ def get_md5(string, encoding='utf-8'):
+ return md5(string.encode(encoding)).hexdigest()
+
+ salt = random.randint(32768, 65536)
+ sign = get_md5(app_id + md5(file_data).hexdigest() + str(salt) + cuid + mac + app_key)
+
+ payload = {'from': from_lang, 'to': to_lang, 'appid': app_id, 'salt': salt, 'sign': sign, 'cuid': cuid,
+ 'mac': mac}
+ image = {'image': (f"p_image.{ picture_type }", file_data, "multipart/form-data")}
+
+ response = requests.post(url, params=payload, files=image)
+ result = response.json()
+
+ if result['error_code'] == 0:
+ return Response(result, status=status.HTTP_200_OK)
+ else:
+ result = p_code[result['error_code']]
+ return Response(result, status=status.HTTP_400_BAD_REQUEST)
+
+from .info.baidu_lang_info import y_speech_type, pcm_y_lang_type, y_lang_type
+
+import time
+
+import hmac
+import hashlib
+import base64
+
+
+@permission_classes([AllowAny])
+class SpeechRecognitionView(APIView):
+
+ parser_classes = [MultiPartParser, FormParser]
+
+ def post(self, request):
+ speech_type = request.GET.get("speech_type")
+
+ if speech_type not in y_speech_type:
+ return Response({"message": "不支持的语音类型."}, status=status.HTTP_400_BAD_REQUEST)
+
+ from_lang = request.GET.get('from_lang')
+ to_lang = request.GET.get('to_lang')
+
+ if (from_lang == to_lang) or (to_lang == "auto"):
+ return Response({"message": "翻译数据错误."}, status=status.HTTP_400_BAD_REQUEST)
+
+
+ if (from_lang not in (pcm_y_lang_type.keys() if speech_type == "pcm" else y_lang_type.keys())) or (to_lang not in (pcm_y_lang_type.keys() if speech_type == "pcm" else y_lang_type.keys())):
+ return Response({"message": "翻译语种异常."}, status=status.HTTP_400_BAD_REQUEST)
+
+ voice = request.FILES.get("voice").read()
+
+ base64_hmac_voice = base64.b64encode(voice).decode('utf-8')
+
+ timestamp = str(int(time.time()))
+
+ path = "/api/trans/v2/voicetrans"
+
+ url = endpoint + path
+
+ msg = appid + timestamp + base64_hmac_voice
+ sign = base64.b64encode(hmac.new(app_key.encode("utf-8"), msg.encode("utf-8"), digestmod=hashlib.sha256).digest())
+
+ print(sign)
+
+ headers = {
+ "Content-Type": "application/json",
+ "X-Appid": app_id,
+ "X-Timestamp": timestamp,
+ "X-Sign": sign,
+ "X-EncryptType": "hmac256"
+ }
+
+ data = {
+ "format": speech_type,
+ "voice": base64_hmac_voice,
+ "to": to_lang,
+ "from": from_lang,
+ }
+
+ response = requests.post(url, headers = headers,data = data)
+
+ result = response.json()
return Response(result, status=status.HTTP_200_OK)
\ No newline at end of file
diff --git a/api/views/__pycache__/BaiduFanyiView.cpython-312.pyc b/api/views/__pycache__/BaiduFanyiView.cpython-312.pyc
new file mode 100644
index 0000000..cbdc3a7
Binary files /dev/null and b/api/views/__pycache__/BaiduFanyiView.cpython-312.pyc differ
diff --git a/api/views/__pycache__/GetIPDataView.cpython-312.pyc b/api/views/__pycache__/GetIPDataView.cpython-312.pyc
new file mode 100644
index 0000000..b07b3bd
Binary files /dev/null and b/api/views/__pycache__/GetIPDataView.cpython-312.pyc differ
diff --git a/api/views/__pycache__/__init__.cpython-312.pyc b/api/views/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..2d019e3
Binary files /dev/null and b/api/views/__pycache__/__init__.cpython-312.pyc differ
diff --git a/api/views/info/__init__.py b/api/views/info/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/api/views/info/__pycache__/__init__.cpython-312.pyc b/api/views/info/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..1b34ed6
Binary files /dev/null and b/api/views/info/__pycache__/__init__.cpython-312.pyc differ
diff --git a/api/views/info/__pycache__/baidu_fanyi_appid.cpython-312.pyc b/api/views/info/__pycache__/baidu_fanyi_appid.cpython-312.pyc
new file mode 100644
index 0000000..fac5ad0
Binary files /dev/null and b/api/views/info/__pycache__/baidu_fanyi_appid.cpython-312.pyc differ
diff --git a/api/views/info/__pycache__/baidu_lang_info.cpython-312.pyc b/api/views/info/__pycache__/baidu_lang_info.cpython-312.pyc
new file mode 100644
index 0000000..0858e30
Binary files /dev/null and b/api/views/info/__pycache__/baidu_lang_info.cpython-312.pyc differ
diff --git a/api/views/info/baidu_fanyi_appid.py b/api/views/info/baidu_fanyi_appid.py
new file mode 100644
index 0000000..f0cb899
--- /dev/null
+++ b/api/views/info/baidu_fanyi_appid.py
@@ -0,0 +1,4 @@
+appid = '20220718001275847'
+appkey = 'CJHmvf7qGs3szQy32cMg'
+
+endpoint = 'http://api.fanyi.baidu.com'
\ No newline at end of file
diff --git a/api/views/info/baidu_lang_info.py b/api/views/info/baidu_lang_info.py
new file mode 100644
index 0000000..695fe83
--- /dev/null
+++ b/api/views/info/baidu_lang_info.py
@@ -0,0 +1,1184 @@
+# 图片识别支持类型
+cuid = 'APICUID'
+mac = 'mac'
+
+p_lang_type = {
+ "zh": "中文",
+ "en": "英语",
+ "jp": "日语",
+ "kor": "韩语",
+ "fra": "法语",
+ "spa": "西班牙语",
+ "ru": "俄语",
+ "pt": "葡萄牙语",
+ "de": "德语",
+ "it": "意大利语",
+ "dan": "丹麦语",
+ "nl": "荷兰语",
+ "may": "马来语",
+ "swe": "瑞典语",
+ "id": "印尼语",
+ "pl": "波兰语",
+ "rom": "罗马尼亚语",
+ "tr": "土耳其语",
+ "el": "希腊语",
+ "hu": "匈牙利语",
+ "auto": "自动识别"
+}
+
+
+
+p_code = {
+ "0": {"code": "0", "message": "成功"},
+ "52001": {"code": "52001", "message": "请求超时"},
+ "52002": {"code": "52002", "message": "服务端系统错误"},
+ "52003": {"code": "52003", "message": "未授权用户"},
+ "52010": {"code": "52010", "message": "开放设备授权容量不足"},
+ "54000": {"code": "54000", "message": "必填参数为空或固定参数有误"},
+ "54001": {"code": "54001", "message": "签名错误"},
+ "54003": {"code": "54003", "message": "访问频率受限"},
+ "54004": {"code": "54004", "message": "账户余额不足"},
+ "54005": {"code": "54005", "message": "长query请求频繁"},
+ "58000": {"code": "58000", "message": "客户端IP非法"},
+ "58001": {"code": "58001", "message": "译文语言方向不支持"},
+ "69001": {"code": "69001", "message": "上传图片数据有误"},
+ "69002": {"code": "69002", "message": "图片识别超时"},
+ "69003": {"code": "69003", "message": "内容识别失败"},
+ "69004": {"code": "69004", "message": "识别内容为空"},
+ "69005": {"code": "69005", "message": "图片大小超限(超过4M)"},
+ "69006": {"code": "69006", "message": "图片尺寸不符合标准(最短边至少30px,最长边最大4096px)"},
+ "69007": {"code": "69007", "message": "图片格式不支持(png/jpg)"},
+ "69008": {"code": "69008", "message": "设备号为空"},
+ "69012": {"code": "69012", "message": "文字贴合参数异常"}
+}
+
+# 语音识别翻译
+y_lang_type = {'zh': '中文', 'en': '英语', 'jp': '日语', 'kor': '韩语', 'yue': '粤语', 'ru': '俄语', 'de': '德语', 'fra': '法语', 'th': '泰语', 'pt': '葡萄牙语', 'spa': '西班牙语', 'ara': '阿拉伯语(巴林)'}
+
+pcm_y_lang_type = {'zh': '中文', 'yue': '粤语'}
+
+y_speech_type = ["pcm", "wav", "amr", "m4a"] # 最多支持的语音类型
+
+
+# 通用文本翻译
+languages = {'ara': {'name': '阿拉伯语', 'code': 'ara', 'detect': True}, 'gle': {'name': '爱尔兰语', 'code': 'gle', 'detect': True}, 'oci': {'name': '奥克语', 'code': 'oci', 'detect': True}, 'alb': {'name': '阿尔巴尼亚语', 'code': 'alb', 'detect': True}, 'arq': {'name': '阿尔及利亚阿拉伯语', 'code': 'arq', 'detect': False}, 'aka': {'name': '阿肯语', 'code': 'aka', 'detect': False}, 'arg': {'name': '阿拉贡语', 'code': 'arg', 'detect': False}, 'amh': {'name': '阿姆哈拉语', 'code': 'amh', 'detect': True}, 'asm': {'name': '阿萨姆语', 'code': 'asm', 'detect': True}, 'aym': {'name': '艾马拉语', 'code': 'aym', 'detect': False}, 'aze': {'name': '阿塞拜疆语', 'code': 'aze', 'detect': True}, 'ast': {'name': '阿斯图里亚斯语', 'code': 'ast', 'detect': True}, 'oss': {'name': '奥塞梯语', 'code': 'oss', 'detect': False}, 'est': {'name': '爱沙尼亚语', 'code': 'est', 'detect': True}, 'oji': {'name': '奥杰布瓦语', 'code': 'oji', 'detect': False}, 'ori': {'name': '奥里亚语', 'code': 'ori', 'detect': True}, 'orm': {'name': '奥罗莫语', 'code': 'orm', 'detect': False}, 'pl': {'name': '波兰语', 'code': 'pl', 'detect': True}, 'per': {'name': '波斯语', 'code': 'per', 'detect': True}, 'bre': {'name': '布列塔尼语', 'code': 'bre', 'detect': True}, 'bak': {'name': '巴什基尔语', 'code': 'bak', 'detect': False}, 'baq': {'name': '巴斯克语', 'code': 'baq', 'detect': True}, 'pot': {'name': '巴西葡萄牙语', 'code': 'pot', 'detect': False}, 'bel': {'name': '白俄罗斯语', 'code': 'bel', 'detect': True}, 'ber': {'name': '柏柏尔语', 'code': 'ber', 'detect': True}, 'pam': {'name': '邦板牙语', 'code': 'pam', 'detect': False}, 'bul': {'name': '保加利亚语', 'code': 'bul', 'detect': True}, 'sme': {'name': '北方萨米语', 'code': 'sme', 'detect': False}, 'ped': {'name': '北索托语', 'code': 'ped', 'detect': False}, 'bem': {'name': '本巴语', 'code': 'bem', 'detect': False}, 'bli': {'name': '比林语', 'code': 'bli', 'detect': False}, 'bis': {'name': '比斯拉马语', 'code': 'bis', 'detect': False}, 'bal': {'name': '俾路支语', 'code': 'bal', 'detect': False}, 'ice': {'name': '冰岛语', 'code': 'ice', 'detect': True}, 'bos': {'name': '波斯尼亚语', 'code': 'bos', 'detect': True}, 'bho': {'name': '博杰普尔语', 'code': 'bho', 'detect': False}, 'chv': {'name': '楚瓦什语', 'code': 'chv', 'detect': False}, 'tso': {'name': '聪加语', 'code': 'tso', 'detect': False}, 'dan': {'name': '丹麦语', 'code': 'dan', 'detect': True}, 'de': {'name': '德语', 'code': 'de', 'detect': True}, 'tat': {'name': '鞑靼语', 'code': 'tat', 'detect': True}, 'sha': {'name': '掸语', 'code': 'sha', 'detect': False}, 'tet': {'name': '德顿语', 'code': 'tet', 'detect': False}, 'div': {'name': '迪维希语', 'code': 'div', 'detect': False}, 'log': {'name': '低地德语', 'code': 'log', 'detect': True}, 'ru': {'name': '俄语', 'code': 'ru', 'detect': True}, 'fra': {'name': '法语', 'code': 'fra', 'detect': True}, 'fil': {'name': '菲律宾语', 'code': 'fil', 'detect': True}, 'fin': {'name': '芬兰语', 'code': 'fin', 'detect': True}, 'san': {'name': '梵语', 'code': 'san', 'detect': False}, 'fri': {'name': '弗留利语', 'code': 'fri', 'detect': False}, 'ful': {'name': '富拉尼语', 'code': 'ful', 'detect': False}, 'fao': {'name': '法罗语', 'code': 'fao', 'detect': False}, 'gla': {'name': '盖尔语', 'code': 'gla', 'detect': False}, 'kon': {'name': '刚果语', 'code': 'kon', 'detect': False}, 'ups': {'name': '高地索布语', 'code': 'ups', 'detect': False}, 'hkm': {'name': '高棉语', 'code': 'hkm', 'detect': True}, 'kal': {'name': '格陵兰语', 'code': 'kal', 'detect': False}, 'geo': {'name': '格鲁吉亚语', 'code': 'geo', 'detect': True}, 'guj': {'name': '古吉拉特语', 'code': 'guj', 'detect': True}, 'gra': {'name': '古希腊语', 'code': 'gra', 'detect': False}, 'eno': {'name': '古英语', 'code': 'eno', 'detect': False}, 'grn': {'name': '瓜拉尼语', 'code': 'grn', 'detect': False}, 'kor': {'name': '韩语', 'code': 'kor', 'detect': True}, 'nl': {'name': '荷兰语', 'code': 'nl', 'detect': True}, 'hup': {'name': '胡帕语', 'code': 'hup', 'detect': False}, 'hak': {'name': '哈卡钦语', 'code': 'hak', 'detect': False}, 'ht': {'name': '海地语', 'code': 'ht', 'detect': False}, 'mot': {'name': '黑山语', 'code': 'mot', 'detect': False}, 'hau': {'name': '豪萨语', 'code': 'hau', 'detect': False}, 'kir': {'name': '吉尔吉斯语', 'code': 'kir', 'detect': False}, 'glg': {'name': '加利西亚语', 'code': 'glg', 'detect': True}, 'frn': {'name': '加拿大法语', 'code': 'frn', 'detect': False}, 'cat': {'name': '加泰罗尼亚语', 'code': 'cat', 'detect': True}, 'cs': {'name': '捷克语', 'code': 'cs', 'detect': True}, 'kab': {'name': '卡拜尔语', 'code': 'kab', 'detect': True}, 'kan': {'name': '卡纳达语', 'code': 'kan', 'detect': True}, 'kau': {'name': '卡努里语', 'code': 'kau', 'detect': False}, 'kah': {'name': '卡舒比语', 'code': 'kah', 'detect': False}, 'cor': {'name': '康瓦尔语', 'code': 'cor', 'detect': False}, 'xho': {'name': '科萨语', 'code': 'xho', 'detect': True}, 'cos': {'name': '科西嘉语', 'code': 'cos', 'detect': False}, 'cre': {'name': '克里克语', 'code': 'cre', 'detect': False}, 'cri': {'name': '克里米亚鞑靼语', 'code': 'cri', 'detect': False}, 'kli': {'name': '克林贡语', 'code': 'kli', 'detect': False}, 'hrv': {'name': '克罗地亚语', 'code': 'hrv', 'detect': True}, 'que': {'name': '克丘亚语', 'code': 'que', 'detect': False}, 'kas': {'name': '克什米尔语', 'code': 'kas', 'detect': False}, 'kok': {'name': '孔卡尼语', 'code': 'kok', 'detect': False}, 'kur': {'name': '库尔德语', 'code': 'kur', 'detect': True}, 'lat': {'name': '拉丁语', 'code': 'lat', 'detect': True}, 'lao': {'name': '老挝语', 'code': 'lao', 'detect': False}, 'rom': {'name': '罗马尼亚语', 'code': 'rom', 'detect': True}, 'lag': {'name': '拉特加莱语', 'code': 'lag', 'detect': False}, 'lav': {'name': '拉脱维亚语', 'code': 'lav', 'detect': True}, 'lim': {'name': '林堡语', 'code': 'lim', 'detect': False}, 'lin': {'name': '林加拉语', 'code': 'lin', 'detect': False}, 'lug': {'name': '卢干达语', 'code': 'lug', 'detect': False}, 'ltz': {'name': '卢森堡语', 'code': 'ltz', 'detect': False}, 'ruy': {'name': '卢森尼亚语', 'code': 'ruy', 'detect': False}, 'kin': {'name': '卢旺达语', 'code': 'kin', 'detect': True}, 'lit': {'name': '立陶宛语', 'code': 'lit', 'detect': True}, 'roh': {'name': '罗曼什语', 'code': 'roh', 'detect': False}, 'ro': {'name': '罗姆语', 'code': 'ro', 'detect': False}, 'loj': {'name': '逻辑语', 'code': 'loj', 'detect': False}, 'may': {'name': '马来语', 'code': 'may', 'detect': True}, 'bur': {'name': '缅甸语', 'code': 'bur', 'detect': True}, 'mar': {'name': '马拉地语', 'code': 'mar', 'detect': False}, 'mg': {'name': '马拉加斯语', 'code': 'mg', 'detect': True}, 'mal': {'name': '马拉雅拉姆语', 'code': 'mal', 'detect': True}, 'mac': {'name': '马其顿语', 'code': 'mac', 'detect': True}, 'mah': {'name': '马绍尔语', 'code': 'mah', 'detect': False}, 'mai': {'name': '迈蒂利语', 'code': 'mai', 'detect': True}, 'glv': {'name': '曼克斯语', 'code': 'glv', 'detect': False}, 'mau': {'name': '毛里求斯克里奥尔语', 'code': 'mau', 'detect': False}, 'mao': {'name': '毛利语', 'code': 'mao', 'detect': False}, 'ben': {'name': '孟加拉语', 'code': 'ben', 'detect': True}, 'mlt': {'name': '马耳他语', 'code': 'mlt', 'detect': True}, 'hmn': {'name': '苗语', 'code': 'hmn', 'detect': False}, 'nor': {'name': '挪威语', 'code': 'nor', 'detect': True}, 'nea': {'name': '那不勒斯语', 'code': 'nea', 'detect': False}, 'nbl': {'name': '南恩德贝莱语', 'code': 'nbl', 'detect': False}, 'afr': {'name': '南非荷兰语', 'code': 'afr', 'detect': True}, 'sot': {'name': '南索托语', 'code': 'sot', 'detect': False}, 'nep': {'name': '尼泊尔语', 'code': 'nep', 'detect': True}, 'pt': {'name': '葡萄牙语', 'code': 'pt', 'detect': True}, 'pan': {'name': '旁遮普语', 'code': 'pan', 'detect': True}, 'pap': {'name': '帕皮阿门托语', 'code': 'pap', 'detect': False}, 'pus': {'name': '普什图语', 'code': 'pus', 'detect': False}, 'nya': {'name': '齐切瓦语', 'code': 'nya', 'detect': False}, 'twi': {'name': '契维语', 'code': 'twi', 'detect': False}, 'chr': {'name': '切罗基语', 'code': 'chr', 'detect': False}, 'jp': {'name': '日语', 'code': 'jp', 'detect': True}, 'swe': {'name': '瑞典语', 'code': 'swe', 'detect': True}, 'srd': {'name': '萨丁尼亚语', 'code': 'srd', 'detect': False}, 'sm': {'name': '萨摩亚语', 'code': 'sm', 'detect': False}, 'sec': {'name': '塞尔维亚-克罗地亚语', 'code': 'sec', 'detect': False}, 'srp': {'name': '塞尔维亚语', 'code': 'srp', 'detect': True}, 'sol': {'name': '桑海语', 'code': 'sol', 'detect': False}, 'sin': {'name': '僧伽罗语', 'code': 'sin', 'detect': True}, 'epo': {'name': '世界语', 'code': 'epo', 'detect': True}, 'nob': {'name': '书面挪威语', 'code': 'nob', 'detect': True}, 'sk': {'name': '斯洛伐克语', 'code': 'sk', 'detect': True}, 'slo': {'name': '斯洛文尼亚语', 'code': 'slo', 'detect': True}, 'swa': {'name': '斯瓦希里语', 'code': 'swa', 'detect': True}, 'src': {'name': '塞尔维亚语(西里尔)', 'code': 'src', 'detect': False}, 'som': {'name': '索马里语', 'code': 'som', 'detect': True}, 'sco': {'name': '苏格兰语', 'code': 'sco', 'detect': False}, 'th': {'name': '泰语', 'code': 'th', 'detect': True}, 'tr': {'name': '土耳其语', 'code': 'tr', 'detect': True}, 'tgk': {'name': '塔吉克语', 'code': 'tgk', 'detect': True}, 'tam': {'name': '泰米尔语', 'code': 'tam', 'detect': True}, 'tgl': {'name': '他加禄语', 'code': 'tgl', 'detect': True}, 'tir': {'name': '提格利尼亚语', 'code': 'tir', 'detect': False}, 'tel': {'name': '泰卢固语', 'code': 'tel', 'detect': True}, 'tua': {'name': '突尼斯阿拉伯语', 'code': 'tua', 'detect': False}, 'tuk': {'name': '土库曼语', 'code': 'tuk', 'detect': False}, 'ukr': {'name': '乌克兰语', 'code': 'ukr', 'detect': True}, 'wln': {'name': '瓦隆语', 'code': 'wln', 'detect': True}, 'wel': {'name': '威尔士语', 'code': 'wel', 'detect': True}, 'ven': {'name': '文达语', 'code': 'ven', 'detect': False}, 'wol': {'name': '沃洛夫语', 'code': 'wol', 'detect': False}, 'urd': {'name': '乌尔都语', 'code': 'urd', 'detect': True}, 'spa': {'name': '西班牙语', 'code': 'spa', 'detect': True}, 'heb': {'name': '希伯来语', 'code': 'heb', 'detect': True}, 'el': {'name': '希腊语', 'code': 'el', 'detect': True}, 'hu': {'name': '匈牙利语', 'code': 'hu', 'detect': True}, 'fry': {'name': '西弗里斯语', 'code': 'fry', 'detect': True}, 'sil': {'name': '西里西亚语', 'code': 'sil', 'detect': False}, 'hil': {'name': '希利盖农语', 'code': 'hil', 'detect': False}, 'los': {'name': '下索布语', 'code': 'los', 'detect': False}, 'haw': {'name': '夏威夷语', 'code': 'haw', 'detect': False}, 'nno': {'name': '新挪威语', 'code': 'nno', 'detect': True}, 'nqo': {'name': '西非书面语', 'code': 'nqo', 'detect': False}, 'snd': {'name': '信德语', 'code': 'snd', 'detect': False}, 'sna': {'name': '修纳语', 'code': 'sna', 'detect': False}, 'ceb': {'name': '宿务语', 'code': 'ceb', 'detect': False}, 'syr': {'name': '叙利亚语', 'code': 'syr', 'detect': False}, 'sun': {'name': '巽他语', 'code': 'sun', 'detect': False}, 'en': {'name': '英语', 'code': 'en', 'detect': True}, 'hi': {'name': '印地语', 'code': 'hi', 'detect': True}, 'id': {'name': '印尼语', 'code': 'id', 'detect': True}, 'it': {'name': '意大利语', 'code': 'it', 'detect': True}, 'vie': {'name': '越南语', 'code': 'vie', 'detect': True}, 'yid': {'name': '意第绪语', 'code': 'yid', 'detect': False}, 'ina': {'name': '因特语', 'code': 'ina', 'detect': False}, 'ach': {'name': '亚齐语', 'code': 'ach', 'detect': False}, 'ing': {'name': '印古什语', 'code': 'ing', 'detect': False}, 'ibo': {'name': '伊博语', 'code': 'ibo', 'detect': False}, 'ido': {'name': '伊多语', 'code': 'ido', 'detect': False}, 'yor': {'name': '约鲁巴语', 'code': 'yor', 'detect': False}, 'arm': {'name': '亚美尼亚语', 'code': 'arm', 'detect': True}, 'iku': {'name': '伊努克提图特语', 'code': 'iku', 'detect': False}, 'zh': {'name': '中文(简体)', 'code': 'zh', 'detect': True}, 'cht': {'name': '中文(繁体)', 'code': 'cht', 'detect': True}, 'wyw': {'name': '中文(文言文)', 'code': 'wyw', 'detect': True}, 'yue': {'name': '中文(粤语)', 'code': 'yue', 'detect': True}, 'zaz': {'name': '扎扎其语', 'code': 'zaz', 'detect': False}, 'frm': {'name': '中古法语', 'code': 'frm', 'detect': False}, 'zul': {'name': '祖鲁语', 'code': 'zul', 'detect': False}, 'jav': {'name': '爪哇语', 'code': 'jav', 'detect': False}, 'auto': {'name': '自动检测', 'code': 'auto', 'detect': True}}
+auto_lang = {'ara': {'name': '阿拉伯语', 'code': 'ara', 'detect': True}, 'gle': {'name': '爱尔兰语', 'code': 'gle', 'detect': True}, 'oci': {'name': '奥克语', 'code': 'oci', 'detect': True}, 'alb': {'name': '阿尔巴尼亚语', 'code': 'alb', 'detect': True}, 'amh': {'name': '阿姆哈拉语', 'code': 'amh', 'detect': True}, 'asm': {'name': '阿萨姆语', 'code': 'asm', 'detect': True}, 'aze': {'name': '阿塞拜疆语', 'code': 'aze', 'detect': True}, 'ast': {'name': '阿斯图里亚斯语', 'code': 'ast', 'detect': True}, 'est': {'name': '爱沙尼亚语', 'code': 'est', 'detect': True}, 'ori': {'name': '奥里亚语', 'code': 'ori', 'detect': True}, 'pl': {'name': '波兰语', 'code': 'pl', 'detect': True}, 'per': {'name': '波斯语', 'code': 'per', 'detect': True}, 'bre': {'name': '布列塔尼语', 'code': 'bre', 'detect': True}, 'baq': {'name': '巴斯克语', 'code': 'baq', 'detect': True}, 'bel': {'name': '白俄罗斯语', 'code': 'bel', 'detect': True}, 'ber': {'name': '柏柏尔语', 'code': 'ber', 'detect': True}, 'bul': {'name': '保加利亚语', 'code': 'bul', 'detect': True}, 'ice': {'name': '冰岛语', 'code': 'ice', 'detect': True}, 'bos': {'name': '波斯尼亚语', 'code': 'bos', 'detect': True}, 'dan': {'name': '丹麦语', 'code': 'dan', 'detect': True}, 'de': {'name': '德语', 'code': 'de', 'detect': True}, 'tat': {'name': '鞑靼语', 'code': 'tat', 'detect': True}, 'log': {'name': '低地德语', 'code': 'log', 'detect': True}, 'ru': {'name': '俄语', 'code': 'ru', 'detect': True}, 'fra': {'name': '法语', 'code': 'fra', 'detect': True}, 'fil': {'name': '菲律宾语', 'code': 'fil', 'detect': True}, 'fin': {'name': '芬兰语', 'code': 'fin', 'detect': True}, 'hkm': {'name': '高棉语', 'code': 'hkm', 'detect': True}, 'geo': {'name': '格鲁吉亚语', 'code': 'geo', 'detect': True}, 'guj': {'name': '古吉拉特语', 'code': 'guj', 'detect': True}, 'kor': {'name': '韩语', 'code': 'kor', 'detect': True}, 'nl': {'name': '荷兰语', 'code': 'nl', 'detect': True}, 'glg': {'name': '加利西亚语', 'code': 'glg', 'detect': True}, 'cat': {'name': '加泰罗尼亚语', 'code': 'cat', 'detect': True}, 'cs': {'name': '捷克语', 'code': 'cs', 'detect': True}, 'kab': {'name': '卡拜尔语', 'code': 'kab', 'detect': True}, 'kan': {'name': '卡纳达语', 'code': 'kan', 'detect': True}, 'xho': {'name': '科萨语', 'code': 'xho', 'detect': True}, 'hrv': {'name': '克罗地亚语', 'code': 'hrv', 'detect': True}, 'kur': {'name': '库尔德语', 'code': 'kur', 'detect': True}, 'lat': {'name': '拉丁语', 'code': 'lat', 'detect': True}, 'rom': {'name': '罗马尼亚语', 'code': 'rom', 'detect': True}, 'lav': {'name': '拉脱维亚语', 'code': 'lav', 'detect': True}, 'kin': {'name': '卢旺达语', 'code': 'kin', 'detect': True}, 'lit': {'name': '立陶宛语', 'code': 'lit', 'detect': True}, 'may': {'name': '马来语', 'code': 'may', 'detect': True}, 'bur': {'name': '缅甸语', 'code': 'bur', 'detect': True}, 'mg': {'name': '马拉加斯语', 'code': 'mg', 'detect': True}, 'mal': {'name': '马拉雅拉姆语', 'code': 'mal', 'detect': True}, 'mac': {'name': '马其顿语', 'code': 'mac', 'detect': True}, 'mai': {'name': '迈蒂利语', 'code': 'mai', 'detect': True}, 'ben': {'name': '孟加拉语', 'code': 'ben', 'detect': True}, 'mlt': {'name': '马耳他语', 'code': 'mlt', 'detect': True}, 'nor': {'name': '挪威语', 'code': 'nor', 'detect': True}, 'afr': {'name': '南非荷兰语', 'code': 'afr', 'detect': True}, 'nep': {'name': '尼泊尔语', 'code': 'nep', 'detect': True}, 'pt': {'name': '葡萄牙语', 'code': 'pt', 'detect': True}, 'pan': {'name': '旁遮普语', 'code': 'pan', 'detect': True}, 'jp': {'name': '日语', 'code': 'jp', 'detect': True}, 'swe': {'name': '瑞典语', 'code': 'swe', 'detect': True}, 'srp': {'name': '塞尔维亚语', 'code': 'srp', 'detect': True}, 'sin': {'name': '僧伽罗语', 'code': 'sin', 'detect': True}, 'epo': {'name': '世界语', 'code': 'epo', 'detect': True}, 'nob': {'name': '书面挪威语', 'code': 'nob', 'detect': True}, 'sk': {'name': '斯洛伐克语', 'code': 'sk', 'detect': True}, 'slo': {'name': '斯洛文尼亚语', 'code': 'slo', 'detect': True}, 'swa': {'name': '斯瓦希里语', 'code': 'swa', 'detect': True}, 'som': {'name': '索马里语', 'code': 'som', 'detect': True}, 'th': {'name': '泰语', 'code': 'th', 'detect': True}, 'tr': {'name': '土耳其语', 'code': 'tr', 'detect': True}, 'tgk': {'name': '塔吉克语', 'code': 'tgk', 'detect': True}, 'tam': {'name': '泰米尔语', 'code': 'tam', 'detect': True}, 'tgl': {'name': '他加禄语', 'code': 'tgl', 'detect': True}, 'tel': {'name': '泰卢固语', 'code': 'tel', 'detect': True}, 'ukr': {'name': '乌克兰语', 'code': 'ukr', 'detect': True}, 'wln': {'name': '瓦隆语', 'code': 'wln', 'detect': True}, 'wel': {'name': '威尔士语', 'code': 'wel', 'detect': True}, 'urd': {'name': '乌尔都语', 'code': 'urd', 'detect': True}, 'spa': {'name': '西班牙语', 'code': 'spa', 'detect': True}, 'heb': {'name': '希伯来语', 'code': 'heb', 'detect': True}, 'el': {'name': '希腊语', 'code': 'el', 'detect': True}, 'hu': {'name': '匈牙利语', 'code': 'hu', 'detect': True}, 'fry': {'name': '西弗里斯语', 'code': 'fry', 'detect': True}, 'nno': {'name': '新挪威语', 'code': 'nno', 'detect': True}, 'en': {'name': '英语', 'code': 'en', 'detect': True}, 'hi': {'name': '印地语', 'code': 'hi', 'detect': True}, 'id': {'name': '印尼语', 'code': 'id', 'detect': True}, 'it': {'name': '意大利语', 'code': 'it', 'detect': True}, 'vie': {'name': '越南语', 'code': 'vie', 'detect': True}, 'arm': {'name': '亚美尼亚语', 'code': 'arm', 'detect': True}, 'zh': {'name': '中文(简体)', 'code': 'zh', 'detect': True}, 'cht': {'name': '中文(繁体)', 'code': 'cht', 'detect': True}, 'wyw': {'name': '中文(文言文)', 'code': 'wyw', 'detect': True}, 'yue': {'name': '中文(粤语)', 'code': 'yue', 'detect': True}}
+
+total_languages = 153
+supported_detect = 91
+unsupported_detect = 62
+
+lang = { # 通用文本翻译
+"statistics": {
+ "total_languages": 153,
+ "supported_detect": 91,
+ "unsupported_detect": 62
+},
+"languages": [
+ {
+ "name": "阿拉伯语",
+ "code": "ara",
+ "detect": True
+
+ },
+ {
+ "name": "爱尔兰语",
+ "code": "gle",
+ "detect": True
+
+ },
+ {
+ "name": "奥克语",
+ "code": "oci",
+ "detect": True
+
+ },
+ {
+ "name": "阿尔巴尼亚语",
+ "code": "alb",
+ "detect": True
+
+ },
+ {
+ "name": "阿尔及利亚阿拉伯语",
+ "code": "arq",
+ "detect": False
+ },
+ {
+ "name": "阿肯语",
+ "code": "aka",
+ "detect": False
+ },
+ {
+ "name": "阿拉贡语",
+ "code": "arg",
+ "detect": False
+ },
+ {
+ "name": "阿姆哈拉语",
+ "code": "amh",
+ "detect": True
+
+ },
+ {
+ "name": "阿萨姆语",
+ "code": "asm",
+ "detect": True
+
+ },
+ {
+ "name": "艾马拉语",
+ "code": "aym",
+ "detect": False
+ },
+ {
+ "name": "阿塞拜疆语",
+ "code": "aze",
+ "detect": True
+
+ },
+ {
+ "name": "阿斯图里亚斯语",
+ "code": "ast",
+ "detect": True
+
+ },
+ {
+ "name": "奥塞梯语",
+ "code": "oss",
+ "detect": False
+ },
+ {
+ "name": "爱沙尼亚语",
+ "code": "est",
+ "detect": True
+
+ },
+ {
+ "name": "奥杰布瓦语",
+ "code": "oji",
+ "detect": False
+ },
+ {
+ "name": "奥里亚语",
+ "code": "ori",
+ "detect": True
+
+ },
+ {
+ "name": "奥罗莫语",
+ "code": "orm",
+ "detect": False
+ },
+ {
+ "name": "波兰语",
+ "code": "pl",
+ "detect": True
+
+ },
+ {
+ "name": "波斯语",
+ "code": "per",
+ "detect": True
+
+ },
+ {
+ "name": "布列塔尼语",
+ "code": "bre",
+ "detect": True
+
+ },
+ {
+ "name": "巴什基尔语",
+ "code": "bak",
+ "detect": False
+ },
+ {
+ "name": "巴斯克语",
+ "code": "baq",
+ "detect": True
+
+ },
+ {
+ "name": "巴西葡萄牙语",
+ "code": "pot",
+ "detect": False
+ },
+ {
+ "name": "白俄罗斯语",
+ "code": "bel",
+ "detect": True
+
+ },
+ {
+ "name": "柏柏尔语",
+ "code": "ber",
+ "detect": True
+
+ },
+ {
+ "name": "邦板牙语",
+ "code": "pam",
+ "detect": False
+ },
+ {
+ "name": "保加利亚语",
+ "code": "bul",
+ "detect": True
+
+ },
+ {
+ "name": "北方萨米语",
+ "code": "sme",
+ "detect": False
+ },
+ {
+ "name": "北索托语",
+ "code": "ped",
+ "detect": False
+ },
+ {
+ "name": "本巴语",
+ "code": "bem",
+ "detect": False
+ },
+ {
+ "name": "比林语",
+ "code": "bli",
+ "detect": False
+ },
+ {
+ "name": "比斯拉马语",
+ "code": "bis",
+ "detect": False
+ },
+ {
+ "name": "俾路支语",
+ "code": "bal",
+ "detect": False
+ },
+ {
+ "name": "冰岛语",
+ "code": "ice",
+ "detect": True
+
+ },
+ {
+ "name": "波斯尼亚语",
+ "code": "bos",
+ "detect": True
+
+ },
+ {
+ "name": "博杰普尔语",
+ "code": "bho",
+ "detect": False
+ },
+ {
+ "name": "楚瓦什语",
+ "code": "chv",
+ "detect": False
+ },
+ {
+ "name": "聪加语",
+ "code": "tso",
+ "detect": False
+ },
+ {
+ "name": "丹麦语",
+ "code": "dan",
+ "detect": True
+
+ },
+ {
+ "name": "德语",
+ "code": "de",
+ "detect": True
+
+ },
+ {
+ "name": "鞑靼语",
+ "code": "tat",
+ "detect": True
+
+ },
+ {
+ "name": "掸语",
+ "code": "sha",
+ "detect": False
+ },
+ {
+ "name": "德顿语",
+ "code": "tet",
+ "detect": False
+ },
+ {
+ "name": "迪维希语",
+ "code": "div",
+ "detect": False
+ },
+ {
+ "name": "低地德语",
+ "code": "log",
+ "detect": True
+
+ },
+ {
+ "name": "俄语",
+ "code": "ru",
+ "detect": True
+
+ },
+ {
+ "name": "法语",
+ "code": "fra",
+ "detect": True
+
+ },
+ {
+ "name": "菲律宾语",
+ "code": "fil",
+ "detect": True
+
+ },
+ {
+ "name": "芬兰语",
+ "code": "fin",
+ "detect": True
+
+ },
+ {
+ "name": "梵语",
+ "code": "san",
+ "detect": False
+ },
+ {
+ "name": "弗留利语",
+ "code": "fri",
+ "detect": False
+ },
+ {
+ "name": "富拉尼语",
+ "code": "ful",
+ "detect": False
+ },
+ {
+ "name": "法罗语",
+ "code": "fao",
+ "detect": False
+ },
+ {
+ "name": "盖尔语",
+ "code": "gla",
+ "detect": False
+ },
+ {
+ "name": "刚果语",
+ "code": "kon",
+ "detect": False
+ },
+ {
+ "name": "高地索布语",
+ "code": "ups",
+ "detect": False
+ },
+ {
+ "name": "高棉语",
+ "code": "hkm",
+ "detect": True
+
+ },
+ {
+ "name": "格陵兰语",
+ "code": "kal",
+ "detect": False
+ },
+ {
+ "name": "格鲁吉亚语",
+ "code": "geo",
+ "detect": True
+
+ },
+ {
+ "name": "古吉拉特语",
+ "code": "guj",
+ "detect": True
+
+ },
+ {
+ "name": "古希腊语",
+ "code": "gra",
+ "detect": False
+ },
+ {
+ "name": "古英语",
+ "code": "eno",
+ "detect": False
+ },
+ {
+ "name": "瓜拉尼语",
+ "code": "grn",
+ "detect": False
+ },
+ {
+ "name": "韩语",
+ "code": "kor",
+ "detect": True
+
+ },
+ {
+ "name": "荷兰语",
+ "code": "nl",
+ "detect": True
+
+ },
+ {
+ "name": "胡帕语",
+ "code": "hup",
+ "detect": False
+ },
+ {
+ "name": "哈卡钦语",
+ "code": "hak",
+ "detect": False
+ },
+ {
+ "name": "海地语",
+ "code": "ht",
+ "detect": False
+ },
+ {
+ "name": "黑山语",
+ "code": "mot",
+ "detect": False
+ },
+ {
+ "name": "豪萨语",
+ "code": "hau",
+ "detect": False
+ },
+ {
+ "name": "吉尔吉斯语",
+ "code": "kir",
+ "detect": False
+ },
+ {
+ "name": "加利西亚语",
+ "code": "glg",
+ "detect": True
+
+ },
+ {
+ "name": "加拿大法语",
+ "code": "frn",
+ "detect": False
+ },
+ {
+ "name": "加泰罗尼亚语",
+ "code": "cat",
+ "detect": True
+
+ },
+ {
+ "name": "捷克语",
+ "code": "cs",
+ "detect": True
+
+ },
+ {
+ "name": "卡拜尔语",
+ "code": "kab",
+ "detect": True
+
+ },
+ {
+ "name": "卡纳达语",
+ "code": "kan",
+ "detect": True
+
+ },
+ {
+ "name": "卡努里语",
+ "code": "kau",
+ "detect": False
+ },
+ {
+ "name": "卡舒比语",
+ "code": "kah",
+ "detect": False
+ },
+ {
+ "name": "康瓦尔语",
+ "code": "cor",
+ "detect": False
+ },
+ {
+ "name": "科萨语",
+ "code": "xho",
+ "detect": True
+
+ },
+ {
+ "name": "科西嘉语",
+ "code": "cos",
+ "detect": False
+ },
+ {
+ "name": "克里克语",
+ "code": "cre",
+ "detect": False
+ },
+ {
+ "name": "克里米亚鞑靼语",
+ "code": "cri",
+ "detect": False
+ },
+ {
+ "name": "克林贡语",
+ "code": "kli",
+ "detect": False
+ },
+ {
+ "name": "克罗地亚语",
+ "code": "hrv",
+ "detect": True
+
+ },
+ {
+ "name": "克丘亚语",
+ "code": "que",
+ "detect": False
+ },
+ {
+ "name": "克什米尔语",
+ "code": "kas",
+ "detect": False
+ },
+ {
+ "name": "孔卡尼语",
+ "code": "kok",
+ "detect": False
+ },
+ {
+ "name": "库尔德语",
+ "code": "kur",
+ "detect": True
+
+ },
+ {
+ "name": "拉丁语",
+ "code": "lat",
+ "detect": True
+
+ },
+ {
+ "name": "老挝语",
+ "code": "lao",
+ "detect": False
+ },
+ {
+ "name": "罗马尼亚语",
+ "code": "rom",
+ "detect": True
+
+ },
+ {
+ "name": "拉特加莱语",
+ "code": "lag",
+ "detect": False
+ },
+ {
+ "name": "拉脱维亚语",
+ "code": "lav",
+ "detect": True
+
+ },
+ {
+ "name": "林堡语",
+ "code": "lim",
+ "detect": False
+ },
+ {
+ "name": "林加拉语",
+ "code": "lin",
+ "detect": False
+ },
+ {
+ "name": "卢干达语",
+ "code": "lug",
+ "detect": False
+ },
+ {
+ "name": "卢森堡语",
+ "code": "ltz",
+ "detect": False
+ },
+ {
+ "name": "卢森尼亚语",
+ "code": "ruy",
+ "detect": False
+ },
+ {
+ "name": "卢旺达语",
+ "code": "kin",
+ "detect": True
+
+ },
+ {
+ "name": "立陶宛语",
+ "code": "lit",
+ "detect": True
+
+ },
+ {
+ "name": "罗曼什语",
+ "code": "roh",
+ "detect": False
+ },
+ {
+ "name": "罗姆语",
+ "code": "ro",
+ "detect": False
+ },
+ {
+ "name": "逻辑语",
+ "code": "loj",
+ "detect": False
+ },
+ {
+ "name": "马来语",
+ "code": "may",
+ "detect": True
+
+ },
+ {
+ "name": "缅甸语",
+ "code": "bur",
+ "detect": True
+
+ },
+ {
+ "name": "马拉地语",
+ "code": "mar",
+ "detect": False
+ },
+ {
+ "name": "马拉加斯语",
+ "code": "mg",
+ "detect": True
+
+ },
+ {
+ "name": "马拉雅拉姆语",
+ "code": "mal",
+ "detect": True
+
+ },
+ {
+ "name": "马其顿语",
+ "code": "mac",
+ "detect": True
+
+ },
+ {
+ "name": "马绍尔语",
+ "code": "mah",
+ "detect": False
+ },
+ {
+ "name": "迈蒂利语",
+ "code": "mai",
+ "detect": True
+
+ },
+ {
+ "name": "曼克斯语",
+ "code": "glv",
+ "detect": False
+ },
+ {
+ "name": "毛里求斯克里奥尔语",
+ "code": "mau",
+ "detect": False
+ },
+ {
+ "name": "毛利语",
+ "code": "mao",
+ "detect": False
+ },
+ {
+ "name": "孟加拉语",
+ "code": "ben",
+ "detect": True
+
+ },
+ {
+ "name": "马耳他语",
+ "code": "mlt",
+ "detect": True
+
+ },
+ {
+ "name": "苗语",
+ "code": "hmn",
+ "detect": False
+ },
+ {
+ "name": "挪威语",
+ "code": "nor",
+ "detect": True
+
+ },
+ {
+ "name": "那不勒斯语",
+ "code": "nea",
+ "detect": False
+ },
+ {
+ "name": "南恩德贝莱语",
+ "code": "nbl",
+ "detect": False
+ },
+ {
+ "name": "南非荷兰语",
+ "code": "afr",
+ "detect": True
+
+ },
+ {
+ "name": "南索托语",
+ "code": "sot",
+ "detect": False
+ },
+ {
+ "name": "尼泊尔语",
+ "code": "nep",
+ "detect": True
+
+ },
+ {
+ "name": "葡萄牙语",
+ "code": "pt",
+ "detect": True
+
+ },
+ {
+ "name": "旁遮普语",
+ "code": "pan",
+ "detect": True
+
+ },
+ {
+ "name": "帕皮阿门托语",
+ "code": "pap",
+ "detect": False
+ },
+ {
+ "name": "普什图语",
+ "code": "pus",
+ "detect": False
+ },
+ {
+ "name": "齐切瓦语",
+ "code": "nya",
+ "detect": False
+ },
+ {
+ "name": "契维语",
+ "code": "twi",
+ "detect": False
+ },
+ {
+ "name": "切罗基语",
+ "code": "chr",
+ "detect": False
+ },
+ {
+ "name": "日语",
+ "code": "jp",
+ "detect": True
+
+ },
+ {
+ "name": "瑞典语",
+ "code": "swe",
+ "detect": True
+
+ },
+ {
+ "name": "萨丁尼亚语",
+ "code": "srd",
+ "detect": False
+ },
+ {
+ "name": "萨摩亚语",
+ "code": "sm",
+ "detect": False
+ },
+ {
+ "name": "塞尔维亚-克罗地亚语",
+ "code": "sec",
+ "detect": False
+ },
+ {
+ "name": "塞尔维亚语",
+ "code": "srp",
+ "detect": True
+
+ },
+ {
+ "name": "桑海语",
+ "code": "sol",
+ "detect": False
+ },
+ {
+ "name": "僧伽罗语",
+ "code": "sin",
+ "detect": True
+
+ },
+ {
+ "name": "世界语",
+ "code": "epo",
+ "detect": True
+
+ },
+ {
+ "name": "书面挪威语",
+ "code": "nob",
+ "detect": True
+
+ },
+ {
+ "name": "斯洛伐克语",
+ "code": "sk",
+ "detect": True
+
+ },
+ {
+ "name": "斯洛文尼亚语",
+ "code": "slo",
+ "detect": True
+
+ },
+ {
+ "name": "斯瓦希里语",
+ "code": "swa",
+ "detect": True
+
+ },
+ {
+ "name": "塞尔维亚语(西里尔)",
+ "code": "src",
+ "detect": False
+ },
+ {
+ "name": "索马里语",
+ "code": "som",
+ "detect": True
+
+ },
+ {
+ "name": "苏格兰语",
+ "code": "sco",
+ "detect": False
+ },
+ {
+ "name": "泰语",
+ "code": "th",
+ "detect": True
+
+ },
+ {
+ "name": "土耳其语",
+ "code": "tr",
+ "detect": True
+
+ },
+ {
+ "name": "塔吉克语",
+ "code": "tgk",
+ "detect": True
+
+ },
+ {
+ "name": "泰米尔语",
+ "code": "tam",
+ "detect": True
+
+ },
+ {
+ "name": "他加禄语",
+ "code": "tgl",
+ "detect": True
+
+ },
+ {
+ "name": "提格利尼亚语",
+ "code": "tir",
+ "detect": False
+ },
+ {
+ "name": "泰卢固语",
+ "code": "tel",
+ "detect": True
+
+ },
+ {
+ "name": "突尼斯阿拉伯语",
+ "code": "tua",
+ "detect": False
+ },
+ {
+ "name": "土库曼语",
+ "code": "tuk",
+ "detect": False
+ },
+ {
+ "name": "乌克兰语",
+ "code": "ukr",
+ "detect": True
+
+ },
+ {
+ "name": "瓦隆语",
+ "code": "wln",
+ "detect": True
+
+ },
+ {
+ "name": "威尔士语",
+ "code": "wel",
+ "detect": True
+
+ },
+ {
+ "name": "文达语",
+ "code": "ven",
+ "detect": False
+ },
+ {
+ "name": "沃洛夫语",
+ "code": "wol",
+ "detect": False
+ },
+ {
+ "name": "乌尔都语",
+ "code": "urd",
+ "detect": True
+
+ },
+ {
+ "name": "西班牙语",
+ "code": "spa",
+ "detect": True
+
+ },
+ {
+ "name": "希伯来语",
+ "code": "heb",
+ "detect": True
+
+ },
+ {
+ "name": "希腊语",
+ "code": "el",
+ "detect": True
+
+ },
+ {
+ "name": "匈牙利语",
+ "code": "hu",
+ "detect": True
+
+ },
+ {
+ "name": "西弗里斯语",
+ "code": "fry",
+ "detect": True
+
+ },
+ {
+ "name": "西里西亚语",
+ "code": "sil",
+ "detect": False
+ },
+ {
+ "name": "希利盖农语",
+ "code": "hil",
+ "detect": False
+ },
+ {
+ "name": "下索布语",
+ "code": "los",
+ "detect": False
+ },
+ {
+ "name": "夏威夷语",
+ "code": "haw",
+ "detect": False
+ },
+ {
+ "name": "新挪威语",
+ "code": "nno",
+ "detect": True
+
+ },
+ {
+ "name": "西非书面语",
+ "code": "nqo",
+ "detect": False
+ },
+ {
+ "name": "信德语",
+ "code": "snd",
+ "detect": False
+ },
+ {
+ "name": "修纳语",
+ "code": "sna",
+ "detect": False
+ },
+ {
+ "name": "宿务语",
+ "code": "ceb",
+ "detect": False
+ },
+ {
+ "name": "叙利亚语",
+ "code": "syr",
+ "detect": False
+ },
+ {
+ "name": "巽他语",
+ "code": "sun",
+ "detect": False
+ },
+ {
+ "name": "英语",
+ "code": "en",
+ "detect": True
+
+ },
+ {
+ "name": "印地语",
+ "code": "hi",
+ "detect": True
+
+ },
+ {
+ "name": "印尼语",
+ "code": "id",
+ "detect": True
+
+ },
+ {
+ "name": "意大利语",
+ "code": "it",
+ "detect": True
+
+ },
+ {
+ "name": "越南语",
+ "code": "vie",
+ "detect": True
+
+ },
+ {
+ "name": "意第绪语",
+ "code": "yid",
+ "detect": False
+ },
+ {
+ "name": "因特语",
+ "code": "ina",
+ "detect": False
+ },
+ {
+ "name": "亚齐语",
+ "code": "ach",
+ "detect": False
+ },
+ {
+ "name": "印古什语",
+ "code": "ing",
+ "detect": False
+ },
+ {
+ "name": "伊博语",
+ "code": "ibo",
+ "detect": False
+ },
+ {
+ "name": "伊多语",
+ "code": "ido",
+ "detect": False
+ },
+ {
+ "name": "约鲁巴语",
+ "code": "yor",
+ "detect": False
+ },
+ {
+ "name": "亚美尼亚语",
+ "code": "arm",
+ "detect": True
+
+ },
+ {
+ "name": "伊努克提图特语",
+ "code": "iku",
+ "detect": False
+ },
+ {
+ "name": "中文(简体)",
+ "code": "zh",
+ "detect": True
+
+ },
+ {
+ "name": "中文(繁体)",
+ "code": "cht",
+ "detect": True
+
+ },
+ {
+ "name": "中文(文言文)",
+ "code": "wyw",
+ "detect": True
+
+ },
+ {
+ "name": "中文(粤语)",
+ "code": "yue",
+ "detect": True
+
+ },
+ {
+ "name": "扎扎其语",
+ "code": "zaz",
+ "detect": False
+ },
+ {
+ "name": "中古法语",
+ "code": "frm",
+ "detect": False
+ },
+ {
+ "name": "祖鲁语",
+ "code": "zul",
+ "detect": False
+ },
+ {
+ "name": "爪哇语",
+ "code": "jav",
+ "detect": False
+ },
+ {
+ "name": "自动检测",
+ "code": "auto",
+ "detect": True
+
+ }
+]
+}
\ No newline at end of file
diff --git a/app/__pycache__/apps.cpython-312.pyc b/app/__pycache__/apps.cpython-312.pyc
index efa9a5f..5babf99 100644
Binary files a/app/__pycache__/apps.cpython-312.pyc and b/app/__pycache__/apps.cpython-312.pyc differ
diff --git a/app/__pycache__/models.cpython-312.pyc b/app/__pycache__/models.cpython-312.pyc
index 46df252..c9965e7 100644
Binary files a/app/__pycache__/models.cpython-312.pyc and b/app/__pycache__/models.cpython-312.pyc differ
diff --git a/app/__pycache__/urls.cpython-312.pyc b/app/__pycache__/urls.cpython-312.pyc
new file mode 100644
index 0000000..f80ba61
Binary files /dev/null and b/app/__pycache__/urls.cpython-312.pyc differ
diff --git a/chunyu_project/__pycache__/asgi.cpython-312.pyc b/chunyu_project/__pycache__/asgi.cpython-312.pyc
new file mode 100644
index 0000000..f1e22e9
Binary files /dev/null and b/chunyu_project/__pycache__/asgi.cpython-312.pyc differ
diff --git a/chunyu_project/__pycache__/settings.cpython-312.pyc b/chunyu_project/__pycache__/settings.cpython-312.pyc
index 429287c..f49c08c 100644
Binary files a/chunyu_project/__pycache__/settings.cpython-312.pyc and b/chunyu_project/__pycache__/settings.cpython-312.pyc differ
diff --git a/chunyu_project/__pycache__/urls.cpython-312.pyc b/chunyu_project/__pycache__/urls.cpython-312.pyc
index a059a93..cadcf29 100644
Binary files a/chunyu_project/__pycache__/urls.cpython-312.pyc and b/chunyu_project/__pycache__/urls.cpython-312.pyc differ
diff --git a/chunyu_project/__pycache__/wsgi.cpython-312.pyc b/chunyu_project/__pycache__/wsgi.cpython-312.pyc
deleted file mode 100644
index 30f5668..0000000
Binary files a/chunyu_project/__pycache__/wsgi.cpython-312.pyc and /dev/null differ
diff --git a/chunyu_project/settings.py b/chunyu_project/settings.py
index 35f570b..665a729 100644
--- a/chunyu_project/settings.py
+++ b/chunyu_project/settings.py
@@ -28,6 +28,11 @@ SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
+PASSWORD_HASHERS = [
+ 'django.contrib.auth.hashers.Argon2PasswordHasher',
+ 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
+]
+
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-j+-psq=m++l7hup73k1eg+wm-b&2_)!+_^o=6(=xc$1px*kqt@'
@@ -67,12 +72,12 @@ ALLOWED_HOSTS = ["*"]
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000", # 例如:您的React/Vue开发服务器
"http://127.0.0.1:3000",
+ "http://192.168.1.4:3000",
+ "https://192.168.1.4:3000",
"http://*",
"https://*"
]
-# CORS设置
-CORS_ALLOW_ALL_ORIGINS = False
# 允许凭据
CORS_ALLOW_CREDENTIALS = True
@@ -143,6 +148,17 @@ SIMPLE_JWT = {
'USER_ID_CLAIM': 'user_id',
}
+# 邮件部分
+
+EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # 使用SMTP
+EMAIL_HOST = 'smtp.qq.com' # 需要真实的SMTP服务器
+EMAIL_PORT = 465
+EMAIL_USE_SSL = True
+EMAIL_HOST_USER = 'cs10086086@qq.com' # 真实邮箱账号
+EMAIL_HOST_PASSWORD = 'ldzpymytsinkfjhb' # 真实密码/授权码
+EMAIL_USE_TLS = False
+
+
AUTH_USER_MODEL = 'user.FUser'
INSTALLED_APPS = [
@@ -155,6 +171,8 @@ INSTALLED_APPS = [
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'django.contrib.sites',
# 第三方应用
@@ -168,7 +186,122 @@ INSTALLED_APPS = [
]
+
+# Database
+# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.mysql',
+ 'NAME': 'chunyu_project', # 数据库名
+ 'USER': 'root', # 数据库用户名
+ 'PASSWORD': 'mysql_NpyMCS', # 数据库密码
+ 'HOST': '192.168.1.4', # 数据库主机,留空默认为localhost
+ 'PORT': '3306', # 数据库端口,留空默认为3306
+ 'OPTIONS': {
+ 'charset': 'utf8mb4',
+ 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
+ 'connect_timeout': 10, # 连接超时时间(秒)
+ },
+ 'CONN_MAX_AGE': 3600, # 连接池最大存活时间(秒),0表示每次请求后关闭
+ 'CONN_HEALTH_CHECKS': True, # 连接健康检查
+ 'ATOMIC_REQUESTS': False, # 是否将每个HTTP请求包装在事务中
+ 'AUTOCOMMIT': True, # 是否自动提交事务
+ 'TIME_ZONE': 'Asia/Shanghai',
+ }
+}
+
+
+# Redis配置
+REDIS_HOST = '192.168.1.4' # Redis服务器地址
+REDIS_PORT = 6379 # Redis端口
+REDIS_DB = 0 # Redis数据库编号
+REDIS_PASSWORD = "+Xy2173240465" # Redis密码,没有密码则设为None
+SESSION_DB = 1
+CELERY_BROKER_DB = 2
+CELERY_RESULT_BACKEND_DB = 2
+
+# Django缓存配置(使用Redis作为缓存后端)
+CACHES = {
+ "default": {
+ "BACKEND": "django_redis.cache.RedisCache",
+ "LOCATION": f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}",
+
+ "OPTIONS": {
+ "CLIENT_CLASS": "django_redis.client.DefaultClient",
+ "PASSWORD": REDIS_PASSWORD,
+ "PARSER_CLASS": "redis.connection._HiredisParser", # 使用hiredis提高性能
+ 'CONNECTION_POOL_CLASS': 'redis.BlockingConnectionPool',
+ 'CONNECTION_POOL_CLASS_KWARGS': {
+ 'max_connections': 50,
+ 'timeout': 20,
+ },
+ 'MAX_CONNECTIONS': 50,
+ 'PICKLE_VERSION': -1,
+ 'SOCKET_CONNECT_TIMEOUT': 5,
+ 'SOCKET_TIMEOUT': 5,
+ 'COMPRESSOR': 'django_redis.compressors.zlib.ZlibCompressor',
+ 'SERIALIZER': 'django_redis.serializers.json.JSONSerializer',
+ 'COMPRESS_MIN_LENGTH': 1024 * 10,
+ 'IGNORE_EXCEPTIONS': True, # 缓存宕机时不崩溃
+ },
+ "KEY_PREFIX": "django_", # 缓存键前缀
+ "VERSION": 1, # 缓存版本
+ },
+ 'session': {
+ 'BACKEND': 'django_redis.cache.RedisCache',
+ 'LOCATION': f"redis://{REDIS_HOST}:{REDIS_PORT}/{SESSION_DB}",
+ 'OPTIONS': {
+ 'CLIENT_CLASS': 'django_redis.client.DefaultClient',
+ }
+ },
+ 'celery': {
+ 'BACKEND': 'django_redis.cache.RedisCache',
+ 'LOCATION': 'redis://:password@redis-celery:6379/0',
+ 'OPTIONS': {
+ 'CLIENT_CLASS': 'django_redis.client.DefaultClient',
+ }
+ }
+}
+
+# 会话配置
+SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
+SESSION_CACHE_ALIAS = 'session'
+SESSION_COOKIE_AGE = 1209600
+SESSION_COOKIE_SECURE = True
+SESSION_COOKIE_HTTPONLY = True
+SESSION_COOKIE_SAMESITE = 'Lax'
+
+# Celery配置
+CELERY_BROKER_URL = f'redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{CELERY_BROKER_DB}'
+CELERY_RESULT_BACKEND = f'redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{CELERY_RESULT_BACKEND_DB}'
+CELERY_CACHE_BACKEND = 'celery'
+CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True
+
+# 缓存中间件
+CACHE_MIDDLEWARE_ALIAS = 'default'
+CACHE_MIDDLEWARE_SECONDS = 60 * 15
+CACHE_MIDDLEWARE_KEY_PREFIX = 'chunyu_site'
+
+# 安全头
+SECURE_BROWSER_XSS_FILTER = True
+SECURE_CONTENT_TYPE_NOSNIFF = True
+X_FRAME_OPTIONS = 'DENY'
+
+# 定时任务:预热缓存
+CELERY_BEAT_SCHEDULE = {
+ 'warm-up-cache-every-hour': {
+ 'task': 'core.tasks.warm_up_cache',
+ 'schedule': 3600.0, # 每小时
+ },
+ 'generate-daily-report': {
+ 'task': 'reports.tasks.generate_daily_report',
+ 'schedule': 86400.0, # 每天
+ },
+}
+
MIDDLEWARE = [
+ 'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
@@ -177,8 +310,13 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
+ 'django.middleware.cache.FetchFromCacheMiddleware',
]
+CACHE_MIDDLEWARE_ALIAS = 'default'
+CACHE_MIDDLEWARE_SECONDS = 60 * 15 # 15分钟
+CACHE_MIDDLEWARE_KEY_PREFIX = 'mysite'
+
ROOT_URLCONF = 'chunyu_project.urls'
TEMPLATES = [
@@ -200,16 +338,13 @@ TEMPLATES = [
ASGI_APPLICATION = 'chunyu_project.asgi.application'
-# Database
-# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
-
-DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': BASE_DIR / 'db.sqlite3',
- }
-}
+# 使用Redis作为会话后端
+SESSION_ENGINE = "django.contrib.sessions.backends.cache"
+SESSION_CACHE_ALIAS = "default"
+# 使用Redis作为消息代理
+CELERY_BROKER_URL = 'redis://localhost:6379/0'
+CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
# Password validation
# https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators
@@ -246,3 +381,12 @@ USE_TZ = True
# https://docs.djangoproject.com/en/6.0/howto/static-files/
STATIC_URL = 'static/'
+
+STATICFILES_DIRS = [
+ BASE_DIR / "static",
+]
+
+STATIC_ROOT = BASE_DIR / "staticfiles"
+
+
+
diff --git a/user/__pycache__/__init__.cpython-312.pyc b/user/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..2c758b6
Binary files /dev/null and b/user/__pycache__/__init__.cpython-312.pyc differ
diff --git a/user/__pycache__/admin.cpython-312.pyc b/user/__pycache__/admin.cpython-312.pyc
new file mode 100644
index 0000000..e609062
Binary files /dev/null and b/user/__pycache__/admin.cpython-312.pyc differ
diff --git a/user/__pycache__/apps.cpython-312.pyc b/user/__pycache__/apps.cpython-312.pyc
new file mode 100644
index 0000000..d07179c
Binary files /dev/null and b/user/__pycache__/apps.cpython-312.pyc differ
diff --git a/user/__pycache__/models.cpython-312.pyc b/user/__pycache__/models.cpython-312.pyc
new file mode 100644
index 0000000..b5e3266
Binary files /dev/null and b/user/__pycache__/models.cpython-312.pyc differ
diff --git a/user/__pycache__/urls.cpython-312.pyc b/user/__pycache__/urls.cpython-312.pyc
new file mode 100644
index 0000000..84bfd7b
Binary files /dev/null and b/user/__pycache__/urls.cpython-312.pyc differ
diff --git a/user/migrations/0001_initial.py b/user/migrations/0001_initial.py
index 525cdf0..7615b7e 100644
--- a/user/migrations/0001_initial.py
+++ b/user/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 5.2.12 on 2026-03-25 21:58
+# Generated by Django 5.2.12 on 2026-04-04 09:10
import django.contrib.auth.models
import django.contrib.auth.validators
diff --git a/user/migrations/0002_fuser_issetpassword.py b/user/migrations/0002_fuser_issetpassword.py
new file mode 100644
index 0000000..99770bb
--- /dev/null
+++ b/user/migrations/0002_fuser_issetpassword.py
@@ -0,0 +1,19 @@
+# Generated by Django 5.2.12 on 2026-04-04 13:35
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('user', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='fuser',
+ name='isSetPassword',
+ field=models.BooleanField(blank=True, default=False, verbose_name='是否主动设置过密码'),
+ preserve_default=False,
+ ),
+ ]
diff --git a/user/migrations/__pycache__/0001_initial.cpython-312.pyc b/user/migrations/__pycache__/0001_initial.cpython-312.pyc
new file mode 100644
index 0000000..db19b30
Binary files /dev/null and b/user/migrations/__pycache__/0001_initial.cpython-312.pyc differ
diff --git a/user/migrations/__pycache__/0002_fuser_issetpassword.cpython-312.pyc b/user/migrations/__pycache__/0002_fuser_issetpassword.cpython-312.pyc
new file mode 100644
index 0000000..e5c15e8
Binary files /dev/null and b/user/migrations/__pycache__/0002_fuser_issetpassword.cpython-312.pyc differ
diff --git a/user/migrations/__pycache__/__init__.cpython-312.pyc b/user/migrations/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..7018955
Binary files /dev/null and b/user/migrations/__pycache__/__init__.cpython-312.pyc differ
diff --git a/user/models.py b/user/models.py
index 5d1c288..9db1e7e 100644
--- a/user/models.py
+++ b/user/models.py
@@ -8,6 +8,7 @@ class FUser(AbstractUser):
phone_number = models.CharField(max_length=15, blank=True, verbose_name='手机号')
avatar = models.ImageField(upload_to='avatars/', blank=True, null=True, verbose_name='头像')
bio = models.TextField(max_length=500, blank=True, verbose_name='个人简介')
+ isSetPassword = models.BooleanField(blank=True, verbose_name='是否主动设置过密码')
# class Meta:
# # 定义在管理后台显示的名称
diff --git a/user/urls.py b/user/urls.py
index 3a944c4..9546173 100644
--- a/user/urls.py
+++ b/user/urls.py
@@ -6,11 +6,13 @@ from rest_framework_simplejwt.views import (
TokenVerifyView,
)
+from .views.user import SendUserEmail
+
urlpatterns = [
# JWT 认证接口
- path('user/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
- path('user/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
- path('user/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
-
+ path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
+ path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
+ path('token/verify/', TokenVerifyView.as_view(), name='token_verify'),
+ path('register_send/', SendUserEmail.as_view(), name='send_user_email'),
]
\ No newline at end of file
diff --git a/user/views.py b/user/views.py
deleted file mode 100644
index 956f8b0..0000000
--- a/user/views.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from django.urls import path, include
-from rest_framework_simplejwt.views import (
- TokenObtainPairView,
- TokenRefreshView,
- TokenVerifyView,
-)
-
-urlpatterns = [
- # JWT 认证接口
- path('user/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
- path('user/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
- path('user/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
-
-
-]
\ No newline at end of file
diff --git a/user/views/__init__.py b/user/views/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/user/views/__pycache__/__init__.cpython-312.pyc b/user/views/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..9c2ff45
Binary files /dev/null and b/user/views/__pycache__/__init__.cpython-312.pyc differ
diff --git a/user/views/__pycache__/user.cpython-312.pyc b/user/views/__pycache__/user.cpython-312.pyc
new file mode 100644
index 0000000..aecfc3d
Binary files /dev/null and b/user/views/__pycache__/user.cpython-312.pyc differ
diff --git a/user/views/user.py b/user/views/user.py
new file mode 100644
index 0000000..bc7f268
--- /dev/null
+++ b/user/views/user.py
@@ -0,0 +1,75 @@
+from rest_framework.decorators import permission_classes
+from rest_framework.parsers import MultiPartParser, FormParser
+from rest_framework.permissions import AllowAny
+from rest_framework.views import APIView
+
+from rest_framework.response import Response
+from rest_framework import status
+
+from django.core.mail import EmailMessage
+
+import user.models
+# import os
+from utils import RandCode
+
+from django.core.cache import caches
+
+# 获取不同的缓存实例
+default_cache = caches['default']
+session_cache = caches['session']
+celery_cache = caches['celery']
+
+from ..models import FUser
+
+class SendUserEmail(APIView):
+ permission_classes = [AllowAny]
+ def post(self, request):
+ to_email = request.data.get('to_email', None)
+ print(to_email)
+
+ if to_email is None:
+ return Response(status=status.HTTP_400_BAD_REQUEST)
+
+ subject = 'test邮件'
+ code = RandCode.get_digit_characters_code_8()
+ body = f"你的验证码为 {code}, 该验证码仅用于账号注册, 十分钟内有效."
+ from_email = 'cs10086086@qq.com'
+ to_email = [to_email]
+
+ default_cache.set(f"register_{to_email[0]}", code, timeout=600)
+
+ email = EmailMessage(
+ subject=subject,
+ body=body,
+ from_email=from_email,
+ to=to_email,
+ )
+
+ email.send()
+ return Response('邮件发送成功!', status=status.HTTP_201_CREATED)
+
+class RegisterByEmail(APIView):
+ permission_classes = [AllowAny]
+ def post(self, request):
+ code = request.data.get('code', None)
+ to_email = request.data.get('to_email', None)
+
+ if code is None or to_email is None:
+ return Response(status=status.HTTP_400_BAD_REQUEST)
+
+ vcode = default_cache.get(f"register_{to_email}")
+ if vcode is None:
+ return Response(status=status.HTTP_400_BAD_REQUEST)
+
+ if code == vcode:
+ password = RandCode.get_alphanumeric_characters_code_8()
+ FUser.objects.create_user(
+ username=to_email,
+ email=to_email,
+ password=password
+ )
+ return Response("创建成功.",status=status.HTTP_200_OK)
+ else:
+ return Response(status=status.HTTP_400_BAD_REQUEST)
+
+
diff --git a/utils/RandCode.py b/utils/RandCode.py
new file mode 100644
index 0000000..d5b3da8
--- /dev/null
+++ b/utils/RandCode.py
@@ -0,0 +1,99 @@
+# 包含所有大小写字母、数字和常见可见特殊字符
+all_visible_characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+[]{}|;:,.<>?/~`\"'\\"
+# 包含所有大小写字母和数字
+alphanumeric_characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+# 包含所有数字
+digit_characters = "0123456789"
+# 包含常见可见特殊字符(不包括空格)
+special_characters = "!@#$%^&*()-_=+[]{}|;:,.<>?/~`\"'\\"
+# 包含26个大写字母
+uppercase_only = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+# 包含26个小写字母
+lowercase_only = "abcdefghijklmnopqrstuvwxyz"
+# 包含26个小写字母和0-9 数字
+lowercase_digits = "abcdefghijklmnopqrstuvwxyz0123456789"
+# 包含26个大写字母和0-9 数字
+uppercase_digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+
+import secrets
+
+
+def get_all_visible_characters_code_8():
+ code = ''.join(secrets.choice(all_visible_characters) for _ in range(8))
+
+ while(code[0] == '0'):
+ first_digit = secrets.choice(all_visible_characters) # 1-9
+ code = first_digit + code[1:]
+
+ return code
+
+def get_all_visible_characters_code_6():
+
+ code = ''.join(secrets.choice(all_visible_characters) for _ in range(6))
+
+ while(code[0] == '0'):
+ first_digit = secrets.choice(all_visible_characters) # 1-9
+ code = first_digit + code[1:]
+
+ return code
+
+def get_all_visible_characters_code(length):
+ code = ''.join(secrets.choice(all_visible_characters) for _ in range(length))
+
+ while(code[0] == '0'):
+ first_digit = secrets.choice(all_visible_characters) # 1-9
+ code = first_digit + code[1:]
+
+ return code
+
+def get_alphanumeric_characters_code(length):
+ code = ''.join(secrets.choice(alphanumeric_characters) for _ in range(length))
+
+ while(code[0] == '0'):
+ first_digit = secrets.choice(alphanumeric_characters) # 1-9
+ code = first_digit + code[1:]
+
+ return code
+
+def get_alphanumeric_characters_code_6():
+ code = ''.join(secrets.choice(alphanumeric_characters) for _ in range(6))
+
+ while(code[0] == '0'):
+ first_digit = secrets.choice(alphanumeric_characters) # 1-9
+ code = first_digit + code[1:]
+
+ return code
+
+def get_alphanumeric_characters_code_8():
+ code = ''.join(secrets.choice(alphanumeric_characters) for _ in range(8))
+
+ while(code[0] == '0'):
+ first_digit = secrets.choice(alphanumeric_characters) # 1-9
+ code = first_digit + code[1:]
+
+ return code
+
+def get_digit_characters_code(length):
+ code = ''.join(secrets.choice(digit_characters) for _ in range(length))
+ while(code[0] == '0'):
+ first_digit = secrets.choice(digit_characters)
+ code = first_digit + code[1:]
+
+ return code
+
+def get_digit_characters_code_6():
+ code = ''.join(secrets.choice(digit_characters) for _ in range(6))
+
+ while(code[0] == '0'):
+ first_digit = secrets.choice(digit_characters)
+ code = first_digit + code[1:]
+
+ return code
+
+def get_digit_characters_code_8():
+ code = ''.join(secrets.choice(digit_characters) for _ in range(8))
+ while(code[0] == '0'):
+ first_digit = secrets.choice(digit_characters)
+ code = first_digit + code[1:]
+
+ return code
\ No newline at end of file
diff --git a/utils/__pycache__/RandCode.cpython-312.pyc b/utils/__pycache__/RandCode.cpython-312.pyc
new file mode 100644
index 0000000..dbb781e
Binary files /dev/null and b/utils/__pycache__/RandCode.cpython-312.pyc differ