diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9da420d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "kz_dash"] + path = kz_dash + url = git@192.168.10.51:dash/kz_dash.git diff --git a/backend/apis/account.py b/backend/apis/account.py index b48cb9d..563be0c 100644 --- a/backend/apis/account.py +++ b/backend/apis/account.py @@ -5,8 +5,8 @@ from backend.apis.common import create_list_response_model, create_response_model from models.account import ModelAccount, update_account from models.database import delete_record, get_record, get_record_list, update_record -from utils.response import format_response -from utils.string_helper import get_uuid +from kz_dash.utility.response import format_response +from kz_dash.utility.string_helper import get_uuid api = Namespace("accounts", description="账户相关操作") diff --git a/backend/apis/fund.py b/backend/apis/fund.py index faa636c..d9ac792 100644 --- a/backend/apis/fund.py +++ b/backend/apis/fund.py @@ -9,7 +9,7 @@ update_record, ) from models.fund_user import ModelFundPosition -from utils.response import format_response +from kz_dash.utility.response import format_response api = Namespace("funds", description="基金相关操作") diff --git a/backend/apis/portfolio.py b/backend/apis/portfolio.py index cf8dc91..a50610f 100644 --- a/backend/apis/portfolio.py +++ b/backend/apis/portfolio.py @@ -5,8 +5,8 @@ from backend.apis.common import create_list_response_model, create_response_model from models.account import ModelPortfolio from models.database import delete_record, get_record, get_record_list, update_record -from utils.response import format_response -from utils.string_helper import get_uuid +from kz_dash.utility.response import format_response +from kz_dash.utility.string_helper import get_uuid api = Namespace("portfolios", description="投资组合相关操作") diff --git a/backend/apis/runtime.py b/backend/apis/runtime.py index 0689a5a..dd82146 100644 --- a/backend/apis/runtime.py +++ b/backend/apis/runtime.py @@ -8,7 +8,7 @@ from backend.apis.common import create_response_model from config import VERSION -from utils.response import format_response +from kz_dash.utility.response import format_response logger = logging.getLogger(__name__) diff --git a/backend/apis/task.py b/backend/apis/task.py index 2b1ab79..a40b324 100644 --- a/backend/apis/task.py +++ b/backend/apis/task.py @@ -11,7 +11,7 @@ from models.task import ModelTask from scheduler.job_manager import JobManager from scheduler.tasks import TaskFactory -from utils.response import format_response +from kz_dash.utility.response import format_response logger = logging.getLogger(__name__) diff --git a/components/fund_code_aio.py b/components/fund_code_aio.py index c025ecd..7c8a679 100644 --- a/components/fund_code_aio.py +++ b/components/fund_code_aio.py @@ -8,7 +8,7 @@ from config import DATA_SOURCE_DEFAULT from data_source.proxy import DataSourceProxy -from utils.string_helper import get_uuid +from kz_dash.utility.string_helper import get_uuid logger = logging.getLogger(__name__) diff --git a/data_source/implementations/eastmoney.py b/data_source/implementations/eastmoney.py index a3ee26d..dedd1bf 100644 --- a/data_source/implementations/eastmoney.py +++ b/data_source/implementations/eastmoney.py @@ -9,8 +9,8 @@ from data_source.interface import IDataSource from scheduler.tasks.base import FundType -from utils.datetime_helper import format_date, get_timestamp, get_timestamp_ms -from utils.string_helper import ( +from kz_dash.utility.datetime_helper import format_date, get_timestamp, get_timestamp_ms +from kz_dash.utility.string_helper import ( extract_number_with_unit, generate_random_string, get_json_from_jsonp_simple, diff --git a/data_source/proxy.py b/data_source/proxy.py index 52e3d35..7c6ec74 100644 --- a/data_source/proxy.py +++ b/data_source/proxy.py @@ -4,7 +4,7 @@ from config import DATA_SOURCE_DEFAULT from data_source import DataSourceFactory -from utils.response import format_response +from kz_dash.utility.response import format_response logger = logging.getLogger(__name__) T = TypeVar("T") # 用于泛型返回类型 diff --git a/kz_dash b/kz_dash new file mode 160000 index 0000000..3f3ef41 --- /dev/null +++ b/kz_dash @@ -0,0 +1 @@ +Subproject commit 3f3ef41b6ca3c7d8793d53f30d9b9b2772a1ad55 diff --git a/models/account.py b/models/account.py index fed639b..7a97f9c 100644 --- a/models/account.py +++ b/models/account.py @@ -3,7 +3,7 @@ from peewee import BooleanField, CharField, ForeignKeyField from models.base import BaseModel -from utils.string_helper import get_uuid +from kz_dash.utility.string_helper import get_uuid class ModelAccount(BaseModel): diff --git a/models/base.py b/models/base.py index ad9e6bf..b9b4474 100644 --- a/models/base.py +++ b/models/base.py @@ -6,7 +6,7 @@ from peewee import DatabaseError, DateTimeField, Model, SqliteDatabase from config import DATABASE_CONFIG -from utils.singleton import Singleton +from kz_dash.utility.singleton import Singleton logger = logging.getLogger(__name__) diff --git a/models/database.py b/models/database.py index ddf8af4..afe2b58 100644 --- a/models/database.py +++ b/models/database.py @@ -5,8 +5,8 @@ from playhouse.shortcuts import update_model_from_dict -from utils.datetime_helper import format_datetime -from utils.string_helper import get_uuid +from kz_dash.utility.datetime_helper import format_datetime +from kz_dash.utility.string_helper import get_uuid from .account import ModelAccount, ModelPortfolio from .base import BaseModel, db_connection diff --git a/pages/account/portfolio_modal.py b/pages/account/portfolio_modal.py index aff930c..8a2d235 100644 --- a/pages/account/portfolio_modal.py +++ b/pages/account/portfolio_modal.py @@ -8,7 +8,7 @@ from models.database import update_record from pages.account.table import get_account_table_data from pages.account.utils import validate_name -from utils.string_helper import get_uuid +from kz_dash.utility.string_helper import get_uuid # 组合编辑弹窗模块 diff --git a/pages/account/table.py b/pages/account/table.py index 5f59cbc..d8a15b1 100644 --- a/pages/account/table.py +++ b/pages/account/table.py @@ -7,7 +7,7 @@ from models.account import ModelAccount, ModelPortfolio from models.database import get_record, get_record_list -from utils.datetime_helper import format_datetime +from kz_dash.utility.datetime_helper import format_datetime from .utils import create_operation_buttons diff --git a/pages/task/task_detail.py b/pages/task/task_detail.py index 49920cc..6f3bf0b 100644 --- a/pages/task/task_detail.py +++ b/pages/task/task_detail.py @@ -27,8 +27,8 @@ ) from scheduler.job_manager import JobManager from scheduler.tasks import TaskStatus -from utils.datetime_helper import format_datetime -from utils.string_helper import json_str_to_dict +from kz_dash.utility.datetime_helper import format_datetime +from kz_dash.utility.string_helper import json_str_to_dict logger = logging.getLogger(__name__) diff --git a/pages/task/task_table.py b/pages/task/task_table.py index fb13172..d05ab99 100644 --- a/pages/task/task_table.py +++ b/pages/task/task_table.py @@ -21,7 +21,7 @@ prepare_task_for_display, ) from scheduler.job_manager import JobManager, TaskStatus -from utils.fac_helper import show_message +from kz_dash.utility.fac_helper import show_message logger = logging.getLogger(__name__) diff --git a/pages/task/task_utils.py b/pages/task/task_utils.py index 11dda1c..0883bef 100644 --- a/pages/task/task_utils.py +++ b/pages/task/task_utils.py @@ -17,7 +17,7 @@ from models.database import get_record_count, get_record_list from models.task import ModelTask from scheduler.job_manager import JobManager, TaskStatus -from utils.datetime_helper import format_datetime +from kz_dash.utility.datetime_helper import format_datetime # ============= 状态常量 ============= STATUS_LABELS = { diff --git a/pages/transaction/modal.py b/pages/transaction/modal.py index 98b816c..9abfef8 100644 --- a/pages/transaction/modal.py +++ b/pages/transaction/modal.py @@ -20,8 +20,8 @@ from models.fund import ModelFundNav from models.fund_user import ModelFundTransaction from scheduler.job_manager import JobManager -from utils.fac_helper import show_message -from utils.string_helper import get_uuid +from kz_dash.utility.fac_helper import show_message +from kz_dash.utility.string_helper import get_uuid from .utils import build_cascader_options diff --git a/scheduler/job_manager.py b/scheduler/job_manager.py index efed299..cf77ed6 100644 --- a/scheduler/job_manager.py +++ b/scheduler/job_manager.py @@ -10,8 +10,8 @@ from models.database import delete_record, get_record, get_record_list, update_record from models.task import ModelTask from scheduler.tasks import TaskFactory, TaskStatus -from utils.singleton import Singleton -from utils.string_helper import get_uuid +from kz_dash.utility.singleton import Singleton +from kz_dash.utility.string_helper import get_uuid # 创建调度器实例 scheduler = APScheduler() diff --git a/scheduler/tasks/fund_nav.py b/scheduler/tasks/fund_nav.py index e024b9d..7cbd08a 100644 --- a/scheduler/tasks/fund_nav.py +++ b/scheduler/tasks/fund_nav.py @@ -5,7 +5,7 @@ from data_source.proxy import DataSourceProxy from models.database import update_record from models.fund import ModelFundNav -from utils.datetime_helper import get_date_str_after_days, get_days_between_dates +from kz_dash.utility.datetime_helper import get_date_str_after_days, get_days_between_dates from .base import PARAM_FUND_CODE, BaseTask diff --git a/scheduler/tasks/sync_fund_list.py b/scheduler/tasks/sync_fund_list.py index d5b8a8d..a9a1f54 100644 --- a/scheduler/tasks/sync_fund_list.py +++ b/scheduler/tasks/sync_fund_list.py @@ -6,7 +6,7 @@ from models.database import get_record from models.fund import ModelFund from scheduler.tasks.task_factory import TaskFactory -from utils.datetime_helper import get_date_str_after_days, get_days_between_dates +from kz_dash.utility.datetime_helper import get_date_str_after_days, get_days_between_dates from .base import PARAM_FUND_TYPE, BaseTask diff --git a/scheduler/tasks/sync_fund_nav.py b/scheduler/tasks/sync_fund_nav.py index 9d64276..254c938 100644 --- a/scheduler/tasks/sync_fund_nav.py +++ b/scheduler/tasks/sync_fund_nav.py @@ -7,7 +7,7 @@ from models.database import get_record from models.fund import ModelFund from scheduler.tasks.task_factory import TaskFactory -from utils.datetime_helper import get_date_str_after_days, get_days_between_dates +from kz_dash.utility.datetime_helper import get_date_str_after_days, get_days_between_dates from .base import PARAM_FUND_CODE, PARAM_SUB_TASK_DELAY, BaseTask diff --git a/scheduler/tasks/task_factory.py b/scheduler/tasks/task_factory.py index 42496a2..d19127c 100644 --- a/scheduler/tasks/task_factory.py +++ b/scheduler/tasks/task_factory.py @@ -3,7 +3,7 @@ from config import DEBUG from scheduler.tasks.base import BaseTask -from utils.singleton import Singleton +from kz_dash.utility.singleton import Singleton logger = logging.getLogger(__name__) diff --git a/tests/test_datetime.py b/tests/test_datetime.py index 0d4cc66..d5095cf 100644 --- a/tests/test_datetime.py +++ b/tests/test_datetime.py @@ -1,7 +1,7 @@ import unittest from datetime import date, datetime -from utils.datetime_helper import format_date, format_datetime +from kz_dash.utility.datetime_helper import format_date, format_datetime class TestDatetimeFunctions(unittest.TestCase): diff --git a/tests/test_tasks.py b/tests/test_tasks.py index 32e5829..e08d9dd 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -5,7 +5,7 @@ from data_source.implementations.eastmoney import EastMoneyDataSource from models.database import init_database from scheduler.tasks.fund_detail import FundDetailTask -from utils.string_helper import get_uuid +from kz_dash.utility.string_helper import get_uuid class TestFundDetailTasks(unittest.TestCase): diff --git a/utils/datetime_helper.py b/utils/datetime_helper.py deleted file mode 100644 index 6ae4595..0000000 --- a/utils/datetime_helper.py +++ /dev/null @@ -1,171 +0,0 @@ -import logging -from datetime import date, datetime, timedelta -from typing import Optional, Union - -logger = logging.getLogger(__name__) - - -def format_datetime( - dt: Union[str, datetime, None], - output_format: str = "%Y-%m-%d %H:%M:%S", - default: str = "未知时间", -) -> str: - """ - 统一的日期时间格式化函数 - - Args: - dt: 要格式化的日期时间,可以是datetime对象或ISO格式的字符串 - output_format: 输出格式,默认为 "%Y-%m-%d %H:%M:%S" - default: 当无法格式化时返回的默认值 - - Returns: - 格式化后的时间字符串 - """ - if not dt: - return default - - try: - if isinstance(dt, str): - dt = datetime.fromisoformat(dt) - if isinstance(dt, datetime): - return dt.strftime(output_format) - except (ValueError, TypeError): - pass - - return default - - -def format_date( - dt: Union[str, date, None], - output_format: str = "%Y-%m-%d", - input_format: Optional[str] = None, - default: str = "未知日期", -) -> str: - """ - 统一的日期格式化函数 - - Args: - dt: 要格式化的日期,可以是date对象、ISO格式字符串或指定格式的日期字符串 - output_format: 输出格式,默认为 "%Y-%m-%d" - input_format: 输入日期字符串的格式,如果提供则使用strptime解析 - default: 当无法格式化时返回的默认值 - - Returns: - 格式化后的时间字符串 - """ - if not dt: - return default - - try: - if isinstance(dt, str): - if input_format: - dt = datetime.strptime(dt.strip(), input_format).date() - else: - dt = date.fromisoformat(dt.strip()) - if isinstance(dt, date): - return dt.strftime(output_format) - except (ValueError, TypeError): - pass - - return default - - -def get_timestamp() -> int: - """获取当前时间戳 - - 返回当前时间的Unix时间戳(从1970年1月1日UTC零点开始的秒数)。 - 时间戳为整数,精确到秒。 - - Returns: - int: 当前时间的Unix时间戳 - - Examples: - >>> get_timestamp() - 1709251200 # 2024-03-01 00:00:00 UTC - """ - return int(datetime.now().timestamp()) - - -def get_timestamp_ms() -> int: - """获取当前时间戳(毫秒) - - 返回当前时间的Unix时间戳(从1970年1月1日UTC零点开始的毫秒数)。 - 时间戳为整数,精确到毫秒。 - - Returns: - int: 当前时间的Unix时间戳(毫秒) - - Examples: - >>> get_timestamp_ms() - 1709251200000 # 2024-03-01 00:00:00 UTC - """ - return int(datetime.now().timestamp() * 1000) - - -def get_date_str_after_days(start_date: Union[str, date], days: int) -> str: - """获取开始日期后几天的日期字符串""" - return format_date(get_date_after_days(start_date, days)) - - -def get_days_between_dates(start_date: Union[str, date], end_date: Union[str, date]) -> int: - """计算两个日期之间的天数差值 - - Args: - start_date: 开始日期,可以是date对象或ISO格式字符串 - end_date: 结束日期,可以是date对象或ISO格式字符串 - - Returns: - int: 两个日期之间的天数差值(end_date - start_date) - - Examples: - >>> get_days_between_dates('2024-03-01', '2024-03-02') - 1 - >>> get_days_between_dates(date(2024, 3, 1), date(2024, 2, 29)) - -1 - """ - try: - if isinstance(start_date, str): - start_date = date.fromisoformat(start_date.strip()) - if isinstance(end_date, str): - end_date = date.fromisoformat(end_date.strip()) - - if not isinstance(start_date, date) or not isinstance(end_date, date): - logger.error("无效的日期格式: start_date=%s, end_date=%s", start_date, end_date) - raise ValueError("无效的日期格式") - - return (end_date - start_date).days - - except (ValueError, TypeError) as e: - logger.error("计算日期差值失败: %s", str(e)) - raise - - -def get_date_after_days(start_date: Union[str, date], days: int) -> date: - """获取开始日期后几天的日期 - - Args: - start_date: 开始日期,可以是date对象或ISO格式字符串 - days: 天数,正数表示往后,负数表示往前 - - Returns: - date: 计算后的日期 - - Examples: - >>> get_date_after_days('2024-03-01', 1) - datetime.date(2024, 3, 2) - >>> get_date_after_days(date(2024, 3, 1), -1) - datetime.date(2024, 2, 29) - """ - try: - if isinstance(start_date, str): - start_date = date.fromisoformat(start_date.strip()) - - if not isinstance(start_date, date): - logger.error("无效的日期格式: %s", start_date) - raise ValueError("无效的日期格式") - - return start_date + timedelta(days=days) - - except (ValueError, TypeError) as e: - logger.error("计算日期失败: %s", str(e)) - raise diff --git a/utils/fac_helper.py b/utils/fac_helper.py deleted file mode 100644 index c8344ea..0000000 --- a/utils/fac_helper.py +++ /dev/null @@ -1,20 +0,0 @@ -import feffery_antd_components as fac -from dash import set_props - - -def show_message( - message: str, - display_type: str = "default", - duration: int = 3, - component_id: str = "message-container", -): - """显示消息框 - - Args: - message: 消息内容 - type: 消息类型,可选值: success/error/info/warning/default - """ - set_props( - component_id, - {"children": fac.AntdMessage(content=message, type=display_type, duration=duration)}, - ) diff --git a/utils/response.py b/utils/response.py deleted file mode 100644 index 33769f3..0000000 --- a/utils/response.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import Any, Dict, Optional - - -def format_response( - data: Optional[Any] = None, - message: str = "success", - is_array: bool = True, - code: int = 200, -) -> Dict[str, Any]: - """统一的响应格式 - - Args: - data: 响应数据 - message: 响应消息 - is_array: 数据是否为数组类型 - code: 响应代码 - - Returns: - 统一格式的响应字典 - """ - return { - "code": code, - "message": message, - "data": data or ([] if is_array else None), - } diff --git a/utils/singleton.py b/utils/singleton.py deleted file mode 100644 index 091b21b..0000000 --- a/utils/singleton.py +++ /dev/null @@ -1,32 +0,0 @@ -from threading import Lock -from typing import Any, Dict, Type, TypeVar - -T = TypeVar("T") - - -class Singleton: - """单例模式装饰器""" - - _instances: Dict[Type, Any] = {} - - def __init__(self, cls: Type[T]): - self._cls = cls - - def __call__(self, *args, **kwargs) -> T: - # 添加双重检查锁定模式 - if self._cls not in self._instances: - # 如果实例不存在,加锁创建 - with self._get_lock(): - if self._cls not in self._instances: - self._instances[self._cls] = self._cls(*args, **kwargs) - return self._instances[self._cls] - - def _get_lock(self): - """获取锁对象""" - # 使用类属性作为锁 - if not hasattr(self._cls, "_lock"): - setattr(self._cls, "_lock", Lock()) - return getattr(self._cls, "_lock") - - def __getattr__(self, name): - return getattr(self._cls, name) diff --git a/utils/string_helper.py b/utils/string_helper.py deleted file mode 100644 index 53b2c6f..0000000 --- a/utils/string_helper.py +++ /dev/null @@ -1,102 +0,0 @@ -import json -import random -import re -import string -import uuid - - -def extract_number_with_unit( - text: str, convert_unit: bool = True, unit_type: str = "default" -) -> float: - """ - 从带单位的数值文本中提取数值,支持万、亿、百分比等单位 - - Args: - text: 带单位的数值文本,如"3.964亿份"、"1.2万元"、"5.5亿美元"、"0.60%"等 - convert_unit: 是否转换单位为基本单位。默认为True - True时:"3.964亿" -> 396400000.0, "0.60%" -> 0.6 - False时:"3.964亿" -> 3.964, "0.60%" -> 60.0 - unit_type: 指定单位类型,可选值: - 'default' - 自动检测单位(万、亿) - 'percentage' - 百分比单位 - - Returns: - float: 提取出的数值。如果无法提取则返回0.0 - - Examples: - >>> extract_number_with_unit("3.964亿份") - 396400000.0 - >>> extract_number_with_unit("1.2万元") - 12000.0 - >>> extract_number_with_unit("5.5亿美元", convert_unit=False) - 5.5 - >>> extract_number_with_unit("0.60%", unit_type='percentage') - 0.006 - >>> extract_number_with_unit("0.60%", unit_type='percentage', convert_unit=False) - 0.6 - >>> extract_number_with_unit("无效文本") - 0.0 - """ - try: - # 处理百分比 - if unit_type == "percentage": - number = float(text.split("%")[0].strip()) - return number / 100 if convert_unit else number - - # 处理其他单位 - - match = re.search(r"([\d.]+)([万亿])?", text) - if not match: - return 0.0 - - number = float(match.group(1)) - unit = match.group(2) if match.group(2) else "" - - # 处理单位转换 - if unit == "亿": - return number * 100000000 if convert_unit else number - if unit == "万": - return number * 10000 if convert_unit else number - return number - - except (ValueError, IndexError, AttributeError): - return 0.0 - - -def generate_random_string(length=20, chars=string.digits): - """ - 生成指定长度的随机字符串 - :param length: 长度 - :param chars: 字符集 - :return: 随机字符串 - """ - return "".join(random.choice(chars) for _ in range(length)) - - -def get_json_from_jsonp_simple(jsonp_str): - """ - 从jsonp格式文本中解析json对象 - 快速方法,仅支持:jsonp({"key": "value"}) - """ - # 找到第一个'('和最后一个')' - start = jsonp_str.find("(") + 1 - end = jsonp_str.rfind(")") - - if 0 < start < end: - json_str = jsonp_str[start:end] - return json.loads(json_str) - return None - - -def get_uuid(): - """生成UUID""" - return str(uuid.uuid4()) - - -def json_str_to_dict(data: str) -> dict: - """将数据转换为dict""" - try: - if isinstance(data, str): - return json.loads(data) - except json.JSONDecodeError: - return {"error": "无法解析的参数"}