Browse Source

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

wangmh 8 years ago
parent
commit
98abcdfff3
26 changed files with 1194 additions and 354 deletions
  1. 2 1
      sso-manage-console-web/package.json
  2. 0 36
      sso-manage-console-web/src/assets/js/notifications.js
  3. 301 2
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseAdmin.vue
  4. 66 0
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseApps.vue
  5. 44 0
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseAuth.vue
  6. 13 6
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseBaseAction.vue
  7. 2 1
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseBasicInfo.vue
  8. 2 0
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseHome.vue
  9. 178 120
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseMembers.vue
  10. 85 0
      sso-manage-console-web/src/components/accounts/enterprises/common/AppList.vue
  11. 75 0
      sso-manage-console-web/src/components/accounts/enterprises/common/MessageList.vue
  12. 5 1
      sso-manage-console-web/src/components/accounts/enterprises/index.js
  13. 2 4
      sso-manage-console-web/src/main.js
  14. 15 8
      sso-manage-console-web/src/router/index.js
  15. 9 0
      sso-manage-console-web/src/store/actions.js
  16. 28 0
      sso-manage-console-web/src/store/index.js
  17. 67 0
      sso-manage-console-web/src/store/modules/enterprises.js
  18. 6 0
      sso-manage-console-web/src/store/mutation-types.js
  19. 10 0
      sso-manage-console-web/yarn.lock
  20. 10 13
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/api/UserManageController.java
  21. 24 12
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/api/UserSpaceManageController.java
  22. 37 8
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/UserService.java
  23. 15 3
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/UserSpaceService.java
  24. 83 68
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/UserServiceImpl.java
  25. 99 68
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/UserSpaceServiceImpl.java
  26. 16 3
      sso-server/src/main/java/com/uas/sso/entity/Userspace.java

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

@@ -20,7 +20,8 @@
     "vue": "^2.5.2",
     "vue-notifications": "^0.9.0",
     "vue-router": "^3.0.1",
-    "vuex": "^3.0.1"
+    "vuex": "^3.0.1",
+    "vuex-persist": "^1.1.1"
   },
   "devDependencies": {
     "autoprefixer": "^7.1.2",

+ 0 - 36
sso-manage-console-web/src/assets/js/notifications.js

@@ -1,36 +0,0 @@
-import VueNotifications from 'vue-notifications'
-
-// Include mini-toaster (or any other UI-notification library)
-import miniToastr from 'mini-toastr'
-
-// We shall setup types of the messages. ('error' type - red and 'success' - green in mini-toastr)
-const toastTypes = {
-  success: 'success',
-  error: 'error',
-  info: 'info',
-  warn: 'warn'
-}
-
-// This step requires only for mini-toastr, just an initialization
-miniToastr.init({types: toastTypes, appendTarget: document.body})
-
-// Here we are seting up messages output to `mini-toastr`
-// This mean that in case of 'success' message we will call miniToastr.success(message, title, timeout, cb)
-// In case of 'error' we will call miniToastr.error(message, title, timeout, cb)
-// and etc.
-function toast ({title, message, type, timeout, cb}) {
-  return miniToastr[type](message, title, timeout, cb)
-}
-
-// Here we map vue-notifications method to function abowe (to mini-toastr)
-// By default vue-notifications can have 4 methods: 'success', 'error', 'info' and 'warn'
-// But you can specify whatever methods you want.
-// If you won't specify some method here, output would be sent to the browser's console
-const options = {
-  success: toast,
-  error: toast,
-  info: toast,
-  warn: toast
-}
-
-export { VueNotifications, options }

+ 301 - 2
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseAdmin.vue

@@ -1,19 +1,318 @@
 <template>
   <div>
-    124
+    <message-list :messages="messages">
+      <button class="btn btn-default btn-change" slot="action" @click="changeAdmin">更换管理员</button>
+      <button class="btn btn-default" slot="action" @click="showRecords">更换记录</button>
+    </message-list>
+
+    <!-- 更换管理员对话框 -->
+    <el-dialog
+      title="更换管理员"
+      :visible.sync="changeAdminVisible"
+      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>
+          <!--<el-autocomplete
+            v-model="keyword"
+            placeholder="请输入内容">
+          </el-autocomplete>-->
+          <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="adminUser.userUU">U0516</div>
+        </div>
+        <div class="row">
+          <div class="message-label">姓名</div>
+          <div class="message-value" v-text="adminUser.realName || adminUser.vipName">陈正亮</div>
+        </div>
+        <div class="row">
+          <div class="message-label">手机号</div>
+          <div class="message-value" v-text="adminUser.mobile">13600001122</div>
+        </div>
+        <div class="row">
+          <div class="message-label">邮箱</div>
+          <div class="message-value" v-text="adminUser.email">1891141208@qq.com</div>
+        </div>
+      </div>
+      <!-- 对话框尾部 -->
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitToBeChangedUser">确 定</el-button>
+        <el-button @click="changeAdminVisible = false">取 消</el-button>
+      </span>
+    </el-dialog>
   </div>
 </template>
 
 <script>
+  import axios from '@/assets/js/axios'
+  import MessageList from './common/MessageList'
+  import * as types from '@/store/mutation-types'
+
+  const keys = [
+    {
+      label: 'UU号',
+      value: 'userUU'
+    },
+    {
+      label: '手机号',
+      value: 'mobile'
+    },
+    {
+      label: '邮箱',
+      value: 'email'
+    }
+  ]
+
   export default {
-    name: 'enterprise-admin'
+    name: 'enterprise-admin',
+    components: {
+      MessageList
+    },
+    data () {
+      return {
+        changeAdminVisible: false,
+        items: keys,
+        key: 'userUU',
+        keyword: '',
+        adminUser: {}
+      }
+    },
+    computed: {
+      messages () {
+        const adminInfo = this.$store.getters.enAdmin
+        const enterprise = this.enterprise
+
+        for (let info of adminInfo) {
+          if (info.label === '管理员名称' && info.value) {
+            info.suffixClass = 'admin-message'
+            info.action = () => {
+              const routeLocation = {name: 'UserBasicInfo', params: enterprise.admin, replace: true}
+              this.$router.push(routeLocation)
+            }
+          } else if (info.label === '申诉状态' && info.value) {
+            info.suffixClass = 'admin-message'
+            info.action = () => {
+              const routeLocation = {name: 'AppealHome', replace: true}
+              this.$router.push(routeLocation)
+            }
+          }
+        }
+        return adminInfo
+      },
+      enterprise () {
+        const enterprise = this.$store.state.enterprises.savedEnterprise
+        if (enterprise) {
+          this.adminUser = enterprise.admin
+        }
+
+        return enterprise
+      }
+    },
+    methods: {
+      changeAdmin () {
+        // 初始化对话框信息
+        this.key = 'userUU'
+        this.keyword = ''
+        this.adminUser = this.enterprise.admin || {}
+
+        this.changeAdminVisible = true
+      },
+      showRecords () {
+        // TODO 查看更换记录
+        this.$message.info('此功能暂未实现')
+      },
+      searchAndSelectAdmin () {
+        const params = { spaceUU: this.enterprise.spaceUU, key: this.key, keyword: this.keyword }
+
+        const error = error => {
+          console.log(error)
+        }
+
+        axios.get('/api/user//searchUserByKeyword', { params })
+          .then(userList => {
+            // 展示查询到的用户信息
+            if (userList && userList.length > 0) {
+              this.adminUser = userList[0]
+            } else {
+              this.$message.info('没有找到对应的用户信息!')
+            }
+          })
+          .catch(error)
+      },
+      submitToBeChangedUser () {
+        const spaceUU = this.enterprise.spaceUU
+
+        const params = {}
+        if (this.adminUser && this.adminUser.userUU) {
+          params.userUU = this.adminUser.userUU
+        }
+
+        const success = userSpace => {
+          this.$store.commit(types.CHOOSE_ENTERPRISE, userSpace)
+
+          this.$message.success('保存成功')
+        }
+        const error = response => {
+          this.$message.error(response)
+        }
+        axios.put(`/api/user/space/${spaceUU}/changeAdmin`, {}, { params })
+          .then(success)
+          .catch(error)
+      }
+    }
   }
 </script>
 
 <style scoped>
+  .message-panel .message-action button.btn-change {
+    border-color: #4E8EFC;
+    background-color: #4E8EFC;
+
+    color: #FFFFFF;
+  }
+  .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>
+  .admin-message span:first-child {
+    text-decoration: underline;
 
+    color: #006ACD;
+    cursor: pointer;
+  }
+  .admin-message span:last-child::after {
+    content: '>';
+    color: #646464;
+  }
+  .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>

+ 66 - 0
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseApps.vue

@@ -0,0 +1,66 @@
+<template>
+  <div>
+    <app-list :rows="apps"></app-list>
+  </div>
+</template>
+
+<script>
+  import AppList from './common/AppList'
+
+  export default {
+    name: 'enterprise-apps',
+    components: {
+      AppList
+    },
+    data () {
+      return {
+        apps: [
+          {
+            label: 'UAS系统',
+            status: 2
+          },
+          {
+            label: 'B2B商务平台',
+            status: 2
+          },
+          {
+            label: '优软商城',
+            status: 1
+          },
+          {
+            label: '金融服务',
+            status: 0
+          },
+          {
+            label: '优软人才网',
+            status: 0
+          },
+          {
+            label: 'UU众创',
+            status: 0
+          },
+          {
+            label: '定制商城',
+            status: 0
+          }
+        ]
+      }
+    }
+  }
+</script>
+
+<style scoped>
+  .message-panel .message-action button.btn-auth {
+    border-color: #4E8EFC;
+    background-color: #4E8EFC;
+
+    color: #FFFFFF;
+  }
+</style>
+
+<style>
+  .admin-message::after {
+    content: 'ABV';
+    color: black;
+  }
+</style>

+ 44 - 0
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseAuth.vue

@@ -0,0 +1,44 @@
+<template>
+  <div>
+    <message-list :messages="messages">
+      <button class="btn btn-default btn-auth" slot="action">认证通过</button>
+      <button class="btn btn-default" slot="action">不通过</button>
+    </message-list>
+  </div>
+</template>
+
+<script>
+  import MessageList from './common/MessageList'
+
+  export default {
+    name: 'enterprise-auth',
+    components: {
+      MessageList
+    },
+    data () {
+      return {
+      }
+    },
+    computed: {
+      messages () {
+        return this.$store.getters.enAuthInfo
+      }
+    }
+  }
+</script>
+
+<style scoped>
+  .message-panel .message-action button.btn-auth {
+    border-color: #4E8EFC;
+    background-color: #4E8EFC;
+
+    color: #FFFFFF;
+  }
+</style>
+
+<style>
+  .admin-message::after {
+    content: 'ABV';
+    color: black;
+  }
+</style>

+ 13 - 6
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseBaseAction.vue

@@ -5,15 +5,15 @@
         <li @click="$router.go(-1)">
           <img src="/static/images/go_back.png" alt="GoBack"/>
         </li>
-        <router-link tag="li" active-class="active" :to="{name: 'EnterpriseBasicInfo'}" exact>
+        <router-link tag="li" active-class="active" :to="{name: 'EnterpriseBasicInfo'}" replace>
           <a>基本信息</a><span class="triangle"></span></router-link>
-        <router-link tag="li" active-class="active" :to="{name: 'EnterpriseMembers'}" exact>
+        <router-link tag="li" active-class="active" :to="{name: 'EnterpriseMembers'}" replace>
           <a>成员管理</a><span class="triangle"></span></router-link>
-        <router-link tag="li" active-class="active" :to="{name: 'EnterpriseAdmin'}" exact><a>管理员信息</a><span
+        <router-link tag="li" active-class="active" :to="{name: 'EnterpriseAdmin'}" replace><a>管理员信息</a><span
           class="triangle"></span></router-link>
-        <router-link tag="li" active-class="active" :to="{name: 'EnterpriseAuth'}" exact>
+        <router-link tag="li" active-class="active" :to="{name: 'EnterpriseAuth'}" replace>
           <a>认证信息</a><span class="triangle"></span></router-link>
-        <router-link tag="li" active-class="active" :to="{name: 'EnterpriseApps'}" exact>
+        <router-link tag="li" active-class="active" :to="{name: 'EnterpriseApps'}" replace>
           <a>应用管理</a><span class="triangle"></span></router-link>
       </ul>
     </div>
@@ -23,8 +23,15 @@
 </template>
 
 <script>
+  import * as types from '@/store/mutation-types'
+
   export default {
-    name: 'common-menu'
+    name: 'common-menu',
+    beforeRouteLeave (to, from, next) {
+      // 清除 store 数据
+      this.$store.commit(types.CLEAR_ENTERPRISE)
+      next()
+    }
   }
 </script>
 

+ 2 - 1
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseBasicInfo.vue

@@ -101,7 +101,8 @@
       }
     },
     mounted () {
-      this.enterpriseInfo = Object.assign({}, this.enterpriseInfo, this.$route.params)
+      const enterprise = this.$store.state.enterprises.savedEnterprise
+      this.enterpriseInfo = Object.assign({}, this.enterpriseInfo, enterprise)
     }
   }
 </script>

+ 2 - 0
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseHome.vue

@@ -78,6 +78,7 @@
 <script>
   import _ from 'lodash'
   import axios from '@/assets/js/axios'
+  import * as types from '@/store/mutation-types'
   import {ValidCode} from '@/Constant'
   import CommonHome from '../common/CommonHome'
 
@@ -165,6 +166,7 @@
         this.fetchData()
       },
       handleClick (row) {
+        this.$store.commit(types.CHOOSE_ENTERPRISE, row)
         const routeLocation = {name: 'EnterpriseBasicInfo', params: row}
         this.$router.push(routeLocation)
       }

+ 178 - 120
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseMembers.vue

@@ -1,165 +1,223 @@
 <template>
-  <div class="basic-enterprise-info" style="padding: 30px 0 0;width: 490px;">
-    <el-form ref="form" :model="enterpriseInfo" label-width="150px">
-      <el-form-item label="企业UU">
-        <div>{{ enterpriseInfo.spaceUU }}</div>
-      </el-form-item>
-      <el-form-item label="企业名称">
-        <el-input v-model="enterpriseInfo.spaceName"></el-input>
-      </el-form-item>
-      <el-form-item label="法定代表人">
-        <el-input v-model="enterpriseInfo.corporation">
-          <div slot="append" v-if="enterpriseInfo.corporation">
-            <img src="/static/images/status/right.png" alt="Status">
-          </div>
-          <div slot="append" v-if="!enterpriseInfo.corporation">
-            <img src="/static/images/status/wrong.png" alt="Status">
-          </div>
-        </el-input>
+  <div>
+    <el-container>
+      <el-main>
+        <el-table
+          :data="userList"
+          stripe
+          style="width: 100%">
+          <el-table-column
+            label="操作"
+            width="150">
+            <template slot-scope="scope">
+              <span v-if="scope.row.userUU === enterprise.adminUU" >管理员</span>
+              <el-button type="text" size="small" v-if="scope.row.userUU !== enterprise.adminUU"
+                         @click="handleClick(scope.row)">解除绑定
+              </el-button>
+            </template>
+          </el-table-column>
+          <el-table-column
+            prop="vipName"
+            label="用户名称"
+            width="110">
+          </el-table-column>
+          <el-table-column
+            prop="userUU"
+            label="个人UU"
+            width="130">
+          </el-table-column>
+          <el-table-column
+            prop="mobile"
+            label="手机号"
+            width="140">
+          </el-table-column>
+          <el-table-column
+            prop="email"
+            label="邮箱">
+          </el-table-column>
+        </el-table>
+      </el-main>
+      <el-footer>
+        <el-row type="flex" class="row-bg">
+          <button
+            type="button"
+            class="btn btn-default btn-refresh"
+            @click="fetchData">
+            刷新
+          </button>
 
-      </el-form-item>
-      <el-form-item label="营业执照号">
-        <el-input v-model="enterpriseInfo.businessCode"></el-input>
-      </el-form-item>
-      <el-form-item label="营业执照附件">
-        <el-input v-model="enterpriseInfo.businessCodeImage">
-          <i class="el-input__icon" slot="prefix" style="line-height: 14px;margin-left: 10px;">
-            <img src="/static/images/pic.png" alt="Picture">
-          </i>
-          <i class="el-input__icon" slot="suffix" style="line-height: 12px;">
-            <img src="/static/images/look.png" alt="Look">
-          </i>
-        </el-input>
-      </el-form-item>
-      <el-form-item label="企业地址">
-        <el-input v-model="enterpriseInfo.companyAddress" :readonly="true">
-          <i class="el-input__icon" slot="prefix" style="line-height: 16px;margin-left: 10px;">
-            <img src="/static/images/loc.png" alt="Location">
-          </i>
-        </el-input>
-      </el-form-item>
-      <el-form-item label="认证状态" width="400">
-        <el-select v-model="enterpriseInfo.validCode" placeholder="请选择认证状态">
-          <el-option label="未认证" :value="0"></el-option>
-          <el-option label="申诉中" :value="1"></el-option>
-          <el-option label="已认证" :value="2"></el-option>
-          <el-option label="未通过" :value="3"></el-option>
-        </el-select>
-        <span style="padding-left: 8px;">
-            <img src="/static/images/status/right.png" alt="Status" v-if="enterpriseInfo.validCode === 2">
-            <img src="/static/images/status/wrong.png" alt="Status" v-if="enterpriseInfo.validCode !== 2">
-          </span>
-      </el-form-item>
-      <el-form-item label="注册时间">
-        <!-- TODO 注册时间 -->
-        <div v-text="enterpriseInfo.registerDate">2017-06-11 12:51:44</div>
-      </el-form-item>
-      <el-form-item>
-        <div class="action-form-item">
-          <el-button type="primary" @click="onSubmit">保存修改</el-button>
-        </div>
-      </el-form-item>
-    </el-form>
+          <div class="pagination-area">
+            <el-pagination
+              background
+              layout="prev, pager, next"
+              prev-text="< 上一页"
+              next-text="下一页 >"
+              :page-size="pageParams.size"
+              :total="total"
+              @current-change="handleCurrentChange">
+            </el-pagination>
+          </div>
+        </el-row>
+      </el-footer>
+    </el-container>
   </div>
 </template>
 
 <script>
   import _ from 'lodash'
   import axios from '@/assets/js/axios'
+  import * as types from '@/store/mutation-types'
 
   export default {
-    name: 'enterprise-basic-info',
+    name: 'enterprise-members',
     data () {
       return {
-        enterpriseInfo: {}
+        pageParams: {
+          page: 1,
+          size: 10,
+          spaceUU: null
+        },
+        total: 0,
+        userList: []
+      }
+    },
+    computed: {
+      enterprise () {
+        return this.$store.state.enterprises.savedEnterprise
       }
     },
     methods: {
-      onSubmit () {
-        const data = _.pick(this.enterpriseInfo,
-          [
-            'spaceUU',
-            'spaceName',
-            'corporation',
-            'businessCode',
-            'businessCodeImage',
-            'companyAddress',
-            'validCode'
-          ]
-        )
+      fetchData () {
+        const params = _.defaultsDeep({}, this.pageParams)
+        params.page = params.page - 1
+        params.spaceUU = this.enterprise.spaceUU
+
+        const success = page => {
+          this.userList = page.content || []
+          this.total = page.totalElements
+        }
+        const error = error => {
+          this.$message.error(error)
+        }
+        axios.get('/api/user//showEnUserByPagination', { params }).then(success).catch(error)
+      },
+      handleCurrentChange (currentPage) {
+        this.pageParams.page = currentPage
+        this.fetchData()
+      },
+      handleClick (user) {
+        this.$confirm(`此操作将解绑用户${user.vipName}, 是否继续?`, '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          this.unbindUser(user)
+        }).catch(() => {
+          this.$message({
+            type: 'info',
+            message: '已取消删除'
+          })
+        })
+      },
+      unbindUser (user) {
+        const params = { userUU: user.userUU }
+
+        const success = (space) => {
+          this.$store.commit(types.CHOOSE_ENTERPRISE, space)
+          this.fetchData()
 
-        const success = () => {
-          this.isChange = false
           this.$message.success('保存成功')
-          this.$route.go(-1)
         }
         const error = error => {
           this.$message.error(error)
         }
 
-        axios.put('/api/user/space//modifySpaceInfo', data).then(success).catch(error)
+        axios.put(`/api/user/space/${this.enterprise.spaceUU}/unbindUser`, {}, {params}).then(
+          success).catch(error)
       }
     },
-    mounted () {
-      this.enterpriseInfo = Object.assign({}, this.enterpriseInfo, this.$route.params)
+    watch: {
+      '$route': 'fetchData'
+    },
+    created () {
+      this.fetchData()
     }
   }
 </script>
 
 <style scoped>
-  .action-form-item {
-    margin-left: -128px;
-    margin-top: 70px;
+  .el-main {
+    height: 463px;
+    padding: 0;
   }
-  .action-form-item .el-button {
-    border-radius: 0;
-    padding: 10px 20px;
-  }
-  .el-button {
-    width: 120px;
-    height: 34px;
+  .el-footer {
+    height: 64px;
+    background-color: #E8EDF1;
   }
-  .el-button--primary {
-    background-color: #4E8EFC;
-    border-color: #4E8EFC;
+  .btn-refresh {
+    margin: 17px 0;
+    width: 50px;
+    height: 28px;
+    border-radius: 0;
+
+    color: #000000;
+    font-size: 13px;
+    font-family: "Microsoft YaHei", sans-serif;
   }
-  .el-button--primary.is-disabled {
-    background-color: #D2D2D2;
-    border-color: #D2D2D2;
+  .pagination-area {
+    padding: 17px 0 17px 150px;
   }
 </style>
 
 <style>
-  .basic-enterprise-info .el-input__inner {
-    height: 34px;
-    width: 340px;
-    border-radius: 0;
+  .el-pagination button, .el-pagination span:not([class*=suffix]) {
+    color: #000000;
+    font-weight: normal;
   }
-  .basic-enterprise-info .el-form-item {
-    margin-bottom: 12px;
+  .el-pagination.is-background .btn-next, .el-pagination.is-background .btn-prev, .el-pagination.is-background .el-pager li {
+    font-weight: normal;
   }
-  .basic-enterprise-info .el-form-item__label {
-    margin: 0;
-    height: 34px;
-    padding-left: 26px;
-    text-align: left;
-    line-height: 34px;
-
+  .el-pagination.is-background .el-pager li.active {
+    background-color: #303743;
+    color: #fff;
+  }
+  .el-table th.is-leaf:first-child {
+    padding-left: 40px;
+  }
+  .el-table--enable-row-transition .el-table__body td:first-child {
+    padding-left: 40px;
+  }
+  .el-table th>.cell {
     color: #000000;
-    font-size: 14px;
+    font-size: 13px;
     font-weight: normal;
-    font-family: "SimHei", sans-serif;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  .el-table th {
+    padding: 9px 0 8px;
+  }
+  .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;
   }
-  .basic-enterprise-info .el-form-item__content {
-    height: 34px;
-    line-height: 34px;
+  /* striped row */
+  .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;
   }
-  .basic-enterprise-info div.el-form-item:nth-last-child(3) .el-form-item__content {
-    width: 400px;
+  .el-table td {
+    padding: 4px 0;
+
+    font-size: 13px;
+    font-family: "Microsoft YaHei", sans-serif;
   }
-  .basic-enterprise-info .el-input-group__append {
-    background: none;
-    border: none;
-    padding-left: 12px;
+  .el-table--striped .el-table__body tr.el-table__row--striped td {
+    background: #F4F4F4;
   }
 </style>

+ 85 - 0
sso-manage-console-web/src/components/accounts/enterprises/common/AppList.vue

@@ -0,0 +1,85 @@
+<template>
+  <div class="message-panel">
+    <div class="row" v-for="row in rows">
+      <div class="message-label">
+        <div class="message-label-wrapper">
+          <span>{{row.label}}</span>
+          <span v-if="row.status === 0" style="color: #E68717;">未开通</span>
+          <span v-if="row.status === 1" style="color: #E68717;">开通失败</span>
+          <span v-if="row.status === 2" style="color: #646464;">已开通</span>
+        </div>
+      </div>
+      <div class="message-value">
+        <button class="btn btn-primary btn-open" v-if="row.status === 0" @click="() => { $message.info('开通') }">开通</button>
+        <button class="btn btn-primary btn-reopen" v-if="row.status === 1" @click="() => { $message.info('重新开通') }">重新开通</button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'app-list',
+    props: {
+      rows: Array
+    }
+  }
+</script>
+
+<style scoped>
+  .message-panel {
+    padding-top: 16px;
+  }
+  .message-panel .row {
+    margin: 10px 0;
+    height: 40px;
+    line-height: 40px;
+    padding-left: 26px;
+  }
+  .message-panel .row div {
+    display: inline-block;
+  }
+  .message-panel .row .message-label {
+    padding: 0 20px;
+    width: 330px;
+    height: 40px;
+    border: 1px #D2D2D2 solid;
+
+    color: #000000;
+    font-size: 14px;
+    font-weight: normal;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  .message-panel .row .message-label .message-label-wrapper {
+    display: flex;
+    justify-content: space-between;
+  }
+  .message-panel .row .message-value {
+    margin-left: 20px;
+    width: 100px;
+
+    color: #000000;
+    font-size: 14px;
+    font-weight: normal;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  .message-panel .row .message-value button {
+    height: 26px;
+    outline: none;
+    border-radius: 0;
+
+    color: #FFFFFF;
+    font-size: 12px;
+    font-weight: normal;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  .message-panel .row .message-value btn-open {
+    width: 50px;
+    line-height: 12px;
+  }
+  .message-panel .row .message-value .btn-reopen {
+    width: 64px;
+    line-height: 12px;
+    padding: 6px 0;
+  }
+</style>

+ 75 - 0
sso-manage-console-web/src/components/accounts/enterprises/common/MessageList.vue

@@ -0,0 +1,75 @@
+<template>
+  <div class="message-panel">
+    <div class="row" v-for="message in messages">
+      <div class="message-label">{{ message.label }}</div>
+      <div class="message-value" :class="message.suffixClass">
+        <span @click="message.action()">{{ message.value }}</span>
+        <span v-if="message.suffixClass"></span>
+      </div>
+    </div>
+    <div class="row message-action">
+      <slot name="action"></slot>
+    </div>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'message-list',
+    props: {
+      messages: Array
+    }
+  }
+</script>
+
+<style scoped>
+  .message-panel {
+    padding-top: 16px;
+  }
+  .message-panel .row {
+    margin: 10px 0;
+    height: 34px;
+    line-height: 34px;
+  }
+  .message-panel .row div {
+    display: inline-block;
+    padding: 10px 0;
+    line-height: 14px;
+  }
+  .message-panel .row .message-label {
+    padding-left: 26px;
+    width: 150px;
+
+    color: #000000;
+    font-size: 14px;
+    font-weight: normal;
+    font-family: "SimHei", sans-serif;
+  }
+  .message-panel .row .message-value {
+    width: 600px;
+
+    color: #646464;
+    font-size: 14px;
+    font-weight: normal;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  .message-panel .message-action {
+    margin-top: 70px;
+    padding-left: 26px;
+  }
+  .message-panel .message-action button {
+    margin-right: 26px;
+    width: 120px;
+    height: 34px;
+    line-height: 20px;
+    border-radius: 0;
+
+    color: #000000;
+    font-size: 14px;
+    font-weight: normal;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  .message-panel .message-action button:last-child {
+    margin-right: 0;
+  }
+</style>

+ 5 - 1
sso-manage-console-web/src/components/accounts/enterprises/index.js

@@ -3,11 +3,15 @@ import EnterpriseBasicInfo from './EnterpriseBasicInfo'
 import EnterpriseMembers from './EnterpriseMembers'
 import EnterpriseAdmin from './EnterpriseAdmin'
 import EnterpriseBaseAction from './EnterpriseBaseAction'
+import EnterpriseAuth from './EnterpriseAuth'
+import EnterpriseApps from './EnterpriseApps'
 
 export {
   EnterpriseHome,
   EnterpriseBasicInfo,
   EnterpriseMembers,
   EnterpriseAdmin,
-  EnterpriseBaseAction
+  EnterpriseBaseAction,
+  EnterpriseAuth,
+  EnterpriseApps
 }

+ 2 - 4
sso-manage-console-web/src/main.js

@@ -3,7 +3,7 @@
 import Vue from 'vue'
 import App from './App'
 import router from './router'
-import {options, VueNotifications} from '@/assets/js/notifications'
+import store from './store'
 import 'bootstrap/dist/css/bootstrap.min.css'
 import 'font-awesome/css/font-awesome.min.css'
 import ElementUI from 'element-ui'
@@ -12,9 +12,6 @@ import 'element-ui/lib/theme-chalk/index.css'
 
 Vue.config.productionTip = false
 
-// Activate the plugin
-Vue.use(VueNotifications, options)
-
 // Date format filter
 Vue.filter('date', function (value) {
   if (isNaN(value)) {
@@ -31,6 +28,7 @@ Vue.use(ElementUI)
 new Vue({
   el: '#app',
   router,
+  store,
   template: '<App/>',
   components: { App }
 })

+ 15 - 8
sso-manage-console-web/src/router/index.js

@@ -1,16 +1,19 @@
 import Vue from 'vue'
 import Router from 'vue-router'
+import store from '../store'
 import Index from '@/components/Index'
 import AccountIndex from '@/components/accounts/AccountIndex'
 import {
-  EnterpriseAdmin, EnterpriseBaseAction, EnterpriseBasicInfo,
-  EnterpriseHome, EnterpriseMembers
+  EnterpriseAdmin, EnterpriseApps, EnterpriseAuth, EnterpriseBaseAction,
+  EnterpriseBasicInfo, EnterpriseHome, EnterpriseMembers
 } from '@/components/accounts/enterprises'
 import {UserBasicInfo, UserHome} from '@/components/accounts/users'
 import {AppealHome} from '@/components/accounts/appeals'
 
 Vue.use(Router)
 
+console.log(store)
+
 export default new Router({
   routes: [
     {
@@ -36,11 +39,13 @@ export default new Router({
           path: 'enterprise',
           component: EnterpriseBaseAction,
           beforeEnter: (to, from, next) => {
-            const enterpriseInfo = to.params
-            if (enterpriseInfo.spaceUU) {
+            console.log(store.getters.hasEnInfo)
+
+            //
+            if (store.getters.hasEnInfo) {
               next()
             } else {
-              next({ name: 'EnterpriseHome' })
+              next({ name: 'EnterpriseHome', replace: true })
             }
           },
           children: [
@@ -65,12 +70,14 @@ export default new Router({
             {
               // 企业认证信息
               path: 'auth',
-              name: 'EnterpriseAuth'
+              name: 'EnterpriseAuth',
+              component: EnterpriseAuth
             },
             {
               // 企业应用管理
               path: 'apps',
-              name: 'EnterpriseApps'
+              name: 'EnterpriseApps',
+              component: EnterpriseApps
             }
           ]
         },
@@ -88,7 +95,7 @@ export default new Router({
             if (userInfo.userUU) {
               next()
             } else {
-              next({ name: 'UserHome' })
+              next({ name: 'UserHome', replace: true })
             }
           }
         },

+ 9 - 0
sso-manage-console-web/src/store/actions.js

@@ -0,0 +1,9 @@
+import * as types from './mutation-types'
+
+export const addToCart = ({ commit, product }) => {
+  if (product.inventory > 0) {
+    commit(types.ADD_TO_CART, {
+      id: product.id
+    })
+  }
+}

+ 28 - 0
sso-manage-console-web/src/store/index.js

@@ -1,4 +1,32 @@
 import Vue from 'vue'
 import Vuex from 'vuex'
+import VuexPersistence from 'vuex-persist'
+import createLogger from 'vuex/dist/logger'
+import * as actions from './actions'
+import enterprises from './modules/enterprises'
 
 Vue.use(Vuex)
+
+// Production environments
+const vuexSession = new VuexPersistence({
+  storage: window.sessionStorage
+})
+
+const plugins = [vuexSession.plugin]
+
+// Debug environments
+const debug = process.env.NODE_ENV !== 'production'
+if (debug) {
+  plugins.push(createLogger())
+}
+
+const store = new Vuex.Store({
+  actions,
+  modules: {
+    enterprises
+  },
+  strict: debug,
+  plugins: plugins
+})
+
+export default store

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

@@ -0,0 +1,67 @@
+import * as types from '../mutation-types'
+
+// State
+const state = {
+  savedEnterprise: {}
+}
+
+// Getters
+const getters = {
+  enAdmin: state => {
+    const admin = state.savedEnterprise.admin || {}
+    const messages = []
+
+    messages.push({ label: '个人UU', value: admin.userUU || '' })
+
+    messages.push({ label: '管理员名称', value: admin.vipName || '' })
+
+    messages.push({ label: '手机号', value: admin.mobile || '' })
+
+    messages.push({ label: '邮箱', value: admin.email || '' })
+
+    messages.push({ label: '申诉状态', value: '申诉中' })
+    return messages
+  },
+  enAuthInfo: state => {
+    const enterprise = state.savedEnterprise || {}
+    const messages = []
+
+    messages.push({ label: '企业名称', value: enterprise.spaceName || '' })
+
+    messages.push({ label: '法定代表人', value: enterprise.corporation || '' })
+
+    messages.push({ label: '营业执照号', value: enterprise.businessCode || '' })
+
+    messages.push({ label: '营业执照附件', value: enterprise.businessCodeImage || '' })
+
+    messages.push({ label: '企业地址', value: enterprise.companyAddress || '' })
+    return messages
+  },
+  hasEnInfo: state => {
+    return !!(state.savedEnterprise && state.savedEnterprise.spaceUU)
+  }
+}
+
+// Actions
+const actions = {
+
+}
+
+// Mutations
+const mutations = {
+  [types.CHOOSE_ENTERPRISE] (state, enterprise) {
+    if (enterprise) {
+      state.savedEnterprise = enterprise
+    }
+  },
+  [types.CLEAR_ENTERPRISE] (state) {
+    state.savedEnterprise = {}
+  }
+}
+
+export default {
+  state,
+  getters,
+  actions,
+  mutations
+}

+ 6 - 0
sso-manage-console-web/src/store/mutation-types.js

@@ -0,0 +1,6 @@
+export const CHOOSE_ENTERPRISE = 'CHOOSE_ENTERPRISE'
+export const CLEAR_ENTERPRISE = 'CLEAR_ENTERPRISE'
+export const ADD_TO_CART = 'ADD_TO_CART'
+export const SET_CART_ITEMS = 'SET_CART_ITEMS'
+export const SET_CHECKOUT_STATUS = 'SET_CHECKOUT_STATUS'
+export const RECEIVE_PRODUCTS = 'RECEIVE_PRODUCTS'

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

@@ -3317,6 +3317,10 @@ lodash.memoize@^4.1.2:
   version "4.1.2"
   resolved "http://registry.npm.taobao.org/lodash.memoize/download/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
 
+lodash.merge@^4.6.0:
+  version "4.6.0"
+  resolved "http://registry.npm.taobao.org/lodash.merge/download/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5"
+
 lodash.uniq@^4.5.0:
   version "4.5.0"
   resolved "http://registry.npm.taobao.org/lodash.uniq/download/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
@@ -5532,6 +5536,12 @@ vue@^2.5.2:
   version "2.5.13"
   resolved "http://registry.npm.taobao.org/vue/download/vue-2.5.13.tgz#95bd31e20efcf7a7f39239c9aa6787ce8cf578e1"
 
+vuex-persist@^1.1.1:
+  version "1.1.1"
+  resolved "http://registry.npm.taobao.org/vuex-persist/download/vuex-persist-1.1.1.tgz#dc3875fa17d5a5881a526276608bdfc8001ed0c1"
+  dependencies:
+    lodash.merge "^4.6.0"
+
 vuex@^3.0.1:
   version "3.0.1"
   resolved "http://registry.npm.taobao.org/vuex/download/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2"

+ 10 - 13
sso-manage-console/src/main/java/com/uas/sso/sso/backend/api/UserManageController.java

@@ -1,15 +1,13 @@
 package com.uas.sso.sso.backend.api;
 
 import com.uas.sso.entity.User;
-import com.uas.sso.entity.Userspace;
 import com.uas.sso.sso.backend.dto.UpdateUserInfo;
 import com.uas.sso.sso.backend.service.UserService;
 import com.uas.sso.sso.backend.support.ResultBean;
-import java.util.Set;
+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.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
@@ -39,24 +37,23 @@ public class UserManageController {
             @RequestParam(required = false) Short mobileValidCode,
             @RequestParam(required = false) String key,
             @RequestParam(required = false) String keyword) {
-
+        // Controller中的Pageable类型参数默认根据查询参数 page 和 size 注入并实例化
         return new ResultBean<>(userService.showUserByPagination(page, fromApp, mobileValidCode, key, keyword));
     }
 
-    @RequestMapping(method = RequestMethod.GET, path = "//showUserByPaginationOld",
+    @RequestMapping(method = RequestMethod.GET, path = "//showEnUserByPagination",
             produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
-    public ResultBean<org.springframework.data.domain.Page<User>> showUserByPaginationOld(Pageable page,
-            @RequestParam(required = false) String name,
-            @RequestParam(required = false) String phone) {
-        // Controller中的Pageable类型参数默认根据查询参数 page 和 size 注入并实例化
-        return new ResultBean<>(userService.showUserByPaginationOld(page, name, phone));
+    public ResultBean<org.springframework.data.domain.Page<User>> showEnUserByPagination(Pageable page, Long spaceUU) {
+
+        return new ResultBean<>(userService.showEnUserByPagination(page, spaceUU));
     }
 
-    @RequestMapping(method = RequestMethod.GET, path = "/{userUU}/findSpacesByUser",
+    @RequestMapping(method = RequestMethod.GET, path = "//searchUserByKeyword",
             produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
-    public ResultBean<Set<Userspace>> findSpacesByUser(@PathVariable("userUU") Long userUu) {
+    public ResultBean<List<User>> searchUserByKeyword(@RequestParam("spaceUU") Long spaceUu,
+            String key, String keyword) {
 
-        return new ResultBean<>(userService.findSpacesByUser(userUu));
+        return new ResultBean<>(userService.searchUserByKeyword(spaceUu, key, keyword));
     }
 
     @RequestMapping(method = RequestMethod.PUT, path = "//modifyUserInfo",

+ 24 - 12
sso-manage-console/src/main/java/com/uas/sso/sso/backend/api/UserSpaceManageController.java

@@ -43,20 +43,32 @@ public class UserSpaceManageController {
 
     @RequestMapping(method = RequestMethod.PUT, path = "//modifySpaceInfo",
             produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
-    public ResultBean<Boolean> modifyUserInfo(@RequestBody UpdateSpaceInfo spaceInfo) {
+    public ResultBean<Userspace> modifyUserSpaceInfo(@RequestBody UpdateSpaceInfo spaceInfo) {
 
-        return new ResultBean<>(spaceService.modifyUserInfo(spaceInfo));
+        return new ResultBean<>(spaceService.modifyUserSpaceInfo(spaceInfo));
     }
 
-    @Deprecated
-    @RequestMapping(method = RequestMethod.GET, path = "//showSpaceByPaginationOld",
+    @RequestMapping(method = RequestMethod.PUT, path = "/{spaceUU}/unbindUser",
             produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
-    public ResultBean<org.springframework.data.domain.Page<Userspace>> showUserByPagination(Pageable page,
-            Short validCode,
-            @RequestParam(required = false) String spaceName,
-            @RequestParam(required = false) String businessCode) {
+    public ResultBean<Userspace> unbindUser(@PathVariable("spaceUU") Long spaceUu,
+            @RequestParam("userUU") Long userUu) {
 
-        return new ResultBean<>(spaceService.showSpaceByPagination(page, spaceName, businessCode, validCode));
+        return new ResultBean<>(spaceService.unbindUser(spaceUu, userUu));
+    }
+
+    @RequestMapping(method = RequestMethod.PUT, path = "/{spaceUU}/changeAdmin",
+            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public ResultBean<Userspace> changeAdmin(@PathVariable("spaceUU") Long spaceUu, Long userUU) {
+
+        return new ResultBean<>(spaceService.changeAdmin(spaceUu, userUU));
+    }
+
+    @RequestMapping(method = RequestMethod.PUT, path = "/{spaceUU}/authEnterpriseInfo",
+            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public ResultBean<Userspace> authEnterpriseInfo(@PathVariable("spaceUU") Long spaceUu,
+            Boolean isPass, @RequestParam(required = false) String reason) {
+
+        return new ResultBean<>(spaceService.authEnterpriseInfo(spaceUu, isPass, reason));
     }
 
     @RequestMapping(method = RequestMethod.PUT, path = "/{spaceUU}/addUserToSpace",
@@ -68,8 +80,8 @@ public class UserSpaceManageController {
 
     @RequestMapping(method = RequestMethod.PUT, path = "/{spaceUU}/bindAppWithSpace",
             produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
-    public ResultBean<Boolean> bindAppWithSpace(@PathVariable("spaceUU") Long spaceUu, String appUid) {
-        spaceService.bindAppWithSpace(spaceUu, appUid);
-        return new ResultBean<>(true);
+    public ResultBean<Userspace> bindAppWithSpace(@PathVariable("spaceUU") Long spaceUu, String appUid) {
+
+        return new ResultBean<>(spaceService.bindAppWithSpace(spaceUu, appUid));
     }
 }

+ 37 - 8
sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/UserService.java

@@ -1,10 +1,9 @@
 package com.uas.sso.sso.backend.service;
 
 import com.uas.sso.entity.User;
-import com.uas.sso.entity.Userspace;
 import com.uas.sso.sso.backend.dto.UpdateUserInfo;
 import java.util.List;
-import java.util.Set;
+import javax.validation.constraints.NotNull;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 
@@ -15,14 +14,44 @@ import org.springframework.data.domain.Pageable;
  */
 public interface UserService {
 
-    List<User> showAllUsers();
-
-    Page<User> showUserByPaginationOld(Pageable page, String name, String phone);
-
-    Set<Userspace> findSpacesByUser(Long userUu);
-
+    /**
+     * 分页获取用户信息.
+     *
+     * @param page  分页参数
+     * @param fromApp   注册来源
+     * @param mobileValidCode   收集认证状态
+     * @param key   搜索字段
+     * @param keyword   搜索关键字
+     * @return  获取用户分页数据
+     */
     Page<User> showUserByPagination(Pageable page, String fromApp, Short mobileValidCode, String key,
             String keyword);
 
+    /**
+     * 分页获取企业用户信息.
+     *
+     * @param page  分页参数
+     * @param spaceUU   企业UU
+     * @return  获取后的分页数据
+     */
+    Page<User> showEnUserByPagination(@NotNull Pageable page, @NotNull Long spaceUU);
+
+    /**
+     * 根据关键字搜索企业用户信息.
+     *
+     * @param spaceUu   企业UU
+     * @param key   搜索字段
+     * @param keyword   搜索关键字
+     * @return  用户列表信息
+     */
+    List<User> searchUserByKeyword(Long spaceUu, String key, String keyword);
+
+    /**
+     * 管理员收到客户时,后台修改用户的信息.
+     *
+     * @param userInfo  用户信息
+     * @return  操作状态
+     */
     Boolean modifyUserInfo(UpdateUserInfo userInfo);
 }
+

+ 15 - 3
sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/UserSpaceService.java

@@ -2,6 +2,7 @@ package com.uas.sso.sso.backend.service;
 
 import com.uas.sso.entity.Userspace;
 import com.uas.sso.sso.backend.dto.UpdateSpaceInfo;
+import javax.validation.constraints.NotNull;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 
@@ -15,11 +16,22 @@ public interface UserSpaceService {
     Page<Userspace> showSpaceByPagination(Pageable page, Short validCode, String fromApp, String key,
             String keyword);
 
-    Boolean modifyUserInfo(UpdateSpaceInfo spaceInfo);
+    Userspace modifyUserSpaceInfo(UpdateSpaceInfo spaceInfo);
 
-    Page<Userspace> showSpaceByPagination(Pageable page, String spaceName, String businessCode, Short validCode);
+    /**
+     * 管理员通过后台管理解绑某个企业的用户.
+     *
+     * @param spaceUu   企业UU
+     * @param userUu    用户UU
+     * @return  解绑之后的企业信息
+     */
+    Userspace unbindUser(@NotNull Long spaceUu, @NotNull Long userUu);
+
+    Userspace changeAdmin(@NotNull Long spaceUu, @NotNull Long userUU);
+
+    Userspace authEnterpriseInfo(@NotNull Long spaceUu, @NotNull Boolean isPass, String reason);
 
     void addUserToSpace(Long spaceUu, Long userUu);
 
-    void bindAppWithSpace(Long spaceUu, String appUid);
+    Userspace bindAppWithSpace(Long spaceUu, String appUid);
 }

+ 83 - 68
sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/UserServiceImpl.java

@@ -11,11 +11,14 @@ import com.uas.sso.sso.backend.service.UserService;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.Set;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
 import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
@@ -36,83 +39,95 @@ public class UserServiceImpl implements UserService {
     }
 
     @Override
-    public List<User> showAllUsers() {
-        List<User> all = userDao.findAll();
-
-        if (CollectionUtils.isEmpty(all)) {
-            all = Collections.emptyList();
-        }
-        return all;
-    }
-
-    @Override
-    public Page<User> showUserByPaginationOld(final Pageable page, String name, String phone) {
-
-        return userDao.findAll((root, query, builder) -> {
-            List<Predicate> predicates = new ArrayList<>();
-            if (!StringUtils.isEmpty(name)) {
-                Predicate predicate = builder.like(root.get("realName"), "%" + name + "%");
-                predicates.add(predicate);
-            }
+    public Page<User> showUserByPagination(Pageable page, String fromApp, Short mobileValidCode,
+            String key, String keyword) {
 
-            if (!StringUtils.isEmpty(phone)) {
-                Predicate predicate = builder.like(root.get("mobile"), phone + "%");
-                predicates.add(predicate);
+        return userDao.findAll(new Specification<User>() {
+            @Override
+            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query,
+                    CriteriaBuilder builder) {
+                List<Predicate> predicates = new ArrayList<>();
+                if (!StringUtils.isEmpty(fromApp)) {
+                    Predicate predicate = builder.equal(root.get("fromApp"), fromApp);
+                    predicates.add(predicate);
+                }
+
+                if (!StringUtils.isEmpty(mobileValidCode)) {
+                    Predicate predicate = builder.equal(root.get("mobileValidCode"), mobileValidCode);
+                    predicates.add(predicate);
+                }
+
+                if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(keyword)) {
+                    Predicate predicate = builder.like(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;
             }
-
-            predicates.removeAll(Collections.singletonList(null));
-
-            Predicate[] array = new Predicate[predicates.size()];
-            predicates.toArray(array);
-            Predicate predicate = builder.and(array);
-            query.where(predicate);
-            return null;
         }, page);
     }
 
     @Override
-    public Set<Userspace> findSpacesByUser(Long userUu) {
-        if (userUu == null) {
-            throw new ValidationFailedException("用户UU不能为空");
-        }
-
-        final User user = userDao.findOne(userUu);
-        if (user == null) {
-            throw new ValidationFailedException(String.format("UU %d 对应用户不存在", userUu));
-        }
-
-        return user.getUserSpaces();
+    public Page<User> showEnUserByPagination(Pageable page, Long spaceUU) {
+
+        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.isMember(new Userspace(spaceUU), root.get("userSpaces"));
+                    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;
+            }
+        }, page);
     }
 
     @Override
-    public Page<User> showUserByPagination(Pageable page, String fromApp, Short mobileValidCode,
-            String key, String keyword) {
-
-        return userDao.findAll((root, query, builder) -> {
-            List<Predicate> predicates = new ArrayList<>();
-            if (!StringUtils.isEmpty(fromApp)) {
-                Predicate predicate = builder.equal(root.get("fromApp"), fromApp);
-                predicates.add(predicate);
+    public List<User> searchUserByKeyword(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.isMember(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;
             }
-
-            if (!StringUtils.isEmpty(mobileValidCode)) {
-                Predicate predicate = builder.equal(root.get("mobileValidCode"), mobileValidCode);
-                predicates.add(predicate);
-            }
-
-            if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(keyword)) {
-                Predicate predicate = builder.like(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;
-        }, page);
+        });
     }
 
     @Override

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

@@ -1,5 +1,6 @@
 package com.uas.sso.sso.backend.service.impl;
 
+import com.uas.sso.core.Status;
 import com.uas.sso.dao.AppDao;
 import com.uas.sso.dao.UserDao;
 import com.uas.sso.dao.UserspaceDao;
@@ -12,12 +13,18 @@ import com.uas.sso.sso.backend.service.UserSpaceService;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
 import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.validation.constraints.NotNull;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 
 /**
@@ -46,98 +53,111 @@ public class UserSpaceServiceImpl implements UserSpaceService {
     public Page<Userspace> showSpaceByPagination(Pageable page, Short validCode, String fromApp,
             String key, String keyword) {
 
-        return userspaceDao.findAll((root, query, builder) -> {
-            List<Predicate> predicates = new ArrayList<>();
+        return userspaceDao.findAll(new Specification<Userspace>() {
+            @Override
+            public Predicate toPredicate(Root<Userspace> root, CriteriaQuery<?> query,
+                    CriteriaBuilder builder) {
+                List<Predicate> predicates = new ArrayList<>();
 
-            if (!StringUtils.isEmpty(validCode)) {
-                Predicate predicate = builder.equal(root.get("validCode"), validCode);
-                predicates.add(predicate);
-            }
+                if (!StringUtils.isEmpty(validCode)) {
+                    Predicate predicate = builder.equal(root.get("validCode"), validCode);
+                    predicates.add(predicate);
+                }
 
-            if (!StringUtils.isEmpty(fromApp)) {
-                Predicate predicate = builder.equal(root.get("fromApp"), fromApp);
-                predicates.add(predicate);
-            }
+                if (!StringUtils.isEmpty(fromApp)) {
+                    Predicate predicate = builder.equal(root.get("fromApp"), fromApp);
+                    predicates.add(predicate);
+                }
 
-            // 搜索关键字过滤
-            if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(keyword)) {
-                Predicate predicate = builder.like(root.get(key), "%" + keyword + "%");
-                predicates.add(predicate);
-            }
+                // 搜索关键字过滤
+                if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(keyword)) {
+                    Predicate predicate = builder.like(root.get(key), "%" + keyword + "%");
+                    predicates.add(predicate);
+                }
 
-            predicates.removeAll(Collections.singletonList(null));
+                predicates.removeAll(Collections.singletonList(null));
 
-            Predicate[] array = new Predicate[predicates.size()];
-            predicates.toArray(array);
-            Predicate predicate = builder.and(array);
-            query.where(predicate);
-            return null;
+                Predicate[] array = new Predicate[predicates.size()];
+                predicates.toArray(array);
+                Predicate predicate = builder.and(array);
+                query.where(predicate);
+                return null;
+            }
         }, page);
     }
 
     @Override
-    public Boolean modifyUserInfo(UpdateSpaceInfo spaceInfo) {
+    public Userspace modifyUserSpaceInfo(UpdateSpaceInfo spaceInfo) {
         Userspace userspace = userspaceDao.findOne(spaceInfo.getSpaceUU());
         if (userspace == null) {
             throw new ValidationFailedException(String.format("UU%d对应企业不存在", spaceInfo.getSpaceUU()));
         }
 
-        if (!userspace.getSpaceName().equals(spaceInfo.getSpaceName())) {
-            Userspace existSpace = userspaceDao.findBySpaceName(spaceInfo.getSpaceName());
-            if (existSpace != null) {
-                throw new ValidationFailedException(String.format("企业%s已注册", spaceInfo.getSpaceName()));
-            }
-        }
-
-        // 避免清空营业执照因唯一性约束导致报错
         if (StringUtils.isEmpty(spaceInfo.getBusinessCode())) {
+            // 避免清空营业执照因唯一性约束导致报错
             spaceInfo.setBusinessCode(null);
-        } else {
-            if (!spaceInfo.getBusinessCode().equals(userspace.getBusinessCode())) {
-                Userspace existSpace = userspaceDao.findByBusinessCode(spaceInfo.getBusinessCode());
-                if (existSpace != null) {
-                    throw new ValidationFailedException(String.format("营业执照%s已认证", spaceInfo.getBusinessCode()));
-                }
-            }
         }
 
         userspace = spaceInfo.fillSpaceInfo(userspace);
 
-        userspaceDao.save(userspace);
-        return true;
+        return userspaceDao.save(userspace);
     }
 
     @Override
-    public Page<Userspace> showSpaceByPagination(Pageable page, String spaceName, String businessCode,
-            Short validCode) {
-        if (validCode == null) {
-            throw new ValidationFailedException("企业信息认证状态必须指定");
+    @Transactional(rollbackFor = RuntimeException.class)
+    public Userspace unbindUser(Long spaceUu, Long userUu) {
+        User user = assertUserExist(userUu);
+        Userspace space = assertSpaceExist(spaceUu);
+
+        if (user.getUserUU().equals(space.getAdminUU())) {
+            throw new ValidationFailedException("企业管理员不能直接解绑");
         }
 
-        return userspaceDao.findAll((root, query, builder) -> {
-            List<Predicate> predicates = new ArrayList<>();
+        if (!CollectionUtils.isEmpty(space.getUsers())) {
+            space.getUsers().remove(user);
+        }
+        return userspaceDao.save(space);
+    }
 
-            // 根据企业认证状态进行过滤
-            predicates.add(builder.equal(root.get("validCode"), validCode));
+    @Override
+    @Transactional(rollbackFor = RuntimeException.class)
+    public Userspace changeAdmin(Long spaceUu, Long userUU) {
+        User admin = assertUserExist(userUU);
+        Userspace space = assertSpaceExist(spaceUu);
+
+        // 检验选定管理员用户是否添加到当前企业
+        List<User> userList = space.getUsers();
+        if (CollectionUtils.isEmpty(userList) || !userList.contains(admin)) {
+            throw new ValidationFailedException("企业管理员必须是当前企业用户");
+        }
 
-            if (!StringUtils.isEmpty(spaceName)) {
-                Predicate predicate = builder.like(root.get("spaceName"), "%" + spaceName + "%");
-                predicates.add(predicate);
-            }
+        // 更新管理员信息
+        space.setAdminUU(userUU);
+        space.setAdmin(admin);
+        return userspaceDao.save(space);
+    }
 
-            if (!StringUtils.isEmpty(businessCode)) {
-                Predicate predicate = builder.like(root.get("businessCode"), businessCode + "%");
-                predicates.add(predicate);
-            }
+    private User assertUserExist(Long userUU) {
+        User user = userDao.findOne(userUU);
+        if (user == null) {
+            throw new ValidationFailedException(String.format("用户[%d]不存在", userUU));
+        }
+        return user;
+    }
 
-            predicates.removeAll(Collections.singletonList(null));
+    @Override
+    public Userspace authEnterpriseInfo(@NotNull Long spaceUu, @NotNull Boolean isPass,
+            String reason) {
+        Userspace space = assertSpaceExist(spaceUu);
 
-            Predicate[] array = new Predicate[predicates.size()];
-            predicates.toArray(array);
-            Predicate predicate = builder.and(array);
-            query.where(predicate);
-            return null;
-        }, page);
+        if (isPass) {
+            space.setValidCode((short) Status.AUTHENTICATED.getCode());
+        } else {
+            space.setValidCode((short) Status.NOT_PASSED.getCode());
+        }
+
+        // TODO 业务不清楚
+        return space;
     }
 
     @Override
@@ -167,15 +187,12 @@ public class UserSpaceServiceImpl implements UserSpaceService {
 
     @Override
     @Transactional(rollbackFor = RuntimeException.class)
-    public void bindAppWithSpace(Long spaceUu, String appUid) {
+    public Userspace bindAppWithSpace(Long spaceUu, String appUid) {
         if (spaceUu == null || StringUtils.isEmpty(appUid)) {
             throw new ValidationFailedException("企业UU或应用Uid不能为空");
         }
 
-        Userspace space = userspaceDao.findOne(spaceUu);
-        if (space == null) {
-            throw new ValidationFailedException(String.format("UU %d 对应企业不存在", spaceUu));
-        }
+        Userspace space = assertSpaceExist(spaceUu);
 
         App app = appDao.findOne(appUid);
         if (app == null) {
@@ -186,7 +203,21 @@ public class UserSpaceServiceImpl implements UserSpaceService {
         if (!apps.contains(app)) {
             apps.add(app);
 
-            userspaceDao.save(space);
+            space = userspaceDao.save(space);
+        }
+        return space;
+    }
+
+    /**
+     * 业务逻辑校验-企业UU对应企业是否存在
+     *
+     * @param spaceUu   企业UU
+     */
+    private Userspace assertSpaceExist(@NotNull Long spaceUu) {
+        Userspace space = userspaceDao.findOne(spaceUu);
+        if (space == null) {
+            throw new ValidationFailedException(String.format("企业[%d]不存在", spaceUu));
         }
+        return space;
     }
 }

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

@@ -1,12 +1,20 @@
 package com.uas.sso.entity;
 
 import com.alibaba.fastjson.annotation.JSONField;
-import org.codehaus.jackson.annotate.JsonIgnore;
-
-import javax.persistence.*;
 import java.io.Serializable;
 import java.sql.Timestamp;
 import java.util.List;
+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.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import org.codehaus.jackson.annotate.JsonIgnore;
 
 /**
  * 企业实体
@@ -184,6 +192,7 @@ public class Userspace implements Serializable {
     /**
      * 企业下的用户
      */
+    @com.fasterxml.jackson.annotation.JsonIgnore
     @ManyToMany
     @JoinTable(name = "sso$user_userspace",
             joinColumns = {@JoinColumn(name="space_uu", referencedColumnName="spaceuu")},
@@ -202,6 +211,10 @@ public class Userspace implements Serializable {
     public Userspace() {
     }
 
+    public Userspace(Long spaceUU) {
+        this.spaceUU = spaceUU;
+    }
+
     public Userspace(String spaceName) {
         this.spaceName = spaceName;
     }