代码生产工具

package com.gene.tianShu.common.util;

import cn.hutool.core.util.StrUtil;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.*;
import java.util.*;

public class CodeGenerator {

    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("MySQL JDBC Driver not found!", e);
        }
    }

    // ==================== 配置区 ====================
    private static final String PACKAGE_NAME = "com.gene.tianShu.common"; // 根包路径
    private static final String DATABASE_NAME = "code_gene_flowable"; // 替换为你的数据库名
    private static final String TABLE_NAME = "gene_flow_user,gene_flow_type"; // 表名以 ,分割,不填则生成所有数据表
    private static final String JDBC_URL = "jdbc:mysql://127.0.0.1:3306/" + DATABASE_NAME +
            "?useSSL=false" +
            "&useUnicode=true" +
            "&characterEncoding=utf-8" +
            "&zeroDateTimeBehavior=convertToNull" +
            "&transformedBitIsBoolean=true" +
            "&serverTimezone=Asia/Shanghai" +
            "&allowPublicKeyRetrieval=true";
    private static final String JDBC_USERNAME = "root";
    private static final String JDBC_PASSWORD = "root";

    // ==================== 模板区 ====================

    // Result<T>
    private static final String RESULT_TEMPLATE =
            "package {package}.result;\n\n" +
                    "import lombok.Data;\n\n" +
                    "import java.io.Serializable;\n\n" +
                    "/**\n" +
                    " * 统一响应结果封装\n" +
                    " */\n" +
                    "@Data\n" +
                    "public class Result<T> implements Serializable {\n" +
                    "    private static final long serialVersionUID = 1L;\n\n" +
                    "    private Integer code;           // 状态码:200 成功,400 错误,500 异常\n" +
                    "    private String message;         // 提示信息\n" +
                    "    private T data;                 // 返回数据\n" +
                    "    private Long timestamp;         // 时间戳\n\n" +
                    "    public Result() {\n" +
                    "        this.timestamp = System.currentTimeMillis();\n" +
                    "    }\n\n" +
                    "    public static <T> Result<T> success() {\n" +
                    "        return success(null);\n" +
                    "    }\n\n" +
                    "    public static <T> Result<T> success(T data) {\n" +
                    "        Result<T> result = new Result<>();\n" +
                    "        result.code = 200;\n" +
                    "        result.message = \"操作成功\";\n" +
                    "        result.data = data;\n" +
                    "        return result;\n" +
                    "    }\n\n" +
                    "    public static <T> Result<T> error(String message) {\n" +
                    "        Result<T> result = new Result<>();\n" +
                    "        result.code = 400;\n" +
                    "        result.message = message;\n" +
                    "        return result;\n" +
                    "    }\n\n" +
                    "    public static <T> Result<T> error(Integer code, String message) {\n" +
                    "        Result<T> result = new Result<>();\n" +
                    "        result.code = code;\n" +
                    "        result.message = message;\n" +
                    "        return result;\n" +
                    "    }\n" +
                    "}\n";

    // 全局异常处理器(保留日志,便于运维)
    private static final String GLOBAL_EXCEPTION_HANDLER_TEMPLATE =
            "package {package}.exception;\n\n" +
                    "import cn.hutool.core.util.StrUtil;\n" +
                    "import com.gene.tianShu.common.result.Result;\n" +
                    "import lombok.extern.slf4j.Slf4j;\n" +
                    "import org.springframework.validation.BindException;\n" +
                    "import org.springframework.validation.FieldError;\n" +
                    "import org.springframework.web.bind.MethodArgumentNotValidException;\n" +
                    "import org.springframework.web.bind.annotation.ExceptionHandler;\n" +
                    "import org.springframework.web.bind.annotation.RestControllerAdvice;\n\n" +
                    "import javax.validation.ConstraintViolation;\n" +
                    "import javax.validation.ConstraintViolationException;\n" +
                    "import java.util.Set;\n\n" +
                    "/**\n" +
                    " * 全局异常处理器\n" +
                    " * 统一返回 Result<T> 格式\n" +
                    " */\n" +
                    "@Slf4j\n" +
                    "@RestControllerAdvice\n" +
                    "public class GlobalExceptionHandler {{\n\n" +
                    "    /**\n" +
                    "     * 参数校验失败(@Valid 注解触发)\n" +
                    "     */\n" +
                    "    @ExceptionHandler(MethodArgumentNotValidException.class)\n" +
                    "    public Result<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {{\n" +
                    "        FieldError fieldError = e.getBindingResult().getFieldError();\n" +
                    "        String message = fieldError != null ? fieldError.getDefaultMessage() : \"参数校验失败\";\n" +
                    "        // log.warn(\"参数校验失败: {{}}\", message); // 已清除\n" +
                    "        return Result.error(400, message);\n" +
                    "    }}\n\n" +
                    "    /**\n" +
                    "     * JSR-303 参数校验失败(如 @RequestParam @Validated)\n" +
                    "     */\n" +
                    "    @ExceptionHandler(ConstraintViolationException.class)\n" +
                    "    public Result<?> handleConstraintViolationException(ConstraintViolationException e) {{\n" +
                    "        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();\n" +
                    "        String message = violations.stream()\n" +
                    "                .map(ConstraintViolation::getMessage)\n" +
                    "                .findFirst()\n" +
                    "                .orElse(\"参数校验失败\");\n" +
                    "        // log.warn(\"参数校验失败: {{}}\", message); // 已清除\n" +
                    "        return Result.error(400, message);\n" +
                    "    }}\n\n" +
                    "    /**\n" +
                    "     * 业务逻辑异常(如 throw new IllegalArgumentException(...))\n" +
                    "     */\n" +
                    "    @ExceptionHandler(IllegalArgumentException.class)\n" +
                    "    public Result<?> handleIllegalArgumentException(IllegalArgumentException e) {{\n" +
                    "        String message = e.getMessage();\n" +
                    "        if (StrUtil.isBlank(message)) message = \"参数错误\";\n" +
                    "        // log.warn(\"业务参数错误: {{}}\", message); // 已清除\n" +
                    "        return Result.error(400, message);\n" +
                    "    }}\n\n" +
                    "    /**\n" +
                    "     * 通用异常(500)\n" +
                    "     */\n" +
                    "    @ExceptionHandler(Exception.class)\n" +
                    "    public Result<?> handleException(Exception e) {{\n" +
                    "        String message = \"服务器内部错误\";\n" +
                    "        // log.error(\"系统异常\", e); // 已清除\n" +
                    "        return Result.error(500, message);\n" +
                    "    }}\n" +
                    "}}\n";

    // Entity(关键修改:使用 ASSIGN_ID)
    private static final String ENTITY_TEMPLATE =
            "package {package}.entity;\n\n" +
                    "import com.baomidou.mybatisplus.annotation.*;\n" +
                    "import com.baomidou.mybatisplus.annotation.TableName;\n" +
                    "import io.swagger.annotations.ApiModel;\n" +
                    "import io.swagger.annotations.ApiModelProperty;\n" +
                    "import lombok.AllArgsConstructor;\n" +
                    "import lombok.Builder;\n" +
                    "import lombok.Data;\n" +
                    "import lombok.NoArgsConstructor;\n" +
                    "import com.fasterxml.jackson.annotation.JsonFormat;\n\n" +
                    "import java.time.LocalDateTime;\n\n" +
                    "@TableName(\"{tableName}\")\n" +
                    "@ApiModel(value = \"{className}\", description = \"{classComment}\")\n" +
                    "@Data\n" +
                    "@Builder\n" +
                    "@NoArgsConstructor\n" +
                    "@AllArgsConstructor\n" +
                    "public class {className} {\n\n" +
                    "    @TableId(value = \"id\", type = IdType.ASSIGN_ID) // ✅ 使用雪花算法生成 ID\n" +
                    "    @ApiModelProperty(value = \"主键\", example = \"1687654321098765432\", notes = \"分布式雪花算法生成,64位Long\")\n" +
                    "    private Long id;\n" +
                    "{fields}\n" +
                    "    @TableField(value = \"{createTimeColumn}\", fill = FieldFill.INSERT)\n" +
                    "    @ApiModelProperty(value = \"创建时间\", example = \"2025-04-01 10:00:00\", notes = \"记录创建时间\")\n" +
                    "    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n" +
                    "    private LocalDateTime createTime;\n\n" +
                    "    @TableField(value = \"{updateTimeColumn}\", fill = FieldFill.INSERT_UPDATE)\n" +
                    "    @ApiModelProperty(value = \"更新时间\", example = \"2025-04-02 15:30:00\", notes = \"记录最后更新时间\")\n" +
                    "    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n" +
                    "    private LocalDateTime updateTime;\n\n" +
                    "    @TableLogic(value = \"{deletedColumn}\")\n" +
                    "    @ApiModelProperty(value = \"逻辑删除\", example = \"0\", notes = \"0:未删除, 1:已删除\")\n" +
                    "    private Integer deleted;\n" +
                    "}\n";

    // VO
    private static final String VO_TEMPLATE =
            "package {package}.vo;\n\n" +
                    "import io.swagger.annotations.ApiModel;\n" +
                    "import io.swagger.annotations.ApiModelProperty;\n" +
                    "import lombok.AllArgsConstructor;\n" +
                    "import lombok.Builder;\n" +
                    "import lombok.Data;\n" +
                    "import lombok.NoArgsConstructor;\n" +
                    "import com.fasterxml.jackson.annotation.JsonFormat;\n\n" +
                    "@ApiModel(value = \"{className}\", description = \"{classComment}\")\n" +
                    "@Data\n" +
                    "@Builder\n" +
                    "@NoArgsConstructor\n" +
                    "@AllArgsConstructor\n" +
                    "public class {className} {\n" +
                    "{fields}\n" +
                    "}\n";

    // DTO
    private static final String DTO_TEMPLATE =
            "package {package}.dto;\n\n" +
                    "import io.swagger.annotations.ApiModel;\n" +
                    "import io.swagger.annotations.ApiModelProperty;\n" +
                    "import lombok.AllArgsConstructor;\n" +
                    "import lombok.Builder;\n" +
                    "import lombok.Data;\n" +
                    "import lombok.NoArgsConstructor;\n" +
                    "import com.fasterxml.jackson.annotation.JsonFormat;\n\n" +
                    "import java.time.LocalDateTime;\n\n" +
                    "@ApiModel(value = \"{className}\", description = \"{classComment}\")\n" +
                    "@Data\n" +
                    "@Builder\n" +
                    "@NoArgsConstructor\n" +
                    "@AllArgsConstructor\n" +
                    "public class {className} {\n" +
                    "{fields}\n" +
                    "}\n";

    // Mapper
    private static final String MAPPER_TEMPLATE =
            "package {package}.mapper;\n\n" +
                    "import com.baomidou.mybatisplus.core.mapper.BaseMapper;\n" +
                    "{package}.entity.{className};\n" +
                    "{package}.dto.{className}DTO;\n" +
                    "{package}.vo.{className}VO;\n" +
                    "import org.apache.ibatis.annotations.Mapper;\n\n" +
                    "import java.util.List;\n\n" +
                    "@Mapper\n" +
                    "public interface {className}Mapper extends BaseMapper<{className}> {\n\n" +
                    "    List<{className}DTO> selectByVO({className}VO vo);\n\n" +
                    "    List<{className}DTO> selectByVOWithPage({className}VO vo, int offset, int limit);\n\n" +
                    "    boolean insertBatch(List<{className}> list);\n" +
                    "}\n";

    // Service
    private static final String SERVICE_TEMPLATE =
            "package {package}.service;\n\n" +
                    "import com.baomidou.mybatisplus.extension.service.IService;\n" +
                    "{package}.entity.{className};\n" +
                    "{package}.dto.{className}DTO;\n" +
                    "{package}.vo.{className}VO;\n\n" +
                    "import java.util.List;\n\n" +
                    "public interface {className}Service extends IService<{className}> {{\n\n" +
                    "    {className}DTO getByIdAsDTO(Long id);\n\n" +
                    "    List<{className}DTO> listAsDTO();\n\n" +
                    "    boolean saveOrUpdateVO({className}VO vo); // ✅ 新:无需 ID,传完整 VO\n" +
                    "    boolean deleteByIdVO(Long id);\n\n" +
                    "    List<{className}DTO> queryByVO({className}VO vo);\n\n" +
                    "    List<{className}DTO> queryByVOWithPage({className}VO vo, int page, int size);\n\n" +
                    "    boolean saveBatchVO(List<{className}VO> vos);\n" +
                    "}}\n";

    // ServiceImpl
    private static final String SERVICE_IMPL_TEMPLATE =
            "package {package}.service.impl;\n\n" +
                    "import com.baomidou.mybatisplus.core.metadata.IPage;\n" +
                    "import com.baomidou.mybatisplus.extension.plugins.pagination.Page;\n" +
                    "import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\n" +
                    "{package}.dto.{className}DTO;\n" +
                    "{package}.entity.{className};\n" +
                    "{package}.mapper.{className}Mapper;\n" +
                    "{package}.service.{className}Service;\n" +
                    "{package}.vo.{className}VO;\n" +
                    "import cn.hutool.core.bean.BeanUtil;\n" +
                    "import lombok.extern.slf4j.Slf4j;\n\n" +
                    "import org.springframework.stereotype.Service;\n" +
                    "import org.springframework.util.CollectionUtils;\n\n" +
                    "import java.util.List;\n" +
                    "import java.util.stream.Collectors;\n\n" +
                    "@Slf4j\n" +
                    "@Service\n" +
                    "public class {className}ServiceImpl extends ServiceImpl<{className}Mapper, {className}> implements {className}Service {{\n\n" +

                    "    @Override\n" +
                    "    public {className}DTO getByIdAsDTO(Long id) {{\n" +
                    "        {className} entity = this.getById(id);\n" +
                    "        if (entity == null) {{\n" +
                    "            return null;\n" +
                    "        }}\n" +
                    "        return BeanUtil.copyProperties(entity, {className}DTO.class);\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public List<{className}DTO> listAsDTO() {{\n" +
                    "        List<{className}> list = this.list();\n" +
                    "        return BeanUtil.copyToList(list, {className}DTO.class);\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public boolean saveOrUpdateVO({className}VO vo) {{\n" +
                    "        {className} entity = new {className}();\n" +
                    "        BeanUtil.copyProperties(vo, entity); // 自动映射,id 为 null 时插入,非 null 时更新\n" +
                    "        boolean result = this.saveOrUpdate(entity);\n" +
                    "        return result;\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public boolean deleteByIdVO(Long id) {{\n" +
                    "        return this.removeById(id);\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public List<{className}DTO> queryByVO({className}VO vo) {{\n" +
                    "        return baseMapper.selectByVO(vo);\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public List<{className}DTO> queryByVOWithPage({className}VO vo, int page, int size) {{\n" +
                    "        int offset = (page - 1) * size;\n" +
                    "        return baseMapper.selectByVOWithPage(vo, offset, size);\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public boolean saveBatchVO(List<{className}VO> vos) {{\n" +
                    "        if (CollectionUtils.isEmpty(vos)) {{\n" +
                    "            return false;\n" +
                    "        }}\n" +
                    "        List<{className}> entities = BeanUtil.copyToList(vos, {className}.class);\n" +
                    "        return baseMapper.insertBatch(entities);\n" +
                    "    }}\n\n" +

                    "    private {className}DTO convertToDTO({className} entity) {{\n" +
                    "        if (entity == null) {{\n" +
                    "            return null;\n" +
                    "        }}\n" +
                    "        return BeanUtil.copyProperties(entity, {className}DTO.class);\n" +
                    "    }}\n" +
                    "}}\n";

    // Controller
    private static final String CONTROLLER_TEMPLATE =
            "package {package}.controller;\n\n" +
                    "import {package}.result.Result;\n" +
                    "import {package}.dto.{className}DTO;\n" +
                    "import {package}.service.{className}Service;\n" +
                    "import {package}.vo.{className}VO;\n" +
                    "import io.swagger.annotations.Api;\n" +
                    "import io.swagger.annotations.ApiOperation;\n" +
                    "import io.swagger.annotations.ApiResponse;\n" +
                    "import io.swagger.annotations.ApiResponses;\n" +
                    "import lombok.extern.slf4j.Slf4j;\n\n" +
                    "import org.springframework.beans.factory.annotation.Autowired;\n" +
                    "import org.springframework.web.bind.annotation.*;\n\n" +
                    "import java.util.List;\n\n" +
                    "@Api(tags = \"{classTag}\")\n" +
                    "@RestController\n" +
                    "@RequestMapping(\"/{snakeCase}\")\n" +
                    "@Slf4j\n" +
                    "public class {className}Controller {{\n\n" +
                    "    @Autowired\n" +
                    "    private {className}Service {lowerFirst}Service;\n\n" +

                    "    @ApiResponses({{\n" +
                    "        @ApiResponse(code = 200, message = \"成功\", response = {className}DTO.class),\n" +
                    "        @ApiResponse(code = 400, message = \"参数错误\"),\n" +
                    "        @ApiResponse(code = 500, message = \"服务器内部错误\")\n" +
                    "    }})\n" +
                    "    @ApiOperation(value = \"根据ID查询{className}\", notes = \"根据{className}ID查询{className}详细信息\", response = Result.class)\n" +
                    "    @GetMapping(\"/{id}\")\n" +
                    "    public Result<{className}DTO> get(@PathVariable Long id) {{\n" +
                    "        {className}DTO data = {lowerFirst}Service.getByIdAsDTO(id);\n" +
                    "        return Result.success(data);\n" +
                    "    }}\n\n" +

                    "    @ApiResponses({{\n" +
                    "        @ApiResponse(code = 200, message = \"成功\", response = Boolean.class),\n" +
                    "        @ApiResponse(code = 400, message = \"参数错误\"),\n" +
                    "        @ApiResponse(code = 500, message = \"服务器内部错误\")\n" +
                    "    }})\n" +
                    "    @ApiOperation(value = \"创建或更新{className}\", notes = \"传入完整实体,ID 为 null 时插入,非 null 时更新\", response = Result.class)\n" +
                    "    @PostMapping\n" +
                    "    public Result<Boolean> save(@RequestBody {className}VO vo) {{\n" +
                    "        boolean result = {lowerFirst}Service.saveOrUpdateVO(vo);\n" +
                    "        return Result.success(result);\n" +
                    "    }}\n\n" +

                    "    @ApiResponses({{\n" +
                    "        @ApiResponse(code = 200, message = \"成功\", response = Boolean.class),\n" +
                    "        @ApiResponse(code = 400, message = \"参数错误\"),\n" +
                    "        @ApiResponse(code = 500, message = \"服务器内部错误\")\n" +
                    "    }})\n" +
                    "    @ApiOperation(value = \"批量创建或更新{className}\", notes = \"批量传入实体列表,ID 为 null 时插入,非 null 时更新\", response = Result.class)\n" +
                    "    @PostMapping(\"/batch\")\n" +
                    "    public Result<Boolean> saveBatch(@RequestBody List<{className}VO> vos) {{\n" +
                    "        boolean result = {lowerFirst}Service.saveBatchVO(vos);\n" +
                    "        return Result.success(result);\n" +
                    "    }}\n\n" +

                    "    @ApiResponses({{\n" +
                    "        @ApiResponse(code = 200, message = \"成功\", response = Boolean.class),\n" +
                    "        @ApiResponse(code = 400, message = \"参数错误\"),\n" +
                    "        @ApiResponse(code = 500, message = \"服务器内部错误\")\n" +
                    "    }})\n" +
                    "    @ApiOperation(value = \"创建或更新{className}\", notes = \"传入完整实体,ID 为 null 时插入,非 null 时更新\", response = Result.class)\n" +
                    "    @PutMapping\n" +
                    "    public Result<Boolean> update(@RequestBody {className}VO vo) {{\n" +
                    "        boolean result = {lowerFirst}Service.saveOrUpdateVO(vo);\n" +
                    "        return Result.success(result);\n" +
                    "    }}\n\n" +

                    "    @ApiResponses({{\n" +
                    "        @ApiResponse(code = 200, message = \"成功\", response = Boolean.class),\n" +
                    "        @ApiResponse(code = 400, message = \"参数错误\"),\n" +
                    "        @ApiResponse(code = 500, message = \"服务器内部错误\")\n" +
                    "    }})\n" +
                    "    @ApiOperation(value = \"删除{className}\", notes = \"根据ID逻辑删除{className}\", response = Result.class)\n" +
                    "    @DeleteMapping(\"/{id}\")\n" +
                    "    public Result<Boolean> delete(@PathVariable Long id) {{\n" +
                    "        boolean result = {lowerFirst}Service.deleteByIdVO(id);\n" +
                    "        return Result.success(result);\n" +
                    "    }}\n\n" +

                    "    @ApiResponses({{\n" +
                    "        @ApiResponse(code = 200, message = \"成功\", response = {className}DTO.class, responseContainer = \"List\"),\n" +
                    "        @ApiResponse(code = 400, message = \"参数错误\"),\n" +
                    "        @ApiResponse(code = 500, message = \"服务器内部错误\")\n" +
                    "    }})\n" +
                    "    @ApiOperation(value = \"查询所有{className}\", notes = \"获取所有未删除的{className}列表\", response = Result.class, responseContainer = \"List\")\n" +
                    "    @GetMapping(\"/list\")\n" +
                    "    public Result<List<{className}DTO>> list() {{\n" +
                    "        List<{className}DTO> data = {lowerFirst}Service.listAsDTO();\n" +
                    "        return Result.success(data);\n" +
                    "    }}\n\n" +

                    "    @ApiResponses({{\n" +
                    "        @ApiResponse(code = 200, message = \"成功\", response = {className}DTO.class, responseContainer = \"List\"),\n" +
                    "        @ApiResponse(code = 400, message = \"参数错误\"),\n" +
                    "        @ApiResponse(code = 500, message = \"服务器内部错误\")\n" +
                    "    }})\n" +
                    "    @ApiOperation(value = \"条件查询{className}\", notes = \"根据条件(如姓名、邮箱)模糊查询{className}\", response = Result.class, responseContainer = \"List\")\n" +
                    "    @PostMapping(\"/search\")\n" +
                    "    public Result<List<{className}DTO>> search(@RequestBody {className}VO vo) {{\n" +
                    "        List<{className}DTO> data = {lowerFirst}Service.queryByVO(vo);\n" +
                    "        return Result.success(data);\n" +
                    "    }}\n\n" +

                    "    @ApiResponses({{\n" +
                    "        @ApiResponse(code = 200, message = \"成功\", response = {className}DTO.class, responseContainer = \"List\"),\n" +
                    "        @ApiResponse(code = 400, message = \"参数错误\"),\n" +
                    "        @ApiResponse(code = 500, message = \"服务器内部错误\")\n" +
                    "    }})\n" +
                    "    @ApiOperation(value = \"分页查询{className}\", notes = \"根据条件分页查询{className}列表\", response = Result.class, responseContainer = \"List\")\n" +
                    "    @PostMapping(\"/search/page\")\n" +
                    "    public Result<List<{className}DTO>> searchWithPage(@RequestBody {className}VO vo,\n" +
                    "                                   @RequestParam(defaultValue = \"1\") int page,\n" +
                    "                                   @RequestParam(defaultValue = \"10\") int size) {{\n" +
                    "        List<{className}DTO> data = {lowerFirst}Service.queryByVOWithPage(vo, page, size);\n" +
                    "        return Result.success(data);\n" +
                    "    }}\n" +
                    "}}\n";

    // XML
    private static final String XML_TEMPLATE =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                    "<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n" +
                    "        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n" +
                    "<mapper namespace=\"{package}.mapper.{className}Mapper\">\n\n" +
                    "    <!-- VO 条件查询 -->\n" +
                    "    <select id=\"selectByVO\" resultType=\"{package}.dto.{className}DTO\">\n" +
                    "        SELECT id, name, email, create_time, update_time\n" +
                    "        FROM {tableName}\n" +
                    "        WHERE deleted = 0\n" +
                    "        <where>\n" +
                    "            <if test=\"vo.name != null and vo.name != ''\">\n" +
                    "                AND name LIKE CONCAT('%', #{vo.name}, '%')\n" +
                    "            </if>\n" +
                    "            <if test=\"vo.email != null and vo.email != ''\">\n" +
                    "                AND email LIKE CONCAT('%', #{vo.email}, '%')\n" +
                    "            </if>\n" +
                    "        </where>\n" +
                    "    </select>\n\n" +
                    "    <!-- VO 条件分页查询 -->\n" +
                    "    <select id=\"selectByVOWithPage\" resultType=\"{package}.dto.{className}DTO\">\n" +
                    "        SELECT id, name, email, create_time, update_time\n" +
                    "        FROM {tableName}\n" +
                    "        WHERE deleted = 0\n" +
                    "        <where>\n" +
                    "            <if test=\"vo.name != null and vo.name != ''\">\n" +
                    "                AND name LIKE CONCAT('%', #{vo.name}, '%')\n" +
                    "            </if>\n" +
                    "            <if test=\"vo.email != null and vo.email != ''\">\n" +
                    "                AND email LIKE CONCAT('%', #{vo.email}, '%')\n" +
                    "            </if>\n" +
                    "        </where>\n" +
                    "        LIMIT #{offset}, #{limit}\n" +
                    "    </select>\n\n" +
                    "    <!-- 批量插入 -->\n" +
                    "    <insert id=\"insertBatch\" parameterType=\"java.util.List\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n" +
                    "        INSERT INTO {tableName} (name, email, create_time, update_time, deleted)\n" +
                    "        VALUES\n" +
                    "        <foreach collection=\"list\" item=\"item\" separator=\",\">\n" +
                    "            (\n" +
                    "                #{item.name},\n" +
                    "                #{item.email},\n" +
                    "                NOW(),\n" +
                    "                NOW(),\n" +
                    "                0\n" +
                    "            )\n" +
                    "        </foreach>\n" +
                    "    </insert>\n\n" +
                    "</mapper>\n";

    // Feign Client
    private static final String FEIGN_CLIENT_TEMPLATE =
            "package {package}.feign;\n\n" +
                    "import {package}.dto.{className}DTO;\n" +
                    "import {package}.vo.{className}VO;\n" +
                    "import org.springframework.cloud.openfeign.FeignClient;\n" +
                    "import org.springframework.web.bind.annotation.*;\n\n" +
                    "import java.util.List;\n\n" +
                    "@FeignClient(name = \"{microserviceName}\", fallback = {className}FeignClientFallback.class)\n" +
                    "public interface {className}FeignClient {{\n\n" +
                    "    @GetMapping(\"/{id}\")\n" +
                    "    Result<{className}DTO> get(@PathVariable(\"id\") Long id);\n\n" +

                    "    @PostMapping\n" +
                    "    Result<Boolean> save(@RequestBody {className}VO vo);\n\n" +

                    "    @PostMapping(\"/batch\")\n" +
                    "    Result<Boolean> saveBatch(@RequestBody List<{className}VO> vos);\n\n" +

                    "    @PutMapping\n" +
                    "    Result<Boolean> update(@RequestBody {className}VO vo);\n\n" +

                    "    @DeleteMapping(\"/{id}\")\n" +
                    "    Result<Boolean> delete(@PathVariable(\"id\") Long id);\n\n" +

                    "    @GetMapping(\"/list\")\n" +
                    "    Result<List<{className}DTO>> list();\n\n" +
                    "    @PostMapping(\"/search\")\n" +
                    "    Result<List<{className}DTO>> search(@RequestBody {className}VO vo);\n\n" +
                    "    @PostMapping(\"/search/page\")\n" +
                    "    Result<List<{className}DTO>> searchWithPage(@RequestBody {className}VO vo,\n" +
                    "                           @RequestParam(defaultValue = \"1\") int page,\n" +
                    "                           @RequestParam(defaultValue = \"10\") int size);\n" +
                    "}}\n";

    // Feign Fallback
    private static final String FEIGN_FALLBACK_TEMPLATE =
            "package {package}.feign;\n\n" +
                    "import {package}.dto.{className}DTO;\n" +
                    "import {package}.vo.{className}VO;\n" +
                    "import lombok.extern.slf4j.Slf4j;\n\n" +
                    "import org.springframework.stereotype.Component;\n\n" +
                    "import java.util.List;\n\n" +
                    "@Slf4j\n" +
                    "@Component\n" +
                    "public class {className}FeignClientFallback implements {className}FeignClient {{\n\n" +
                    "    @Override\n" +
                    "    public Result<{className}DTO> get(Long id) {{\n" +
                    "        return Result.error(\"服务不可用\");\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public Result<Boolean> save({className}VO vo) {{\n" +
                    "        return Result.error(\"服务不可用\");\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public Result<Boolean> saveBatch(List<{className}VO> vos) {{\n" +
                    "        return Result.error(\"服务不可用\");\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public Result<Boolean> update({className}VO vo) {{ // ✅ 同步更新\n" +
                    "        return Result.error(\"服务不可用\");\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public Result<Boolean> delete(Long id) {{\n" +
                    "        return Result.error(\"服务不可用\");\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public Result<List<{className}DTO>> list() {{\n" +
                    "        return Result.error(\"服务不可用\");\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public Result<List<{className}DTO>> search({className}VO vo) {{\n" +
                    "        return Result.error(\"服务不可用\");\n" +
                    "    }}\n\n" +

                    "    @Override\n" +
                    "    public Result<List<{className}DTO>> searchWithPage({className}VO vo, int page, int size) {{\n" +
                    "        return Result.error(\"服务不可用\");\n" +
                    "    }}\n" +
                    "}}\n";

    // Feign Config
    private static final String FEIGN_CONFIG_TEMPLATE =
            "package {package}.feign.config;\n\n" +
                    "import feign.Logger;\n" +
                    "import lombok.extern.slf4j.Slf4j;\n\n" +
                    "import org.springframework.context.annotation.Bean;\n" +
                    "import org.springframework.context.annotation.Configuration;\n\n" +
                    "@Slf4j\n" +
                    "@Configuration\n" +
                    "public class {className}FeignConfiguration {{\n\n" +
                    "    @Bean\n" +
                    "    public Logger.Level feignLoggerLevel() {{\n" +
                    "        return Logger.Level.FULL;\n" +
                    "    }}\n" +
                    "}}\n";

    // ==================== 工具方法 ====================

    public static void main(String[] args) throws Exception {
        List<String> allTables = getTableNamesFromDatabase();
        if (allTables.isEmpty()) {
            System.out.println("❌ 未找到任何表,请检查数据库名和连接信息");
            return;
        }

        List<String> targetTables;

        if (StrUtil.isNotEmpty(TABLE_NAME)) {
            String[] split = TABLE_NAME.split(",");
            targetTables = new ArrayList<>();
            for (String arg : split) {
                String tableName = arg.toLowerCase().trim();
                if (!allTables.contains(tableName)) {
                    System.out.println("⚠️  警告:数据库中不存在表 \"" + tableName + "\",跳过生成");
                    continue;
                }
                targetTables.add(tableName);
                System.out.println("✅ 指定生成表: " + tableName);
            }
            if (targetTables.isEmpty()) {
                System.out.println("❌ 指定的表均不存在,退出生成");
                return;
            }
        } else {
            targetTables = allTables;
            System.out.println("✅ 未输入表名,将生成数据库中所有 " + allTables.size() + " 张表");
        }

        for (String tableName : targetTables) {
            System.out.println("🔧 正在生成表: " + tableName + " → 类名: " + toPascalCase(toCamelCase(tableName)));
            generateCodeForTable(tableName);
        }

        System.out.println("🎉 所有表代码生成完成!(使用 IdWorker 雪花主键 + 无 ID 传参模式)");
    }

    private static List<String> getTableNamesFromDatabase() throws SQLException {
        List<String> tables = new ArrayList<>();
        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USERNAME, JDBC_PASSWORD)) {
            String sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = ? AND table_type = 'BASE TABLE'";
            try (PreparedStatement ps = conn.prepareStatement(sql)) {
                ps.setString(1, DATABASE_NAME);
                try (ResultSet rs = ps.executeQuery()) {
                    while (rs.next()) {
                        tables.add(rs.getString("table_name").toLowerCase());
                    }
                }
            }
        }
        return tables;
    }

    private static List<TableColumn> getColumnsFromTable(String tableName) throws SQLException {
        List<TableColumn> columns = new ArrayList<>();
        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USERNAME, JDBC_PASSWORD)) {
            String sql = "SELECT column_name, data_type, is_nullable, column_key, column_comment " +
                    "FROM information_schema.columns WHERE table_schema = ? AND table_name = ? ORDER BY ordinal_position";
            try (PreparedStatement ps = conn.prepareStatement(sql)) {
                ps.setString(1, DATABASE_NAME);
                ps.setString(2, tableName);
                try (ResultSet rs = ps.executeQuery()) {
                    while (rs.next()) {
                        String columnName = rs.getString("column_name");
                        String dataType = rs.getString("data_type");
                        String isNullable = rs.getString("is_nullable");
                        String columnKey = rs.getString("column_key");
                        String comment = rs.getString("column_comment");

                        String javaType = mapJdbcTypeToJava(dataType);
                        String javaField = toCamelCase(columnName);

                        boolean isId = "PRI".equals(columnKey);

                        columns.add(new TableColumn(columnName, javaType, javaField, isId, comment));
                    }
                }
            }
        }
        return columns;
    }

    private static String mapJdbcTypeToJava(String jdbcType) {
        switch (jdbcType.toLowerCase()) {
            case "int": case "integer": case "mediumint": case "smallint": case "tinyint":
                return "Integer";
            case "bigint":
                return "Long";
            case "varchar": case "char": case "text": case "mediumtext": case "longtext":
                return "String";
            case "datetime": case "timestamp":
                return "LocalDateTime";
            case "decimal": case "numeric":
                return "BigDecimal";
            case "double": case "float":
                return "Double";
            case "boolean": case "bit":
                return "Boolean";
            default:
                return "String";
        }
    }

    private static String toCamelCase(String snakeCase) {
        StringBuilder result = new StringBuilder();
        boolean capitalizeNext = false;
        for (char c : snakeCase.toCharArray()) {
            if (c == '_') {
                capitalizeNext = true;
            } else {
                if (capitalizeNext) {
                    result.append(Character.toUpperCase(c));
                    capitalizeNext = false;
                } else {
                    result.append(c);
                }
            }
        }
        return result.toString();
    }

    private static String toPascalCase(String camelCase) {
        return Character.toUpperCase(camelCase.charAt(0)) + camelCase.substring(1);
    }

    private static String toUnderScoreCase(String camelCase) {
        StringBuilder result = new StringBuilder();
        for (char c : camelCase.toCharArray()) {
            if (Character.isUpperCase(c)) {
                result.append('_').append(Character.toLowerCase(c));
            } else {
                result.append(c);
            }
        }
        return result.length() > 0 ? result.toString().substring(1) : result.toString();
    }

    private static String toLowerFirst(String str) {
        return Character.toLowerCase(str.charAt(0)) + str.substring(1);
    }

    private static String getTableComment(String className) {
        String tableName = toUnderScoreCase(className);
        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USERNAME, JDBC_PASSWORD)) {
            String sql = "SELECT table_comment FROM information_schema.tables WHERE table_schema = ? AND table_name = ?";
            try (PreparedStatement ps = conn.prepareStatement(sql)) {
                ps.setString(1, DATABASE_NAME);
                ps.setString(2, tableName);
                try (ResultSet rs = ps.executeQuery()) {
                    if (rs.next()) {
                        return rs.getString("table_comment");
                    }
                }
            }
        } catch (SQLException e) {
            System.err.println("⚠️ 获取表 " + tableName + " 注释失败: " + e.getMessage());
        }
        return "";
    }

    private static String getClassTag(String className) {
        String tableName = toUnderScoreCase(className);
        switch (tableName) {
            case "flow_user":     return "用户管理";
            case "flow_task":     return "任务管理";
            case "flow_process":  return "流程管理";
            case "flow_notice":   return "通知管理";
            case "flow_log":      return "操作日志";
            case "flow_config":   return "配置管理";
            default:
                String[] parts = tableName.split("_");
                if (parts.length > 0) {
                    String lastPart = parts[parts.length - 1];
                    switch (lastPart) {
                        case "user": return "用户管理";
                        case "task": return "任务管理";
                        case "process": return "流程管理";
                        case "notice": return "通知管理";
                        case "log": return "操作日志";
                        case "config": return "配置管理";
                        case "item": return "项目管理";
                        case "order": return "订单管理";
                        default: return lastPart + "管理";
                    }
                }
                return className + "管理";
        }
    }

    private static String getExampleValue(String javaType) {
        if ("String".equals(javaType)) return "example_value";
        if ("Integer".equals(javaType) || "Long".equals(javaType)) return "1687654321098765432"; // ✅ 使用雪花 ID 示例
        if ("Boolean".equals(javaType)) return "true";
        if ("LocalDateTime".equals(javaType)) return "2025-04-01 10:00:00";
        if ("BigDecimal".equals(javaType)) return "100.50";
        if ("Double".equals(javaType)) return "99.99";
        return "example";
    }

    private static String escapeJavaString(String str) {
        if (str == null) return "";
        return str.replace("\\", "\\\\").replace("\"", "\\\"");
    }

    private static void generateCodeForTable(String tableName) throws IOException {
        if (tableName == null || tableName.trim().isEmpty()) {
            System.out.println("❌ 无效的表名:null 或空字符串");
            return;
        }

        String className = toPascalCase(toCamelCase(tableName));
        String packageName = PACKAGE_NAME;

        List<TableColumn> columns = null;
        try {
            columns = getColumnsFromTable(tableName);
        } catch (SQLException e) {
            System.err.println("❌ 获取表 " + tableName + " 字段失败: " + e.getMessage());
            return;
        }

        if (columns == null || columns.isEmpty()) {
            System.out.println("⚠️  表 " + tableName + " 无字段,跳过生成");
            return;
        }

        // ==================== 生成全局工具类(只生成一次)====================
        String resultPath = packageName + ".result";
        String resultFileName = "Result.java";
        File resultFile = new File("src/main/java/" + resultPath.replace('.', '/'), resultFileName);
        if (!resultFile.exists()) {
            String resultContent = generateResult();
            writeFile(resultPath, resultFileName, resultContent);
            System.out.println("✅ 生成统一响应类: " + resultPath + "." + resultFileName);
        }

        String exceptionPath = packageName + ".exception";
        String exceptionFileName = "GlobalExceptionHandler.java";
        File exceptionFile = new File("src/main/java/" + exceptionPath.replace('.', '/'), exceptionFileName);
        if (!exceptionFile.exists()) {
            String exceptionContent = generateGlobalExceptionHandler();
            writeFile(exceptionPath, exceptionFileName, exceptionContent);
            System.out.println("✅ 生成全局异常处理器: " + exceptionPath + "." + exceptionFileName);
        }

        // ==================== 生成业务代码 ====================
        String entityContent = generateEntity(className, tableName, columns);
        writeFile(packageName + ".entity", className + ".java", entityContent);

        String voContent = generateVO(className, columns);
        writeFile(packageName + ".vo", className + "VO.java", voContent);

        String dtoContent = generateDTO(className, columns);
        writeFile(packageName + ".dto", className + "DTO.java", dtoContent);

        String mapperContent = generateMapper(packageName, className);
        writeFile(packageName + ".mapper", className + "Mapper.java", mapperContent);

        String serviceContent = generateService(packageName, className);
        writeFile(packageName + ".service", className + "Service.java", serviceContent);

        String serviceImplContent = generateServiceImpl(packageName, className, columns);
        writeFile(packageName + ".service.impl", className + "ServiceImpl.java", serviceImplContent);

        String controllerContent = generateController(packageName, className);
        writeFile(packageName + ".controller", className + "Controller.java", controllerContent);

        String xmlContent = generateXML(packageName, className, tableName);
        writeFile("mapper", className + "Mapper.xml", xmlContent, true);

        String microserviceName = toUnderScoreCase(className).toLowerCase();
        String feignClientContent = generateFeignClient(packageName, className, microserviceName);
        writeFile(packageName + ".feign", className + "FeignClient.java", feignClientContent);

        String feignFallbackContent = generateFeignFallback(packageName, className);
        writeFile(packageName + ".feign", className + "FeignClientFallback.java", feignFallbackContent);

        String feignConfigContent = generateFeignConfig(packageName, className);
        writeFile(packageName + ".feign.config", className + "FeignConfiguration.java", feignConfigContent);

        System.out.println("✅ 完成生成表: " + tableName + "(使用 IdWorker 雪花主键)");
    }

    private static String generateResult() {
        return RESULT_TEMPLATE
                .replace("{package}", PACKAGE_NAME);
    }

    private static String generateGlobalExceptionHandler() {
        return GLOBAL_EXCEPTION_HANDLER_TEMPLATE
                .replace("{package}", PACKAGE_NAME);
    }

    private static String generateEntity(String className, String tableName, List<TableColumn> columns) {
        StringBuilder fields = new StringBuilder();
        String tableComment = getTableComment(className);
        String classComment = tableComment.isEmpty() ? className + " 实体" : tableComment;

        String createTimeColumn = "create_time";
        String updateTimeColumn = "update_time";
        String deletedColumn = "deleted";

        TableColumn createTimeCol = columns.stream()
                .filter(c -> "createTime".equals(c.getJavaField()))
                .findFirst().orElse(null);
        if (createTimeCol != null) {
            createTimeColumn = toUnderScoreCase(createTimeCol.getColumnName());
        }

        TableColumn updateTimeCol = columns.stream()
                .filter(c -> "updateTime".equals(c.getJavaField()))
                .findFirst().orElse(null);
        if (updateTimeCol != null) {
            updateTimeColumn = toUnderScoreCase(updateTimeCol.getColumnName());
        }

        TableColumn deletedCol = columns.stream()
                .filter(c -> "deleted".equals(c.getJavaField()))
                .findFirst().orElse(null);
        if (deletedCol != null) {
            deletedColumn = toUnderScoreCase(deletedCol.getColumnName());
        }

        for (TableColumn col : columns) {
            if (col.isId()) continue;
            if ("createTime".equals(col.getJavaField()) || "updateTime".equals(col.getJavaField()) || "deleted".equals(col.getJavaField())) continue;

            String comment = col.getComment().isEmpty() ? col.getJavaField() : col.getComment();
            String example = getExampleValue(col.getJavaType());
            String dbColumn = toUnderScoreCase(col.getColumnName());

            if ("LocalDateTime".equals(col.getJavaType())) {
                fields.append(String.format(
                        "    @TableField(value = \"%s\")\n" +
                                "    @ApiModelProperty(value = \"%s\", example = \"%s\", notes = \"%s\")\n" +
                                "    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n" +
                                "    private %s %s;\n",
                        dbColumn,
                        escapeJavaString(comment),
                        example,
                        escapeJavaString(comment),
                        col.getJavaType(),
                        col.getJavaField()
                ));
            } else {
                fields.append(String.format(
                        "    @TableField(value = \"%s\")\n" +
                                "    @ApiModelProperty(value = \"%s\", example = \"%s\", notes = \"%s\")\n" +
                                "    private %s %s;\n",
                        dbColumn,
                        escapeJavaString(comment),
                        example,
                        escapeJavaString(comment),
                        col.getJavaType(),
                        col.getJavaField()
                ));
            }
        }

        fields.append(
                String.format(
                        "    @TableField(value = \"%s\", fill = FieldFill.INSERT)\n" +
                                "    @ApiModelProperty(value = \"创建时间\", example = \"2025-04-01 10:00:00\", notes = \"记录创建时间\")\n" +
                                "    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n" +
                                "    private LocalDateTime createTime;\n\n",
                        createTimeColumn
                )
        );

        fields.append(
                String.format(
                        "    @TableField(value = \"%s\", fill = FieldFill.INSERT_UPDATE)\n" +
                                "    @ApiModelProperty(value = \"更新时间\", example = \"2025-04-02 15:30:00\", notes = \"记录最后更新时间\")\n" +
                                "    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n" +
                                "    private LocalDateTime updateTime;\n\n",
                        updateTimeColumn
                )
        );

        fields.append(
                String.format(
                        "    @TableLogic(value = \"%s\")\n" +
                                "    @ApiModelProperty(value = \"逻辑删除\", example = \"0\", notes = \"0:未删除, 1:已删除\")\n" +
                                "    private Integer deleted;\n",
                        deletedColumn
                )
        );

        return ENTITY_TEMPLATE
                .replace("{package}", PACKAGE_NAME)
                .replace("{className}", className)
                .replace("{tableName}", tableName)
                .replace("{classComment}", classComment)
                .replace("{createTimeColumn}", createTimeColumn)
                .replace("{updateTimeColumn}", updateTimeColumn)
                .replace("{deletedColumn}", deletedColumn)
                .replace("{fields}", fields.toString());
    }

    private static String generateVO(String className, List<TableColumn> columns) {
        StringBuilder fields = new StringBuilder();
        String tableComment = getTableComment(className);
        String classComment = tableComment.isEmpty() ? className + " VO" : tableComment;

        for (TableColumn col : columns) {
            if (col.isId()) continue;
            if ("createTime".equals(col.getJavaField()) || "updateTime".equals(col.getJavaField()) || "deleted".equals(col.getJavaField())) continue;

            String comment = col.getComment().isEmpty() ? col.getJavaField() : col.getComment();
            String example = getExampleValue(col.getJavaType());
            String dbColumn = toUnderScoreCase(col.getColumnName());

            if ("LocalDateTime".equals(col.getJavaType())) {
                fields.append(String.format(
                        "    @ApiModelProperty(value = \"%s\", example = \"%s\", notes = \"%s\")\n" +
                                "    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n" +
                                "    private %s %s;\n",
                        escapeJavaString(comment),
                        example,
                        escapeJavaString(comment),
                        col.getJavaType(),
                        col.getJavaField()
                ));
            } else {
                fields.append(String.format(
                        "    @ApiModelProperty(value = \"%s\", example = \"%s\", notes = \"%s\")\n" +
                                "    private %s %s;\n",
                        escapeJavaString(comment),
                        example,
                        escapeJavaString(comment),
                        col.getJavaType(),
                        col.getJavaField()
                ));
            }
        }

        fields.append(
                "    @ApiModelProperty(value = \"创建时间\", example = \"2025-04-01 10:00:00\", notes = \"记录创建时间\")\n" +
                        "    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n" +
                        "    private LocalDateTime createTime;\n\n"
        );

        fields.append(
                "    @ApiModelProperty(value = \"更新时间\", example = \"2025-04-02 15:30:00\", notes = \"记录最后更新时间\")\n" +
                        "    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n" +
                        "    private LocalDateTime updateTime;\n\n"
        );

        return VO_TEMPLATE
                .replace("{package}", PACKAGE_NAME)
                .replace("{className}", className + "VO")
                .replace("{classComment}", classComment)
                .replace("{fields}", fields.toString());
    }

    private static String generateDTO(String className, List<TableColumn> columns) {
        StringBuilder fields = new StringBuilder();
        String tableComment = getTableComment(className);
        String classComment = tableComment.isEmpty() ? className + " DTO" : tableComment;

        for (TableColumn col : columns) {
            String comment = "";
            String example = "";

            if ("createTime".equals(col.getJavaField())) {
                comment = "创建时间";
                example = "2025-04-01 10:00:00";
                fields.append(String.format(
                        "    @ApiModelProperty(value = \"%s\", example = \"%s\", notes = \"%s\")\n" +
                                "    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n" +
                                "    private %s %s;\n",
                        escapeJavaString(comment),
                        example,
                        escapeJavaString(comment),
                        col.getJavaType(),
                        col.getJavaField()
                ));
            } else if ("updateTime".equals(col.getJavaField())) {
                comment = "更新时间";
                example = "2025-04-02 15:30:00";
                fields.append(String.format(
                        "    @ApiModelProperty(value = \"%s\", example = \"%s\", notes = \"%s\")\n" +
                                "    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n" +
                                "    private %s %s;\n",
                        escapeJavaString(comment),
                        example,
                        escapeJavaString(comment),
                        col.getJavaType(),
                        col.getJavaField()
                ));
            } else if (!col.isId()) {
                comment = col.getComment().isEmpty() ? col.getJavaField() : col.getComment();
                example = getExampleValue(col.getJavaType());

                if ("LocalDateTime".equals(col.getJavaType())) {
                    fields.append(String.format(
                            "    @ApiModelProperty(value = \"%s\", example = \"%s\", notes = \"%s\")\n" +
                                    "    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n" +
                                    "    private %s %s;\n",
                            escapeJavaString(comment),
                            example,
                            escapeJavaString(comment),
                            col.getJavaType(),
                            col.getJavaField()
                    ));
                } else {
                    fields.append(String.format(
                            "    @ApiModelProperty(value = \"%s\", example = \"%s\", notes = \"%s\")\n" +
                                    "    private %s %s;\n",
                            escapeJavaString(comment),
                            example,
                            escapeJavaString(comment),
                            col.getJavaType(),
                            col.getJavaField()
                    ));
                }
            }
        }

        return DTO_TEMPLATE
                .replace("{package}", PACKAGE_NAME)
                .replace("{className}", className + "DTO")
                .replace("{classComment}", classComment)
                .replace("{fields}", fields.toString());
    }

    private static String generateMapper(String packageName, String className) {
        return MAPPER_TEMPLATE
                .replace("{package}", packageName)
                .replace("{className}", className);
    }

    private static String generateService(String packageName, String className) {
        return SERVICE_TEMPLATE
                .replace("{package}", packageName)
                .replace("{className}", className);
    }

    private static String generateServiceImpl(String packageName, String className, List<TableColumn> columns) {
        return SERVICE_IMPL_TEMPLATE
                .replace("{package}", packageName)
                .replace("{className}", className)
                .replace("\"{}\"", "\"" + className + "\"");
    }

    private static String generateController(String packageName, String className) {
        String snakeCase = toUnderScoreCase(className);
        String lowerFirst = toLowerFirst(className);
        String classTag = getClassTag(className);

        return CONTROLLER_TEMPLATE
                .replace("{package}", packageName)
                .replace("{className}", className)
                .replace("{snakeCase}", snakeCase)
                .replace("{lowerFirst}", lowerFirst)
                .replace("{classTag}", classTag)
                .replace("{builderFields}", getBuilderFields(className, snakeCase))
                .replace("\"{}\"", "\"" + className + "\"");
    }

    private static String generateXML(String packageName, String className, String tableName) {
        return XML_TEMPLATE
                .replace("{package}", packageName)
                .replace("{className}", className)
                .replace("{tableName}", tableName);
    }

    private static String generateFeignClient(String packageName, String className, String microserviceName) {
        return FEIGN_CLIENT_TEMPLATE
                .replace("{package}", packageName)
                .replace("{className}", className)
                .replace("{microserviceName}", microserviceName);
    }

    private static String generateFeignFallback(String packageName, String className) {
        return FEIGN_FALLBACK_TEMPLATE
                .replace("{package}", packageName)
                .replace("{className}", className)
                .replace("\"{}\"", "\"" + className + "\"");
    }

    private static String generateFeignConfig(String packageName, String className) {
        return FEIGN_CONFIG_TEMPLATE
                .replace("{package}", packageName)
                .replace("{className}", className);
    }

    private static String getBuilderFields(String className, String tableName) {
        if ("flow_user".equals(tableName)) {
            return "                        .name(vo.getName())\n" +
                    "                        .email(vo.getEmail())";
        }
        return "                        .name(vo.getName())\n" +
                "                        .email(vo.getEmail())";
    }

    private static class TableColumn {
        private String columnName;
        private String javaType;
        private String javaField;
        private boolean id;
        private String comment;

        public TableColumn(String columnName, String javaType, String javaField, boolean id, String comment) {
            this.columnName = columnName;
            this.javaType = javaType;
            this.javaField = javaField;
            this.id = id;
            this.comment = comment == null ? "" : comment;
        }

        public String getColumnName() { return columnName; }
        public String getJavaType() { return javaType; }
        public String getJavaField() { return javaField; }
        public boolean isId() { return id; }
        public String getComment() { return comment; }
    }

    private static void writeFile(String packagePath, String fileName, String content) throws IOException {
        writeFile(packagePath, fileName, content, false);
    }

    private static void writeFile(String packagePath, String fileName, String content, boolean isResource) throws IOException {
        String basePath = "src/main/java/";
        if (isResource) basePath = "src/main/resources/";

        String dirPath = basePath + packagePath.replace('.', '/');
        File dir = new File(dirPath);
        if (!dir.exists()) dir.mkdirs();

        File file = new File(dir, fileName);
        FileWriter writer = new FileWriter(file);
        writer.write(content);
        writer.close();
        System.out.println("✅ 生成文件: " + file.getAbsolutePath());
    }
}

 

posted @ 2026-06-13 15:34  chen1777  阅读(2)  评论(0)    收藏  举报