后台手册2

## 数据权限 在实际开发中,需要设置用户只能查看哪些部门的数据,这种情况一般称为数据权限。 例如对于销售,财务的数据,它们是非常敏感的,因此要求对数据权限进行控制, 对于基于集团性的应用系统而言,就更多需要控制好各自公司的数据了。如设置只能看本公司、或者本部门的数据,对于特殊的领导,可能需要跨部门的数据, 因此程序不能硬编码那个领导该访问哪些数据,需要进行后台的权限和数据权限的控制。 > 提示 > 默认系统管理员admin拥有所有数据权限(userId=1),默认角色拥有所有数据权限(如不需要数据权限不用设置数据权限操作) ### 注解参数说明 |参数|类型|默认值|描述| |-|-|-|-| |deptAlias|String|空|部门表的别名| |userAlias|String|空|用户表的别名| ### 数据权限使用 1、在(系统管理-角色管理)设置需要数据权限的角色 目前支持以下几种权限 - 全部数据权限 - 自定数据权限 - 部门数据权限 - 部门及以下数据权限 - 仅本人数据权限 2、在需要数据权限控制方法上添加@DataScope注解,其中d和u用来表示表的别名 部门数据权限注解 ```java @DataScope(deptAlias = "d") public List<...> select(...) { return mapper.select(...); } ``` 部门及用户权限注解 ```java @DataScope(deptAlias = "d", userAlias = "u") public List<...> select(...) { return mapper.select(...); } ``` 3、在mybatis查询底部标签添加数据范围过滤 ```xml <select id="select" parameterType="..." resultMap="...Result"> <include refid="select...Vo"/> <!-- 数据范围过滤 --> ${params.dataScope} </select> ``` 例如:用户管理(未过滤数据权限的情况): ```sql select u.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_name from sys_user u left join sys_dept d on u.dept_id = d.dept_id where u.del_flag = '0' ``` 例如:用户管理(已过滤数据权限的情况): ```sql select u.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_name from sys_user u left join sys_dept d on u.dept_id = d.dept_id where u.del_flag = '0' and u.dept_id in ( select dept_id from sys_role_dept where role_id = 2 ) ``` 结果很明显,我们多了如下语句。通过角色部门表(sys_role_dept)完成了数据权限过滤 ```sql and u.dept_id in ( select dept_id from sys_role_dept where role_id = 2 ) ``` 逻辑实现代码 com.ruoyi.framework.aspectj.DataScopeAspect > 提示 > 仅实体继承BaseEntity才会进行处理,SQL语句会存放到BaseEntity对象中的params属性中,然后在xml中通过${params.dataScope}获取拼接后的语句。 ## 多数据源 在实际开发中,经常可能遇到在一个应用中可能需要访问多个数据库的情况,在项目中使用注解来完成此项功能。 在需要被切换数据源的Service或Mapper方法上添加@DataSource注解,使用方法如下: ```java @DataSource(value = DataSourceType.MASTER) public List<...> select(...) { return mapper.select(...); } ``` 其中value用来表示数据源名称,除MASTER和SLAVE其他均需要进行配置。 #注解参数说明 |参数|类型|默认值|描述| |-|-|-|-| |value |DataSourceType|DataSourceType.MASTER |主库| ### 多数据源使用 1、在application-druid.yml配置从库数据源 ```taml # 从库数据源 slave: # 从数据源开关/默认关闭 enabled: true url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: password ``` 2、在DataSourceType类添加数据源枚举 ```java /** * 从库 */ SLAVE ``` 3、在DruidConfig配置读取数据源 ```java @Bean @ConfigurationProperties("spring.datasource.druid.slave") @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") public DataSource slaveDataSource(DruidProperties druidProperties) { DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); return druidProperties.dataSource(dataSource); } ``` 4、在DruidConfig类dataSource方法添加数据源 ```java setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); ``` 5、在需要使用多数据源方法或类上添加@DataSource注解,其中value用来表示数据源 ```java @DataSource(value = DataSourceType.SLAVE) public List<SysUser> selectUserList(SysUser user) { return userMapper.selectUserList(user); } ``` ```java @Service @DataSource(value = DataSourceType.SLAVE) public class SysUserServiceImpl ``` ### 手动切换数据源 在需要切换数据源的方法中使用DynamicDataSourceContextHolder类实现手动切换,使用方法如下: ```java public List<SysUser> selectUserList(SysUser user) { DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name()); List<SysUser> userList = userMapper.selectUserList(user); DynamicDataSourceContextHolder.clearDataSourceType(); return userList; } ``` 逻辑实现代码 com.ruoyi.framework.aspectj.DataSourceAspect > 注意:目前配置了一个从库,默认关闭状态。如果不需要多数据源不用做任何配置。 另外可新增多个从库。支持不同数据源(Mysql、Oracle、SQLServer) > 提示 > 如果有Service方法内多个注解无效的情况使用内部方法调用SpringUtils.getAopProxy(this).xxxxxx(xxxx); ## 代码生成 大部分项目里其实有很多代码都是重复的,几乎每个基础模块的代码都有增删改查的功能,而这些功能都是大同小异, 如果这些功能都要自己去写,将会大大浪费我们的精力降低效率。所以这种重复性的代码可以使用代码生成。 ### 默认配置 单应用在resources目录下的application.yml,多模块ruoyi-generator中的resources目录下的generator.yml,可以自己根据实际情况调整默认配置。 ```yaml # 代码生成 gen: # 开发者姓名,生成到类注释上 author: ruoyi # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool packageName: com.ruoyi.system # 自动去除表前缀,默认是false autoRemovePre: false # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) tablePrefix: sys_ ``` ### 单表结构 新建数据库表结构(单表) ```sql drop table if exists sys_student; create table sys_student ( student_id int(11) auto_increment comment '编号', student_name varchar(30) default '' comment '学生名称', student_age int(3) default null comment '年龄', student_hobby varchar(30) default '' comment '爱好(0代码 1音乐 2电影)', student_sex char(1) default '0' comment '性别(0男 1女 2未知)', student_status char(1) default '0' comment '状态(0正常 1停用)', student_birthday datetime comment '生日', primary key (student_id) ) engine=innodb auto_increment=1 comment = '学生信息表'; ``` ### 树表结构 新建数据库表结构(树表) ```sql drop table if exists sys_product; create table sys_product ( product_id bigint(20) not null auto_increment comment '产品id', parent_id bigint(20) default 0 comment '父产品id', product_name varchar(30) default '' comment '产品名称', order_num int(4) default 0 comment '显示顺序', status char(1) default '0' comment '产品状态(0正常 1停用)', primary key (product_id) ) engine=innodb auto_increment=1 comment = '产品表'; ``` ### 主子表结构 新建数据库表结构(主子表) ```sql -- ---------------------------- -- 客户表 -- ---------------------------- drop table if exists sys_customer; create table sys_customer ( customer_id bigint(20) not null auto_increment comment '客户id', customer_name varchar(30) default '' comment '客户姓名', phonenumber varchar(11) default '' comment '手机号码', sex varchar(20) default null comment '客户性别', birthday datetime comment '客户生日', remark varchar(500) default null comment '客户描述', primary key (customer_id) ) engine=innodb auto_increment=1 comment = '客户表'; -- ---------------------------- -- 商品表 -- ---------------------------- drop table if exists sys_goods; create table sys_goods ( goods_id bigint(20) not null auto_increment comment '商品id', customer_id bigint(20) not null comment '客户id', name varchar(30) default '' comment '商品名称', weight int(5) default null comment '商品重量', price decimal(6,2) default null comment '商品价格', date datetime comment '商品时间', type char(1) default null comment '商品种类', primary key (goods_id) ) engine=innodb auto_increment=1 comment = '商品表'; ``` ## 代码生成使用 1、登录系统(系统工具 -> 代码生成 -> 导入对应表) 2、代码生成列表中找到需要表(可预览、编辑、同步、删除生成配置) 3、点击生成代码会得到一个ruoyi.zip执行sql文件,按照包内目录结构复制到自己的项目中即可 > 代码生成支持编辑、预览、同步 > 预览:对生成的代码提前预览,防止出现一些不符合预期的情况。 > 同步:对原表的字段进行同步,包括新增、删除、修改的字段处理。 > 修改:对生成的代码基本信息、字段信息、生成信息做一系列的调整。 另外多模块所有代码生成的相关业务逻辑代码在ruoyi-generator模块,不需要可以自行删除模块。 ## 定时任务 在实际项目开发中Web应用有一类不可缺少的,那就是定时任务。 定时任务的场景可以说非常广泛,比如某些视频网站,购买会员后,每天会给会员送成长值,每月会给会员送一些电影券; 比如在保证最终一致性的场景中,往往利用定时任务调度进行一些比对工作;比如一些定时需要生成的报表、邮件;比如一些需要定时清理数据的任务等。 所以我们提供方便友好的web界面,实现动态管理任务,可以达到动态控制定时任务启动、暂停、重启、删除、添加、修改等操作,极大地方便了开发过程。 > 提示 > 关于定时任务使用流程 1、后台添加定时任务处理类(支持Bean调用、Class类调用) Bean调用示例:需要添加对应Bean注解@Component或@Service。调用目标字符串:ryTask.ryParams('ry') Class类调用示例:添加类和方法指定包即可。调用目标字符串:com.ruoyi.quartz.task.RyTask.ryParams('ry') ```java /** * 定时任务调度测试 * * @author ruoyi */ @Component("ryTask") public class RyTask { public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) { System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i)); } public void ryParams(String params) { System.out.println("执行有参方法:" + params); } public void ryNoParams() { System.out.println("执行无参方法"); } } ``` 2、前端新建定时任务信息(系统监控 -> 定时任务) - 任务名称:自定义,如:定时查询任务状态 - 任务分组:根据字典sys_job_group配置 - 调用目标字符串:设置后台任务方法名称参数 - 执行表达式:可查询官方cron表达式介绍 - 执行策略:定时任务自定义执行策略 - 并发执行:是否需要多个任务间同时执行 - 状态:是否启动定时任务 - 备注:定时任务描述信息 3、点击执行一次,测试定时任务是否正常及调度日志是否正确记录,如正常执行表示任务配置成功。 **执行策略详解:** - 立即执行(所有misfire的任务会马上执行)打个比方,如果9点misfire了,在10:15系统恢复之后,9点,10点的misfire会马上执行 - 执行一次(会合并部分的misfire,正常执行下一个周期的任务)假设9,10的任务都misfire了,系统在10:15分起来了。只会执行一次misfire,下次正点执行。 - 放弃执行(所有的misfire不管,执行下一个周期的任务) **方法参数详解:** - 字符串(需要单引号''标识 如:ryTask.ryParams(’ry’)) - 布尔类型(需要true false标识 如:ryTask.ryParams(true)) - 长整型(需要L标识 如:ryTask.ryParams(2000L)) - 浮点型(需要D标识 如:ryTask.ryParams(316.50D)) - 整型(纯数字即可) cron表达式语法: [秒] [分] [小时] [日] [月] [周] [年] |说明 |必填|允许填写的值|允许的通配符| |-|-|-|-| |秒|是|0-59|, - * /| |分|是|0-59|, - * /| |时|是|0-23|, - * /| |日|是|1-31|, - * /| |月|是|1-12 / JAN-DEC|, - * ? / L W| |周|是|1-7 or SUN-SAT|, - * ? / L #| |年||是|1970-2099|, - * / **通配符说明:** `* `表示所有值。 例如:在分的字段上设置 *,表示每一分钟都会触发 `? `表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为”?” 具体设置为 0 0 0 10 * ? `- `表示区间。例如 在小时上设置 “10-12”,表示 10,11,12点都会触发 `, `表示指定多个值,例如在周字段上设置 “MON,WED,FRI” 表示周一,周三和周五触发 `/ `用于递增触发。如在秒上面设置”5/15” 表示从5秒开始,每增15秒触发(5,20,35,50)。 在月字段上设置’1/3’所示每月1号开始,每隔三天触发一次 `L `表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于”7”或”SAT”。如果在”L”前加上数字,则表示该数据的最后一个。例如在周字段上设置”6L”这样的格式,则表示“本月最后一个星期五” `W `表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上置”15W”,表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 “1W”,它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,”W”前只能设置具体的数字,不允许区间”-“) `# `序号(表示每月的第几个周几),例如在周字段上设置”6#3”表示在每月的第三个周六.注意如果指定”#5”,正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了) ;小提示:’L’和 ‘W’可以一组合使用。如果在日字段上设置”LW”,则表示在本月的最后一个工作日触发;周字段的设置,若使用英文字母是不区分大小写的,即MON与mon相同 **常用表达式例子**: |表达式|说明| |-|-| |0 0 2 1 * ? *|表示在每月的1日的凌晨2点调整任务| |0 15 10 ? * MON-FRI|表示周一到周五每天上午10:15执行作业| |0 15 10 ? 6L 2002-2006|表示2002-2006年的每个月的最后一个星期五上午10:15执行作| |0 0 10,14,16 * * ?|每天上午10点,下午2点,4点| |0 0/30 9-17 * * ?|朝九晚五工作时间内每半小时| |0 0 12 ? * WED|表示每个星期三中午12点| |0 0 12 * * ?|每天中午12点触发| |0 15 10 ? * *|每天上午10:15触发| |0 15 10 * * ?|每天上午10:15触发| |0 15 10 * * ? *|每天上午10:15触发| |0 15 10 * * ? 2005|2005年的每天上午10:15触发| |0 * 14 * * ?|在每天下午2点到下午2:59期间的每1分钟触发| |0 0/5 14 * * ?|在每天下午2点到下午2:55期间的每5分钟触发| |0 0/5 14,18 * * ?|在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发| |0 0-5 14 * * ?|在每天下午2点到下午2:05期间的每1分钟触发| |0 10,44 14 ? 3 WED|每年三月的星期三的下午2:10和2:44触发| |0 15 10 ? * MON-FRI|周一至周五的上午10:15触发| |0 15 10 15 * ?|每月15日上午10:15触发| |0 15 10 L * ?|每月最后一日的上午10:15触发| |0 15 10 ? * 6L|每月的最后一个星期五上午10:15触发| |0 15 10 ? * 6L 2002-2005|2002年至2005年的每月的最后一个星期五上午10:15触发| |0 15 10 ? * 6#3|每月的第三个星期五上午10:15触发| 多模块所有定时任务的相关业务逻辑代码在ruoyi-quartz模块,可以自行调整或剔除 > 注意:不同数据源定时任务都有对应脚本,Oracle、Mysql已经有了,其他的可自行下载执行 ## 系统接口 在现在的开发过程中还有很大一部分公司都是以口口相传的方式来进行前后端的联调,而接口文档很大一部分都只停留在了说说而已的地步,或者写了代码再写文档。 还有一点就是文档的修改,定义好的接口并不是一成不变的,可能在开发过程中文档修改不止一次的变化,这个时候就会很难受了。 只要不是强制性要求,没人会愿意写这东西,而且在写的过程中,一个字母的错误就会导致联调时候的很大麻烦,但是通过Swagger,我们可以省略了这一步,而且文档出错率近乎于零, 只要你在写代码的时候,稍加几个注解,文档自动生成。 1、在控制层Controller中添加注解来描述接口信息如: ```java @Api("参数配置") @Controller @RequestMapping("/system/config") public class ConfigController ``` 2、在方法中配置接口的标题信息 ```java @ApiOperation("查询参数列表") @ResponseBody public TableDataInfo list(Config config) { startPage(); List<Config> list = configService.selectConfigList(config); return getDataTable(list); } ``` 3、在系统工具-系统接口测试相关接口 > 注意:SwaggerConfig可以指定根据注解或者包名扫描具体的API **API详细说明** |作用范围 |API |使用位置| |-|-|-| |协议集描述|@Api|用于controller类上| |对象属性|@ApiModelProperty|用在出入参数对象的字段上| |协议描述|@ApiOperation|用在controller的方法上| |Response集|@ApiResponses|用在controller的方法上| |Response|@ApiResponse|用在 @ApiResponses里边| |非对象参数集|@ApiImplicitParams|用在controller的方法上| |非对象参数描述|@ApiImplicitParam|用在@ApiImplicitParams的方法里边| |描述返回对象的意义|@ApiModel|用在返回对象类上| `api`标记,用在类上,说明该类的作用。可以标记一个Controller类做为Swagger文档资源,使用方式: ```java @Api(value = "/user", description = "用户管理") ``` 与Controller注解并列使用。 属性配置: |属性名称 |备注| |-|-| |value|url的路径值| |tags|如果设置这个值、value的值会被覆盖| |description|对api资源的描述| |basePath|基本路径可以不配置| |position|如果配置多个Api 想改变显示的顺序位置| |produces|For example, "application/json, application/xml"| |consumes|For example, "application/json, application/xml"| |protocols|Possible values: http, https, ws, wss.| |authorizations|高级特性认证时配置| |hidden|配置为true 将在文档中隐藏| `ApiOperation`标记,用在方法上,说明方法的作用,每一个url资源的定义,使用方式: ```java @ApiOperation("获取用户信息") ``` 与Controller中的方法并列使用,属性配置: |属性名称 |备注| |-|-| |value|url的路径值| |tags|如果设置这个值、value的值会被覆盖| |description|对api资源的描述| |basePath|基本路径可以不配置| |position|如果配置多个Api 想改变显示的顺序位置| |produces|For example, "application/json, application/xml"| |consumes|For example, "application/json, application/xml"| |protocols|Possible values: http, https, ws, wss.| |authorizations|高级特性认证时配置| |hidden|配置为true将在文档中隐藏| |response|返回的对象| |responseContainer|这些对象是有效的 "List", "Set" or "Map".,其他无效| |httpMethod|"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" and "PATCH"| |code|http的状态码 默认 200| |extensions|扩展属性| `ApiParam`标记,请求属性,使用方式: ```java public TableDataInfo list(@ApiParam(value = "查询用户列表", required = true)User user) ``` 与Controller中的方法并列使用,属性配置: |属性名称 |备注| |-|-| |name|属性名称| |value|属性值| |defaultValue|默认属性值| |allowableValues|可以不配置| |required|是否属性必填| |access|不过多描述| |allowMultiple|默认为false| |hidden|隐藏该属性| |example|举例子| `ApiResponse`标记,响应配置,使用方式: ```java @ApiResponse(code = 400, message = "查询用户失败") ``` 与Controller中的方法并列使用,属性配置: |属性名称 |备注| |-|-| |code|http的状态码| |message|描述| |response|默认响应类 Void| |reference|参考ApiOperation中配置| |responseHeaders|参考 ResponseHeader 属性配置说明| |responseContainer|参考ApiOperation中配置| `ApiResponses`标记,响应集配置,使用方式: ```java @ApiResponses({ @ApiResponse(code = 400, message = "无效的用户") }) ``` 与Controller中的方法并列使用,属性配置: |属性名称 |备注| |-|-| |value |多个ApiResponse配置| |ResponseHeader|标记,响应头设置,使用方法| ```java @ResponseHeader(name="head",description="响应头设计") ``` 与Controller中的方法并列使用,属性配置: |属性名称 |备注| |-|-| |name|响应头名称| |description|描述| |response|默认响应类 void| |responseContainer|参考ApiOperation中配置| ## 防重复提交 在接口方法上添加@RepeatSubmit注解即可,注解参数说明: |参数|类型|默认值|描述| |-|-|-|-| |interval|int|5000|间隔时间(ms),小于此时间视为重复提交| |message|String|不允许重复提交,请稍后再试|提示消息| 示例1:采用默认参数 ```java @RepeatSubmit public AjaxResult addSave(...) { return success(...); } ``` 示例2:指定防重复时间和错误消息 ```java @RepeatSubmit(interval = 1000, message = "请求过于频繁") public AjaxResult addSave(...) { return success(...); } ``` ## 国际化支持 在我们开发WEB项目的时候,项目可能涉及到在国外部署或者应用,也有可能会有国外的用户对项目进行访问,那么在这种项目中, 为客户展现的页面或者操作的信息就需要使用不同的语言,这就是我们所说的项目国际化。 目前项目已经支持多语言国际化,接下来我们介绍如何使用。 ### 后台国际化流程 1、修改I18nConfig设置默认语言,如默认中文: ```java // 默认语言,英文可以设置Locale.US slr.setDefaultLocale(Locale.SIMPLIFIED_CHINESE); ``` 2、修改配置application.yml中的basename国际化文件,默认是i18n路径下messages文件 (比如现在国际化文件是xx_zh_CN.properties、xx_en_US.properties,那么basename配置应为是i18n/xx ```yaml spring: # 资源信息 messages: # 国际化资源文件路径 basename: static/i18n/messages ``` 3、i18n目录文件下定义资源文件 美式英语 messages_en_US.properties ```propertise user.login.username=User name user.login.password=Password user.login.code=Security code user.login.remember=Remember me user.login.submit=Sign In ``` 中文简体 messages_zh_CN.properties ```propertise user.login.username=用户名 user.login.password=密码 user.login.code=验证码 user.login.remember=记住我 user.login.submit=登录 ``` 4、java代码使用MessageUtils获取国际化 ```java 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") ``` ### 前端国际化流程 1、html使用国际化#{资源文件key} ```js <form id="signupForm"> <h4 class="no-margins">登录:</h4> <p class="m-t-md">你若不离不弃,我必生死相依</p> <input type="text" name="username" class="form-control uname" th:placeholder="#{user.login.username}" /> <input type="password" name="password" class="form-control pword" th:placeholder="#{user.login.password}" /> <div class="row m-t" th:if="${captchaEnabled==true}"> <div class="col-xs-6"> <input type="text" name="validateCode" class="form-control code" th:placeholder="#{user.login.code}" maxlength="5" autocomplete="off"> </div> <div class="col-xs-6"> <a href="javascript:void(0);" title="点击更换验证码"> <img th:src="@{captcha/captchaImage(type=${captchaType})}" class="imgcode" width="85%"/> </a> </div> </div> <div class="checkbox-custom" th:classappend="${captchaEnabled==false} ? 'm-t'"> <input type="checkbox" id="rememberme" name="rememberme"> <label for="rememberme" th:text="#{user.login.remember}">记住我</label> </div> <button class="btn btn-success btn-block" id="btnSubmit" data-loading="正在验证登录,请稍后..." th:text="#{user.login.submit}">登录</button> </form> ``` 2、js使用国际化 首先在文件引入jquery-i18n-properties依赖,然后在初始化后即可通过JS函数获取对应国际化文件的内容。 ```js <!--jQuery国际化插件--> <script src="../static/js/jquery.i18n.properties.min.js" th:src="@{/js/jquery.i18n.properties.min.js}"></script> <script th:inline="javascript"> //获取应用路径 var ROOT = [[${#servletContext.contextPath}]]; //获取默认语言 var LANG_COUNTRY = [[${#locale.language+'_'+#locale.country}]]; //初始化i18n插件 $.i18n.properties({ path: ROOT + '/i18n/',//这里表示访问路径 name: 'messages',//文件名开头 language: LANG_COUNTRY,//文件名语言 例如en_US mode: 'map'//默认值 }); //初始化i18n函数 function i18n(msgKey) { try { return $.i18n.prop(msgKey); } catch (e) { return msgKey; } } //获取国际化翻译值 console.log(i18n('user.login.username')); console.log(i18n('user.login.password')); console.log(i18n('user.login.code')); console.log(i18n('user.login.remember')); console.log(i18n('user.login.submit')); </script> ``` 3、界面定义切换语言 ```js <a href="?lang=en_US"> 英语 </a> <a href="?lang=zh_CN"> 中文 </a> ``` ## 新建子模块 Maven多模块下新建子模块流程案例。 1、新建业务模块目录,例如:ruoyi-test。 2、在ruoyi-test业务模块下新建pom.xml文件以及src\main\java,src\main\resources目录。 ```xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>ruoyi</artifactId> <groupId>com.ruoyi</groupId> <version>x.x.x</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>ruoyi-test</artifactId> <description> test系统模块 </description> <dependencies> <!-- 通用工具--> <dependency> <groupId>com.ruoyi</groupId> <artifactId>ruoyi-common</artifactId> </dependency> </dependencies> </project> ``` 3、根目录pom.xml依赖声明节点dependencies中添加依赖 ```xml <!-- 测试模块--> <dependency> <groupId>com.ruoyi</groupId> <artifactId>ruoyi-test</artifactId> <version>${ruoyi.version}</version> </dependency> ``` 4、根目录pom.xml模块节点modules添加业务模块 ```xml <module>ruoyi-test</module> ``` 5、ruoyi-admin目录pom.xml添加模块依赖 ```xml <!-- 测试模块--> <dependency> <groupId>com.ruoyi</groupId> <artifactId>ruoyi-test</artifactId> </dependency> ``` 6、测试模块 在ruoyi-test业务模块添加com.ruoyi.test包,新建TestService.java ```java public class TestService { public String helloTest() { return "hello"; } } ``` 在ruoyi-admin新建测试类,调用helloTest成功返回hello代表成功。