瀏覽代碼

后台应用管理页面应用操作接口实现

huxz 8 年之前
父節點
當前提交
db33c61d49

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

@@ -14,7 +14,10 @@
     "axios": "^0.17.1",
     "bootstrap": "3",
     "font-awesome": "^4.7.0",
+    "lodash": "^4.17.4",
+    "mini-toastr": "^0.7.2",
     "vue": "^2.5.2",
+    "vue-notifications": "^0.9.0",
     "vue-router": "^3.0.1",
     "vuex": "^3.0.1"
   },

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

@@ -0,0 +1,34 @@
+import axios from 'axios'
+
+const instance = axios.create({})
+
+// Add a response interceptor to handle responses
+instance.interceptors.response.use(function (response) {
+  const body = response.data
+
+  if (!body.success) {
+    return Promise.reject(body.message)
+  }
+  return body.data
+}, function (error) {
+  // Unified handle errors
+  let err = null
+  if (error.response) {
+    // The request was made and the server responded with a status code
+    // that falls out of the range of 2xx
+    const response = error.response
+    err = response.data && response.data.message ? response.data.message : response
+  } else if (error.request) {
+    // The request was made but no response was received
+    // `error.request` is an instance of XMLHttpRequest in the browser
+    // and an instance of http.ClientRequest in node.js
+    err = error.request
+  } else {
+    // Something happened in setting up the request that triggered an Error
+    err = error.message
+  }
+
+  return Promise.reject(err)
+})
+
+export default instance

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

@@ -0,0 +1,36 @@
+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 }

+ 7 - 4
sso-manage-console-web/src/components/app/AppHome.vue

@@ -63,13 +63,13 @@
       </div>
     </div>
     <!-- modal -->
-    <app-modal :visible.sync="showModal" :data="selectedApp" @success=""/>
+    <app-modal :visible.sync="showModal" :data="selectedApp" @success="saveModifiedAppSuccess()"/>
   </div>
 </template>
 
 <!--suppress JSUnusedGlobalSymbols -->
 <script>
-import axios from 'axios'
+import axios from '@/assets/js/axios'
 import AppModal from './modal/AppModal'
 
 export default {
@@ -98,15 +98,18 @@ export default {
     },
     loadAppsList () {
       axios.get('/api/app//showAllApps').then(response => {
-        this.appList = response.data
+        this.appList = response
       }, response => {
+        // TODO 展示错误提示页面
         console.log('error', response)
       })
     },
     addOrModifyAppInfo (app) {
       this.showModal = true
       this.selectedApp = app || {}
-      console.log(this.selectedApp)
+    },
+    saveModifiedAppSuccess () {
+      this.loadAppsList()
     }
   }
 }

+ 50 - 23
sso-manage-console-web/src/components/app/modal/AppModal.vue

@@ -185,7 +185,7 @@
         </div>
         <div class="modal-footer">
           <button type="button" class="btn btn-blank" data-dismiss="modal" @click="appVisible = false">取消</button>
-          <button type="button" class="btn btn-blank btn-del" style="display: none;">删除</button>
+          <button type="button" class="btn btn-blank btn-del" v-show="!isAdd" @click="deleteAppInfo()">删除</button>
           <button type="button" class="btn btn-default btn-submit" @click="saveAppInfo()">保存</button>
         </div>
       </div>
@@ -194,7 +194,8 @@
 </template>
 
 <script>
-import axios from 'axios'
+import _ from 'lodash'
+import axios from '@/assets/js/axios'
 
 export default {
   name: 'app-modal',
@@ -205,6 +206,7 @@ export default {
   data () {
     return {
       appVisible: false,
+      isAdd: true,
       appInfo: {}
     }
   },
@@ -216,36 +218,54 @@ export default {
       this.$emit('update:visible', value)
     },
     data: function (value) {
-      console.log(value)
-      // Deep copy data to appInfo, when data changes.
-      const temp = JSON.parse(JSON.stringify(value || {}))
-      temp.type = temp.userControl ? 'control' : 'default'
+      value = value || {}
 
-      // To implement two-way bindings, handle copy of prop before assign to
-      // the data of component
-      this.appInfo = temp
+      this.isAdd = !value.uid
 
+      // Set default values for app
+      const defaults = { type: value.userControl ? 'control' : 'default' }
+
+      // Set default values for new app info
+      if (!value.userControl && !value.uid) {
+        defaults.defaultUse = '1'
+        defaults.personalEnable = 1
+      }
+
+      // To implement two-way bindings, deep copy data to appInfo, when data changes.
+      this.appInfo = _.defaults({}, value, defaults)
       console.log('App type: ', this.appInfo.type)
     }
   },
   methods: {
     saveAppInfo () {
-      console.log(this.appInfo)
-      if (this.appInfo.uid) {
-        axios.put('/api/app//updateApp', this.appInfo)
-          .then(response => {
-            console.log(response.data)
-          }).catch(response => {
-            console.log(response)
-        })
+      if (this.appInfo.type === 'control') {
+        this.appInfo.userControl = this.appInfo.userControl || ' '
+      }
+      const appInfo = _.omit(this.appInfo, 'type')
+      console.log(appInfo)
+
+      const success = () => {
+        this.$emit('success')
+        this.appVisible = false
+      }
+      const error = response => { this.showAddError({ message: response }) }
+
+      if (!this.isAdd) {
+        axios.put('/api/app//updateApp', appInfo).then(success).catch(error)
       } else {
-        axios.post('/api/app//addApp', this.appInfo)
-        .then(response => {
-          console.log(response.data)
-        }).catch(response => {
-          console.log(response)
-        })
+        axios.post('/api/app//addApp', appInfo).then(success).catch(error)
       }
+    },
+    deleteAppInfo () {
+      const uid = this.appInfo.uid || ''
+
+      const success = () => {
+        this.$emit('success')
+        this.appVisible = false
+      }
+      const error = response => { this.showAddError({ message: response }) }
+
+      axios.delete(`/api/app/${uid}/deleteApp`).then(success).catch(error)
     }
   },
   computed: {
@@ -255,6 +275,13 @@ export default {
     showControl () {
       return this.appInfo.type === 'control'
     }
+  },
+  notifications: {
+    showAddError: {
+      title: 'Add Failed',
+      message: 'Failed to add app info',
+      type: 'error'
+    }
   }
 }
 </script>

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

@@ -3,12 +3,16 @@
 import Vue from 'vue'
 import App from './App'
 import router from './router'
+import { VueNotifications, options } from '@/assets/js/notifications'
 import 'bootstrap/dist/css/bootstrap.min.css'
 import 'font-awesome/css/font-awesome.min.css'
 import './assets/styles/console.css'
 
 Vue.config.productionTip = false
 
+// Activate the plugin
+Vue.use(VueNotifications, options)
+
 /* eslint-disable no-new */
 new Vue({
   el: '#app',

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

@@ -3464,6 +3464,10 @@ mimic-fn@^1.0.0:
   version "1.1.0"
   resolved "http://registry.npm.taobao.org/mimic-fn/download/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
 
+mini-toastr@^0.7.2:
+  version "0.7.2"
+  resolved "http://registry.npm.taobao.org/mini-toastr/download/mini-toastr-0.7.2.tgz#125d9c066e2d3c1b519efb10412dc4e2169d46ee"
+
 minimalistic-assert@^1.0.0:
   version "1.0.0"
   resolved "http://registry.npm.taobao.org/minimalistic-assert/download/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
@@ -5475,6 +5479,10 @@ vue-loader@^13.3.0:
     vue-style-loader "^3.0.0"
     vue-template-es2015-compiler "^1.6.0"
 
+vue-notifications@^0.9.0:
+  version "0.9.0"
+  resolved "http://registry.npm.taobao.org/vue-notifications/download/vue-notifications-0.9.0.tgz#7d25e19a5e4872442e3527f252007d538af4ba6e"
+
 vue-router@^3.0.1:
   version "3.0.1"
   resolved "http://registry.npm.taobao.org/vue-router/download/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9"

+ 15 - 6
sso-manage-console/src/main/java/com/uas/sso/sso/backend/api/AppManagerController.java

@@ -2,10 +2,12 @@ package com.uas.sso.sso.backend.api;
 
 import com.uas.sso.entity.App;
 import com.uas.sso.sso.backend.service.AppService;
+import com.uas.sso.sso.backend.support.ResultBean;
 import java.util.List;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.MediaType;
 import org.springframework.util.ObjectUtils;
+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;
@@ -29,21 +31,28 @@ public class AppManagerController {
 
     @RequestMapping(method = RequestMethod.GET, path = "//showAllApps",
             produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
-    public List<App> showAllApps() {
-        return appService.findAllApps();
+    public ResultBean<List<App>> showAllApps() {
+        return new ResultBean<>(appService.findAllApps());
     }
 
     @RequestMapping(method = RequestMethod.POST, path = "//addApp",
             produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
-    public Boolean addAppInfo(@RequestBody App app) {
+    public ResultBean<Boolean> addAppInfo(@RequestBody App app) {
         appService.addAppInfo(app);
-        return true;
+        return new ResultBean<>(true);
     }
 
     @RequestMapping(method = RequestMethod.PUT, path = "//updateApp",
             produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
-    public Boolean updateAppInfo(@RequestBody App app) {
+    public ResultBean<Boolean> updateAppInfo(@RequestBody App app) {
         appService.updateApp(app);
-        return true;
+        return new ResultBean<>(true);
+    }
+
+    @RequestMapping(method = RequestMethod.DELETE, path = "/{uid}/deleteApp",
+            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public ResultBean<Boolean> deleteAppInfo(@PathVariable("uid") String uid) {
+        appService.deleteApp(uid);
+        return new ResultBean<>(true);
     }
 }

+ 58 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/config/ControllerExceptionHandler.java

@@ -0,0 +1,58 @@
+package com.uas.sso.sso.backend.config;
+
+import com.uas.sso.sso.backend.support.ResultBean;
+import java.io.IOException;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+/**
+ * Use @ControllerAdvice annotation to handler exceptions.
+ *
+ * <p>Tip: ResponseEntityExceptionHandler, write to the response with a {@link HttpMessageConverter
+ * message converter}</p>
+ *
+ * @author huxz
+ */
+@ControllerAdvice(annotations = {RestController.class})
+public class ControllerExceptionHandler extends ResponseEntityExceptionHandler {
+
+    private static final String MESSAGE_BAD_REQUEST =
+            HttpStatus.BAD_REQUEST.value() + " " + HttpStatus.BAD_REQUEST.getReasonPhrase();
+
+    /**
+     * Handler IOException and RuntimeException.
+     *
+     * @param ex exception info
+     * @return response entity
+     */
+    @ExceptionHandler(value = {IOException.class, RuntimeException.class})
+    @ResponseBody
+    public ResponseEntity<ResultBean> handlerCommonExceptions(Throwable ex) {
+        logger.error("System errors occur", ex);
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.add("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE);
+        return new ResponseEntity<>(new ResultBean<>(ex), headers,
+                HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+
+    @Override
+    protected ResponseEntity<Object> handleHttpMessageNotReadable(
+            HttpMessageNotReadableException ex,
+            HttpHeaders headers, HttpStatus status, WebRequest request) {
+        logger.error(MESSAGE_BAD_REQUEST, ex);
+
+        headers.add("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE);
+        return handleExceptionInternal(ex, new ResultBean<>(ex), headers, status, request);
+    }
+}

+ 19 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/exceptions/ValidationFailedException.java

@@ -0,0 +1,19 @@
+package com.uas.sso.sso.backend.exceptions;
+
+/**
+ * {@code ValidationFailedException} is the exception which can be thrown
+ * after validation of arguments.
+ *
+ * @author huxz
+ */
+@SuppressWarnings("unused")
+public class ValidationFailedException extends RuntimeException {
+
+    public ValidationFailedException(String message) {
+        super(message);
+    }
+
+    public ValidationFailedException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 2 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/AppService.java

@@ -15,4 +15,6 @@ public interface AppService {
     void addAppInfo(App app);
 
     void updateApp(App app);
+
+    void deleteApp(String uid);
 }

+ 56 - 2
sso-manage-console/src/main/java/com/uas/sso/sso/backend/service/impl/AppServiceImpl.java

@@ -1,8 +1,10 @@
 package com.uas.sso.sso.backend.service.impl;
 
 import com.alibaba.druid.support.json.JSONUtils;
+import com.alibaba.fastjson.JSON;
 import com.uas.sso.dao.AppDao;
 import com.uas.sso.entity.App;
+import com.uas.sso.sso.backend.exceptions.ValidationFailedException;
 import com.uas.sso.sso.backend.service.AppService;
 import java.util.Collections;
 import java.util.List;
@@ -11,6 +13,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
 
 /**
  * An implementations of {@code AppService}.
@@ -40,12 +43,63 @@ public class AppServiceImpl implements AppService {
     @Override
     @Transactional(rollbackFor = RuntimeException.class)
     public void addAppInfo(App app) {
-        System.out.println(JSONUtils.toJSONString(app));
+        if (app == null) {
+            throw new ValidationFailedException("应用信息不能为空");
+        } else if (StringUtils.isEmpty(app.getUid()) || StringUtils.isEmpty(app.getDescription())) {
+            throw new ValidationFailedException("应用uid或介绍信息不能为空");
+        }
+
+        App existOne = appDao.findOne(app.getUid());
+        if (existOne != null) {
+            throw new ValidationFailedException(String.format("应用uid %s 已经被使用", app.getUid()));
+        }
+
+        // If app is controlled by other app, check this app whether exist and handler app info
+        if (!StringUtils.isEmpty(app.getUserControl())) {
+            existOne = appDao.findOne(app.getUserControl());
+            if (existOne == null) {
+                throw new ValidationFailedException(
+                        String.format("级联应用uid %s 不存在", app.getUserControl()));
+            }
+        }
+
+        appDao.save(app);
     }
 
     @Override
     @Transactional(rollbackFor = RuntimeException.class)
     public void updateApp(App app) {
-        System.out.println(JSONUtils.toJSONString(app));
+        if (app == null) {
+            throw new ValidationFailedException("应用信息不能为空");
+        } else if (StringUtils.isEmpty(app.getUid()) || StringUtils.isEmpty(app.getDescription())) {
+            throw new ValidationFailedException("应用uid或介绍信息不能为空");
+        }
+
+        App existOne = appDao.findOne(app.getUid());
+        if (existOne == null) {
+            throw new ValidationFailedException(String.format("应用uid %s 不存在", app.getUid()));
+        }
+
+        // If app is controlled by other app, check this app whether exist and handler app info
+        if (!StringUtils.isEmpty(app.getUserControl())) {
+            App controlOne = appDao.findOne(app.getUserControl());
+            if (controlOne == null) {
+                throw new ValidationFailedException(
+                        String.format("级联应用uid %s 不存在", app.getUserControl()));
+            }
+            existOne.setUserControl(app.getUserControl());
+        }
+        existOne = app;
+
+        appDao.save(existOne);
+    }
+
+    @Override
+    public void deleteApp(String uid) {
+        if (StringUtils.isEmpty(uid)) {
+            throw new ValidationFailedException("应用uid不能为空");
+        }
+
+        appDao.delete(uid);
     }
 }

+ 87 - 0
sso-manage-console/src/main/java/com/uas/sso/sso/backend/support/ResultBean.java

@@ -0,0 +1,87 @@
+package com.uas.sso.sso.backend.support;
+
+/**
+ * Unified Response Date format.
+ *
+ * @param <T>
+ *   the type of response data
+ * @author huxz
+ */
+@SuppressWarnings("unused")
+public class ResultBean<T> {
+
+  private Boolean success;
+
+  private T data;
+
+  private ErrorCode code;
+
+  private String message;
+
+  enum ErrorCode {
+
+    /**
+     * Request occurs errors.
+     */
+    FAIL,
+
+    /**
+     * Request has no permission.
+     */
+    NO_PERMISSION
+  }
+
+  public ResultBean() {
+  }
+
+  public ResultBean(T data) {
+    this.success = true;
+    this.data = data;
+  }
+
+  /**
+   * response result when error occurs.
+   *
+   * @param e   exception info
+   */
+  public ResultBean(Throwable e) {
+    this.success = false;
+    this.message = e.getMessage();
+
+    // Set default code
+    this.code = ErrorCode.FAIL;
+  }
+
+  public Boolean getSuccess() {
+    return success;
+  }
+
+  public void setSuccess(Boolean success) {
+    this.success = success;
+  }
+
+  public T getData() {
+    return data;
+  }
+
+  public void setData(T data) {
+    this.data = data;
+  }
+
+  public ErrorCode getCode() {
+    return code;
+  }
+
+  public void setCode(ErrorCode code) {
+    this.code = code;
+  }
+
+  public String getMessage() {
+    return message;
+  }
+
+  public void setMessage(String message) {
+    this.message = message;
+  }
+
+}

+ 3 - 2
sso-manage-console/src/main/resources/application.yml

@@ -15,8 +15,9 @@ spring:
   jpa:
     database: mysql
     show-sql: false
-    hibernate:
-      ddl-auto: "update"
+# 避免自动建表更新账户中心的数据库结构
+#     hibernate:
+#      ddl-auto: "update"
 
 # Write logs to current dictionary where jar is.
 logging:

+ 2 - 0
sso-manage-console/src/main/resources/config/application-dev.properties

@@ -18,4 +18,6 @@ app.datasource.maxPoolPreparedStatementPerConnectionSize=20
 app.datasource.filters=stat,slf4j
 app.datasource.connectionProperties=druid.stat.mergeSql=false;druid.stat.slowSqlMillis=5000
 
+spring.jpa.show-sql=true
+
 management.security.enabled=false

+ 5 - 2
sso-server/src/main/java/com/uas/sso/entity/App.java

@@ -1,7 +1,10 @@
 package com.uas.sso.entity;
 
-import javax.persistence.*;
 import java.io.Serializable;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
 
 /**
  * 应用
@@ -23,7 +26,7 @@ public class App implements Serializable {
      */
     @Id
     @Column(name = "uid_")
-    @GeneratedValue
+    // @GeneratedValue
     private String uid;
 
     /**