任务列表页,修复服务端一些 bug

pull/1/head
Doflatango 2017-01-22 15:18:08 +08:00 committed by miraclesu
parent ff1d79bb4a
commit 5b4900d6c4
10 changed files with 133 additions and 20 deletions

View File

@ -64,14 +64,14 @@ func (c *Client) Put(key, val string, opts ...client.OpOption) (*client.PutRespo
return c.Client.Put(ctx, key, val, opts...) return c.Client.Put(ctx, key, val, opts...)
} }
func (c *Client) PutWithRev(key, val string, rev int64) (*client.PutResponse, error) { func (c *Client) PutWithModRev(key, val string, rev int64) (*client.PutResponse, error) {
if rev == 0 { if rev == 0 {
return c.Put(key, val) return c.Put(key, val)
} }
ctx, cancel := context.WithTimeout(context.Background(), c.reqTimeout) ctx, cancel := context.WithTimeout(context.Background(), c.reqTimeout)
tresp, err := DefalutClient.Txn(ctx). tresp, err := DefalutClient.Txn(ctx).
If(client.Compare(client.Version(key), "=", rev)). If(client.Compare(client.ModRevision(key), "=", rev)).
Then(client.OpPut(key, val)). Then(client.OpPut(key, val)).
Commit() Commit()
cancel() cancel()

View File

@ -85,13 +85,13 @@ func (g *Group) Key() string {
return GroupKey(g.ID) return GroupKey(g.ID)
} }
func (g *Group) Put(rev int64) (*client.PutResponse, error) { func (g *Group) Put(modRev int64) (*client.PutResponse, error) {
b, err := json.Marshal(g) b, err := json.Marshal(g)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return DefalutClient.PutWithRev(g.Key(), string(b), rev) return DefalutClient.PutWithModRev(g.Key(), string(b), modRev)
} }
func (g *Group) Check() error { func (g *Group) Check() error {

View File

@ -58,6 +58,11 @@ func (j *JobRule) included(nid string, gs map[string]*Group) (string, bool) {
} }
func GetJob(group, id string) (job *Job, err error) { func GetJob(group, id string) (job *Job, err error) {
job, _, err = GetJobAndRev(group, id)
return
}
func GetJobAndRev(group, id string) (job *Job, rev int64, err error) {
resp, err := DefalutClient.Get(JobKey(group, id)) resp, err := DefalutClient.Get(JobKey(group, id))
if err != nil { if err != nil {
return return
@ -68,6 +73,7 @@ func GetJob(group, id string) (job *Job, err error) {
return return
} }
rev = resp.Kvs[0].ModRevision
err = json.Unmarshal(resp.Kvs[0].Value, &job) err = json.Unmarshal(resp.Kvs[0].Value, &job)
return return
} }

View File

@ -3,7 +3,6 @@ package web
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"path"
"sort" "sort"
"strings" "strings"
@ -44,6 +43,39 @@ func (j *Job) DeleteJob(w http.ResponseWriter, r *http.Request) {
outJSONWithCode(w, http.StatusNoContent, nil) outJSONWithCode(w, http.StatusNoContent, nil)
} }
func (j *Job) ChangeJobStatus(w http.ResponseWriter, r *http.Request) {
job := &models.Job{}
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&job)
if err != nil {
outJSONError(w, http.StatusBadRequest, err.Error())
return
}
r.Body.Close()
vars := mux.Vars(r)
originJob, rev, err := models.GetJobAndRev(vars["group"], vars["id"])
if err != nil {
outJSONError(w, http.StatusInternalServerError, err.Error())
return
}
originJob.Pause = job.Pause
b, err := json.Marshal(originJob)
if err != nil {
outJSONError(w, http.StatusInternalServerError, err.Error())
return
}
_, err = models.DefalutClient.PutWithModRev(originJob.Key(), string(b), rev)
if err != nil {
outJSONError(w, http.StatusInternalServerError, err.Error())
return
}
outJSON(w, originJob)
}
func (j *Job) UpdateJob(w http.ResponseWriter, r *http.Request) { func (j *Job) UpdateJob(w http.ResponseWriter, r *http.Request) {
job := &models.Job{} job := &models.Job{}
decoder := json.NewDecoder(r.Body) decoder := json.NewDecoder(r.Body)
@ -71,7 +103,7 @@ func (j *Job) UpdateJob(w http.ResponseWriter, r *http.Request) {
return return
} }
_, err = models.DefalutClient.Put(path.Join(conf.Config.Cmd, job.Group, job.ID), string(b)) _, err = models.DefalutClient.Put(job.Key(), string(b))
if err != nil { if err != nil {
outJSONError(w, http.StatusInternalServerError, err.Error()) outJSONError(w, http.StatusInternalServerError, err.Error())
return return
@ -107,7 +139,7 @@ func (j *Job) GetGroups(w http.ResponseWriter, r *http.Request) {
func (j *Job) GetListByGroupName(w http.ResponseWriter, r *http.Request) { func (j *Job) GetListByGroupName(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
resp, err := models.DefalutClient.Get(path.Join(conf.Config.Cmd, vars["name"]), clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend)) resp, err := models.DefalutClient.Get(conf.Config.Cmd+vars["name"], clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend))
if err != nil { if err != nil {
outJSONError(w, http.StatusInternalServerError, err.Error()) outJSONError(w, http.StatusInternalServerError, err.Error())
return return
@ -115,13 +147,13 @@ func (j *Job) GetListByGroupName(w http.ResponseWriter, r *http.Request) {
var jobList = make([]*models.Job, 0, resp.Count) var jobList = make([]*models.Job, 0, resp.Count)
for i := range resp.Kvs { for i := range resp.Kvs {
job := &models.Job{} job := models.Job{}
err = json.Unmarshal(resp.Kvs[i].Value, &job) err = json.Unmarshal(resp.Kvs[i].Value, &job)
if err != nil { if err != nil {
outJSONError(w, http.StatusInternalServerError, err.Error()) outJSONError(w, http.StatusInternalServerError, err.Error())
return return
} }
jobList = append(jobList) jobList = append(jobList, &job)
} }
outJSON(w, jobList) outJSON(w, jobList)

View File

@ -39,9 +39,9 @@ func (n *Node) UpdateGroup(w http.ResponseWriter, r *http.Request) {
return return
} }
// @TODO rev // @TODO modRev
var rev int64 = 0 var modRev int64 = 0
if _, err = g.Put(rev); err != nil { if _, err = g.Put(modRev); err != nil {
outJSONError(w, http.StatusBadRequest, err.Error()) outJSONError(w, http.StatusBadRequest, err.Error())
return return
} }

View File

@ -25,6 +25,9 @@ func InitRouters() (s *http.Server, err error) {
// create/update a job // create/update a job
h = BaseHandler{Handle: jobHandler.UpdateJob} h = BaseHandler{Handle: jobHandler.UpdateJob}
subrouter.Handle("/job", h).Methods("PUT") subrouter.Handle("/job", h).Methods("PUT")
// pause/start
h = BaseHandler{Handle: jobHandler.ChangeJobStatus}
subrouter.Handle("/job/{group}-{id}", h).Methods("POST")
// get a job // get a job
h = BaseHandler{Handle: jobHandler.GetJob} h = BaseHandler{Handle: jobHandler.GetJob}
subrouter.Handle("/job/{group}-{id}", h).Methods("GET") subrouter.Handle("/job/{group}-{id}", h).Methods("GET")

View File

@ -1,12 +1,44 @@
<style scope>
.clearfix:after {content:""; clear:both; display:table;}
</style>
<template> <template>
<div> <div>
<JobToolbar/> <JobToolbar class="clearfix"/>
<form class="ui form"> <form class="ui form">
<div class="field"> <div class="field">
<label>任务分组</label> <label>选择一个分组显示其下的任务</label>
<Dropdown title="选择分组" v-bind:items="groups" v-on:change="changeGroup"/> <Dropdown title="选择分组" v-bind:items="groups" v-on:change="changeGroup"/>
</div> </div>
</form> </form>
<table class="ui hover celled striped blue table" v-if="jobs.length > 0">
<thead>
<tr>
<th class="collapsing center aligned">操作</th>
<th class="collapsing center aligned">状态</th>
<th width="200px" class="center aligned">分组</th>
<th class="center aligned">名称</th>
</tr>
</thead>
<tbody>
<tr v-for="(job, index) in jobs">
<td class="center aligned">
<div class="ui icon dropdown">
<i class="content icon"></i>
<div class="menu">
<div class="item" v-on:click="$router.push('/job/edit/'+job.group+'/'+job.id)">编辑</div>
<div class="item" v-if="job.pause" v-on:click="changeStatus(job.group, job.id, index, !job.pause)"></div>
<div class="item" v-if="!job.pause" v-on:click="changeStatus(job.group, job.id, index, !job.pause)"></div>
<div class="divider"></div>
<div class="item" style="color:red;" v-on:click="removeJob(job.group, job.id, index)">删除</div>
</div>
</div>
</td>
<td class="center aligned"><i class="icon" v-bind:class="{pause: job.pause, play: !job.pause, green: !job.pause}"></i></td>
<td>{{job.group}}</td>
<td>{{job.name}}</td>
</tr>
</tbody>
</table>
</div> </div>
</template> </template>
@ -18,7 +50,9 @@ export default {
name: 'job', name: 'job',
data: function(){ data: function(){
return { return {
groups: [] groups: [],
group: '',
jobs: []
} }
}, },
@ -31,7 +65,35 @@ export default {
}, },
methods: { methods: {
changeGroup: function(){} changeGroup: function(val, text){
var vm = this;
this.group = val;
this.refreshList();
},
refreshList: function(){
var vm = this;
this.$rest.GET('job/group/'+this.group).onsucceed(200, (resp)=>{
vm.jobs = resp;
vm.$nextTick(()=>{
$(vm.$el).find('table .ui.dropdown').dropdown();
});
}).do();
},
removeJob: function(group, id, index){
var vm = this;
this.$rest.DELETE('job/'+group+'-'+id).onsucceed(204, (resp)=>{
vm.jobs.splice(index, 1);
}).do();
},
changeStatus: function(group, id, index, isPause){
var vm = this;
this.$rest.POST('job/'+group+'-'+id, {"pause": isPause}).onsucceed(200, (resp)=>{
vm.refreshList();
}).do();
}
}, },
components: { components: {

View File

@ -21,7 +21,7 @@
<input type="text" v-model="job.cmd" placeholder="任务脚本"> <input type="text" v-model="job.cmd" placeholder="任务脚本">
</div> </div>
<div class="field"> <div class="field">
<span v-if="job.rules.length == 0"><i class="warning circle icon"></i></span> <span v-if="!job.rules || job.rules.length == 0"><i class="warning circle icon"></i></span>
</div> </div>
<JobEditRule v-for="(rule, index) in job.rules" :rule="rule" :index="index" v-on:remove="removeRule" v-on:change="changeRule"/> <JobEditRule v-for="(rule, index) in job.rules" :rule="rule" :index="index" v-on:remove="removeRule" v-on:change="changeRule"/>
<div class="two fields"> <div class="two fields">
@ -84,7 +84,7 @@ export default {
var exceptCode = this.action == 'CREATE' ? 201 : 200; var exceptCode = this.action == 'CREATE' ? 201 : 200;
this.loading = true; this.loading = true;
var vm = this; var vm = this;
this.$rest.PUT('job', JSON.stringify(this.job)) this.$rest.PUT('job', this.job)
.onsucceed(exceptCode, ()=>{vm.$router.push('/job')}) .onsucceed(exceptCode, ()=>{vm.$router.push('/job')})
.onfailed((resp)=>{console.log(resp)}) .onfailed((resp)=>{console.log(resp)})
.onend(()=>{vm.loading=false}) .onend(()=>{vm.loading=false})
@ -93,9 +93,15 @@ export default {
}, },
mounted: function(){ mounted: function(){
this.action = this.$route.path.indexOf('/job/create') === 0 ? 'CREATE' : 'UPDATE';
var vm = this; var vm = this;
if (this.$route.path.indexOf('/job/create') === 0) {
this.action = 'CREATE';
} else {
this.action = 'UPDATE';
this.$rest.GET('job/'+this.$route.params.group+'-'+this.$route.params.id).onsucceed(200, (resp)=>{vm.job = resp}).do();
}
this.$rest.GET('job/groups').onsucceed(200, (resp)=>{ this.$rest.GET('job/groups').onsucceed(200, (resp)=>{
!resp.includes('default') && resp.unshift('default'); !resp.includes('default') && resp.unshift('default');
vm.groups = resp; vm.groups = resp;

View File

@ -37,6 +37,9 @@ var sendXHR = function(opt) {
typeof opt.onend == 'function' && opt.onend(xhr); typeof opt.onend == 'function' && opt.onend(xhr);
} }
if (typeof opt.data == 'object') {
opt.data = JSON.stringify(opt.data);
}
xhr.send(opt.data); xhr.send(opt.data);
} }

View File

@ -32,6 +32,7 @@ var routes = [
{path: '/log', component: Log}, {path: '/log', component: Log},
{path: '/job', component: Job}, {path: '/job', component: Job},
{path: '/job/create', component: JobEdit}, {path: '/job/create', component: JobEdit},
{path: '/job/edit/:group/:id', component: JobEdit},
{path: '/node', component: Node} {path: '/node', component: Node}
]; ];