主要修改在asr-monitor-test 修改小程序手机号码登录 小程序的TTS生成(查一查、AI) 增加和支付相关的功能
This commit is contained in:
@@ -141,15 +141,35 @@ def upload_file(tenant_id,mesum_id):
|
|||||||
|
|
||||||
#logging.info(f"mesumid={mesum_id} {joined_string}")
|
#logging.info(f"mesumid={mesum_id} {joined_string}")
|
||||||
|
|
||||||
prompt = (f"你是一名图片识别和理解助手"
|
prompt1 = (f"你是一名图片识别和理解助手"
|
||||||
f"任务是先识别图片中文字,然后理解文字中包含的内容,分析哪一项可以作为识别出文字的标题,"
|
f"任务是先识别图片中文字,然后理解文字中包含的内容,分析哪一项可以作为识别出文字的标题,"
|
||||||
f"你的回答有3个结果,第一个结果匹配出的结果,JSON键值为antique"
|
f"你的回答有3个结果,第一个结果匹配出的结果,JSON键值为antique"
|
||||||
f"从下面的候选项:{antiques_selected}进行匹配,每一个候选项中间以','分割,如果没有任何匹配则结果为'',以免误触发讲解,匹配成功则输出匹配出的内容"
|
f"从下面的候选项:{antiques_selected}进行匹配,每一个候选项中间以';'分割,如果没有任何匹配则结果为'',以免误触发讲解,匹配成功则输出匹配出的内容"
|
||||||
f",第二个结果是原始识别的所有文字,json 键值为text"
|
f",第二个结果是原始识别的所有文字,json 键值为text"
|
||||||
f"第三个结果是识别出文字与匹配项列表中元素的匹配度,范围从0-1,1表示100%匹配,0表示完全不匹配,JSON键值为match_score,"
|
f"第三个结果是识别出文字与匹配项列表中元素的匹配度,范围从0-1,1表示100%匹配,0表示完全不匹配,JSON键值为match_score,"
|
||||||
"3个结果输出以{ }的json格式给出,匹配出文物、事件、人物的结果键值为antique"
|
"3个结果输出以{ }的json格式给出,匹配出文物、事件、人物的结果键值为antique"
|
||||||
f"原始数据的键值为text,输出是1个完整的JSON数据,不要有多余的前置和后置内容,确保前端能正确解析出JSON数据")
|
f"原始数据的键值为text,输出是1个完整的JSON数据,不要有多余的前置和后置内容,确保前端能正确解析出JSON数据")
|
||||||
|
|
||||||
|
prompt = (
|
||||||
|
f"作为图片识别和理解助手,您的任务是:"
|
||||||
|
f"\n1. 精确识别图片中的文字内容"
|
||||||
|
f"\n2. 理解文字语义"
|
||||||
|
f"\n3. 从以下候选标题中选择最佳匹配项:"
|
||||||
|
f"\n [{antiques_selected}]"
|
||||||
|
f"\n\n### 输出要求:"
|
||||||
|
f"\n- 以严格JSON格式输出,包含3个字段:"
|
||||||
|
f"\n • `antique`: 匹配的标题(多个用英文分号';'分割,最多匹配3个,无匹配则空字符串)"
|
||||||
|
f"\n • `text`: 识别出的完整文字"
|
||||||
|
f"\n • `match_score`: 整体匹配度(0-1的浮点数),1=完全匹配"
|
||||||
|
f"\n\n### 匹配规则:"
|
||||||
|
f"\n1. 语义匹配优先于字面匹配"
|
||||||
|
f"\n2. 考虑同义词、近义词和描述性匹配"
|
||||||
|
f"\n3. 允许部分匹配(如'青铜酒器'匹配'青铜器')"
|
||||||
|
f"\n4. 若无明确匹配项,`antique`返回空字符串"
|
||||||
|
f"\n\n### 重要:"
|
||||||
|
f"\n- 输出必须是可直接解析的JSON,无任何前置/后置文本"
|
||||||
|
f"\n- 匹配度评分需客观反映文本与候选标题的相似度"
|
||||||
|
)
|
||||||
file = request.files['file']
|
file = request.files['file']
|
||||||
|
|
||||||
if file.filename == '':
|
if file.filename == '':
|
||||||
@@ -221,12 +241,23 @@ def upload_file(tenant_id,mesum_id):
|
|||||||
message = response.choices[0].message
|
message = response.choices[0].message
|
||||||
parsed_json_res = parse_markdown_json(message.content)
|
parsed_json_res = parse_markdown_json(message.content)
|
||||||
parsed_json_data = {"antique": "", "text": "", "match_score": 0}
|
parsed_json_data = {"antique": "", "text": "", "match_score": 0}
|
||||||
|
matchedArray = []
|
||||||
if parsed_json_res.get('success') is True:
|
if parsed_json_res.get('success') is True:
|
||||||
parsed_json_data = parsed_json_res.get('data')
|
parsed_json_data = parsed_json_res.get('data')
|
||||||
for item in labels_with_id:
|
matchedAntiqueArray = parsed_json_data.get('antique').split(';') # 识别出的文物的数组,中间以';'分割,可能有多个
|
||||||
if item['label'] == parsed_json_data.get('antique'):
|
if len(matchedAntiqueArray) ==1: # 只有一个匹配项,直接返回
|
||||||
parsed_json_data['id'] = item.get('id')
|
for item in labels_with_id:
|
||||||
|
if item['label'] == parsed_json_data.get('antique'):
|
||||||
|
parsed_json_data['id'] = item.get('id')
|
||||||
|
else: # 有多个匹配项,需要进行多个匹配
|
||||||
|
for label in matchedAntiqueArray:
|
||||||
|
antique= {'label':label}
|
||||||
|
for item in labels_with_id:
|
||||||
|
if item['label'] == label:
|
||||||
|
antique['id'] = item.get('id')
|
||||||
|
matchedArray.append(antique)
|
||||||
|
if len(matchedArray) > 0:
|
||||||
|
parsed_json_data['matchedArray'] = matchedArray
|
||||||
logging.info(f"{parsed_json_data}")
|
logging.info(f"{parsed_json_data}")
|
||||||
return jsonify({'message': 'File uploaded successfully','text': message.content,
|
return jsonify({'message': 'File uploaded successfully','text': message.content,
|
||||||
'data': parsed_json_data}), 200
|
'data': parsed_json_data}), 200
|
||||||
@@ -372,6 +403,9 @@ def start_background_cleaner():
|
|||||||
# 应用启动时启动清理线程
|
# 应用启动时启动清理线程
|
||||||
start_background_cleaner()
|
start_background_cleaner()
|
||||||
|
|
||||||
|
# 在返回大模型对话的文本中,同时生成tts音频,由dialog_service 中的StreamSessionManager进行管理
|
||||||
|
# session_id 为 def create_session(self, tts_model,sample_rate =8000, stream_format='mp3'):
|
||||||
|
# session_id = str(uuid.uuid4())
|
||||||
@manager.route('/tts_stream/<session_id>',methods=['GET'])
|
@manager.route('/tts_stream/<session_id>',methods=['GET'])
|
||||||
def tts_stream(session_id):
|
def tts_stream(session_id):
|
||||||
session = stream_manager.sessions.get(session_id)
|
session = stream_manager.sessions.get(session_id)
|
||||||
@@ -415,7 +449,6 @@ def tts_stream(session_id):
|
|||||||
if session:
|
if session:
|
||||||
# 延迟关闭会话,确保所有数据已发送
|
# 延迟关闭会话,确保所有数据已发送
|
||||||
stream_manager.close_session(session_id)
|
stream_manager.close_session(session_id)
|
||||||
logging.info(f"Session {session_id} closed. {total_audio_strean_length}")
|
|
||||||
# 关键响应头设置
|
# 关键响应头设置
|
||||||
|
|
||||||
if session['stream_format'] == "wav":
|
if session['stream_format'] == "wav":
|
||||||
@@ -656,7 +689,8 @@ def dialog_tts_post(tenant_id, chat_id):
|
|||||||
f"{tts_sample_rate} {tts_stream_format}")
|
f"{tts_sample_rate} {tts_stream_format}")
|
||||||
# 返回音频流URL
|
# 返回音频流URL
|
||||||
return jsonify({"tts_url": audio_stream_url, "audio_stream_id": audio_stream_id,
|
return jsonify({"tts_url": audio_stream_url, "audio_stream_id": audio_stream_id,
|
||||||
"sample_rate":tts_sample_rate, "stream_format":tts_stream_format,})
|
"sample_rate":tts_sample_rate, "stream_format":tts_stream_format,
|
||||||
|
"ws_url":audio_stream_url})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"请求处理失败: {str(e)}", exc_info=True)
|
logging.error(f"请求处理失败: {str(e)}", exc_info=True)
|
||||||
@@ -802,6 +836,15 @@ def minio_put_obj(tenant_id):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return get_error_data_result(message=f"minio put object error {e}")
|
return get_error_data_result(message=f"minio put object error {e}")
|
||||||
|
|
||||||
|
@manager.route('/minio/list/<bucket>/<prefix>', methods=['GET'])
|
||||||
|
@token_required
|
||||||
|
def list_objects(tenant_id,bucket: str, prefix: str = "", recursive: bool = True):
|
||||||
|
try:
|
||||||
|
result=minio_client.list_objects(bucket,prefix ,True)
|
||||||
|
return get_result(data=result)
|
||||||
|
except Exception as e:
|
||||||
|
return get_error_data_result(message=f"minio put list objects error {e}")
|
||||||
|
|
||||||
#------------------------------------------------
|
#------------------------------------------------
|
||||||
def audio_fade_in(audio_data, fade_length):
|
def audio_fade_in(audio_data, fade_length):
|
||||||
# 假设音频数据是16位单声道PCM
|
# 假设音频数据是16位单声道PCM
|
||||||
|
|||||||
@@ -1025,6 +1025,11 @@ class MesumOverview(DataBaseModel):
|
|||||||
null=True,
|
null=True,
|
||||||
help_text="图片地址",
|
help_text="图片地址",
|
||||||
index=False)
|
index=False)
|
||||||
|
photo_prefix = CharField(
|
||||||
|
max_length=255,
|
||||||
|
null=True,
|
||||||
|
help_text="文物图片前缀",
|
||||||
|
index=False)
|
||||||
address = CharField(
|
address = CharField(
|
||||||
max_length=1024,
|
max_length=1024,
|
||||||
null=True,
|
null=True,
|
||||||
@@ -1055,6 +1060,8 @@ class MesumAntique(DataBaseModel):
|
|||||||
combined = TextField(null=True)
|
combined = TextField(null=True)
|
||||||
ttsUrl_adult = CharField(max_length=256, null=True)
|
ttsUrl_adult = CharField(max_length=256, null=True)
|
||||||
ttsUrl_child = CharField(max_length=256, null=True)
|
ttsUrl_child = CharField(max_length=256, null=True)
|
||||||
|
photo_url = CharField(max_length=256, null=True)
|
||||||
|
orgin_text = TextField(null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = 'mesum_antique'
|
db_table = 'mesum_antique'
|
||||||
|
|||||||
367
api/db/services/ali_tts_service.py
Normal file
367
api/db/services/ali_tts_service.py
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
import asyncio,logging
|
||||||
|
from collections import deque
|
||||||
|
import threading, time,queue,uuid,time,array
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
|
ALI_KEY = "sk-a47a3fb5f4a94f66bbaf713779101c75"
|
||||||
|
from dashscope.api_entities.dashscope_response import SpeechSynthesisResponse
|
||||||
|
from dashscope.audio.tts import (
|
||||||
|
ResultCallback as TTSResultCallback,
|
||||||
|
SpeechSynthesizer as TTSSpeechSynthesizer,
|
||||||
|
SpeechSynthesisResult as TTSSpeechSynthesisResult,
|
||||||
|
)
|
||||||
|
# cyx 2025 01 19 测试cosyvoice 使用tts_v2 版本
|
||||||
|
from dashscope.audio.tts_v2 import (
|
||||||
|
ResultCallback as CosyResultCallback,
|
||||||
|
SpeechSynthesizer as CosySpeechSynthesizer,
|
||||||
|
AudioFormat,
|
||||||
|
)
|
||||||
|
|
||||||
|
class QwenTTS:
|
||||||
|
def __init__(self, key,format="mp3",sample_rate=44100, model_name="cosyvoice-v1/longyuan"):
|
||||||
|
import dashscope
|
||||||
|
import ssl
|
||||||
|
logging.info(f"---QwenTTS Construtor-- {format} {sample_rate} {model_name}") # cyx
|
||||||
|
self.model_name = model_name
|
||||||
|
dashscope.api_key = key
|
||||||
|
ssl._create_default_https_context = ssl._create_unverified_context # 禁用验证
|
||||||
|
self.synthesizer = None
|
||||||
|
self.callback = None
|
||||||
|
self.is_cosyvoice = False
|
||||||
|
self.voice = ""
|
||||||
|
self.format = format
|
||||||
|
self.sample_rate = sample_rate
|
||||||
|
if '/' in model_name:
|
||||||
|
parts = model_name.split('/', 1)
|
||||||
|
# 返回分离后的两个字符串parts[0], parts[1]
|
||||||
|
if parts[0] == 'cosyvoice-v1':
|
||||||
|
self.is_cosyvoice = True
|
||||||
|
self.voice = parts[1]
|
||||||
|
|
||||||
|
class Callback(TTSResultCallback):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.dque = deque()
|
||||||
|
|
||||||
|
def _run(self):
|
||||||
|
while True:
|
||||||
|
if not self.dque:
|
||||||
|
time.sleep(0)
|
||||||
|
continue
|
||||||
|
val = self.dque.popleft()
|
||||||
|
if val:
|
||||||
|
yield val
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
def on_open(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_complete(self):
|
||||||
|
self.dque.append(None)
|
||||||
|
|
||||||
|
def on_error(self, response: SpeechSynthesisResponse):
|
||||||
|
print("Qwen tts error", str(response))
|
||||||
|
raise RuntimeError(str(response))
|
||||||
|
|
||||||
|
def on_close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_event(self, result: TTSSpeechSynthesisResult):
|
||||||
|
if result.get_audio_frame() is not None:
|
||||||
|
self.dque.append(result.get_audio_frame())
|
||||||
|
|
||||||
|
# --------------------------
|
||||||
|
|
||||||
|
class Callback_Cosy(CosyResultCallback):
|
||||||
|
def __init__(self,on_audio_data) -> None:
|
||||||
|
self.dque = deque()
|
||||||
|
self.on_audio_data = on_audio_data
|
||||||
|
|
||||||
|
def _run(self):
|
||||||
|
while True:
|
||||||
|
if not self.dque:
|
||||||
|
time.sleep(0)
|
||||||
|
continue
|
||||||
|
val = self.dque.popleft()
|
||||||
|
if val:
|
||||||
|
yield val
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
def on_open(self):
|
||||||
|
logging.info("---Qwen tts on_open---")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_complete(self):
|
||||||
|
self.dque.append(None)
|
||||||
|
|
||||||
|
def on_error(self, response: SpeechSynthesisResponse):
|
||||||
|
print("Qwen tts error", str(response))
|
||||||
|
raise RuntimeError(str(response))
|
||||||
|
|
||||||
|
def on_close(self):
|
||||||
|
# print("---Qwen call back close") # cyx
|
||||||
|
logging.info("---Qwen tts on_close---")
|
||||||
|
pass
|
||||||
|
|
||||||
|
""" canceled for test 语音大模型CosyVoice
|
||||||
|
def on_event(self, result: SpeechSynthesisResult):
|
||||||
|
if result.get_audio_frame() is not None:
|
||||||
|
self.dque.append(result.get_audio_frame())
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_event(self, message):
|
||||||
|
# print(f"recv speech synthsis message {message}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 以下适合语音大模型CosyVoice
|
||||||
|
def on_data(self, data: bytes) -> None:
|
||||||
|
if len(data) > 0:
|
||||||
|
if self.on_audio_data:
|
||||||
|
self.on_audio_data(data)
|
||||||
|
else:
|
||||||
|
self.dque.append(data)
|
||||||
|
|
||||||
|
# --------------------------
|
||||||
|
|
||||||
|
def tts(self, text):
|
||||||
|
print(f"--QwenTTS--tts_stream begin-- {text} {self.is_cosyvoice} {self.voice}") # cyx
|
||||||
|
# text = self.normalize_text(text)
|
||||||
|
try:
|
||||||
|
# if self.model_name != 'cosyvoice-v1':
|
||||||
|
if self.is_cosyvoice is False:
|
||||||
|
self.callback = self.Callback()
|
||||||
|
TTSSpeechSynthesizer.call(model=self.model_name,
|
||||||
|
text=text,
|
||||||
|
callback=self.callback,
|
||||||
|
format="wav") # format="mp3")
|
||||||
|
else:
|
||||||
|
self.callback = self.Callback_Cosy(None)
|
||||||
|
format =self.get_audio_format(self.format,self.sample_rate)
|
||||||
|
self.synthesizer = CosySpeechSynthesizer(
|
||||||
|
model='cosyvoice-v1',
|
||||||
|
# voice="longyuan", #"longfei",
|
||||||
|
voice=self.voice,
|
||||||
|
callback=self.callback,
|
||||||
|
format=format
|
||||||
|
)
|
||||||
|
self.synthesizer.call(text)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"---dale---20 error {e}") # cyx
|
||||||
|
# -----------------------------------
|
||||||
|
try:
|
||||||
|
for data in self.callback._run():
|
||||||
|
#logging.info(f"dashcope return data {len(data)}")
|
||||||
|
yield data
|
||||||
|
# print(f"---Qwen return data {num_tokens_from_string(text)}")
|
||||||
|
# yield num_tokens_from_string(text)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(f"**ERROR**: {e}")
|
||||||
|
|
||||||
|
def init_streaming_call(self, on_data):
|
||||||
|
try:
|
||||||
|
self.callback = self.Callback_Cosy(on_data)
|
||||||
|
format =self.get_audio_format(self.format,self.sample_rate)
|
||||||
|
self.synthesizer = CosySpeechSynthesizer(
|
||||||
|
model='cosyvoice-v1',
|
||||||
|
# voice="longyuan", #"longfei",
|
||||||
|
voice=self.voice,
|
||||||
|
callback=self.callback,
|
||||||
|
format=format
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"---dale---30 error {e}") # cyx
|
||||||
|
# -----------------------------------
|
||||||
|
|
||||||
|
def streaming_call(self,text):
|
||||||
|
if self.synthesizer:
|
||||||
|
self.synthesizer.streaming_call(text)
|
||||||
|
def end_streaming_call(self):
|
||||||
|
if self.synthesizer:
|
||||||
|
self.synthesizer.streaming_complete()
|
||||||
|
|
||||||
|
def get_audio_format(self, format: str, sample_rate: int):
|
||||||
|
"""动态获取音频格式"""
|
||||||
|
from dashscope.audio.tts_v2 import AudioFormat
|
||||||
|
format_map = {
|
||||||
|
(8000, 'mp3'): AudioFormat.MP3_8000HZ_MONO_128KBPS,
|
||||||
|
(8000, 'pcm'): AudioFormat.PCM_8000HZ_MONO_16BIT,
|
||||||
|
(8000, 'wav'): AudioFormat.WAV_8000HZ_MONO_16BIT,
|
||||||
|
(16000, 'pcm'): AudioFormat.PCM_16000HZ_MONO_16BIT,
|
||||||
|
(22050, 'mp3'): AudioFormat.MP3_22050HZ_MONO_256KBPS,
|
||||||
|
(22050, 'pcm'): AudioFormat.PCM_22050HZ_MONO_16BIT,
|
||||||
|
(22050, 'wav'): AudioFormat.WAV_22050HZ_MONO_16BIT,
|
||||||
|
(44100, 'mp3'): AudioFormat.MP3_44100HZ_MONO_256KBPS,
|
||||||
|
(44100, 'pcm'): AudioFormat.PCM_44100HZ_MONO_16BIT,
|
||||||
|
(44100, 'wav'): AudioFormat.WAV_44100HZ_MONO_16BIT,
|
||||||
|
(48000, 'mp3'): AudioFormat.MP3_48000HZ_MONO_256KBPS,
|
||||||
|
(48000, 'pcm'): AudioFormat.PCM_48000HZ_MONO_16BIT,
|
||||||
|
(48000, 'wav'):AudioFormat.WAV_48000HZ_MONO_16BIT
|
||||||
|
|
||||||
|
}
|
||||||
|
return format_map.get((sample_rate, format), AudioFormat.MP3_16000HZ_MONO_128KBPS)
|
||||||
|
|
||||||
|
class StreamSessionManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.sessions = {} # {session_id: {'tts_model': obj, 'buffer': queue, 'task_queue': Queue}}
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.executor = ThreadPoolExecutor(max_workers=30) # 固定大小线程池
|
||||||
|
self.gc_interval = 300 # 5分钟清理一次 5 x 60 300秒
|
||||||
|
self.gc_tts = 10 # 10s 大模型开始输出文本有可能需要比较久,2025年5 24 从3s->10s
|
||||||
|
|
||||||
|
def create_session(self, tts_model,sample_rate =8000, stream_format='mp3',voice='cosyvoice-v1/longxiaochun'):
|
||||||
|
session_id = str(uuid.uuid4())
|
||||||
|
def on_audio_data(chunk):
|
||||||
|
session = self.sessions.get(session_id)
|
||||||
|
first_chunk = not session['tts_chunk_data_valid']
|
||||||
|
if session['stream_format'] == 'wav':
|
||||||
|
if first_chunk:
|
||||||
|
chunk_len = len(chunk)
|
||||||
|
if chunk_len > 2048:
|
||||||
|
session['buffer'].put(audio_fade_in(chunk, 1024))
|
||||||
|
else:
|
||||||
|
session['buffer'].put(audio_fade_in(chunk, chunk_len))
|
||||||
|
else:
|
||||||
|
session['buffer'].put(chunk)
|
||||||
|
else:
|
||||||
|
session['buffer'].put(chunk)
|
||||||
|
session['last_active'] = time.time()
|
||||||
|
session['audio_chunk_count'] = session['audio_chunk_count'] + 1
|
||||||
|
if session['tts_chunk_data_valid'] is False:
|
||||||
|
session['tts_chunk_data_valid'] = True # 20250510 增加,表示连接TTS后台已经返回,可以通知前端了
|
||||||
|
|
||||||
|
with self.lock:
|
||||||
|
ali_tts_model = QwenTTS(ALI_KEY,stream_format, sample_rate,voice.split('@')[0])
|
||||||
|
self.sessions[session_id] = {
|
||||||
|
'tts_model': ali_tts_model, #tts_model,
|
||||||
|
'buffer': queue.Queue(maxsize=300), # 线程安全队列
|
||||||
|
'task_queue': queue.Queue(),
|
||||||
|
'active': True,
|
||||||
|
'last_active': time.time(),
|
||||||
|
'audio_chunk_count':0,
|
||||||
|
'finished': threading.Event(), # 添加事件对象
|
||||||
|
'sample_rate':sample_rate,
|
||||||
|
'stream_format':stream_format,
|
||||||
|
"tts_chunk_data_valid":False,
|
||||||
|
'voice':voice,
|
||||||
|
}
|
||||||
|
self.sessions[session_id]['tts_model'].init_streaming_call(on_audio_data)
|
||||||
|
# 启动任务处理线程
|
||||||
|
threading.Thread(target=self._process_tasks, args=(session_id,), daemon=True).start()
|
||||||
|
return session_id
|
||||||
|
|
||||||
|
def append_text(self, session_id, text):
|
||||||
|
with self.lock:
|
||||||
|
session = self.sessions.get(session_id)
|
||||||
|
if not session: return
|
||||||
|
# 将文本放入任务队列(非阻塞)
|
||||||
|
#logging.info(f"StreamSessionManager append_text {text}")
|
||||||
|
try:
|
||||||
|
session['task_queue'].put(text, block=False)
|
||||||
|
except queue.Full:
|
||||||
|
logging.warning(f"Session {session_id} task queue full")
|
||||||
|
|
||||||
|
def _process_tasks(self, session_id):
|
||||||
|
"""任务处理线程(每个会话独立)"""
|
||||||
|
while True:
|
||||||
|
session = self.sessions.get(session_id)
|
||||||
|
if not session or not session['active']:
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
#logging.info(f"StreamSessionManager _process_tasks {session['task_queue'].qsize()}")
|
||||||
|
# 合并多个文本块(最多等待50ms)
|
||||||
|
texts = []
|
||||||
|
while len(texts) < 5: # 最大合并5个文本块
|
||||||
|
try:
|
||||||
|
text = session['task_queue'].get(timeout=0.1)
|
||||||
|
#logging.info(f"StreamSessionManager _process_tasks --0 {len(texts)}")
|
||||||
|
texts.append(text)
|
||||||
|
except queue.Empty:
|
||||||
|
break
|
||||||
|
|
||||||
|
if texts:
|
||||||
|
session['last_active'] = time.time() # 如果有处理文本,重置活跃时间
|
||||||
|
# 提交到线程池处理
|
||||||
|
#future=self.executor.submit(
|
||||||
|
# self._generate_audio,
|
||||||
|
# session_id,
|
||||||
|
# ' '.join(texts) # 合并文本减少请求次数
|
||||||
|
#)
|
||||||
|
#future.result() # 等待转换任务执行完毕
|
||||||
|
session['tts_model'].streaming_call(''.join(texts))
|
||||||
|
session['last_active'] = time.time()
|
||||||
|
# 会话超时检查
|
||||||
|
if time.time() - session['last_active'] > self.gc_interval:
|
||||||
|
self.close_session(session_id)
|
||||||
|
break
|
||||||
|
if time.time() - session['last_active'] > self.gc_tts:
|
||||||
|
session['tts_model'].end_streaming_call()
|
||||||
|
session['finished'].set()
|
||||||
|
break
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Task processing error: {str(e)}")
|
||||||
|
|
||||||
|
def _generate_audio(self, session_id, text):
|
||||||
|
"""实际生成音频(线程池执行)"""
|
||||||
|
session = self.sessions.get(session_id)
|
||||||
|
if not session: return
|
||||||
|
# logging.info(f"_generate_audio:{text}")
|
||||||
|
first_chunk = True
|
||||||
|
logging.info(f"转换开始!!! {text}")
|
||||||
|
try:
|
||||||
|
for chunk in session['tts_model'].tts(text,session['sample_rate'],session['stream_format']):
|
||||||
|
if session['stream_format'] == 'wav':
|
||||||
|
if first_chunk:
|
||||||
|
chunk_len = len(chunk)
|
||||||
|
if chunk_len > 2048:
|
||||||
|
session['buffer'].put(audio_fade_in(chunk,1024))
|
||||||
|
else:
|
||||||
|
session['buffer'].put(audio_fade_in(chunk, chunk_len))
|
||||||
|
first_chunk = False
|
||||||
|
else:
|
||||||
|
session['buffer'].put(chunk)
|
||||||
|
else:
|
||||||
|
session['buffer'].put(chunk)
|
||||||
|
session['last_active'] = time.time()
|
||||||
|
session['audio_chunk_count'] = session['audio_chunk_count'] + 1
|
||||||
|
if session['tts_chunk_data_valid'] is False:
|
||||||
|
session['tts_chunk_data_valid'] = True #20250510 增加,表示连接TTS后台已经返回,可以通知前端了
|
||||||
|
logging.info(f"转换结束!!! {session['audio_chunk_count'] }")
|
||||||
|
except Exception as e:
|
||||||
|
session['buffer'].put(f"ERROR:{str(e)}")
|
||||||
|
logging.info(f"--_generate_audio--error {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
def close_session(self, session_id):
|
||||||
|
with self.lock:
|
||||||
|
if session_id in self.sessions:
|
||||||
|
logging.info(f"--Session {session_id} close_session")
|
||||||
|
# 标记会话为不活跃
|
||||||
|
self.sessions[session_id]['active'] = False
|
||||||
|
# 延迟2秒后清理资源
|
||||||
|
threading.Timer(1, self._clean_session, args=[session_id]).start()
|
||||||
|
|
||||||
|
def _clean_session(self, session_id):
|
||||||
|
with self.lock:
|
||||||
|
if session_id in self.sessions:
|
||||||
|
del self.sessions[session_id]
|
||||||
|
|
||||||
|
def get_session(self, session_id):
|
||||||
|
return self.sessions.get(session_id)
|
||||||
|
|
||||||
|
|
||||||
|
stream_manager_w_stream = StreamSessionManager()
|
||||||
|
def audio_fade_in(audio_data, fade_length):
|
||||||
|
# 假设音频数据是16位单声道PCM
|
||||||
|
# 将二进制数据转换为整数数组
|
||||||
|
samples = array.array('h', audio_data)
|
||||||
|
|
||||||
|
# 对前fade_length个样本进行淡入处理
|
||||||
|
for i in range(fade_length):
|
||||||
|
fade_factor = i / fade_length
|
||||||
|
samples[i] = int(samples[i] * fade_factor)
|
||||||
|
|
||||||
|
# 将整数数组转换回二进制数据
|
||||||
|
return samples.tobytes()
|
||||||
@@ -46,7 +46,8 @@ class MesumAntiqueService(CommonService):
|
|||||||
if mesum_brief:
|
if mesum_brief:
|
||||||
categories_text= mesum_brief[0].category
|
categories_text= mesum_brief[0].category
|
||||||
# 统一替换中文分号为英文分号,并去除末尾分号
|
# 统一替换中文分号为英文分号,并去除末尾分号
|
||||||
categories_text = categories_text.replace(";", ";").rstrip(";")
|
if categories_text:
|
||||||
|
categories_text = categories_text.replace(";", ";").rstrip(";")
|
||||||
# 分割并清理空格/空值
|
# 分割并清理空格/空值
|
||||||
mesum_antique_categories = [dynasty.strip() for dynasty in categories_text.split(";") if dynasty.strip()]
|
mesum_antique_categories = [dynasty.strip() for dynasty in categories_text.split(";") if dynasty.strip()]
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ from api.utils.file_utils import get_project_base_directory
|
|||||||
from peewee import fn
|
from peewee import fn
|
||||||
import threading, queue,uuid,time,array
|
import threading, queue,uuid,time,array
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
from api.db.services.ali_tts_service import (stream_manager_w_stream as stream_manager)
|
||||||
|
|
||||||
def audio_fade_in(audio_data, fade_length):
|
def audio_fade_in(audio_data, fade_length):
|
||||||
# 假设音频数据是16位单声道PCM
|
# 假设音频数据是16位单声道PCM
|
||||||
@@ -173,8 +174,7 @@ class StreamSessionManager:
|
|||||||
def get_session(self, session_id):
|
def get_session(self, session_id):
|
||||||
return self.sessions.get(session_id)
|
return self.sessions.get(session_id)
|
||||||
|
|
||||||
|
stream_manager_bk = StreamSessionManager()
|
||||||
stream_manager = StreamSessionManager()
|
|
||||||
|
|
||||||
class DialogService(CommonService):
|
class DialogService(CommonService):
|
||||||
model = Dialog
|
model = Dialog
|
||||||
@@ -756,16 +756,20 @@ def chat(dialog, messages, stream=True, **kwargs):
|
|||||||
if stream:
|
if stream:
|
||||||
last_ans = ""
|
last_ans = ""
|
||||||
answer = ""
|
answer = ""
|
||||||
|
audio_url = None
|
||||||
|
if not kwargs.get('tts_disable'):
|
||||||
# 创建TTS会话(提前初始化)
|
# 创建TTS会话(提前初始化)
|
||||||
tts_session_id = stream_manager.create_session(tts_mdl,sample_rate=tts_sample_rate,stream_format=tts_stream_format)
|
tts_session_id = stream_manager.create_session(tts_mdl,sample_rate=tts_sample_rate,stream_format=tts_stream_format,
|
||||||
tts_session = stream_manager.get_session(tts_session_id)
|
voice = kwargs.get('tts_model'))
|
||||||
audio_url = f"/tts_stream/{tts_session_id}"
|
tts_session = stream_manager.get_session(tts_session_id)
|
||||||
|
audio_url = f"/tts_stream/{tts_session_id}"
|
||||||
send_tts_url = False
|
send_tts_url = False
|
||||||
chunk_buffer = [] # 新增文本缓冲
|
chunk_buffer = [] # 新增文本缓冲
|
||||||
last_flush_time = time.time() # 初始化时间戳
|
last_flush_time = time.time() # 初始化时间戳
|
||||||
# 下面优先处理知识库中没有找到相关内容 cyx 20250323 修改
|
# 下面优先处理知识库中没有找到相关内容 cyx 20250323 修改
|
||||||
if not kwargs["knowledge"] or kwargs["knowledge"] =="" or len(kwargs["knowledge"]) < 4:
|
if not kwargs["knowledge"] or kwargs["knowledge"] =="" or len(kwargs["knowledge"]) < 4:
|
||||||
stream_manager.append_text(tts_session_id, "未找到相关内容")
|
if not kwargs.get('tts_disable'):
|
||||||
|
stream_manager.append_text(tts_session_id, "未找到相关内容")
|
||||||
yield {
|
yield {
|
||||||
"answer": "未找到相关内容",
|
"answer": "未找到相关内容",
|
||||||
"delta_ans": "未找到相关内容",
|
"delta_ans": "未找到相关内容",
|
||||||
@@ -810,18 +814,19 @@ def chat(dialog, messages, stream=True, **kwargs):
|
|||||||
yield {"answer": answer, "delta_ans": sanitized_text, "reference": {}}
|
yield {"answer": answer, "delta_ans": sanitized_text, "reference": {}}
|
||||||
"""
|
"""
|
||||||
# 首块返回音频URL
|
# 首块返回音频URL
|
||||||
if send_tts_url is False and tts_session['tts_chunk_data_valid'] is True:
|
if send_tts_url is False and not kwargs.get('tts_disable'):
|
||||||
yield {
|
if tts_session['tts_chunk_data_valid'] is True:
|
||||||
"answer": answer,
|
yield {
|
||||||
"delta_ans": sanitized_text,
|
"answer": answer,
|
||||||
"session_id": tts_session_id,
|
"delta_ans": sanitized_text,
|
||||||
"reference": {},
|
"session_id": tts_session_id,
|
||||||
"audio_stream_url": audio_url,
|
"reference": {},
|
||||||
"sample_rate":tts_sample_rate,
|
"audio_stream_url": audio_url,
|
||||||
"stream_format":tts_stream_format,
|
"sample_rate":tts_sample_rate,
|
||||||
}
|
"stream_format":tts_stream_format,
|
||||||
send_tts_url = True # 发送一次tts url 给前端即可,不能重复发送
|
}
|
||||||
logging.info(f"--chat retur tts url {audio_url}")
|
send_tts_url = True # 发送一次tts url 给前端即可,不能重复发送
|
||||||
|
logging.info(f"--chat retur tts url {audio_url}")
|
||||||
else:
|
else:
|
||||||
yield {"answer": answer, "delta_ans": sanitized_text,"reference": {}}
|
yield {"answer": answer, "delta_ans": sanitized_text,"reference": {}}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
TIMEZONE=Asia/Shanghai
|
TIMEZONE=Asia/Shanghai
|
||||||
|
DASHSCOPE_API_KEY = sk-a47a3fb5f4a94f66bbaf713779101c75
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
467
asr-monitor-test/app.log
Normal file
467
asr-monitor-test/app.log
Normal file
@@ -0,0 +1,467 @@
|
|||||||
|
INFO: Started server process [50175]
|
||||||
|
INFO: Waiting for application startup.
|
||||||
|
21:51:05.972 - INFO - 监控服务已启动
|
||||||
|
INFO: Application startup complete.
|
||||||
|
INFO: Uvicorn running on http://0.0.0.0:9580 (Press CTRL+C to quit)
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
ASR & Monitor Service Start
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
INFO: 35.203.210.77:60796 - "GET / HTTP/1.1" 404 Not Found
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
INFO: 221.238.94.141:45448 - "GET / HTTP/1.0" 404 Not Found
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
INFO: 221.238.94.141:45526 - "OPTIONS / HTTP/1.0" 404 Not Found
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
INFO: 221.238.94.141:45640 - "GET /nice%20ports%2C/Trinity.txt.bak HTTP/1.0" 404 Not Found
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
INFO: 221.238.94.141:38084 - "GET /api HTTP/1.0" 404 Not Found
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
INFO: 221.238.94.141:38098 - "GET /hazelcast/rest/cluster HTTP/1.0" 404 Not Found
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
WARNING: Invalid HTTP request received.
|
||||||
|
INFO: 221.238.94.141:38374 - "GET / HTTP/1.1" 404 Not Found
|
||||||
|
06:57:54.681 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 106.55.206.109:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
06:57:55.071 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 43.144.107.210:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
06:59:18.037 - INFO - Creating TTS request: {'text': '陶鬲形猪首盖盉,商,通高27厘米,腹径20.5厘米,2010年安钢大道M78出土,中国社会科学院考古研究所藏。器型呈猪首状,长嘴,圆眼微凸,双耳直立,憨态可掬。肩部饰三角绳纹,腹及袋足饰细绳纹,作为温酒的容器,加热后美酒从它的口中流出。', 'session_id': '0b4cdbaeaf9111efa53df171065841e8', 'delay_gen_audio': True, 'tts_sample_rate': 48000, 'tts_stream_format': 'mp3', 'model_name': 'cosyvoice-v1/longyuan@Tongyi-Qianwen', 'sample_rate': 48000, 'stream_format': 'mp3'}
|
||||||
|
INFO: 43.144.107.28:0 - "POST /tts/chats/39e9a2ba5a4711f0865bbb55c66f9471/tts?device_id=17513727656058688719 HTTP/1.1" 200 OK
|
||||||
|
INFO: ('1.13.185.116', 50174) - "WebSocket /tts/chats/39e9a2ba5a4711f0865bbb55c66f9471/tts/d45fdb6d-9bcb-4458-9b83-d63f3eb69720" [accepted]
|
||||||
|
06:59:19.472 - INFO - 新连接建立: 414fa19a-4425-4b60-b172-ec661a3457b2
|
||||||
|
06:59:19.472 - INFO - 已经启动 start tts task {audio_stream_id}
|
||||||
|
06:59:19.473 - INFO - ---begin--init QwenTTS-- mp3 48000 cosyvoice-v1/longyuan@Tongyi-Qianwen cosyvoice-v1/longyuan
|
||||||
|
06:59:19.473 - INFO - QwenTTS--get_audio_format-- mp3 48000
|
||||||
|
06:59:19.473 - INFO - setup_tts longyuan MP3 with 48000Hz sample rate, mono channel, 256kbps
|
||||||
|
INFO: connection open
|
||||||
|
06:59:19.617 - INFO - Websocket connected
|
||||||
|
06:59:19.729 - INFO - Qwen CosyVoice tts open
|
||||||
|
06:59:28.170 - INFO - --data_handler on_complete
|
||||||
|
06:59:28.170 - INFO - Qwen CosyVoice tts close
|
||||||
|
06:59:28.170 - INFO - --tts task event set error = None
|
||||||
|
INFO: connection closed
|
||||||
|
07:00:54.600 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 43.144.107.210:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
07:00:54.946 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 43.140.60.44:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
07:04:53.633 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 43.144.107.28:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
07:04:53.793 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 43.140.60.44:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
07:04:57.090 - INFO - Creating TTS request: {'text': '1945年10月,通县民主政府成立,机关设在潮白河西岸侯各庄西北角的一座菩萨庙(圆通庵)内,领导全县人民进行反蒋解放斗争。', 'session_id': '0b4cdbaeaf9111efa53df171065841e8', 'delay_gen_audio': True, 'tts_sample_rate': 48000, 'tts_stream_format': 'mp3', 'model_name': 'cosyvoice-v1/longyuan@Tongyi-Qianwen', 'sample_rate': 48000, 'stream_format': 'mp3'}
|
||||||
|
INFO: 43.140.60.33:0 - "POST /tts/chats/9cb04ecead5111ef99e80242ac120006/tts?device_id=17513727656058688719 HTTP/1.1" 200 OK
|
||||||
|
INFO: ('1.13.185.116', 33684) - "WebSocket /tts/chats/9cb04ecead5111ef99e80242ac120006/tts/c83abcdf-225e-41a1-a20d-0cc228b7c547" [accepted]
|
||||||
|
07:04:57.520 - INFO - 新连接建立: 2025badf-3414-48c6-8382-76c51673d9ef
|
||||||
|
07:04:57.520 - INFO - 已经启动 start tts task {audio_stream_id}
|
||||||
|
07:04:57.520 - INFO - ---begin--init QwenTTS-- mp3 48000 cosyvoice-v1/longyuan@Tongyi-Qianwen cosyvoice-v1/longyuan
|
||||||
|
07:04:57.520 - INFO - QwenTTS--get_audio_format-- mp3 48000
|
||||||
|
07:04:57.520 - INFO - setup_tts longyuan MP3 with 48000Hz sample rate, mono channel, 256kbps
|
||||||
|
INFO: connection open
|
||||||
|
07:04:57.639 - INFO - Websocket connected
|
||||||
|
07:04:57.768 - INFO - Qwen CosyVoice tts open
|
||||||
|
07:05:01.717 - INFO - --data_handler on_complete
|
||||||
|
07:05:01.717 - INFO - Qwen CosyVoice tts close
|
||||||
|
07:05:01.717 - INFO - --tts task event set error = None
|
||||||
|
INFO: connection closed
|
||||||
|
07:06:17.189 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 43.140.60.44:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
07:06:17.383 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 43.140.60.33:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
07:06:20.644 - INFO - Creating TTS request: {'text': '元大都水利建设最终形成了金水河、高梁河两进,坝河、通惠河两出,瓮山泊、积水潭两大蓄水水面格局,水利景观呈现“一心、一廊”的发展特征。城内供水区以积水潭为核心,通惠河漕运走廊连接北运河与白浮泉,串联起自然山水、寺庙、集市、私园、公共桥闸等景观,充分体现了城市、水利、景观三者之间的相互作用。元代水利建设对北京城市水利景观和生态环境具有深远的影响。', 'session_id': '0b4cdbaeaf9111efa53df171065841e8', 'delay_gen_audio': True, 'tts_sample_rate': 48000, 'tts_stream_format': 'mp3', 'model_name': 'cosyvoice-v1/longyuan@Tongyi-Qianwen', 'sample_rate': 48000, 'stream_format': 'mp3'}
|
||||||
|
INFO: 43.140.60.33:0 - "POST /tts/chats/9cb04ecead5111ef99e80242ac120006/tts?device_id=17513727656058688719 HTTP/1.1" 200 OK
|
||||||
|
INFO: ('1.13.185.116', 47246) - "WebSocket /tts/chats/9cb04ecead5111ef99e80242ac120006/tts/7d2bd2dd-dfc0-43e6-9b23-08e5251bb954" [accepted]
|
||||||
|
07:06:21.077 - INFO - 新连接建立: caa0458b-7662-4ebe-9a6b-480c57c400b6
|
||||||
|
07:06:21.077 - INFO - 已经启动 start tts task {audio_stream_id}
|
||||||
|
07:06:21.077 - INFO - ---begin--init QwenTTS-- mp3 48000 cosyvoice-v1/longyuan@Tongyi-Qianwen cosyvoice-v1/longyuan
|
||||||
|
07:06:21.078 - INFO - QwenTTS--get_audio_format-- mp3 48000
|
||||||
|
07:06:21.078 - INFO - setup_tts longyuan MP3 with 48000Hz sample rate, mono channel, 256kbps
|
||||||
|
INFO: connection open
|
||||||
|
07:06:21.194 - INFO - Websocket connected
|
||||||
|
07:06:21.324 - INFO - Qwen CosyVoice tts open
|
||||||
|
07:06:32.422 - INFO - --data_handler on_complete
|
||||||
|
07:06:32.422 - INFO - Qwen CosyVoice tts close
|
||||||
|
07:06:32.422 - INFO - --tts task event set error = None
|
||||||
|
INFO: connection closed
|
||||||
|
07:44:06.202 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
07:44:06.290 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
07:44:52.287 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
07:44:52.440 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
07:45:23.056 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
07:45:23.159 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
07:54:21.106 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
07:54:21.161 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
07:54:28.720 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
07:54:28.799 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
07:54:35.913 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
07:54:36.001 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
07:54:36.411 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
07:54:36.509 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
07:55:22.522 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
07:55:22.582 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
07:56:58.308 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
07:56:58.385 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:07:38.414 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:07:38.499 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:10:57.429 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:10:57.486 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:11:53.340 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:11:53.398 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:16:33.756 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:16:33.813 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:17:30.384 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:17:30.490 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:18:33.644 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:18:33.709 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:19:50.951 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:19:51.034 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:21:37.950 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:21:38.023 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:22:05.386 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:22:05.441 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:23:13.075 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:23:13.153 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:24:49.462 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:24:49.563 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:26:08.181 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:26:08.282 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:26:49.301 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:26:49.374 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:27:41.833 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:27:41.893 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:28:50.023 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:28:50.083 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:29:21.179 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:29:21.387 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:30:19.839 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:30:20.031 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:31:11.560 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:31:11.723 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:33:16.896 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:33:17.108 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:33:51.413 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:33:51.479 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:35:10.447 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:35:10.505 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:36:24.091 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:36:24.187 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:37:18.392 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:37:18.507 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:38:42.906 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 106.55.206.109:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:38:43.148 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 43.140.60.33:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:49:31.793 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 106.55.206.109:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:49:31.991 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 43.140.60.33:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:51:30.837 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:51:30.938 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:55:28.363 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:55:28.421 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
08:56:53.353 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
08:56:53.419 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
09:00:01.531 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
09:00:01.590 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
09:00:07.397 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
09:00:07.486 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
09:00:47.232 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
09:00:47.306 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
09:01:19.694 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
09:01:19.959 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
09:02:46.650 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
09:02:46.718 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
09:03:29.115 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
09:03:29.384 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
09:05:12.089 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
09:05:12.145 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
09:10:24.333 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
09:10:24.586 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
09:11:14.693 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
09:11:14.781 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
09:11:45.666 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
09:11:45.755 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
09:12:29.573 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
09:12:29.813 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
10:36:24.208 - INFO - verify_token user={'user_id': '689cac0d-062e-45df-88bf-d7c4b16ff226', 'openid': 'obKSz7bHxzDFWnLHmOrnY_-8D6fI', 'phone': '13810887276', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2ODljYWMwZC0wNjJlLTQ1ZGYtODhiZi1kN2M0YjE2ZmYyMjYiLCJleHAiOjE3NTIwMzM3NzV9.Go3vld8ijajTsYSwzA45YS47O9thXdnLtrF0G7x1BAM', 'balance': 0, 'status': 1, 'last_login_time': 1751428975, 'create_time': 1748951630, 'create_date': datetime.datetime(2025, 6, 3, 19, 53, 50), 'update_time': 1751428975, 'update_date': datetime.datetime(2025, 7, 2, 12, 2, 55)}
|
||||||
|
INFO: 223.104.41.17:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
10:36:24.285 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 223.104.41.17:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
10:36:34.664 - INFO - verify_token user={'user_id': '689cac0d-062e-45df-88bf-d7c4b16ff226', 'openid': 'obKSz7bHxzDFWnLHmOrnY_-8D6fI', 'phone': '13810887276', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2ODljYWMwZC0wNjJlLTQ1ZGYtODhiZi1kN2M0YjE2ZmYyMjYiLCJleHAiOjE3NTIwMzM3NzV9.Go3vld8ijajTsYSwzA45YS47O9thXdnLtrF0G7x1BAM', 'balance': 0, 'status': 1, 'last_login_time': 1751428975, 'create_time': 1748951630, 'create_date': datetime.datetime(2025, 6, 3, 19, 53, 50), 'update_time': 1751428975, 'update_date': datetime.datetime(2025, 7, 2, 12, 2, 55)}
|
||||||
|
INFO: 223.104.41.17:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
10:36:34.746 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 223.104.41.17:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
10:36:38.066 - INFO - Creating TTS request: {'text': '铜条形驭马器,商,长18.3厘米,宽1.4厘米,高2.2厘米,2021年邵家棚M109出土,安阳市文物考古研究院藏。\n', 'session_id': '0b4cdbaeaf9111efa53df171065841e8', 'delay_gen_audio': True, 'tts_sample_rate': 48000, 'tts_stream_format': 'mp3', 'model_name': 'cosyvoice-v1/longyuan@Tongyi-Qianwen', 'sample_rate': 48000, 'stream_format': 'mp3'}
|
||||||
|
INFO: 223.104.41.17:0 - "POST /tts/chats/39e9a2ba5a4711f0865bbb55c66f9471/tts?device_id=17507669394291632844 HTTP/1.1" 200 OK
|
||||||
|
INFO: ('1.13.185.116', 36130) - "WebSocket /tts/chats/39e9a2ba5a4711f0865bbb55c66f9471/tts/de035ae4-81d8-408b-b551-2eac17b07e50" [accepted]
|
||||||
|
10:36:38.312 - INFO - 新连接建立: 733148f7-d2ac-4fa2-ad3a-b41fb684d08e
|
||||||
|
10:36:38.312 - INFO - 已经启动 start tts task {audio_stream_id}
|
||||||
|
INFO: connection open
|
||||||
|
10:36:38.313 - INFO - ---begin--init QwenTTS-- mp3 48000 cosyvoice-v1/longyuan@Tongyi-Qianwen cosyvoice-v1/longyuan
|
||||||
|
10:36:38.313 - INFO - QwenTTS--get_audio_format-- mp3 48000
|
||||||
|
10:36:38.313 - INFO - setup_tts longyuan MP3 with 48000Hz sample rate, mono channel, 256kbps
|
||||||
|
10:36:38.425 - INFO - Websocket connected
|
||||||
|
10:36:38.560 - INFO - Qwen CosyVoice tts open
|
||||||
|
10:36:42.366 - INFO - --data_handler on_complete
|
||||||
|
10:36:42.366 - INFO - Qwen CosyVoice tts close
|
||||||
|
10:36:42.366 - INFO - --tts task event set error = None
|
||||||
|
INFO: connection closed
|
||||||
|
10:36:49.363 - INFO - verify_token user={'user_id': '689cac0d-062e-45df-88bf-d7c4b16ff226', 'openid': 'obKSz7bHxzDFWnLHmOrnY_-8D6fI', 'phone': '13810887276', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2ODljYWMwZC0wNjJlLTQ1ZGYtODhiZi1kN2M0YjE2ZmYyMjYiLCJleHAiOjE3NTIwMzM3NzV9.Go3vld8ijajTsYSwzA45YS47O9thXdnLtrF0G7x1BAM', 'balance': 0, 'status': 1, 'last_login_time': 1751428975, 'create_time': 1748951630, 'create_date': datetime.datetime(2025, 6, 3, 19, 53, 50), 'update_time': 1751428975, 'update_date': datetime.datetime(2025, 7, 2, 12, 2, 55)}
|
||||||
|
INFO: 223.104.41.17:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
10:36:49.440 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 223.104.41.17:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
10:36:53.851 - INFO - Creating TTS request: {'text': '铜铙,商,①高18厘米,口长13.2厘米,口宽10.1厘米;②高13厘米,口长11.6厘米,口宽8.7厘米;③高11.9厘米,口长9.4厘米,口宽7厘米;1983年戚家庄东地出土,安阳市文物考古研究院藏。\n', 'session_id': '0b4cdbaeaf9111efa53df171065841e8', 'delay_gen_audio': True, 'tts_sample_rate': 48000, 'tts_stream_format': 'mp3', 'model_name': 'cosyvoice-v1/longyuan@Tongyi-Qianwen', 'sample_rate': 48000, 'stream_format': 'mp3'}
|
||||||
|
INFO: 223.104.41.17:0 - "POST /tts/chats/39e9a2ba5a4711f0865bbb55c66f9471/tts?device_id=17507669394291632844 HTTP/1.1" 200 OK
|
||||||
|
INFO: ('1.13.185.116', 49668) - "WebSocket /tts/chats/39e9a2ba5a4711f0865bbb55c66f9471/tts/c6355d58-9d94-4726-bf00-0581bcdb0e28" [accepted]
|
||||||
|
10:36:54.094 - INFO - 新连接建立: fc397484-625d-4db9-9fba-6d5a8949a57a
|
||||||
|
10:36:54.094 - INFO - 已经启动 start tts task {audio_stream_id}
|
||||||
|
10:36:54.094 - INFO - ---begin--init QwenTTS-- mp3 48000 cosyvoice-v1/longyuan@Tongyi-Qianwen cosyvoice-v1/longyuan
|
||||||
|
10:36:54.094 - INFO - QwenTTS--get_audio_format-- mp3 48000
|
||||||
|
10:36:54.094 - INFO - setup_tts longyuan MP3 with 48000Hz sample rate, mono channel, 256kbps
|
||||||
|
INFO: connection open
|
||||||
|
10:36:54.202 - INFO - Websocket connected
|
||||||
|
10:36:54.341 - INFO - Qwen CosyVoice tts open
|
||||||
|
10:37:05.913 - INFO - --data_handler on_complete
|
||||||
|
10:37:05.914 - INFO - Qwen CosyVoice tts close
|
||||||
|
10:37:05.914 - INFO - --tts task event set error = None
|
||||||
|
INFO: connection closed
|
||||||
|
INFO: ('1.13.185.116', 59460) - "WebSocket /tts/chats/39e9a2ba5a4711f0865bbb55c66f9471/tts/9d5e77cc-ad9b-4a60-812f-880d2f182543" [accepted]
|
||||||
|
10:37:23.067 - INFO - 新连接建立: d3f6a319-1597-4283-8330-a26c2582c91c
|
||||||
|
INFO: connection open
|
||||||
|
10:37:23.121 - INFO - 代理文本流: completions_url=http://localhost:9380/api/v1/chats/39e9a2ba5a4711f0865bbb55c66f9471/completions {'question': '请介绍铜铙', 'stream': True, 'tts_model': 'cosyvoice-v1/longyuan@Tongyi-Qianwen', 'tts_sample_rate': 8000, 'tts_stream_format': 'mp3', 'tts_disable': True}
|
||||||
|
10:37:23.121 - INFO - ---begin--init QwenTTS-- mp3 8000 cosyvoice-v1/longyuan@Tongyi-Qianwen cosyvoice-v1/longyuan
|
||||||
|
10:37:23.121 - INFO - QwenTTS--get_audio_format-- mp3 8000
|
||||||
|
10:37:23.121 - INFO - setup_tts longyuan MP3 with 8000Hz sample rate, mono channel, 128kbps
|
||||||
|
10:37:26.511 - INFO - HTTP Request: POST http://localhost:9380/api/v1/chats/39e9a2ba5a4711f0865bbb55c66f9471/completions "HTTP/1.1 200 OK"
|
||||||
|
10:37:26.511 - INFO - 响应状态: HTTP 200
|
||||||
|
10:37:26.511 - INFO - 开始处理SSE流
|
||||||
|
10:37:26.651 - INFO - Websocket connected
|
||||||
|
10:37:26.775 - INFO - Qwen CosyVoice tts open
|
||||||
|
INFO: ('1.13.185.116', 49122) - "WebSocket /tts/chats/39e9a2ba5a4711f0865bbb55c66f9471/tts/64880966-3b94-4fae-ac7e-e37e16768c2a" [accepted]
|
||||||
|
10:37:26.927 - INFO - 新连接建立: f666fc5e-48bd-4451-ac13-65c656784d3f
|
||||||
|
INFO: connection open
|
||||||
|
10:37:39.969 - INFO - SSE流处理完成,事件数: 23
|
||||||
|
INFO: connection closed
|
||||||
|
INFO: connection closed
|
||||||
|
10:37:45.801 - INFO - 发送时检测到断开连接: f666fc5e-48bd-4451-ac13-65c656784d3f,
|
||||||
|
10:38:00.989 - ERROR - error from callback <bound method SpeechSynthesizer.on_message of <dashscope.audio.tts_v2.speech_synthesizer.SpeechSynthesizer object at 0x7f25f18a86a0>>: {"header":{"task_id":"8ed5859fd51746068babf1776c7c84df","event":"task-failed","error_code":"InvalidParameter","error_message":"request timeout after 23 seconds.","attributes":{}},"payload":{}}
|
||||||
|
10:38:00.989 - ERROR - error from callback <bound method SpeechSynthesizer.on_error of <dashscope.audio.tts_v2.speech_synthesizer.SpeechSynthesizer object at 0x7f25f18a86a0>>: websocket closed due to websocket closed due to {"header":{"task_id":"8ed5859fd51746068babf1776c7c84df","event":"task-failed","error_code":"InvalidParameter","error_message":"request timeout after 23 seconds.","attributes":{}},"payload":{}}
|
||||||
|
10:38:00.989 - INFO - tearing down on exception websocket closed due to websocket closed due to websocket closed due to {"header":{"task_id":"8ed5859fd51746068babf1776c7c84df","event":"task-failed","error_code":"InvalidParameter","error_message":"request timeout after 23 seconds.","attributes":{}},"payload":{}}
|
||||||
|
10:38:00.989 - ERROR - close status: 1007
|
||||||
|
Qwen tts error {"header":{"task_id":"8ed5859fd51746068babf1776c7c84df","event":"task-failed","error_code":"InvalidParameter","error_message":"request timeout after 23 seconds.","attributes":{}},"payload":{}}
|
||||||
|
websocket closed due to {"header":{"task_id":"8ed5859fd51746068babf1776c7c84df","event":"task-failed","error_code":"InvalidParameter","error_message":"request timeout after 23 seconds.","attributes":{}},"payload":{}}
|
||||||
|
websocket closed due to websocket closed due to {"header":{"task_id":"8ed5859fd51746068babf1776c7c84df","event":"task-failed","error_code":"InvalidParameter","error_message":"request timeout after 23 seconds.","attributes":{}},"payload":{}}
|
||||||
|
websocket closed due to websocket closed due to websocket closed due to {"header":{"task_id":"8ed5859fd51746068babf1776c7c84df","event":"task-failed","error_code":"InvalidParameter","error_message":"request timeout after 23 seconds.","attributes":{}},"payload":{}}
|
||||||
|
INFO: 183.163.60.214:0 - "GET /auth/get_museum_list HTTP/1.1" 401 Unauthorized
|
||||||
|
Exception in thread Thread-1544 (_process_tasks):
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
|
||||||
|
self.run()
|
||||||
|
File "/usr/lib/python3.10/threading.py", line 953, in run
|
||||||
|
self._target(*self._args, **self._kwargs)
|
||||||
|
File "/home/ubuntu/ragflow/asr-monitor-test/app/tts_service.py", line 192, in _process_tasks
|
||||||
|
session['tts_model'].end_streaming_call()
|
||||||
|
File "/home/ubuntu/ragflow/asr-monitor-test/app/tts_service.py", line 1005, in end_streaming_call
|
||||||
|
self.synthesizer.streaming_complete()
|
||||||
|
File "/home/ubuntu/ragflow/asr-monitor/venv/lib/python3.10/site-packages/dashscope/audio/tts_v2/speech_synthesizer.py", line 374, in streaming_complete
|
||||||
|
self.__send_str(request)
|
||||||
|
File "/home/ubuntu/ragflow/asr-monitor/venv/lib/python3.10/site-packages/dashscope/audio/tts_v2/speech_synthesizer.py", line 285, in __send_str
|
||||||
|
self.ws.send(data)
|
||||||
|
File "/home/ubuntu/ragflow/asr-monitor/venv/lib/python3.10/site-packages/websocket/_app.py", line 291, in send
|
||||||
|
raise WebSocketConnectionClosedException("Connection is already closed.")
|
||||||
|
websocket._exceptions.WebSocketConnectionClosedException: Connection is already closed.
|
||||||
|
INFO: 183.163.60.214:0 - "GET /auth/get_museum_list HTTP/1.1" 401 Unauthorized
|
||||||
|
INFO: 123.115.202.182:0 - "GET /auth/get_museum_list HTTP/1.1" 401 Unauthorized
|
||||||
|
11:23:13.037 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:23:13.104 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:25:03.125 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:25:03.187 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:26:37.995 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:26:38.077 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:27:45.347 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:27:45.695 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:28:25.996 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 61.141.77.65:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:28:26.062 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 61.141.77.65:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:29:40.340 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:29:40.639 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:30:15.537 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 61.141.77.65:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:30:15.603 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 61.141.77.65:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:30:52.210 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 61.141.77.65:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:30:52.288 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 61.141.77.65:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:35:02.399 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:35:02.737 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:36:02.938 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:36:03.034 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:37:17.719 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:37:17.802 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:37:47.273 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 61.141.77.65:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:37:47.367 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 61.141.77.65:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:39:13.999 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:39:14.337 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:41:51.134 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:41:51.495 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:43:01.977 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:43:02.050 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 163.125.202.178:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:44:30.162 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 43.144.107.210:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:44:30.345 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 43.140.60.44:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
|
11:45:40.101 - INFO - verify_token user={'user_id': '76538cf0-a6cf-4aa8-8440-382dd2330384', 'openid': 'obKSz7V6a-avAF-vtQrnk_rnuSGE', 'phone': '18676776176', 'email': None, 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3NjUzOGNmMC1hNmNmLTRhYTgtODQ0MC0zODJkZDIzMzAzODQiLCJleHAiOjE3NTI1ODI1ODN9.GONQPAzcWnYQN2i14zfNhjU8OT1to7fWn6ZA6lNdSHY', 'balance': 0, 'status': 1, 'last_login_time': 1751977783, 'create_time': 1748960538, 'create_date': datetime.datetime(2025, 6, 3, 22, 22, 18), 'update_time': 1751977783, 'update_date': datetime.datetime(2025, 7, 8, 20, 29, 43)}
|
||||||
|
INFO: 43.144.107.210:0 - "GET /auth/verify HTTP/1.1" 200 OK
|
||||||
|
11:45:40.260 - INFO - get_museum_id_auth=[1, 2, 3]
|
||||||
|
INFO: 43.140.60.44:0 - "GET /auth/get_museum_id_auth HTTP/1.1" 200 OK
|
||||||
148
asr-monitor-test/app/chat_service.py
Normal file
148
asr-monitor-test/app/chat_service.py
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
import array
|
||||||
|
import asyncio
|
||||||
|
import base64
|
||||||
|
import binascii
|
||||||
|
import datetime
|
||||||
|
import gzip
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import queue
|
||||||
|
import re
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
from collections import deque
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
from copy import deepcopy
|
||||||
|
from datetime import timedelta
|
||||||
|
from io import BytesIO
|
||||||
|
from threading import Lock, Thread
|
||||||
|
from timeit import default_timer as timer
|
||||||
|
from typing import Optional, Dict, Any
|
||||||
|
|
||||||
|
from fastapi import FastAPI, UploadFile, File, Form, Header
|
||||||
|
from fastapi import WebSocket, APIRouter, WebSocketDisconnect, Request, Body, Query, Depends
|
||||||
|
from fastapi.responses import StreamingResponse, JSONResponse, Response
|
||||||
|
from openai import OpenAI
|
||||||
|
chat_router = APIRouter()
|
||||||
|
|
||||||
|
# 从环境变量读取 OpenAI API 密钥
|
||||||
|
openai_api_key = os.getenv("DASHSCOPE_API_KEY")
|
||||||
|
if not openai_api_key:
|
||||||
|
raise RuntimeError("DASHSCOPE_API_KEY environment variable not set")
|
||||||
|
|
||||||
|
class MillisecondsFormatter(logging.Formatter):
|
||||||
|
"""自定义日志格式器,添加毫秒时间戳"""
|
||||||
|
def formatTime(self, record, datefmt=None):
|
||||||
|
# 将时间戳转换为本地时间元组
|
||||||
|
ct = self.converter(record.created)
|
||||||
|
# 格式化为 "小时:分钟:秒"
|
||||||
|
t = time.strftime("%H:%M:%S", ct)
|
||||||
|
# 添加毫秒(3位)
|
||||||
|
return f"{t}.{int(record.msecs):03d}"
|
||||||
|
|
||||||
|
# 配置全局日志格式
|
||||||
|
def configure_logging():
|
||||||
|
# 创建 Formatter
|
||||||
|
log_format = "%(asctime)s - %(levelname)s - %(message)s"
|
||||||
|
formatter = MillisecondsFormatter(log_format)
|
||||||
|
|
||||||
|
# 获取根 Logger 并清除已有配置
|
||||||
|
root_logger = logging.getLogger()
|
||||||
|
root_logger.handlers = []
|
||||||
|
|
||||||
|
# 创建并配置 Handler(输出到控制台)
|
||||||
|
console_handler = logging.StreamHandler()
|
||||||
|
console_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# 设置日志级别并添加 Handler
|
||||||
|
root_logger.setLevel(logging.INFO)
|
||||||
|
root_logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
# 调用配置函数(程序启动时运行一次)
|
||||||
|
configure_logging()
|
||||||
|
|
||||||
|
# 简单的 API 密钥验证(可选)
|
||||||
|
def verify_api_key(request: Request):
|
||||||
|
"""简单的 API 密钥验证"""
|
||||||
|
if request.headers.get("X-API-KEY") != os.getenv("APP_API_KEY", "default_key"):
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid API key")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@chat_router.post("/completion")
|
||||||
|
async def chat_completion(request: Request):
|
||||||
|
"""
|
||||||
|
与大语言模型进行对话
|
||||||
|
|
||||||
|
请求体示例:
|
||||||
|
{
|
||||||
|
"messages": [
|
||||||
|
{"role": "system", "content": "你是一个有帮助的助手"},
|
||||||
|
{"role": "user", "content": "你好,请介绍一下FastAPI"}
|
||||||
|
],
|
||||||
|
"model": "gpt-3.5-turbo",
|
||||||
|
"temperature": 0.7,
|
||||||
|
"max_tokens": 500
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 手动解析请求体
|
||||||
|
request_data = await request.json()
|
||||||
|
|
||||||
|
# 验证必需字段
|
||||||
|
if "messages" not in request_data or not isinstance(request_data["messages"], list):
|
||||||
|
raise HTTPException(status_code=400, detail="Missing or invalid 'messages' field")
|
||||||
|
|
||||||
|
# 提取参数并提供默认值
|
||||||
|
messages = request_data["messages"]
|
||||||
|
model = request_data.get("model", "qwen-plus-latest")
|
||||||
|
temperature = float(request_data.get("temperature", 0.7))
|
||||||
|
max_tokens = int(request_data.get("max_tokens", 500))
|
||||||
|
|
||||||
|
# 验证消息结构
|
||||||
|
for msg in messages:
|
||||||
|
if "role" not in msg or "content" not in msg:
|
||||||
|
raise HTTPException(status_code=400, detail="Invalid message structure")
|
||||||
|
if not isinstance(msg["role"], str) or not isinstance(msg["content"], str):
|
||||||
|
raise HTTPException(status_code=400, detail="Message content must be strings")
|
||||||
|
|
||||||
|
logging.info(f"Received chat request: model={model}, messages={len(messages)}")
|
||||||
|
client = OpenAI(
|
||||||
|
# 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx",
|
||||||
|
api_key=os.getenv("DASHSCOPE_API_KEY"),
|
||||||
|
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||||
|
)
|
||||||
|
|
||||||
|
# 调用 OpenAI API
|
||||||
|
response = client.chat.completions.create(
|
||||||
|
model=model,
|
||||||
|
messages=messages,
|
||||||
|
temperature=temperature
|
||||||
|
)
|
||||||
|
|
||||||
|
# 处理响应
|
||||||
|
if not response.choices:
|
||||||
|
raise HTTPException(status_code=500, detail="No response from AI model")
|
||||||
|
|
||||||
|
choice = response.choices[0]
|
||||||
|
message = choice.message
|
||||||
|
|
||||||
|
return {
|
||||||
|
"message": {
|
||||||
|
"role": message.role,
|
||||||
|
"content": message.content
|
||||||
|
},
|
||||||
|
"finish_reason": choice.finish_reason
|
||||||
|
}
|
||||||
|
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
raise HTTPException(status_code=400, detail="Invalid JSON format")
|
||||||
|
except ValueError as e:
|
||||||
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("Internal server error")
|
||||||
|
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
|
||||||
@@ -5,9 +5,9 @@ from contextlib import contextmanager
|
|||||||
from config import DATABASE_CONFIG
|
from config import DATABASE_CONFIG
|
||||||
from datetime import datetime,timedelta
|
from datetime import datetime,timedelta
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional,Union
|
from typing import Union, List, Dict, Optional
|
||||||
from tenacity import retry, stop_after_attempt, wait_fixed, retry_if_exception_type
|
from tenacity import retry, stop_after_attempt, wait_fixed, retry_if_exception_type
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
@@ -389,7 +389,7 @@ def create_user(data: dict):
|
|||||||
|
|
||||||
|
|
||||||
# 查询用户(多条件)
|
# 查询用户(多条件)
|
||||||
def get_users(status: int = None, email: str = None, phone: str = None):
|
def get_users(status: int = None, email: str = None, phone: str = None,openid: str = None):
|
||||||
base_sql = "SELECT * FROM rag_flow.users_info WHERE 1=1"
|
base_sql = "SELECT * FROM rag_flow.users_info WHERE 1=1"
|
||||||
params = []
|
params = []
|
||||||
|
|
||||||
@@ -404,7 +404,10 @@ def get_users(status: int = None, email: str = None, phone: str = None):
|
|||||||
if phone:
|
if phone:
|
||||||
base_sql += " AND phone = %s"
|
base_sql += " AND phone = %s"
|
||||||
params.append(phone)
|
params.append(phone)
|
||||||
|
|
||||||
|
if openid:
|
||||||
|
base_sql += " AND openid = %s"
|
||||||
|
params.append(openid)
|
||||||
base_sql += " ORDER BY create_time DESC"
|
base_sql += " ORDER BY create_time DESC"
|
||||||
|
|
||||||
return execute_query(base_sql, tuple(params))
|
return execute_query(base_sql, tuple(params))
|
||||||
@@ -531,4 +534,672 @@ def update_login_info(user_id: str, token: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
execute_query(sql, params)
|
execute_query(sql, params)
|
||||||
return get_user_by_id(user_id)
|
return get_user_by_id(user_id)
|
||||||
|
|
||||||
|
|
||||||
|
# database.py
|
||||||
|
#------------------------------------------------------------
|
||||||
|
def get_museum_subscriptions_by_museum(museum_id: int) -> list:
|
||||||
|
"""
|
||||||
|
获取指定博物馆的所有有效订阅套餐
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 查询指定博物馆的所有可用订阅套餐
|
||||||
|
- 返回结果包含博物馆订阅信息和关联的模板信息
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- museum_id: 博物馆ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 包含订阅信息的字典列表
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 只返回 is_active=1 的有效订阅
|
||||||
|
- 通过 JOIN 关联 subscription_templates 表获取模板信息
|
||||||
|
- 结果按有效期类型排序,便于前端展示
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
SELECT
|
||||||
|
ms.id,
|
||||||
|
ms.museum_id,
|
||||||
|
ms.template_id,
|
||||||
|
ms.price,
|
||||||
|
ms.sub_id,
|
||||||
|
ms.is_active,
|
||||||
|
ms.created_date,
|
||||||
|
ms.updated_date,
|
||||||
|
st.name AS template_name,
|
||||||
|
st.description AS template_description,
|
||||||
|
st.validity_type
|
||||||
|
FROM museum_subscriptions ms
|
||||||
|
JOIN subscription_templates st ON ms.template_id = st.id
|
||||||
|
WHERE ms.museum_id = %s AND ms.is_active = 1
|
||||||
|
ORDER BY st.validity_type
|
||||||
|
"""
|
||||||
|
return execute_query(sql, (museum_id,))
|
||||||
|
|
||||||
|
|
||||||
|
def get_museum_subscription_by_id(subscription_id: str) -> dict:
|
||||||
|
"""
|
||||||
|
根据ID获取博物馆订阅套餐的详细信息
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 通过订阅ID获取完整的订阅信息
|
||||||
|
- 包含关联的模板信息
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- subscription_id: 博物馆订阅ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 订阅信息的字典,如果不存在则返回None
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 使用内连接获取模板信息
|
||||||
|
- 确保返回完整的订阅+模板数据
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
SELECT
|
||||||
|
ms.*,
|
||||||
|
st.name AS template_name,
|
||||||
|
st.description AS template_description,
|
||||||
|
st.validity_type
|
||||||
|
FROM museum_subscriptions ms
|
||||||
|
JOIN subscription_templates st ON ms.template_id = st.id
|
||||||
|
WHERE ms.sub_id = %s
|
||||||
|
"""
|
||||||
|
result = execute_query(sql, (subscription_id,))
|
||||||
|
return result[0] if result else None
|
||||||
|
|
||||||
|
|
||||||
|
def create_order(order_data: dict) -> int:
|
||||||
|
"""
|
||||||
|
创建新的订阅订单
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 在 subscription_orders 表中插入新订单记录
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- order_data: 包含订单数据的字典,字段包括:
|
||||||
|
order_id: 订单号 (必需)
|
||||||
|
user_id: 用户ID (必需)
|
||||||
|
museum_subscription_id: 博物馆订阅ID (必需)
|
||||||
|
amount: 订单金额 (默认0.00)
|
||||||
|
status: 订单状态 (默认'created')
|
||||||
|
transaction_id: 支付交易号 (可选)
|
||||||
|
create_date: 创建时间 (默认当前时间)
|
||||||
|
pay_time: 支付时间 (可选)
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 执行结果的行数
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 为可选字段提供默认值
|
||||||
|
- 使用参数化查询防止SQL注入
|
||||||
|
- 处理所有必需的订单字段
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
INSERT INTO subscription_orders (
|
||||||
|
order_id,
|
||||||
|
user_id,
|
||||||
|
museum_subscription_id,
|
||||||
|
amount,
|
||||||
|
status,
|
||||||
|
transaction_id,
|
||||||
|
create_date,
|
||||||
|
pay_time
|
||||||
|
) VALUES (
|
||||||
|
%s, %s, %s, %s, %s, %s, %s, %s
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
# 设置默认值
|
||||||
|
params = (
|
||||||
|
order_data.get("order_id"),
|
||||||
|
order_data.get("user_id"),
|
||||||
|
order_data.get("museum_subscription_id"),
|
||||||
|
order_data.get("amount", 0.00),
|
||||||
|
order_data.get("status", "created"),
|
||||||
|
order_data.get("transaction_id"),
|
||||||
|
order_data.get("create_date", datetime.now()),
|
||||||
|
order_data.get("pay_time")
|
||||||
|
)
|
||||||
|
return execute_query(sql, params, autocommit=True)
|
||||||
|
|
||||||
|
|
||||||
|
def update_order(order_id: str, update_data: dict) -> int:
|
||||||
|
"""
|
||||||
|
更新订单信息
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 动态更新订单的字段
|
||||||
|
- 只更新提供的字段
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- order_id: 要更新的订单ID
|
||||||
|
- update_data: 包含更新字段的字典,可选字段包括:
|
||||||
|
status: 订单状态
|
||||||
|
transaction_id: 支付交易号
|
||||||
|
amount: 订单金额
|
||||||
|
pay_time: 支付时间
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 受影响的行数
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 只允许更新预定义的字段
|
||||||
|
- 防止更新不允许的字段
|
||||||
|
- 使用参数化查询确保安全
|
||||||
|
"""
|
||||||
|
# 定义允许更新的字段及其SQL部分
|
||||||
|
allowed_fields = {
|
||||||
|
'status': 'status = %s',
|
||||||
|
'transaction_id': 'transaction_id = %s',
|
||||||
|
'amount': 'amount = %s',
|
||||||
|
'pay_time': 'pay_time = %s'
|
||||||
|
}
|
||||||
|
|
||||||
|
update_fields = []
|
||||||
|
params = []
|
||||||
|
|
||||||
|
# 收集要更新的字段
|
||||||
|
for field, sql_part in allowed_fields.items():
|
||||||
|
if field in update_data:
|
||||||
|
update_fields.append(sql_part)
|
||||||
|
params.append(update_data[field])
|
||||||
|
|
||||||
|
# 如果没有提供有效更新字段,直接返回
|
||||||
|
if not update_fields:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# 构建动态SQL
|
||||||
|
set_clause = ", ".join(update_fields)
|
||||||
|
sql = f"UPDATE subscription_orders SET {set_clause} WHERE order_id = %s"
|
||||||
|
params.append(order_id)
|
||||||
|
|
||||||
|
return execute_query(sql, tuple(params), autocommit=True)
|
||||||
|
|
||||||
|
|
||||||
|
from typing import Union, List, Dict, Optional
|
||||||
|
|
||||||
|
def get_order_by_id(order_id: str = None, user_id: str = None,combined = None) -> Union[Dict, List[Dict], None]:
|
||||||
|
"""
|
||||||
|
根据订单ID或用户ID查询订单信息
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 支持通过 order_id 或 user_id 查询订单信息
|
||||||
|
- 当传入 order_id 时,返回单个订单(字典)
|
||||||
|
- 当传入 user_id 时,返回该用户的所有订单(列表)
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- order_id: 订单号(字符串)
|
||||||
|
- user_id: 用户ID(字符串)
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 如果传入 order_id: 返回单个订单的字典(如果存在)
|
||||||
|
- 如果传入 user_id: 返回该用户的所有订单列表(可能为空)
|
||||||
|
- 如果两个参数都未传入,返回 None
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 使用参数化查询防止 SQL 注入
|
||||||
|
- 当同时传入 order_id 和 user_id 时,优先使用 order_id
|
||||||
|
"""
|
||||||
|
if not order_id and not user_id:
|
||||||
|
return None # 两个参数都未传入,直接返回 None
|
||||||
|
sql_wo_condition= """
|
||||||
|
SELECT
|
||||||
|
o.order_id,
|
||||||
|
o.user_id,
|
||||||
|
u.phone,
|
||||||
|
u.openid,
|
||||||
|
o.museum_subscription_id AS subscription_id,
|
||||||
|
ms.museum_id,
|
||||||
|
ms.template_id,
|
||||||
|
t.name AS template_name,
|
||||||
|
t.description AS template_desc,
|
||||||
|
t.validity_type,
|
||||||
|
ms.price AS subscription_price,
|
||||||
|
o.amount AS order_amount,
|
||||||
|
o.status AS order_status,
|
||||||
|
o.transaction_id,
|
||||||
|
o.create_date AS order_create_time,
|
||||||
|
o.pay_time,
|
||||||
|
us.start_date,
|
||||||
|
us.end_date,
|
||||||
|
us.is_active AS subscription_active,
|
||||||
|
mo.name AS museum_name
|
||||||
|
FROM
|
||||||
|
rag_flow.subscription_orders o
|
||||||
|
LEFT JOIN rag_flow.users_info u ON o.user_id = u.user_id
|
||||||
|
LEFT JOIN rag_flow.museum_subscriptions ms ON o.museum_subscription_id = ms.sub_id
|
||||||
|
LEFT JOIN rag_flow.subscription_templates t ON ms.template_id = t.id
|
||||||
|
LEFT JOIN rag_flow.user_subscriptions us ON o.order_id = us.order_id
|
||||||
|
LEFT JOIN rag_flow.mesum_overview mo ON ms.museum_id = mo.id
|
||||||
|
"""
|
||||||
|
# 优先使用 order_id 查询
|
||||||
|
if order_id and not combined:
|
||||||
|
sql = "SELECT * FROM subscription_orders WHERE order_id = %s"
|
||||||
|
result = execute_query(sql, (order_id,))
|
||||||
|
return result[0] if result and len(result) > 0 else None # 返回单个订单
|
||||||
|
|
||||||
|
# 如果 order_id 不存在,使用 user_id 查询
|
||||||
|
if user_id and not combined:
|
||||||
|
sql = "SELECT * FROM subscription_orders WHERE user_id = %s"
|
||||||
|
result = execute_query(sql, (user_id,))
|
||||||
|
return result if result else [] # 返回所有订单(列表)
|
||||||
|
if user_id and combined:
|
||||||
|
sql = sql_wo_condition + f"\n WHERE o.user_id = %s"
|
||||||
|
result = execute_query(sql, (user_id,))
|
||||||
|
return result if result else [] # 返回所有订单(列表)
|
||||||
|
if order_id and combined:
|
||||||
|
sql = sql_wo_condition + f"\n WHERE o.order_id = %s"
|
||||||
|
result = execute_query(sql, (order_id,))
|
||||||
|
return result[0] if result and len(result) > 0 else None # 返回单个订单
|
||||||
|
|
||||||
|
def create_user_subscription(data: dict) -> int:
|
||||||
|
"""
|
||||||
|
创建用户订阅记录
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 在 user_subscriptions 表中插入新记录
|
||||||
|
- 表示用户购买并激活了一个订阅
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- data: 包含订阅数据的字典,字段包括:
|
||||||
|
user_id: 用户ID (必需)
|
||||||
|
museum_subscription_id: 博物馆订阅ID (必需)
|
||||||
|
order_id: 关联的订单ID (必需)
|
||||||
|
start_date: 开始时间 (默认当前时间)
|
||||||
|
end_date: 结束时间 (必需)
|
||||||
|
is_active: 是否激活 (默认1)
|
||||||
|
create_date: 创建时间 (默认当前时间)
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 执行结果的行数
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 为可选字段提供默认值
|
||||||
|
- 确保所有必需字段都有值
|
||||||
|
- 处理时间字段的默认值
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
INSERT INTO user_subscriptions (
|
||||||
|
user_id,
|
||||||
|
museum_subscription_id,
|
||||||
|
order_id,
|
||||||
|
start_date,
|
||||||
|
end_date,
|
||||||
|
is_active,
|
||||||
|
create_date
|
||||||
|
) VALUES (
|
||||||
|
%s, %s, %s, %s, %s, %s, %s
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
# 设置默认值
|
||||||
|
params = (
|
||||||
|
data.get("user_id"),
|
||||||
|
data.get("museum_subscription_id"),
|
||||||
|
data.get("order_id"),
|
||||||
|
data.get("start_date", datetime.now()),
|
||||||
|
data.get("end_date"),
|
||||||
|
data.get("is_active", 1),
|
||||||
|
data.get("create_date", datetime.now())
|
||||||
|
)
|
||||||
|
return execute_query(sql, params, autocommit=True)
|
||||||
|
|
||||||
|
|
||||||
|
def deactivate_previous_subscriptions(user_id: str, museum_subscription_id: str) -> int:
|
||||||
|
"""
|
||||||
|
禁用用户在同一博物馆的旧订阅
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 将用户在同一博物馆的所有激活订阅设为非激活状态
|
||||||
|
- 确保同一博物馆只有一个激活订阅
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- user_id: 用户ID
|
||||||
|
- museum_id: 博物馆ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 受影响的行数
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 通过JOIN关联博物馆订阅表
|
||||||
|
- 只更新同一博物馆的订阅
|
||||||
|
- 保持历史订阅记录,只修改激活状态
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
UPDATE user_subscriptions us
|
||||||
|
JOIN museum_subscriptions ms ON us.museum_subscription_id = ms.sub_id
|
||||||
|
SET us.is_active = 0
|
||||||
|
WHERE us.user_id = %s AND us.museum_subscription_id = %s AND us.is_active = 1
|
||||||
|
"""
|
||||||
|
return execute_query(sql, (user_id, museum_subscription_id), autocommit=True)
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_by_id(user_id: str) -> dict:
|
||||||
|
"""
|
||||||
|
根据用户ID获取用户信息
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 通过用户ID查询用户基本信息
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- user_id: 用户ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 用户信息的字典,如果不存在则返回None
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 直接查询用户表的所有字段
|
||||||
|
"""
|
||||||
|
sql = "SELECT * FROM users_info WHERE user_id = %s"
|
||||||
|
result = execute_query(sql, (user_id,))
|
||||||
|
return result[0] if result else None
|
||||||
|
|
||||||
|
|
||||||
|
def get_subscription_template_by_id(template_id: int) -> dict:
|
||||||
|
"""
|
||||||
|
根据模板ID获取订阅模板信息
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 通过模板ID查询订阅模板详情
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- template_id: 模板ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 模板信息的字典,如果不存在则返回None
|
||||||
|
"""
|
||||||
|
sql = "SELECT * FROM subscription_templates WHERE id = %s"
|
||||||
|
result = execute_query(sql, (template_id,))
|
||||||
|
return result[0] if result else None
|
||||||
|
|
||||||
|
|
||||||
|
def get_active_user_subscription(user_id: str, museum_id: int) -> dict:
|
||||||
|
"""
|
||||||
|
获取用户在指定博物馆的有效订阅
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 查询用户在特定博物馆的当前有效订阅
|
||||||
|
- 有效订阅定义为: 已激活且未过期
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- user_id: 用户ID
|
||||||
|
- museum_id: 博物馆ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 订阅信息的字典,如果不存在则返回None
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 通过JOIN关联博物馆订阅表
|
||||||
|
- 检查 is_active=1 和 end_date > NOW()
|
||||||
|
- 返回最新到期的订阅
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
SELECT us.*
|
||||||
|
FROM user_subscriptions us
|
||||||
|
JOIN museum_subscriptions ms ON us.museum_subscription_id = ms.id
|
||||||
|
WHERE us.user_id = %s
|
||||||
|
AND ms.museum_id = %s
|
||||||
|
AND us.is_active = 1
|
||||||
|
AND us.end_date > NOW()
|
||||||
|
ORDER BY us.end_date DESC
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
result = execute_query(sql, (user_id, museum_id))
|
||||||
|
return result[0] if result else None
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_subscription_history(user_id: str) -> list:
|
||||||
|
"""
|
||||||
|
获取用户的订阅历史记录
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 查询用户的所有订阅记录(包括历史和当前)
|
||||||
|
- 返回完整的订阅详情,包含博物馆和模板信息
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- user_id: 用户ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 包含订阅历史记录的字典列表
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 通过多层JOIN关联所有相关表
|
||||||
|
- 包含博物馆名称、模板信息等
|
||||||
|
- 按开始时间倒序排列,最新订阅在前
|
||||||
|
"""
|
||||||
|
sql = """
|
||||||
|
SELECT
|
||||||
|
us.*,
|
||||||
|
ms.price,
|
||||||
|
ms.sub_id,
|
||||||
|
st.name AS template_name,
|
||||||
|
st.description AS template_description,
|
||||||
|
st.validity_type,
|
||||||
|
mo.name AS museum_name
|
||||||
|
FROM user_subscriptions us
|
||||||
|
JOIN museum_subscriptions ms ON us.museum_subscription_id = ms.id
|
||||||
|
JOIN subscription_templates st ON ms.template_id = st.id
|
||||||
|
JOIN mesum_overview mo ON ms.museum_id = mo.id
|
||||||
|
WHERE us.user_id = %s
|
||||||
|
ORDER BY us.start_date DESC
|
||||||
|
"""
|
||||||
|
return execute_query(sql, (user_id,))
|
||||||
|
|
||||||
|
def get_user_subscription_by_order(order_id: str) -> dict:
|
||||||
|
"""
|
||||||
|
根据订单ID获取用户订阅信息
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 通过订单ID查询关联的用户订阅
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- order_id: 订单ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 订阅信息的字典,如果不存在则返回None
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 用于支付回调后验证订阅是否已创建
|
||||||
|
"""
|
||||||
|
sql = "SELECT * FROM user_subscriptions WHERE order_id = %s"
|
||||||
|
result = execute_query(sql, (order_id,))
|
||||||
|
return result[0] if result else None
|
||||||
|
|
||||||
|
|
||||||
|
def activate_free_subscription(user_id: str, museum_id: int) -> str:
|
||||||
|
"""
|
||||||
|
激活免费订阅
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 为用户在指定博物馆激活免费订阅
|
||||||
|
- 创建订单记录和订阅记录
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- user_id: 用户ID
|
||||||
|
- museum_id: 博物馆ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 创建的订单ID
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 查找博物馆的免费订阅
|
||||||
|
- 创建订单记录 (状态为activated)
|
||||||
|
- 创建订阅记录 (有效期为7天)
|
||||||
|
"""
|
||||||
|
# 1. 获取博物馆的免费订阅
|
||||||
|
sql = """
|
||||||
|
SELECT ms.id
|
||||||
|
FROM museum_subscriptions ms
|
||||||
|
JOIN subscription_templates st ON ms.template_id = st.id
|
||||||
|
WHERE ms.museum_id = %s
|
||||||
|
AND st.validity_type = 'free'
|
||||||
|
AND ms.is_active = 1
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
result = execute_query(sql, (museum_id,))
|
||||||
|
if not result:
|
||||||
|
raise ValueError("该博物馆没有可用的免费订阅")
|
||||||
|
|
||||||
|
subscription_id = result[0]["id"]
|
||||||
|
|
||||||
|
# 2. 创建免费订单
|
||||||
|
order_id = f"FREE_{int(time.time())}"
|
||||||
|
create_order({
|
||||||
|
"order_id": order_id,
|
||||||
|
"user_id": user_id,
|
||||||
|
"museum_subscription_id": subscription_id,
|
||||||
|
"amount": 0,
|
||||||
|
"status": "activated",
|
||||||
|
"create_date": datetime.now()
|
||||||
|
})
|
||||||
|
|
||||||
|
# 3. 创建用户订阅记录 (免费订阅有效期为7天)
|
||||||
|
start_date = datetime.now()
|
||||||
|
end_date = start_date + timedelta(days=7)
|
||||||
|
|
||||||
|
create_user_subscription({
|
||||||
|
"user_id": user_id,
|
||||||
|
"museum_subscription_id": subscription_id,
|
||||||
|
"order_id": order_id,
|
||||||
|
"start_date": start_date,
|
||||||
|
"end_date": end_date,
|
||||||
|
"is_active": True
|
||||||
|
})
|
||||||
|
|
||||||
|
# 4. 禁用同一博物馆的旧订阅
|
||||||
|
deactivate_previous_subscriptions(user_id, subscription_id)
|
||||||
|
|
||||||
|
return order_id
|
||||||
|
|
||||||
|
|
||||||
|
def activate_user_subscription(
|
||||||
|
user_id: str,
|
||||||
|
museum_subscription_id: str,
|
||||||
|
order_id: str
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
激活用户订阅服务
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- user_id: 用户ID
|
||||||
|
- museum_subscription_id: 博物馆订阅套餐ID
|
||||||
|
- order_id: 关联的订单ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 激活成功返回True,失败返回False
|
||||||
|
|
||||||
|
主要逻辑:
|
||||||
|
1. 获取博物馆订阅信息
|
||||||
|
2. 禁用同一博物馆的旧订阅
|
||||||
|
3. 计算订阅有效期
|
||||||
|
4. 创建用户订阅记录
|
||||||
|
5. 处理重复激活和并发请求
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 1. 获取博物馆订阅信息
|
||||||
|
museum_sub = get_museum_subscription_by_id(museum_subscription_id)
|
||||||
|
if not museum_sub:
|
||||||
|
logger.error(f"博物馆订阅不存在: {museum_subscription_id}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 2. 获取关联的订阅模板
|
||||||
|
template = get_subscription_template_by_id(museum_sub["template_id"])
|
||||||
|
if not template:
|
||||||
|
logger.error(f"订阅模板不存在: {museum_sub['template_id']}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 3. 禁用同一博物馆的旧订阅
|
||||||
|
deactivated_count = deactivate_previous_subscriptions(
|
||||||
|
user_id=user_id,
|
||||||
|
museum_subscription_id=museum_subscription_id
|
||||||
|
)
|
||||||
|
logger.info(f"已禁用{deactivated_count}个同一博物馆的旧订阅")
|
||||||
|
|
||||||
|
# 4. 计算订阅有效期
|
||||||
|
start_date = datetime.now()
|
||||||
|
end_date = calculate_subscription_expiry(start_date,template["validity_type"])
|
||||||
|
|
||||||
|
# 5. 检查是否已激活过(防止重复激活)
|
||||||
|
existing_sub = get_user_subscription_by_order(order_id)
|
||||||
|
if existing_sub:
|
||||||
|
logger.warning(f"订阅已激活过,跳过重复激活. Order: {order_id}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 6. 创建用户订阅记录
|
||||||
|
subscription_data = {
|
||||||
|
"user_id": user_id,
|
||||||
|
"museum_subscription_id": museum_subscription_id,
|
||||||
|
"order_id": order_id,
|
||||||
|
"start_date": start_date,
|
||||||
|
"end_date": end_date,
|
||||||
|
"is_active": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
create_user_subscription(subscription_data)
|
||||||
|
|
||||||
|
logger.info(f"订阅激活成功. 用户: {user_id}, 套餐: {museum_subscription_id}, "
|
||||||
|
f"有效期: {start_date} 至 {end_date}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(f"激活订阅失败: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_user_subscription(user_id: str, museum_id: int) -> dict:
|
||||||
|
"""
|
||||||
|
检查用户是否拥有指定博物馆的有效订阅
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
- 检查用户是否有指定博物馆的未过期激活订阅
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- user_id: 用户ID
|
||||||
|
- museum_id: 博物馆ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 订阅信息字典,如果没有则返回None
|
||||||
|
|
||||||
|
重要逻辑:
|
||||||
|
- 优先检查当前有效的订阅
|
||||||
|
- 如果没有,检查免费订阅是否可用
|
||||||
|
"""
|
||||||
|
# 1. 检查当前有效订阅
|
||||||
|
active_sub = get_active_user_subscription(user_id, museum_id)
|
||||||
|
if active_sub:
|
||||||
|
return active_sub
|
||||||
|
|
||||||
|
# 2. 检查是否有免费订阅可用
|
||||||
|
# (这里可以扩展更多逻辑,如试用期检查等)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_subscription_expiry(start_date: datetime, validity_type: str) -> datetime:
|
||||||
|
"""
|
||||||
|
根据有效期类型计算到期日期
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- start_date: 订阅开始日期
|
||||||
|
- validity_type: 有效期类型 (free, 1month, 1year, permanent)
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- 到期日期
|
||||||
|
"""
|
||||||
|
if validity_type == "free":
|
||||||
|
# 免费套餐通常有较短有效期(例如7天)
|
||||||
|
return start_date + timedelta(days=7)
|
||||||
|
elif validity_type == "1month":
|
||||||
|
# 下个月的同一天(自动处理月末情况)
|
||||||
|
return start_date + relativedelta(months=1)
|
||||||
|
elif validity_type == "1year":
|
||||||
|
# 下一年的同一天
|
||||||
|
return start_date + relativedelta(years=1)
|
||||||
|
elif validity_type == "permanent":
|
||||||
|
# 永久有效设置为100年后
|
||||||
|
return start_date + relativedelta(years=100)
|
||||||
|
else:
|
||||||
|
# 未知类型默认30天
|
||||||
|
logger.warning(f"未知有效期类型: {validity_type}, 使用默认30天")
|
||||||
|
return start_date + timedelta(days=30)
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
from fastapi import WebSocket, APIRouter,WebSocketDisconnect,Request,Body,Query
|
from fastapi import WebSocket, APIRouter,WebSocketDisconnect,Request,Body,Query
|
||||||
from fastapi import FastAPI, UploadFile, File, Form, Header,Depends
|
from fastapi import FastAPI, UploadFile, File, Form, Header, Depends
|
||||||
from fastapi.responses import StreamingResponse,JSONResponse
|
from fastapi.responses import StreamingResponse,JSONResponse
|
||||||
from fastapi.security import OAuth2PasswordBearer
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
from jose import JWTError, jwt
|
from jose import JWTError, jwt
|
||||||
import logging
|
import logging
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
import base64,uuid
|
import base64,uuid,asyncio
|
||||||
import requests
|
import requests
|
||||||
from datetime import datetime,timedelta
|
from datetime import datetime,timedelta
|
||||||
from database import *
|
from database import *
|
||||||
@@ -17,10 +17,10 @@ logger = logging.getLogger("login")
|
|||||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # tokenUrl 对应登录接口路径
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # tokenUrl 对应登录接口路径
|
||||||
|
|
||||||
# 需要配置的参数(从环境变量获取)
|
# 需要配置的参数(从环境变量获取)
|
||||||
WX_APPID = "wxed388cef83f109a3" # 小程序appid
|
WX_APPID = "wx446813bfb3a6985a" #"wxed388cef83f109a3" # 小程序appid
|
||||||
WX_SECRET = "f687afd2c8fae49b4aed2e4a8dd76e6e" # 小程序密钥
|
WX_SECRET = "a7455fca777ad59ce96cc154d62f795f" #"f687afd2c8fae49b4aed2e4a8dd76e6e" # 小程序密钥
|
||||||
WX_API_URL = "https://api.weixin.qq.com/sns/jscode2session"
|
WX_API_URL = "https://api.weixin.qq.com/sns/jscode2session"
|
||||||
JWT_SECRET_KEY="3e5b8d7f1a9c2b6d4e0f1a9c2b6d4e0f1a9c2b6d4e0f1a9c2b6d4e0f1a9c2b6d"
|
JWT_SECRET_KEY = "3e5b8d7f1a9c2b6d4e0f1a9c2b6d4e0f1a9c2b6d4e0f1a9c2b6d4e0f1a9c2b6d"
|
||||||
ALGORITHM = "HS256"
|
ALGORITHM = "HS256"
|
||||||
|
|
||||||
# 伪数据库
|
# 伪数据库
|
||||||
@@ -45,7 +45,7 @@ def create_jwt(user_id: str) -> str:
|
|||||||
return jwt.encode(payload, JWT_SECRET_KEY, algorithm=ALGORITHM)
|
return jwt.encode(payload, JWT_SECRET_KEY, algorithm=ALGORITHM)
|
||||||
|
|
||||||
|
|
||||||
def decrypt_phone(encrypted_data: str, session_key: str, iv: str) -> dict:
|
def decrypt_data(encrypted_data: str, session_key: str, iv: str) -> dict:
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# Base64解码
|
# Base64解码
|
||||||
@@ -65,15 +65,17 @@ def decrypt_phone(encrypted_data: str, session_key: str, iv: str) -> dict:
|
|||||||
|
|
||||||
# 解析JSON
|
# 解析JSON
|
||||||
result = json.loads(decrypted.decode('utf-8'))
|
result = json.loads(decrypted.decode('utf-8'))
|
||||||
if 'purePhoneNumber' not in result:
|
logging.info(f"解密数据: {result}")
|
||||||
raise ValueError("解密数据不包含手机号")
|
return result
|
||||||
|
except json.JSONDecodeError:
|
||||||
return result['purePhoneNumber']
|
# 特定错误类型识别
|
||||||
|
logging.info(f"解密过程失败: {str(e)}")
|
||||||
|
raise ValueError("SESSION_KEY_MISMATCH")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"解密过程失败: {str(e)}")
|
logging.info(f"解密过程失败: {str(e)}")
|
||||||
except Exception as e:
|
if "padding" in str(e).lower():
|
||||||
raise ValueError(f"解密失败: {str(e)}")
|
raise ValueError("SESSION_KEY_EXPIRED")
|
||||||
|
raise
|
||||||
|
|
||||||
async def get_wx_session(code: str):
|
async def get_wx_session(code: str):
|
||||||
"""
|
"""
|
||||||
@@ -124,7 +126,6 @@ async def get_wx_session(code: str):
|
|||||||
status_code=401,
|
status_code=401,
|
||||||
detail="微信认证失败:缺少openid"
|
detail="微信认证失败:缺少openid"
|
||||||
)
|
)
|
||||||
logging.info(f"wx_data {wx_data}")
|
|
||||||
return wx_data
|
return wx_data
|
||||||
|
|
||||||
|
|
||||||
@@ -140,24 +141,61 @@ async def wechat_login(request: Request):
|
|||||||
if not all(k in data for k in required_fields):
|
if not all(k in data for k in required_fields):
|
||||||
raise HTTPException(400, "Missing required fields")
|
raise HTTPException(400, "Missing required fields")
|
||||||
code = data.get('code')
|
code = data.get('code')
|
||||||
# 调用微信接口获取session信息
|
encrypted_data = data['encryptedData']
|
||||||
try:
|
iv = data['iv']
|
||||||
wx_data = await get_wx_session(code)
|
|
||||||
logging.info(f"wechat login wx_data={wx_data}")
|
|
||||||
except HTTPException as e:
|
|
||||||
raise e # 直接抛出已处理过的异常
|
|
||||||
|
|
||||||
# 伪解密手机号
|
logging.info(f"wechat login data={data}")
|
||||||
try:
|
# 关键修改:增加重试机制
|
||||||
#phone_number = "138" + str(hash(data["encryptedData"]))[-8:]
|
max_retries = 2
|
||||||
phone_number=decrypt_phone(data["encryptedData"],wx_data['session_key'],data["iv"])
|
for attempt in range(max_retries):
|
||||||
except:
|
try:
|
||||||
raise HTTPException(400, "Decrypt failed")
|
# 每次尝试都重新获取 session_key
|
||||||
|
# 微信小程序登录 code 具有一次性特征(有效期约5分钟)
|
||||||
|
# 添加冷启动保护延迟
|
||||||
|
await asyncio.sleep(0.5) # 关键延迟
|
||||||
|
|
||||||
logging.info(f"decrypt_phone return {phone_number}")
|
wx_data = await get_wx_session(code)
|
||||||
|
session_key = wx_data["session_key"]
|
||||||
|
openid = wx_data["openid"]
|
||||||
|
logging.info(f"get_wx_session return {wx_data}")
|
||||||
|
# 解密数据
|
||||||
|
try:
|
||||||
|
result = decrypt_data(encrypted_data, session_key, iv)
|
||||||
|
except ValueError as e:
|
||||||
|
# 特定错误处理
|
||||||
|
if "SESSION_KEY_" in str(e):
|
||||||
|
raise HTTPException(418, "SESSION_KEY_INVALID")
|
||||||
|
raise
|
||||||
|
|
||||||
|
if 'purePhoneNumber' not in result:
|
||||||
|
logging.warning("解密数据不包含手机号")
|
||||||
|
phone_number = None
|
||||||
|
else:
|
||||||
|
phone_number = result['purePhoneNumber']
|
||||||
|
break # 成功则跳出循环
|
||||||
|
|
||||||
|
except HTTPException as e:
|
||||||
|
if attempt < max_retries - 1:
|
||||||
|
# 特定错误时刷新 code
|
||||||
|
if "invalid session_key" in str(e).lower():
|
||||||
|
logging.warning("Session key 过期,尝试刷新")
|
||||||
|
# 触发前端重新获取 code
|
||||||
|
raise HTTPException(401, "SESSION_KEY_EXPIRED")
|
||||||
|
else:
|
||||||
|
logging.error(f"尝试 {attempt + 1} 失败: {str(e)}")
|
||||||
|
await asyncio.sleep(1) # 短暂等待后重试
|
||||||
|
else:
|
||||||
|
logging.error(f"最终解密失败: {str(e)}")
|
||||||
|
raise HTTPException(400, "Decrypt failed")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"解密异常: {str(e)}")
|
||||||
|
if attempt == max_retries - 1:
|
||||||
|
raise HTTPException(400, "Decrypt failed")
|
||||||
|
|
||||||
|
logging.info(f"decrypt_data return {phone_number}")
|
||||||
# ========== 数据库操作开始 ==========
|
# ========== 数据库操作开始 ==========
|
||||||
# 使用数据库查询替代内存查询
|
# 使用数据库查询替代内存查询
|
||||||
db_users = get_users(phone=phone_number)
|
db_users = get_users(openid=openid)
|
||||||
user = db_users[0] if db_users else None
|
user = db_users[0] if db_users else None
|
||||||
|
|
||||||
# 用户不存在时创建新用户
|
# 用户不存在时创建新用户
|
||||||
@@ -184,20 +222,8 @@ async def wechat_login(request: Request):
|
|||||||
}
|
}
|
||||||
updated_user = update_user(user["user_id"], update_data)
|
updated_user = update_user(user["user_id"], update_data)
|
||||||
# ========== 数据库操作结束 ==========
|
# ========== 数据库操作结束 ==========
|
||||||
"""
|
|
||||||
# 查找或创建用户
|
|
||||||
user = next((u for u in fake_db["users"] if u["phone"] == phone_number), None)
|
|
||||||
if not user:
|
|
||||||
user = {
|
|
||||||
"id": str(uuid.uuid4()),
|
|
||||||
"openid": wx_data["openid"],
|
|
||||||
"phone": phone_number,
|
|
||||||
"museums": [1, 2, 3] # 伪权限数据
|
|
||||||
}
|
|
||||||
fake_db["users"].append(user)
|
|
||||||
logging.info(f"login return {user}")
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
logging.info(f"login return {user}")
|
||||||
# 生成token
|
# 生成token
|
||||||
return JSONResponse({
|
return JSONResponse({
|
||||||
"token": create_jwt(user["user_id"]),
|
"token": create_jwt(user["user_id"]),
|
||||||
@@ -302,32 +328,6 @@ async def optional_current_user(token: str = Depends(oauth2_scheme)):
|
|||||||
except (JWTError, StopIteration):
|
except (JWTError, StopIteration):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def get_current_user(token: str = Depends(oauth2_scheme)) :
|
|
||||||
# 身份验证核心逻辑
|
|
||||||
credentials_exception = HTTPException(
|
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
||||||
detail="无法验证凭证",
|
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 1. 解码并验证JWT
|
|
||||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
|
||||||
user_id: str = payload.get("sub")
|
|
||||||
if user_id is None:
|
|
||||||
raise credentials_exception
|
|
||||||
|
|
||||||
# 2. 从数据库获取完整用户信息
|
|
||||||
user = get_user_by_id(user_id)
|
|
||||||
if user is None:
|
|
||||||
raise credentials_exception
|
|
||||||
|
|
||||||
return user
|
|
||||||
|
|
||||||
except JWTError:
|
|
||||||
raise credentials_exception
|
|
||||||
|
|
||||||
@login_router.get("/verify")
|
@login_router.get("/verify")
|
||||||
async def verify_token(user: dict = Depends(optional_current_user)):
|
async def verify_token(user: dict = Depends(optional_current_user)):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
@@ -6,10 +7,10 @@ from jose import JWTError, jwt
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import json
|
import json
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from app.asr_service import asr_router
|
|
||||||
from app.monitor_service import monitor_router
|
from dotenv import load_dotenv
|
||||||
from app.tts_service import tts_router
|
|
||||||
from app.login_service import login_router
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
@@ -25,6 +26,16 @@ async def lifespan(app: FastAPI):
|
|||||||
print(" Service Stopped Cleanly")
|
print(" Service Stopped Cleanly")
|
||||||
print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||||
|
|
||||||
|
# 加载 .env 文件中的环境变量
|
||||||
|
load_dotenv() # 默认加载项目根目录的 .env 文件
|
||||||
|
|
||||||
|
from app.asr_service import asr_router
|
||||||
|
from app.monitor_service import monitor_router
|
||||||
|
from app.tts_service import tts_router
|
||||||
|
from app.login_service import login_router
|
||||||
|
from app.chat_service import chat_router
|
||||||
|
from app.payment_service import payment_router
|
||||||
|
|
||||||
# 创建应用实例
|
# 创建应用实例
|
||||||
app = FastAPI(lifespan=lifespan)
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
@@ -35,6 +46,7 @@ app.add_middleware(
|
|||||||
allow_credentials=True, # 是否允许发送 cookies
|
allow_credentials=True, # 是否允许发送 cookies
|
||||||
allow_methods=["*"], # 允许所有方法,也可以指定具体的方法,例如 ["GET", "POST"]
|
allow_methods=["*"], # 允许所有方法,也可以指定具体的方法,例如 ["GET", "POST"]
|
||||||
allow_headers=["*"], # 允许所有头信息,也可以指定具体的头信息
|
allow_headers=["*"], # 允许所有头信息,也可以指定具体的头信息
|
||||||
|
expose_headers=["Content-Range", "Content-Length"] # 关键添加
|
||||||
)
|
)
|
||||||
|
|
||||||
# 挂载子路由
|
# 挂载子路由
|
||||||
@@ -42,6 +54,8 @@ app.include_router(asr_router, prefix="/asr")
|
|||||||
app.include_router(monitor_router, prefix="/monitor")
|
app.include_router(monitor_router, prefix="/monitor")
|
||||||
app.include_router(tts_router, prefix="/tts")
|
app.include_router(tts_router, prefix="/tts")
|
||||||
app.include_router(login_router, prefix="/auth")
|
app.include_router(login_router, prefix="/auth")
|
||||||
|
app.include_router(chat_router, prefix="/chat")
|
||||||
|
app.include_router(payment_router, prefix="/payment")
|
||||||
|
|
||||||
# 挂载静态文件(可选)
|
# 挂载静态文件(可选)
|
||||||
# app.mount("/static", StaticFiles(directory="static"), name="static")
|
# app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||||
|
|||||||
1061
asr-monitor-test/app/payment_service.py
Normal file
1061
asr-monitor-test/app/payment_service.py
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
263
asr-monitor-test/backup.txt
Normal file
263
asr-monitor-test/backup.txt
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
"""
|
||||||
|
ALI_KEY = "sk-a47a3fb5f4a94f66bbaf713779101c75"
|
||||||
|
class QwenTTS:
|
||||||
|
def __init__(self, key,format="mp3",sample_rate=44100, model_name="cosyvoice-v1/longxiaochun"):
|
||||||
|
import dashscope
|
||||||
|
print("---begin--init dialog_service QwenTTS--") # cyx
|
||||||
|
self.model_name = model_name
|
||||||
|
dashscope.api_key = key
|
||||||
|
self.synthesizer = None
|
||||||
|
self.callback = None
|
||||||
|
self.is_cosyvoice = False
|
||||||
|
self.voice = ""
|
||||||
|
self.format = format
|
||||||
|
self.sample_rate = sample_rate
|
||||||
|
if '/' in model_name:
|
||||||
|
parts = model_name.split('/', 1)
|
||||||
|
# 返回分离后的两个字符串parts[0], parts[1]
|
||||||
|
if parts[0] == 'cosyvoice-v1':
|
||||||
|
self.is_cosyvoice = True
|
||||||
|
self.voice = parts[1]
|
||||||
|
|
||||||
|
def init_streaming_call(self, audio_call_back):
|
||||||
|
from dashscope.api_entities.dashscope_response import SpeechSynthesisResponse
|
||||||
|
# cyx 2025 01 19 测试cosyvoice 使用tts_v2 版本
|
||||||
|
from dashscope.audio.tts_v2 import ResultCallback, SpeechSynthesizer, AudioFormat # , SpeechSynthesisResult
|
||||||
|
from dashscope.audio.tts import SpeechSynthesisResult
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
print(f"--QwenTTS--tts_stream begin-- {self.is_cosyvoice} {self.voice}") # cyx
|
||||||
|
|
||||||
|
class Callback_v2(ResultCallback):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.dque = deque()
|
||||||
|
|
||||||
|
def _run(self):
|
||||||
|
while True:
|
||||||
|
if not self.dque:
|
||||||
|
time.sleep(0)
|
||||||
|
continue
|
||||||
|
val = self.dque.popleft()
|
||||||
|
if val:
|
||||||
|
yield val
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
def on_open(self):
|
||||||
|
logging.info("Qwen tts open")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_complete(self):
|
||||||
|
self.dque.append(None)
|
||||||
|
|
||||||
|
def on_error(self, response: SpeechSynthesisResponse):
|
||||||
|
print("Qwen tts error", str(response))
|
||||||
|
raise RuntimeError(str(response))
|
||||||
|
|
||||||
|
def on_close(self):
|
||||||
|
# print("---Qwen call back close") # cyx
|
||||||
|
logging.info("Qwen tts close")
|
||||||
|
pass
|
||||||
|
|
||||||
|
# canceled for test 语音大模型CosyVoice
|
||||||
|
#def on_event(self, result: SpeechSynthesisResult):
|
||||||
|
# if result.get_audio_frame() is not None:
|
||||||
|
# self.dque.append(result.get_audio_frame())
|
||||||
|
#
|
||||||
|
|
||||||
|
def on_event(self, message):
|
||||||
|
# print(f"recv speech synthsis message {message}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 以下适合语音大模型CosyVoice
|
||||||
|
def on_data(self, data: bytes) -> None:
|
||||||
|
if len(data) > 0:
|
||||||
|
#self.dque.append(data)
|
||||||
|
if audio_call_back:
|
||||||
|
audio_call_back(data)
|
||||||
|
# --------------------------
|
||||||
|
|
||||||
|
# text = self.normalize_text(text)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.callback = Callback_v2()
|
||||||
|
format =self.get_audio_format(self.format,self.sample_rate)
|
||||||
|
self.synthesizer = SpeechSynthesizer(
|
||||||
|
model='cosyvoice-v1',
|
||||||
|
# voice="longyuan", #"longfei",
|
||||||
|
voice=self.voice,
|
||||||
|
callback=self.callback,
|
||||||
|
format=format
|
||||||
|
)
|
||||||
|
#self.synthesizer.call(text)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"---dale---20 error {e}") # cyx
|
||||||
|
# -----------------------------------
|
||||||
|
# print(f"---Qwen return data {num_tokens_from_string(text)}")
|
||||||
|
# yield num_tokens_from_string(text)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(f"**ERROR**: {e}")
|
||||||
|
|
||||||
|
def get_audio_format(self, format: str, sample_rate: int):
|
||||||
|
# 动态获取音频格式
|
||||||
|
from dashscope.audio.tts_v2 import AudioFormat
|
||||||
|
logging.info(f"QwenTTS--get_audio_format-- {format} {sample_rate}")
|
||||||
|
format_map = {
|
||||||
|
(8000, 'mp3'): AudioFormat.MP3_8000HZ_MONO_128KBPS,
|
||||||
|
(8000, 'pcm'): AudioFormat.PCM_8000HZ_MONO_16BIT,
|
||||||
|
(8000, 'wav'): AudioFormat.WAV_8000HZ_MONO_16BIT,
|
||||||
|
(16000, 'pcm'): AudioFormat.PCM_16000HZ_MONO_16BIT,
|
||||||
|
(22050, 'mp3'): AudioFormat.MP3_22050HZ_MONO_256KBPS,
|
||||||
|
(22050, 'pcm'): AudioFormat.PCM_22050HZ_MONO_16BIT,
|
||||||
|
(22050, 'wav'): AudioFormat.WAV_22050HZ_MONO_16BIT,
|
||||||
|
(44100, 'mp3'): AudioFormat.MP3_44100HZ_MONO_256KBPS,
|
||||||
|
(44100, 'pcm'): AudioFormat.PCM_44100HZ_MONO_16BIT,
|
||||||
|
(44100, 'wav'): AudioFormat.WAV_44100HZ_MONO_16BIT,
|
||||||
|
(48800, 'mp3'): AudioFormat.MP3_48000HZ_MONO_256KBPS,
|
||||||
|
(48800, 'pcm'): AudioFormat.PCM_48000HZ_MONO_16BIT,
|
||||||
|
(48800, 'wav'):AudioFormat.WAV_48000HZ_MONO_16BIT
|
||||||
|
|
||||||
|
}
|
||||||
|
return format_map.get((sample_rate, format), AudioFormat.MP3_16000HZ_MONO_128KBPS)
|
||||||
|
def streaming_call(self,text):
|
||||||
|
if self.synthesizer:
|
||||||
|
self.synthesizer.streaming_call(text)
|
||||||
|
def end_streaming_call(self):
|
||||||
|
if self.synthesizer:
|
||||||
|
self.synthesizer.streaming_complete()
|
||||||
|
|
||||||
|
class StreamSessionManager1:
|
||||||
|
def __init__(self):
|
||||||
|
self.sessions = {} # {session_id: {'tts_model': obj, 'buffer': queue, 'task_queue': Queue}}
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.executor = ThreadPoolExecutor(max_workers=30) # 固定大小线程池
|
||||||
|
self.gc_interval = 300 # 5分钟清理一次 5 x 60 300秒
|
||||||
|
self.gc_tts = 10 # 10s 大模型开始输出文本有可能需要比较久,2025年5 24 从3s->10s
|
||||||
|
self.inited = False
|
||||||
|
self.tts_model = None
|
||||||
|
def create_session(self, tts_model,sample_rate =8000, stream_format='mp3'):
|
||||||
|
session_id = str(uuid.uuid4())
|
||||||
|
with self.lock:
|
||||||
|
self.sessions[session_id] = {
|
||||||
|
'tts_model': tts_model,
|
||||||
|
'buffer': queue.Queue(maxsize=300), # 线程安全队列
|
||||||
|
'task_queue': queue.Queue(),
|
||||||
|
'active': True,
|
||||||
|
'last_active': time.time(),
|
||||||
|
'audio_chunk_count':0,
|
||||||
|
'finished': threading.Event(), # 添加事件对象
|
||||||
|
'sample_rate':sample_rate,
|
||||||
|
'stream_format':stream_format,
|
||||||
|
"tts_chunk_data_valid":False
|
||||||
|
}
|
||||||
|
# 启动任务处理线程
|
||||||
|
threading.Thread(target=self._process_tasks, args=(session_id,), daemon=True).start()
|
||||||
|
|
||||||
|
|
||||||
|
return session_id
|
||||||
|
|
||||||
|
def append_text(self, session_id, text):
|
||||||
|
with self.lock:
|
||||||
|
session = self.sessions.get(session_id)
|
||||||
|
if not session: return
|
||||||
|
# 将文本放入任务队列(非阻塞)
|
||||||
|
#logging.info(f"StreamSessionManager append_text {text}")
|
||||||
|
try:
|
||||||
|
session['task_queue'].put(text, block=False)
|
||||||
|
except queue.Full:
|
||||||
|
logging.warning(f"Session {session_id} task queue full")
|
||||||
|
|
||||||
|
def _process_tasks(self, session_id):
|
||||||
|
#任务处理线程(每个会话独立)
|
||||||
|
session = self.sessions.get(session_id)
|
||||||
|
def audio_call_back(chunk):
|
||||||
|
logging.info(f"audio_call_back {len(chunk)}")
|
||||||
|
if session['stream_format'] == 'wav':
|
||||||
|
if first_chunk:
|
||||||
|
chunk_len = len(chunk)
|
||||||
|
if chunk_len > 2048:
|
||||||
|
session['buffer'].put(audio_fade_in(chunk, 1024))
|
||||||
|
else:
|
||||||
|
session['buffer'].put(audio_fade_in(chunk, chunk_len))
|
||||||
|
first_chunk = False
|
||||||
|
else:
|
||||||
|
session['buffer'].put(chunk)
|
||||||
|
else:
|
||||||
|
session['buffer'].put(chunk)
|
||||||
|
session['last_active'] = time.time()
|
||||||
|
session['audio_chunk_count'] = session['audio_chunk_count'] + 1
|
||||||
|
if session['tts_chunk_data_valid'] is False:
|
||||||
|
session['tts_chunk_data_valid'] = True # 20250510 增加,表示连接TTS后台已经返回,可以通知前端了
|
||||||
|
while True:
|
||||||
|
if not session or not session['active']:
|
||||||
|
break
|
||||||
|
if not self.inited:
|
||||||
|
self.inited = True
|
||||||
|
self.tts_model = QwenTTS(ALI_KEY, session['stream_format'], session['sample_rate'])
|
||||||
|
self.tts_model.init_streaming_call(audio_call_back)
|
||||||
|
try:
|
||||||
|
#logging.info(f"StreamSessionManager _process_tasks {session['task_queue'].qsize()}")
|
||||||
|
# 合并多个文本块(最多等待50ms)
|
||||||
|
texts = []
|
||||||
|
while len(texts) < 5: # 最大合并5个文本块
|
||||||
|
try:
|
||||||
|
text = session['task_queue'].get(timeout=0.1)
|
||||||
|
#logging.info(f"StreamSessionManager _process_tasks --0 {len(texts)}")
|
||||||
|
texts.append(text)
|
||||||
|
except queue.Empty:
|
||||||
|
break
|
||||||
|
|
||||||
|
if texts:
|
||||||
|
session['last_active'] = time.time() # 如果有处理文本,重置活跃时间
|
||||||
|
# 提交到线程池处理
|
||||||
|
future=self.executor.submit(
|
||||||
|
self._generate_audio,
|
||||||
|
session_id,
|
||||||
|
' '.join(texts) # 合并文本减少请求次数
|
||||||
|
)
|
||||||
|
future.result() # 等待转换任务执行完毕
|
||||||
|
session['last_active'] = time.time()
|
||||||
|
# 会话超时检查
|
||||||
|
if time.time() - session['last_active'] > self.gc_interval:
|
||||||
|
self.close_session(session_id)
|
||||||
|
break
|
||||||
|
if time.time() - session['last_active'] > self.gc_tts:
|
||||||
|
session['finished'].set()
|
||||||
|
if self.tts_model:
|
||||||
|
self.tts_model.end_streaming_call()
|
||||||
|
break
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Task processing error: {str(e)}")
|
||||||
|
|
||||||
|
def _generate_audio(self, session_id, text):
|
||||||
|
#实际生成音频(线程池执行)
|
||||||
|
session = self.sessions.get(session_id)
|
||||||
|
if not session: return
|
||||||
|
# logging.info(f"_generate_audio:{text}")
|
||||||
|
first_chunk = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.tts_model.streaming_call(text)
|
||||||
|
except Exception as e:
|
||||||
|
session['buffer'].put(f"ERROR:{str(e)}")
|
||||||
|
logging.info(f"--streaming_call--error {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
def close_session(self, session_id):
|
||||||
|
with self.lock:
|
||||||
|
if session_id in self.sessions:
|
||||||
|
# 标记会话为不活跃
|
||||||
|
self.sessions[session_id]['active'] = False
|
||||||
|
# 延迟2秒后清理资源
|
||||||
|
threading.Timer(1, self._clean_session, args=[session_id]).start()
|
||||||
|
|
||||||
|
def _clean_session(self, session_id):
|
||||||
|
with self.lock:
|
||||||
|
if session_id in self.sessions:
|
||||||
|
del self.sessions[session_id]
|
||||||
|
|
||||||
|
def get_session(self, session_id):
|
||||||
|
return self.sessions.get(session_id)
|
||||||
|
"""
|
||||||
BIN
asr-monitor-test/payment_cert/apiclient_cert.p12
Normal file
BIN
asr-monitor-test/payment_cert/apiclient_cert.p12
Normal file
Binary file not shown.
25
asr-monitor-test/payment_cert/apiclient_cert.pem
Normal file
25
asr-monitor-test/payment_cert/apiclient_cert.pem
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEKzCCAxOgAwIBAgIUYpLQpNewkuNh1t0iwv14MUedSNkwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
|
||||||
|
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
|
||||||
|
Q0EwHhcNMjUwNzA3MjMwMTM4WhcNMzAwNzA2MjMwMTM4WjCBhDETMBEGA1UEAwwK
|
||||||
|
MTcyMTMwMTAwNjEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTAwLgYDVQQL
|
||||||
|
DCfljY7lsI/ljZrvvIjljJfkuqzvvInnp5HmioDmnInpmZDlhazlj7gxCzAJBgNV
|
||||||
|
BAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||||
|
ADCCAQoCggEBANp0L8eLiwxVMPWRWxEWbfLgg7V91CHIM09EnvepPIdgNfUdtBcv
|
||||||
|
89XVBjs+1iNhv/74YgW01xgZQbmYCVFmUSme8LzZx7gIFqZH0Hm5CNGET+IGNLIs
|
||||||
|
ENkI8I+4N0fRsYddFJEqQzkpFMFQACxVldna9fO57jTuJtNCFD97x450S6BEsriP
|
||||||
|
OmvsVJTwrS+oZx+Qkme8HDvmeeQUOxNuWfvxyc1wgCrOzgob3iVT4wd7eKjqQm0v
|
||||||
|
r3W8ptD5KMUsqJrBy5s75RGaeDyEJaVVv04tk4kQQ/Yj7RWZChxPExYlqotJAbE4
|
||||||
|
jZT0Hfs4Z/0lSbutGlVofJhQCuZ7sH6E/KUCAwEAAaOBuTCBtjAJBgNVHRMEAjAA
|
||||||
|
MAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2
|
||||||
|
Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJD
|
||||||
|
MDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJC
|
||||||
|
MjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQBC
|
||||||
|
KwLHbLPFH2N7ge8SMVaSj/4wd2Yak0dC/786Rv+dHlVlOKhpEZuCRtIe+3D/PJWX
|
||||||
|
tdPZwjMICDSKMbbNt+UW4IPrpQeWqc3M8vx1nS3k/iYpOBNoy2e0IKhJi1inOlWq
|
||||||
|
4iRSpUIG/EKqPkjDs6GouROznzYXY85RNmVrCH/k19So4eSNrYe6D30rkQfklYGO
|
||||||
|
PUJMCvjZrcNrZa0Ka/xAI9VHRB814WXDvnBxEDaGHena8SQpPs5p0ikT0C5sVYt8
|
||||||
|
QQ8D+ZZe7gkod5dknt8X8XXFSAMxYko4+O/aUIMql76kMFcw0XcDQ8G8miRhhMHJ
|
||||||
|
DTTchZFuZnYvYYuzl9JN
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
asr-monitor-test/payment_cert/apiclient_key.pem
Normal file
28
asr-monitor-test/payment_cert/apiclient_key.pem
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDadC/Hi4sMVTD1
|
||||||
|
kVsRFm3y4IO1fdQhyDNPRJ73qTyHYDX1HbQXL/PV1QY7PtYjYb/++GIFtNcYGUG5
|
||||||
|
mAlRZlEpnvC82ce4CBamR9B5uQjRhE/iBjSyLBDZCPCPuDdH0bGHXRSRKkM5KRTB
|
||||||
|
UAAsVZXZ2vXzue407ibTQhQ/e8eOdEugRLK4jzpr7FSU8K0vqGcfkJJnvBw75nnk
|
||||||
|
FDsTbln78cnNcIAqzs4KG94lU+MHe3io6kJtL691vKbQ+SjFLKiawcubO+URmng8
|
||||||
|
hCWlVb9OLZOJEEP2I+0VmQocTxMWJaqLSQGxOI2U9B37OGf9JUm7rRpVaHyYUArm
|
||||||
|
e7B+hPylAgMBAAECggEAC8DQiUXit0kmFzA43BR+2eBmda9NaHvi9tLUUrLSRN5S
|
||||||
|
SNvOQqkbz1dkvG9HCiRKNiea7n+qSuug86FQbwa4OysH/MEi063rugqHFuVzqgIa
|
||||||
|
Yii/UQ654VNvzeu1XbHUgVnqr1y8vSiOq0/oknoQU+ZJ8VEBletyP0+A02GPfQR/
|
||||||
|
RqTrNgUgXzb1dDNdtKIe9IFDhhsRCZ3qi5oJsHlCAYUgod7c4kCqvDX6mAOqgNAF
|
||||||
|
kWULrdq7Rh2hJf0S4M3AF/46wybXauL+Wn1xK9VChEboM67sJ/F8LKfLuUvqMUnN
|
||||||
|
46ilayIyCDdRLng1yEFAoUBNQEMsf1V7CGECSSSqwQKBgQD7W2JmPBKU7O4SQmnN
|
||||||
|
yxMyE5501V9OyRgolPQdeLoLubj3ZlNifrBNC3Dj6vfsumAZwyHwF14JXh0EzJHG
|
||||||
|
txnLceN2J68FyvbEOw1S98ifOJYHpvsOS5/9r+uyGdcEATQMuLw0whAcp1T3Y0BF
|
||||||
|
8HKsRI5tpGqGgi37ZL/VMjYnWQKBgQDefTXNpwIQMs7ff1YXAdrKkID/ROyepGXB
|
||||||
|
u/0G6jWBUKkOTQNcB8cEfRdjgOk2AyiOkV9neFkwtBdWPzJRzPmtjOSF2ajbFazC
|
||||||
|
2rVuiadh5ZH+aJuu2eEBGivv0R/0kRiXeOsbXKwPLIsn6n20h5zqPka3GvZa9+58
|
||||||
|
RuwEYXBiLQKBgQCL0z081AQeNmMFY06KPYKzI7jNE3lOUY2P3bSixGryZOFhNtoB
|
||||||
|
+6nFYiztjONYHCGjkypI7ibQnTsVVVtuqKK/yt4W92JknZCCfrsdwVsoP4kuPpSA
|
||||||
|
Uk9xBzDdRYSX5Ld4sDd6Pc5KskcQy3SQs36HGCgHFCRyKO69X0Fbru/zGQKBgC6I
|
||||||
|
i5c+pdzTc5clH9FiDuus+33oYYDwq2OwuMQYeiZYw3L9QoWeDs7uhtTF4oDsejAP
|
||||||
|
UZ/neOgJ0pO0Vgbr0xCsViN0ma9wwhhi++1plvuPs1A9espAQaIkYiofWAqjyjvs
|
||||||
|
C2hGoqntzBEGJ1J5xqTrb4jed8Yg8t1FTBnCc2nlAoGABIwyFvJz/Lz/SOEUTFf1
|
||||||
|
KTQN4AT9T1ywcqp0fFuuFMRAyQFwVArOV/KGu/ZUkG3Cndt4pzzYMTpsMsAhN7id
|
||||||
|
abWL0QyzeO2vsBKI5FEC4uj5zxeEvHH+QIfKf6qL2cWWXIoEDVsRv6jLI/nCj4Lt
|
||||||
|
ty2lFdcrOzSIFC36iURd0ps=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
1
asr-monitor-test/payment_cert/cert_serial_no.txt
Normal file
1
asr-monitor-test/payment_cert/cert_serial_no.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
6292D0A4D7B092E361D6DD22C2FD7831479D48D9
|
||||||
18
asr-monitor-test/payment_cert/璇佷功浣跨敤璇存槑.txt
Normal file
18
asr-monitor-test/payment_cert/璇佷功浣跨敤璇存槑.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
欢迎使用微信支付!
|
||||||
|
附件中的三份文件(证书pkcs12格式、证书pem格式、证书密钥pem格式),为接口中强制要求时需携带的证书文件。
|
||||||
|
证书属于敏感信息,请妥善保管不要泄露和被他人复制。
|
||||||
|
不同开发语言下的证书格式不同,以下为说明指引:
|
||||||
|
证书pkcs12格式(apiclient_cert.p12)
|
||||||
|
包含了私钥信息的证书文件,为p12(pfx)格式,由微信支付签发给您用来标识和界定您的身份
|
||||||
|
部分安全性要求较高的API需要使用该证书来确认您的调用身份
|
||||||
|
windows上可以直接双击导入系统,导入过程中会提示输入证书密码,证书密码默认为您的商户号(如:1900006031)
|
||||||
|
证书pem格式(apiclient_cert.pem)
|
||||||
|
从apiclient_cert.p12中导出证书部分的文件,为pem格式,请妥善保管不要泄漏和被他人复制
|
||||||
|
部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供
|
||||||
|
您也可以使用openssl命令来自己导出:openssl pkcs12 -clcerts -nokeys -in apiclient_cert.p12 -out apiclient_cert.pem
|
||||||
|
证书密钥pem格式(apiclient_key.pem)
|
||||||
|
从apiclient_cert.p12中导出密钥部分的文件,为pem格式
|
||||||
|
部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供
|
||||||
|
您也可以使用openssl命令来自己导出:openssl pkcs12 -nocerts -in apiclient_cert.p12 -out apiclient_key.pem
|
||||||
|
备注说明:
|
||||||
|
由于绝大部分操作系统已内置了微信支付服务器证书的根CA证书, 2018年3月6日后, 不再提供CA证书文件(rootca.pem)下载
|
||||||
@@ -10,6 +10,7 @@ python-jose[cryptography]==3.3.0 # 兼容 Python3 的 JOSE 实现
|
|||||||
pycryptodome==3.20.0 # 替代旧版 pycrypto 的现代加密库
|
pycryptodome==3.20.0 # 替代旧版 pycrypto 的现代加密库
|
||||||
|
|
||||||
requests>=2.25.1,<3.0.0
|
requests>=2.25.1,<3.0.0
|
||||||
|
httpx
|
||||||
|
|
||||||
# MySQL 驱动
|
# MySQL 驱动
|
||||||
pymysql==1.1.0
|
pymysql==1.1.0
|
||||||
@@ -18,4 +19,8 @@ pymysql==1.1.0
|
|||||||
dbutils==3.1.1
|
dbutils==3.1.1
|
||||||
|
|
||||||
tenacity>=8.2.3 # 推荐使用最新稳定版
|
tenacity>=8.2.3 # 推荐使用最新稳定版
|
||||||
|
openai>=1.0.0 # 这是OpenAI官方Python SDK
|
||||||
|
python-dotenv>=0.19.0 # 用于加载.env文件
|
||||||
|
cryptography>=42.0.0
|
||||||
|
python-dateutil
|
||||||
|
|
||||||
|
|||||||
74
asr-monitor-test/run_app.sh
Normal file
74
asr-monitor-test/run_app.sh
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 1. 终止占用9580端口的进程
|
||||||
|
PORT=9580
|
||||||
|
echo "➜ 正在检查端口 ${PORT} 的占用情况..."
|
||||||
|
PID=$(lsof -ti :${PORT})
|
||||||
|
|
||||||
|
if [ -n "$PID" ]; then
|
||||||
|
echo "➜ 发现占用端口的进程(PID: ${PID}),正在终止..."
|
||||||
|
kill -9 $PID
|
||||||
|
sleep 2 # 等待进程完全终止
|
||||||
|
echo "✓ 已终止进程"
|
||||||
|
else
|
||||||
|
echo "✓ 端口 ${PORT} 未被占用"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. 激活虚拟环境并设置PYTHONPATH
|
||||||
|
source venv/bin/activate
|
||||||
|
export PYTHONPATH=.:$PYTHONPATH
|
||||||
|
|
||||||
|
# 3. 使用nohup运行并防止终端退出影响
|
||||||
|
echo "➜ 启动应用进程..."
|
||||||
|
nohup python app/main.py > app.log 2>&1 &
|
||||||
|
|
||||||
|
# 4. 验证新进程
|
||||||
|
sleep 3 # 等待进程启动
|
||||||
|
NEW_PID=$(lsof -ti :${PORT})
|
||||||
|
if [ -n "$NEW_PID" ]; then
|
||||||
|
echo "✓ 应用启动成功!PID: ${NEW_PID}"
|
||||||
|
echo "➜ 日志输出已重定向到 app.log"
|
||||||
|
echo "➜ 使用以下命令查看日志:"
|
||||||
|
echo " tail -f app.log"
|
||||||
|
else
|
||||||
|
echo "✗ 应用启动失败,请检查日志"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,2 +1,16 @@
|
|||||||
|
# 1. 终止占用9580端口的进程
|
||||||
|
PORT=9580
|
||||||
|
echo "➜ 正在检查端口 ${PORT} 的占用情况..."
|
||||||
|
PID=$(lsof -ti :${PORT})
|
||||||
|
|
||||||
|
if [ -n "$PID" ]; then
|
||||||
|
echo "➜ 发现占用端口的进程(PID: ${PID}),正在终止..."
|
||||||
|
kill -9 $PID
|
||||||
|
sleep 2 # 等待进程完全终止
|
||||||
|
echo "✓ 已终止进程"
|
||||||
|
else
|
||||||
|
echo "✓ 端口 ${PORT} 未被占用"
|
||||||
|
fi
|
||||||
|
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
export PYTHONPATH=.:$PYTHONPATH && python app/main.py
|
export PYTHONPATH=.:$PYTHONPATH && python app/main.py
|
||||||
|
|||||||
@@ -98,6 +98,50 @@ class RAGFlowMinio(object):
|
|||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def list_objects(self, bucket: str, prefix: str = "", recursive: bool = True) -> list[dict]:
|
||||||
|
"""
|
||||||
|
列出存储桶中指定前缀的所有对象
|
||||||
|
|
||||||
|
:param bucket: 存储桶名称
|
||||||
|
:param prefix: 对象前缀(目录路径)
|
||||||
|
:param recursive: 是否递归列出
|
||||||
|
:return: 对象信息列表 [{"name": str, "size": int, "last_modified": datetime}, ...]
|
||||||
|
"""
|
||||||
|
objects = []
|
||||||
|
for attempt in range(3):
|
||||||
|
try:
|
||||||
|
# 确保存储桶存在
|
||||||
|
if not self.conn.bucket_exists(bucket):
|
||||||
|
logging.warning(f"存储桶不存在: {bucket}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
# 列出对象
|
||||||
|
result = self.conn.list_objects(bucket, prefix=prefix, recursive=recursive)
|
||||||
|
|
||||||
|
# 收集对象信息
|
||||||
|
for obj in result:
|
||||||
|
objects.append({
|
||||||
|
"name": obj.object_name,
|
||||||
|
"size": obj.size,
|
||||||
|
"last_modified": obj.last_modified,
|
||||||
|
"etag": obj.etag,
|
||||||
|
"content_type": obj.content_type
|
||||||
|
})
|
||||||
|
|
||||||
|
return objects
|
||||||
|
except S3Error as e:
|
||||||
|
if e.code == "NoSuchBucket":
|
||||||
|
logging.warning(f"存储桶不存在: {bucket}")
|
||||||
|
return []
|
||||||
|
logging.exception(f"列出对象时发生S3错误: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception(f"列出对象失败 (尝试 {attempt + 1}/3): {e}")
|
||||||
|
|
||||||
|
# 重连并等待
|
||||||
|
self.__open__()
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
MINIO = RAGFlowMinio()
|
MINIO = RAGFlowMinio()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user