diff --git a/agri-admin/pom.xml b/agri-admin/pom.xml index 4374d8f..065a4ce 100644 --- a/agri-admin/pom.xml +++ b/agri-admin/pom.xml @@ -60,7 +60,11 @@ com.agri agri-generator - + + + com.agri + agri-api + diff --git a/agri-api/.mvn/wrapper/maven-wrapper.properties b/agri-api/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..8dea6c2 --- /dev/null +++ b/agri-api/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,3 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip diff --git a/agri-api/pom.xml b/agri-api/pom.xml new file mode 100644 index 0000000..ba5e754 --- /dev/null +++ b/agri-api/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + agri + com.agri + 3.9.0 + + agri-api + + + 三方接口接入模块 + + + + + + com.agri + agri-common + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.5.15 + + true + + + + + repackage + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.1.0 + + false + ${project.artifactId} + + + + ${project.artifactId} + + + diff --git a/agri-api/src/main/java/com/agri/AgriApiApplication.java b/agri-api/src/main/java/com/agri/AgriApiApplication.java new file mode 100644 index 0000000..8a49093 --- /dev/null +++ b/agri-api/src/main/java/com/agri/AgriApiApplication.java @@ -0,0 +1,13 @@ +package com.agri; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AgriApiApplication { + + public static void main(String[] args) { + SpringApplication.run(AgriApiApplication.class, args); + } + +} diff --git a/agri-api/src/main/resources/application.properties b/agri-api/src/main/resources/application.properties new file mode 100644 index 0000000..7d631cb --- /dev/null +++ b/agri-api/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=agri-api diff --git a/agri-generator/pom.xml b/agri-generator/pom.xml index 7783e22..802f2b5 100644 --- a/agri-generator/pom.xml +++ b/agri-generator/pom.xml @@ -35,6 +35,29 @@ druid-spring-boot-starter + + + org.springframework.boot + spring-boot-starter-freemarker + + + + com.baomidou + mybatis-plus-boot-starter + 3.5.3.1 + + + + + cn.hutool + hutool-all + 5.8.20 + + + org.projectlombok + lombok + provided + \ No newline at end of file diff --git a/agri-generator/src/main/java/com/agri/generator/controller/SysApiInfoController.java b/agri-generator/src/main/java/com/agri/generator/controller/SysApiInfoController.java new file mode 100644 index 0000000..90cbaab --- /dev/null +++ b/agri-generator/src/main/java/com/agri/generator/controller/SysApiInfoController.java @@ -0,0 +1,123 @@ +package com.agri.generator.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import com.agri.generator.dto.ApiConfigDTO; +import com.agri.generator.util.CodeGenerator; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.agri.common.annotation.Log; +import com.agri.common.core.controller.BaseController; +import com.agri.common.core.domain.AjaxResult; +import com.agri.common.enums.BusinessType; +import com.agri.generator.domain.SysApiInfo; +import com.agri.generator.service.ISysApiInfoService; +import com.agri.common.utils.poi.ExcelUtil; +import com.agri.common.core.page.TableDataInfo; + +/** + * 接口基础信息Controller + * + * @author agri + * @date 2025-12-30 + */ +@RestController +@RequestMapping("/tool/api") +public class SysApiInfoController extends BaseController +{ + @Autowired + private ISysApiInfoService sysApiInfoService; + + + @Autowired + private CodeGenerator codeGenerator; + /** + * 查询接口基础信息列表 + */ + @PreAuthorize("@ss.hasPermi('tool:api:list')") + @GetMapping("/list") + public TableDataInfo list(SysApiInfo sysApiInfo) + { + startPage(); + List list = sysApiInfoService.selectSysApiInfoList(sysApiInfo); + return getDataTable(list); + } + + /** + * 导出接口基础信息列表 + */ + @PreAuthorize("@ss.hasPermi('tool:api:export')") + @Log(title = "接口基础信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, SysApiInfo sysApiInfo) + { + List list = sysApiInfoService.selectSysApiInfoList(sysApiInfo); + ExcelUtil util = new ExcelUtil(SysApiInfo.class); + util.exportExcel(response, list, "接口基础信息数据"); + } + + /** + * 获取接口基础信息详细信息 + */ + @PreAuthorize("@ss.hasPermi('tool:api:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(sysApiInfoService.selectSysApiInfoById(id)); + } + + /** + * 新增接口基础信息 + */ + @PreAuthorize("@ss.hasPermi('tool:api:add')") + @Log(title = "接口基础信息", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysApiInfo sysApiInfo) + { + return toAjax(sysApiInfoService.insertSysApiInfo(sysApiInfo)); + } + + /** + * 修改接口基础信息 + */ + @PreAuthorize("@ss.hasPermi('tool:api:edit')") + @Log(title = "接口基础信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysApiInfo sysApiInfo) + { + return toAjax(sysApiInfoService.updateSysApiInfo(sysApiInfo)); + } + + /** + * 接收前端配置,生成接口相关代码 + */ + @PostMapping("/code") + public String generateCode(@RequestBody ApiConfigDTO apiConfig) { + try { + codeGenerator.generateApiCode(apiConfig); + return "代码生成成功,生成路径:" + System.getProperty("user.dir") + "/generated-code/"; + } catch (Exception e) { + return "代码生成失败:" + e.getMessage(); + } + } + + /** + * 删除接口基础信息 + */ + @PreAuthorize("@ss.hasPermi('tool:api:remove')") + @Log(title = "接口基础信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(sysApiInfoService.deleteSysApiInfoByIds(ids)); + } +} diff --git a/agri-generator/src/main/java/com/agri/generator/domain/SysApiInfo.java b/agri-generator/src/main/java/com/agri/generator/domain/SysApiInfo.java new file mode 100644 index 0000000..23fc5fc --- /dev/null +++ b/agri-generator/src/main/java/com/agri/generator/domain/SysApiInfo.java @@ -0,0 +1,128 @@ +package com.agri.generator.domain; + +import java.util.List; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.agri.common.annotation.Excel; +import com.agri.common.core.domain.BaseEntity; + +/** + * 接口基础信息对象 sys_api_info + * + * @author agri + * @date 2025-12-30 + */ +public class SysApiInfo extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键ID */ + private Long id; + + /** 请求路径(对应前端prop:requestPath) */ + @Excel(name = "请求路径", readConverterExp = "对=应前端prop:requestPath") + private String requestPath; + + /** 接口类型(厂家)(对应前端prop:apiVendor) */ + @Excel(name = "接口类型", readConverterExp = "厂=家") + private Long apiVendor; + + /** 接口说明(对应前端prop:apiDesc) */ + @Excel(name = "接口说明", readConverterExp = "对=应前端prop:apiDesc") + private String apiDesc; + + /** 接口备注(对应前端prop:apiRemark) */ + @Excel(name = "接口备注", readConverterExp = "对=应前端prop:apiRemark") + private String apiRemark; + + /** 逻辑删除标识(0-未删,1-已删) */ + private Integer isDeleted; + + /** 接口参数信息 */ + private List sysApiParamList; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + + public void setRequestPath(String requestPath) + { + this.requestPath = requestPath; + } + + public String getRequestPath() + { + return requestPath; + } + + public void setApiVendor(Long apiVendor) + { + this.apiVendor = apiVendor; + } + + public Long getApiVendor() + { + return apiVendor; + } + + public void setApiDesc(String apiDesc) + { + this.apiDesc = apiDesc; + } + + public String getApiDesc() + { + return apiDesc; + } + + public void setApiRemark(String apiRemark) + { + this.apiRemark = apiRemark; + } + + public String getApiRemark() + { + return apiRemark; + } + + public void setIsDeleted(Integer isDeleted) + { + this.isDeleted = isDeleted; + } + + public Integer getIsDeleted() + { + return isDeleted; + } + + public List getSysApiParamList() + { + return sysApiParamList; + } + + public void setSysApiParamList(List sysApiParamList) + { + this.sysApiParamList = sysApiParamList; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("requestPath", getRequestPath()) + .append("apiVendor", getApiVendor()) + .append("apiDesc", getApiDesc()) + .append("apiRemark", getApiRemark()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .append("isDeleted", getIsDeleted()) + .append("sysApiParamList", getSysApiParamList()) + .toString(); + } +} diff --git a/agri-generator/src/main/java/com/agri/generator/domain/SysApiParam.java b/agri-generator/src/main/java/com/agri/generator/domain/SysApiParam.java new file mode 100644 index 0000000..bd531ad --- /dev/null +++ b/agri-generator/src/main/java/com/agri/generator/domain/SysApiParam.java @@ -0,0 +1,149 @@ +package com.agri.generator.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.agri.common.annotation.Excel; +import com.agri.common.core.domain.BaseEntity; + +/** + * 接口参数对象 sys_api_param + * + * @author agri + * @date 2025-12-30 + */ +public class SysApiParam extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键ID */ + private Long id; + + /** 关联api_info的主键ID */ + private Long apiId; + + /** 参数类型(0-入参,1-出参) */ + @Excel(name = "参数类型", readConverterExp = "0=-入参,1-出参") + private Long paramType; + + /** 参数名(对应前端prop:paramName) */ + @Excel(name = "参数名", readConverterExp = "对=应前端prop:paramName") + private String paramName; + + /** 是否必选(仅入参有效)(对应前端prop:required) */ + @Excel(name = "是否必选", readConverterExp = "仅=入参有效") + private Long required; + + /** 参数数据类型(对应前端prop:paramType) */ + @Excel(name = "参数数据类型", readConverterExp = "对=应前端prop:paramType") + private String paramDataType; + + /** 参数说明/注释(入参:paramDesc,出参:paramComment) */ + @Excel(name = "参数说明/注释", readConverterExp = "入=参:paramDesc,出参:paramComment") + private String paramDesc; + + /** 是否生成实体(入参:genRequestEntity,出参:genResponseEntity) */ + @Excel(name = "是否生成实体", readConverterExp = "入=参:genRequestEntity,出参:genResponseEntity") + private Long genEntity; + + /** 是否生成实体 */ + private Long isDeleted; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setApiId(Long apiId) + { + this.apiId = apiId; + } + + public Long getApiId() + { + return apiId; + } + public void setParamType(Long paramType) + { + this.paramType = paramType; + } + + public Long getParamType() + { + return paramType; + } + public void setParamName(String paramName) + { + this.paramName = paramName; + } + + public String getParamName() + { + return paramName; + } + public void setRequired(Long required) + { + this.required = required; + } + + public Long getRequired() + { + return required; + } + public void setParamDataType(String paramDataType) + { + this.paramDataType = paramDataType; + } + + public String getParamDataType() + { + return paramDataType; + } + public void setParamDesc(String paramDesc) + { + this.paramDesc = paramDesc; + } + + public String getParamDesc() + { + return paramDesc; + } + public void setGenEntity(Long genEntity) + { + this.genEntity = genEntity; + } + + public Long getGenEntity() + { + return genEntity; + } + public void setIsDeleted(Long isDeleted) + { + this.isDeleted = isDeleted; + } + + public Long getIsDeleted() + { + return isDeleted; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("apiId", getApiId()) + .append("paramType", getParamType()) + .append("paramName", getParamName()) + .append("required", getRequired()) + .append("paramDataType", getParamDataType()) + .append("paramDesc", getParamDesc()) + .append("genEntity", getGenEntity()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .append("isDeleted", getIsDeleted()) + .toString(); + } +} diff --git a/agri-generator/src/main/java/com/agri/generator/dto/ApiConfigDTO.java b/agri-generator/src/main/java/com/agri/generator/dto/ApiConfigDTO.java new file mode 100644 index 0000000..b393a93 --- /dev/null +++ b/agri-generator/src/main/java/com/agri/generator/dto/ApiConfigDTO.java @@ -0,0 +1,40 @@ +package com.agri.generator.dto; + +import lombok.Data; +import java.util.List; + +/** + * 接口配置接收模型 + */ +@Data +public class ApiConfigDTO { + /** 请求路径 */ + private String requestPath; + /** 接口类型(厂家) */ + private String apiVendor; + /** 接口说明 */ + private String apiDesc; + /** 接口备注 */ + private String apiRemark; + /** 入参列表 */ + private List requestParams; + /** 出参列表 */ + private List responseParams; + + /** + * 参数子模型 + */ + @Data + public static class ApiParamDTO { + /** 参数名 */ + private String paramName; + /** 是否必选(仅入参) */ + private Boolean required; + /** 参数类型(String/Integer等) */ + private String paramType; + /** 参数说明/注释 */ + private String paramDesc; + /** 是否生成实体 */ + private Boolean genEntity; + } +} \ No newline at end of file diff --git a/agri-generator/src/main/java/com/agri/generator/mapper/SysApiInfoMapper.java b/agri-generator/src/main/java/com/agri/generator/mapper/SysApiInfoMapper.java new file mode 100644 index 0000000..4e6d781 --- /dev/null +++ b/agri-generator/src/main/java/com/agri/generator/mapper/SysApiInfoMapper.java @@ -0,0 +1,87 @@ +package com.agri.generator.mapper; + +import java.util.List; +import com.agri.generator.domain.SysApiInfo; +import com.agri.generator.domain.SysApiParam; + +/** + * 接口基础信息Mapper接口 + * + * @author agri + * @date 2025-12-30 + */ +public interface SysApiInfoMapper +{ + /** + * 查询接口基础信息 + * + * @param id 接口基础信息主键 + * @return 接口基础信息 + */ + public SysApiInfo selectSysApiInfoById(Long id); + + /** + * 查询接口基础信息列表 + * + * @param sysApiInfo 接口基础信息 + * @return 接口基础信息集合 + */ + public List selectSysApiInfoList(SysApiInfo sysApiInfo); + + /** + * 新增接口基础信息 + * + * @param sysApiInfo 接口基础信息 + * @return 结果 + */ + public int insertSysApiInfo(SysApiInfo sysApiInfo); + + /** + * 修改接口基础信息 + * + * @param sysApiInfo 接口基础信息 + * @return 结果 + */ + public int updateSysApiInfo(SysApiInfo sysApiInfo); + + /** + * 删除接口基础信息 + * + * @param id 接口基础信息主键 + * @return 结果 + */ + public int deleteSysApiInfoById(Long id); + + /** + * 批量删除接口基础信息 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteSysApiInfoByIds(Long[] ids); + + /** + * 批量删除接口参数 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteSysApiParamByApiIds(Long[] ids); + + /** + * 批量新增接口参数 + * + * @param sysApiParamList 接口参数列表 + * @return 结果 + */ + public int batchSysApiParam(List sysApiParamList); + + + /** + * 通过接口基础信息主键删除接口参数信息 + * + * @param id 接口基础信息ID + * @return 结果 + */ + public int deleteSysApiParamByApiId(Long id); +} diff --git a/agri-generator/src/main/java/com/agri/generator/service/ISysApiInfoService.java b/agri-generator/src/main/java/com/agri/generator/service/ISysApiInfoService.java new file mode 100644 index 0000000..3027771 --- /dev/null +++ b/agri-generator/src/main/java/com/agri/generator/service/ISysApiInfoService.java @@ -0,0 +1,61 @@ +package com.agri.generator.service; + +import java.util.List; +import com.agri.generator.domain.SysApiInfo; + +/** + * 接口基础信息Service接口 + * + * @author agri + * @date 2025-12-30 + */ +public interface ISysApiInfoService +{ + /** + * 查询接口基础信息 + * + * @param id 接口基础信息主键 + * @return 接口基础信息 + */ + public SysApiInfo selectSysApiInfoById(Long id); + + /** + * 查询接口基础信息列表 + * + * @param sysApiInfo 接口基础信息 + * @return 接口基础信息集合 + */ + public List selectSysApiInfoList(SysApiInfo sysApiInfo); + + /** + * 新增接口基础信息 + * + * @param sysApiInfo 接口基础信息 + * @return 结果 + */ + public int insertSysApiInfo(SysApiInfo sysApiInfo); + + /** + * 修改接口基础信息 + * + * @param sysApiInfo 接口基础信息 + * @return 结果 + */ + public int updateSysApiInfo(SysApiInfo sysApiInfo); + + /** + * 批量删除接口基础信息 + * + * @param ids 需要删除的接口基础信息主键集合 + * @return 结果 + */ + public int deleteSysApiInfoByIds(Long[] ids); + + /** + * 删除接口基础信息信息 + * + * @param id 接口基础信息主键 + * @return 结果 + */ + public int deleteSysApiInfoById(Long id); +} diff --git a/agri-generator/src/main/java/com/agri/generator/service/impl/SysApiInfoServiceImpl.java b/agri-generator/src/main/java/com/agri/generator/service/impl/SysApiInfoServiceImpl.java new file mode 100644 index 0000000..c773e9f --- /dev/null +++ b/agri-generator/src/main/java/com/agri/generator/service/impl/SysApiInfoServiceImpl.java @@ -0,0 +1,134 @@ +package com.agri.generator.service.impl; + +import java.util.List; +import com.agri.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.util.ArrayList; +import com.agri.common.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; +import com.agri.generator.domain.SysApiParam; +import com.agri.generator.mapper.SysApiInfoMapper; +import com.agri.generator.domain.SysApiInfo; +import com.agri.generator.service.ISysApiInfoService; + +/** + * 接口基础信息Service业务层处理 + * + * @author agri + * @date 2025-12-30 + */ +@Service +public class SysApiInfoServiceImpl implements ISysApiInfoService +{ + @Autowired + private SysApiInfoMapper sysApiInfoMapper; + + /** + * 查询接口基础信息 + * + * @param id 接口基础信息主键 + * @return 接口基础信息 + */ + @Override + public SysApiInfo selectSysApiInfoById(Long id) + { + return sysApiInfoMapper.selectSysApiInfoById(id); + } + + /** + * 查询接口基础信息列表 + * + * @param sysApiInfo 接口基础信息 + * @return 接口基础信息 + */ + @Override + public List selectSysApiInfoList(SysApiInfo sysApiInfo) + { + return sysApiInfoMapper.selectSysApiInfoList(sysApiInfo); + } + + /** + * 新增接口基础信息 + * + * @param sysApiInfo 接口基础信息 + * @return 结果 + */ + @Transactional + @Override + public int insertSysApiInfo(SysApiInfo sysApiInfo) + { + sysApiInfo.setCreateTime(DateUtils.getNowDate()); + int rows = sysApiInfoMapper.insertSysApiInfo(sysApiInfo); + insertSysApiParam(sysApiInfo); + return rows; + } + + /** + * 修改接口基础信息 + * + * @param sysApiInfo 接口基础信息 + * @return 结果 + */ + @Transactional + @Override + public int updateSysApiInfo(SysApiInfo sysApiInfo) + { + sysApiInfo.setUpdateTime(DateUtils.getNowDate()); + sysApiInfoMapper.deleteSysApiParamByApiId(sysApiInfo.getId()); + insertSysApiParam(sysApiInfo); + return sysApiInfoMapper.updateSysApiInfo(sysApiInfo); + } + + /** + * 批量删除接口基础信息 + * + * @param ids 需要删除的接口基础信息主键 + * @return 结果 + */ + @Transactional + @Override + public int deleteSysApiInfoByIds(Long[] ids) + { + sysApiInfoMapper.deleteSysApiParamByApiIds(ids); + return sysApiInfoMapper.deleteSysApiInfoByIds(ids); + } + + /** + * 删除接口基础信息信息 + * + * @param id 接口基础信息主键 + * @return 结果 + */ + @Transactional + @Override + public int deleteSysApiInfoById(Long id) + { + sysApiInfoMapper.deleteSysApiParamByApiId(id); + return sysApiInfoMapper.deleteSysApiInfoById(id); + } + + /** + * 新增接口参数信息 + * + * @param sysApiInfo 接口基础信息对象 + */ + public void insertSysApiParam(SysApiInfo sysApiInfo) + { + List sysApiParamList = sysApiInfo.getSysApiParamList(); + Long id = sysApiInfo.getId(); + if (StringUtils.isNotNull(sysApiParamList)) + { + List list = new ArrayList(); + for (SysApiParam sysApiParam : sysApiParamList) + { + sysApiParam.setApiId(id); + list.add(sysApiParam); + } + if (list.size() > 0) + { + sysApiInfoMapper.batchSysApiParam(list); + } + } + } +} diff --git a/agri-generator/src/main/java/com/agri/generator/util/CodeGenerator.java b/agri-generator/src/main/java/com/agri/generator/util/CodeGenerator.java new file mode 100644 index 0000000..37f8370 --- /dev/null +++ b/agri-generator/src/main/java/com/agri/generator/util/CodeGenerator.java @@ -0,0 +1,290 @@ +package com.agri.generator.util; + + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; +import com.agri.generator.dto.ApiConfigDTO; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 代码生成工具类 + */ +@Component +public class CodeGenerator { + // Freemarker配置 + private Configuration freemarkerConfig; + + // 代码生成根目录(可配置到application.yml) + private static final String CODE_ROOT_PATH = System.getProperty("user.dir") + "/generated-code/"; + // 基础包名 + private static final String BASE_PACKAGE = "com.example.api"; + + @PostConstruct + public void init() { + // 初始化Freemarker + freemarkerConfig = new Configuration(Configuration.VERSION_2_3_31); + + // 设置模板目录(resources/templates) + freemarkerConfig.setClassForTemplateLoading(this.getClass(), "/templates/"); + freemarkerConfig.setDefaultEncoding("UTF-8"); + + // 创建代码根目录 + FileUtil.mkdir(CODE_ROOT_PATH); + } + + /** + * 生成接口相关代码(实体+接口方法+枚举) + */ + public void generateApiCode(ApiConfigDTO apiConfig) { + try { + // 1. 生成入参实体(仅生成标记为genEntity=true的参数) + generateRequestEntity(apiConfig); + // 2. 生成出参实体(仅生成标记为genEntity=true的参数) + generateResponseEntity(apiConfig); + // 3. 生成接口方法 + generateApiMethod(apiConfig); + // 4. 生成枚举(需传入所有接口配置,此处示例传单个,实际可传列表) + List apiList = Collections.singletonList(apiConfig); + generateApiEnum(apiList); + } catch (Exception e) { + throw new RuntimeException("代码生成失败", e); + } + } + + /** + * 生成入参实体 + */ + private void generateRequestEntity(ApiConfigDTO apiConfig) throws IOException, TemplateException { + // 过滤需要生成实体的入参 + List requestParams = apiConfig.getRequestParams().stream() + .filter(ApiConfigDTO.ApiParamDTO::getGenEntity) + .collect(Collectors.toList()); + if (requestParams.isEmpty()) { + return; + } + + // 实体类名(路径转驼峰,如/api/user/login → UserLoginRequest) + String className = getClassName(apiConfig.getRequestPath(), "Request"); + // 模板参数 + Map dataModel = new HashMap<>(); + dataModel.put("packageName", BASE_PACKAGE + ".request"); + dataModel.put("className", className); + dataModel.put("apiDesc", apiConfig.getApiDesc()); + dataModel.put("apiRemark", apiConfig.getApiRemark()); + dataModel.put("params", requestParams); + dataModel.put("createTime", new Date()); + + // 生成文件 + Template template = freemarkerConfig.getTemplate("entity.ftl"); + String filePath = CODE_ROOT_PATH + "request/" + className + ".java"; + writeFile(template, dataModel, filePath); + } + + /** + * 生成出参实体 + */ + private void generateResponseEntity(ApiConfigDTO apiConfig) throws IOException, TemplateException { + // 过滤需要生成实体的出参 + List responseParams = apiConfig.getResponseParams().stream() + .filter(ApiConfigDTO.ApiParamDTO::getGenEntity) + .collect(Collectors.toList()); + if (responseParams.isEmpty()) { + return; + } + + // 实体类名(路径转驼峰,如/api/user/login → UserLoginResponse) + String className = getClassName(apiConfig.getRequestPath(), "Response"); + // 模板参数 + Map dataModel = new HashMap<>(); + dataModel.put("packageName", BASE_PACKAGE + ".response"); + dataModel.put("className", className); + dataModel.put("apiDesc", apiConfig.getApiDesc()); + dataModel.put("apiRemark", apiConfig.getApiRemark()); + dataModel.put("params", responseParams); + dataModel.put("createTime", new Date()); + + // 生成文件 + Template template = freemarkerConfig.getTemplate("entity.ftl"); + String filePath = CODE_ROOT_PATH + "response/" + className + ".java"; + writeFile(template, dataModel, filePath); + } + + /** + * 生成接口方法 + */ + private void generateApiMethod(ApiConfigDTO apiConfig) throws IOException, TemplateException { + // 接口类名(路径转驼峰,如/api/user/login → UserLoginApi) + String className = getClassName(apiConfig.getRequestPath(), "Api"); + // 方法名(路径转小驼峰,如/api/user/login → userLogin) + String methodName = this.uncapitalize(getClassName(apiConfig.getRequestPath(), "")); + // 出参实体名(默认用Response,若生成则用实际实体名) + String responseEntityName = "Response"; + List responseParams = apiConfig.getResponseParams().stream() + .filter(ApiConfigDTO.ApiParamDTO::getGenEntity) + .collect(Collectors.toList()); + if (!responseParams.isEmpty()) { + responseEntityName = getClassName(apiConfig.getRequestPath(), "Response"); + } + + // 模板参数 + Map dataModel = new HashMap<>(); + dataModel.put("packageName", BASE_PACKAGE + ".controller"); + dataModel.put("className", className); + dataModel.put("methodName", methodName); + dataModel.put("requestMethod", "Post"); // 默认POST,可根据实际需求调整 + dataModel.put("requestPath", apiConfig.getRequestPath()); + dataModel.put("apiVendor", apiConfig.getApiVendor()); + dataModel.put("apiDesc", apiConfig.getApiDesc()); + dataModel.put("apiRemark", apiConfig.getApiRemark()); + dataModel.put("requestParams", apiConfig.getRequestParams()); + dataModel.put("responseEntityName", responseEntityName); + dataModel.put("createTime", new Date()); + + // 生成文件 + Template template = freemarkerConfig.getTemplate("apiMethod.ftl"); + String filePath = CODE_ROOT_PATH + "controller/" + className + ".java"; + writeFile(template, dataModel, filePath); + } + + /** + * 生成接口路径枚举 + */ + private void generateApiEnum(List apiList) throws IOException, TemplateException { + // 处理枚举数据(路径转枚举名,如/api/user/login → USER_LOGIN) + List> enumDataList = new ArrayList<>(); + for (ApiConfigDTO api : apiList) { + Map enumData = new HashMap<>(); + enumData.put("enumName", getEnumName(api.getRequestPath())); + enumData.put("requestPath", api.getRequestPath()); + enumData.put("apiDesc", api.getApiDesc()); + enumDataList.add(enumData); + } + + // 模板参数 + Map dataModel = new HashMap<>(); + dataModel.put("packageName", BASE_PACKAGE + ".enums"); + dataModel.put("apiList", enumDataList); + dataModel.put("createTime", new Date()); + + // 生成文件 + Template template = freemarkerConfig.getTemplate("enum.ftl"); + String filePath = CODE_ROOT_PATH + "enums/ApiPathEnum.java"; + writeFile(template, dataModel, filePath); + } + + /** + * 路径转类名(如/api/user/login → UserLogin + suffix) + */ + private String getClassName(String path, String suffix) { + // 去除前缀/,按/分割,转驼峰 + String[] parts = StrUtil.removePrefix(path, "/").split("/"); + StringBuilder className = new StringBuilder(); + for (String part : parts) { + className.append(StrUtil.upperFirst(part)); + } + return className + suffix; + } + + /** + * 路径转枚举名(如/api/user/login → USER_LOGIN) + */ + private String getEnumName(String path) { + String[] parts = StrUtil.removePrefix(path, "/").split("/"); + StringBuilder enumName = new StringBuilder(); + for (int i = 0; i < parts.length; i++) { + enumName.append(parts[i].toUpperCase()); + if (i < parts.length - 1) { + enumName.append("_"); + } + } + return enumName.toString(); + } + + /** + * 写入文件 + */ + private void writeFile(Template template, Map dataModel, String filePath) throws IOException, TemplateException { + File file = new File(filePath); + FileUtil.mkParentDirs(file); // 创建父目录 + try (Writer writer = new FileWriter(file, false)) { + template.process(dataModel, writer); + } + } + + // ==================== 替代StrUtil的原生方法 ==================== + /** + * 判断字符串是否为空(空白字符也算空) + */ + private boolean isBlank(String str) { + if (str == null) { + return true; + } + int length = str.length(); + for (int i = 0; i < length; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } + + /** + * 判断字符串是否非空 + */ + private boolean isNotBlank(String str) { + return !isBlank(str); + } + + /** + * 去除字符串前缀 + */ + private String removePrefix(String str, String prefix) { + if (str == null || prefix == null) { + return str; + } + if (str.startsWith(prefix)) { + return str.substring(prefix.length()); + } + return str; + } + + /** + * 首字母大写 + */ + private String upperFirst(String str) { + if (isBlank(str)) { + return str; + } + char firstChar = str.charAt(0); + if (Character.isUpperCase(firstChar)) { + return str; + } + return Character.toUpperCase(firstChar) + str.substring(1); + } + + + /** + * 首字母小写 + */ + private String uncapitalize(String str) { + if (isBlank(str)) { + return str; + } + char firstChar = str.charAt(0); + if (Character.isLowerCase(firstChar)) { + return str; + } + return Character.toLowerCase(firstChar) + str.substring(1); + } + +} \ No newline at end of file diff --git a/agri-generator/src/main/resources/mapper/tool/SysApiInfoMapper.xml b/agri-generator/src/main/resources/mapper/tool/SysApiInfoMapper.xml new file mode 100644 index 0000000..24cdbe7 --- /dev/null +++ b/agri-generator/src/main/resources/mapper/tool/SysApiInfoMapper.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select id, request_path, api_vendor, api_desc, api_remark, create_time, update_time, is_deleted from sys_api_info + + + + + + + + + + insert into sys_api_info + + request_path, + api_vendor, + api_desc, + api_remark, + create_time, + update_time, + is_deleted, + + + #{requestPath}, + #{apiVendor}, + #{apiDesc}, + #{apiRemark}, + #{createTime}, + #{updateTime}, + #{isDeleted}, + + + + + update sys_api_info + + request_path = #{requestPath}, + api_vendor = #{apiVendor}, + api_desc = #{apiDesc}, + api_remark = #{apiRemark}, + create_time = #{createTime}, + update_time = #{updateTime}, + is_deleted = #{isDeleted}, + + where id = #{id} + + + + delete from sys_api_info where id = #{id} + + + + delete from sys_api_info where id in + + #{id} + + + + + delete from sys_api_param where api_id in + + #{apiId} + + + + + delete from sys_api_param where api_id = #{apiId} + + + + insert into sys_api_param( id, api_id, param_type, param_name, required, param_data_type, param_desc, gen_entity, create_time, update_time, is_deleted) values + + ( #{item.id}, #{item.apiId}, #{item.paramType}, #{item.paramName}, #{item.required}, #{item.paramDataType}, #{item.paramDesc}, #{item.genEntity}, #{item.createTime}, #{item.updateTime}, #{item.isDeleted}) + + + \ No newline at end of file diff --git a/agri-generator/src/main/resources/templates/apiMethod.ftl b/agri-generator/src/main/resources/templates/apiMethod.ftl new file mode 100644 index 0000000..b8b5c8c --- /dev/null +++ b/agri-generator/src/main/resources/templates/apiMethod.ftl @@ -0,0 +1,38 @@ +package ${packageName}; + +import org.springframework.web.bind.annotation.${requestMethod}; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +/** +* ${apiVendor}接口实现类 +* ${apiDesc} +* 备注:${apiRemark} +* @author 自动生成 +* @date ${createTime} +*/ +@RestController +@RequestMapping("/api") +public class ${className} { + + /** + * ${apiDesc} + * 接口路径:${requestPath} + */ + @${requestMethod}("${requestPath}") + public ${responseEntityName} ${methodName}( + <#if requestParams?exists && requestParams?size > 0> + <#list requestParams as param> + <#if param.required?? && param.required>@RequestParam(required = true) + <#if !param.required?? || !param.required>@RequestParam(required = false) + ${param.paramType} ${param.paramName} + <#if param_has_next>, + + + ) { + // 接口业务逻辑(需手动实现) + return new ${responseEntityName}(); + } +} \ No newline at end of file diff --git a/agri-generator/src/main/resources/templates/entity.ftl b/agri-generator/src/main/resources/templates/entity.ftl new file mode 100644 index 0000000..364b58b --- /dev/null +++ b/agri-generator/src/main/resources/templates/entity.ftl @@ -0,0 +1,26 @@ +package ${packageName}; + +import lombok.Data; +import java.io.Serializable; + +/** +* ${className} +* ${apiDesc} +* 备注:${apiRemark} +* @author 自动生成 +* @date ${createTime} +*/ +@Data +public class ${className} implements Serializable { + private static final long serialVersionUID = 1L; + + <#list params as param> + /** + * ${param.paramDesc} + <#if param.required?? && param.required> + * 必选参数 + + */ + private ${param.paramType} ${param.paramName}; + +} \ No newline at end of file diff --git a/agri-generator/src/main/resources/templates/enum.ftl b/agri-generator/src/main/resources/templates/enum.ftl new file mode 100644 index 0000000..c5b7335 --- /dev/null +++ b/agri-generator/src/main/resources/templates/enum.ftl @@ -0,0 +1,32 @@ +package ${packageName}; + +/** +* 接口路径枚举 +* @author 自动生成 +* @date ${createTime} +*/ +public enum ApiPathEnum { + <#list apiList as api> + /** + * ${api.apiDesc} + */ + ${api.enumName}("${api.requestPath}", "${api.apiDesc}") + <#if api_has_next>, + ; + + private final String path; + private final String desc; + + ApiPathEnum(String path, String desc) { + this.path = path; + this.desc = desc; + } + + public String getPath() { + return path; + } + + public String getDesc() { + return desc; + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 84eb889..4684661 100644 --- a/pom.xml +++ b/pom.xml @@ -219,6 +219,12 @@ ${agri.version} + + + com.agri + agri-api + ${agri.version} + @@ -229,6 +235,7 @@ agri-quartz agri-generator agri-common + agri-api pom