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());
}
}