Sfoglia il codice sorgente

shiro搭建以及飞书接口对接

hcst_sunzhen 5 anni fa
parent
commit
acdb0f99c0
35 ha cambiato i file con 3512 aggiunte e 0 eliminazioni
  1. 39 0
      pom.xml
  2. 208 0
      src/main/java/cn/com/ctop/okr/config/ShiroConfig.java
  3. 129 0
      src/main/java/cn/com/ctop/okr/constant/CommonConstant.java
  4. 158 0
      src/main/java/cn/com/ctop/okr/controller/LoginController.java
  5. 19 0
      src/main/java/cn/com/ctop/okr/entity/CorpDepartInfo.java
  6. 42 0
      src/main/java/cn/com/ctop/okr/entity/CorpUserInfo.java
  7. 26 0
      src/main/java/cn/com/ctop/okr/feishu/constant/FeishuConstant.java
  8. 89 0
      src/main/java/cn/com/ctop/okr/feishu/provider/AuthProvider.java
  9. 90 0
      src/main/java/cn/com/ctop/okr/feishu/provider/DepartmentProvider.java
  10. 44 0
      src/main/java/cn/com/ctop/okr/feishu/provider/MessageProvider.java
  11. 90 0
      src/main/java/cn/com/ctop/okr/feishu/provider/SendImage.java
  12. 17 0
      src/main/java/cn/com/ctop/okr/mapper/CorpDepartInfoMapper.java
  13. 11 0
      src/main/java/cn/com/ctop/okr/mapper/CorpUserInfoMapper.java
  14. 17 0
      src/main/java/cn/com/ctop/okr/mapper/xml/CorpUserInfoMapper.xml
  15. 16 0
      src/main/java/cn/com/ctop/okr/mapper/xml/DepartUserInfoMapper.xml
  16. 15 0
      src/main/java/cn/com/ctop/okr/service/CorpUserInfoService.java
  17. 41 0
      src/main/java/cn/com/ctop/okr/service/impl/CorpUserInfoServiceImpl.java
  18. 28 0
      src/main/java/cn/com/ctop/okr/shiro/authc/JwtToken.java
  19. 165 0
      src/main/java/cn/com/ctop/okr/shiro/authc/ShiroRealm.java
  20. 82 0
      src/main/java/cn/com/ctop/okr/shiro/authc/aop/JwtFilter.java
  21. 68 0
      src/main/java/cn/com/ctop/okr/shiro/authc/aop/ResourceCheckFilter.java
  22. 10 0
      src/main/java/cn/com/ctop/okr/shiro/vo/DefContants.java
  23. 28 0
      src/main/java/cn/com/ctop/okr/shiro/vo/ResponseBean.java
  24. 24 0
      src/main/java/cn/com/ctop/okr/shiro/vo/UserBean.java
  25. 66 0
      src/main/java/cn/com/ctop/okr/utils/Check.java
  26. 145 0
      src/main/java/cn/com/ctop/okr/utils/HttpUtils.java
  27. 88 0
      src/main/java/cn/com/ctop/okr/utils/JeecgDataAutorUtils.java
  28. 198 0
      src/main/java/cn/com/ctop/okr/utils/JwtUtil.java
  29. 574 0
      src/main/java/cn/com/ctop/okr/utils/RedisUtil.java
  30. 31 0
      src/main/java/cn/com/ctop/okr/utils/ResultMapUtils.java
  31. 88 0
      src/main/java/cn/com/ctop/okr/utils/SpringContextUtils.java
  32. 114 0
      src/main/java/cn/com/ctop/okr/utils/StatusCode.java
  33. 551 0
      src/main/java/cn/com/ctop/okr/utils/oConvertUtils.java
  34. 98 0
      src/main/java/cn/com/ctop/okr/vo/LoginUser.java
  35. 103 0
      src/main/java/cn/com/ctop/okr/vo/Result.java

+ 39 - 0
pom.xml

@@ -138,6 +138,45 @@
             <artifactId>log4j</artifactId>
             <version>1.2.17</version>
         </dependency>
+        <!--JWT-->
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>3.7.0</version>
+        </dependency>
+        <!--shiro-->
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-spring-boot-starter</artifactId>
+            <version>1.4.0</version>
+        </dependency>
+        <!-- HttpClient -->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.9</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore</artifactId>
+            <version>4.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpmime</artifactId>
+            <version>4.5</version>
+        </dependency>
+        <!--  -->
+        <!-- Redis -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.6</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

+ 208 - 0
src/main/java/cn/com/ctop/okr/config/ShiroConfig.java

@@ -0,0 +1,208 @@
+package cn.com.ctop.okr.config;
+
+import cn.com.ctop.okr.shiro.authc.ShiroRealm;
+import cn.com.ctop.okr.shiro.authc.aop.JwtFilter;
+import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
+import org.apache.shiro.mgt.DefaultSubjectDAO;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.spring.LifecycleBeanPostProcessor;
+import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
+import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.DependsOn;
+
+import javax.servlet.Filter;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @author: Scott
+ * @date: 2018/2/7
+ * @description: shiro 配置类
+ */
+
+@Configuration
+public class ShiroConfig {
+
+    /**
+     * Filter Chain定义说明
+     * <p>
+     * 1、一个URL可以配置多个Filter,使用逗号分隔
+     * 2、当设置多个过滤器时,全部验证通过,才视为通过
+     * 3、部分过滤器可指定参数,如perms,roles
+     */
+    @Bean("shiroFilter")
+    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
+        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
+        shiroFilterFactoryBean.setSecurityManager(securityManager);
+        // 拦截器
+        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
+        // 配置不会被拦截的链接 顺序判断
+        //登录接口排除
+        filterChainDefinitionMap.put("/sys/login", "anon");
+        filterChainDefinitionMap.put("/sys/wxlogin", "anon");
+
+        //获取加密串
+        filterChainDefinitionMap.put("/sys/getEncryptedString", "anon");
+        //短信验证码
+        filterChainDefinitionMap.put("/sys/sms", "anon");
+        //手机登录
+        filterChainDefinitionMap.put("/sys/phoneLogin", "anon");
+        //校验用户是否存在
+        filterChainDefinitionMap.put("/sys/user/checkOnlyUser", "anon");
+        //用户注册
+        filterChainDefinitionMap.put("/sys/user/register", "anon");
+        //根据手机号获取用户信息
+        filterChainDefinitionMap.put("/sys/user/querySysUser", "anon");
+        //用户忘记密码验证手机号
+        filterChainDefinitionMap.put("/sys/user/phoneVerification", "anon");
+        //用户更改密码
+        filterChainDefinitionMap.put("/sys/user/passwordChange", "anon");
+        //登录验证码
+        filterChainDefinitionMap.put("/auth/2step-code", "anon");
+        //图片预览不限制token
+        filterChainDefinitionMap.put("/sys/common/view/**", "anon");
+        //文件下载不限制token
+        filterChainDefinitionMap.put("/sys/common/download/**", "anon");
+        //pdf预览
+        filterChainDefinitionMap.put("/sys/common/pdf/**", "anon");
+        filterChainDefinitionMap.put("/ctop/bytedanceAreaInfo/**", "anon");
+        filterChainDefinitionMap.put("/ctop/bytedanceInterestCategory/**", "anon");
+        filterChainDefinitionMap.put("/template/creative/words/get", "anon");
+        filterChainDefinitionMap.put("/appium/crawler/*", "anon");
+
+        filterChainDefinitionMap.put("/ctop/kuaishouChannelRegInfo/*", "anon");
+
+
+        filterChainDefinitionMap.put("/ctop/byteDance/homePage/report/*", "anon");
+        filterChainDefinitionMap.put("/ctop/material/top/cost", "anon");
+
+        filterChainDefinitionMap.put("/v1/**", "anon");
+
+        filterChainDefinitionMap.put("/generic/**", "anon");
+        filterChainDefinitionMap.put("/", "anon");
+        filterChainDefinitionMap.put("/doc.html", "anon");
+        filterChainDefinitionMap.put("/**/*.js", "anon");
+        filterChainDefinitionMap.put("/**/*.css", "anon");
+        filterChainDefinitionMap.put("/**/*.html", "anon");
+        filterChainDefinitionMap.put("/**/*.svg", "anon");
+        filterChainDefinitionMap.put("/**/*.jpg", "anon");
+        filterChainDefinitionMap.put("/**/*.png", "anon");
+        filterChainDefinitionMap.put("/**/*.ico", "anon");
+        filterChainDefinitionMap.put("/druid/**", "anon");
+        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
+        filterChainDefinitionMap.put("/swagger**/**", "anon");
+        filterChainDefinitionMap.put("/webjars/**", "anon");
+        filterChainDefinitionMap.put("/v2/**", "anon");
+        filterChainDefinitionMap.put("/kuaishou", "anon");
+        filterChainDefinitionMap.put("/kuaishou/report", "anon");
+        filterChainDefinitionMap.put("/ctop/bindAccount/**", "anon");
+        filterChainDefinitionMap.put("/**/*.mp4", "anon");
+
+        //性能监控
+        filterChainDefinitionMap.put("/actuator/metrics/**", "anon");
+        filterChainDefinitionMap.put("/actuator/httptrace/**", "anon");
+        filterChainDefinitionMap.put("/actuator/redis/**", "anon");
+        //消息队列
+        filterChainDefinitionMap.put("/mq/**", "anon");
+        //模板测试
+        filterChainDefinitionMap.put("/test/**", "anon");
+        //oauth接口
+        filterChainDefinitionMap.put("/auth/**", "anon");
+        filterChainDefinitionMap.put("/ctop/syncdata/**", "anon");
+        //头条接口
+        filterChainDefinitionMap.put("/bytedance", "anon");
+        filterChainDefinitionMap.put("/toutiao/dictitem/list", "anon");
+        filterChainDefinitionMap.put("/toutiao/loadAllData", "anon");
+        filterChainDefinitionMap.put("/toutiao/loadSingleData", "anon");
+        filterChainDefinitionMap.put("/kuaishou/getReport/historyData", "anon");
+        filterChainDefinitionMap.put("/toutiao/advertiser/**", "anon");
+        filterChainDefinitionMap.put("/toutiao/industry/list", "anon");
+        filterChainDefinitionMap.put("/template/bytedance/industry/list", "anon");
+        filterChainDefinitionMap.put("/template/convert/list", "anon");
+        filterChainDefinitionMap.put("/template/convert/list/url", "anon");
+        //文件上传接口
+        filterChainDefinitionMap.put("/upload/**", "anon");
+        filterChainDefinitionMap.put("/appium/appiumTask/run", "anon");
+
+        filterChainDefinitionMap.put("/system/userCompany/**", "anon");
+        filterChainDefinitionMap.put("/qywexin", "anon");
+        filterChainDefinitionMap.put("/sys/feishu/**", "anon");
+        filterChainDefinitionMap.put("/test/getGroupData/**", "anon");
+        filterChainDefinitionMap.put("/test/getVideoAndImage/**", "anon");
+        filterChainDefinitionMap.put("/test/gerCreative/**", "anon");
+
+        filterChainDefinitionMap.put("/report/kuaishouReportDailyAgent/get", "anon");
+
+        filterChainDefinitionMap.put("/appium/appiumTask/run/login", "anon");
+        filterChainDefinitionMap.put("/zip/download", "anon");
+        //爬虫接口
+        filterChainDefinitionMap.put("/graphql/video", "anon");
+        filterChainDefinitionMap.put("/ks/web/test", "anon");
+        filterChainDefinitionMap.put("/ureport/**", "anon");
+        //filterChainDefinitionMap.put("/ctop/performance/show/**", "anon");
+        filterChainDefinitionMap.put("/job/test/*", "anon");
+
+        // 添加自己的过滤器并且取名为jwt
+        Map<String, Filter> filterMap = new HashMap<>(1);
+        filterMap.put("jwt", new JwtFilter());
+        shiroFilterFactoryBean.setFilters(filterMap);
+        // <!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边
+        filterChainDefinitionMap.put("/**", "jwt");
+
+        // 未授权界面返回JSON
+        shiroFilterFactoryBean.setUnauthorizedUrl("/sys/common/403");
+        shiroFilterFactoryBean.setLoginUrl("/sys/common/403");
+        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
+        return shiroFilterFactoryBean;
+    }
+
+    @Bean("securityManager")
+    public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) {
+        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
+        securityManager.setRealm(myRealm);
+
+        /*
+         * 关闭shiro自带的session,详情见文档
+         * http://shiro.apache.org/session-management.html#SessionManagement-
+         * StatelessApplications%28Sessionless%29
+         */
+        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
+        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
+        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
+        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
+        securityManager.setSubjectDAO(subjectDAO);
+
+        return securityManager;
+    }
+
+    /**
+     * 下面的代码是添加注解支持
+     *
+     * @return
+     */
+    @Bean
+    @DependsOn("lifecycleBeanPostProcessor")
+    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
+        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
+        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
+        return defaultAdvisorAutoProxyCreator;
+    }
+
+    @Bean
+    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
+        return new LifecycleBeanPostProcessor();
+    }
+
+    @Bean
+    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
+        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
+        advisor.setSecurityManager(securityManager);
+        return advisor;
+    }
+
+}

+ 129 - 0
src/main/java/cn/com/ctop/okr/constant/CommonConstant.java

@@ -0,0 +1,129 @@
+package cn.com.ctop.okr.constant;
+
+public interface CommonConstant {
+
+	/**
+	 * 正常状态
+	 */
+
+	public static final Integer STATUS_NORMAL = 0;
+
+
+	/**
+	 * 禁用状态
+	 */
+
+	public static final Integer STATUS_DISABLE = -1;
+
+
+	/**
+	 * 删除标志
+	 */
+	public static final Integer DEL_FLAG_1 = 1;
+
+	/**
+	 * 未删除
+	 */
+
+	public static final Integer DEL_FLAG_0 = 0;
+
+	/**
+	 * 系统日志类型: 登录
+	 */
+	public static final int LOG_TYPE_1 = 1;
+
+	/**
+	 * 系统日志类型: 操作
+	 */
+	public static final int LOG_TYPE_2 = 2;
+
+
+	/** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */
+    public static final Integer SC_INTERNAL_SERVER_ERROR_500 = 500;
+    /** {@code 200 OK} (HTTP/1.0 - RFC 1945) */
+    public static final Integer SC_OK_200 = 200;
+
+    /**访问权限认证未通过 510*/
+    public static final Integer SC_JEECG_NO_AUTHZ=510;
+
+    /** 登录用户拥有角色缓存KEY前缀 */
+    public static String LOGIN_USER_CACHERULES_ROLE = "loginUser_cacheRules::Roles_";
+    /** 登录用户拥有权限缓存KEY前缀 */
+    public static String LOGIN_USER_CACHERULES_PERMISSION  = "loginUser_cacheRules::Permissions_";
+    /** 登录用户令牌缓存KEY前缀 */
+
+    public static final int TOKEN_EXPIRE_TIME = 3600;
+
+    public static final String PREFIX_USER_TOKEN  = "PREFIX_USER_TOKEN_";
+
+    /**
+     *  0:一级菜单
+     */
+    public static final Integer MENU_TYPE_0  = 0;
+   /**
+    *  1:子菜单
+    */
+    public static final Integer MENU_TYPE_1  = 1;
+    /**
+     *  2:按钮权限
+     */
+    public static final Integer MENU_TYPE_2  = 2;
+
+    /**通告对象类型(USER:指定用户,ALL:全体用户)*/
+    public static final String MSG_TYPE_UESR  = "USER";
+    public static final String MSG_TYPE_ALL  = "ALL";
+
+    /**发布状态(0未发布,1已发布,2已撤销)*/
+    public static final String NO_SEND  = "0";
+    public static final String HAS_SEND  = "1";
+    public static final String HAS_CANCLE  = "2";
+
+    /**阅读状态(0未读,1已读)*/
+    public static final String HAS_READ_FLAG  = "1";
+    public static final String NO_READ_FLAG  = "0";
+
+    /**优先级(L低,M中,H高)*/
+    public static final String PRIORITY_L  = "L";
+    public static final String PRIORITY_M  = "M";
+    public static final String PRIORITY_H  = "H";
+
+    /**
+     * 短信模板方式  0 .登录模板、1.注册模板、2.忘记密码模板
+     */
+    public static final String SMS_TPL_TYPE_0  = "0";
+    public static final String SMS_TPL_TYPE_1  = "1";
+    public static final String SMS_TPL_TYPE_2  = "2";
+
+    /**
+     * 状态(0无效1有效)
+     */
+    public static final String STATUS_0 = "0";
+    public static final String STATUS_1 = "1";
+
+    /**
+     * 同步工作流引擎1同步0不同步
+     */
+    public static final String ACT_SYNC_0 = "0";
+    public static final String ACT_SYNC_1 = "1";
+
+    /**
+     * 消息类型1:通知公告2:系统消息
+     */
+    public static final String MSG_CATEGORY_1 = "1";
+    public static final String MSG_CATEGORY_2 = "2";
+
+    /**
+     * 是否配置菜单的数据权限 1是0否
+     */
+    public static final Integer RULE_FLAG_0 = 0;
+    public static final Integer RULE_FLAG_1 = 1;
+
+    /**
+     * 是否用户已被冻结 1(解冻)正常 2冻结
+     */
+    public static final Integer USER_UNFREEZE = 1;
+    public static final Integer USER_FREEZE = 2;
+
+    /**字典翻译文本后缀*/
+    public static final String DICT_TEXT_SUFFIX = "_dictText";
+}

+ 158 - 0
src/main/java/cn/com/ctop/okr/controller/LoginController.java

@@ -0,0 +1,158 @@
+package cn.com.ctop.okr.controller;
+
+import cn.com.ctop.okr.constant.CommonConstant;
+import cn.com.ctop.okr.entity.CorpDepartInfo;
+import cn.com.ctop.okr.entity.CorpUserInfo;
+import cn.com.ctop.okr.feishu.provider.AuthProvider;
+import cn.com.ctop.okr.service.CorpUserInfoService;
+import cn.com.ctop.okr.utils.JwtUtil;
+import cn.com.ctop.okr.utils.RedisUtil;
+import cn.com.ctop.okr.utils.ResultMapUtils;
+import cn.com.ctop.okr.utils.StatusCode;
+import cn.com.ctop.okr.vo.Result;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Controller
+@RequestMapping("/")
+public class LoginController {
+    private static Logger logger = Logger.getLogger(LoginController.class);
+    @Autowired
+    private RedisUtil redisUtil;
+    @Autowired
+    private CorpUserInfoService corpUserInfoService;
+
+    @GetMapping("feishu/url")
+    public Map<String,Object> getUrl(){
+        Map<String,Object>result = new HashMap<>();
+        try {
+            String url = AuthProvider.getAuthUrl("");
+            result.put("url",url);
+            ResultMapUtils.setResultMap(result, StatusCode.COMMON_SUCCESS);
+            return result;
+        } catch (Exception e) {
+            e.printStackTrace();
+            ResultMapUtils.setResultMap(result, StatusCode.COMMON_SERVER_ERROR);
+            return result;
+        }
+    }
+    @GetMapping("/feishu/auth")
+    public Result<JSONObject> feishuAuth(HttpServletRequest request,
+                                         HttpServletResponse response,
+                                         @RequestParam(name = "code",defaultValue = "code") String code,
+                                         @RequestParam(name = "state",defaultValue = "_t") String state) throws IOException {
+        Result<JSONObject> result = new Result<>();
+        //1:根据code值获取用户飞书信息
+        try {
+            if(code.trim().equals("code")){
+                JSONObject setData = new JSONObject();
+                String url = AuthProvider.getAuthUrl("");
+                setData.put("url",url);
+                setData.put("type",1);
+                result.setResult(setData);
+                result.success("重定向");
+                return result;
+            }
+            JSONObject data = AuthProvider.getFeishuUserInfo(code);
+            String accessToken = data.getJSONObject("data").getString("access_token");
+            String openId = data.getJSONObject("data").getString("open_id");
+            //根据accessToken
+            JSONObject userData = AuthProvider.getUserInfo(accessToken);
+            JSONObject dataObject = userData.getJSONObject("data");
+            String email = dataObject.getString("email");
+            if(null == email||email.trim().equals("")){
+                return result.error500(StatusCode.CTOP_FEISHU_EMAIL_NOT_EXIST.getDesc());
+            }
+
+            Integer status = dataObject.getInteger("status");
+            if (status == 1) {
+                result.error500("该用户处于非正常状态!");
+                return result;
+            }
+
+            CorpUserInfo userInfo = corpUserInfoService.getUserInfoByOpenId(openId);
+
+            //用户登录信息
+            Result<JSONObject> getResult = userInfo(userInfo, null);
+            return getResult;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return result.error500(StatusCode.COMMON_SERVER_ERROR.getDesc());
+        }
+    }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    /**
+     * 用户信息
+     *
+     * @param user
+     * @param result
+     * @return
+     */
+    private Result<JSONObject> userInfo(CorpUserInfo user, Result<JSONObject> result) {
+        String name = user.getOpenId();
+        String email = user.getEmail();
+        // 生成token
+        String token = JwtUtil.sign(name, email);
+        redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
+        // 设置超时时间
+        redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);
+
+        // 获取用户部门信息
+        JSONObject obj = new JSONObject();
+        List<CorpDepartInfo> departs = corpUserInfoService.queryUserDeparts(user.getOpenId());
+        obj.put("departs", departs);
+        if (departs == null || departs.size() == 0) {
+            obj.put("multi_depart", 0);
+        } else if (departs.size() == 1) {
+            //sysUserService.updateUserDepart(username, departs.get(0).getOrgCode());
+            obj.put("multi_depart", 1);
+        } else {
+            obj.put("multi_depart", 2);
+        }
+        obj.put("token", token);
+        obj.put("userInfo", user);
+        result.setResult(obj);
+        result.success("登录成功");
+        return result;
+    }
+
+
+
+
+
+
+}

+ 19 - 0
src/main/java/cn/com/ctop/okr/entity/CorpDepartInfo.java

@@ -0,0 +1,19 @@
+package cn.com.ctop.okr.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+@Data
+@TableName("feishu_corp_depart_info")
+public class CorpDepartInfo {
+    @TableId(value = "id",type = IdType.AUTO)
+    private Long id;
+    private String department_id;
+    private String department_name;
+    private String open_department_id;
+    private String parent_id;
+    private String parent_open_department_id;
+
+}

+ 42 - 0
src/main/java/cn/com/ctop/okr/entity/CorpUserInfo.java

@@ -0,0 +1,42 @@
+package cn.com.ctop.okr.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@TableName("feishu_corp_user_info")
+public class CorpUserInfo {
+    @TableId(value = "id",type = IdType.AUTO)
+    private Long id;
+    private String openId;
+    private String name;
+    private String name_py;
+    private String email;
+    private String mobile;
+    private Integer status;
+    private String employeeId;
+    private String employeeNo;
+    private Integer employeeType;
+    private String union_id;
+    private Integer isTenantManager;
+    private String enName;
+    private Integer gender;
+    private String avatarUrl;
+    private String avatar72;
+    private String avatar240;
+    private String avatar640;
+    private String country;
+    private String city;
+    private String workStation;
+    private String leaderEmployeeId;
+    private String leaderOpenId;
+    private String leaderUnionId;
+    private Date userUpdateTime;
+    private Date createTime;
+    private Date updateTime;
+
+}

+ 26 - 0
src/main/java/cn/com/ctop/okr/feishu/constant/FeishuConstant.java

@@ -0,0 +1,26 @@
+package cn.com.ctop.okr.feishu.constant;
+
+
+public class FeishuConstant {
+    public static final String FEISHU_API_URL = "https://open.feishu.cn/open-apis";
+    public static final String FEISHU_APP_ID = "cli_9e68af69ca34d00e";
+    public static final String FEISHU_APP_SECRET = "0RsCBRCLx1rG8HKfeZNeAbddxZ3Rsnwv";
+    public static final String APP_ACCESS_TOKEN = "/auth/v3/app_access_token/internal/";
+    public static final String TENANT_ACCESS_TOKEN = "/auth/v3/tenant_access_token/internal/";
+    public static final String AUTH_INDEX = "/authen/v1/index";
+    public static final String AUTH_ACCESS_TOKEN = "/authen/v1/access_token";
+    public static final String REFRESH_AUTH_ACCESS_TOKEN = "/authen/v1/refresh_access_token";
+    public static final String USER_INFO = "/authen/v1/user_info";
+    public static final String MESSAGE_BATCH_SEND = "/message/v4/batch_send/";
+    public static final String MESSAGE_SEND = "/message/v4/send/";
+    public static final String NOTIFY_SEND = "/notify/v4/appnotify";
+    public static final String DEPARTMENT_INFO= "/contact/v1/department/info/get";  //获取部门详情
+    public static final String DEPARTMENT_SIMPLE_LIST= "/contact/v1/department/simple/list";  //获取子部门列
+    public static final String DEPARTMENT_USER_DETAIL_LIST= "/contact/v1/department/user/detail/list";  //获取部门用户详情信息
+
+
+
+    public static final String REDIRECT_URL = "http://adsp.tjyourong.com.cn/user/FStransfer";
+
+//    public static final String REDIRECT_URL = "http://192.168.0.111:8088/jeecg-boot/sys/feishu/auth";
+}

+ 89 - 0
src/main/java/cn/com/ctop/okr/feishu/provider/AuthProvider.java

@@ -0,0 +1,89 @@
+package cn.com.ctop.okr.feishu.provider;
+
+import cn.com.ctop.okr.feishu.constant.FeishuConstant;
+import cn.com.ctop.okr.utils.HttpUtils;
+import com.alibaba.fastjson.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URLEncoder;
+
+public class AuthProvider {
+    /**
+     *  获取授权url
+     * @param state
+     * @return
+     * @throws UnsupportedEncodingException
+     * @throws MalformedURLException
+     */
+    public static String getAuthUrl(String state) throws UnsupportedEncodingException {
+        String redirectUrl = FeishuConstant.REDIRECT_URL;
+        return FeishuConstant.FEISHU_API_URL+FeishuConstant.AUTH_INDEX+"?redirect_uri="+ URLEncoder.encode(redirectUrl,"UTF-8")+"&app_id="+FeishuConstant.FEISHU_APP_ID+"&state="+state;
+    }
+
+    /**
+     * 根据code获取用户登录信息
+     * @param code
+     * @return
+     * @throws Exception
+     */
+    public static JSONObject getFeishuUserInfo(String code) throws Exception {
+        JSONObject params = new JSONObject();
+        params.put("app_access_token",getAppAccessToken());
+        params.put("grant_type","authorization_code");
+        params.put("code",code);
+        return HttpUtils.feishuPostRequest(FeishuConstant.FEISHU_API_URL+FeishuConstant.AUTH_ACCESS_TOKEN,params);
+    }
+
+    public static JSONObject refreshAccessToken(String refreshToken) throws Exception{
+        JSONObject params = new JSONObject();
+        params.put("app_access_token",getAppAccessToken());
+        params.put("grant_type","refresh_token");
+        params.put("refresh_token",refreshToken);
+        return HttpUtils.feishuPostRequest(FeishuConstant.FEISHU_API_URL+FeishuConstant.REFRESH_AUTH_ACCESS_TOKEN,params);
+    }
+
+    public static JSONObject getUserInfo(String accessToken){
+        JSONObject params = new JSONObject();
+        params.put("user_access_token",accessToken);
+        return HttpUtils.feishuGetRequest("Bearer "+accessToken,FeishuConstant.FEISHU_API_URL+FeishuConstant.USER_INFO,params);
+    }
+
+    /**
+     * 获取企业自建应用token信息
+     * 调用接口获取应用资源时使用
+     * @return
+     * @throws Exception
+     */
+    public static String getAppAccessToken() throws Exception {
+        JSONObject params = new JSONObject();
+        params.put("app_id",FeishuConstant.FEISHU_APP_ID);
+        params.put("app_secret",FeishuConstant.FEISHU_APP_SECRET);
+        JSONObject resultNode = HttpUtils.feishuPostRequest(FeishuConstant.FEISHU_API_URL+FeishuConstant.APP_ACCESS_TOKEN,params);
+        if (resultNode.getIntValue("code")==0){
+            return resultNode.getString("app_access_token");
+        }else {
+            throw new Exception();
+        }
+    }
+
+    /**
+     * 获取企业自建应用租户token
+     * 调用接口获取企业资源时使用
+     * @return
+     * @throws Exception
+     */
+    public static String getTenantAccessToken() throws Exception {
+        JSONObject params = new JSONObject();
+        params.put("app_id",FeishuConstant.FEISHU_APP_ID);
+        params.put("app_secret",FeishuConstant.FEISHU_APP_SECRET);
+        JSONObject resultNode = HttpUtils.feishuPostRequest(FeishuConstant.FEISHU_API_URL+FeishuConstant.TENANT_ACCESS_TOKEN,params);
+        if (resultNode.getIntValue("code")==0){
+            return resultNode.getString("tenant_access_token");
+        }else {
+            throw new Exception();
+        }
+    }
+
+
+}

+ 90 - 0
src/main/java/cn/com/ctop/okr/feishu/provider/DepartmentProvider.java

@@ -0,0 +1,90 @@
+package cn.com.ctop.okr.feishu.provider;
+
+import cn.com.ctop.okr.feishu.constant.FeishuConstant;
+import cn.com.ctop.okr.utils.HttpUtils;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.lang.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class DepartmentProvider {
+
+    /**
+     * 获取子部门列表(遍历获取所有部门)
+     * @param page_token
+     * @return
+     * @throws Exception
+     */
+    public static void getDepartSimpleList(String department_id, String page_token) throws Exception {
+        Map<String,String> headers = new HashMap<>();
+        TreeMap<String,Object> params = new TreeMap<>();
+        headers.put("Authorization","Bearer "+ AuthProvider.getAppAccessToken());
+        params.put("department_id", department_id);
+        if(StringUtils.isNotBlank(page_token)){
+            params.put("page_token",page_token);
+        }
+        params.put("page_size",100);
+        params.put("fetch_child",true);
+
+        String resultStr = HttpUtils.httpGetRequest(FeishuConstant.FEISHU_API_URL+FeishuConstant.DEPARTMENT_SIMPLE_LIST,headers,params);
+        JSONObject resultJson = JSONObject.parseObject(resultStr);
+        System.out.println(resultJson);
+
+        Boolean hasMore = resultJson.getJSONObject("data").getBoolean("has_more");
+        if(hasMore == true){
+            String pageToken = resultJson.getString("page_token");
+            getDepartSimpleList(department_id, pageToken);
+        }
+    }
+
+    public static void getDepartmentUserDetailList(String department_id, String page_token) throws Exception {
+        Map<String,String> headers = new HashMap<>();
+        TreeMap<String,Object> params = new TreeMap<>();
+        headers.put("Authorization","Bearer "+ AuthProvider.getAppAccessToken());
+        params.put("department_id", department_id);
+        if(StringUtils.isNotBlank(page_token)){
+            params.put("page_token",page_token);
+        }
+        params.put("page_size",100);
+        params.put("fetch_child",true);
+
+        String resultStr = HttpUtils.httpGetRequest(FeishuConstant.FEISHU_API_URL+FeishuConstant.DEPARTMENT_USER_DETAIL_LIST,headers,params);
+        JSONObject resultJson = JSONObject.parseObject(resultStr);
+        System.out.println(resultJson);
+
+        Boolean hasMore = resultJson.getJSONObject("data").getBoolean("has_more");
+        if(hasMore == true){
+            String pageToken = resultJson.getString("page_token");
+            getDepartmentUserDetailList(department_id, pageToken);
+        }
+    }
+
+
+
+    public static void main(String[] args) throws Exception {
+        //getDepartSimpleList("0",null);
+        getDepartmentUserDetailList("od-ed707e2994b4251d5edc20bb35f0ced9", null);
+    }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}

+ 44 - 0
src/main/java/cn/com/ctop/okr/feishu/provider/MessageProvider.java

@@ -0,0 +1,44 @@
+package cn.com.ctop.okr.feishu.provider;
+
+import cn.com.ctop.okr.feishu.constant.FeishuConstant;
+import cn.com.ctop.okr.utils.HttpUtils;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+public class MessageProvider {
+    public static JSONObject batchSend(JSONArray departmentIds, JSONArray openIds, JSONArray userIds, String content) throws Exception {
+        String tenantAccessToken = AuthProvider.getTenantAccessToken();
+        JSONObject contentJson = new JSONObject();
+        contentJson.put("text",content);
+        JSONObject params = new JSONObject();
+        if(null!=departmentIds&&!departmentIds.isEmpty()){
+            params.put("department_ids",departmentIds);
+        }
+        if(null!=openIds&&!openIds.isEmpty()){
+            params.put("open_ids",openIds);
+        }
+        if(null!=userIds&&!userIds.isEmpty()){
+            params.put("user_ids",userIds);
+        }
+        params.put("msg_type","text");
+        params.put("content",contentJson);
+        return HttpUtils.feishuPostRequest("Bearer "+tenantAccessToken, FeishuConstant.FEISHU_API_URL+FeishuConstant.MESSAGE_BATCH_SEND,params);
+    }
+
+    public static void main(String[] args) {
+        JSONArray openIds = new JSONArray();
+        openIds.add("ou_e18db8d339cc33fe1446ff7bd774c074");
+        openIds.add("ou_08ec7422306fa2bfd1baae9b2f724cb4");
+        try {
+            StringBuffer text = new StringBuffer();
+            text.append("项目出价预警").append("\n\r").append("您的项目:").append( "测试项目,").append("")
+                    .append("授权名称为:伊对 下的,").append("").append("")
+                    .append("大于项目设置的最高出价:").append(10000 / 1000).append(" 元! 请您及时调整。");
+            batchSend(null,openIds,null,text.toString());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+}

+ 90 - 0
src/main/java/cn/com/ctop/okr/feishu/provider/SendImage.java

@@ -0,0 +1,90 @@
+package cn.com.ctop.okr.feishu.provider;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.util.EntityUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+
+
+public class SendImage {
+    public static CloseableHttpClient getHttpClient() {
+        try {
+            SSLContextBuilder builder = new SSLContextBuilder();
+            builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
+            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(builder.build(),
+                    NoopHostnameVerifier.INSTANCE);
+            Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
+                    .register("http", new PlainConnectionSocketFactory())
+                    .register("https", sslConnectionSocketFactory)
+                    .build();
+            PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
+            cm.setMaxTotal(100);
+            CloseableHttpClient httpclient = HttpClients.custom()
+                    .setSSLSocketFactory(sslConnectionSocketFactory)
+                    .setDefaultCookieStore(new BasicCookieStore())
+                    .setConnectionManager(cm).build();
+            return httpclient;
+        } catch (KeyManagementException e) {
+            e.printStackTrace();
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        } catch (KeyStoreException e) {
+            e.printStackTrace();
+        }
+        return HttpClients.createDefault();
+    }
+    public static String SendImageByApacheHttpClient(File file,String tenantAccessToken) throws IOException {
+        CloseableHttpClient client = getHttpClient();
+        HttpPost post = new HttpPost("https://open.feishu.cn/open-apis/image/v4/put/");
+        final MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+        FileBody bin = new FileBody(file);
+        builder.addPart("image", bin);
+        builder.addTextBody("image_type", "message");
+        HttpEntity multiPartEntity = builder.build();
+        post.setEntity(multiPartEntity);
+        post.setHeader("Authorization", "Bearer "+tenantAccessToken);
+        CloseableHttpResponse response = client.execute(post);
+        System.out.println("http response code:" + response.getStatusLine().getStatusCode());
+        for (Header header: response.getAllHeaders()) {
+            System.out.println(header.toString());
+        }
+        HttpEntity resEntity = response.getEntity();
+        if (resEntity == null) {
+            System.out.println("never here?");
+            return "";
+        }
+        System.out.println("Response content length: " + resEntity.getContentLength());
+        return EntityUtils.toString(resEntity);
+    }
+    public static void main(String[] args) throws Exception {
+        File file = new File("D:\\android\\15812888.jpeg");
+        String token = AuthProvider.getTenantAccessToken();
+        String result = SendImageByApacheHttpClient(file,token);
+        System.out.println(result);
+        /**
+         * {"code":0,"msg":"ok","data":{"image_key":"img_f8bb4cdf-fd0b-4c29-98c5-5147efd4b13g"}}
+         */
+    }
+
+}

+ 17 - 0
src/main/java/cn/com/ctop/okr/mapper/CorpDepartInfoMapper.java

@@ -0,0 +1,17 @@
+package cn.com.ctop.okr.mapper;
+
+import cn.com.ctop.okr.entity.CorpDepartInfo;
+import cn.com.ctop.okr.entity.CorpUserInfo;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface CorpDepartInfoMapper extends BaseMapper<CorpDepartInfo> {
+
+
+    List<CorpDepartInfo> queryUserDeparts(@Param("openId")String openId);
+
+
+
+}

+ 11 - 0
src/main/java/cn/com/ctop/okr/mapper/CorpUserInfoMapper.java

@@ -0,0 +1,11 @@
+package cn.com.ctop.okr.mapper;
+
+import cn.com.ctop.okr.entity.CorpUserInfo;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+public interface CorpUserInfoMapper extends BaseMapper<CorpUserInfo> {
+
+
+    CorpUserInfo getUserInfoByOpenId(@Param("openId")String openId);
+}

+ 17 - 0
src/main/java/cn/com/ctop/okr/mapper/xml/CorpUserInfoMapper.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.com.ctop.okr.mapper.CorpUserInfoMapper">
+
+    <select id="getUserInfoByOpenId" resultType="cn.com.ctop.okr.entity.CorpUserInfo">
+        select
+        *
+        from
+        feishu_corp_user_info
+        where
+        open_id = #{openId}
+
+    </select>
+
+
+
+</mapper>

+ 16 - 0
src/main/java/cn/com/ctop/okr/mapper/xml/DepartUserInfoMapper.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.com.ctop.okr.mapper.CorpDepartInfoMapper">
+
+    <select id="queryUserDeparts" resultType="cn.com.ctop.okr.entity.CorpDepartInfo">
+        select
+        b.*
+        from
+        feishu_corp_user_depart_map a
+        left join
+        feishu_corp_depart_info b on a.department_id = b.department_id
+        where
+        open_id = #{openId}
+    </select>
+
+</mapper>

+ 15 - 0
src/main/java/cn/com/ctop/okr/service/CorpUserInfoService.java

@@ -0,0 +1,15 @@
+package cn.com.ctop.okr.service;
+
+import cn.com.ctop.okr.entity.CorpDepartInfo;
+import cn.com.ctop.okr.entity.CorpUserInfo;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.util.List;
+
+public interface CorpUserInfoService extends IService<CorpUserInfo> {
+
+    List<CorpDepartInfo> queryUserDeparts(String openId);
+
+    CorpUserInfo getUserInfoByOpenId(String openId);
+
+}

+ 41 - 0
src/main/java/cn/com/ctop/okr/service/impl/CorpUserInfoServiceImpl.java

@@ -0,0 +1,41 @@
+package cn.com.ctop.okr.service.impl;
+
+import cn.com.ctop.okr.entity.CorpDepartInfo;
+import cn.com.ctop.okr.entity.CorpUserInfo;
+import cn.com.ctop.okr.mapper.CorpDepartInfoMapper;
+import cn.com.ctop.okr.mapper.CorpUserInfoMapper;
+import cn.com.ctop.okr.service.CorpUserInfoService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class CorpUserInfoServiceImpl extends ServiceImpl<CorpUserInfoMapper, CorpUserInfo> implements CorpUserInfoService {
+
+    @Autowired
+    private CorpDepartInfoMapper corpDepartInfoMapper;
+    @Autowired
+    private CorpUserInfoMapper corpUserInfoMapper;
+
+    @Override
+    public List<CorpDepartInfo> queryUserDeparts(String openId){
+        return corpDepartInfoMapper.queryUserDeparts(openId);
+    }
+
+    public CorpUserInfo saveUserInfo(String openId){
+
+
+
+        return null;
+    }
+
+    public CorpUserInfo getUserInfoByOpenId(String openId){
+        return corpUserInfoMapper.getUserInfoByOpenId(openId);
+    }
+
+
+
+}

+ 28 - 0
src/main/java/cn/com/ctop/okr/shiro/authc/JwtToken.java

@@ -0,0 +1,28 @@
+package cn.com.ctop.okr.shiro.authc;
+
+import org.apache.shiro.authc.AuthenticationToken;
+
+/**
+ * @Author Scott
+ * @create 2018-07-12 15:19
+ * @desc
+ **/
+public class JwtToken implements AuthenticationToken {
+
+	private static final long serialVersionUID = 1L;
+	private String token;
+
+    public JwtToken(String token) {
+        this.token = token;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return token;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return token;
+    }
+}

+ 165 - 0
src/main/java/cn/com/ctop/okr/shiro/authc/ShiroRealm.java

@@ -0,0 +1,165 @@
+package cn.com.ctop.okr.shiro.authc;
+
+import cn.com.ctop.okr.constant.CommonConstant;
+import cn.com.ctop.okr.entity.CorpUserInfo;
+import cn.com.ctop.okr.mapper.CorpUserInfoMapper;
+import cn.com.ctop.okr.utils.Check;
+import cn.com.ctop.okr.utils.JwtUtil;
+import cn.com.ctop.okr.utils.RedisUtil;
+import cn.com.ctop.okr.utils.oConvertUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Description: 用户登录鉴权和获取用户授权
+ * @Author: Scott
+ * @Date: 2019-4-23 8:13
+ * @Version: 1.1
+ */
+@Component
+@Slf4j
+public class ShiroRealm extends AuthorizingRealm {
+
+    @Autowired
+    @Lazy
+    private CorpUserInfoMapper corpUserInfoMapper;
+    @Autowired
+    @Lazy
+    private RedisUtil redisUtil;
+
+    /**
+     * 必须重写此方法,不然Shiro会报错
+     */
+    @Override
+    public boolean supports(AuthenticationToken token) {
+        return token instanceof JwtToken;
+    }
+
+    /**
+     * 功能: 获取用户权限信息,包括角色以及权限。只有当触发检测用户权限时才会调用此方法,例如checkRole,checkPermission
+     *
+     * @return AuthorizationInfo 权限信息
+     */
+    @Override
+    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+        log.info("————权限认证 [ roles、permissions]————");
+        CorpUserInfo sysUser = null;
+        String username = null;
+        if (principals != null) {
+            sysUser = (CorpUserInfo) principals.getPrimaryPrincipal();
+            username = sysUser.getName();
+        }
+        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
+
+        // 设置用户拥有的角色集合,比如“admin,test”
+        //Set<String> roleSet = sysUserService.getUserRolesSet(username);
+        //info.setRoles(roleSet);
+
+        // 设置用户拥有的权限集合,比如“sys:role:add,sys:user:add”
+        //Set<String> permissionSet = sysUserService.getUserPermissionsSet(username);
+        //info.addStringPermissions(permissionSet);
+        return info;
+    }
+
+    /**
+     * 功能: 用来进行身份认证,也就是说验证用户输入的账号和密码是否正确,获取身份验证信息,错误抛出异常
+     *
+     * @param auth 用户身份信息 token
+     * @return 返回封装了用户信息的 AuthenticationInfo 实例
+     */
+    @Override
+    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
+        if (!Check.isNull(auth)) {
+            String token = (String) auth.getCredentials();
+            if (token != null) {
+                CorpUserInfo loginUser = this.checkUserTokenIsEffect(token);
+                return new SimpleAuthenticationInfo(loginUser, token, getName());
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 校验token的有效性
+     *
+     * @param token
+     */
+    public CorpUserInfo checkUserTokenIsEffect(String token) throws AuthenticationException {
+        if (Check.isNull(token)) {
+            log.error("token为空");
+            return null;
+
+        }
+
+        // 解密获得username,用于和数据库进行对比
+        String username = JwtUtil.getUsername(token);  //username即为open_id
+        if (username == null) {
+            log.error("token非法无效!");
+            // throw new AuthenticationException("token非法无效!");
+        }
+        // 查询用户信息
+        CorpUserInfo loginUser = new CorpUserInfo();
+        CorpUserInfo userInfo = corpUserInfoMapper.getUserInfoByOpenId(username);
+        if (userInfo == null) {
+            throw new AuthenticationException("用户不存在!");
+        }
+
+        // 校验token是否超时失效 & 或者账号密码是否错误
+//        if (!jwtTokenRefresh(token, username, sysUser.getPassword())) {
+//            log.error("Token失效,请重新登录!");
+//        }
+
+        // 判断用户状态
+        //if (sysUser.getStatus() != 1) {
+        //    throw new AuthenticationException("账号已被锁定,请联系管理员!");
+        //}
+        BeanUtils.copyProperties(userInfo, loginUser);
+        return loginUser;
+    }
+
+    /**
+     * JWTToken刷新生命周期 (解决用户一直在线操作,提供Token失效问题)
+     * 1、登录成功后将用户的JWT生成的Token作为k、v存储到cache缓存里面(这时候k、v值一样)
+     * 2、当该用户再次请求时,通过JWTFilter层层校验之后会进入到doGetAuthenticationInfo进行身份验证
+     * 3、当该用户这次请求JWTToken值还在生命周期内,则会通过重新PUT的方式k、v都为Token值,缓存中的token值生命周期时间重新计算(这时候k、v值一样)
+     * 4、当该用户这次请求jwt生成的token值已经超时,但该token对应cache中的k还是存在,则表示该用户一直在操作只是JWT的token失效了,程序会给token对应的k映射的v值重新生成JWTToken并覆盖v值,该缓存生命周期重新计算
+     * 5、当该用户这次请求jwt在生成的token值已经超时,并在cache中不存在对应的k,则表示该用户账户空闲超时,返回用户信息已失效,请重新登录。
+     * 6、每次当返回为true情况下,都会给Response的Header中设置Authorization,该Authorization映射的v为cache对应的v值。
+     * 7、注:当前端接收到Response的Header中的Authorization值会存储起来,作为以后请求token使用
+     * 参考方案:https://blog.csdn.net/qq394829044/article/details/82763936
+     *
+     * @param userName
+     * @param passWord
+     * @return
+     */
+    public boolean jwtTokenRefresh(String token, String userName, String passWord) {
+        String cacheToken = String.valueOf(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token));
+        if (oConvertUtils.isNotEmpty(cacheToken)) {
+            // 校验token有效性
+            if (!JwtUtil.verify(token, userName, passWord)) {
+                String newAuthorization = JwtUtil.sign(userName, passWord);
+                redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, newAuthorization);
+                // 设置超时时间
+                redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);
+            } else {
+                redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, cacheToken);
+                // 设置超时时间
+                redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);
+            }
+            return true;
+        }
+        return false;
+    }
+
+}

+ 82 - 0
src/main/java/cn/com/ctop/okr/shiro/authc/aop/JwtFilter.java

@@ -0,0 +1,82 @@
+package cn.com.ctop.okr.shiro.authc.aop;
+
+import cn.com.ctop.okr.shiro.authc.JwtToken;
+import cn.com.ctop.okr.shiro.vo.DefContants;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @Description: 鉴权登录拦截器
+ * @Author: Scott
+ * @Date: 2018/10/7
+ **/
+@Slf4j
+public class JwtFilter extends BasicHttpAuthenticationFilter {
+
+    /**
+     * 执行登录认证
+     *
+     * @param request
+     * @param response
+     * @param mappedValue
+     * @return
+     */
+    @Override
+    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
+
+        boolean successful = false;
+        try {
+            successful = executeLogin(request, response);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return successful;
+
+    }
+
+    /**
+     *
+     */
+    @Override
+    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
+
+        try {
+            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+            String token = httpServletRequest.getHeader(DefContants.X_ACCESS_TOKEN);
+            JwtToken jwtToken = new JwtToken(token);
+            // 提交给realm进行登入,如果错误他会抛出异常并被捕获
+            getSubject(request, response).login(jwtToken);
+            // 如果没有抛出异常则代表登入成功,返回true
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
+
+
+    }
+
+    /**
+     * 对跨域提供支持
+     */
+    @Override
+    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
+        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
+        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
+        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
+        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
+        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
+            httpServletResponse.setStatus(HttpStatus.OK.value());
+            return false;
+        }
+        return super.preHandle(request, response);
+    }
+}

+ 68 - 0
src/main/java/cn/com/ctop/okr/shiro/authc/aop/ResourceCheckFilter.java

@@ -0,0 +1,68 @@
+package cn.com.ctop.okr.shiro.authc.aop;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.web.filter.AccessControlFilter;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @Author Scott
+ * @create 2019-02-01 15:56
+ * @desc 鉴权请求URL访问权限拦截器
+ */
+@Slf4j
+public class ResourceCheckFilter extends AccessControlFilter {
+
+	private String errorUrl;
+
+	public String getErrorUrl() {
+		return errorUrl;
+	}
+
+	public void setErrorUrl(String errorUrl) {
+		this.errorUrl = errorUrl;
+	}
+
+	/**
+	 * 表示是否允许访问 ,如果允许访问返回true,否则false;
+	 *
+	 * @param servletRequest
+	 * @param servletResponse
+	 * @param o               表示写在拦截器中括号里面的字符串 mappedValue 就是 [urls] 配置中拦截器参数部分
+	 * @return
+	 * @throws Exception
+	 */
+	@Override
+	protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
+		Subject subject = getSubject(servletRequest, servletResponse);
+		String url = getPathWithinApplication(servletRequest);
+		log.info("当前用户正在访问的 url => " + url);
+		return subject.isPermitted(url);
+	}
+
+	/**
+	 * onAccessDenied:表示当访问拒绝时是否已经处理了; 如果返回 true 表示需要继续处理; 如果返回 false
+	 * 表示该拦截器实例已经处理了,将直接返回即可。
+	 *
+	 * @param servletRequest
+	 * @param servletResponse
+	 * @return
+	 * @throws Exception
+	 */
+	@Override
+	protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
+		log.info("当 isAccessAllowed 返回 false 的时候,才会执行 method onAccessDenied ");
+
+		HttpServletRequest request = (HttpServletRequest) servletRequest;
+		HttpServletResponse response = (HttpServletResponse) servletResponse;
+		response.sendRedirect(request.getContextPath() + this.errorUrl);
+
+		// 返回 false 表示已经处理,例如页面跳转啥的,表示不在走以下的拦截器了(如果还有配置的话)
+		return false;
+	}
+
+}

+ 10 - 0
src/main/java/cn/com/ctop/okr/shiro/vo/DefContants.java

@@ -0,0 +1,10 @@
+package cn.com.ctop.okr.shiro.vo;
+
+/**
+ * @author jeecg-boot
+ */
+public class DefContants {
+
+    public static String X_ACCESS_TOKEN = "X-Access-Token";
+
+}

+ 28 - 0
src/main/java/cn/com/ctop/okr/shiro/vo/ResponseBean.java

@@ -0,0 +1,28 @@
+package cn.com.ctop.okr.shiro.vo;
+
+import lombok.Data;
+
+@Data
+public class ResponseBean {
+
+    /**
+     * http 状态码
+     */
+    private int code;
+
+    /**
+     * 返回信息
+     */
+    private String msg;
+
+    /**
+     * 返回的数据
+     */
+    private Object data;
+
+    public ResponseBean(int code, String msg, Object data) {
+        this.code = code;
+        this.msg = msg;
+        this.data = data;
+    }
+}

+ 24 - 0
src/main/java/cn/com/ctop/okr/shiro/vo/UserBean.java

@@ -0,0 +1,24 @@
+package cn.com.ctop.okr.shiro.vo;
+
+import lombok.Data;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author jeecg-boot
+ * 2019年12月5日19:57:58
+ */
+@Data
+public class UserBean {
+    private String username;
+    private String password;
+    /**
+     * 用户所有角色值,用于shiro做角色权限的判断
+     */
+    private Set<String> roles = new HashSet<>();
+    /**
+     * 用户所有权限值,用于shiro做资源权限的判断
+     */
+    private Set<String> perms = new HashSet<>();
+}

+ 66 - 0
src/main/java/cn/com/ctop/okr/utils/Check.java

@@ -0,0 +1,66 @@
+package cn.com.ctop.okr.utils;
+
+import com.alibaba.fastjson.JSONArray;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+public class Check {
+    private Check() {
+    }
+
+    public static boolean isNull(List<?> list) {
+        return list == null || list.isEmpty();
+    }
+
+    public static boolean isNull(Long l) {
+        return l == null || l == 0;
+    }
+
+    public static boolean isNull(Collection<?> collection) {
+        return collection == null || collection.isEmpty();
+    }
+
+    public static boolean isNull(JSONArray jsonArray) {
+        return jsonArray == null || jsonArray.isEmpty();
+    }
+
+
+    public static boolean isNull(String str) {
+        return str == null || str.isEmpty();
+    }
+
+    public static boolean isNull(byte[] bytes) {
+        return bytes == null || bytes.length == 0;
+    }
+
+    public static boolean hasNull(Object... objects) {
+        for (Object obj : objects) {
+            if (obj == null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static boolean allEmpty(String... strs) {
+        for (String str : strs) {
+            if (StringUtils.isNotEmpty(str)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static boolean isNull(Object object) {
+        return object == null;
+    }
+
+    public static boolean isNullMap(Map<?, ?> map) {
+        return map == null || map.isEmpty();
+    }
+}

+ 145 - 0
src/main/java/cn/com/ctop/okr/utils/HttpUtils.java

@@ -668,4 +668,149 @@ public class HttpUtils {
         }
         return strReturn;
     }
+
+    public static JSONObject feishuGetRequest(String url, JSONObject params){
+        return feishuGetRequest(null,url,params);
+    }
+    public static JSONObject feishuPostRequest(String url,JSONObject params){
+        return feishuPostRequest(null,url,params);
+    }
+
+    public static String feishuGetRequestString(String url) {
+        // 构造请求
+        HttpEntityEnclosingRequestBase httpEntity = new HttpEntityEnclosingRequestBase() {
+            @Override
+            public String getMethod() {
+                return "GET";
+            }
+        };
+        CloseableHttpResponse response = null;
+        CloseableHttpClient client = null;
+        try {
+            client = HttpClientBuilder.create().build();
+            httpEntity.setURI(URI.create(url));
+            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(12000).setConnectTimeout(12000).build();
+            httpEntity.setConfig(requestConfig);
+            response = client.execute(httpEntity);
+            if (response != null && response.getStatusLine().getStatusCode() == 200) {
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
+                StringBuilder result = new StringBuilder();
+                String line = "";
+                while ((line = bufferedReader.readLine()) != null) {
+                    result.append(line);
+                }
+                bufferedReader.close();
+                return result.toString();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (response != null) {
+                    response.close();
+                }
+                if (client != null) {
+                    client.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    public static JSONObject feishuGetRequest(String accessToken, String url, JSONObject params) {
+        System.out.println(url);
+        // 构造请求
+        HttpEntityEnclosingRequestBase httpEntity = new HttpEntityEnclosingRequestBase() {
+            @Override
+            public String getMethod() {
+                return "GET";
+            }
+        };
+        if(accessToken != null){
+            httpEntity.setHeader("Authorization", accessToken);
+        }
+        CloseableHttpResponse response = null;
+        CloseableHttpClient client = null;
+        try {
+            client = HttpClientBuilder.create().build();
+            httpEntity.setURI(URI.create(url));
+            if (params != null){
+                httpEntity.setEntity(new StringEntity(params.toJSONString(), ContentType.APPLICATION_JSON));
+            }
+            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(12000).setConnectTimeout(12000).build();
+            httpEntity.setConfig(requestConfig);
+            response = client.execute(httpEntity);
+            if (response != null && response.getStatusLine().getStatusCode() == 200) {
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
+                StringBuilder result = new StringBuilder();
+                String line = "";
+                while ((line = bufferedReader.readLine()) != null) {
+                    result.append(line);
+                }
+                bufferedReader.close();
+                return JSONObject.parseObject(result.toString());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (response != null) {
+                    response.close();
+                }
+                if (client != null) {
+                    client.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+
+
+    public static JSONObject feishuPostRequest(String accessToken, String url, JSONObject params) {
+        // 构造请求
+        HttpPost httpEntity = new HttpPost(url);
+        if(accessToken != null){
+            httpEntity.setHeader("Authorization", accessToken);
+        }
+        CloseableHttpResponse response = null;
+        CloseableHttpClient client = null;
+        try {
+            client = HttpClientBuilder.create().build();
+            httpEntity.setEntity(new StringEntity(params.toJSONString(), ContentType.APPLICATION_JSON));
+            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(12000).setConnectTimeout(12000).build();
+            httpEntity.setConfig(requestConfig);
+            response = client.execute(httpEntity);
+            if (response != null && response.getStatusLine().getStatusCode() == 200) {
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
+                StringBuilder result = new StringBuilder();
+                String line = "";
+                while ((line = bufferedReader.readLine()) != null) {
+                    result.append(line);
+                }
+                bufferedReader.close();
+                return JSONObject.parseObject(result.toString());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (response != null) {
+                    response.close();
+                }
+                if (client != null) {
+                    client.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+
 }

+ 88 - 0
src/main/java/cn/com/ctop/okr/utils/JeecgDataAutorUtils.java

@@ -0,0 +1,88 @@
+//package cn.com.ctop.okr.utils;
+//
+//import org.jeecg.common.system.vo.SysUserCacheInfo;
+//import org.jeecg.common.util.SpringContextUtils;
+//import org.jeecg.modules.system.entity.SysPermissionDataRule;
+//import org.springframework.util.StringUtils;
+//
+//import javax.servlet.http.HttpServletRequest;
+//import java.util.ArrayList;
+//import java.util.List;
+//
+///**
+// * @ClassName: JeecgDataAutorUtils
+// * @Description: 数据权限查询规则容器工具类
+// * @Author: 张代浩
+// * @Date: 2012-12-15 下午11:27:39
+// *
+// */
+//public class JeecgDataAutorUtils {
+//
+//	public static final String MENU_DATA_AUTHOR_RULES = "MENU_DATA_AUTHOR_RULES";
+//
+//	public static final String MENU_DATA_AUTHOR_RULE_SQL = "MENU_DATA_AUTHOR_RULE_SQL";
+//
+//	public static final String SYS_USER_INFO = "SYS_USER_INFO";
+//
+//	/**
+//	 * 往链接请求里面,传入数据查询条件
+//	 *
+//	 * @param request
+//     * @param dataRules
+//	 */
+//	public static synchronized void installDataSearchConditon(HttpServletRequest request, List<SysPermissionDataRule> dataRules) {
+//		@SuppressWarnings("unchecked")
+//        // 1.先从request获取MENU_DATA_AUTHOR_RULES,如果存则获取到LIST
+//                List<SysPermissionDataRule> list = (List<SysPermissionDataRule>) loadDataSearchConditon();
+//		if (list==null) {
+//			// 2.如果不存在,则new一个list
+//			list = new ArrayList<SysPermissionDataRule>();
+//		}
+//		for (SysPermissionDataRule tsDataRule : dataRules) {
+//			list.add(tsDataRule);
+//		}
+//        // 3.往list里面增量存指
+//        request.setAttribute(MENU_DATA_AUTHOR_RULES, list);
+//	}
+//
+//	/**
+//	 * 获取请求对应的数据权限规则
+//     *
+//	 * @return
+//	 */
+//	@SuppressWarnings("unchecked")
+//	public static synchronized List<SysPermissionDataRule> loadDataSearchConditon() {
+//		return (List<SysPermissionDataRule>) SpringContextUtils.getHttpServletRequest().getAttribute(MENU_DATA_AUTHOR_RULES);
+//
+//	}
+//
+//	/**
+//	 * 获取请求对应的数据权限SQL
+//     *
+//	 * @return
+//	 */
+//    public static synchronized String loadDataSearchConditonSqlString() {
+//		return (String) SpringContextUtils.getHttpServletRequest().getAttribute(MENU_DATA_AUTHOR_RULE_SQL);
+//	}
+//
+//	/**
+//	 * 往链接请求里面,传入数据查询条件
+//	 *
+//	 * @param request
+//	 */
+//	public static synchronized void installDataSearchConditon(HttpServletRequest request, String sql) {
+//        String ruleSql = (String) loadDataSearchConditonSqlString();
+//		if (!StringUtils.hasText(ruleSql)) {
+//			request.setAttribute(MENU_DATA_AUTHOR_RULE_SQL,sql);
+//		}
+//	}
+//
+//	public static synchronized void installUserInfo(HttpServletRequest request, SysUserCacheInfo userinfo) {
+//		request.setAttribute(SYS_USER_INFO, userinfo);
+//	}
+//
+//	public static synchronized SysUserCacheInfo loadUserInfo() {
+//		return (SysUserCacheInfo) SpringContextUtils.getHttpServletRequest().getAttribute(SYS_USER_INFO);
+//
+//	}
+//}

+ 198 - 0
src/main/java/cn/com/ctop/okr/utils/JwtUtil.java

@@ -0,0 +1,198 @@
+package cn.com.ctop.okr.utils;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.util.Date;
+
+/**
+ * @Author Scott
+ * @Date 2018-07-12 14:23
+ * @Desc JWT工具类
+ **/
+public class JwtUtil {
+
+    /**
+     * 过期时间30分钟
+     */
+    public static final long EXPIRE_TIME = 30 * 60 * 1000;
+
+    /**
+     * 校验token是否正确
+     *
+     * @param token  密钥
+     * @param secret 用户的密码
+     * @return 是否正确
+     */
+    public static boolean verify(String token, String username, String secret) {
+        try {
+            // 根据密码生成JWT效验器
+            Algorithm algorithm = Algorithm.HMAC256(secret);
+            JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();
+            // 效验TOKEN
+            DecodedJWT jwt = verifier.verify(token);
+            return true;
+        } catch (Exception exception) {
+            return false;
+        }
+    }
+
+    /**
+     * 获得token中的信息无需secret解密也能获得
+     *
+     * @return token中包含的用户名
+     */
+    public static String getUsername(String token) {
+        try {
+            DecodedJWT jwt = JWT.decode(token);
+            return jwt.getClaim("username").asString();
+        } catch (JWTDecodeException e) {
+            return null;
+        }
+    }
+
+    /**
+     * 生成签名,5min后过期
+     *
+     * @param username 用户名
+     * @param secret   用户的密码
+     * @return 加密的token
+     */
+    public static String sign(String username, String secret) {
+        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
+        Algorithm algorithm = Algorithm.HMAC256(secret);
+        // 附带username信息
+        return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);
+
+    }
+
+    /**
+     * 根据request中的token获取用户账号
+     *
+     * @param request
+     * @return
+     * @throws Exception
+     */
+    public static String getUserNameByToken(HttpServletRequest request) throws Exception {
+        String accessToken = request.getHeader("X-Access-Token");
+        String username = getUsername(accessToken);
+        if (oConvertUtils.isEmpty(username)) {
+            throw new Exception("未获取到用户");
+        }
+        return username;
+    }
+
+    /**
+     * 从session中获取变量
+     *
+     * @param key
+     * @return
+     */
+    public static String getSessionData(String key) {
+        //${myVar}%
+        //得到${} 后面的值
+        String moshi = "";
+        if (key.indexOf("}") != -1) {
+            moshi = key.substring(key.indexOf("}") + 1);
+        }
+        String returnValue = null;
+        if (key.contains("#{")) {
+            key = key.substring(2, key.indexOf("}"));
+        }
+        if (oConvertUtils.isNotEmpty(key)) {
+            HttpSession session = SpringContextUtils.getHttpServletRequest().getSession();
+            returnValue = (String) session.getAttribute(key);
+        }
+        //结果加上${} 后面的值
+        if (returnValue != null) {
+            returnValue = returnValue + moshi;
+        }
+        return returnValue;
+    }
+
+    /**
+     * 从当前用户中获取变量
+     *
+     * @param key
+     * @param user
+     * @return
+     */
+    //
+    ////TODO 急待改造 sckjkdsjsfjdk
+    //public static String getUserSystemData(String key, SysUserCacheInfo user) {
+    //    if (user == null) {
+    //        user = JeecgDataAutorUtils.loadUserInfo();
+    //    }
+    //    //#{sys_user_code}%
+    //
+    //    // 获取登录用户信息
+    //    LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+    //
+    //    String moshi = "";
+    //    if (key.indexOf("}") != -1) {
+    //        moshi = key.substring(key.indexOf("}") + 1);
+    //    }
+    //    String returnValue = null;
+    //    //针对特殊标示处理#{sysOrgCode},判断替换
+    //    if (key.contains("#{")) {
+    //        key = key.substring(2, key.indexOf("}"));
+    //    } else {
+    //        key = key;
+    //    }
+    //    //替换为系统登录用户帐号
+    //    if (key.equals(DataBaseConstant.SYS_USER_CODE) || key.equals(DataBaseConstant.SYS_USER_CODE_TABLE)) {
+    //        if (user == null) {
+    //            returnValue = sysUser.getUsername();
+    //        } else {
+    //            returnValue = user.getSysUserCode();
+    //        }
+    //    }
+    //    //替换为系统登录用户真实名字
+    //    if (key.equals(DataBaseConstant.SYS_USER_NAME) || key.equals(DataBaseConstant.SYS_USER_NAME_TABLE)) {
+    //        if (user == null) {
+    //            returnValue = sysUser.getRealname();
+    //        } else {
+    //            returnValue = user.getSysUserName();
+    //        }
+    //    }
+    //
+    //    //替换为系统用户登录所使用的机构编码
+    //    if (key.equals(DataBaseConstant.SYS_ORG_CODE) || key.equals(DataBaseConstant.SYS_ORG_CODE_TABLE)) {
+    //        if (user == null) {
+    //            returnValue = sysUser.getOrgCode();
+    //        } else {
+    //            returnValue = user.getSysOrgCode();
+    //        }
+    //
+    //    }
+    //    //替换为系统用户所拥有的所有机构编码
+    //    if (key.equals(DataBaseConstant.SYS_MULTI_ORG_CODE) || key.equals(DataBaseConstant.SYS_MULTI_ORG_CODE)) {
+    //        if (user.isOneDepart()) {
+    //            returnValue = user.getSysMultiOrgCode().get(0);
+    //        } else {
+    //            returnValue = Joiner.on(",").join(user.getSysMultiOrgCode());
+    //        }
+    //    }
+    //    //替换为当前系统时间(年月日)
+    //    if (key.equals(DataBaseConstant.SYS_DATE) || key.equals(DataBaseConstant.SYS_DATE_TABLE)) {
+    //        returnValue = user.getSysDate();
+    //    }
+    //    //替换为当前系统时间(年月日时分秒)
+    //    if (key.equals(DataBaseConstant.SYS_TIME) || key.equals(DataBaseConstant.SYS_TIME_TABLE)) {
+    //        returnValue = user.getSysTime();
+    //    }
+    //    //流程状态默认值(默认未发起)
+    //    if (key.equals(DataBaseConstant.BPM_STATUS_TABLE) || key.equals(DataBaseConstant.BPM_STATUS_TABLE)) {
+    //        returnValue = "1";
+    //    }
+    //    if (returnValue != null) {
+    //        returnValue = returnValue + moshi;
+    //    }
+    //    return returnValue;
+    //}
+}

+ 574 - 0
src/main/java/cn/com/ctop/okr/utils/RedisUtil.java

@@ -0,0 +1,574 @@
+package cn.com.ctop.okr.utils;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * redis 工具类
+ * @Author Scott
+ *
+ */
+@Component
+public class RedisUtil {
+
+	@Autowired
+	private RedisTemplate<String, Object> redisTemplate;
+	@Autowired
+	private StringRedisTemplate stringRedisTemplate;
+
+	/**
+	 * 指定缓存失效时间
+     *
+	 * @param key  键
+	 * @param time 时间(秒)
+	 * @return
+	 */
+	public boolean expire(String key, long time) {
+		try {
+			if (time > 0) {
+				redisTemplate.expire(key, time, TimeUnit.SECONDS);
+			}
+			return true;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 根据key 获取过期时间
+     *
+	 * @param key 键 不能为null
+	 * @return 时间(秒) 返回0代表为永久有效
+	 */
+	public long getExpire(String key) {
+		return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+	}
+
+	/**
+	 * 判断key是否存在
+     *
+	 * @param key 键
+	 * @return true 存在 false不存在
+	 */
+	public boolean hasKey(String key) {
+		try {
+			return redisTemplate.hasKey(key);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 删除缓存
+     *
+	 * @param key 可以传一个值 或多个
+	 */
+	@SuppressWarnings("unchecked")
+	public void del(String... key) {
+		if (key != null && key.length > 0) {
+			if (key.length == 1) {
+				redisTemplate.delete(key[0]);
+			} else {
+				redisTemplate.delete(CollectionUtils.arrayToList(key));
+			}
+		}
+	}
+
+	// ============================String=============================
+	/**
+	 * 普通缓存获取
+     *
+	 * @param key 键
+	 * @return 值
+	 */
+	public Object get(String key) {
+		return key == null ? null : redisTemplate.opsForValue().get(key);
+	}
+
+	/**
+	 * 普通缓存放入
+     *
+	 * @param key   键
+	 * @param value 值
+	 * @return true成功 false失败
+	 */
+	public boolean set(String key, Object value) {
+		try {
+			redisTemplate.opsForValue().set(key, value);
+			return true;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+
+	}
+
+	/**
+	 * 普通缓存放入并设置时间
+     *
+	 * @param key   键
+	 * @param value 值
+	 * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
+	 * @return true成功 false 失败
+	 */
+	public boolean set(String key, Object value, long time) {
+		try {
+			if (time > 0) {
+				redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+			} else {
+				set(key, value);
+			}
+			return true;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 递增
+     *
+	 * @param key 键
+     * @param delta  要增加几(大于0)
+	 * @return
+	 */
+	public long incr(String key, long delta) {
+		if (delta < 0) {
+			throw new RuntimeException("递增因子必须大于0");
+		}
+		return redisTemplate.opsForValue().increment(key, delta);
+	}
+
+	/**
+	 * 递减
+     *
+	 * @param key 键
+     * @param delta 要减少几(小于0)
+	 * @return
+	 */
+	public long decr(String key, long delta) {
+		if (delta < 0) {
+			throw new RuntimeException("递减因子必须大于0");
+		}
+		return redisTemplate.opsForValue().increment(key, -delta);
+	}
+
+	// ================================Map=================================
+	/**
+	 * HashGet
+     *
+	 * @param key  键 不能为null
+	 * @param item 项 不能为null
+	 * @return 值
+	 */
+	public Object hget(String key, String item) {
+		return redisTemplate.opsForHash().get(key, item);
+	}
+
+	/**
+	 * 获取hashKey对应的所有键值
+     *
+	 * @param key 键
+	 * @return 对应的多个键值
+	 */
+	public Map<Object, Object> hmget(String key) {
+		return redisTemplate.opsForHash().entries(key);
+	}
+
+	/**
+	 * HashSet
+     *
+	 * @param key 键
+	 * @param map 对应多个键值
+	 * @return true 成功 false 失败
+	 */
+	public boolean hmset(String key, Map<String, Object> map) {
+		try {
+			redisTemplate.opsForHash().putAll(key, map);
+			return true;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * HashSet 并设置时间
+     *
+	 * @param key  键
+	 * @param map  对应多个键值
+	 * @param time 时间(秒)
+	 * @return true成功 false失败
+	 */
+	public boolean hmset(String key, Map<String, Object> map, long time) {
+		try {
+			redisTemplate.opsForHash().putAll(key, map);
+			if (time > 0) {
+				expire(key, time);
+			}
+			return true;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 向一张hash表中放入数据,如果不存在将创建
+     *
+	 * @param key   键
+	 * @param item  项
+	 * @param value 值
+	 * @return true 成功 false失败
+	 */
+	public boolean hset(String key, String item, Object value) {
+		try {
+			redisTemplate.opsForHash().put(key, item, value);
+			return true;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 向一张hash表中放入数据,如果不存在将创建
+     *
+	 * @param key   键
+	 * @param item  项
+	 * @param value 值
+	 * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
+	 * @return true 成功 false失败
+	 */
+	public boolean hset(String key, String item, Object value, long time) {
+		try {
+			redisTemplate.opsForHash().put(key, item, value);
+			if (time > 0) {
+				expire(key, time);
+			}
+			return true;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 删除hash表中的值
+     *
+	 * @param key  键 不能为null
+	 * @param item 项 可以使多个 不能为null
+	 */
+	public void hdel(String key, Object... item) {
+		redisTemplate.opsForHash().delete(key, item);
+	}
+
+	/**
+	 * 判断hash表中是否有该项的值
+     *
+	 * @param key  键 不能为null
+	 * @param item 项 不能为null
+	 * @return true 存在 false不存在
+	 */
+	public boolean hHasKey(String key, String item) {
+		return redisTemplate.opsForHash().hasKey(key, item);
+	}
+
+	/**
+	 * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     *
+	 * @param key  键
+	 * @param item 项
+	 * @param by   要增加几(大于0)
+	 * @return
+	 */
+	public double hincr(String key, String item, double by) {
+		return redisTemplate.opsForHash().increment(key, item, by);
+	}
+
+	/**
+	 * hash递减
+     *
+	 * @param key  键
+	 * @param item 项
+	 * @param by   要减少记(小于0)
+	 * @return
+	 */
+	public double hdecr(String key, String item, double by) {
+		return redisTemplate.opsForHash().increment(key, item, -by);
+	}
+
+	// ============================set=============================
+	/**
+	 * 根据key获取Set中的所有值
+     *
+	 * @param key 键
+	 * @return
+	 */
+	public Set<Object> sGet(String key) {
+		try {
+			return redisTemplate.opsForSet().members(key);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	/**
+	 * 根据value从一个set中查询,是否存在
+     *
+	 * @param key   键
+	 * @param value 值
+	 * @return true 存在 false不存在
+	 */
+	public boolean sHasKey(String key, Object value) {
+		try {
+			return redisTemplate.opsForSet().isMember(key, value);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 将数据放入set缓存
+     *
+	 * @param key    键
+	 * @param values 值 可以是多个
+	 * @return 成功个数
+	 */
+	public long sSet(String key, Object... values) {
+		try {
+			return redisTemplate.opsForSet().add(key, values);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return 0;
+		}
+	}
+
+	/**
+	 * 将set数据放入缓存
+     *
+	 * @param key    键
+	 * @param time   时间(秒)
+	 * @param values 值 可以是多个
+	 * @return 成功个数
+	 */
+	public long sSetAndTime(String key, long time, Object... values) {
+		try {
+			Long count = redisTemplate.opsForSet().add(key, values);
+			if (time > 0) {
+				expire(key, time);
+			}
+			return count;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return 0;
+		}
+	}
+
+	/**
+	 * 获取set缓存的长度
+     *
+	 * @param key 键
+	 * @return
+	 */
+	public long sGetSetSize(String key) {
+		try {
+			return redisTemplate.opsForSet().size(key);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return 0;
+		}
+	}
+
+	/**
+	 * 移除值为value的
+     *
+	 * @param key    键
+	 * @param values 值 可以是多个
+	 * @return 移除的个数
+	 */
+	public long setRemove(String key, Object... values) {
+		try {
+			Long count = redisTemplate.opsForSet().remove(key, values);
+			return count;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return 0;
+		}
+	}
+	// ===============================list=================================
+
+	/**
+	 * 获取list缓存的内容
+     *
+	 * @param key   键
+	 * @param start 开始
+	 * @param end   结束 0 到 -1代表所有值
+	 * @return
+	 */
+	public List<Object> lGet(String key, long start, long end) {
+		try {
+			return redisTemplate.opsForList().range(key, start, end);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	/**
+	 * 获取list缓存的长度
+     *
+	 * @param key 键
+	 * @return
+	 */
+	public long lGetListSize(String key) {
+		try {
+			return redisTemplate.opsForList().size(key);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return 0;
+		}
+	}
+
+	/**
+	 * 通过索引 获取list中的值
+     *
+	 * @param key   键
+	 * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
+	 * @return
+	 */
+	public Object lGetIndex(String key, long index) {
+		try {
+			return redisTemplate.opsForList().index(key, index);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	/**
+	 * 将list放入缓存
+     *
+	 * @param key   键
+	 * @param value 值
+	 * @param time  时间(秒)
+	 * @return
+	 */
+	public boolean lSet(String key, Object value) {
+		try {
+			redisTemplate.opsForList().rightPush(key, value);
+			return true;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 将list放入缓存
+     *
+	 * @param key   键
+	 * @param value 值
+	 * @param time  时间(秒)
+	 * @return
+	 */
+	public boolean lSet(String key, Object value, long time) {
+		try {
+			redisTemplate.opsForList().rightPush(key, value);
+			if (time > 0) {
+				expire(key, time);
+			}
+			return true;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 将list放入缓存
+     *
+	 * @param key   键
+	 * @param value 值
+	 * @param time  时间(秒)
+	 * @return
+	 */
+	public boolean lSet(String key, List<Object> value) {
+		try {
+			redisTemplate.opsForList().rightPushAll(key, value);
+			return true;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 将list放入缓存
+     *
+	 * @param key   键
+	 * @param value 值
+	 * @param time  时间(秒)
+	 * @return
+	 */
+	public boolean lSet(String key, List<Object> value, long time) {
+		try {
+			redisTemplate.opsForList().rightPushAll(key, value);
+			if (time > 0) {
+				expire(key, time);
+			}
+			return true;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 根据索引修改list中的某条数据
+     *
+	 * @param key   键
+	 * @param index 索引
+	 * @param value 值
+	 * @return
+	 */
+	public boolean lUpdateIndex(String key, long index, Object value) {
+		try {
+			redisTemplate.opsForList().set(key, index, value);
+			return true;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+
+	/**
+	 * 移除N个值为value
+     *
+	 * @param key   键
+	 * @param count 移除多少个
+	 * @param value 值
+	 * @return 移除的个数
+	 */
+	public long lRemove(String key, long count, Object value) {
+		try {
+			Long remove = redisTemplate.opsForList().remove(key, count, value);
+			return remove;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return 0;
+		}
+	}
+}

+ 31 - 0
src/main/java/cn/com/ctop/okr/utils/ResultMapUtils.java

@@ -0,0 +1,31 @@
+package cn.com.ctop.okr.utils;
+
+import java.util.Map;
+
+/**
+ * @author jeecg-boot
+ * 2019年12月5日19:57:58
+ */
+public class ResultMapUtils {
+    private static final String MAP_CODE = "code";
+    private static final String MAP_MESSAGE = "message";
+    private static final String MAP_SUCCESS = "success";
+
+    public static void setResultMap(Map<String, Object> map, Integer statusCode) {
+        map.put(MAP_CODE, statusCode);
+        map.put(MAP_MESSAGE, StatusCode.getDesc(statusCode));
+        map.put(MAP_SUCCESS, StatusCode.getFlag(statusCode));
+    }
+
+    public static void setResultMap(Map<String, Object> map, StatusCode statusCode) {
+        map.put(MAP_CODE, statusCode.getCode());
+        map.put(MAP_MESSAGE, statusCode.getDesc());
+        map.put(MAP_SUCCESS, statusCode.getFlag());
+    }
+
+    public static void setResultMap(Map<String, Object> map, int statusCode,String message,boolean flag) {
+        map.put(MAP_CODE, statusCode);
+        map.put(MAP_MESSAGE, message);
+        map.put(MAP_SUCCESS, flag);
+    }
+}

+ 88 - 0
src/main/java/cn/com/ctop/okr/utils/SpringContextUtils.java

@@ -0,0 +1,88 @@
+package cn.com.ctop.okr.utils;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author jeecg-boot
+ * 2019年12月5日19:57:58
+ */
+@Component
+public class SpringContextUtils implements ApplicationContextAware {
+
+    /**
+     * 上下文对象实例
+     */
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        SpringContextUtils.applicationContext = applicationContext;
+    }
+
+    /**
+     * 获取applicationContext
+     *
+     * @return
+     */
+    public static ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+
+    /**
+     * 获取HttpServletRequest
+     */
+    public static HttpServletRequest getHttpServletRequest() {
+        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+    }
+
+    public static String getDomain(){
+        HttpServletRequest request = getHttpServletRequest();
+        StringBuffer url = request.getRequestURL();
+        return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
+    }
+
+    public static String getOrigin(){
+        HttpServletRequest request = getHttpServletRequest();
+        return request.getHeader("Origin");
+    }
+
+    /**
+     * 通过name获取 Bean.
+     *
+     * @param name
+     * @return
+     */
+    public static Object getBean(String name) {
+        return getApplicationContext().getBean(name);
+    }
+
+    /**
+     * 通过class获取Bean.
+     *
+     * @param clazz
+     * @param       <T>
+     * @return
+     */
+    public static <T> T getBean(Class<T> clazz) {
+        return getApplicationContext().getBean(clazz);
+    }
+
+    /**
+     * 通过name,以及Clazz返回指定的Bean
+     *
+     * @param name
+     * @param clazz
+     * @param       <T>
+     * @return
+     */
+    public static <T> T getBean(String name, Class<T> clazz) {
+        return getApplicationContext().getBean(name, clazz);
+    }
+}

+ 114 - 0
src/main/java/cn/com/ctop/okr/utils/StatusCode.java

@@ -0,0 +1,114 @@
+package cn.com.ctop.okr.utils;
+
+/**
+ * @author jeecg-boot
+ * 2019年12月5日19:57:58
+ */
+public enum StatusCode {
+    /**
+     * 0   请求成功
+     * -101 参数异常
+     * -102 服务器异常
+     * -103 数据不存在
+     * -104 渠道不存在
+     * -105 规则尚未配置
+     * -106 规则为空
+     * -107 同盾数据为空
+     * -108 模型服务异常
+     * -109 深度搜索服务异常
+     * -201 探针数据为空
+     */
+    COMMON_SUCCESS("success", 0, true),
+    COMMON_PARAM_ERROR("参数异常", -1, false),
+    COMMON_ACCOUNT_ERROR("参数异常", -2, false),
+    COMMON_DATA_NOT_EXIST("数据不存在或者已经被删除", -101, false),
+    COMMON_VERSION_NOT_EXIST("版本号不存在", -102, false),
+    BYTEDANCE_VIDEO_UPLOAD_FAIL("今日头条视频文件上传失败", -201, false),
+    MATERIAL_UPLOAD_FAIL("素材文件上传失败", -201, false),
+    IMAGE_NUMBER_SHORTAGE("今日头条图片文件上传失败", -202, false),
+    BYTEDANCE_IMAGE_UPLOAD_FAIL("今日头条图片文件上传失败", -203, false),
+    TEMPLATE_NAME_HAS_EXIST("模板名称已存在", -204, false),
+    TEMPLATE_NAME_IS_NULL("模板名称为空", -205, false),
+    FILE_HAS_NOT_UPLOAD("文件尚未上传", -206, false),
+    ACCOUNT_HAS_NOT_EXIST("该用户信息不存在", -207, false),
+    USERNAME_OR_PASSWORD_ERROR("登录用户名或密码错误", -208, false),
+    ACCOUNT_HAS_NO_AUTHORIZE("该用户尚未进行授权", -209, false),
+    FILE_HAS_UPLOAD("文件已经上传过", 0, false),
+    COMMON_SERVER_ERROR("服务器内部错误", -102, false),
+    CTOP_HAS_NO_PERFORMANCE_STATISTIC_LOGIC("绩效统计逻辑尚未确定", -103, false),
+    CTOP_SCRIPT_FILE_NOT_EXIST("脚本文件不存在", -104, false),
+    CTOP_PROJECT_NOT_EXIST("项目不存在", -105, false),
+    CTOP_FEISHU_EMAIL_NOT_EXIST("飞书账户未绑定邮箱", -106, false),
+    CTOP_FEISHU_ACCOUNT_NOT_EXIST("该用户未注册系统账户,请联系管理员", -107, false),
+
+    KUAISHOU_CRAWLER_APP_EXIT("快手app异常退出", -1001, false),
+    KUAISHOU_CRAWLER_APP_ELEMENT_ERROR("快手元素异常", -1002, false),
+    KUAISHOU_CRAWLER_APP_ELEMENT_IS_NULL("此快手元素获取不到", -1003, false),
+    KUAISHOU_CRAWLER_APP_MOBILE_CODE_IS_NULL("短信验证码未获取到", -1004, false),
+    KUAISHOU_CRAWLER_APP_ELEMENT_DETAIL_ERROR("快手元素内容异常", -1005, false),
+    KUAISHOU_CRAWLER_APP_NO_ENABLED_DEVICE("暂无可用设备", -2001, false),
+    KUAISHOU_OPTINIZER_CONFIG_IS_NULL("快手媒体优化师配置信息不全", -3001, false),
+    KUAISHOU_OPTINIZER_PERSONNAL_DATA_IS_NULL("快手媒体优化师个人消耗数据不存在", -3002, false),
+    BYTEDANCE_OPTINIZER_PERSONNAL_DATA_IS_NULL("头条媒体优化师个人消耗数据不存在", -3003, false),
+    OPTINIZER_PERSONNAL_DATA_IS_NULL("优化师个人消耗数据不存在", -3004, false),
+
+    BYTEDACNE_API_GROUP_CREATE_ERROR("头条API创建广告组异常",-4001,false),
+
+    KUAISHOU_CRAWLER_APP_SUCCESS("爬虫运行正常", 0, true),
+    KUAISHOU_CRAWLER_APP_TASK_END("爬虫任务运行结束", 1, true),
+    KUAISHOU_CRAWLER_APP_JOB_StART("任务开始执行开始", 0, true),
+    KUAISHOU_CRAWLER_APP_JOB_END("任务运行结束", 1, true),
+    MODEL_VIDEO_NOT_EXIST("模板视频不存在或者已经被删除", 0, false);
+    private String desc;
+    private int code;
+    private boolean flag;
+
+    StatusCode(String desc, int code, boolean flag) {
+        this.desc = desc;
+        this.code = code;
+        this.flag = flag;
+    }
+
+    public static String getDesc(int code) {
+        for (StatusCode error : StatusCode.values()) {
+            if (error.getCode() == code) {
+                return error.getDesc();
+            }
+        }
+        return null;
+    }
+
+    public static Boolean getFlag(int code) {
+        for (StatusCode error : StatusCode.values()) {
+            if (error.getCode() == code) {
+                return error.getFlag();
+            }
+        }
+        return null;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public boolean getFlag() {
+        return flag;
+    }
+
+    public void setFlag(boolean flag) {
+        this.flag = flag;
+    }
+
+}

+ 551 - 0
src/main/java/cn/com/ctop/okr/utils/oConvertUtils.java

@@ -0,0 +1,551 @@
+package cn.com.ctop.okr.utils;
+
+import org.apache.commons.lang.StringEscapeUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.sql.Date;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * @Author  张代浩
+ *
+ */
+public class oConvertUtils {
+	public static boolean isEmpty(Object object) {
+		if (object == null) {
+			return (true);
+		}
+		if ("".equals(object)) {
+			return (true);
+		}
+		if ("null".equals(object)) {
+			return (true);
+		}
+		return (false);
+	}
+
+	public static boolean isNotEmpty(Object object) {
+        if (object != null && !"".equals(object) && !"null".equals(object)) {
+			return (true);
+		}
+		return (false);
+	}
+
+	public static String decode(String strIn, String sourceCode, String targetCode) {
+		String temp = code2code(strIn, sourceCode, targetCode);
+		return temp;
+	}
+
+	private static String code2code(String strIn, String sourceCode, String targetCode) {
+        if (strIn == null || "".equals(strIn.trim())) {
+			return strIn;
+		}
+		String strOut = null;
+		try {
+			byte[] b = strIn.getBytes(sourceCode);
+			strOut = new String(b, targetCode);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+		return strOut;
+	}
+
+	public static int getInt(String s, int defval) {
+		if (s == null || s.trim().equals("")) {
+			return (defval);
+		}
+		try {
+			return (Integer.parseInt(s));
+		} catch (NumberFormatException e) {
+			return (defval);
+		}
+	}
+
+	public static int getInt(String s) {
+		if (s == null || s.trim().equals("")) {
+			return 0;
+		}
+		try {
+			return (Integer.parseInt(s));
+		} catch (NumberFormatException e) {
+			return 0;
+		}
+	}
+
+	public static int getInt(String s, Integer df) {
+		if (s == null || s.trim().equals("")) {
+			return df;
+		}
+		try {
+			return (Integer.parseInt(s));
+		} catch (NumberFormatException e) {
+			return 0;
+		}
+	}
+
+	public static Integer[] getInts(String[] s) {
+		if (s == null) {
+			return null;
+		}
+		Integer[] integer = new Integer[s.length];
+		for (int i = 0; i < s.length; i++) {
+			integer[i] = Integer.parseInt(s[i]);
+		}
+		return integer;
+
+	}
+
+	public static double getDouble(String s, double defval) {
+		if (s == null || s.trim().equals("")) {
+			return (defval);
+		}
+		try {
+			return (Double.parseDouble(s));
+		} catch (NumberFormatException e) {
+			return (defval);
+		}
+	}
+
+	public static double getDou(Double s, double defval) {
+		if (s == null) {
+			return (defval);
+		}
+		return s;
+	}
+
+	public static int getInt(Object object, int defval) {
+		if (isEmpty(object)) {
+			return (defval);
+		}
+		try {
+			return (Integer.parseInt(object.toString()));
+		} catch (NumberFormatException e) {
+			return (defval);
+		}
+	}
+
+	public static Integer getInt(Object object) {
+		if (isEmpty(object)) {
+			return null;
+		}
+		try {
+			return (Integer.parseInt(object.toString()));
+		} catch (NumberFormatException e) {
+			return null;
+		}
+	}
+
+	public static int getInt(BigDecimal s, int defval) {
+		if (s == null) {
+			return (defval);
+		}
+		return s.intValue();
+	}
+
+	public static Integer[] getIntegerArray(String[] object) {
+		int len = object.length;
+		Integer[] result = new Integer[len];
+		try {
+			for (int i = 0; i < len; i++) {
+                result[i] = Integer.getInteger(object[i].trim());
+			}
+			return result;
+		} catch (NumberFormatException e) {
+			return null;
+		}
+	}
+
+	public static String getString(String s) {
+		return (getString(s, ""));
+	}
+
+	/**
+	 * 转义成Unicode编码
+	 * @param s
+	 * @return
+	 */
+    public static String escapeJava(Object s) {
+		return StringEscapeUtils.escapeJava(getString(s));
+    }
+
+	public static String getString(Object object) {
+		if (isEmpty(object)) {
+			return "";
+		}
+		return (object.toString().trim());
+	}
+
+	public static String getString(int i) {
+		return (String.valueOf(i));
+	}
+
+	public static String getString(float i) {
+		return (String.valueOf(i));
+	}
+
+	public static String getString(String s, String defval) {
+		if (isEmpty(s)) {
+			return (defval);
+		}
+		return (s.trim());
+	}
+
+	public static String getString(Object s, String defval) {
+		if (isEmpty(s)) {
+			return (defval);
+		}
+		return (s.toString().trim());
+	}
+
+	public static long stringToLong(String str) {
+		return Long.valueOf(str);
+	}
+
+	/**
+	 * 获取本机IP
+	 */
+	public static String getIp() {
+		String ip = null;
+		try {
+			InetAddress address = InetAddress.getLocalHost();
+			ip = address.getHostAddress();
+
+		} catch (UnknownHostException e) {
+			e.printStackTrace();
+		}
+		return ip;
+	}
+
+	/**
+	 * 判断一个类是否为基本数据类型。
+     *
+	 * @param clazz
+	 *            要判断的类。
+	 * @return true 表示为基本数据类型。
+	 */
+	private static boolean isBaseDataType(Class clazz) {
+		return (clazz.equals(String.class) || clazz.equals(Integer.class) || clazz.equals(Byte.class) || clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Character.class) || clazz.equals(Short.class) || clazz.equals(BigDecimal.class) || clazz.equals(BigInteger.class) || clazz.equals(Boolean.class) || clazz.equals(Date.class) || clazz.isPrimitive());
+	}
+
+	/**
+	 * @param request
+	 *            IP
+	 * @return IP Address
+	 */
+	public static String getIpAddrByRequest(HttpServletRequest request) {
+		String ip = request.getHeader("x-forwarded-for");
+		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("Proxy-Client-IP");
+		}
+		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("WL-Proxy-Client-IP");
+		}
+		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getRemoteAddr();
+		}
+		return ip;
+	}
+
+	/**
+	 * @return 本机IP
+	 * @throws SocketException
+	 */
+	public static String getRealIp() throws SocketException {
+        // 本地IP,如果没有配置外网IP则返回它
+        String localip = null;
+        // 外网IP
+        String netip = null;
+
+		Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
+		InetAddress ip = null;
+        // 是否找到外网IP
+        boolean finded = false;
+		while (netInterfaces.hasMoreElements() && !finded) {
+			NetworkInterface ni = netInterfaces.nextElement();
+			Enumeration<InetAddress> address = ni.getInetAddresses();
+			while (address.hasMoreElements()) {
+				ip = address.nextElement();
+				if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(':') == -1) {
+					netip = ip.getHostAddress();
+					finded = true;
+					break;
+				} else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(':') == -1) {
+					localip = ip.getHostAddress();
+				}
+			}
+		}
+
+		if (netip != null && !"".equals(netip)) {
+			return netip;
+		} else {
+			return localip;
+		}
+	}
+
+	/**
+	 * java去除字符串中的空格、回车、换行符、制表符
+     *
+	 * @param str
+	 * @return
+	 */
+	public static String replaceBlank(String str) {
+		String dest = "";
+		if (str != null) {
+			Pattern p = Pattern.compile("\\s*|\t|\r|\n");
+			Matcher m = p.matcher(str);
+			dest = m.replaceAll("");
+		}
+		return dest;
+
+	}
+
+	/**
+	 * 判断元素是否在数组内
+     *
+	 * @param substring
+	 * @param source
+	 * @return
+	 */
+	public static boolean isIn(String substring, String[] source) {
+		if (source == null || source.length == 0) {
+			return false;
+		}
+		for (int i = 0; i < source.length; i++) {
+			String aSource = source[i];
+			if (aSource.equals(substring)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * SET转换MAP
+     *
+	 * @param setobj
+	 * @return
+	 */
+    public static Map<Object, Object> setToMap(Set<Object> setobj) {
+		Map<Object, Object> map = new HashMap<>();
+		for (Iterator iterator = setobj.iterator(); iterator.hasNext();) {
+			Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) iterator.next();
+			map.put(entry.getKey().toString(), entry.getValue() == null ? "" : entry.getValue().toString().trim());
+		}
+		return map;
+
+	}
+
+    public static boolean isInnerIp(String ipAddress) {
+		boolean isInnerIp = false;
+		long ipNum = getIpNum(ipAddress);
+		/**
+		 * 私有IP:A类 10.0.0.0-10.255.255.255 B类 172.16.0.0-172.31.255.255 C类 192.168.0.0-192.168.255.255 当然,还有127这个网段是环回地址
+		 **/
+		long aBegin = getIpNum("10.0.0.0");
+		long aEnd = getIpNum("10.255.255.255");
+		long bBegin = getIpNum("172.16.0.0");
+		long bEnd = getIpNum("172.31.255.255");
+		long cBegin = getIpNum("192.168.0.0");
+		long cEnd = getIpNum("192.168.255.255");
+        isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd) || "127.0.0.1".equals(ipAddress);
+		return isInnerIp;
+	}
+
+	private static long getIpNum(String ipAddress) {
+		String[] ip = ipAddress.split("\\.");
+		long a = Integer.parseInt(ip[0]);
+		long b = Integer.parseInt(ip[1]);
+		long c = Integer.parseInt(ip[2]);
+		long d = Integer.parseInt(ip[3]);
+		return a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
+	}
+
+	private static boolean isInner(long userIp, long begin, long end) {
+		return (userIp >= begin) && (userIp <= end);
+	}
+
+	/**
+	 * 将下划线大写方式命名的字符串转换为驼峰式。
+	 * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
+	 * 例如:hello_world->helloWorld
+     *
+	 * @param name
+	 *            转换前的下划线大写方式命名的字符串
+	 * @return 转换后的驼峰式命名的字符串
+	 */
+	public static String camelName(String name) {
+		StringBuilder result = new StringBuilder();
+		// 快速检查
+		if (name == null || name.isEmpty()) {
+			// 没必要转换
+			return "";
+		} else if (!name.contains("_")) {
+			// 不含下划线,仅将首字母小写
+			//update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+			//update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+			return name.substring(0, 1).toLowerCase() + name.substring(1).toLowerCase();
+			//update-end--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+		}
+		// 用下划线将原始字符串分割
+        String[] camels = name.split("_");
+		for (String camel : camels) {
+			// 跳过原始字符串中开头、结尾的下换线或双重下划线
+			if (camel.isEmpty()) {
+				continue;
+			}
+			// 处理真正的驼峰片段
+			if (result.length() == 0) {
+				// 第一个驼峰片段,全部字母都小写
+				result.append(camel.toLowerCase());
+			} else {
+				// 其他的驼峰片段,首字母大写
+				result.append(camel.substring(0, 1).toUpperCase());
+				result.append(camel.substring(1).toLowerCase());
+			}
+		}
+		return result.toString();
+	}
+
+	/**
+	 * 将下划线大写方式命名的字符串转换为驼峰式。
+	 * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
+	 * 例如:hello_world,test_id->helloWorld,testId
+	 *
+     * @param names
+	 *            转换前的下划线大写方式命名的字符串
+	 * @return 转换后的驼峰式命名的字符串
+	 */
+	public static String camelNames(String names) {
+        if (names == null || "".equals(names)) {
+			return null;
+		}
+		StringBuffer sf = new StringBuffer();
+		String[] fs = names.split(",");
+		for (String field : fs) {
+			field = camelName(field);
+			sf.append(field + ",");
+		}
+		String result = sf.toString();
+		return result.substring(0, result.length() - 1);
+	}
+
+	//update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+	/**
+	 * 将下划线大写方式命名的字符串转换为驼峰式。(首字母写)
+	 * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
+	 * 例如:hello_world->HelloWorld
+     *
+	 * @param name
+	 *            转换前的下划线大写方式命名的字符串
+	 * @return 转换后的驼峰式命名的字符串
+	 */
+	public static String camelNameCapFirst(String name) {
+		StringBuilder result = new StringBuilder();
+		// 快速检查
+		if (name == null || name.isEmpty()) {
+			// 没必要转换
+			return "";
+		} else if (!name.contains("_")) {
+			// 不含下划线,仅将首字母小写
+			return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();
+		}
+		// 用下划线将原始字符串分割
+        String[] camels = name.split("_");
+		for (String camel : camels) {
+			// 跳过原始字符串中开头、结尾的下换线或双重下划线
+			if (camel.isEmpty()) {
+				continue;
+			}
+			// 其他的驼峰片段,首字母大写
+			result.append(camel.substring(0, 1).toUpperCase());
+			result.append(camel.substring(1).toLowerCase());
+		}
+		return result.toString();
+	}
+	//update-end--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+
+    /**
+	 * 将驼峰命名转化成下划线
+	 * @param para
+	 * @return
+	 */
+	public static String camelToUnderline(String para){
+        if(para.length()<3){
+            return para.toLowerCase();
+        }
+        StringBuilder sb=new StringBuilder(para);
+        //定位
+        int temp = 0;
+        //从第三个字符开始 避免命名不规范
+        for(int i=2;i<para.length();i++){
+            if(Character.isUpperCase(para.charAt(i))){
+                sb.insert(i+temp, "_");
+                temp+=1;
+            }
+        }
+        return sb.toString().toLowerCase();
+	}
+
+	/**
+	 * 随机数
+	 * @param place 定义随机数的位数
+	 */
+	public static String randomGen(int place) {
+		String base = "qwertyuioplkjhgfdsazxcvbnmQAZWSXEDCRFVTGBYHNUJMIKLOP0123456789";
+		StringBuffer sb = new StringBuffer();
+		Random rd = new Random();
+		for(int i=0;i<place;i++) {
+			sb.append(base.charAt(rd.nextInt(base.length())));
+		}
+		return sb.toString();
+	}
+
+    /**
+	 * 获取类的所有属性,包括父类
+     *
+	 * @param object
+	 * @return
+	 */
+	public static Field[] getAllFields(Object object) {
+		Class<?> clazz = object.getClass();
+		List<Field> fieldList = new ArrayList<>();
+		while (clazz != null) {
+			fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
+			clazz = clazz.getSuperclass();
+		}
+		Field[] fields = new Field[fieldList.size()];
+		fieldList.toArray(fields);
+		return fields;
+	}
+
+
+    /**
+	  * 将map的key全部转成小写
+	 * @param list
+	 * @return
+	 */
+	public static List<Map<String, Object>> toLowerCasePageList(List<Map<String, Object>> list){
+		List<Map<String, Object>> select = new ArrayList<>();
+		for (Map<String, Object> row : list) {
+			 Map<String, Object> resultMap = new HashMap<>();
+            Set<String> keySet = row.keySet();
+            for (String key : keySet) {
+                String newKey = key.toLowerCase();
+                resultMap.put(newKey, row.get(key));
+			 }
+			 select.add(resultMap);
+		}
+		return select;
+	}
+}

+ 98 - 0
src/main/java/cn/com/ctop/okr/vo/LoginUser.java

@@ -0,0 +1,98 @@
+package cn.com.ctop.okr.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+/**
+ * <p>
+ * 在线用户信息
+ * </p>
+ *
+ * @Author scott
+ * @since 2018-12-20
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class LoginUser {
+
+	/**
+	 * 登录人id
+	 */
+	private String id;
+
+	/**
+	 * 登录人账号
+	 */
+	private String username;
+
+	/**
+	 * 登录人名字
+	 */
+	private String realname;
+
+     /**
+      * 当前登录部门code
+      */
+    private String orgCode;
+	/**
+	 * 头像
+	 */
+	private String avatar;
+
+	/**
+	 * 生日
+	 */
+	@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
+	@DateTimeFormat(pattern = "yyyy-MM-dd")
+	private Date birthday;
+
+	/**
+	 * 性别(1:男 2:女)
+	 */
+	private Integer sex;
+
+	/**
+	 * 电子邮件
+	 */
+	private String email;
+
+	/**
+	 * 电话
+	 */
+	private String phone;
+
+	/**
+	 * 状态(1:正常 2:冻结 )
+	 */
+	private Integer status;
+
+	private String delFlag;
+	/**
+     * 同步工作流引擎1同步0不同步
+     */
+    private String activitiSync;
+
+	@Override
+	public String toString() {
+		return "LoginUser{" +
+				"id='" + id + '\'' +
+				", username='" + username + '\'' +
+				", realname='" + realname + '\'' +
+				", orgCode='" + orgCode + '\'' +
+				", avatar='" + avatar + '\'' +
+				", birthday=" + birthday +
+				", sex=" + sex +
+				", email='" + email + '\'' +
+				", phone='" + phone + '\'' +
+				", status=" + status +
+				", delFlag='" + delFlag + '\'' +
+				", activitiSync='" + activitiSync + '\'' +
+				'}';
+	}
+}

+ 103 - 0
src/main/java/cn/com/ctop/okr/vo/Result.java

@@ -0,0 +1,103 @@
+package cn.com.ctop.okr.vo;
+
+import cn.com.ctop.okr.constant.CommonConstant;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ *   接口返回数据格式
+ * @author scott
+ * @email jeecgos@163.com
+ * @date  2019年1月19日
+ */
+@Data
+public class Result<T> implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 成功标志
+	 */
+	private boolean success = true;
+
+	/**
+	 * 返回处理消息
+	 */
+	private String message = "操作成功!";
+
+	/**
+	 * 返回代码
+	 */
+	private Integer code = 0;
+
+	/**
+	 * 返回数据对象 data
+	 */
+	private T result;
+
+	/**
+	 * 时间戳
+	 */
+	private long timestamp = System.currentTimeMillis();
+	public Result() {
+
+	}
+
+	public Result<T> error500(String message) {
+		this.message = message;
+		this.code = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
+		this.success = false;
+		return this;
+	}
+
+	public Result<T> success(String message) {
+		this.message = message;
+		this.code = CommonConstant.SC_OK_200;
+		this.success = true;
+		return this;
+	}
+
+	public static Result<Object> ok() {
+		Result<Object> r = new Result<Object>();
+		r.setSuccess(true);
+		r.setCode(CommonConstant.SC_OK_200);
+		r.setMessage("成功");
+		return r;
+	}
+
+	public static Result<Object> ok(String msg) {
+		Result<Object> r = new Result<Object>();
+		r.setSuccess(true);
+		r.setCode(CommonConstant.SC_OK_200);
+		r.setMessage(msg);
+		return r;
+	}
+
+	public static Result<Object> ok(Object data) {
+		Result<Object> r = new Result<Object>();
+		r.setSuccess(true);
+		r.setCode(CommonConstant.SC_OK_200);
+		r.setResult(data);
+		return r;
+	}
+
+	public static Result<Object> error(String msg) {
+		return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
+	}
+
+	public static Result<Object> error(int code, String msg) {
+		Result<Object> r = new Result<Object>();
+		r.setCode(code);
+		r.setMessage(msg);
+		r.setSuccess(false);
+		return r;
+	}
+
+	/**
+	 * 无权限访问返回结果
+	 */
+	public static Result<Object> noauth(String msg) {
+		return error(CommonConstant.SC_JEECG_NO_AUTHZ, msg);
+	}
+}