diff --git a/generator.py b/generator.py index 81f9acd..5d81797 100644 --- a/generator.py +++ b/generator.py @@ -1,12 +1,13 @@ import logging import os from datetime import datetime -from jinja2 import Environment, FileSystemLoader +from jinja2 import Environment, FileSystemLoader,TemplateNotFound # from config import * from db import get_table, get_columns from utils import * import argparse import yaml +import re env = Environment(loader=FileSystemLoader("templates")) @@ -24,28 +25,60 @@ def build_fields(table_name): }) return fields -def render(template_name, out_path, context, overwrite=False): +def render(template_name, out_path, context, java_version, overwrite=False): """ :param template_name: 模板文件名 :param out_path: 输出文件路径 :param context: 渲染上下文 :param overwrite: 是否覆盖已存在文件,默认 False """ - # 文件存在且不允许覆盖 → 直接跳过 + + # 文件存在且不允许覆盖 → 跳过 if os.path.exists(out_path) and not overwrite: logging.info("Skip exists file: %s", out_path) return - tpl = env.get_template(template_name) + if java_version != "": + template_name = java_version+"."+template_name + + real_template = template_name + + print(">>>>template_name:", template_name) + + # 1️⃣ 尝试原模板 + try: + env.loader.get_source(env, real_template) + except TemplateNotFound: + # 2️⃣ 去掉数字前缀,如 21.controller.java.j2 → controller.java.j2 + fallback = re.sub(r"^\d+\.", "", template_name) + + if fallback != template_name: + try: + env.loader.get_source(env, fallback) + logging.warning( + "Template not found: %s, fallback to %s", + template_name, fallback + ) + real_template = fallback + except TemplateNotFound: + raise FileNotFoundError( + f"Template not found: {template_name} and fallback {fallback}" + ) + else: + raise FileNotFoundError(f"Template not found: {template_name}") + + # 3️⃣ 正式渲染 + tpl = env.get_template(real_template) content = tpl.render(**context) os.makedirs(os.path.dirname(out_path), exist_ok=True) with open(out_path, "w", encoding="utf-8") as f: f.write(content) - logging.info("Generated file: %s", out_path) + logging.info("Generated file: %s (template: %s)", out_path, real_template) -def generate(table_names: list[str], model_names: list[str], conf_name: str, over_write: bool): + +def generate(table_names: list[str], model_names: list[str], conf_name: str, over_write: bool, java_version: str): # context = { # "mainModule": MAIN_MODULE, @@ -119,6 +152,7 @@ def generate(table_names: list[str], model_names: list[str], conf_name: str, ove "entity.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/entity/{entity}.java", context, + java_version, over_write ) @@ -126,6 +160,7 @@ def generate(table_names: list[str], model_names: list[str], conf_name: str, ove "controller.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/controller/{entity}Controller.java", context, + java_version, over_write ) @@ -133,6 +168,7 @@ def generate(table_names: list[str], model_names: list[str], conf_name: str, ove "service.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/service/{entity}Service.java", context, + java_version, over_write ) @@ -140,6 +176,7 @@ def generate(table_names: list[str], model_names: list[str], conf_name: str, ove "serviceImpl.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/service/impl/{entity}MPJBaseServiceImpl.java", context, + java_version, over_write ) @@ -147,6 +184,7 @@ def generate(table_names: list[str], model_names: list[str], conf_name: str, ove "mapper.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/mapper/{entity}Mapper.java", context, + java_version, over_write ) @@ -154,6 +192,7 @@ def generate(table_names: list[str], model_names: list[str], conf_name: str, ove "mapper.xml.j2", f"{MAIN_MODULE}/{MODULE_NAME}/src/main/resources/mappers/{entity}Mapper.xml", context, + java_version, over_write ) @@ -162,28 +201,32 @@ def generate(table_names: list[str], model_names: list[str], conf_name: str, ove render( "baseEntity.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/entity/BaseEntity.java", - context + context, + java_version ) # common MybatisPlusConfig render( "mybatisPlusConfig.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/MybatisPlusConfig.java", - context + context, + java_version ) # common MybatisPlusConfig render( "webLogAspect.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/WebLogAspect.java", - context + context, + java_version ) # common 基础输出result render( "result.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/vo/Result.java", - context + context, + java_version ) #Util 公共功能 @@ -191,56 +234,64 @@ def generate(table_names: list[str], model_names: list[str], conf_name: str, ove render( file, f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/unit/{file.replace('.j2', '')}", - context + context, + java_version ) # application 启动的方法 render( "application.java.j2", f"{BASE_DIR}{MAIN_BASE_PACKAGE_DIR}/Application.java", - context + context, + java_version ) # test 测试类 render( "applicationTests.java.j2", f"{BASE_DIR}{OUTPUT_DIR}/test/{to_path(BASE_PACKAGE)}/ApplicationTests.java", - context + context, + java_version ) # 主pom文件 render( "main.pom.xml.j2", f"{BASE_DIR}{MAIN_MODULE}/pom.xml", - context + context, + java_version ) # 子项目pom文件 render( "project.pom.xml.j2", f"{BASE_DIR}{MAIN_MODULE}/{MODULE_NAME}/pom.xml", - context + context, + java_version ) #项目的yml配置文件 resources 生成环境配置为了最低限度能将项目跑起来 render( "application.yml.j2", f"{MAIN_MODULE}/{MODULE_NAME}/src/main/resources/application.yml", - context + context, + java_version ) #项目开发环境的yml配置文件 resources yml 只生成dev环境配置为了最低限度能将项目跑起来 render( "application-dev.yml.j2", f"{MAIN_MODULE}/{MODULE_NAME}/src/main/resources/application-dev.yml", - context + context, + java_version ) #项目开发环境的yml配置文件 resources yml 只生成dev环境配置为了最低限度能将项目跑起来 render( "logback.xml.j2", f"{MAIN_MODULE}/{MODULE_NAME}/src/main/resources/logback.xml", - context + context, + java_version ) # ========= 功能模块 ========= @@ -249,51 +300,59 @@ def generate(table_names: list[str], model_names: list[str], conf_name: str, ove case "swagger": # common Swagger2 render( - "swagger2.java.j2", + "SwaggerConfig.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/Swagger2.java", - context + context, + java_version ) case "saToken": # common GlobalException soToken 报错自定义 render( "globalException.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/GlobalException.java", - context + context, + java_version ) render( - "saTokenConfigure.java.j2", + "SaTokenConfig.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/SaTokenConfig.java", - context + context, + java_version ) case "minio": #MinioConfig render( "MinioConfig.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/MinioConfig.java", - context + context, + java_version ) render( "MinioUpController.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/controller/MinioUpController.java", - context + context, + java_version ) render( "MinioUpComponent.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/unit/MinioUpComponent.java", - context + context, + java_version ) case "xxlJob": # common XxlJobConfig render( "xxlJobConfig.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/XxlJobConfig.java", - context + context, + java_version ) # common xxjob的测试类 render( "testJob.java.j2", f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/job/TestJob.java", - context + context, + java_version ) if __name__ == "__main__": @@ -302,11 +361,13 @@ if __name__ == "__main__": tables = [t.strip() for t in args.tab.split(",") if t.strip()] models = [m.strip() for m in args.model.split(",") if m.strip()] conf = args.conf - re = args.re + version = args.jdk + rew = args.rew generate( table_names=tables, model_names=models, conf_name=conf, - over_write=re + java_version=version, + over_write=rew ) \ No newline at end of file diff --git a/templates/21.FilesUtil.java.j2 b/templates/21.FilesUtil.java.j2 new file mode 100644 index 0000000..3e6e737 --- /dev/null +++ b/templates/21.FilesUtil.java.j2 @@ -0,0 +1,141 @@ +package {{ package.Common }}.unit; + +import org.apache.commons.io.FileUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; +import java.util.Base64; + +public class FilesUtil { + + /** + * 根据文件路径获取文件字节流 + * @return + * filePath 文件路径 + * @throws IOException + */ + public static byte[] toByteArray(String filePath) throws IOException { + File f = new File(filePath); + if (!f.exists()) { + throw new FileNotFoundException("文件不存在"); + } + + ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length()); + BufferedInputStream in = null; + try { + in = new BufferedInputStream(new FileInputStream(f)); + int buf_size = 1024; + byte[] buffer = new byte[buf_size]; + int len = 0; + while (-1 != (len = in.read(buffer, 0, buf_size))) { + bos.write(buffer, 0, len); + } + return bos.toByteArray(); + } catch (IOException e) { + e.printStackTrace(); + throw e; + } finally { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + bos.close(); + } + } + public static File multipartFileToFile(MultipartFile file) throws Exception { + File toFile = null; + if (file.equals("") || file.getSize() <= 0) { + file = null; + } else { + InputStream ins = null; + ins = file.getInputStream(); + toFile = new File(file.getOriginalFilename()); + inputStreamToFile(ins, toFile); + ins.close(); + } + return toFile; + + } + + + + private static void inputStreamToFile(InputStream ins, File file) { + try { + OutputStream os = new FileOutputStream(file); + int bytesRead = 0; + byte[] buffer = new byte[8192]; + while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { + os.write(buffer, 0, bytesRead); + } + os.close(); + ins.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static String TransformPhotoToBase64Data(String path){ + Base64.Encoder encoder= Base64.getEncoder(); //获取Base64编码器 + byte [] ImgContainer = null ; //数据集缓存器 + FileInputStream fileInputStream = null; //文件输入流 + try { + System.out.println(path); + File file=new File(path); + fileInputStream = new FileInputStream(file); //到指定路径寻找文件 + ImgContainer = new byte[fileInputStream.available()]; //设置图片字节数据缓冲区大小 + fileInputStream.read(ImgContainer); //将数据流中的图片数据读进缓冲区 + String Base64ImgData =encoder.encodeToString(ImgContainer); //将图片编码转换成Base64格式的数据集 + fileInputStream.close(); //关闭数据流 + return Base64ImgData; //将缓冲区数据转换成字符数据返回 + } catch (FileNotFoundException e) { + return "找不到指定文件!"; + } catch (IOException e) { + e.printStackTrace(); + } + return "null"; + } + + public static String getBase64String(MultipartFile multiPartFile) throws IOException { + String baseStr = null; + + //把MultipartFile转化为File + File file = new File(multiPartFile.getOriginalFilename()); + FileUtils.copyInputStreamToFile(multiPartFile.getInputStream(), file); + + try {//file转base64 + FileInputStream inputStream = new FileInputStream(file); + byte[] buffer = new byte[(int) file.length()]; + inputStream.read(buffer); + inputStream.close(); + baseStr = Base64.getEncoder().encodeToString(buffer); + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + //删除临时文件 + if (file.exists()) { + file.delete(); + } + baseStr = baseStr.replaceAll("\r\n", ""); + return baseStr; + } + + /** + * 返回文件扩展名,带。 + * @param file + * @return + */ + public static String getFileExtension(MultipartFile file) { + String fileName = file.getOriginalFilename(); + int dotIndex = fileName.lastIndexOf("."); + if (dotIndex == -1) { + return ""; + } else { + return "."+fileName.substring(dotIndex + 1); + } + } + +} \ No newline at end of file diff --git a/templates/21.MinioUpComponent.java.j2 b/templates/21.MinioUpComponent.java.j2 new file mode 100644 index 0000000..1df9f4e --- /dev/null +++ b/templates/21.MinioUpComponent.java.j2 @@ -0,0 +1,190 @@ +package {{ package.Common }}.unit; +import {{ package.Common }}.config.MinioConfig; + +import io.minio.*; +import io.minio.http.Method; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.TimeUnit; + + +/**** + * @Description: 上传 文件组件 + * @Author: {{author}} + * @Date: {{date}} + * @Wechat: {{ wechat }} + */ +@Slf4j +@Component +public class MinioUpComponent { + + private final static String separator = "/"; + @Autowired + private MinioConfig minioConfig; + @Autowired + private MinioClient minioClient; + + /** + * @param dirPath + * @param filename yyyy/mm/dd/file.jpg + * @return + */ + public static String builderFilePath(String dirPath, String filename) { + StringBuilder stringBuilder = new StringBuilder(50); + if (!StringUtils.isEmpty(dirPath)) { + stringBuilder.append(dirPath).append(separator); + } + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); + String todayStr = sdf.format(new Date()); + stringBuilder.append(todayStr).append(separator); + stringBuilder.append(filename); + return stringBuilder.toString(); + } + + /** + * 上传图片文件 + * + * @param prefix 文件前缀 + * @param filename 文件名 + * @param inputStream 文件流 + * @return 文件全路径 + */ + public String uploadImg(String prefix, String filename, InputStream inputStream) { + String filePath = builderFilePath(prefix, filename); + try { + PutObjectArgs putObjectArgs = PutObjectArgs.builder() + .object(filePath) + .contentType("image/jpg") + .bucket(minioConfig.getBucketName()).stream(inputStream, inputStream.available(), -1) + .build(); + minioClient.putObject(putObjectArgs); + StringBuilder urlPath = new StringBuilder(minioConfig.getReadPath()); + urlPath.append(separator + minioConfig.getBucketName()); + urlPath.append(separator); + urlPath.append(filePath); + return urlPath.toString(); + } catch (Exception ex) { + log.error("minio put file error.", ex); + throw new RuntimeException("上传文件失败"); + } + } + + /** + * 上传html文件 + * + * @param prefix 文件前缀 + * @param filename 文件名 + * @param inputStream 文件流 + * @return 文件全路径 + */ + public String uploadHtml(String prefix, String filename, InputStream inputStream) { + String filePath = builderFilePath(prefix, filename); + try { + PutObjectArgs putObjectArgs = PutObjectArgs.builder() + .object(filePath) + .contentType("text/html") + .bucket(minioConfig.getBucketName()) + .stream(inputStream, inputStream.available(), -1) + .build(); + minioClient.putObject(putObjectArgs); + StringBuilder urlPath = new StringBuilder(minioConfig.getReadPath()); + urlPath.append(separator + minioConfig.getBucketName()); + urlPath.append(separator); + urlPath.append(filePath); + return urlPath.toString(); + } catch (Exception ex) { + log.error("minio put file error.", ex); + ex.printStackTrace(); + throw new RuntimeException("上传文件失败"); + } + } + + /** + * 防盗链接有效期按分钟 + * + * @param filePath 文件前缀 + * @param expiry 超时分钟 + * @return 文件全路径 + */ + public String getPresignObjectUrl(String filePath, Integer expiry) { + try { + filePath = filePath.replace("http://health.reglory.com.cn:9001/health-bucket/", ""); + GetPresignedObjectUrlArgs putObjectArgs = GetPresignedObjectUrlArgs.builder() + .object(filePath) + .bucket(minioConfig.getBucketName()) + .method(Method.GET) +// .region("health.reglory.com.cn:9001") + .expiry(expiry, TimeUnit.MINUTES) + .build(); + return minioClient.getPresignedObjectUrl(putObjectArgs); + } catch (Exception ex) { + log.error("minio put file error.", ex); + ex.printStackTrace(); + throw new RuntimeException("上传文件失败"); + } + } + + /** + * 删除文件 + * + * @param pathUrl 文件全路径 + */ + public void delete(String pathUrl) { + String key = pathUrl.replace(minioConfig.getEndpoint() + "/", ""); + int index = key.indexOf(separator); + String bucket = key.substring(0, index); + String filePath = key.substring(index + 1); + // 删除Objects + RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build(); + try { + minioClient.removeObject(removeObjectArgs); + } catch (Exception e) { + log.error("minio remove file error. pathUrl:{}", pathUrl); + e.printStackTrace(); + } + } + + + /** + * 下载文件 + * + * @param pathUrl 文件全路径 + * @return 文件流 + */ + public byte[] downLoadFile(String pathUrl) { + String key = pathUrl.replace(minioConfig.getEndpoint() + "/", ""); + int index = key.indexOf(separator); + //String bucket = key.substring(0, index); + String filePath = key.substring(index + 1); + InputStream inputStream = null; + try { + inputStream = minioClient.getObject( + GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(filePath).build() + ); + } catch (Exception e) { + log.error("minio down file error. pathUrl:{}", pathUrl); + e.printStackTrace(); + } + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + byte[] buff = new byte[100]; + int rc = 0; + while (true) { + try { + if (!((rc = inputStream.read(buff, 0, 100)) > 0)) break; + } catch (IOException e) { + e.printStackTrace(); + } + byteArrayOutputStream.write(buff, 0, rc); + } + return byteArrayOutputStream.toByteArray(); + } +} diff --git a/templates/21.SaTokenConfig.java.j2 b/templates/21.SaTokenConfig.java.j2 new file mode 100644 index 0000000..ad4a759 --- /dev/null +++ b/templates/21.SaTokenConfig.java.j2 @@ -0,0 +1,44 @@ +package {{ package.Common }}.config; + +import cn.dev33.satoken.interceptor.SaInterceptor; +import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.stp.StpUtil; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import jakarta.annotation.Resource; +import java.util.List; + +/** + * SaToken配置 + * @Author: {{author}} + * @Date: {{date}} + * @Wechat: {{ wechat }} + * 解决@RequestAttribute、@RequestParam和@RequestBody三种类型的时间类型参数接收与转换问题 + */ +@Configuration +public class SaTokenConfig implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + + // 注册 Sa-Token 拦截器,定义详细认证规则 + registry.addInterceptor(new SaInterceptor(handler -> { + // 指定一条 match 规则 + SaRouter + .match("/models/**") // 拦截的 path 列表,可以写多个 */ + // .notMatch("/models/health-user/login") // 排除掉的 path 列表,可以写多个 + .check(r -> StpUtil.checkLogin()); // 要执行的校验动作,可以写完整的 lambda 表达式 + + //拦截并写操作日志 + // SaRouter.match(r).check(t->{ + // SaLog.info("操作日志:{}", SaLog.getParamJson()); + // SaLog.info("操作日志:{}", SaLog.getRequestBody()); + // ); + + })).addPathPatterns("/**"); + } + + +} diff --git a/templates/21.SwaggerConfig.java.j2 b/templates/21.SwaggerConfig.java.j2 new file mode 100644 index 0000000..d64b800 --- /dev/null +++ b/templates/21.SwaggerConfig.java.j2 @@ -0,0 +1,45 @@ +package {{ package.Common }}.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Swagger3 / OpenAPI 配置 + * + * @Author: snail + * @Date: 2026-02-06 + * @Wechat: ot_bus + * + * 访问地址: + * http://localhost:port/swagger-ui/index.html + */ +@Configuration +public class SwaggerConfig { + + @Value("${swagger.show:true}") + private boolean swaggerShow; + + @Bean + public OpenAPI openAPI() { + if (!swaggerShow) { + // 生产环境关闭文档 + return new OpenAPI(); + } + + return new OpenAPI() + .info(new Info() + .title("数据中心接口文档") + .description("数据中心相关接口文档") + .version("1.0") + .contact(new Contact() + .name("snail") + .url("https://www.deepseek.com/") + .email("") + ) + ); + } +} diff --git a/templates/21.controller.java.j2 b/templates/21.controller.java.j2 new file mode 100644 index 0000000..4a617d0 --- /dev/null +++ b/templates/21.controller.java.j2 @@ -0,0 +1,107 @@ +package {{ package.Controller }}; +import {{ package.Entity }}.{{ table.entity }}; +import {{ package.Service }}.{{ table.entity }}Service; +import {{ package.Common }}.vo.Result; + +//--- import 固定引入 ---// +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; +//--- import 固定引入 ---// + +/** + *
+ * {{ table.comment }} 前端控制器 + *
+ * @Author: {{author}} + * @Date: {{date}} + * @Wechat: {{ wechat }} + */ +@Api(tags = "{{ table.comment }}") +{% if restControllerStyle %} +@RestController +{% else %} +@Controller +{% endif %} +@RequestMapping("{{ table.name }}") +public class {{ table.entity }}Controller { + + @Resource + private {{ table.entity }}Service {{table.lowerEntity}}Service; + + @ApiOperation(value = "{{ table.comment }}分页列表查询", response = {{ table.entity }}.class) + @PostMapping(value = "/page") + public Result