1 Star 0 Fork 1

高开天 / T-FAST

forked from THM / T-FAST 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MulanPSL-2.0

T-FAST

简介

T-FAST是一款基于SpringBoot+MyBatis-Plus的快速开发脚手架,拥有完整的权限管理功能,可对接Vue前端,开箱即用。

技术选型

技术 版本 说明
SpringBoot 2.7.0 容器+MVC框架
SpringSecurity 5.7.1 认证和授权框架
MyBatis 3.5.4 ORM框架
MyBatis-Plus 3.3.2 MyBatis增强工具
MyBatis-Plus Generator 3.3.2 数据层代码生成器
Swagger-UI 3.0.0 文档生产工具
Redis 5.0 分布式缓存
Docker 20.10.17 应用容器引擎
Druid 1.1.10 数据库连接池
Hutool 5.8.8 Java工具类库
JWT 0.9.0 JWT登录支持
Lombok 1.18.24 简化对象封装工具
minio 8.4.2 oss对象存储
JDK 11.0.6 JDK

表结构

环境搭建

简化依赖服务,只需安装最常用的MySql和Redis服务即可

开发规约

项目包结构

src
├── core                                      -- 系统核心
|   ├── annotation                            -- 相关注解
|   ├── aspect                                -- 相关切面
|   ├── config                                -- 相关配置
|   ├── constant                              -- 常量信息
|   ├── entity                                -- 公共实体
|   ├── enums                                 -- 枚举信息
|   ├── exception                             -- 全局异常处理相关类
|   ├── generator                             -- MyBatis-Plus代码生成器
|   ├── oss                                   -- 对象存储
|   ├── service                               -- 通用业务类
|   ├── security                              -- SpringSecurity认证授权相关代码
|   ├── task                                  -- 定时任务
|   |   ├── base                              -- 抽象定时任务父类以及注册器
|   |   └── job                               -- 具体定时任务
|   └── utils                                 -- 通用工具类
├── modules                                   -- 存放业务代码的基础包
|   └── sys                                   -- 系统管理模块业务代码
|       ├── controller                        -- 该模块相关接口
|       ├── mapper                            -- 该模块相关Mapper接口
|       ├── model                             -- 该模块相关实体类
|       |   ├── dto                           -- 该模块相关请求实体
|       |   └── vo                            -- 该模块相关响应实体
|       └── service                           -- 该模块相关业务处理类

资源文件说明

resources
├── log                                 -- Logback日志配置文件
├── mapper                              -- MyBatis中mapper.xml存放位置
├── application.yml                     -- SpringBoot通用配置文件
├── application-dev.yml                 -- SpringBoot开发环境配置文件
├── application-prod.yml                -- SpringBoot生产环境配置文件
└── generator.properties                -- MyBatis-Plus代码生成器配置

接口定义规则

描述 method path
分页查询数据 GET /{控制器路由名称}/page
获取数据列表 GET /{控制器路由名称}/list
获取指定记录详情 GET /{控制器路由名称}/{id}
新增数据 POST /{控制器路由名称}/insert
修改数据 PUT /{控制器路由名称}/update
删除数据 DELETE /{控制器路由名称}/delete/{id}

项目运行

直接运行启动类TFastApplicationmain函数

业务代码开发流程

创建业务表

示例:创建好sys模块的所有表,需要注意的是一定要写好表字段的注释,这样实体类和接口文档中就会自动生成字段说明了。

使用代码生成器

运行MyBatisPlusGenerator类的main方法来生成代码,可直接生成controller、service、mapper、model、mapper.xml的代码,无需手动创建。

  • 代码生成器支持两种模式,一种生成单表的代码,比如只生成sys_dict表代码可以先输入模块名称sys,后输入sys_dict
  • 生成代码结构预览
  • 直接生成整个模块代码,比如生成sys模块代码,先输入模块名称sys,再输入表通配sys_*

编写业务代码

参数定义与校验

  • 请求参数

    请求参数统一定义在该模块下的model/dto

    • 新增参数格式:xxxInsertDto

      @Data
      @EqualsAndHashCode(callSuper = false)
      public class MenuInsertDto {
      
        @ApiModelProperty(value = "父级ID 0-表示没有父级 ")
        private Long parentId;
      
        @ApiModelProperty(value = "菜单标题", required = true)
        @NotEmpty(message = "菜单标题不能为空")
        @Size(max = 12, message = "菜单标题不能超过12位")
        private String title;
      
        @ApiModelProperty(value = "菜单排序")
        private Integer sort;
      
        @ApiModelProperty(value = "前端路径", required = true)
        @NotEmpty(message = "菜单标题不能为空")
        @Size(max = 36, message = "菜单标题不能超过36位")
        private String path;
      
        @ApiModelProperty(value = "前端图标")
        private String icon;
      
        @ApiModelProperty(value = "前端隐藏 0-隐藏  1-显示")
        private Integer hidden;
      }
    • 修改参数格式:xxxUpdateDto

      @Data
      @EqualsAndHashCode(callSuper = false)
      public class MenuUpdateDto {
      
          @ApiModelProperty(value = "菜单ID", required = true)
          @NotNull(message = "菜单ID不能为空")
          private Long id;
      
          @ApiModelProperty(value = "父级ID 0-表示没有父级 ")
          private Long parentId;
      
          @ApiModelProperty(value = "菜单标题")
          @Size(max = 12, message = "菜单标题不能超过12位")
          private String title;
      
          @ApiModelProperty(value = "菜单层级")
          private Integer level;
      
          @ApiModelProperty(value = "菜单排序")
          private Integer sort;
      
          @ApiModelProperty(value = "前端路径")
          @Size(max = 36, message = "前端路径不能超过36位")
          private String path;
      
          @ApiModelProperty(value = "前端图标")
          private String icon;
      
          @ApiModelProperty(value = "前端隐藏 0-隐藏  1-显示")
          private Integer hidden;
      
          @ApiModelProperty(value = "删除标识 0-删除 1-未删除")
          private Integer delFlag;
      }
    • 参数校验

      接口中添加@Validated注解开启参数校验功能即可。

       @RestController
       @Api(tags = "系统:菜单管理")
       @RequestMapping("/sysMenu")
       public class SysMenuController {
      
           @Resource
           private SysMenuService sysMenuService;
      
           @ApiOperation("新增菜单信息")
           @PostMapping("/insert")
           public ViewResult<Long> insert(@Validated @RequestBody MenuInsertDto dto){
               return ViewResult.success(sysMenuService.insert(dto));
           }
      
           @ApiOperation("修改菜单信息")
           @PutMapping("/update")
           public ViewResult<String> update(@Validated @RequestBody MenuUpdateDto dto) {
               boolean success = sysMenuService.update(dto);
               if (success) {
                   return ViewResult.success();
               }
               return ViewResult.error();
           }
      }

单表操作

由于MyBatis-Plus提供的增强功能相当强大,单表查询几乎不用手写SQL,直接使用ServiceImpl和BaseMapper中提供的方法即可。

比如我们的菜单管理业务实现类UmsMenuServiceImpl中的方法都直接使用了这些方法。

@Service
public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> implements SysDictService {

    @Resource
    private SysDictItemService sysDictItemService;

    @Override
    public Page<SysDict> page(String keyword, Integer pageNum, Integer pageSize) {
        Page<SysDict> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<SysDict> wrapper = Wrappers.lambdaQuery();
        if (StrUtil.isNotEmpty(keyword)) {
            wrapper.like(SysDict::getType, keyword)
                    .or()
                    .like(SysDict::getName, keyword);
        }
        wrapper.ne(SysDict::getDelFlag, DeleteFlagEnum.DELETED.getCode());
        return this.page(page, wrapper);
    }

    @Override
    @Transactional
    public Long insert(DictInsertDto dto) {
        SysDict sysDict = this.getOne(Wrappers
                .<SysDict>lambdaQuery()
                .eq(SysDict::getType, dto.getType()));
        if (Objects.nonNull(sysDict)){
            Asserts.fail("字典已经存在");
        }
        sysDict = BeanUtil.toBean(dto,SysDict.class);
        SysUserDetails currentUser = SecurityUtil.getCurrentUser();
        sysDict.setCreateUser(currentUser.getUserId());
        sysDict.setCreateTime(currentUser.getDate());
        sysDict.setUpdateUser(currentUser.getUserId());
        sysDict.setUpdateTime(currentUser.getDate());
        this.save(sysDict);
        return sysDict.getId();
    }

    @Override
    @Transactional
    public Boolean update(DictUpdateDto dto) {
        if (Objects.isNull(this.getById(dto.getId()))){
            Asserts.fail("字典信息不存在");
        }
        SysDict sysDict = this.getOne(Wrappers
                .<SysDict>lambdaQuery()
                .eq(SysDict::getType, dto.getType())
                .ne(SysDict::getId, dto.getId()));
        if (Objects.nonNull(sysDict)){
            Asserts.fail(dto.getType() + "字典已经存在");
        }
        sysDict = BeanUtil.toBean(dto, SysDict.class);
        //修改字典类型,同时修改字典项信息
        if (StrUtil.isNotEmpty(sysDict.getType())){
            sysDictItemService.update(Wrappers
                    .<SysDictItem>lambdaUpdate()
                    .set(SysDictItem::getType,sysDict.getType())
                    .eq(SysDictItem::getDictId,sysDict.getId()));
        }
        SysUserDetails currentUser = SecurityUtil.getCurrentUser();
        sysDict.setUpdateUser(currentUser.getUserId());
        sysDict.setUpdateTime(currentUser.getDate());
        //删除字典相关缓存
        sysDictItemService.delDictCache(sysDict.getType());
        return this.updateById(sysDict);
    }

    @Override
    @Transactional
    public Boolean delete(Long dictId) {
        SysDict sysDict = this.getById(dictId);
        if (Objects.isNull(sysDict)){
            Asserts.fail("字典信息不存在");
        }
        int count = sysDictItemService.count(Wrappers
                .<SysDictItem>lambdaQuery()
                .eq(SysDictItem::getDictId, dictId)
                .eq(SysDictItem::getDelFlag,DeleteFlagEnum.UNDELETE.getCode()));
        if (count != 0){
            Asserts.fail("存在字典项信息,不能删除字典信息");
        }
        SysUserDetails currentUser = SecurityUtil.getCurrentUser();
        sysDict.setUpdateUser(currentUser.getUserId());
        sysDict.setUpdateTime(currentUser.getDate());
        sysDict.setDelFlag(DeleteFlagEnum.DELETED.getCode());
        //删除与菜单相关缓存
        sysDictItemService.delDictCache(sysDict.getType());
        return this.updateById(sysDict);
    }
}

分页查询

对于分页查询MyBatis-Plus原生支持,不需要再整合其他插件,直接构造Page对象,然后调用ServiceImpl中的page方法即可。

  • 单表分页查询
    @Service
    public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> implements SysDictService {
    
      @Resource
      private SysDictItemService sysDictItemService;
    
      @Override
      public Page<SysDict> page(String keyword, Integer pageNum, Integer pageSize) {
        Page<SysDict> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<SysDict> wrapper = Wrappers.lambdaQuery();
        if (StrUtil.isNotEmpty(keyword)) {
          wrapper.like(SysDict::getType, keyword)
                  .or()
                  .like(SysDict::getName, keyword);
        }
        wrapper.ne(SysDict::getDelFlag, DeleteFlagEnum.DELETED.getCode());
        return this.page(page, wrapper);
      }
    }
  • 多表分页查询
    • service
        @Service
        public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> implements SysLogService {
      
          @Resource
          private SysUserService sysUserService;
      
          @Override
          public Page<LogVo> page(String keyword, Integer type, Integer pageNum, Integer pageSize) {
              Page<LogVo> page = new Page<>(pageNum,pageSize);
              List<LogVo> logVos = this.baseMapper.page(keyword, type, page);
              page.setRecords(logVos);
              return page;
          }
        }
    • mapper
      public interface SysLogMapper extends BaseMapper<SysLog> {
      
      /**
       * 根据条件分页获取日志列表
       * @param keyword  关键字:日志标题
       * @param type     日志类型
       * @param iPage  分页参数
       * @return
       */
      List<LogVo> page(@Param("keyword") String keyword,
                       @Param("type") Integer type,
                       IPage<LogVo> iPage);
      
      }
    • xml
      <select id="page" resultType="com.thm.tfast.modules.sys.model.vo.log.LogVo">
          select
          a.id,
          a.type,
          a.title,
          a.ip,
          a.url,
          a.method,
          a.agent,
          a.params,
          a.time,
          a.exception,
          a.create_time as createTime,
          a.create_user as createUser,
          b.user_name as createUserName
          from sys_log a
          join sys_user b on a.create_user = b.id
          <where>
              <if test="keyword != null and keyword !=''">
                  and a.title like concat(#{keyword},'%')
              </if>
              <if test="type != null">
                  and a.type = #{type}
              </if>
          </where>
      </select>

多表查询

对于多表查询,我们需要手写mapper.xml中的SQL实现,由于之前我们已经生成了mapper.xml文件,所以我们直接在Mapper接口中定义好方法,然后在mapper.xml写好SQL实现即可。

  • 比如说我们需要写一个根据用户ID获取其分配的权限的方法,首先我们在SysPermissionMapper接口中添加好getPermissionByUserId方法
    public interface SysPermissionMapper extends BaseMapper<SysPermission> {
      /**
       * 通过用户ID获取权限集合
       * @param userId 用户ID
       * @return 权限集合
       */
      List<SysPermission> getPermissionByUserId(Long userId);
    }
  • 然后在SysPermissionMapper.xml添加该方法的对应SQL实现即可。
    <select id="getPermissionByUserId" parameterType="java.lang.Long" resultMap="BaseResultMap">
          select distinct a.id,
                          a.permission,
                          a.description,
                          a.del_flag,
                          a.create_time,
                          a.create_user,
                          a.update_time,
                          a.update_user
          from sys_permission a
                   join sys_role_permission b on a.id = b.permission_id
                   join sys_user_role c on b.role_id = c.role_id and c.user_id = #{userId}
          where a.del_flag != 0
    </select>

定时任务

项目已经提供定时任务模板,也可以自行实现

  • 创建定时任务如SimpleTask并继承AbstractTask
    @Slf4j
    @Component
    public class SimpleTask extends AbstractTask {
    
    
      @Override
      @PostConstruct
      public void init(AbstractTask task) {
          super.init(this);
      }
    
      @Override
      @Scheduled(cron = "0/5 * * * * ?")
      public void execute() {
          try {
              log.info("简单的定时任务!!!!");
              status = 1;
          } catch (Exception e){
              status = 2;
              log.error("任务运行异常,e={}",e.getMessage());
          }
      }
    
      @Override
      public String getTaskName() {
          return SimpleTask.class.getSimpleName();
      }
    
      @Override
      public String getDesc() {
          return "示例定时任务";
      }
    
      @Override
      public Integer getStatus() {
          return status;
      }
      }

项目部署

项目采用docker容器化部署,非docker环境请求自行部署

  • 部署环境准备 项目依赖环境docker-compose.yml已经提供 路径:https://gitee.com/Thmspring/t-fast/blob/master/doc/docker/environment/docker-compose.yml
    docker-compose -f docker-compose.yml up -d
  • jar部署
    • 项目采用DockerFile部署已经编写完整的部署脚本,结果如下:
    • apps:存在配置文件和运行jar
    • app.env:配置jar名称(影响apps目录下jar包名称)、jvm参数、引用配置文件(影响apps目录下配置文件)
    • docker-compose.yml:配置启动容器名称、Dockerfile、映射容器卷等
    • Dockerfile:定义容器工作目录、启动运行脚本
    • entrypoint.sh:容器启动后运行脚本
    • global.env:全局配置(如果没有app.env则此配置文件生效)
    • 部署步骤
      1. 进入服务器创建服务目录如:deploy
         mkdir deploy
      2. 进入目录创建apps文件夹并上传相关文件
         cd deploy
         # 上传:app.env、docker-compose.yml、Dockerfile、entrypoint.sh、global.env
         mkdir apps
         cd apps
         # 上传:application-prod.yml、t-fast.jar
      3. 启动容器
        cd deploy
        # 启动
        docker-compose -f docker-compose.yml up -d
        # 停止
        docker-compose -f docker-compose.yml down
        # 查看日志
        docker logs -f t-fast
        # 查看映射日志
        cd logs
        tail -f t-fast.log
木兰宽松许可证, 第2版 木兰宽松许可证, 第2版 2020年1月 http://license.coscl.org.cn/MulanPSL2 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: 0. 定义 “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 “法人实体”是指提交贡献的机构及其“关联实体”。 “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 1. 授予版权许可 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 2. 授予专利许可 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 3. 无商标许可 “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 4. 分发限制 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 5. 免责声明与责任限制 “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 6. 语言 “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 条款结束 如何将木兰宽松许可证,第2版,应用到您的软件 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; 3, 请将如下声明文本放入每个源文件的头部注释中。 Copyright (c) [Year] [name of copyright holder] [Software Name] is licensed under Mulan PSL v2. You can use this software according to the terms and conditions of the Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. Mulan Permissive Software License,Version 2 Mulan Permissive Software License,Version 2 (Mulan PSL v2) January 2020 http://license.coscl.org.cn/MulanPSL2 Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: 0. Definition Software means the program and related documents which are licensed under this License and comprise all Contribution(s). Contribution means the copyrightable work licensed by a particular Contributor under this License. Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. Legal Entity means the entity making a Contribution and all its Affiliates. Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. 1. Grant of Copyright License Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. 2. Grant of Patent License Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. 3. No Trademark License No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. 4. Distribution Restriction You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. 5. Disclaimer of Warranty and Limitation of Liability THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 6. Language THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. END OF THE TERMS AND CONDITIONS How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. Copyright (c) [Year] [name of copyright holder] [Software Name] is licensed under Mulan PSL v2. You can use this software according to the terms and conditions of the Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details.

简介

T-FAST是一款基于SpringBoot+MyBatis-Plus的快速开发脚手架,拥有完整的权限管理功能,可对接Vue前端,开箱即用。 展开 收起
MulanPSL-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/guojf/t-fast.git
git@gitee.com:guojf/t-fast.git
guojf
t-fast
T-FAST
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891