feat:增加封禁账号功能 & 退出时情况所有缓存

This commit is contained in:
amos
2026-01-06 17:49:59 +08:00
parent 85e441d5cc
commit b65f97f123
11 changed files with 130 additions and 7 deletions

View File

@@ -13,11 +13,11 @@ android {
applicationId = "com.memory.app"
minSdk = 26
targetSdk = 35
versionCode = 42
versionName = "1.5.8"
versionCode = 43
versionName = "1.5.9"
buildConfigField("String", "API_BASE_URL", "\"https://x.amos.us.kg/api/\"")
buildConfigField("int", "VERSION_CODE", "42")
buildConfigField("int", "VERSION_CODE", "43")
}
signingConfigs {

View File

@@ -74,6 +74,11 @@ fun MainNavigation(
val uploadProgress by homeViewModel.uploadProgress.collectAsState()
LaunchedEffect(user) {
// 用户变化时清空旧数据并重新加载
homeViewModel.clearData()
profileViewModel.clearData()
notificationViewModel.clearData()
homeViewModel.loadPosts()
profileViewModel.setUser(user)
profileViewModel.loadProfile()
notificationViewModel.loadUnreadCount()

View File

@@ -228,6 +228,13 @@ class HomeViewModel : ViewModel() {
loadPosts()
}
fun clearData() {
_posts.value = emptyList()
_currentUser.value = null
currentPage = 1
hasMore = true
}
fun createPost(context: Context, content: String, images: List<Uri>, visibility: Int = 0, musicUrl: String? = null) {
if (_isPosting.value) return
viewModelScope.launch {

View File

@@ -76,4 +76,9 @@ class NotificationViewModel : ViewModel() {
}
}
}
fun clearData() {
_notifications.value = emptyList()
_unreadCount.value = 0
}
}

View File

@@ -95,4 +95,11 @@ class ProfileViewModel : ViewModel() {
fun setUser(user: User?) {
_user.value = user
}
fun clearData() {
_user.value = null
_postCount.value = 0
_likeCount.value = 0
_dayCount.value = 1
}
}

View File

@@ -181,5 +181,8 @@ func migrate(db *sql.DB) error {
)
`)
// 迁移:添加 is_disabled 字段(账号禁用)
db.Exec("ALTER TABLE users ADD COLUMN is_disabled INTEGER DEFAULT 0")
return nil
}

View File

@@ -106,7 +106,7 @@ func (h *PostHandler) List(c *gin.Context) {
LIMIT ? OFFSET ?
`, userID, pageSize, offset)
} else {
// 普通用户:可以看到公开帖子(包括超管的公开帖子)+ 自己的私密帖子
// 普通用户:只能看到公开帖子 + 自己的私密帖子(不能看到别人的私密帖子)
rows, err = h.db.Query(`
SELECT p.id, p.user_id, p.content, p.created_at, p.updated_at, COALESCE(p.visibility, 0),
u.id, u.username, u.nickname, u.avatar_url, COALESCE(u.is_superadmin, 0),
@@ -115,7 +115,7 @@ func (h *PostHandler) List(c *gin.Context) {
(SELECT COUNT(*) FROM comments WHERE post_id = p.id) as comment_count
FROM posts p
JOIN users u ON p.user_id = u.id
WHERE COALESCE(p.visibility, 0) = 0 OR p.user_id = ?
WHERE (COALESCE(p.visibility, 0) = 0) OR (COALESCE(p.visibility, 0) = 1 AND p.user_id = ?)
ORDER BY p.created_at DESC
LIMIT ? OFFSET ?
`, userID, userID, pageSize, offset)

View File

@@ -2,6 +2,7 @@ package handler
import (
"database/sql"
"fmt"
"net/http"
"memory/internal/config"
@@ -155,3 +156,79 @@ func (h *UserHandler) UpdateSettings(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "settings updated"})
}
// 获取所有用户列表(管理员)
func (h *UserHandler) ListUsers(c *gin.Context) {
rows, err := h.db.Query(`
SELECT id, username, nickname, avatar_url, bio, is_admin, COALESCE(is_superadmin, 0), COALESCE(is_disabled, 0), created_at
FROM users ORDER BY created_at DESC
`)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "database error"})
return
}
defer rows.Close()
var users []model.User
for rows.Next() {
var user model.User
err := rows.Scan(&user.ID, &user.Username, &user.Nickname, &user.AvatarURL, &user.Bio, &user.IsAdmin, &user.IsSuperAdmin, &user.IsDisabled, &user.CreatedAt)
if err != nil {
continue
}
users = append(users, user)
}
c.JSON(http.StatusOK, gin.H{"users": users})
}
// 禁用用户账号
func (h *UserHandler) DisableUser(c *gin.Context) {
targetUserID := c.Param("id")
currentUserID := middleware.GetUserID(c)
// 不能禁用自己
if targetUserID == fmt.Sprintf("%d", currentUserID) {
c.JSON(http.StatusBadRequest, gin.H{"error": "不能禁用自己的账号"})
return
}
// 检查目标用户是否是超管
var isSuperAdmin bool
err := h.db.QueryRow("SELECT COALESCE(is_superadmin, 0) FROM users WHERE id = ?", targetUserID).Scan(&isSuperAdmin)
if err == sql.ErrNoRows {
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
return
}
if isSuperAdmin {
c.JSON(http.StatusForbidden, gin.H{"error": "不能禁用超级管理员"})
return
}
_, err = h.db.Exec("UPDATE users SET is_disabled = 1 WHERE id = ?", targetUserID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "禁用失败"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "账号已禁用"})
}
// 启用用户账号
func (h *UserHandler) EnableUser(c *gin.Context) {
targetUserID := c.Param("id")
result, err := h.db.Exec("UPDATE users SET is_disabled = 0 WHERE id = ?", targetUserID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "启用失败"})
return
}
rowsAffected, _ := result.RowsAffected()
if rowsAffected == 0 {
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "账号已启用"})
}

View File

@@ -1,6 +1,7 @@
package middleware
import (
"database/sql"
"net/http"
"strings"
@@ -15,7 +16,7 @@ type Claims struct {
jwt.RegisteredClaims
}
func AuthMiddleware(jwtSecret string) gin.HandlerFunc {
func AuthMiddleware(jwtSecret string, db *sql.DB) gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
@@ -48,6 +49,20 @@ func AuthMiddleware(jwtSecret string) gin.HandlerFunc {
return
}
// 检查账号是否被禁用
var isDisabled bool
err = db.QueryRow("SELECT COALESCE(is_disabled, 0) FROM users WHERE id = ?", claims.UserID).Scan(&isDisabled)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "user not found"})
c.Abort()
return
}
if isDisabled {
c.JSON(http.StatusForbidden, gin.H{"error": "account_disabled", "message": "您的账号已被禁用"})
c.Abort()
return
}
c.Set("user_id", claims.UserID)
c.Set("is_admin", claims.IsAdmin)
c.Set("is_superadmin", claims.IsSuperAdmin)

View File

@@ -11,6 +11,7 @@ type User struct {
Bio string `json:"bio"`
IsAdmin bool `json:"is_admin"`
IsSuperAdmin bool `json:"is_superadmin"`
IsDisabled bool `json:"is_disabled"`
CreatedAt time.Time `json:"created_at"`
}

View File

@@ -55,7 +55,7 @@ func Setup(db *sql.DB, cfg *config.Config) *gin.Engine {
// 需要认证的接口
auth := r.Group("/api")
auth.Use(middleware.AuthMiddleware(cfg.JWTSecret))
auth.Use(middleware.AuthMiddleware(cfg.JWTSecret, db))
{
// 帖子
auth.GET("/posts", postHandler.List)
@@ -102,6 +102,9 @@ func Setup(db *sql.DB, cfg *config.Config) *gin.Engine {
admin.GET("/settings", userHandler.GetSettings)
admin.PUT("/settings", userHandler.UpdateSettings)
admin.POST("/version", versionHandler.SetVersion)
admin.GET("/users", userHandler.ListUsers)
admin.PUT("/users/:id/disable", userHandler.DisableUser)
admin.PUT("/users/:id/enable", userHandler.EnableUser)
}
}