提交 f5dd22eb 编写于 作者: J Jason Song 提交者: GitHub

Merge pull request #426 from lepdou/index_page_refactor

refactor index page. add favorites function module
package com.ctrip.framework.apollo.portal.controller;
import com.google.common.collect.Sets;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.http.MultiResponseEntity;
......@@ -17,12 +19,15 @@ import com.ctrip.framework.apollo.portal.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
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;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
......@@ -46,8 +51,19 @@ public class AppController {
private UserService userService;
@RequestMapping("")
public List<App> findAllApp() {
return appService.findAll();
public List<App> findApps(@RequestParam(value = "appIds", required = false) String appIds) {
if (StringUtils.isEmpty(appIds)){
return appService.findAll();
}else {
return appService.findByAppIds(Sets.newHashSet(appIds.split(",")));
}
}
@RequestMapping("/by-owner")
public List<App> findAppsByOwner(@RequestParam("owner") String owner, Pageable page){
return appService.findByOwnerName(owner, page);
}
@RequestMapping("/{appId}/navtree")
......
package com.ctrip.framework.apollo.portal.controller;
import com.ctrip.framework.apollo.portal.entity.po.Favorite;
import com.ctrip.framework.apollo.portal.service.FavoriteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
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;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class FavoriteController {
@Autowired
private FavoriteService favoriteService;
@RequestMapping(value = "/favorites", method = RequestMethod.POST)
public Favorite addFavorite(@RequestBody Favorite favorite) {
return favoriteService.addFavorite(favorite);
}
@RequestMapping("/favorites")
public List<Favorite> findFavorites(@RequestParam(value = "userId", required = false) String userId,
@RequestParam(value = "appId", required = false) String appId,
Pageable page) {
return favoriteService.search(userId, appId, page);
}
@RequestMapping(value = "/favorites/{favoriteId}", method = RequestMethod.DELETE)
public void deleteFavorite(@PathVariable long favoriteId) {
favoriteService.deleteFavorite(favoriteId);
}
@RequestMapping(value = "/favorites/{favoriteId}", method = RequestMethod.PUT)
public void toTop(@PathVariable long favoriteId) {
favoriteService.adjustFavoriteToFirst(favoriteId);
}
}
package com.ctrip.framework.apollo.portal.entity.po;
import com.ctrip.framework.apollo.common.entity.BaseEntity;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name = "Favorite")
@SQLDelete(sql = "Update Favorite set isDeleted = 1 where id = ?")
@Where(clause = "isDeleted = 0")
public class Favorite extends BaseEntity {
@Column(name = "AppId", nullable = false)
private String appId;
@Column(name = "UserId", nullable = false)
private String userId;
@Column(name = "Position")
private long position;
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public long getPosition() {
return position;
}
public void setPosition(long position) {
this.position = position;
}
}
package com.ctrip.framework.apollo.portal.entity.po;
public class UserInfo {
private String userId;
private String name;
private String email;
......@@ -28,4 +29,20 @@ public class UserInfo {
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object o) {
if (o instanceof UserInfo) {
if (o == this){
return true;
}
UserInfo anotherUser = (UserInfo) o;
return userId.equals(anotherUser.userId);
} else {
return false;
}
}
}
......@@ -2,11 +2,19 @@ package com.ctrip.framework.apollo.portal.repository;
import com.ctrip.framework.apollo.common.entity.App;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
import java.util.Set;
public interface AppRepository extends PagingAndSortingRepository<App, Long> {
App findByAppId(String appId);
List<App> findByOwnerName(String ownerName, Pageable page);
List<App> findByAppIdIn(Set<String> appIds);
}
package com.ctrip.framework.apollo.portal.repository;
import com.ctrip.framework.apollo.portal.entity.po.Favorite;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
public interface FavoriteRepository extends PagingAndSortingRepository<Favorite, Long> {
List<Favorite> findByUserIdOrderByPositionAscDataChangeCreatedTimeAsc(String userId, Pageable page);
List<Favorite> findByAppIdOrderByPositionAscDataChangeCreatedTimeAsc(String appId, Pageable page);
Favorite findFirst1ByUserIdOrderByPositionAscDataChangeCreatedTimeAsc(String userId);
Favorite findByUserIdAndAppId(String userId, String appId);
}
......@@ -18,12 +18,14 @@ import com.dianping.cat.Cat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.HttpStatusCodeException;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@Service
public class AppService {
......@@ -54,6 +56,14 @@ public class AppService {
return Lists.newArrayList((apps));
}
public List<App> findByAppIds(Set<String> appIds){
return appRepository.findByAppIdIn(appIds);
}
public List<App> findByOwnerName(String ownerName, Pageable page){
return appRepository.findByOwnerName(ownerName, page);
}
public App load(String appId) {
App app = appRepository.findByAppId(appId);
if (app == null) {
......
package com.ctrip.framework.apollo.portal.service;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.portal.auth.UserInfoHolder;
import com.ctrip.framework.apollo.portal.entity.po.Favorite;
import com.ctrip.framework.apollo.portal.entity.po.UserInfo;
import com.ctrip.framework.apollo.portal.repository.FavoriteRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@Service
public class FavoriteService {
public static final long POSITION_DEFAULT = 10000;
@Autowired
private UserInfoHolder userInfoHolder;
@Autowired
private FavoriteRepository favoriteRepository;
@Autowired
private UserService userService;
public Favorite addFavorite(Favorite favorite) {
UserInfo user = userService.findByUserId(favorite.getUserId());
if (user == null) {
throw new BadRequestException("user not exist");
}
UserInfo loginUser = userInfoHolder.getUser();
//user can only add himself favorite app
if (!loginUser.equals(user)) {
throw new BadRequestException("add favorite fail. "
+ "because favorite's user is not current login user.");
}
Favorite checkedFavorite = favoriteRepository.findByUserIdAndAppId(loginUser.getUserId(), favorite.getAppId());
if (checkedFavorite != null) {
return checkedFavorite;
}
favorite.setPosition(POSITION_DEFAULT);
favorite.setDataChangeCreatedBy(user.getUserId());
favorite.setDataChangeLastModifiedBy(user.getUserId());
return favoriteRepository.save(favorite);
}
public List<Favorite> search(String userId, String appId, Pageable page) {
boolean isUserIdEmpty = StringUtils.isEmpty(userId);
boolean isAppIdEmpty = StringUtils.isEmpty(appId);
if (isAppIdEmpty && isUserIdEmpty) {
throw new BadRequestException("user id and app id can't be empty at the same time");
}
//search by userId
if (isAppIdEmpty && !isUserIdEmpty) {
return favoriteRepository.findByUserIdOrderByPositionAscDataChangeCreatedTimeAsc(userId, page);
}
//search by appId
if (!isAppIdEmpty && isUserIdEmpty) {
return favoriteRepository.findByAppIdOrderByPositionAscDataChangeCreatedTimeAsc(appId, page);
}
//search by userId and appId
return Arrays.asList(favoriteRepository.findByUserIdAndAppId(userId, appId));
}
public void deleteFavorite(long favoriteId) {
Favorite favorite = favoriteRepository.findOne(favoriteId);
checkUserOperatePermission(favorite);
favoriteRepository.delete(favorite);
}
public void adjustFavoriteToFirst(long favoriteId) {
Favorite favorite = favoriteRepository.findOne(favoriteId);
checkUserOperatePermission(favorite);
String userId = favorite.getUserId();
Favorite firstFavorite = favoriteRepository.findFirst1ByUserIdOrderByPositionAscDataChangeCreatedTimeAsc(userId);
long minPosition = firstFavorite.getPosition();
favorite.setPosition(minPosition - 1);
favoriteRepository.save(favorite);
}
private void checkUserOperatePermission(Favorite favorite) {
if (favorite == null) {
throw new BadRequestException("favorite not exist");
}
if (!Objects.equals(userInfoHolder.getUser().getUserId(), favorite.getUserId())) {
throw new BadRequestException("can not operate other person's favorite");
}
}
}
......@@ -86,7 +86,6 @@
<!--angular-->
<script src="vendor/angular/angular.min.js"></script>
<script src="vendor/angular/angular-route.min.js"></script>
<script src="vendor/angular/angular-resource.min.js"></script>
<script src="vendor/angular/angular-toastr-1.4.1.tpls.min.js"></script>
<script src="vendor/angular/loading-bar.min.js"></script>
......
......@@ -106,7 +106,6 @@
<!--angular-->
<script src="vendor/angular/angular.min.js"></script>
<script src="vendor/angular/angular-route.min.js"></script>
<script src="vendor/angular/angular-resource.min.js"></script>
<script src="vendor/angular/angular-toastr-1.4.1.tpls.min.js"></script>
<script src="vendor/angular/loading-bar.min.js"></script>
......
......@@ -31,13 +31,31 @@
</div>
<div class="J_appFound hidden col-md-3 col-xs-3 col-sm-3" ng-show="!notFoundApp">
<div id="treeview"></div>
<section class="panel">
<header class="panel-heading">
<img src="img/env.png" class="i-20">&nbsp;环境列表
<span class="pull-right"
data-tooltip="tooltip" data-placement="bottom" title="通过切换环境、集群来管理不同环境、集群的配置">
<img src="img/question.png" class="i-20"/>
</span>
</header>
<div id="treeview" class="no-radius"></div>
</section>
<!--app info-->
<section class="panel">
<header class="panel-heading">
<img src="img/info.png" class="i-25-20"/> 应用信息
<span class="tools pull-right">
<a href="javascript:;" class="icon-chevron-down"></a>
<img src="img/info.png" class="i-25-20"/> 项目信息
<span class="pull-right cursor-pointer">
<a ng-if="!favoriteId" ng-click="addFavorite()"
data-tooltip="tooltip" data-placement="bottom" title="收藏">
<img src="img/unlike.png" class="i-20"/>
</a>
<a ng-if="favoriteId" ng-click="deleteFavorite()"
data-tooltip="tooltip" data-placement="bottom" title="取消收藏">
<img src="img/like.png" class="i-20"/>
</a>
</span>
</header>
<div class="panel-body">
......@@ -525,7 +543,6 @@
<!--angular-->
<script src="vendor/angular/angular.min.js"></script>
<script src="vendor/angular/angular-ui-router.min.js"></script>
<script src="vendor/angular/angular-resource.min.js"></script>
<script src="vendor/angular/angular-toastr-1.4.1.tpls.min.js"></script>
<script src="vendor/angular/loading-bar.min.js"></script>
......@@ -556,6 +573,7 @@
<script type="application/javascript" src="scripts/services/CommitService.js"></script>
<script type="application/javascript" src="scripts/services/NamespaceLockService.js"></script>
<script type="application/javascript" src="scripts/services/InstanceService.js"></script>
<script type="application/javascript" src="scripts/services/FavoriteService.js"></script>
<script type="application/javascript" src="scripts/AppUtils.js"></script>
......
......@@ -97,7 +97,6 @@
<!--angular-->
<script src="../vendor/angular/angular.min.js"></script>
<script src="../vendor/angular/angular-route.min.js"></script>
<script src="../vendor/angular/angular-resource.min.js"></script>
<script src="../vendor/angular/angular-toastr-1.4.1.tpls.min.js"></script>
<script src="../vendor/angular/loading-bar.min.js"></script>
......
......@@ -233,7 +233,6 @@
<!--angular-->
<script src="../vendor/angular/angular.min.js"></script>
<script src="../vendor/angular/angular-route.min.js"></script>
<script src="../vendor/angular/angular-resource.min.js"></script>
<script src="../vendor/angular/angular-toastr-1.4.1.tpls.min.js"></script>
<script src="../vendor/angular/loading-bar.min.js"></script>
......
......@@ -13,55 +13,97 @@
<title>apollo</title>
</head>
<body ng-controller="IndexController">
<body>
<div class="site-notice"><label>当前站点支持</label><label ng-repeat="env in envs" ng-bind="env" style="margin-left: 5px;"></label>
环境
</div>
<header class="site-header jumbotron">
<div class="container">
<div class="row">
<div class="col-xs-12"><h1>Apollo</h1>
<p>携程统一配置中心<br>
<span class="package-amount">共收录了 <strong ng-bind="appsCount"></strong> 个项目</span>
<a class="btn btn-success" href="app.html">创建项目</a>
</p>
<form class="" role="search">
<div class="form-group"><input type="text" class="form-control search clearable"
placeholder="搜索App, 例如:apollo" ng-model="searchKey"
ng-change="search()"></div>
</form>
</div>
</div>
</div>
<apollonav></apollonav>
</header>
<div id="app-list" ng-controller="IndexController">
<div class="container-fluid apollo-container">
<div class="list-group apps">
<a class="package list-group-item" href="config.html?#/appid={{app.appId}}"
ng-repeat="app in apps ">
<div class="row">
<div class="col-md-3"><h4 class="apps-name" ng-bind="app.appId"></h4></div>
<div class="col-md-7 hidden-xs">
<p class="apps-description" ng-bind="app.name"></p>
<section class="media create-app-list">
<aside class="media-left text-center">
<h5>我的项目</h5>
</aside>
<aside class="media-body">
<div class="col-md-2 text-center" ng-click="goToCreateAppPage()">
<div href="#" class="thumbnail create-btn hover cursor-pointer">
<img src="img/plus-white.png"/>
<h5>创建项目</h5>
</div>
</div>
<div class="col-md-2 text-center" ng-repeat="app in createdApps" ng-click="goToAppHomePage(app.appId)">
<div href="#" class="thumbnail hover cursor-pointer">
<h4 ng-bind="app.appId"></h4>
<h5 ng-bind="app.name"></h5>
</div>
</div>
<div class="col-md-2 text-center" ng-show="hasMoreCreatedApps"
ng-click="getUserCreatedApps()">
<div href="#" class="thumbnail hover cursor-pointer">
<img class="more-img" src="img/more.png"/>
<h5>加载更多</h5>
</div>
<div class="col-md-2">
<p class="apps-description">
<span ng-bind="app.ownerName"></span>
<br>
<span ng-bind="app.ownerEmail"></span>
</div>
</aside>
</section>
<section class="media favorites-app-list">
<aside class="media-left text-center">
<h5>收藏的项目</h5>
</aside>
<aside class="media-body">
<div class="app-panel col-md-2 text-center"
ng-repeat="app in favorites"
ng-click="goToAppHomePage(app.appId)"
ng-mouseover="toggleOperationBtn(app)"
ng-mouseout="toggleOperationBtn(app)">
<div class="thumbnail hover">
<h4 ng-bind="app.appId"></h4>
<h5 ng-bind="app.name"></h5>
<p class="operate-panel" ng-show="app.showOperationBtn">
<button class="btn btn-default btn-xs" title="置顶"
ng-click="toTop(app.favoriteId);$event.stopPropagation();">
<img src="img/top.png" class="i-15">
</button>
<button class="btn btn-default btn-xs" title="取消收藏"
ng-click="deleteFavorite(app.favoriteId);$event.stopPropagation();">
<img src="img/like.png" class="i-15">
</button>
</p>
</div>
</div>
<div class="col-md-2 text-center" ng-show="hasMoreFavorites"
ng-click="getUserFavorites()">
<div href="#" class="thumbnail hover cursor-pointer">
<img class="more-img" src="img/more.png"/>
<h5>加载更多</h5>
</div>
</div>
<div class="no-favorites text-center" ng-show="!favorites || favorites.length == 0">
<h4>您还没有收藏过任何项目,在项目主页可以收藏项目哟~</h4>
</div>
</aside>
</section>
<section class="media visit-app-list" ng-show="visitedApps && visitedApps.length">
<aside class="media-left text-center">
<h5>最近浏览的项目</h5>
</aside>
<aside class="media-body">
<div class="app-panel col-md-2 text-center"
ng-repeat="app in visitedApps"
ng-click="goToAppHomePage(app.appId)">
<div class="thumbnail hover">
<h4 ng-bind="app.appId"></h4>
<h5 ng-bind="app.name"></h5>
</div>
</div>
</a>
</div>
</aside>
</section>
</div>
<div ng-include="'views/common/footer.html'"></div>
<!--angular-->
......@@ -82,7 +124,11 @@
<script type="application/javascript" src="scripts/app.js"></script>
<script type="application/javascript" src="scripts/services/AppService.js"></script>
<script type="application/javascript" src="scripts/services/EnvService.js"></script>
<script type="application/javascript" src="scripts/services/UserService.js"></script>
<script type="application/javascript" src="scripts/services/FavoriteService.js"></script>
<script type="application/javascript" src="scripts/AppUtils.js"></script>
<script type="application/javascript" src="scripts/directive/directive.js"></script>
<script type="application/javascript" src="scripts/controller/IndexController.js"></script>
</body>
......
......@@ -107,7 +107,6 @@
<!--angular-->
<script src="../vendor/angular/angular.min.js"></script>
<script src="../vendor/angular/angular-route.min.js"></script>
<script src="../vendor/angular/angular-resource.min.js"></script>
<script src="../vendor/angular/angular-toastr-1.4.1.tpls.min.js"></script>
<script src="../vendor/angular/loading-bar.min.js"></script>
......
......@@ -9,7 +9,7 @@ var directive_module = angular.module('apollo.directive', ['app.service', 'app.u
/** page module 定义*/
// 首页
var index_module = angular.module('index', ['toastr', 'app.service', 'app.util', 'angular-loading-bar']);
var index_module = angular.module('index', ['toastr', 'app.service', 'apollo.directive', 'app.util', 'angular-loading-bar']);
//项目主页
var application_module = angular.module('application', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar', 'valdr']);
//创建项目页面
......
index_module.controller('IndexController', ['$scope', '$window', 'toastr', 'AppService', 'AppUtil', 'EnvService',
function ($scope, $window, toastr, AppService, AppUtil, EnvService) {
$scope.envs = [];
$scope.selectedEnv = '';
EnvService.find_all_envs().then(function (result) {
$scope.envs = result;
//default select first env
$scope.switchEnv($scope.envs[0]);
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "load env error");
});
$scope.switchEnv = function (env) {
$scope.selectedEnv = env;
loadApps(env);
};
var sourceApps = [];
function loadApps(env){
AppService.find_all_app(env).then(function (result) {
sourceApps = sortApps(result);
$scope.apps = sourceApps;
$scope.appsCount = sourceApps.length;
$scope.selectedEnv = env;
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "load apps error");
});
}
var VISITED_APPS_STORAGE_KEY = "VisitedApps";
//访问过的App放在列表最前面,方便用户选择
function sortApps(sourceApps) {
var visitedApps = JSON.parse(localStorage.getItem(VISITED_APPS_STORAGE_KEY));
if (!visitedApps){
return sourceApps;
index_module.controller('IndexController', ['$scope', '$window', 'toastr', 'AppUtil', 'AppService',
'UserService', 'FavoriteService',
IndexController]);
function IndexController($scope, $window, toastr, AppUtil, AppService, UserService, FavoriteService) {
$scope.userId = '';
$scope.getUserCreatedApps = getUserCreatedApps;
$scope.getUserFavorites = getUserFavorites;
$scope.goToAppHomePage = goToAppHomePage;
$scope.goToCreateAppPage = goToCreateAppPage;
$scope.toggleOperationBtn = toggleOperationBtn;
$scope.toTop = toTop;
$scope.deleteFavorite = deleteFavorite;
UserService.load_user().then(function (result) {
$scope.userId = result.userId;
$scope.createdAppPage = 0;
$scope.createdApps = [];
$scope.hasMoreCreatedApps = true;
$scope.favoritesPage = 0;
$scope.favorites = [];
$scope.hasMoreFavorites = true;
$scope.visitedApps = [];
getUserCreatedApps();
getUserFavorites();
initUserVisitedApps();
});
function getUserCreatedApps() {
var size = 10;
AppService.find_app_by_owner($scope.userId, $scope.createdAppPage, size)
.then(function (result) {
$scope.createdAppPage += 1;
$scope.hasMoreCreatedApps = result.length == size;
if (!result || result.length == 0) {
return;
}
var existedVisitedAppsMap = {};
visitedApps.forEach(function (app) {
existedVisitedAppsMap[app] = true;
result.forEach(function (app) {
$scope.createdApps.push(app);
});
var sortedApps = [];
sourceApps.forEach(function (app) {
if (existedVisitedAppsMap[app.appId]){
sortedApps.push(app);
}
});
sourceApps.forEach(function (app) {
if (!existedVisitedAppsMap[app.appId]){
sortedApps.push(app);
}
})
}
function getUserFavorites() {
var size = 11;
FavoriteService.findFavorites($scope.userId, '', $scope.favoritesPage, size)
.then(function (result) {
$scope.favoritesPage += 1;
$scope.hasMoreFavorites = result.length == size;
if (!result || result.length == 0) {
return;
}
var appIds = [];
result.forEach(function (favorite) {
appIds.push(favorite.appId);
});
return sortedApps;
}
$scope.search = function () {
var key = $scope.searchKey.toLocaleLowerCase();
if (key == '') {
$scope.apps = sourceApps;
return;
}
var result = [];
sourceApps.forEach(function (item) {
if (item.appId.toLocaleLowerCase().indexOf(key) >= 0 ||
item.name.toLocaleLowerCase().indexOf(key) >= 0) {
result.push(item);
}
AppService.find_apps(appIds.join(","))
.then(function (apps) {
//sort
var appIdMapApp = {};
apps.forEach(function (app) {
appIdMapApp[app.appId] = app;
});
result.forEach(function (favorite) {
var app = appIdMapApp[favorite.appId];
app.favoriteId = favorite.id;
$scope.favorites.push(app);
});
});
})
}
function initUserVisitedApps() {
var VISITED_APPS_STORAGE_KEY = "VisitedAppsV2";
var visitedAppsObject = JSON.parse(localStorage.getItem(VISITED_APPS_STORAGE_KEY));
if (!visitedAppsObject) {
visitedAppsObject = {};
}
var userVisitedApps = visitedAppsObject[$scope.userId];
if (userVisitedApps && userVisitedApps.length > 0) {
AppService.find_apps(userVisitedApps.join(","))
.then(function (apps) {
apps.forEach(function (app) {
$scope.visitedApps.push(app);
});
});
}
}
function goToCreateAppPage() {
$window.location.href = "/app.html";
}
function goToAppHomePage(appId) {
$window.location.href = "/config.html?#/appid=" + appId;
}
function toggleOperationBtn(app) {
app.showOperationBtn = !app.showOperationBtn;
}
function toTop(favoriteId) {
FavoriteService.toTop(favoriteId).then(function () {
toastr.success("置顶成功");
reload();
})
}
function deleteFavorite(favoriteId) {
FavoriteService.deleteFavorite(favoriteId).then(function () {
toastr.success("取消收藏成功");
reload();
})
}
function reload() {
setTimeout(function () {
$window.location.reload();
}, 500);
$scope.apps = result;
};
}
}]);
}
application_module.controller("ConfigBaseInfoController",
['$rootScope', '$scope', '$location', 'toastr', 'AppService', 'PermissionService',
['$rootScope', '$scope', '$location', 'toastr', 'UserService', 'AppService',
'FavoriteService',
'PermissionService',
'AppUtil', ConfigBaseInfoController]);
function ConfigBaseInfoController($rootScope, $scope, $location, toastr, AppService, PermissionService,
AppUtil) {
function ConfigBaseInfoController($rootScope, $scope, $location, toastr, UserService, AppService, FavoriteService,
PermissionService,
AppUtil) {
var appId = AppUtil.parseParams($location.$$url).appid;
$rootScope.hideTip = JSON.parse(localStorage.getItem("hideTip"));
initPage();
//save user recent visited apps
var VISITED_APPS_STORAGE_KEY = "VisitedApps";
var visitedApps = JSON.parse(localStorage.getItem(VISITED_APPS_STORAGE_KEY));
var hasSaved = false;
if (visitedApps) {
visitedApps.forEach(function (app) {
if (app == appId) {
hasSaved = true;
return;
}
function initPage() {
$rootScope.hideTip = JSON.parse(localStorage.getItem("hideTip"));
//load session storage to recovery scene
var scene = JSON.parse(sessionStorage.getItem(appId));
$rootScope.pageContext = {
appId: appId,
env: scene ? scene.env : '',
clusterName: scene ? scene.cluster : 'default'
};
UserService.load_user().then(function (result) {
$rootScope.pageContext.userId = result.userId;
handleFavorite();
recordVisitApp();
});
} else {
visitedApps = [];
loadAppInfo();
handlePermission();
}
if (!hasSaved) {
visitedApps.push(appId);
localStorage.setItem(VISITED_APPS_STORAGE_KEY,
JSON.stringify(visitedApps));
function recordVisitApp() {
//save user recent visited apps
var VISITED_APPS_STORAGE_KEY = "VisitedAppsV2";
var visitedAppsObject = JSON.parse(localStorage.getItem(VISITED_APPS_STORAGE_KEY));
var hasSaved = false;
if (visitedAppsObject) {
var visitedApps = visitedAppsObject[$rootScope.pageContext.userId];
if (visitedApps && visitedApps.length > 0){
visitedApps.forEach(function (app) {
if (app == appId) {
hasSaved = true;
return;
}
});
}
} else {
visitedAppsObject = {};
visitedAppsObject[$rootScope.pageContext.userId] = [];
}
var currentUserVisitedApps = visitedAppsObject[$rootScope.pageContext.userId];
if (!hasSaved) {
//if queue's length bigger than 6 will remove oldest app
if (currentUserVisitedApps.length >= 6){
currentUserVisitedApps.splice(0, 1);
}
visitedAppsObject[$rootScope.pageContext.userId].push($rootScope.pageContext.appId);
localStorage.setItem(VISITED_APPS_STORAGE_KEY,
JSON.stringify(visitedAppsObject));
}
}
//load session storage to recovery scene
var scene = JSON.parse(sessionStorage.getItem(appId));
function loadAppInfo() {
$scope.notFoundApp = true;
AppService.load($rootScope.pageContext.appId).then(function (result) {
$scope.notFoundApp = false;
var pageContext = {
appId: appId,
env: scene ? scene.env : '',
clusterName: scene ? scene.cluster : 'default'
};
$scope.appBaseInfo = result;
$scope.appBaseInfo.orgInfo = result.orgName + '(' + result.orgId + ')';
$rootScope.pageContext = pageContext;
loadNavTree();
$scope.notFoundApp = true;
////// app info //////
AppService.load($rootScope.pageContext.appId).then(function (result) {
$scope.notFoundApp = false;
$(".J_appFound").removeClass("hidden");
}, function (result) {
$(".J_appNotFound").removeClass("hidden");
});
$scope.appBaseInfo = result;
$scope.appBaseInfo.orgInfo = result.orgName + '(' + result.orgId + ')';
////// 补缺失的环境 //////
$scope.missEnvs = [];
AppService.find_miss_envs($rootScope.pageContext.appId).then(function (result) {
$scope.missEnvs = AppUtil.collectData(result);
}, function (result) {
loadNavTree();
});
$(".J_appFound").removeClass("hidden");
}, function (result) {
$(".J_appNotFound").removeClass("hidden");
});
$scope.createAppInMissEnv = function () {
var count = 0;
$scope.missEnvs.forEach(function (env) {
AppService.create_remote(env, $scope.appBaseInfo).then(function (result) {
toastr.success(env, '创建成功');
count++;
if (count == $scope.missEnvs.length) {
location.reload(true);
}
}, function (result) {
toastr.error(AppUtil.errorMsg(result), '创建失败:' + env);
count++;
if (count == $scope.missEnvs.length) {
location.reload(true);
}
});
});
};
}
function loadNavTree() {
AppService.load_nav_tree($rootScope.pageContext.appId).then(function (result) {
......@@ -66,12 +122,12 @@ function ConfigBaseInfoController($rootScope, $scope, $location, toastr, AppServ
return;
}
//default first env if session storage is empty
if (!pageContext.env) {
pageContext.env = nodes[0].env;
if (!$rootScope.pageContext.env) {
$rootScope.pageContext.env = nodes[0].env;
}
$rootScope.refreshNamespaces();
nodes.forEach(function (env, envIdx) {
nodes.forEach(function (env) {
if (!env.clusters || env.clusters.length == 0) {
return;
}
......@@ -82,7 +138,7 @@ function ConfigBaseInfoController($rootScope, $scope, $location, toastr, AppServ
//如果env下面只有一个default集群则不显示集群列表
if (env.clusters && env.clusters.length == 1 && env.clusters[0].name
== 'default') {
if (pageContext.env == env.env) {
if ($rootScope.pageContext.env == env.env) {
node.state = {};
node.state.selected = true;
}
......@@ -90,12 +146,12 @@ function ConfigBaseInfoController($rootScope, $scope, $location, toastr, AppServ
} else {
node.selectable = false;
//cluster list
env.clusters.forEach(function (cluster, clusterIdx) {
env.clusters.forEach(function (cluster) {
var clusterNode = {},
parentNode = [];
//default selection from session storage or first env & first cluster
if (pageContext.env == env.env && pageContext.clusterName
if ($rootScope.pageContext.env == env.env && $rootScope.pageContext.clusterName
== cluster.name) {
clusterNode.state = {};
clusterNode.state.selected = true;
......@@ -168,54 +224,66 @@ function ConfigBaseInfoController($rootScope, $scope, $location, toastr, AppServ
});
}
////// 补缺失的环境 //////
$scope.missEnvs = [];
AppService.find_miss_envs($rootScope.pageContext.appId).then(function (result) {
$scope.missEnvs = AppUtil.collectData(result);
}, function (result) {
});
$scope.createAppInMissEnv = function () {
var count = 0;
$scope.missEnvs.forEach(function (env) {
AppService.create_remote(env, $scope.appBaseInfo).then(function (result) {
toastr.success(env, '创建成功');
count++;
if (count == $scope.missEnvs.length) {
location.reload(true);
}
}, function (result) {
toastr.error(AppUtil.errorMsg(result), '创建失败:' + env);
count++;
if (count == $scope.missEnvs.length) {
location.reload(true);
function handleFavorite() {
FavoriteService.findFavorites($rootScope.pageContext.userId,
$rootScope.pageContext.appId)
.then(function (result) {
if (result && result.length) {
$scope.favoriteId = result[0].id;
}
});
});
};
//permission
PermissionService.has_create_namespace_permission(appId).then(function (result) {
$scope.hasCreateNamespacePermission = result.hasPermission;
}, function (result) {
$scope.addFavorite = function () {
var favorite = {
userId: $rootScope.pageContext.userId,
appId: $rootScope.pageContext.appId
};
FavoriteService.addFavorite(favorite)
.then(function (result) {
$scope.favoriteId = result.id;
toastr.success("收藏成功");
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "收藏失败");
})
};
$scope.deleteFavorite = function () {
FavoriteService.deleteFavorite($scope.favoriteId)
.then(function (result) {
$scope.favoriteId = 0;
toastr.success("取消收藏成功");
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "取消收藏失败");
})
};
}
function handlePermission() {
//permission
PermissionService.has_create_namespace_permission(appId).then(function (result) {
$scope.hasCreateNamespacePermission = result.hasPermission;
}, function (result) {
});
});
PermissionService.has_create_cluster_permission(appId).then(function (result) {
$scope.hasCreateClusterPermission = result.hasPermission;
}, function (result) {
PermissionService.has_create_cluster_permission(appId).then(function (result) {
$scope.hasCreateClusterPermission = result.hasPermission;
}, function (result) {
});
});
PermissionService.has_assign_user_permission(appId).then(function (result) {
$scope.hasAssignUserPermission = result.hasPermission;
}, function (result) {
PermissionService.has_assign_user_permission(appId).then(function (result) {
$scope.hasAssignUserPermission = result.hasPermission;
}, function (result) {
});
});
$scope.showMasterPermissionTips = function () {
$("#masterNoPermissionDialog").modal('show');
$scope.showMasterPermissionTips = function () {
$("#masterNoPermissionDialog").modal('show');
};
}
}
......
......@@ -10,7 +10,7 @@ directive_module.directive('apollonav', function ($compile, $window, toastr, App
scope.sourceApps = [];
scope.copyedApps = [];
AppService.find_all_app().then(function (result) {
AppService.find_apps().then(function (result) {
result.forEach(function (app) {
app.selected = false;
scope.sourceApps.push(app);
......
appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
var app_resource = $resource('/apps/:appId', {}, {
find_all_app:{
find_apps: {
method: 'GET',
isArray: true,
url:'/apps'
url: '/apps'
},
find_app_by_owner: {
method: 'GET',
isArray: true,
url: '/apps/by-owner'
},
load_navtree:{
load_navtree: {
methode: 'GET',
isArray:false,
url:'/apps/:appId/navtree'
isArray: false,
url: '/apps/:appId/navtree'
},
load_app: {
method: 'GET',
......@@ -28,24 +33,38 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
}
});
return {
find_all_app: function () {
find_apps: function (appIds) {
if (!appIds) {
appIds = '';
}
var d = $q.defer();
app_resource.find_all_app({
}, function (result) {
app_resource.find_apps({appIds: appIds}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
load_nav_tree: function (appId){
find_app_by_owner: function (owner, page, size) {
var d = $q.defer();
app_resource.find_app_by_owner({
owner: owner,
page: page,
size: size
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
load_nav_tree: function (appId) {
var d = $q.defer();
app_resource.load_navtree({
appId: appId
}, function(result){
appId: appId
}, function (result) {
d.resolve(result);
}, function(result){
}, function (result) {
d.reject(result);
});
return d.promise;
......@@ -61,7 +80,7 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
},
create_remote: function (env, app) {
var d = $q.defer();
app_resource.create_app_remote({env:env}, app, function (result) {
app_resource.create_app_remote({env: env}, app, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
......@@ -71,8 +90,8 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
load: function (appId) {
var d = $q.defer();
app_resource.load_app({
appId: appId
}, function (result) {
appId: appId
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
......@@ -82,7 +101,7 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
find_miss_envs: function (appId) {
var d = $q.defer();
app_resource.find_miss_envs({
appId: appId
appId: appId
}, function (result) {
d.resolve(result);
}, function (result) {
......
appService.service('FavoriteService', ['$resource', '$q', function ($resource, $q) {
var resource = $resource('', {}, {
find_favorites: {
method: 'GET',
url: '/favorites',
isArray: true
},
add_favorite: {
method: 'POST',
url: '/favorites'
},
delete_favorite: {
method: 'DELETE',
url: '/favorites/:favoriteId'
},
to_top: {
method: 'PUT',
url: '/favorites/:favoriteId'
}
});
return {
findFavorites: function (userId, appId, page, size) {
var d = $q.defer();
resource.find_favorites({
userId: userId,
appId: appId,
page: page,
size: size
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
addFavorite: function (favorite) {
var d = $q.defer();
resource.add_favorite({}, favorite, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
deleteFavorite: function (favoriteId) {
var d = $q.defer();
resource.delete_favorite({
favoriteId: favoriteId
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
toTop: function (favoriteId) {
var d = $q.defer();
resource.to_top({
favoriteId: favoriteId
}, {}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
}
}
}]);
......@@ -67,7 +67,6 @@
<!--angular-->
<script src="vendor/angular/angular.min.js"></script>
<script src="vendor/angular/angular-route.min.js"></script>
<script src="vendor/angular/angular-resource.min.js"></script>
<script src="vendor/angular/angular-toastr-1.4.1.tpls.min.js"></script>
<script src="vendor/angular/loading-bar.min.js"></script>
......
......@@ -25,23 +25,27 @@ p, td, span {
word-break: break-all;
}
.no-radius {
border-radius: 0;
}
.cursor-pointer{
.no-margin {
margin: 0;
}
.cursor-pointer {
cursor: pointer;
}
pre {
white-space: pre-wrap; /* Since CSS 2.1 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
white-space: pre-wrap; /* Since CSS 2.1 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
.hover:hover{
.hover:hover {
background: #f5f5f5;
cursor: pointer
}
......@@ -75,7 +79,7 @@ pre {
width: 15px;
}
.badge{
.badge {
padding: 1px 4px;
}
......@@ -89,7 +93,7 @@ pre {
color: #0f0f0f;
}
.panel-default > .panel-heading .badge{
.panel-default > .panel-heading .badge {
}
......@@ -210,6 +214,14 @@ table th {
min-width: 405px;
}
#treeview .list-group{
margin: 0;
}
#treeview .list-group .list-group-item{
border: 0;
border-top: 1px solid #eff2f7;
}
.project-info th {
text-align: right;
padding: 4px 6px;
......@@ -225,11 +237,11 @@ table th {
min-height: 500px;
}
#itemModal .modal-dialog{
#itemModal .modal-dialog {
width: 860px;
}
#itemModal .modal-body{
#itemModal .modal-body {
padding-bottom: 0;
}
......@@ -350,12 +362,12 @@ table th {
}
.instance-view .btn-primary .badge{
.instance-view .btn-primary .badge {
color: #337ab7;
background-color: #fff;
}
.instance-view .btn-default .badge{
.instance-view .btn-default .badge {
background: #777;
color: #fff;
}
......@@ -546,3 +558,64 @@ table th {
font-size: 18px;
}
/*index page*/
#app-list .media-body {
padding-top: 15px;
}
#app-list .media {
background: #fff;
display: table;
}
#app-list .media-left {
width: 1000px;
color: #fff;
display: table-cell;
vertical-align: middle;
}
#app-list .more-img {
width: 30px;
height: 30px;
}
#app-list .app-panel {
position: relative;
}
#app-list .operate-panel {
position: absolute;
top: 5px;
right: 20px;
}
.create-app-list .media-left {
background: #a9d96c;
}
.create-app-list .create-btn {
background: #a9d96c;
color: #fff
}
.create-app-list .create-btn:hover {
background: #81AB56;
}
.create-app-list .create-btn img {
width: 26px;
height: 26px
}
.favorites-app-list .media-left {
background: #57c8f2;
}
.favorites-app-list .no-favorites{
padding-bottom: 15px;
}
.visit-app-list .media-left {
background: #41cac0;
}
package com.ctrip.framework.apollo.portal.service;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.portal.AbstractIntegrationTest;
import com.ctrip.framework.apollo.portal.entity.po.Favorite;
import com.ctrip.framework.apollo.portal.repository.FavoriteRepository;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.jdbc.Sql;
import java.util.List;
public class FavoriteServiceTest extends AbstractIntegrationTest {
@Autowired
private FavoriteService favoriteService;
@Autowired
private FavoriteRepository favoriteRepository;
private String testUser = "apollo";
@Test
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testAddNormalFavorite() {
String testApp = "testApp";
Favorite favorite = instanceOfFavorite(testUser, testApp);
favoriteService.addFavorite(favorite);
List<Favorite> createdFavorites = favoriteService.search(testUser, testApp, new PageRequest(0, 10));
Assert.assertEquals(1, createdFavorites.size());
Assert.assertEquals(FavoriteService.POSITION_DEFAULT, createdFavorites.get(0).getPosition());
Assert.assertEquals(testUser, createdFavorites.get(0).getUserId());
Assert.assertEquals(testApp, createdFavorites.get(0).getAppId());
}
@Test(expected = BadRequestException.class)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testAddFavoriteErrorUser() {
String testApp = "testApp";
Favorite favorite = instanceOfFavorite("errorUser", testApp);
favoriteService.addFavorite(favorite);
}
@Test
@Sql(scripts = "/sql/favorites/favorites.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testSearchByUserId() {
List<Favorite> favorites = favoriteService.search(testUser, null, new PageRequest(0, 10));
Assert.assertEquals(4, favorites.size());
}
@Test
@Sql(scripts = "/sql/favorites/favorites.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testSearchByAppId() {
List<Favorite> favorites = favoriteService.search(null, "test0621-04", new PageRequest(0, 10));
Assert.assertEquals(3, favorites.size());
}
@Test
@Sql(scripts = "/sql/favorites/favorites.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testSearchByAppIdAndUserId() {
List<Favorite> favorites = favoriteService.search(testUser, "test0621-04", new PageRequest(0, 10));
Assert.assertEquals(1, favorites.size());
}
@Test(expected = BadRequestException.class)
@Sql(scripts = "/sql/favorites/favorites.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testSearchWithErrorParams() {
favoriteService.search(null, null, new PageRequest(0, 10));
}
@Test
@Sql(scripts = "/sql/favorites/favorites.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testDeleteFavorite() {
long legalFavoriteId = 21L;
favoriteService.deleteFavorite(legalFavoriteId);
Assert.assertNull(favoriteRepository.findOne(legalFavoriteId));
}
@Test(expected = BadRequestException.class)
@Sql(scripts = "/sql/favorites/favorites.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testDeleteFavoriteFail() {
long anotherPersonFavoriteId = 23L;
favoriteService.deleteFavorite(anotherPersonFavoriteId);
Assert.assertNull(favoriteRepository.findOne(anotherPersonFavoriteId));
}
@Test(expected = BadRequestException.class)
@Sql(scripts = "/sql/favorites/favorites.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testAdjustFavoriteError() {
long anotherPersonFavoriteId = 23;
favoriteService.adjustFavoriteToFirst(anotherPersonFavoriteId);
}
@Test
@Sql(scripts = "/sql/favorites/favorites.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testAdjustFavorite() {
long toAdjustFavoriteId = 20;
favoriteService.adjustFavoriteToFirst(toAdjustFavoriteId);
List<Favorite> favorites = favoriteService.search(testUser, null, new PageRequest(0, 10));
Favorite firstFavorite = favorites.get(0);
Favorite secondFavorite = favorites.get(1);
Assert.assertEquals(toAdjustFavoriteId, firstFavorite.getId());
Assert.assertEquals(firstFavorite.getPosition() + 1, secondFavorite.getPosition());
}
private Favorite instanceOfFavorite(String userId, String appId) {
Favorite favorite = new Favorite();
favorite.setAppId(appId);
favorite.setUserId(userId);
favorite.setDataChangeCreatedBy(userId);
favorite.setDataChangeLastModifiedBy(userId);
return favorite;
}
}
......@@ -3,4 +3,3 @@ spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy
spring.jpa.properties.hibernate.show_sql=true
spring.h2.console.enabled = true
spring.h2.console.settings.web-allow-others=true
......@@ -3,3 +3,4 @@ delete from Role;
delete from RolePermission;
delete from UserRole;
delete from AppNamespace;
DELETE FROM Favorite;
INSERT INTO `favorite` (`Id`, `UserId`, `AppId`, `Position`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_CreatedTime`, `DataChange_LastModifiedBy`, `DataChange_LastTime`)
VALUES
(18, 'apollo', 'test0621-03', 10000, 0, 'apollo', '2016-10-10 17:45:30', 'apollo', '2016-10-10 17:45:30'),
(19, 'apollo', '100003173', 9999, 0, 'apollo', '2016-10-10 17:45:42', 'apollo', '2016-10-10 17:51:12'),
(20, 'apollo', 'test0621-01', 10000, 00000000, 'apollo', '2016-10-10 17:50:57', 'apollo', '2016-10-10 17:50:57'),
(21, 'apollo', 'test0621-04', 10000, 00000000, 'apollo', '2016-10-10 17:55:03', 'apollo', '2016-10-10 17:55:03'),
(22, 'apollo2', 'test0621-04', 10000, 00000000, 'apollo', '2016-10-10 17:55:21', 'apollo', '2016-10-10 17:55:21'),
(23, 'apollo3', 'test0621-04', 10000, 00000000, 'apollo', '2016-10-10 17:55:21', 'apollo', '2016-10-10 17:55:21');
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册