fastgenerateapi 0.0.23__py2.py3-none-any.whl → 0.0.25__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.

Files changed (29) hide show
  1. fastgenerateapi/__version__.py +1 -1
  2. fastgenerateapi/api_view/base_view.py +29 -14
  3. fastgenerateapi/api_view/delete_tree_view.py +13 -11
  4. fastgenerateapi/api_view/delete_view.py +12 -11
  5. fastgenerateapi/api_view/get_tree_view.py +2 -1
  6. fastgenerateapi/api_view/mixin/dbmodel_mixin.py +5 -3
  7. fastgenerateapi/example/models.py +19 -11
  8. fastgenerateapi/example/views.py +38 -26
  9. fastgenerateapi/my_fields/__init__.py +4 -0
  10. fastgenerateapi/my_fields/aes_field.py +166 -0
  11. fastgenerateapi/my_fields/enum_field.py +215 -0
  12. fastgenerateapi/my_fields/pk_field.py +68 -0
  13. fastgenerateapi/my_fields/pwd_field.py +81 -0
  14. fastgenerateapi/my_fields/soft_delete_field.py +54 -0
  15. fastgenerateapi/schemas_factory/common_function.py +2 -1
  16. fastgenerateapi/schemas_factory/common_schema_factory.py +1 -1
  17. fastgenerateapi/schemas_factory/create_schema_factory.py +9 -3
  18. fastgenerateapi/schemas_factory/get_all_schema_factory.py +7 -0
  19. fastgenerateapi/schemas_factory/get_one_schema_factory.py +8 -0
  20. fastgenerateapi/schemas_factory/get_tree_schema_factory.py +8 -0
  21. fastgenerateapi/schemas_factory/update_schema_factory.py +9 -3
  22. fastgenerateapi/settings/settings.py +12 -7
  23. fastgenerateapi/utils/aes.py +93 -0
  24. fastgenerateapi/utils/snowflake.py +148 -0
  25. {fastgenerateapi-0.0.23.dist-info → fastgenerateapi-0.0.25.dist-info}/METADATA +1 -1
  26. {fastgenerateapi-0.0.23.dist-info → fastgenerateapi-0.0.25.dist-info}/RECORD +29 -21
  27. {fastgenerateapi-0.0.23.dist-info → fastgenerateapi-0.0.25.dist-info}/WHEEL +1 -1
  28. {fastgenerateapi-0.0.23.dist-info → fastgenerateapi-0.0.25.dist-info}/LICENSE +0 -0
  29. {fastgenerateapi-0.0.23.dist-info → fastgenerateapi-0.0.25.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,215 @@
1
+ from enum import IntEnum
2
+ from typing import (
3
+ TYPE_CHECKING,
4
+ Any,
5
+ Callable,
6
+ Dict,
7
+ Generic,
8
+ List,
9
+ Optional,
10
+ Tuple,
11
+ Type,
12
+ TypeVar,
13
+ Union,
14
+ overload,
15
+ )
16
+
17
+ from fastapi import HTTPException
18
+ from tortoise.fields.data import IntEnumFieldInstance
19
+
20
+ if TYPE_CHECKING: # pragma: nocoverage
21
+ from tortoise.models import Model
22
+
23
+
24
+ class IntEnumField(IntEnumFieldInstance):
25
+ """
26
+ 传入参数数组,默认生成枚举类,数字从 1 开始
27
+ 例如:
28
+ enum_list = ["A", "B"]
29
+ 相当于枚举
30
+ class CategoryEnum(IntEnum):
31
+ one = 1
32
+ two = 2
33
+ 通过方法 get_name 获取对应的值, "A"
34
+ """
35
+
36
+ def __init__(self, enum_list: List[any], **kwargs: Any) -> None:
37
+ kwargs.setdefault("description", self.get_description(enum_list))
38
+ self.enum_list = enum_list
39
+ self.description = kwargs.get("description")
40
+ super().__init__(enum_type=self.create_enum(enum_list), **kwargs)
41
+
42
+ @property
43
+ def constraints(self) -> dict:
44
+ return {
45
+ "ge": 0,
46
+ "le": len(self.enum_list),
47
+ }
48
+
49
+ def to_db_value(self, value: Any, instance: "Union[Type[Model], Model]") -> Any:
50
+ """
51
+ Converts from the Python type to the DB type.
52
+ """
53
+ if value is not None:
54
+ value = int(value) # pylint: disable=E1102
55
+ if value > len(self.enum_list) or 0 > value:
56
+ raise HTTPException(detail=f"枚举值:{value} 校验失败。【{self.description}】", status_code=422)
57
+ self.validate(value)
58
+ return value
59
+
60
+ def to_python_value(self, value: Any) -> Any:
61
+ """
62
+ Converts from the DB type to the Python type.
63
+ """
64
+ if value is not None:
65
+ value = int(value) # pylint: disable=E1102
66
+ self.validate(value)
67
+ return IntEnumClass(value, self.enum_list)
68
+
69
+ def get_description(self, enum_list):
70
+ description = ""
71
+ for index, val in enumerate(enum_list, 1):
72
+ description += f"{index}:{val};"
73
+ return description
74
+
75
+ def create_enum(self, enum_list):
76
+ # 创建枚举类的成员字典,确保值是唯一的
77
+
78
+ members = {self.number_to_words(name): name for name, _ in enumerate(enum_list, 1)}
79
+
80
+ # 使用Enum的元类EnumMeta来动态创建枚举类
81
+ enum_class = IntEnum("CategoryEnum", members)
82
+
83
+ # 返回创建的枚举类
84
+ return enum_class
85
+
86
+ def number_to_words(self, num):
87
+ # 定义数字到单词的映射
88
+ ones = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten',
89
+ 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen']
90
+ tens = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']
91
+
92
+ if num == 0:
93
+ return 'zero'
94
+
95
+ if num < 20:
96
+ return ones[num]
97
+
98
+ if num < 100:
99
+ ten_digit = num // 10
100
+ one_digit = num % 10
101
+ if one_digit == 0:
102
+ return tens[ten_digit]
103
+ else:
104
+ return tens[ten_digit] + '_' + ones[one_digit]
105
+
106
+ if num < 1000:
107
+ hundred_digit = num // 100
108
+ remaining = num % 100
109
+ return ones[hundred_digit] + '_hundred_' + self.number_to_words(remaining)
110
+
111
+ # 对于大于1000的数字,可以进一步扩展这个函数来处理
112
+ # 但请注意,标准的英文数字读法会变得复杂,涉及"thousand", "million", "billion"等词
113
+
114
+ # 这里仅处理到9999,如果需要处理更大的数字,请继续扩展这个函数
115
+ thousand_digit = num // 1000
116
+ remaining = num % 1000
117
+ return self.number_to_words(thousand_digit) + '_thousand_' + self.number_to_words(remaining)
118
+
119
+
120
+ class IntEnumClass:
121
+ def __init__(self, value: int, name_list: list):
122
+ self.value = value
123
+ self.name_list = name_list
124
+
125
+ def __str__(self):
126
+ return f"{self.value}"
127
+
128
+ @property
129
+ def name(self):
130
+ try:
131
+ return self.name_list[self.value-1]
132
+ except:
133
+ return self.value
134
+
135
+ def __add__(self, other: Union["IntEnumClass", int]):
136
+ if isinstance(other, IntEnumClass):
137
+ return IntEnumClass(self.value + other.value, self.name_list)
138
+ elif isinstance(other, int):
139
+ return IntEnumClass(self.value + other, self.name_list)
140
+ else:
141
+ raise TypeError(f"unsupported operand type(s) for +: 'NamedInt' and '{type(other).__name__}'")
142
+
143
+ def __sub__(self, other: Union["IntEnumClass", int]):
144
+ if isinstance(other, IntEnumClass):
145
+ return IntEnumClass(self.value - other.value, self.name_list)
146
+ elif isinstance(other, int):
147
+ return IntEnumClass(self.value - other, self.name_list)
148
+ else:
149
+ raise TypeError(f"unsupported operand type(s) for -: 'IntEnumClass' and '{type(other).__name__}'")
150
+
151
+ def __mul__(self, other: Union["IntEnumClass", int]):
152
+ if isinstance(other, IntEnumClass):
153
+ return IntEnumClass(self.value * other.value, self.name_list)
154
+ elif isinstance(other, int):
155
+ return IntEnumClass(self.value * other, self.name_list)
156
+ else:
157
+ raise TypeError(f"unsupported operand type(s) for *: 'IntEnumClass' and '{type(other).__name__}'")
158
+
159
+ def __truediv__(self, other: Union["IntEnumClass", int]):
160
+ if isinstance(other, IntEnumClass):
161
+ if other.value == 0:
162
+ raise ZeroDivisionError("division by zero")
163
+ return IntEnumClass(self.value // other.value, self.name_list)
164
+ elif isinstance(other, int):
165
+ if other == 0:
166
+ raise ZeroDivisionError("division by zero")
167
+ return IntEnumClass(self.value // other, self.name_list)
168
+ else:
169
+ raise TypeError(f"unsupported operand type(s) for /: 'IntEnumClass' and '{type(other).__name__}'")
170
+
171
+ def __eq__(self, other: Union["IntEnumClass", int]):
172
+ if isinstance(other, IntEnumClass):
173
+ return self.value == other.value
174
+ elif isinstance(other, int):
175
+ return self.value == other
176
+ return NotImplemented
177
+
178
+ def __ne__(self, other: Union["IntEnumClass", int]):
179
+ result = self.__eq__(other)
180
+ if result is NotImplemented:
181
+ return result
182
+ return not result
183
+
184
+ def __lt__(self, other: Union["IntEnumClass", int]):
185
+ if isinstance(other, IntEnumClass):
186
+ return self.value < other.value
187
+ elif isinstance(other, int):
188
+ return self.value < other
189
+ return NotImplemented
190
+
191
+ def __le__(self, other: Union["IntEnumClass", int]):
192
+ if isinstance(other, IntEnumClass):
193
+ return self.value <= other.value
194
+ elif isinstance(other, int):
195
+ return self.value <= other
196
+ return NotImplemented
197
+
198
+ def __gt__(self, other: Union["IntEnumClass", int]):
199
+ if isinstance(other, IntEnumClass):
200
+ return self.value > other.value
201
+ elif isinstance(other, int):
202
+ return self.value > other
203
+ return NotImplemented
204
+
205
+ def __ge__(self, other: Union["IntEnumClass", int]):
206
+ if isinstance(other, IntEnumClass):
207
+ return self.value >= other.value
208
+ elif isinstance(other, int):
209
+ return self.value >= other
210
+ return NotImplemented
211
+
212
+
213
+
214
+
215
+
@@ -0,0 +1,68 @@
1
+ from typing import (
2
+ TYPE_CHECKING,
3
+ Any,
4
+ Callable,
5
+ Dict,
6
+ Generic,
7
+ List,
8
+ Optional,
9
+ Tuple,
10
+ Type,
11
+ TypeVar,
12
+ Union,
13
+ overload,
14
+ )
15
+ from tortoise.fields import Field
16
+
17
+ if TYPE_CHECKING: # pragma: nocoverage
18
+ from tortoise.models import Model
19
+
20
+ from fastgenerateapi.utils.snowflake import worker
21
+
22
+
23
+ class PrimaryKeyField(Field[str], str):
24
+ """
25
+ Big integer field. (64-bit signed)
26
+ """
27
+
28
+ SQL_TYPE = "BIGINT"
29
+ allows_generated = True
30
+
31
+ def __init__(self, **kwargs: Any) -> None:
32
+ kwargs["generated"] = False
33
+ kwargs["unique"] = True
34
+ kwargs["description"] = "主键"
35
+ kwargs["default"] = worker.get_id
36
+ super().__init__(pk=True, **kwargs)
37
+
38
+ def to_db_value(self, value: Any, instance: "Union[Type[Model], Model]") -> Any:
39
+ """
40
+ Converts from the Python type to the DB type.
41
+ """
42
+ if value is not None:
43
+ value = int(value) # pylint: disable=E1102
44
+ self.validate(value)
45
+ return value
46
+
47
+ def to_python_value(self, value: Any) -> Any:
48
+ """
49
+ Converts from the DB type to the Python type.
50
+ """
51
+ if value is not None:
52
+ value = str(value) # pylint: disable=E1102
53
+ self.validate(value)
54
+ return value
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
@@ -0,0 +1,81 @@
1
+ from typing import (
2
+ TYPE_CHECKING,
3
+ Any,
4
+ Callable,
5
+ Dict,
6
+ Generic,
7
+ List,
8
+ Optional,
9
+ Tuple,
10
+ Type,
11
+ TypeVar,
12
+ Union,
13
+ overload,
14
+ )
15
+
16
+ from passlib.handlers.pbkdf2 import pbkdf2_sha256
17
+ from tortoise.fields import CharField
18
+ if TYPE_CHECKING: # pragma: nocoverage
19
+ from tortoise.models import Model
20
+
21
+
22
+ class PasswordField(CharField):
23
+ """
24
+ Character field.
25
+
26
+ You must provide the following:
27
+
28
+ ``salt`` (str):
29
+ """
30
+
31
+ def __init__(self, salt: str, rounds=1000, **kwargs: Any) -> None:
32
+ kwargs.setdefault("max_length", 255)
33
+ kwargs.setdefault("description", "密码")
34
+ self.salt = salt
35
+ self.rounds = rounds
36
+ super().__init__(**kwargs)
37
+
38
+ def to_db_value(self, value: Any, instance: "Union[Type[Model], Model]") -> Any:
39
+ """
40
+ Converts from the Python type to the DB type.
41
+ """
42
+ if value is not None:
43
+ if not isinstance(value, self.field_type):
44
+ value = self.field_type(value) # pylint: disable=E1102
45
+ custom_pbkdf2 = pbkdf2_sha256.using(salt=self.salt.encode('utf-8'), rounds=self.rounds)
46
+ value = custom_pbkdf2.hash(value)
47
+
48
+ self.validate(value)
49
+ return value
50
+
51
+ def to_python_value(self, value: Any) -> Any:
52
+ """
53
+ Converts from the DB type to the Python type.
54
+ """
55
+ if value is not None and not isinstance(value, self.field_type):
56
+ value = PasswordString(self.field_type(value), self.salt, self.rounds) # pylint: disable=E1102
57
+ self.validate(value)
58
+ return value
59
+
60
+
61
+ class PasswordString(str):
62
+ def __init__(self, password, salt, rounds):
63
+ self.value = password
64
+ self.salt = salt
65
+ self.rounds = rounds
66
+ super().__init__()
67
+
68
+ def __str__(self):
69
+ return self.value
70
+
71
+ def check_valid(self, password: str) -> bool:
72
+ """
73
+ 检查密码是否正确
74
+ """
75
+ custom_pbkdf2 = pbkdf2_sha256.using(salt=self.salt.encode('utf-8'), rounds=self.rounds)
76
+ return self.password == custom_pbkdf2.hash(password)
77
+
78
+
79
+
80
+
81
+
@@ -0,0 +1,54 @@
1
+ from typing import (
2
+ TYPE_CHECKING,
3
+ Any,
4
+ Callable,
5
+ Dict,
6
+ Generic,
7
+ List,
8
+ Optional,
9
+ Tuple,
10
+ Type,
11
+ TypeVar,
12
+ Union,
13
+ overload,
14
+ )
15
+ from tortoise.fields import IntField
16
+ if TYPE_CHECKING: # pragma: nocoverage
17
+ from tortoise.models import Model
18
+
19
+
20
+ class SoftDeleteField(IntField):
21
+ """
22
+ Integer field. (64-bit signed)
23
+ """
24
+
25
+ allows_generated = False
26
+
27
+ def __init__(self, **kwargs: Any) -> None:
28
+ kwargs.setdefault("index", True)
29
+ kwargs.setdefault("null", True)
30
+ kwargs.setdefault("default", None)
31
+ kwargs.setdefault("description", "软删除")
32
+ super().__init__(**kwargs)
33
+
34
+ def to_db_value(self, value: Any, instance: "Union[Type[Model], Model]") -> Any:
35
+ """
36
+ Converts from the Python type to the DB type.
37
+ """
38
+ if value is not None and not isinstance(value, self.field_type):
39
+ value = self.field_type(value) # pylint: disable=E1102
40
+ if value == 0:
41
+ value = None
42
+ self.validate(value)
43
+ return value
44
+
45
+ def to_python_value(self, value: Any) -> Any:
46
+ """
47
+ Converts from the DB type to the Python type.
48
+ """
49
+ if value is not None and not isinstance(value, self.field_type):
50
+ value = self.field_type(value) # pylint: disable=E1102
51
+ self.validate(value)
52
+ return value
53
+
54
+
@@ -63,7 +63,8 @@ def get_field_info_from_model_class(model_class: Type[Model], field: str, descri
63
63
  return Optional[str], FieldInfo(default=None, description=f"{field}")
64
64
  return get_field_info(value, description=description)
65
65
 
66
- if field_info := model_class._meta.fields_map.get(field.split("__", maxsplit=1)[0]):
66
+ field_info = model_class._meta.fields_map.get(field.split("__", maxsplit=1)[0])
67
+ if field_info:
67
68
  description += field_info.description or ""
68
69
 
69
70
  model_class = DBModelMixin._get_foreign_key_relation_class(model_class, field.split("__", maxsplit=1)[0])
@@ -26,7 +26,7 @@ def common_schema_factory(
26
26
  include_fields = set()
27
27
  exclude_fields = set()
28
28
  if exclude_readonly:
29
- exclude_fields.update(["id", "is_active", "created_at", "modified_at", "updated_at"])
29
+ exclude_fields.update(["id", "is_active", "deleted_at", "created_at", "modified_at", "updated_at"])
30
30
  if hasattr(model_class, "PydanticMeta"):
31
31
  if hasattr(model_class.PydanticMeta, "include"):
32
32
  include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.include)
@@ -34,10 +34,16 @@ def create_schema_factory(
34
34
  include_fields.update(all_fields_info.keys())
35
35
  if hasattr(model_class.PydanticMeta, "exclude"):
36
36
  exclude_fields.update(model_class.PydanticMeta.exclude)
37
+ if hasattr(model_class.PydanticMeta, "save_include"):
38
+ save_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.save_include)
39
+ all_fields_info.update(save_include_fields_dict)
40
+ include_fields.update(save_include_fields_dict.keys())
37
41
  if hasattr(model_class.PydanticMeta, "create_include"):
38
- get_one_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.create_include)
39
- all_fields_info.update(get_one_include_fields_dict)
40
- include_fields.update(get_one_include_fields_dict.keys())
42
+ create_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.create_include)
43
+ all_fields_info.update(create_include_fields_dict)
44
+ include_fields.update(create_include_fields_dict.keys())
45
+ if hasattr(model_class.PydanticMeta, "save_exclude"):
46
+ exclude_fields.update(model_class.PydanticMeta.save_exclude)
41
47
  if hasattr(model_class.PydanticMeta, "create_exclude"):
42
48
  exclude_fields.update(model_class.PydanticMeta.create_exclude)
43
49
  else:
@@ -40,6 +40,13 @@ def get_all_schema_factory(
40
40
  if hasattr(model_class.PydanticMeta, "exclude"):
41
41
  exclude_fields.update(model_class.PydanticMeta.exclude)
42
42
 
43
+ if hasattr(model_class.PydanticMeta, "get_include"):
44
+ get_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.get_include)
45
+ all_fields_info.update(get_include_fields_dict)
46
+ include_fields.update(get_include_fields_dict.keys())
47
+ if hasattr(model_class.PydanticMeta, "get_exclude"):
48
+ exclude_fields.update(model_class.PydanticMeta.get_exclude)
49
+
43
50
  # get_all_include
44
51
  if hasattr(model_class.PydanticMeta, "get_all_include"):
45
52
  get_all_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.get_all_include)
@@ -33,6 +33,14 @@ def get_one_schema_factory(
33
33
  include_fields.update(all_fields_info.keys())
34
34
  if hasattr(model_class.PydanticMeta, "exclude"):
35
35
  exclude_fields.update(model_class.PydanticMeta.exclude)
36
+
37
+ if hasattr(model_class.PydanticMeta, "get_include"):
38
+ get_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.get_include)
39
+ all_fields_info.update(get_include_fields_dict)
40
+ include_fields.update(get_include_fields_dict.keys())
41
+ if hasattr(model_class.PydanticMeta, "get_exclude"):
42
+ exclude_fields.update(model_class.PydanticMeta.get_exclude)
43
+
36
44
  if hasattr(model_class.PydanticMeta, "get_one_include"):
37
45
  get_one_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.get_one_include)
38
46
  all_fields_info.update(get_one_include_fields_dict)
@@ -38,6 +38,14 @@ def get_tree_schema_factory(
38
38
  include_fields.update(include_fields_dict.keys())
39
39
  else:
40
40
  include_fields.update(all_fields_info.keys())
41
+
42
+ if hasattr(model_class.PydanticMeta, "get_include"):
43
+ get_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.get_include)
44
+ all_fields_info.update(get_include_fields_dict)
45
+ include_fields.update(get_include_fields_dict.keys())
46
+ if hasattr(model_class.PydanticMeta, "get_exclude"):
47
+ exclude_fields.update(model_class.PydanticMeta.get_exclude)
48
+
41
49
  if hasattr(model_class.PydanticMeta, "get_tree_exclude"):
42
50
  exclude_fields.update(model_class.PydanticMeta.get_tree_exclude)
43
51
  if hasattr(model_class.PydanticMeta, "exclude"):
@@ -34,10 +34,16 @@ def update_schema_factory(
34
34
  include_fields.update(all_fields_info.keys())
35
35
  if hasattr(model_class.PydanticMeta, "exclude"):
36
36
  exclude_fields.update(model_class.PydanticMeta.exclude)
37
+ if hasattr(model_class.PydanticMeta, "save_include"):
38
+ save_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.save_include)
39
+ all_fields_info.update(save_include_fields_dict)
40
+ include_fields.update(save_include_fields_dict.keys())
37
41
  if hasattr(model_class.PydanticMeta, "update_include"):
38
- get_one_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.update_include)
39
- all_fields_info.update(get_one_include_fields_dict)
40
- include_fields.update(get_one_include_fields_dict.keys())
42
+ update_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.update_include)
43
+ all_fields_info.update(update_include_fields_dict)
44
+ include_fields.update(update_include_fields_dict.keys())
45
+ if hasattr(model_class.PydanticMeta, "save_exclude"):
46
+ exclude_fields.update(model_class.PydanticMeta.update_exclude)
41
47
  if hasattr(model_class.PydanticMeta, "update_exclude"):
42
48
  exclude_fields.update(model_class.PydanticMeta.update_exclude)
43
49
  else:
@@ -29,12 +29,12 @@ class AppSettings(BaseSettings):
29
29
  ROUTER_WHETHER_UNDERLINE_TO_STRIKE: Optional[bool] = Field(default=False, description="路由是否下划线转中划线")
30
30
  ROUTER_WHETHER_ADD_SUFFIX: Optional[bool] = Field(default=True, description="增删改查路由是否添加后缀")
31
31
  ROUTER_CREATE_SUFFIX_FIELD: Optional[str] = Field(default='create', description="创建路由后缀字段")
32
- ROUTER_GET_ONE_SUFFIX_FIELD: Optional[str] = Field(default='get-one', description="获取一个路由后缀字段")
33
- ROUTER_GET_ALL_SUFFIX_FIELD: Optional[str] = Field(default='get-all', description="获取列表路由后缀字段")
34
- ROUTER_GET_TREE_SUFFIX_FIELD: Optional[str] = Field(default='get-tree', description="获取树状数据路由后缀字段")
32
+ ROUTER_GET_ONE_SUFFIX_FIELD: Optional[str] = Field(default='get_one', description="获取一个路由后缀字段")
33
+ ROUTER_GET_ALL_SUFFIX_FIELD: Optional[str] = Field(default='get_all', description="获取列表路由后缀字段")
34
+ ROUTER_GET_TREE_SUFFIX_FIELD: Optional[str] = Field(default='get_tree', description="获取树状数据路由后缀字段")
35
35
  ROUTER_UPDATE_SUFFIX_FIELD: Optional[str] = Field(default='update', description="修改路由后缀字段")
36
36
  ROUTER_DELETE_SUFFIX_FIELD: Optional[str] = Field(default='delete', description="删除路由后缀字段")
37
- ROUTER_RECURSION_DELETE_SUFFIX_FIELD: Optional[str] = Field(default='delete-tree', description="递归删除路由后缀字段")
37
+ ROUTER_RECURSION_DELETE_SUFFIX_FIELD: Optional[str] = Field(default='delete_tree', description="递归删除路由后缀字段")
38
38
 
39
39
  # 函数转换路由时,默认添加字段,(遵循restful规范时,get路由处理方案)
40
40
  RESTFUL_GET_ROUTER_ADD_PREFIX: Optional[str] = Field(default='', description="函数转换路由时:前缀添加字段")
@@ -46,9 +46,14 @@ class AppSettings(BaseSettings):
46
46
  RESTFUL_DELETE_ROUTER_ADD_PREFIX: Optional[str] = Field(default='', description="函数转换路由时:前缀添加字段")
47
47
  RESTFUL_DELETE_ROUTER_ADD_SUFFIX: Optional[str] = Field(default='', description="函数转换路由时:后缀pk前添加字段")
48
48
 
49
+ # 分布式id
50
+ WORKER_ID: Optional[int] = Field(default=1, description="数据中心(机器区域)ID")
51
+ DATACENTER_ID: Optional[int] = Field(default=1, description="机器ID")
52
+
49
53
  # 数据库字段默认值
50
- WHETHER_DELETE_FIELD: Optional[str] = Field(default="is_active", description="是否删除字段")
51
- ACTIVE_DEFAULT_VALUE: Optional[bool] = Field(default=True, description="有效的默认值")
54
+ WHETHER_DELETE_FIELD: Optional[str] = Field(default="deleted_at", description="是否删除字段;推荐命名 >> deleted_at;is_active")
55
+ DELETE_FIELD_TYPE: Optional[str] = Field(default="time", description="删除字段类型;推荐命名 >> time;bool")
56
+ # ACTIVE_DEFAULT_VALUE: Optional[bool] = Field(default=True, description="有效的默认值")
52
57
  GET_EXCLUDE_ACTIVE_VALUE: Optional[bool] = Field(default=True, description="查询结果是否排除有效字段")
53
58
  CREATE_EXCLUDE_ACTIVE_VALUE: Optional[bool] = Field(default=True, description="创建是否排除有效字段")
54
59
  UPDATE_EXCLUDE_ACTIVE_VALUE: Optional[bool] = Field(default=True, description="修改是否排除有效字段")
@@ -70,7 +75,7 @@ class AppSettings(BaseSettings):
70
75
  SCHEMAS_UNDERLINE_WHETHER_DOUBLE_TO_SINGLE: Optional[bool] = Field(default=True, description="序列化字段是否双下划线转单下划线")
71
76
 
72
77
  class Config:
73
- # env_prefix = 'APP_'
78
+ env_prefix = 'APP_'
74
79
  env_file = "./.env"
75
80
  case_sensitive = True
76
81
 
@@ -0,0 +1,93 @@
1
+ from typing import Union
2
+
3
+ from Crypto import Random
4
+ from Crypto.Cipher import AES
5
+ import base64
6
+
7
+
8
+ class AESCipher(object):
9
+ """
10
+ 可用于二进制(文件)和字符串加密
11
+ """
12
+ def __init__(self, salt):
13
+ '''
14
+ CBC加密需要一个十六位的key(密钥)和一个十六位iv(偏移量)
15
+ '''
16
+ self.salt = self.check_key(salt)
17
+ # 这里使用随机偏移量
18
+ # self.iv = self.check_key(iv) if iv else self.salt
19
+ # 数据块的大小 16位
20
+ self.BS = AES.block_size
21
+ # CBC模式 相对安全 因为有偏移向量 iv 也是16位字节的
22
+ self.mode = AES.MODE_CBC
23
+ # 填充函数 因为AES加密是一段一段加密的 每段都是BS位字节,不够的话是需要自己填充的
24
+ self.pad = lambda s: s + (self.BS - len(s) % self.BS) * chr(self.BS - len(s) % self.BS).encode('utf-8')
25
+ # 将填充的数据剔除
26
+ self.unpad = lambda s: s[:-ord(s[len(s) - 1:])]
27
+
28
+ def encrypt(self, text: Union[bytes, str]) -> bytes:
29
+ text = self.check_data(text)
30
+ text = self.pad(text)
31
+ # 随机获取iv
32
+ iv = Random.new().read(AES.block_size)
33
+ # 初始化自定义
34
+ cipher = AES.new(self.salt, self.mode, iv)
35
+ # 此处是将密文和iv一起 base64 解密的时候就可以根据这个iv来解密
36
+ return base64.b64encode(iv + cipher.encrypt(text))
37
+ # return base64.b64encode(iv + cipher.encrypt(text)).decode("utf-8")
38
+
39
+ def decrypt(self, text: Union[bytes, str, int]) -> bytes:
40
+ text = self.check_data(text)
41
+ # 先将密文进行base64解码
42
+ text = base64.b64decode(text)
43
+ # 取出iv值
44
+ iv = text[:self.BS]
45
+ # 初始化自定义
46
+ cipher = AES.new(self.salt, self.mode, iv)
47
+ return self.unpad(cipher.decrypt(text[self.BS:]))
48
+ # return self.unpad(cipher.decrypt(text[self.BS:])).decode("utf-8")
49
+
50
+ def check_key(self, key: Union[bytes, str]) -> bytes:
51
+ '''
52
+ 检测key的长度是否为16,24或者32bytes的长度
53
+ '''
54
+ if isinstance(key, bytes):
55
+ assert len(key) in [16, 24, 32]
56
+ return key
57
+ elif isinstance(key, str):
58
+ assert len(key.encode("utf-8")) in [16, 24, 32]
59
+ return key.encode("utf-8")
60
+ else:
61
+ raise Exception(f'密钥必须为str或bytes,不能为{type(key)}')
62
+
63
+ def check_data(self, data: Union[bytes, str, int]) -> bytes:
64
+ '''
65
+ 检测加密的数据类型
66
+ '''
67
+ if isinstance(data, int):
68
+ data = str(data).encode('utf-8')
69
+ elif isinstance(data, str):
70
+ data = data.encode('utf-8')
71
+ elif isinstance(data, bytes):
72
+ pass
73
+ else:
74
+ raise Exception(f'加密的数据必须为str或bytes,不能为{type(data)}')
75
+ return data
76
+
77
+
78
+ # if __name__ == '__main__':
79
+ # # 加密字符串
80
+ # import json
81
+ # # aes_encrypt = AESCipher('ningbozhihuirend') # 自己设定的密钥
82
+ # aes_encrypt = AESCipher('ningbozhihuirend') # 自己设定的密钥
83
+ # data = json.dumps({"mobile": "1312345646", "content": "xczczczc"})
84
+ # print(type(data))
85
+ # print(data)
86
+ # e = aes_encrypt.encrypt(data).decode("utf-8") # 加密内容
87
+ # print(type(e))
88
+ # print(e)
89
+ # d = aes_encrypt.decrypt(e).decode("utf-8")
90
+ # print(type(d))
91
+ # print(json.loads(d))
92
+ # print(len(e))
93
+ # print("加密后%s,解密后%s" % (e, d))