ruoyi_generator.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. # -*- coding: utf-8 -*-
  2. # @Author : YY
  3. # @FileName: ruoyi_generator.py
  4. import json
  5. import os
  6. from typing import List
  7. from zipfile import ZipFile
  8. from io import BytesIO
  9. from jinja2 import Environment, FileSystemLoader
  10. from ruoyi_common.utils import StringUtil
  11. from ruoyi_common.constant import Constants
  12. from ruoyi_generator.domain.entity import GenTable, GenTableColumn
  13. from ruoyi_generator.mapper import gen_table_mapper, gen_table_column_mapper
  14. from ruoyi_generator.util import GenUtils
  15. from ruoyi_generator.config import GeneratorConfig
  16. from datetime import datetime
  17. from ruoyi_admin.ext import db
  18. from sqlalchemy import text
  19. from flask import Flask
  20. from ruoyi_admin import create_app
  21. from ruoyi_generator.mapper import gen_table_mapper
  22. from ruoyi_generator.domain.entity import GenTable
  23. from ruoyi_generator.config import GeneratorConfig
  24. from ruoyi_common.utils import StringUtil
  25. from ruoyi_generator.util import to_underscore
  26. class RuoYiGenerator:
  27. def __init__(self):
  28. # 初始化模板引擎
  29. self.template_env = Environment(
  30. loader=FileSystemLoader(os.path.join(os.path.dirname(__file__), 'vm')),
  31. autoescape=False # 关闭自动转义,避免HTML转义字符
  32. )
  33. def get_template_data(self, table_id: int) -> dict:
  34. """
  35. 获取模板数据
  36. Args:
  37. table_id (int): 表ID
  38. Returns:
  39. dict: 模板数据
  40. """
  41. # 查询表信息
  42. table = gen_table_mapper.select_by_id(table_id)
  43. if not table:
  44. raise Exception(f"表ID {table_id} 不存在")
  45. # 查询列信息
  46. columns = gen_table_column_mapper.select_list_by_table_id(table_id)
  47. table.columns = columns
  48. # 设置主键列
  49. pk_columns = [column for column in columns if column.is_pk == '1']
  50. if pk_columns:
  51. table.pk_column = pk_columns[0]
  52. # 设置其他属性
  53. if table.options:
  54. try:
  55. table.options = json.loads(table.options) if isinstance(table.options, str) else table.options
  56. table.tree_name = table.options.get('treeName')
  57. table.tree_code = table.options.get('treeCode')
  58. table.tree_parent_code = table.options.get('treeParentCode')
  59. except Exception:
  60. pass
  61. # 设置模板数据
  62. data = {
  63. 'table': table,
  64. 'constants': Constants,
  65. 'datetime': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  66. }
  67. return data
  68. def generate_files(self, table_id: int) -> dict:
  69. """
  70. 生成文件内容
  71. Args:
  72. table_id (int): 表ID
  73. Returns:
  74. dict: 生成的文件内容,key为文件路径,value为文件内容
  75. """
  76. # 获取模板数据
  77. data = self.get_template_data(table_id)
  78. table = data['table']
  79. # 获取所有模板文件
  80. template_files = self.get_template_files()
  81. # 生成文件
  82. generated_files = {}
  83. for template_file in template_files:
  84. try:
  85. # 渲染模板
  86. template = self.template_env.get_template(template_file)
  87. content = template.render(**data)
  88. # 生成文件名
  89. file_name = GenUtils.get_file_name(template_file, table)
  90. generated_files[file_name] = content
  91. except Exception as e:
  92. print(f"渲染模板 {template_file} 失败: {e}")
  93. continue
  94. return generated_files
  95. def get_template_files(self) -> List[str]:
  96. """
  97. 获取模板文件列表
  98. Returns:
  99. List[str]: 模板文件列表
  100. """
  101. template_files = []
  102. vm_dir = os.path.join(os.path.dirname(__file__), 'vm')
  103. # 递归遍历vm目录下的所有.vm文件
  104. for root, dirs, files in os.walk(vm_dir):
  105. for file in files:
  106. if file.endswith('.vm'):
  107. # 获取相对于vm目录的路径
  108. rel_path = os.path.relpath(os.path.join(root, file), vm_dir)
  109. template_files.append(rel_path.replace('\\', '/'))
  110. return template_files
  111. def preview_code(self, table_id: int) -> dict:
  112. """
  113. 预览代码
  114. Args:
  115. table_id (int): 表ID
  116. Returns:
  117. dict: 生成的代码
  118. """
  119. try:
  120. # 生成文件内容
  121. generated_files = self.generate_files(table_id)
  122. # 返回生成的代码
  123. return generated_files
  124. except Exception as e:
  125. print(f"预览代码失败: {e}")
  126. return {}
  127. def download_code(self, table_id: int) -> bytes:
  128. """
  129. 下载代码
  130. Args:
  131. table_id (int): 表ID
  132. Returns:
  133. bytes: 生成的代码压缩包
  134. """
  135. # 生成文件内容
  136. generated_files = self.generate_files(table_id)
  137. # 创建ZIP文件
  138. zip_buffer = BytesIO()
  139. with ZipFile(zip_buffer, 'w') as zip_file:
  140. for file_path, content in generated_files.items():
  141. zip_file.writestr(file_path, content)
  142. zip_buffer.seek(0)
  143. return zip_buffer.getvalue()
  144. def import_table(self, table_name: str) -> bool:
  145. """导入表"""
  146. try:
  147. # 检查表是否已存在
  148. if gen_table_mapper.exists_table(table_name):
  149. # 如果表已存在,直接同步字段信息
  150. from ruoyi_generator.service import GenTableService
  151. service = GenTableService()
  152. return service.synch_db(table_name)
  153. # 创建表信息
  154. table = GenTable()
  155. table.table_name = table_name
  156. # 获取表注释
  157. try:
  158. table_comment_result = db.session.execute(
  159. text("SELECT table_comment FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = :table_name"),
  160. {"table_name": table_name}
  161. ).fetchone()
  162. table.table_comment = table_comment_result[0] if table_comment_result and table_comment_result[0] else table_name
  163. except Exception as e:
  164. print(f"获取表注释失败: {e}")
  165. table.table_comment = table_name
  166. # 处理表名前缀
  167. clean_table_name = GenUtils.remove_table_prefix(table_name) if GeneratorConfig.auto_remove_pre else table_name
  168. # 使用下划线命名法而不是驼峰命名法
  169. table.class_name = to_underscore(clean_table_name)
  170. table.package_name = GeneratorConfig.package_name
  171. table.module_name = StringUtil.substring_before(clean_table_name, "_") if hasattr(StringUtil, 'substring_before') and "_" in clean_table_name else clean_table_name
  172. table.business_name = StringUtil.substring_after(clean_table_name, "_") if hasattr(StringUtil, 'substring_after') and "_" in clean_table_name else clean_table_name
  173. table.function_name = table.business_name
  174. table.function_author = GeneratorConfig.author
  175. table.create_by = "admin"
  176. # 插入表信息到数据库
  177. table_id = gen_table_mapper.insert(table)
  178. # 获取表列信息
  179. columns = gen_table_mapper.select_db_table_columns_by_name(table_name)
  180. # 即使没有列信息也继续处理
  181. if not columns:
  182. print(f"警告:未能获取到表 {table_name} 的列信息")
  183. for i, column in enumerate(columns or []):
  184. column.table_id = table_id
  185. column.sort = i + 1
  186. column.create_by = "admin"
  187. # 设置默认的字段属性
  188. if not column.java_type:
  189. if column.column_type in ['int', 'integer', 'tinyint', 'smallint', 'mediumint']:
  190. column.java_type = 'Integer'
  191. elif column.column_type in ['bigint']:
  192. column.java_type = 'Long'
  193. elif column.column_type in ['float', 'double', 'decimal', 'numeric']:
  194. column.java_type = 'BigDecimal'
  195. elif column.column_type in ['date', 'datetime', 'timestamp']:
  196. column.java_type = 'Date'
  197. else:
  198. column.java_type = 'String'
  199. if not column.java_field:
  200. column.java_field = GenUtils.to_camel_case(column.column_name)
  201. if not column.html_type:
  202. if column.column_type in ['date', 'datetime', 'timestamp']:
  203. column.html_type = 'datetime'
  204. elif column.column_type in ['text', 'longtext', 'mediumtext']:
  205. column.html_type = 'textarea'
  206. elif column.column_type in ['tinyint'] and column.column_name in ['status', 'is_delete', 'is_enabled']:
  207. column.html_type = 'radio'
  208. else:
  209. column.html_type = 'input'
  210. if not column.query_type:
  211. if column.column_type in ['varchar', 'char', 'text']:
  212. column.query_type = 'LIKE'
  213. else:
  214. column.query_type = 'EQ'
  215. gen_table_column_mapper.insert(column)
  216. return True
  217. except Exception as e:
  218. print(f"导入表失败: {e}")
  219. return False