rainycode 1.1.9__tar.gz → 1.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {rainycode-1.1.9 → rainycode-1.3.0}/PKG-INFO +5 -3
  2. {rainycode-1.1.9 → rainycode-1.3.0}/common_base/aiorequests.py +3 -3
  3. {rainycode-1.1.9 → rainycode-1.3.0}/common_base/exception.py +2 -1
  4. {rainycode-1.1.9 → rainycode-1.3.0}/common_base/logging.py +2 -2
  5. {rainycode-1.1.9 → rainycode-1.3.0}/common_depends/auth_depend.py +7 -3
  6. {rainycode-1.1.9 → rainycode-1.3.0}/common_models/base_model.py +21 -12
  7. {rainycode-1.1.9 → rainycode-1.3.0}/common_models/user_model.py +1 -0
  8. {rainycode-1.1.9 → rainycode-1.3.0}/common_models/wechat_model.py +4 -1
  9. {rainycode-1.1.9 → rainycode-1.3.0}/common_servers/api/auth_api.py +2 -2
  10. {rainycode-1.1.9 → rainycode-1.3.0}/common_servers/api/wechat_api.py +3 -2
  11. {rainycode-1.1.9 → rainycode-1.3.0}/common_servers/router.py +1 -2
  12. {rainycode-1.1.9 → rainycode-1.3.0}/common_servers/schemas/auth_schema.py +2 -1
  13. {rainycode-1.1.9 → rainycode-1.3.0}/common_servers/service/auth_svc.py +32 -28
  14. {rainycode-1.1.9 → rainycode-1.3.0}/common_servers/service/wechat_svc.py +2 -1
  15. {rainycode-1.1.9 → rainycode-1.3.0}/common_utils/bcrypt_util.py +0 -1
  16. {rainycode-1.1.9 → rainycode-1.3.0}/common_utils/captcha_util.py +1 -0
  17. {rainycode-1.1.9 → rainycode-1.3.0}/common_utils/ip_util.py +1 -0
  18. {rainycode-1.1.9 → rainycode-1.3.0}/common_utils/jwt_util.py +3 -1
  19. {rainycode-1.1.9 → rainycode-1.3.0}/common_utils/snowflake_util.py +2 -2
  20. {rainycode-1.1.9 → rainycode-1.3.0}/common_utils/wechat_util.py +5 -3
  21. {rainycode-1.1.9 → rainycode-1.3.0}/core/databases/aiodb.py +29 -28
  22. {rainycode-1.1.9 → rainycode-1.3.0}/core/databases/aioredis.py +17 -16
  23. {rainycode-1.1.9 → rainycode-1.3.0}/core/middleware/http_middleware.py +4 -2
  24. {rainycode-1.1.9 → rainycode-1.3.0}/core/start.py +16 -6
  25. {rainycode-1.1.9 → rainycode-1.3.0}/pyproject.toml +9 -7
  26. {rainycode-1.1.9 → rainycode-1.3.0}/rainycode.egg-info/PKG-INFO +5 -3
  27. {rainycode-1.1.9 → rainycode-1.3.0}/rainycode.egg-info/SOURCES.txt +0 -1
  28. {rainycode-1.1.9 → rainycode-1.3.0}/rainycode.egg-info/requires.txt +4 -1
  29. rainycode-1.1.9/common_servers/schemas/__init__.py +0 -0
  30. {rainycode-1.1.9 → rainycode-1.3.0}/common_base/consts.py +0 -0
  31. {rainycode-1.1.9 → rainycode-1.3.0}/common_base/response.py +0 -0
  32. {rainycode-1.1.9 → rainycode-1.3.0}/common_models/__init__.py +0 -0
  33. {rainycode-1.1.9 → rainycode-1.3.0}/core/base_config.py +0 -0
  34. {rainycode-1.1.9 → rainycode-1.3.0}/rainycode.egg-info/dependency_links.txt +0 -0
  35. {rainycode-1.1.9 → rainycode-1.3.0}/rainycode.egg-info/top_level.txt +0 -0
  36. {rainycode-1.1.9 → rainycode-1.3.0}/setup.cfg +0 -0
@@ -1,9 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rainycode
3
- Version: 1.1.9
3
+ Version: 1.3.0
4
4
  Summary: FastAPI base modules
5
- Requires-Python: >=3.8
6
- Requires-Dist: build==1.4.0
5
+ Requires-Python: >=3.10
7
6
  Requires-Dist: aerich==0.9.2
8
7
  Requires-Dist: fastapi==0.133.1
9
8
  Requires-Dist: aiohttp==3.13.3
@@ -18,3 +17,6 @@ Requires-Dist: pycryptodome==3.23.0
18
17
  Requires-Dist: uvicorn[standard]==0.41.0
19
18
  Requires-Dist: tortoise-orm[asyncmy]==0.25.4
20
19
  Requires-Dist: pydantic-settings==2.13.1
20
+ Provides-Extra: release
21
+ Requires-Dist: build==1.4.0; extra == "release"
22
+ Requires-Dist: twine==6.1.0; extra == "release"
@@ -1,8 +1,8 @@
1
1
  import json
2
2
  import ssl
3
- import certifi
4
- import aiohttp
5
3
 
4
+ import aiohttp
5
+ import certifi
6
6
 
7
7
  # 模块级别 SSL 上下文,使用 certifi 证书,避免重复创建
8
8
  _SSL_CONTEXT = ssl.create_default_context(cafile=certifi.where())
@@ -10,7 +10,7 @@ _SSL_CONTEXT = ssl.create_default_context(cafile=certifi.where())
10
10
 
11
11
  def parse_url(url, params=None, suffix=None):
12
12
  if params:
13
- query = "&".join(["{}={}".format(k, params[k]) for k in sorted(params.keys())])
13
+ query = "&".join([f"{k}={params[k]}" for k in sorted(params.keys())])
14
14
  if "?" in url:
15
15
  url += "&" + query
16
16
  else:
@@ -1,5 +1,6 @@
1
- from common_base.logging import logger
2
1
  from typing import cast
2
+
3
+ from common_base.logging import logger
3
4
  from common_base.response import ErrorReturn
4
5
  from fastapi import HTTPException, Request, status
5
6
  from fastapi.exceptions import RequestValidationError, ResponseValidationError
@@ -1,12 +1,12 @@
1
+ import logging
1
2
  import os
2
3
  import sys
3
- import logging
4
4
  import traceback
5
5
 
6
6
 
7
7
  def logging_config(app_name: str):
8
8
  # 日志目录
9
- log_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../log"))
9
+ log_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../logs"))
10
10
  if not os.path.exists(log_dir):
11
11
  os.mkdir(log_dir)
12
12
  file_path = log_dir + '/' + app_name + '.log'
@@ -1,11 +1,15 @@
1
1
  import json
2
- from fastapi import Depends, Request
3
- from fastapi.security import OAuth2PasswordBearer, SecurityScopes
4
- from common_base.consts import REDIS_PREFIX_BLOCKLIST, REDIS_PREFIX_USER_INFO
2
+
3
+ from common_base.consts import (
4
+ REDIS_PREFIX_BLOCKLIST,
5
+ REDIS_PREFIX_USER_INFO,
6
+ )
5
7
  from common_base.exception import CustomException
6
8
  from common_models.user_model import User
7
9
  from common_utils.jwt_util import JwtUtil
8
10
  from core.databases.aioredis import AioRedis
11
+ from fastapi import Depends, Request
12
+ from fastapi.security import OAuth2PasswordBearer, SecurityScopes
9
13
 
10
14
  oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/auth_center/login")
11
15
 
@@ -1,12 +1,13 @@
1
1
  import uuid
2
2
  from collections.abc import Iterable
3
- from typing_extensions import Self
4
- from typing import Optional, Any
5
- from tortoise import fields, BaseDBAsyncClient
6
- from tortoise.expressions import Q
7
- from tortoise.queryset import BulkCreateQuery, QuerySet, QuerySetSingle
3
+ from typing import Any, Optional
4
+
8
5
  from common_utils.snowflake_util import get_snowflake_id
9
6
  from core.databases.aiodb import MySQLUtils
7
+ from tortoise import BaseDBAsyncClient, fields
8
+ from tortoise.expressions import Q
9
+ from tortoise.queryset import BulkCreateQuery, QuerySet, QuerySetSingle
10
+ from typing_extensions import Self
10
11
 
11
12
 
12
13
  class BaseModel(MySQLUtils):
@@ -14,7 +15,7 @@ class BaseModel(MySQLUtils):
14
15
  @classmethod
15
16
  async def create(
16
17
  cls,
17
- using_db: Optional[BaseDBAsyncClient] = None,
18
+ using_db: BaseDBAsyncClient | None = None,
18
19
  **kwargs: Any
19
20
  ) -> Self:
20
21
  if not kwargs.get('id', 0) and 'id' in cls._meta.fields:
@@ -51,11 +52,11 @@ class BaseModel(MySQLUtils):
51
52
  def bulk_create(
52
53
  cls,
53
54
  objects: Iterable[Self],
54
- batch_size: Optional[int] = None,
55
+ batch_size: int | None = None,
55
56
  ignore_conflicts: bool = False,
56
- update_fields: Optional[Iterable[str]] = None,
57
- on_conflict: Optional[Iterable[str]] = None,
58
- using_db: Optional[BaseDBAsyncClient] = None,
57
+ update_fields: Iterable[str] | None = None,
58
+ on_conflict: Iterable[str] | None = None,
59
+ using_db: BaseDBAsyncClient | None = None,
59
60
  **kwargs: Any
60
61
  ) -> "BulkCreateQuery[Self]":
61
62
  if 'id' in cls._meta.fields:
@@ -80,8 +81,8 @@ class BaseModel(MySQLUtils):
80
81
 
81
82
  async def save(
82
83
  self,
83
- using_db: Optional[BaseDBAsyncClient] = None,
84
- update_fields: Optional[Iterable[str]] = None,
84
+ using_db: BaseDBAsyncClient | None = None,
85
+ update_fields: Iterable[str] | None = None,
85
86
  force_create: bool = False,
86
87
  force_update: bool = False,
87
88
  **kwargs: Any
@@ -92,6 +93,14 @@ class BaseModel(MySQLUtils):
92
93
  force_update=force_update)
93
94
 
94
95
 
96
+ class TimeModel(BaseModel):
97
+ create_time = fields.DatetimeField(auto_now_add=True, description='创建时间')
98
+ update_time = fields.DatetimeField(auto_now=True, description='更新时间')
99
+
100
+ class Meta:
101
+ abstract = True
102
+
103
+
95
104
  class CreateModel(BaseModel):
96
105
  create_time = fields.DatetimeField(auto_now_add=True, description='创建时间')
97
106
  creator = fields.CharField(max_length=50, null=True, description="创建人")
@@ -1,4 +1,5 @@
1
1
  from tortoise import fields
2
+
2
3
  from common_models.base_model import UpdateModel
3
4
 
4
5
 
@@ -1,7 +1,10 @@
1
- from tortoise import fields
2
1
  from enum import Enum
2
+
3
+ from tortoise import fields
4
+
3
5
  from common_models.base_model import CreateModel
4
6
 
7
+
5
8
  class AppTypeEnum(str, Enum):
6
9
  MINI_PROGRAM = "mini_program"
7
10
  OFFICIAL_ACCOUNT = "official_account"
@@ -1,9 +1,9 @@
1
- from common_models.user_model import User
2
- from fastapi import APIRouter, Security, Body, Query, Request
3
1
  from common_base.response import SuccessReturn
4
2
  from common_depends.auth_depend import login_require
3
+ from common_models.user_model import User
5
4
  from common_servers.schemas.auth_schema import *
6
5
  from common_servers.service.auth_svc import AuthService
6
+ from fastapi import APIRouter, Body, Query, Request, Security
7
7
 
8
8
  router = APIRouter(prefix="/auth")
9
9
 
@@ -1,12 +1,13 @@
1
1
  import json
2
2
  from urllib.parse import urlencode
3
- from fastapi import APIRouter, Request, Query, Response
4
- from fastapi.responses import RedirectResponse
3
+
5
4
  from common_base.consts import REDIS_PREFIX_OAUTH_STATE
6
5
  from common_base.exception import CustomException
7
6
  from common_servers.service.auth_svc import AuthService
8
7
  from common_servers.service.wechat_svc import WechatService
9
8
  from core.databases.aioredis import AioRedis
9
+ from fastapi import APIRouter, Query, Request, Response
10
+ from fastapi.responses import RedirectResponse
10
11
 
11
12
  router = APIRouter(prefix="/wechat")
12
13
 
@@ -1,7 +1,6 @@
1
- from fastapi import APIRouter
2
1
  from common_servers.api.auth_api import router as auth_router
3
2
  from common_servers.api.wechat_api import router as wechat_router
4
-
3
+ from fastapi import APIRouter
5
4
 
6
5
  base_router = APIRouter()
7
6
 
@@ -1,4 +1,5 @@
1
- from pydantic import BaseModel, Field, EmailStr
1
+ from pydantic import BaseModel, Field
2
+
2
3
 
3
4
  class UserLogin(BaseModel):
4
5
  """用户登录模型"""
@@ -1,32 +1,36 @@
1
- from datetime import datetime, timedelta
2
- import uuid
3
1
  import json
4
2
  import re
3
+ import uuid
4
+ from datetime import datetime, timedelta
5
5
  from urllib.parse import urlencode
6
- from fastapi.responses import RedirectResponse
6
+
7
7
  from common_base.consts import (
8
8
  REDIS_PREFIX_BLOCKLIST,
9
- REDIS_PREFIX_REFRESH_TOKEN,
9
+ REDIS_PREFIX_LOGIN_LOCK_IP,
10
+ REDIS_PREFIX_LOGIN_LOCK_USER,
10
11
  REDIS_PREFIX_OAUTH_STATE,
12
+ REDIS_PREFIX_REFRESH_TOKEN,
11
13
  REDIS_PREFIX_USER_INFO,
12
14
  REDIS_PREFIX_WECHAT_CODE,
13
15
  REDIS_PREFIX_WECHAT_OAUTH_CODE,
14
- REDIS_PREFIX_LOGIN_LOCK_USER,
15
- REDIS_PREFIX_LOGIN_LOCK_IP,
16
16
  )
17
17
  from common_base.exception import CustomException
18
18
  from common_base.logging import logger
19
19
  from common_models.user_model import User
20
- from common_models.wechat_model import WechatApp, WechatUser, AppTypeEnum
20
+ from common_models.wechat_model import AppTypeEnum, WechatApp, WechatUser
21
+ from common_servers.schemas.auth_schema import (
22
+ UserLogin,
23
+ UserRegister,
24
+ WechatLogin,
25
+ )
21
26
  from common_utils.bcrypt_util import PwdUtil
22
27
  from common_utils.jwt_util import JwtUtil
23
28
  from common_utils.wechat_util import WechatUtil
29
+ from core.base_config import base_config
24
30
  from core.databases.aioredis import AioRedis
25
- from common_servers.schemas.auth_schema import UserRegister, UserLogin, WechatLogin
26
- from tortoise.transactions import atomic
31
+ from fastapi.responses import RedirectResponse
27
32
  from tortoise.expressions import Q
28
- from core.base_config import base_config
29
-
33
+ from tortoise.transactions import atomic
30
34
 
31
35
  # 登录限流配置
32
36
  LOGIN_FAIL_LIMIT = 5 # 最大失败次数
@@ -219,14 +223,14 @@ class AuthService:
219
223
  raise CustomException(msg="授权码已失效,请重新授权")
220
224
 
221
225
  # 获取公众号配置
222
- app_config = await WechatApp.get_or_none(id=app_pk, app_type=AppTypeEnum.OFFICIAL_ACCOUNT)
223
- if not app_config:
226
+ wechat_app = await WechatApp.get_or_none(id=app_pk, app_type=AppTypeEnum.OFFICIAL_ACCOUNT)
227
+ if not wechat_app:
224
228
  raise CustomException(msg="公众号配置不存在")
225
229
 
226
230
  # 用 code 换取 access_token 和 openid
227
231
  wx_res = await WechatUtil.get_oauth_access_token(
228
- app_config.app_id,
229
- app_config.app_secret,
232
+ wechat_app.app_id,
233
+ wechat_app.app_secret,
230
234
  code
231
235
  )
232
236
  openid = wx_res.get("openid")
@@ -239,7 +243,7 @@ class AuthService:
239
243
  await AioRedis.set(code_key, "1", ex=300)
240
244
 
241
245
  # 检查用户是否已存在
242
- wechat_user = await WechatUser.get_or_none(openid=openid, wechat_app=app_config).select_related("user")
246
+ wechat_user = await WechatUser.get_or_none(openid=openid, wechat_app=wechat_app).select_related("user")
243
247
 
244
248
  if wechat_user:
245
249
  # 用户已存在,直接登录
@@ -257,7 +261,7 @@ class AuthService:
257
261
  # 使用 _bind_or_register_wechat_user 创建或绑定用户
258
262
  user = await cls._bind_or_register_wechat_user(
259
263
  openid=openid,
260
- app_config=app_config,
264
+ wechat_app=wechat_app,
261
265
  unionid=wx_res.get("unionid"),
262
266
  nickname=user_info.get("nickname"),
263
267
  avatar=user_info.get("headimgurl")
@@ -275,7 +279,7 @@ class AuthService:
275
279
  api_prefix = f"/api/{base_config.app_name}" if base_config.app_name else ""
276
280
  callback_url = f"{base_config.base_url}{api_prefix}/wechat/oauth/callback"
277
281
  userinfo_auth_url = WechatUtil.build_oauth_url(
278
- appid=app_config.app_id,
282
+ appid=wechat_app.app_id,
279
283
  redirect_uri=callback_url,
280
284
  scope="snsapi_userinfo",
281
285
  state=new_state
@@ -321,10 +325,10 @@ class AuthService:
321
325
  async def _authenticate_wechat_mini(cls, data: WechatLogin) -> User:
322
326
  """微信小程序认证"""
323
327
  # 1. 获取小程序配置
324
- app_config = await WechatApp.get_or_none(
328
+ wechat_app = await WechatApp.get_or_none(
325
329
  app_id=data.app_id, app_type=AppTypeEnum.MINI_PROGRAM
326
330
  )
327
- if not app_config:
331
+ if not wechat_app:
328
332
  raise CustomException(msg="无效的小程序配置")
329
333
 
330
334
  # 2. 检查 code 是否已使用(防重放)
@@ -334,7 +338,7 @@ class AuthService:
334
338
 
335
339
  # 3. code 换取 session_key 和 openid
336
340
  wx_res = await WechatUtil.code2session(
337
- app_config.app_id, app_config.app_secret, data.code
341
+ wechat_app.app_id, wechat_app.app_secret, data.code
338
342
  )
339
343
  openid = wx_res.get("openid")
340
344
  unionid = wx_res.get("unionid")
@@ -350,7 +354,7 @@ class AuthService:
350
354
  phone = None
351
355
  if data.encrypted_data and data.iv:
352
356
  phone_info = WechatUtil.decrypt_data(
353
- session_key, data.encrypted_data, data.iv, app_config.app_id
357
+ session_key, data.encrypted_data, data.iv, wechat_app.app_id
354
358
  )
355
359
  phone = phone_info.get("phoneNumber")
356
360
  # 校验手机号格式
@@ -360,7 +364,7 @@ class AuthService:
360
364
  # 6. 使用 _bind_or_register_wechat_user 处理用户绑定/注册
361
365
  return await cls._bind_or_register_wechat_user(
362
366
  openid=openid,
363
- app_config=app_config,
367
+ wechat_app=wechat_app,
364
368
  unionid=unionid,
365
369
  phone=phone,
366
370
  session_key=session_key,
@@ -430,7 +434,7 @@ class AuthService:
430
434
  async def _bind_or_register_wechat_user(
431
435
  cls,
432
436
  openid: str,
433
- app_config: WechatApp,
437
+ wechat_app: WechatApp,
434
438
  unionid: str | None = None,
435
439
  phone: str | None = None,
436
440
  nickname: str | None = None,
@@ -442,7 +446,7 @@ class AuthService:
442
446
 
443
447
  Args:
444
448
  openid: 微信 OpenID
445
- app_config: 微信应用配置
449
+ wechat_app: 微信应用配置
446
450
  unionid: 微信 UnionID(可选,用于跨应用关联)
447
451
  phone: 手机号(可选,小程序一键登录获取)
448
452
  nickname: 微信昵称
@@ -454,7 +458,7 @@ class AuthService:
454
458
  """
455
459
  # 1. 检查 WechatUser 是否已存在
456
460
  wechat_user = await WechatUser.get_or_none(
457
- openid=openid, wechat_app=app_config
461
+ openid=openid, wechat_app=wechat_app
458
462
  ).select_related("user")
459
463
 
460
464
  if wechat_user:
@@ -480,7 +484,7 @@ class AuthService:
480
484
  if not user and unionid:
481
485
  other_wechat_user = (
482
486
  await WechatUser.filter(unionid=unionid)
483
- .exclude(wechat_app=app_config)
487
+ .exclude(wechat_app=wechat_app)
484
488
  .first()
485
489
  .select_related("user")
486
490
  )
@@ -502,7 +506,7 @@ class AuthService:
502
506
  # 4. 创建 WechatUser 关联
503
507
  await WechatUser.create(
504
508
  user=user,
505
- wechat_app=app_config,
509
+ wechat_app=wechat_app,
506
510
  openid=openid,
507
511
  unionid=unionid,
508
512
  session_key=session_key,
@@ -1,6 +1,7 @@
1
- import time
2
1
  import hashlib
2
+ import time
3
3
  import xml.etree.ElementTree as ET
4
+
4
5
  from common_base.exception import CustomException
5
6
  from common_base.logging import logger
6
7
  from common_models.wechat_model import WechatApp
@@ -1,6 +1,5 @@
1
1
  from passlib.context import CryptContext
2
2
 
3
-
4
3
  # 密码加密配置
5
4
  PwdContext = CryptContext(
6
5
  schemes=["bcrypt"],
@@ -2,6 +2,7 @@ import base64
2
2
  import random
3
3
  import string
4
4
  from io import BytesIO
5
+
5
6
  from PIL import Image, ImageDraw, ImageFont
6
7
 
7
8
 
@@ -1,4 +1,5 @@
1
1
  import re
2
+
2
3
  from common_base import aiorequests
3
4
 
4
5
 
@@ -1,8 +1,10 @@
1
- import jwt
2
1
  from datetime import datetime, timedelta
3
2
  from typing import Any
3
+
4
+ import jwt
4
5
  from core.base_config import base_config
5
6
 
7
+
6
8
  class JwtUtil:
7
9
  """
8
10
  JWT 工具类
@@ -1,8 +1,8 @@
1
+ import hashlib
1
2
  import os
2
- import time
3
3
  import socket
4
+ import time
4
5
  import uuid
5
- import hashlib
6
6
 
7
7
 
8
8
  class SnowflakeIDGenerator:
@@ -1,10 +1,12 @@
1
+ import base64
2
+ import json
3
+ from urllib.parse import quote
4
+
1
5
  from common_base import aiorequests
2
6
  from common_base.exception import CustomException
3
7
  from common_base.logging import logger
4
8
  from Crypto.Cipher import AES
5
- from urllib.parse import quote
6
- import base64
7
- import json
9
+
8
10
 
9
11
  class WechatUtil:
10
12
 
@@ -1,13 +1,14 @@
1
1
  import datetime
2
- from typing import Dict, List, Optional, Any, Tuple
2
+ from contextlib import asynccontextmanager
3
+ from typing import Any
4
+
5
+ from core.base_config import base_config
3
6
  from fastapi import FastAPI
4
7
  from fastapi.encoders import jsonable_encoder
5
8
  from tortoise import Tortoise
6
- from tortoise.models import Model
7
9
  from tortoise.contrib.fastapi import RegisterTortoise
8
- from tortoise.queryset import QuerySetSingle, QuerySet
9
- from contextlib import asynccontextmanager
10
- from core.base_config import base_config
10
+ from tortoise.models import Model
11
+ from tortoise.queryset import QuerySet, QuerySetSingle
11
12
 
12
13
  db_models = [
13
14
  'aerich.models',
@@ -92,8 +93,8 @@ class MySQLUtils(Model):
92
93
  @staticmethod
93
94
  def json_encoder(
94
95
  obj: Any,
95
- include: Optional[List[str]] = None,
96
- exclude: Optional[List[str]] = None,
96
+ include: list[str] | None = None,
97
+ exclude: list[str] | None = None,
97
98
  **kwargs
98
99
  ):
99
100
  return jsonable_encoder(
@@ -109,13 +110,13 @@ class MySQLUtils(Model):
109
110
  async def model2dict(
110
111
  cls,
111
112
  model: Model,
112
- include: List[str] | None = None,
113
- exclude: List[str] | None = None,
114
- alias: List[Tuple[str, str]] | None = None,
115
- fetch_related: List[str] | None = None,
113
+ include: list[str] | None = None,
114
+ exclude: list[str] | None = None,
115
+ alias: list[tuple[str, str]] | None = None,
116
+ fetch_related: list[str] | None = None,
116
117
  flat: bool = False, # 扁平化
117
118
  **kwargs
118
- ) -> Dict[str, Any]:
119
+ ) -> dict[str, Any]:
119
120
  meta = getattr(cls, 'PydanticMeta', getattr(cls, 'Meta', None))
120
121
  if meta:
121
122
  _include = getattr(meta, 'include', None)
@@ -224,15 +225,15 @@ class MySQLUtils(Model):
224
225
  async def get_data(
225
226
  cls,
226
227
  *args,
227
- include: List[str] | None = None, # 包含哪些字段,默认全部
228
- exclude: List[str] | None = None, # 排除哪些字段,默认全部
229
- alias: List[Tuple[str, str]] | None = None, # 字段别名,如[('id', 'uid')]
230
- fetch_related: List[str] | None = None, # 对应关系表
228
+ include: list[str] | None = None, # 包含哪些字段,默认全部
229
+ exclude: list[str] | None = None, # 排除哪些字段,默认全部
230
+ alias: list[tuple[str, str]] | None = None, # 字段别名,如[('id', 'uid')]
231
+ fetch_related: list[str] | None = None, # 对应关系表
231
232
  flat: bool = False, # 扁平化
232
233
  deleted: bool | None = None,
233
234
  queryset_model: QuerySetSingle | Model | None = None,
234
235
  **kwargs
235
- ) -> Dict[str, Any] | None:
236
+ ) -> dict[str, Any] | None:
236
237
  if 'deleted' in cls._meta.fields and deleted is None:
237
238
  kwargs['deleted'] = False
238
239
 
@@ -264,15 +265,15 @@ class MySQLUtils(Model):
264
265
  async def get_or_none_data(
265
266
  cls,
266
267
  *args,
267
- include: List[str] | None = None, # 包含哪些字段,默认全部
268
- exclude: List[str] | None = None, # 排除哪些字段,默认全部
269
- alias: List[Tuple[str, str]] | None = None, # 字段别名,如[('id', 'uid')]
270
- fetch_related: List[str] | None = None, # 对应关系表
268
+ include: list[str] | None = None, # 包含哪些字段,默认全部
269
+ exclude: list[str] | None = None, # 排除哪些字段,默认全部
270
+ alias: list[tuple[str, str]] | None = None, # 字段别名,如[('id', 'uid')]
271
+ fetch_related: list[str] | None = None, # 对应关系表
271
272
  flat: bool = False, # 扁平化
272
273
  deleted: bool | None = None,
273
274
  queryset_model: QuerySetSingle | Model | None = None,
274
275
  **kwargs
275
- ) -> Dict[str, Any] | None:
276
+ ) -> dict[str, Any] | None:
276
277
  if 'deleted' in cls._meta.fields and deleted is None:
277
278
  kwargs['deleted'] = False
278
279
 
@@ -307,16 +308,16 @@ class MySQLUtils(Model):
307
308
  page_no: int | None = 0,
308
309
  page_size: int | None = 0,
309
310
  nocount: bool = False,
310
- include: List[str] | None = None, # 包含哪些字段,默认全部
311
- exclude: List[str] | None = None, # 排除哪些字段,默认全部
312
- alias: List[Tuple[str, str]] | None = None, # 字段别名,如[('id', 'uid')]
313
- fetch_related: List[str] | None = None, # 对应关系表
311
+ include: list[str] | None = None, # 包含哪些字段,默认全部
312
+ exclude: list[str] | None = None, # 排除哪些字段,默认全部
313
+ alias: list[tuple[str, str]] | None = None, # 字段别名,如[('id', 'uid')]
314
+ fetch_related: list[str] | None = None, # 对应关系表
314
315
  flat: bool = False, # 扁平化
315
316
  deleted: bool | None = None,
316
317
  queryset: QuerySet | None = None,
317
- order_by: List[str] | None = None,
318
+ order_by: list[str] | None = None,
318
319
  **kwargs
319
- ) -> Dict[str, Any] | List[Dict[str, Any]]:
320
+ ) -> dict[str, Any] | list[dict[str, Any]]:
320
321
  if 'deleted' in cls._meta.fields and deleted is None:
321
322
  kwargs['deleted'] = False
322
323
 
@@ -1,12 +1,13 @@
1
- import time
2
1
  import asyncio
2
+ import time
3
+ from collections.abc import Callable, Mapping
3
4
  from contextlib import asynccontextmanager
4
- from typing import Optional, Union, Callable, Mapping
5
- from redis.asyncio import Redis
6
- from redis import asyncio as aioredis
7
- from redis.typing import KeyT, EncodableT, ExpiryT, ZScoreBoundT, AnyKeyT
5
+
8
6
  from common_base.logging import log_exc
9
7
  from core.base_config import base_config
8
+ from redis import asyncio as aioredis
9
+ from redis.asyncio import Redis
10
+ from redis.typing import AnyKeyT, EncodableT, ExpiryT, KeyT, ZScoreBoundT
10
11
 
11
12
 
12
13
  class AioRedis:
@@ -33,7 +34,7 @@ class AioRedis:
33
34
  )
34
35
  cls.__redis_pool = Redis(connection_pool=pool)
35
36
  # 测试连接是否有效
36
- cls.__redis_pool.ping()
37
+ await cls.__redis_pool.ping() # pyright: ignore[reportGeneralTypeIssues]
37
38
  except Exception as e:
38
39
  log_exc(e)
39
40
  raise
@@ -63,7 +64,7 @@ class AioRedis:
63
64
  pass
64
65
 
65
66
  @classmethod
66
- async def get(cls, name: KeyT) -> Optional[str]:
67
+ async def get(cls, name: KeyT) -> str | None:
67
68
  redis = await cls.connect()
68
69
  result = await redis.get(name)
69
70
  return result
@@ -73,11 +74,11 @@ class AioRedis:
73
74
  cls,
74
75
  name: KeyT,
75
76
  value: EncodableT,
76
- ex: Union[ExpiryT, None] = None, # 超时秒数
77
- px: Union[ExpiryT, None] = None, # 超时毫秒
77
+ ex: ExpiryT | None = None, # 超时秒数
78
+ px: ExpiryT | None = None, # 超时毫秒
78
79
  nx: bool = False,
79
80
  xx: bool = False
80
- ) -> Optional[bool]:
81
+ ) -> bool | None:
81
82
  redis = await cls.connect()
82
83
  result = await redis.set(name, value, ex=ex, px=px, nx=nx, xx=xx)
83
84
  return result
@@ -86,7 +87,7 @@ class AioRedis:
86
87
  async def exists(
87
88
  cls,
88
89
  *names: KeyT
89
- ) -> Optional[bool]:
90
+ ) -> bool | None:
90
91
  redis = await cls.connect()
91
92
  result = await redis.exists(*names)
92
93
  return result
@@ -102,7 +103,7 @@ class AioRedis:
102
103
  incr: bool = False,
103
104
  gt: bool = False,
104
105
  lt: bool = False
105
- ) -> Union[int, float]:
106
+ ) -> int | float:
106
107
  redis = await cls.connect()
107
108
  result = await redis.zadd(name, mapping, nx, xx, ch, incr, gt, lt)
108
109
  return result
@@ -118,7 +119,7 @@ class AioRedis:
118
119
  cls,
119
120
  name: KeyT,
120
121
  value: EncodableT
121
- ) -> Optional[float]:
122
+ ) -> float | None:
122
123
  redis = await cls.connect()
123
124
  result = await redis.zscore(name, value)
124
125
  return result
@@ -135,10 +136,10 @@ class AioRedis:
135
136
  name: KeyT,
136
137
  max: ZScoreBoundT,
137
138
  min: ZScoreBoundT,
138
- start: Union[int, None] = None,
139
- num: Union[int, None] = None,
139
+ start: int | None = None,
140
+ num: int | None = None,
140
141
  withscores: bool = False,
141
- score_cast_func: Union[type, Callable] = float,
142
+ score_cast_func: type | Callable = float,
142
143
  ):
143
144
  redis = await cls.connect()
144
145
  result = await redis.zrevrangebyscore(name, max, min, start, num, withscores, score_cast_func)
@@ -1,9 +1,11 @@
1
1
  import time
2
+
3
+ from common_base.logging import logger
2
4
  from fastapi import Request
3
5
  from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
4
- from starlette.types import ASGIApp
5
6
  from starlette.responses import Response
6
- from common_base.logging import logger
7
+ from starlette.types import ASGIApp
8
+
7
9
 
8
10
  class RequestHttpMiddleware(BaseHTTPMiddleware):
9
11
  def __init__(self, app: ASGIApp) -> None:
@@ -1,15 +1,25 @@
1
1
  import os
2
- from typing import Any, AsyncGenerator
2
+ from collections.abc import AsyncGenerator
3
3
  from contextlib import asynccontextmanager
4
- from fastapi import FastAPI, HTTPException
5
- from fastapi.exceptions import RequestValidationError, ResponseValidationError
6
- from starlette.middleware.cors import CORSMiddleware
7
- from starlette.staticfiles import StaticFiles
8
- from common_base.exception import AllExceptionHandler, CustomException, CustomExceptionHandler, HttpExceptionHandler, RequestValidationHandle, ResponseValidationHandle, ValueExceptionHandler
4
+ from typing import Any
5
+
6
+ from common_base.exception import (
7
+ AllExceptionHandler,
8
+ CustomException,
9
+ CustomExceptionHandler,
10
+ HttpExceptionHandler,
11
+ RequestValidationHandle,
12
+ ResponseValidationHandle,
13
+ ValueExceptionHandler,
14
+ )
9
15
  from common_base.logging import logger
10
16
  from core.base_config import BaseConfig, base_config
11
17
  from core.databases.aiodb import AioDb
12
18
  from core.middleware.http_middleware import RequestHttpMiddleware
19
+ from fastapi import FastAPI, HTTPException
20
+ from fastapi.exceptions import RequestValidationError, ResponseValidationError
21
+ from starlette.middleware.cors import CORSMiddleware
22
+ from starlette.staticfiles import StaticFiles
13
23
 
14
24
 
15
25
  def start_server(config: BaseConfig):
@@ -4,12 +4,11 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rainycode"
7
- version = "1.1.9"
7
+ version = "1.3.0"
8
8
  description = "FastAPI base modules"
9
- requires-python = ">=3.8"
9
+ requires-python = ">=3.10"
10
10
 
11
11
  dependencies = [
12
- "build == 1.4.0",
13
12
  "aerich == 0.9.2",
14
13
  "fastapi == 0.133.1",
15
14
  "aiohttp == 3.13.3",
@@ -26,9 +25,12 @@ dependencies = [
26
25
  "pydantic-settings == 2.13.1",
27
26
  ]
28
27
 
28
+ [project.optional-dependencies]
29
+ release = [
30
+ "build == 1.4.0",
31
+ "twine == 6.1.0",
32
+ ]
33
+
29
34
  [tool.setuptools.packages.find]
30
35
  where = ["."]
31
- include = ["core*", "common*", "utlis*"]
32
-
33
- [tool.setuptools.package-data]
34
- utlis = ["py.typed"]
36
+ include = ["core*", "common*"]
@@ -1,9 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rainycode
3
- Version: 1.1.9
3
+ Version: 1.3.0
4
4
  Summary: FastAPI base modules
5
- Requires-Python: >=3.8
6
- Requires-Dist: build==1.4.0
5
+ Requires-Python: >=3.10
7
6
  Requires-Dist: aerich==0.9.2
8
7
  Requires-Dist: fastapi==0.133.1
9
8
  Requires-Dist: aiohttp==3.13.3
@@ -18,3 +17,6 @@ Requires-Dist: pycryptodome==3.23.0
18
17
  Requires-Dist: uvicorn[standard]==0.41.0
19
18
  Requires-Dist: tortoise-orm[asyncmy]==0.25.4
20
19
  Requires-Dist: pydantic-settings==2.13.1
20
+ Provides-Extra: release
21
+ Requires-Dist: build==1.4.0; extra == "release"
22
+ Requires-Dist: twine==6.1.0; extra == "release"
@@ -12,7 +12,6 @@ common_models/wechat_model.py
12
12
  common_servers/router.py
13
13
  common_servers/api/auth_api.py
14
14
  common_servers/api/wechat_api.py
15
- common_servers/schemas/__init__.py
16
15
  common_servers/schemas/auth_schema.py
17
16
  common_servers/service/auth_svc.py
18
17
  common_servers/service/wechat_svc.py
@@ -1,4 +1,3 @@
1
- build==1.4.0
2
1
  aerich==0.9.2
3
2
  fastapi==0.133.1
4
3
  aiohttp==3.13.3
@@ -13,3 +12,7 @@ pycryptodome==3.23.0
13
12
  uvicorn[standard]==0.41.0
14
13
  tortoise-orm[asyncmy]==0.25.4
15
14
  pydantic-settings==2.13.1
15
+
16
+ [release]
17
+ build==1.4.0
18
+ twine==6.1.0
File without changes
File without changes
File without changes