完成 PR 列表

This commit is contained in:
巴拉迪维 2022-04-19 20:44:12 +08:00
parent 0f203d4f47
commit 3fcf982bc6
6 changed files with 259 additions and 6 deletions

View File

@ -193,4 +193,24 @@ function deleteCommit(sha) {
});
}//end of if
});
}
function deletePR(prID) {
$('body').modal('confirm','删除 PullRequest','确认删除 <b>'+prID+'</b> 的 PullRequest 记录吗?', function(choice){
if(choice) {
$.ajax({
type:"POST",
url: "/admin/prs/"+prID+"/delete",
data: {
"type": "gitee"
},
success: function() {
successToast('操作成功')
},
error: function(e) {
errorToast($.parseJSON(e.responseText).message)
}
});
}//end of if
});
}

143
controller/prs_c.go Normal file
View File

@ -0,0 +1,143 @@
// Copyright (c) [2022] [巴拉迪维 BaratSemet]
// [ohUrlShortener] is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
package controller
import (
"log"
"net/http"
gitee_model "repostats/model/gitee"
"repostats/storage"
gitee_storage "repostats/storage/gitee"
"repostats/utils"
"strconv"
"strings"
"github.com/gin-gonic/gin"
)
// 转向到 Pull Request 列表页面
//
func PRsPage(ctx *gin.Context) {
strPage := ctx.DefaultQuery("page", strconv.Itoa(storage.DEFAULT_PAGE_NUMBER))
strSize := ctx.DefaultQuery("size", strconv.Itoa(storage.DEFAULT_PAGE_SIZE))
strPRID := ctx.DefaultQuery("id", "")
page, err := strconv.Atoi(strPage)
if err != nil || page < storage.DEFAULT_PAGE_NUMBER {
page = storage.DEFAULT_PAGE_NUMBER
}
size, err := strconv.Atoi(strSize)
if err != nil || size < storage.DEFAULT_PAGE_SIZE || size > storage.DEFAULT_MAX_PAGE_SIZE {
size = storage.DEFAULT_PAGE_SIZE
}
prID, err := strconv.Atoi(strPRID)
if err != nil {
prID = 0
}
var count int
var prs []gitee_model.PullRequest
if prID <= 0 {
count, err = gitee_storage.FindTotalPRsCount()
prs, err = gitee_storage.FindPagedPRs(page, size)
if err != nil {
log.Printf("error %s", err)
ctx.HTML(http.StatusOK, "prs.html", gin.H{
"title": "Pull Request - RepoStats",
"current_url": ctx.Request.URL.Path,
"error": "内部错误,请联系管理员",
})
return
}
} else {
pr, errr := gitee_storage.FindPRByID(prID)
if errr != nil {
log.Printf("error %s", err)
ctx.HTML(http.StatusOK, "prs.html", gin.H{
"title": "Pull Request - RepoStats",
"current_url": ctx.Request.URL.Path,
"error": "内部错误,请联系管理员",
})
return
}
count = 1
prs = append(prs, pr)
}
ctx.HTML(http.StatusOK, "prs.html", gin.H{
"title": "Pull Request - RepoStats",
"current_url": ctx.Request.URL.Path,
"prs": prs,
"total_item": count,
"current_page": page,
"page_size": size,
"first_page": page == 1,
"last_page": page >= (count/size)+1,
})
}
// 删除 commit
//
func PRDelete(ctx *gin.Context) {
strID := ctx.Param("id")
strType := ctx.DefaultPostForm("type", "gitee")
if utils.EmptyString(strID) {
ctx.JSON(http.StatusBadRequest, gin.H{
"message": "PR ID 参数非法",
})
return
}
if !strings.EqualFold(strings.ToLower(strType), "gitee") {
ctx.JSON(http.StatusBadRequest, gin.H{
"message": "type 参数非法",
})
return
}
prID, err := strconv.Atoi(strID)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"message": "PR ID 参数非法",
})
return
}
found, err := gitee_storage.FindPRByID(prID)
if err != nil {
log.Println(err)
ctx.JSON(http.StatusBadRequest, gin.H{
"message": "内部错误,请联系管理员! ",
})
return
}
if found.IsNilOrEmpty() {
ctx.JSON(http.StatusBadRequest, gin.H{
"message": "找不到指定的 PullRequest, ID=" + strID,
})
return
}
err = gitee_storage.DeletePR(prID)
if err != nil {
log.Println(err)
ctx.JSON(http.StatusBadRequest, gin.H{
"message": "内部错误,请联系管理员! ",
})
return
}
ctx.JSON(http.StatusOK, nil)
}

View File

@ -38,6 +38,6 @@ type PullRequest struct {
} `json:"head" db:"head"`
}
func (pr PullRequest) isNilOrEmpty() bool {
func (pr PullRequest) IsNilOrEmpty() bool {
return reflect.DeepEqual(pr, PullRequest{})
}

View File

@ -80,13 +80,16 @@ func initRouter(router *gin.Engine) {
admin.GET("/schedule", controller.SchedulePage)
admin.GET("/gitee", controller.GiteePage)
admin.GET("/commits", controller.CommitsPage)
admin.POST("/commits/:sha/delete", controller.CommitDelete)
admin.GET("/repos", controller.ReposPage)
admin.PUT("/repos/:repoID/change_state", controller.RepoStateChange)
admin.POST("/repos/:repoID/delete", controller.RepoDelete)
admin.GET("/commits", controller.CommitsPage)
admin.POST("/commits/:sha/delete", controller.CommitDelete)
admin.GET("/prs", controller.PRsPage)
admin.POST("/prs/:id/delete", controller.PRDelete)
admin.GET("/grafana", controller.GrafanaPage)
public := router.Group("/admin") //Same url path with /admin WITHOUT auth handler

View File

@ -10,6 +10,7 @@ package gitee
import (
gitee_mode "repostats/model/gitee"
gitee_model "repostats/model/gitee"
"repostats/storage"
)
@ -29,6 +30,22 @@ func BulkSavePullRequests(prs []gitee_mode.PullRequest) error {
return storage.DbNamedExec(query, prs)
}
func FindTotalPRsCount() (int, error) {
var count int
query := `SELECT count(pr.id) FROM gitee.pull_requests pr`
return count, storage.DbGet(query, &count)
}
func FindPagedPRs(page, size int) ([]gitee_mode.PullRequest, error) {
if page < 1 {
page = 1
}
prs := []gitee_model.PullRequest{}
query := prQueryPrefix + ` ORDER BY pr.created_at DESC LIMIT $1 OFFSET $2`
offset := (page - 1) * size
return prs, storage.DbSelect(query, &prs, size, offset)
}
func FindPRByID(prID int) (gitee_mode.PullRequest, error) {
found := gitee_mode.PullRequest{}
err := storage.DbGet(prQueryPrefix+` WHERE pr.id = $1`, &found, prID)
@ -37,13 +54,13 @@ func FindPRByID(prID int) (gitee_mode.PullRequest, error) {
func FindPRs() ([]gitee_mode.PullRequest, error) {
found := []gitee_mode.PullRequest{}
err := storage.DbSelect(prQueryPrefix+` ORDER BY pr.updated_at DESC`, &found)
err := storage.DbSelect(prQueryPrefix+` ORDER BY pr.created_at DESC`, &found)
return found, err
}
func FindPRsByRepoID(repoID int) ([]gitee_mode.PullRequest, error) {
found := []gitee_mode.PullRequest{}
err := storage.DbSelect(prQueryPrefix+` WHERE pr.repo_id = $1 ORDER BY pr.updated_at DESC`, &found, repoID)
err := storage.DbSelect(prQueryPrefix+` WHERE pr.repo_id = $1 ORDER BY pr.created_at DESC`, &found, repoID)
return found, err
}

70
templates/prs.html Normal file
View File

@ -0,0 +1,70 @@
{{define "prs.html" -}}
{{template "header.html" .}}
{{template "admin-left-menu.html" .}}
<div id="admin-right-content">
<div class="ui basic segment">
<h3 class="ui header">Pull Request 列表</h3>
<div class="ui grid stackable padded">
<div class="column" style="max-width: 100%; overflow-x: auto;">
<table class="ui celled striped small table">
<thead>
<tr>
<th colspan="11">
<p>共计 <span style="color: red;">{{.total_item}}</span> 条记录,当前 <span style="color: red;">{{.current_page}} </span> / <span style="color: red;">{{add (div .total_item .page_size) 1}}</span></p>
</th>
</tr>
<tr>
<th class="center aligned collapsing">序号</th>
<th class="center aligned collapsing">PR编号</th>
<th class="center aligned collapsing">仓库编号</th>
<th>标题</th>
<th class="center aligned collapsing">状态</th>
<th class="center aligned collapsing">创建时间</th>
<th class="center aligned collapsing">更新时间</th>
<th class="center aligned collapsing">关闭时间</th>
<th class="center aligned collapsing">可合并</th>
<th class="center aligned collapsing">合并时间</th>
<th class="center aligned">操作</th>
</tr>
</thead>
<tbody>
{{range $i,$pr := .prs}}
{{if eq "closed" $pr.State}}<tr class="negative">{{else if eq "merged" $pr.State}}<tr class="positive">{{else}}<tr>{{end}}
<td class="center aligned collapsing">{{add $i 1}}</td>
<td class="center aligned collapsing">{{$pr.ID}}</td>
<td class="center aligned collapsing"><a target="_blank" href="/admin/repos?id={{$pr.RepoID}}">{{$pr.RepoID}}</a></td>
<td><a href="{{$pr.HTMLURL}}" target="_blank">{{$pr.Title}}</a></td>
<td class="center aligned collapsing">{{$pr.State}}</td>
<td class="center aligned collapsing">{{$pr.CreatedAt | date "2006-01-02 15:04:05"}}</td>
<td class="center aligned collapsing">{{$pr.UpdatedAt | date "2006-01-02 15:04:05"}}</td>
<td class="center aligned collapsing">{{$pr.ClosedAt | date "2006-01-02 15:04:05"}}</td>
<td class="center aligned collapsing">{{$pr.Mergeable}}</td>
<td class="center aligned collapsing">{{$pr.MergedAt | date "2006-01-02 15:04:05"}}</td>
<td class="center aligned collapsing">
<a href="javascript:deletePR('{{$pr.ID}}')">删除</a>
</td>
</tr>
{{end}}
</tbody>
<tfoot>
{{if .total_item}}
<tr>
<td colspan="11" class="center aligned">
<div class="ui tiny pagination menu">
<a class="item" target="_self" href="{{.current_url}}?page=1&size={{.page_size}}">首页</a>
<a class="{{if .first_page}}disabled{{end}} item" target="_self" href="{{.current_url}}?page={{sub .current_page 1}}&size={{.page_size}}"><<</a>
<div class="active item">{{.current_page}}</div>
<a class="{{if .last_page}}disabled{{end}} item" target="_self" href="{{.current_url}}?page={{add .current_page 1}}&size={{.page_size}}">>></a>
<a class="item" target="_self" href="{{.current_url}}?page={{add (div .total_item .page_size) 1}}&size={{.page_size}}">末页</a>
</div>
</td>
</tr>
{{end}}
</tfoot>
</table>
</div>
</div><!--end fo grid-->
</div> <!--end of basic segment-->
</div><!--end of admin-right-content-->
{{template "footer.html" .}}
{{end -}}