super-dev 2.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.
- super_dev/__init__.py +11 -0
- super_dev/analyzer/__init__.py +34 -0
- super_dev/analyzer/analyzer.py +440 -0
- super_dev/analyzer/detectors.py +511 -0
- super_dev/analyzer/models.py +285 -0
- super_dev/cli.py +3257 -0
- super_dev/config/__init__.py +11 -0
- super_dev/config/frontend.py +557 -0
- super_dev/config/manager.py +281 -0
- super_dev/creators/__init__.py +26 -0
- super_dev/creators/creator.py +134 -0
- super_dev/creators/document_generator.py +2473 -0
- super_dev/creators/frontend_builder.py +371 -0
- super_dev/creators/implementation_builder.py +789 -0
- super_dev/creators/prompt_generator.py +289 -0
- super_dev/creators/requirement_parser.py +354 -0
- super_dev/creators/spec_builder.py +195 -0
- super_dev/deployers/__init__.py +20 -0
- super_dev/deployers/cicd.py +1269 -0
- super_dev/deployers/delivery.py +229 -0
- super_dev/deployers/migration.py +1032 -0
- super_dev/design/__init__.py +74 -0
- super_dev/design/aesthetics.py +530 -0
- super_dev/design/charts.py +396 -0
- super_dev/design/codegen.py +379 -0
- super_dev/design/engine.py +528 -0
- super_dev/design/generator.py +395 -0
- super_dev/design/landing.py +422 -0
- super_dev/design/tech_stack.py +524 -0
- super_dev/design/tokens.py +269 -0
- super_dev/design/ux_guide.py +391 -0
- super_dev/exceptions.py +119 -0
- super_dev/experts/__init__.py +19 -0
- super_dev/experts/service.py +161 -0
- super_dev/integrations/__init__.py +7 -0
- super_dev/integrations/manager.py +264 -0
- super_dev/orchestrator/__init__.py +12 -0
- super_dev/orchestrator/engine.py +958 -0
- super_dev/orchestrator/experts.py +423 -0
- super_dev/orchestrator/knowledge.py +352 -0
- super_dev/orchestrator/quality.py +356 -0
- super_dev/reviewers/__init__.py +17 -0
- super_dev/reviewers/code_review.py +471 -0
- super_dev/reviewers/quality_gate.py +964 -0
- super_dev/reviewers/redteam.py +881 -0
- super_dev/skills/__init__.py +7 -0
- super_dev/skills/manager.py +307 -0
- super_dev/specs/__init__.py +44 -0
- super_dev/specs/generator.py +264 -0
- super_dev/specs/manager.py +428 -0
- super_dev/specs/models.py +348 -0
- super_dev/specs/validator.py +415 -0
- super_dev/utils/__init__.py +11 -0
- super_dev/utils/logger.py +133 -0
- super_dev/web/api.py +1402 -0
- super_dev-2.0.0.dist-info/METADATA +252 -0
- super_dev-2.0.0.dist-info/RECORD +61 -0
- super_dev-2.0.0.dist-info/WHEEL +5 -0
- super_dev-2.0.0.dist-info/entry_points.txt +2 -0
- super_dev-2.0.0.dist-info/licenses/LICENSE +21 -0
- super_dev-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1032 @@
|
|
|
1
|
+
"""
|
|
2
|
+
数据库迁移生成器 - 生成数据库迁移脚本
|
|
3
|
+
|
|
4
|
+
开发:Excellent(11964948@qq.com)
|
|
5
|
+
功能:基于 Spec 规范生成数据库迁移脚本
|
|
6
|
+
作用:自动化数据库版本管理
|
|
7
|
+
创建时间:2025-12-30
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import datetime
|
|
11
|
+
import re
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DatabaseType(Enum):
|
|
18
|
+
"""数据库类型"""
|
|
19
|
+
POSTGRESQL = "postgresql"
|
|
20
|
+
MYSQL = "mysql"
|
|
21
|
+
MONGODB = "mongodb"
|
|
22
|
+
SQLITE = "sqlite"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ORMType(Enum):
|
|
26
|
+
"""ORM 框架类型"""
|
|
27
|
+
PRISMA = "prisma"
|
|
28
|
+
TYPEORM = "typeorm"
|
|
29
|
+
SEQUELIZE = "sequelize"
|
|
30
|
+
SQLALCHEMY = "sqlalchemy"
|
|
31
|
+
DJANGO = "django"
|
|
32
|
+
MONGOOSE = "mongoose"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class Column:
|
|
37
|
+
"""数据库列定义"""
|
|
38
|
+
name: str
|
|
39
|
+
type: str
|
|
40
|
+
nullable: bool = True
|
|
41
|
+
primary_key: bool = False
|
|
42
|
+
unique: bool = False
|
|
43
|
+
default: str | None = None
|
|
44
|
+
foreign_key: str | None = None
|
|
45
|
+
comment: str | None = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class Table:
|
|
50
|
+
"""数据库表定义"""
|
|
51
|
+
name: str
|
|
52
|
+
columns: list[Column]
|
|
53
|
+
indexes: list[str] | None = None
|
|
54
|
+
comment: str | None = None
|
|
55
|
+
|
|
56
|
+
def __post_init__(self):
|
|
57
|
+
if self.indexes is None:
|
|
58
|
+
self.indexes = []
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class MigrationGenerator:
|
|
62
|
+
"""数据库迁移生成器"""
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
project_dir: Path,
|
|
67
|
+
name: str,
|
|
68
|
+
tech_stack: dict,
|
|
69
|
+
db_type: DatabaseType = DatabaseType.POSTGRESQL,
|
|
70
|
+
orm_type: ORMType | None = None
|
|
71
|
+
):
|
|
72
|
+
self.project_dir = Path(project_dir).resolve()
|
|
73
|
+
self.name = name
|
|
74
|
+
self.tech_stack = tech_stack
|
|
75
|
+
self.backend = tech_stack.get("backend", "node")
|
|
76
|
+
|
|
77
|
+
# 自动推断 ORM 类型
|
|
78
|
+
if orm_type is None:
|
|
79
|
+
orm_type = self._infer_orm_type()
|
|
80
|
+
self.orm_type = orm_type
|
|
81
|
+
self.db_type = db_type
|
|
82
|
+
|
|
83
|
+
# 读取 Spec 规范
|
|
84
|
+
self.entities = self._load_entities()
|
|
85
|
+
|
|
86
|
+
def generate(self) -> dict[str, str]:
|
|
87
|
+
"""生成迁移脚本"""
|
|
88
|
+
if self.orm_type == ORMType.PRISMA:
|
|
89
|
+
return self._generate_prisma_migration()
|
|
90
|
+
elif self.orm_type == ORMType.TYPEORM:
|
|
91
|
+
return self._generate_typeorm_migration()
|
|
92
|
+
elif self.orm_type == ORMType.SQLALCHEMY:
|
|
93
|
+
return self._generate_sqlalchemy_migration()
|
|
94
|
+
elif self.orm_type == ORMType.DJANGO:
|
|
95
|
+
return self._generate_django_migration()
|
|
96
|
+
elif self.orm_type == ORMType.SEQUELIZE:
|
|
97
|
+
return self._generate_sequelize_migration()
|
|
98
|
+
elif self.orm_type == ORMType.MONGOOSE:
|
|
99
|
+
return self._generate_mongoose_migration()
|
|
100
|
+
else:
|
|
101
|
+
return self._generate_sql_migration()
|
|
102
|
+
|
|
103
|
+
def _infer_orm_type(self) -> ORMType:
|
|
104
|
+
"""推断 ORM 类型"""
|
|
105
|
+
backend = self.backend.lower()
|
|
106
|
+
|
|
107
|
+
if backend == "node":
|
|
108
|
+
# 检查 package.json
|
|
109
|
+
package_json = self.project_dir / "package.json"
|
|
110
|
+
if package_json.exists():
|
|
111
|
+
content = package_json.read_text(encoding='utf-8')
|
|
112
|
+
if "prisma" in content:
|
|
113
|
+
return ORMType.PRISMA
|
|
114
|
+
elif "typeorm" in content:
|
|
115
|
+
return ORMType.TYPEORM
|
|
116
|
+
elif "sequelize" in content:
|
|
117
|
+
return ORMType.SEQUELIZE
|
|
118
|
+
# 默认使用 Prisma
|
|
119
|
+
return ORMType.PRISMA
|
|
120
|
+
|
|
121
|
+
elif backend == "python":
|
|
122
|
+
# 检查 requirements.txt 或 pyproject.toml
|
|
123
|
+
for file_name in ["requirements.txt", "pyproject.toml"]:
|
|
124
|
+
req_file = self.project_dir / file_name
|
|
125
|
+
if req_file.exists():
|
|
126
|
+
content = req_file.read_text(encoding='utf-8')
|
|
127
|
+
if "sqlalchemy" in content:
|
|
128
|
+
return ORMType.SQLALCHEMY
|
|
129
|
+
elif "django" in content:
|
|
130
|
+
return ORMType.DJANGO
|
|
131
|
+
# 默认使用 SQLAlchemy
|
|
132
|
+
return ORMType.SQLALCHEMY
|
|
133
|
+
|
|
134
|
+
# 默认返回 SQL
|
|
135
|
+
return ORMType.PRISMA
|
|
136
|
+
|
|
137
|
+
def _load_entities(self) -> list[Table]:
|
|
138
|
+
"""从 Spec 规范加载实体定义"""
|
|
139
|
+
entities = []
|
|
140
|
+
|
|
141
|
+
# 1) 尝试从当前规范读取
|
|
142
|
+
spec_dir = self.project_dir / ".super-dev" / "specs"
|
|
143
|
+
if spec_dir.exists():
|
|
144
|
+
for spec_file in spec_dir.rglob("spec.md"):
|
|
145
|
+
content = spec_file.read_text(encoding='utf-8')
|
|
146
|
+
entities.extend(self._parse_entities_from_spec(content))
|
|
147
|
+
|
|
148
|
+
# 2) 尝试从变更提案规范读取(pipeline 常见路径)
|
|
149
|
+
changes_dir = self.project_dir / ".super-dev" / "changes"
|
|
150
|
+
change_spec_files: list[Path] = []
|
|
151
|
+
if changes_dir.exists():
|
|
152
|
+
change_spec_files = list(changes_dir.glob("*/specs/*/spec.md"))
|
|
153
|
+
for spec_file in change_spec_files:
|
|
154
|
+
content = spec_file.read_text(encoding='utf-8')
|
|
155
|
+
entities.extend(self._parse_entities_from_spec(content))
|
|
156
|
+
|
|
157
|
+
# 3) 没有显式实体定义时,按模块线索推断(优先于默认模板)
|
|
158
|
+
if not entities:
|
|
159
|
+
module_hints = self._collect_module_hints(change_spec_files)
|
|
160
|
+
if module_hints:
|
|
161
|
+
entities = self._generate_entities_from_modules(module_hints)
|
|
162
|
+
|
|
163
|
+
# 4) 最后兜底默认实体
|
|
164
|
+
if not entities:
|
|
165
|
+
entities = self._generate_default_entities()
|
|
166
|
+
|
|
167
|
+
return entities
|
|
168
|
+
|
|
169
|
+
def _collect_module_hints(self, change_spec_files: list[Path]) -> list[str]:
|
|
170
|
+
hints: list[str] = []
|
|
171
|
+
|
|
172
|
+
# 变更规范目录名(.super-dev/changes/<id>/specs/<module>/spec.md)
|
|
173
|
+
for spec_file in change_spec_files:
|
|
174
|
+
module = spec_file.parent.name.strip().lower()
|
|
175
|
+
if module and module not in hints and module not in {"core", "workflow"}:
|
|
176
|
+
hints.append(module)
|
|
177
|
+
|
|
178
|
+
# 当前规范目录名(.super-dev/specs/<module>/spec.md)
|
|
179
|
+
specs_dir = self.project_dir / ".super-dev" / "specs"
|
|
180
|
+
if specs_dir.exists():
|
|
181
|
+
for module_dir in specs_dir.iterdir():
|
|
182
|
+
if not module_dir.is_dir():
|
|
183
|
+
continue
|
|
184
|
+
module = module_dir.name.strip().lower()
|
|
185
|
+
if module and module not in hints and not module.startswith("."):
|
|
186
|
+
hints.append(module)
|
|
187
|
+
|
|
188
|
+
# API 契约中的模块路径
|
|
189
|
+
api_contract = self.project_dir / "backend" / "API_CONTRACT.md"
|
|
190
|
+
if api_contract.exists():
|
|
191
|
+
content = api_contract.read_text(encoding="utf-8", errors="ignore")
|
|
192
|
+
for match in re.findall(r"/api/([a-zA-Z0-9_-]+)", content):
|
|
193
|
+
module = match.strip().lower()
|
|
194
|
+
if module and module not in hints and module not in {"health", "ready"}:
|
|
195
|
+
hints.append(module)
|
|
196
|
+
|
|
197
|
+
return hints[:12]
|
|
198
|
+
|
|
199
|
+
def _generate_entities_from_modules(self, modules: list[str]) -> list[Table]:
|
|
200
|
+
entities: list[Table] = []
|
|
201
|
+
seen_tables: set[str] = set()
|
|
202
|
+
|
|
203
|
+
for module in modules:
|
|
204
|
+
table_name = self._to_table_name(module)
|
|
205
|
+
if table_name in seen_tables:
|
|
206
|
+
continue
|
|
207
|
+
seen_tables.add(table_name)
|
|
208
|
+
entities.append(
|
|
209
|
+
Table(
|
|
210
|
+
name=table_name,
|
|
211
|
+
columns=self._default_columns_for_module(module),
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
return entities
|
|
216
|
+
|
|
217
|
+
def _to_table_name(self, module: str) -> str:
|
|
218
|
+
cleaned = re.sub(r"[^a-z0-9_]", "_", module.lower()).strip("_")
|
|
219
|
+
if not cleaned:
|
|
220
|
+
return "records"
|
|
221
|
+
if cleaned.endswith("s"):
|
|
222
|
+
return cleaned
|
|
223
|
+
return f"{cleaned}s"
|
|
224
|
+
|
|
225
|
+
def _default_columns_for_module(self, module: str) -> list[Column]:
|
|
226
|
+
module = module.lower()
|
|
227
|
+
base_columns = [
|
|
228
|
+
Column("id", "uuid", primary_key=True, comment="主键"),
|
|
229
|
+
Column("created_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
230
|
+
Column("updated_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
if module in {"auth", "user", "users", "account", "accounts"}:
|
|
234
|
+
return [
|
|
235
|
+
Column("id", "uuid", primary_key=True, comment="用户 ID"),
|
|
236
|
+
Column("email", "varchar(255)", unique=True, comment="邮箱"),
|
|
237
|
+
Column("password_hash", "varchar(255)", comment="密码哈希"),
|
|
238
|
+
Column("status", "varchar(32)", default="'active'", comment="状态"),
|
|
239
|
+
Column("created_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
240
|
+
Column("updated_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
241
|
+
]
|
|
242
|
+
if module in {"payment", "payments", "billing"}:
|
|
243
|
+
return [
|
|
244
|
+
Column("id", "uuid", primary_key=True, comment="支付 ID"),
|
|
245
|
+
Column("order_id", "uuid", comment="订单 ID"),
|
|
246
|
+
Column("amount", "decimal(12,2)", comment="支付金额"),
|
|
247
|
+
Column("currency", "varchar(10)", default="'CNY'"),
|
|
248
|
+
Column("status", "varchar(32)", default="'pending'"),
|
|
249
|
+
Column("method", "varchar(32)", comment="支付方式"),
|
|
250
|
+
Column("created_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
251
|
+
Column("updated_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
252
|
+
]
|
|
253
|
+
if module in {"order", "orders"}:
|
|
254
|
+
return [
|
|
255
|
+
Column("id", "uuid", primary_key=True, comment="订单 ID"),
|
|
256
|
+
Column("user_id", "uuid", comment="用户 ID"),
|
|
257
|
+
Column("total_amount", "decimal(12,2)", comment="订单金额"),
|
|
258
|
+
Column("status", "varchar(32)", default="'draft'"),
|
|
259
|
+
Column("created_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
260
|
+
Column("updated_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
261
|
+
]
|
|
262
|
+
if module in {"product", "products", "catalog"}:
|
|
263
|
+
return [
|
|
264
|
+
Column("id", "uuid", primary_key=True, comment="商品 ID"),
|
|
265
|
+
Column("name", "varchar(255)", comment="商品名称"),
|
|
266
|
+
Column("price", "decimal(12,2)", comment="价格"),
|
|
267
|
+
Column("stock", "int", default="0", comment="库存"),
|
|
268
|
+
Column("created_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
269
|
+
Column("updated_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
270
|
+
]
|
|
271
|
+
if module in {"booking", "appointment", "appointments", "schedule"}:
|
|
272
|
+
return [
|
|
273
|
+
Column("id", "uuid", primary_key=True, comment="预约 ID"),
|
|
274
|
+
Column("user_id", "uuid", comment="用户 ID"),
|
|
275
|
+
Column("scheduled_at", "timestamp", comment="预约时间"),
|
|
276
|
+
Column("status", "varchar(32)", default="'pending'"),
|
|
277
|
+
Column("notes", "text", comment="备注"),
|
|
278
|
+
Column("created_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
279
|
+
Column("updated_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
280
|
+
]
|
|
281
|
+
|
|
282
|
+
# 通用模块:保持最小可用结构
|
|
283
|
+
return [
|
|
284
|
+
Column("id", "uuid", primary_key=True, comment="主键"),
|
|
285
|
+
Column("name", "varchar(255)", comment="名称"),
|
|
286
|
+
Column("status", "varchar(32)", default="'active'"),
|
|
287
|
+
*base_columns[1:],
|
|
288
|
+
]
|
|
289
|
+
|
|
290
|
+
def _parse_entities_from_spec(self, content: str) -> list[Table]:
|
|
291
|
+
"""从 Spec 内容解析实体定义"""
|
|
292
|
+
entities = []
|
|
293
|
+
|
|
294
|
+
# 简单解析:查找 ## Entity 或 ### Entity 标题
|
|
295
|
+
lines = content.split("\n")
|
|
296
|
+
current_entity: str | None = None
|
|
297
|
+
current_columns: list[Column] = []
|
|
298
|
+
|
|
299
|
+
for line in lines:
|
|
300
|
+
if line.startswith("### ") and "Table" in line:
|
|
301
|
+
# 新实体
|
|
302
|
+
if current_entity and current_columns:
|
|
303
|
+
entities.append(Table(current_entity, current_columns))
|
|
304
|
+
|
|
305
|
+
# 提取表名
|
|
306
|
+
table_name = line.replace("###", "").strip().split()[0].lower()
|
|
307
|
+
current_entity = table_name
|
|
308
|
+
current_columns = []
|
|
309
|
+
|
|
310
|
+
elif current_entity and line.strip().startswith("-"):
|
|
311
|
+
# 解析列定义
|
|
312
|
+
col_def = line.strip()[1:].strip()
|
|
313
|
+
current_columns.append(self._parse_column(col_def))
|
|
314
|
+
|
|
315
|
+
# 添加最后一个实体
|
|
316
|
+
if current_entity and current_columns:
|
|
317
|
+
entities.append(Table(current_entity, current_columns))
|
|
318
|
+
|
|
319
|
+
return entities
|
|
320
|
+
|
|
321
|
+
def _parse_column(self, col_def: str) -> Column:
|
|
322
|
+
"""解析列定义"""
|
|
323
|
+
# 简单解析,实际应该更复杂
|
|
324
|
+
parts = col_def.split(":")
|
|
325
|
+
if len(parts) >= 2:
|
|
326
|
+
name = parts[0].strip()
|
|
327
|
+
col_type = parts[1].strip()
|
|
328
|
+
else:
|
|
329
|
+
name = col_def
|
|
330
|
+
col_type = "string"
|
|
331
|
+
|
|
332
|
+
return Column(
|
|
333
|
+
name=name,
|
|
334
|
+
type=col_type,
|
|
335
|
+
nullable="primary" not in col_def.lower(),
|
|
336
|
+
primary_key="primary" in col_def.lower()
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
def _generate_default_entities(self) -> list[Table]:
|
|
340
|
+
"""生成默认实体"""
|
|
341
|
+
return [
|
|
342
|
+
Table(
|
|
343
|
+
name="users",
|
|
344
|
+
columns=[
|
|
345
|
+
Column("id", "uuid", primary_key=True, comment="用户 ID"),
|
|
346
|
+
Column("email", "varchar(255)", unique=True, comment="邮箱"),
|
|
347
|
+
Column("password_hash", "varchar(255)", comment="密码哈希"),
|
|
348
|
+
Column("name", "varchar(100)", comment="用户名"),
|
|
349
|
+
Column("created_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
350
|
+
Column("updated_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
351
|
+
],
|
|
352
|
+
indexes=["idx_users_email", "idx_users_created_at"]
|
|
353
|
+
),
|
|
354
|
+
Table(
|
|
355
|
+
name="auth_tokens",
|
|
356
|
+
columns=[
|
|
357
|
+
Column("id", "uuid", primary_key=True),
|
|
358
|
+
Column("user_id", "uuid", foreign_key="users.id", comment="用户 ID"),
|
|
359
|
+
Column("token", "varchar(500)", unique=True, comment="Token"),
|
|
360
|
+
Column("expires_at", "timestamp", comment="过期时间"),
|
|
361
|
+
Column("created_at", "timestamp", default="CURRENT_TIMESTAMP"),
|
|
362
|
+
],
|
|
363
|
+
indexes=["idx_auth_tokens_user_id", "idx_auth_tokens_token"]
|
|
364
|
+
),
|
|
365
|
+
]
|
|
366
|
+
|
|
367
|
+
def _generate_prisma_migration(self) -> dict[str, str]:
|
|
368
|
+
"""生成 Prisma 迁移"""
|
|
369
|
+
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
370
|
+
|
|
371
|
+
# schema.prisma
|
|
372
|
+
schema_lines = [
|
|
373
|
+
"// This is your Prisma schema file,",
|
|
374
|
+
"// learn more about it in the docs: https://pris.ly/d/prisma-schema",
|
|
375
|
+
"",
|
|
376
|
+
'generator client {',
|
|
377
|
+
' provider = "prisma-client-js"',
|
|
378
|
+
'}',
|
|
379
|
+
"",
|
|
380
|
+
'datasource db {',
|
|
381
|
+
f' provider = "{self._get_prisma_provider()}"',
|
|
382
|
+
' url = env("DATABASE_URL")',
|
|
383
|
+
'}',
|
|
384
|
+
"",
|
|
385
|
+
]
|
|
386
|
+
|
|
387
|
+
# 添加模型
|
|
388
|
+
for entity in self.entities:
|
|
389
|
+
schema_lines.append(f'model {entity.name.capitalize()} {{')
|
|
390
|
+
for col in entity.columns:
|
|
391
|
+
schema_lines.append(f' {col.name} {self._prisma_type(col.type)}'
|
|
392
|
+
f'{"" if col.nullable else "?"}')
|
|
393
|
+
schema_lines.append("}")
|
|
394
|
+
schema_lines.append("")
|
|
395
|
+
|
|
396
|
+
# migration.sql
|
|
397
|
+
migration_lines = [
|
|
398
|
+
"-- Migration SQL",
|
|
399
|
+
f"-- Created: {datetime.datetime.now().isoformat()}",
|
|
400
|
+
"",
|
|
401
|
+
]
|
|
402
|
+
|
|
403
|
+
for entity in self.entities:
|
|
404
|
+
migration_lines.append(f"-- Create {entity.name} table")
|
|
405
|
+
migration_lines.append(f"CREATE TABLE \"{entity.name}\" (")
|
|
406
|
+
col_defs = []
|
|
407
|
+
for col in entity.columns:
|
|
408
|
+
col_defs.append(f' "{col.name}" {self._sql_type(col.type)}'
|
|
409
|
+
f'{"" if col.nullable else " NOT NULL"}'
|
|
410
|
+
f'{" PRIMARY KEY" if col.primary_key else ""}'
|
|
411
|
+
f'{" UNIQUE" if col.unique else ""}')
|
|
412
|
+
migration_lines.append(",\n".join(col_defs))
|
|
413
|
+
migration_lines.append(");")
|
|
414
|
+
migration_lines.append("")
|
|
415
|
+
|
|
416
|
+
return {
|
|
417
|
+
"prisma/schema.prisma": "\n".join(schema_lines),
|
|
418
|
+
f"prisma/migrations/{timestamp}_init/migration.sql": "\n".join(migration_lines),
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
def _generate_typeorm_migration(self) -> dict[str, str]:
|
|
422
|
+
"""生成 TypeORM 迁移"""
|
|
423
|
+
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
424
|
+
|
|
425
|
+
entity_lines = []
|
|
426
|
+
for entity in self.entities:
|
|
427
|
+
entity_lines.extend([
|
|
428
|
+
"import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';",
|
|
429
|
+
"",
|
|
430
|
+
"@Entity()",
|
|
431
|
+
f"export class {entity.name.capitalize()} {{",
|
|
432
|
+
])
|
|
433
|
+
|
|
434
|
+
for col in entity.columns:
|
|
435
|
+
decorator = ""
|
|
436
|
+
if col.primary_key:
|
|
437
|
+
decorator = "@PrimaryGeneratedColumn('uuid')"
|
|
438
|
+
else:
|
|
439
|
+
col_type = self._typeorm_type(col.type)
|
|
440
|
+
decorator = f"@Column({{ type: '{col_type}'{{ nullable: {str(col.nullable).lower()} }} }})"
|
|
441
|
+
|
|
442
|
+
entity_lines.append(f" {decorator}")
|
|
443
|
+
entity_lines.append(f" {col.name}: {self._typescript_type(col.type)};")
|
|
444
|
+
|
|
445
|
+
entity_lines.append("}")
|
|
446
|
+
entity_lines.append("")
|
|
447
|
+
|
|
448
|
+
# 迁移文件
|
|
449
|
+
migration_lines = [
|
|
450
|
+
"import { MigrationInterface, QueryRunner, Table } from 'typeorm';",
|
|
451
|
+
"",
|
|
452
|
+
f"export class Init{timestamp} {{",
|
|
453
|
+
f" name = 'Init{timestamp}';",
|
|
454
|
+
"",
|
|
455
|
+
" public async up(queryRunner: QueryRunner): Promise<void> {",
|
|
456
|
+
"",
|
|
457
|
+
]
|
|
458
|
+
|
|
459
|
+
for entity in self.entities:
|
|
460
|
+
migration_lines.append(f" // Create {entity.name} table")
|
|
461
|
+
migration_lines.append(" await queryRunner.createTable(new Table({")
|
|
462
|
+
migration_lines.append(f" name: '{entity.name}',")
|
|
463
|
+
migration_lines.append(" columns: [")
|
|
464
|
+
|
|
465
|
+
for col in entity.columns:
|
|
466
|
+
migration_lines.append(" {")
|
|
467
|
+
migration_lines.append(f" name: '{col.name}',")
|
|
468
|
+
migration_lines.append(f" type: '{self._sql_type(col.type)}',")
|
|
469
|
+
migration_lines.append(f" isPrimary: {str(col.primary_key).lower()},")
|
|
470
|
+
migration_lines.append(f" isNullable: {str(col.nullable).lower()},")
|
|
471
|
+
migration_lines.append(f" isUnique: {str(col.unique).lower()},")
|
|
472
|
+
migration_lines.append(" },")
|
|
473
|
+
|
|
474
|
+
migration_lines.append(" ],")
|
|
475
|
+
migration_lines.append(" }));")
|
|
476
|
+
migration_lines.append("")
|
|
477
|
+
|
|
478
|
+
migration_lines.extend([
|
|
479
|
+
" }",
|
|
480
|
+
"",
|
|
481
|
+
" public async down(queryRunner: QueryRunner): Promise<void> {",
|
|
482
|
+
"",
|
|
483
|
+
])
|
|
484
|
+
|
|
485
|
+
for entity in self.entities:
|
|
486
|
+
migration_lines.append(f" await queryRunner.dropTable('{entity.name}');")
|
|
487
|
+
|
|
488
|
+
migration_lines.extend([
|
|
489
|
+
"",
|
|
490
|
+
" }",
|
|
491
|
+
"}",
|
|
492
|
+
])
|
|
493
|
+
|
|
494
|
+
return {
|
|
495
|
+
f"src/migrations/{timestamp}_Init.ts": "\n".join(entity_lines + ["", ""] + migration_lines),
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
def _generate_sqlalchemy_migration(self) -> dict[str, str]:
|
|
499
|
+
"""生成 SQLAlchemy 迁移"""
|
|
500
|
+
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
501
|
+
|
|
502
|
+
# models.py
|
|
503
|
+
models_lines = [
|
|
504
|
+
'from sqlalchemy import Column, String, DateTime, Boolean, Integer, ForeignKey, create_engine',
|
|
505
|
+
'from sqlalchemy.ext.declarative import declarative_base',
|
|
506
|
+
'from sqlalchemy.orm import sessionmaker, relationship',
|
|
507
|
+
'import uuid',
|
|
508
|
+
"",
|
|
509
|
+
"Base = declarative_base()",
|
|
510
|
+
"",
|
|
511
|
+
]
|
|
512
|
+
|
|
513
|
+
for entity in self.entities:
|
|
514
|
+
models_lines.append(f'class {entity.name.capitalize()}(Base):')
|
|
515
|
+
models_lines.append(f' __tablename__ = "{entity.name}"')
|
|
516
|
+
models_lines.append("")
|
|
517
|
+
|
|
518
|
+
for col in entity.columns:
|
|
519
|
+
py_type = self._python_sqlalchemy_type(col.type)
|
|
520
|
+
nullable = f"nullable={col.nullable}"
|
|
521
|
+
primary_key = f"primary_key={col.primary_key}"
|
|
522
|
+
unique = f"unique={col.unique}" if col.unique else ""
|
|
523
|
+
default_arg = f"default={col.default}" if col.default else ""
|
|
524
|
+
|
|
525
|
+
models_lines.append(f' {col.name} = Column({py_type}, {nullable}, {primary_key}, {unique}, {default_arg})'.replace(", True, ", ", ").replace(", False, ", ", ").replace(", )", ")"))
|
|
526
|
+
|
|
527
|
+
models_lines.append("")
|
|
528
|
+
models_lines.append("")
|
|
529
|
+
|
|
530
|
+
# alembic 迁移
|
|
531
|
+
migration_lines = [
|
|
532
|
+
'"""Initial migration',
|
|
533
|
+
"",
|
|
534
|
+
f'Revision: {timestamp}',
|
|
535
|
+
'Create Date: ' + datetime.datetime.now().isoformat() + '',
|
|
536
|
+
'"""',
|
|
537
|
+
"from alembic import op",
|
|
538
|
+
"import sqlalchemy as sa",
|
|
539
|
+
"",
|
|
540
|
+
"",
|
|
541
|
+
"def upgrade():",
|
|
542
|
+
"",
|
|
543
|
+
]
|
|
544
|
+
|
|
545
|
+
for entity in self.entities:
|
|
546
|
+
migration_lines.append(' op.create_table(')
|
|
547
|
+
migration_lines.append(f' "{entity.name}",')
|
|
548
|
+
migration_lines.append(" sa.Column(")
|
|
549
|
+
|
|
550
|
+
for i, col in enumerate(entity.columns):
|
|
551
|
+
col_type = self._sqlalchemy_type(col.type)
|
|
552
|
+
migration_lines.append(f' "{col.name}", {col_type}, ')
|
|
553
|
+
migration_lines.append(f' nullable={col.nullable}, ')
|
|
554
|
+
migration_lines.append(f' primary_key={col.primary_key}, ')
|
|
555
|
+
if i < len(entity.columns) - 1:
|
|
556
|
+
migration_lines.append(" ),")
|
|
557
|
+
else:
|
|
558
|
+
migration_lines.append(" ),")
|
|
559
|
+
migration_lines.append(" )")
|
|
560
|
+
migration_lines.append("")
|
|
561
|
+
|
|
562
|
+
migration_lines.extend([
|
|
563
|
+
"",
|
|
564
|
+
"def downgrade():",
|
|
565
|
+
"",
|
|
566
|
+
])
|
|
567
|
+
|
|
568
|
+
for entity in self.entities:
|
|
569
|
+
migration_lines.append(f' op.drop_table("{entity.name}")')
|
|
570
|
+
|
|
571
|
+
return {
|
|
572
|
+
"src/models/__init__.py": "\n".join(models_lines),
|
|
573
|
+
f"alembic/versions/{timestamp}_initial_migration.py": "\n".join(migration_lines),
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
def _generate_django_migration(self) -> dict[str, str]:
|
|
577
|
+
"""生成 Django 迁移"""
|
|
578
|
+
timestamp = datetime.datetime.now().strftime("%Y%d%m%H%M%S")
|
|
579
|
+
|
|
580
|
+
# models.py
|
|
581
|
+
models_lines = [
|
|
582
|
+
"from django.db import models",
|
|
583
|
+
"import uuid",
|
|
584
|
+
"",
|
|
585
|
+
"",
|
|
586
|
+
]
|
|
587
|
+
|
|
588
|
+
for entity in self.entities:
|
|
589
|
+
models_lines.append(f'class {entity.name.capitalize()}(models.Model):')
|
|
590
|
+
models_lines.append(f' """{entity.comment or entity.name}"""')
|
|
591
|
+
models_lines.append("")
|
|
592
|
+
|
|
593
|
+
for col in entity.columns:
|
|
594
|
+
field_type = self._django_field_type(col.type)
|
|
595
|
+
nullable = f"null={col.nullable}, " if col.nullable else ""
|
|
596
|
+
unique = f"unique={col.unique}, " if col.unique else ""
|
|
597
|
+
default = f'default="{col.default}", ' if col.default else ""
|
|
598
|
+
|
|
599
|
+
if col.primary_key:
|
|
600
|
+
models_lines.append(' id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)')
|
|
601
|
+
else:
|
|
602
|
+
models_lines.append(f' {col.name} = models.{field_type}({nullable}{unique}{default})')
|
|
603
|
+
|
|
604
|
+
models_lines.append("")
|
|
605
|
+
models_lines.append(" class Meta:")
|
|
606
|
+
models_lines.append(f' db_table = "{entity.name}"')
|
|
607
|
+
models_lines.append("")
|
|
608
|
+
models_lines.append("")
|
|
609
|
+
|
|
610
|
+
# Django 迁移文件
|
|
611
|
+
migration_lines = [
|
|
612
|
+
"# Generated by Django",
|
|
613
|
+
"from django.db import migrations, models",
|
|
614
|
+
"import django.db.models.deletion",
|
|
615
|
+
"import uuid",
|
|
616
|
+
"",
|
|
617
|
+
"",
|
|
618
|
+
"class Migration(migrations.Migration):",
|
|
619
|
+
"",
|
|
620
|
+
' initial = True',
|
|
621
|
+
"",
|
|
622
|
+
' dependencies = [',
|
|
623
|
+
' ]',
|
|
624
|
+
"",
|
|
625
|
+
' operations = [',
|
|
626
|
+
]
|
|
627
|
+
|
|
628
|
+
for entity in self.entities:
|
|
629
|
+
migration_lines.append(' migrations.CreateModel(')
|
|
630
|
+
migration_lines.append(f' name=\'{entity.name.capitalize()}\',')
|
|
631
|
+
migration_lines.append(' fields=[')
|
|
632
|
+
|
|
633
|
+
for col in entity.columns:
|
|
634
|
+
field_def = self._django_migration_field(col)
|
|
635
|
+
migration_lines.append(f' {field_def},')
|
|
636
|
+
|
|
637
|
+
migration_lines.append(' ],')
|
|
638
|
+
migration_lines.append(' ),')
|
|
639
|
+
|
|
640
|
+
migration_lines.append(' ]')
|
|
641
|
+
|
|
642
|
+
return {
|
|
643
|
+
"src/models.py": "\n".join(models_lines),
|
|
644
|
+
f"src/migrations/{timestamp}_initial.py": "\n".join(migration_lines),
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
def _generate_sequelize_migration(self) -> dict[str, str]:
|
|
648
|
+
"""生成 Sequelize 迁移"""
|
|
649
|
+
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
650
|
+
|
|
651
|
+
# models
|
|
652
|
+
models_lines = []
|
|
653
|
+
for entity in self.entities:
|
|
654
|
+
models_lines.extend([
|
|
655
|
+
"module.exports = (sequelize, DataTypes) => {",
|
|
656
|
+
f" const {entity.name.capitalize()} = sequelize.define('{entity.name}', {{",
|
|
657
|
+
])
|
|
658
|
+
|
|
659
|
+
for col in entity.columns:
|
|
660
|
+
sequelize_type = self._sequelize_type(col.type)
|
|
661
|
+
models_lines.append(f" {col.name}: {{")
|
|
662
|
+
models_lines.append(f" type: DataTypes.{sequelize_type},")
|
|
663
|
+
models_lines.append(f" allowNull: {str(col.nullable).lower()},")
|
|
664
|
+
models_lines.append(f" unique: {str(col.unique).lower()},")
|
|
665
|
+
models_lines.append(f" primaryKey: {str(col.primary_key).lower()},")
|
|
666
|
+
models_lines.append(" },")
|
|
667
|
+
|
|
668
|
+
models_lines.extend([
|
|
669
|
+
" }, {",
|
|
670
|
+
f" tableName: '{entity.name}',",
|
|
671
|
+
" timestamps: true,",
|
|
672
|
+
" });",
|
|
673
|
+
" return {entity.name.capitalize()};",
|
|
674
|
+
"};",
|
|
675
|
+
"",
|
|
676
|
+
])
|
|
677
|
+
|
|
678
|
+
# migration
|
|
679
|
+
migration_lines = [
|
|
680
|
+
"'use strict';",
|
|
681
|
+
"",
|
|
682
|
+
"module.exports = {",
|
|
683
|
+
" up: async (queryInterface, Sequelize) => {",
|
|
684
|
+
"",
|
|
685
|
+
]
|
|
686
|
+
|
|
687
|
+
for entity in self.entities:
|
|
688
|
+
migration_lines.extend([
|
|
689
|
+
f' await queryInterface.createTable("{entity.name}", {{',
|
|
690
|
+
])
|
|
691
|
+
|
|
692
|
+
for col in entity.columns:
|
|
693
|
+
col_type = self._sequelize_type(col.type)
|
|
694
|
+
migration_lines.extend([
|
|
695
|
+
f' {col.name}: {{',
|
|
696
|
+
f' type: Sequelize.{col_type},',
|
|
697
|
+
f' allowNull: {str(col.nullable).lower()},',
|
|
698
|
+
f' unique: {str(col.unique).lower()},',
|
|
699
|
+
f' primaryKey: {str(col.primary_key).lower()},',
|
|
700
|
+
' },',
|
|
701
|
+
])
|
|
702
|
+
|
|
703
|
+
migration_lines.extend([
|
|
704
|
+
" });",
|
|
705
|
+
"",
|
|
706
|
+
])
|
|
707
|
+
|
|
708
|
+
migration_lines.extend([
|
|
709
|
+
" },",
|
|
710
|
+
"",
|
|
711
|
+
" down: async (queryInterface, Sequelize) => {",
|
|
712
|
+
"",
|
|
713
|
+
])
|
|
714
|
+
|
|
715
|
+
for entity in self.entities:
|
|
716
|
+
migration_lines.append(f' await queryInterface.dropTable("{entity.name}");')
|
|
717
|
+
|
|
718
|
+
migration_lines.extend([
|
|
719
|
+
"",
|
|
720
|
+
" },",
|
|
721
|
+
"};",
|
|
722
|
+
])
|
|
723
|
+
|
|
724
|
+
return {
|
|
725
|
+
f"src/models/{timestamp}-create-{self.name}.js": "\n".join(models_lines + ["", ""] + migration_lines),
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
def _generate_mongoose_migration(self) -> dict[str, str]:
|
|
729
|
+
"""生成 Mongoose 迁移"""
|
|
730
|
+
models_lines = []
|
|
731
|
+
for entity in self.entities:
|
|
732
|
+
models_lines.extend([
|
|
733
|
+
"const mongoose = require('mongoose');",
|
|
734
|
+
"",
|
|
735
|
+
f"const {entity.name.capitalize()}Schema = new mongoose.Schema({{" if entity.name == self.entities[0].name else f"const {entity.name.capitalize()}Schema = new mongoose.Schema({{",
|
|
736
|
+
])
|
|
737
|
+
|
|
738
|
+
for col in entity.columns:
|
|
739
|
+
mongoose_type = self._mongoose_type(col.type)
|
|
740
|
+
required = "true" if not col.nullable else "false"
|
|
741
|
+
unique = "true" if col.unique else "false"
|
|
742
|
+
|
|
743
|
+
models_lines.extend([
|
|
744
|
+
f" {col.name}: {{",
|
|
745
|
+
f" type: {mongoose_type},",
|
|
746
|
+
f" required: {required},",
|
|
747
|
+
f" unique: {unique},",
|
|
748
|
+
" },",
|
|
749
|
+
])
|
|
750
|
+
|
|
751
|
+
models_lines.extend([
|
|
752
|
+
"}, {",
|
|
753
|
+
" timestamps: true,",
|
|
754
|
+
f" collection: '{entity.name}',",
|
|
755
|
+
"});",
|
|
756
|
+
"",
|
|
757
|
+
f"module.exports = mongoose.model('{entity.name.capitalize()}', {entity.name.capitalize()}Schema);",
|
|
758
|
+
"",
|
|
759
|
+
])
|
|
760
|
+
|
|
761
|
+
return {
|
|
762
|
+
f"src/models/{entity.name}.model.js": "\n".join(models_lines),
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
def _generate_sql_migration(self) -> dict[str, str]:
|
|
766
|
+
"""生成原生 SQL 迁移"""
|
|
767
|
+
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
768
|
+
|
|
769
|
+
# up migration
|
|
770
|
+
up_lines = [
|
|
771
|
+
f"-- Migration Up: {timestamp}",
|
|
772
|
+
f"-- Created: {datetime.datetime.now().isoformat()}",
|
|
773
|
+
"",
|
|
774
|
+
]
|
|
775
|
+
|
|
776
|
+
for entity in self.entities:
|
|
777
|
+
up_lines.extend([
|
|
778
|
+
f"-- Create {entity.name} table",
|
|
779
|
+
f"CREATE TABLE IF NOT EXISTS {entity.name} (",
|
|
780
|
+
])
|
|
781
|
+
|
|
782
|
+
col_defs = []
|
|
783
|
+
for col in entity.columns:
|
|
784
|
+
col_defs.append(f" {col.name} {self._sql_type(col.type)}"
|
|
785
|
+
f"{'' if col.nullable else ' NOT NULL'}"
|
|
786
|
+
f"{' PRIMARY KEY' if col.primary_key else ''}"
|
|
787
|
+
f"{' UNIQUE' if col.unique else ''}"
|
|
788
|
+
f"{',' if col != entity.columns[-1] else ''}")
|
|
789
|
+
|
|
790
|
+
up_lines.extend(col_defs)
|
|
791
|
+
up_lines.extend([
|
|
792
|
+
");",
|
|
793
|
+
"",
|
|
794
|
+
])
|
|
795
|
+
|
|
796
|
+
# down migration
|
|
797
|
+
down_lines = [
|
|
798
|
+
f"-- Migration Down: {timestamp}",
|
|
799
|
+
"",
|
|
800
|
+
]
|
|
801
|
+
|
|
802
|
+
for entity in self.entities:
|
|
803
|
+
down_lines.append(f"DROP TABLE IF EXISTS {entity.name} CASCADE;")
|
|
804
|
+
|
|
805
|
+
return {
|
|
806
|
+
f"migrations/{timestamp}_up.sql": "\n".join(up_lines),
|
|
807
|
+
f"migrations/{timestamp}_down.sql": "\n".join(down_lines),
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
# ==================== 类型转换方法 ====================
|
|
811
|
+
|
|
812
|
+
def _get_prisma_provider(self) -> str:
|
|
813
|
+
"""获取 Prisma 数据库提供者"""
|
|
814
|
+
providers = {
|
|
815
|
+
DatabaseType.POSTGRESQL: "postgresql",
|
|
816
|
+
DatabaseType.MYSQL: "mysql",
|
|
817
|
+
DatabaseType.SQLITE: "sqlite",
|
|
818
|
+
DatabaseType.MONGODB: "mongodb",
|
|
819
|
+
}
|
|
820
|
+
return providers.get(self.db_type, "postgresql")
|
|
821
|
+
|
|
822
|
+
def _prisma_type(self, col_type: str) -> str:
|
|
823
|
+
"""转换为 Prisma 类型"""
|
|
824
|
+
type_map = {
|
|
825
|
+
"uuid": "String",
|
|
826
|
+
"varchar": "String",
|
|
827
|
+
"string": "String",
|
|
828
|
+
"text": "String",
|
|
829
|
+
"int": "Int",
|
|
830
|
+
"integer": "Int",
|
|
831
|
+
"bigint": "BigInt",
|
|
832
|
+
"float": "Float",
|
|
833
|
+
"double": "Float",
|
|
834
|
+
"decimal": "Decimal",
|
|
835
|
+
"boolean": "Boolean",
|
|
836
|
+
"bool": "Boolean",
|
|
837
|
+
"date": "DateTime",
|
|
838
|
+
"datetime": "DateTime",
|
|
839
|
+
"timestamp": "DateTime",
|
|
840
|
+
"json": "Json",
|
|
841
|
+
"bytes": "Bytes",
|
|
842
|
+
}
|
|
843
|
+
return type_map.get(col_type.lower().split("(")[0], "String")
|
|
844
|
+
|
|
845
|
+
def _sql_type(self, col_type: str) -> str:
|
|
846
|
+
"""转换为 SQL 类型"""
|
|
847
|
+
type_map = {
|
|
848
|
+
"uuid": "UUID",
|
|
849
|
+
"varchar": "VARCHAR(255)",
|
|
850
|
+
"string": "VARCHAR(255)",
|
|
851
|
+
"text": "TEXT",
|
|
852
|
+
"int": "INTEGER",
|
|
853
|
+
"integer": "INTEGER",
|
|
854
|
+
"bigint": "BIGINT",
|
|
855
|
+
"float": "FLOAT",
|
|
856
|
+
"double": "DOUBLE PRECISION",
|
|
857
|
+
"decimal": "DECIMAL(10, 2)",
|
|
858
|
+
"boolean": "BOOLEAN",
|
|
859
|
+
"bool": "BOOLEAN",
|
|
860
|
+
"date": "DATE",
|
|
861
|
+
"datetime": "TIMESTAMP",
|
|
862
|
+
"timestamp": "TIMESTAMP",
|
|
863
|
+
"json": "JSONB",
|
|
864
|
+
"bytes": "BYTEA",
|
|
865
|
+
}
|
|
866
|
+
return type_map.get(col_type.lower().split("(")[0], "VARCHAR(255)")
|
|
867
|
+
|
|
868
|
+
def _typescript_type(self, col_type: str) -> str:
|
|
869
|
+
"""转换为 TypeScript 类型"""
|
|
870
|
+
type_map = {
|
|
871
|
+
"uuid": "string",
|
|
872
|
+
"varchar": "string",
|
|
873
|
+
"string": "string",
|
|
874
|
+
"text": "string",
|
|
875
|
+
"int": "number",
|
|
876
|
+
"integer": "number",
|
|
877
|
+
"bigint": "number",
|
|
878
|
+
"float": "number",
|
|
879
|
+
"double": "number",
|
|
880
|
+
"decimal": "number",
|
|
881
|
+
"boolean": "boolean",
|
|
882
|
+
"bool": "boolean",
|
|
883
|
+
"date": "Date",
|
|
884
|
+
"datetime": "Date",
|
|
885
|
+
"timestamp": "Date",
|
|
886
|
+
"json": "object",
|
|
887
|
+
"bytes": "Buffer",
|
|
888
|
+
}
|
|
889
|
+
return type_map.get(col_type.lower().split("(")[0], "any")
|
|
890
|
+
|
|
891
|
+
def _typeorm_type(self, col_type: str) -> str:
|
|
892
|
+
"""转换为 TypeORM 类型"""
|
|
893
|
+
return self._sql_type(col_type)
|
|
894
|
+
|
|
895
|
+
def _python_sqlalchemy_type(self, col_type: str) -> str:
|
|
896
|
+
"""转换为 Python SQLAlchemy 类型"""
|
|
897
|
+
type_map = {
|
|
898
|
+
"uuid": "String(36)",
|
|
899
|
+
"varchar": "String",
|
|
900
|
+
"string": "String",
|
|
901
|
+
"text": "Text",
|
|
902
|
+
"int": "Integer",
|
|
903
|
+
"integer": "Integer",
|
|
904
|
+
"bigint": "BigInteger",
|
|
905
|
+
"float": "Float",
|
|
906
|
+
"double": "Float",
|
|
907
|
+
"decimal": "Numeric(10, 2)",
|
|
908
|
+
"boolean": "Boolean",
|
|
909
|
+
"bool": "Boolean",
|
|
910
|
+
"date": "Date",
|
|
911
|
+
"datetime": "DateTime",
|
|
912
|
+
"timestamp": "DateTime",
|
|
913
|
+
"json": "JSON",
|
|
914
|
+
"bytes": "LargeBinary",
|
|
915
|
+
}
|
|
916
|
+
return type_map.get(col_type.lower().split("(")[0], "String")
|
|
917
|
+
|
|
918
|
+
def _sqlalchemy_type(self, col_type: str) -> str:
|
|
919
|
+
"""转换为 SQLAlchemy Column 类型"""
|
|
920
|
+
type_map = {
|
|
921
|
+
"uuid": "sa.String(36)",
|
|
922
|
+
"varchar": "sa.String",
|
|
923
|
+
"string": "sa.String",
|
|
924
|
+
"text": "sa.Text",
|
|
925
|
+
"int": "sa.Integer",
|
|
926
|
+
"integer": "sa.Integer",
|
|
927
|
+
"bigint": "sa.BigInteger",
|
|
928
|
+
"float": "sa.Float",
|
|
929
|
+
"double": "sa.Float",
|
|
930
|
+
"decimal": "sa.Numeric(10, 2)",
|
|
931
|
+
"boolean": "sa.Boolean",
|
|
932
|
+
"bool": "sa.Boolean",
|
|
933
|
+
"date": "sa.Date",
|
|
934
|
+
"datetime": "sa.DateTime",
|
|
935
|
+
"timestamp": "sa.DateTime",
|
|
936
|
+
"json": "sa.JSON",
|
|
937
|
+
"bytes": "sa.LargeBinary",
|
|
938
|
+
}
|
|
939
|
+
return type_map.get(col_type.lower().split("(")[0], "sa.String")
|
|
940
|
+
|
|
941
|
+
def _django_field_type(self, col_type: str) -> str:
|
|
942
|
+
"""转换为 Django Field 类型"""
|
|
943
|
+
type_map = {
|
|
944
|
+
"uuid": "UUIDField",
|
|
945
|
+
"varchar": "CharField",
|
|
946
|
+
"string": "CharField",
|
|
947
|
+
"text": "TextField",
|
|
948
|
+
"int": "IntegerField",
|
|
949
|
+
"integer": "IntegerField",
|
|
950
|
+
"bigint": "BigIntegerField",
|
|
951
|
+
"float": "FloatField",
|
|
952
|
+
"double": "FloatField",
|
|
953
|
+
"decimal": "DecimalField",
|
|
954
|
+
"boolean": "BooleanField",
|
|
955
|
+
"bool": "BooleanField",
|
|
956
|
+
"date": "DateField",
|
|
957
|
+
"datetime": "DateTimeField",
|
|
958
|
+
"timestamp": "DateTimeField",
|
|
959
|
+
"json": "JSONField",
|
|
960
|
+
"bytes": "BinaryField",
|
|
961
|
+
}
|
|
962
|
+
return type_map.get(col_type.lower().split("(")[0], "CharField")
|
|
963
|
+
|
|
964
|
+
def _django_migration_field(self, col: Column) -> str:
|
|
965
|
+
"""生成 Django 迁移字段定义"""
|
|
966
|
+
field_type = self._django_field_type(col.type)
|
|
967
|
+
args = []
|
|
968
|
+
|
|
969
|
+
if col.primary_key:
|
|
970
|
+
return "models.UUIDField(primary_key=True, default=uuid.uuid4)"
|
|
971
|
+
|
|
972
|
+
if not col.nullable:
|
|
973
|
+
args.append("null=False")
|
|
974
|
+
|
|
975
|
+
if col.unique:
|
|
976
|
+
args.append("unique=True")
|
|
977
|
+
|
|
978
|
+
if col.default:
|
|
979
|
+
args.append(f'default="{col.default}"')
|
|
980
|
+
|
|
981
|
+
# 根据类型添加 max_length
|
|
982
|
+
if field_type in ["CharField", "UUIDField"]:
|
|
983
|
+
args.append("max_length=255")
|
|
984
|
+
|
|
985
|
+
args_str = ", ".join(args) if args else ""
|
|
986
|
+
return f'models.{field_type}({args_str})'
|
|
987
|
+
|
|
988
|
+
def _sequelize_type(self, col_type: str) -> str:
|
|
989
|
+
"""转换为 Sequelize 类型"""
|
|
990
|
+
type_map = {
|
|
991
|
+
"uuid": "UUID",
|
|
992
|
+
"varchar": "STRING",
|
|
993
|
+
"string": "STRING",
|
|
994
|
+
"text": "TEXT",
|
|
995
|
+
"int": "INTEGER",
|
|
996
|
+
"integer": "INTEGER",
|
|
997
|
+
"bigint": "BIGINT",
|
|
998
|
+
"float": "FLOAT",
|
|
999
|
+
"double": "DOUBLE",
|
|
1000
|
+
"decimal": "DECIMAL(10, 2)",
|
|
1001
|
+
"boolean": "BOOLEAN",
|
|
1002
|
+
"bool": "BOOLEAN",
|
|
1003
|
+
"date": "DATE",
|
|
1004
|
+
"datetime": "DATE",
|
|
1005
|
+
"timestamp": "DATE",
|
|
1006
|
+
"json": "JSON",
|
|
1007
|
+
"bytes": "BLOB",
|
|
1008
|
+
}
|
|
1009
|
+
return type_map.get(col_type.lower().split("(")[0], "STRING")
|
|
1010
|
+
|
|
1011
|
+
def _mongoose_type(self, col_type: str) -> str:
|
|
1012
|
+
"""转换为 Mongoose 类型"""
|
|
1013
|
+
type_map = {
|
|
1014
|
+
"uuid": "Schema.Types.UUID",
|
|
1015
|
+
"varchar": "String",
|
|
1016
|
+
"string": "String",
|
|
1017
|
+
"text": "String",
|
|
1018
|
+
"int": "Number",
|
|
1019
|
+
"integer": "Number",
|
|
1020
|
+
"bigint": "Schema.Types.Long",
|
|
1021
|
+
"float": "Number",
|
|
1022
|
+
"double": "Number",
|
|
1023
|
+
"decimal": "Schema.Types.Decimal128",
|
|
1024
|
+
"boolean": "Boolean",
|
|
1025
|
+
"bool": "Boolean",
|
|
1026
|
+
"date": "Date",
|
|
1027
|
+
"datetime": "Date",
|
|
1028
|
+
"timestamp": "Date",
|
|
1029
|
+
"json": "Schema.Types.Mixed",
|
|
1030
|
+
"bytes": "Buffer",
|
|
1031
|
+
}
|
|
1032
|
+
return type_map.get(col_type.lower().split("(")[0], "String")
|