Skip to content

Commit 912e6a5

Browse files
Resolve issue 55 (#58)
* fix: ActionResult * feat: Exception * feat: 添加冻结机制 * docs: 完善文档
1 parent 635637f commit 912e6a5

File tree

13 files changed

+289
-14
lines changed

13 files changed

+289
-14
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
- 🔐 安全控制: 支持负余额限制
3737
- 📝 批量操作: 支持批量修改用户的货币数据
3838
- 🔍 时间范围审计日志: 从时间范围获取交易记录
39-
- 🚀 导出数据: 支持从Json文件导入导出到Json文件
39+
- 🚀 导出数据: 支持从Json文件导入/导出到Json文件
4040
- 🔧 依赖注入: 支持依赖注入模式调用
4141

4242
### 快速开始

docs/apis/advanced.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,44 @@ async def get_or_create_account(
148148
...
149149
```
150150

151+
### `~~.set_frozen`
152+
153+
```python
154+
async def set_frozen(
155+
account_id: str,
156+
frozen: bool,
157+
currency_id: str | None = None,
158+
session: AsyncSession | None = None,
159+
) -> None:
160+
"""设置账户特定货币的冻结状态
161+
162+
Args:
163+
account_id (str): 账户ID
164+
frozen (bool): 是否冻结
165+
currency_id (str | None, optional): 货币ID. Defaults to None.
166+
session (AsyncSession | None, optional): 异步Session. Defaults to None.
167+
"""
168+
...
169+
```
170+
171+
### `set_frozen_all`
172+
173+
```python
174+
async def set_frozen_all(
175+
account_id: str,
176+
frozen: bool,
177+
session: AsyncSession | None = None,
178+
):
179+
"""冻结这个账户ID下的所有货币储备
180+
181+
Args:
182+
account_id (str): 账户ID
183+
frozen (bool): 是否冻结
184+
session (AsyncSession | None, optional): 异步Session. Defaults to None.
185+
"""
186+
...
187+
```
188+
151189
### `~~.del_account`
152190

153191
```python

docs/apis/kernel.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,27 @@ class AccountRepository:
6666
"""获取或创建用户账户"""
6767
...
6868

69+
async def set_account_frozen(
70+
self,
71+
account_id: str,
72+
currency_id: str,
73+
frozen: bool,
74+
) -> None:
75+
"""设置账户冻结状态"""
76+
...
77+
78+
async def is_account_frozen(
79+
self,
80+
account_id: str,
81+
currency_id: str,
82+
) -> bool:
83+
"""判断账户是否冻结"""
84+
...
85+
6986
async def get_balance(self, account_id: str) -> float | None:
7087
"""获取账户余额"""
7188
...
72-
89+
7390
async def update_balance(
7491
self, account_id: str, amount: float, currency_id: str
7592
) -> tuple[float, float]:

docs/apis/standard.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,34 @@ async def list_accounts(currency_id: str | None = None) -> list[UserAccountData]
2121
...
2222
```
2323

24+
### `~~.set_frozen`
25+
26+
```python
27+
async def set_frozen(account_id: str, currency_id: str, frozen: bool) -> None:
28+
"""设置账户特定货币冻结状态
29+
30+
Args:
31+
account_id (str): 用户ID
32+
currency_id (str): 货币ID
33+
frozen (bool): 是否冻结
34+
"""
35+
...
36+
```
37+
38+
### `~~.set_frozen_all`
39+
40+
```python
41+
async def set_frozen_all(account_id: str, frozen: bool) -> None:
42+
"""冻结账户的所有货币资产
43+
44+
Args:
45+
account_id (str): 账户ID
46+
frozen (bool): 是否冻结
47+
"""
48+
...
49+
```
50+
51+
2452
### `~~.get_or_create_account`
2553

2654
```python

nonebot_plugin_value/api/api_balance.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,35 @@
88
from ..services.balance import del_balance as _d_balance
99
from ..services.balance import get_or_create_account as _go_account
1010
from ..services.balance import list_accounts as _list_accounts
11+
from ..services.balance import set_frozen as _set_frozen
12+
from ..services.balance import set_frozen_all as _set_frozen_all
1113
from ..services.balance import transfer_funds as _transfer
1214
from .api_currency import get_default_currency as _get_default
1315

1416

17+
async def set_frozen_all(account_id: str, frozen: bool) -> None:
18+
"""冻结账户的所有货币资产
19+
20+
Args:
21+
account_id (str): 账户ID
22+
frozen (bool): 是否冻结
23+
"""
24+
async with get_session() as session:
25+
await _set_frozen_all(account_id, frozen, session)
26+
27+
28+
async def set_frozen(account_id: str, currency_id: str, frozen: bool) -> None:
29+
"""设置账户特定货币冻结状态
30+
31+
Args:
32+
account_id (str): 用户ID
33+
currency_id (str): 货币ID
34+
frozen (bool): 是否冻结
35+
"""
36+
async with get_session() as session:
37+
await _set_frozen(account_id, frozen, currency_id, session)
38+
39+
1540
async def list_accounts(currency_id: str | None = None) -> list[UserAccountData]:
1641
"""获取指定货币(或默认)的账户列表
1742

nonebot_plugin_value/api/depends/data_classes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ async def __call__(self, event: Event) -> list[TransactionData]:
2424
)
2525
start_time, end_time = self.timerange
2626
return await get_transaction_history_by_time_range(
27-
to_uuid(event.get_user_id()), start_time, end_time, self.limit
28-
)
27+
to_uuid(event.get_user_id()), start_time, end_time, self.limit
28+
)
2929

3030

3131
@dataclass

nonebot_plugin_value/dump_tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ async def dump_data() -> MigrationData:
4141
for currency in currencies:
4242
accounts = await list_accounts(currency_id=currency.id)
4343
for account in accounts:
44-
transactions = await get_transaction_history(account.id)
44+
transactions = await get_transaction_history(account.id, 100)
4545
data.accounts.append(
4646
AccountMigrationData(
4747
account_data=account,

nonebot_plugin_value/exception.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
class BaseFailed(Exception):
2+
"""
3+
Base failed exception for all exceptions
4+
"""
5+
6+
7+
# start of basic exceptions
8+
9+
10+
class AccountException(BaseFailed):
11+
"""
12+
Account exception
13+
"""
14+
15+
16+
class CurrencyException(BaseFailed):
17+
"""
18+
Currency exception
19+
"""
20+
21+
22+
class TransactionException(BaseFailed):
23+
"""
24+
Transaction exception
25+
"""
26+
27+
28+
# end of basic exceptions
29+
30+
31+
# start of not found exceptions
32+
33+
34+
class NotFoundException(BaseFailed):
35+
"""
36+
Not found exception
37+
"""
38+
39+
40+
class AccountNotFound(NotFoundException, AccountException):
41+
"""
42+
Account not found exception
43+
"""
44+
45+
46+
class CurrencyNotFound(NotFoundException, CurrencyException):
47+
"""
48+
Currency not found exception
49+
"""
50+
51+
52+
class TransactionNotFound(NotFoundException, TransactionException):
53+
"""
54+
Transaction not found exception
55+
"""
56+
57+
58+
# end of not found exceptions
59+
60+
# start of other exceptions
61+
62+
63+
class AccountFrozen(AccountException):
64+
"""
65+
Account frozen exception (will be raised when trying to perform an action on a frozen account)
66+
"""
File renamed without changes.

nonebot_plugin_value/repository.py

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
from nonebot_plugin_orm import AsyncSession
77
from sqlalchemy import insert, select, update
88

9+
from .exception import (
10+
AccountFrozen,
11+
AccountNotFound,
12+
CurrencyNotFound,
13+
TransactionException,
14+
TransactionNotFound,
15+
)
916
from .models.balance import Transaction, UserAccount
1017
from .models.currency import CurrencyMeta
1118
from .pyd_models.currency_pyd import CurrencyData
@@ -96,7 +103,7 @@ async def remove_currency(self, currency_id: str):
96103
)
97104
).scalar_one_or_none()
98105
if not currency:
99-
raise ValueError("Currency not found")
106+
raise CurrencyNotFound(f"Currency {currency_id} not found")
100107
await session.delete(currency)
101108
users = await session.execute(
102109
select(UserAccount)
@@ -124,7 +131,7 @@ async def get_or_create_account(
124131
result = await session.execute(stmt)
125132
currency = result.scalar_one_or_none()
126133
if currency is None:
127-
raise ValueError(f"Currency {currency_id} not found")
134+
raise CurrencyNotFound(f"Currency {currency_id} not found")
128135

129136
# 检查账户是否存在
130137
stmt = (
@@ -158,6 +165,39 @@ async def get_or_create_account(
158165
session.add(account)
159166
return account
160167

168+
async def set_account_frozen(
169+
self,
170+
account_id: str,
171+
currency_id: str,
172+
frozen: bool,
173+
) -> None:
174+
"""设置账户冻结状态"""
175+
async with self.session as session:
176+
account = await self.get_or_create_account(account_id, currency_id)
177+
session.add(account)
178+
account.frozen = frozen
179+
await session.commit()
180+
181+
async def set_frozen_all(self, account_id: str, frozen: bool):
182+
async with self.session as session:
183+
result = await session.execute(
184+
select(UserAccount).where(UserAccount.id == account_id)
185+
)
186+
accounts = result.scalars().all()
187+
session.add_all(accounts)
188+
for account in accounts:
189+
account.frozen = frozen
190+
await session.commit()
191+
192+
async def is_account_frozen(
193+
self,
194+
account_id: str,
195+
currency_id: str,
196+
) -> bool:
197+
"""判断账户是否冻结"""
198+
async with self.session:
199+
return (await self.get_or_create_account(account_id, currency_id)).frozen
200+
161201
async def get_balance(self, account_id: str, currency_id: str) -> float | None:
162202
"""获取账户余额"""
163203
uni_id = get_uni_id(account_id, currency_id)
@@ -180,16 +220,21 @@ async def update_balance(
180220
).scalar_one_or_none()
181221

182222
if account is None:
183-
raise ValueError("Account not found")
223+
raise AccountNotFound("Account not found")
184224
session.add(account)
185225

226+
if account.frozen:
227+
raise AccountFrozen(
228+
f"Account {account_id} on currency {currency_id} is frozen"
229+
)
230+
186231
# 获取货币规则
187232
currency = await session.get(CurrencyMeta, account.currency_id)
188233
session.add(currency)
189234

190235
# 负余额检查
191236
if amount < 0 and not getattr(currency, "allow_negative", False):
192-
raise ValueError("Insufficient funds")
237+
raise TransactionException("Insufficient funds")
193238

194239
# 记录原始余额
195240
old_balance = account.balance
@@ -235,7 +280,7 @@ async def remove_account(self, account_id: str, currency_id: str | None = None):
235280
)
236281
accounts = (await session.execute(stmt)).scalars().all()
237282
if not accounts:
238-
raise ValueError("Account not found")
283+
raise AccountNotFound("Account not found")
239284
for account in accounts:
240285
await session.delete(account)
241286
await session.commit()
@@ -337,6 +382,6 @@ async def remove_transaction(self, transaction_id: str) -> None:
337382
)
338383
).scalar_one_or_none()
339384
if not transaction:
340-
raise ValueError("Transaction not found")
385+
raise TransactionNotFound("Transaction not found")
341386
await session.delete(transaction)
342387
await session.commit()

0 commit comments

Comments
 (0)