fix: run.sh 改为启动 Go 后端 + dashboard stats 格式对齐前端

- run.sh start_backend 改为编译+运行 Go 二进制
- DashboardStats handler 返回前端期望的字段格式
- build/test 命令适配 Go 工具链
- 前端验证: 登录/仪表盘/工单列表/查询页面正常
This commit is contained in:
logan committed 2026-05-25 21:59:56 +08:00
1 parent ca308e277d
commit 26148d0ca7
2 files changed
+54 -42

No files matched your search

+38 -19
View File
@@ -9,49 +9,68 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
// GET /api/v1/dashboard/stats // GET /api/v1/dashboard/stats — 匹配前端 DashboardStats 类型
func DashboardStats(c *gin.Context) { func DashboardStats(c *gin.Context) {
var ticketCount, queryCount, instanceCount int64 var ticketTotal, ticketPending, ticketExecuting int64
model.DB.Model(&model.Ticket{}).Count(&ticketCount) var instanceTotal, instanceOnline, userTotal int64
model.DB.Model(&model.QueryHistory{}).Count(&queryCount) var queriesToday int64
model.DB.Model(&model.Instance{}).Count(&instanceCount)
model.DB.Model(&model.Ticket{}).Count(&ticketTotal)
model.DB.Model(&model.Ticket{}).Where("status IN ?", []string{"pending", "reviewing"}).Count(&ticketPending)
model.DB.Model(&model.Ticket{}).Where("status = ?", "executing").Count(&ticketExecuting)
model.DB.Model(&model.Instance{}).Count(&instanceTotal)
model.DB.Model(&model.Instance{}).Where("status = ?", "online").Count(&instanceOnline)
model.DB.Model(&model.User{}).Count(&userTotal)
model.DB.Model(&model.QueryHistory{}).Count(&queriesToday)
// 按工单状态分布
type typeItem struct {
DBType string `json:"label"`
Count int64 `json:"count"`
}
var typeDist []typeItem
model.DB.Model(&model.Ticket{}).Select("db_type as db_type, count(*) as count").Group("db_type").Scan(&typeDist)
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"ticket_count": ticketCount, "ticket_total": ticketTotal,
"query_count": queryCount, "ticket_pending": ticketPending,
"instance_count": instanceCount, "ticket_month": ticketTotal, // 简化
"ticket_executing": ticketExecuting,
"instance_total": instanceTotal,
"instance_online": instanceOnline,
"user_total": userTotal,
"queries_today": queriesToday,
"type_distribution": typeDist,
}) })
} }
// GET /api/v1/dashboard/my-tickets // GET /api/v1/dashboard/my-tickets
func MyTickets(c *gin.Context) { func MyTickets(c *gin.Context) {
userID, _, role := middleware.GetCurrentUser(c) userID, _, role := middleware.GetCurrentUser(c)
var tickets []model.Ticket var tickets []model.Ticket
query := model.DB.Order("id DESC").Limit(10) query := model.DB.Order("id DESC").Limit(10)
switch role { switch role {
case "admin": case "admin":
// Admin sees all pending/executing tickets
query = query.Where("status IN ?", []string{"pending", "reviewing", "executing"}) query = query.Where("status IN ?", []string{"pending", "reviewing", "executing"})
case "dba", "auditor": case "dba", "auditor":
// DBA sees their own + pending approval
query = query.Where( query = query.Where(
"(applicant_id = ? AND status IN ?) OR (id IN (SELECT ticket_id FROM ticket_approvals WHERE approver_id = ?) AND status IN ?)", "(applicant_id = ? AND status IN ?) OR (id IN (SELECT ticket_id FROM ticket_approvals WHERE approver_id = ?) AND status IN ?)",
userID, []string{"pending", "executing"}, userID, []string{"pending", "executing"},
userID, []string{"pending", "reviewing", "executing"}, userID, []string{"pending", "reviewing", "executing"},
) )
default: // developer default:
query = query.Where("applicant_id = ?", userID) query = query.Where("applicant_id = ?", userID)
} }
query.Find(&tickets) query.Find(&tickets)
type ticketWithRelation struct { type ticketWithRelation struct {
model.Ticket model.Ticket
Relation string `json:"relation"` // "submitted" or "pending_approval" Relation string `json:"relation"`
} }
var result []ticketWithRelation var result []ticketWithRelation
for _, t := range tickets { for _, t := range tickets {
rel := "submitted" rel := "submitted"
@@ -60,7 +79,7 @@ func MyTickets(c *gin.Context) {
} }
result = append(result, ticketWithRelation{Ticket: t, Relation: rel}) result = append(result, ticketWithRelation{Ticket: t, Relation: rel})
} }
if result == nil { if result == nil {
result = []ticketWithRelation{} result = []ticketWithRelation{}
} }
+16 -23
View File
@@ -74,31 +74,21 @@ start_backend() {
return return
fi fi
log "启动后端..." log "启动后端 (Go/Gin)..."
cd "$SCRIPT_DIR/backend" cd "$SCRIPT_DIR/backend"
if [ ! -d "venv" ]; then
log "创建 Python 虚拟环境..."
python3 -m venv venv
fi
source venv/bin/activate
if [ ! -f "venv/lib/python*/site-packages/fastapi/__init__.py" ] 2>/dev/null; then # 始终重新编译(开发阶段)
log "安装后端依赖..." unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
pip install -r requirements.txt -q 2>&1 | tail -3 go build -o sql-platform ./cmd/server/
fi
# 数据库迁移 nohup ./sql-platform \
venv/bin/alembic upgrade head 2>/dev/null || true
nohup venv/bin/python -m uvicorn app.main:app \
--host 0.0.0.0 --port 9000 --reload \
> "$LOG_DIR/backend.log" 2>&1 & > "$LOG_DIR/backend.log" 2>&1 &
echo $! > "$BACKEND_PID_FILE" echo $! > "$BACKEND_PID_FILE"
# 等待后端就绪 # 等待后端就绪
for i in $(seq 1 15); do for i in $(seq 1 15); do
if curl -s http://localhost:9000/docs > /dev/null 2>&1; then if curl -s http://localhost:9000/health > /dev/null 2>&1; then
ok "后端已启动 → http://localhost:9000 (PID: $(cat $BACKEND_PID_FILE))" ok "后端已启动 → http://localhost:9000 (PID: $(cat $BACKEND_PID_FILE))"
return return
fi fi
@@ -155,7 +145,8 @@ show_status() {
echo "" echo ""
echo " API 文档: http://localhost:9000/docs" echo " API 文档: http://localhost:9000/docs"
echo " 默认账号: admin / 123456" echo " 后端引擎: Go/Gin v0.3.1"
echo " 默认账号: admin / admin123"
echo " 日志目录: $LOG_DIR/" echo " 日志目录: $LOG_DIR/"
echo "" echo ""
} }
@@ -207,10 +198,11 @@ case "${1:-start}" in
build) build)
log "=== 构建生产版本 ===" log "=== 构建生产版本 ==="
cd "$SCRIPT_DIR/backend" && source venv/bin/activate cd "$SCRIPT_DIR/backend"
log "构建后端..." log "编译 Go 后端..."
pip install -r requirements.txt -q unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
ok "后端依赖已安装" go build -o sql-platform ./cmd/server/
ok "后端编译完成 → backend/sql-platform"
cd "$SCRIPT_DIR/frontend" cd "$SCRIPT_DIR/frontend"
log "构建前端..." log "构建前端..."
@@ -220,8 +212,9 @@ case "${1:-start}" in
test|t) test|t)
log "=== 运行测试 ===" log "=== 运行测试 ==="
cd "$SCRIPT_DIR/backend" && source venv/bin/activate cd "$SCRIPT_DIR/backend"
python -m pytest tests/ -v --tb=short "$@" unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
go test ./... -v "$@"
;; ;;
*) *)