Skip to content

Commit bc92931

Browse files
Fix detached (#71)
* fix: 修复 DetachedInstanceError * 更新测试用例,CI/CD流程,修复部分已知问题 * style: 格式化代码 * 简化流程
1 parent 245b8dc commit bc92931

File tree

16 files changed

+304
-51
lines changed

16 files changed

+304
-51
lines changed

.github/workflows/CI.yml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ jobs:
2626
uv venv .venv
2727
uv sync
2828
29-
- name: Install dependencies
30-
run: |
31-
pipx install nb-cli
32-
3329
- name: Get Python path
3430
run: |
3531
PYTHON_BIN="$(uv run python -c 'import sys; print(sys.executable)')"
@@ -46,9 +42,10 @@ jobs:
4642
with:
4743
args: check . --exit-non-zero-on-fix
4844

49-
- name: Check database
50-
run: nb orm upgrade
51-
45+
- name: Prepare database
46+
run: uv run nb orm upgrade
47+
- name: Test
48+
run: uv run pytest ./tests/value_tests/*
5249
- name: Build package
5350
run: uv build # 生成构建产物到dist目录
5451

.github/workflows/PR.yml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ jobs:
2626
uv venv .venv
2727
uv sync
2828
29-
- name: Install dependencies
30-
run: |
31-
pipx install nb-cli
32-
3329
- name: Get Python path
3430
run: |
3531
PYTHON_BIN="$(uv run python -c 'import sys; print(sys.executable)')"
@@ -46,8 +42,11 @@ jobs:
4642
with:
4743
args: check . --exit-non-zero-on-fix
4844

49-
- name: Check database
50-
run: nb orm upgrade
45+
- name: Prepare database
46+
run: uv run nb orm upgrade
47+
48+
- name: Test
49+
run: uv run pytest ./tests/value_tests/*
5150

5251
- name: Build package
5352
run: uv build # 生成构建产物到dist目录

nonebot_plugin_value/api/api_balance.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ async def get_or_create_account(
9292
currency_id = (await _get_default()).id
9393
async with get_session() as session:
9494
data = await _go_account(user_id, currency_id, session)
95+
session.add(data)
9596
return UserAccountData.model_validate(data, from_attributes=True)
9697

9798

nonebot_plugin_value/api/api_currency.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ async def remove_currency(currency_id: str) -> None:
2929
3030
Args:
3131
currency_id (str): 货币唯一ID
32-
33-
Returns:
34-
bool: 是否删除成功
3532
"""
3633

3734
await _remove_currency(currency_id)

nonebot_plugin_value/hook/exception.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class CancelAction(BaseException):
1616
Exception raised when the user cancels an action.
1717
"""
1818

19+
1920
class DataUpdate(Exception):
2021
"""
2122
Exception raised when the data updated

nonebot_plugin_value/repository.py

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from nonebot import logger
77
from nonebot_plugin_orm import AsyncSession
8-
from sqlalchemy import delete, insert, select, update
8+
from sqlalchemy import delete, select, update
99

1010
from .exception import (
1111
AccountFrozen,
@@ -40,18 +40,19 @@ async def get_or_create_currency(
4040
)
4141
)
4242
if (currency := stmt.scalars().first()) is not None:
43+
session.add(currency)
4344
return currency, True
44-
await self.createcurrency(currency_data)
45-
result = await self.get_currency(currency_data.id)
46-
assert result is not None
45+
result = await self.createcurrency(currency_data)
4746
return result, False
4847

49-
async def createcurrency(self, currency_data: CurrencyData):
48+
async def createcurrency(self, currency_data: CurrencyData) -> CurrencyMeta:
5049
async with self.session as session:
5150
"""创建新货币"""
52-
stmt = insert(CurrencyMeta).values(**dict(currency_data))
53-
await session.execute(stmt)
51+
currency = CurrencyMeta(**currency_data.model_dump())
52+
session.add(currency)
5453
await session.commit()
54+
await session.refresh(currency)
55+
return currency
5556

5657
async def update_currency(self, currency_data: CurrencyData) -> CurrencyMeta:
5758
"""更新货币信息"""
@@ -162,6 +163,7 @@ async def get_or_create_account(
162163
)
163164
session.add(account)
164165
await session.commit()
166+
await session.refresh(account)
165167
return account
166168
except Exception:
167169
await session.rollback()
@@ -345,18 +347,9 @@ async def create_transaction(
345347
)
346348
session.add(transaction_data)
347349
await session.commit()
348-
stmt = (
349-
select(Transaction)
350-
.where(
351-
Transaction.id == uuid,
352-
)
353-
.with_for_update()
354-
)
355-
result = await session.execute(stmt)
356-
transaction = result.scalar_one_or_none()
357-
assert transaction, f"无法读取到交易记录[... WHERE id = {uuid} ...]!"
358-
session.add(transaction)
359-
return transaction
350+
await session.refresh(transaction_data)
351+
session.add(transaction_data)
352+
return transaction_data
360353

361354
async def get_transaction_history(
362355
self, account_id: str, limit: int = 100

nonebot_plugin_value/services/balance.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,6 @@ async def batch_del_balance(
149149

150150
if not all(r.success for r in result_list):
151151
return [] if not return_all_on_fail else result_list
152-
await session.commit()
153152
return result_list
154153

155154

@@ -261,7 +260,6 @@ async def batch_add_balance(
261260
result_list.append(data)
262261
if not all(r.success for r in result_list):
263262
return [] if not return_all_on_fail else result_list
264-
await session.commit()
265263
return result_list
266264

267265

@@ -323,15 +321,12 @@ async def add_balance(
323321
balance_before,
324322
balance_after,
325323
)
326-
# 在更新余额前重新获取账户对象以避免DetachedInstanceError
327-
updated_account = await account_repo.get_or_create_account(user_id, currency_id)
324+
account = await account_repo.get_or_create_account(user_id, currency_id)
328325
await account_repo.update_balance(
329-
updated_account.id,
326+
account.id,
330327
balance_after,
331328
currency_id,
332329
)
333-
if arg_session is None:
334-
await session.commit()
335330
try:
336331
await HooksManager().run_hooks(
337332
HooksType.post(),
@@ -428,12 +423,12 @@ async def transfer_funds(
428423
return TransferResult(success=True, message=f"取消了交易:{e.message}")
429424
from_balance_before, from_balance_after = await account_repo.update_balance(
430425
from_account_id,
431-
-amount,
426+
from_balance_before - amount,
432427
currency_id,
433428
)
434429
to_balance_before, to_balance_after = await account_repo.update_balance(
435430
to_account_id,
436-
amount,
431+
to_balance_before + amount,
437432
currency_id,
438433
)
439434
timestamp = datetime.now(timezone.utc)
@@ -457,10 +452,6 @@ async def transfer_funds(
457452
balance_after=to_balance_after,
458453
timestamp=timestamp,
459454
)
460-
461-
# 提交事务
462-
if arg_session is None:
463-
await session.commit()
464455
try:
465456
await HooksManager().run_hooks(
466457
HooksType.post(),

nonebot_plugin_value/services/transaction.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ async def remove_transaction(
7272
await TransactionRepository(session).remove_transaction(transaction_id)
7373
return True
7474
except Exception:
75-
await session.rollback()
7675
if fail_then_throw:
7776
raise
7877
return False

pyproject.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "nonebot-plugin-value"
3-
version = "0.1.3.post1"
3+
version = "0.1.3.post2"
44
description = "Economy API for NoneBot2"
55
readme = "README.md"
66
requires-python = ">=3.10, <4.0.0"
@@ -12,6 +12,7 @@ dependencies = [
1212
"nonebot-plugin-localstore>=0.7.4",
1313
"nonebot-plugin-orm>=0.8.2",
1414
"nonebot2>=2.4.2",
15+
1516
"sqlalchemy>=2.0.41",
1617
]
1718

@@ -25,6 +26,8 @@ dev-dependencies = [
2526
"aiosqlite>=0.19.0",
2627
"aiomysql>=0.2.0",
2728
"nonebot2[fastapi]>=2.4.2",
29+
"nonebug>=0.4.3",
30+
"pytest-asyncio>=1.1.0",
2831
]
2932

3033
[tool.uv.pip]
@@ -64,5 +67,9 @@ typeCheckingMode = "strict"
6467

6568
# Nonebot Test
6669

70+
[tool.pytest.ini_options]
71+
asyncio_mode = "auto"
72+
asyncio_default_fixture_loop_scope = "session"
73+
6774
[tool.nonebot]
6875
plugins = ["nonebot_plugin_value"]

tests/conftest.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import sys
2+
from pathlib import Path
3+
4+
import nonebot
5+
import pytest
6+
from pytest_asyncio import is_async_test
7+
8+
# 将项目根目录添加到sys.path中
9+
project_root = Path(__file__).parent.parent
10+
sys.path.insert(0, str(project_root))
11+
12+
13+
def pytest_collection_modifyitems(items: list[pytest.Item]):
14+
pytest_asyncio_tests = (item for item in items if is_async_test(item))
15+
session_scope_marker = pytest.mark.asyncio(loop_scope="session")
16+
for async_test in pytest_asyncio_tests:
17+
async_test.add_marker(session_scope_marker, append=False)
18+
19+
20+
@pytest.fixture(scope="session", autouse=True)
21+
async def after_nonebot_init(after_nonebot_init: None):
22+
# 加载插件
23+
nonebot.load_from_toml("pyproject.toml")

0 commit comments

Comments
 (0)