Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ jobs:
uv venv .venv
uv sync

- name: Install dependencies
run: |
pipx install nb-cli

- name: Get Python path
run: |
PYTHON_BIN="$(uv run python -c 'import sys; print(sys.executable)')"
Expand All @@ -46,9 +42,10 @@ jobs:
with:
args: check . --exit-non-zero-on-fix

- name: Check database
run: nb orm upgrade

- name: Prepare database
run: uv run nb orm upgrade
- name: Test
run: uv run pytest ./tests/value_tests/*
- name: Build package
run: uv build # 生成构建产物到dist目录

Expand Down
11 changes: 5 additions & 6 deletions .github/workflows/PR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ jobs:
uv venv .venv
uv sync

- name: Install dependencies
run: |
pipx install nb-cli

- name: Get Python path
run: |
PYTHON_BIN="$(uv run python -c 'import sys; print(sys.executable)')"
Expand All @@ -46,8 +42,11 @@ jobs:
with:
args: check . --exit-non-zero-on-fix

- name: Check database
run: nb orm upgrade
- name: Prepare database
run: uv run nb orm upgrade

- name: Test
run: uv run pytest ./tests/value_tests/*

- name: Build package
run: uv build # 生成构建产物到dist目录
1 change: 1 addition & 0 deletions nonebot_plugin_value/api/api_balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ async def get_or_create_account(
currency_id = (await _get_default()).id
async with get_session() as session:
data = await _go_account(user_id, currency_id, session)
session.add(data)
return UserAccountData.model_validate(data, from_attributes=True)


Expand Down
3 changes: 0 additions & 3 deletions nonebot_plugin_value/api/api_currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ async def remove_currency(currency_id: str) -> None:

Args:
currency_id (str): 货币唯一ID

Returns:
bool: 是否删除成功
"""

await _remove_currency(currency_id)
Expand Down
1 change: 1 addition & 0 deletions nonebot_plugin_value/hook/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class CancelAction(BaseException):
Exception raised when the user cancels an action.
"""


class DataUpdate(Exception):
"""
Exception raised when the data updated
Expand Down
31 changes: 12 additions & 19 deletions nonebot_plugin_value/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from nonebot import logger
from nonebot_plugin_orm import AsyncSession
from sqlalchemy import delete, insert, select, update
from sqlalchemy import delete, select, update

from .exception import (
AccountFrozen,
Expand Down Expand Up @@ -40,18 +40,19 @@ async def get_or_create_currency(
)
)
if (currency := stmt.scalars().first()) is not None:
session.add(currency)
return currency, True
await self.createcurrency(currency_data)
result = await self.get_currency(currency_data.id)
assert result is not None
result = await self.createcurrency(currency_data)
return result, False

async def createcurrency(self, currency_data: CurrencyData):
async def createcurrency(self, currency_data: CurrencyData) -> CurrencyMeta:
async with self.session as session:
"""创建新货币"""
stmt = insert(CurrencyMeta).values(**dict(currency_data))
await session.execute(stmt)
currency = CurrencyMeta(**currency_data.model_dump())
session.add(currency)
await session.commit()
await session.refresh(currency)
return currency

async def update_currency(self, currency_data: CurrencyData) -> CurrencyMeta:
"""更新货币信息"""
Expand Down Expand Up @@ -162,6 +163,7 @@ async def get_or_create_account(
)
session.add(account)
await session.commit()
await session.refresh(account)
return account
except Exception:
await session.rollback()
Expand Down Expand Up @@ -345,18 +347,9 @@ async def create_transaction(
)
session.add(transaction_data)
await session.commit()
stmt = (
select(Transaction)
.where(
Transaction.id == uuid,
)
.with_for_update()
)
result = await session.execute(stmt)
transaction = result.scalar_one_or_none()
assert transaction, f"无法读取到交易记录[... WHERE id = {uuid} ...]!"
session.add(transaction)
return transaction
await session.refresh(transaction_data)
session.add(transaction_data)
return transaction_data

async def get_transaction_history(
self, account_id: str, limit: int = 100
Expand Down
17 changes: 4 additions & 13 deletions nonebot_plugin_value/services/balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ async def batch_del_balance(

if not all(r.success for r in result_list):
return [] if not return_all_on_fail else result_list
await session.commit()
return result_list


Expand Down Expand Up @@ -261,7 +260,6 @@ async def batch_add_balance(
result_list.append(data)
if not all(r.success for r in result_list):
return [] if not return_all_on_fail else result_list
await session.commit()
return result_list


Expand Down Expand Up @@ -323,15 +321,12 @@ async def add_balance(
balance_before,
balance_after,
)
# 在更新余额前重新获取账户对象以避免DetachedInstanceError
updated_account = await account_repo.get_or_create_account(user_id, currency_id)
account = await account_repo.get_or_create_account(user_id, currency_id)
await account_repo.update_balance(
updated_account.id,
account.id,
balance_after,
currency_id,
)
if arg_session is None:
await session.commit()
try:
await HooksManager().run_hooks(
HooksType.post(),
Expand Down Expand Up @@ -428,12 +423,12 @@ async def transfer_funds(
return TransferResult(success=True, message=f"取消了交易:{e.message}")
from_balance_before, from_balance_after = await account_repo.update_balance(
from_account_id,
-amount,
from_balance_before - amount,
currency_id,
)
to_balance_before, to_balance_after = await account_repo.update_balance(
to_account_id,
amount,
to_balance_before + amount,
currency_id,
)
timestamp = datetime.now(timezone.utc)
Expand All @@ -457,10 +452,6 @@ async def transfer_funds(
balance_after=to_balance_after,
timestamp=timestamp,
)

# 提交事务
if arg_session is None:
await session.commit()
try:
await HooksManager().run_hooks(
HooksType.post(),
Expand Down
1 change: 0 additions & 1 deletion nonebot_plugin_value/services/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ async def remove_transaction(
await TransactionRepository(session).remove_transaction(transaction_id)
return True
except Exception:
await session.rollback()
if fail_then_throw:
raise
return False
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "nonebot-plugin-value"
version = "0.1.3.post1"
version = "0.1.3.post2"
description = "Economy API for NoneBot2"
readme = "README.md"
requires-python = ">=3.10, <4.0.0"
Expand All @@ -12,6 +12,7 @@ dependencies = [
"nonebot-plugin-localstore>=0.7.4",
"nonebot-plugin-orm>=0.8.2",
"nonebot2>=2.4.2",

"sqlalchemy>=2.0.41",
]

Expand All @@ -25,6 +26,8 @@ dev-dependencies = [
"aiosqlite>=0.19.0",
"aiomysql>=0.2.0",
"nonebot2[fastapi]>=2.4.2",
"nonebug>=0.4.3",
"pytest-asyncio>=1.1.0",
]

[tool.uv.pip]
Expand Down Expand Up @@ -64,5 +67,9 @@ typeCheckingMode = "strict"

# Nonebot Test

[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "session"

[tool.nonebot]
plugins = ["nonebot_plugin_value"]
23 changes: 23 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import sys
from pathlib import Path

import nonebot
import pytest
from pytest_asyncio import is_async_test

# 将项目根目录添加到sys.path中
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))


def pytest_collection_modifyitems(items: list[pytest.Item]):
pytest_asyncio_tests = (item for item in items if is_async_test(item))
session_scope_marker = pytest.mark.asyncio(loop_scope="session")
for async_test in pytest_asyncio_tests:
async_test.add_marker(session_scope_marker, append=False)


@pytest.fixture(scope="session", autouse=True)
async def after_nonebot_init(after_nonebot_init: None):
# 加载插件
nonebot.load_from_toml("pyproject.toml")
19 changes: 19 additions & 0 deletions tests/value_tests/balance_transfer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest
from nonebug import App # type:ignore


@pytest.mark.asyncio
async def test_transfer(app: App):
from nonebot_plugin_value.api.api_balance import (
add_balance,
get_or_create_account,
transfer_funds,
)
from nonebot_plugin_value.uuid_lib import to_uuid

u1 = to_uuid("u1")
u2 = to_uuid("u2")
await get_or_create_account(u1)
await get_or_create_account(u2)
await add_balance(u1, 100)
assert (await transfer_funds(u1, u2, 50)).balance == 50
44 changes: 44 additions & 0 deletions tests/value_tests/batch_run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import pytest
from nonebug import App # type:ignore


@pytest.mark.asyncio
async def test_batch_operations(app: App):
from nonebot_plugin_value.api.api_balance import (
batch_add_balance,
batch_del_balance,
del_account,
get_or_create_account,
list_accounts,
)
from nonebot_plugin_value.api.api_currency import (
CurrencyData,
get_or_create_currency,
list_currencies,
)
from nonebot_plugin_value.uuid_lib import to_uuid

ac_uid: list[str] = [to_uuid(f"account{i}") for i in range(100)]
cu_id = to_uuid("currency")
cu_data = CurrencyData(id=cu_id, display_name="currency", symbol="C")
await get_or_create_currency(cu_data)
for uid in ac_uid:
await get_or_create_account(uid)
await get_or_create_account(uid, cu_id)
await batch_add_balance(
[(uid, 1) for uid in ac_uid],
)
await batch_add_balance(
[(uid, 1) for uid in ac_uid],
currency_id=cu_id,
)
await batch_del_balance(
[(uid, 1) for uid in ac_uid],
)
await batch_del_balance(
[(uid, 1) for uid in ac_uid],
currency_id=cu_id,
)
await list_accounts()
assert all([await del_account(uid) for uid in ac_uid])
await list_currencies()
30 changes: 30 additions & 0 deletions tests/value_tests/create_currency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import pytest
from nonebug import App # type: ignore


@pytest.mark.asyncio
async def test_new_currency(app: App):
from nonebot_plugin_value.api.api_balance import (
add_balance,
del_account,
del_balance,
get_or_create_account,
)
from nonebot_plugin_value.api.api_currency import (
CurrencyData,
get_currency,
get_or_create_currency,
remove_currency,
)
from nonebot_plugin_value.uuid_lib import to_uuid

c_id = "114514"
u_id = to_uuid("114514")
currency = CurrencyData(id=c_id, display_name="test", symbol="t")
await get_or_create_currency(currency)
await get_or_create_account(u_id, c_id)
await add_balance(u_id, 114514, "1919810", c_id)
await del_balance(u_id, 114514, "1919810", c_id)
assert await del_account(u_id, c_id)
await remove_currency(c_id)
assert not await get_currency(c_id)
27 changes: 27 additions & 0 deletions tests/value_tests/default_account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import pytest
from nonebug import App # type: ignore


@pytest.mark.asyncio
async def test_balance(app: App):
from nonebot_plugin_value.api.api_balance import (
add_balance,
del_account,
del_balance,
get_or_create_account,
)
from nonebot_plugin_value.uuid_lib import to_uuid

account = to_uuid("Test_Example")
await get_or_create_account(account)
await add_balance(
account,
100,
"add",
)
await del_balance(
account,
100,
"del",
)
assert await del_account(account)
Loading
Loading