Browse Source

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

wangmh 8 years ago
parent
commit
602ba8b1bb
33 changed files with 1373 additions and 422 deletions
  1. 13 1
      sso-manage-console-web/src/App.vue
  2. 8 0
      sso-manage-console-web/src/assets/js/axios.js
  3. 3 0
      sso-manage-console-web/src/components/accounts/AccountIndex.vue
  4. 7 2
      sso-manage-console-web/src/components/accounts/AccountMenu.vue
  5. 509 51
      sso-manage-console-web/src/components/accounts/appeals/AppealHome.vue
  6. 22 22
      sso-manage-console-web/src/components/accounts/common/CommonHome.vue
  7. 3 12
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseAdmin.vue
  8. 46 33
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseApps.vue
  9. 128 5
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseAuth.vue
  10. 12 3
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseBasicInfo.vue
  11. 17 18
      sso-manage-console-web/src/components/accounts/enterprises/EnterpriseHome.vue
  12. 4 5
      sso-manage-console-web/src/components/accounts/enterprises/common/AppList.vue
  13. 11 13
      sso-manage-console-web/src/components/accounts/users/UserHome.vue
  14. 0 20
      sso-manage-console-web/src/components/common/DataList.vue
  15. 0 36
      sso-manage-console-web/src/components/common/NavBar.vue
  16. 16 3
      sso-manage-console-web/src/components/common/NavHeader.vue
  17. 0 98
      sso-manage-console-web/src/components/common/PageBar.vue
  18. 0 55
      sso-manage-console-web/src/components/common/SearchDialog.vue
  19. 0 4
      sso-manage-console-web/src/components/common/index.js
  20. 4 5
      sso-manage-console-web/src/store/index.js
  21. 46 0
      sso-manage-console-web/src/store/modules/accounts.js
  22. 50 3
      sso-manage-console-web/src/store/modules/enterprises.js
  23. 7 0
      sso-manage-console-web/src/store/modules/index.js
  24. 2 0
      sso-manage-console-web/src/store/mutation-types.js
  25. 61 0
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/api/AppealBackendController.java
  26. 43 0
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/AppealService.java
  27. 207 0
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/AppealServiceImpl.java
  28. 35 27
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/ChangeAdminServiceImpl.java
  29. 4 1
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/UserServiceImpl.java
  30. 32 4
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/UserSpaceServiceImpl.java
  31. 70 0
      sso-manage-console/src/main/java/com/uas/sso/sso/backend/util/JacksonUtils.java
  32. 12 1
      sso-server/src/main/java/com/uas/sso/dao/UserspaceValidDao.java
  33. 1 0
      sso-server/src/main/java/com/uas/sso/entity/UserQuestion.java

+ 13 - 1
sso-manage-console-web/src/App.vue

@@ -12,8 +12,20 @@
     name: 'app',
     components: {
       NavHeader
+    },
+    created () {
+      // 首次获取未处理申诉的数量
+      this.$store.dispatch('countUnHandleAppeals')
+      // 建立定时任务不断获取未处理申诉的数量
+      window.appealsCount = setInterval(() => {
+        this.$store.dispatch('countUnHandleAppeals')
+      }, 3000)
+    },
+    beforeRouteLeave () {
+      console.log('清除定时器')
+      clearInterval(window.appealsCount)
     }
-}
+  }
 </script>
 
 <!-- Global CSS for all vue components -->

+ 8 - 0
sso-manage-console-web/src/assets/js/axios.js

@@ -1,11 +1,19 @@
+import _ from 'lodash'
 import axios from 'axios'
 
+const DataFormatError = new Error('后台响应数据格式异常')
+
 const instance = axios.create({})
 
 // Add a response interceptor to handle responses
 instance.interceptors.response.use(function (response) {
   const body = response.data
 
+  // Check whether response data is unified format `Object`.
+  if (_.isString(body)) {
+    return Promise.reject(DataFormatError.message)
+  }
+
   if (!body.success) {
     return Promise.reject(body.message)
   }

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

@@ -17,6 +17,9 @@
     name: 'account-index',
     components: {
       AccountMenu
+    },
+    created () {
+      this.$store.dispatch('retrieveAllApps')
     }
   }
 </script>

+ 7 - 2
sso-manage-console-web/src/components/accounts/AccountMenu.vue

@@ -8,7 +8,7 @@
         <a><img src="/static/images/data.png" alt="Data">用户管理</a>
       </router-link>
       <router-link tag="li" active-class="active" :to="{ name: 'AppealHome' }">
-        <a><img src="/static/images/data.png" alt="Data">申诉管理</a>
+        <a><img src="/static/images/data.png" alt="Data"><el-badge :is-dot="isNotification" class="item">申诉管理</el-badge></a>
       </router-link>
     </ul>
   </div>
@@ -16,7 +16,12 @@
 
 <script>
   export default {
-    name: 'account-menu'
+    name: 'account-menu',
+    computed: {
+      isNotification () {
+        return this.$store.getters.isNotification
+      }
+    }
   }
 </script>
 

+ 509 - 51
sso-manage-console-web/src/components/accounts/appeals/AppealHome.vue

@@ -1,11 +1,19 @@
 <template>
-  <common-home>
+  <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="value" placeholder="不限">
+      <el-select v-model="pageParams.type" clearable placeholder="不限" @change="handleRefreshData">
         <el-option
-          v-for="item in options"
+          v-for="item in typeOptions"
           :key="item.value"
           :label="item.label"
           :value="item.value">
@@ -14,9 +22,9 @@
 
       <!-- 申诉来源 -->
       <label>申诉来源</label>
-      <el-select v-model="value" placeholder="不限">
+      <el-select v-model="pageParams.fromApp" clearable placeholder="不限" @change="handleRefreshData">
         <el-option
-          v-for="item in options"
+          v-for="item in fromAppOptions"
           :key="item.value"
           :label="item.label"
           :value="item.value">
@@ -25,9 +33,9 @@
 
       <!-- 申诉状态 -->
       <label>申诉状态</label>
-      <el-select v-model="value" placeholder="不限">
+      <el-select v-model="pageParams.status" clearable placeholder="不限" @change="handleRefreshData">
         <el-option
-          v-for="item in options"
+          v-for="item in statusOptions"
           :key="item.value"
           :label="item.label"
           :value="item.value">
@@ -36,79 +44,529 @@
     </div>
 
     <el-table
-      :data="tableData"
+      :data="pageContent"
       stripe
       style="width: 100%">
       <el-table-column
         label="操作"
-        width="130"
-        align="center">
+        width="120">
         <template slot-scope="scope">
-          <el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
+          <el-button type="text" size="small" v-if="scope.row.status === 1" @click="approveRequest(scope.row)">审批</el-button>
+          <el-button type="text" size="small" v-else @click="showDetail(scope.row)">查看</el-button>
         </template>
       </el-table-column>
       <el-table-column
-        prop="name"
-        label="姓名"
-        width="180">
+        prop="status"
+        label="申诉状态"
+        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 === 3">未通过</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="申诉人"
+        width="100">
+        <template slot-scope="scope">
+          <span v-if="scope.row.submitterUU > 0" v-text="scope.row.contactName"></span>
+          <span v-else>游客</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="submitterUU"
+        label="个人UU"
+        width="80">
+        <template slot-scope="scope">
+          <span v-if="scope.row.submitterUU > 0" v-text="scope.row.submitterUU"></span>
+          <span v-else>未登录</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="type"
+        label="申诉类型"
+        width="90">
+        <template slot-scope="scope">
+          <span v-if="scope.row.type === 'validAccount'">验证手机</span>
+          <span v-else-if="scope.row.type === 'changeAdmin'">更换管理员</span>
+          <span v-else-if="scope.row.type === 'resetPassword'">找回密码</span>
+          <span v-else>{{scope.row.type}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="fromApp"
+        label="申诉来源"
+        width="100">
+      </el-table-column>
+      <el-table-column
+        prop="submitDate"
+        label="申诉时间"
+        :formatter="formatDate"
+        width="150">
+      </el-table-column>
+      <el-table-column
+        prop="contactName"
+        label="联系人"
+        width="100">
+      </el-table-column>
+      <el-table-column
+        prop="contactTel"
+        label="手机号"
+        width="120">
       </el-table-column>
       <el-table-column
-        prop="address"
-        label="地址">
+        prop="contactEmail"
+        label="邮箱">
       </el-table-column>
     </el-table>
+
+    <!-- 申诉审核对话框 -->
+    <el-dialog
+      :title="appealTitle"
+      :visible.sync="isShowDialog"
+      width="920px"
+      :show-close="true"
+      :append-to-body="true">
+
+      <div class="reset-or-validate" v-if="approveType === 'resetPassword' || approveType === 'validAccount'">
+        <div class="row">
+          <div class="col-lg-6">
+            <div class="message-label">UU号</div>
+            <div class="message-value" v-text="selectedAppeal.submitterUU"></div>
+          </div>
+          <div class="col-lg-6">
+            <div class="message-label">申诉来源</div>
+            <div class="message-value" v-text="selectedAppeal.fromApp || '无'"></div>
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-lg-6">
+            <div class="message-label">申诉人</div>
+            <div class="message-value message-value-highlight" v-text="selectedAppeal.contactName"></div>
+          </div>
+          <div class="col-lg-6">
+            <div class="message-label">联系人</div>
+            <div class="message-value" v-text="selectedAppeal.contactName"></div>
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-lg-6">
+            <div class="message-label">新手机号</div>
+            <div class="message-value" v-text="selectedAppeal.mobile"></div>
+          </div>
+          <div class="col-lg-6">
+            <div class="message-label">手机号</div>
+            <div class="message-value" v-text="selectedAppeal.contactTel"></div>
+          </div>
+        </div>
+        <!-- 找回密码 -->
+        <div class="reset-password" v-if="approveType === 'resetPassword'">
+          <div class="row">
+            <div class="col-lg-6">
+              <div class="message-label">新密码</div>
+              <div class="message-value" v-text="selectedAppeal.password"></div>
+            </div>
+            <div class="col-lg-6">
+              <div class="message-label">电子邮箱</div>
+              <div class="message-value" v-text="selectedAppeal.contactEmail"></div>
+            </div>
+          </div>
+        </div>
+        <!-- 验证手机 -->
+        <div class="validate-mobile" v-if="approveType === 'validAccount'">
+          <div class="row">
+            <div class="col-lg-6">
+              <div class="message-label">原手机号</div>
+              <div class="message-value" v-text="selectedAppeal.contactTel"></div>
+            </div>
+            <div class="col-lg-6">
+              <div class="message-label">电子邮箱</div>
+              <div class="message-value" v-text="selectedAppeal.contactEmail"></div>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col-lg-6">
+              <div class="message-label">登录密码</div>
+              <div class="message-value">
+                <img src="/static/images/status/right.png" alt="Status" v-if="selectedAppeal.password">
+                <img src="/static/images/status/wrong.png" alt="Status" v-if="!selectedAppeal.password">
+              </div>
+            </div>
+          </div>
+        </div>
+        <!-- 申诉说明 -->
+        <div class="row appeal-reason">
+          <div class="message-label">申述说明</div>
+          <div class="message-value" v-text="selectedAppeal.description"></div>
+        </div>
+      </div>
+
+      <!-- 更换管理员 -->
+      <div class="change-admin" v-if="approveType === 'changeAdmin'">
+        <div style="border-bottom: 1px solid #E5E5E5;padding-bottom: 40px;">
+          <div class="row">
+            <div class="col-lg-6">
+              <div class="message-label">企业UU</div>
+              <div class="message-value" v-text="selectedAppeal.spaceUU"></div>
+            </div>
+            <div class="col-lg-6">
+              <div class="message-label">申诉来源</div>
+              <div class="message-value" v-text="selectedAppeal.fromApp || '无'"></div>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col-lg-6">
+              <div class="message-label">企业名称</div>
+              <div class="message-value" v-text="selectedAppeal.spaceName"></div>
+            </div>
+            <div class="col-lg-6">
+              <div class="message-label">法定代表人</div>
+              <div class="message-value" v-text="selectedAppeal.corporation"></div>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col-lg-6">
+              <div class="message-label">营业执照号</div>
+              <div class="message-value" v-text="selectedAppeal.businessCode"></div>
+            </div>
+            <div class="col-lg-6">
+              <div class="message-label">营业执照附件</div>
+              <div class="message-value" v-text="selectedAppeal.businessCodeImage"></div>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col-lg-6" style="width: 840px;">
+              <div class="message-label">注册地址</div>
+              <div class="message-value" v-text="selectedAppeal.regAddress"></div>
+            </div>
+          </div>
+          <!-- 申诉说明 -->
+          <div class="row appeal-reason">
+            <div class="message-label">申述说明</div>
+            <div class="message-value" v-text="selectedAppeal.description"></div>
+          </div>
+        </div>
+        <div style="border-bottom: 1px solid #E5E5E5;padding: 26px 0;">
+          <div class="row">
+            <div class="col-lg-6">
+              <div class="message-label">管理员UU</div>
+              <div class="message-value" v-text="selectedAppeal.admin.userUU"></div>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col-lg-6">
+              <div class="message-label">管理员姓名</div>
+              <div class="message-value" v-text="selectedAppeal.admin.vipName"></div>
+            </div>
+            <div class="col-lg-6">
+              <div class="message-label">管理员手机号</div>
+              <div class="message-value" v-text="selectedAppeal.admin.mobile"></div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 对话框尾部 -->
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="approveAppeal(selectedAppeal.id, true)">通过</el-button>
+        <el-button @click="approveAppeal(selectedAppeal.id, false)">不通过</el-button>
+      </span>
+    </el-dialog>
   </common-home>
 </template>
 
 <script>
+  import _ from 'lodash'
+  import axios from '@/assets/js/axios'
   import CommonHome from '../common/CommonHome'
 
+  function showAppealsByPagination (pageParams, success, error) {
+    const params = _.defaultsDeep({}, pageParams)
+    params.page = params.page - 1
+
+    axios.get('/api/appeal//showAppealByPagination', { params })
+      .then(success)
+      .catch(error)
+  }
+
+  function approveAppealRequest (appealId, isPass, success, error) {
+    const params = { isPass }
+
+    axios.put(`/api/appeal/${appealId}/approveAppealRequest`, {}, { params })
+      .then(success)
+      .catch(error)
+  }
+
+  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
+  }
+
+  const typeOptions = [
+    {
+      label: '找回密码',
+      value: 'resetPassword'
+    },
+    {
+      label: '验证手机',
+      value: 'validAccount'
+    },
+    {
+      label: '更换管理员',
+      value: 'changeAdmin'
+    }
+  ]
+
+  const statusOptions = [
+    {
+      label: '已认证',
+      key: 'AUTHENTICATED',
+      value: 2
+    }, {
+      label: '未认证',
+      key: 'NOT_APPLYING',
+      value: 0
+    }, {
+      label: '待认证',
+      key: 'TO_BE_CERTIFIED',
+      value: 1
+    }, {
+      label: '未通过',
+      key: 'NOT_PASSED',
+      value: 3
+    }
+  ]
+
+  const searchKeys = [
+    {
+      label: '手机号',
+      value: 'contactTel'
+    }
+  ]
+
+  const appealTitles = {
+    resetPassword: '找回密码',
+    validAccount: '验证手机',
+    changeAdmin: '更换管理员'
+  }
+
   export default {
-    name: 'user-home',
+    name: 'appeal-home',
     components: {
       CommonHome
     },
     data () {
       return {
-        tableData: [{
-          date: '查看',
-          name: '王小虎',
-          address: '上海市普陀区金沙江路 1518 弄'
-        }, {
-          date: '查看',
-          name: '王小虎',
-          address: '上海市普陀区金沙江路 1517 弄'
-        }, {
-          date: '查看',
-          name: '王小虎',
-          address: '上海市普陀区金沙江路 1519 弄'
-        }, {
-          date: '查看',
-          name: '王小虎',
-          address: '上海市普陀区金沙江路 1516 弄'
-        }],
-        options: [{
-          value: '选项1',
-          label: '黄金糕'
-        }, {
-          value: '选项2',
-          label: '双皮奶'
-        }, {
-          value: '选项3',
-          label: '蚵仔煎'
-        }, {
-          value: '选项4',
-          label: '龙须面'
-        }, {
-          value: '选项5',
-          label: '北京烤鸭'
-        }],
-        value: ''
+        pageParams: {
+          page: 1,
+          size: 8,
+          type: null,
+          fromApp: null,
+          status: null,
+          key: 'contactTel',
+          keyword: null
+        },
+        pageContent: [],
+        total: 0,
+        formatDate: formatDate,
+        typeOptions: typeOptions,
+        statusOptions: statusOptions,
+        searchKeys: searchKeys,
+        isShowDialog: false,
+        approveType: '',
+        selectedAppeal: {}
       }
+    },
+    computed: {
+      appealTitle () {
+        return appealTitles[this.approveType]
+      },
+      fromAppOptions () {
+        return this.$store.getters.fromAppOptions
+      }
+    },
+    methods: {
+      showErrorMessage (error) {
+        this.$message.error(error)
+      },
+      fetchData () {
+        const success = page => {
+          console.log(page)
+          this.pageContent = page.content || []
+          this.total = page.totalElements
+        }
+
+        showAppealsByPagination(this.pageParams, success, this.showErrorMessage)
+      },
+      handleCurrentChange (currentPage) {
+        this.pageParams.page = currentPage
+        this.fetchData()
+      },
+      handleRefreshData () {
+        console.log(this.pageParams)
+        this.fetchData()
+      },
+      handleSearchAction (key, keyword) {
+        console.log(key, keyword)
+        this.pageParams.key = key
+        this.pageParams.keyword = keyword
+
+        this.fetchData()
+      },
+      approveRequest (appeal) {
+        if (appeal.type !== 'resetPassword' && appeal.type !== 'validAccount' &&
+          appeal.type !== 'changeAdmin') {
+          this.$message.error(`不支持的申诉类型${appeal.type()}`)
+          return -1
+        }
+
+        this.approveType = appeal.type
+        const submitInfo = JSON.parse(appeal.submitInfo)
+        this.selectedAppeal = _.defaultsDeep({}, appeal, submitInfo)
+        if (appeal.type === 'changeAdmin' && !this.selectedAppeal.admin) {
+          this.selectedAppeal.admin = {}
+        }
+        this.isShowDialog = true
+
+        console.log(this.selectedAppeal)
+      },
+      showDetail () {
+        this.$message.info('此功能暂未开通')
+      },
+      approveAppeal (appealId, isPass) {
+        const success = result => {
+          if (result) {
+            this.isShowDialog = false
+            this.$message.info('操作成功')
+
+            // 刷新页面数据
+            this.fetchData()
+          }
+        }
+
+        approveAppealRequest(appealId, isPass, success, this.showErrorMessage)
+      }
+    },
+    created () {
+      this.fetchData()
+    },
+    watch: {
+      '$route': 'fetchData'
     }
   }
 </script>
 
 <style scoped>
+  .row {
+    margin: 0;
+    padding: 16px 0;
+    height: 46px;
+    line-height: 14px;
+  }
+  .row .col-lg-6 {
+    padding: 0;
+  }
+  .row .col-lg-6>div {
+    display: inline-block;
+  }
+  .message-label {
+    width: 105px;
 
+    color: #000000;
+    font-size: 14px;
+    font-weight: normal;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  .message-value {
+    color: #828282;
+    font-size: 14px;
+    font-weight: normal;
+    font-family: "Microsoft YaHei", sans-serif;
+  }
+  .message-value-highlight {
+    color: #000000;
+    font-weight: bold;
+  }
+  .reset-or-validate {
+    width: 840px;
+  }
+  .appeal-reason {
+    width: 840px;
+  }
+  .appeal-reason>div {
+    display: inline-block;
+  }
+  .appeal-reason .message-value {
+    width: 730px;
+  }
+  .change-admin {
+    width: 840px;
+  }
+</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>

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

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="common-list-page">
     <el-container>
       <el-header>
         <el-row type="flex" class="row-bg" justify="space-between">
@@ -171,78 +171,78 @@
 
 <style>
   /* screen select */
-  .appeal-conditions div label {
+  .common-list-page .appeal-conditions div label {
     margin: 0 14px 0 28px;
     color: #000000;
     font-size: 13px;
     font-family: "Microsoft YaHei", sans-serif;
   }
-  .appeal-conditions div label:first-child {
+  .common-list-page .appeal-conditions div label:first-child {
     margin-left: 0;
   }
   /* element ui */
-  .el-select .el-input .el-input__inner {
+  .common-list-page .el-select .el-input .el-input__inner {
     color: #505050;
     font-size: 13px;
     font-family: "Microsoft YaHei", sans-serif;
   }
-  .el-select .el-input input.el-input__inner::-webkit-input-placeholder {
+  .common-list-page .el-select .el-input input.el-input__inner::-webkit-input-placeholder {
     color: #505050;
     font-size: 13px;
     font-family: "Microsoft YaHei", sans-serif;
   }
-  .el-select .el-input input.el-input__inner:-moz-placeholder {
+  .common-list-page .el-select .el-input input.el-input__inner:-moz-placeholder {
     color: #505050;
     font-size: 13px;
     font-family: "Microsoft YaHei", sans-serif;
   }
-  .el-select .el-input input.el-input__inner::-moz-placeholder {
+  .common-list-page .el-select .el-input input.el-input__inner::-moz-placeholder {
     color: #505050;
     font-size: 13px;
     font-family: "Microsoft YaHei", sans-serif;
   }
-  .el-select .el-input input.el-input__inner:-ms-input-placeholder {
+  .common-list-page .el-select .el-input input.el-input__inner:-ms-input-placeholder {
     color: #505050;
     font-size: 13px;
     font-family: "Microsoft YaHei", sans-serif;
   }
-  .appeal-conditions .el-input .el-input__inner {
+  .common-list-page .appeal-conditions .el-input .el-input__inner {
     height: 32px;
     width: 100px;
     border-radius: 0;
   }
-  .search-conditions .el-input .el-input__inner {
+  .common-list-page .search-conditions .el-input .el-input__inner {
     height: 32px;
     width: 120px;
     border-radius: 0;
   }
-  .el-pagination button, .el-pagination span:not([class*=suffix]) {
+  .common-list-page .el-pagination button, .el-pagination span:not([class*=suffix]) {
     color: #000000;
     font-weight: normal;
   }
-  .el-pagination.is-background .btn-next, .el-pagination.is-background .btn-prev, .el-pagination.is-background .el-pager li {
+  .common-list-page .el-pagination.is-background .btn-next, .el-pagination.is-background .btn-prev, .el-pagination.is-background .el-pager li {
     font-weight: normal;
   }
-  .el-pagination.is-background .el-pager li.active {
+  .common-list-page .el-pagination.is-background .el-pager li.active {
     background-color: #303743;
     color: #fff;
   }
-  .el-table th.is-leaf:first-child {
+  .common-list-page .el-table th.is-leaf:first-child {
     padding-left: 40px;
   }
-  .el-table--enable-row-transition .el-table__body td:first-child {
+  .common-list-page .el-table--enable-row-transition .el-table__body td:first-child {
     padding-left: 40px;
   }
-  .el-table th>.cell {
+  .common-list-page .el-table th>.cell {
     color: #000000;
     font-size: 13px;
     font-weight: normal;
     font-family: "Microsoft YaHei", sans-serif;
   }
-  .el-table th {
-    padding: 9px 8px 8px;
+  .common-list-page .el-table th {
+    padding: 9px 0 8px;
   }
-  .el-table--enable-row-hover .el-table__body tr:hover>td {
+  .common-list-page .el-table--enable-row-hover .el-table__body tr:hover>td {
     background-color: #D0E5F5;
 
     color: #000000;
@@ -250,20 +250,20 @@
     font-family: "Microsoft YaHei", sans-serif;
   }
   /* striped row */
-  .el-table--enable-row-hover .el-table__body tr.el-table__row--striped:hover>td {
+  .common-list-page .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;
   }
-  .el-table td {
+  .common-list-page .el-table td {
     padding: 4px 0;
 
     font-size: 13px;
     font-family: "Microsoft YaHei", sans-serif;
   }
-  .el-table--striped .el-table__body tr.el-table__row--striped td {
+  .common-list-page .el-table--striped .el-table__body tr.el-table__row--striped td {
     background: #F4F4F4;
   }
 </style>

+ 3 - 12
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseAdmin.vue

@@ -24,10 +24,6 @@
         </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>
@@ -118,12 +114,7 @@
         return adminInfo
       },
       enterprise () {
-        const enterprise = this.$store.state.enterprises.savedEnterprise
-        if (enterprise) {
-          this.adminUser = enterprise.admin
-        }
-
-        return enterprise
+        return this.$store.state.enterprises.savedEnterprise
       }
     },
     methods: {
@@ -136,8 +127,8 @@
         this.changeAdminVisible = true
       },
       showRecords () {
-        // TODO 查看更换记录
-        this.$message.info('此功能暂未实现')
+        const routeLocation = {name: 'AppealHome', replace: true}
+        this.$router.push(routeLocation)
       },
       searchAndSelectAdmin () {
         const params = { spaceUU: this.enterprise.spaceUU, key: this.key, keyword: this.keyword }

+ 46 - 33
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseApps.vue

@@ -5,8 +5,19 @@
 </template>
 
 <script>
+  import axios from '@/assets/js/axios'
+  import * as types from '@/store/mutation-types'
   import AppList from './common/AppList'
 
+  function bindAppWithSpace (spaceUU, appUid, success, error) {
+    console.log(appUid)
+    const params = { appUid }
+
+    axios.put(`/api/user/space/${spaceUU}/bindAppWithSpace`, {}, { params })
+      .then(success)
+      .catch(error)
+  }
+
   export default {
     name: 'enterprise-apps',
     components: {
@@ -14,36 +25,41 @@
     },
     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
+      }
+    },
+    computed: {
+      apps () {
+        // 1 开通失败, 2 已开通, 0 未开通
+        const appList = this.$store.getters.enAppsList
+
+        for (const app of appList) {
+          if (app.status === 0 || app.status === 1) {
+            app.action = this.openApplication
           }
-        ]
+        }
+        console.log(this.$store.getters.enAppsList)
+        return this.$store.getters.enAppsList
+      },
+      enterprise () {
+        return this.$store.state.enterprises.savedEnterprise
+      }
+    },
+    methods: {
+      showErrorMessage (error) {
+        this.$message.error(error)
+      },
+      openApplication (app) {
+        console.log('开通')
+        const spaceUU = this.enterprise.spaceUU
+
+        const success = userSpace => {
+          this.$store.commit(types.CHOOSE_ENTERPRISE, userSpace)
+
+          this.$message.success('保存成功')
+        }
+
+        console.log(app)
+        bindAppWithSpace(spaceUU, app.uid, success, this.showErrorMessage)
       }
     }
   }
@@ -59,8 +75,5 @@
 </style>
 
 <style>
-  .admin-message::after {
-    content: 'ABV';
-    color: black;
-  }
+
 </style>

+ 128 - 5
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseAuth.vue

@@ -1,14 +1,35 @@
 <template>
   <div>
     <message-list :messages="messages">
-      <button class="btn btn-default btn-auth" slot="action">认证通过</button>
-      <button class="btn btn-default" slot="action">不通过</button>
+      <button class="btn btn-default btn-auth" slot="action" @click="passAuth">认证通过</button>
+      <button class="btn btn-default" slot="action" @click="failAuth">不通过</button>
     </message-list>
+
+    <!-- 更换管理员对话框 -->
+    <el-dialog
+      title="未通过原因"
+      :visible.sync="isShowReasonDialog"
+      width="450px"
+      :show-close="true"
+      :append-to-body="true">
+      <!-- 对话框内容 -->
+      <!-- 用户信息展示 -->
+      <div class="input-fail-pass-reason">
+        <textarea class="form-control" rows="4" placeholder="请输入未通过原因" v-model="reason"></textarea>
+      </div>
+      <!-- 对话框尾部 -->
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitFailReason">确 定</el-button>
+        <el-button @click="isShowReasonDialog = 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'
 
   export default {
     name: 'enterprise-auth',
@@ -17,11 +38,61 @@
     },
     data () {
       return {
+        isShowReasonDialog: false,
+        reason: ''
       }
     },
     computed: {
       messages () {
         return this.$store.getters.enAuthInfo
+      },
+      enterprise () {
+        return this.$store.state.enterprises.savedEnterprise
+      }
+    },
+    methods: {
+      showErrorMessage (error) {
+        this.$message.error(error)
+      },
+      passAuth () {
+        const success = userSpace => {
+          this.$store.commit(types.CHOOSE_ENTERPRISE, userSpace)
+
+          this.$message.success('保存成功')
+        }
+
+        this.saveAuthInfo(true, null, success, this.showErrorMessage)
+      },
+      failAuth () {
+        this.reason = ''
+
+        this.isShowReasonDialog = true
+      },
+      submitFailReason () {
+        if (!this.reason) {
+          this.$message.error('不通过原因不能为空')
+          return 0
+        }
+
+        const success = userSpace => {
+          this.$store.commit(types.CHOOSE_ENTERPRISE, userSpace)
+
+          this.$message.success('保存成功')
+          this.isShowReasonDialog = false
+        }
+
+        this.saveAuthInfo(false, this.reason, success, this.showErrorMessage)
+      },
+      saveAuthInfo (isPass, reason, success, error) {
+        const spaceUU = this.enterprise.spaceUU
+        const params = { isPass }
+        if (!isPass) {
+          params.reason = reason
+        }
+
+        return axios.put(`/api/user/space/${spaceUU}/authEnterpriseInfo`, {}, { params })
+          .then(success)
+          .catch(error)
       }
     }
   }
@@ -37,8 +108,60 @@
 </style>
 
 <style>
-  .admin-message::after {
-    content: 'ABV';
-    color: black;
+  .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;
+  }
+  .input-fail-pass-reason {
+    margin: 0 30px 30px -10px;
+    width: 400px;
+  }
+  .input-fail-pass-reason textarea {
+    width: 400px;
+    resize: none;
+  }
+  .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>

+ 12 - 3
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseBasicInfo.vue

@@ -26,7 +26,7 @@
           <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;">
+          <i class="el-input__icon" slot="suffix" style="line-height: 12px; cursor: pointer;" @click="showPicture">
             <img src="/static/images/look.png" alt="Look">
           </i>
         </el-input>
@@ -66,6 +66,7 @@
 <script>
   import _ from 'lodash'
   import axios from '@/assets/js/axios'
+  import * as types from '@/store/mutation-types'
 
   export default {
     name: 'enterprise-basic-info',
@@ -88,16 +89,24 @@
           ]
         )
 
-        const success = () => {
+        const success = userSpace => {
           this.isChange = false
+          this.$store.commit(types.CHOOSE_ENTERPRISE, userSpace)
+
           this.$message.success('保存成功')
-          this.$router.go(-1)
         }
         const error = error => {
           this.$message.error(error)
         }
 
         axios.put('/api/user/space//modifySpaceInfo', data).then(success).catch(error)
+      },
+      showPicture () {
+        if (this.enterpriseInfo.businessCodeImage) {
+          window.open(this.enterpriseInfo.businessCodeImage)
+          return 0
+        }
+        this.$message.info('该企业没有上传营业执照附件')
       }
     },
     mounted () {

+ 17 - 18
sso-manage-console-web/src/components/accounts/enterprises/EnterpriseHome.vue

@@ -53,10 +53,10 @@
         label="认证状态"
         width="100">
         <template slot-scope="scope">
-          <span v-if="scope.row.validCode === 0" style="padding-left: 8px;">未申请</span>
-          <span v-if="scope.row.validCode === 1" style="padding-left: 8px;">待认证</span>
-          <span v-if="scope.row.validCode === 2" style="padding-left: 8px;">已认证</span>
-          <span v-if="scope.row.validCode === 3" style="padding-left: 8px;">未通过</span>
+          <span v-if="scope.row.validCode === 0">未申请</span>
+          <span v-if="scope.row.validCode === 1">待认证</span>
+          <span v-if="scope.row.validCode === 2">已认证</span>
+          <span v-if="scope.row.validCode === 3">未通过</span>
         </template>
       </el-table-column>
       <el-table-column
@@ -64,7 +64,7 @@
         label="管理员"
         width="100">
         <template slot-scope="scope" v-if="scope.row.admin && scope.row.admin.userUU">
-          <span v-text="scope.row.admin.vipName" style="padding-left: 8px;"></span>
+          <span v-text="scope.row.admin.vipName"></span>
         </template>
       </el-table-column>
       <el-table-column
@@ -82,22 +82,18 @@
   import {ValidCode} from '@/Constant'
   import CommonHome from '../common/CommonHome'
 
-  const fromApps = [
-    {
-      label: 'B2B',
-      value: 'b2b'
-    }, {
-      label: 'UAS',
-      value: 'uas'
-    }, {
-      label: '优软商城',
-      value: 'mall'
-    }
-  ]
   const searchKeys = [
     {
       label: '企业名称',
       value: 'spaceName'
+    },
+    {
+      label: '营业执照号',
+      value: 'businessCode'
+    },
+    {
+      label: 'UU号',
+      value: 'spaceUU'
     }
   ]
 
@@ -118,7 +114,6 @@
         },
         pageContent: [],
         total: 0,
-        fromAppOptions: fromApps,
         validCodeOptions: ValidCode,
         searchKeys: searchKeys
       }
@@ -129,6 +124,9 @@
     computed: {
       tableData () {
         return this.pageContent
+      },
+      fromAppOptions () {
+        return this.$store.getters.fromAppOptions
       }
     },
     watch: {
@@ -144,6 +142,7 @@
           this.total = page.totalElements
         }
         const error = response => {
+          console.log(response)
           this.$message.error(response)
         }
 

+ 4 - 5
sso-manage-console-web/src/components/accounts/enterprises/common/AppList.vue

@@ -10,8 +10,8 @@
         </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>
+        <button class="btn btn-primary btn-open" v-if="row.status === 0" @click="row.action(row)">开通</button>
+        <button class="btn btn-primary btn-reopen" v-if="row.status === 1" @click="row.action(row)">重新开通</button>
       </div>
     </div>
   </div>
@@ -65,6 +65,7 @@
   }
   .message-panel .row .message-value button {
     height: 26px;
+    line-height: 12px;
     outline: none;
     border-radius: 0;
 
@@ -73,13 +74,11 @@
     font-weight: normal;
     font-family: "Microsoft YaHei", sans-serif;
   }
-  .message-panel .row .message-value btn-open {
+  .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>

+ 11 - 13
sso-manage-console-web/src/components/accounts/users/UserHome.vue

@@ -94,22 +94,18 @@
   import {ValidCode} from '@/Constant'
   import CommonHome from '../common/CommonHome'
 
-  const fromApps = [
-    {
-      label: 'B2B',
-      value: 'b2b'
-    }, {
-      label: 'UAS',
-      value: 'uas'
-    }, {
-      label: '优软商城',
-      value: 'mall'
-    }
-  ]
   const searchKeys = [
     {
       label: '用户名称',
       value: 'vipName'
+    },
+    {
+      label: '个人UU',
+      value: 'userUU'
+    },
+    {
+      label: '手机号',
+      value: 'mobile'
     }
   ]
 
@@ -130,7 +126,6 @@
         },
         pageContent: [],
         total: 0,
-        fromAppOptions: fromApps,
         mobileValidCodeOptions: ValidCode,
         searchKeys: searchKeys
       }
@@ -141,6 +136,9 @@
     computed: {
       tableData () {
         return this.pageContent
+      },
+      fromAppOptions () {
+        return this.$store.getters.fromAppOptions
       }
     },
     watch: {

+ 0 - 20
sso-manage-console-web/src/components/common/DataList.vue

@@ -1,20 +0,0 @@
-<template>
-  <div>
-    <slot>
-      <div class="x-empty" style="display: block;">
-        <i class="fa fa-coffee"></i>
-        <p>这里很干净!</p>
-      </div>
-    </slot>
-  </div>
-</template>
-
-<script>
-  export default {
-    name: 'DataList'
-  }
-</script>
-
-<style scoped>
-
-</style>

+ 0 - 36
sso-manage-console-web/src/components/common/NavBar.vue

@@ -1,36 +0,0 @@
-<template>
-  <nav class="x-navigation">
-    <div class="container">
-      <div class="navbar-header">
-        <a class="navbar-brand" href="#">控制台</a>
-      </div>
-      <div class="collapse navbar-collapse">
-        <ul class="x-menu">
-          <!-- 激活<a>标签外层元素 -->
-          <router-link tag="li" active-class="nav-current" to="/" exact><a>首页</a></router-link>
-          <router-link tag="li" active-class="nav-current" to="/app"><a>应用管理</a></router-link>
-          <router-link tag="li" active-class="nav-current" to="/user_space"><a>企业管理</a></router-link>
-          <router-link tag="li" active-class="nav-current" to="/user"><a>用户管理</a></router-link>
-          <router-link tag="li" active-class="nav-current" to="/admin"><a>更换管理员</a></router-link>
-          <router-link tag="li" active-class="nav-current" to="/asset"><a>资源管理</a></router-link>
-          <router-link tag="li" active-class="nav-current" to="/settings"><a>设置</a></router-link>
-        </ul>
-      </div>
-    </div>
-  </nav>
-</template>
-
-<script>
-  export default {
-    name: 'NavBar',
-    computed: {
-      nav_current () {
-        console.log(this.$store)
-      }
-    }
-  }
-</script>
-
-<style>
-
-</style>

+ 16 - 3
sso-manage-console-web/src/components/common/NavHeader.vue

@@ -14,8 +14,8 @@
       <!-- Links -->
       <div class="collapse navbar-collapse">
         <ul class="nav navbar-nav navbar-left">
-          <router-link tag="li" active-class="active" to="/index"><a>首页</a></router-link>
-          <router-link tag="li" active-class="active" to="/accounts"><a>账户管理</a></router-link>
+          <router-link tag="li" active-class="active" to="/" exact><a>首页</a></router-link>
+          <router-link tag="li" active-class="active" to="/accounts"><a><el-badge :is-dot="isNotification" class="item">账户管理</el-badge></a></router-link>
           <router-link tag="li" active-class="active" to="/system"><a>系统</a></router-link>
           <router-link tag="li" active-class="active" to="/content"><a>内容</a></router-link>
         </ul>
@@ -26,7 +26,12 @@
 
 <script>
   export default {
-    name: 'nav-header'
+    name: 'nav-header',
+    computed: {
+      isNotification () {
+        return this.$store.getters.isNotification
+      }
+    }
   }
 </script>
 
@@ -70,3 +75,11 @@
     background: none;
   }
 </style>
+
+<style>
+  #app .el-badge__content.is-fixed.is-dot {
+    right: 0;
+    background-color: #FF0000;
+    border-color: #FF0000;
+  }
+</style>

+ 0 - 98
sso-manage-console-web/src/components/common/PageBar.vue

@@ -1,98 +0,0 @@
-<template>
-  <!-- pagination start -->
-  <div class="x-mod-footer">
-    <div class="text-center" v-show="total > 0">
-      <!-- 0 < page <= 8 -->
-      <ul class="pagination pull-right" v-if="showFirstKindPageNumbersBar">
-        <li v-if="pageParams.page > 1"><a @click="jumpPage(pageParams.page - 1)">上一页</a></li>
-        <li :class="{active: 1 === pageParams.page}"><a role="page" @click="jumpPage(1)">1</a></li>
-        <li v-for="num in numbersOnePage" :class="{active: num === pageParams.page}"><a role="page" @click="jumpPage(num)">{{num}}</a></li>
-        <li v-if="totalPages > 8"><a>...</a></li>
-        <li v-if="totalPages > 8"><a v-text="totalPages" @click="jumpPage(totalPages)"></a></li>
-        <li v-if="pageParams.page < totalPages"><a @click="jumpPage(pageParams.page + 1)">下一页</a></li>
-      </ul>
-      <!-- 8 < page <= (totalPages - totalPages % 8) -->
-      <ul class="pagination pull-right" v-if="showSecondKindPageNumbersBar">
-        <li v-if="pageParams.page > 1"><a @click="jumpPage(pageParams.page - 1)">上一页</a></li>
-        <li><a role="page" @click="jumpPage(1)">1</a></li>
-        <li><a>...</a></li>
-        <li v-for="num in numbersOnePage" :class="{active: num === pageParams.page}"><a role="page" @click="jumpPage(num)">{{num}}</a></li>
-        <li><a>...</a></li>
-        <li><a role="page" @click="jumpPage(totalPages)">{{totalPages}}</a></li>
-        <li v-if="pageParams.page < totalPages"><a @click="jumpPage(pageParams.page + 1)">下一页</a></li>
-      </ul>
-      <!-- (totalPages - totalPages % 8) < page <= totalPages -->
-      <ul class="pagination pull-right" v-if="showThirdKindPageNumbersBar">
-        <li v-if="pageParams.page > 1"><a @click="jumpPage(pageParams.page - 1)">上一页</a></li>
-        <li><a role="page" @click="jumpPage(1)">1</a></li>
-        <li><a>...</a></li>
-        <li v-for="num in numbersOnePage" :class="{active: num === pageParams.page}"><a role="page" @click="jumpPage(num)">{{num}}</a></li>
-        <!--<li><a role="page">{{totalPages}}</a></li>-->
-        <li v-if="pageParams.page < totalPages"><a @click="jumpPage(pageParams.page + 1)">下一页</a></li>
-      </ul>
-    </div>
-  </div>
-  <!-- pagination end -->
-</template>
-
-<script>
-import _ from 'lodash'
-
-export default {
-  name: 'PageBar',
-  props: {
-    pageParams: Object,
-    totalPages: Number,
-    total: Number
-  },
-  computed: {
-    lastPageNumber () {
-      return this.totalPages % 8 !== 0 ? this.totalPages - this.totalPages % 8 : this.totalPages - 8
-    },
-    showFirstKindPageNumbersBar () {
-      if (this.totalPages <= 8) {
-        return true
-      }
-      return this.pageParams.page > 0 && this.pageParams.page <= 8
-    },
-    showSecondKindPageNumbersBar () {
-      if (this.totalPages <= 16) {
-        return false
-      }
-      return this.pageParams.page > 8 && this.pageParams.page <= this.lastPageNumber
-    },
-    showThirdKindPageNumbersBar () {
-      if (this.totalPages <= 8) {
-        return false
-      }
-      return this.pageParams.page > this.lastPageNumber && this.pageParams.page <= this.totalPages
-    },
-    numbersOnePage () {
-      const page = this.pageParams.page - 1
-
-      if (this.pageParams.page > 0 && this.pageParams.page <= 8) {
-        const max = this.totalPages > 8 ? 9 : this.totalPages + 1
-        return _.range(2, max)
-      }
-      if (this.pageParams.page > 8 && this.pageParams.page <= this.lastPageNumber) {
-        const min = page - page % 8
-        return _.range(min + 1, min + 1 + 8)
-      }
-      if (this.pageParams.page > this.lastPageNumber && this.pageParams.page <= this.totalPages) {
-        const min = page - page % 8
-        return _.range(min + 1, this.totalPages + 1)
-      }
-    }
-  },
-  methods: {
-    jumpPage (page) {
-      this.$emit('changePage', page)
-      console.log('changePage', page)
-    }
-  }
-}
-</script>
-
-<style>
-
-</style>

+ 0 - 55
sso-manage-console-web/src/components/common/SearchDialog.vue

@@ -1,55 +0,0 @@
-<template>
-  <!-- Title with search dialog -->
-  <h2 class="btn-group">
-    <span class="btn btn-lg x-btn-search" @click="visible = !visible">
-      <i class="fa fa-search"></i>
-      <span v-text="title">企业</span>
-    </span>
-    <div class="dropdown-menu" id="searchlist" style="padding: 15px; width: 240px; display: block;" v-if="visible">
-      <form>
-        <slot></slot>
-        <div class="form-group">
-          <div>
-            <label>分页设置</label>
-          </div>
-          <label class="radio-inline" v-for="pageSize in pageSizes"><input type="radio" name="pageSize" :value="pageSize" :checked="checked === pageSize" @change="$emit('change', $event.target.value)"> {{pageSize}}
-          </label>
-        </div>
-        <button class="btn btn-default btn-block btn-search" type="button" @click="beginSearch()">搜索</button>
-      </form>
-    </div>
-  </h2>
-</template>
-
-<script>
-  export default {
-    name: 'SearchDialog',
-    props: {
-      title: String,
-      pageSizes: Array,
-      checked: {
-        type: Number,
-        default: 0
-      }
-    },
-    model: {
-      prop: 'checked',
-      event: 'change'
-    },
-    data () {
-      return {
-        visible: false
-      }
-    },
-    methods: {
-      beginSearch () {
-        this.visible = false
-        this.$emit('search')
-      }
-    }
-  }
-</script>
-
-<style scoped>
-
-</style>

+ 0 - 4
sso-manage-console-web/src/components/common/index.js

@@ -1,9 +1,5 @@
-import SearchDialog from './SearchDialog'
-import DataList from './DataList'
 import NavHeader from './NavHeader'
 
 export {
-  SearchDialog,
-  DataList,
   NavHeader
 }

+ 4 - 5
sso-manage-console-web/src/store/index.js

@@ -3,13 +3,14 @@ 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'
+import * as modules from './modules'
 
 Vue.use(Vuex)
 
 // Production environments
 const vuexSession = new VuexPersistence({
-  storage: window.sessionStorage
+  storage: window.sessionStorage,
+  modules: ['enterprises']
 })
 
 const plugins = [vuexSession.plugin]
@@ -22,9 +23,7 @@ if (debug) {
 
 const store = new Vuex.Store({
   actions,
-  modules: {
-    enterprises
-  },
+  modules: modules,
   strict: debug,
   plugins: plugins
 })

+ 46 - 0
sso-manage-console-web/src/store/modules/accounts.js

@@ -0,0 +1,46 @@
+import axios from '@/assets/js/axios'
+import * as types from '../mutation-types'
+
+function countUnHandleAppeals () {
+  return axios.get('/api/appeal//countUnHandleAppeals')
+}
+
+// State
+const state = {
+  unHandleCount: 0
+}
+
+// Getters
+const getters = {
+  isNotification: state => {
+    return state.unHandleCount > 0
+  }
+}
+
+// Actions
+const actions = {
+  countUnHandleAppeals (context) {
+    return countUnHandleAppeals()
+      .then(count => {
+        console.log(count)
+        context.commit(types.COUNT_UN_HANDLE_APPEALS, count)
+      })
+      .catch(response => {
+        console.log(response)
+      })
+  }
+}
+
+// Mutations
+const mutations = {
+  [types.COUNT_UN_HANDLE_APPEALS] (state, count) {
+    state.unHandleCount = count || 0
+  }
+}
+
+export default {
+  state,
+  getters,
+  actions,
+  mutations
+}

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

@@ -1,13 +1,18 @@
+import axios from '@/assets/js/axios'
 import * as types from '../mutation-types'
 
 // State
 const state = {
-  savedEnterprise: {}
+  savedEnterprise: {},
+  allApps: []
 }
 
+const validStatus = ['未申请', '申诉中', '已认证', '未通过']
+
 // Getters
 const getters = {
   enAdmin: state => {
+    const enterprise = state.savedEnterprise || {}
     const admin = state.savedEnterprise.admin || {}
     const messages = []
 
@@ -19,7 +24,7 @@ const getters = {
 
     messages.push({ label: '邮箱', value: admin.email || '' })
 
-    messages.push({ label: '申诉状态', value: '申诉中' })
+    messages.push({ label: '申诉状态', value: validStatus[enterprise.validCode] })
     return messages
   },
   enAuthInfo: state => {
@@ -39,12 +44,51 @@ const getters = {
   },
   hasEnInfo: state => {
     return !!(state.savedEnterprise && state.savedEnterprise.spaceUU)
+  },
+  enAppsList: state => {
+    const apps = state.savedEnterprise.apps
+    const allApps = state.allApps
+    const appStatus = []
+
+    for (const app of allApps) {
+      let status = false
+      for (const enApp of apps) {
+        if (enApp.uid === app.uid) {
+          status = true
+          break
+        }
+      }
+
+      appStatus.push({label: app.description, uid: app.uid, status: (status ? 2 : 0)})
+    }
+
+    return appStatus
+  },
+  fromAppOptions: state => {
+    const allApps = state.allApps
+    const options = []
+
+    for (const app of allApps) {
+      const option = { label: app.description, value: app.uid }
+      options.push(option)
+    }
+    return options
   }
 }
 
 // Actions
 const actions = {
-
+  retrieveAllApps (context) {
+    console.log(context)
+
+    return axios.get('/api/app//showAllApps')
+      .then(appList => {
+        context.commit(types.ALL_APPS, appList)
+      })
+      .catch(response => {
+        console.log(response)
+      })
+  }
 }
 
 // Mutations
@@ -56,6 +100,9 @@ const mutations = {
   },
   [types.CLEAR_ENTERPRISE] (state) {
     state.savedEnterprise = {}
+  },
+  [types.ALL_APPS] (state, appList) {
+    state.allApps = appList
   }
 }
 

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

@@ -0,0 +1,7 @@
+import accounts from './accounts'
+import enterprises from './enterprises'
+
+export {
+  accounts,
+  enterprises
+}

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

@@ -1,5 +1,7 @@
 export const CHOOSE_ENTERPRISE = 'CHOOSE_ENTERPRISE'
 export const CLEAR_ENTERPRISE = 'CLEAR_ENTERPRISE'
+export const ALL_APPS = 'ALL_APPS'
+export const COUNT_UN_HANDLE_APPEALS = 'COUNT_UN_HANDLE_APPEALS'
 export const ADD_TO_CART = 'ADD_TO_CART'
 export const SET_CART_ITEMS = 'SET_CART_ITEMS'
 export const SET_CHECKOUT_STATUS = 'SET_CHECKOUT_STATUS'

+ 61 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/api/AppealBackendController.java

@@ -0,0 +1,61 @@
+package com.uas.sso.sso.backend.api;
+
+import com.uas.sso.entity.Appeal;
+import com.uas.sso.sso.backend.service.AppealService;
+import com.uas.sso.sso.backend.support.ResultBean;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Api interface implementations for managing appeal.
+ *
+ * @author huxz
+ */
+@RestController
+@RequestMapping(path = "/api/appeal")
+public class AppealBackendController {
+
+    private final AppealService appealService;
+
+    @Autowired
+    public AppealBackendController(AppealService appealService) {
+        this.appealService = appealService;
+    }
+
+    @RequestMapping(method = RequestMethod.GET, path = "//showAppealByPagination",
+            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public ResultBean<org.springframework.data.domain.Page<Appeal>> showAppealByPagination(
+            Pageable page,
+            @RequestParam(required = false) String type,
+            @RequestParam(required = false) String fromApp,
+            @RequestParam(required = false) Short status,
+            @RequestParam(required = false) String key,
+            @RequestParam(required = false) String keyword) {
+
+        return new ResultBean<>(
+                appealService.showAppealsByPagination(page, type, fromApp, status, key, keyword));
+    }
+
+    @RequestMapping(method = RequestMethod.PUT, path = "/{appealId}/approveAppealRequest",
+            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public ResultBean<Boolean> approveAppealRequest(@PathVariable("appealId") Long appealId,
+            Boolean isPass) {
+
+        appealService.approveAppealRequest(appealId, isPass);
+        return new ResultBean<>(true);
+    }
+
+    @RequestMapping(method = RequestMethod.GET, path = "//countUnHandleAppeals",
+            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public ResultBean<Long> countUnHandleAppeals() {
+
+        return new ResultBean<>(appealService.countUnHandleAppeals());
+    }
+
+}

+ 43 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/AppealService.java

@@ -0,0 +1,43 @@
+package com.uas.sso.sso.backend.service;
+
+import com.uas.sso.entity.Appeal;
+import com.uas.sso.entity.Userspace;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * Abstract class for operations about appeal when admin operates.
+ *
+ * @author huxz
+ */
+public interface AppealService {
+
+    /**
+     * 分页查询申诉管理信息
+     *
+     * @param page  分页参数
+     * @param type  申诉类型
+     * @param fromApp   申诉来源
+     * @param status    申诉状态
+     * @param key   搜索字段
+     * @param keyword   搜索关键字
+     * @return  分页申诉数据
+     */
+    Page<Appeal> showAppealsByPagination(Pageable page, String type, String fromApp,
+            Short status, String key, String keyword);
+
+    /**
+     * 审核未处理的申诉信息
+     *
+     * @param appealId  申诉信息id
+     * @param isPass    是否审核通过
+     */
+    void approveAppealRequest(Long appealId, Boolean isPass);
+
+    /**
+     * 统计未处理的申诉信息的数量
+     *
+     * @return  未处理申诉信息数量
+     */
+    Long countUnHandleAppeals();
+}

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

@@ -0,0 +1,207 @@
+package com.uas.sso.sso.backend.service.impl;
+
+import com.uas.sso.dao.AppealDao;
+import com.uas.sso.dao.UserDao;
+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.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.List;
+import java.util.Map;
+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.apache.log4j.Logger;
+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.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * An implementations of {@code AppService}.
+ *
+ * @author huxz
+ */
+@Service
+public class AppealServiceImpl implements AppealService {
+
+    private static final Logger logger = Logger.getLogger(AppealServiceImpl.class);
+
+    private static final String RESET_PASS = "resetPassword";
+
+    private static final String VALID_MOBILE = "validAccount";
+
+    private static final String CHANGE_ADMIN = "changeAdmin";
+
+    private final AppealDao appealDao;
+
+    private final UserDao userDao;
+
+    private final UserspaceDao spaceDao;
+
+    @Autowired
+    public AppealServiceImpl(AppealDao appealDao, UserDao userDao,
+            UserspaceDao spaceDao) {
+        this.appealDao = appealDao;
+        this.userDao = userDao;
+        this.spaceDao = spaceDao;
+    }
+
+    @Override
+    public Page<Appeal> showAppealsByPagination(Pageable page, String type, String fromApp,
+            Short status, String key, String keyword) {
+
+        return appealDao.findAll(new Specification<Appeal>() {
+            @Override
+            public Predicate toPredicate(Root<Appeal> root, CriteriaQuery<?> query,
+                    CriteriaBuilder builder) {
+                List<Predicate> predicates = new ArrayList<>();
+                // 申诉类型
+                if (!StringUtils.isEmpty(type)) {
+                    Predicate predicate = builder.equal(root.get("type"), type);
+                    predicates.add(predicate);
+                }
+                // 申诉来源
+                if (!StringUtils.isEmpty(fromApp)) {
+                    Predicate predicate = builder.equal(root.get("fromApp"), fromApp);
+                    predicates.add(predicate);
+                }
+                // 申诉状态
+                if (status != null) {
+                    Predicate predicate = builder.equal(root.get("status"), status);
+                    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
+    @Transactional(rollbackFor = RuntimeException.class)
+    public void approveAppealRequest(Long appealId, Boolean isPass) {
+        Assert.notNull(isPass, "审核状态不能为空");
+        Appeal appeal = assertAppealExist(appealId);
+
+        appeal.setAuditor("系统管理员");
+        appeal.setAuditDate(new Timestamp(System.currentTimeMillis()));
+        appeal.setStatus((short) (isPass ? 2 : 3));
+        appealDao.save(appeal);
+
+
+        if (!isPass) {
+            return;
+        }
+
+        // 审核通过之后,更新用户和企业信息
+        Map map = JacksonUtils.fromJson(appeal.getSubmitInfo(), Map.class);
+        Assert.notNull(map, "申诉提交信息不能为空");
+
+        if (RESET_PASS.equals(appeal.getType()) || VALID_MOBILE.equals(appeal.getType())) {
+            User user = assertUserExist(appeal.getSubmitterUU());
+            user.setMobile(appeal.getMobile());
+
+            if (RESET_PASS.equals(appeal.getType())) {
+                String password = (String) map.get("password");
+
+                if (StringUtils.isEmpty(password) || password.length() < 32) {
+                    throw new ValidationFailedException("重置密码不存在或密码未被加密");
+                }
+                user.setPassword(password);
+            }
+            userDao.save(user);
+        } else if (CHANGE_ADMIN.equals(appeal.getType())) {
+            Long spaceUU = ((Integer) map.get("spaceUU")).longValue();
+            Assert.notNull(map, "更换管理员申诉企业UU不能为空");
+
+            Userspace space = assertSpaceExist(spaceUU);
+            User user = userDao.findByMobile(appeal.getMobile());
+            if (user == null) {
+                throw new ValidationFailedException(
+                        String.format("拥有手机号[%s]的用户不存在", appeal.getMobile()));
+            }
+
+            space.setAdmin(user);
+            space.setAdminUU(user.getUserUU());
+            // 更新企业信息
+            space.setSpaceName((String) map.get("spaceName"));
+            space.setCorporation((String) map.get("corporation"));
+            space.setBusinessCode((String) map.get("businessCode"));
+            space.setBusinessCodeImage((String) map.get("businessCodeImage"));
+            space.setRegAddress((String) map.get("regAddress"));
+            // 更新企业认证状态
+            space.setValidCode((short) 2);
+            spaceDao.save(space);
+        } else {
+            logger.info("暂无支持申诉类型");
+        }
+    }
+
+    @Override
+    public Long countUnHandleAppeals() {
+
+        return appealDao.count(new Specification<Appeal>() {
+            @Override
+            public Predicate toPredicate(Root<Appeal> root, CriteriaQuery<?> query,
+                    CriteriaBuilder builder) {
+                // 查询条件:status = 1
+                Predicate predicate = builder.equal(root.get("status"), 1);
+
+                query.where(predicate);
+                return null;
+            }
+        });
+    }
+
+    private Appeal assertAppealExist(Long appealId) {
+        Appeal appeal = appealDao.findOne(appealId);
+        if (appeal == null) {
+            throw new ValidationFailedException(String.format("申诉 %d 不存在", appealId));
+        }
+        return appeal;
+    }
+
+    private User assertUserExist(Long userUU) {
+        User user = userDao.findOne(userUU);
+        if (user == null) {
+            throw new ValidationFailedException(String.format("用户[%d]不存在", userUU));
+        }
+        return user;
+    }
+
+    /**
+     * 业务逻辑校验-企业UU对应企业是否存在
+     *
+     * @param spaceUu   企业UU
+     */
+    private Userspace assertSpaceExist(@NotNull Long spaceUu) {
+        Userspace space = spaceDao.findOne(spaceUu);
+        if (space == null) {
+            throw new ValidationFailedException(String.format("企业[%d]不存在", spaceUu));
+        }
+        return space;
+    }
+}

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

@@ -13,11 +13,15 @@ import com.uas.sso.sso.backend.service.ChangeAdminService;
 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.Assert;
@@ -55,34 +59,38 @@ public class ChangeAdminServiceImpl implements ChangeAdminService {
             throw new ValidationFailedException("企业信息认证状态必须指定");
         }
 
-        return changeAdminDao.findAll((root, query, builder) -> {
-            List<Predicate> predicates = new ArrayList<>();
-
-            // 根据企业认证状态进行过滤
-            predicates.add(builder.equal(root.get("validCode"), validCode));
-
-            if (!StringUtils.isEmpty(spaceName)) {
-                Predicate predicate = builder.like(root.get("userspace").get("spaceName"), "%" + spaceName + "%");
-                predicates.add(predicate);
-            }
-
-            if (!StringUtils.isEmpty(businessCode)) {
-                Predicate predicate = builder.like(root.get("userspace").get("businessCode"), businessCode + "%");
-                predicates.add(predicate);
+        return changeAdminDao.findAll(new Specification<ChangeAdmin>() {
+            @Override
+            public Predicate toPredicate(Root<ChangeAdmin> root, CriteriaQuery<?> query,
+                    CriteriaBuilder builder) {
+                List<Predicate> predicates = new ArrayList<>();
+
+                // 根据企业认证状态进行过滤
+                predicates.add(builder.equal(root.get("validCode"), validCode));
+
+                if (!StringUtils.isEmpty(spaceName)) {
+                    Predicate predicate = builder.like(root.get("userspace").get("spaceName"), "%" + spaceName + "%");
+                    predicates.add(predicate);
+                }
+
+                if (!StringUtils.isEmpty(businessCode)) {
+                    Predicate predicate = builder.like(root.get("userspace").get("businessCode"), businessCode + "%");
+                    predicates.add(predicate);
+                }
+
+                if (!StringUtils.isEmpty(userName)) {
+                    Predicate predicate = builder.like(root.get("submitterName"), "%" + userName + "%");
+                    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(userName)) {
-                Predicate predicate = builder.like(root.get("submitterName"), "%" + userName + "%");
-                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);
     }
 

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

@@ -57,7 +57,10 @@ public class UserServiceImpl implements UserService {
                     predicates.add(predicate);
                 }
 
-                if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(keyword)) {
+                if ("userUU".equals(key) && !StringUtils.isEmpty(keyword)) {
+                    Predicate predicate = builder.equal(root.get(key), keyword);
+                    predicates.add(predicate);
+                } else if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(keyword)) {
                     Predicate predicate = builder.like(root.get(key), "%" + keyword + "%");
                     predicates.add(predicate);
                 }

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

@@ -4,14 +4,18 @@ import com.uas.sso.core.Status;
 import com.uas.sso.dao.AppDao;
 import com.uas.sso.dao.UserDao;
 import com.uas.sso.dao.UserspaceDao;
+import com.uas.sso.dao.UserspaceValidDao;
 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.dto.UpdateSpaceInfo;
 import com.uas.sso.sso.backend.exceptions.ValidationFailedException;
 import com.uas.sso.sso.backend.service.UserSpaceService;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import javax.persistence.criteria.CriteriaBuilder;
 import javax.persistence.criteria.CriteriaQuery;
@@ -41,12 +45,15 @@ public class UserSpaceServiceImpl implements UserSpaceService {
 
     private final AppDao appDao;
 
+    private final UserspaceValidDao spaceValidDao;
+
     @Autowired
     public UserSpaceServiceImpl(UserspaceDao userspaceDao, UserDao userDao,
-            AppDao appDao) {
+            AppDao appDao, UserspaceValidDao spaceValidDao) {
         this.userspaceDao = userspaceDao;
         this.userDao = userDao;
         this.appDao = appDao;
+        this.spaceValidDao = spaceValidDao;
     }
 
     @Override
@@ -70,7 +77,10 @@ public class UserSpaceServiceImpl implements UserSpaceService {
                 }
 
                 // 搜索关键字过滤
-                if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(keyword)) {
+                if ("spaceUU".equals(key) && !StringUtils.isEmpty(keyword)) {
+                    Predicate predicate = builder.equal(root.get(key), keyword);
+                    predicates.add(predicate);
+                } else if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(keyword)) {
                     Predicate predicate = builder.like(root.get(key), "%" + keyword + "%");
                     predicates.add(predicate);
                 }
@@ -146,6 +156,7 @@ public class UserSpaceServiceImpl implements UserSpaceService {
     }
 
     @Override
+    @Transactional(rollbackFor = RuntimeException.class)
     public Userspace authEnterpriseInfo(@NotNull Long spaceUu, @NotNull Boolean isPass,
             String reason) {
         Userspace space = assertSpaceExist(spaceUu);
@@ -156,8 +167,25 @@ public class UserSpaceServiceImpl implements UserSpaceService {
             space.setValidCode((short) Status.NOT_PASSED.getCode());
         }
 
-        // TODO 业务不清楚
-        return space;
+        // 更新企业认证日志信息
+        List<UserspaceValid> validList = spaceValidDao.findBySpaceUUAndValidCode(spaceUu, (short) 1);
+        if (CollectionUtils.isEmpty(validList)) {
+            throw new ValidationFailedException("当前企业没有发起认证申请");
+        }
+
+        for (UserspaceValid valid : validList) {
+            valid.setAuditor("系统管理员");
+
+            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            valid.setValidDate(dateFormat.format(new Date()));
+            valid.setValidCode(space.getValidCode());
+            if (!isPass) {
+                valid.setInvalidReason(reason);
+            }
+        }
+        spaceValidDao.save(validList);
+
+        return userspaceDao.save(space);
     }
 
     @Override

+ 70 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/util/JacksonUtils.java

@@ -0,0 +1,70 @@
+package com.uas.sso.sso.backend.util;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.log4j.Logger;
+
+/**
+ * JSON 工具类
+ *
+ * @author huxz
+ */
+public final class JacksonUtils {
+
+	private static final Logger logger = Logger.getLogger(JacksonUtils.class);
+
+	private static ObjectMapper objectMapper;
+
+	/**
+	 * 把JSON文本parse为JavaBean
+	 */
+	public static <T> T fromJson(String text, Class<T> clazz) {
+		if (objectMapper == null) {
+			objectMapper = new ObjectMapper();
+		}
+		try {
+			return objectMapper.readValue(text, clazz);
+		} catch (IOException e) {
+		    logger.error(e);
+		}
+		return null;
+	}
+
+	/**
+	 * 把JSON文本parse成JavaBean集合
+	 */
+	public static <T> List<T> fromJsonArray(String text, Class<T> clazz) {
+		if (objectMapper == null) {
+			objectMapper = new ObjectMapper();
+		}
+		JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, clazz);
+		try {
+			return objectMapper.readValue(text, javaType);
+		} catch (IOException e) {
+            logger.error(e);
+		}
+		return Collections.emptyList();
+	}
+
+	/**
+	 * 将JavaBean序列化为JSON文本
+	 */
+	public static String toJson(Object object) {
+		if (objectMapper == null) {
+			objectMapper = new ObjectMapper();
+		}
+		try {
+			return objectMapper.writeValueAsString(object);
+		} catch (JsonProcessingException e) {
+            logger.error(e);
+		}
+		return null;
+	}
+
+}

+ 12 - 1
sso-server/src/main/java/com/uas/sso/dao/UserspaceValidDao.java

@@ -1,6 +1,7 @@
 package com.uas.sso.dao;
 
 import com.uas.sso.entity.UserspaceValid;
+import java.util.List;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 
@@ -9,5 +10,15 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
  * @create 2018-01-17 17:50
  * @desc
  **/
-public interface UserspaceValidDao extends JpaRepository<UserspaceValid, Long>, JpaSpecificationExecutor<UserspaceValid> {
+public interface UserspaceValidDao extends JpaRepository<UserspaceValid, Long>,
+        JpaSpecificationExecutor<UserspaceValid> {
+
+    /**
+     * 查询某个企业的某各状态的企业认证日志信息
+     *
+     * @param spaceUU   企业UU
+     * @param validCode 企业认证状态
+     * @return  企业认证日志列表
+     */
+    List<UserspaceValid> findBySpaceUUAndValidCode(Long spaceUU, Short validCode);
 }

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

@@ -37,6 +37,7 @@ public class UserQuestion implements Serializable {
     /**
      * 用户uu号
      */
+    @com.fasterxml.jackson.annotation.JsonIgnore
     @ManyToOne(cascade = {CascadeType.REFRESH}, fetch = FetchType.LAZY)
     @JoinColumn(name="useruu", insertable=false, updatable=false)
     private User user;