Initial commit: GovAI 政务AI平台
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/enterprise-ai-platform/server/internal/middleware"
|
||||
"github.com/enterprise-ai-platform/server/internal/response"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type FavoriteHandler struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewFavoriteHandler(pool *pgxpool.Pool) *FavoriteHandler {
|
||||
return &FavoriteHandler{pool: pool}
|
||||
}
|
||||
|
||||
func (h *FavoriteHandler) AddFavorite(w http.ResponseWriter, r *http.Request) {
|
||||
appID := chi.URLParam(r, "id")
|
||||
userID := middleware.GetUserID(r.Context())
|
||||
|
||||
_, err := h.pool.Exec(r.Context(),
|
||||
`INSERT INTO app_favorites (user_id, app_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`,
|
||||
userID, appID)
|
||||
if err != nil {
|
||||
response.InternalError(w, "收藏失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.pool.Exec(r.Context(),
|
||||
`UPDATE applications SET favorite_count = favorite_count + 1 WHERE id = $1`, appID)
|
||||
|
||||
response.JSON(w, http.StatusOK, map[string]bool{"favorited": true})
|
||||
}
|
||||
|
||||
func (h *FavoriteHandler) RemoveFavorite(w http.ResponseWriter, r *http.Request) {
|
||||
appID := chi.URLParam(r, "id")
|
||||
userID := middleware.GetUserID(r.Context())
|
||||
|
||||
tag, err := h.pool.Exec(r.Context(),
|
||||
`DELETE FROM app_favorites WHERE user_id = $1 AND app_id = $2`,
|
||||
userID, appID)
|
||||
if err != nil {
|
||||
response.InternalError(w, "取消收藏失败")
|
||||
return
|
||||
}
|
||||
|
||||
if tag.RowsAffected() > 0 {
|
||||
h.pool.Exec(r.Context(),
|
||||
`UPDATE applications SET favorite_count = GREATEST(favorite_count - 1, 0) WHERE id = $1`, appID)
|
||||
}
|
||||
|
||||
response.JSON(w, http.StatusOK, map[string]bool{"favorited": false})
|
||||
}
|
||||
|
||||
func (h *FavoriteHandler) ListFavorites(w http.ResponseWriter, r *http.Request) {
|
||||
userID := middleware.GetUserID(r.Context())
|
||||
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
offset := (page - 1) * 20
|
||||
|
||||
rows, err := h.pool.Query(r.Context(), `
|
||||
SELECT a.id, a.name, a.slug, a.description, a.icon_url,
|
||||
c.name as category_name, c.slug as category_slug,
|
||||
a.usage_count, a.avg_rating, a.rating_count
|
||||
FROM app_favorites f
|
||||
JOIN applications a ON f.app_id = a.id
|
||||
LEFT JOIN categories c ON a.category_id = c.id
|
||||
WHERE f.user_id = $1
|
||||
ORDER BY f.created_at DESC
|
||||
LIMIT 20 OFFSET $2`, userID, offset)
|
||||
if err != nil {
|
||||
response.InternalError(w, "查询收藏失败")
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
apps := scanAppList(rows)
|
||||
response.JSON(w, http.StatusOK, apps)
|
||||
}
|
||||
|
||||
// --- Rating ---
|
||||
|
||||
type ratingRequest struct {
|
||||
Score int `json:"score"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
func (h *FavoriteHandler) AddRating(w http.ResponseWriter, r *http.Request) {
|
||||
appID := chi.URLParam(r, "id")
|
||||
userID := middleware.GetUserID(r.Context())
|
||||
|
||||
var req ratingRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
response.BadRequest(w, "无效的请求格式")
|
||||
return
|
||||
}
|
||||
if req.Score < 1 || req.Score > 5 {
|
||||
response.BadRequest(w, "评分必须在1-5之间")
|
||||
return
|
||||
}
|
||||
|
||||
_, err := h.pool.Exec(r.Context(), `
|
||||
INSERT INTO app_ratings (app_id, user_id, score, comment)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (app_id, user_id)
|
||||
DO UPDATE SET score = EXCLUDED.score, comment = EXCLUDED.comment`,
|
||||
appID, userID, req.Score, req.Comment)
|
||||
if err != nil {
|
||||
response.InternalError(w, "评分失败")
|
||||
return
|
||||
}
|
||||
|
||||
// Update app avg rating
|
||||
var avgRating float32
|
||||
var ratingCount int
|
||||
h.pool.QueryRow(r.Context(),
|
||||
`SELECT COALESCE(AVG(score)::REAL, 0), COUNT(*) FROM app_ratings WHERE app_id = $1`,
|
||||
appID).Scan(&avgRating, &ratingCount)
|
||||
|
||||
h.pool.Exec(r.Context(),
|
||||
`UPDATE applications SET avg_rating = $2, rating_count = $3 WHERE id = $1`,
|
||||
appID, avgRating, ratingCount)
|
||||
|
||||
response.JSON(w, http.StatusOK, map[string]any{
|
||||
"avg_rating": avgRating,
|
||||
"rating_count": ratingCount,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *FavoriteHandler) ListRatings(w http.ResponseWriter, r *http.Request) {
|
||||
appID := chi.URLParam(r, "id")
|
||||
|
||||
rows, err := h.pool.Query(r.Context(), `
|
||||
SELECT r.id, r.score, r.comment, r.created_at,
|
||||
u.name as user_name, u.avatar_url
|
||||
FROM app_ratings r
|
||||
JOIN users u ON r.user_id = u.id
|
||||
WHERE r.app_id = $1
|
||||
ORDER BY r.created_at DESC LIMIT 50`, appID)
|
||||
if err != nil {
|
||||
response.InternalError(w, "查询评分失败")
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var ratings []map[string]any
|
||||
for rows.Next() {
|
||||
var id string
|
||||
var score int
|
||||
var comment *string
|
||||
var createdAt string
|
||||
var userName string
|
||||
var avatarURL *string
|
||||
if err := rows.Scan(&id, &score, &comment, &createdAt, &userName, &avatarURL); err != nil {
|
||||
continue
|
||||
}
|
||||
ratings = append(ratings, map[string]any{
|
||||
"id": id, "score": score, "comment": comment,
|
||||
"created_at": createdAt, "user_name": userName, "user_avatar": avatarURL,
|
||||
})
|
||||
}
|
||||
if ratings == nil {
|
||||
ratings = []map[string]any{}
|
||||
}
|
||||
response.JSON(w, http.StatusOK, ratings)
|
||||
}
|
||||
|
||||
func (h *FavoriteHandler) PersonalStats(w http.ResponseWriter, r *http.Request) {
|
||||
userID := middleware.GetUserID(r.Context())
|
||||
|
||||
var totalConversations, totalTokens, favoriteCount int
|
||||
h.pool.QueryRow(r.Context(),
|
||||
`SELECT COUNT(*) FROM app_usage_logs WHERE user_id = $1`, userID).Scan(&totalConversations)
|
||||
h.pool.QueryRow(r.Context(),
|
||||
`SELECT COALESCE(SUM(total_tokens), 0) FROM app_usage_logs WHERE user_id = $1`, userID).Scan(&totalTokens)
|
||||
h.pool.QueryRow(r.Context(),
|
||||
`SELECT COUNT(*) FROM app_favorites WHERE user_id = $1`, userID).Scan(&favoriteCount)
|
||||
|
||||
var recentApps []map[string]any
|
||||
rows, err := h.pool.Query(r.Context(), `
|
||||
SELECT DISTINCT ON (l.app_id) a.id, a.name, a.icon_url, l.created_at
|
||||
FROM app_usage_logs l
|
||||
JOIN applications a ON l.app_id = a.id
|
||||
WHERE l.user_id = $1
|
||||
ORDER BY l.app_id, l.created_at DESC
|
||||
LIMIT 5`, userID)
|
||||
if err == nil {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var id, name string
|
||||
var icon *string
|
||||
var at string
|
||||
if rows.Scan(&id, &name, &icon, &at) == nil {
|
||||
recentApps = append(recentApps, map[string]any{
|
||||
"id": id, "name": name, "icon_url": icon, "last_used": at,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if recentApps == nil {
|
||||
recentApps = []map[string]any{}
|
||||
}
|
||||
|
||||
response.JSON(w, http.StatusOK, map[string]any{
|
||||
"total_conversations": totalConversations,
|
||||
"total_tokens": totalTokens,
|
||||
"favorite_count": favoriteCount,
|
||||
"recent_apps": recentApps,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user