1616from server .utils .auth_middleware import get_db
1717from src .storage .conversation import ConversationManager
1818from src .storage .db .models import User
19+ from src .utils .datetime_utils import UTC , ensure_shanghai , shanghai_now , utc_now
1920from src .utils .logging_config import logger
2021
2122
@@ -231,7 +232,7 @@ async def get_user_activity_stats(
231232 try :
232233 from src .storage .db .models import User , Conversation
233234
234- now = datetime . utcnow ()
235+ now = utc_now ()
235236
236237 # Conversations may store either the numeric user primary key or the login user_id string.
237238 # Join condition accounts for both representations.
@@ -305,7 +306,7 @@ async def get_tool_call_stats(
305306 try :
306307 from src .storage .db .models import ToolCall
307308
308- now = datetime . utcnow ()
309+ now = utc_now ()
309310
310311 # 基础工具调用统计
311312 total_calls = db .query (func .count (ToolCall .id )).scalar () or 0
@@ -746,26 +747,30 @@ async def get_call_timeseries_stats(
746747 from src .storage .db .models import Conversation , Message , ToolCall
747748
748749 # 计算时间范围(使用北京时间 UTC+8)
749- now = datetime .utcnow ()
750+ now = utc_now ()
751+ local_now = shanghai_now ()
750752
751753 if time_range == "7hours" :
752754 intervals = 7
753755 # 包含当前小时:从6小时前开始
754756 start_time = now - timedelta (hours = intervals - 1 )
755- # SQLite compatible approach: 使用datetime函数转换UTC时间为北京时间
756757 group_format = func .strftime ("%Y-%m-%d %H:00" , func .datetime (Message .created_at , "+8 hours" ))
758+ base_local_time = ensure_shanghai (start_time )
757759 elif time_range == "7weeks" :
758760 intervals = 7
759- # 包含当前周:从6周前开始
760- start_time = now - timedelta (weeks = intervals - 1 )
761- # SQLite compatible approach: 使用datetime函数转换UTC时间为北京时间
761+ # 包含当前周:从6周前开始,并对齐到当周周一 00:00
762+ local_start = local_now - timedelta (weeks = intervals - 1 )
763+ local_start = local_start - timedelta (days = local_start .weekday ())
764+ local_start = local_start .replace (hour = 0 , minute = 0 , second = 0 , microsecond = 0 )
765+ start_time = local_start .astimezone (UTC )
762766 group_format = func .strftime ("%Y-%W" , func .datetime (Message .created_at , "+8 hours" ))
767+ base_local_time = local_start
763768 else : # 7days (default)
764769 intervals = 7
765770 # 包含当前天:从6天前开始
766771 start_time = now - timedelta (days = intervals - 1 )
767- # SQLite compatible approach: 使用datetime函数转换UTC时间为北京时间
768772 group_format = func .strftime ("%Y-%m-%d" , func .datetime (Message .created_at , "+8 hours" ))
773+ base_local_time = ensure_shanghai (start_time )
769774
770775 # 根据类型查询数据
771776 if type == "models" :
@@ -783,22 +788,23 @@ async def get_call_timeseries_stats(
783788 .order_by (group_format )
784789 )
785790 elif type == "agents" :
786- # 智能体调用统计(基于对话数量 ,按智能体分组)
791+ # 智能体调用统计(基于对话更新时间 ,按智能体分组)
787792 # 为对话创建独立的时间格式化器
788793 if time_range == "7hours" :
789- conv_group_format = func .strftime ("%Y-%m-%d %H:00" , func .datetime (Conversation .created_at , "+8 hours" ))
794+ conv_group_format = func .strftime ("%Y-%m-%d %H:00" , func .datetime (Conversation .updated_at , "+8 hours" ))
790795 elif time_range == "7weeks" :
791- conv_group_format = func .strftime ("%Y-%W" , func .datetime (Conversation .created_at , "+8 hours" ))
796+ conv_group_format = func .strftime ("%Y-%W" , func .datetime (Conversation .updated_at , "+8 hours" ))
792797 else : # 7days
793- conv_group_format = func .strftime ("%Y-%m-%d" , func .datetime (Conversation .created_at , "+8 hours" ))
798+ conv_group_format = func .strftime ("%Y-%m-%d" , func .datetime (Conversation .updated_at , "+8 hours" ))
794799
795800 query = (
796801 db .query (
797802 conv_group_format .label ("date" ),
798803 func .count (Conversation .id ).label ("count" ),
799804 Conversation .agent_id .label ("category" ),
800805 )
801- .filter (Conversation .created_at >= start_time )
806+ .filter (Conversation .updated_at .isnot (None ))
807+ .filter (Conversation .updated_at >= start_time )
802808 .group_by (conv_group_format , Conversation .agent_id )
803809 .order_by (conv_group_format )
804810 )
@@ -894,8 +900,16 @@ async def get_call_timeseries_stats(
894900
895901 # 重新组织数据:按时间点分组每个类别的数据
896902 time_data = {}
903+
904+ def normalize_week_key (raw_key : str ) -> str :
905+ base_date = datetime .strptime (f"{ raw_key } -1" , "%Y-%W-%w" )
906+ iso_year , iso_week , _ = base_date .isocalendar ()
907+ return f"{ iso_year } -{ iso_week :02d} "
908+
897909 for result in results :
898910 date_key = result .date
911+ if time_range == "7weeks" :
912+ date_key = normalize_week_key (date_key )
899913 category = getattr (result , "category" , "unknown" )
900914 count = result .count
901915
@@ -906,8 +920,8 @@ async def get_call_timeseries_stats(
906920
907921 # 填充缺失的时间点(使用北京时间)
908922 data = []
909- # 从start_time开始,转换为北京时间
910- current_time = start_time + timedelta ( hours = 8 )
923+ # 从起始点开始(北京时间)
924+ current_time = base_local_time
911925
912926 if time_range == "7hours" :
913927 delta = timedelta (hours = 1 )
@@ -920,9 +934,8 @@ async def get_call_timeseries_stats(
920934 if time_range == "7hours" :
921935 date_key = current_time .strftime ("%Y-%m-%d %H:00" )
922936 elif time_range == "7weeks" :
923- # 计算ISO周数
924- week_num = current_time .isocalendar ()[1 ]
925- date_key = f"{ current_time .year } -{ week_num :02d} "
937+ iso_year , iso_week , _ = current_time .isocalendar ()
938+ date_key = f"{ iso_year } -{ iso_week :02d} "
926939 else :
927940 date_key = current_time .strftime ("%Y-%m-%d" )
928941
0 commit comments