rainycode 1.0.0__py3-none-any.whl
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.
- common/aiorequests.py +31 -0
- common/consts.py +3 -0
- common/exception.py +68 -0
- common/logging.py +84 -0
- common/response.py +63 -0
- common_depend/auth_depend.py +75 -0
- common_model/base_model.py +200 -0
- common_model/user_model.py +25 -0
- common_model/wechat_model.py +43 -0
- common_utlis/bcrypt_util.py +63 -0
- common_utlis/captcha_util.py +130 -0
- common_utlis/ip_util.py +83 -0
- common_utlis/jwt_util.py +47 -0
- common_utlis/snowflake_util.py +205 -0
- core/base_config.py +28 -0
- core/databases/aiodb.py +360 -0
- core/databases/aioredis.py +279 -0
- core/middleware/http_middleware.py +28 -0
- core/start.py +93 -0
- rainycode-1.0.0.dist-info/METADATA +15 -0
- rainycode-1.0.0.dist-info/RECORD +23 -0
- rainycode-1.0.0.dist-info/WHEEL +5 -0
- rainycode-1.0.0.dist-info/top_level.txt +5 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import random
|
|
3
|
+
import string
|
|
4
|
+
from io import BytesIO
|
|
5
|
+
from PIL import Image, ImageDraw, ImageFont
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CaptchaUtil:
|
|
9
|
+
"""
|
|
10
|
+
验证码工具类
|
|
11
|
+
"""
|
|
12
|
+
@classmethod
|
|
13
|
+
def generate_captcha(cls) -> tuple[str, str]:
|
|
14
|
+
"""
|
|
15
|
+
生成带有噪声和干扰的验证码图片:4位随机字符
|
|
16
|
+
:return: [base64编码的图片字符串, 验证码值]
|
|
17
|
+
"""
|
|
18
|
+
# 生成4位随机验证码
|
|
19
|
+
chars = string.digits + string.ascii_letters
|
|
20
|
+
captcha_value = ''.join(random.sample(chars, 4))
|
|
21
|
+
|
|
22
|
+
# 创建一张随机颜色背景的图片
|
|
23
|
+
width, height = 160, 60
|
|
24
|
+
background_color = tuple(random.randint(230, 255) for _ in range(3))
|
|
25
|
+
image = Image.new('RGB', (width, height), color=background_color)
|
|
26
|
+
draw = ImageDraw.Draw(image)
|
|
27
|
+
|
|
28
|
+
# 使用指定字体
|
|
29
|
+
font = ImageFont.truetype(font='static/assets/font/Arial.ttf', size=40)
|
|
30
|
+
|
|
31
|
+
# 计算文本总宽度和高度
|
|
32
|
+
total_width = sum(draw.textbbox((0, 0), char, font=font)[2] for char in captcha_value)
|
|
33
|
+
text_height = draw.textbbox((0, 0), captcha_value[0], font=font)[3]
|
|
34
|
+
|
|
35
|
+
# 计算起始位置,使文字居中
|
|
36
|
+
x_start = (width - total_width) / 2
|
|
37
|
+
y_start = (height - text_height) / 2 - draw.textbbox((0, 0), captcha_value[0], font=font)[1]
|
|
38
|
+
|
|
39
|
+
# 绘制字符
|
|
40
|
+
x = x_start
|
|
41
|
+
for char in captcha_value:
|
|
42
|
+
# 使用深色文字,增加对比度
|
|
43
|
+
text_color = tuple(random.randint(0, 80) for _ in range(3))
|
|
44
|
+
|
|
45
|
+
# 随机偏移,增加干扰
|
|
46
|
+
x_offset = x + random.uniform(-2, 2)
|
|
47
|
+
y_offset = y_start + random.uniform(-2, 2)
|
|
48
|
+
|
|
49
|
+
# 绘制字符
|
|
50
|
+
draw.text((x_offset, y_offset), char, font=font, fill=text_color)
|
|
51
|
+
|
|
52
|
+
# 更新x坐标,增加字符间距的随机性
|
|
53
|
+
x += draw.textbbox((0, 0), char, font=font)[2] + random.uniform(1, 5)
|
|
54
|
+
|
|
55
|
+
# 添加干扰线
|
|
56
|
+
for _ in range(4):
|
|
57
|
+
line_color = tuple(random.randint(150, 200) for _ in range(3))
|
|
58
|
+
points = [(i, int(random.uniform(0, height))) for i in range(0, width, 20)]
|
|
59
|
+
draw.line(points, fill=line_color, width=1)
|
|
60
|
+
|
|
61
|
+
# 添加随机噪点
|
|
62
|
+
for _ in range(width * height // 60):
|
|
63
|
+
point_color = tuple(random.randint(0, 255) for _ in range(3))
|
|
64
|
+
draw.point(
|
|
65
|
+
(random.randint(0, width), random.randint(0, height)),
|
|
66
|
+
fill=point_color
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# 将图像数据保存到内存中并转换为base64
|
|
70
|
+
buffer = BytesIO()
|
|
71
|
+
image.save(buffer, format='PNG', optimize=True)
|
|
72
|
+
base64_string = base64.b64encode(buffer.getvalue()).decode()
|
|
73
|
+
|
|
74
|
+
return base64_string, captcha_value
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def captcha_arithmetic(cls) -> tuple[str, int]:
|
|
78
|
+
"""
|
|
79
|
+
创建验证码图片: 加减乘运算
|
|
80
|
+
:return: [base64编码的图片字符串, 计算结果]
|
|
81
|
+
"""
|
|
82
|
+
# 创建空白图像,使用随机浅色背景
|
|
83
|
+
background_color = tuple(random.randint(230, 255) for _ in range(3))
|
|
84
|
+
image = Image.new('RGB', (160, 60), color=background_color)
|
|
85
|
+
draw = ImageDraw.Draw(image)
|
|
86
|
+
|
|
87
|
+
# 设置字体
|
|
88
|
+
font = ImageFont.truetype(font='static/assets/font/Arial.ttf', size=40)
|
|
89
|
+
|
|
90
|
+
# 生成运算数字和运算符
|
|
91
|
+
operators = ['+', '-', '*']
|
|
92
|
+
operator = random.choice(operators)
|
|
93
|
+
|
|
94
|
+
# 对于减法,确保num1大于num2
|
|
95
|
+
if operator == '-':
|
|
96
|
+
num1 = random.randint(6, 10)
|
|
97
|
+
num2 = random.randint(1, 5)
|
|
98
|
+
else:
|
|
99
|
+
num1 = random.randint(1, 9)
|
|
100
|
+
num2 = random.randint(1, 9)
|
|
101
|
+
|
|
102
|
+
# 计算结果
|
|
103
|
+
result_map = {
|
|
104
|
+
'+': lambda x, y: x + y,
|
|
105
|
+
'-': lambda x, y: x - y,
|
|
106
|
+
'*': lambda x, y: x * y
|
|
107
|
+
}
|
|
108
|
+
captcha_value = result_map[operator](num1, num2)
|
|
109
|
+
|
|
110
|
+
# 绘制文本,使用深色增加对比度
|
|
111
|
+
text = f'{num1} {operator} {num2} = ?'
|
|
112
|
+
text_bbox = draw.textbbox((0, 0), text, font=font)
|
|
113
|
+
text_width = text_bbox[2] - text_bbox[0]
|
|
114
|
+
x = (160 - text_width) // 2
|
|
115
|
+
draw.text((x, 15), text, fill=(0, 0, 139), font=font)
|
|
116
|
+
|
|
117
|
+
# 添加干扰线
|
|
118
|
+
for _ in range(3):
|
|
119
|
+
line_color = tuple(random.randint(150, 200) for _ in range(3))
|
|
120
|
+
draw.line([
|
|
121
|
+
(random.randint(0, 160), random.randint(0, 60)),
|
|
122
|
+
(random.randint(0, 160), random.randint(0, 60))
|
|
123
|
+
], fill=line_color, width=1)
|
|
124
|
+
|
|
125
|
+
# 将图像数据保存到内存中并转换为base64
|
|
126
|
+
buffer = BytesIO()
|
|
127
|
+
image.save(buffer, format='PNG', optimize=True)
|
|
128
|
+
base64_string = base64.b64encode(buffer.getvalue()).decode()
|
|
129
|
+
|
|
130
|
+
return base64_string, captcha_value
|
common_utlis/ip_util.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from common import aiorequests
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class IpUtil:
|
|
7
|
+
"""
|
|
8
|
+
获取IP归属地工具类
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def is_valid_ip(cls, ip: str) -> bool:
|
|
13
|
+
"""
|
|
14
|
+
校验IP格式是否合法
|
|
15
|
+
|
|
16
|
+
:param ip: IP地址
|
|
17
|
+
:return: 是否合法
|
|
18
|
+
"""
|
|
19
|
+
ip_pattern = r'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
|
|
20
|
+
return bool(re.match(ip_pattern, ip))
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def is_private_ip(cls, ip: str) -> bool:
|
|
24
|
+
"""
|
|
25
|
+
校验IP是否为内网IP
|
|
26
|
+
|
|
27
|
+
:param ip: IP地址
|
|
28
|
+
:return: 是否为内网IP
|
|
29
|
+
"""
|
|
30
|
+
ip_parts = list(map(int, ip.split('.')))
|
|
31
|
+
|
|
32
|
+
# 检查是否为 10.0.0.0/8
|
|
33
|
+
if ip_parts[0] == 10:
|
|
34
|
+
return True
|
|
35
|
+
|
|
36
|
+
# 检查是否为 172.16.0.0/12
|
|
37
|
+
if ip_parts[0] == 172 and 16 <= ip_parts[1] <= 31:
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
# 检查是否为 192.168.0.0/16
|
|
41
|
+
if ip_parts[0] == 192 and ip_parts[1] == 168:
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
# 检查是否为 127.0.0.0/8
|
|
45
|
+
if ip_parts[0] == 127:
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
async def get_ip_location(cls, ip: str) -> str | None:
|
|
53
|
+
"""
|
|
54
|
+
获取IP归属地信息
|
|
55
|
+
|
|
56
|
+
:param ip: IP地址
|
|
57
|
+
:return: IP归属地信息
|
|
58
|
+
"""
|
|
59
|
+
# 校验IP格式
|
|
60
|
+
if not cls.is_valid_ip(ip):
|
|
61
|
+
# IP格式不合法
|
|
62
|
+
return "未知"
|
|
63
|
+
|
|
64
|
+
# 内网IP直接返回
|
|
65
|
+
if cls.is_private_ip(ip):
|
|
66
|
+
return '内网IP'
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
# 使用ip-api.com API获取IP归属地信息
|
|
70
|
+
response = await aiorequests.get(f'http://ip-api.com/json/{ip}')
|
|
71
|
+
if response and response.json().get('ret') == 200:
|
|
72
|
+
result = response.json().get('data', {})
|
|
73
|
+
return f"{result.get('country','')}-{result.get('prov','')}-{result.get('city','')}-{result.get('area','')}-{result.get('isp','')}"
|
|
74
|
+
|
|
75
|
+
response = await aiorequests.get(f'https://qifu-api.baidubce.com/ip/geo/v1/district?ip={ip}')
|
|
76
|
+
if response and response.json().get('code') == "Success":
|
|
77
|
+
data = response.json().get('data', {})
|
|
78
|
+
# 修正原代码中的格式错误
|
|
79
|
+
return f"{data.get('country','')}-{data.get('prov','')}-{data.get('city','')}-{data.get('district','')}-{data.get('isp','')}"
|
|
80
|
+
|
|
81
|
+
except Exception as e:
|
|
82
|
+
# 获取IP归属地失败
|
|
83
|
+
return "未知"
|
common_utlis/jwt_util.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import jwt
|
|
2
|
+
from datetime import datetime, timedelta
|
|
3
|
+
from typing import Any
|
|
4
|
+
from core.base_config import base_config
|
|
5
|
+
|
|
6
|
+
class JwtUtil:
|
|
7
|
+
"""
|
|
8
|
+
JWT 工具类
|
|
9
|
+
"""
|
|
10
|
+
SECRET_KEY = base_config.jwt_secret_key
|
|
11
|
+
ALGORITHM = "HS256"
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def create_access_token(cls, data: dict, expires_delta: timedelta) -> str:
|
|
15
|
+
"""
|
|
16
|
+
生成访问令牌
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
data: 数据载荷
|
|
20
|
+
expires_delta: 过期时间差 (必须指定)
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
str: JWT Token
|
|
24
|
+
"""
|
|
25
|
+
to_encode = data.copy()
|
|
26
|
+
expire = datetime.now() + expires_delta
|
|
27
|
+
|
|
28
|
+
to_encode.update({"exp": expire})
|
|
29
|
+
encoded_jwt = jwt.encode(to_encode, cls.SECRET_KEY, algorithm=cls.ALGORITHM)
|
|
30
|
+
return encoded_jwt
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def decode_token(cls, token: str) -> dict[str, Any] | None:
|
|
34
|
+
"""
|
|
35
|
+
解析令牌
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
token: JWT Token
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
dict | None: 解析后的数据,解析失败返回 None
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
payload = jwt.decode(token, cls.SECRET_KEY, algorithms=[cls.ALGORITHM])
|
|
45
|
+
return payload
|
|
46
|
+
except jwt.PyJWTError:
|
|
47
|
+
return None
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
import socket
|
|
4
|
+
import uuid
|
|
5
|
+
import hashlib
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SnowflakeIDGenerator:
|
|
9
|
+
"""
|
|
10
|
+
雪花算法ID生成器
|
|
11
|
+
|
|
12
|
+
雪花ID结构:
|
|
13
|
+
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
|
|
14
|
+
1位符号位 - 41位时间戳 - 5位数据中心ID - 5位机器ID - 12位序列号
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, datacenter_id: int = 0, machine_id: int = 0):
|
|
18
|
+
"""
|
|
19
|
+
初始化雪花ID生成器
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
datacenter_id: 数据中心ID,范围0-31
|
|
23
|
+
machine_id: 机器ID,范围0-31
|
|
24
|
+
"""
|
|
25
|
+
# 起始时间戳:2025-07-01 00:00:00
|
|
26
|
+
self.twepoch = 1751328000000
|
|
27
|
+
|
|
28
|
+
# 各部分占位长度
|
|
29
|
+
self.datacenter_id_bits = 5 # 数据中心ID长度
|
|
30
|
+
self.machine_id_bits = 5 # 机器ID长度
|
|
31
|
+
self.sequence_bits = 12 # 序列号长度
|
|
32
|
+
|
|
33
|
+
# 各部分最大值
|
|
34
|
+
self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits) # 31
|
|
35
|
+
self.max_machine_id = -1 ^ (-1 << self.machine_id_bits) # 31
|
|
36
|
+
self.max_sequence = -1 ^ (-1 << self.sequence_bits) # 4095
|
|
37
|
+
|
|
38
|
+
# 各部分偏移量
|
|
39
|
+
self.machine_id_shift = self.sequence_bits
|
|
40
|
+
self.datacenter_id_shift = self.sequence_bits + self.machine_id_bits
|
|
41
|
+
self.timestamp_shift = self.sequence_bits + self.machine_id_bits + self.datacenter_id_bits
|
|
42
|
+
|
|
43
|
+
# 参数检查
|
|
44
|
+
if datacenter_id > self.max_datacenter_id or datacenter_id < 0:
|
|
45
|
+
raise ValueError(f"数据中心ID不能大于{self.max_datacenter_id}或小于0")
|
|
46
|
+
if machine_id > self.max_machine_id or machine_id < 0:
|
|
47
|
+
raise ValueError(f"机器ID不能大于{self.max_machine_id}或小于0")
|
|
48
|
+
|
|
49
|
+
self.datacenter_id = datacenter_id
|
|
50
|
+
self.machine_id = machine_id
|
|
51
|
+
self.sequence = 0
|
|
52
|
+
self.last_timestamp = -1
|
|
53
|
+
|
|
54
|
+
def _gen_timestamp(self) -> int:
|
|
55
|
+
"""
|
|
56
|
+
生成当前时间戳
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
当前时间戳(毫秒)
|
|
60
|
+
"""
|
|
61
|
+
return int(time.time() * 1000)
|
|
62
|
+
|
|
63
|
+
def _til_next_millis(self, last_timestamp: int) -> int:
|
|
64
|
+
"""
|
|
65
|
+
等待到下一个毫秒
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
last_timestamp: 上一个时间戳
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
下一个时间戳
|
|
72
|
+
"""
|
|
73
|
+
timestamp = self._gen_timestamp()
|
|
74
|
+
while timestamp <= last_timestamp:
|
|
75
|
+
timestamp = self._gen_timestamp()
|
|
76
|
+
return timestamp
|
|
77
|
+
|
|
78
|
+
def get_id(self) -> int:
|
|
79
|
+
"""
|
|
80
|
+
生成下一个ID
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
雪花算法生成的ID
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
RuntimeError: 当时钟回拨时抛出异常
|
|
87
|
+
"""
|
|
88
|
+
timestamp = self._gen_timestamp()
|
|
89
|
+
|
|
90
|
+
# 时钟回拨检查
|
|
91
|
+
if timestamp < self.last_timestamp:
|
|
92
|
+
raise RuntimeError(f"时钟回拨,拒绝生成ID,上一时间戳: {self.last_timestamp}, 当前时间戳: {timestamp}")
|
|
93
|
+
|
|
94
|
+
# 同一毫秒内
|
|
95
|
+
if timestamp == self.last_timestamp:
|
|
96
|
+
self.sequence = (self.sequence + 1) & self.max_sequence
|
|
97
|
+
# 同一毫秒内序列号用完
|
|
98
|
+
if self.sequence == 0:
|
|
99
|
+
timestamp = self._til_next_millis(self.last_timestamp)
|
|
100
|
+
else:
|
|
101
|
+
# 不同毫秒,序列号重置
|
|
102
|
+
self.sequence = 0
|
|
103
|
+
|
|
104
|
+
self.last_timestamp = timestamp
|
|
105
|
+
|
|
106
|
+
# 生成ID
|
|
107
|
+
return ((timestamp - self.twepoch) << self.timestamp_shift) | \
|
|
108
|
+
(self.datacenter_id << self.datacenter_id_shift) | \
|
|
109
|
+
(self.machine_id << self.machine_id_shift) | \
|
|
110
|
+
self.sequence
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_machine_info() -> tuple[int, int]:
|
|
114
|
+
"""
|
|
115
|
+
获取机器信息,用于生成数据中心ID和机器ID
|
|
116
|
+
|
|
117
|
+
使用多种机器特征信息生成稳定且唯一的ID:
|
|
118
|
+
- 主机名
|
|
119
|
+
- 完整IP地址
|
|
120
|
+
- 完整MAC地址
|
|
121
|
+
- 进程ID
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Tuple[int, int]: (datacenter_id, machine_id)
|
|
125
|
+
"""
|
|
126
|
+
try:
|
|
127
|
+
# 收集多种机器特征信息
|
|
128
|
+
hostname = socket.gethostname()
|
|
129
|
+
ip = socket.gethostbyname(hostname)
|
|
130
|
+
mac = uuid.UUID(int=uuid.getnode()).hex
|
|
131
|
+
pid = os.getpid()
|
|
132
|
+
|
|
133
|
+
# 为数据中心ID创建特征字符串
|
|
134
|
+
# 使用主机名和IP地址
|
|
135
|
+
datacenter_feature = f"{hostname}:{ip}"
|
|
136
|
+
datacenter_hash = int(hashlib.md5(datacenter_feature.encode()).hexdigest(), 16)
|
|
137
|
+
datacenter_id = datacenter_hash % 32 # 确保在0-31范围内
|
|
138
|
+
|
|
139
|
+
# 为机器ID创建特征字符串
|
|
140
|
+
# 使用MAC地址和进程ID
|
|
141
|
+
machine_feature = f"{mac}:{pid}"
|
|
142
|
+
machine_hash = int(hashlib.md5(machine_feature.encode()).hexdigest(), 16)
|
|
143
|
+
machine_id = machine_hash % 32 # 确保在0-31范围内
|
|
144
|
+
|
|
145
|
+
return datacenter_id, machine_id
|
|
146
|
+
except Exception as e:
|
|
147
|
+
raise RuntimeError(f"获取机器信息失败: {e}")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# 单例模式,默认实例
|
|
151
|
+
_default_snowflake_generator: SnowflakeIDGenerator | None = None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def get_snowflake_id(datacenter_id: int | None = None, machine_id: int | None = None) -> int:
|
|
155
|
+
"""
|
|
156
|
+
获取雪花算法生成的ID
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
datacenter_id: 数据中心ID,范围0-31,默认自动获取
|
|
160
|
+
machine_id: 机器ID,范围0-31,默认自动获取
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
雪花算法生成的ID
|
|
164
|
+
"""
|
|
165
|
+
global _default_snowflake_generator
|
|
166
|
+
|
|
167
|
+
if _default_snowflake_generator is None:
|
|
168
|
+
# 如果未指定datacenter_id或machine_id,则自动获取
|
|
169
|
+
if datacenter_id is None or machine_id is None:
|
|
170
|
+
auto_datacenter_id, auto_machine_id = get_machine_info()
|
|
171
|
+
datacenter_id = datacenter_id if datacenter_id is not None else auto_datacenter_id
|
|
172
|
+
machine_id = machine_id if machine_id is not None else auto_machine_id
|
|
173
|
+
|
|
174
|
+
_default_snowflake_generator = SnowflakeIDGenerator(datacenter_id, machine_id)
|
|
175
|
+
|
|
176
|
+
return _default_snowflake_generator.get_id()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_snowflake_id():
|
|
180
|
+
"""
|
|
181
|
+
测试雪花算法ID生成
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
dict: 测试结果,包含生成的ID和相关信息
|
|
185
|
+
"""
|
|
186
|
+
# 获取机器信息
|
|
187
|
+
datacenter_id, machine_id = get_machine_info()
|
|
188
|
+
|
|
189
|
+
# 生成ID
|
|
190
|
+
snowflake_id = get_snowflake_id()
|
|
191
|
+
|
|
192
|
+
# 返回测试结果
|
|
193
|
+
return {
|
|
194
|
+
"snowflake_id": snowflake_id,
|
|
195
|
+
"datacenter_id": datacenter_id,
|
|
196
|
+
"machine_id": machine_id,
|
|
197
|
+
"binary": bin(snowflake_id),
|
|
198
|
+
"hex": hex(snowflake_id),
|
|
199
|
+
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
if __name__ == '__main__':
|
|
204
|
+
result = test_snowflake_id()
|
|
205
|
+
print(result)
|
core/base_config.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BaseConfig(BaseSettings):
|
|
5
|
+
app_debug: bool = False
|
|
6
|
+
app_title: str = ''
|
|
7
|
+
app_name: str = ''
|
|
8
|
+
|
|
9
|
+
redis_url: str = ''
|
|
10
|
+
mysql_url: str = ''
|
|
11
|
+
|
|
12
|
+
# 跨域
|
|
13
|
+
cors_origins: list[str] = ['*']
|
|
14
|
+
cors_allow_credentials: bool = True
|
|
15
|
+
cors_allow_methods: list[str] = ['GET', 'POST', 'PUT', 'DELETE']
|
|
16
|
+
cors_allow_headers: list[str] = ['*']
|
|
17
|
+
|
|
18
|
+
# jwt密钥
|
|
19
|
+
jwt_secret_key: str = "bgb0tnl9d58+6n-6h-ea&u^1#s0ccp!794=krylacjq75vzps$"
|
|
20
|
+
|
|
21
|
+
model_config = SettingsConfigDict(
|
|
22
|
+
env_file=('envs/dev.env', 'envs/pro.env'),
|
|
23
|
+
env_file_encoding="utf-8",
|
|
24
|
+
case_sensitive=False,
|
|
25
|
+
extra='ignore'
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
base_config = BaseConfig()
|