fastgenerateapi 0.0.28__py2.py3-none-any.whl → 1.1.7__py2.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.
Potentially problematic release.
This version of fastgenerateapi might be problematic. Click here for more details.
- fastgenerateapi/__init__.py +2 -2
- fastgenerateapi/__version__.py +1 -1
- fastgenerateapi/api_view/base_view.py +17 -7
- fastgenerateapi/api_view/create_view.py +1 -1
- fastgenerateapi/api_view/delete_filter_view.py +1 -1
- fastgenerateapi/api_view/delete_tree_view.py +3 -3
- fastgenerateapi/api_view/delete_view.py +3 -3
- fastgenerateapi/api_view/get_all_view.py +10 -8
- fastgenerateapi/api_view/get_one_view.py +1 -1
- fastgenerateapi/api_view/get_relation_view.py +1 -1
- fastgenerateapi/api_view/get_tree_view.py +1 -1
- fastgenerateapi/api_view/mixin/base_mixin.py +11 -7
- fastgenerateapi/api_view/mixin/dbmodel_mixin.py +30 -20
- fastgenerateapi/api_view/mixin/response_mixin.py +68 -38
- fastgenerateapi/api_view/mixin/tool_mixin.py +1 -357
- fastgenerateapi/api_view/mixin/utils/__init__.py +0 -0
- fastgenerateapi/api_view/mixin/utils/docx_util.py +399 -0
- fastgenerateapi/api_view/mixin/utils/file_util.py +30 -0
- fastgenerateapi/api_view/mixin/utils/pdf_util.py +76 -0
- fastgenerateapi/api_view/mixin/utils/xlsx_util.py +336 -0
- fastgenerateapi/api_view/mixin/utils/zip_util.py +50 -0
- fastgenerateapi/api_view/switch_view.py +2 -2
- fastgenerateapi/api_view/update_relation_view.py +3 -3
- fastgenerateapi/api_view/update_view.py +1 -1
- fastgenerateapi/cache/cache_decorator.py +1 -1
- fastgenerateapi/controller/filter_controller.py +68 -26
- fastgenerateapi/controller/router_controller.py +9 -9
- fastgenerateapi/controller/rpc_controller.py +1 -1
- fastgenerateapi/controller/ws_controller.py +1 -1
- fastgenerateapi/deps/filter_params_deps.py +34 -4
- fastgenerateapi/deps/paginator_deps.py +4 -4
- fastgenerateapi/deps/tree_params_deps.py +4 -4
- fastgenerateapi/fastapi_utils/__init__.py +0 -0
- fastgenerateapi/fastapi_utils/all.py +5 -0
- fastgenerateapi/fastapi_utils/param_utils.py +37 -0
- fastgenerateapi/fastapi_utils/response_utils.py +344 -0
- fastgenerateapi/model/__init__.py +0 -0
- fastgenerateapi/model/base_model.py +56 -0
- fastgenerateapi/my_fields/enum_field.py +5 -5
- fastgenerateapi/my_fields/validator.py +60 -0
- fastgenerateapi/pydantic_utils/base_model.py +46 -20
- fastgenerateapi/pydantic_utils/base_settings.py +16 -0
- fastgenerateapi/pydantic_utils/json_encoders.py +2 -1
- fastgenerateapi/schemas_factory/common_function.py +1 -1
- fastgenerateapi/schemas_factory/common_schema_factory.py +4 -4
- fastgenerateapi/schemas_factory/create_schema_factory.py +4 -4
- fastgenerateapi/schemas_factory/filter_schema_factory.py +6 -6
- fastgenerateapi/schemas_factory/get_all_schema_factory.py +5 -5
- fastgenerateapi/schemas_factory/get_one_schema_factory.py +4 -3
- fastgenerateapi/schemas_factory/get_relation_schema_factory.py +3 -3
- fastgenerateapi/schemas_factory/get_tree_schema_factory.py +3 -3
- fastgenerateapi/schemas_factory/response_factory.py +3 -3
- fastgenerateapi/schemas_factory/sql_get_all_schema_factory.py +3 -3
- fastgenerateapi/schemas_factory/update_schema_factory.py +4 -4
- fastgenerateapi/settings/__init__.py +6 -0
- fastgenerateapi/settings/all_settings.py +91 -0
- fastgenerateapi/settings/{settings.py → app_settings.py} +9 -9
- fastgenerateapi/settings/db_settings.py +69 -0
- fastgenerateapi/settings/file_settings.py +24 -0
- fastgenerateapi/settings/jwt_settings.py +23 -0
- fastgenerateapi/settings/otlp_settings.py +69 -0
- fastgenerateapi/settings/redis_settings.py +16 -0
- fastgenerateapi/settings/sms_settings.py +25 -0
- fastgenerateapi/settings/system_settings.py +30 -0
- fastgenerateapi/utils/auto_discover.py +61 -0
- fastgenerateapi/utils/file_utils.py +76 -0
- fastgenerateapi/utils/pwd_utils.py +49 -0
- fastgenerateapi/utils/ramdom_utils.py +48 -0
- fastgenerateapi/utils/snowflake.py +23 -20
- fastgenerateapi/utils/str_util.py +120 -0
- fastgenerateapi/utils/swagger_to_js.py +26 -0
- {fastgenerateapi-0.0.28.dist-info → fastgenerateapi-1.1.7.dist-info}/METADATA +61 -24
- fastgenerateapi-1.1.7.dist-info/RECORD +109 -0
- {fastgenerateapi-0.0.28.dist-info → fastgenerateapi-1.1.7.dist-info}/WHEEL +1 -1
- {fastgenerateapi-0.0.28.dist-info → fastgenerateapi-1.1.7.dist-info}/top_level.txt +1 -0
- script/__init__.py +2 -0
- fastgenerateapi/settings/register_settings.py +0 -6
- fastgenerateapi/utils/parse_str.py +0 -36
- fastgenerateapi-0.0.28.dist-info/RECORD +0 -82
- {fastgenerateapi-0.0.28.dist-info → fastgenerateapi-1.1.7.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
from fastapi import FastAPI
|
|
5
|
+
from opentelemetry import metrics, trace
|
|
6
|
+
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
|
|
7
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
8
|
+
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
|
|
9
|
+
from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
|
|
10
|
+
from opentelemetry.instrumentation.tortoiseorm import TortoiseORMInstrumentor
|
|
11
|
+
from opentelemetry.sdk.metrics import MeterProvider
|
|
12
|
+
from opentelemetry.sdk.metrics._internal.export import PeriodicExportingMetricReader
|
|
13
|
+
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
|
|
14
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
15
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
16
|
+
|
|
17
|
+
from pydantic import Field
|
|
18
|
+
from pydantic_settings import BaseSettings
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class OpenTelemetrySettings(BaseSettings):
|
|
22
|
+
"""
|
|
23
|
+
跟踪和指标配置
|
|
24
|
+
"""
|
|
25
|
+
ENDPOINT: Optional[str] = Field(default=None, description="访问接口", example="http://127.0.0.1")
|
|
26
|
+
AUTHORIZATION: Optional[str] = Field(default=None, description="认证字符串")
|
|
27
|
+
|
|
28
|
+
def setup_open_telemetry(self, app: FastAPI, service_name: str):
|
|
29
|
+
if not self.ENDPOINT:
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
with httpx.Client() as client:
|
|
33
|
+
health_check = client.get(f"{self.ENDPOINT}/healthz", timeout=3).json()
|
|
34
|
+
if health_check["status"] != "ok":
|
|
35
|
+
raise Exception("otlp服务异常")
|
|
36
|
+
resource = Resource(attributes={
|
|
37
|
+
SERVICE_NAME: service_name
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
tracer_provider = TracerProvider(resource=resource)
|
|
41
|
+
headers = {
|
|
42
|
+
"Authorization": self.AUTHORIZATION} if self.AUTHORIZATION else {}
|
|
43
|
+
exporter = OTLPMetricExporter(
|
|
44
|
+
endpoint=f"{self.ENDPOINT}/api/default/v1/metrics",
|
|
45
|
+
headers=headers,
|
|
46
|
+
)
|
|
47
|
+
meter_provider = MeterProvider([PeriodicExportingMetricReader(exporter)], resource=resource)
|
|
48
|
+
metrics.set_meter_provider(meter_provider)
|
|
49
|
+
processor = BatchSpanProcessor(
|
|
50
|
+
OTLPSpanExporter(
|
|
51
|
+
endpoint=f"{self.ENDPOINT}/api/default/v1/traces",
|
|
52
|
+
headers=headers
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
tracer_provider.add_span_processor(processor)
|
|
56
|
+
trace.set_tracer_provider(tracer_provider)
|
|
57
|
+
FastAPIInstrumentor.instrument_app(
|
|
58
|
+
app,
|
|
59
|
+
tracer_provider=tracer_provider,
|
|
60
|
+
meter_provider=meter_provider,
|
|
61
|
+
)
|
|
62
|
+
TortoiseORMInstrumentor().instrument(tracer_provider=tracer_provider)
|
|
63
|
+
HTTPXClientInstrumentor().instrument(tracer_provider=tracer_provider)
|
|
64
|
+
|
|
65
|
+
class Config:
|
|
66
|
+
env_prefix = 'OTLP_'
|
|
67
|
+
env_file = "./.env"
|
|
68
|
+
case_sensitive = True
|
|
69
|
+
extra = 'allow'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RedisSettings(BaseSettings):
|
|
8
|
+
URL: Optional[str] = Field(default="redis://127.0.0.1", description="IP地址")
|
|
9
|
+
PORT: Optional[int] = Field(default=6379, description="映射端口")
|
|
10
|
+
PASSWORD: Optional[str] = Field(default="", description="密码")
|
|
11
|
+
|
|
12
|
+
class Config:
|
|
13
|
+
env_prefix = 'REDIS_'
|
|
14
|
+
env_file = "./.env"
|
|
15
|
+
case_sensitive = True
|
|
16
|
+
extra = 'allow'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SmsSettings(BaseSettings):
|
|
8
|
+
"""
|
|
9
|
+
短信配置
|
|
10
|
+
Sms Settings
|
|
11
|
+
"""
|
|
12
|
+
CACHE_BACKEND: Optional[str] = Field(default="redis", description="缓存类型", title="cache_backend(redis, inmemory)")
|
|
13
|
+
CACHE_BACKEND_DSN: Optional[str] = Field(default="redis://localhost:6379/1", description="缓存dsn", title="cache_backend_dsn")
|
|
14
|
+
IS_LIMIT_CODE_FREQUENCY: Optional[bool] = Field(default=True, description="是否限制频率", title="code frequency check")
|
|
15
|
+
CHECK_CODE_RESEND_TIME: Optional[int] = Field(default=1, description="验证码重新发送时间", title="check code resend time (minute)")
|
|
16
|
+
DEFAULT_CODE: Optional[int] = Field(default="654123", description="默认验证码", title="sms default code")
|
|
17
|
+
|
|
18
|
+
class Config:
|
|
19
|
+
env_prefix = 'Sms_'
|
|
20
|
+
env_file = "./.env"
|
|
21
|
+
case_sensitive = True
|
|
22
|
+
extra = 'allow'
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SystemSettings(BaseSettings):
|
|
8
|
+
"""
|
|
9
|
+
System Settings
|
|
10
|
+
"""
|
|
11
|
+
NAME: str = Field(default='fastgenerateapi', description="项目名称")
|
|
12
|
+
HOST: Optional[str] = Field(default='127.0.0.1', description="本地运行host")
|
|
13
|
+
PORT: Optional[int] = Field(default=8001, description="本地运行port")
|
|
14
|
+
DEBUG: bool = Field(default=True, description="是否开启调试模式,本地修改自动重载")
|
|
15
|
+
|
|
16
|
+
# BASE_DIR: Union[PathType, str, None]
|
|
17
|
+
DOMAIN: Optional[str] = Field(default="http://127.0.0.1:8001", description="服务域名(对外暴露的域名地址包含协议)")
|
|
18
|
+
FIELD_SECRET: Optional[str] = Field(default=None, description="字段加密密钥")
|
|
19
|
+
|
|
20
|
+
# 分布式id
|
|
21
|
+
WORKER_ID: Optional[int] = Field(default='1', description="数据中心(机器区域)ID")
|
|
22
|
+
DATACENTER_ID: Optional[int] = Field(default='1', description="机器ID")
|
|
23
|
+
|
|
24
|
+
class Config:
|
|
25
|
+
env_prefix = ''
|
|
26
|
+
env_file = "./.env"
|
|
27
|
+
case_sensitive = True
|
|
28
|
+
extra = 'allow'
|
|
29
|
+
|
|
30
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional, List
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_path_list(path: str, match_prefix: str, prefix: str = "") -> List[str]:
|
|
8
|
+
files = []
|
|
9
|
+
prefix = prefix + "." + os.path.basename(path) if prefix else os.path.basename(path)
|
|
10
|
+
for item in Path(path).iterdir():
|
|
11
|
+
if item.is_file():
|
|
12
|
+
if item.name.startswith(match_prefix) and item.name.endswith(".py"):
|
|
13
|
+
files.append(prefix+"."+item.name[:-3])
|
|
14
|
+
elif item.is_dir():
|
|
15
|
+
if item.name.startswith(match_prefix):
|
|
16
|
+
for s_item in Path(os.path.join(path, item.name)).iterdir():
|
|
17
|
+
if s_item.is_file() and s_item.name.startswith(match_prefix):
|
|
18
|
+
files.append(prefix + "." + item.name + "." + s_item.name[:-3])
|
|
19
|
+
else:
|
|
20
|
+
files += get_path_list(os.path.join(path, item.name), match_prefix, prefix)
|
|
21
|
+
|
|
22
|
+
return files
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def discover_models(
|
|
26
|
+
base_path="apps", # 模块路径
|
|
27
|
+
include: Optional[List[str]] = None, # 包含的模型
|
|
28
|
+
exclude: Optional[List[str]] = None, # 排除的模型
|
|
29
|
+
) -> List[str]:
|
|
30
|
+
"""
|
|
31
|
+
自动发现models,并生成tortoise-orm所需要的字符串
|
|
32
|
+
会自动添加 aerich.models,如不需要请在 exclude 参数排除
|
|
33
|
+
"""
|
|
34
|
+
models_list = ["aerich.models"] + get_path_list(base_path, "models")
|
|
35
|
+
return include + [model for model in models_list if model not in exclude] if exclude else models_list
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def discover_routes(
|
|
39
|
+
app, # Fastapi 生成的实体类
|
|
40
|
+
base_path="apps", # 模块路径
|
|
41
|
+
prefix="", # 路由前缀
|
|
42
|
+
include: Optional[List[str]] = None, # 包含的路由
|
|
43
|
+
exclude: Optional[List[str]] = None, # 排除的路由
|
|
44
|
+
):
|
|
45
|
+
"""自动发现路由,并注册"""
|
|
46
|
+
paths = include if include else []
|
|
47
|
+
|
|
48
|
+
for path in paths + get_path_list(base_path, "router"):
|
|
49
|
+
if exclude and path in exclude:
|
|
50
|
+
continue
|
|
51
|
+
module = importlib.import_module(path)
|
|
52
|
+
if hasattr(module, "router"):
|
|
53
|
+
app.include_router(prefix=prefix, router=getattr(module, "router"))
|
|
54
|
+
return app
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == '__main__':
|
|
58
|
+
# print(discover_models("../../modules"))
|
|
59
|
+
print(get_path_list("../../modules", "router"))
|
|
60
|
+
|
|
61
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import struct
|
|
2
|
+
|
|
3
|
+
type_dict = {
|
|
4
|
+
# '424D': 'bmp',
|
|
5
|
+
'FFD8FF': 'jpg',
|
|
6
|
+
# '2E524D46': 'rm',
|
|
7
|
+
# '4D546864': 'mid',
|
|
8
|
+
'89504E47': 'png',
|
|
9
|
+
'47494638': 'gif',
|
|
10
|
+
'49492A00': 'tif',
|
|
11
|
+
# '41433130': 'dwg',
|
|
12
|
+
# '38425053': 'psd',
|
|
13
|
+
# '2142444E': 'pst',
|
|
14
|
+
# 'FF575043': 'wpd',
|
|
15
|
+
# 'AC9EBD8F': 'qdf',
|
|
16
|
+
# 'E3828596': 'pwl',
|
|
17
|
+
'504B0304': 'zip',
|
|
18
|
+
'52617221': 'rar',
|
|
19
|
+
'57415645': 'wav',
|
|
20
|
+
'41564920': 'avi',
|
|
21
|
+
'2E7261FD': 'ram',
|
|
22
|
+
'000001BA': 'mpg',
|
|
23
|
+
'000001B3': 'mpg',
|
|
24
|
+
'6D6F6F76': 'mov',
|
|
25
|
+
# '7B5C727466': 'rtf',
|
|
26
|
+
'3C3F786D6C': 'xml',
|
|
27
|
+
'68746D6C3E': 'html',
|
|
28
|
+
'D0CF11E0': 'doc/xls',
|
|
29
|
+
'255044462D312E': 'pdf',
|
|
30
|
+
'CFAD12FEC5FD746F': 'dbx',
|
|
31
|
+
# '3026B2758E66CF11': 'asf',
|
|
32
|
+
'5374616E64617264204A': 'mdb',
|
|
33
|
+
# '252150532D41646F6265': 'ps/eps',
|
|
34
|
+
# '44656C69766572792D646174653A': 'eml'
|
|
35
|
+
}
|
|
36
|
+
max_len = len(max(type_dict, key=len)) // 2
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def get_filetype(file):
|
|
40
|
+
# 读取二进制文件开头一定的长度
|
|
41
|
+
if isinstance(file, str):
|
|
42
|
+
filename = file
|
|
43
|
+
with open(filename, 'rb') as f:
|
|
44
|
+
byte = f.read(max_len)
|
|
45
|
+
else:
|
|
46
|
+
filename = file.filename
|
|
47
|
+
byte = await file.read(max_len)
|
|
48
|
+
await file.seek(0)
|
|
49
|
+
# 解析为元组
|
|
50
|
+
byte_list = struct.unpack('B' * max_len, byte)
|
|
51
|
+
# 转为16进制
|
|
52
|
+
code = ''.join([('%X' % each).zfill(2) for each in byte_list])
|
|
53
|
+
# 根据标识符筛选判断文件格式
|
|
54
|
+
result = list(filter(lambda x: code.startswith(x), type_dict))
|
|
55
|
+
nametype = filename.split('.')[-1]
|
|
56
|
+
if result:
|
|
57
|
+
filetype = type_dict[result[0]]
|
|
58
|
+
if filetype == "zip":
|
|
59
|
+
filetype = nametype if nametype in ['xlsx', 'docx'] else "zip"
|
|
60
|
+
elif filetype == "doc/xls":
|
|
61
|
+
filetype = nametype if nametype in ['doc', 'xls'] else None
|
|
62
|
+
elif filetype == "jpg":
|
|
63
|
+
filetype = nametype if nametype in ['jpg', 'jpeg'] else None
|
|
64
|
+
else:
|
|
65
|
+
filetype = filetype if filetype == nametype else None
|
|
66
|
+
else:
|
|
67
|
+
filetype = nametype if nametype in ['mp3', 'mp4', 'txt'] else None
|
|
68
|
+
return filetype
|
|
69
|
+
|
|
70
|
+
file_type = {
|
|
71
|
+
"image": ["jpg", "png", "gif", "tif"],
|
|
72
|
+
"document": ["zip", "rar", "wav", "xml", "html", "doc", "xls", "pdf", "dbx", "mdb"],
|
|
73
|
+
"voice": ["mp3", "amr"],
|
|
74
|
+
"video": ["wav", "avi", "ram", "mpg", "mov", "mp4"],
|
|
75
|
+
}
|
|
76
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
from passlib.hash import pbkdf2_sha256, md5_crypt
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def make_random_salt(length=64):
|
|
7
|
+
"""
|
|
8
|
+
生成随机salt字符串,可指定长度
|
|
9
|
+
:param length: int
|
|
10
|
+
:return: salt: str
|
|
11
|
+
"""
|
|
12
|
+
seed = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&*="
|
|
13
|
+
sa = []
|
|
14
|
+
for i in range(length):
|
|
15
|
+
sa.append(random.choice(seed))
|
|
16
|
+
salt = ''.join(sa)
|
|
17
|
+
return salt
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def create_password_hash(password: str, salt: str) -> str:
|
|
21
|
+
"""
|
|
22
|
+
根据传入的password与salt参数生成hash值
|
|
23
|
+
:param password:
|
|
24
|
+
:param salt:
|
|
25
|
+
:return:
|
|
26
|
+
"""
|
|
27
|
+
custom_pbkdf2 = pbkdf2_sha256.using(salt=salt.encode('utf-8'), rounds=1000)
|
|
28
|
+
return custom_pbkdf2.hash(password)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def validate_password(password: str, salt: str, password_hash: str) -> bool:
|
|
32
|
+
"""
|
|
33
|
+
password 校验
|
|
34
|
+
:param password:
|
|
35
|
+
:param salt:
|
|
36
|
+
:param password_hash:
|
|
37
|
+
:return:
|
|
38
|
+
"""
|
|
39
|
+
validating_password_hash = create_password_hash(password, salt)
|
|
40
|
+
return validating_password_hash == password_hash
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# if __name__ == '__main__':
|
|
44
|
+
# # salt = make_random_salt(length=60)
|
|
45
|
+
# salt = "lpiZRQwWoj&*wh29Jovk$uA3YGP$TSQYpM4ooy1z3zGuNT=vmLpb&Ves"
|
|
46
|
+
# hash_str = create_password_hash('123456132312312312312321', salt)
|
|
47
|
+
# print(hash_str)
|
|
48
|
+
# print(len(hash_str))
|
|
49
|
+
# print(validate_password(password='123456132312312312312321', salt=salt, password_hash=hash_str))
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import random
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_code(n=4):
|
|
7
|
+
"""
|
|
8
|
+
生成随机验证码,数字和字母的概率各一半
|
|
9
|
+
:param n: 验证码长度
|
|
10
|
+
:return: str
|
|
11
|
+
"""
|
|
12
|
+
s = ''
|
|
13
|
+
for i in range(n):
|
|
14
|
+
ret_num = random.randint(0, 9)
|
|
15
|
+
ret_alpha = chr(random.randint(65, 90))
|
|
16
|
+
result = random.choice([ret_num, ret_alpha])
|
|
17
|
+
s += str(result)
|
|
18
|
+
return s
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def random_str() -> str:
|
|
22
|
+
"""
|
|
23
|
+
唯一随机字符串
|
|
24
|
+
:return: str
|
|
25
|
+
"""
|
|
26
|
+
only = hashlib.md5(str(uuid.uuid1()).encode(encoding='UTF-8')).hexdigest()
|
|
27
|
+
return str(only)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_code_number(n: int = 6) -> str:
|
|
31
|
+
"""
|
|
32
|
+
随机数字
|
|
33
|
+
:param n: 长度
|
|
34
|
+
:return: str
|
|
35
|
+
"""
|
|
36
|
+
code = ""
|
|
37
|
+
for i in range(n):
|
|
38
|
+
ch = chr(random.randrange(ord('0'), ord('9') + 1))
|
|
39
|
+
code += ch
|
|
40
|
+
|
|
41
|
+
return code
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == '__main__':
|
|
45
|
+
print(get_code())
|
|
46
|
+
print(random_str())
|
|
47
|
+
print(get_code_number(6))
|
|
48
|
+
|
|
@@ -5,7 +5,7 @@ from fastapi.exceptions import RequestValidationError
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
# 64位ID的划分
|
|
8
|
-
from fastgenerateapi.settings.
|
|
8
|
+
from fastgenerateapi.settings.all_settings import settings
|
|
9
9
|
|
|
10
10
|
WORKER_ID_BITS = 5 # 机器位
|
|
11
11
|
DATACENTER_ID_BITS = 5 # 数据位
|
|
@@ -73,30 +73,31 @@ class IdWorker(object):
|
|
|
73
73
|
"""
|
|
74
74
|
return int(time.time() * 1000)
|
|
75
75
|
|
|
76
|
-
def
|
|
76
|
+
def get_code(self, prefix: str = ""):
|
|
77
77
|
"""
|
|
78
|
-
|
|
78
|
+
获取有序编码,高并发不推荐使用,会出现重复值
|
|
79
79
|
:return:
|
|
80
80
|
"""
|
|
81
|
-
|
|
81
|
+
def make_code():
|
|
82
|
+
timestamp = self._gen_timestamp()
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
# 时钟回拨
|
|
85
|
+
if timestamp < self.last_timestamp:
|
|
86
|
+
raise RequestValidationError
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
if timestamp == self.last_timestamp:
|
|
89
|
+
self.sequence = (self.sequence + 1) & 7
|
|
90
|
+
if self.sequence == 0:
|
|
91
|
+
timestamp = self._til_next_millis(self.last_timestamp)
|
|
92
|
+
else:
|
|
93
|
+
self.sequence = 0
|
|
93
94
|
|
|
94
|
-
|
|
95
|
+
self.last_timestamp = timestamp
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return
|
|
97
|
+
new_id = (int((timestamp - TWEPOCH) / 100) << 2) | (self.datacenter_id << 2) | \
|
|
98
|
+
(self.worker_id << 2) | self.sequence
|
|
99
|
+
return prefix + str(new_id)
|
|
100
|
+
return make_code
|
|
100
101
|
|
|
101
102
|
def get_id(self):
|
|
102
103
|
"""
|
|
@@ -139,8 +140,10 @@ class IdWorker(object):
|
|
|
139
140
|
worker = IdWorker(datacenter_id=settings.app_settings.DATACENTER_ID, worker_id=settings.app_settings.WORKER_ID)
|
|
140
141
|
|
|
141
142
|
|
|
142
|
-
|
|
143
|
-
#
|
|
143
|
+
if __name__ == '__main__':
|
|
144
|
+
# worker = IdWorker()
|
|
145
|
+
print(worker.get_code()())
|
|
146
|
+
print(worker.get_code("A")())
|
|
144
147
|
# pk = worker.get_id()
|
|
145
148
|
# print(pk)
|
|
146
149
|
# print(len(str(pk)), len("9223372036854775807"))
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from typing import Union, Optional, List
|
|
2
|
+
|
|
3
|
+
from fastgenerateapi.settings.all_settings import settings
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def parse_str_to_bool(val: Union[int, str, bool, None]) -> bool:
|
|
7
|
+
"""
|
|
8
|
+
解析字符串到布尔值
|
|
9
|
+
"""
|
|
10
|
+
if type(val) == bool:
|
|
11
|
+
return val
|
|
12
|
+
elif type(val) == int:
|
|
13
|
+
return val != 0
|
|
14
|
+
elif type(val) == str:
|
|
15
|
+
if val.upper() in ("1", "ON", "TRUE"):
|
|
16
|
+
return True
|
|
17
|
+
elif val.upper() in ("0", "OFF", "FALSE"):
|
|
18
|
+
return False
|
|
19
|
+
return settings.app_settings.DEFAULT_WHETHER_PAGE
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def parse_str_to_int(val: Union[int, str, None]) -> int:
|
|
23
|
+
"""
|
|
24
|
+
解析字符串到数值
|
|
25
|
+
"""
|
|
26
|
+
if type(val) == int:
|
|
27
|
+
return val
|
|
28
|
+
elif type(val) == str:
|
|
29
|
+
try:
|
|
30
|
+
val = int(val)
|
|
31
|
+
except Exception as e:
|
|
32
|
+
val = 0
|
|
33
|
+
return val
|
|
34
|
+
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def parse_str_to_list(val: Optional[str], split_list: Optional[List[str]] = None) -> List[str]:
|
|
39
|
+
"""
|
|
40
|
+
某些字段可能由、,,等拼接,字符串分割为列表
|
|
41
|
+
:param val: 我、你,他,她
|
|
42
|
+
:param split_list: 默认 ["、", ",", ","]
|
|
43
|
+
:return: ["我", "你", "他", "她"]
|
|
44
|
+
"""
|
|
45
|
+
# 如果输入为空,返回空列表
|
|
46
|
+
if not val:
|
|
47
|
+
return []
|
|
48
|
+
|
|
49
|
+
# 设置默认分隔符列表
|
|
50
|
+
if split_list is None:
|
|
51
|
+
split_list = ["、", ",", ","]
|
|
52
|
+
|
|
53
|
+
# 初始化结果列表
|
|
54
|
+
result = [val]
|
|
55
|
+
|
|
56
|
+
# 遍历分隔符,逐步拆分字符串
|
|
57
|
+
for sep in split_list:
|
|
58
|
+
result = [item for sublist in result for item in sublist.split(sep)]
|
|
59
|
+
|
|
60
|
+
# 去除每个元素的前后空格
|
|
61
|
+
result = [item.strip() for item in result]
|
|
62
|
+
|
|
63
|
+
# 过滤掉空字符串
|
|
64
|
+
result = [item for item in result if item]
|
|
65
|
+
|
|
66
|
+
return result
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def number_to_chinese(num):
|
|
70
|
+
"""
|
|
71
|
+
数字转换为大写中文,会自动带上整字
|
|
72
|
+
:param num:
|
|
73
|
+
:return:
|
|
74
|
+
"""
|
|
75
|
+
if num == 0:
|
|
76
|
+
return "零元整"
|
|
77
|
+
dict1 = {1: '壹', 2: '贰', 3: '叁', 4: '肆', 5: '伍', 6: '陆', 7: '柒', 8: '捌', 9: '玖', 0: '零'}
|
|
78
|
+
dict2 = {2: '拾', 3: '佰', 4: '仟', 5: '万', 6: '拾', 7: '佰', 8: '仟', 1: '元', 9: '角', 10: '分', 11: '整'}
|
|
79
|
+
money = ''
|
|
80
|
+
flag = False
|
|
81
|
+
flag2 = False
|
|
82
|
+
count = 0
|
|
83
|
+
count2 = 8
|
|
84
|
+
strnum = str(num)
|
|
85
|
+
aa = strnum.split('.')
|
|
86
|
+
bb = list(str(aa[:1])[2:-2])
|
|
87
|
+
cc = list(str(aa[1]).rstrip("0")[:2]) if len(aa) > 1 else []
|
|
88
|
+
|
|
89
|
+
for i in reversed(bb):
|
|
90
|
+
count = count + 1
|
|
91
|
+
if int(i) == 0:
|
|
92
|
+
if flag:
|
|
93
|
+
if count != 5:
|
|
94
|
+
continue
|
|
95
|
+
else:
|
|
96
|
+
money = dict2[count] + money
|
|
97
|
+
else:
|
|
98
|
+
if not flag2:
|
|
99
|
+
money = dict2[count] + money
|
|
100
|
+
else:
|
|
101
|
+
if count != 5:
|
|
102
|
+
money = '零' + money
|
|
103
|
+
else:
|
|
104
|
+
money = dict2[count] + '零' + money
|
|
105
|
+
flag = True
|
|
106
|
+
else:
|
|
107
|
+
flag = False
|
|
108
|
+
flag2 = True
|
|
109
|
+
money = dict1[int(i)] + dict2[count] + money
|
|
110
|
+
for i in cc:
|
|
111
|
+
count2 = count2 + 1
|
|
112
|
+
money = money + dict1[int(i)] + dict2[count2]
|
|
113
|
+
|
|
114
|
+
return money + '整'
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if __name__ == '__main__':
|
|
118
|
+
# test parse_str_to_list
|
|
119
|
+
print(parse_str_to_list("我、你,他,她"))
|
|
120
|
+
print(parse_str_to_list("我|你&他 她", split_list=["|", "&", " "]))
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SwaggerUtil:
|
|
5
|
+
|
|
6
|
+
@staticmethod
|
|
7
|
+
def swagger_to_js(swagger_json: Optional[dict]):
|
|
8
|
+
"""
|
|
9
|
+
通过swagger文档自动生成js接口文件,方便前后端开发
|
|
10
|
+
如果 swagger_json 为空,
|
|
11
|
+
- 优先检查运行路径下的swagger.json文件
|
|
12
|
+
- 其次检查 127.0.0.1:8000/openapi.json 接口
|
|
13
|
+
:param swagger_json:
|
|
14
|
+
:return:
|
|
15
|
+
"""
|
|
16
|
+
# 参考go zero 生成js文件的样式
|
|
17
|
+
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|