Skip to content

Commit

Permalink
JTools v3.20.0
Browse files Browse the repository at this point in the history
功能变动:

- 支持配置 JKit 简书数据源 Endpoint
- 优化后端服务中的数据库连接池管理策略
- 使用 `Literal` 替代 `Enum`
- 应用数据库表结构变动
- 应用最新 linting 与 formatting 规则
- 更新 .gitignore
- 更新 LICENSE 年份

依赖变动:

- 升级到 JKit v3.0.0b2
- 升级到 sshared v0.21.0
- 升级到 Caddy v2.9
- 升级到 Ruff v0.9.0
- 更新依赖库
  • Loading branch information
FHU-yezi committed Jan 31, 2025
2 parents b27150f + 6405e38 commit 819b887
Show file tree
Hide file tree
Showing 29 changed files with 707 additions and 605 deletions.
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
**/node_modules
**/dist
**/dev-dist
.ruff_cache
.venv
**/__pycache__
**/node_modules

config.toml
2 changes: 1 addition & 1 deletion Dockerfile.frontend
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ RUN bun install --prod --frozen-lockfile
COPY frontend .
RUN bun run build

FROM caddy:2.8-alpine
FROM caddy:2.9-alpine

WORKDIR /app

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 叶子
Copyright (c) 2025 叶子

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
28 changes: 17 additions & 11 deletions backend/api/v1/articles.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from __future__ import annotations

from collections import Counter
from datetime import date, datetime, timedelta
from typing import Annotated, Optional
from typing import Annotated

from jkit.article import Article
from jkit.config import CONFIG as JKIT_CONFIG
from jkit.constants import ARTICLE_SLUG_REGEX
from jkit.exceptions import ResourceUnavailableError
from litestar import Response, Router, get
Expand All @@ -25,13 +28,16 @@
)
from utils.config import CONFIG

if CONFIG.jianshu_endpoint:
JKIT_CONFIG.datasources.jianshu.endpoint = CONFIG.jianshu_endpoint

splitter = WordSplitter(
access_key_id=CONFIG.word_split_access_key.access_key_id,
access_key_secret=CONFIG.word_split_access_key.access_key_secret,
)


async def get_earliest_can_recommend_date(author_slug: str) -> Optional[date]:
async def get_earliest_can_recommend_date(author_slug: str) -> date | None:
counted_article_slugs = set()

latest_onrank_record = await ArticleEarningRankingRecord.get_latest_record(
Expand All @@ -40,7 +46,7 @@ async def get_earliest_can_recommend_date(author_slug: str) -> Optional[date]:
if not latest_onrank_record:
return None

interval_days = 10 if latest_onrank_record.ranking <= 30 else 7
interval_days = 10 if latest_onrank_record.ranking <= 30 else 7 # noqa: PLR2004
counted_article_slugs.add(latest_onrank_record.slug)

now_record = latest_onrank_record
Expand All @@ -57,15 +63,15 @@ async def get_earliest_can_recommend_date(author_slug: str) -> Optional[date]:
counted_article_slugs.add(pervious_record.slug)

if (
now_record.ranking <= 30
and (now_record.date - pervious_record.date).days + 1 >= 10
now_record.ranking <= 30 # noqa: PLR2004
and (now_record.date - pervious_record.date).days + 1 >= 10 # noqa: PLR2004
) or (
now_record.ranking > 30
and (now_record.date - pervious_record.date).days + 1 >= 7
now_record.ranking > 30 # noqa: PLR2004
and (now_record.date - pervious_record.date).days + 1 >= 7 # noqa: PLR2004
):
return latest_onrank_record.date + timedelta(days=interval_days)

if pervious_record.ranking <= 30:
if pervious_record.ranking <= 30: # noqa: PLR2004
interval_days += 10
else:
interval_days += 7
Expand Down Expand Up @@ -119,7 +125,7 @@ async def get_word_freq_handler(

article_info = await article.info
title = article_info.title
text = article_info.text_content
text = article_info.content_text

word_freq = dict(Counter(await splitter.split(text)).most_common(100))

Expand All @@ -135,7 +141,7 @@ class GetLPRecommendCheckResponse(Struct, **RESPONSE_STRUCT_CONFIG):
article_title: str
can_recommend_now: bool
FP_reward: float = field(name="FPReward")
next_can_recommend_date: Optional[datetime]
next_can_recommend_date: datetime | None


@get(
Expand Down Expand Up @@ -184,7 +190,7 @@ async def get_LP_recommend_check_handler( # noqa: N802
article_fp_reward = article_info.earned_fp_amount
article_next_can_recommend_date = await get_earliest_can_recommend_date(author_slug)

can_recommend_now = article_fp_reward < 35 and (
can_recommend_now = article_fp_reward < 35 and ( # noqa: PLR2004
not article_next_can_recommend_date
or article_next_can_recommend_date <= datetime.now().date()
)
Expand Down
38 changes: 20 additions & 18 deletions backend/api/v1/jpep/ftn_macket.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from __future__ import annotations

from asyncio import gather
from datetime import datetime
from typing import Annotated, Literal, Optional
from typing import Annotated, Literal

from jkit.jpep.platform_settings import PlatformSettings
from jkit.jpep.rules import Rules
from litestar import Response, Router, get
from litestar.params import Parameter
from msgspec import Struct, field
from sshared.time import get_datetime_before_now, parse_td_str
from sshared.time import get_past_datetime_from_now, parse_td_str
from sspeedup.api.litestar import (
RESPONSE_STRUCT_CONFIG,
generate_response_spec,
Expand All @@ -21,7 +23,7 @@
"1d": "day",
}

PLATFORM_SETTINGS = PlatformSettings()
RULES = Rules()


class GetRulesResponse(Struct, **RESPONSE_STRUCT_CONFIG):
Expand All @@ -40,23 +42,23 @@ class GetRulesResponse(Struct, **RESPONSE_STRUCT_CONFIG):
},
)
async def get_rules_handler() -> Response:
settings = await PLATFORM_SETTINGS.get_data()
rules = await RULES.get_rules()

return success(
data=GetRulesResponse(
is_open=settings.opening,
is_open=rules.opening,
# TODO
buy_order_minimum_price=settings.ftn_sell_trade_minimum_price,
sell_order_minimum_price=settings.ftn_buy_trade_minimum_price,
FTN_order_fee=settings.ftn_trade_fee,
goods_order_fee=settings.goods_trade_fee,
buy_order_minimum_price=rules.ftn_buy_trade_minimum_price,
sell_order_minimum_price=rules.ftn_buy_trade_minimum_price,
FTN_order_fee=rules.ftn_trade_fee,
goods_order_fee=rules.goods_trade_fee,
)
)


class GetCurrentPriceResponse(Struct, **RESPONSE_STRUCT_CONFIG):
buy_price: Optional[float]
sell_price: Optional[float]
buy_price: float | None
sell_price: float | None


@get(
Expand All @@ -81,8 +83,8 @@ async def get_current_price_handler() -> Response:


class GetCurrentAmountResponse(Struct, **RESPONSE_STRUCT_CONFIG):
buy_amount: Optional[int]
sell_amount: Optional[int]
buy_amount: int | None
sell_amount: int | None


@get(
Expand Down Expand Up @@ -121,14 +123,14 @@ async def get_price_history_handler(
type_: Annotated[
Literal["buy", "sell"], Parameter(description="交易单类型", query="type")
],
range: Annotated[ # noqa: A002
range: Annotated[
Literal["24h", "7d", "15d", "30d"], Parameter(description="时间范围")
],
resolution: Annotated[Literal["5m", "1h", "1d"], Parameter(description="统计粒度")],
) -> Response:
history = await FTNMacketRecord.get_price_history(
type=type_.upper(), # type: ignore
start_time=get_datetime_before_now(parse_td_str(range)),
start_time=get_past_datetime_from_now(parse_td_str(range)),
resolution=RESOLUTION_MAPPING[resolution],
)

Expand All @@ -154,14 +156,14 @@ async def get_amount_history_handler(
type_: Annotated[
Literal["buy", "sell"], Parameter(description="交易单类型", query="type")
],
range: Annotated[ # noqa: A002
range: Annotated[
Literal["24h", "7d", "15d", "30d"], Parameter(description="时间范围")
],
resolution: Annotated[Literal["5m", "1h", "1d"], Parameter(description="统计粒度")],
) -> Response:
history = await FTNMacketRecord.get_amount_history(
type=type_.upper(), # type: ignore
start_time=get_datetime_before_now(parse_td_str(range)),
start_time=get_past_datetime_from_now(parse_td_str(range)),
resolution=RESOLUTION_MAPPING[resolution],
)

Expand Down
24 changes: 11 additions & 13 deletions backend/api/v1/lottery.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

from asyncio import gather
from datetime import datetime, timedelta
from typing import Annotated, Literal, Optional
from typing import Annotated, Literal

from jkit.identifier_convert import user_slug_to_url
from litestar import Response, Router, get
Expand All @@ -25,7 +27,7 @@
"锦鲤头像框1年",
]

RANGE_TO_TIMEDELTA: dict[str, Optional[timedelta]] = {
RANGE_TO_TIMEDELTA: dict[str, timedelta | None] = {
"1d": timedelta(days=1),
"7d": timedelta(days=7),
"30d": timedelta(days=30),
Expand All @@ -37,17 +39,13 @@
def get_summary_average_wins_count_per_winner(
wins_count: dict[str, int], winners_count: dict[str, int]
) -> dict[str, float]:
result: dict[str, float] = {}
result: dict[str, float] = dict.fromkeys(wins_count.keys(), 0.0)

for reward_name in wins_count:
# 该奖项无人中奖
if wins_count[reward_name] == 0:
result[reward_name] = 0
for reward_name, wins in wins_count.items():
if winners_count[reward_name] == 0:
continue

result[reward_name] = round(
wins_count[reward_name] / winners_count[reward_name], 3
)
result[reward_name] = round(wins / winners_count[reward_name], 3)

return result

Expand Down Expand Up @@ -115,7 +113,7 @@ async def get_records_handler(
offset: Annotated[int, Parameter(description="分页偏移", ge=0)] = 0,
limit: Annotated[int, Parameter(description="结果数量", gt=0, lt=100)] = 20,
excluded_awards: Annotated[
Optional[list[str]], Parameter(description="排除奖项列表", max_items=10)
list[str] | None, Parameter(description="排除奖项列表", max_items=10)
] = None,
) -> Response:
records: list[GetRecordsItem] = []
Expand Down Expand Up @@ -168,7 +166,7 @@ class GetSummaryResponse(Struct, **RESPONSE_STRUCT_CONFIG):
},
)
async def get_summary_handler(
range: Annotated[ # noqa: A002
range: Annotated[
Literal["1d", "7d", "30d", "all"], Parameter(description="时间范围")
],
) -> Response:
Expand Down Expand Up @@ -216,7 +214,7 @@ class GetRewardWinsHistoryResponse(Struct, **RESPONSE_STRUCT_CONFIG):
},
)
async def get_reward_wins_history_handler(
range: Annotated[Literal["1d", "30d", "60d"], Parameter(description="时间范围")], # noqa: A002
range: Annotated[Literal["1d", "30d", "60d"], Parameter(description="时间范围")],
resolution: Annotated[Literal["1h", "1d"], Parameter(description="统计粒度")],
) -> Response:
history = await LotteryWinRecord.get_wins_history(
Expand Down
30 changes: 14 additions & 16 deletions backend/api/v1/status.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

from datetime import datetime
from typing import Annotated, Optional
from typing import Annotated

from litestar import Response, Router, get
from litestar.params import Parameter
Expand All @@ -13,7 +15,7 @@
success,
)

from models.tool import StatusEnum, Tool
from models.tool import StatusType, Tool
from utils.config import CONFIG
from utils.tools_status import (
get_data_count,
Expand All @@ -39,23 +41,19 @@ async def get_handler() -> Response:
return success(
data=GetResponse(
version=VERSION,
downgraded_tools=list(
await Tool.get_tools_slugs_by_status(StatusEnum.DOWNGRADED)
),
unavailable_tools=list(
await Tool.get_tools_slugs_by_status(StatusEnum.UNAVAILABLE)
),
downgraded_tools=list(await Tool.get_tools_slugs_by_status("DOWNGRADED")),
unavailable_tools=list(await Tool.get_tools_slugs_by_status("UNAVAILABLE")),
)
)


class GetToolStatusResponse(Struct, **RESPONSE_STRUCT_CONFIG):
status: StatusEnum
reason: Optional[str]
last_update_time: Optional[datetime]
data_update_freq: Optional[str]
data_count: Optional[int]
data_source: Optional[dict[str, str]]
status: StatusType
reason: str | None
last_update_time: datetime | None
data_update_freq: str | None
data_count: int | None
data_source: dict[str, str] | None


@get(
Expand Down Expand Up @@ -83,15 +81,15 @@ async def get_tool_status_handler(
# 处理未填写 word_split_access_key 配置项的情况
if (
tool_name == "article-wordcloud-generator"
and tool.status == StatusEnum.NORMAL.value
and tool.status == "NORMAL"
and not (
CONFIG.word_split_access_key.access_key_id
and CONFIG.word_split_access_key.access_key_secret
)
):
return success(
data=GetToolStatusResponse(
status=StatusEnum.UNAVAILABLE,
status="UNAVAILABLE",
reason="后端未设置分词服务凭据",
last_update_time=last_update_time,
data_update_freq=tool.data_update_freq,
Expand Down
Loading

0 comments on commit 819b887

Please sign in to comment.