ruoyi_generator.py 10.0 KB

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