varoptions={url:prefix+"/list",columns:[{field:'id',title:'主键'},{field:'name',title:'名称'}]};$.table.init(options);123456789101112自定义查询条件参数(特殊情况提前设置查询条件下使用)
List
Postpost=postService.selectPostById(1L);startPage();List
项目分页插件默认是Mysql语法,如果项目改为其他数据库需修改配置application.yml文件中的属性helperDialect:你的数据库
注意
只要你可以保证在PageHelper方法调用后紧跟MyBatis查询方法,这就是安全的。因为PageHelper在finally代码段中自动清除了ThreadLocal存储的对象。如果代码在进入Executor前发生异常,就会导致线程不可用,这属于人为的Bug(例如接口方法和XML中的不匹配,导致找不到MappedStatement时),这种情况由于线程不可用,也不会导致ThreadLocal参数被错误的使用。
在实际开发中经常需要使用导入导出功能来加快数据的操作。在项目中可以使用注解来完成此项功能。在需要被导入导出的实体类属性添加@Excel注解,目前支持参数如下:
1、前端调用封装好的方法$.table.init,传入后台exportUrl
varoptions={exportUrl:prefix+"/export",columns:[{field:'id',title:'主键'},{field:'name',title:'名称'}]};$.table.init(options);1234567891011122、添加导出按钮事件
@PostMapping("/export")@ResponseBodypublicAjaxResultexport(Useruser){List
1、前端调用封装好的方法$.table.init,传入后台importUrl。
varoptions={importUrl:prefix+"/importData",columns:[{field:'id',title:'主键'},{field:'name',title:'名称'}]};$.table.init(options);1234567891011122、添加导入按钮事件
@Excel(name="用户序号")privateLongid;@Excel(name="部门编号",type=Type.IMPORT)privateLongdeptId;@Excel(name="用户名称")privateStringuserName;/**导出部门多个对象*/@Excels({@Excel(name="部门名称",targetAttr="deptName",type=Type.EXPORT),@Excel(name="部门负责人",targetAttr="leader",type=Type.EXPORT)})privateSysDeptdept;/**导出部门单个对象*/@Excel(name="部门名称",targetAttr="deptName",type=Type.EXPORT)privateSysDeptdept;123456789101112131415161718195、在Controller添加导入方法,updateSupport属性为是否存在则覆盖(可选)
@PostMapping("/importData")@ResponseBodypublicAjaxResultimportData(MultipartFilefile,booleanupdateSupport)throwsException{ExcelUtil
也可以直接到main运行此方法测试。
导出用户管理表格新增标题(用户列表)
publicAjaxResultexport(SysUseruser){List
1、在实体类用Excel注解handler属性指定自定义的数据处理器
publicclassUserextendsBaseEntity{@Excel(name="用户名称",handler=MyDataHandler.class,args={"aaa","bbb"})privateStringuserName;}123452、编写数据处理器MyDataHandler继承ExcelHandlerAdapter,返回值为处理后的值。
publicclassMyDataHandlerimplementsExcelHandlerAdapter{@OverridepublicObjectformat(Objectvalue,String[]args,Cellcell,Workbookwb){//value为返回单元格显示内容值//args为excel注解传递的args数组值//cell为单元格对象//wb为工作簿对象returnvalue;}}1234567891011123、编写示例,用户名为若依则单元格文字设置为红色
有时候我们在很多属性字段中都加了Excel注解,但仅希望在Excel文件中显示指定列属性,那么我们可以进行如下处理。
示例:导出仅显示列信息。
示例:对用户进行条件判断,符合条件则隐藏属性。导出的文件则不会显示此列信息。
SysUser.java
publicclassSysRole{@Excel(name="角色编号",cellType=ColumnType.NUMERIC)privateStringroleId;@Excel(name="角色名称")privateStringroleName;@Excel(name="角色字符")privateStringroleKey;publicStringgetRoleId(){returnroleId;}publicvoidsetRoleId(StringroleId){this.roleId=roleId;}publicStringgetRoleName(){returnroleName;}publicvoidsetRoleName(StringroleName){this.roleName=roleName;}publicStringgetRoleKey(){returnroleKey;}publicvoidsetRoleKey(StringroleKey){this.roleKey=roleKey;}}123456789101112131415161718192021222324252627282930313233343536373839404142测试验证
首先创建一张上传文件的表,例如:
2、参考示例修改代码。
@PostMapping("/add")@ResponseBodypublicAjaxResultaddSave(@RequestParam("file")MultipartFilefile,SysFileInfofileInfo)throwsIOException{//上传文件路径StringfilePath=RuoYiConfig.getUploadPath();//上传并返回新文件名称StringfileName=FileUploadUtils.upload(filePath,file);fileInfo.setFilePath(fileName);returntoAjax(sysFileInfoService.insertSysFileInfo(fileInfo));}12345678910114、上传成功后需要预览可以对该属性格式化处理
{field:'filePath',title:'文件预览',formatter:function(value,row,index){return$.table.imageView(value);}},1234567如需对文件格式控制,设置application.yml中的multipart属性
#文件上传servlet:multipart:#单个文件大小max-file-size:10MB#设置总上传的文件大小max-request-size:20MB1234567注意:如果只是单纯的上传一张图片没有其他参数可以使用通用方法/common/upload请求处理方法com.ruoyi.web.controller.common.CommonController
1、参考示例代码。
@RequiresRoles注解用于配置接口要求用户拥有某(些)角色才可访问,它拥有两个参数
示例1:以下代码表示必须拥有admin角色才可访问
@RequiresRoles("admin")publicAjaxResultsave(...){returnAjaxResult.success(...);}12345示例2:以下代码表示必须拥有admin和common角色才可访问
@RequiresRoles({"admin","common"})publicAjaxResultsave(...){returnAjaxResult.success(...);}12345示例3:以下代码表示需要拥有admin或common角色才可访问
示例1:以下代码表示必须拥有system:user:add权限才可访问
@RequiresPermissions("system:user:add")publicAjaxResultsave(...){returnAjaxResult.success(...);}12345示例2:以下代码表示必须拥有system:user:add和system:user:edit权限才可访问
@RequiresPermissions({"system:user:add","system:user:edit"})publicAjaxResultsave(...){returnAjaxResult.success(...);}12345示例3:以下代码表示需要拥有system:user:add或system:user:edit权限才可访问
@RequiresPermissions(value={"system:user:add","system:user:edit"},logical=Logical.OR)publicAjaxResultsave(...){returnAjaxResult.success(...);}12345提示
示例:编程式判断资源访问权限
Subjectsubject=ShiroUtils.getSubject();subject.isPermitted(permission)//验证是否有资源权限subject.isPermittedAll(permissions)//验证是否有资源权限(列表)//例如:if(subject.isPermitted("sys:user:edit")){System.out.println("当前用户有编辑用户权限");}123456789示例:编程式判断角色访问权限
//验证是否有资源权限filterChainDefinitionMap.put("/system/user/add","perms[system:user:add]");filterChainDefinitionMap.put("/system/user/**","perms[system:user:*]");//验证是否有资源权限(列表),相当于isPermitedAll()方法。filterChainDefinitionMap.put("/system/user/**","perms[system:user:add,system:user:edit]");12345示例:基于URI判断角色访问权限
//验证是否有角色权限filterChainDefinitionMap.put("/system/user/**","roles[admin]");//验证是否有角色权限(列表),相当于hasAllRoles()方法。filterChainDefinitionMap.put("/system/user/**","roles[admin,common]");1234URI通配符
提示
例如用户新增需要插入用户表、用户与岗位关联表、用户与角色关联表,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作,这样可以防止出现脏数据,就可以使用事务让它实现回退。做法非常简单,我们只需要在方法或类添加@Transactional注解即可。
@TransactionalpublicintinsertUser(Useruser){//新增用户信息introws=userMapper.insertUser(user);//新增用户岗位关联insertUserPost(user);//新增用户与角色管理insertUserRole(user);returnrows;}1234567891011@TransactionalpublicintinsertUser(Useruser)throwsException{//新增用户信息introws=userMapper.insertUser(user);//新增用户岗位关联insertUserPost(user);//新增用户与角色管理insertUserRole(user);//模拟抛出SQLException异常booleanflag=true;if(flag){thrownewSQLException("发生异常了..");}returnrows;}1234567891011121314151617原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。如果想针对检查异常进行事务回滚,可以在@Transactional注解里使用rollbackFor属性明确指定异常。例如下面这样,就可以正常回滚:
@Transactional(rollbackFor=Exception.class)publicintinsertUser(Useruser)throwsException{//新增用户信息introws=userMapper.insertUser(user);//新增用户岗位关联insertUserPost(user);//新增用户与角色管理insertUserRole(user);//模拟抛出SQLException异常booleanflag=true;if(flag){thrownewSQLException("发生异常了..");}returnrows;}1234567891011121314151617@TransactionalpublicintinsertUser(Useruser)throwsException{//新增用户信息introws=userMapper.insertUser(user);//新增用户岗位关联insertUserPost(user);//新增用户与角色管理insertUserRole(user);//模拟抛出SQLException异常booleanflag=true;if(flag){try{//谨慎:尽量不要在业务层捕捉异常并处理thrownewSQLException("发生异常了..");}catch(Exceptione){e.printStackTrace();}}returnrows;}12345678910111213141516171819202122232425推荐做法:在业务层统一抛出异常,然后在控制层统一处理。
@TransactionalpublicintinsertUser(Useruser)throwsException{//新增用户信息introws=userMapper.insertUser(user);//新增用户岗位关联insertUserPost(user);//新增用户与角色管理insertUserRole(user);//模拟抛出SQLException异常booleanflag=true;if(flag){thrownewRuntimeException("发生异常了..");}returnrows;}1234567891011121314151617注意事项
Transactional注解的常用属性表:
事务的传播机制是指如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。即:在执行一个@Transactinal注解标注的方法时,开启了事务;当该方法还在执行中时,另一个人也触发了该方法;那么此时怎么算事务呢,这时就可以通过事务的传播机制来指定处理方式。
TransactionDefinition传播行为的常量:
所谓全局异常处理器就是使用@ControllerAdvice注解。示例如下:
1、统一返回实体定义
@ExceptionHandler(LoginException.class)publicObjectloginException(HttpServletRequestrequest,LoginExceptione){log.error(e.getMessage(),e);if(ServletUtils.isAjaxRequest(request)){returnAjaxResult.error(e.getMessage());}else{returnnewModelAndView("/error/500");}}1234567891011121314若依系统的全局异常处理器GlobalExceptionHandler注意:如果全部异常处理返回json,那么可以使用@RestControllerAdvice代替@ControllerAdvice,这样在方法上就可以不需要添加@ResponseBody。
无法捕获异常?
如果您的异常无法捕获,您可以从以下几个方面着手检查
异常是否已被处理,即抛出异常后被catch,打印了日志或抛出了其它异常异常是否非Controller抛出,即在拦截器或过滤器中出现的异常
springboot中可以用@Validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。
publicAjaxResultadd(@Validated@RequestBodySysUseruser){.....}12342、然后在对应字段Get方法加上参数校验注解,如果不符合验证要求,则会以message的信息为准,返回给前端。
1、新增Xss注解,设置自定义校验器XssValidator.class
/***自定义xss校验注解**@authorruoyi*/@Retention(RetentionPolicy.RUNTIME)@Target(value={ElementType.METHOD,ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.PARAMETER})@Constraint(validatedBy={XssValidator.class})public@interfaceXss{Stringmessage()default"不允许任何脚本运行";Class<>[]groups()default{};Class
/***自定义xss校验注解实现**@authorruoyi*/publicclassXssValidatorimplementsConstraintValidator
如果是在方法里面校验整个实体,参考示例。
新增类接口,用于标识出不同的操作类型
publicinterfaceAdd{}publicinterfaceEdit{}12345678Controller.java
//新增publicAjaxResultaddSave(@Validated(Add.class)@RequestBodyXxxxxxxx){returnsuccess(xxxx);}//编辑publicAjaxResulteditSave(@Validated(Edit.class)@RequestBodyXxxxxxxx){returnsuccess(xxxx);}1234567891011Model.java
//仅在新增时验证@NotNull(message="不能为空",groups={Add.class})privateStringxxxx;//在新增和修改时验证@NotBlank(message="不能为空",groups={Add.class,Edit.class})privateStringxxxx;1234567提示
如果你有更多操作类型,也可以自定义类统一管理,使用方式就变成了Type.Add、Type.Edit、Type.Xxxx等。
基于Jackson拓展,只需要在字段上添加脱敏注解,即可实现对该字段进行脱敏。
在字段上添加脱敏注解。如下所示:
publicclassUserextendsBaseEntity{@Sensitive(desensitizedType=DesensitizedType.PHONE)privateStringphonenumber;}12345所有类型如下:
其他的脱敏策略根据自己的需求在DesensitizedType.java去定义,可以是方法,也可以是正则表达式。
在需要被记录日志的controller方法上添加@Log注解,使用方法如下:
/***测试*/TEST,12342、在sys_dict_data字典数据表中初始化操作业务类型
insertintosys_dict_datavalues(25,10,'测试','10','sys_oper_type','','primary','N','0','admin','2018-03-1611-33-00','ry','2018-03-1611-33-00','测试操作');13、在Controller中使用注解
在实际开发中,需要设置用户只能查看哪些部门的数据,这种情况一般称为数据权限。例如对于销售,财务的数据,它们是非常敏感的,因此要求对数据权限进行控制,对于基于集团性的应用系统而言,就更多需要控制好各自公司的数据了。如设置只能看本公司、或者本部门的数据,对于特殊的领导,可能需要跨部门的数据,因此程序不能硬编码那个领导该访问哪些数据,需要进行后台的权限和数据权限的控制。
默认系统管理员admin拥有所有数据权限(userId=1),默认角色拥有所有数据权限(如不需要数据权限不用设置数据权限操作)
1、在(系统管理-角色管理)设置需要数据权限的角色目前支持以下几种权限
2、在需要数据权限控制方法上添加@DataScope注解,其中d和u用来表示表的别名
部门数据权限注解
@DataScope(deptAlias="d")publicList<...>select(...){returnmapper.select(...);}12345部门及用户权限注解
@DataScope(deptAlias="d",userAlias="u")publicList<...>select(...){returnmapper.select(...);}123453、在mybatis查询底部标签添加数据范围过滤
selectu.user_id,u.dept_id,u.login_name,u.user_name,u.email,u.phonenumber,u.password,u.sex,u.avatar,u.salt,u.status,u.del_flag,u.login_ip,u.login_date,u.create_by,u.create_time,u.remark,d.dept_namefromsys_useruleftjoinsys_deptdonu.dept_id=d.dept_idwhereu.del_flag='0'1234567例如:用户管理(已过滤数据权限的情况):
selectu.user_id,u.dept_id,u.login_name,u.user_name,u.email,u.phonenumber,u.password,u.sex,u.avatar,u.salt,u.status,u.del_flag,u.login_ip,u.login_date,u.create_by,u.create_time,u.remark,d.dept_namefromsys_useruleftjoinsys_deptdonu.dept_id=d.dept_idwhereu.del_flag='0'andu.dept_idin(selectdept_idfromsys_role_deptwhererole_id=2)123456789101112结果很明显,我们多了如下语句。通过角色部门表(sys_role_dept)完成了数据权限过滤
andu.dept_idin(selectdept_idfromsys_role_deptwhererole_id=2)12345逻辑实现代码com.ruoyi.framework.aspectj.DataScopeAspect
仅实体继承BaseEntity才会进行处理,SQL语句会存放到BaseEntity对象中的params属性中,然后在xml中通过${params.dataScope}获取拼接后的语句。PS:如果是自己的业务表需要实现数据权限,需要有dept_id和user_id这两个字段。
在实际开发中,经常可能遇到在一个应用中可能需要访问多个数据库的情况,在项目中使用注解来完成此项功能。
在需要被切换数据源的Service或Mapper方法上添加@DataSource注解,使用方法如下:
@DataSource(value=DataSourceType.MASTER)publicList<...>select(...){returnmapper.select(...);}12345其中value用来表示数据源名称,除MASTER和SLAVE其他均需要进行配置。
1、在application-druid.yml配置从库数据源
#从库数据源slave:#从数据源开关/默认关闭enabled:trueurl:jdbc:mysql://localhost:3306/testuseUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username:rootpassword:password12345672、在DataSourceType类添加数据源枚举
/***从库*/SLAVE12343、在DruidConfig配置读取数据源
@Bean@ConfigurationProperties("spring.datasource.druid.slave")@ConditionalOnProperty(prefix="spring.datasource.druid.slave",name="enabled",havingValue="true")publicDataSourceslaveDataSource(DruidPropertiesdruidProperties){DruidDataSourcedataSource=DruidDataSourceBuilder.create().build();returndruidProperties.dataSource(dataSource);}123456784、在DruidConfig类dataSource方法添加数据源
setDataSource(targetDataSources,DataSourceType.SLAVE.name(),"slaveDataSource");15、在需要使用多数据源方法或类上添加@DataSource注解,其中value用来表示数据源
publicList
注意:目前配置了一个从库,默认关闭状态。如果不需要多数据源不用做任何配置。另外可新增多个从库。支持不同数据源(Mysql、Oracle、SQLServer)
如果有Service方法内多个注解无效的情况使用内部方法调用SpringUtils.getAopProxy(this).xxxxxx(xxxx);
大部分项目里其实有很多代码都是重复的,几乎每个基础模块的代码都有增删改查的功能,而这些功能都是大同小异,如果这些功能都要自己去写,将会大大浪费我们的精力降低效率。所以这种重复性的代码可以使用代码生成。
单应用在resources目录下的application.yml,多模块ruoyi-generator中的resources目录下的generator.yml,可以自己根据实际情况调整默认配置。
2、代码生成列表中找到需要表(可预览、编辑、同步、删除生成配置)
3、点击生成代码会得到一个ruoyi.zip执行sql文件,按照包内目录结构复制到自己的项目中即可
代码生成支持编辑、预览、同步
预览:对生成的代码提前预览,防止出现一些不符合预期的情况。
同步:对原表的字段进行同步,包括新增、删除、修改的字段处理。
修改:对生成的代码基本信息、字段信息、生成信息做一系列的调整。
在实际项目开发中Web应用有一类不可缺少的,那就是定时任务。定时任务的场景可以说非常广泛,比如某些视频网站,购买会员后,每天会给会员送成长值,每月会给会员送一些电影券;比如在保证最终一致性的场景中,往往利用定时任务调度进行一些比对工作;比如一些定时需要生成的报表、邮件;比如一些需要定时清理数据的任务等。所以我们提供方便友好的web界面,实现动态管理任务,可以达到动态控制定时任务启动、暂停、重启、删除、添加、修改等操作,极大地方便了开发过程。
关于定时任务使用流程
1、后台添加定时任务处理类(支持Bean调用、Class类调用)Bean调用示例:需要添加对应Bean注解@Component或@Service。调用目标字符串:ryTask.ryParams('ry')Class类调用示例:添加类和方法指定包即可。调用目标字符串:com.ruoyi.quartz.task.RyTask.ryParams('ry')
/***定时任务调度测试**@authorruoyi*/@Component("ryTask")publicclassRyTask{publicvoidryMultipleParams(Strings,Booleanb,Longl,Doubled,Integeri){System.out.println(StringUtils.format("执行多参方法:字符串类型{},布尔类型{},长整型{},浮点型{},整形{}",s,b,l,d,i));}publicvoidryParams(Stringparams){System.out.println("执行有参方法:"+params);}publicvoidryNoParams(){System.out.println("执行无参方法");}}12345678910111213141516171819202122232、前端新建定时任务信息(系统监控->定时任务)任务名称:自定义,如:定时查询任务状态任务分组:根据字典sys_job_group配置调用目标字符串:设置后台任务方法名称参数执行表达式:可查询官方cron表达式介绍执行策略:定时任务自定义执行策略并发执行:是否需要多个任务间同时执行状态:是否启动定时任务备注:定时任务描述信息
3、点击执行一次,测试定时任务是否正常及调度日志是否正确记录,如正常执行表示任务配置成功。
执行策略详解:立即执行(所有misfire的任务会马上执行)打个比方,如果9点misfire了,在10:15系统恢复之后,9点,10点的misfire会马上执行执行一次(会合并部分的misfire,正常执行下一个周期的任务)假设9,10的任务都misfire了,系统在10:15分起来了。只会执行一次misfire,下次正点执行。放弃执行(所有的misfire不管,执行下一个周期的任务)
方法参数详解:字符串(需要单引号''标识如:ryTask.ryParams(’ry’))布尔类型(需要truefalse标识如:ryTask.ryParams(true))长整型(需要L标识如:ryTask.ryParams(2000L))浮点型(需要D标识如:ryTask.ryParams(316.50D))整型(纯数字即可)
cron表达式语法:[秒][分][小时][日][月][周][年]
常用表达式例子:
注意:不同数据源定时任务都有对应脚本,Oracle、Mysql已经有了,其他的可自行下载执行
在现在的开发过程中还有很大一部分公司都是以口口相传的方式来进行前后端的联调,而接口文档很大一部分都只停留在了说说而已的地步,或者写了代码再写文档。还有一点就是文档的修改,定义好的接口并不是一成不变的,可能在开发过程中文档修改不止一次的变化,这个时候就会很难受了。只要不是强制性要求,没人会愿意写这东西,而且在写的过程中,一个字母的错误就会导致联调时候的很大麻烦,但是通过Swagger,我们可以省略了这一步,而且文档出错率近乎于零,只要你在写代码的时候,稍加几个注解,文档自动生成。
1、在控制层Controller中添加注解来描述接口信息如:
@Api("参数配置")@Controller@RequestMapping("/system/config")publicclassConfigController12342、在方法中配置接口的标题信息
注意:SwaggerConfig可以指定根据注解或者包名扫描具体的API
API详细说明
api标记,用在类上,说明该类的作用。可以标记一个Controller类做为Swagger文档资源,使用方式:
@Api(value="/user",description="用户管理")1与Controller注解并列使用。属性配置:
ApiOperation标记,用在方法上,说明方法的作用,每一个url资源的定义,使用方式:
@ApiOperation("获取用户信息")1与Controller中的方法并列使用,属性配置:
ApiParam标记,请求属性,使用方式:
publicTableDataInfolist(@ApiParam(value="查询用户列表",required=true)Useruser)1与Controller中的方法并列使用,属性配置:
ApiResponse标记,响应配置,使用方式:
@ApiResponse(code=400,message="查询用户失败")1与Controller中的方法并列使用,属性配置:
ApiResponses标记,响应集配置,使用方式:
@ApiResponses({@ApiResponse(code=400,message="无效的用户")})1与Controller中的方法并列使用,属性配置:
ResponseHeader标记,响应头设置,使用方法
@ResponseHeader(name="head",description="响应头设计")1与Controller中的方法并列使用,属性配置:
在接口方法上添加@RepeatSubmit注解即可,注解参数说明:
示例1:采用默认参数
1、修改I18nConfig设置默认语言,如默认中文:
//默认语言,英文可以设置Locale.USslr.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);122、修改配置application.yml中的basename国际化文件,默认是i18n路径下messages文件(比如现在国际化文件是xx_zh_CN.properties、xx_en_US.properties,那么basename配置应为是i18n/xx
spring:#资源信息messages:#国际化资源文件路径basename:static/i18n/messages123453、i18n目录文件下定义资源文件美式英语messages_en_US.properties
user.login.username=Usernameuser.login.password=Passworduser.login.code=Securitycodeuser.login.remember=Remembermeuser.login.submit=SignIn12345中文简体messages_zh_CN.properties
MessageUtils.message("user.login.username")MessageUtils.message("user.login.password")MessageUtils.message("user.login.code")MessageUtils.message("user.login.remember")MessageUtils.message("user.login.submit")12345Excel导出标题国际化在MessageUtils.java新增解析国际化parseMessage方法。
//写入列信息attr.name()修改为MessageUtils.parseMessage(attr.name())//cell.setCellValue(attr.name());这里是注释原方法cell.setCellValue(MessageUtils.parseMessage(attr.name()));//修改后方法123在任意需要导出的实体类属性上,修改注解name为国际化key,注意中间加${},示例。
1、新建业务模块目录,例如:ruoyi-test。
2、在ruoyi-test业务模块下新建pom.xml文件以及src\main\java,src\main\resources目录。
在ruoyi-test业务模块添加com.ruoyi.test包,新建TestService.java
publicclassTestService{publicStringhelloTest(){return"hello";}}1234567在ruoyi-admin新建测试类,调用helloTest成功返回hello代表成功。