Browse Source

Merge remote-tracking branch 'origin/dev' into dev

xlq 8 years ago
parent
commit
2c1cd7faac
52 changed files with 2302 additions and 250 deletions
  1. 14 62
      build.gradle
  2. 2 2
      gradle/persist-publish.gradle
  3. 111 0
      sso-common/src/main/java/com/uas/sso/entity/ApplyUserSpaceView.java
  4. 272 1
      sso-common/src/main/java/com/uas/sso/util/AccountUtils.java
  5. 1 0
      sso-manage-console-web/package.json
  6. 34 0
      sso-manage-console-web/src/assets/js/utils.js
  7. 30 12
      sso-manage-console-web/src/components/accounts/appeals/AppealHome.vue
  8. 3 0
      sso-manage-console-web/src/components/accounts/common/CommonHome.vue
  9. 197 4
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseAdmin.vue
  10. 6 3
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseHome.vue
  11. 254 1
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseMembers.vue
  12. 0 1
      sso-manage-console-web/src/components/accounts/users/UserBasicInfo.vue
  13. 241 88
      sso-manage-console-web/src/components/accounts/users/UserHome.vue
  14. 1 1
      sso-manage-console-web/src/store/modules/enterprises.js
  15. 1 1
      sso-manage-console-web/webpack/webpack.base.conf.js
  16. 12 0
      sso-manage-console-web/yarn.lock
  17. 2 2
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/ManageConsoleApplication.java
  18. 9 0
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/api/ChangeAdminController.java
  19. 26 8
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/api/UserManageController.java
  20. 17 0
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/dao/AdminChangeRecordDao.java
  21. 80 0
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/dto/AddNewUserInfo.java
  22. 117 0
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/entity/AdminChangeRecord.java
  23. 19 0
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/entity/AdminChangeType.java
  24. 98 0
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/entity/FlowBase.java
  25. 10 0
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/ChangeAdminService.java
  26. 20 1
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/UserBackendService.java
  27. 37 1
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/AppealServiceImpl.java
  28. 29 1
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/ChangeAdminServiceImpl.java
  29. 80 4
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/UserBackendServiceImpl.java
  30. 41 13
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/UserSpaceServiceImpl.java
  31. 18 0
      sso-server/build.gradle
  32. 6 1
      sso-server/src/main/java/com/uas/sso/controller/ChangeAdminController.java
  33. 11 0
      sso-server/src/main/java/com/uas/sso/controller/LoginController.java
  34. 2 2
      sso-server/src/main/java/com/uas/sso/controller/PersonalRegisterController.java
  35. 12 0
      sso-server/src/main/java/com/uas/sso/controller/UserManagerController.java
  36. 14 1
      sso-server/src/main/java/com/uas/sso/controller/UserspaceManagerController.java
  37. 1 1
      sso-server/src/main/java/com/uas/sso/core/Const.java
  38. 34 1
      sso-server/src/main/java/com/uas/sso/core/Status.java
  39. 11 1
      sso-server/src/main/java/com/uas/sso/core/Type.java
  40. 4 4
      sso-server/src/main/java/com/uas/sso/dao/UserspaceDao.java
  41. 23 7
      sso-server/src/main/java/com/uas/sso/entity/App.java
  42. 194 0
      sso-server/src/main/java/com/uas/sso/entity/PageStyle.java
  43. 16 10
      sso-server/src/main/java/com/uas/sso/entity/User.java
  44. 2 1
      sso-server/src/main/java/com/uas/sso/entity/UserRecord.java
  45. 1 1
      sso-server/src/main/java/com/uas/sso/entity/Userspace.java
  46. 1 0
      sso-server/src/main/java/com/uas/sso/service/ApplyUserSpaceService.java
  47. 7 0
      sso-server/src/main/java/com/uas/sso/service/UserService.java
  48. 9 1
      sso-server/src/main/java/com/uas/sso/service/UserspaceService.java
  49. 17 4
      sso-server/src/main/java/com/uas/sso/service/impl/ApplyUserSpaceServiceImpl.java
  50. 21 0
      sso-server/src/main/java/com/uas/sso/service/impl/UserServiceImpl.java
  51. 14 9
      sso-server/src/main/java/com/uas/sso/service/impl/UserspaceServiceImpl.java
  52. 120 0
      sso-server/src/main/java/com/uas/sso/util/InfoAsyncUtils.java

+ 14 - 62
build.gradle

@@ -1,70 +1,22 @@
-buildscript {
-    repositories {
-        maven { url "https://plugins.gradle.org/m2/" }
-        maven { url 'http://113.105.74.141:8081/artifactory/libs-release-local' }
-        maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }
-        jcenter()
-    }
-    dependencies {
-        classpath 'com.uas.demo.mesh:spring-boot-docker-plugin:1.0.0'
-        classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.6.RELEASE"
-    }
-}
 
-group 'com.uas.sso'
-version '0.0.1-SNAPSHOT'
+allprojects {
+    group = 'com.uas.sso'
+    version = '0.0.1-SNAPSHOT'
 
-apply plugin: 'java'
-apply plugin: 'maven'
-apply plugin: "org.springframework.boot"
-apply plugin: 'com.uas.docker.spring.boot'
-
-sourceCompatibility = 1.8
-
-repositories {
-    maven { url 'http://10.10.101.21:8081/artifactory/libs-snapshot' }
-    maven { url 'http://10.10.101.21:8081/artifactory/libs-release' }
-    maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }
-    jcenter()
-    mavenCentral()
-}
-
-dependencyManagement {
-    imports {
-        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Camden.SR5'
-    }
+    apply plugin: 'maven'
 }
 
-dependencies {
-    compile "org.springframework.boot:spring-boot-starter-web"
-    compile("org.springframework.boot:spring-boot-starter-thymeleaf")
-    // Spring Boot will create a MultipartConfigElement bean and make itself ready for file uploads.
-    compile("org.springframework.cloud:spring-cloud-starter-feign")
-    compile("org.springframework.cloud:spring-cloud-starter-ribbon")
-    compile("org.springframework.cloud:spring-cloud-starter-hystrix")
-    compile "com.uas.cloud.base.commons:common:0.1.0"
+subprojects {
+    apply plugin: 'java'
+    sourceCompatibility = 1.8
 
-    compile("org.springframework.boot:spring-boot-devtools")
-
-    compile("org.springframework.boot:spring-boot-starter-actuator")
-    compile("org.webjars:webjars-locator")
-
-    compile "commons-fileupload:commons-fileupload:1.3.1"
+    repositories {
+        /*mavenLocal()*/
 
-    testCompile "org.springframework.boot:spring-boot-starter-test"
-}
+        jcenter()
+        maven { url "http://113.105.74.141:8081/artifactory/libs-release" }
+        maven { url "http://113.105.74.141:8081/artifactory/libs-snapshot" }
+    }
 
-customDocker {
-    baseImage 'hub.c.163.com/library/java:8-jre-alpine'
-    registry '10.10.100.200:5000'
+    [compileJava, compileTestJava, javadoc]*.options*.encoding = 'UTF-8'
 }
-
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: "http://10.10.101.21:8081/artifactory/libs-release-local") {
-                authentication(userName: "yingp", password: "111111")
-            }
-        }
-    }
-}.dependsOn build

+ 2 - 2
gradle/persist-publish.gradle

@@ -20,7 +20,7 @@ task persistJar(type: Jar) {
     baseName "sso-persist"
     from sourceSets.main.output
     exclude "spring", "config"
-    include "**/entity/**", "**/dao/**", "**/core/**"
+    include "**/entity/**", "**/dao/**", "**/core/**", "**/util/**"
 }
 
 task persistSourcesJar(type: Jar) {
@@ -28,7 +28,7 @@ task persistSourcesJar(type: Jar) {
     classifier 'sources'
     from sourceSets.main.allSource
     exclude "spring", "config"
-    include "**/entity/**", "**/dao/**"
+    include "**/entity/**", "**/dao/**", "**/core/**", "**/util/**"
 }
 
 // Create artifact for other project import

+ 111 - 0
sso-common/src/main/java/com/uas/sso/entity/ApplyUserSpaceView.java

@@ -0,0 +1,111 @@
+package com.uas.sso.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author wangmh
+ * @create 2018-01-12 16:12
+ * @desc 用户绑定企业
+ **/
+public class ApplyUserSpaceView implements Serializable {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户uu号
+     */
+    private Long userUU;
+
+    /**
+     * 用户
+     */
+    private UserView userView;
+
+    /**
+     * 企业uu号
+     */
+    private Long spaceUU;
+
+    /**
+     * 企业信息
+     */
+    private UserSpaceView userSpaceView;
+
+    /**
+     * 申请状态
+     */
+    private int status;
+
+    /**
+     * 申请时间
+     */
+    private Date submitDate;
+
+    /**
+     * 处理时间
+     */
+    private Date processTime;
+
+    public ApplyUserSpaceView() {
+    }
+
+    public Long getUserUU() {
+        return userUU;
+    }
+
+    public void setUserUU(Long userUU) {
+        this.userUU = userUU;
+    }
+
+    public UserView getUserView() {
+        return userView;
+    }
+
+    public void setUserView(UserView userView) {
+        this.userView = userView;
+    }
+
+    public Long getSpaceUU() {
+        return spaceUU;
+    }
+
+    public void setSpaceUU(Long spaceUU) {
+        this.spaceUU = spaceUU;
+    }
+
+    public UserSpaceView getUserSpaceView() {
+        return userSpaceView;
+    }
+
+    public void setUserSpaceView(UserSpaceView userSpaceView) {
+        this.userSpaceView = userSpaceView;
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+    public void setStatus(int status) {
+        this.status = status;
+    }
+
+    public Date getSubmitDate() {
+        return submitDate;
+    }
+
+    public void setSubmitDate(Date submitDate) {
+        this.submitDate = submitDate;
+    }
+
+    public Date getProcessTime() {
+        return processTime;
+    }
+
+    public void setProcessTime(Date processTime) {
+        this.processTime = processTime;
+    }
+}

+ 272 - 1
sso-common/src/main/java/com/uas/sso/util/AccountUtils.java

@@ -2,18 +2,25 @@ package com.uas.sso.util;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.TypeReference;
 import com.uas.sso.AccountConfig;
 import com.uas.sso.ResultWrap;
 import com.uas.sso.common.util.HttpUtil;
+import com.uas.sso.entity.ApplyUserSpaceView;
 import com.uas.sso.entity.UserSpaceView;
 import com.uas.sso.entity.UserView;
+import org.springframework.data.domain.Page;
 import org.springframework.ui.ModelMap;
 import org.springframework.util.StringUtils;
 
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * 客户端使用,操作企业资料、用户资料
  *
- * @author yingp
+ * @author wangmh
  *
  */
 public class AccountUtils {
@@ -78,6 +85,31 @@ public class AccountUtils {
      * @throws Exception
      */
     public static void unbindUserSpace(Long userUU, Long spaceUU) throws Exception {
+        String saveUrl = AccountConfig.getUserSaveUrl();
+        if (!StringUtils.isEmpty(saveUrl)) {
+            ModelMap formData = new ModelMap();
+            formData.put("_operate", "bind");
+            formData.put("userUU", userUU);
+            formData.put("spaceUU", spaceUU);
+            HttpUtil.ResponseWrap res = HttpUtil.doPost(saveUrl, formData);
+            if (!res.isSuccess()) {
+                throw new Exception(res.getContent());
+            } else {
+                ResultWrap result = JSON.parseObject(res.getContent(), ResultWrap.class);
+                if (!result.isSuccess()) {
+                    throw new Exception(result.getErrMsg());
+                }
+            }
+        }
+    }
+
+    /**
+     * 用户绑定企业
+     * @param userUU 用户uu号
+     * @param spaceUU 企业uu号
+     * @throws Exception
+     */
+    public static void bindUserSpace(Long userUU, Long spaceUU) throws Exception {
         String saveUrl = AccountConfig.getUserSaveUrl();
         if (!StringUtils.isEmpty(saveUrl)) {
             ModelMap formData = new ModelMap();
@@ -95,6 +127,245 @@ public class AccountUtils {
             }
         }
     }
+
+    /**
+     * 根据营业执照号获得企业信息
+     * @param businessCode 营业执照号
+     * @return
+     */
+    public List<UserSpaceView> findByBusinessCode(String businessCode) throws Exception {
+        String saveUrl = AccountConfig.getSpaceSaveUrl();
+        if (!StringUtils.isEmpty(saveUrl)) {
+            ModelMap formData = new ModelMap();
+            saveUrl = saveUrl + "/info/businessCode";
+            formData.put("businessCode", businessCode);
+            HttpUtil.ResponseWrap res = HttpUtil.doPost(saveUrl, formData);
+            if (!res.isSuccess()) {
+                throw new Exception(res.getContent());
+            } else {
+                ResultWrap result = JSON.parseObject(res.getContent(), ResultWrap.class);
+                if (!result.isSuccess()) {
+                    throw new Exception(result.getErrMsg());
+                } else if (result.getContent() != null){
+                    return JSON.parseArray(result.getContent().toString(), UserSpaceView.class);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 根据营业执照号获得企业信息
+     * @param spaceName 营业执照号
+     * @return
+     */
+    public List<UserSpaceView> findBySpaceName(String spaceName) throws Exception {
+        String saveUrl = AccountConfig.getSpaceSaveUrl();
+        if (!StringUtils.isEmpty(saveUrl)) {
+            ModelMap formData = new ModelMap();
+            saveUrl = saveUrl + "/info/businessCode";
+            formData.put("spaceName", spaceName);
+            HttpUtil.ResponseWrap res = HttpUtil.doPost(saveUrl, formData);
+            if (!res.isSuccess()) {
+                throw new Exception(res.getContent());
+            } else {
+                ResultWrap result = JSON.parseObject(res.getContent(), ResultWrap.class);
+                if (!result.isSuccess()) {
+                    throw new Exception(result.getErrMsg());
+                } else if (result.getContent() != null){
+                    return JSON.parseArray(result.getContent().toString(), UserSpaceView.class);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 用户申请绑定企业
+     * @param userUU 用户uu号
+     * @param spaceUU 企业uu号
+     * @throws Exception
+     */
+    public static void applyUserSpace(Long userUU, Long spaceUU) throws Exception {
+        String saveUrl = AccountConfig.getUserSaveUrl();
+        if (!StringUtils.isEmpty(saveUrl)) {
+            ModelMap formData = new ModelMap();
+            saveUrl = saveUrl + "/apply/bind";
+            formData.put("userUU", userUU);
+            formData.put("spaceUU", spaceUU);
+            HttpUtil.ResponseWrap res = HttpUtil.doPost(saveUrl, formData);
+            if (!res.isSuccess()) {
+                throw new Exception(res.getContent());
+            } else {
+                ResultWrap result = JSON.parseObject(res.getContent(), ResultWrap.class);
+                if (!result.isSuccess()) {
+                    throw new Exception(result.getErrMsg());
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 分页查找个人申请记录
+     *
+     * @author wangmh
+     * @date 2018/2/1 15:47
+     * @param userUU 用户uu号
+     * @param page 页数(从0开始)
+     * @param size 每页大小
+     * @return
+     * @throws Exception
+     */
+    public static Page<ApplyUserSpaceView> findApplyInfo(Long userUU, int page, int size) throws Exception {
+        String url = AccountConfig.getUserSaveUrl();
+        if (!StringUtils.isEmpty(url)) {
+            url = url + "/apply/info";
+            ModelMap data = new ModelMap();
+            data.put("userUU", userUU);
+            data.put("page", page);
+            data.put("size", size);
+            HttpUtil.ResponseWrap res = HttpUtil.doGet(url, data);
+            if (!res.isSuccess()) {
+                throw new Exception(res.getContent());
+            } else {
+                ResultWrap result = JSON.parseObject(res.getContent(), ResultWrap.class);
+                if (!result.isSuccess()) {
+                    throw new Exception(result.getErrMsg());
+                } else if (result.getContent() != null) {
+                    return JSON.parseObject(result.getContent().toString(), new TypeReference<Page<ApplyUserSpaceView>>() {
+                    });
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 统计该企业审批记录数量
+     * @param spaceUU 企业uu号
+     * @return
+     * @throws Exception
+     */
+    public static Map<String, Integer> applyCount(Long spaceUU) throws Exception {
+        String url = AccountConfig.getSpaceSaveUrl();
+        if (!StringUtils.isEmpty(url)) {
+            url = url + "/apply/count";
+            ModelMap data = new ModelMap();
+            data.put("spaceUU", spaceUU);
+            HttpUtil.ResponseWrap res = HttpUtil.doGet(url, data);
+            if (!res.isSuccess()) {
+                throw new Exception(res.getContent());
+            } else {
+                ResultWrap result = JSON.parseObject(res.getContent(), ResultWrap.class);
+                if (!result.isSuccess()) {
+                    throw new Exception(result.getErrMsg());
+                } else if (result.getContent() != null) {
+                    return JSON.parseObject(result.getContent().toString(), Map.class);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 校验企业名称
+     * @param spaceName 企业名称
+     */
+    public static void checkSpaceName(String spaceName) throws Exception {
+        String url = AccountConfig.getSpaceSaveUrl();
+        if (!StringUtils.isEmpty(url)) {
+            url = url + "/checkSpaceName";
+            ModelMap data = new ModelMap();
+            data.put("spaceName", spaceName);
+            HttpUtil.ResponseWrap res = HttpUtil.doGet(url, data);
+            if (!res.isSuccess()) {
+                throw new Exception(res.getContent());
+            } else {
+                ResultWrap result = JSON.parseObject(res.getContent(), ResultWrap.class);
+                if (!result.isSuccess()) {
+                    throw new Exception(result.getErrMsg());
+                }
+            }
+        }
+    }
+
+    /**
+     * 校验企业营业执照号
+     * @param businessCode 企业营业执照号
+     */
+    public static void checkBusinessCode(String businessCode) throws Exception {
+        String url = AccountConfig.getSpaceSaveUrl();
+        if (!StringUtils.isEmpty(url)) {
+            url = url + "/checkBusinessCode";
+            ModelMap data = new ModelMap();
+            data.put("businessCode", businessCode);
+            HttpUtil.ResponseWrap res = HttpUtil.doGet(url, data);
+            if (!res.isSuccess()) {
+                throw new Exception(res.getContent());
+            } else {
+                ResultWrap result = JSON.parseObject(res.getContent(), ResultWrap.class);
+                if (!result.isSuccess()) {
+                    throw new Exception(result.getErrMsg());
+                }
+            }
+        }
+    }
+
+    /**
+     * 根据用户uu号获取用户信息
+     * @param userUU 用户uu号
+     * @return
+     * @throws Exception
+     */
+    public static UserView findByUserUU(Long userUU) throws Exception {
+        String url = AccountConfig.getUserSaveUrl();
+        if (!StringUtils.isEmpty(url)) {
+            url = url + "/info";
+            ModelMap data = new ModelMap();
+            data.put("userUU", userUU);
+            HttpUtil.ResponseWrap res = HttpUtil.doGet(url, data);
+            if (!res.isSuccess()) {
+                throw new Exception(res.getContent());
+            } else {
+                ResultWrap result = JSON.parseObject(res.getContent(), ResultWrap.class);
+                if (!result.isSuccess()) {
+                    throw new Exception(result.getErrMsg());
+                } else if (result.getContent() != null) {
+                    return JSON.parseObject(result.getContent().toString(), UserView.class);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 根据用户uu号获取用户信息
+     * @param spaceUU 企业uu号
+     * @return
+     * @throws Exception
+     */
+    public static UserSpaceView findBySpaceUU(Long spaceUU) throws Exception {
+        String url = AccountConfig.getUserSaveUrl();
+        if (!StringUtils.isEmpty(url)) {
+            url = url + "/checkBusinessCode";
+            ModelMap data = new ModelMap();
+            data.put("spaceUU", spaceUU);
+            HttpUtil.ResponseWrap res = HttpUtil.doGet(url, data);
+            if (!res.isSuccess()) {
+                throw new Exception(res.getContent());
+            } else {
+                ResultWrap result = JSON.parseObject(res.getContent(), ResultWrap.class);
+                if (!result.isSuccess()) {
+                    throw new Exception(result.getErrMsg());
+                } else if (result.getContent() != null) {
+                    return JSON.parseObject(result.getContent().toString(), UserSpaceView.class);
+                }
+            }
+        }
+        return null;
+    }
+
 /// 之后方法会恢复并修改
 //
 //	/**

+ 1 - 0
sso-manage-console-web/package.json

@@ -12,6 +12,7 @@
   },
   "dependencies": {
     "axios": "^0.17.1",
+    "babel-polyfill": "^6.26.0",
     "bootstrap": "3",
     "element-ui": "^2.0.11",
     "font-awesome": "^4.7.0",

+ 34 - 0
sso-manage-console-web/src/assets/js/utils.js

@@ -0,0 +1,34 @@
+function formatDate (row, column, value) {
+  if (!value) {
+    return ''
+  }
+
+  const date = new Date(value)
+  let str = ''
+  str += date.getFullYear() + '-'
+  if (date.getMonth() + 1 < 10) {
+    str += '0'
+  }
+  str += (date.getMonth() + 1) + '-'
+  if (date.getDate() < 10) {
+    str += '0'
+  }
+  str += date.getDate() + ' '
+  if (date.getHours() < 10) {
+    str += '0'
+  }
+  str += date.getHours() + ':'
+  if (date.getMinutes() < 10) {
+    str += '0'
+  }
+  str += date.getMinutes() + ':'
+  if (date.getSeconds() < 10) {
+    str += '0'
+  }
+  str += date.getSeconds()
+  return str
+}
+
+export {
+  formatDate
+}

+ 30 - 12
sso-manage-console-web/src/components/accounts/appeals/AppealHome.vue

@@ -61,8 +61,8 @@
         width="100">
         <template slot-scope="scope">
           <span v-if="scope.row.status === 2">已认证</span>
-          <span v-if="scope.row.status === 0">未认证</span>
-          <span v-if="scope.row.status === 1" style="color: #E68615;">待认证</span>
+          <!--<span v-if="scope.row.status === 0">未认证</span>-->
+          <span v-if="scope.row.status === 1" style="color: #E68615;">申诉中</span>
           <span v-if="scope.row.status === 3">未通过</span>
         </template>
       </el-table-column>
@@ -267,11 +267,26 @@
         </div>
       </div>
 
+      <div v-if="!canApprove">
+        <div>
+          <div class="row">
+            <div class="col-lg-6">
+              <div class="message-label">审核状态</div>
+              <div class="message-value" v-if="selectedAppeal.status === 2" style="color: #008000;">已通过</div>
+              <div class="message-value" v-if="selectedAppeal.status === 3" style="color: #FF0000;">未通过</div>
+            </div>
+          </div>
+        </div>
+      </div>
+
       <!-- 对话框尾部 -->
-      <span slot="footer" class="dialog-footer">
+      <span slot="footer" class="dialog-footer" v-if="canApprove">
         <el-button type="primary" @click="approveAppeal(selectedAppeal.id, true)">通过</el-button>
         <el-button @click="approveAppeal(selectedAppeal.id, false)">不通过</el-button>
       </span>
+      <span slot="footer" class="dialog-footer" v-if="!canApprove">
+        <el-button @click="isShowDialog = false">关闭</el-button>
+      </span>
     </el-dialog>
   </common-home>
 </template>
@@ -350,11 +365,7 @@
       key: 'AUTHENTICATED',
       value: 2
     }, {
-      label: '未认证',
-      key: 'NOT_APPLYING',
-      value: 0
-    }, {
-      label: '待认证',
+      label: '申诉中',
       key: 'TO_BE_CERTIFIED',
       value: 1
     }, {
@@ -401,7 +412,8 @@
         searchKeys: searchKeys,
         isShowDialog: false,
         approveType: '',
-        selectedAppeal: {}
+        selectedAppeal: {},
+        canApprove: false
       }
     },
     computed: {
@@ -440,7 +452,7 @@
 
         this.fetchData()
       },
-      approveRequest (appeal) {
+      handleAppeal (appeal) {
         if (appeal.type !== 'resetPassword' && appeal.type !== 'validAccount' &&
           appeal.type !== 'changeAdmin') {
           this.$message.error(`不支持的申诉类型${appeal.type()}`)
@@ -453,12 +465,18 @@
         if (appeal.type === 'changeAdmin' && !this.selectedAppeal.admin) {
           this.selectedAppeal.admin = {}
         }
+      },
+      approveRequest (appeal) {
+        this.handleAppeal(appeal)
         this.isShowDialog = true
+        this.canApprove = true
 
         console.log(this.selectedAppeal)
       },
-      showDetail () {
-        this.$message.info('此功能暂未开通')
+      showDetail (appeal) {
+        this.handleAppeal(appeal)
+        this.isShowDialog = true
+        this.canApprove = false
       },
       approveAppeal (appealId, isPass) {
         const success = result => {

+ 3 - 0
sso-manage-console-web/src/components/accounts/common/CommonHome.vue

@@ -41,6 +41,8 @@
             刷新
           </button>
 
+          <slot name="actions"></slot>
+
           <div class="pagination-area">
             <el-pagination
               background
@@ -102,6 +104,7 @@
   .el-main {
     height: 463px;
     padding: 0;
+    -ms-flex-preferred-size: 463px;
   }
   .el-footer {
     height: 64px;

+ 197 - 4
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseAdmin.vue

@@ -54,6 +54,80 @@
         <el-button @click="changeAdminVisible = false">取 消</el-button>
       </span>
     </el-dialog>
+
+    <!-- 更换记录查看对话框 -->
+    <el-dialog
+      title="更换记录"
+      :visible.sync="changeRecordVisible"
+      width="1070px"
+      :show-close="true"
+      :append-to-body="true">
+      <!-- 更换记录列表 -->
+      <div class="change-records-list">
+        <el-table
+          :data="recordsData"
+          stripe
+          style="width: 100%">
+          <el-table-column
+            prop="oldAdminName"
+            label="原管理员"
+            width="110">
+          </el-table-column>
+          <el-table-column
+            prop="oldAdminTel"
+            label="原手机号"
+            width="120">
+          </el-table-column>
+          <el-table-column
+            prop="newAdminName"
+            label="新管理员"
+            width="110">
+          </el-table-column>
+          <el-table-column
+            prop="newAdminTel"
+            label="新手机号"
+            width="120">
+          </el-table-column>
+          <el-table-column
+            prop="sponsorName"
+            label="申请人"
+            width="110">
+          </el-table-column>
+          <el-table-column
+            prop="sponsorTel"
+            label="申请人手机号"
+            width="120">
+          </el-table-column>
+          <el-table-column
+            prop="changeType"
+            label="更换方式"
+            width="120">
+            <template slot-scope="scope">
+              <span v-if="scope.row.changeType === 'ADMIN_OPERATION'">管理员操作</span>
+              <span v-if="scope.row.changeType === 'USER_APPEAL'">人工申诉</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            prop="launchTime"
+            label="时间"
+            :formatter="formatDate">
+          </el-table-column>
+        </el-table>
+      </div>
+
+      <!-- 对话框尾部 -->
+      <span slot="footer" class="dialog-footer">
+        <el-pagination
+          background
+          layout="prev, pager, next"
+          prev-text="< 上一页"
+          next-text="下一页 >"
+          :page-size="recordsPageParams.size"
+          :total="total"
+          @current-change="handleCurrentPage">
+            </el-pagination>
+      </span>
+    </el-dialog>
   </div>
 </template>
 
@@ -61,6 +135,7 @@
   import axios from '@/assets/js/axios'
   import MessageList from './common/MessageList'
   import * as types from '@/store/mutation-types'
+  import { formatDate } from '@/assets/js/utils'
 
   const keys = [
     {
@@ -77,6 +152,10 @@
     }
   ]
 
+  function showRecordsByPagination (params) {
+    return axios.get('/api/user/space/admin//showRecordsByPagination', { params })
+  }
+
   export default {
     name: 'enterprise-admin',
     components: {
@@ -88,7 +167,15 @@
         items: keys,
         key: 'userUU',
         keyword: '',
-        adminUser: {}
+        adminUser: {},
+        changeRecordVisible: false,
+        recordsPageParams: {
+          page: 1,
+          size: 9,
+          spaceUU: ''
+        },
+        recordsData: [],
+        total: 0
       }
     },
     computed: {
@@ -103,7 +190,7 @@
               const routeLocation = {name: 'UserBasicInfo', params: enterprise.admin, replace: true}
               this.$router.push(routeLocation)
             }
-          } else if (info.label === '申诉状态' && info.value) {
+          } else if (info.label === '申诉状态' && info.value && info.value === '申诉中') {
             info.suffixClass = 'admin-message'
             info.action = () => {
               const routeLocation = {name: 'AppealHome', replace: true}
@@ -118,6 +205,7 @@
       }
     },
     methods: {
+      formatDate: formatDate,
       changeAdmin () {
         // 初始化对话框信息
         this.key = 'userUU'
@@ -127,8 +215,15 @@
         this.changeAdminVisible = true
       },
       showRecords () {
-        const routeLocation = {name: 'AppealHome', replace: true}
-        this.$router.push(routeLocation)
+        this.changeRecordVisible = true
+        this.recordsPageParams = {
+          page: 1,
+          size: 9,
+          spaceUU: this.enterprise.spaceUU
+        }
+        this.fetchRecordData()
+        /*const routeLocation = {name: 'AppealHome', replace: true}
+        this.$router.push(routeLocation)*/
       },
       searchAndSelectAdmin () {
         const params = { spaceUU: this.enterprise.spaceUU, key: this.key, keyword: this.keyword }
@@ -160,6 +255,7 @@
           this.$store.commit(types.CHOOSE_ENTERPRISE, userSpace)
 
           this.$message.success('保存成功')
+          this.changeAdminVisible = false
         }
         const error = response => {
           this.$message.error(response)
@@ -167,6 +263,24 @@
         axios.put(`/api/user/space/${spaceUU}/changeAdmin`, {}, { params })
           .then(success)
           .catch(error)
+      },
+      fetchRecordData () {
+        const params = _.defaultsDeep({}, this.recordsPageParams)
+        params.page = params.page - 1
+
+        const success = pageInfo => {
+          this.recordsData = pageInfo.content
+          this.total = pageInfo.totalElements
+          console.log(pageInfo)
+        }
+        const error = error => {
+          console.log(error)
+        }
+        showRecordsByPagination(params).then(success).catch(error)
+      },
+      handleCurrentPage (currentPage) {
+        this.recordsPageParams.page = currentPage
+        this.fetchRecordData()
       }
     }
   }
@@ -245,6 +359,10 @@
 
     color: #4A7DE1;
   }
+  .change-records-list {
+    margin: -22px 0 0 -40px;
+    min-height: 440px;
+  }
 </style>
 
 <style>
@@ -306,4 +424,79 @@
     background-color: #4E8EFC;
     color: #FFFFFF;
   }
+  /* List */
+  .change-records-list .el-table th.is-leaf:first-child {
+    padding-left: 35px;
+  }
+  .change-records-list .el-table--enable-row-transition .el-table__body td:first-child {
+    padding-left: 35px;
+  }
+  .change-records-list .el-table th>.cell {
+    color: #000000;
+    font-size: 13px;
+    font-weight: normal;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  .change-records-list .el-table th {
+    padding: 9px 0 8px;
+  }
+  .change-records-list .el-table--enable-row-hover .el-table__body tr:hover>td {
+    background-color: #D0E5F5;
+
+    color: #000000;
+    font-size: 13px;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  /* striped row */
+  .change-records-list .el-table--enable-row-hover .el-table__body tr.el-table__row--striped:hover>td {
+    background-color: #D0E5F5;
+
+    color: #000000;
+    font-size: 13px;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  .change-records-list .el-table td {
+    padding: 10px 0;
+
+    font-size: 13px;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  .change-records-list .el-table--striped .el-table__body tr.el-table__row--striped td {
+    background: #F4F4F4;
+  }
+  .dialog-footer .el-pagination button,
+  .dialog-footer .el-pagination span:not([class*=suffix]) {
+    color: #000000;
+    font-weight: normal;
+  }
+  .dialog-footer .el-pagination button span {
+    height: 26px;
+    background-color: #FFFFFF;
+    border: none;
+    border-radius: 0;
+  }
+  .dialog-footer .el-pagination.is-background .btn-next,
+  .dialog-footer .el-pagination.is-background .btn-prev,
+  .dialog-footer .el-pagination.is-background .el-pager li {
+    font-weight: normal;
+  }
+  .dialog-footer .el-pagination.is-background .btn-next,
+  .dialog-footer .el-pagination.is-background .btn-prev {
+    padding-right: 5px;
+    padding-left: 5px;
+
+    background-color: #FFFFFF;
+    border: 1px solid #CFCFCF;
+    border-radius: 0;
+  }
+  .dialog-footer .el-pagination.is-background .el-pager li {
+    background-color: #FFFFFF;
+    border: 1px solid #CFCFCF;
+    border-radius: 0;
+  }
+  .dialog-footer .el-pagination.is-background .el-pager li.active {
+    background-color: #303743;
+    border-color: #303743;
+    color: #fff;
+  }
 </style>

+ 6 - 3
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseHome.vue

@@ -54,7 +54,7 @@
         width="100">
         <template slot-scope="scope">
           <span v-if="scope.row.validCode === 0">未申请</span>
-          <span v-if="scope.row.validCode === 1">待认证</span>
+          <span v-if="scope.row.validCode === 1" style="color: #E68615;">待认证</span>
           <span v-if="scope.row.validCode === 2">已认证</span>
           <span v-if="scope.row.validCode === 3">未通过</span>
         </template>
@@ -69,7 +69,8 @@
       </el-table-column>
       <el-table-column
         prop="registerDate"
-        label="注册时间">
+        label="注册时间"
+        :formatter="formatDate">
       </el-table-column>
     </el-table>
   </common-home>
@@ -78,6 +79,7 @@
 <script>
   import _ from 'lodash'
   import axios from '@/assets/js/axios'
+  import { formatDate } from '@/assets/js/utils'
   import * as types from '@/store/mutation-types'
   import {ValidCode} from '@/Constant'
   import CommonHome from '../common/CommonHome'
@@ -168,7 +170,8 @@
         this.$store.commit(types.CHOOSE_ENTERPRISE, row)
         const routeLocation = {name: 'EnterpriseBasicInfo', params: row}
         this.$router.push(routeLocation)
-      }
+      },
+      formatDate: formatDate
     }
   }
 </script>

+ 254 - 1
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseMembers.vue

@@ -20,6 +20,11 @@
             prop="vipName"
             label="用户名称"
             width="110">
+            <template slot-scope="scope">
+              <el-button type="text" size="small" @click="routeToUserDetail(scope.row)">
+                {{ scope.row.vipName }}
+              </el-button>
+            </template>
           </el-table-column>
           <el-table-column
             prop="userUU"
@@ -46,6 +51,13 @@
             刷新
           </button>
 
+          <button
+            type="button"
+            class="btn btn-default btn-refresh btn-bind"
+            @click="showBindDialog">
+            绑定用户
+          </button>
+
           <div class="pagination-area">
             <el-pagination
               background
@@ -60,6 +72,56 @@
         </el-row>
       </el-footer>
     </el-container>
+
+    <!-- 绑定用户对话框 -->
+    <el-dialog
+      title="绑定用户"
+      :visible.sync="isBindUser"
+      width="450px"
+      :show-close="true"
+      :append-to-body="true">
+      <!-- 对话框内容 -->
+      <el-row type="flex" class="row-bg">
+        <el-select v-model="key" placeholder="请选择">
+          <el-option
+            v-for="item in items"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value">
+          </el-option>
+        </el-select>
+        <div class="input-group search-group" style="width: 242px;margin-left: 5px;">
+          <el-input placeholder="请输入内容" v-model="keyword"></el-input>
+          <span class="input-group-btn">
+            <button class="btn btn-default btn-search" type="button" @click="searchAndSelectAdmin">搜索</button>
+          </span>
+        </div>
+      </el-row>
+      <!-- 用户信息展示 -->
+      <div class="selected-user-info">
+        <div class="row">
+          <div class="message-label">UU号</div>
+          <div class="message-value" v-text="bindUserInfo.userUU">U0516</div>
+        </div>
+        <div class="row">
+          <div class="message-label">姓名</div>
+          <div class="message-value" v-text="bindUserInfo.realName || bindUserInfo.vipName">陈正亮</div>
+        </div>
+        <div class="row">
+          <div class="message-label">手机号</div>
+          <div class="message-value" v-text="bindUserInfo.mobile">13600001122</div>
+        </div>
+        <div class="row">
+          <div class="message-label">邮箱</div>
+          <div class="message-value" v-text="bindUserInfo.email">1891141208@qq.com</div>
+        </div>
+      </div>
+      <!-- 对话框尾部 -->
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="bindUserToCurrentEnterprise">确 定</el-button>
+        <el-button @click="isBindUser = false">取 消</el-button>
+      </span>
+    </el-dialog>
   </div>
 </template>
 
@@ -68,6 +130,26 @@
   import axios from '@/assets/js/axios'
   import * as types from '@/store/mutation-types'
 
+  const keys = [
+    {
+      label: 'UU号',
+      value: 'userUU'
+    },
+    {
+      label: '手机号',
+      value: 'mobile'
+    },
+    {
+      label: '邮箱',
+      value: 'email'
+    }
+  ]
+
+  function bindUserToEnterprise (spaceUU, userUu) {
+    const params = { userUu }
+    return axios.put(`/api/user/space/${spaceUU}/addUserToSpace`, {}, { params })
+  }
+
   export default {
     name: 'enterprise-members',
     data () {
@@ -78,7 +160,12 @@
           spaceUU: null
         },
         total: 0,
-        userList: []
+        userList: [],
+        isBindUser: false,
+        items: keys,
+        key: 'userUU',
+        keyword: '',
+        bindUserInfo: {}
       }
     },
     computed: {
@@ -134,6 +221,53 @@
 
         axios.put(`/api/user/space/${this.enterprise.spaceUU}/unbindUser`, {}, {params}).then(
           success).catch(error)
+      },
+      showBindDialog () {
+        this.isBindUser = true
+        this.key = 'userUU'
+        this.keyword = ''
+        this.bindUserInfo = {}
+      },
+      searchAndSelectAdmin () {
+        const params = { spaceUU: this.enterprise.spaceUU, key: this.key, keyword: this.keyword }
+
+        const error = error => {
+          console.log(error)
+        }
+
+        axios.get('/api/user//searchUserFromOthers', { params })
+        .then(userList => {
+          // 展示查询到的用户信息
+          if (userList && userList.length > 0) {
+            this.bindUserInfo = userList[0]
+          } else {
+            this.$message.info('没有找到对应非当前企业的用户信息!')
+          }
+        })
+        .catch(error)
+      },
+      bindUserToCurrentEnterprise () {
+        const spaceUU = this.enterprise.spaceUU
+        const userUU = this.bindUserInfo.userUU
+
+        const success = result => {
+          if (result) {
+            this.fetchData()
+            this.isBindUser = false
+            this.$message.success('绑定成功')
+          }
+        }
+        const error = response => {
+          this.$message.error(response)
+        }
+
+        bindUserToEnterprise(spaceUU, userUU)
+          .then(success)
+          .catch(error)
+      },
+      routeToUserDetail (user) {
+        const routeLocation = {name: 'UserBasicInfo', params: user, replace: true}
+        this.$router.push(routeLocation)
       }
     },
     watch: {
@@ -149,6 +283,7 @@
   .el-main {
     height: 463px;
     padding: 0;
+    -ms-flex-preferred-size: 463px;
   }
   .el-footer {
     height: 64px;
@@ -164,9 +299,79 @@
     font-size: 13px;
     font-family: "Microsoft YaHei", sans-serif;
   }
+  .btn-bind {
+    width: 80px;
+    margin-left: 15px;
+  }
   .pagination-area {
     padding: 17px 0 17px 150px;
   }
+  .selected-user-info {
+    margin-top: 20px;
+    margin-bottom: 50px;
+  }
+  .selected-user-info .row {
+    margin: 15px 0;
+    height: 34px;
+  }
+  .selected-user-info .row div {
+    display: inline-block;
+  }
+  .selected-user-info .row .message-label {
+    padding: 10px;
+    width: 90px;
+    height: 34px;
+    line-height: 14px;
+
+    color: #000000;
+    font-size: 14px;
+    font-weight: normal;
+    font-family: "SimHei", sans-serif;
+  }
+  .selected-user-info .row .message-value {
+    padding: 10px;
+    width: 280px;
+    height: 34px;
+    line-height: 14px;
+    background-color: #F1F1F1;
+
+    color: #646464;
+    font-size: 14px;
+    font-weight: normal;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  .input-group {
+    border: 1px solid #D2D2D2;
+  }
+  .input-group-btn::before {
+    content: '';
+    position: absolute;
+    top: 10px;
+    left: 0;
+    width: 1px;
+    height: 13px;
+    background: #D2D2D2;
+    z-index: 5;
+  }
+  .btn-search {
+    width: 58px;
+    height: 30px;
+    border-radius: 0;
+    outline: none;
+    border: none;
+
+    color: #2190E1;
+  }
+  .btn-search:active,
+  .btn-search:hover,
+  .btn-search:visited,
+  .btn-search:focus {
+    outline: none;
+    background: none;
+    box-shadow: none;
+
+    color: #4A7DE1;
+  }
 </style>
 
 <style>
@@ -220,4 +425,52 @@
   .el-table--striped .el-table__body tr.el-table__row--striped td {
     background: #F4F4F4;
   }
+  .el-dialog {
+    border-radius: 5px;
+  }
+  .el-dialog__header {
+    padding: 14px 20px;
+    height: 44px;
+    line-height: 16px;
+    border: 1px none #D2D2D2;
+    border-bottom-style: solid;
+  }
+  .el-dialog__header .el-dialog__title {
+    color: #000000;
+    font-size: 16px;
+    font-weight: normal;
+    font-family: "SimHei", sans-serif;
+  }
+  .el-dialog__body {
+    padding: 22px 0 22px 40px;
+  }
+  .el-select .el-input__inner {
+    border-radius: 0;
+    width: 128px;
+    height: 32px;
+  }
+  .search-group .el-input__inner {
+    width: 186px;
+    height: 30px;
+    border-radius: 0;
+    border: none;
+  }
+  .el-dialog__footer {
+    text-align: center;
+  }
+  .el-dialog__footer .el-button {
+    width: 180px;
+    height: 30px;
+    border-radius: 15px;
+    line-height: 14px;
+    padding: 8px 0;
+
+    background: none;
+
+    color: #656565;
+  }
+  .el-dialog__footer .el-button--primary {
+    background-color: #4E8EFC;
+    color: #FFFFFF;
+  }
 </style>

+ 0 - 1
sso-manage-console-web/src/components/accounts/users/UserBasicInfo.vue

@@ -114,7 +114,6 @@
         const success = () => {
           this.isChange = false
           this.$message.success('保存成功')
-          this.$router.go(-1)
         }
         const error = error => {
           this.$message.error(error)

+ 241 - 88
sso-manage-console-web/src/components/accounts/users/UserHome.vue

@@ -1,91 +1,147 @@
 <template>
-  <common-home
-    :pageSize="pageParams.size"
-    :total="total"
-    :searchKeys="searchKeys"
-    :searchKey="pageParams.key"
-    :searchKeyword="pageParams.keyword"
-    @current-change="handleCurrentChange"
-    @refresh-data="handleRefreshData"
-    @search="handleSearchAction">
-    <div slot="screen-type">
-      <!-- 注册来源 -->
-      <label>注册来源</label>
-      <el-select v-model="pageParams.fromApp" clearable placeholder="不限" @change="handleRefreshData">
-        <el-option
-          v-for="item in fromAppOptions"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value">
-        </el-option>
-      </el-select>
-
-      <!-- 手机认证状态 -->
-      <label>手机认证状态</label>
-      <el-select v-model="pageParams.mobileValidCode" clearable placeholder="不限" @change="handleRefreshData">
-        <el-option
-          v-for="item in mobileValidCodeOptions"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value">
-        </el-option>
-      </el-select>
-    </div>
-
-    <el-table
-      :data="tableData"
-      stripe
-      style="width: 100%">
-      <el-table-column
-        label="操作">
-        <template slot-scope="scope">
-          <el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
-        </template>
-      </el-table-column>
-      <el-table-column
-        prop="vipName"
-        label="用户名称">
-      </el-table-column>
-      <el-table-column
-        prop="userUU"
-        label="个人UU">
-      </el-table-column>
-      <el-table-column
-        prop="mobile"
-        label="手机号"
-        width="170">
-        <template slot-scope="scope">
-          {{ scope.row.mobile }}
-          <span class="valid-code1" v-if="scope.row.mobileValidCode === 2">(已认证)</span>
-          <span class="valid-code0" v-if="scope.row.mobileValidCode !== 2">(未认证)</span>
-        </template>
-      </el-table-column>
-      <el-table-column
-        prop="email"
-        label="邮箱"
-        width="250">
-        <template slot-scope="scope" v-if="scope.row.email">
-          {{ scope.row.email }}
-          <span class="valid-code1" v-if="scope.row.emailValidCode === 2">(已认证)</span>
-          <span class="valid-code0" v-if="scope.row.emailValidCode !== 2">(未认证)</span>
-        </template>
-      </el-table-column>
-      <el-table-column
-        prop="registerDate"
-        label="注册时间"
-        :formatter="formatDate"
-        width="160">
-      </el-table-column>
-      <el-table-column
-        prop="lastLogin"
-        label="上次登录">
-      </el-table-column>
-      <el-table-column
-        prop="fromApp"
-        label="注册来源">
-      </el-table-column>
-    </el-table>
-  </common-home>
+  <div>
+    <common-home
+      :pageSize="pageParams.size"
+      :total="total"
+      :searchKeys="searchKeys"
+      :searchKey="pageParams.key"
+      :searchKeyword="pageParams.keyword"
+      @current-change="handleCurrentChange"
+      @refresh-data="handleRefreshData"
+      @search="handleSearchAction">
+      <div slot="screen-type">
+        <!-- 注册来源 -->
+        <label>注册来源</label>
+        <el-select v-model="pageParams.fromApp" clearable placeholder="不限" @change="handleRefreshData">
+          <el-option
+            v-for="item in fromAppOptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value">
+          </el-option>
+        </el-select>
+
+        <!-- 手机认证状态 -->
+        <label>手机认证状态</label>
+        <el-select v-model="pageParams.mobileValidCode" clearable placeholder="不限" @change="handleRefreshData">
+          <el-option
+            v-for="item in mobileValidCodeOptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value">
+          </el-option>
+        </el-select>
+      </div>
+
+      <el-table
+        :data="tableData"
+        stripe
+        style="width: 100%">
+        <el-table-column
+          label="操作">
+          <template slot-scope="scope">
+            <el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="vipName"
+          label="用户名称">
+        </el-table-column>
+        <el-table-column
+          prop="userUU"
+          label="个人UU">
+        </el-table-column>
+        <el-table-column
+          prop="mobile"
+          label="手机号"
+          width="170">
+          <template slot-scope="scope">
+            {{ scope.row.mobile }}
+            <span class="valid-code1" v-if="scope.row.mobileValidCode === 2">(已认证)</span>
+            <span class="valid-code0" v-if="scope.row.mobileValidCode !== 2">(未认证)</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="email"
+          label="邮箱"
+          width="250">
+          <template slot-scope="scope" v-if="scope.row.email">
+            {{ scope.row.email }}
+            <span class="valid-code1" v-if="scope.row.emailValidCode === 2">(已认证)</span>
+            <span class="valid-code0" v-if="scope.row.emailValidCode !== 2">(未认证)</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="registerDate"
+          label="注册时间"
+          :formatter="formatDate"
+          width="160">
+        </el-table-column>
+        <el-table-column
+          prop="lastLogin"
+          label="上次登录">
+        </el-table-column>
+        <el-table-column
+          prop="fromApp"
+          label="注册来源">
+        </el-table-column>
+      </el-table>
+
+      <div class="user-actions" slot="actions">
+        <button
+          type="button"
+          class="btn btn-default btn-refresh"
+          @click="isAddNewUser = true; newUserInfo = {}">
+          新增
+        </button>
+      </div>
+    </common-home>
+
+    <!-- 新增用户对话框 -->
+    <el-dialog
+      title="新增用户"
+      :visible.sync="isAddNewUser"
+      width="450px"
+      :show-close="true"
+      :append-to-body="true">
+      <!-- 用户信息展示 -->
+      <div class="new-user-info">
+        <form class="form-horizontal">
+          <div class="form-group">
+            <label class="col-sm-2 control-label">姓名</label>
+            <div class="col-sm-10">
+              <input type="text" class="form-control" title="vipName" v-model="newUserInfo.vipName" required/>
+            </div>
+          </div>
+          <div class="form-group">
+            <label class="col-sm-2 control-label">手机号</label>
+            <div class="col-sm-10">
+              <input type="tel" class="form-control" title="mobile" v-model="newUserInfo.mobile" required/>
+            </div>
+          </div>
+          <div class="form-group">
+            <label class="col-sm-2 control-label">邮箱</label>
+            <div class="col-sm-10">
+              <input type="email" class="form-control" title="email" v-model="newUserInfo.email"/>
+            </div>
+          </div>
+          <div class="form-group">
+            <label class="col-sm-2 control-label">密码</label>
+            <div class="col-sm-10">
+              <!-- 暂时使用文本 -->
+              <input type="text" class="form-control" title="password" v-model="newUserInfo.password" required/>
+            </div>
+          </div>
+        </form>
+      </div>
+      <!-- 对话框尾部 -->
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="addNewUser">保 存</el-button>
+        <el-button @click="isAddNewUser = false">取 消</el-button>
+      </span>
+    </el-dialog>
+  </div>
+
 </template>
 
 <script>
@@ -109,6 +165,11 @@
     }
   ]
 
+  function addNewUserInfo (user) {
+    return axios.post('/api/user//addNewUser', user)
+      .then(user => { return user })
+  }
+
   export default {
     name: 'user-home',
     components: {
@@ -127,7 +188,9 @@
         pageContent: [],
         total: 0,
         mobileValidCodeOptions: ValidCode,
-        searchKeys: searchKeys
+        searchKeys: searchKeys,
+        isAddNewUser: false,
+        newUserInfo: {}
       }
     },
     created () {
@@ -179,6 +242,23 @@
         const routeLocation = {name: 'UserBasicInfo', params: row}
         this.$router.push(routeLocation)
       },
+      addNewUser () {
+        const body = _.defaultsDeep({}, this.newUserInfo)
+
+        const error = response => {
+          this.$message.error(response)
+        }
+
+        addNewUserInfo(body)
+          .then(user => {
+            console.log(user)
+            this.$message.success('新增成功')
+            this.isAddNewUser = false
+
+            // 刷新页面数据
+            this.fetchData()
+          }).catch(error)
+      },
       formatDate (row, column, value) {
         if (!value) {
           return ''
@@ -227,4 +307,77 @@
   .el-table td span.valid-code3{
     color: #606266;
   }
+  .user-actions {
+    padding: 17px;
+  }
+  .user-actions button {
+    height: 28px;
+    line-height: 14px;
+    border-radius: 0;
+  }
+  .new-user-info {
+    width: 420px;
+  }
+  .new-user-info .form-horizontal .row {
+    margin: 0;
+  }
+  .new-user-info .form-horizontal .control-label {
+    text-align: left;
+
+    color: #000000;
+  }
+  .new-user-info .form-horizontal .form-control {
+    width: 300px;
+    border-radius: 0;
+    height: 34px;
+    line-height: 14px;
+
+    color: #646464;
+  }
+  .new-user-info .form-horizontal .control-label,
+  .new-user-info .form-horizontal .form-control {
+    font-size: 14px;
+    font-weight: normal;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+</style>
+
+<style>
+  .el-dialog {
+    border-radius: 5px;
+  }
+  .el-dialog__header {
+    padding: 14px 20px;
+    height: 44px;
+    line-height: 16px;
+    border: 1px none #D2D2D2;
+    border-bottom-style: solid;
+  }
+  .el-dialog__header .el-dialog__title {
+    color: #000000;
+    font-size: 16px;
+    font-weight: normal;
+    font-family: "SimHei", sans-serif;
+  }
+  .el-dialog__body {
+    padding: 22px 0 22px 40px;
+  }
+  .el-dialog__footer {
+    text-align: center;
+  }
+  .el-dialog__footer .el-button {
+    width: 180px;
+    height: 30px;
+    border-radius: 15px;
+    line-height: 14px;
+    padding: 8px 0;
+
+    background: none;
+
+    color: #656565;
+  }
+  .el-dialog__footer .el-button--primary {
+    background-color: #4E8EFC;
+    color: #FFFFFF;
+  }
 </style>

+ 1 - 1
sso-manage-console-web/src/store/modules/enterprises.js

@@ -7,7 +7,7 @@ const state = {
   allApps: []
 }
 
-const validStatus = ['未申请', '申诉中', '已认证', '未通过']
+const validStatus = ['未申请', '申诉中', '未申请', '未申请']
 
 // Getters
 const getters = {

+ 1 - 1
sso-manage-console-web/webpack/webpack.base.conf.js

@@ -22,7 +22,7 @@ const createLintingRule = () => ({
 module.exports = {
   context: path.resolve(__dirname, '../'),
   entry: {
-    app: './src/main.js'
+    app: ['babel-polyfill', './src/main.js']
   },
   output: {
     path: config.build.assetsRoot,

+ 12 - 0
sso-manage-console-web/yarn.lock

@@ -736,6 +736,14 @@ babel-plugin-transform-vue-jsx@^3.5.0:
   dependencies:
     esutils "^2.0.2"
 
+babel-polyfill@^6.26.0:
+  version "6.26.0"
+  resolved "http://registry.npm.taobao.org/babel-polyfill/download/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153"
+  dependencies:
+    babel-runtime "^6.26.0"
+    core-js "^2.5.0"
+    regenerator-runtime "^0.10.5"
+
 babel-preset-env@^1.3.2:
   version "1.6.1"
   resolved "http://registry.npm.taobao.org/babel-preset-env/download/babel-preset-env-1.6.1.tgz#a18b564cc9b9afdf4aae57ae3c1b0d99188e6f48"
@@ -4589,6 +4597,10 @@ regenerate@^1.2.1:
   version "1.3.3"
   resolved "http://registry.npm.taobao.org/regenerate/download/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f"
 
+regenerator-runtime@^0.10.5:
+  version "0.10.5"
+  resolved "http://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
+
 regenerator-runtime@^0.11.0:
   version "0.11.1"
   resolved "http://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"

+ 2 - 2
sso-manage-console/src/main/java/com/uas/sso/sso/backend/ManageConsoleApplication.java

@@ -11,8 +11,8 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  * @author huxz
  */
 @SpringBootApplication
-@EntityScan(basePackages = {"com.uas.sso.entity"})
-@EnableJpaRepositories(basePackages = {"com.uas.sso.dao"})
+@EntityScan(basePackages = {"com.uas.sso.entity", "com.uas.sso.sso.backend.entity"})
+@EnableJpaRepositories(basePackages = {"com.uas.sso.dao", "com.uas.sso.sso.backend.dao"})
 public class ManageConsoleApplication {
 
     public static void main(String[] args) {

+ 9 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/api/ChangeAdminController.java

@@ -3,6 +3,7 @@ package com.uas.sso.sso.backend.api;
 import com.uas.sso.entity.ChangeAdmin;
 import com.uas.sso.sso.backend.dto.ChangeAdminAudit;
 import com.uas.sso.sso.backend.dto.ChangeAdminCommit;
+import com.uas.sso.sso.backend.entity.AdminChangeRecord;
 import com.uas.sso.sso.backend.service.ChangeAdminService;
 import com.uas.sso.sso.backend.support.ResultBean;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -44,6 +45,14 @@ public class ChangeAdminController {
                 .showUserByPagination(page, validCode, spaceName, businessCode, userName));
     }
 
+    @RequestMapping(method = RequestMethod.GET, path = "//showRecordsByPagination",
+            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public ResultBean<Page<AdminChangeRecord>> showRecordsByPagination(Pageable page, Long spaceUU) {
+
+        return new ResultBean<>(changeAdminService
+                .showRecordsByPagination(page, spaceUU));
+    }
+
     @RequestMapping(method = RequestMethod.POST, path = "//submitChange",
             produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
     public ResultBean<ChangeAdmin> submitChangeAdminRequest(

+ 26 - 8
sso-manage-console/src/main/java/com/uas/sso/sso/backend/api/UserManageController.java

@@ -1,13 +1,15 @@
 package com.uas.sso.sso.backend.api;
 
 import com.uas.sso.entity.User;
+import com.uas.sso.sso.backend.dto.AddNewUserInfo;
 import com.uas.sso.sso.backend.dto.UpdateUserInfo;
-import com.uas.sso.sso.backend.service.UserService;
+import com.uas.sso.sso.backend.service.UserBackendService;
 import com.uas.sso.sso.backend.support.ResultBean;
 import java.util.List;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Pageable;
 import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
@@ -23,11 +25,11 @@ import org.springframework.web.bind.annotation.RestController;
 @RequestMapping(path = "/api/user")
 public class UserManageController {
 
-    private final UserService userService;
+    private final UserBackendService userBackendService;
 
     @Autowired
-    public UserManageController(UserService userService) {
-        this.userService = userService;
+    public UserManageController(UserBackendService userBackendService) {
+        this.userBackendService = userBackendService;
     }
 
     @RequestMapping(method = RequestMethod.GET, path = "//showUserByPagination",
@@ -38,14 +40,15 @@ public class UserManageController {
             @RequestParam(required = false) String key,
             @RequestParam(required = false) String keyword) {
         // Controller中的Pageable类型参数默认根据查询参数 page 和 size 注入并实例化
-        return new ResultBean<>(userService.showUserByPagination(page, fromApp, mobileValidCode, key, keyword));
+        return new ResultBean<>(
+                userBackendService.showUserByPagination(page, fromApp, mobileValidCode, key, keyword));
     }
 
     @RequestMapping(method = RequestMethod.GET, path = "//showEnUserByPagination",
             produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
     public ResultBean<org.springframework.data.domain.Page<User>> showEnUserByPagination(Pageable page, Long spaceUU) {
 
-        return new ResultBean<>(userService.showEnUserByPagination(page, spaceUU));
+        return new ResultBean<>(userBackendService.showEnUserByPagination(page, spaceUU));
     }
 
     @RequestMapping(method = RequestMethod.GET, path = "//searchUserByKeyword",
@@ -53,13 +56,28 @@ public class UserManageController {
     public ResultBean<List<User>> searchUserByKeyword(@RequestParam("spaceUU") Long spaceUu,
             String key, String keyword) {
 
-        return new ResultBean<>(userService.searchUserByKeyword(spaceUu, key, keyword));
+        return new ResultBean<>(userBackendService.searchUserByKeyword(spaceUu, key, keyword));
+    }
+
+    @RequestMapping(method = RequestMethod.GET, path = "//searchUserFromOthers",
+            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public ResultBean<List<User>> searchUserFromOthers(@RequestParam("spaceUU") Long spaceUu,
+            String key, String keyword) {
+
+        return new ResultBean<>(userBackendService.searchUserFromOthers(spaceUu, key, keyword));
     }
 
     @RequestMapping(method = RequestMethod.PUT, path = "//modifyUserInfo",
             produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
     public ResultBean<Boolean> modifyUserInfo(@RequestBody UpdateUserInfo userInfo) {
 
-        return new ResultBean<>(userService.modifyUserInfo(userInfo));
+        return new ResultBean<>(userBackendService.modifyUserInfo(userInfo));
+    }
+
+    @RequestMapping(method = RequestMethod.POST, path = "//addNewUser",
+            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public ResultBean<User> addNewUser(@RequestBody @Validated AddNewUserInfo userInfo) {
+
+        return new ResultBean<>(userBackendService.addNewUser(userInfo));
     }
 }

+ 17 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/dao/AdminChangeRecordDao.java

@@ -0,0 +1,17 @@
+package com.uas.sso.sso.backend.dao;
+
+import com.uas.sso.sso.backend.entity.AdminChangeRecord;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.stereotype.Repository;
+
+/**
+ * A repository stores enterprises admin change records.
+ *
+ * @author huxz
+ */
+@Repository
+public interface AdminChangeRecordDao extends JpaRepository<AdminChangeRecord, Long>,
+        JpaSpecificationExecutor<AdminChangeRecord> {
+
+}

+ 80 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/dto/AddNewUserInfo.java

@@ -0,0 +1,80 @@
+package com.uas.sso.sso.backend.dto;
+
+import com.uas.sso.entity.User;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 新增用户信息DTO.
+ *
+ * @author huxz
+ */
+public class AddNewUserInfo {
+
+    /**
+     * 会员名
+     */
+    @NotNull(message = "个人名称不能为空")
+    private String vipName;
+
+    /**
+     * 手机号
+     */
+    @NotNull(message = "用户手机号不能为空")
+    private String mobile;
+
+    /**
+     * 用户邮箱
+     */
+    private String email;
+
+    /**
+     * 用户密码
+     */
+    @NotNull(message = "用户密码不能为空")
+    private String password;
+
+    public String getVipName() {
+        return vipName;
+    }
+
+    public void setVipName(String vipName) {
+        this.vipName = vipName;
+    }
+
+    public String getMobile() {
+        return mobile;
+    }
+
+    public void setMobile(String mobile) {
+        this.mobile = mobile;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public User fillUserInfo(User user) {
+        if (user == null) {
+            user = new User();
+        }
+
+        user.setVipName(this.vipName);
+        user.setMobile(this.mobile);
+        user.setEmail(this.email);
+        user.setPassword(this.password);
+        return user;
+    }
+}

+ 117 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/entity/AdminChangeRecord.java

@@ -0,0 +1,117 @@
+package com.uas.sso.sso.backend.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+/**
+ * Record logs when super admin changed enterprise' admin.
+ *
+ * @author huxz
+ */
+@Entity
+@Table(name = "sso$us_admin_change_record")
+public class AdminChangeRecord extends FlowBase {
+
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @Column(name = "space_uu")
+    private Long spaceUU;
+
+    /**
+     * 原管理员姓名
+     */
+    @Column(name = "admin_name_old", length = 50)
+    private String oldAdminName;
+
+    /**
+     * 原管理员手机
+     */
+    @Column(name = "admin_tel_old", length = 24)
+    private String oldAdminTel;
+
+    /**
+     * 新管理员姓名
+     */
+    @Column(name = "admin_name_new", length = 50)
+    private String newAdminName;
+
+    /**
+     * 新管理员手机
+     */
+    @Column(name = "admin_tel_new", length = 24)
+    private String newAdminTel;
+
+    /**
+     * 管理更换方式
+     */
+    @Column(name = "change_type")
+    @Enumerated(EnumType.STRING)
+    private AdminChangeType changeType;
+
+    public AdminChangeRecord() {
+        super();
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getSpaceUU() {
+        return spaceUU;
+    }
+
+    public void setSpaceUU(Long spaceUU) {
+        this.spaceUU = spaceUU;
+    }
+
+    public String getOldAdminName() {
+        return oldAdminName;
+    }
+
+    public void setOldAdminName(String oldAdminName) {
+        this.oldAdminName = oldAdminName;
+    }
+
+    public String getOldAdminTel() {
+        return oldAdminTel;
+    }
+
+    public void setOldAdminTel(String oldAdminTel) {
+        this.oldAdminTel = oldAdminTel;
+    }
+
+    public String getNewAdminName() {
+        return newAdminName;
+    }
+
+    public void setNewAdminName(String newAdminName) {
+        this.newAdminName = newAdminName;
+    }
+
+    public String getNewAdminTel() {
+        return newAdminTel;
+    }
+
+    public void setNewAdminTel(String newAdminTel) {
+        this.newAdminTel = newAdminTel;
+    }
+
+    public AdminChangeType getChangeType() {
+        return changeType;
+    }
+
+    public void setChangeType(AdminChangeType changeType) {
+        this.changeType = changeType;
+    }
+}

+ 19 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/entity/AdminChangeType.java

@@ -0,0 +1,19 @@
+package com.uas.sso.sso.backend.entity;
+
+/**
+ * Enum class represents methods of changing admin.
+ *
+ * @author huxz
+ */
+public enum AdminChangeType {
+
+    /**
+     * 管理员操作
+     */
+    ADMIN_OPERATION,
+
+    /**
+     * 人工申诉
+     */
+    USER_APPEAL
+}

+ 98 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/entity/FlowBase.java

@@ -0,0 +1,98 @@
+package com.uas.sso.sso.backend.entity;
+
+import java.util.Date;
+import javax.persistence.Column;
+import javax.persistence.MappedSuperclass;
+
+/**
+ * Represent approval flow base info when user launched a request.
+ *
+ * @author huxz
+ */
+@MappedSuperclass
+public class FlowBase {
+
+    /**
+     * 审批流发起人姓名
+     */
+    @Column(name = "sponsor_name", length = 50)
+    private String sponsorName;
+
+    /**
+     * 审批发起人手机
+     */
+    @Column(name = "sponsor_tel", length = 24)
+    private String sponsorTel;
+
+    /**
+     * 发起日期
+     */
+    @Column(name = "launch_time")
+    private Date launchTime;
+
+    /**
+     * 审核人姓名
+     */
+    @Column(name = "auditor_name", length = 50)
+    private String auditorName;
+
+    /**
+     * 审核人手机
+     */
+    @Column(name = "auditor_tel", length = 24)
+    private String auditorTel;
+
+    /**
+     * 审核时间
+     */
+    @Column(name = "audit_time")
+    private Date auditTime;
+
+    public String getSponsorName() {
+        return sponsorName;
+    }
+
+    public void setSponsorName(String sponsorName) {
+        this.sponsorName = sponsorName;
+    }
+
+    public String getSponsorTel() {
+        return sponsorTel;
+    }
+
+    public void setSponsorTel(String sponsorTel) {
+        this.sponsorTel = sponsorTel;
+    }
+
+    public Date getLaunchTime() {
+        return launchTime;
+    }
+
+    public void setLaunchTime(Date launchTime) {
+        this.launchTime = launchTime;
+    }
+
+    public String getAuditorName() {
+        return auditorName;
+    }
+
+    public void setAuditorName(String auditorName) {
+        this.auditorName = auditorName;
+    }
+
+    public String getAuditorTel() {
+        return auditorTel;
+    }
+
+    public void setAuditorTel(String auditorTel) {
+        this.auditorTel = auditorTel;
+    }
+
+    public Date getAuditTime() {
+        return auditTime;
+    }
+
+    public void setAuditTime(Date auditTime) {
+        this.auditTime = auditTime;
+    }
+}

+ 10 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/ChangeAdminService.java

@@ -3,6 +3,7 @@ package com.uas.sso.sso.backend.service;
 import com.uas.sso.entity.ChangeAdmin;
 import com.uas.sso.sso.backend.dto.ChangeAdminAudit;
 import com.uas.sso.sso.backend.dto.ChangeAdminCommit;
+import com.uas.sso.sso.backend.entity.AdminChangeRecord;
 import javax.validation.constraints.NotNull;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -17,6 +18,15 @@ public interface ChangeAdminService {
     Page<ChangeAdmin> showUserByPagination(Pageable page, Short validCode,
             String spaceName, String businessCode, String userName);
 
+    /**
+     * 分页获取某企业的管理员更换记录.
+     *
+     * @param page  分页参数
+     * @param spaceUU   企业UU
+     * @return  分页数据
+     */
+    Page<AdminChangeRecord> showRecordsByPagination(Pageable page, Long spaceUU);
+
     /**
      * Just for testing
      *

+ 20 - 1
sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/UserService.java → sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/UserBackendService.java

@@ -1,6 +1,7 @@
 package com.uas.sso.sso.backend.service;
 
 import com.uas.sso.entity.User;
+import com.uas.sso.sso.backend.dto.AddNewUserInfo;
 import com.uas.sso.sso.backend.dto.UpdateUserInfo;
 import java.util.List;
 import javax.validation.constraints.NotNull;
@@ -12,7 +13,7 @@ import org.springframework.data.domain.Pageable;
  *
  * @author huxz
  */
-public interface UserService {
+public interface UserBackendService {
 
     /**
      * 分页获取用户信息.
@@ -46,6 +47,16 @@ public interface UserService {
      */
     List<User> searchUserByKeyword(Long spaceUu, String key, String keyword);
 
+    /**
+     * 根据关键字搜索非当前企业的用户信息
+     *
+     * @param spaceUu   企业UU
+     * @param key   搜索字段
+     * @param keyword   搜索关键字
+     * @return  用户列表信息
+     */
+    List<User> searchUserFromOthers(Long spaceUu, String key, String keyword);
+
     /**
      * 管理员收到客户时,后台修改用户的信息.
      *
@@ -53,5 +64,13 @@ public interface UserService {
      * @return  操作状态
      */
     Boolean modifyUserInfo(UpdateUserInfo userInfo);
+
+    /**
+     * 后台新增用户。
+     *
+     * @param userInfo  用户信息
+     * @return  新创建的用户
+     */
+    User addNewUser(AddNewUserInfo userInfo);
 }
 

+ 37 - 1
sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/AppealServiceImpl.java

@@ -6,20 +6,26 @@ import com.uas.sso.dao.UserspaceDao;
 import com.uas.sso.entity.Appeal;
 import com.uas.sso.entity.User;
 import com.uas.sso.entity.Userspace;
+import com.uas.sso.sso.backend.dao.AdminChangeRecordDao;
+import com.uas.sso.sso.backend.entity.AdminChangeRecord;
+import com.uas.sso.sso.backend.entity.AdminChangeType;
 import com.uas.sso.sso.backend.exceptions.ValidationFailedException;
 import com.uas.sso.sso.backend.service.AppealService;
 import com.uas.sso.sso.backend.util.JacksonUtils;
 import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import javax.persistence.criteria.CriteriaBuilder;
 import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Order;
 import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Root;
 import javax.validation.constraints.NotNull;
 import org.apache.log4j.Logger;
+import org.hibernate.jpa.criteria.OrderImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -51,12 +57,15 @@ public class AppealServiceImpl implements AppealService {
 
     private final UserspaceDao spaceDao;
 
+    private final AdminChangeRecordDao recordDao;
+
     @Autowired
     public AppealServiceImpl(AppealDao appealDao, UserDao userDao,
-            UserspaceDao spaceDao) {
+            UserspaceDao spaceDao, AdminChangeRecordDao recordDao) {
         this.appealDao = appealDao;
         this.userDao = userDao;
         this.spaceDao = spaceDao;
+        this.recordDao = recordDao;
     }
 
     @Override
@@ -95,6 +104,13 @@ public class AppealServiceImpl implements AppealService {
                 predicates.toArray(array);
                 Predicate predicate = builder.and(array);
                 query.where(predicate);
+
+                List<Order> orderList = new ArrayList<>();
+                Order order = new OrderImpl(root.get("status"), true);
+                orderList.add(order);
+                order = new OrderImpl(root.get("submitDate"), false);
+                orderList.add(order);
+                query.orderBy(orderList);
                 return null;
             }
         }, page);
@@ -144,6 +160,26 @@ public class AppealServiceImpl implements AppealService {
                         String.format("拥有手机号[%s]的用户不存在", appeal.getMobile()));
             }
 
+            // 记录管理员更换记录
+            AdminChangeRecord record = new AdminChangeRecord();
+            record.setSpaceUU(spaceUU);
+
+            User oldAdmin = space.getAdmin();
+            record.setSponsorName(appeal.getContactName());
+            record.setSponsorTel(appeal.getContactTel());
+            record.setLaunchTime(new Date());
+            record.setOldAdminName(oldAdmin.getVipName());
+            record.setOldAdminTel(oldAdmin.getMobile());
+
+            record.setAuditorName("系统管理员");
+            record.setAuditorTel("123456789");
+            record.setAuditTime(new Date());
+            record.setNewAdminName(user.getVipName());
+            record.setNewAdminTel(user.getMobile());
+
+            record.setChangeType(AdminChangeType.USER_APPEAL);
+            recordDao.save(record);
+
             space.setAdmin(user);
             space.setAdminUU(user.getUserUU());
             // 更新企业信息

+ 29 - 1
sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/ChangeAdminServiceImpl.java

@@ -6,8 +6,10 @@ import com.uas.sso.dao.UserspaceDao;
 import com.uas.sso.entity.ChangeAdmin;
 import com.uas.sso.entity.User;
 import com.uas.sso.entity.Userspace;
+import com.uas.sso.sso.backend.dao.AdminChangeRecordDao;
 import com.uas.sso.sso.backend.dto.ChangeAdminAudit;
 import com.uas.sso.sso.backend.dto.ChangeAdminCommit;
+import com.uas.sso.sso.backend.entity.AdminChangeRecord;
 import com.uas.sso.sso.backend.exceptions.ValidationFailedException;
 import com.uas.sso.sso.backend.service.ChangeAdminService;
 import java.util.ArrayList;
@@ -44,12 +46,16 @@ public class ChangeAdminServiceImpl implements ChangeAdminService {
 
     private final UserDao userDao;
 
+    private final AdminChangeRecordDao recordDao;
+
     @Autowired
     public ChangeAdminServiceImpl(ChangeAdminDao changeAdminDao,
-            UserspaceDao userspaceDao, UserDao userDao) {
+            UserspaceDao userspaceDao, UserDao userDao,
+            AdminChangeRecordDao recordDao) {
         this.changeAdminDao = changeAdminDao;
         this.userspaceDao = userspaceDao;
         this.userDao = userDao;
+        this.recordDao = recordDao;
     }
 
     @Override
@@ -94,6 +100,28 @@ public class ChangeAdminServiceImpl implements ChangeAdminService {
         }, page);
     }
 
+    @Override
+    public Page<AdminChangeRecord> showRecordsByPagination(Pageable page, Long spaceUU) {
+
+        return recordDao.findAll(new Specification<AdminChangeRecord>() {
+            @Override
+            public Predicate toPredicate(Root<AdminChangeRecord> root, CriteriaQuery<?> query,
+                    CriteriaBuilder builder) {
+                List<Predicate> predicates = new ArrayList<>();
+
+                // 根据企业UU进行过滤
+                predicates.add(builder.equal(root.get("spaceUU"), spaceUU));
+
+                predicates.removeAll(Collections.singletonList(null));
+
+                Predicate[] array = predicates.toArray(new Predicate[predicates.size()]);
+                Predicate predicate = builder.and(array);
+                query.where(predicate);
+                return null;
+            }
+        }, page);
+    }
+
     @Override
     @Transactional(rollbackFor = RuntimeException.class)
     public ChangeAdmin submitChangeAdminRequest(@NotNull ChangeAdminCommit commit) {

+ 80 - 4
sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/UserServiceImpl.java → sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/UserBackendServiceImpl.java

@@ -1,13 +1,20 @@
 package com.uas.sso.sso.backend.service.impl;
 
 import com.uas.sso.core.Const;
+import com.uas.sso.core.Status;
+import com.uas.sso.dao.AppDao;
 import com.uas.sso.dao.UserDao;
+import com.uas.sso.entity.App;
 import com.uas.sso.entity.User;
+import com.uas.sso.entity.UserRecord;
 import com.uas.sso.entity.Userspace;
 import com.uas.sso.sso.backend.AuthenticationUtils;
+import com.uas.sso.sso.backend.dto.AddNewUserInfo;
 import com.uas.sso.sso.backend.dto.UpdateUserInfo;
 import com.uas.sso.sso.backend.exceptions.ValidationFailedException;
-import com.uas.sso.sso.backend.service.UserService;
+import com.uas.sso.sso.backend.service.UserBackendService;
+import com.uas.sso.util.InfoAsyncUtils;
+import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -24,17 +31,20 @@ import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 
 /**
- * An implementations of {@code UserService}.
+ * An implementations of {@code UserBackendService}.
  *
  * @author huxz
  */
 @Service
-public class UserServiceImpl implements UserService {
+public class UserBackendServiceImpl implements UserBackendService {
+
+    private final AppDao appDao;
 
     private final UserDao userDao;
 
     @Autowired
-    public UserServiceImpl(UserDao userDao) {
+    public UserBackendServiceImpl(AppDao appDao, UserDao userDao) {
+        this.appDao = appDao;
         this.userDao = userDao;
     }
 
@@ -133,6 +143,37 @@ public class UserServiceImpl implements UserService {
         });
     }
 
+    @Override
+    public List<User> searchUserFromOthers(Long spaceUu, String key, String keyword) {
+
+        return userDao.findAll(new Specification<User>() {
+            @Override
+            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query,
+                    CriteriaBuilder builder) {
+                List<Predicate> predicates = new ArrayList<>();
+
+                if (spaceUu != null) {
+                    Predicate predicate = builder.isNotMember(new Userspace(spaceUu), root.get("userSpaces"));
+                    predicates.add(predicate);
+                }
+
+                if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(keyword)) {
+                    Predicate predicate = builder.equal(root.get(key), keyword);
+                    predicates.add(predicate);
+                }
+
+                // 移除无效条件表达式
+                predicates.removeAll(Collections.singletonList(null));
+
+                Predicate[] array = new Predicate[predicates.size()];
+                predicates.toArray(array);
+                Predicate predicate = builder.and(array);
+                query.where(predicate);
+                return null;
+            }
+        });
+    }
+
     @Override
     public Boolean modifyUserInfo(UpdateUserInfo userInfo) {
 
@@ -162,4 +203,39 @@ public class UserServiceImpl implements UserService {
         return true;
     }
 
+    @Override
+    public User addNewUser(AddNewUserInfo userInfo) {
+        emailHasRegistered(userInfo.getMobile());
+
+        User user = userInfo.fillUserInfo(new User());
+        if (StringUtils.isEmpty(user.getMobileArea())) {
+            user.setMobileArea(Const.CONTINENT);
+        }
+
+        user.setRegisterDate(new Timestamp(System.currentTimeMillis()));
+        Long uu = userDao.findMaxUU();
+        user.setUserUU(uu == null ? 10000 : (uu + 1));
+        user.setSalt(String.valueOf(user.getUserUU()));
+        user.setMobileValidCode((short) Status.AUTHENTICATED.getCode());
+        user.setEmailValidCode((short) Status.NOT_APPLYING.getCode());
+        user.setIdentityValidCode((short) Status.NOT_APPLYING.getCode());
+        user.setPassword(InfoAsyncUtils.encryptePassword(Const.ENCRY_FORMAT, user.getPassword(), user.getSalt()));
+        user.setUserRecord(new UserRecord());
+        user.setFromApp("sso");
+        user  = userDao.save(user);
+
+        List<App> appList = appDao.findAll();
+        List<String> urlsFromApps = InfoAsyncUtils.getUserBackUrlsFromApps(appList);
+
+        InfoAsyncUtils.syncUserInfo(urlsFromApps, user.toView(), "管理新增用户");
+        return user;
+    }
+
+    private void emailHasRegistered(String mobile) {
+        User user = userDao.findByMobile(mobile);
+        if (user != null) {
+            throw new ValidationFailedException("手机号已经注册");
+        }
+    }
+
 }

+ 41 - 13
sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/UserSpaceServiceImpl.java

@@ -9,7 +9,10 @@ import com.uas.sso.entity.App;
 import com.uas.sso.entity.User;
 import com.uas.sso.entity.Userspace;
 import com.uas.sso.entity.UserspaceValid;
+import com.uas.sso.sso.backend.dao.AdminChangeRecordDao;
 import com.uas.sso.sso.backend.dto.UpdateSpaceInfo;
+import com.uas.sso.sso.backend.entity.AdminChangeRecord;
+import com.uas.sso.sso.backend.entity.AdminChangeType;
 import com.uas.sso.sso.backend.exceptions.ValidationFailedException;
 import com.uas.sso.sso.backend.service.UserSpaceService;
 import java.text.SimpleDateFormat;
@@ -19,9 +22,11 @@ import java.util.Date;
 import java.util.List;
 import javax.persistence.criteria.CriteriaBuilder;
 import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Order;
 import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Root;
 import javax.validation.constraints.NotNull;
+import org.hibernate.jpa.criteria.OrderImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -47,13 +52,17 @@ public class UserSpaceServiceImpl implements UserSpaceService {
 
     private final UserspaceValidDao spaceValidDao;
 
+    private final AdminChangeRecordDao recordDao;
+
     @Autowired
     public UserSpaceServiceImpl(UserspaceDao userspaceDao, UserDao userDao,
-            AppDao appDao, UserspaceValidDao spaceValidDao) {
+            AppDao appDao, UserspaceValidDao spaceValidDao,
+            AdminChangeRecordDao recordDao) {
         this.userspaceDao = userspaceDao;
         this.userDao = userDao;
         this.appDao = appDao;
         this.spaceValidDao = spaceValidDao;
+        this.recordDao = recordDao;
     }
 
     @Override
@@ -91,6 +100,13 @@ public class UserSpaceServiceImpl implements UserSpaceService {
                 predicates.toArray(array);
                 Predicate predicate = builder.and(array);
                 query.where(predicate);
+
+                List<Order> orderList = new ArrayList<>();
+                Order order = new OrderImpl(root.get("registerDate"), false);
+                orderList.add(order);
+                order = new OrderImpl(root.get("validCode"), true);
+                orderList.add(order);
+                query.orderBy(orderList);
                 return null;
             }
         }, page);
@@ -141,6 +157,28 @@ public class UserSpaceServiceImpl implements UserSpaceService {
             throw new ValidationFailedException("企业管理员必须是当前企业用户");
         }
 
+        // 记录管理员更换记录
+        AdminChangeRecord record = new AdminChangeRecord();
+        record.setSpaceUU(spaceUu);
+
+        User oldAdmin = space.getAdmin();
+        record.setSponsorName("系统管理员");
+        record.setSponsorTel("123456789");
+        if (oldAdmin != null) {
+            record.setOldAdminName(oldAdmin.getVipName());
+            record.setOldAdminTel(oldAdmin.getMobile());
+        }
+        record.setLaunchTime(new Date());
+
+        record.setAuditorName("系统管理员");
+        record.setAuditorTel("123456789");
+        record.setAuditTime(new Date());
+        record.setNewAdminName(admin.getVipName());
+        record.setNewAdminTel(admin.getMobile());
+
+        record.setChangeType(AdminChangeType.ADMIN_OPERATION);
+        recordDao.save(record);
+
         // 更新管理员信息
         space.setAdminUU(userUU);
         space.setAdmin(admin);
@@ -191,19 +229,9 @@ public class UserSpaceServiceImpl implements UserSpaceService {
     @Override
     @Transactional(rollbackFor = RuntimeException.class)
     public void addUserToSpace(Long spaceUu, Long userUu) {
-        if (spaceUu == null || userUu == null) {
-            throw new ValidationFailedException("企业UU和用户UU不能为空");
-        }
-
-        Userspace space = userspaceDao.findOne(spaceUu);
-        if (space == null) {
-            throw new ValidationFailedException(String.format("UU %d 对应企业不存在", spaceUu));
-        }
+        Userspace space = assertSpaceExist(spaceUu);
 
-        User user = userDao.findOne(userUu);
-        if (user == null) {
-            throw new ValidationFailedException(String.format("UU %d 对应用户不存在", userUu));
-        }
+        User user = assertUserExist(userUu);
 
         List<User> users = space.getUsers();
         if (!users.contains(user)) {

+ 18 - 0
sso-server/build.gradle

@@ -1,7 +1,20 @@
+buildscript {
+    repositories {
+        maven { url "https://plugins.gradle.org/m2/" }
+        maven { url 'http://113.105.74.141:8081/artifactory/libs-release-local' }
+        maven { url "http://maven.aliyun.com/nexus/content/groups/public/" }
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.uas.demo.mesh:spring-boot-docker-plugin:1.0.2'
+    }
+}
+
 plugins {
     id "org.springframework.boot" version '1.5.9.RELEASE'
 }
 
+apply plugin: 'com.uas.docker.spring.boot'
 apply from: "$rootDir/gradle/persist-publish.gradle"
 
 configurations {
@@ -47,3 +60,8 @@ dependencies {
 
     testCompile("org.springframework.boot:spring-boot-starter-test")
 }
+
+customDocker {
+    baseImage 'hub.c.163.com/library/java:8-jre-alpine'
+    registry '10.10.100.200:5000'
+}

+ 6 - 1
sso-server/src/main/java/com/uas/sso/controller/ChangeAdminController.java

@@ -51,7 +51,12 @@ public class ChangeAdminController extends BaseController {
         UserAccount userAccount = SystemSession.getUserAccount();
         User user = userService.findOne(userAccount.getUserUU());
 
-        // 判断是否未企业管理员
+        // 个人账号只能申述
+        if (StringUtils.isEmpty(userAccount.getSpaceUU())) {
+            return success();
+        }
+
+        // 不是管理员只能申述
         Userspace userspace = userspaceService.findOne(userAccount.getSpaceUU());
         if (!userspace.getAdminUU().equals(user.getUserUU())) {
             return success();

+ 11 - 0
sso-server/src/main/java/com/uas/sso/controller/LoginController.java

@@ -24,6 +24,7 @@ import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.io.IOException;
@@ -376,4 +377,14 @@ public class LoginController extends BaseController {
         return success(new ModelMap("isLogin", token != null));
     }
 
+    /**
+     * 页面样式
+     * @param appId 应用id
+     * @return
+     */
+    @RequestMapping(value = "/page/style", method = RequestMethod.GET)
+    public ModelMap getPageStyle(@RequestParam(defaultValue = "sso") String appId) {
+        App app = appService.findOne(appId);
+        return success(app.getPageStyle());
+    }
 }

+ 2 - 2
sso-server/src/main/java/com/uas/sso/controller/PersonalRegisterController.java

@@ -72,14 +72,14 @@ public class PersonalRegisterController extends BaseController {
 
         // 注册并添加注册日志
         user.setFromApp(StringUtils.isEmpty(appId) ? "sso" : appId);
-        userService.register(user);
+        user = userService.register(user);
         registerLogger.info(Type.REGISTER_PERSONAL.getValue(), Step.FIRST.getValue(), "个人注册成功", user, user.getFromApp());
 
         // 注册成功后删除验证码token
         tokenService.delete(token);
 
         // 设置返回数据
-        return success();
+        return success(new ModelMap("userUU", user.getUserUU()));
     }
 
     /**

+ 12 - 0
sso-server/src/main/java/com/uas/sso/controller/UserManagerController.java

@@ -102,4 +102,16 @@ public class UserManagerController extends BaseController {
         applyUserSpaceService.applyBindUserSpace(userUU, spaceUU);
         return success();
     }
+
+    /**
+     * 根据用户uu号查询分页查询申请信息
+     * @param userUU 用户uu号
+     * @param page 当前页(从0开始),默认值为0
+     * @param size 每页大小,默认值为20
+     * @return org.springframework.data.domain.Page,content为ApplyUserSpace集合
+     */
+    @RequestMapping(value = "/apply/info", method = RequestMethod.GET)
+    public ModelMap findApplyInfo(Long userUU, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) {
+        return success(applyUserSpaceService.findApplyByUserUU(userUU, page, size));
+    }
 }

+ 14 - 1
sso-server/src/main/java/com/uas/sso/controller/UserspaceManagerController.java

@@ -35,7 +35,7 @@ public class UserspaceManagerController extends BaseController {
      * @param spaceName 企业名称
      * @return
      */
-    @RequestMapping("/checkSpaceName")
+    @RequestMapping(value = "/checkSpaceName", method = RequestMethod.GET)
     public ModelMap checkSpaceName(String spaceName) {
         userspaceService.checkSpaceName(spaceName);
         return success();
@@ -131,6 +131,19 @@ public class UserspaceManagerController extends BaseController {
         return success(userspaceService.findBySpaceName(name));
     }
 
+    /**
+     * 根据企业名称查询企业
+     * @param businessCode 企业营业执照号
+     * @return 企业信息
+     */
+    @RequestMapping(value = "/info/businessCode", method = RequestMethod.GET)
+    public ModelMap findByBusinessCode(String businessCode) {
+        if (StringUtils.isEmpty(businessCode)) {
+            return success();
+        }
+        return success(userspaceService.findByBusinessCode(businessCode));
+    }
+
     /**
      * 统计申请已审批和未审批数量
      * @param spaceUU 企业uu号

+ 1 - 1
sso-server/src/main/java/com/uas/sso/core/Const.java

@@ -26,7 +26,7 @@ public class Const {
     /**
      * 用户uu号正则
      */
-    public static String REGEXP_USERUU_NUMBER = "^[0-9]{10}$";
+    public static String REGEXP_USERUU_NUMBER = "^[0-9]{5, 10}$";
 
     /**
      * 中国大陆

+ 34 - 1
sso-server/src/main/java/com/uas/sso/core/Status.java

@@ -12,10 +12,31 @@ public enum Status {
     TO_BE_CERTIFIED(1, "待认证"),
     AUTHENTICATED(2, "已认证"),
     NOT_PASSED(3, "未通过"),
+
+    /**
+     * {@code 310 未启用}
+     */
+    UNAPPLY(310, "未启用"),
     /**
      * {@code 311 申请中}
      */
     UNAUDIT(311, "申请中"),
+    /**
+     * {@code 312 未激活}
+     */
+    UNACT(312, "未激活"),
+    /**
+     * {@code 313 已激活}
+     */
+    ENABLED(313, "已激活"),
+    /**
+     * {@code 314 已禁用}
+     */
+    DISABLED(314, "已禁用"),
+    /**
+     * {@code 315 已注销,已取消}
+     */
+    CANCELLED(315, "已注销"),
     /**
      * {@code 316 已同意}
      */
@@ -23,7 +44,19 @@ public enum Status {
     /**
      * {@code 317 已拒绝}
      */
-    DISAGREE(317, "已拒绝");
+    DISAGREE(317, "已拒绝"),
+    /**
+     * {@code 400 信息资料还未验证}
+     */
+    UNAPPROVED(400, "待验证"),
+    /**
+     * {@code 401 信息资料已进行验证,但未通过审核}
+     */
+    APPROVED_ERROR(401, "验证不通过"),
+    /**
+     * {@code 402 信息资料已验证通过}
+     */
+    APPROVED_SUCCESS(402, "已验证");
 
     /**
      * 代码

+ 11 - 1
sso-server/src/main/java/com/uas/sso/core/Type.java

@@ -46,7 +46,17 @@ public enum Type {
     /**
      * 注册类型为企业注册
      */
-    REGISTER_USERSPACE("USERSPACE");
+    REGISTER_USERSPACE("USERSPACE"),
+
+    /**
+     * 用户绑定企业
+     */
+    BIND_USERSPACE("BIND"),
+
+    /**
+     * 用户解除绑定企业
+     */
+    UNBIND_USERSPACE("UNBIND");
 
     private String value;
 

+ 4 - 4
sso-server/src/main/java/com/uas/sso/dao/UserspaceDao.java

@@ -27,7 +27,7 @@ public interface UserspaceDao extends JpaRepository<Userspace, Long>, JpaSpecifi
      * @param spaceName 企业名称
      * @return 企业信息
      */
-    Set<Userspace> findBySpaceName(String spaceName);
+    Userspace findBySpaceName(String spaceName);
 
     /**
      * 根据企业营业执照号查找企业信息
@@ -35,7 +35,7 @@ public interface UserspaceDao extends JpaRepository<Userspace, Long>, JpaSpecifi
      * @param businessCode 企业营业执照号
      * @return
      */
-    Set<Userspace> findByBusinessCode(String businessCode);
+    Userspace findByBusinessCode(String businessCode);
 
     /**
      * 找到企业最大的uu号
@@ -58,7 +58,7 @@ public interface UserspaceDao extends JpaRepository<Userspace, Long>, JpaSpecifi
      * @param validCode 认证状态
      * @return 企业信息
      */
-    List<Userspace> findBySpaceNameAndValidCode(String spaceName, Short validCode);
+    Userspace findBySpaceNameAndValidCode(String spaceName, Short validCode);
 
     /**
      * 根据企业营业执照号查询认证状态的企业
@@ -66,7 +66,7 @@ public interface UserspaceDao extends JpaRepository<Userspace, Long>, JpaSpecifi
      * @param validCode 认证状态
      * @return 企业信息
      */
-    List<Userspace> findByBusinessCodeAndValidCode(String businessCode, short validCode);
+    Userspace findByBusinessCodeAndValidCode(String businessCode, short validCode);
 
     /**
      * 根据关键词搜索相关的企业名

+ 23 - 7
sso-server/src/main/java/com/uas/sso/entity/App.java

@@ -1,10 +1,10 @@
 package com.uas.sso.entity;
 
+import com.alibaba.fastjson.annotation.JSONField;
+import org.codehaus.jackson.annotate.JsonIgnore;
+
 import java.io.Serializable;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import javax.persistence.*;
 
 /**
  * 应用
@@ -26,7 +26,6 @@ public class App implements Serializable {
      */
     @Id
     @Column(name = "uid_")
-    // @GeneratedValue
     private String uid;
 
     /**
@@ -107,6 +106,9 @@ public class App implements Serializable {
     @Column(name = "personal_enable", nullable = false)
     private int personalEnable;
 
+    @OneToOne(mappedBy = "app", fetch = FetchType.LAZY)
+    private PageStyle pageStyle;
+
     public String getUid() {
         return uid;
     }
@@ -219,10 +221,24 @@ public class App implements Serializable {
         this.personalEnable = personalEnable;
     }
 
+    @JsonIgnore
+    @JSONField(serialize = false)
+    public PageStyle getPageStyle() {
+        return pageStyle;
+    }
+
+    public void setPageStyle(PageStyle pageStyle) {
+        this.pageStyle = pageStyle;
+    }
+
     @Override
     public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
 
         App app = (App) o;
 

+ 194 - 0
sso-server/src/main/java/com/uas/sso/entity/PageStyle.java

@@ -0,0 +1,194 @@
+package com.uas.sso.entity;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import org.codehaus.jackson.annotate.JsonIgnore;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * @author wangmh
+ * @create 2018-02-05 8:47
+ * @desc 页面对应用独特样式
+ **/
+@Entity
+@Table(name = "sso$app$style")
+public class PageStyle implements Serializable {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 应用Id
+     */
+    @Id
+    @Column(name = "app_uid")
+    private String appId;
+
+    /**
+     * 应用
+     */
+    @OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
+    @JoinColumn(name = "app_uid", insertable = false, updatable = false)
+    private App app;
+
+    /**
+     * 应用描述
+     */
+    @Column(name = "app_desc")
+    private String desc;
+
+    /**
+     * logo图片
+     */
+    @Column(name = "logo_url")
+    private String logoUrl;
+
+    /**
+     * 背景图片
+     */
+    @Column(name = "bg_url")
+    private String bgUrl;
+
+    /**
+     * 背景颜色
+     */
+    @Column(name = "bg_color")
+    private String bgColor;
+
+    /**
+     * 背景图片链接
+     */
+    @Column(name = "bg_link")
+    private String bgLink;
+
+    /**
+     * 主标题
+     */
+    @Column(name = "title")
+    private String title;
+
+    /**
+     * 副标题
+     */
+    @Column(name = "subtitle")
+    private String subtitle;
+
+    /**
+     * 按钮样式(json字符串)
+     */
+    @Column(name = "btns")
+    private String btns;
+
+    /**
+     * 字体颜色
+     */
+    @Column(name = "font_color")
+    private String fontColor;
+
+    /**
+     * 相对字体颜色
+     */
+    @Column(name = "reserve_color")
+    private String reserveColor;
+
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
+
+    @JsonIgnore
+    @JSONField(serialize = false)
+    public App getApp() {
+        return app;
+    }
+
+    public void setApp(App app) {
+        this.app = app;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    public String getLogoUrl() {
+        return logoUrl;
+    }
+
+    public void setLogoUrl(String logoUrl) {
+        this.logoUrl = logoUrl;
+    }
+
+    public String getBgUrl() {
+        return bgUrl;
+    }
+
+    public void setBgUrl(String bgUrl) {
+        this.bgUrl = bgUrl;
+    }
+
+    public String getBgColor() {
+        return bgColor;
+    }
+
+    public void setBgColor(String bgColor) {
+        this.bgColor = bgColor;
+    }
+
+    public String getBgLink() {
+        return bgLink;
+    }
+
+    public void setBgLink(String bgLink) {
+        this.bgLink = bgLink;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getSubtitle() {
+        return subtitle;
+    }
+
+    public void setSubtitle(String subtitle) {
+        this.subtitle = subtitle;
+    }
+
+    public String getBtns() {
+        return btns;
+    }
+
+    public void setBtns(String btns) {
+        this.btns = btns;
+    }
+
+    public String getFontColor() {
+        return fontColor;
+    }
+
+    public void setFontColor(String fontColor) {
+        this.fontColor = fontColor;
+    }
+
+    public String getReserveColor() {
+        return reserveColor;
+    }
+
+    public void setReserveColor(String reserveColor) {
+        this.reserveColor = reserveColor;
+    }
+}

+ 16 - 10
sso-server/src/main/java/com/uas/sso/entity/User.java

@@ -6,16 +6,8 @@ import java.sql.Timestamp;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import javax.persistence.CascadeType;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.Id;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToMany;
-import javax.persistence.OneToMany;
-import javax.persistence.OneToOne;
-import javax.persistence.Table;
+import javax.persistence.*;
+
 import org.codehaus.jackson.annotate.JsonIgnore;
 
 /**
@@ -184,6 +176,12 @@ public class User implements Serializable {
     @Column(name = "_lock")
     private Integer lock;
 
+    /**
+     * imId
+     */
+    @Column(name = "im_id")
+    private String imId;
+
     /**
      * 用户登录记录
      */
@@ -409,6 +407,14 @@ public class User implements Serializable {
         this.lock = lock;
     }
 
+    public String getImId() {
+        return imId;
+    }
+
+    public void setImId(String imId) {
+        this.imId = imId;
+    }
+
     @JsonIgnore
     @JSONField(serialize = false)
     public UserRecord getUserRecord() {

+ 2 - 1
sso-server/src/main/java/com/uas/sso/entity/UserRecord.java

@@ -29,7 +29,8 @@ public class UserRecord implements Serializable {
     @Column(name = "pwd_error_count", nullable = false)
     private int pwdErrorCount;
 
-    @OneToOne(mappedBy = "userRecord")
+    @OneToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "user_uu", unique = true)
     private User user;
 
     public UserRecord() {

+ 1 - 1
sso-server/src/main/java/com/uas/sso/entity/Userspace.java

@@ -41,7 +41,7 @@ public class Userspace implements Serializable {
     /**
      * 企业名称
      */
-    @Column(name = "space_name", nullable = false)
+    @Column(name = "space_name", nullable = false, unique = true)
     private String spaceName;
 
     /**

+ 1 - 0
sso-server/src/main/java/com/uas/sso/service/ApplyUserSpaceService.java

@@ -59,4 +59,5 @@ public interface ApplyUserSpaceService {
      */
     void disagreeApply(Long userUU, Long id);
 
+    Page<ApplyUserSpace> findApplyByUserUU(Long userUU, int page, int size);
 }

+ 7 - 0
sso-server/src/main/java/com/uas/sso/service/UserService.java

@@ -195,6 +195,13 @@ public interface UserService {
      */
     void bindUserspace(Long userUU, Long spaceUU);
 
+    /**
+     * 用户解除绑定企业
+     * @param userUU 用户uu号
+     * @param spaceUU 绑定的企业uu号
+     */
+    void unbindUserspace(Long userUU, Long spaceUU);
+
     /**
      * 设置用户密保
      * @param userUU 用户uu号

+ 9 - 1
sso-server/src/main/java/com/uas/sso/service/UserspaceService.java

@@ -4,6 +4,7 @@ import com.uas.sso.entity.User;
 import com.uas.sso.entity.Userspace;
 import org.springframework.data.domain.Page;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -95,7 +96,7 @@ public interface UserspaceService {
      * @param spaceName 企业名称
      * @return
      */
-    Set<Userspace> findBySpaceName(String spaceName);
+    Userspace findBySpaceName(String spaceName);
 
     /**
      * 企业解除绑定应用
@@ -110,4 +111,11 @@ public interface UserspaceService {
      * @param appId 应用id
      */
     void bindApp(Long spaceUU, String appId);
+
+    /**
+     * 根据企业营业执照号获取企业信息
+     * @param businessCode 企业营业执照号
+     * @return
+     */
+     Userspace findByBusinessCode(String businessCode);
 }

+ 17 - 4
sso-server/src/main/java/com/uas/sso/service/impl/ApplyUserSpaceServiceImpl.java

@@ -18,10 +18,7 @@ import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
 
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Predicate;
-import javax.persistence.criteria.Root;
+import javax.persistence.criteria.*;
 import java.sql.Timestamp;
 import java.util.*;
 
@@ -171,4 +168,20 @@ public class ApplyUserSpaceServiceImpl implements ApplyUserSpaceService {
         applyUserSpace.setProcessTime(new Timestamp(System.currentTimeMillis()));
         applyUserSpaceDao.save(applyUserSpace);
     }
+
+    @Override
+    public Page<ApplyUserSpace> findApplyByUserUU(Long userUU, int page, int size) {
+        Pageable pageable = new PageRequest(page, size);
+        Page<ApplyUserSpace> pApply = applyUserSpaceDao.findAll(new Specification<ApplyUserSpace>() {
+            @Override
+            public Predicate toPredicate(Root<ApplyUserSpace> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
+                List<Predicate> list = new ArrayList<>();
+                list.add(cb.equal(root.join("user", JoinType.INNER).get("userUU").as(Long.class), userUU));
+                Predicate[] predicates = new Predicate[list.size()];
+                predicates = list.toArray(predicates);
+                return cb.and(predicates);
+            }
+        }, pageable);
+        return pApply;
+    }
 }

+ 21 - 0
sso-server/src/main/java/com/uas/sso/service/impl/UserServiceImpl.java

@@ -116,6 +116,7 @@ public class UserServiceImpl implements UserService {
         user.setIdentityValidCode((short) Status.NOT_APPLYING.getCode());
         user.setPassword(getEncryPassword(Const.ENCRY_FORMAT, user.getPassword(), user.getSalt()));
         user.setUserRecord(new UserRecord());
+        user.getUserRecord().setUser(user);
 
         userDao.save(user);
         userLog.info(user, Type.UPDATE_REGISTER.getValue());
@@ -357,6 +358,26 @@ public class UserServiceImpl implements UserService {
 
         // 保存
         userDao.save(user);
+
+        // 保存日志
+        userLog.info(user, Type.BIND_USERSPACE.getValue()+spaceUU);
+    }
+
+    @Override
+    public void unbindUserspace(Long userUU, Long spaceUU) {
+        // 找到用户和企业
+        User user = findOne(userUU);
+        Userspace userspace = userspaceService.findOne(spaceUU);
+
+        // 将企业添加到用户列表上
+        Set<Userspace> userspaces = user.getUserSpaces();
+        userspaces.remove(userspace);
+
+        // 保存
+        userDao.save(user);
+
+        // 保存日志
+        userLog.info(user, Type.UNBIND_USERSPACE.getValue()+spaceUU);
     }
 
     @Override

+ 14 - 9
sso-server/src/main/java/com/uas/sso/service/impl/UserspaceServiceImpl.java

@@ -174,16 +174,16 @@ public class UserspaceServiceImpl implements UserspaceService {
 
     @Override
     public void checkSpaceName(String spaceName) {
-        Set<Userspace> userSpaces = userspaceDao.findBySpaceName(spaceName);
-        if (!CollectionUtils.isEmpty(userSpaces)) {
+        Userspace userSpace = userspaceDao.findBySpaceName(spaceName);
+        if (userSpace != null) {
             throw new VisibleError("该企业名称已被注册,请确认");
         }
     }
 
     @Override
     public void checkBusinessCode(String businessCode) {
-        Set<Userspace> userSpaces = userspaceDao.findByBusinessCode(businessCode);
-        if (!CollectionUtils.isEmpty(userSpaces)) {
+        Userspace userSpace = userspaceDao.findByBusinessCode(businessCode);
+        if (userSpace != null) {
             throw new VisibleError("该企业营业执照已被注册,请确认");
         }
     }
@@ -195,14 +195,14 @@ public class UserspaceServiceImpl implements UserspaceService {
 
     @Override
     public boolean spaceNameIsValid(String spaceName) {
-        List<Userspace> userSpaces = userspaceDao.findBySpaceNameAndValidCode(spaceName, (short) Status.AUTHENTICATED.getCode());
-        return !CollectionUtils.isEmpty(userSpaces);
+        Userspace userSpace = userspaceDao.findBySpaceName(spaceName);
+        return userSpace == null ? false : userSpace.getValidCode() == Status.AUTHENTICATED.getCode();
     }
 
     @Override
     public boolean businessCodeIsValid(String businessCode) {
-        List<Userspace> userSpaces = userspaceDao.findByBusinessCodeAndValidCode(businessCode, (short) Status.AUTHENTICATED.getCode());
-        return !CollectionUtils.isEmpty(userSpaces);
+        Userspace userSpace = userspaceDao.findByBusinessCode(businessCode);
+        return userSpace == null ? false : userSpace.getValidCode() == Status.AUTHENTICATED.getCode();
     }
 
     @Override
@@ -235,7 +235,7 @@ public class UserspaceServiceImpl implements UserspaceService {
     }
 
     @Override
-    public Set<Userspace> findBySpaceName(String spaceName) {
+    public Userspace findBySpaceName(String spaceName) {
         return userspaceDao.findBySpaceName(spaceName);
     }
 
@@ -278,4 +278,9 @@ public class UserspaceServiceImpl implements UserspaceService {
         userspace.getApps().add(app);
         userspaceDao.save(userspace);
     }
+
+    @Override
+    public Userspace findByBusinessCode(String businessCode) {
+        return userspaceDao.findByBusinessCode(businessCode);
+    }
 }

+ 120 - 0
sso-server/src/main/java/com/uas/sso/util/InfoAsyncUtils.java

@@ -0,0 +1,120 @@
+package com.uas.sso.util;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.uas.sso.common.encrypt.MD5;
+import com.uas.sso.common.util.HttpUtil;
+import com.uas.sso.core.Const;
+import com.uas.sso.core.ICallable;
+import com.uas.sso.entity.App;
+import com.uas.sso.entity.UserView;
+import com.uas.sso.exception.AccountException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.log4j.Logger;
+import org.springframework.util.Assert;
+
+/**
+ * Utils class is designed to sync user information with other apps
+ * and handle user information.
+ *
+ * @author huxz
+ */
+public class InfoAsyncUtils {
+
+    public static final Logger logger = Logger.getLogger(InfoAsyncUtils.class);
+
+    /**
+     * 生成加密密码
+     *
+     * @param format 密码加密格式
+     * @param password 明文密码
+     * @param salt 盐值
+     * @return 密文密码
+     */
+    public static String encryptePassword(String format, String password, String salt) {
+        // 验证加密格式
+        if (StringUtils.isEmpty(format)) {
+            return password;
+        }
+
+        // 验证密码信息,长度超过32认为是已加密过的密文
+        int minPwdLen = 4;
+        int maxPwdLen = 32;
+        if (StringUtils.isEmpty(password) || password.length() < minPwdLen
+                || password.length() >= maxPwdLen) {
+            throw new AccountException("invalid password");
+        }
+
+        // 加密格式: "$password{$salt}"
+        String cipher = format.replace(Const.ENCRY_PARAM_PASSWORD, password);
+        cipher = cipher.replace(Const.ENCRY_PARAM_SALT, salt == null ? "" : salt);
+        return MD5.toMD5(cipher);
+    }
+
+    /**
+     * 获取所有应用的有效用户信息更新接口.
+     *
+     * @param appList 应用列表
+     * @return 用户更新接口链接列表
+     */
+    public static List<String> getUserBackUrlsFromApps(List<App> appList) {
+        if (CollectionUtils.isEmpty(appList)) {
+            return Collections.emptyList();
+        }
+
+        List<String> backUserUrls = new ArrayList<>();
+        for (App app : appList) {
+            if (app != null && StringUtils.isEmpty(app.getUserControl())
+                    && !StringUtils.isEmpty(app.getBackUserUrl())) {
+                backUserUrls.add(app.getUid() + ":" + app.getBackUserUrl());
+            }
+        }
+        return backUserUrls;
+    }
+
+    /**
+     * 同步用户信息到各个应用
+     *
+     * @param backUserUrls  同步接口URL
+     * @param userView  用户信息视图
+     * @param msg   同步信息描述,用户区分同步类型
+     */
+    public static void syncUserInfo(final List<String> backUserUrls, final UserView userView,
+            final String msg) {
+        if (CollectionUtils.isEmpty(backUserUrls)) {
+            logger.warn("用户更新接口URL列表为空列表");
+            return;
+        }
+        Assert.notNull(userView, "用户信息不能为空");
+        Assert.isTrue(!org.springframework.util.StringUtils.isEmpty(msg), "同步信息描述不能为空");
+
+        ExecuteUtils.execute(new ICallable<Void, String>() {
+
+            @Override
+            public Void call(String url) {
+                String[] split = url.split(":");
+                String appId = split[0];
+                try {
+                    url = split[1];
+                    JSONObject formData = JSON.parseObject(JSON.toJSONString(userView));
+
+                    HttpUtil.ResponseWrap res = HttpUtil.doPost(url, formData, 30000);
+                    if (!res.isSuccess()) {
+                        logger.error(String.format("%s:同步用户信息失败, %s, %s, %s", msg, appId,
+                                JSON.toJSONString(userView), res.getContent()));
+                    } else {
+                        logger.info(String.format("%s:同步用户信息成功, %s, %s", msg, appId,
+                                JSON.toJSONString(userView)));
+                    }
+                } catch (Exception e) {
+                    logger.error(String.format("%s:同步用户信息失败, %s, %s, %s", msg, appId,
+                            JSON.toJSONString(userView), e.getMessage()));
+                }
+                return null;
+            }
+        }, backUserUrls);
+    }
+
+}