public final class Constants {
    /**
     * 系统属性
     * DP_开头的属性全部从DynamicProperty表读取
     */
    public static final String DP_SYSTEM_VERSION = "System.Version"; //用户获取当前系统的版本
    public static final String DP_STATIC_PATH = "Static.Path"; //系统的静态资源地址

    /**
     * 业务属性
     */
    public static final String SESSION_USER = "SESSION_USER";

    /**
     * 操作名称
     */
    public static final String OP_NAME = "op";

    /**
     * 消息key
     */
    public static final String MESSAGE = "message";

    /**
     * 错误key
     */
    public static final String ERROR = "error";

    /**
     * 上个页面地址
     */
    public static final String BACK_URL = "BackURL";
    public static final String IGNORE_BACK_URL = "ignoreBackURL";

    /**
     * 当前请求的地址 带参数
     */
    public static final String CURRENT_URL = "currentURL";

    /**
     * 当前请求的地址 不带参数
     */
    public static final String NO_QUERYSTRING_CURRENT_URL = "noQueryStringCurrentURL";
    public static final String CONTEXT_PATH = "ctx";

    /**
     * 当前登录的用户
     */
    public static final String CURRENT_USER = "CurrentUser";
    public static final String CURRENT_USERNAME = "username";

    /**
     * 各种正则表达式
     */
    public static final String USERNAME_PATTERN = "^[\\u4E00-\\u9FA5\\uf900-\\ufa2d_a-zA-Z][\\u4E00-\\u9FA5\\uf900-\\ufa2d\\w]{1,19}$";
    public static final String MOBILE_PHONE_PATTERN = "^0{0,1}(13[0-9]|15[0-9]|14[0-9]|18[0-9])[0-9]{8}$";
    public static final String EMAIL_PATTERN = "^\\w+([-.]\\w+)*@\\w+([-]\\w+)*\\.(\\w+([-]\\w+)*\\.)*[a-z]{2,3}$";
}
 public class UserLogUtil {
    private static final Logger USER_LOGGER = LoggerFactory.getLogger("USER_LOG");

    /**
     * <p>记录格式 [ip][用户名][操作][错误消息]<p/>
     * 注意操作如下：
     * loginError 登录失败
     * loginSuccess 登录成功
     * passwordError 密码错误
     * changePassword 修改密码
     * changeStatus 修改状态
     *
     * @param username
     * @param op
     * @param msg
     * @param args
     */
    public static void log(String username, String op, String msg, Object... args) {
        StringBuilder s = new StringBuilder();
        s.append(getBlock(IpUtil.getIp()));
        s.append(getBlock(username));
        s.append(getBlock(op));
        s.append(getBlock(msg));

        USER_LOGGER.info(s.toString(), args);
    }

    public static String getBlock(Object msg) {
        if (msg == null) {
            msg = "";
        }
        return "[" + msg.toString() + "]";
    }
}
 public class BTQueryParameter implements Serializable {
    private static final long serialVersionUID = 639145165702621352L;
    private String searchText;
    private Integer pageSize;
    private Integer pageNumber;
    private String sortName;
    private String sortOrder;

    public String getSearchText() {
        return searchText;
    }

    public void setSearchText(String searchText) {
        this.searchText = searchText;
    }

    public Integer getPageSize() {
        return pageSize;
    }

    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }

    public Integer getPageNumber() {
        return pageNumber;
    }

    /**
     * Bootstrap Table的分页是从1开始的
     * 这里需要做兼容，将首页下标改为0
     * @param pageNumber
     */
    public void setPageNumber(Integer pageNumber) {
        this.pageNumber = pageNumber - 1;
    }

    public String getSortName() {
        return sortName;
    }

    public void setSortName(String sortName) {
        this.sortName = sortName;
    }

    public String getSortOrder() {
        return sortOrder;
    }

    public void setSortOrder(String sortOrder) {
        this.sortOrder = sortOrder;
    }
}
 @JSONType(orders = {"currentPage", "pageSize", "total", "totalPage", "records"})
public class Page<E> implements Serializable {
    private static final long serialVersionUID = -4439159377751637892L;
    private List<E> records;
    private Integer pageSize = 1;
    private Integer currentPage = 0;
    private Long total;
    private Long totalPage;

    public List<E> getRecords() {
        return records;
    }

    public void setRecords(List<E> records) {
        this.records = records;
    }

    public Integer getPageSize() {
        return pageSize;
    }

    /**
     * 每页记录数最小为1
     * @param pageSize 每页记录数
     */
    public void setPageSize(Integer pageSize) {
        if (pageSize != null && pageSize > 1) {
            this.pageSize = pageSize;
        }
    }

    public Integer getCurrentPage() {
        return currentPage;
    }

    /**
     * 页码从0开始
     * @param currentPage 当前页
     */
    public void setCurrentPage(Integer currentPage) {
        if (currentPage != null && currentPage > 0) {
            this.currentPage = currentPage;
        }
    }

    public Long getTotal() {
        return total;
    }

    public void setTotal(Long total) {
        this.total = total;
    }

    public Long getTotalPage() {
        totalPage = (total + pageSize - 1) / pageSize;
        return totalPage;
    }
}
 public class Result implements Serializable {
    private int code;
    private String message;

    public Result(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
 @Entity
@Table(name = "resource")
@Cache(region = "all", usage = CacheConcurrencyStrategy.READ_WRITE)
@EnabledQueryCache
@JSONType(orders = {"id", "resourceKey", "resourceValue", "url", "parentId", "parentIds", "type", "menuIcon", "sequence", "available"})
public class Resource extends BaseEntity {
    private static final long serialVersionUID = -7686411622173094721L;
    /**
     * 自增主键
     */
    @Id
    @GeneratedValue
    @Column(name = "id")
    private Long id;

    /**
     * 资源key
     */
    @Column(name = "resource_key", length = 128)
    private String resourceKey;

    /**
     * 资源名称
     */
    @Column(name = "resource_value", length = 128, nullable = false)
    private String resourceValue;

    /**
     * 资源标识地址
     */
    @Column(name = "url", length = 256)
    private String url;

    /**
     * 上级资源ID
     */
    @Column(name = "parent_id")
    private Long parentId;

    /**
     * 所有上级资源ID
     * 分隔符是 /
     */
    @Column(name = "parent_ids", length = 256)
    private String parentIds;

    /**
     * 可用状态
     */
    @Column(name = "available")
    private Boolean available = Boolean.FALSE;

    /**
     * 资源类型
     */
    @Column(name = "type")
    @Enumerated(EnumType.STRING)
    private ResourceType type;

    /**
     * 资源图标
     */
    @Column(name = "menu_icon", length = 128)
    private String menuIcon;

    /**
     * 菜单排序
     */
    @Column(name = "sequence")
    private Integer sequence;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getResourceKey() {
        return resourceKey;
    }

    public void setResourceKey(String resourceKey) {
        this.resourceKey = resourceKey;
    }

    public String getResourceValue() {
        return resourceValue;
    }

    public void setResourceValue(String resourceValue) {
        this.resourceValue = resourceValue;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Long getParentId() {
        return parentId;
    }

    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    public String getParentIds() {
        return parentIds;
    }

    public void setParentIds(String parentIds) {
        this.parentIds = parentIds;
    }

    public Boolean getAvailable() {
        return available;
    }

    public void setAvailable(Boolean available) {
        this.available = available;
    }

    public ResourceType getType() {
        return type;
    }

    public void setType(ResourceType type) {
        this.type = type;
    }

    public String getMenuIcon() {
        return menuIcon;
    }

    public void setMenuIcon(String menuIcon) {
        this.menuIcon = menuIcon;
    }

    public Integer getSequence() {
        return sequence;
    }

    public void setSequence(Integer sequence) {
        this.sequence = sequence;
    }
}
 public enum ResourceType {
    view("视图"),menu("菜单"),entity("实体"),url("链接");

    private final String info;

    private ResourceType(String info) {
        this.info = info;
    }

    public String getInfo() {
        return info;
    }
}
 @Entity
@Table(name = "user")
@DynamicInsert(value = true)
@Cache(region = "all", usage = CacheConcurrencyStrategy.READ_WRITE)
@EnabledQueryCache
@JSONType(orders = {"id", "username", "nickname", "email", "mobilePhone", "status", "departmentKey", "createTime", "modifyTime", "roles"},
            ignores = {"password", "salt", "deleted"})
public class User extends BaseEntity implements LogicDeletable {
    private static final long serialVersionUID = 2225038507614711877L;
    /**
     * 自增主键ID
     */
    @Id
    @GeneratedValue
    @Column(name = "id")
    private Long id;

    /**
     * 用户名（可用于登陆，唯一）
     */
    @Column(name = "username", length = 32, nullable = false)
    private String username;

    /**
     * 用户昵称（不可用于登陆，不唯一）
     */
    @Column(name = "nickname", length = 32, nullable = false)
    private String nickname;

    /**
     * 用户密码
     */
    @Column(name = "password", length = 32, nullable = false)
    private String password;
    /**
     * 加密密码时用的盐
     */
    @Column(name = "salt", length = 8, nullable = false)
    private String salt;

    /**
     * 用户邮箱（用于登陆，唯一）
     */
    @Column(name = "email", length = 32, nullable = false, unique = true)
    private String email;

    /**
     * 用户手机号码
     */
    @Column(name = "mobile_phone", length = 16)
    private String mobilePhone;

    /**
     * 用户所属部门的key
     */
    @Column(name = "department_key", length = 32)
    private String departmentKey;

    /**
     * 创建该用户的时间
     */
    @Column(name = "create_time")
    @Temporal(TemporalType.TIMESTAMP)
    @JSONField(format = "yyyy-MM-dd HH:mm:ss.SSS")
    private Date createTime;

    /**
     * 上次变更用户信息的时间
     */
    @Column(name = "modify_time")
    @Temporal(TemporalType.TIMESTAMP)
    @JSONField(format = "yyyy-MM-dd HH:mm:ss.SSS")
    private Date modifyTime;

    /**
     * 用户的逻辑删除状态
     */
    @Column(name = "deleted")
    private Boolean deleted = Boolean.FALSE;

    /**
     * 用户状态
     */
    @Column(name = "status")
    @Enumerated(EnumType.STRING)
    private UserStatus status = UserStatus.normal;

    /**
     * 用户的角色关系
     */
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = { @JoinColumn(name = "role_id") })
    @Cache(region = "all", usage = CacheConcurrencyStrategy.READ_WRITE)
    private Set<Role> roles = new HashSet<Role>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getMobilePhone() {
        return mobilePhone;
    }

    public void setMobilePhone(String mobilePhone) {
        this.mobilePhone = mobilePhone;
    }

    public String getDepartmentKey() {
        return departmentKey;
    }

    public void setDepartmentKey(String departmentKey) {
        this.departmentKey = departmentKey;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getModifyTime() {
        return modifyTime;
    }

    public void setModifyTime(Date modifyTime) {
        this.modifyTime = modifyTime;
    }

    @Override
    public Boolean getDeleted() {
        return this.deleted;
    }

    @Override
    public void setDeleted(Boolean deleted) {
        this.deleted = deleted;
    }

    @Override
    public void markDeleted() {
        this.deleted = Boolean.TRUE;
    }

    public UserStatus getStatus() {
        return status;
    }

    public void setStatus(UserStatus status) {
        this.status = status;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

}
 @Service
public class ResourceService extends BaseService<Resource> {
    @javax.annotation.Resource
    private PermissionService permissionService;
    @javax.annotation.Resource
    private AuthorizationService authorizationService;
    private ResourceDao resourceDao;
    @javax.annotation.Resource
    public void setResourceDao(ResourceDao resourceDao) {
        this.resourceDao = resourceDao;
        this.dao = resourceDao;
    }

    /**
     * 得到真实的资源标识  即 上级资源标识:子级资源标识
     * @param resource 目标资源
     * @return String
     */
    public String findActualResourceKey(Resource resource) {

        if(resource == null) {
            return null;
        }

        StringBuilder sb = new StringBuilder(resource.getResourceKey());

        boolean hasKey = !StringUtil.isEmpty(resource.getResourceKey());

        Resource parent;
        Long parentId = resource.getParentId();
        if (parentId == null) {
            parent = null;
        } else {
            parent = get(parentId);
        }
        while(parent != null) {
            if(!StringUtil.isEmpty(parent.getResourceKey())) {
                sb.insert(0, parent.getResourceKey() + ":");
                hasKey = true;
            }
            parentId = parent.getParentId();
            if (parentId == null) {
                parent = null;
            } else {
                parent = get(parentId);
            }
        }

        // 如果用户没有声明资源标识，并且没有上级资源标识，那么就为空
        if(!hasKey) {
            return "";
        }

        // 删除末尾的:
        int length = sb.length();
        if(length > 0 && sb.lastIndexOf(":") == length - 1) {
            sb.deleteCharAt(length - 1);
        }

        // 如果有子级资源，最后拼一个*
        if (!findChildren(resource.getId()).isEmpty()) {
            sb.append(":*");
        }

        return sb.toString();
    }

    /**
     * 获取根资源ID
     */
    public Long getRootResourceId() {
        return resourceDao.getRootResourceId();
    }

    /**
     * 获取资源节点
     * @return 资源节点
     */
    public List<ResourceNode> findResourceNode(Role role) {
        List<ResourceNode> nodes = new ArrayList<ResourceNode>();

        if (role != null) {
            Set<String> permissions = authorizationService.findPermissionsStr(role);
            Long rootId = getRootResourceId();
            List<Resource> resources = findChildren(rootId);

            for (Resource resource : resources) {
                ResourceNode node = convertToResourceNode(resource, permissions);
                if (node != null) {
                    nodes.add(node);
                }
            }
        }

        return nodes;
    }

    /**
     * 根据父资源ID获取子资源
     * @param parentId 父资源ID
     * @return 子资源列表
     */
    public List<Resource> findChildren(Long parentId) {
        Searchable searchable = SearchRequest.newSearchRequest();
        searchable.addSearchFilter(Condition.newCondition("parentId", parentId));
        return find(searchable);
    }

    /**
     * 递归生成资源树
     * @param resource 资源
     * @param actualPermissions 角色对资源实际拥有的权限
     * @return 资源节点
     */
    public ResourceNode convertToResourceNode(Resource resource, Set<String> actualPermissions) {
        List<Resource> resources = findChildren(resource.getId());
        ResourceNode node = new ResourceNode(resource.getId(), resource.getResourceKey(), resource.getResourceValue());

        // 当前资源没有子资源
        if (resources == null || resources.isEmpty()) {
            // 既没有子资源有没有资源标识符的资源直接过滤掉，菜单也不做授权展示
            if (StringUtil.isEmpty(resource.getResourceKey()) || ResourceType.menu.equals(resource.getType())) {

                return null;
            } else {
                // 增加操作权限状态
                List<ResourceNode.PermissionState> states = new ArrayList<ResourceNode.PermissionState>();
                List<Permission> permissions = permissionService.findAll();
                String actualResourceKey = findActualResourceKey(resource);

                for (Permission permission : permissions) {
                    if (ResourceType.entity.equals(resource.getType())) {// 实体类型的资源可以进行CRUD操作
                        // 判定用户是否有操作该资源的权限
                        Boolean authorized = hasPermission(actualResourceKey + ":" + permission.getPermissionKey(), actualPermissions);
                        states.add(node.new PermissionState(permission, authorized));
                    } else if (ResourceType.view.equals(resource.getType())) { // 视图类资源只有view权限
                        if (ResourceType.view.name().equals(permission.getPermissionKey())) {
                            Boolean authorized = hasPermission(actualResourceKey + ":" + permission.getPermissionKey(), actualPermissions);
                            states.add(node.new PermissionState(permission, authorized));
                        }
                    }
                }
                node.setPermissionStates(states);

                return node;
            }
        } else {
            List<ResourceNode> childNodes = new ArrayList<ResourceNode>();
            for (Resource childResource : resources) {
                ResourceNode childNode = convertToResourceNode(childResource, actualPermissions);
                if (childNode != null) {
                    childNodes.add(childNode);
                }
            }
            // 如果没有子节点，删除该授权项
            if (childNodes.isEmpty()) {
                node = null;
            } else {
                node.setChildren(childNodes);
            }

            return node;
        }
    }

    /**
     * 判定角色是否对资源拥有权限
     * @param permission 待判定权限
     * @param actualPermissions 角色对资源实际拥有的权限
     * @return 角色是否对资源拥有权限
     */
    private boolean hasPermission(String permission, Set<String> actualPermissions) {
        return actualPermissions.contains(permission);
    }

    /**
     * 根据角色集合获取主菜单
     * @param roles 角色集合
     * @return 菜单列表
     */
    public List<Menu> findMenu(Set<Role> roles) {
        List<Menu> menus = new ArrayList<Menu>();

        if (roles != null && !roles.isEmpty()) {
            // 先组装用户的权限列表
            Set<String> permissions = new HashSet<String>();
            for (Role role : roles) {
                Set<String> tmpPermissions = authorizationService.findPermissionsStr(role);
                if (!tmpPermissions.isEmpty()) {
                    permissions.addAll(tmpPermissions);
                }
            }
            Long rootId = getRootResourceId();
            List<Resource> resources = findChildren(rootId);

            // 资源转化成菜单
            for (Resource resource : resources) {
                Menu menu = convertToMenu(resource, permissions);
                if (menu != null) {
                    menus.add(menu);
                }
            }
        }

        return menus;
    }

    /**
     * 递归生成菜单树
     * @param resource 资源
     * @param actualPermissions 角色对资源实际拥有的权限
     * @return 菜单
     */
    public Menu convertToMenu(Resource resource, Set<String> actualPermissions) {
        List<Resource> resources = findChildren(resource.getId());
        Menu menu = new Menu();
        menu.setMenuKey(resource.getResourceKey());
        menu.setMenuValue(resource.getResourceValue());
        menu.setUrl(resource.getUrl());
        menu.setMenuIcon(resource.getMenuIcon());
        menu.setSequence(resource.getSequence());

        List<Menu> childMenus = new ArrayList<Menu>();

        // 当前资源没有子资源，所有人可见
        if (resources == null || resources.isEmpty()) {

            return menu;
        } else { // 有子资源，根据子资源进行权限鉴定
            boolean authorized = false;
            for (Resource childResource : resources) {
                // 只将资源类型为菜单的资源添加到菜单列表中
                if (ResourceType.menu.equals(childResource.getType())) {
                    Menu childMenu = convertToMenu(childResource, actualPermissions);
                    if (childMenu != null) {
                        childMenus.add(childMenu);
                        authorized = true;
                    }
                } else {
                    if (ResourceType.view.equals(childResource.getType()) || ResourceType.entity.equals(childResource.getType())) {// 实体和视图可以配置权限
                        String actualResourceKey = findActualResourceKey(childResource);
                        authorized = hasPermission(actualResourceKey + ":" + ResourceType.view.name(), actualPermissions) ? true : authorized;
                    }
                }
            }
            // 子级资源鉴权通过，增加子级菜单
            if (authorized) {
                menu.setChildren(childMenus);
            } else { // 子级资源鉴权不通过，将当前菜单删除
                menu = null;
            }
            return menu;
        }
    }

    /**
     * 删除资源角色关系
     * @param resource 资源
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {RuntimeException.class})
    public void deleteResource(Resource resource) {
        resourceDao.deleteRoleResource(resource.getId());
        delete(resource);
    }
}
 public class Condition extends AbstractSearchFilter {
    private static final long serialVersionUID = -5881232644192853247L;

    private List<SearchFilter> childConditions = new LinkedList<SearchFilter>();

    /**
     * 当操作符是一元操作符的时候, value可以为空
     * @param property
     * @param operator
     * @param value
     * @return 过滤条件 {@link Condition}
     */
    public static Condition newCondition(final SearchType type, final String property, final SearchOperator operator, final Object value) {
        return new Condition(type, property, operator, value);
    }

    /**
     * 默认条件连接类型为 AND (and)
     * @param property
     * @param operator
     * @param value
     * @return 过滤条件 {@link Condition}
     */
    public static Condition newCondition(final String property, final SearchOperator operator, final Object value) {
        return newCondition(SearchType.AND, property, operator, value);
    }

    /**
     * 默认操作符为 eq (=)
     * @param property
     * @param value
     * @return 过滤条件 {@link Condition}
     */
    public static Condition newCondition(final String property, final Object value) {
        return newCondition(property, SearchOperator.eq, value);
    }

    private Condition(final SearchType type, final String property, final SearchOperator operator, final Object value) {
        super(type, property, operator, value);
    }

    /**
     * 预加工value, 使其符合sql语句
     * @return Object
     */
    public Object getFormatValues() {
        SearchOperator operator = this.getOperator();
        Object value = this.getValue();
        if (operator == SearchOperator.like || operator == SearchOperator.notLike) {
            return "%" + value + "%";
        }
        if (operator == SearchOperator.prefixLike || operator == SearchOperator.prefixNotLike) {
            return value + "%";
        }

        if (operator == SearchOperator.suffixLike || operator == SearchOperator.suffixNotLike) {
            return "%" + value;
        }
        return value;
    }

    /**
     * @return 当前过滤条件为in过滤返回true否则false
     */
    public boolean isInFilter() {
        return SearchOperator.isAllowMultiValue(getOperator());
    }

    /**
     * @return 当前过滤条件为一元过滤返回true否则false
     */
    public boolean isUnaryFilter() {
        return SearchOperator.isAllowBlankValue(getOperator());
    }

    /**
     * 给当前过滤条件添加子条件，如or (a = 1 and b = 2)
     * @param property
     * @param operator
     * @param value
     * @return 过滤条件 {@link Condition}
     */
    public Condition addChildCondition(final String property, final SearchOperator operator, final Object value) {
        return addChildCondition(Condition.newCondition(property, operator, value));
    }

    public Condition addChildCondition(final SearchFilter filter) {
        if (filter == null) {
            return this;
        }
        int index = this.childConditions.indexOf(filter);
        if (index == -1) {
            this.childConditions.add(filter);
        } else {
            this.childConditions.set(index, filter);
        }
        return this;
    }

    public Condition addChildConditions(final Collection<? extends SearchFilter> childConditions) {
        if (childConditions == null || childConditions.isEmpty()) {
            return this;
        }

        for (SearchFilter filter : childConditions) {
            addChildCondition(filter);
        }
        return this;
    }

    public Condition removeChildCondition(final String property) {
        if (StringUtil.isEmpty(property)) {
            return this;
        }

        Iterator<SearchFilter> iterator = this.childConditions.iterator();
        while (iterator.hasNext()) {
            SearchFilter filter = iterator.next();
            if (property.equals(filter.getProperty())) {
                iterator.remove();
            }
        }

        return this;
    }

    public Condition removeChildCondition(final String property, final SearchOperator operator) {
        if (StringUtil.isEmpty(property) || operator == null) {
            return this;
        }

        Iterator<SearchFilter> iterator = this.childConditions.iterator();
        while (iterator.hasNext()) {
            SearchFilter filter = iterator.next();
            if (property.equals(filter.getProperty()) && filter.getOperator() == operator) {
                iterator.remove();
            }
        }

        return this;
    }

    @Override
    public boolean hasChildFilter() {
        return !this.childConditions.isEmpty();
    }

    @Override
    public Collection<SearchFilter> getChildFilters() {
        return Collections.unmodifiableCollection(this.childConditions);
    }

    @Override
    public String toString() {
        return "Condition{" +
                "type=" + getType() +
                ", property=" + getProperty() +
                ", operator=" + getOperator() +
                ", value=" + getValue() +
                "}";
    }
}
 public class PageRequest extends AbstractPageRequest {
    private static final long serialVersionUID = 3818454816004846209L;

    public static PageRequest newPageRequest(int page, int size) {
        return new PageRequest(page, size);
    }

    public static PageRequest newPageRequest(int page, int size, Sort sort) {
        return new PageRequest(page, size, sort);
    }

    public PageRequest(int page, int size, Sort.Direction direction, String... properties) {
        super(page, size, new Sort(direction, properties));
    }

    public PageRequest(int page, int size, Sort sort) {
        super(page, size, sort);
    }

    public PageRequest(int page, int size) {
        super(page, size, null);
    }

    @Override
    public String toString() {
        return "PageRequest{" +
                "size=" + getPageSize() +
                ", page=" + getPageNumber() +
                ", sort=" + getSort() +
                '}';
    }
}
 public class SearchRequest extends AbstractSearchRequest {
    private static final long serialVersionUID = 7126784443721187450L;

    public static SearchRequest newSearchRequest(List<SearchFilter> filters, Sort sort, Pageable page) {
        return new SearchRequest(filters, sort, page);
    }

    public static SearchRequest newSearchRequest(Sort sort, Pageable page) {
        return new SearchRequest(sort, page);
    }

    public static SearchRequest newSearchRequest() {
        return new SearchRequest();
    }

    public SearchRequest(List<SearchFilter> filters, Sort sort, Pageable page) {
        super(filters, sort, page);
    }

    public SearchRequest(Sort sort, Pageable page) {
        this(null, sort, page);
    }

    public SearchRequest() {
        this(null, null, null);
    }

    @Override
    public Searchable setPage(int pageNumber, int pageSize) {
        return setPage(new PageRequest(pageNumber, pageSize));
    }

    @Override
    public String toString() {
        return "SearchRequest{" +
                "filters=" + getSearchFilters() +
                ", page=" + getPage() +
                ", sort=" + getSort() +
                '}';
    }
}
 public class Sort implements Iterable<Sort.Order>, Serializable {
    private static final long serialVersionUID = -6056743972633766976L;
    public static final Direction DEFAULT_DIRECTION = Direction.asc;
    private final List<Order> orders;

    /**
     * 使用给定的 {@link Order}s 实例化一个 {@link Sort}.
     *
     * @param orders must not be {@literal null}.
     */
    public Sort(Order... orders) {
        this(Arrays.asList(orders));
    }

    /**
     * 实例化一个 {@link Sort}.
     *
     * @param orders must not be {@literal null} or contain {@literal null}.
     */
    public Sort(List<Order> orders) {

        if (null == orders || orders.isEmpty()) {
            throw new IllegalArgumentException("You have to provide at least one sort property to sort by!");
        }

        this.orders = orders;
    }

    /**
     * 使用默认排序方向 {@link Direction#asc} 实例化一个 {@link Sort}.
     *
     * @param properties must not be {@literal null} or contain {@literal null} or empty strings
     */
    public Sort(String... properties) {
        this(DEFAULT_DIRECTION, properties);
    }

    /**
     * 根据给定的排序方向和属性实例化一个 {@link Sort}.
     *
     * @param direction defaults to {@link Sort#DEFAULT_DIRECTION} (for {@literal null} cases, too)
     * @param properties must not be {@literal null}, empty or contain {@literal null} or empty strings.
     */
    public Sort(Direction direction, String... properties) {
        this(direction, properties == null ? new ArrayList<String>() : Arrays.asList(properties));
    }

    /**
     * 根据给定的排序方向和属性实例化一个 {@link Sort}.
     *
     * @param direction defaults to {@link Sort#DEFAULT_DIRECTION} (for {@literal null} cases, too)
     * @param properties must not be {@literal null} or contain {@literal null} or empty strings.
     */
    public Sort(Direction direction, List<String> properties) {

        if (properties == null || properties.isEmpty()) {
            throw new IllegalArgumentException("You have to provide at least one property to sort by!");
        }

        this.orders = new ArrayList<Order>(properties.size());

        for (String property : properties) {
            this.orders.add(new Order(direction, property));
        }
    }

    /**
     * 将给定的 {@link Sort} 中的 {@link Order}s 与当前的 {@link Sort} 结合并返回新的 {@link Sort}
     *
     * @param sort can be {@literal null}.
     * @return {@link Sort}
     */
    public Sort and(Sort sort) {

        if (sort == null) {
            return this;
        }

        ArrayList<Order> these = new ArrayList<Order>(this.orders);

        for (Order order : sort) {
            these.add(order);
        }

        return new Sort(these);
    }

    /**
     * 返回指定属性的排序方向.
     *
     * @param property
     * @return {@link Sort.Order}
     */
    public Order getOrderFor(String property) {

        for (Order order : this) {
            if (order.getProperty().equals(property)) {
                return order;
            }
        }

        return null;
    }

    /**
     * 返回 {@link Sort} 中 {@link Order} 的数目
     *
     * @return {@link Sort}
     */
    public int size() {
        return this.orders.size();
    }

    /**
     * 当排序条件 {@link Order} 列表中不包含元素时返回true
     * @return boolean
     */
    public boolean isEmpty() {
        return this.orders.isEmpty();
    }

    /**
     * 当排序条件 {@link Order} 列表中包含元素时返回true
     * @return boolean
     */
    public boolean isNotEmpty() {
        return !this.orders.isEmpty();
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Iterable#iterator()
     */
    @Override
    public Iterator<Order> iterator() {
        return this.orders.iterator();
    }

    /*
 * (non-Javadoc)
 * @see java.lang.Object#equals(java.lang.Object)
 */
    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Sort)) {
            return false;
        }

        Sort that = (Sort) obj;

        return this.orders.equals(that.orders);
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {

        int result = 17;
        result = 31 * result + orders.hashCode();
        return result;
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return StringUtils.collectionToCommaDelimitedString(orders);
    }

    public static enum Direction {
        asc, desc;

        /**
         * 通过一个给定的 {@link String} 值来返回 {@link Direction} enum.
         * @param value
         * @throws IllegalArgumentException 在给定的String不能解析为Direction时抛出
         * @return {@link Direction}
         */
        public static Direction fromString(String value) {

            try {
                return Direction.valueOf(value.toUpperCase(Locale.US));
            } catch (Exception e) {
                throw new IllegalArgumentException(String.format(
                        "Invalid value '%s' for orders given! Has to be either 'desc' or 'asc' (case insensitive).", value), e);
            }
        }
    }

    public static class Order implements Serializable {
        private static final long serialVersionUID = -4603422140888624728L;
        private final Direction direction;
        private final String property;

        public Order(Direction direction, String property) {
            if (StringUtil.isEmpty(property)) {
                throw new IllegalArgumentException("Property must not null or empty!");
            }
            this.direction = direction;
            this.property = property;
        }

        public Order(String property) {
            this(Sort.DEFAULT_DIRECTION, property);
        }

        /**
         * 返回当前 {@link Order} 的属性所对应的排序方向.
         *
         * @return {@link Direction}
         */
        public Direction getDirection() {
            return direction;
        }

        /**
         * 返回当前 {@link Order} 的属性.
         *
         * @return {@link String}
         */
        public String getProperty() {
            return property;
        }

        /**
         * 返回当前 {@link Order} 的属性所对应的排序方向是否为升序
         *
         * @return 升序返回true
         */
        public boolean isAscending() {
            return this.direction.equals(Direction.asc);
        }

        /*
        * (non-Javadoc)
        * @see java.lang.Object#hashCode()
        */
        @Override
        public int hashCode() {

            int result = 17;

            result = 31 * result + direction.hashCode();
            result = 31 * result + property.hashCode();

            return result;
        }

        /*
         * (non-Javadoc)
         * @see java.lang.Object#equals(java.lang.Object)
         */
        @Override
        public boolean equals(Object obj) {

            if (this == obj) {
                return true;
            }

            if (!(obj instanceof Order)) {
                return false;
            }

            Order that = (Order) obj;

            return this.direction.equals(that.direction) && this.property.equals(that.property);
        }

        /*
         * (non-Javadoc)
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString() {
            return String.format("{%s: %s}", property, direction);
        }

    }
}
 public enum SearchOperator {
    eq("等于", "="), ne("不等于", "!="),
    gt("大于", ">"), gte("大于等于", ">="), lt("小于", "<"), lte("小于等于", "<="),
    prefixLike("前缀模糊匹配", "like"), prefixNotLike("前缀模糊不匹配", "not like"),
    suffixLike("后缀模糊匹配", "like"), suffixNotLike("后缀模糊不匹配", "not like"),
    like("模糊匹配", "like"), notLike("不匹配", "not like"),
    isNull("空", "is null"), isNotNull("非空", "is not null"),
    in("包含", "in"), notIn("不包含", "not in"), custom("自定义默认的", null);

    private final String info;
    private final String symbol;

    SearchOperator(final String info, String symbol) {
        this.info = info;
        this.symbol = symbol;
    }

    public String getInfo() {
        return info;
    }

    public String getSymbol() {
        return symbol;
    }

    public static String toStringAllOperator() {
        return Arrays.toString(SearchOperator.values());
    }

    /**
     * 是否为一元操作
     * 一元操作符只需要一个参数
     * @param operator
     * @return 如果操作符是"is not null"或者"is null"则返回true否则false
     */
    public static boolean isAllowBlankValue(final SearchOperator operator) {
        return operator == SearchOperator.isNotNull || operator == SearchOperator.isNull;
    }

    /**
     * 是否允许多值操作
     * in操作符允许接收多个值
     * @param operator
     * @return 如果操作符是"is not null"或者"is null"则返回true否则false
     */
    public static boolean isAllowMultiValue(final SearchOperator operator) {
        return operator == SearchOperator.in;
    }

    public static SearchOperator valueBySymbol(String symbol) throws SearchException {
        symbol = formatSymbol(symbol);
        for (SearchOperator operator : values()) {
            if (operator.getSymbol().equals(symbol)) {
                return operator;
            }
        }
        throw new SearchException("SearchOperator not match search operator symbol : " + symbol);
    }

    private static String formatSymbol(String symbol) {
        if (StringUtil.isEmpty(symbol)) {
            return symbol;
        }
        return symbol.trim().toLowerCase().replace("  ", " ");
    }
} public enum SearchType {
    OR("OR连接", "or"), AND("AND连接","and");

    private final String info;
    private final String type;

    SearchType(final String info, String type) {
        this.info = info;
        this.type = type;
    }

    public String getInfo() {
        return this.info;
    }

    public String getTypeStr() {
        return this.type;
    }
}
 public interface Searchable {

    /**
     * 添加过滤条件 {@link SearchFilter}
     * 如果添加时不加操作符, 默认操作应该是 eq (=)
     * @param property 需要过滤的属性名
     * @param value 给定的属性值
     * @return 查询条件 {@link Searchable}
     */
    public Searchable addSearchParam(final String property, final Object value);

    /**
     * 添加一组查询参数来添加一组过滤条件 {@link SearchFilter}
     * @param searchParams 查询参数, key是属性名, value是属性值
     * @return 查询条件 {@link Searchable}
     */
    public Searchable addSearchParams(final Map<String, Object> searchParams);

    /**
     * 通过指定的参数添加过滤条件 {@link SearchFilter}
     * @param property 需要过滤的属性名
     * @param operator 操作符
     * @param value 给定的属性值
     * @return 查询条件 {@link Searchable}
     */
    public Searchable addSearchParam(final String property, final SearchOperator operator, final Object value);

    /**
     * 添加过滤条件 {@link SearchFilter}
     * @param filter 过滤条件 {@link SearchFilter}
     * @return 查询条件 {@link Searchable}
     */
    public Searchable addSearchFilter(final SearchFilter filter);

    /**
     * 添加多个and连接的过滤条件 {@link SearchFilter}
     * @param filters 过滤条件 {@link SearchFilter}
     * @return 查询条件 {@link Searchable}
     */
    public Searchable addSearchFilters(final Collection<? extends SearchFilter> filters);

    /**
     * 获取查询过滤条件 {@link SearchFilter}
     * @return 过滤条件 {@link SearchFilter}的集合
     */
    public Collection<SearchFilter> getSearchFilters();

    /**
     * 是否有过滤条件 {@link SearchFilter}
     * @return 当且仅当过滤条件 {@link SearchFilter}个数大于0才返回true
     */
    public boolean hasSearchFilter();

    /**
     * 移除指定属性对应的所有过滤条件 {@link SearchFilter}
     * @param property 目标过滤条件 {@link SearchFilter}的属性名
     * @return 查询条件 {@link Searchable}
     */
    public Searchable removeSearchFilter(final String property);

    /**
     * 移除指定属性和操作符的过滤条件 {@link SearchFilter}
     * @param property 目标过滤条件 {@link SearchFilter}的属性名
     * @param operator 目标过滤条件 {@link SearchFilter}的操作符
     * @return 查询条件 {@link Searchable}
     */
    public Searchable removeSearchFilter(String property, SearchOperator operator);

    /**
     * 获取排序信息
     * @return 排序条件 {@link Sort}
     */
    public Sort getSort();

    /**
     * 通过指定参数添加排序条件 {@link Sort}
     * @param direction 排序方向
     * @param property 排序所根据的属性
     * @return 查询条件 {@link Searchable}
     */
    public Searchable addSort(final Sort.Direction direction, String property);

    /**
     * 是否有排序条件 {@link Sort}
     * @return 当且仅当排序条件 {@link Sort}个数大于0才返回true
     */
    public boolean hashSort();

    /**
     * 移除所有排序条件 {@link Sort}
     */
    public void removeSort();

    /**
     * 通过指定参数添加分页条件 {@link Pageable}
     * @param pageNumber 分页页码 索引从 0 开始
     * @param pageSize 每页大小
     * @return 查询条件 {@link Searchable}
     */
    public Searchable setPage(final int pageNumber, final int pageSize);

    /**
     * 添加分页条件 {@link Pageable}
     * @param page
     * @return 查询条件 {@link Searchable}
     */
    public Searchable setPage(Pageable page);

    /**
     * 获取分页和排序信息
     * @return 分页条件 {@link Pageable}
     */
    public Pageable getPage();

    /**
     * 是否有分页条件 {@link Pageable}
     * @return 有分页条件 {@link Pageable}时返回true
     */
    public boolean hasPageable();

    /**
     * 移除所有分页条件 {@link Pageable}
     */
    public void removePageable();

    /**
     * 获取过滤条件 {@link SearchFilter}中给定属性对应的值
     * @param property 要查找的属性
     * @return 过滤条件 {@link SearchFilter}中给定属性对应的值
     */
    public Object getValue(final String property);
}
 public class StringUtil {
    public static final String CHARSET_UTF8 = "UTF-8";

    /**
     * 将一个数组中的内容连接成一个字符串，各个数组中的元素使用参数delim分隔
     * @param arr
     * @param delim
     * @return
     */
    public static String arrayToString(String[] arr, String delim) {
        if (arr == null)
            return "null";
        else {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < arr.length; i++) {
                if (i > 0)
                    sb.append(delim);
                sb.append(arr[i]);
            }
            return sb.toString();
        }
    }

    /**
     * 本地字符集转换成unicode
     * @param s
     * @return java.lang.String
     */
    public static String native2unicode(String s) {
        if(s==null || s.length()==0) {
            return null;
        }
        byte [] buffer = new byte[s.length()];

        for(int i=0;i<s.length();i++) {
            if( s.charAt(i)>=0x100 ) {
                return s;
            }
            buffer[i] = (byte)s.charAt(i);
        }
        return new String(buffer);
    }

    /**
     * unicode转为本地字符集
     * @param s Unicode编码的字符串
     * @return String
     */
    public static String unicode2native(String s) {
        if(s==null || s.length()==0) {
            return null;
        }
        char [] buffer = new char[s.length() * 2];
        char c;
        int j=0;
        for(int i=0;i<s.length();i++) {
            if(s.charAt(i)>=0x100){
                c = s.charAt(i);
                byte [] buf = (""+c).getBytes();
                buffer[j++] = (char)buf[0];
                buffer[j++] = (char)buf[1];
            } else {
                buffer[j++] = s.charAt(i);
            }
        }
        return new String(buffer,0,j);
    }

    /**
     * 对字符串中制定的字符进行替换
     * @param inString
     * @param oldPattern
     * @param newPattern
     * @return
     */
    public static String replace(String inString, String oldPattern, String newPattern) {
        if (inString == null) {
            return null;
        }
        if (oldPattern == null || newPattern == null) {
            return inString;
        }

        StringBuffer sbuf = new StringBuffer();
        // output StringBuffer we'll build up
        int pos = 0; // Our position in the old string
        int index = inString.indexOf(oldPattern);
        // the index of an occurrence we've found, or -1
        int patLen = oldPattern.length();
        while (index >= 0) {
            sbuf.append(inString.substring(pos, index));
            sbuf.append(newPattern);
            pos = index + patLen;
            index = inString.indexOf(oldPattern, pos);
        }
        sbuf.append(inString.substring(pos));

        // remember to append any characters to the right of a match
        return sbuf.toString();
    }

    /**
     * 替换输入字符串中的HTML字符（如：< > ' \ \r \n）
     * @param old
     * @return
     */
    public static String replaceHtml(String old){
        String rt = replace(old,"&","&amp;");
        rt = replace(rt,"<","&lt;");
        rt = replace(rt,">","&gt;");
        rt = replace(rt,"'","&#39;");
        rt = replace(rt,"\"","&quot;");
        rt = replace(rt,"\r","&#xd;");
        rt = replace(rt,"\n","&#xa;");
        return rt;
    }


    /**
     * sql操作时，单引号(')是一个关键字。如果插入或查询时的字段值里有单引号，
     * 会出错。必须将字段值里的单引号转换成两个单引号。
     * @param old 源字符串
     * @return 转化后的字符串
     */
    public static String replaceSql(String old){
        String rt=replace(old,"'","''");
        return rt;
    }

    /**
     * 将list转换成数组String[]
     * @param list
     * @return
     */
    public static String[] list2StrArray(ArrayList list) {

        String strArray[] = new String[list.size()];
        for (int i = 0; i < list.size(); i++) {
            strArray[i] = (String) list.get(i);
        }
        return strArray;
    }

    /**
     * 将list组装成striing
     * 格式如：('a','b')
     * @param list
     * @return
     */
    public static String getSqlList(String[] list) {
        StringBuffer strBuffer;
        strBuffer = new StringBuffer();
        strBuffer.append("(");
        strBuffer.append("'" + list[0] + "'");
        int i = 1;
        int len = list.length;
        while (i < len) {
            strBuffer.append(",");
            strBuffer.append("'" + list[i++] + "'");
        }// end while
        strBuffer.append(")");
        return strBuffer.toString();

    }

    /**
     * 检查对象是否为数字型字符串。
     */
    public static boolean isNumeric(Object obj) {
        if (obj == null) {
            return false;
        }
        String str = obj.toString();
        int sz = str.length();
        for (int i = 0; i < sz; i++) {
            if (!Character.isDigit(str.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    /**
     * 检查指定的字符串列表是否不为空。
     */
    public static boolean isNotEmpty(String... values) {
        boolean result = true;
        if (values == null || values.length == 0) {
            result = false;
        } else {
            for (String value : values) {
                result &= !isEmpty(value);
            }
        }
        return result;
    }

    /**
     * 把通用字符编码的字符串转化为汉字编码。
     */
    public static String unicodeToChinese(String unicode) {
        StringBuilder out = new StringBuilder();
        if (!isEmpty(unicode)) {
            for (int i = 0; i < unicode.length(); i++) {
                out.append(unicode.charAt(i));
            }
        }
        return out.toString();
    }

    /**
     * 过滤不可见字符
     */
    public static String stripNonValidXMLCharacters(String input) {
        if (input == null || ("".equals(input)))
            return "";
        StringBuilder out = new StringBuilder();
        char current;
        for (int i = 0; i < input.length(); i++) {
            current = input.charAt(i);
            if ((current == 0x9) || (current == 0xA) || (current == 0xD)
                    || ((current >= 0x20) && (current <= 0xD7FF))
                    || ((current >= 0xE000) && (current <= 0xFFFD))
                    || ((current >= 0x10000) && (current <= 0x10FFFF)))
                out.append(current);
        }
        return out.toString();
    }

    /**
     * 检查指定的字符串是否为空。
     * <ul>
     * <li>SysUtils.isEmpty(null) = true</li>
     * <li>SysUtils.isEmpty("") = true</li>
     * <li>SysUtils.isEmpty("   ") = true</li>
     * <li>SysUtils.isEmpty("abc") = false</li>
     * </ul>
     *
     * @param value 待检查的字符串
     * @return true/false
     */
    public static boolean isEmpty(String value) {
        int strLen;
        if (value == null || (strLen = value.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if ((Character.isWhitespace(value.charAt(i)) == false)) {
                return false;
            }
        }
        return true;
    }

    public static String join(Object array[], char separator)
    {
        if(array == null)
            return null;
        else
            return join(array, separator, 0, array.length);
    }

    public static String join(Object array[], char separator, int startIndex, int endIndex)
    {
        if(array == null)
            return null;
        int noOfItems = endIndex - startIndex;
        if(noOfItems <= 0)
            return "";
        StringBuilder buf = new StringBuilder(noOfItems * 16);
        for(int i = startIndex; i < endIndex; i++)
        {
            if(i > startIndex)
                buf.append(separator);
            if(array[i] != null)
                buf.append(array[i]);
        }

        return buf.toString();
    }
} public abstract class BaseController {
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Date.class, new CustomDateEditor());
    }
    protected void writeJSON(HttpServletResponse response, String json) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(json);
    }

    protected void writeJSON(HttpServletResponse response, Object obj) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue));
    }
}

import com.teemo.core.Constants;
import com.teemo.core.util.UserLogUtil;
import com.teemo.dto.BTQueryParameter;
import com.teemo.dto.Page;
import com.teemo.dto.Result;
import com.teemo.entity.Resource;
import com.teemo.entity.ResourceType;
import com.teemo.entity.User;
import com.teemo.service.ResourceService;
import core.support.Condition;
import core.support.PageRequest;
import core.support.SearchRequest;
import core.support.Sort;
import core.support.search.SearchOperator;
import core.support.search.SearchType;
import core.support.search.Searchable;
import core.util.StringUtil;
import core.web.controller.BaseController;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
 * Copyright (c) 2016- https://github.com/beiyoufx
 *
 * Licensed under the GPL-3.0
 */
package com.teemo.web.controller;



/**
 * @author yongjie.teng
 * @date 16-12-14 下午3:57
 * @email yongjie.teng@foxmail.com
 * @package com.teemo.web.controller
 */
@Controller
@RequestMapping("/sys/resource")
public class ResourceController extends BaseController {
    @javax.annotation.Resource
    private ResourceService resourceService;

    @RequiresPermissions(value = "sys:resource:view")
    @RequestMapping(value = "/main", method = RequestMethod.GET)
    public String main(HttpServletResponse response) throws IOException {
        return "admin/resource/main";
    }

    @RequiresPermissions(value = "sys:resource:view")
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public void get(@PathVariable Long id, HttpServletResponse response) throws IOException {
        Resource resource = resourceService.get(id);
        writeJSON(response, resource);
    }

    @RequiresPermissions(value = "sys:resource:view")
    @RequestMapping(value = "/find", method = RequestMethod.GET)
    public void find(HttpServletResponse response, BTQueryParameter queryParameter) throws IOException {
        Page<Resource> page = new Page<Resource>();
        if (queryParameter != null) {

            // 设置分页
            PageRequest pageRequest = PageRequest.newPageRequest(0, 10);
            if (queryParameter.getPageNumber() != null && queryParameter.getPageNumber() >= 0
                    && queryParameter.getPageSize() != null && queryParameter.getPageSize() > 0) {
                pageRequest = PageRequest.newPageRequest(queryParameter.getPageNumber(), queryParameter.getPageSize());
            } else {
                queryParameter.setPageSize(pageRequest.getPageSize());
            }

            // 设置查询条件
            Searchable searchable = SearchRequest.newSearchRequest();
            searchable.setPage(pageRequest);
            if (StringUtil.isNotEmpty(queryParameter.getSearchText())) {
                searchable.addSearchFilter(Condition.newCondition(SearchType.OR, "resourceKey", SearchOperator.like, queryParameter.getSearchText()));