| @ -0,0 +1,46 @@ | |||
| ###################################################################### | |||
| # Build Tools | |||
| .gradle | |||
| /build/ | |||
| !gradle/wrapper/gradle-wrapper.jar | |||
| target/ | |||
| !.mvn/wrapper/maven-wrapper.jar | |||
| ###################################################################### | |||
| # IDE | |||
| ### STS ### | |||
| .apt_generated | |||
| .classpath | |||
| .factorypath | |||
| .project | |||
| .settings | |||
| .springBeans | |||
| ### IntelliJ IDEA ### | |||
| .idea | |||
| *.iws | |||
| *.iml | |||
| *.ipr | |||
| ### JRebel ### | |||
| rebel.xml | |||
| ### NetBeans ### | |||
| nbproject/private/ | |||
| build/* | |||
| nbbuild/ | |||
| dist/ | |||
| nbdist/ | |||
| .nb-gradle/ | |||
| ###################################################################### | |||
| # Others | |||
| *.log | |||
| *.xml.versionsBackup | |||
| *.swp | |||
| !*/build/*.java | |||
| !*/build/*.html | |||
| !*/build/*.xml | |||
| @ -0,0 +1 @@ | |||
| <h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">华西海圻</h1 | |||
| @ -0,0 +1,12 @@ | |||
| @echo off | |||
| echo. | |||
| echo [信息] 清理工程target生成路径。 | |||
| echo. | |||
| %~d0 | |||
| cd %~dp0 | |||
| cd .. | |||
| call mvn clean | |||
| pause | |||
| @ -0,0 +1,12 @@ | |||
| @echo off | |||
| echo. | |||
| echo [信息] 打包Web工程,生成war/jar包文件。 | |||
| echo. | |||
| %~d0 | |||
| cd %~dp0 | |||
| cd .. | |||
| call mvn clean package -Dmaven.test.skip=true | |||
| pause | |||
| @ -0,0 +1,14 @@ | |||
| @echo off | |||
| echo. | |||
| echo hxhq-auth | |||
| echo. | |||
| cd %~dp0 | |||
| cd ../hxhq-auth/target | |||
| set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m | |||
| java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar hxhq-auth.jar | |||
| cd bin | |||
| pause | |||
| @ -0,0 +1,14 @@ | |||
| @echo off | |||
| echo. | |||
| echo hxhq-gateway | |||
| echo. | |||
| cd %~dp0 | |||
| cd ../hxhq-gateway/target | |||
| set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m | |||
| java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar hxhq-gateway.jar | |||
| cd bin | |||
| pause | |||
| @ -0,0 +1,14 @@ | |||
| @echo off | |||
| echo. | |||
| echo hxhq-modules-file | |||
| echo. | |||
| cd %~dp0 | |||
| cd ../hxhq-modules/hxhq-file/target | |||
| set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m | |||
| java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar hxhq-modules-file.jar | |||
| cd bin | |||
| pause | |||
| @ -0,0 +1,14 @@ | |||
| @echo off | |||
| echo. | |||
| echo hxhq-modules-gen | |||
| echo. | |||
| cd %~dp0 | |||
| cd ../hxhq-modules/hxhq-gen/target | |||
| set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m | |||
| java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar hxhq-modules-gen.jar | |||
| cd bin | |||
| pause | |||
| @ -0,0 +1,14 @@ | |||
| @echo off | |||
| echo. | |||
| echo hxhq-modules-job | |||
| echo. | |||
| cd %~dp0 | |||
| cd ../hxhq-modules/hxhq-job/target | |||
| set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m | |||
| java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar hxhq-modules-job.jar | |||
| cd bin | |||
| pause | |||
| @ -0,0 +1,14 @@ | |||
| @echo off | |||
| echo. | |||
| echo hxhq-modules-systemhxhq-modules-system | |||
| echo. | |||
| cd %~dp0 | |||
| cd ../hxhq-modules/hxhq-system/target | |||
| set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m | |||
| java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar hxhq-modules-system.jar | |||
| cd bin | |||
| pause | |||
| @ -0,0 +1,14 @@ | |||
| @echo off | |||
| echo. | |||
| echo hxhq-visual-monitor | |||
| echo. | |||
| cd %~dp0 | |||
| cd ../hxhq-visual/hxhq-monitor/target | |||
| set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m | |||
| java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar hxhq-visual-monitor.jar | |||
| cd bin | |||
| pause | |||
| @ -0,0 +1,28 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xmlns="http://maven.apache.org/POM/4.0.0" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
| <parent> | |||
| <groupId>com.hxhq</groupId> | |||
| <artifactId>hxhq-api</artifactId> | |||
| <version>3.6.6</version> | |||
| </parent> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <artifactId>hxhq-api-system</artifactId> | |||
| <description> | |||
| hxhq-api-system系统接口模块 | |||
| </description> | |||
| <dependencies> | |||
| <!-- hxhq Common Core--> | |||
| <dependency> | |||
| <groupId>com.hxhq</groupId> | |||
| <artifactId>hxhq-common-core</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| </project> | |||
| @ -0,0 +1,40 @@ | |||
| package com.hxhq.system.api; | |||
| import org.springframework.cloud.openfeign.FeignClient; | |||
| import org.springframework.http.MediaType; | |||
| import org.springframework.web.bind.annotation.DeleteMapping; | |||
| import org.springframework.web.bind.annotation.PostMapping; | |||
| import org.springframework.web.bind.annotation.RequestParam; | |||
| import org.springframework.web.bind.annotation.RequestPart; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| import com.hxhq.common.core.constant.ServiceNameConstants; | |||
| import com.hxhq.common.core.domain.R; | |||
| import com.hxhq.system.api.domain.SysFile; | |||
| import com.hxhq.system.api.factory.RemoteFileFallbackFactory; | |||
| /** | |||
| * 文件服务 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @FeignClient(contextId = "remoteFileService", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class) | |||
| public interface RemoteFileService | |||
| { | |||
| /** | |||
| * 上传文件 | |||
| * | |||
| * @param file 文件信息 | |||
| * @return 结果 | |||
| */ | |||
| @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) | |||
| public R<SysFile> upload(@RequestPart(value = "file") MultipartFile file); | |||
| /** | |||
| * 删除文件 | |||
| * | |||
| * @param fileUrl 文件地址 | |||
| * @return 结果 | |||
| */ | |||
| @DeleteMapping(value = "/delete", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | |||
| public R<Boolean> delete(@RequestParam("fileUrl") String fileUrl); | |||
| } | |||
| @ -0,0 +1,41 @@ | |||
| package com.hxhq.system.api; | |||
| import org.springframework.cloud.openfeign.FeignClient; | |||
| import org.springframework.web.bind.annotation.PostMapping; | |||
| import org.springframework.web.bind.annotation.RequestBody; | |||
| import org.springframework.web.bind.annotation.RequestHeader; | |||
| import com.hxhq.common.core.constant.SecurityConstants; | |||
| import com.hxhq.common.core.constant.ServiceNameConstants; | |||
| import com.hxhq.common.core.domain.R; | |||
| import com.hxhq.system.api.domain.SysLogininfor; | |||
| import com.hxhq.system.api.domain.SysOperLog; | |||
| import com.hxhq.system.api.factory.RemoteLogFallbackFactory; | |||
| /** | |||
| * 日志服务 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @FeignClient(contextId = "remoteLogService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteLogFallbackFactory.class) | |||
| public interface RemoteLogService | |||
| { | |||
| /** | |||
| * 保存系统日志 | |||
| * @param sysOperLog | |||
| * @param source | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| @PostMapping("/operlog") | |||
| public R<Boolean> saveLog(@RequestBody SysOperLog sysOperLog, @RequestHeader(SecurityConstants.FROM_SOURCE) String source) throws Exception; | |||
| /** | |||
| * 保存访问记录 | |||
| * | |||
| * @param sysLogininfor 访问实体 | |||
| * @param source 请求来源 | |||
| * @return 结果 | |||
| */ | |||
| @PostMapping("/logininfor") | |||
| public R<Boolean> saveLogininfor(@RequestBody SysLogininfor sysLogininfor, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); | |||
| } | |||
| @ -0,0 +1,54 @@ | |||
| package com.hxhq.system.api; | |||
| import org.springframework.cloud.openfeign.FeignClient; | |||
| import org.springframework.web.bind.annotation.GetMapping; | |||
| import org.springframework.web.bind.annotation.PathVariable; | |||
| import org.springframework.web.bind.annotation.PostMapping; | |||
| import org.springframework.web.bind.annotation.PutMapping; | |||
| import org.springframework.web.bind.annotation.RequestBody; | |||
| import org.springframework.web.bind.annotation.RequestHeader; | |||
| import com.hxhq.common.core.constant.SecurityConstants; | |||
| import com.hxhq.common.core.constant.ServiceNameConstants; | |||
| import com.hxhq.common.core.domain.R; | |||
| import com.hxhq.system.api.domain.SysUser; | |||
| import com.hxhq.system.api.factory.RemoteUserFallbackFactory; | |||
| import com.hxhq.system.api.model.LoginUser; | |||
| /** | |||
| * 用户服务 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class) | |||
| public interface RemoteUserService | |||
| { | |||
| /** | |||
| * 通过用户名查询用户信息 | |||
| * | |||
| * @param username 用户名 | |||
| * @param source 请求来源 | |||
| * @return 结果 | |||
| */ | |||
| @GetMapping("/user/info/{username}") | |||
| public R<LoginUser> getUserInfo(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); | |||
| /** | |||
| * 注册用户信息 | |||
| * | |||
| * @param sysUser 用户信息 | |||
| * @param source 请求来源 | |||
| * @return 结果 | |||
| */ | |||
| @PostMapping("/user/register") | |||
| public R<Boolean> registerUserInfo(@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); | |||
| /** | |||
| * 记录用户登录IP地址和登录时间 | |||
| * | |||
| * @param sysUser 用户信息 | |||
| * @param source 请求来源 | |||
| * @return 结果 | |||
| */ | |||
| @PutMapping("/user/recordlogin") | |||
| public R<Boolean> recordUserLogin(@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); | |||
| } | |||
| @ -0,0 +1,203 @@ | |||
| package com.hxhq.system.api.domain; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| import javax.validation.constraints.Email; | |||
| import javax.validation.constraints.NotBlank; | |||
| import javax.validation.constraints.NotNull; | |||
| import javax.validation.constraints.Size; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.hxhq.common.core.web.domain.BaseEntity; | |||
| /** | |||
| * 部门表 sys_dept | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class SysDept extends BaseEntity | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| /** 部门ID */ | |||
| private Long deptId; | |||
| /** 父部门ID */ | |||
| private Long parentId; | |||
| /** 祖级列表 */ | |||
| private String ancestors; | |||
| /** 部门名称 */ | |||
| private String deptName; | |||
| /** 显示顺序 */ | |||
| private Integer orderNum; | |||
| /** 负责人 */ | |||
| private String leader; | |||
| /** 联系电话 */ | |||
| private String phone; | |||
| /** 邮箱 */ | |||
| private String email; | |||
| /** 部门状态:0正常,1停用 */ | |||
| private String status; | |||
| /** 删除标志(0代表存在 2代表删除) */ | |||
| private String delFlag; | |||
| /** 父部门名称 */ | |||
| private String parentName; | |||
| /** 子部门 */ | |||
| private List<SysDept> children = new ArrayList<SysDept>(); | |||
| public Long getDeptId() | |||
| { | |||
| return deptId; | |||
| } | |||
| public void setDeptId(Long deptId) | |||
| { | |||
| this.deptId = deptId; | |||
| } | |||
| public Long getParentId() | |||
| { | |||
| return parentId; | |||
| } | |||
| public void setParentId(Long parentId) | |||
| { | |||
| this.parentId = parentId; | |||
| } | |||
| public String getAncestors() | |||
| { | |||
| return ancestors; | |||
| } | |||
| public void setAncestors(String ancestors) | |||
| { | |||
| this.ancestors = ancestors; | |||
| } | |||
| @NotBlank(message = "部门名称不能为空") | |||
| @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") | |||
| public String getDeptName() | |||
| { | |||
| return deptName; | |||
| } | |||
| public void setDeptName(String deptName) | |||
| { | |||
| this.deptName = deptName; | |||
| } | |||
| @NotNull(message = "显示顺序不能为空") | |||
| public Integer getOrderNum() | |||
| { | |||
| return orderNum; | |||
| } | |||
| public void setOrderNum(Integer orderNum) | |||
| { | |||
| this.orderNum = orderNum; | |||
| } | |||
| public String getLeader() | |||
| { | |||
| return leader; | |||
| } | |||
| public void setLeader(String leader) | |||
| { | |||
| this.leader = leader; | |||
| } | |||
| @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") | |||
| public String getPhone() | |||
| { | |||
| return phone; | |||
| } | |||
| public void setPhone(String phone) | |||
| { | |||
| this.phone = phone; | |||
| } | |||
| @Email(message = "邮箱格式不正确") | |||
| @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") | |||
| public String getEmail() | |||
| { | |||
| return email; | |||
| } | |||
| public void setEmail(String email) | |||
| { | |||
| this.email = email; | |||
| } | |||
| public String getStatus() | |||
| { | |||
| return status; | |||
| } | |||
| public void setStatus(String status) | |||
| { | |||
| this.status = status; | |||
| } | |||
| public String getDelFlag() | |||
| { | |||
| return delFlag; | |||
| } | |||
| public void setDelFlag(String delFlag) | |||
| { | |||
| this.delFlag = delFlag; | |||
| } | |||
| public String getParentName() | |||
| { | |||
| return parentName; | |||
| } | |||
| public void setParentName(String parentName) | |||
| { | |||
| this.parentName = parentName; | |||
| } | |||
| public List<SysDept> getChildren() | |||
| { | |||
| return children; | |||
| } | |||
| public void setChildren(List<SysDept> children) | |||
| { | |||
| this.children = children; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) | |||
| .append("deptId", getDeptId()) | |||
| .append("parentId", getParentId()) | |||
| .append("ancestors", getAncestors()) | |||
| .append("deptName", getDeptName()) | |||
| .append("orderNum", getOrderNum()) | |||
| .append("leader", getLeader()) | |||
| .append("phone", getPhone()) | |||
| .append("email", getEmail()) | |||
| .append("status", getStatus()) | |||
| .append("delFlag", getDelFlag()) | |||
| .append("createBy", getCreateBy()) | |||
| .append("createTime", getCreateTime()) | |||
| .append("updateBy", getUpdateBy()) | |||
| .append("updateTime", getUpdateTime()) | |||
| .toString(); | |||
| } | |||
| } | |||
| @ -0,0 +1,176 @@ | |||
| package com.hxhq.system.api.domain; | |||
| import javax.validation.constraints.NotBlank; | |||
| import javax.validation.constraints.Size; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.hxhq.common.core.annotation.Excel; | |||
| import com.hxhq.common.core.annotation.Excel.ColumnType; | |||
| import com.hxhq.common.core.constant.UserConstants; | |||
| import com.hxhq.common.core.web.domain.BaseEntity; | |||
| /** | |||
| * 字典数据表 sys_dict_data | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class SysDictData extends BaseEntity | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| /** 字典编码 */ | |||
| @Excel(name = "字典编码", cellType = ColumnType.NUMERIC) | |||
| private Long dictCode; | |||
| /** 字典排序 */ | |||
| @Excel(name = "字典排序", cellType = ColumnType.NUMERIC) | |||
| private Long dictSort; | |||
| /** 字典标签 */ | |||
| @Excel(name = "字典标签") | |||
| private String dictLabel; | |||
| /** 字典键值 */ | |||
| @Excel(name = "字典键值") | |||
| private String dictValue; | |||
| /** 字典类型 */ | |||
| @Excel(name = "字典类型") | |||
| private String dictType; | |||
| /** 样式属性(其他样式扩展) */ | |||
| private String cssClass; | |||
| /** 表格字典样式 */ | |||
| private String listClass; | |||
| /** 是否默认(Y是 N否) */ | |||
| @Excel(name = "是否默认", readConverterExp = "Y=是,N=否") | |||
| private String isDefault; | |||
| /** 状态(0正常 1停用) */ | |||
| @Excel(name = "状态", readConverterExp = "0=正常,1=停用") | |||
| private String status; | |||
| public Long getDictCode() | |||
| { | |||
| return dictCode; | |||
| } | |||
| public void setDictCode(Long dictCode) | |||
| { | |||
| this.dictCode = dictCode; | |||
| } | |||
| public Long getDictSort() | |||
| { | |||
| return dictSort; | |||
| } | |||
| public void setDictSort(Long dictSort) | |||
| { | |||
| this.dictSort = dictSort; | |||
| } | |||
| @NotBlank(message = "字典标签不能为空") | |||
| @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") | |||
| public String getDictLabel() | |||
| { | |||
| return dictLabel; | |||
| } | |||
| public void setDictLabel(String dictLabel) | |||
| { | |||
| this.dictLabel = dictLabel; | |||
| } | |||
| @NotBlank(message = "字典键值不能为空") | |||
| @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") | |||
| public String getDictValue() | |||
| { | |||
| return dictValue; | |||
| } | |||
| public void setDictValue(String dictValue) | |||
| { | |||
| this.dictValue = dictValue; | |||
| } | |||
| @NotBlank(message = "字典类型不能为空") | |||
| @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") | |||
| public String getDictType() | |||
| { | |||
| return dictType; | |||
| } | |||
| public void setDictType(String dictType) | |||
| { | |||
| this.dictType = dictType; | |||
| } | |||
| @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") | |||
| public String getCssClass() | |||
| { | |||
| return cssClass; | |||
| } | |||
| public void setCssClass(String cssClass) | |||
| { | |||
| this.cssClass = cssClass; | |||
| } | |||
| public String getListClass() | |||
| { | |||
| return listClass; | |||
| } | |||
| public void setListClass(String listClass) | |||
| { | |||
| this.listClass = listClass; | |||
| } | |||
| public boolean getDefault() | |||
| { | |||
| return UserConstants.YES.equals(this.isDefault); | |||
| } | |||
| public String getIsDefault() | |||
| { | |||
| return isDefault; | |||
| } | |||
| public void setIsDefault(String isDefault) | |||
| { | |||
| this.isDefault = isDefault; | |||
| } | |||
| public String getStatus() | |||
| { | |||
| return status; | |||
| } | |||
| public void setStatus(String status) | |||
| { | |||
| this.status = status; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) | |||
| .append("dictCode", getDictCode()) | |||
| .append("dictSort", getDictSort()) | |||
| .append("dictLabel", getDictLabel()) | |||
| .append("dictValue", getDictValue()) | |||
| .append("dictType", getDictType()) | |||
| .append("cssClass", getCssClass()) | |||
| .append("listClass", getListClass()) | |||
| .append("isDefault", getIsDefault()) | |||
| .append("status", getStatus()) | |||
| .append("createBy", getCreateBy()) | |||
| .append("createTime", getCreateTime()) | |||
| .append("updateBy", getUpdateBy()) | |||
| .append("updateTime", getUpdateTime()) | |||
| .append("remark", getRemark()) | |||
| .toString(); | |||
| } | |||
| } | |||
| @ -0,0 +1,96 @@ | |||
| package com.hxhq.system.api.domain; | |||
| import javax.validation.constraints.NotBlank; | |||
| import javax.validation.constraints.Pattern; | |||
| import javax.validation.constraints.Size; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.hxhq.common.core.annotation.Excel; | |||
| import com.hxhq.common.core.annotation.Excel.ColumnType; | |||
| import com.hxhq.common.core.web.domain.BaseEntity; | |||
| /** | |||
| * 字典类型表 sys_dict_type | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class SysDictType extends BaseEntity | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| /** 字典主键 */ | |||
| @Excel(name = "字典主键", cellType = ColumnType.NUMERIC) | |||
| private Long dictId; | |||
| /** 字典名称 */ | |||
| @Excel(name = "字典名称") | |||
| private String dictName; | |||
| /** 字典类型 */ | |||
| @Excel(name = "字典类型") | |||
| private String dictType; | |||
| /** 状态(0正常 1停用) */ | |||
| @Excel(name = "状态", readConverterExp = "0=正常,1=停用") | |||
| private String status; | |||
| public Long getDictId() | |||
| { | |||
| return dictId; | |||
| } | |||
| public void setDictId(Long dictId) | |||
| { | |||
| this.dictId = dictId; | |||
| } | |||
| @NotBlank(message = "字典名称不能为空") | |||
| @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") | |||
| public String getDictName() | |||
| { | |||
| return dictName; | |||
| } | |||
| public void setDictName(String dictName) | |||
| { | |||
| this.dictName = dictName; | |||
| } | |||
| @NotBlank(message = "字典类型不能为空") | |||
| @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") | |||
| @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") | |||
| public String getDictType() | |||
| { | |||
| return dictType; | |||
| } | |||
| public void setDictType(String dictType) | |||
| { | |||
| this.dictType = dictType; | |||
| } | |||
| public String getStatus() | |||
| { | |||
| return status; | |||
| } | |||
| public void setStatus(String status) | |||
| { | |||
| this.status = status; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) | |||
| .append("dictId", getDictId()) | |||
| .append("dictName", getDictName()) | |||
| .append("dictType", getDictType()) | |||
| .append("status", getStatus()) | |||
| .append("createBy", getCreateBy()) | |||
| .append("createTime", getCreateTime()) | |||
| .append("updateBy", getUpdateBy()) | |||
| .append("updateTime", getUpdateTime()) | |||
| .append("remark", getRemark()) | |||
| .toString(); | |||
| } | |||
| } | |||
| @ -0,0 +1,50 @@ | |||
| package com.hxhq.system.api.domain; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| /** | |||
| * 文件信息 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class SysFile | |||
| { | |||
| /** | |||
| * 文件名称 | |||
| */ | |||
| private String name; | |||
| /** | |||
| * 文件地址 | |||
| */ | |||
| private String url; | |||
| public String getName() | |||
| { | |||
| return name; | |||
| } | |||
| public void setName(String name) | |||
| { | |||
| this.name = name; | |||
| } | |||
| public String getUrl() | |||
| { | |||
| return url; | |||
| } | |||
| public void setUrl(String url) | |||
| { | |||
| this.url = url; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) | |||
| .append("name", getName()) | |||
| .append("url", getUrl()) | |||
| .toString(); | |||
| } | |||
| } | |||
| @ -0,0 +1,102 @@ | |||
| package com.hxhq.system.api.domain; | |||
| import java.util.Date; | |||
| import com.fasterxml.jackson.annotation.JsonFormat; | |||
| import com.hxhq.common.core.annotation.Excel; | |||
| import com.hxhq.common.core.annotation.Excel.ColumnType; | |||
| import com.hxhq.common.core.web.domain.BaseEntity; | |||
| /** | |||
| * 系统访问记录表 sys_logininfor | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class SysLogininfor extends BaseEntity | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| /** ID */ | |||
| @Excel(name = "序号", cellType = ColumnType.NUMERIC) | |||
| private Long infoId; | |||
| /** 用户账号 */ | |||
| @Excel(name = "用户账号") | |||
| private String userName; | |||
| /** 状态 0成功 1失败 */ | |||
| @Excel(name = "状态", readConverterExp = "0=成功,1=失败") | |||
| private String status; | |||
| /** 地址 */ | |||
| @Excel(name = "地址") | |||
| private String ipaddr; | |||
| /** 描述 */ | |||
| @Excel(name = "描述") | |||
| private String msg; | |||
| /** 访问时间 */ | |||
| @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") | |||
| @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") | |||
| private Date accessTime; | |||
| public Long getInfoId() | |||
| { | |||
| return infoId; | |||
| } | |||
| public void setInfoId(Long infoId) | |||
| { | |||
| this.infoId = infoId; | |||
| } | |||
| public String getUserName() | |||
| { | |||
| return userName; | |||
| } | |||
| public void setUserName(String userName) | |||
| { | |||
| this.userName = userName; | |||
| } | |||
| public String getStatus() | |||
| { | |||
| return status; | |||
| } | |||
| public void setStatus(String status) | |||
| { | |||
| this.status = status; | |||
| } | |||
| public String getIpaddr() | |||
| { | |||
| return ipaddr; | |||
| } | |||
| public void setIpaddr(String ipaddr) | |||
| { | |||
| this.ipaddr = ipaddr; | |||
| } | |||
| public String getMsg() | |||
| { | |||
| return msg; | |||
| } | |||
| public void setMsg(String msg) | |||
| { | |||
| this.msg = msg; | |||
| } | |||
| public Date getAccessTime() | |||
| { | |||
| return accessTime; | |||
| } | |||
| public void setAccessTime(Date accessTime) | |||
| { | |||
| this.accessTime = accessTime; | |||
| } | |||
| } | |||
| @ -0,0 +1,255 @@ | |||
| package com.hxhq.system.api.domain; | |||
| import java.util.Date; | |||
| import com.fasterxml.jackson.annotation.JsonFormat; | |||
| import com.hxhq.common.core.annotation.Excel; | |||
| import com.hxhq.common.core.annotation.Excel.ColumnType; | |||
| import com.hxhq.common.core.web.domain.BaseEntity; | |||
| /** | |||
| * 操作日志记录表 oper_log | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class SysOperLog extends BaseEntity | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| /** 日志主键 */ | |||
| @Excel(name = "操作序号", cellType = ColumnType.NUMERIC) | |||
| private Long operId; | |||
| /** 操作模块 */ | |||
| @Excel(name = "操作模块") | |||
| private String title; | |||
| /** 业务类型(0其它 1新增 2修改 3删除) */ | |||
| @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据") | |||
| private Integer businessType; | |||
| /** 业务类型数组 */ | |||
| private Integer[] businessTypes; | |||
| /** 请求方法 */ | |||
| @Excel(name = "请求方法") | |||
| private String method; | |||
| /** 请求方式 */ | |||
| @Excel(name = "请求方式") | |||
| private String requestMethod; | |||
| /** 操作类别(0其它 1后台用户 2手机端用户) */ | |||
| @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户") | |||
| private Integer operatorType; | |||
| /** 操作人员 */ | |||
| @Excel(name = "操作人员") | |||
| private String operName; | |||
| /** 部门名称 */ | |||
| @Excel(name = "部门名称") | |||
| private String deptName; | |||
| /** 请求url */ | |||
| @Excel(name = "请求地址") | |||
| private String operUrl; | |||
| /** 操作地址 */ | |||
| @Excel(name = "操作地址") | |||
| private String operIp; | |||
| /** 请求参数 */ | |||
| @Excel(name = "请求参数") | |||
| private String operParam; | |||
| /** 返回参数 */ | |||
| @Excel(name = "返回参数") | |||
| private String jsonResult; | |||
| /** 操作状态(0正常 1异常) */ | |||
| @Excel(name = "状态", readConverterExp = "0=正常,1=异常") | |||
| private Integer status; | |||
| /** 错误消息 */ | |||
| @Excel(name = "错误消息") | |||
| private String errorMsg; | |||
| /** 操作时间 */ | |||
| @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") | |||
| @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") | |||
| private Date operTime; | |||
| /** 消耗时间 */ | |||
| @Excel(name = "消耗时间", suffix = "毫秒") | |||
| private Long costTime; | |||
| public Long getOperId() | |||
| { | |||
| return operId; | |||
| } | |||
| public void setOperId(Long operId) | |||
| { | |||
| this.operId = operId; | |||
| } | |||
| public String getTitle() | |||
| { | |||
| return title; | |||
| } | |||
| public void setTitle(String title) | |||
| { | |||
| this.title = title; | |||
| } | |||
| public Integer getBusinessType() | |||
| { | |||
| return businessType; | |||
| } | |||
| public void setBusinessType(Integer businessType) | |||
| { | |||
| this.businessType = businessType; | |||
| } | |||
| public Integer[] getBusinessTypes() | |||
| { | |||
| return businessTypes; | |||
| } | |||
| public void setBusinessTypes(Integer[] businessTypes) | |||
| { | |||
| this.businessTypes = businessTypes; | |||
| } | |||
| public String getMethod() | |||
| { | |||
| return method; | |||
| } | |||
| public void setMethod(String method) | |||
| { | |||
| this.method = method; | |||
| } | |||
| public String getRequestMethod() | |||
| { | |||
| return requestMethod; | |||
| } | |||
| public void setRequestMethod(String requestMethod) | |||
| { | |||
| this.requestMethod = requestMethod; | |||
| } | |||
| public Integer getOperatorType() | |||
| { | |||
| return operatorType; | |||
| } | |||
| public void setOperatorType(Integer operatorType) | |||
| { | |||
| this.operatorType = operatorType; | |||
| } | |||
| public String getOperName() | |||
| { | |||
| return operName; | |||
| } | |||
| public void setOperName(String operName) | |||
| { | |||
| this.operName = operName; | |||
| } | |||
| public String getDeptName() | |||
| { | |||
| return deptName; | |||
| } | |||
| public void setDeptName(String deptName) | |||
| { | |||
| this.deptName = deptName; | |||
| } | |||
| public String getOperUrl() | |||
| { | |||
| return operUrl; | |||
| } | |||
| public void setOperUrl(String operUrl) | |||
| { | |||
| this.operUrl = operUrl; | |||
| } | |||
| public String getOperIp() | |||
| { | |||
| return operIp; | |||
| } | |||
| public void setOperIp(String operIp) | |||
| { | |||
| this.operIp = operIp; | |||
| } | |||
| public String getOperParam() | |||
| { | |||
| return operParam; | |||
| } | |||
| public void setOperParam(String operParam) | |||
| { | |||
| this.operParam = operParam; | |||
| } | |||
| public String getJsonResult() | |||
| { | |||
| return jsonResult; | |||
| } | |||
| public void setJsonResult(String jsonResult) | |||
| { | |||
| this.jsonResult = jsonResult; | |||
| } | |||
| public Integer getStatus() | |||
| { | |||
| return status; | |||
| } | |||
| public void setStatus(Integer status) | |||
| { | |||
| this.status = status; | |||
| } | |||
| public String getErrorMsg() | |||
| { | |||
| return errorMsg; | |||
| } | |||
| public void setErrorMsg(String errorMsg) | |||
| { | |||
| this.errorMsg = errorMsg; | |||
| } | |||
| public Date getOperTime() | |||
| { | |||
| return operTime; | |||
| } | |||
| public void setOperTime(Date operTime) | |||
| { | |||
| this.operTime = operTime; | |||
| } | |||
| public Long getCostTime() | |||
| { | |||
| return costTime; | |||
| } | |||
| public void setCostTime(Long costTime) | |||
| { | |||
| this.costTime = costTime; | |||
| } | |||
| } | |||
| @ -0,0 +1,241 @@ | |||
| package com.hxhq.system.api.domain; | |||
| import java.util.Set; | |||
| import javax.validation.constraints.NotBlank; | |||
| import javax.validation.constraints.NotNull; | |||
| import javax.validation.constraints.Size; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.hxhq.common.core.annotation.Excel; | |||
| import com.hxhq.common.core.annotation.Excel.ColumnType; | |||
| import com.hxhq.common.core.web.domain.BaseEntity; | |||
| /** | |||
| * 角色表 sys_role | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class SysRole extends BaseEntity | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| /** 角色ID */ | |||
| @Excel(name = "角色序号", cellType = ColumnType.NUMERIC) | |||
| private Long roleId; | |||
| /** 角色名称 */ | |||
| @Excel(name = "角色名称") | |||
| private String roleName; | |||
| /** 角色权限 */ | |||
| @Excel(name = "角色权限") | |||
| private String roleKey; | |||
| /** 角色排序 */ | |||
| @Excel(name = "角色排序") | |||
| private Integer roleSort; | |||
| /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ | |||
| @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") | |||
| private String dataScope; | |||
| /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ | |||
| private boolean menuCheckStrictly; | |||
| /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ | |||
| private boolean deptCheckStrictly; | |||
| /** 角色状态(0正常 1停用) */ | |||
| @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") | |||
| private String status; | |||
| /** 删除标志(0代表存在 2代表删除) */ | |||
| private String delFlag; | |||
| /** 用户是否存在此角色标识 默认不存在 */ | |||
| private boolean flag = false; | |||
| /** 菜单组 */ | |||
| private Long[] menuIds; | |||
| /** 部门组(数据权限) */ | |||
| private Long[] deptIds; | |||
| /** 角色菜单权限 */ | |||
| private Set<String> permissions; | |||
| public SysRole() | |||
| { | |||
| } | |||
| public SysRole(Long roleId) | |||
| { | |||
| this.roleId = roleId; | |||
| } | |||
| public Long getRoleId() | |||
| { | |||
| return roleId; | |||
| } | |||
| public void setRoleId(Long roleId) | |||
| { | |||
| this.roleId = roleId; | |||
| } | |||
| public boolean isAdmin() | |||
| { | |||
| return isAdmin(this.roleId); | |||
| } | |||
| public static boolean isAdmin(Long roleId) | |||
| { | |||
| return roleId != null && 1L == roleId; | |||
| } | |||
| @NotBlank(message = "角色名称不能为空") | |||
| @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") | |||
| public String getRoleName() | |||
| { | |||
| return roleName; | |||
| } | |||
| public void setRoleName(String roleName) | |||
| { | |||
| this.roleName = roleName; | |||
| } | |||
| @NotBlank(message = "权限字符不能为空") | |||
| @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") | |||
| public String getRoleKey() | |||
| { | |||
| return roleKey; | |||
| } | |||
| public void setRoleKey(String roleKey) | |||
| { | |||
| this.roleKey = roleKey; | |||
| } | |||
| @NotNull(message = "显示顺序不能为空") | |||
| public Integer getRoleSort() | |||
| { | |||
| return roleSort; | |||
| } | |||
| public void setRoleSort(Integer roleSort) | |||
| { | |||
| this.roleSort = roleSort; | |||
| } | |||
| public String getDataScope() | |||
| { | |||
| return dataScope; | |||
| } | |||
| public void setDataScope(String dataScope) | |||
| { | |||
| this.dataScope = dataScope; | |||
| } | |||
| public boolean isMenuCheckStrictly() | |||
| { | |||
| return menuCheckStrictly; | |||
| } | |||
| public void setMenuCheckStrictly(boolean menuCheckStrictly) | |||
| { | |||
| this.menuCheckStrictly = menuCheckStrictly; | |||
| } | |||
| public boolean isDeptCheckStrictly() | |||
| { | |||
| return deptCheckStrictly; | |||
| } | |||
| public void setDeptCheckStrictly(boolean deptCheckStrictly) | |||
| { | |||
| this.deptCheckStrictly = deptCheckStrictly; | |||
| } | |||
| public String getStatus() | |||
| { | |||
| return status; | |||
| } | |||
| public void setStatus(String status) | |||
| { | |||
| this.status = status; | |||
| } | |||
| public String getDelFlag() | |||
| { | |||
| return delFlag; | |||
| } | |||
| public void setDelFlag(String delFlag) | |||
| { | |||
| this.delFlag = delFlag; | |||
| } | |||
| public boolean isFlag() | |||
| { | |||
| return flag; | |||
| } | |||
| public void setFlag(boolean flag) | |||
| { | |||
| this.flag = flag; | |||
| } | |||
| public Long[] getMenuIds() | |||
| { | |||
| return menuIds; | |||
| } | |||
| public void setMenuIds(Long[] menuIds) | |||
| { | |||
| this.menuIds = menuIds; | |||
| } | |||
| public Long[] getDeptIds() | |||
| { | |||
| return deptIds; | |||
| } | |||
| public void setDeptIds(Long[] deptIds) | |||
| { | |||
| this.deptIds = deptIds; | |||
| } | |||
| public Set<String> getPermissions() | |||
| { | |||
| return permissions; | |||
| } | |||
| public void setPermissions(Set<String> permissions) | |||
| { | |||
| this.permissions = permissions; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) | |||
| .append("roleId", getRoleId()) | |||
| .append("roleName", getRoleName()) | |||
| .append("roleKey", getRoleKey()) | |||
| .append("roleSort", getRoleSort()) | |||
| .append("dataScope", getDataScope()) | |||
| .append("menuCheckStrictly", isMenuCheckStrictly()) | |||
| .append("deptCheckStrictly", isDeptCheckStrictly()) | |||
| .append("status", getStatus()) | |||
| .append("delFlag", getDelFlag()) | |||
| .append("createBy", getCreateBy()) | |||
| .append("createTime", getCreateTime()) | |||
| .append("updateBy", getUpdateBy()) | |||
| .append("updateTime", getUpdateTime()) | |||
| .append("remark", getRemark()) | |||
| .toString(); | |||
| } | |||
| } | |||
| @ -0,0 +1,339 @@ | |||
| package com.hxhq.system.api.domain; | |||
| import java.util.Date; | |||
| import java.util.List; | |||
| import javax.validation.constraints.*; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.hxhq.common.core.annotation.Excel; | |||
| import com.hxhq.common.core.annotation.Excel.ColumnType; | |||
| import com.hxhq.common.core.annotation.Excel.Type; | |||
| import com.hxhq.common.core.constant.UserConstants; | |||
| import com.hxhq.common.core.annotation.Excels; | |||
| import com.hxhq.common.core.web.domain.BaseEntity; | |||
| import com.hxhq.common.core.xss.Xss; | |||
| /** | |||
| * 用户对象 sys_user | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class SysUser extends BaseEntity | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| /** 用户ID */ | |||
| @Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号") | |||
| private Long userId; | |||
| /** 部门ID */ | |||
| @Excel(name = "部门编号", type = Type.IMPORT) | |||
| private Long deptId; | |||
| /** 用户账号 */ | |||
| @Excel(name = "登录名称") | |||
| private String userName; | |||
| /** 用户昵称 */ | |||
| @Excel(name = "用户名称") | |||
| private String nickName; | |||
| /** 用户邮箱 */ | |||
| @Excel(name = "用户邮箱") | |||
| private String email; | |||
| /** 手机号码 */ | |||
| @Excel(name = "手机号码", cellType = ColumnType.TEXT) | |||
| private String phonenumber; | |||
| /** 用户性别 */ | |||
| @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") | |||
| private String sex; | |||
| /** 用户头像 */ | |||
| private String avatar; | |||
| /** 密码 */ | |||
| private String password; | |||
| /** 账号状态(0正常 1停用) */ | |||
| @Excel(name = "账号状态", readConverterExp = "0=正常,1=停用") | |||
| private String status; | |||
| /** 删除标志(0代表存在 2代表删除) */ | |||
| private String delFlag; | |||
| /** 最后登录IP */ | |||
| @Excel(name = "最后登录IP", type = Type.EXPORT) | |||
| private String loginIp; | |||
| /** 最后登录时间 */ | |||
| @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) | |||
| private Date loginDate; | |||
| /** 密码最后更新时间 */ | |||
| private Date pwdUpdateDate; | |||
| /** 部门对象 */ | |||
| @Excels({ | |||
| @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), | |||
| @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) | |||
| }) | |||
| private SysDept dept; | |||
| /** 角色对象 */ | |||
| private List<SysRole> roles; | |||
| /** 角色组 */ | |||
| private Long[] roleIds; | |||
| /** 岗位组 */ | |||
| private Long[] postIds; | |||
| /** 角色ID */ | |||
| private Long roleId; | |||
| public SysUser() | |||
| { | |||
| } | |||
| public SysUser(Long userId) | |||
| { | |||
| this.userId = userId; | |||
| } | |||
| public Long getUserId() | |||
| { | |||
| return userId; | |||
| } | |||
| public void setUserId(Long userId) | |||
| { | |||
| this.userId = userId; | |||
| } | |||
| public boolean isAdmin() | |||
| { | |||
| return isAdmin(this.userId); | |||
| } | |||
| public static boolean isAdmin(Long userId) | |||
| { | |||
| return UserConstants.isAdmin(userId); | |||
| } | |||
| public Long getDeptId() | |||
| { | |||
| return deptId; | |||
| } | |||
| public void setDeptId(Long deptId) | |||
| { | |||
| this.deptId = deptId; | |||
| } | |||
| @Xss(message = "用户昵称不能包含脚本字符") | |||
| @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") | |||
| public String getNickName() | |||
| { | |||
| return nickName; | |||
| } | |||
| public void setNickName(String nickName) | |||
| { | |||
| this.nickName = nickName; | |||
| } | |||
| @Xss(message = "用户账号不能包含脚本字符") | |||
| @NotBlank(message = "用户账号不能为空") | |||
| @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") | |||
| public String getUserName() | |||
| { | |||
| return userName; | |||
| } | |||
| public void setUserName(String userName) | |||
| { | |||
| this.userName = userName; | |||
| } | |||
| @Email(message = "邮箱格式不正确") | |||
| @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") | |||
| public String getEmail() | |||
| { | |||
| return email; | |||
| } | |||
| public void setEmail(String email) | |||
| { | |||
| this.email = email; | |||
| } | |||
| @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") | |||
| public String getPhonenumber() | |||
| { | |||
| return phonenumber; | |||
| } | |||
| public void setPhonenumber(String phonenumber) | |||
| { | |||
| this.phonenumber = phonenumber; | |||
| } | |||
| public String getSex() | |||
| { | |||
| return sex; | |||
| } | |||
| public void setSex(String sex) | |||
| { | |||
| this.sex = sex; | |||
| } | |||
| public String getAvatar() | |||
| { | |||
| return avatar; | |||
| } | |||
| public void setAvatar(String avatar) | |||
| { | |||
| this.avatar = avatar; | |||
| } | |||
| public String getPassword() | |||
| { | |||
| return password; | |||
| } | |||
| public void setPassword(String password) | |||
| { | |||
| this.password = password; | |||
| } | |||
| public String getStatus() | |||
| { | |||
| return status; | |||
| } | |||
| public void setStatus(String status) | |||
| { | |||
| this.status = status; | |||
| } | |||
| public String getDelFlag() | |||
| { | |||
| return delFlag; | |||
| } | |||
| public void setDelFlag(String delFlag) | |||
| { | |||
| this.delFlag = delFlag; | |||
| } | |||
| public String getLoginIp() | |||
| { | |||
| return loginIp; | |||
| } | |||
| public void setLoginIp(String loginIp) | |||
| { | |||
| this.loginIp = loginIp; | |||
| } | |||
| public Date getLoginDate() | |||
| { | |||
| return loginDate; | |||
| } | |||
| public void setLoginDate(Date loginDate) | |||
| { | |||
| this.loginDate = loginDate; | |||
| } | |||
| public Date getPwdUpdateDate() | |||
| { | |||
| return pwdUpdateDate; | |||
| } | |||
| public void setPwdUpdateDate(Date pwdUpdateDate) | |||
| { | |||
| this.pwdUpdateDate = pwdUpdateDate; | |||
| } | |||
| public SysDept getDept() | |||
| { | |||
| return dept; | |||
| } | |||
| public void setDept(SysDept dept) | |||
| { | |||
| this.dept = dept; | |||
| } | |||
| public List<SysRole> getRoles() | |||
| { | |||
| return roles; | |||
| } | |||
| public void setRoles(List<SysRole> roles) | |||
| { | |||
| this.roles = roles; | |||
| } | |||
| public Long[] getRoleIds() | |||
| { | |||
| return roleIds; | |||
| } | |||
| public void setRoleIds(Long[] roleIds) | |||
| { | |||
| this.roleIds = roleIds; | |||
| } | |||
| public Long[] getPostIds() | |||
| { | |||
| return postIds; | |||
| } | |||
| public void setPostIds(Long[] postIds) | |||
| { | |||
| this.postIds = postIds; | |||
| } | |||
| public Long getRoleId() | |||
| { | |||
| return roleId; | |||
| } | |||
| public void setRoleId(Long roleId) | |||
| { | |||
| this.roleId = roleId; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) | |||
| .append("userId", getUserId()) | |||
| .append("deptId", getDeptId()) | |||
| .append("userName", getUserName()) | |||
| .append("nickName", getNickName()) | |||
| .append("email", getEmail()) | |||
| .append("phonenumber", getPhonenumber()) | |||
| .append("sex", getSex()) | |||
| .append("avatar", getAvatar()) | |||
| .append("password", getPassword()) | |||
| .append("status", getStatus()) | |||
| .append("delFlag", getDelFlag()) | |||
| .append("loginIp", getLoginIp()) | |||
| .append("loginDate", getLoginDate()) | |||
| .append("pwdUpdateDate", getPwdUpdateDate()) | |||
| .append("createBy", getCreateBy()) | |||
| .append("createTime", getCreateTime()) | |||
| .append("updateBy", getUpdateBy()) | |||
| .append("updateTime", getUpdateTime()) | |||
| .append("remark", getRemark()) | |||
| .append("dept", getDept()) | |||
| .toString(); | |||
| } | |||
| } | |||
| @ -0,0 +1,41 @@ | |||
| package com.hxhq.system.api.factory; | |||
| import com.hxhq.system.api.RemoteFileService; | |||
| import com.hxhq.system.api.domain.SysFile; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.cloud.openfeign.FallbackFactory; | |||
| import org.springframework.stereotype.Component; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| import com.hxhq.common.core.domain.R; | |||
| /** | |||
| * 文件服务降级处理 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @Component | |||
| public class RemoteFileFallbackFactory implements FallbackFactory<RemoteFileService> | |||
| { | |||
| private static final Logger log = LoggerFactory.getLogger(RemoteFileFallbackFactory.class); | |||
| @Override | |||
| public RemoteFileService create(Throwable throwable) | |||
| { | |||
| log.error("文件服务调用失败:{}", throwable.getMessage()); | |||
| return new RemoteFileService() | |||
| { | |||
| @Override | |||
| public R<SysFile> upload(MultipartFile file) | |||
| { | |||
| return R.fail("上传文件失败:" + throwable.getMessage()); | |||
| } | |||
| @Override | |||
| public R<Boolean> delete(String fileUrl) | |||
| { | |||
| return R.fail("删除文件失败:" + throwable.getMessage()); | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| @ -0,0 +1,42 @@ | |||
| package com.hxhq.system.api.factory; | |||
| import com.hxhq.system.api.RemoteLogService; | |||
| import com.hxhq.system.api.domain.SysLogininfor; | |||
| import com.hxhq.system.api.domain.SysOperLog; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.cloud.openfeign.FallbackFactory; | |||
| import org.springframework.stereotype.Component; | |||
| import com.hxhq.common.core.domain.R; | |||
| /** | |||
| * 日志服务降级处理 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @Component | |||
| public class RemoteLogFallbackFactory implements FallbackFactory<RemoteLogService> | |||
| { | |||
| private static final Logger log = LoggerFactory.getLogger(RemoteLogFallbackFactory.class); | |||
| @Override | |||
| public RemoteLogService create(Throwable throwable) | |||
| { | |||
| log.error("日志服务调用失败:{}", throwable.getMessage()); | |||
| return new RemoteLogService() | |||
| { | |||
| @Override | |||
| public R<Boolean> saveLog(SysOperLog sysOperLog, String source) | |||
| { | |||
| return R.fail("保存操作日志失败:" + throwable.getMessage()); | |||
| } | |||
| @Override | |||
| public R<Boolean> saveLogininfor(SysLogininfor sysLogininfor, String source) | |||
| { | |||
| return R.fail("保存登录日志失败:" + throwable.getMessage()); | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| @ -0,0 +1,47 @@ | |||
| package com.hxhq.system.api.factory; | |||
| import com.hxhq.system.api.RemoteUserService; | |||
| import com.hxhq.system.api.domain.SysUser; | |||
| import com.hxhq.system.api.model.LoginUser; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.cloud.openfeign.FallbackFactory; | |||
| import org.springframework.stereotype.Component; | |||
| import com.hxhq.common.core.domain.R; | |||
| /** | |||
| * 用户服务降级处理 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @Component | |||
| public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserService> | |||
| { | |||
| private static final Logger log = LoggerFactory.getLogger(RemoteUserFallbackFactory.class); | |||
| @Override | |||
| public RemoteUserService create(Throwable throwable) | |||
| { | |||
| log.error("用户服务调用失败:{}", throwable.getMessage()); | |||
| return new RemoteUserService() | |||
| { | |||
| @Override | |||
| public R<LoginUser> getUserInfo(String username, String source) | |||
| { | |||
| return R.fail("获取用户失败:" + throwable.getMessage()); | |||
| } | |||
| @Override | |||
| public R<Boolean> registerUserInfo(SysUser sysUser, String source) | |||
| { | |||
| return R.fail("注册用户失败:" + throwable.getMessage()); | |||
| } | |||
| @Override | |||
| public R<Boolean> recordUserLogin(SysUser sysUser, String source) | |||
| { | |||
| return R.fail("记录用户登录信息失败:" + throwable.getMessage()); | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| @ -0,0 +1,150 @@ | |||
| package com.hxhq.system.api.model; | |||
| import java.io.Serializable; | |||
| import java.util.Set; | |||
| import com.hxhq.system.api.domain.SysUser; | |||
| /** | |||
| * 用户信息 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class LoginUser implements Serializable | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| /** | |||
| * 用户唯一标识 | |||
| */ | |||
| private String token; | |||
| /** | |||
| * 用户名id | |||
| */ | |||
| private Long userid; | |||
| /** | |||
| * 用户名 | |||
| */ | |||
| private String username; | |||
| /** | |||
| * 登录时间 | |||
| */ | |||
| private Long loginTime; | |||
| /** | |||
| * 过期时间 | |||
| */ | |||
| private Long expireTime; | |||
| /** | |||
| * 登录IP地址 | |||
| */ | |||
| private String ipaddr; | |||
| /** | |||
| * 权限列表 | |||
| */ | |||
| private Set<String> permissions; | |||
| /** | |||
| * 角色列表 | |||
| */ | |||
| private Set<String> roles; | |||
| /** | |||
| * 用户信息 | |||
| */ | |||
| private SysUser sysUser; | |||
| public String getToken() | |||
| { | |||
| return token; | |||
| } | |||
| public void setToken(String token) | |||
| { | |||
| this.token = token; | |||
| } | |||
| public Long getUserid() | |||
| { | |||
| return userid; | |||
| } | |||
| public void setUserid(Long userid) | |||
| { | |||
| this.userid = userid; | |||
| } | |||
| public String getUsername() | |||
| { | |||
| return username; | |||
| } | |||
| public void setUsername(String username) | |||
| { | |||
| this.username = username; | |||
| } | |||
| public Long getLoginTime() | |||
| { | |||
| return loginTime; | |||
| } | |||
| public void setLoginTime(Long loginTime) | |||
| { | |||
| this.loginTime = loginTime; | |||
| } | |||
| public Long getExpireTime() | |||
| { | |||
| return expireTime; | |||
| } | |||
| public void setExpireTime(Long expireTime) | |||
| { | |||
| this.expireTime = expireTime; | |||
| } | |||
| public String getIpaddr() | |||
| { | |||
| return ipaddr; | |||
| } | |||
| public void setIpaddr(String ipaddr) | |||
| { | |||
| this.ipaddr = ipaddr; | |||
| } | |||
| public Set<String> getPermissions() | |||
| { | |||
| return permissions; | |||
| } | |||
| public void setPermissions(Set<String> permissions) | |||
| { | |||
| this.permissions = permissions; | |||
| } | |||
| public Set<String> getRoles() | |||
| { | |||
| return roles; | |||
| } | |||
| public void setRoles(Set<String> roles) | |||
| { | |||
| this.roles = roles; | |||
| } | |||
| public SysUser getSysUser() | |||
| { | |||
| return sysUser; | |||
| } | |||
| public void setSysUser(SysUser sysUser) | |||
| { | |||
| this.sysUser = sysUser; | |||
| } | |||
| } | |||
| @ -0,0 +1,3 @@ | |||
| com.hxhq.system.api.factory.RemoteUserFallbackFactory | |||
| com.hxhq.system.api.factory.RemoteLogFallbackFactory | |||
| com.hxhq.system.api.factory.RemoteFileFallbackFactory | |||
| @ -0,0 +1,22 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
| <parent> | |||
| <groupId>com.hxhq</groupId> | |||
| <artifactId>hxhq</artifactId> | |||
| <version>3.6.6</version> | |||
| </parent> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <modules> | |||
| <module>hxhq-api-system</module> | |||
| </modules> | |||
| <artifactId>hxhq-api</artifactId> | |||
| <packaging>pom</packaging> | |||
| <description> | |||
| hxhq-api系统接口 | |||
| </description> | |||
| </project> | |||
| @ -0,0 +1,74 @@ | |||
| <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> | |||
| <groupId>com.hxhq</groupId> | |||
| <artifactId>hxhq</artifactId> | |||
| <version>3.6.6</version> | |||
| </parent> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <artifactId>hxhq-auth</artifactId> | |||
| <description> | |||
| hxhq-auth认证授权中心 | |||
| </description> | |||
| <dependencies> | |||
| <!-- SpringCloud Alibaba Nacos --> | |||
| <dependency> | |||
| <groupId>com.alibaba.cloud</groupId> | |||
| <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> | |||
| </dependency> | |||
| <!-- SpringCloud Alibaba Nacos Config --> | |||
| <dependency> | |||
| <groupId>com.alibaba.cloud</groupId> | |||
| <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> | |||
| </dependency> | |||
| <!-- SpringCloud Alibaba Sentinel --> | |||
| <dependency> | |||
| <groupId>com.alibaba.cloud</groupId> | |||
| <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> | |||
| </dependency> | |||
| <!-- SpringBoot Web --> | |||
| <dependency> | |||
| <groupId>org.springframework.boot</groupId> | |||
| <artifactId>spring-boot-starter-web</artifactId> | |||
| </dependency> | |||
| <!-- SpringBoot Actuator --> | |||
| <dependency> | |||
| <groupId>org.springframework.boot</groupId> | |||
| <artifactId>spring-boot-starter-actuator</artifactId> | |||
| </dependency> | |||
| <!-- hxhq Common Security--> | |||
| <dependency> | |||
| <groupId>com.hxhq</groupId> | |||
| <artifactId>hxhq-common-security</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| <finalName>${project.artifactId}</finalName> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.springframework.boot</groupId> | |||
| <artifactId>spring-boot-maven-plugin</artifactId> | |||
| <executions> | |||
| <execution> | |||
| <goals> | |||
| <goal>repackage</goal> | |||
| </goals> | |||
| </execution> | |||
| </executions> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| </project> | |||
| @ -0,0 +1,22 @@ | |||
| package com.hxhq.auth; | |||
| import org.springframework.boot.SpringApplication; | |||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | |||
| import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; | |||
| import com.hxhq.common.security.annotation.EnableRyFeignClients; | |||
| /** | |||
| * 认证授权中心 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @EnableRyFeignClients | |||
| @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class }) | |||
| public class HxhqAuthApplication | |||
| { | |||
| public static void main(String[] args) | |||
| { | |||
| SpringApplication.run(HxhqAuthApplication.class, args); | |||
| System.out.println("认证授权中心启动成功"); | |||
| } | |||
| } | |||
| @ -0,0 +1,79 @@ | |||
| package com.hxhq.auth.controller; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import com.hxhq.auth.form.LoginBody; | |||
| import com.hxhq.auth.form.RegisterBody; | |||
| import com.hxhq.auth.service.SysLoginService; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.web.bind.annotation.DeleteMapping; | |||
| import org.springframework.web.bind.annotation.PostMapping; | |||
| import org.springframework.web.bind.annotation.RequestBody; | |||
| import org.springframework.web.bind.annotation.RestController; | |||
| import com.hxhq.common.core.domain.R; | |||
| import com.hxhq.common.core.utils.JwtUtils; | |||
| import com.hxhq.common.core.utils.StringUtils; | |||
| import com.hxhq.common.security.auth.AuthUtil; | |||
| import com.hxhq.common.security.service.TokenService; | |||
| import com.hxhq.common.security.utils.SecurityUtils; | |||
| import com.hxhq.system.api.model.LoginUser; | |||
| /** | |||
| * token 控制 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @RestController | |||
| public class TokenController | |||
| { | |||
| @Autowired | |||
| private TokenService tokenService; | |||
| @Autowired | |||
| private SysLoginService sysLoginService; | |||
| @PostMapping("login") | |||
| public R<?> login(@RequestBody LoginBody form) | |||
| { | |||
| // 用户登录 | |||
| LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword()); | |||
| // 获取登录token | |||
| return R.ok(tokenService.createToken(userInfo)); | |||
| } | |||
| @DeleteMapping("logout") | |||
| public R<?> logout(HttpServletRequest request) | |||
| { | |||
| String token = SecurityUtils.getToken(request); | |||
| if (StringUtils.isNotEmpty(token)) | |||
| { | |||
| String username = JwtUtils.getUserName(token); | |||
| // 删除用户缓存记录 | |||
| AuthUtil.logoutByToken(token); | |||
| // 记录用户退出日志 | |||
| sysLoginService.logout(username); | |||
| } | |||
| return R.ok(); | |||
| } | |||
| @PostMapping("refresh") | |||
| public R<?> refresh(HttpServletRequest request) | |||
| { | |||
| LoginUser loginUser = tokenService.getLoginUser(request); | |||
| if (StringUtils.isNotNull(loginUser)) | |||
| { | |||
| // 刷新令牌有效期 | |||
| tokenService.refreshToken(loginUser); | |||
| return R.ok(); | |||
| } | |||
| return R.ok(); | |||
| } | |||
| @PostMapping("register") | |||
| public R<?> register(@RequestBody RegisterBody registerBody) | |||
| { | |||
| // 用户注册 | |||
| sysLoginService.register(registerBody.getUsername(), registerBody.getPassword()); | |||
| return R.ok(); | |||
| } | |||
| } | |||
| @ -0,0 +1,39 @@ | |||
| package com.hxhq.auth.form; | |||
| /** | |||
| * 用户登录对象 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class LoginBody | |||
| { | |||
| /** | |||
| * 用户名 | |||
| */ | |||
| private String username; | |||
| /** | |||
| * 用户密码 | |||
| */ | |||
| private String password; | |||
| public String getUsername() | |||
| { | |||
| return username; | |||
| } | |||
| public void setUsername(String username) | |||
| { | |||
| this.username = username; | |||
| } | |||
| public String getPassword() | |||
| { | |||
| return password; | |||
| } | |||
| public void setPassword(String password) | |||
| { | |||
| this.password = password; | |||
| } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| package com.hxhq.auth.form; | |||
| /** | |||
| * 用户注册对象 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class RegisterBody extends LoginBody | |||
| { | |||
| } | |||
| @ -0,0 +1,156 @@ | |||
| package com.hxhq.auth.service; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Component; | |||
| import com.hxhq.common.core.constant.CacheConstants; | |||
| import com.hxhq.common.core.constant.Constants; | |||
| import com.hxhq.common.core.constant.SecurityConstants; | |||
| import com.hxhq.common.core.constant.UserConstants; | |||
| import com.hxhq.common.core.domain.R; | |||
| import com.hxhq.common.core.enums.UserStatus; | |||
| import com.hxhq.common.core.exception.ServiceException; | |||
| import com.hxhq.common.core.text.Convert; | |||
| import com.hxhq.common.core.utils.DateUtils; | |||
| import com.hxhq.common.core.utils.StringUtils; | |||
| import com.hxhq.common.core.utils.ip.IpUtils; | |||
| import com.hxhq.common.redis.service.RedisService; | |||
| import com.hxhq.common.security.utils.SecurityUtils; | |||
| import com.hxhq.system.api.RemoteUserService; | |||
| import com.hxhq.system.api.domain.SysUser; | |||
| import com.hxhq.system.api.model.LoginUser; | |||
| /** | |||
| * 登录校验方法 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @Component | |||
| public class SysLoginService | |||
| { | |||
| @Autowired | |||
| private RemoteUserService remoteUserService; | |||
| @Autowired | |||
| private SysPasswordService passwordService; | |||
| @Autowired | |||
| private SysRecordLogService recordLogService; | |||
| @Autowired | |||
| private RedisService redisService; | |||
| /** | |||
| * 登录 | |||
| */ | |||
| public LoginUser login(String username, String password) | |||
| { | |||
| // 用户名或密码为空 错误 | |||
| if (StringUtils.isAnyBlank(username, password)) | |||
| { | |||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); | |||
| throw new ServiceException("用户/密码必须填写"); | |||
| } | |||
| // 密码如果不在指定范围内 错误 | |||
| if (password.length() < UserConstants.PASSWORD_MIN_LENGTH | |||
| || password.length() > UserConstants.PASSWORD_MAX_LENGTH) | |||
| { | |||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围"); | |||
| throw new ServiceException("用户密码不在指定范围"); | |||
| } | |||
| // 用户名不在指定范围内 错误 | |||
| if (username.length() < UserConstants.USERNAME_MIN_LENGTH | |||
| || username.length() > UserConstants.USERNAME_MAX_LENGTH) | |||
| { | |||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); | |||
| throw new ServiceException("用户名不在指定范围"); | |||
| } | |||
| // IP黑名单校验 | |||
| String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST)); | |||
| if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) | |||
| { | |||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单"); | |||
| throw new ServiceException("很遗憾,访问IP已被列入系统黑名单"); | |||
| } | |||
| // 查询用户信息 | |||
| R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); | |||
| if (R.FAIL == userResult.getCode()) | |||
| { | |||
| throw new ServiceException(userResult.getMsg()); | |||
| } | |||
| LoginUser userInfo = userResult.getData(); | |||
| SysUser user = userResult.getData().getSysUser(); | |||
| if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) | |||
| { | |||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); | |||
| throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); | |||
| } | |||
| if (UserStatus.DISABLE.getCode().equals(user.getStatus())) | |||
| { | |||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); | |||
| throw new ServiceException("对不起,您的账号:" + username + " 已停用"); | |||
| } | |||
| passwordService.validate(user, password); | |||
| recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); | |||
| recordLoginInfo(user.getUserId()); | |||
| return userInfo; | |||
| } | |||
| /** | |||
| * 记录登录信息 | |||
| * | |||
| * @param userId 用户ID | |||
| */ | |||
| public void recordLoginInfo(Long userId) | |||
| { | |||
| SysUser sysUser = new SysUser(); | |||
| sysUser.setUserId(userId); | |||
| // 更新用户登录IP | |||
| sysUser.setLoginIp(IpUtils.getIpAddr()); | |||
| // 更新用户登录时间 | |||
| sysUser.setLoginDate(DateUtils.getNowDate()); | |||
| remoteUserService.recordUserLogin(sysUser, SecurityConstants.INNER); | |||
| } | |||
| public void logout(String loginName) | |||
| { | |||
| recordLogService.recordLogininfor(loginName, Constants.LOGOUT, "退出成功"); | |||
| } | |||
| /** | |||
| * 注册 | |||
| */ | |||
| public void register(String username, String password) | |||
| { | |||
| // 用户名或密码为空 错误 | |||
| if (StringUtils.isAnyBlank(username, password)) | |||
| { | |||
| throw new ServiceException("用户/密码必须填写"); | |||
| } | |||
| if (username.length() < UserConstants.USERNAME_MIN_LENGTH | |||
| || username.length() > UserConstants.USERNAME_MAX_LENGTH) | |||
| { | |||
| throw new ServiceException("账户长度必须在2到20个字符之间"); | |||
| } | |||
| if (password.length() < UserConstants.PASSWORD_MIN_LENGTH | |||
| || password.length() > UserConstants.PASSWORD_MAX_LENGTH) | |||
| { | |||
| throw new ServiceException("密码长度必须在5到20个字符之间"); | |||
| } | |||
| // 注册用户信息 | |||
| SysUser sysUser = new SysUser(); | |||
| sysUser.setUserName(username); | |||
| sysUser.setNickName(username); | |||
| sysUser.setPwdUpdateDate(DateUtils.getNowDate()); | |||
| sysUser.setPassword(SecurityUtils.encryptPassword(password)); | |||
| R<?> registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER); | |||
| if (R.FAIL == registerResult.getCode()) | |||
| { | |||
| throw new ServiceException(registerResult.getMsg()); | |||
| } | |||
| recordLogService.recordLogininfor(username, Constants.REGISTER, "注册成功"); | |||
| } | |||
| } | |||
| @ -0,0 +1,85 @@ | |||
| package com.hxhq.auth.service; | |||
| import java.util.concurrent.TimeUnit; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Component; | |||
| import com.hxhq.common.core.constant.CacheConstants; | |||
| import com.hxhq.common.core.constant.Constants; | |||
| import com.hxhq.common.core.exception.ServiceException; | |||
| import com.hxhq.common.redis.service.RedisService; | |||
| import com.hxhq.common.security.utils.SecurityUtils; | |||
| import com.hxhq.system.api.domain.SysUser; | |||
| /** | |||
| * 登录密码方法 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @Component | |||
| public class SysPasswordService | |||
| { | |||
| @Autowired | |||
| private RedisService redisService; | |||
| private int maxRetryCount = CacheConstants.PASSWORD_MAX_RETRY_COUNT; | |||
| private Long lockTime = CacheConstants.PASSWORD_LOCK_TIME; | |||
| @Autowired | |||
| private SysRecordLogService recordLogService; | |||
| /** | |||
| * 登录账户密码错误次数缓存键名 | |||
| * | |||
| * @param username 用户名 | |||
| * @return 缓存键key | |||
| */ | |||
| private String getCacheKey(String username) | |||
| { | |||
| return CacheConstants.PWD_ERR_CNT_KEY + username; | |||
| } | |||
| public void validate(SysUser user, String password) | |||
| { | |||
| String username = user.getUserName(); | |||
| Integer retryCount = redisService.getCacheObject(getCacheKey(username)); | |||
| if (retryCount == null) | |||
| { | |||
| retryCount = 0; | |||
| } | |||
| if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) | |||
| { | |||
| String errMsg = String.format("密码输入错误%s次,帐户锁定%s分钟", maxRetryCount, lockTime); | |||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL,errMsg); | |||
| throw new ServiceException(errMsg); | |||
| } | |||
| if (!matches(user, password)) | |||
| { | |||
| retryCount = retryCount + 1; | |||
| recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, String.format("密码输入错误%s次", retryCount)); | |||
| redisService.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); | |||
| throw new ServiceException("用户不存在/密码错误"); | |||
| } | |||
| else | |||
| { | |||
| clearLoginRecordCache(username); | |||
| } | |||
| } | |||
| public boolean matches(SysUser user, String rawPassword) | |||
| { | |||
| return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); | |||
| } | |||
| public void clearLoginRecordCache(String loginName) | |||
| { | |||
| if (redisService.hasKey(getCacheKey(loginName))) | |||
| { | |||
| redisService.deleteObject(getCacheKey(loginName)); | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,48 @@ | |||
| package com.hxhq.auth.service; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Component; | |||
| import com.hxhq.common.core.constant.Constants; | |||
| import com.hxhq.common.core.constant.SecurityConstants; | |||
| import com.hxhq.common.core.utils.StringUtils; | |||
| import com.hxhq.common.core.utils.ip.IpUtils; | |||
| import com.hxhq.system.api.RemoteLogService; | |||
| import com.hxhq.system.api.domain.SysLogininfor; | |||
| /** | |||
| * 记录日志方法 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @Component | |||
| public class SysRecordLogService | |||
| { | |||
| @Autowired | |||
| private RemoteLogService remoteLogService; | |||
| /** | |||
| * 记录登录信息 | |||
| * | |||
| * @param username 用户名 | |||
| * @param status 状态 | |||
| * @param message 消息内容 | |||
| * @return | |||
| */ | |||
| public void recordLogininfor(String username, String status, String message) | |||
| { | |||
| SysLogininfor logininfor = new SysLogininfor(); | |||
| logininfor.setUserName(username); | |||
| logininfor.setIpaddr(IpUtils.getIpAddr()); | |||
| logininfor.setMsg(message); | |||
| // 日志状态 | |||
| if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) | |||
| { | |||
| logininfor.setStatus(Constants.LOGIN_SUCCESS_STATUS); | |||
| } | |||
| else if (Constants.LOGIN_FAIL.equals(status)) | |||
| { | |||
| logininfor.setStatus(Constants.LOGIN_FAIL_STATUS); | |||
| } | |||
| remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER); | |||
| } | |||
| } | |||
| @ -0,0 +1,10 @@ | |||
| Spring Boot Version: ${spring-boot.version} | |||
| Spring Application Name: ${spring.application.name} | |||
| _ _ _ | |||
| (_) | | | | | |||
| _ __ _ _ ___ _ _ _ ______ __ _ _ _ | |_ | |__ | |||
| | '__|| | | | / _ \ | | | || ||______| / _` || | | || __|| '_ \ | |||
| | | | |_| || (_) || |_| || | | (_| || |_| || |_ | | | | | |||
| |_| \__,_| \___/ \__, ||_| \__,_| \__,_| \__||_| |_| | |||
| __/ | | |||
| |___/ | |||
| @ -0,0 +1,25 @@ | |||
| # Tomcat | |||
| server: | |||
| port: 9200 | |||
| # Spring | |||
| spring: | |||
| application: | |||
| # 应用名称 | |||
| name: hxhq-auth | |||
| profiles: | |||
| # 环境配置 | |||
| active: dev | |||
| cloud: | |||
| nacos: | |||
| discovery: | |||
| # 服务注册地址 | |||
| server-addr: 127.0.0.1:8848 | |||
| config: | |||
| # 配置中心地址 | |||
| server-addr: 127.0.0.1:8848 | |||
| # 配置文件格式 | |||
| file-extension: yml | |||
| # 共享配置 | |||
| shared-configs: | |||
| - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} | |||
| @ -0,0 +1,74 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <configuration scan="true" scanPeriod="60 seconds" debug="false"> | |||
| <!-- 日志存放路径 --> | |||
| <property name="log.path" value="logs/hxhq-auth" /> | |||
| <!-- 日志输出格式 --> | |||
| <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /> | |||
| <!-- 控制台输出 --> | |||
| <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> | |||
| <encoder> | |||
| <pattern>${log.pattern}</pattern> | |||
| </encoder> | |||
| </appender> | |||
| <!-- 系统日志输出 --> | |||
| <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||
| <file>${log.path}/info.log</file> | |||
| <!-- 循环政策:基于时间创建日志文件 --> | |||
| <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |||
| <!-- 日志文件名格式 --> | |||
| <fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern> | |||
| <!-- 日志最大的历史 60天 --> | |||
| <maxHistory>60</maxHistory> | |||
| </rollingPolicy> | |||
| <encoder> | |||
| <pattern>${log.pattern}</pattern> | |||
| </encoder> | |||
| <filter class="ch.qos.logback.classic.filter.LevelFilter"> | |||
| <!-- 过滤的级别 --> | |||
| <level>INFO</level> | |||
| <!-- 匹配时的操作:接收(记录) --> | |||
| <onMatch>ACCEPT</onMatch> | |||
| <!-- 不匹配时的操作:拒绝(不记录) --> | |||
| <onMismatch>DENY</onMismatch> | |||
| </filter> | |||
| </appender> | |||
| <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||
| <file>${log.path}/error.log</file> | |||
| <!-- 循环政策:基于时间创建日志文件 --> | |||
| <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |||
| <!-- 日志文件名格式 --> | |||
| <fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern> | |||
| <!-- 日志最大的历史 60天 --> | |||
| <maxHistory>60</maxHistory> | |||
| </rollingPolicy> | |||
| <encoder> | |||
| <pattern>${log.pattern}</pattern> | |||
| </encoder> | |||
| <filter class="ch.qos.logback.classic.filter.LevelFilter"> | |||
| <!-- 过滤的级别 --> | |||
| <level>ERROR</level> | |||
| <!-- 匹配时的操作:接收(记录) --> | |||
| <onMatch>ACCEPT</onMatch> | |||
| <!-- 不匹配时的操作:拒绝(不记录) --> | |||
| <onMismatch>DENY</onMismatch> | |||
| </filter> | |||
| </appender> | |||
| <!-- 系统模块日志级别控制 --> | |||
| <logger name="com.hxhq" level="info" /> | |||
| <!-- Spring日志级别控制 --> | |||
| <logger name="org.springframework" level="warn" /> | |||
| <root level="info"> | |||
| <appender-ref ref="console" /> | |||
| </root> | |||
| <!--系统操作日志--> | |||
| <root level="info"> | |||
| <appender-ref ref="file_info" /> | |||
| <appender-ref ref="file_error" /> | |||
| </root> | |||
| </configuration> | |||
| @ -0,0 +1,117 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xmlns="http://maven.apache.org/POM/4.0.0" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
| <parent> | |||
| <groupId>com.hxhq</groupId> | |||
| <artifactId>hxhq-common</artifactId> | |||
| <version>3.6.6</version> | |||
| </parent> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <artifactId>hxhq-common-core</artifactId> | |||
| <description> | |||
| hxhq-common-core核心模块 | |||
| </description> | |||
| <dependencies> | |||
| <!-- SpringCloud Openfeign --> | |||
| <dependency> | |||
| <groupId>org.springframework.cloud</groupId> | |||
| <artifactId>spring-cloud-starter-openfeign</artifactId> | |||
| </dependency> | |||
| <!-- SpringCloud Loadbalancer --> | |||
| <dependency> | |||
| <groupId>org.springframework.cloud</groupId> | |||
| <artifactId>spring-cloud-starter-loadbalancer</artifactId> | |||
| </dependency> | |||
| <!-- Spring Context Support --> | |||
| <dependency> | |||
| <groupId>org.springframework</groupId> | |||
| <artifactId>spring-context-support</artifactId> | |||
| </dependency> | |||
| <!-- Spring Web --> | |||
| <dependency> | |||
| <groupId>org.springframework</groupId> | |||
| <artifactId>spring-web</artifactId> | |||
| </dependency> | |||
| <!-- Transmittable ThreadLocal --> | |||
| <dependency> | |||
| <groupId>com.alibaba</groupId> | |||
| <artifactId>transmittable-thread-local</artifactId> | |||
| </dependency> | |||
| <!-- Pagehelper --> | |||
| <dependency> | |||
| <groupId>com.github.pagehelper</groupId> | |||
| <artifactId>pagehelper-spring-boot-starter</artifactId> | |||
| </dependency> | |||
| <!-- Hibernate Validator --> | |||
| <dependency> | |||
| <groupId>org.springframework.boot</groupId> | |||
| <artifactId>spring-boot-starter-validation</artifactId> | |||
| </dependency> | |||
| <!-- Jackson --> | |||
| <dependency> | |||
| <groupId>com.fasterxml.jackson.core</groupId> | |||
| <artifactId>jackson-databind</artifactId> | |||
| </dependency> | |||
| <!-- Alibaba Fastjson --> | |||
| <dependency> | |||
| <groupId>com.alibaba.fastjson2</groupId> | |||
| <artifactId>fastjson2</artifactId> | |||
| </dependency> | |||
| <!-- Jwt --> | |||
| <dependency> | |||
| <groupId>io.jsonwebtoken</groupId> | |||
| <artifactId>jjwt</artifactId> | |||
| </dependency> | |||
| <!-- Jaxb --> | |||
| <dependency> | |||
| <groupId>javax.xml.bind</groupId> | |||
| <artifactId>jaxb-api</artifactId> | |||
| </dependency> | |||
| <!-- Apache Lang3 --> | |||
| <dependency> | |||
| <groupId>org.apache.commons</groupId> | |||
| <artifactId>commons-lang3</artifactId> | |||
| </dependency> | |||
| <!-- Commons Io --> | |||
| <dependency> | |||
| <groupId>commons-io</groupId> | |||
| <artifactId>commons-io</artifactId> | |||
| </dependency> | |||
| <!-- excel工具 --> | |||
| <dependency> | |||
| <groupId>org.apache.poi</groupId> | |||
| <artifactId>poi-ooxml</artifactId> | |||
| </dependency> | |||
| <!-- Java Servlet --> | |||
| <dependency> | |||
| <groupId>javax.servlet</groupId> | |||
| <artifactId>javax.servlet-api</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.baomidou</groupId> | |||
| <artifactId>mybatis-plus-boot-starter</artifactId> | |||
| </dependency> | |||
| </dependencies> | |||
| </project> | |||
| @ -0,0 +1,219 @@ | |||
| package com.hxhq.common.core.annotation; | |||
| import java.lang.annotation.ElementType; | |||
| import java.lang.annotation.Retention; | |||
| import java.lang.annotation.RetentionPolicy; | |||
| import java.lang.annotation.Target; | |||
| import java.math.BigDecimal; | |||
| import org.apache.poi.ss.usermodel.HorizontalAlignment; | |||
| import org.apache.poi.ss.usermodel.IndexedColors; | |||
| import com.hxhq.common.core.utils.poi.ExcelHandlerAdapter; | |||
| /** | |||
| * 自定义导出Excel数据注解 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @Retention(RetentionPolicy.RUNTIME) | |||
| @Target(ElementType.FIELD) | |||
| public @interface Excel | |||
| { | |||
| /** | |||
| * 导出时在excel中排序 | |||
| */ | |||
| public int sort() default Integer.MAX_VALUE; | |||
| /** | |||
| * 导出到Excel中的名字. | |||
| */ | |||
| public String name() default ""; | |||
| /** | |||
| * 日期格式, 如: yyyy-MM-dd | |||
| */ | |||
| public String dateFormat() default ""; | |||
| /** | |||
| * 读取内容转表达式 (如: 0=男,1=女,2=未知) | |||
| */ | |||
| public String readConverterExp() default ""; | |||
| /** | |||
| * 分隔符,读取字符串组内容 | |||
| */ | |||
| public String separator() default ","; | |||
| /** | |||
| * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) | |||
| */ | |||
| public int scale() default -1; | |||
| /** | |||
| * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN | |||
| */ | |||
| public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; | |||
| /** | |||
| * 导出时在excel中每个列的高度 | |||
| */ | |||
| public double height() default 14; | |||
| /** | |||
| * 导出时在excel中每个列的宽度 | |||
| */ | |||
| public double width() default 16; | |||
| /** | |||
| * 文字后缀,如% 90 变成90% | |||
| */ | |||
| public String suffix() default ""; | |||
| /** | |||
| * 当值为空时,字段的默认值 | |||
| */ | |||
| public String defaultValue() default ""; | |||
| /** | |||
| * 提示信息 | |||
| */ | |||
| public String prompt() default ""; | |||
| /** | |||
| * 是否允许内容换行 | |||
| */ | |||
| public boolean wrapText() default false; | |||
| /** | |||
| * 设置只能选择不能输入的列内容. | |||
| */ | |||
| public String[] combo() default {}; | |||
| /** | |||
| * 是否需要纵向合并单元格,应对需求:含有list集合单元格) | |||
| */ | |||
| public boolean needMerge() default false; | |||
| /** | |||
| * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. | |||
| */ | |||
| public boolean isExport() default true; | |||
| /** | |||
| * 另一个类中的属性名称,支持多级获取,以小数点隔开 | |||
| */ | |||
| public String targetAttr() default ""; | |||
| /** | |||
| * 是否自动统计数据,在最后追加一行统计数据总和 | |||
| */ | |||
| public boolean isStatistics() default false; | |||
| /** | |||
| * 导出类型(0数字 1字符串) | |||
| */ | |||
| public ColumnType cellType() default ColumnType.STRING; | |||
| /** | |||
| * 导出列头背景颜色 | |||
| */ | |||
| public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; | |||
| /** | |||
| * 导出列头字体颜色 | |||
| */ | |||
| public IndexedColors headerColor() default IndexedColors.WHITE; | |||
| /** | |||
| * 导出单元格背景颜色 | |||
| */ | |||
| public IndexedColors backgroundColor() default IndexedColors.WHITE; | |||
| /** | |||
| * 导出单元格字体颜色 | |||
| */ | |||
| public IndexedColors color() default IndexedColors.BLACK; | |||
| /** | |||
| * 导出字段对齐方式 | |||
| */ | |||
| public HorizontalAlignment align() default HorizontalAlignment.CENTER; | |||
| /** | |||
| * 自定义数据处理器 | |||
| */ | |||
| public Class<?> handler() default ExcelHandlerAdapter.class; | |||
| /** | |||
| * 自定义数据处理器参数 | |||
| */ | |||
| public String[] args() default {}; | |||
| /** | |||
| * 字段类型(0:导出导入;1:仅导出;2:仅导入) | |||
| */ | |||
| Type type() default Type.ALL; | |||
| /** | |||
| * 类型 | |||
| */ | |||
| public enum Type | |||
| { | |||
| /** | |||
| * | |||
| */ | |||
| ALL(0), | |||
| /** | |||
| * | |||
| */ | |||
| EXPORT(1), | |||
| /** | |||
| * | |||
| */ | |||
| IMPORT(2); | |||
| private final int value; | |||
| Type(int value) | |||
| { | |||
| this.value = value; | |||
| } | |||
| public int value() | |||
| { | |||
| return this.value; | |||
| } | |||
| } | |||
| /** | |||
| * 列类型 | |||
| */ | |||
| public enum ColumnType | |||
| { | |||
| /** | |||
| * | |||
| */ | |||
| NUMERIC(0), | |||
| /** | |||
| * | |||
| */ | |||
| STRING(1), | |||
| /** | |||
| * | |||
| */ | |||
| IMAGE(2), | |||
| /** | |||
| * | |||
| */ | |||
| TEXT(3); | |||
| private final int value; | |||
| ColumnType(int value) | |||
| { | |||
| this.value = value; | |||
| } | |||
| public int value() | |||
| { | |||
| return this.value; | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,18 @@ | |||
| package com.hxhq.common.core.annotation; | |||
| import java.lang.annotation.ElementType; | |||
| import java.lang.annotation.Retention; | |||
| import java.lang.annotation.RetentionPolicy; | |||
| import java.lang.annotation.Target; | |||
| /** | |||
| * Excel注解集 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @Target(ElementType.FIELD) | |||
| @Retention(RetentionPolicy.RUNTIME) | |||
| public @interface Excels | |||
| { | |||
| Excel[] value(); | |||
| } | |||
| @ -0,0 +1,45 @@ | |||
| package com.hxhq.common.core.config; | |||
| import com.baomidou.mybatisplus.annotation.DbType; | |||
| import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; | |||
| import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; | |||
| import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; | |||
| import org.springframework.context.annotation.Bean; | |||
| import org.springframework.context.annotation.Configuration; | |||
| /** | |||
| * @author memory | |||
| */ | |||
| @Configuration | |||
| public class MyBatisPlusConfig { | |||
| /** | |||
| * 分页插件 | |||
| */ | |||
| @Bean | |||
| public PaginationInnerInterceptor paginationInterceptor() { | |||
| PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(); | |||
| // 设置请求的页面大于最大页后是否进行回滚, true回滚, false继续 | |||
| paginationInterceptor.setOverflow(false); | |||
| // 设置最大单页限制数量, 默认 500 条, -1 不受限制 | |||
| paginationInterceptor.setDbType(DbType.MYSQL); | |||
| paginationInterceptor.setMaxLimit(-1L); | |||
| return paginationInterceptor; | |||
| } | |||
| /** | |||
| * 乐观锁插件 | |||
| */ | |||
| @Bean | |||
| public OptimisticLockerInnerInterceptor optimisticLockerInterceptor() { | |||
| return new OptimisticLockerInnerInterceptor(); | |||
| } | |||
| /** | |||
| * 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html | |||
| */ | |||
| public BlockAttackInnerInterceptor blockAttackInnerInterceptor() { | |||
| return new BlockAttackInnerInterceptor(); | |||
| } | |||
| } | |||
| @ -0,0 +1,59 @@ | |||
| package com.hxhq.common.core.constant; | |||
| /** | |||
| * 缓存常量信息 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class CacheConstants | |||
| { | |||
| /** | |||
| * 缓存有效期,默认720(分钟) | |||
| */ | |||
| public final static long EXPIRATION = 720; | |||
| /** | |||
| * 缓存刷新时间,默认120(分钟) | |||
| */ | |||
| public final static long REFRESH_TIME = 120; | |||
| /** | |||
| * 密码最大错误次数 | |||
| */ | |||
| public final static int PASSWORD_MAX_RETRY_COUNT = 5; | |||
| /** | |||
| * 密码锁定时间,默认10(分钟) | |||
| */ | |||
| public final static long PASSWORD_LOCK_TIME = 10; | |||
| /** | |||
| * 权限缓存前缀 | |||
| */ | |||
| public final static String LOGIN_TOKEN_KEY = "login_tokens:"; | |||
| /** | |||
| * 验证码 redis key | |||
| */ | |||
| public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; | |||
| /** | |||
| * 参数管理 cache key | |||
| */ | |||
| public static final String SYS_CONFIG_KEY = "sys_config:"; | |||
| /** | |||
| * 字典管理 cache key | |||
| */ | |||
| public static final String SYS_DICT_KEY = "sys_dict:"; | |||
| /** | |||
| * 登录账户密码错误次数 redis key | |||
| */ | |||
| public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; | |||
| /** | |||
| * 登录IP黑名单 cache key | |||
| */ | |||
| public static final String SYS_LOGIN_BLACKIPLIST = SYS_CONFIG_KEY + "sys.login.blackIPList"; | |||
| } | |||
| @ -0,0 +1,145 @@ | |||
| package com.hxhq.common.core.constant; | |||
| /** | |||
| * 通用常量信息 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class Constants | |||
| { | |||
| /** | |||
| * UTF-8 字符集 | |||
| */ | |||
| public static final String UTF8 = "UTF-8"; | |||
| /** | |||
| * GBK 字符集 | |||
| */ | |||
| public static final String GBK = "GBK"; | |||
| /** | |||
| * www主域 | |||
| */ | |||
| public static final String WWW = "www."; | |||
| /** | |||
| * RMI 远程方法调用 | |||
| */ | |||
| public static final String LOOKUP_RMI = "rmi:"; | |||
| /** | |||
| * LDAP 远程方法调用 | |||
| */ | |||
| public static final String LOOKUP_LDAP = "ldap:"; | |||
| /** | |||
| * LDAPS 远程方法调用 | |||
| */ | |||
| public static final String LOOKUP_LDAPS = "ldaps:"; | |||
| /** | |||
| * http请求 | |||
| */ | |||
| public static final String HTTP = "http://"; | |||
| /** | |||
| * https请求 | |||
| */ | |||
| public static final String HTTPS = "https://"; | |||
| /** | |||
| * 成功标记 | |||
| */ | |||
| public static final Integer SUCCESS = 200; | |||
| /** | |||
| * 失败标记 | |||
| */ | |||
| public static final Integer FAIL = 500; | |||
| /** | |||
| * 登录成功状态 | |||
| */ | |||
| public static final String LOGIN_SUCCESS_STATUS = "0"; | |||
| /** | |||
| * 登录失败状态 | |||
| */ | |||
| public static final String LOGIN_FAIL_STATUS = "1"; | |||
| /** | |||
| * 登录成功 | |||
| */ | |||
| public static final String LOGIN_SUCCESS = "Success"; | |||
| /** | |||
| * 注销 | |||
| */ | |||
| public static final String LOGOUT = "Logout"; | |||
| /** | |||
| * 注册 | |||
| */ | |||
| public static final String REGISTER = "Register"; | |||
| /** | |||
| * 登录失败 | |||
| */ | |||
| public static final String LOGIN_FAIL = "Error"; | |||
| /** | |||
| * 所有权限标识 | |||
| */ | |||
| public static final String ALL_PERMISSION = "*:*:*"; | |||
| /** | |||
| * 管理员角色权限标识 | |||
| */ | |||
| public static final String SUPER_ADMIN = "admin"; | |||
| /** | |||
| * 当前记录起始索引 | |||
| */ | |||
| public static final String PAGE_NUM = "pageNum"; | |||
| /** | |||
| * 每页显示记录数 | |||
| */ | |||
| public static final String PAGE_SIZE = "pageSize"; | |||
| /** | |||
| * 排序列 | |||
| */ | |||
| public static final String ORDER_BY_COLUMN = "orderByColumn"; | |||
| /** | |||
| * 排序的方向 "desc" 或者 "asc". | |||
| */ | |||
| public static final String IS_ASC = "isAsc"; | |||
| /** | |||
| * 验证码有效期(分钟) | |||
| */ | |||
| public static final long CAPTCHA_EXPIRATION = 2; | |||
| /** | |||
| * 资源映射路径 前缀 | |||
| */ | |||
| public static final String RESOURCE_PREFIX = "/profile"; | |||
| /** | |||
| * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) | |||
| */ | |||
| public static final String[] JSON_WHITELIST_STR = { "com.hxhq" }; | |||
| /** | |||
| * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) | |||
| */ | |||
| public static final String[] JOB_WHITELIST_STR = { "com.hxhq.job.task" }; | |||
| /** | |||
| * 定时任务违规的字符 | |||
| */ | |||
| public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", | |||
| "org.springframework", "org.apache", "com.hxhq.common.core.utils.file" }; | |||
| } | |||
| @ -0,0 +1,117 @@ | |||
| package com.hxhq.common.core.constant; | |||
| /** | |||
| * 代码生成通用常量 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class GenConstants | |||
| { | |||
| /** 单表(增删改查) */ | |||
| public static final String TPL_CRUD = "crud"; | |||
| /** 树表(增删改查) */ | |||
| public static final String TPL_TREE = "tree"; | |||
| /** 主子表(增删改查) */ | |||
| public static final String TPL_SUB = "sub"; | |||
| /** 树编码字段 */ | |||
| public static final String TREE_CODE = "treeCode"; | |||
| /** 树父编码字段 */ | |||
| public static final String TREE_PARENT_CODE = "treeParentCode"; | |||
| /** 树名称字段 */ | |||
| public static final String TREE_NAME = "treeName"; | |||
| /** 上级菜单ID字段 */ | |||
| public static final String PARENT_MENU_ID = "parentMenuId"; | |||
| /** 上级菜单名称字段 */ | |||
| public static final String PARENT_MENU_NAME = "parentMenuName"; | |||
| /** 数据库字符串类型 */ | |||
| public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" }; | |||
| /** 数据库文本类型 */ | |||
| public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; | |||
| /** 数据库时间类型 */ | |||
| public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; | |||
| /** 数据库数字类型 */ | |||
| public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", | |||
| "bit", "bigint", "float", "double", "decimal" }; | |||
| /** 页面不需要编辑字段 */ | |||
| public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; | |||
| /** 页面不需要显示的列表字段 */ | |||
| public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", | |||
| "update_time" }; | |||
| /** 页面不需要查询字段 */ | |||
| public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", | |||
| "update_time", "remark" }; | |||
| /** Entity基类字段 */ | |||
| public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; | |||
| /** Tree基类字段 */ | |||
| public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors" }; | |||
| /** 文本框 */ | |||
| public static final String HTML_INPUT = "input"; | |||
| /** 文本域 */ | |||
| public static final String HTML_TEXTAREA = "textarea"; | |||
| /** 下拉框 */ | |||
| public static final String HTML_SELECT = "select"; | |||
| /** 单选框 */ | |||
| public static final String HTML_RADIO = "radio"; | |||
| /** 复选框 */ | |||
| public static final String HTML_CHECKBOX = "checkbox"; | |||
| /** 日期控件 */ | |||
| public static final String HTML_DATETIME = "datetime"; | |||
| /** 图片上传控件 */ | |||
| public static final String HTML_IMAGE_UPLOAD = "imageUpload"; | |||
| /** 文件上传控件 */ | |||
| public static final String HTML_FILE_UPLOAD = "fileUpload"; | |||
| /** 富文本控件 */ | |||
| public static final String HTML_EDITOR = "editor"; | |||
| /** 字符串类型 */ | |||
| public static final String TYPE_STRING = "String"; | |||
| /** 整型 */ | |||
| public static final String TYPE_INTEGER = "Integer"; | |||
| /** 长整型 */ | |||
| public static final String TYPE_LONG = "Long"; | |||
| /** 浮点型 */ | |||
| public static final String TYPE_DOUBLE = "Double"; | |||
| /** 高精度计算类型 */ | |||
| public static final String TYPE_BIGDECIMAL = "BigDecimal"; | |||
| /** 时间类型 */ | |||
| public static final String TYPE_DATE = "Date"; | |||
| /** 模糊查询 */ | |||
| public static final String QUERY_LIKE = "LIKE"; | |||
| /** 相等查询 */ | |||
| public static final String QUERY_EQ = "EQ"; | |||
| /** 需要 */ | |||
| public static final String REQUIRE = "1"; | |||
| } | |||
| @ -0,0 +1,94 @@ | |||
| package com.hxhq.common.core.constant; | |||
| /** | |||
| * 返回状态码 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class HttpStatus | |||
| { | |||
| /** | |||
| * 操作成功 | |||
| */ | |||
| public static final int SUCCESS = 200; | |||
| /** | |||
| * 对象创建成功 | |||
| */ | |||
| public static final int CREATED = 201; | |||
| /** | |||
| * 请求已经被接受 | |||
| */ | |||
| public static final int ACCEPTED = 202; | |||
| /** | |||
| * 操作已经执行成功,但是没有返回数据 | |||
| */ | |||
| public static final int NO_CONTENT = 204; | |||
| /** | |||
| * 资源已被移除 | |||
| */ | |||
| public static final int MOVED_PERM = 301; | |||
| /** | |||
| * 重定向 | |||
| */ | |||
| public static final int SEE_OTHER = 303; | |||
| /** | |||
| * 资源没有被修改 | |||
| */ | |||
| public static final int NOT_MODIFIED = 304; | |||
| /** | |||
| * 参数列表错误(缺少,格式不匹配) | |||
| */ | |||
| public static final int BAD_REQUEST = 400; | |||
| /** | |||
| * 未授权 | |||
| */ | |||
| public static final int UNAUTHORIZED = 401; | |||
| /** | |||
| * 访问受限,授权过期 | |||
| */ | |||
| public static final int FORBIDDEN = 403; | |||
| /** | |||
| * 资源,服务未找到 | |||
| */ | |||
| public static final int NOT_FOUND = 404; | |||
| /** | |||
| * 不允许的http方法 | |||
| */ | |||
| public static final int BAD_METHOD = 405; | |||
| /** | |||
| * 资源冲突,或者资源被锁 | |||
| */ | |||
| public static final int CONFLICT = 409; | |||
| /** | |||
| * 不支持的数据,媒体类型 | |||
| */ | |||
| public static final int UNSUPPORTED_TYPE = 415; | |||
| /** | |||
| * 系统内部错误 | |||
| */ | |||
| public static final int ERROR = 500; | |||
| /** | |||
| * 接口未实现 | |||
| */ | |||
| public static final int NOT_IMPLEMENTED = 501; | |||
| /** | |||
| * 系统警告消息 | |||
| */ | |||
| public static final int WARN = 601; | |||
| } | |||
| @ -0,0 +1,50 @@ | |||
| package com.hxhq.common.core.constant; | |||
| /** | |||
| * 任务调度通用常量 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class ScheduleConstants | |||
| { | |||
| public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; | |||
| /** 执行目标key */ | |||
| public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; | |||
| /** 默认 */ | |||
| public static final String MISFIRE_DEFAULT = "0"; | |||
| /** 立即触发执行 */ | |||
| public static final String MISFIRE_IGNORE_MISFIRES = "1"; | |||
| /** 触发一次执行 */ | |||
| public static final String MISFIRE_FIRE_AND_PROCEED = "2"; | |||
| /** 不触发立即执行 */ | |||
| public static final String MISFIRE_DO_NOTHING = "3"; | |||
| public enum Status | |||
| { | |||
| /** | |||
| * 正常 | |||
| */ | |||
| NORMAL("0"), | |||
| /** | |||
| * 暂停 | |||
| */ | |||
| PAUSE("1"); | |||
| private String value; | |||
| private Status(String value) | |||
| { | |||
| this.value = value; | |||
| } | |||
| public String getValue() | |||
| { | |||
| return value; | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,49 @@ | |||
| package com.hxhq.common.core.constant; | |||
| /** | |||
| * 权限相关通用常量 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class SecurityConstants | |||
| { | |||
| /** | |||
| * 用户ID字段 | |||
| */ | |||
| public static final String DETAILS_USER_ID = "user_id"; | |||
| /** | |||
| * 用户名字段 | |||
| */ | |||
| public static final String DETAILS_USERNAME = "username"; | |||
| /** | |||
| * 授权信息字段 | |||
| */ | |||
| public static final String AUTHORIZATION_HEADER = "Authorization"; | |||
| /** | |||
| * 请求来源 | |||
| */ | |||
| public static final String FROM_SOURCE = "from-source"; | |||
| /** | |||
| * 内部请求 | |||
| */ | |||
| public static final String INNER = "inner"; | |||
| /** | |||
| * 用户标识 | |||
| */ | |||
| public static final String USER_KEY = "user_key"; | |||
| /** | |||
| * 登录用户 | |||
| */ | |||
| public static final String LOGIN_USER = "login_user"; | |||
| /** | |||
| * 角色权限 | |||
| */ | |||
| public static final String ROLE_PERMISSION = "role_permission"; | |||
| } | |||
| @ -0,0 +1,24 @@ | |||
| package com.hxhq.common.core.constant; | |||
| /** | |||
| * 服务名称 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class ServiceNameConstants | |||
| { | |||
| /** | |||
| * 认证服务的serviceid | |||
| */ | |||
| public static final String AUTH_SERVICE = "hxhq-auth"; | |||
| /** | |||
| * 系统模块的serviceid | |||
| */ | |||
| public static final String SYSTEM_SERVICE = "hxhq-system"; | |||
| /** | |||
| * 文件服务的serviceid | |||
| */ | |||
| public static final String FILE_SERVICE = "hxhq-file"; | |||
| } | |||
| @ -0,0 +1,20 @@ | |||
| package com.hxhq.common.core.constant; | |||
| /** | |||
| * Token的Key常量 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class TokenConstants | |||
| { | |||
| /** | |||
| * 令牌前缀 | |||
| */ | |||
| public static final String PREFIX = "Bearer "; | |||
| /** | |||
| * 令牌秘钥 | |||
| */ | |||
| public final static String SECRET = "abcdefghijklmnopqrstuvwxyz"; | |||
| } | |||
| @ -0,0 +1,88 @@ | |||
| package com.hxhq.common.core.constant; | |||
| /** | |||
| * 用户常量信息 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class UserConstants | |||
| { | |||
| /** | |||
| * 平台内系统用户的唯一标志 | |||
| */ | |||
| public static final String SYS_USER = "SYS_USER"; | |||
| /** 正常状态 */ | |||
| public static final String NORMAL = "0"; | |||
| /** 异常状态 */ | |||
| public static final String EXCEPTION = "1"; | |||
| /** 用户封禁状态 */ | |||
| public static final String USER_DISABLE = "1"; | |||
| /** 角色正常状态 */ | |||
| public static final String ROLE_NORMAL = "0"; | |||
| /** 角色封禁状态 */ | |||
| public static final String ROLE_DISABLE = "1"; | |||
| /** 部门正常状态 */ | |||
| public static final String DEPT_NORMAL = "0"; | |||
| /** 部门停用状态 */ | |||
| public static final String DEPT_DISABLE = "1"; | |||
| /** 字典正常状态 */ | |||
| public static final String DICT_NORMAL = "0"; | |||
| /** 是否为系统默认(是) */ | |||
| public static final String YES = "Y"; | |||
| /** 是否菜单外链(是) */ | |||
| public static final String YES_FRAME = "0"; | |||
| /** 是否菜单外链(否) */ | |||
| public static final String NO_FRAME = "1"; | |||
| /** 菜单类型(目录) */ | |||
| public static final String TYPE_DIR = "M"; | |||
| /** 菜单类型(菜单) */ | |||
| public static final String TYPE_MENU = "C"; | |||
| /** 菜单类型(按钮) */ | |||
| public static final String TYPE_BUTTON = "F"; | |||
| /** Layout组件标识 */ | |||
| public final static String LAYOUT = "Layout"; | |||
| /** ParentView组件标识 */ | |||
| public final static String PARENT_VIEW = "ParentView"; | |||
| /** InnerLink组件标识 */ | |||
| public final static String INNER_LINK = "InnerLink"; | |||
| /** 校验是否唯一的返回标识 */ | |||
| public final static boolean UNIQUE = true; | |||
| public final static boolean NOT_UNIQUE = false; | |||
| /** | |||
| * 用户名长度限制 | |||
| */ | |||
| public static final int USERNAME_MIN_LENGTH = 2; | |||
| public static final int USERNAME_MAX_LENGTH = 20; | |||
| /** | |||
| * 密码长度限制 | |||
| */ | |||
| public static final int PASSWORD_MIN_LENGTH = 5; | |||
| public static final int PASSWORD_MAX_LENGTH = 20; | |||
| public static boolean isAdmin(Long userId) | |||
| { | |||
| return userId != null && 1L == userId; | |||
| } | |||
| } | |||
| @ -0,0 +1,98 @@ | |||
| package com.hxhq.common.core.context; | |||
| import java.util.Map; | |||
| import java.util.concurrent.ConcurrentHashMap; | |||
| import com.alibaba.ttl.TransmittableThreadLocal; | |||
| import com.hxhq.common.core.text.Convert; | |||
| import com.hxhq.common.core.utils.StringUtils; | |||
| import com.hxhq.common.core.constant.SecurityConstants; | |||
| /** | |||
| * 获取当前线程变量中的 用户id、用户名称、Token等信息 | |||
| * 注意: 必须在网关通过请求头的方法传入,同时在HeaderInterceptor拦截器设置值。 否则这里无法获取 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class SecurityContextHolder | |||
| { | |||
| private static final TransmittableThreadLocal<Map<String, Object>> THREAD_LOCAL = new TransmittableThreadLocal<>(); | |||
| public static void set(String key, Object value) | |||
| { | |||
| Map<String, Object> map = getLocalMap(); | |||
| map.put(key, value == null ? StringUtils.EMPTY : value); | |||
| } | |||
| public static String get(String key) | |||
| { | |||
| Map<String, Object> map = getLocalMap(); | |||
| return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY)); | |||
| } | |||
| public static <T> T get(String key, Class<T> clazz) | |||
| { | |||
| Map<String, Object> map = getLocalMap(); | |||
| return StringUtils.cast(map.getOrDefault(key, null)); | |||
| } | |||
| public static Map<String, Object> getLocalMap() | |||
| { | |||
| Map<String, Object> map = THREAD_LOCAL.get(); | |||
| if (map == null) | |||
| { | |||
| map = new ConcurrentHashMap<String, Object>(16); | |||
| THREAD_LOCAL.set(map); | |||
| } | |||
| return map; | |||
| } | |||
| public static void setLocalMap(Map<String, Object> threadLocalMap) | |||
| { | |||
| THREAD_LOCAL.set(threadLocalMap); | |||
| } | |||
| public static Long getUserId() | |||
| { | |||
| return Convert.toLong(get(SecurityConstants.DETAILS_USER_ID), 0L); | |||
| } | |||
| public static void setUserId(String account) | |||
| { | |||
| set(SecurityConstants.DETAILS_USER_ID, account); | |||
| } | |||
| public static String getUserName() | |||
| { | |||
| return get(SecurityConstants.DETAILS_USERNAME); | |||
| } | |||
| public static void setUserName(String username) | |||
| { | |||
| set(SecurityConstants.DETAILS_USERNAME, username); | |||
| } | |||
| public static String getUserKey() | |||
| { | |||
| return get(SecurityConstants.USER_KEY); | |||
| } | |||
| public static void setUserKey(String userKey) | |||
| { | |||
| set(SecurityConstants.USER_KEY, userKey); | |||
| } | |||
| public static String getPermission() | |||
| { | |||
| return get(SecurityConstants.ROLE_PERMISSION); | |||
| } | |||
| public static void setPermission(String permissions) | |||
| { | |||
| set(SecurityConstants.ROLE_PERMISSION, permissions); | |||
| } | |||
| public static void remove() | |||
| { | |||
| THREAD_LOCAL.remove(); | |||
| } | |||
| } | |||
| @ -0,0 +1,104 @@ | |||
| package com.hxhq.common.core.domain; | |||
| import com.baomidou.mybatisplus.annotation.*; | |||
| import com.fasterxml.jackson.annotation.JsonFormat; | |||
| import java.io.Serializable; | |||
| import java.util.Date; | |||
| /** | |||
| * Entity基类 | |||
| * | |||
| * @author badcat | |||
| */ | |||
| public class MpBaseEntity implements Serializable | |||
| { | |||
| @TableField(exist = false) | |||
| private static final long serialVersionUID = 1L; | |||
| /** 编号 */ | |||
| @TableId(type = IdType.AUTO) | |||
| private Long id; | |||
| /** 创建者 */ | |||
| @TableField(fill = FieldFill.INSERT) | |||
| private String createBy; | |||
| /** 创建时间 */ | |||
| @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") | |||
| @TableField(fill = FieldFill.INSERT) | |||
| private Date createTime; | |||
| /** 更新者 */ | |||
| @TableField(fill = FieldFill.UPDATE) | |||
| private String updateBy; | |||
| /** 更新时间 */ | |||
| @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") | |||
| @TableField(fill = FieldFill.UPDATE) | |||
| private Date updateTime; | |||
| /** 备注 */ | |||
| private String remark; | |||
| @TableField(value = "del_flag", fill = FieldFill.INSERT) | |||
| @TableLogic | |||
| protected String delFlag = "0"; | |||
| public Long getId() { | |||
| return id; | |||
| } | |||
| public void setId(Long id) { | |||
| this.id = id; | |||
| } | |||
| public String getCreateBy() { | |||
| return createBy; | |||
| } | |||
| public void setCreateBy(String createBy) { | |||
| this.createBy = createBy; | |||
| } | |||
| public Date getCreateTime() { | |||
| return createTime; | |||
| } | |||
| public void setCreateTime(Date createTime) { | |||
| this.createTime = createTime; | |||
| } | |||
| public String getUpdateBy() { | |||
| return updateBy; | |||
| } | |||
| public void setUpdateBy(String updateBy) { | |||
| this.updateBy = updateBy; | |||
| } | |||
| public Date getUpdateTime() { | |||
| return updateTime; | |||
| } | |||
| public void setUpdateTime(Date updateTime) { | |||
| this.updateTime = updateTime; | |||
| } | |||
| public String getRemark() { | |||
| return remark; | |||
| } | |||
| public void setRemark(String remark) { | |||
| this.remark = remark; | |||
| } | |||
| public String getDelFlag() { | |||
| return delFlag; | |||
| } | |||
| public void setDelFlag(String delFlag) { | |||
| this.delFlag = delFlag; | |||
| } | |||
| } | |||
| @ -0,0 +1,116 @@ | |||
| package com.hxhq.common.core.domain; | |||
| import java.io.Serializable; | |||
| import com.hxhq.common.core.constant.Constants; | |||
| /** | |||
| * 响应信息主体 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @SuppressWarnings("AlibabaClassNamingShouldBeCamel") | |||
| public class R<T> implements Serializable | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| /** 成功 */ | |||
| public static final int SUCCESS = Constants.SUCCESS; | |||
| /** 失败 */ | |||
| public static final int FAIL = Constants.FAIL; | |||
| private int code; | |||
| private String msg; | |||
| private T data; | |||
| public static <T> R<T> ok() | |||
| { | |||
| return restResult(null, SUCCESS, null); | |||
| } | |||
| public static <T> R<T> ok(T data) | |||
| { | |||
| return restResult(data, SUCCESS, null); | |||
| } | |||
| public static <T> R<T> ok(T data, String msg) | |||
| { | |||
| return restResult(data, SUCCESS, msg); | |||
| } | |||
| public static <T> R<T> fail() | |||
| { | |||
| return restResult(null, FAIL, null); | |||
| } | |||
| public static <T> R<T> fail(String msg) | |||
| { | |||
| return restResult(null, FAIL, msg); | |||
| } | |||
| public static <T> R<T> fail(T data) | |||
| { | |||
| return restResult(data, FAIL, null); | |||
| } | |||
| public static <T> R<T> fail(T data, String msg) | |||
| { | |||
| return restResult(data, FAIL, msg); | |||
| } | |||
| public static <T> R<T> fail(int code, String msg) | |||
| { | |||
| return restResult(null, code, msg); | |||
| } | |||
| private static <T> R<T> restResult(T data, int code, String msg) | |||
| { | |||
| R<T> apiResult = new R<>(); | |||
| apiResult.setCode(code); | |||
| apiResult.setData(data); | |||
| apiResult.setMsg(msg); | |||
| return apiResult; | |||
| } | |||
| public int getCode() | |||
| { | |||
| return code; | |||
| } | |||
| public void setCode(int code) | |||
| { | |||
| this.code = code; | |||
| } | |||
| public String getMsg() | |||
| { | |||
| return msg; | |||
| } | |||
| public void setMsg(String msg) | |||
| { | |||
| this.msg = msg; | |||
| } | |||
| public T getData() | |||
| { | |||
| return data; | |||
| } | |||
| public void setData(T data) | |||
| { | |||
| this.data = data; | |||
| } | |||
| public static <T> Boolean isError(R<T> ret) | |||
| { | |||
| return !isSuccess(ret); | |||
| } | |||
| public static <T> Boolean isSuccess(R<T> ret) | |||
| { | |||
| return R.SUCCESS == ret.getCode(); | |||
| } | |||
| } | |||
| @ -0,0 +1,41 @@ | |||
| package com.hxhq.common.core.enums; | |||
| /** | |||
| * 用户状态 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public enum UserStatus | |||
| { | |||
| /** | |||
| *正常 | |||
| */ | |||
| OK("0", "正常"), | |||
| /** | |||
| *停用 | |||
| */ | |||
| DISABLE("1", "停用"), | |||
| /** | |||
| * 删除 | |||
| */ | |||
| DELETED("2", "删除"); | |||
| private final String code; | |||
| private final String info; | |||
| UserStatus(String code, String info) | |||
| { | |||
| this.code = code; | |||
| this.info = info; | |||
| } | |||
| public String getCode() | |||
| { | |||
| return code; | |||
| } | |||
| public String getInfo() | |||
| { | |||
| return info; | |||
| } | |||
| } | |||
| @ -0,0 +1,16 @@ | |||
| package com.hxhq.common.core.exception; | |||
| /** | |||
| * 验证码错误异常类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class CaptchaException extends RuntimeException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public CaptchaException(String msg) | |||
| { | |||
| super(msg); | |||
| } | |||
| } | |||
| @ -0,0 +1,31 @@ | |||
| package com.hxhq.common.core.exception; | |||
| /** | |||
| * 检查异常 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class CheckedException extends RuntimeException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public CheckedException(String message) | |||
| { | |||
| super(message); | |||
| } | |||
| public CheckedException(Throwable cause) | |||
| { | |||
| super(cause); | |||
| } | |||
| public CheckedException(String message, Throwable cause) | |||
| { | |||
| super(message, cause); | |||
| } | |||
| public CheckedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) | |||
| { | |||
| super(message, cause, enableSuppression, writableStackTrace); | |||
| } | |||
| } | |||
| @ -0,0 +1,15 @@ | |||
| package com.hxhq.common.core.exception; | |||
| /** | |||
| * 演示模式异常 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class DemoModeException extends RuntimeException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public DemoModeException() | |||
| { | |||
| } | |||
| } | |||
| @ -0,0 +1,58 @@ | |||
| package com.hxhq.common.core.exception; | |||
| /** | |||
| * 全局异常 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class GlobalException extends RuntimeException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| /** | |||
| * 错误提示 | |||
| */ | |||
| private String message; | |||
| /** | |||
| * 错误明细,内部调试错误 | |||
| * | |||
| * 和 {@link CommonResult#getDetailMessage()} 一致的设计 | |||
| */ | |||
| private String detailMessage; | |||
| /** | |||
| * 空构造方法,避免反序列化问题 | |||
| */ | |||
| public GlobalException() | |||
| { | |||
| } | |||
| public GlobalException(String message) | |||
| { | |||
| this.message = message; | |||
| } | |||
| public String getDetailMessage() | |||
| { | |||
| return detailMessage; | |||
| } | |||
| public GlobalException setDetailMessage(String detailMessage) | |||
| { | |||
| this.detailMessage = detailMessage; | |||
| return this; | |||
| } | |||
| @Override | |||
| public String getMessage() | |||
| { | |||
| return message; | |||
| } | |||
| public GlobalException setMessage(String message) | |||
| { | |||
| this.message = message; | |||
| return this; | |||
| } | |||
| } | |||
| @ -0,0 +1,16 @@ | |||
| package com.hxhq.common.core.exception; | |||
| /** | |||
| * 内部认证异常 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class InnerAuthException extends RuntimeException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public InnerAuthException(String message) | |||
| { | |||
| super(message); | |||
| } | |||
| } | |||
| @ -0,0 +1,15 @@ | |||
| package com.hxhq.common.core.exception; | |||
| /** | |||
| * 权限异常 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class PreAuthorizeException extends RuntimeException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public PreAuthorizeException() | |||
| { | |||
| } | |||
| } | |||
| @ -0,0 +1,74 @@ | |||
| package com.hxhq.common.core.exception; | |||
| /** | |||
| * 业务异常 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public final class ServiceException extends RuntimeException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| /** | |||
| * 错误码 | |||
| */ | |||
| private Integer code; | |||
| /** | |||
| * 错误提示 | |||
| */ | |||
| private String message; | |||
| /** | |||
| * 错误明细,内部调试错误 | |||
| * | |||
| * 和 {@link CommonResult#getDetailMessage()} 一致的设计 | |||
| */ | |||
| private String detailMessage; | |||
| /** | |||
| * 空构造方法,避免反序列化问题 | |||
| */ | |||
| public ServiceException() | |||
| { | |||
| } | |||
| public ServiceException(String message) | |||
| { | |||
| this.message = message; | |||
| } | |||
| public ServiceException(String message, Integer code) | |||
| { | |||
| this.message = message; | |||
| this.code = code; | |||
| } | |||
| public String getDetailMessage() | |||
| { | |||
| return detailMessage; | |||
| } | |||
| @Override | |||
| public String getMessage() | |||
| { | |||
| return message; | |||
| } | |||
| public Integer getCode() | |||
| { | |||
| return code; | |||
| } | |||
| public ServiceException setMessage(String message) | |||
| { | |||
| this.message = message; | |||
| return this; | |||
| } | |||
| public ServiceException setDetailMessage(String detailMessage) | |||
| { | |||
| this.detailMessage = detailMessage; | |||
| return this; | |||
| } | |||
| } | |||
| @ -0,0 +1,26 @@ | |||
| package com.hxhq.common.core.exception; | |||
| /** | |||
| * 工具类异常 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class UtilException extends RuntimeException | |||
| { | |||
| private static final long serialVersionUID = 8247610319171014183L; | |||
| public UtilException(Throwable e) | |||
| { | |||
| super(e.getMessage(), e); | |||
| } | |||
| public UtilException(String message) | |||
| { | |||
| super(message); | |||
| } | |||
| public UtilException(String message, Throwable throwable) | |||
| { | |||
| super(message, throwable); | |||
| } | |||
| } | |||
| @ -0,0 +1,16 @@ | |||
| package com.hxhq.common.core.exception.auth; | |||
| /** | |||
| * 未能通过的登录认证异常 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class NotLoginException extends RuntimeException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public NotLoginException(String message) | |||
| { | |||
| super(message); | |||
| } | |||
| } | |||
| @ -0,0 +1,23 @@ | |||
| package com.hxhq.common.core.exception.auth; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| /** | |||
| * 未能通过的权限认证异常 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class NotPermissionException extends RuntimeException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public NotPermissionException(String permission) | |||
| { | |||
| super(permission); | |||
| } | |||
| public NotPermissionException(String[] permissions) | |||
| { | |||
| super(StringUtils.join(permissions, ",")); | |||
| } | |||
| } | |||
| @ -0,0 +1,23 @@ | |||
| package com.hxhq.common.core.exception.auth; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| /** | |||
| * 未能通过的角色认证异常 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class NotRoleException extends RuntimeException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public NotRoleException(String role) | |||
| { | |||
| super(role); | |||
| } | |||
| public NotRoleException(String[] roles) | |||
| { | |||
| super(StringUtils.join(roles, ",")); | |||
| } | |||
| } | |||
| @ -0,0 +1,79 @@ | |||
| package com.hxhq.common.core.exception.base; | |||
| /** | |||
| * 基础异常 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class BaseException extends RuntimeException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| /** | |||
| * 所属模块 | |||
| */ | |||
| private String module; | |||
| /** | |||
| * 错误码 | |||
| */ | |||
| private String code; | |||
| /** | |||
| * 错误码对应的参数 | |||
| */ | |||
| private Object[] args; | |||
| /** | |||
| * 错误消息 | |||
| */ | |||
| private String defaultMessage; | |||
| public BaseException(String module, String code, Object[] args, String defaultMessage) | |||
| { | |||
| this.module = module; | |||
| this.code = code; | |||
| this.args = args; | |||
| this.defaultMessage = defaultMessage; | |||
| } | |||
| public BaseException(String module, String code, Object[] args) | |||
| { | |||
| this(module, code, args, null); | |||
| } | |||
| public BaseException(String module, String defaultMessage) | |||
| { | |||
| this(module, null, null, defaultMessage); | |||
| } | |||
| public BaseException(String code, Object[] args) | |||
| { | |||
| this(null, code, args, null); | |||
| } | |||
| public BaseException(String defaultMessage) | |||
| { | |||
| this(null, null, null, defaultMessage); | |||
| } | |||
| public String getModule() | |||
| { | |||
| return module; | |||
| } | |||
| public String getCode() | |||
| { | |||
| return code; | |||
| } | |||
| public Object[] getArgs() | |||
| { | |||
| return args; | |||
| } | |||
| public String getDefaultMessage() | |||
| { | |||
| return defaultMessage; | |||
| } | |||
| } | |||
| @ -0,0 +1,19 @@ | |||
| package com.hxhq.common.core.exception.file; | |||
| import com.hxhq.common.core.exception.base.BaseException; | |||
| /** | |||
| * 文件信息异常类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class FileException extends BaseException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public FileException(String code, Object[] args, String msg) | |||
| { | |||
| super("file", code, args, msg); | |||
| } | |||
| } | |||
| @ -0,0 +1,16 @@ | |||
| package com.hxhq.common.core.exception.file; | |||
| /** | |||
| * 文件名称超长限制异常类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class FileNameLengthLimitExceededException extends FileException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public FileNameLengthLimitExceededException(int defaultFileNameLength) | |||
| { | |||
| super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }, "the filename is too long"); | |||
| } | |||
| } | |||
| @ -0,0 +1,16 @@ | |||
| package com.hxhq.common.core.exception.file; | |||
| /** | |||
| * 文件名大小限制异常类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class FileSizeLimitExceededException extends FileException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public FileSizeLimitExceededException(long defaultMaxSize) | |||
| { | |||
| super("upload.exceed.maxSize", new Object[] { defaultMaxSize }, "the filesize is too large"); | |||
| } | |||
| } | |||
| @ -0,0 +1,61 @@ | |||
| package com.hxhq.common.core.exception.file; | |||
| import java.io.PrintStream; | |||
| import java.io.PrintWriter; | |||
| /** | |||
| * 文件上传异常类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class FileUploadException extends Exception | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| private final Throwable cause; | |||
| public FileUploadException() | |||
| { | |||
| this(null, null); | |||
| } | |||
| public FileUploadException(final String msg) | |||
| { | |||
| this(msg, null); | |||
| } | |||
| public FileUploadException(String msg, Throwable cause) | |||
| { | |||
| super(msg); | |||
| this.cause = cause; | |||
| } | |||
| @Override | |||
| public void printStackTrace(PrintStream stream) | |||
| { | |||
| super.printStackTrace(stream); | |||
| if (cause != null) | |||
| { | |||
| stream.println("Caused by:"); | |||
| cause.printStackTrace(stream); | |||
| } | |||
| } | |||
| @Override | |||
| public void printStackTrace(PrintWriter writer) | |||
| { | |||
| super.printStackTrace(writer); | |||
| if (cause != null) | |||
| { | |||
| writer.println("Caused by:"); | |||
| cause.printStackTrace(writer); | |||
| } | |||
| } | |||
| @Override | |||
| public Throwable getCause() | |||
| { | |||
| return cause; | |||
| } | |||
| } | |||
| @ -0,0 +1,80 @@ | |||
| package com.hxhq.common.core.exception.file; | |||
| import java.util.Arrays; | |||
| /** | |||
| * 文件上传 误异常类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class InvalidExtensionException extends FileUploadException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| private String[] allowedExtension; | |||
| private String extension; | |||
| private String filename; | |||
| public InvalidExtensionException(String[] allowedExtension, String extension, String filename) | |||
| { | |||
| super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]"); | |||
| this.allowedExtension = allowedExtension; | |||
| this.extension = extension; | |||
| this.filename = filename; | |||
| } | |||
| public String[] getAllowedExtension() | |||
| { | |||
| return allowedExtension; | |||
| } | |||
| public String getExtension() | |||
| { | |||
| return extension; | |||
| } | |||
| public String getFilename() | |||
| { | |||
| return filename; | |||
| } | |||
| public static class InvalidImageExtensionException extends InvalidExtensionException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) | |||
| { | |||
| super(allowedExtension, extension, filename); | |||
| } | |||
| } | |||
| public static class InvalidFlashExtensionException extends InvalidExtensionException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) | |||
| { | |||
| super(allowedExtension, extension, filename); | |||
| } | |||
| } | |||
| public static class InvalidMediaExtensionException extends InvalidExtensionException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) | |||
| { | |||
| super(allowedExtension, extension, filename); | |||
| } | |||
| } | |||
| public static class InvalidVideoExtensionException extends InvalidExtensionException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) | |||
| { | |||
| super(allowedExtension, extension, filename); | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,60 @@ | |||
| package com.hxhq.common.core.exception.job; | |||
| /** | |||
| * 计划策略异常 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class TaskException extends Exception | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| private Code code; | |||
| public TaskException(String msg, Code code) | |||
| { | |||
| this(msg, code, null); | |||
| } | |||
| public TaskException(String msg, Code code, Exception nestedEx) | |||
| { | |||
| super(msg, nestedEx); | |||
| this.code = code; | |||
| } | |||
| public Code getCode() | |||
| { | |||
| return code; | |||
| } | |||
| /** | |||
| * 类型 | |||
| */ | |||
| public enum Code | |||
| { | |||
| /** | |||
| * | |||
| */ | |||
| TASK_EXISTS, | |||
| /** | |||
| * | |||
| */ | |||
| NO_TASK_EXISTS, | |||
| /** | |||
| * | |||
| */ | |||
| TASK_ALREADY_STARTED, | |||
| /** | |||
| * | |||
| */ | |||
| UNKNOWN, | |||
| /** | |||
| * | |||
| */ | |||
| CONFIG_ERROR, | |||
| /** | |||
| * | |||
| */ | |||
| TASK_NODE_NOT_AVAILABLE | |||
| } | |||
| } | |||
| @ -0,0 +1,16 @@ | |||
| package com.hxhq.common.core.exception.user; | |||
| /** | |||
| * 验证码失效异常类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class CaptchaExpireException extends UserException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public CaptchaExpireException() | |||
| { | |||
| super("user.jcaptcha.expire", null); | |||
| } | |||
| } | |||
| @ -0,0 +1,18 @@ | |||
| package com.hxhq.common.core.exception.user; | |||
| import com.hxhq.common.core.exception.base.BaseException; | |||
| /** | |||
| * 用户信息异常类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class UserException extends BaseException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public UserException(String code, Object[] args) | |||
| { | |||
| super("user", code, args, null); | |||
| } | |||
| } | |||
| @ -0,0 +1,16 @@ | |||
| package com.hxhq.common.core.exception.user; | |||
| /** | |||
| * 用户密码不正确或不符合规范异常类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class UserPasswordNotMatchException extends UserException | |||
| { | |||
| private static final long serialVersionUID = 1L; | |||
| public UserPasswordNotMatchException() | |||
| { | |||
| super("user.password.not.match", null); | |||
| } | |||
| } | |||
| @ -0,0 +1,87 @@ | |||
| package com.hxhq.common.core.text; | |||
| import java.nio.charset.Charset; | |||
| import java.nio.charset.StandardCharsets; | |||
| import com.hxhq.common.core.utils.StringUtils; | |||
| /** | |||
| * 字符集工具类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class CharsetKit | |||
| { | |||
| /** ISO-8859-1 */ | |||
| public static final String ISO_8859_1 = "ISO-8859-1"; | |||
| /** UTF-8 */ | |||
| public static final String UTF_8 = "UTF-8"; | |||
| /** GBK */ | |||
| public static final String GBK = "GBK"; | |||
| /** ISO-8859-1 */ | |||
| public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); | |||
| /** UTF-8 */ | |||
| public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); | |||
| /** GBK */ | |||
| public static final Charset CHARSET_GBK = Charset.forName(GBK); | |||
| /** | |||
| * 转换为Charset对象 | |||
| * | |||
| * @param charset 字符集,为空则返回默认字符集 | |||
| * @return Charset | |||
| */ | |||
| public static Charset charset(String charset) | |||
| { | |||
| return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); | |||
| } | |||
| /** | |||
| * 转换字符串的字符集编码 | |||
| * | |||
| * @param source 字符串 | |||
| * @param srcCharset 源字符集,默认ISO-8859-1 | |||
| * @param destCharset 目标字符集,默认UTF-8 | |||
| * @return 转换后的字符集 | |||
| */ | |||
| public static String convert(String source, String srcCharset, String destCharset) | |||
| { | |||
| return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); | |||
| } | |||
| /** | |||
| * 转换字符串的字符集编码 | |||
| * | |||
| * @param source 字符串 | |||
| * @param srcCharset 源字符集,默认ISO-8859-1 | |||
| * @param destCharset 目标字符集,默认UTF-8 | |||
| * @return 转换后的字符集 | |||
| */ | |||
| public static String convert(String source, Charset srcCharset, Charset destCharset) | |||
| { | |||
| if (null == srcCharset) | |||
| { | |||
| srcCharset = StandardCharsets.ISO_8859_1; | |||
| } | |||
| if (null == destCharset) | |||
| { | |||
| destCharset = StandardCharsets.UTF_8; | |||
| } | |||
| if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) | |||
| { | |||
| return source; | |||
| } | |||
| return new String(source.getBytes(srcCharset), destCharset); | |||
| } | |||
| /** | |||
| * @return 系统字符集编码 | |||
| */ | |||
| public static String systemCharset() | |||
| { | |||
| return Charset.defaultCharset().name(); | |||
| } | |||
| } | |||
| @ -0,0 +1,92 @@ | |||
| package com.hxhq.common.core.text; | |||
| import com.hxhq.common.core.utils.StringUtils; | |||
| /** | |||
| * 字符串格式化 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class StrFormatter | |||
| { | |||
| public static final String EMPTY_JSON = "{}"; | |||
| public static final char C_BACKSLASH = '\\'; | |||
| public static final char C_DELIM_START = '{'; | |||
| public static final char C_DELIM_END = '}'; | |||
| /** | |||
| * 格式化字符串<br> | |||
| * 此方法只是简单将占位符 {} 按照顺序替换为参数<br> | |||
| * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br> | |||
| * 例:<br> | |||
| * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br> | |||
| * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br> | |||
| * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> | |||
| * | |||
| * @param strPattern 字符串模板 | |||
| * @param argArray 参数列表 | |||
| * @return 结果 | |||
| */ | |||
| public static String format(final String strPattern, final Object... argArray) | |||
| { | |||
| if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) | |||
| { | |||
| return strPattern; | |||
| } | |||
| final int strPatternLength = strPattern.length(); | |||
| // 初始化定义好的长度以获得更好的性能 | |||
| StringBuilder sbuf = new StringBuilder(strPatternLength + 50); | |||
| int handledPosition = 0; | |||
| int delimIndex;// 占位符所在位置 | |||
| for (int argIndex = 0; argIndex < argArray.length; argIndex++) | |||
| { | |||
| delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); | |||
| if (delimIndex == -1) | |||
| { | |||
| if (handledPosition == 0) | |||
| { | |||
| return strPattern; | |||
| } | |||
| else | |||
| { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 | |||
| sbuf.append(strPattern, handledPosition, strPatternLength); | |||
| return sbuf.toString(); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) | |||
| { | |||
| if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) | |||
| { | |||
| // 转义符之前还有一个转义符,占位符依旧有效 | |||
| sbuf.append(strPattern, handledPosition, delimIndex - 1); | |||
| sbuf.append(Convert.utf8Str(argArray[argIndex])); | |||
| handledPosition = delimIndex + 2; | |||
| } | |||
| else | |||
| { | |||
| // 占位符被转义 | |||
| argIndex--; | |||
| sbuf.append(strPattern, handledPosition, delimIndex - 1); | |||
| sbuf.append(C_DELIM_START); | |||
| handledPosition = delimIndex + 1; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| // 正常占位符 | |||
| sbuf.append(strPattern, handledPosition, delimIndex); | |||
| sbuf.append(Convert.utf8Str(argArray[argIndex])); | |||
| handledPosition = delimIndex + 2; | |||
| } | |||
| } | |||
| } | |||
| // 加入最后一个占位符后所有的字符 | |||
| sbuf.append(strPattern, handledPosition, strPattern.length()); | |||
| return sbuf.toString(); | |||
| } | |||
| } | |||
| @ -0,0 +1,191 @@ | |||
| package com.hxhq.common.core.utils; | |||
| import java.lang.management.ManagementFactory; | |||
| import java.text.ParseException; | |||
| import java.text.SimpleDateFormat; | |||
| import java.time.LocalDate; | |||
| import java.time.LocalDateTime; | |||
| import java.time.LocalTime; | |||
| import java.time.ZoneId; | |||
| import java.time.ZonedDateTime; | |||
| import java.util.Date; | |||
| import org.apache.commons.lang3.time.DateFormatUtils; | |||
| /** | |||
| * 时间工具类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class DateUtils extends org.apache.commons.lang3.time.DateUtils | |||
| { | |||
| public static String YYYY = "yyyy"; | |||
| public static String YYYY_MM = "yyyy-MM"; | |||
| public static String YYYY_MM_DD = "yyyy-MM-dd"; | |||
| public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; | |||
| public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; | |||
| private static String[] parsePatterns = { | |||
| "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", | |||
| "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", | |||
| "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; | |||
| /** | |||
| * 获取当前Date型日期 | |||
| * | |||
| * @return Date() 当前日期 | |||
| */ | |||
| public static Date getNowDate() | |||
| { | |||
| return new Date(); | |||
| } | |||
| /** | |||
| * 获取当前日期, 默认格式为yyyy-MM-dd | |||
| * | |||
| * @return String | |||
| */ | |||
| public static String getDate() | |||
| { | |||
| return dateTimeNow(YYYY_MM_DD); | |||
| } | |||
| public static final String getTime() | |||
| { | |||
| return dateTimeNow(YYYY_MM_DD_HH_MM_SS); | |||
| } | |||
| public static final String dateTimeNow() | |||
| { | |||
| return dateTimeNow(YYYYMMDDHHMMSS); | |||
| } | |||
| public static final String dateTimeNow(final String format) | |||
| { | |||
| return parseDateToStr(format, new Date()); | |||
| } | |||
| public static final String dateTime(final Date date) | |||
| { | |||
| return parseDateToStr(YYYY_MM_DD, date); | |||
| } | |||
| public static final String parseDateToStr(final String format, final Date date) | |||
| { | |||
| return new SimpleDateFormat(format).format(date); | |||
| } | |||
| public static final Date dateTime(final String format, final String ts) | |||
| { | |||
| try | |||
| { | |||
| return new SimpleDateFormat(format).parse(ts); | |||
| } | |||
| catch (ParseException e) | |||
| { | |||
| throw new RuntimeException(e); | |||
| } | |||
| } | |||
| /** | |||
| * 日期路径 即年/月/日 如2018/08/08 | |||
| */ | |||
| public static final String datePath() | |||
| { | |||
| Date now = new Date(); | |||
| return DateFormatUtils.format(now, "yyyy/MM/dd"); | |||
| } | |||
| /** | |||
| * 日期路径 即年/月/日 如20180808 | |||
| */ | |||
| public static final String dateTime() | |||
| { | |||
| Date now = new Date(); | |||
| return DateFormatUtils.format(now, "yyyyMMdd"); | |||
| } | |||
| /** | |||
| * 日期型字符串转化为日期 格式 | |||
| */ | |||
| public static Date parseDate(Object str) | |||
| { | |||
| if (str == null) | |||
| { | |||
| return null; | |||
| } | |||
| try | |||
| { | |||
| return parseDate(str.toString(), parsePatterns); | |||
| } | |||
| catch (ParseException e) | |||
| { | |||
| return null; | |||
| } | |||
| } | |||
| /** | |||
| * 获取服务器启动时间 | |||
| */ | |||
| public static Date getServerStartDate() | |||
| { | |||
| long time = ManagementFactory.getRuntimeMXBean().getStartTime(); | |||
| return new Date(time); | |||
| } | |||
| /** | |||
| * 计算相差天数 | |||
| */ | |||
| public static int differentDaysByMillisecond(Date date1, Date date2) | |||
| { | |||
| return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); | |||
| } | |||
| /** | |||
| * 计算时间差 | |||
| * | |||
| * @param endDate 最后时间 | |||
| * @param startTime 开始时间 | |||
| * @return 时间差(天/小时/分钟) | |||
| */ | |||
| public static String timeDistance(Date endDate, Date startTime) | |||
| { | |||
| long nd = 1000 * 24 * 60 * 60; | |||
| long nh = 1000 * 60 * 60; | |||
| long nm = 1000 * 60; | |||
| // long ns = 1000; | |||
| // 获得两个时间的毫秒时间差异 | |||
| long diff = endDate.getTime() - startTime.getTime(); | |||
| // 计算差多少天 | |||
| long day = diff / nd; | |||
| // 计算差多少小时 | |||
| long hour = diff % nd / nh; | |||
| // 计算差多少分钟 | |||
| long min = diff % nd % nh / nm; | |||
| // 计算差多少秒//输出结果 | |||
| // long sec = diff % nd % nh % nm / ns; | |||
| return day + "天" + hour + "小时" + min + "分钟"; | |||
| } | |||
| /** | |||
| * 增加 LocalDateTime ==> Date | |||
| */ | |||
| public static Date toDate(LocalDateTime temporalAccessor) | |||
| { | |||
| ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); | |||
| return Date.from(zdt.toInstant()); | |||
| } | |||
| /** | |||
| * 增加 LocalDate ==> Date | |||
| */ | |||
| public static Date toDate(LocalDate temporalAccessor) | |||
| { | |||
| LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); | |||
| ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); | |||
| return Date.from(zdt.toInstant()); | |||
| } | |||
| } | |||
| @ -0,0 +1,39 @@ | |||
| package com.hxhq.common.core.utils; | |||
| import java.io.PrintWriter; | |||
| import java.io.StringWriter; | |||
| import org.apache.commons.lang3.exception.ExceptionUtils; | |||
| /** | |||
| * 错误信息处理类。 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class ExceptionUtil | |||
| { | |||
| /** | |||
| * 获取exception的详细错误信息。 | |||
| */ | |||
| public static String getExceptionMessage(Throwable e) | |||
| { | |||
| StringWriter sw = new StringWriter(); | |||
| e.printStackTrace(new PrintWriter(sw, true)); | |||
| return sw.toString(); | |||
| } | |||
| public static String getRootErrorMessage(Exception e) | |||
| { | |||
| Throwable root = ExceptionUtils.getRootCause(e); | |||
| root = (root == null ? e : root); | |||
| if (root == null) | |||
| { | |||
| return ""; | |||
| } | |||
| String msg = root.getMessage(); | |||
| if (msg == null) | |||
| { | |||
| return "null"; | |||
| } | |||
| return StringUtils.defaultString(msg); | |||
| } | |||
| } | |||
| @ -0,0 +1,124 @@ | |||
| package com.hxhq.common.core.utils; | |||
| import java.util.Map; | |||
| import com.hxhq.common.core.text.Convert; | |||
| import com.hxhq.common.core.constant.SecurityConstants; | |||
| import com.hxhq.common.core.constant.TokenConstants; | |||
| import io.jsonwebtoken.Claims; | |||
| import io.jsonwebtoken.Jwts; | |||
| import io.jsonwebtoken.SignatureAlgorithm; | |||
| /** | |||
| * Jwt工具类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class JwtUtils | |||
| { | |||
| public static String secret = TokenConstants.SECRET; | |||
| /** | |||
| * 从数据声明生成令牌 | |||
| * | |||
| * @param claims 数据声明 | |||
| * @return 令牌 | |||
| */ | |||
| public static String createToken(Map<String, Object> claims) | |||
| { | |||
| String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact(); | |||
| return token; | |||
| } | |||
| /** | |||
| * 从令牌中获取数据声明 | |||
| * | |||
| * @param token 令牌 | |||
| * @return 数据声明 | |||
| */ | |||
| public static Claims parseToken(String token) | |||
| { | |||
| return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); | |||
| } | |||
| /** | |||
| * 根据令牌获取用户标识 | |||
| * | |||
| * @param token 令牌 | |||
| * @return 用户ID | |||
| */ | |||
| public static String getUserKey(String token) | |||
| { | |||
| Claims claims = parseToken(token); | |||
| return getValue(claims, SecurityConstants.USER_KEY); | |||
| } | |||
| /** | |||
| * 根据令牌获取用户标识 | |||
| * | |||
| * @param claims 身份信息 | |||
| * @return 用户ID | |||
| */ | |||
| public static String getUserKey(Claims claims) | |||
| { | |||
| return getValue(claims, SecurityConstants.USER_KEY); | |||
| } | |||
| /** | |||
| * 根据令牌获取用户ID | |||
| * | |||
| * @param token 令牌 | |||
| * @return 用户ID | |||
| */ | |||
| public static String getUserId(String token) | |||
| { | |||
| Claims claims = parseToken(token); | |||
| return getValue(claims, SecurityConstants.DETAILS_USER_ID); | |||
| } | |||
| /** | |||
| * 根据身份信息获取用户ID | |||
| * | |||
| * @param claims 身份信息 | |||
| * @return 用户ID | |||
| */ | |||
| public static String getUserId(Claims claims) | |||
| { | |||
| return getValue(claims, SecurityConstants.DETAILS_USER_ID); | |||
| } | |||
| /** | |||
| * 根据令牌获取用户名 | |||
| * | |||
| * @param token 令牌 | |||
| * @return 用户名 | |||
| */ | |||
| public static String getUserName(String token) | |||
| { | |||
| Claims claims = parseToken(token); | |||
| return getValue(claims, SecurityConstants.DETAILS_USERNAME); | |||
| } | |||
| /** | |||
| * 根据身份信息获取用户名 | |||
| * | |||
| * @param claims 身份信息 | |||
| * @return 用户名 | |||
| */ | |||
| public static String getUserName(Claims claims) | |||
| { | |||
| return getValue(claims, SecurityConstants.DETAILS_USERNAME); | |||
| } | |||
| /** | |||
| * 根据身份信息获取键值 | |||
| * | |||
| * @param claims 身份信息 | |||
| * @param key 键 | |||
| * @return 值 | |||
| */ | |||
| public static String getValue(Claims claims, String key) | |||
| { | |||
| return Convert.toStr(claims.get(key), ""); | |||
| } | |||
| } | |||
| @ -0,0 +1,35 @@ | |||
| package com.hxhq.common.core.utils; | |||
| import com.github.pagehelper.PageHelper; | |||
| import com.hxhq.common.core.web.page.PageDomain; | |||
| import com.hxhq.common.core.web.page.TableSupport; | |||
| import com.hxhq.common.core.utils.sql.SqlUtil; | |||
| /** | |||
| * 分页工具类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class PageUtils extends PageHelper | |||
| { | |||
| /** | |||
| * 设置请求分页数据 | |||
| */ | |||
| public static void startPage() | |||
| { | |||
| PageDomain pageDomain = TableSupport.buildPageRequest(); | |||
| Integer pageNum = pageDomain.getPageNum(); | |||
| Integer pageSize = pageDomain.getPageSize(); | |||
| String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); | |||
| Boolean reasonable = pageDomain.getReasonable(); | |||
| PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); | |||
| } | |||
| /** | |||
| * 清理分页的线程变量 | |||
| */ | |||
| public static void clearPage() | |||
| { | |||
| PageHelper.clearPage(); | |||
| } | |||
| } | |||
| @ -0,0 +1,339 @@ | |||
| package com.hxhq.common.core.utils; | |||
| import java.io.IOException; | |||
| import java.io.UnsupportedEncodingException; | |||
| import java.net.URLDecoder; | |||
| import java.net.URLEncoder; | |||
| import java.util.Collections; | |||
| import java.util.Enumeration; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import javax.servlet.ServletRequest; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import javax.servlet.http.HttpSession; | |||
| import com.hxhq.common.core.text.Convert; | |||
| import org.springframework.core.io.buffer.DataBuffer; | |||
| import org.springframework.http.HttpHeaders; | |||
| import org.springframework.http.HttpStatus; | |||
| import org.springframework.http.MediaType; | |||
| import org.springframework.http.server.reactive.ServerHttpResponse; | |||
| import org.springframework.util.LinkedCaseInsensitiveMap; | |||
| import org.springframework.web.context.request.RequestAttributes; | |||
| import org.springframework.web.context.request.RequestContextHolder; | |||
| import org.springframework.web.context.request.ServletRequestAttributes; | |||
| import com.alibaba.fastjson2.JSON; | |||
| import com.hxhq.common.core.constant.Constants; | |||
| import com.hxhq.common.core.domain.R; | |||
| import reactor.core.publisher.Mono; | |||
| /** | |||
| * 客户端工具类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class ServletUtils | |||
| { | |||
| /** | |||
| * 获取String参数 | |||
| */ | |||
| public static String getParameter(String name) | |||
| { | |||
| return getRequest().getParameter(name); | |||
| } | |||
| /** | |||
| * 获取String参数 | |||
| */ | |||
| public static String getParameter(String name, String defaultValue) | |||
| { | |||
| return Convert.toStr(getRequest().getParameter(name), defaultValue); | |||
| } | |||
| /** | |||
| * 获取Integer参数 | |||
| */ | |||
| public static Integer getParameterToInt(String name) | |||
| { | |||
| return Convert.toInt(getRequest().getParameter(name)); | |||
| } | |||
| /** | |||
| * 获取Integer参数 | |||
| */ | |||
| public static Integer getParameterToInt(String name, Integer defaultValue) | |||
| { | |||
| return Convert.toInt(getRequest().getParameter(name), defaultValue); | |||
| } | |||
| /** | |||
| * 获取Boolean参数 | |||
| */ | |||
| public static Boolean getParameterToBool(String name) | |||
| { | |||
| return Convert.toBool(getRequest().getParameter(name)); | |||
| } | |||
| /** | |||
| * 获取Boolean参数 | |||
| */ | |||
| public static Boolean getParameterToBool(String name, Boolean defaultValue) | |||
| { | |||
| return Convert.toBool(getRequest().getParameter(name), defaultValue); | |||
| } | |||
| /** | |||
| * 获得所有请求参数 | |||
| * | |||
| * @param request 请求对象{@link ServletRequest} | |||
| * @return Map | |||
| */ | |||
| public static Map<String, String[]> getParams(ServletRequest request) | |||
| { | |||
| final Map<String, String[]> map = request.getParameterMap(); | |||
| return Collections.unmodifiableMap(map); | |||
| } | |||
| /** | |||
| * 获得所有请求参数 | |||
| * | |||
| * @param request 请求对象{@link ServletRequest} | |||
| * @return Map | |||
| */ | |||
| public static Map<String, String> getParamMap(ServletRequest request) | |||
| { | |||
| Map<String, String> params = new HashMap<>(16); | |||
| for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) | |||
| { | |||
| params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); | |||
| } | |||
| return params; | |||
| } | |||
| /** | |||
| * 获取request | |||
| */ | |||
| public static HttpServletRequest getRequest() | |||
| { | |||
| try | |||
| { | |||
| return getRequestAttributes().getRequest(); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| return null; | |||
| } | |||
| } | |||
| /** | |||
| * 获取response | |||
| */ | |||
| public static HttpServletResponse getResponse() | |||
| { | |||
| try | |||
| { | |||
| return getRequestAttributes().getResponse(); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| return null; | |||
| } | |||
| } | |||
| /** | |||
| * 获取session | |||
| */ | |||
| public static HttpSession getSession() | |||
| { | |||
| return getRequest().getSession(); | |||
| } | |||
| public static ServletRequestAttributes getRequestAttributes() | |||
| { | |||
| try | |||
| { | |||
| RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); | |||
| return (ServletRequestAttributes) attributes; | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| return null; | |||
| } | |||
| } | |||
| public static String getHeader(HttpServletRequest request, String name) | |||
| { | |||
| String value = request.getHeader(name); | |||
| if (StringUtils.isEmpty(value)) | |||
| { | |||
| return StringUtils.EMPTY; | |||
| } | |||
| return urlDecode(value); | |||
| } | |||
| public static Map<String, String> getHeaders(HttpServletRequest request) | |||
| { | |||
| Map<String, String> map = new LinkedCaseInsensitiveMap<>(); | |||
| Enumeration<String> enumeration = request.getHeaderNames(); | |||
| if (enumeration != null) | |||
| { | |||
| while (enumeration.hasMoreElements()) | |||
| { | |||
| String key = enumeration.nextElement(); | |||
| String value = request.getHeader(key); | |||
| map.put(key, value); | |||
| } | |||
| } | |||
| return map; | |||
| } | |||
| /** | |||
| * 将字符串渲染到客户端 | |||
| * | |||
| * @param response 渲染对象 | |||
| * @param string 待渲染的字符串 | |||
| */ | |||
| public static void renderString(HttpServletResponse response, String string) | |||
| { | |||
| try | |||
| { | |||
| response.setStatus(200); | |||
| response.setContentType("application/json"); | |||
| response.setCharacterEncoding("utf-8"); | |||
| response.getWriter().print(string); | |||
| } | |||
| catch (IOException e) | |||
| { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| /** | |||
| * 是否是Ajax异步请求 | |||
| * | |||
| * @param request | |||
| */ | |||
| public static boolean isAjaxRequest(HttpServletRequest request) | |||
| { | |||
| String accept = request.getHeader("accept"); | |||
| String a1 = "application/json"; | |||
| String a2 = "X-Requested-With"; | |||
| String a3 = "XMLHttpRequest"; | |||
| if (accept != null && accept.contains(a1)) | |||
| { | |||
| return true; | |||
| } | |||
| String xRequestedWith = request.getHeader(a2); | |||
| if (xRequestedWith != null && xRequestedWith.contains(a3)) | |||
| { | |||
| return true; | |||
| } | |||
| String uri = request.getRequestURI(); | |||
| String a4 = ".json"; | |||
| String a5 = ".xml"; | |||
| if (StringUtils.inStringIgnoreCase(uri, a4, a5)) | |||
| { | |||
| return true; | |||
| } | |||
| String ajax = request.getParameter("__ajax"); | |||
| return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); | |||
| } | |||
| /** | |||
| * 内容编码 | |||
| * | |||
| * @param str 内容 | |||
| * @return 编码后的内容 | |||
| */ | |||
| public static String urlEncode(String str) | |||
| { | |||
| try | |||
| { | |||
| return URLEncoder.encode(str, Constants.UTF8); | |||
| } | |||
| catch (UnsupportedEncodingException e) | |||
| { | |||
| return StringUtils.EMPTY; | |||
| } | |||
| } | |||
| /** | |||
| * 内容解码 | |||
| * | |||
| * @param str 内容 | |||
| * @return 解码后的内容 | |||
| */ | |||
| public static String urlDecode(String str) | |||
| { | |||
| try | |||
| { | |||
| return URLDecoder.decode(str, Constants.UTF8); | |||
| } | |||
| catch (UnsupportedEncodingException e) | |||
| { | |||
| return StringUtils.EMPTY; | |||
| } | |||
| } | |||
| /** | |||
| * 设置webflux模型响应 | |||
| * | |||
| * @param response ServerHttpResponse | |||
| * @param value 响应内容 | |||
| * @return Mono<Void> | |||
| */ | |||
| public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value) | |||
| { | |||
| return webFluxResponseWriter(response, HttpStatus.OK, value, R.FAIL); | |||
| } | |||
| /** | |||
| * 设置webflux模型响应 | |||
| * | |||
| * @param response ServerHttpResponse | |||
| * @param code 响应状态码 | |||
| * @param value 响应内容 | |||
| * @return Mono<Void> | |||
| */ | |||
| public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value, int code) | |||
| { | |||
| return webFluxResponseWriter(response, HttpStatus.OK, value, code); | |||
| } | |||
| /** | |||
| * 设置webflux模型响应 | |||
| * | |||
| * @param response ServerHttpResponse | |||
| * @param status http状态码 | |||
| * @param code 响应状态码 | |||
| * @param value 响应内容 | |||
| * @return Mono<Void> | |||
| */ | |||
| public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code) | |||
| { | |||
| return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code); | |||
| } | |||
| /** | |||
| * 设置webflux模型响应 | |||
| * | |||
| * @param response ServerHttpResponse | |||
| * @param contentType content-type | |||
| * @param status http状态码 | |||
| * @param code 响应状态码 | |||
| * @param value 响应内容 | |||
| * @return Mono<Void> | |||
| */ | |||
| public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) | |||
| { | |||
| response.setStatusCode(status); | |||
| response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType); | |||
| R<?> result = R.fail(code, value.toString()); | |||
| DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes()); | |||
| return response.writeWith(Mono.just(dataBuffer)); | |||
| } | |||
| } | |||
| @ -0,0 +1,114 @@ | |||
| package com.hxhq.common.core.utils; | |||
| import org.springframework.aop.framework.AopContext; | |||
| import org.springframework.beans.BeansException; | |||
| import org.springframework.beans.factory.NoSuchBeanDefinitionException; | |||
| import org.springframework.beans.factory.config.BeanFactoryPostProcessor; | |||
| import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; | |||
| import org.springframework.stereotype.Component; | |||
| /** | |||
| * spring工具类 方便在非spring管理环境中获取bean | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @Component | |||
| public final class SpringUtils implements BeanFactoryPostProcessor | |||
| { | |||
| /** Spring应用上下文环境 */ | |||
| private static ConfigurableListableBeanFactory beanFactory; | |||
| @Override | |||
| public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException | |||
| { | |||
| SpringUtils.beanFactory = beanFactory; | |||
| } | |||
| /** | |||
| * 获取对象 | |||
| * | |||
| * @param name | |||
| * @return Object 一个以所给名字注册的bean的实例 | |||
| * @throws org.springframework.beans.BeansException | |||
| * | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public static <T> T getBean(String name) throws BeansException | |||
| { | |||
| return (T) beanFactory.getBean(name); | |||
| } | |||
| /** | |||
| * 获取类型为requiredType的对象 | |||
| * | |||
| * @param clz | |||
| * @return | |||
| * @throws org.springframework.beans.BeansException | |||
| * | |||
| */ | |||
| public static <T> T getBean(Class<T> clz) throws BeansException | |||
| { | |||
| T result = (T) beanFactory.getBean(clz); | |||
| return result; | |||
| } | |||
| /** | |||
| * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true | |||
| * | |||
| * @param name | |||
| * @return boolean | |||
| */ | |||
| public static boolean containsBean(String name) | |||
| { | |||
| return beanFactory.containsBean(name); | |||
| } | |||
| /** | |||
| * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) | |||
| * | |||
| * @param name | |||
| * @return boolean | |||
| * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException | |||
| * | |||
| */ | |||
| public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException | |||
| { | |||
| return beanFactory.isSingleton(name); | |||
| } | |||
| /** | |||
| * @param name | |||
| * @return Class 注册对象的类型 | |||
| * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException | |||
| * | |||
| */ | |||
| public static Class<?> getType(String name) throws NoSuchBeanDefinitionException | |||
| { | |||
| return beanFactory.getType(name); | |||
| } | |||
| /** | |||
| * 如果给定的bean名字在bean定义中有别名,则返回这些别名 | |||
| * | |||
| * @param name | |||
| * @return | |||
| * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException | |||
| * | |||
| */ | |||
| public static String[] getAliases(String name) throws NoSuchBeanDefinitionException | |||
| { | |||
| return beanFactory.getAliases(name); | |||
| } | |||
| /** | |||
| * 获取aop代理对象 | |||
| * | |||
| * @param invoker | |||
| * @return | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public static <T> T getAopProxy(T invoker) | |||
| { | |||
| return (T) AopContext.currentProxy(); | |||
| } | |||
| } | |||
| @ -0,0 +1,634 @@ | |||
| package com.hxhq.common.core.utils; | |||
| import java.util.Collection; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import org.springframework.util.AntPathMatcher; | |||
| import com.hxhq.common.core.constant.Constants; | |||
| import com.hxhq.common.core.text.StrFormatter; | |||
| /** | |||
| * 字符串工具类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class StringUtils extends org.apache.commons.lang3.StringUtils | |||
| { | |||
| /** 空字符串 */ | |||
| private static final String NULLSTR = ""; | |||
| /** 下划线 */ | |||
| private static final char SEPARATOR = '_'; | |||
| /** 星号 */ | |||
| private static final char ASTERISK = '*'; | |||
| /** | |||
| * 获取参数不为空值 | |||
| * | |||
| * @param value defaultValue 要判断的value | |||
| * @return value 返回值 | |||
| */ | |||
| public static <T> T nvl(T value, T defaultValue) | |||
| { | |||
| return value != null ? value : defaultValue; | |||
| } | |||
| /** | |||
| * * 判断一个Collection是否为空, 包含List,Set,Queue | |||
| * | |||
| * @param coll 要判断的Collection | |||
| * @return true:为空 false:非空 | |||
| */ | |||
| public static boolean isEmpty(Collection<?> coll) | |||
| { | |||
| return isNull(coll) || coll.isEmpty(); | |||
| } | |||
| /** | |||
| * * 判断一个Collection是否非空,包含List,Set,Queue | |||
| * | |||
| * @param coll 要判断的Collection | |||
| * @return true:非空 false:空 | |||
| */ | |||
| public static boolean isNotEmpty(Collection<?> coll) | |||
| { | |||
| return !isEmpty(coll); | |||
| } | |||
| /** | |||
| * * 判断一个对象数组是否为空 | |||
| * | |||
| * @param objects 要判断的对象数组 | |||
| ** @return true:为空 false:非空 | |||
| */ | |||
| public static boolean isEmpty(Object[] objects) | |||
| { | |||
| return isNull(objects) || (objects.length == 0); | |||
| } | |||
| /** | |||
| * * 判断一个对象数组是否非空 | |||
| * | |||
| * @param objects 要判断的对象数组 | |||
| * @return true:非空 false:空 | |||
| */ | |||
| public static boolean isNotEmpty(Object[] objects) | |||
| { | |||
| return !isEmpty(objects); | |||
| } | |||
| /** | |||
| * * 判断一个Map是否为空 | |||
| * | |||
| * @param map 要判断的Map | |||
| * @return true:为空 false:非空 | |||
| */ | |||
| public static boolean isEmpty(Map<?, ?> map) | |||
| { | |||
| return isNull(map) || map.isEmpty(); | |||
| } | |||
| /** | |||
| * * 判断一个Map是否为空 | |||
| * | |||
| * @param map 要判断的Map | |||
| * @return true:非空 false:空 | |||
| */ | |||
| public static boolean isNotEmpty(Map<?, ?> map) | |||
| { | |||
| return !isEmpty(map); | |||
| } | |||
| /** | |||
| * * 判断一个字符串是否为空串 | |||
| * | |||
| * @param str String | |||
| * @return true:为空 false:非空 | |||
| */ | |||
| public static boolean isEmpty(String str) | |||
| { | |||
| return isNull(str) || NULLSTR.equals(str.trim()); | |||
| } | |||
| /** | |||
| * * 判断一个字符串是否为非空串 | |||
| * | |||
| * @param str String | |||
| * @return true:非空串 false:空串 | |||
| */ | |||
| public static boolean isNotEmpty(String str) | |||
| { | |||
| return !isEmpty(str); | |||
| } | |||
| /** | |||
| * * 判断一个对象是否为空 | |||
| * | |||
| * @param object Object | |||
| * @return true:为空 false:非空 | |||
| */ | |||
| public static boolean isNull(Object object) | |||
| { | |||
| return object == null; | |||
| } | |||
| /** | |||
| * * 判断一个对象是否非空 | |||
| * | |||
| * @param object Object | |||
| * @return true:非空 false:空 | |||
| */ | |||
| public static boolean isNotNull(Object object) | |||
| { | |||
| return !isNull(object); | |||
| } | |||
| /** | |||
| * * 判断一个对象是否是数组类型(Java基本型别的数组) | |||
| * | |||
| * @param object 对象 | |||
| * @return true:是数组 false:不是数组 | |||
| */ | |||
| public static boolean isArray(Object object) | |||
| { | |||
| return isNotNull(object) && object.getClass().isArray(); | |||
| } | |||
| /** | |||
| * 去空格 | |||
| */ | |||
| public static String trim(String str) | |||
| { | |||
| return (str == null ? "" : str.trim()); | |||
| } | |||
| /** | |||
| * 替换指定字符串的指定区间内字符为"*" | |||
| * | |||
| * @param str 字符串 | |||
| * @param startInclude 开始位置(包含) | |||
| * @param endExclude 结束位置(不包含) | |||
| * @return 替换后的字符串 | |||
| */ | |||
| public static String hide(CharSequence str, int startInclude, int endExclude) | |||
| { | |||
| if (isEmpty(str)) | |||
| { | |||
| return NULLSTR; | |||
| } | |||
| final int strLength = str.length(); | |||
| if (startInclude > strLength) | |||
| { | |||
| return NULLSTR; | |||
| } | |||
| if (endExclude > strLength) | |||
| { | |||
| endExclude = strLength; | |||
| } | |||
| if (startInclude > endExclude) | |||
| { | |||
| // 如果起始位置大于结束位置,不替换 | |||
| return NULLSTR; | |||
| } | |||
| final char[] chars = new char[strLength]; | |||
| for (int i = 0; i < strLength; i++) | |||
| { | |||
| if (i >= startInclude && i < endExclude) | |||
| { | |||
| chars[i] = ASTERISK; | |||
| } | |||
| else | |||
| { | |||
| chars[i] = str.charAt(i); | |||
| } | |||
| } | |||
| return new String(chars); | |||
| } | |||
| /** | |||
| * 截取字符串 | |||
| * | |||
| * @param str 字符串 | |||
| * @param start 开始 | |||
| * @return 结果 | |||
| */ | |||
| public static String substring(final String str, int start) | |||
| { | |||
| if (str == null) | |||
| { | |||
| return NULLSTR; | |||
| } | |||
| if (start < 0) | |||
| { | |||
| start = str.length() + start; | |||
| } | |||
| if (start < 0) | |||
| { | |||
| start = 0; | |||
| } | |||
| if (start > str.length()) | |||
| { | |||
| return NULLSTR; | |||
| } | |||
| return str.substring(start); | |||
| } | |||
| /** | |||
| * 截取字符串 | |||
| * | |||
| * @param str 字符串 | |||
| * @param start 开始 | |||
| * @param end 结束 | |||
| * @return 结果 | |||
| */ | |||
| public static String substring(final String str, int start, int end) | |||
| { | |||
| if (str == null) | |||
| { | |||
| return NULLSTR; | |||
| } | |||
| if (end < 0) | |||
| { | |||
| end = str.length() + end; | |||
| } | |||
| if (start < 0) | |||
| { | |||
| start = str.length() + start; | |||
| } | |||
| if (end > str.length()) | |||
| { | |||
| end = str.length(); | |||
| } | |||
| if (start > end) | |||
| { | |||
| return NULLSTR; | |||
| } | |||
| if (start < 0) | |||
| { | |||
| start = 0; | |||
| } | |||
| if (end < 0) | |||
| { | |||
| end = 0; | |||
| } | |||
| return str.substring(start, end); | |||
| } | |||
| /** | |||
| * 在字符串中查找第一个出现的 `open` 和最后一个出现的 `close` 之间的子字符串 | |||
| * | |||
| * @param str 要截取的字符串 | |||
| * @param open 起始字符串 | |||
| * @param close 结束字符串 | |||
| * @return 截取结果 | |||
| */ | |||
| public static String substringBetweenLast(final String str, final String open, final String close) | |||
| { | |||
| if (isEmpty(str) || isEmpty(open) || isEmpty(close)) | |||
| { | |||
| return NULLSTR; | |||
| } | |||
| final int start = str.indexOf(open); | |||
| if (start != INDEX_NOT_FOUND) | |||
| { | |||
| final int end = str.lastIndexOf(close); | |||
| if (end != INDEX_NOT_FOUND) | |||
| { | |||
| return str.substring(start + open.length(), end); | |||
| } | |||
| } | |||
| return NULLSTR; | |||
| } | |||
| /** | |||
| * 判断是否为空,并且不是空白字符 | |||
| * | |||
| * @param str 要判断的value | |||
| * @return 结果 | |||
| */ | |||
| public static boolean hasText(String str) | |||
| { | |||
| return (str != null && !str.isEmpty() && containsText(str)); | |||
| } | |||
| private static boolean containsText(CharSequence str) | |||
| { | |||
| int strLen = str.length(); | |||
| for (int i = 0; i < strLen; i++) | |||
| { | |||
| if (!Character.isWhitespace(str.charAt(i))) | |||
| { | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| /** | |||
| * 格式化文本, {} 表示占位符<br> | |||
| * 此方法只是简单将占位符 {} 按照顺序替换为参数<br> | |||
| * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br> | |||
| * 例:<br> | |||
| * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br> | |||
| * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br> | |||
| * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> | |||
| * | |||
| * @param template 文本模板,被替换的部分用 {} 表示 | |||
| * @param params 参数值 | |||
| * @return 格式化后的文本 | |||
| */ | |||
| public static String format(String template, Object... params) | |||
| { | |||
| if (isEmpty(params) || isEmpty(template)) | |||
| { | |||
| return template; | |||
| } | |||
| return StrFormatter.format(template, params); | |||
| } | |||
| /** | |||
| * 是否为http(s)://开头 | |||
| * | |||
| * @param link 链接 | |||
| * @return 结果 | |||
| */ | |||
| public static boolean ishttp(String link) | |||
| { | |||
| return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); | |||
| } | |||
| /** | |||
| * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value | |||
| * | |||
| * @param collection 给定的集合 | |||
| * @param array 给定的数组 | |||
| * @return boolean 结果 | |||
| */ | |||
| public static boolean containsAny(Collection<String> collection, String... array) | |||
| { | |||
| if (isEmpty(collection) || isEmpty(array)) | |||
| { | |||
| return false; | |||
| } | |||
| else | |||
| { | |||
| for (String str : array) | |||
| { | |||
| if (collection.contains(str)) | |||
| { | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| } | |||
| /** | |||
| * 驼峰转下划线命名 | |||
| */ | |||
| public static String toUnderScoreCase(String str) | |||
| { | |||
| if (str == null) | |||
| { | |||
| return null; | |||
| } | |||
| StringBuilder sb = new StringBuilder(); | |||
| // 前置字符是否大写 | |||
| boolean preCharIsUpperCase = true; | |||
| // 当前字符是否大写 | |||
| boolean curreCharIsUpperCase = true; | |||
| // 下一字符是否大写 | |||
| boolean nexteCharIsUpperCase = true; | |||
| for (int i = 0; i < str.length(); i++) | |||
| { | |||
| char c = str.charAt(i); | |||
| if (i > 0) | |||
| { | |||
| preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); | |||
| } | |||
| else | |||
| { | |||
| preCharIsUpperCase = false; | |||
| } | |||
| curreCharIsUpperCase = Character.isUpperCase(c); | |||
| if (i < (str.length() - 1)) | |||
| { | |||
| nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); | |||
| } | |||
| Boolean a = (i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase; | |||
| if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) | |||
| { | |||
| sb.append(SEPARATOR); | |||
| } | |||
| else if (a) | |||
| { | |||
| sb.append(SEPARATOR); | |||
| } | |||
| sb.append(Character.toLowerCase(c)); | |||
| } | |||
| return sb.toString(); | |||
| } | |||
| /** | |||
| * 是否包含字符串 | |||
| * | |||
| * @param str 验证字符串 | |||
| * @param strs 字符串组 | |||
| * @return 包含返回true | |||
| */ | |||
| public static boolean inStringIgnoreCase(String str, String... strs) | |||
| { | |||
| if (str != null && strs != null) | |||
| { | |||
| for (String s : strs) | |||
| { | |||
| if (str.equalsIgnoreCase(trim(s))) | |||
| { | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| /** | |||
| * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld | |||
| * | |||
| * @param name 转换前的下划线大写方式命名的字符串 | |||
| * @return 转换后的驼峰式命名的字符串 | |||
| */ | |||
| public static String convertToCamelCase(String name) | |||
| { | |||
| StringBuilder result = new StringBuilder(); | |||
| String a = "_"; | |||
| // 快速检查 | |||
| if (name == null || name.isEmpty()) | |||
| { | |||
| // 没必要转换 | |||
| return ""; | |||
| } | |||
| else if (!name.contains(a)) | |||
| { | |||
| // 不含下划线,仅将首字母大写 | |||
| return name.substring(0, 1).toUpperCase() + name.substring(1); | |||
| } | |||
| // 用下划线将原始字符串分割 | |||
| String[] camels = name.split(a); | |||
| for (String camel : camels) | |||
| { | |||
| // 跳过原始字符串中开头、结尾的下换线或双重下划线 | |||
| if (camel.isEmpty()) | |||
| { | |||
| continue; | |||
| } | |||
| // 首字母大写 | |||
| result.append(camel.substring(0, 1).toUpperCase()); | |||
| result.append(camel.substring(1).toLowerCase()); | |||
| } | |||
| return result.toString(); | |||
| } | |||
| /** | |||
| * 驼峰式命名法 | |||
| * 例如:user_name->userName | |||
| */ | |||
| public static String toCamelCase(String s) | |||
| { | |||
| if (s == null) | |||
| { | |||
| return null; | |||
| } | |||
| if (s.indexOf(SEPARATOR) == -1) | |||
| { | |||
| return s; | |||
| } | |||
| s = s.toLowerCase(); | |||
| StringBuilder sb = new StringBuilder(s.length()); | |||
| boolean upperCase = false; | |||
| for (int i = 0; i < s.length(); i++) | |||
| { | |||
| char c = s.charAt(i); | |||
| if (c == SEPARATOR) | |||
| { | |||
| upperCase = true; | |||
| } | |||
| else if (upperCase) | |||
| { | |||
| sb.append(Character.toUpperCase(c)); | |||
| upperCase = false; | |||
| } | |||
| else | |||
| { | |||
| sb.append(c); | |||
| } | |||
| } | |||
| return sb.toString(); | |||
| } | |||
| /** | |||
| * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 | |||
| * | |||
| * @param str 指定字符串 | |||
| * @param strs 需要检查的字符串数组 | |||
| * @return 是否匹配 | |||
| */ | |||
| public static boolean matches(String str, List<String> strs) | |||
| { | |||
| if (isEmpty(str) || isEmpty(strs)) | |||
| { | |||
| return false; | |||
| } | |||
| for (String pattern : strs) | |||
| { | |||
| if (isMatch(pattern, str)) | |||
| { | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| /** | |||
| * 判断url是否与规则配置: | |||
| * ? 表示单个字符; | |||
| * * 表示一层路径内的任意字符串,不可跨层级; | |||
| * ** 表示任意层路径; | |||
| * | |||
| * @param pattern 匹配规则 | |||
| * @param url 需要匹配的url | |||
| * @return | |||
| */ | |||
| public static boolean isMatch(String pattern, String url) | |||
| { | |||
| AntPathMatcher matcher = new AntPathMatcher(); | |||
| return matcher.match(pattern, url); | |||
| } | |||
| @SuppressWarnings("unchecked") | |||
| public static <T> T cast(Object obj) | |||
| { | |||
| return (T) obj; | |||
| } | |||
| /** | |||
| * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 | |||
| * | |||
| * @param num 数字对象 | |||
| * @param size 字符串指定长度 | |||
| * @return 返回数字的字符串格式,该字符串为指定长度。 | |||
| */ | |||
| public static final String padl(final Number num, final int size) | |||
| { | |||
| return padl(num.toString(), size, '0'); | |||
| } | |||
| /** | |||
| * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 | |||
| * | |||
| * @param s 原始字符串 | |||
| * @param size 字符串指定长度 | |||
| * @param c 用于补齐的字符 | |||
| * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 | |||
| */ | |||
| public static final String padl(final String s, final int size, final char c) | |||
| { | |||
| final StringBuilder sb = new StringBuilder(size); | |||
| if (s != null) | |||
| { | |||
| final int len = s.length(); | |||
| if (s.length() <= size) | |||
| { | |||
| for (int i = size - len; i > 0; i--) | |||
| { | |||
| sb.append(c); | |||
| } | |||
| sb.append(s); | |||
| } | |||
| else | |||
| { | |||
| return s.substring(len - size, len); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for (int i = size; i > 0; i--) | |||
| { | |||
| sb.append(c); | |||
| } | |||
| } | |||
| return sb.toString(); | |||
| } | |||
| } | |||
| @ -0,0 +1,110 @@ | |||
| package com.hxhq.common.core.utils.bean; | |||
| import java.lang.reflect.Method; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| import java.util.regex.Matcher; | |||
| import java.util.regex.Pattern; | |||
| /** | |||
| * Bean 工具类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class BeanUtils extends org.springframework.beans.BeanUtils | |||
| { | |||
| /** Bean方法名中属性名开始的下标 */ | |||
| private static final int BEAN_METHOD_PROP_INDEX = 3; | |||
| /** * 匹配getter方法的正则表达式 */ | |||
| private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); | |||
| /** * 匹配setter方法的正则表达式 */ | |||
| private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); | |||
| /** | |||
| * Bean属性复制工具方法。 | |||
| * | |||
| * @param dest 目标对象 | |||
| * @param src 源对象 | |||
| */ | |||
| public static void copyBeanProp(Object dest, Object src) | |||
| { | |||
| try | |||
| { | |||
| copyProperties(src, dest); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| /** | |||
| * 获取对象的setter方法。 | |||
| * | |||
| * @param obj 对象 | |||
| * @return 对象的setter方法列表 | |||
| */ | |||
| public static List<Method> getSetterMethods(Object obj) | |||
| { | |||
| // setter方法列表 | |||
| List<Method> setterMethods = new ArrayList<Method>(); | |||
| // 获取所有方法 | |||
| Method[] methods = obj.getClass().getMethods(); | |||
| // 查找setter方法 | |||
| for (Method method : methods) | |||
| { | |||
| Matcher m = SET_PATTERN.matcher(method.getName()); | |||
| if (m.matches() && (method.getParameterTypes().length == 1)) | |||
| { | |||
| setterMethods.add(method); | |||
| } | |||
| } | |||
| // 返回setter方法列表 | |||
| return setterMethods; | |||
| } | |||
| /** | |||
| * 获取对象的getter方法。 | |||
| * | |||
| * @param obj 对象 | |||
| * @return 对象的getter方法列表 | |||
| */ | |||
| public static List<Method> getGetterMethods(Object obj) | |||
| { | |||
| // getter方法列表 | |||
| List<Method> getterMethods = new ArrayList<Method>(); | |||
| // 获取所有方法 | |||
| Method[] methods = obj.getClass().getMethods(); | |||
| // 查找getter方法 | |||
| for (Method method : methods) | |||
| { | |||
| Matcher m = GET_PATTERN.matcher(method.getName()); | |||
| if (m.matches() && (method.getParameterTypes().length == 0)) | |||
| { | |||
| getterMethods.add(method); | |||
| } | |||
| } | |||
| // 返回getter方法列表 | |||
| return getterMethods; | |||
| } | |||
| /** | |||
| * 检查Bean方法名中的属性名是否相等。<br> | |||
| * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 | |||
| * | |||
| * @param m1 方法名1 | |||
| * @param m2 方法名2 | |||
| * @return 属性名一样返回true,否则返回false | |||
| */ | |||
| public static boolean isMethodPropEquals(String m1, String m2) | |||
| { | |||
| return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); | |||
| } | |||
| } | |||
| @ -0,0 +1,24 @@ | |||
| package com.hxhq.common.core.utils.bean; | |||
| import java.util.Set; | |||
| import javax.validation.ConstraintViolation; | |||
| import javax.validation.ConstraintViolationException; | |||
| import javax.validation.Validator; | |||
| /** | |||
| * bean对象属性验证 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class BeanValidators | |||
| { | |||
| public static void validateWithException(Validator validator, Object object, Class<?>... groups) | |||
| throws ConstraintViolationException | |||
| { | |||
| Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups); | |||
| if (!constraintViolations.isEmpty()) | |||
| { | |||
| throw new ConstraintViolationException(constraintViolations); | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,131 @@ | |||
| package com.hxhq.common.core.utils.file; | |||
| import java.io.File; | |||
| import java.util.Objects; | |||
| import org.apache.commons.io.FilenameUtils; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| /** | |||
| * 文件类型工具类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class FileTypeUtils | |||
| { | |||
| /** | |||
| * 获取文件类型 | |||
| * <p> | |||
| * 例如: hxhq.txt, 返回: txt | |||
| * | |||
| * @param file 文件名 | |||
| * @return 后缀(不含".") | |||
| */ | |||
| public static String getFileType(File file) | |||
| { | |||
| if (null == file) | |||
| { | |||
| return StringUtils.EMPTY; | |||
| } | |||
| return getFileType(file.getName()); | |||
| } | |||
| /** | |||
| * 获取文件类型 | |||
| * <p> | |||
| * 例如: hxhq.txt, 返回: txt | |||
| * | |||
| * @param fileName 文件名 | |||
| * @return 后缀(不含".") | |||
| */ | |||
| public static String getFileType(String fileName) | |||
| { | |||
| int separatorIndex = fileName.lastIndexOf("."); | |||
| if (separatorIndex < 0) | |||
| { | |||
| return ""; | |||
| } | |||
| return fileName.substring(separatorIndex + 1).toLowerCase(); | |||
| } | |||
| /** | |||
| * 获取文件名的后缀 | |||
| * | |||
| * @param file 表单文件 | |||
| * @return 后缀名 | |||
| */ | |||
| public static final String getExtension(MultipartFile file) | |||
| { | |||
| String extension = FilenameUtils.getExtension(file.getOriginalFilename()); | |||
| if (StringUtils.isEmpty(extension)) | |||
| { | |||
| extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); | |||
| } | |||
| return extension; | |||
| } | |||
| /** | |||
| * 获取文件类型 | |||
| * | |||
| * @param photoByte 文件字节码 | |||
| * @return 后缀(不含".") | |||
| */ | |||
| public static String getFileExtendName(byte[] photoByte) | |||
| { | |||
| String strFileExtendName = "JPG"; | |||
| int a1 = 0; | |||
| int a2 = 71; | |||
| int a3 = 1; | |||
| int a4 = 73; | |||
| int a5 = 2; | |||
| int a6 = 70; | |||
| int a7 = 3; | |||
| int a8 = 56; | |||
| int a9 = 4; | |||
| int a10 = 55; | |||
| int a11 = 57; | |||
| int a12 = 5; | |||
| int a13 = 97; | |||
| int a14 = 6; | |||
| int a15 = 74; | |||
| int a16 = 7; | |||
| int a17 = 70; | |||
| int a18 = 8; | |||
| int a19 = 73; | |||
| int a20 = 9; | |||
| int a21 = 70; | |||
| int a22 = 66; | |||
| int a23 = 1; | |||
| int a24 = 77; | |||
| int a25 = 1; | |||
| int a26 = 80; | |||
| int a27 = 2; | |||
| int a28 = 78; | |||
| int a29 = 3; | |||
| int a30 = 71; | |||
| Boolean a = (photoByte[a1] == a2) && (photoByte[a3] == a4) && (photoByte[a5] == a6) && (photoByte[a7] == a8) | |||
| && ((photoByte[a9] == a10) || (photoByte[a9] == a11)) && (photoByte[a12] == a13); | |||
| Boolean b = (photoByte[a14] == a15) && (photoByte[a16] == a17) && (photoByte[a18] == a19) && (photoByte[a20] == a21); | |||
| if (a) | |||
| { | |||
| strFileExtendName = "GIF"; | |||
| } | |||
| else if (b) | |||
| { | |||
| strFileExtendName = "JPG"; | |||
| } | |||
| else if ((photoByte[a1] == a22) && (photoByte[a23] == a24)) | |||
| { | |||
| strFileExtendName = "BMP"; | |||
| } | |||
| else if ((photoByte[a25] == a26) && (photoByte[a27] == a28) && (photoByte[a29] == a30)) | |||
| { | |||
| strFileExtendName = "PNG"; | |||
| } | |||
| return strFileExtendName; | |||
| } | |||
| } | |||
| @ -0,0 +1,258 @@ | |||
| package com.hxhq.common.core.utils.file; | |||
| import java.io.File; | |||
| import java.io.FileInputStream; | |||
| import java.io.FileNotFoundException; | |||
| import java.io.IOException; | |||
| import java.io.OutputStream; | |||
| import java.io.UnsupportedEncodingException; | |||
| import java.net.URLEncoder; | |||
| import java.nio.charset.StandardCharsets; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import com.hxhq.common.core.utils.StringUtils; | |||
| import org.apache.commons.lang3.ArrayUtils; | |||
| /** | |||
| * 文件处理工具类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class FileUtils | |||
| { | |||
| /** 字符常量:斜杠 {@code '/'} */ | |||
| public static final char SLASH = '/'; | |||
| /** 字符常量:反斜杠 {@code '\\'} */ | |||
| public static final char BACKSLASH = '\\'; | |||
| public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; | |||
| /** | |||
| * 输出指定文件的byte数组 | |||
| * | |||
| * @param filePath 文件路径 | |||
| * @param os 输出流 | |||
| * @return | |||
| */ | |||
| public static void writeBytes(String filePath, OutputStream os) throws IOException | |||
| { | |||
| FileInputStream fis = null; | |||
| try | |||
| { | |||
| File file = new File(filePath); | |||
| if (!file.exists()) | |||
| { | |||
| throw new FileNotFoundException(filePath); | |||
| } | |||
| fis = new FileInputStream(file); | |||
| byte[] b = new byte[1024]; | |||
| int length; | |||
| while ((length = fis.read(b)) > 0) | |||
| { | |||
| os.write(b, 0, length); | |||
| } | |||
| } | |||
| catch (IOException e) | |||
| { | |||
| throw e; | |||
| } | |||
| finally | |||
| { | |||
| if (os != null) | |||
| { | |||
| try | |||
| { | |||
| os.close(); | |||
| } | |||
| catch (IOException e1) | |||
| { | |||
| e1.printStackTrace(); | |||
| } | |||
| } | |||
| if (fis != null) | |||
| { | |||
| try | |||
| { | |||
| fis.close(); | |||
| } | |||
| catch (IOException e1) | |||
| { | |||
| e1.printStackTrace(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * 删除文件 | |||
| * | |||
| * @param filePath 文件 | |||
| * @return | |||
| */ | |||
| public static boolean deleteFile(String filePath) | |||
| { | |||
| boolean flag = false; | |||
| File file = new File(filePath); | |||
| // 路径为文件且不为空则进行删除 | |||
| if (file.isFile() && file.exists()) | |||
| { | |||
| flag = file.delete(); | |||
| } | |||
| return flag; | |||
| } | |||
| /** | |||
| * 文件名称验证 | |||
| * | |||
| * @param filename 文件名称 | |||
| * @return true 正常 false 非法 | |||
| */ | |||
| public static boolean isValidFilename(String filename) | |||
| { | |||
| return filename.matches(FILENAME_PATTERN); | |||
| } | |||
| /** | |||
| * 校验文件路径合法性(安全性与扩展名) | |||
| * | |||
| * @param fileUrl 待校验的文件地址 | |||
| * @return true 正常 false 非法 | |||
| */ | |||
| public static boolean validateFilePath(String fileUrl) | |||
| { | |||
| // 禁止目录上跳级别 | |||
| String a = ".."; | |||
| if (StringUtils.contains(fileUrl, a)) | |||
| { | |||
| return false; | |||
| } | |||
| // 判断是否在允许下载的文件规则内 | |||
| return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(fileUrl)); | |||
| } | |||
| /** | |||
| * 下载文件名重新编码 | |||
| * | |||
| * @param request 请求对象 | |||
| * @param fileName 文件名 | |||
| * @return 编码后的文件名 | |||
| */ | |||
| public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException | |||
| { | |||
| final String agent = request.getHeader("USER-AGENT"); | |||
| String filename = fileName; | |||
| String a = "MSIE"; | |||
| String a1 = "Firefox"; | |||
| String a2 = "Chrome"; | |||
| if (agent.contains(a)) | |||
| { | |||
| // IE浏览器 | |||
| filename = URLEncoder.encode(filename, "utf-8"); | |||
| filename = filename.replace("+", " "); | |||
| } | |||
| else if (agent.contains(a1)) | |||
| { | |||
| // 火狐浏览器 | |||
| filename = new String(fileName.getBytes(), "ISO8859-1"); | |||
| } | |||
| else if (agent.contains(a2)) | |||
| { | |||
| // google浏览器 | |||
| filename = URLEncoder.encode(filename, "utf-8"); | |||
| } | |||
| else | |||
| { | |||
| // 其它浏览器 | |||
| filename = URLEncoder.encode(filename, "utf-8"); | |||
| } | |||
| return filename; | |||
| } | |||
| /** | |||
| * 返回文件名 | |||
| * | |||
| * @param filePath 文件 | |||
| * @return 文件名 | |||
| */ | |||
| public static String getName(String filePath) | |||
| { | |||
| if (null == filePath) | |||
| { | |||
| return null; | |||
| } | |||
| int len = filePath.length(); | |||
| if (0 == len) | |||
| { | |||
| return filePath; | |||
| } | |||
| if (isFileSeparator(filePath.charAt(len - 1))) | |||
| { | |||
| // 以分隔符结尾的去掉结尾分隔符 | |||
| len--; | |||
| } | |||
| int begin = 0; | |||
| char c; | |||
| for (int i = len - 1; i > -1; i--) | |||
| { | |||
| c = filePath.charAt(i); | |||
| if (isFileSeparator(c)) | |||
| { | |||
| // 查找最后一个路径分隔符(/或者\) | |||
| begin = i + 1; | |||
| break; | |||
| } | |||
| } | |||
| return filePath.substring(begin, len); | |||
| } | |||
| /** | |||
| * 是否为Windows或者Linux(Unix)文件分隔符<br> | |||
| * Windows平台下分隔符为\,Linux(Unix)为/ | |||
| * | |||
| * @param c 字符 | |||
| * @return 是否为Windows或者Linux(Unix)文件分隔符 | |||
| */ | |||
| public static boolean isFileSeparator(char c) | |||
| { | |||
| return SLASH == c || BACKSLASH == c; | |||
| } | |||
| /** | |||
| * 下载文件名重新编码 | |||
| * | |||
| * @param response 响应对象 | |||
| * @param realFileName 真实文件名 | |||
| * @return | |||
| */ | |||
| public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException | |||
| { | |||
| String percentEncodedFileName = percentEncode(realFileName); | |||
| StringBuilder contentDispositionValue = new StringBuilder(); | |||
| contentDispositionValue.append("attachment; filename=") | |||
| .append(percentEncodedFileName) | |||
| .append(";") | |||
| .append("filename*=") | |||
| .append("utf-8''") | |||
| .append(percentEncodedFileName); | |||
| response.setHeader("Content-disposition", contentDispositionValue.toString()); | |||
| response.setHeader("download-filename", percentEncodedFileName); | |||
| } | |||
| /** | |||
| * 百分号编码工具方法 | |||
| * | |||
| * @param s 需要百分号编码的字符串 | |||
| * @return 百分号编码后的字符串 | |||
| */ | |||
| public static String percentEncode(String s) throws UnsupportedEncodingException | |||
| { | |||
| String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); | |||
| return encode.replaceAll("\\+", "%20"); | |||
| } | |||
| } | |||
| @ -0,0 +1,84 @@ | |||
| package com.hxhq.common.core.utils.file; | |||
| import java.io.ByteArrayInputStream; | |||
| import java.io.InputStream; | |||
| import java.net.URL; | |||
| import java.net.URLConnection; | |||
| import java.util.Arrays; | |||
| import org.apache.poi.util.IOUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| /** | |||
| * 图片处理工具类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class ImageUtils | |||
| { | |||
| private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); | |||
| public static byte[] getImage(String imagePath) | |||
| { | |||
| InputStream is = getFile(imagePath); | |||
| try | |||
| { | |||
| return IOUtils.toByteArray(is); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| log.error("图片加载异常 {}", e); | |||
| return null; | |||
| } | |||
| finally | |||
| { | |||
| IOUtils.closeQuietly(is); | |||
| } | |||
| } | |||
| public static InputStream getFile(String imagePath) | |||
| { | |||
| try | |||
| { | |||
| byte[] result = readFile(imagePath); | |||
| result = Arrays.copyOf(result, result.length); | |||
| return new ByteArrayInputStream(result); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| log.error("获取图片异常 {}", e); | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * 读取文件为字节数据 | |||
| * | |||
| * @param url 地址 | |||
| * @return 字节数据 | |||
| */ | |||
| public static byte[] readFile(String url) | |||
| { | |||
| InputStream in = null; | |||
| try | |||
| { | |||
| // 网络地址 | |||
| URL urlObj = new URL(url); | |||
| URLConnection urlConnection = urlObj.openConnection(); | |||
| urlConnection.setConnectTimeout(30 * 1000); | |||
| urlConnection.setReadTimeout(60 * 1000); | |||
| urlConnection.setDoInput(true); | |||
| in = urlConnection.getInputStream(); | |||
| return IOUtils.toByteArray(in); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| log.error("访问文件异常 {}", e); | |||
| return null; | |||
| } | |||
| finally | |||
| { | |||
| IOUtils.closeQuietly(in); | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,59 @@ | |||
| package com.hxhq.common.core.utils.file; | |||
| /** | |||
| * 媒体类型工具类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class MimeTypeUtils | |||
| { | |||
| public static final String IMAGE_PNG = "image/png"; | |||
| public static final String IMAGE_JPG = "image/jpg"; | |||
| public static final String IMAGE_JPEG = "image/jpeg"; | |||
| public static final String IMAGE_BMP = "image/bmp"; | |||
| public static final String IMAGE_GIF = "image/gif"; | |||
| public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; | |||
| public static final String[] FLASH_EXTENSION = { "swf", "flv" }; | |||
| public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", | |||
| "asf", "rm", "rmvb" }; | |||
| public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; | |||
| public static final String[] DEFAULT_ALLOWED_EXTENSION = { | |||
| // 图片 | |||
| "bmp", "gif", "jpg", "jpeg", "png", | |||
| // word excel powerpoint | |||
| "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", | |||
| // 压缩文件 | |||
| "rar", "zip", "gz", "bz2", | |||
| // 视频格式 | |||
| "mp4", "avi", "rmvb", | |||
| "pdf" }; | |||
| public static String getExtension(String prefix) | |||
| { | |||
| switch (prefix) | |||
| { | |||
| case IMAGE_PNG: | |||
| return "png"; | |||
| case IMAGE_JPG: | |||
| return "jpg"; | |||
| case IMAGE_JPEG: | |||
| return "jpeg"; | |||
| case IMAGE_BMP: | |||
| return "bmp"; | |||
| case IMAGE_GIF: | |||
| return "gif"; | |||
| default: | |||
| return ""; | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,172 @@ | |||
| package com.hxhq.common.core.utils.html; | |||
| import com.hxhq.common.core.utils.StringUtils; | |||
| /** | |||
| * 转义和反转义工具类 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class EscapeUtil | |||
| { | |||
| public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; | |||
| private static final char[][] TEXT = new char[64][]; | |||
| static | |||
| { | |||
| int a64 = 64; | |||
| for (int i = 0; i < a64; i++) | |||
| { | |||
| TEXT[i] = new char[] { (char) i }; | |||
| } | |||
| // special HTML characters | |||
| TEXT['\''] = "'".toCharArray(); | |||
| // 双引号 | |||
| TEXT['"'] = """.toCharArray(); | |||
| // &符 | |||
| TEXT['&'] = "&".toCharArray(); | |||
| // 小于号 | |||
| TEXT['<'] = "<".toCharArray(); | |||
| // 大于号 | |||
| TEXT['>'] = ">".toCharArray(); | |||
| } | |||
| /** | |||
| * 转义文本中的HTML字符为安全的字符 | |||
| * | |||
| * @param text 被转义的文本 | |||
| * @return 转义后的文本 | |||
| */ | |||
| public static String escape(String text) | |||
| { | |||
| return encode(text); | |||
| } | |||
| /** | |||
| * 还原被转义的HTML特殊字符 | |||
| * | |||
| * @param content 包含转义符的HTML内容 | |||
| * @return 转换后的字符串 | |||
| */ | |||
| public static String unescape(String content) | |||
| { | |||
| return decode(content); | |||
| } | |||
| /** | |||
| * 清除所有HTML标签,但是不删除标签内的内容 | |||
| * | |||
| * @param content 文本 | |||
| * @return 清除标签后的文本 | |||
| */ | |||
| public static String clean(String content) | |||
| { | |||
| return new HtmlFilter().filter(content); | |||
| } | |||
| /** | |||
| * Escape编码 | |||
| * | |||
| * @param text 被编码的文本 | |||
| * @return 编码后的字符 | |||
| */ | |||
| private static String encode(String text) | |||
| { | |||
| if (StringUtils.isEmpty(text)) | |||
| { | |||
| return StringUtils.EMPTY; | |||
| } | |||
| final StringBuilder tmp = new StringBuilder(text.length() * 6); | |||
| char c; | |||
| for (int i = 0; i < text.length(); i++) | |||
| { | |||
| c = text.charAt(i); | |||
| if (c < 256) | |||
| { | |||
| tmp.append("%"); | |||
| if (c < 16) | |||
| { | |||
| tmp.append("0"); | |||
| } | |||
| tmp.append(Integer.toString(c, 16)); | |||
| } | |||
| else | |||
| { | |||
| tmp.append("%u"); | |||
| if (c <= 0xfff) | |||
| { | |||
| // issue#I49JU8@Gitee | |||
| tmp.append("0"); | |||
| } | |||
| tmp.append(Integer.toString(c, 16)); | |||
| } | |||
| } | |||
| return tmp.toString(); | |||
| } | |||
| /** | |||
| * Escape解码 | |||
| * | |||
| * @param content 被转义的内容 | |||
| * @return 解码后的字符串 | |||
| */ | |||
| public static String decode(String content) | |||
| { | |||
| if (StringUtils.isEmpty(content)) | |||
| { | |||
| return content; | |||
| } | |||
| StringBuilder tmp = new StringBuilder(content.length()); | |||
| int lastPos = 0, pos = 0; | |||
| char ch; | |||
| while (lastPos < content.length()) | |||
| { | |||
| pos = content.indexOf("%", lastPos); | |||
| if (pos == lastPos) | |||
| { | |||
| if (content.charAt(pos + 1) == 'u') | |||
| { | |||
| ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); | |||
| tmp.append(ch); | |||
| lastPos = pos + 6; | |||
| } | |||
| else | |||
| { | |||
| ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); | |||
| tmp.append(ch); | |||
| lastPos = pos + 3; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| if (pos == -1) | |||
| { | |||
| tmp.append(content.substring(lastPos)); | |||
| lastPos = content.length(); | |||
| } | |||
| else | |||
| { | |||
| tmp.append(content.substring(lastPos, pos)); | |||
| lastPos = pos; | |||
| } | |||
| } | |||
| } | |||
| return tmp.toString(); | |||
| } | |||
| public static void main(String[] args) | |||
| { | |||
| String html = "<script>alert(1);</script>"; | |||
| String escape = EscapeUtil.escape(html); | |||
| // String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>"; | |||
| // String html = "<123"; | |||
| // String html = "123>"; | |||
| System.out.println("clean: " + EscapeUtil.clean(html)); | |||
| System.out.println("escape: " + escape); | |||
| System.out.println("unescape: " + EscapeUtil.unescape(escape)); | |||
| } | |||
| } | |||
| @ -0,0 +1,551 @@ | |||
| package com.hxhq.common.core.utils.html; | |||
| import java.util.ArrayList; | |||
| import java.util.Collections; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import java.util.concurrent.ConcurrentHashMap; | |||
| import java.util.concurrent.ConcurrentMap; | |||
| import java.util.regex.Matcher; | |||
| import java.util.regex.Pattern; | |||
| /** | |||
| * HTML过滤器,用于去除XSS漏洞隐患。 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public final class HtmlFilter | |||
| { | |||
| /** | |||
| * regex flag union representing /si modifiers in php | |||
| **/ | |||
| private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; | |||
| private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL); | |||
| private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); | |||
| private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); | |||
| private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); | |||
| private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); | |||
| private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); | |||
| private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); | |||
| private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); | |||
| private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); | |||
| private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); | |||
| private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); | |||
| private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); | |||
| private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); | |||
| private static final Pattern P_END_ARROW = Pattern.compile("^>"); | |||
| private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); | |||
| private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); | |||
| private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); | |||
| private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); | |||
| private static final Pattern P_AMP = Pattern.compile("&"); | |||
| private static final Pattern P_QUOTE = Pattern.compile("\""); | |||
| private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); | |||
| private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); | |||
| private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); | |||
| /** | |||
| * @xxx could grow large... maybe use sesat's ReferenceMap | |||
| */ | |||
| private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); | |||
| private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); | |||
| /** | |||
| * set of allowed html elements, along with allowed attributes for each element | |||
| **/ | |||
| private final Map<String, List<String>> vAllowed; | |||
| /** | |||
| * counts of open tags for each (allowable) html element | |||
| **/ | |||
| private final Map<String, Integer> vTagCounts = new HashMap<>(); | |||
| /** | |||
| * html elements which must always be self-closing (e.g. "<img />") | |||
| **/ | |||
| private final String[] vSelfClosingTags; | |||
| /** | |||
| * html elements which must always have separate opening and closing tags (e.g. "<b></b>") | |||
| **/ | |||
| private final String[] vNeedClosingTags; | |||
| /** | |||
| * set of disallowed html elements | |||
| **/ | |||
| private final String[] vDisallowed; | |||
| /** | |||
| * attributes which should be checked for valid protocols | |||
| **/ | |||
| private final String[] vProtocolAtts; | |||
| /** | |||
| * allowed protocols | |||
| **/ | |||
| private final String[] vAllowedProtocols; | |||
| /** | |||
| * tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />") | |||
| **/ | |||
| private final String[] vRemoveBlanks; | |||
| /** | |||
| * entities allowed within html markup | |||
| **/ | |||
| private final String[] vAllowedEntities; | |||
| /** | |||
| * flag determining whether comments are allowed in input String. | |||
| */ | |||
| private final boolean stripComment; | |||
| private final boolean encodeQuotes; | |||
| /** | |||
| * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>" | |||
| * becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped. | |||
| */ | |||
| private final boolean alwaysMakeTags; | |||
| /** | |||
| * Default constructor. | |||
| */ | |||
| public HtmlFilter() | |||
| { | |||
| vAllowed = new HashMap<>(); | |||
| final ArrayList<String> aAtts = new ArrayList<>(); | |||
| aAtts.add("href"); | |||
| aAtts.add("target"); | |||
| vAllowed.put("a", aAtts); | |||
| final ArrayList<String> imgAtts = new ArrayList<>(); | |||
| imgAtts.add("src"); | |||
| imgAtts.add("width"); | |||
| imgAtts.add("height"); | |||
| imgAtts.add("alt"); | |||
| vAllowed.put("img", imgAtts); | |||
| final ArrayList<String> noAtts = new ArrayList<>(); | |||
| vAllowed.put("b", noAtts); | |||
| vAllowed.put("strong", noAtts); | |||
| vAllowed.put("i", noAtts); | |||
| vAllowed.put("em", noAtts); | |||
| vSelfClosingTags = new String[] { "img" }; | |||
| vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; | |||
| vDisallowed = new String[] {}; | |||
| // no ftp. | |||
| vAllowedProtocols = new String[] { "http", "mailto", "https" }; | |||
| vProtocolAtts = new String[] { "src", "href" }; | |||
| vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; | |||
| vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; | |||
| stripComment = true; | |||
| encodeQuotes = true; | |||
| alwaysMakeTags = false; | |||
| } | |||
| /** | |||
| * Map-parameter configurable constructor. | |||
| * | |||
| * @param conf map containing configuration. keys match field names. | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public HtmlFilter(final Map<String, Object> conf) | |||
| { | |||
| assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; | |||
| assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; | |||
| assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; | |||
| assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; | |||
| assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; | |||
| assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; | |||
| assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; | |||
| assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; | |||
| vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed")); | |||
| vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); | |||
| vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); | |||
| vDisallowed = (String[]) conf.get("vDisallowed"); | |||
| vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); | |||
| vProtocolAtts = (String[]) conf.get("vProtocolAtts"); | |||
| vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); | |||
| vAllowedEntities = (String[]) conf.get("vAllowedEntities"); | |||
| stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; | |||
| encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; | |||
| alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; | |||
| } | |||
| private void reset() | |||
| { | |||
| vTagCounts.clear(); | |||
| } | |||
| /** | |||
| * my versions of some PHP library functions | |||
| * @param decimal | |||
| * @return | |||
| */ | |||
| public static String chr(final int decimal) | |||
| { | |||
| return String.valueOf((char) decimal); | |||
| } | |||
| public static String htmlSpecialChars(final String s) | |||
| { | |||
| String result = s; | |||
| result = regexReplace(P_AMP, "&", result); | |||
| result = regexReplace(P_QUOTE, """, result); | |||
| result = regexReplace(P_LEFT_ARROW, "<", result); | |||
| result = regexReplace(P_RIGHT_ARROW, ">", result); | |||
| return result; | |||
| } | |||
| // --------------------------------------------------------------- | |||
| /** | |||
| * given a user submitted input String, filter out any invalid or restricted html. | |||
| * | |||
| * @param input text (i.e. submitted by a user) than may contain html | |||
| * @return "clean" version of input, with only valid, whitelisted html elements allowed | |||
| */ | |||
| public String filter(final String input) | |||
| { | |||
| reset(); | |||
| String s = input; | |||
| s = escapeComments(s); | |||
| s = balancehtml(s); | |||
| s = checkTags(s); | |||
| s = processRemoveBlanks(s); | |||
| // s = validateEntities(s); | |||
| return s; | |||
| } | |||
| public boolean isAlwaysMakeTags() | |||
| { | |||
| return alwaysMakeTags; | |||
| } | |||
| public boolean isStripComments() | |||
| { | |||
| return stripComment; | |||
| } | |||
| private String escapeComments(final String s) | |||
| { | |||
| final Matcher m = P_COMMENTS.matcher(s); | |||
| final StringBuffer buf = new StringBuffer(); | |||
| if (m.find()) | |||
| { | |||
| // (.*?) | |||
| final String match = m.group(1); | |||
| m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->")); | |||
| } | |||
| m.appendTail(buf); | |||
| return buf.toString(); | |||
| } | |||
| private String balancehtml(String s) | |||
| { | |||
| if (alwaysMakeTags) | |||
| { | |||
| // | |||
| // try and form html | |||
| // | |||
| s = regexReplace(P_END_ARROW, "", s); | |||
| // 不追加结束标签 | |||
| s = regexReplace(P_BODY_TO_END, "<$1>", s); | |||
| s = regexReplace(P_XML_CONTENT, "$1<$2", s); | |||
| } | |||
| else | |||
| { | |||
| // | |||
| // escape stray brackets | |||
| // | |||
| s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); | |||
| s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); | |||
| // | |||
| // the last regexp causes '<>' entities to appear | |||
| // (we need to do a lookahead assertion so that the last bracket can | |||
| // be used in the next pass of the regexp) | |||
| // | |||
| s = regexReplace(P_BOTH_ARROWS, "", s); | |||
| } | |||
| return s; | |||
| } | |||
| private String checkTags(String s) | |||
| { | |||
| Matcher m = P_TAGS.matcher(s); | |||
| final StringBuffer buf = new StringBuffer(); | |||
| while (m.find()) | |||
| { | |||
| String replaceStr = m.group(1); | |||
| replaceStr = processTag(replaceStr); | |||
| m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); | |||
| } | |||
| m.appendTail(buf); | |||
| // these get tallied in processTag | |||
| // (remember to reset before subsequent calls to filter method) | |||
| final StringBuilder sBuilder = new StringBuilder(buf.toString()); | |||
| for (String key : vTagCounts.keySet()) | |||
| { | |||
| for (int ii = 0; ii < vTagCounts.get(key); ii++) | |||
| { | |||
| sBuilder.append("</").append(key).append(">"); | |||
| } | |||
| } | |||
| s = sBuilder.toString(); | |||
| return s; | |||
| } | |||
| private String processRemoveBlanks(final String s) | |||
| { | |||
| String result = s; | |||
| for (String tag : vRemoveBlanks) | |||
| { | |||
| if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) | |||
| { | |||
| P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">")); | |||
| } | |||
| result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); | |||
| if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) | |||
| { | |||
| P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); | |||
| } | |||
| result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); | |||
| } | |||
| return result; | |||
| } | |||
| private static String regexReplace(final Pattern regexPattern, final String replacement, final String s) | |||
| { | |||
| Matcher m = regexPattern.matcher(s); | |||
| return m.replaceAll(replacement); | |||
| } | |||
| private String processTag(final String s) | |||
| { | |||
| Matcher m = P_END_TAG.matcher(s); | |||
| if (m.find()){ | |||
| final String name = m.group(1).toLowerCase(); | |||
| if (allowed(name)){ | |||
| if (!inArray(name, vSelfClosingTags)){ | |||
| if (vTagCounts.containsKey(name)){ | |||
| vTagCounts.put(name, vTagCounts.get(name) - 1); | |||
| return "</" + name + ">"; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| m = P_START_TAG.matcher(s); | |||
| if (m.find()){ | |||
| final String name = m.group(1).toLowerCase(); | |||
| final String body = m.group(2); | |||
| String ending = m.group(3); | |||
| if (allowed(name)){ | |||
| final StringBuilder params = new StringBuilder(); | |||
| final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); | |||
| final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); | |||
| final List<String> paramNames = new ArrayList<>(); | |||
| final List<String> paramValues = new ArrayList<>(); | |||
| while (m2.find()){ | |||
| // ([a-z0-9]+) | |||
| paramNames.add(m2.group(1)); | |||
| // (.*?) | |||
| paramValues.add(m2.group(3)); | |||
| } | |||
| while (m3.find()){ | |||
| // ([a-z0-9]+) | |||
| paramNames.add(m3.group(1)); | |||
| // ([^\"\\s']+) | |||
| paramValues.add(m3.group(3)); | |||
| } | |||
| String paramName, paramValue; | |||
| for (int ii = 0; ii < paramNames.size(); ii++) | |||
| { | |||
| paramName = paramNames.get(ii).toLowerCase(); | |||
| paramValue = paramValues.get(ii); | |||
| if (allowedAttribute(name, paramName)){ | |||
| if (inArray(paramName, vProtocolAtts)){ | |||
| paramValue = processParamProtocol(paramValue); | |||
| } | |||
| params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); | |||
| } | |||
| } | |||
| if (inArray(name, vSelfClosingTags)){ | |||
| ending = " /"; | |||
| } | |||
| if (inArray(name, vNeedClosingTags)){ | |||
| ending = ""; | |||
| } | |||
| if (ending == null || ending.length() < 1){ | |||
| if (vTagCounts.containsKey(name)){ | |||
| vTagCounts.put(name, vTagCounts.get(name) + 1); | |||
| } | |||
| else{ | |||
| vTagCounts.put(name, 1); | |||
| } | |||
| } | |||
| else{ | |||
| ending = " /"; | |||
| } | |||
| return "<" + name + params + ending + ">"; | |||
| } | |||
| else{ | |||
| return ""; | |||
| } | |||
| } | |||
| m = P_COMMENT.matcher(s); | |||
| if (!stripComment && m.find()){ | |||
| return "<" + m.group() + ">"; | |||
| } | |||
| return ""; | |||
| } | |||
| private String processParamProtocol(String s) | |||
| { | |||
| s = decodeEntities(s); | |||
| final Matcher m = P_PROTOCOL.matcher(s); | |||
| if (m.find()) | |||
| { | |||
| final String protocol = m.group(1); | |||
| if (!inArray(protocol, vAllowedProtocols)) | |||
| { | |||
| // bad protocol, turn into local anchor link instead | |||
| s = "#" + s.substring(protocol.length() + 1); | |||
| String a = "#//"; | |||
| if (s.startsWith(a)) | |||
| { | |||
| s = "#" + s.substring(3); | |||
| } | |||
| } | |||
| } | |||
| return s; | |||
| } | |||
| private String decodeEntities(String s) | |||
| { | |||
| StringBuffer buf = new StringBuffer(); | |||
| Matcher m = P_ENTITY.matcher(s); | |||
| while (m.find()) | |||
| { | |||
| final String match = m.group(1); | |||
| final int decimal = Integer.decode(match).intValue(); | |||
| m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); | |||
| } | |||
| m.appendTail(buf); | |||
| s = buf.toString(); | |||
| buf = new StringBuffer(); | |||
| m = P_ENTITY_UNICODE.matcher(s); | |||
| while (m.find()) | |||
| { | |||
| final String match = m.group(1); | |||
| final int decimal = Integer.valueOf(match, 16).intValue(); | |||
| m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); | |||
| } | |||
| m.appendTail(buf); | |||
| s = buf.toString(); | |||
| buf = new StringBuffer(); | |||
| m = P_ENCODE.matcher(s); | |||
| while (m.find()) | |||
| { | |||
| final String match = m.group(1); | |||
| final int decimal = Integer.valueOf(match, 16).intValue(); | |||
| m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); | |||
| } | |||
| m.appendTail(buf); | |||
| s = buf.toString(); | |||
| s = validateEntities(s); | |||
| return s; | |||
| } | |||
| private String validateEntities(final String s) | |||
| { | |||
| StringBuffer buf = new StringBuffer(); | |||
| // validate entities throughout the string | |||
| Matcher m = P_VALID_ENTITIES.matcher(s); | |||
| while (m.find()) | |||
| { | |||
| // ([^&;]*) | |||
| final String one = m.group(1); | |||
| // (?=(;|&|$)) | |||
| final String two = m.group(2); | |||
| m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); | |||
| } | |||
| m.appendTail(buf); | |||
| return encodeQuotes(buf.toString()); | |||
| } | |||
| private String encodeQuotes(final String s) | |||
| { | |||
| if (encodeQuotes) | |||
| { | |||
| StringBuffer buf = new StringBuffer(); | |||
| Matcher m = P_VALID_QUOTES.matcher(s); | |||
| while (m.find()) | |||
| { | |||
| // (>|^) | |||
| final String one = m.group(1); | |||
| // ([^<]+?) | |||
| final String two = m.group(2); | |||
| // (<|$) | |||
| final String three = m.group(3); | |||
| // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) | |||
| m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); | |||
| } | |||
| m.appendTail(buf); | |||
| return buf.toString(); | |||
| } | |||
| else | |||
| { | |||
| return s; | |||
| } | |||
| } | |||
| private String checkEntity(final String preamble, final String term) | |||
| { | |||
| return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; | |||
| } | |||
| private boolean isValidEntity(final String entity) | |||
| { | |||
| return inArray(entity, vAllowedEntities); | |||
| } | |||
| private static boolean inArray(final String s, final String[] array) | |||
| { | |||
| for (String item : array) | |||
| { | |||
| if (item != null && item.equals(s)) | |||
| { | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| private boolean allowed(final String name) | |||
| { | |||
| return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); | |||
| } | |||
| private boolean allowedAttribute(final String name, final String paramName) | |||
| { | |||
| return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); | |||
| } | |||
| } | |||
| @ -0,0 +1,387 @@ | |||
| package com.hxhq.common.core.utils.ip; | |||
| import java.net.InetAddress; | |||
| import java.net.UnknownHostException; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import com.hxhq.common.core.utils.StringUtils; | |||
| import com.hxhq.common.core.utils.ServletUtils; | |||
| /** | |||
| * 获取IP方法 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public class IpUtils | |||
| { | |||
| public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"; | |||
| /** | |||
| * 匹配 ip | |||
| */ | |||
| public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")"; | |||
| public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; | |||
| /** | |||
| * 匹配网段 | |||
| */ | |||
| public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; | |||
| /** | |||
| * 获取客户端IP | |||
| * | |||
| * @return IP地址 | |||
| */ | |||
| public static String getIpAddr() | |||
| { | |||
| return getIpAddr(ServletUtils.getRequest()); | |||
| } | |||
| /** | |||
| * 获取客户端IP | |||
| * | |||
| * @param request 请求对象 | |||
| * @return IP地址 | |||
| */ | |||
| public static String getIpAddr(HttpServletRequest request) | |||
| { | |||
| if (request == null) | |||
| { | |||
| return "unknown"; | |||
| } | |||
| String ip = request.getHeader("x-forwarded-for"); | |||
| String a1 = "unknown"; | |||
| if (ip == null || ip.length() == 0 || a1.equalsIgnoreCase(ip)) | |||
| { | |||
| ip = request.getHeader("Proxy-Client-IP"); | |||
| } | |||
| if (ip == null || ip.length() == 0 || a1.equalsIgnoreCase(ip)) | |||
| { | |||
| ip = request.getHeader("X-Forwarded-For"); | |||
| } | |||
| if (ip == null || ip.length() == 0 || a1.equalsIgnoreCase(ip)) | |||
| { | |||
| ip = request.getHeader("WL-Proxy-Client-IP"); | |||
| } | |||
| if (ip == null || ip.length() == 0 || a1.equalsIgnoreCase(ip)) | |||
| { | |||
| ip = request.getHeader("X-Real-IP"); | |||
| } | |||
| if (ip == null || ip.length() == 0 || a1.equalsIgnoreCase(ip)) | |||
| { | |||
| ip = request.getRemoteAddr(); | |||
| } | |||
| return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); | |||
| } | |||
| /** | |||
| * 检查是否为内部IP地址 | |||
| * | |||
| * @param ip IP地址 | |||
| * @return 结果 | |||
| */ | |||
| public static boolean internalIp(String ip) | |||
| { | |||
| byte[] addr = textToNumericFormatV4(ip); | |||
| return internalIp(addr) || "127.0.0.1".equals(ip); | |||
| } | |||
| /** | |||
| * 检查是否为内部IP地址 | |||
| * | |||
| * @param addr byte地址 | |||
| * @return 结果 | |||
| */ | |||
| private static boolean internalIp(byte[] addr) | |||
| { | |||
| int a1 = 2; | |||
| if (StringUtils.isNull(addr) || addr.length < a1) | |||
| { | |||
| return true; | |||
| } | |||
| final byte b0 = addr[0]; | |||
| final byte b1 = addr[1]; | |||
| // 10.x.x.x/8 | |||
| final byte section1 = 0x0A; | |||
| // 172.16.x.x/12 | |||
| final byte section2 = (byte) 0xAC; | |||
| final byte section3 = (byte) 0x10; | |||
| final byte section4 = (byte) 0x1F; | |||
| // 192.168.x.x/16 | |||
| final byte section5 = (byte) 0xC0; | |||
| final byte section6 = (byte) 0xA8; | |||
| switch (b0) | |||
| { | |||
| case section1: | |||
| return true; | |||
| case section2: | |||
| if (b1 >= section3 && b1 <= section4) | |||
| { | |||
| return true; | |||
| } | |||
| case section5: | |||
| switch (b1) | |||
| { | |||
| case section6: | |||
| return true; | |||
| default: | |||
| return false; | |||
| } | |||
| default: | |||
| return false; | |||
| } | |||
| } | |||
| /** | |||
| * 将IPv4地址转换成字节 | |||
| * | |||
| * @param text IPv4地址 | |||
| * @return byte 字节 | |||
| */ | |||
| public static byte[] textToNumericFormatV4(String text) | |||
| { | |||
| if (text.length() == 0){ | |||
| return null; | |||
| } | |||
| byte[] bytes = new byte[4]; | |||
| String[] elements = text.split("\\.", -1); | |||
| try{ | |||
| long l; | |||
| int i; | |||
| switch (elements.length){ | |||
| case 1: | |||
| l = Long.parseLong(elements[0]); | |||
| Long a1 = 4294967295L; | |||
| if ((l < 0L) || (l > a1)){ | |||
| return null; | |||
| } | |||
| bytes[0] = (byte) (int) (l >> 24 & 0xFF); | |||
| bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); | |||
| bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); | |||
| bytes[3] = (byte) (int) (l & 0xFF); | |||
| break; | |||
| case 2: | |||
| l = Integer.parseInt(elements[0]); | |||
| Long a2 = 255L; | |||
| if ((l < 0L) || (l > a2)){ | |||
| return null; | |||
| } | |||
| bytes[0] = (byte) (int) (l & 0xFF); | |||
| l = Integer.parseInt(elements[1]); | |||
| Long a3 = 16777215L; | |||
| if ((l < 0L) || (l > a3)){ | |||
| return null; | |||
| } | |||
| bytes[1] = (byte) (int) (l >> 16 & 0xFF); | |||
| bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); | |||
| bytes[3] = (byte) (int) (l & 0xFF); | |||
| break; | |||
| case 3: | |||
| int a4 = 2; | |||
| for (i = 0; i < a4; ++i){ | |||
| l = Integer.parseInt(elements[i]); | |||
| if ((l < 0L) || (l > 255L)){ | |||
| return null; | |||
| } | |||
| bytes[i] = (byte) (int) (l & 0xFF); | |||
| } | |||
| l = Integer.parseInt(elements[2]); | |||
| Long a5 = 65535L; | |||
| if ((l < 0L) || (l > a5)){ | |||
| return null; | |||
| } | |||
| bytes[2] = (byte) (int) (l >> 8 & 0xFF); | |||
| bytes[3] = (byte) (int) (l & 0xFF); | |||
| break; | |||
| case 4: | |||
| int a6 = 4; | |||
| for (i = 0; i < a6; ++i){ | |||
| l = Integer.parseInt(elements[i]); | |||
| if ((l < 0L) || (l > 255L)){ | |||
| return null; | |||
| } | |||
| bytes[i] = (byte) (int) (l & 0xFF); | |||
| } | |||
| break; | |||
| default: | |||
| return null; | |||
| } | |||
| } | |||
| catch (NumberFormatException e){ | |||
| return null; | |||
| } | |||
| return bytes; | |||
| } | |||
| /** | |||
| * 获取IP地址 | |||
| * | |||
| * @return 本地IP地址 | |||
| */ | |||
| public static String getHostIp() | |||
| { | |||
| try | |||
| { | |||
| return InetAddress.getLocalHost().getHostAddress(); | |||
| } | |||
| catch (UnknownHostException e) | |||
| { | |||
| } | |||
| return "127.0.0.1"; | |||
| } | |||
| /** | |||
| * 获取主机名 | |||
| * | |||
| * @return 本地主机名 | |||
| */ | |||
| public static String getHostName() | |||
| { | |||
| try | |||
| { | |||
| return InetAddress.getLocalHost().getHostName(); | |||
| } | |||
| catch (UnknownHostException e) | |||
| { | |||
| } | |||
| return "未知"; | |||
| } | |||
| /** | |||
| * 从多级反向代理中获得第一个非unknown IP地址 | |||
| * | |||
| * @param ip 获得的IP地址 | |||
| * @return 第一个非unknown IP地址 | |||
| */ | |||
| public static String getMultistageReverseProxyIp(String ip) | |||
| { | |||
| // 多级反向代理检测 | |||
| String a = ","; | |||
| if (ip != null && ip.indexOf(a) > 0) | |||
| { | |||
| final String[] ips = ip.trim().split(a); | |||
| for (String subIp : ips) | |||
| { | |||
| if (false == isUnknown(subIp)) | |||
| { | |||
| ip = subIp; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| return StringUtils.substring(ip, 0, 255); | |||
| } | |||
| /** | |||
| * 检测给定字符串是否为未知,多用于检测HTTP请求相关 | |||
| * | |||
| * @param checkString 被检测的字符串 | |||
| * @return 是否未知 | |||
| */ | |||
| public static boolean isUnknown(String checkString) | |||
| { | |||
| return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); | |||
| } | |||
| /** | |||
| * 是否为IP | |||
| */ | |||
| public static boolean isip(String ip) | |||
| { | |||
| return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP); | |||
| } | |||
| /** | |||
| * 是否为IP,或 *为间隔的通配符地址 | |||
| */ | |||
| public static boolean isIpWildCard(String ip) | |||
| { | |||
| return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD); | |||
| } | |||
| /** | |||
| * 检测参数是否在ip通配符里 | |||
| */ | |||
| public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip) | |||
| { | |||
| String[] s1 = ipWildCard.split("\\."); | |||
| String[] s2 = ip.split("\\."); | |||
| boolean isMatchedSeg = true; | |||
| String a = "*"; | |||
| for (int i = 0; i < s1.length && !a.equals(s1[i]); i++) | |||
| { | |||
| if (!s1[i].equals(s2[i])) | |||
| { | |||
| isMatchedSeg = false; | |||
| break; | |||
| } | |||
| } | |||
| return isMatchedSeg; | |||
| } | |||
| /** | |||
| * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串 | |||
| */ | |||
| public static boolean isipsegment(String ipSeg) | |||
| { | |||
| return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG); | |||
| } | |||
| /** | |||
| * 判断ip是否在指定网段中 | |||
| */ | |||
| public static boolean ipIsInNetNoCheck(String iparea, String ip) | |||
| { | |||
| int idx = iparea.indexOf('-'); | |||
| String[] sips = iparea.substring(0, idx).split("\\."); | |||
| String[] sipe = iparea.substring(idx + 1).split("\\."); | |||
| String[] sipt = ip.split("\\."); | |||
| long ips = 0L, ipe = 0L, ipt = 0L; | |||
| int a = 4; | |||
| for (int i = 0; i < a; ++i) | |||
| { | |||
| ips = ips << 8 | Integer.parseInt(sips[i]); | |||
| ipe = ipe << 8 | Integer.parseInt(sipe[i]); | |||
| ipt = ipt << 8 | Integer.parseInt(sipt[i]); | |||
| } | |||
| if (ips > ipe) | |||
| { | |||
| long t = ips; | |||
| ips = ipe; | |||
| ipe = t; | |||
| } | |||
| return ips <= ipt && ipt <= ipe; | |||
| } | |||
| /** | |||
| * 校验ip是否符合过滤串规则 | |||
| * | |||
| * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99` | |||
| * @param ip 校验IP地址 | |||
| * @return boolean 结果 | |||
| */ | |||
| public static boolean isMatchedIp(String filter, String ip) | |||
| { | |||
| if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) | |||
| { | |||
| return false; | |||
| } | |||
| String[] ips = filter.split(";"); | |||
| for (String iStr : ips) | |||
| { | |||
| if (isip(iStr) && iStr.equals(ip)) | |||
| { | |||
| return true; | |||
| } | |||
| else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) | |||
| { | |||
| return true; | |||
| } | |||
| else if (isipsegment(iStr) && ipIsInNetNoCheck(iStr, ip)) | |||
| { | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| } | |||
| @ -0,0 +1,24 @@ | |||
| package com.hxhq.common.core.utils.poi; | |||
| import org.apache.poi.ss.usermodel.Cell; | |||
| import org.apache.poi.ss.usermodel.Workbook; | |||
| /** | |||
| * Excel数据格式处理适配器 | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| public interface ExcelHandlerAdapter | |||
| { | |||
| /** | |||
| * 格式化 | |||
| * | |||
| * @param value 单元格数据值 | |||
| * @param args excel注解args参数组 | |||
| * @param cell 单元格对象 | |||
| * @param wb 工作簿对象 | |||
| * | |||
| * @return 处理后的值 | |||
| */ | |||
| Object format(Object value, String[] args, Cell cell, Workbook wb); | |||
| } | |||
| @ -0,0 +1,413 @@ | |||
| package com.hxhq.common.core.utils.reflect; | |||
| import java.lang.reflect.Field; | |||
| import java.lang.reflect.InvocationTargetException; | |||
| import java.lang.reflect.Method; | |||
| import java.lang.reflect.Modifier; | |||
| import java.lang.reflect.ParameterizedType; | |||
| import java.lang.reflect.Type; | |||
| import java.util.Date; | |||
| import com.hxhq.common.core.text.Convert; | |||
| import com.hxhq.common.core.utils.DateUtils; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.apache.commons.lang3.Validate; | |||
| import org.apache.poi.ss.usermodel.DateUtil; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| /** | |||
| * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. | |||
| * | |||
| * @author hxhq | |||
| */ | |||
| @SuppressWarnings("rawtypes") | |||
| public class ReflectUtils | |||
| { | |||
| private static final String SETTER_PREFIX = "set"; | |||
| private static final String GETTER_PREFIX = "get"; | |||
| private static final String CGLIB_CLASS_SEPARATOR = "$$"; | |||
| private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); | |||
| /** | |||
| * 调用Getter方法. | |||
| * 支持多级,如:对象名.对象名.方法 | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public static <E> E invokeGetter(Object obj, String propertyName) | |||
| { | |||
| Object object = obj; | |||
| String a = "."; | |||
| for (String name : StringUtils.split(propertyName, a)) | |||
| { | |||
| String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); | |||
| object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); | |||
| } | |||
| return (E) object; | |||
| } | |||
| /** | |||
| * 调用Setter方法, 仅匹配方法名。 | |||
| * 支持多级,如:对象名.对象名.方法 | |||
| */ | |||
| public static <E> void invokeSetter(Object obj, String propertyName, E value) | |||
| { | |||
| Object object = obj; | |||
| String[] names = StringUtils.split(propertyName, "."); | |||
| for (int i = 0; i < names.length; i++) | |||
| { | |||
| if (i < names.length - 1) | |||
| { | |||
| String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); | |||
| object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); | |||
| } | |||
| else | |||
| { | |||
| String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); | |||
| invokeMethodByName(object, setterMethodName, new Object[] { value }); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public static <E> E getFieldValue(final Object obj, final String fieldName) | |||
| { | |||
| Field field = getAccessibleField(obj, fieldName); | |||
| if (field == null) | |||
| { | |||
| logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); | |||
| return null; | |||
| } | |||
| E result = null; | |||
| try | |||
| { | |||
| result = (E) field.get(obj); | |||
| } | |||
| catch (IllegalAccessException e) | |||
| { | |||
| logger.error("不可能抛出的异常{}", e.getMessage()); | |||
| } | |||
| return result; | |||
| } | |||
| /** | |||
| * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. | |||
| */ | |||
| public static <E> void setFieldValue(final Object obj, final String fieldName, final E value) | |||
| { | |||
| Field field = getAccessibleField(obj, fieldName); | |||
| if (field == null) | |||
| { | |||
| logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); | |||
| return; | |||
| } | |||
| try | |||
| { | |||
| field.set(obj, value); | |||
| } | |||
| catch (IllegalAccessException e) | |||
| { | |||
| logger.error("不可能抛出的异常: {}", e.getMessage()); | |||
| } | |||
| } | |||
| /** | |||
| * 直接调用对象方法, 无视private/protected修饰符. | |||
| * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. | |||
| * 同时匹配方法名+参数类型, | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes, | |||
| final Object[] args) | |||
| { | |||
| if (obj == null || methodName == null) | |||
| { | |||
| return null; | |||
| } | |||
| Method method = getAccessibleMethod(obj, methodName, parameterTypes); | |||
| if (method == null) | |||
| { | |||
| logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); | |||
| return null; | |||
| } | |||
| try | |||
| { | |||
| return (E) method.invoke(obj, args); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; | |||
| throw convertReflectionExceptionToUnchecked(msg, e); | |||
| } | |||
| } | |||
| /** | |||
| * 直接调用对象方法, 无视private/protected修饰符, | |||
| * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. | |||
| * 只匹配函数名,如果有多个同名函数调用第一个。 | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args) | |||
| { | |||
| Method method = getAccessibleMethodByName(obj, methodName, args.length); | |||
| if (method == null) | |||
| { | |||
| // 如果为空不报错,直接返回空。 | |||
| logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); | |||
| return null; | |||
| } | |||
| try | |||
| { | |||
| // 类型转换(将参数数据类型转换为目标方法参数类型) | |||
| Class<?>[] cs = method.getParameterTypes(); | |||
| for (int i = 0; i < cs.length; i++) | |||
| { | |||
| if (args[i] != null && !args[i].getClass().equals(cs[i])) | |||
| { | |||
| if (cs[i] == String.class) | |||
| { | |||
| args[i] = Convert.toStr(args[i]); | |||
| if (StringUtils.endsWith((String) args[i], ".0")) | |||
| { | |||
| args[i] = StringUtils.substringBefore((String) args[i], ".0"); | |||
| } | |||
| } | |||
| else if (cs[i] == Integer.class) | |||
| { | |||
| args[i] = Convert.toInt(args[i]); | |||
| } | |||
| else if (cs[i] == Long.class) | |||
| { | |||
| args[i] = Convert.toLong(args[i]); | |||
| } | |||
| else if (cs[i] == Double.class) | |||
| { | |||
| args[i] = Convert.toDouble(args[i]); | |||
| } | |||
| else if (cs[i] == Float.class) | |||
| { | |||
| args[i] = Convert.toFloat(args[i]); | |||
| } | |||
| else if (cs[i] == Date.class) | |||
| { | |||
| if (args[i] instanceof String) | |||
| { | |||
| args[i] = DateUtils.parseDate(args[i]); | |||
| } | |||
| else | |||
| { | |||
| args[i] = DateUtil.getJavaDate((Double) args[i]); | |||
| } | |||
| } | |||
| else if (cs[i] == boolean.class || cs[i] == Boolean.class) | |||
| { | |||
| args[i] = Convert.toBool(args[i]); | |||
| } | |||
| } | |||
| } | |||
| return (E) method.invoke(obj, args); | |||
| } | |||
| catch (Exception e) | |||
| { | |||
| String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; | |||
| throw convertReflectionExceptionToUnchecked(msg, e); | |||
| } | |||
| } | |||
| /** | |||
| * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. | |||
| * 如向上转型到Object仍无法找到, 返回null. | |||
| */ | |||
| public static Field getAccessibleField(final Object obj, final String fieldName) | |||
| { | |||
| // 为空不报错。直接返回 null | |||
| if (obj == null) | |||
| { | |||
| return null; | |||
| } | |||
| Validate.notBlank(fieldName, "fieldName can't be blank"); | |||
| for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) | |||
| { | |||
| try | |||
| { | |||
| Field field = superClass.getDeclaredField(fieldName); | |||
| makeAccessible(field); | |||
| return field; | |||
| } | |||
| catch (NoSuchFieldException e) | |||
| { | |||
| continue; | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. | |||
| * 如向上转型到Object仍无法找到, 返回null. | |||
| * 匹配函数名+参数类型。 | |||
| * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) | |||
| */ | |||
| public static Method getAccessibleMethod(final Object obj, final String methodName, | |||
| final Class<?>... parameterTypes) | |||
| { | |||
| // 为空不报错。直接返回 null | |||
| if (obj == null) | |||
| { | |||
| return null; | |||
| } | |||
| Validate.notBlank(methodName, "methodName can't be blank"); | |||
| for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) | |||
| { | |||
| try | |||
| { | |||
| Method method = searchType.getDeclaredMethod(methodName, parameterTypes); | |||
| makeAccessible(method); | |||
| return method; | |||
| } | |||
| catch (NoSuchMethodException e) | |||
| { | |||
| continue; | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. | |||
| * 如向上转型到Object仍无法找到, 返回null. | |||
| * 只匹配函数名。 | |||
| * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) | |||
| */ | |||
| public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) | |||
| { | |||
| // 为空不报错。直接返回 null | |||
| if (obj == null) | |||
| { | |||
| return null; | |||
| } | |||
| Validate.notBlank(methodName, "methodName can't be blank"); | |||
| for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) | |||
| { | |||
| Method[] methods = searchType.getDeclaredMethods(); | |||
| for (Method method : methods) | |||
| { | |||
| if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) | |||
| { | |||
| makeAccessible(method); | |||
| return method; | |||
| } | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 | |||
| */ | |||
| public static void makeAccessible(Method method) | |||
| { | |||
| Boolean a = (!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) | |||
| && !method.isAccessible(); | |||
| if (a) | |||
| { | |||
| method.setAccessible(true); | |||
| } | |||
| } | |||
| /** | |||
| * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 | |||
| */ | |||
| public static void makeAccessible(Field field) | |||
| { | |||
| Boolean a = (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) | |||
| || Modifier.isFinal(field.getModifiers())) && !field.isAccessible(); | |||
| if (a) | |||
| { | |||
| field.setAccessible(true); | |||
| } | |||
| } | |||
| /** | |||
| * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 | |||
| * 如无法找到, 返回Object.class. | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public static <T> Class<T> getClassGenricType(final Class clazz) | |||
| { | |||
| return getClassGenricType(clazz, 0); | |||
| } | |||
| /** | |||
| * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. | |||
| * 如无法找到, 返回Object.class. | |||
| */ | |||
| public static Class getClassGenricType(final Class clazz, final int index) | |||
| { | |||
| Type genType = clazz.getGenericSuperclass(); | |||
| if (!(genType instanceof ParameterizedType)) | |||
| { | |||
| logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); | |||
| return Object.class; | |||
| } | |||
| Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); | |||
| if (index >= params.length || index < 0) | |||
| { | |||
| logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " | |||
| + params.length); | |||
| return Object.class; | |||
| } | |||
| if (!(params[index] instanceof Class)) | |||
| { | |||
| logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); | |||
| return Object.class; | |||
| } | |||
| return (Class) params[index]; | |||
| } | |||
| public static Class<?> getUserClass(Object instance) | |||
| { | |||
| if (instance == null) | |||
| { | |||
| throw new RuntimeException("Instance must not be null"); | |||
| } | |||
| Class clazz = instance.getClass(); | |||
| if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) | |||
| { | |||
| Class<?> superClass = clazz.getSuperclass(); | |||
| if (superClass != null && !Object.class.equals(superClass)) | |||
| { | |||
| return superClass; | |||
| } | |||
| } | |||
| return clazz; | |||
| } | |||
| /** | |||
| * 将反射时的checked exception转换为unchecked exception. | |||
| */ | |||
| public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) | |||
| { | |||
| if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException | |||
| || e instanceof NoSuchMethodException) | |||
| { | |||
| return new IllegalArgumentException(msg, e); | |||
| } | |||
| else if (e instanceof InvocationTargetException) | |||
| { | |||
| return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); | |||
| } | |||
| return new RuntimeException(msg, e); | |||
| } | |||
| } | |||