feat:超管显示功能
This commit is contained in:
@@ -83,6 +83,9 @@ interface ApiService {
|
||||
@PUT("user/avatar")
|
||||
suspend fun updateAvatar(@Body request: Map<String, String>): Response<MessageResponse>
|
||||
|
||||
@GET("user/{id}")
|
||||
suspend fun getUserInfo(@Path("id") userId: Long): Response<UserInfoResponse>
|
||||
|
||||
// Search
|
||||
@GET("search")
|
||||
suspend fun search(
|
||||
|
||||
@@ -11,6 +11,7 @@ data class User(
|
||||
@SerialName("avatar_url") val avatarUrl: String = "",
|
||||
val bio: String = "",
|
||||
@SerialName("is_admin") val isAdmin: Boolean = false,
|
||||
@SerialName("is_superadmin") val isSuperAdmin: Boolean = false,
|
||||
@SerialName("created_at") val createdAt: String = ""
|
||||
)
|
||||
|
||||
@@ -137,6 +138,26 @@ data class ProfileResponse(
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class UserInfoResponse(
|
||||
val user: User,
|
||||
@SerialName("post_count") val postCount: Int,
|
||||
@SerialName("like_count") val likeCount: Int
|
||||
) {
|
||||
fun getDaysSinceRegistration(): Int {
|
||||
return try {
|
||||
val createdAt = user.createdAt
|
||||
if (createdAt.isEmpty()) return 0
|
||||
val formatter = java.time.format.DateTimeFormatter.ISO_DATE_TIME
|
||||
val createdDate = java.time.LocalDateTime.parse(createdAt.replace(" ", "T").substringBefore("+").substringBefore("Z"), formatter).toLocalDate()
|
||||
val today = java.time.LocalDate.now()
|
||||
java.time.temporal.ChronoUnit.DAYS.between(createdDate, today).toInt() + 1
|
||||
} catch (e: Exception) {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class IdResponse(
|
||||
val id: Long
|
||||
|
||||
@@ -18,9 +18,17 @@ import androidx.compose.material.icons.outlined.Close
|
||||
import androidx.compose.material.icons.outlined.Edit
|
||||
import androidx.compose.material.icons.outlined.FavoriteBorder
|
||||
import androidx.compose.material.icons.outlined.Mood
|
||||
import androidx.compose.material.icons.outlined.Article
|
||||
import androidx.compose.material.icons.outlined.Favorite
|
||||
import androidx.compose.material.icons.outlined.CalendarMonth
|
||||
import androidx.compose.material.icons.filled.Favorite
|
||||
import androidx.compose.material.icons.filled.Verified
|
||||
import androidx.compose.material.icons.filled.Shield
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||
@@ -29,6 +37,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -38,9 +47,12 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import coil.compose.AsyncImage
|
||||
import com.memory.app.data.api.ApiClient
|
||||
import com.memory.app.data.model.Post
|
||||
import com.memory.app.data.model.UserInfoResponse
|
||||
import com.memory.app.ui.theme.*
|
||||
import com.memory.app.util.TimeUtils
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun PostCard(
|
||||
@@ -55,6 +67,7 @@ fun PostCard(
|
||||
) {
|
||||
var showEmojiPicker by remember { mutableStateOf(false) }
|
||||
var showImageViewerIndex by remember { mutableStateOf<Int?>(null) }
|
||||
var showUserInfo by remember { mutableStateOf(false) }
|
||||
val canEdit = currentUserId > 0 && post.userId == currentUserId
|
||||
|
||||
Column(
|
||||
@@ -65,12 +78,15 @@ fun PostCard(
|
||||
.padding(horizontal = 20.dp, vertical = 20.dp)
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
// Avatar
|
||||
// Avatar - 点击显示用户信息
|
||||
val isSuperAdmin = post.user?.isSuperAdmin == true
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant),
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
.clickable { showUserInfo = true },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (post.user?.avatarUrl?.isNotEmpty() == true) {
|
||||
@@ -101,14 +117,19 @@ fun PostCard(
|
||||
fontSize = 15.sp,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
// 认证徽章
|
||||
// 超管/认证徽章
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Verified,
|
||||
contentDescription = "已认证",
|
||||
tint = Brand500,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
if (isSuperAdmin) {
|
||||
// 超管专属徽章 - 彩虹渐变盾牌
|
||||
RainbowShieldBadge(size = 18.dp)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Verified,
|
||||
contentDescription = "已认证",
|
||||
tint = Brand500,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = " · ${TimeUtils.formatRelative(post.createdAt)}",
|
||||
fontSize = 12.sp,
|
||||
@@ -246,6 +267,14 @@ fun PostCard(
|
||||
onDismiss = { showImageViewerIndex = null }
|
||||
)
|
||||
}
|
||||
|
||||
// User Info Dialog
|
||||
if (showUserInfo && post.user != null) {
|
||||
UserInfoDialog(
|
||||
userId = post.userId,
|
||||
onDismiss = { showUserInfo = false }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -381,6 +410,334 @@ fun EmojiPickerDialog(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserInfoDialog(
|
||||
userId: Long,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
var userInfo by remember { mutableStateOf<UserInfoResponse?>(null) }
|
||||
var isLoading by remember { mutableStateOf(true) }
|
||||
var error by remember { mutableStateOf<String?>(null) }
|
||||
var showAvatarViewer by remember { mutableStateOf(false) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(userId) {
|
||||
scope.launch {
|
||||
try {
|
||||
val response = ApiClient.api.getUserInfo(userId)
|
||||
if (response.isSuccessful) {
|
||||
userInfo = response.body()
|
||||
} else {
|
||||
error = "加载失败"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
error = "网络错误"
|
||||
} finally {
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dialog(onDismissRequest = onDismiss) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(24.dp),
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
tonalElevation = 2.dp,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
when {
|
||||
isLoading -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(56.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(28.dp),
|
||||
color = Brand500,
|
||||
strokeWidth = 2.5.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
error != null -> {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(32.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = error!!,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text("关闭", color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
}
|
||||
}
|
||||
}
|
||||
userInfo != null -> {
|
||||
val info = userInfo!!
|
||||
val isSuperAdmin = info.user.isSuperAdmin
|
||||
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
// 关闭按钮
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
IconButton(
|
||||
onClick = onDismiss,
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.size(32.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Close,
|
||||
contentDescription = "关闭",
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 头像和基本信息
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp)
|
||||
.padding(bottom = 20.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
// 头像 - 可点击放大
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(72.dp)
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
.clickable {
|
||||
if (info.user.avatarUrl.isNotEmpty()) {
|
||||
showAvatarViewer = true
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (info.user.avatarUrl.isNotEmpty()) {
|
||||
AsyncImage(
|
||||
model = info.user.avatarUrl,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = info.user.nickname.firstOrNull()?.toString() ?: "?",
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 26.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(14.dp))
|
||||
|
||||
// 用户名和徽章
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = info.user.nickname.ifEmpty { info.user.username },
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 18.sp,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
if (isSuperAdmin) {
|
||||
RainbowShieldBadge(size = 20.dp)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Verified,
|
||||
contentDescription = null,
|
||||
tint = Brand500,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
|
||||
Text(
|
||||
text = "@${info.user.username}",
|
||||
fontSize = 13.sp,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
||||
// 简介
|
||||
if (info.user.bio.isNotEmpty()) {
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
Text(
|
||||
text = info.user.bio,
|
||||
fontSize = 13.sp,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = 18.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 统计数据卡片
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(bottom = 20.dp),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 16.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
UserStatItem(
|
||||
value = info.postCount.toString(),
|
||||
label = "帖子"
|
||||
)
|
||||
// 分隔线
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(1.dp)
|
||||
.height(32.dp)
|
||||
.background(MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.3f))
|
||||
)
|
||||
UserStatItem(
|
||||
value = info.likeCount.toString(),
|
||||
label = "获赞"
|
||||
)
|
||||
// 分隔线
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(1.dp)
|
||||
.height(32.dp)
|
||||
.background(MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.3f))
|
||||
)
|
||||
UserStatItem(
|
||||
value = "${info.getDaysSinceRegistration()}",
|
||||
label = "天"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 头像放大查看
|
||||
if (showAvatarViewer && userInfo?.user?.avatarUrl?.isNotEmpty() == true) {
|
||||
Dialog(
|
||||
onDismissRequest = { showAvatarViewer = false },
|
||||
properties = DialogProperties(usePlatformDefaultWidth = false)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black.copy(alpha = 0.9f))
|
||||
.clickable { showAvatarViewer = false },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
AsyncImage(
|
||||
model = userInfo!!.user.avatarUrl,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(0.85f)
|
||||
.aspectRatio(1f)
|
||||
.clip(RoundedCornerShape(16.dp)),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
|
||||
// 关闭按钮
|
||||
IconButton(
|
||||
onClick = { showAvatarViewer = false },
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(16.dp)
|
||||
.size(40.dp)
|
||||
.background(Color.Black.copy(alpha = 0.5f), CircleShape)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Close,
|
||||
contentDescription = "关闭",
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UserStatItem(
|
||||
value: String,
|
||||
label: String
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(
|
||||
text = value,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 17.sp,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = 12.sp,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 彩虹渐变盾牌徽章
|
||||
@Composable
|
||||
fun RainbowShieldBadge(
|
||||
size: androidx.compose.ui.unit.Dp = 18.dp,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val rainbowColors = listOf(
|
||||
Color(0xFFFF6B6B), // 红
|
||||
Color(0xFFFF9F43), // 橙
|
||||
Color(0xFFFECA57), // 黄
|
||||
Color(0xFF5CD85A), // 绿
|
||||
Color(0xFF54A0FF), // 蓝
|
||||
Color(0xFF9B59B6), // 紫
|
||||
Color(0xFFFF6B6B) // 红(循环)
|
||||
)
|
||||
val rainbowBrush = Brush.sweepGradient(rainbowColors)
|
||||
|
||||
Box(
|
||||
modifier = modifier.size(size),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
// 彩虹渐变背景圆环
|
||||
Canvas(modifier = Modifier.size(size)) {
|
||||
drawCircle(
|
||||
brush = rainbowBrush,
|
||||
radius = size.toPx() / 2 - 1.dp.toPx(),
|
||||
style = Stroke(width = 2.dp.toPx())
|
||||
)
|
||||
}
|
||||
// 盾牌图标
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Shield,
|
||||
contentDescription = "超级管理员",
|
||||
tint = Color(0xFF54A0FF),
|
||||
modifier = Modifier.size(size * 0.7f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EditPostDialog(
|
||||
post: Post,
|
||||
|
||||
@@ -80,7 +80,7 @@ func (h *PostHandler) List(c *gin.Context) {
|
||||
// 超管:看到所有帖子
|
||||
rows, err = h.db.Query(`
|
||||
SELECT p.id, p.user_id, p.content, p.created_at, p.updated_at,
|
||||
u.id, u.username, u.nickname, u.avatar_url,
|
||||
u.id, u.username, u.nickname, u.avatar_url, COALESCE(u.is_superadmin, 0),
|
||||
(SELECT COUNT(*) FROM likes WHERE post_id = p.id) as like_count,
|
||||
(SELECT COUNT(*) FROM likes WHERE post_id = p.id AND user_id = ?) as liked,
|
||||
(SELECT COUNT(*) FROM comments WHERE post_id = p.id) as comment_count
|
||||
@@ -93,7 +93,7 @@ func (h *PostHandler) List(c *gin.Context) {
|
||||
// 普通用户:只能看到非超管的帖子
|
||||
rows, err = h.db.Query(`
|
||||
SELECT p.id, p.user_id, p.content, p.created_at, p.updated_at,
|
||||
u.id, u.username, u.nickname, u.avatar_url,
|
||||
u.id, u.username, u.nickname, u.avatar_url, COALESCE(u.is_superadmin, 0),
|
||||
(SELECT COUNT(*) FROM likes WHERE post_id = p.id) as like_count,
|
||||
(SELECT COUNT(*) FROM likes WHERE post_id = p.id AND user_id = ?) as liked,
|
||||
(SELECT COUNT(*) FROM comments WHERE post_id = p.id) as comment_count
|
||||
@@ -119,7 +119,7 @@ func (h *PostHandler) List(c *gin.Context) {
|
||||
var updatedAt sql.NullTime
|
||||
err := rows.Scan(
|
||||
&post.ID, &post.UserID, &post.Content, &post.CreatedAt, &updatedAt,
|
||||
&user.ID, &user.Username, &user.Nickname, &user.AvatarURL,
|
||||
&user.ID, &user.Username, &user.Nickname, &user.AvatarURL, &user.IsSuperAdmin,
|
||||
&post.LikeCount, &liked, &post.CommentCount,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -153,7 +153,7 @@ func (h *PostHandler) Get(c *gin.Context) {
|
||||
var updatedAt sql.NullTime
|
||||
err := h.db.QueryRow(`
|
||||
SELECT p.id, p.user_id, p.content, p.created_at, p.updated_at,
|
||||
u.id, u.username, u.nickname, u.avatar_url,
|
||||
u.id, u.username, u.nickname, u.avatar_url, COALESCE(u.is_superadmin, 0),
|
||||
(SELECT COUNT(*) FROM likes WHERE post_id = p.id) as like_count,
|
||||
(SELECT COUNT(*) FROM likes WHERE post_id = p.id AND user_id = ?) as liked,
|
||||
(SELECT COUNT(*) FROM comments WHERE post_id = p.id) as comment_count
|
||||
@@ -162,7 +162,7 @@ func (h *PostHandler) Get(c *gin.Context) {
|
||||
WHERE p.id = ?
|
||||
`, userID, postID).Scan(
|
||||
&post.ID, &post.UserID, &post.Content, &post.CreatedAt, &updatedAt,
|
||||
&user.ID, &user.Username, &user.Nickname, &user.AvatarURL,
|
||||
&user.ID, &user.Username, &user.Nickname, &user.AvatarURL, &user.IsSuperAdmin,
|
||||
&post.LikeCount, &liked, &post.CommentCount,
|
||||
)
|
||||
|
||||
|
||||
@@ -88,6 +88,37 @@ func (h *UserHandler) UpdateAvatar(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "avatar updated"})
|
||||
}
|
||||
|
||||
// 获取指定用户的公开信息
|
||||
func (h *UserHandler) GetUserInfo(c *gin.Context) {
|
||||
targetUserID := c.Param("id")
|
||||
|
||||
var user model.User
|
||||
err := h.db.QueryRow(`
|
||||
SELECT id, username, nickname, avatar_url, bio, COALESCE(is_superadmin, 0), created_at
|
||||
FROM users WHERE id = ?
|
||||
`, targetUserID).Scan(&user.ID, &user.Username, &user.Nickname, &user.AvatarURL, &user.Bio, &user.IsSuperAdmin, &user.CreatedAt)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "database error"})
|
||||
return
|
||||
}
|
||||
|
||||
// 获取统计数据
|
||||
var postCount, likeCount int
|
||||
h.db.QueryRow("SELECT COUNT(*) FROM posts WHERE user_id = ?", targetUserID).Scan(&postCount)
|
||||
h.db.QueryRow("SELECT COUNT(*) FROM likes l JOIN posts p ON l.post_id = p.id WHERE p.user_id = ?", targetUserID).Scan(&likeCount)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"user": user,
|
||||
"post_count": postCount,
|
||||
"like_count": likeCount,
|
||||
})
|
||||
}
|
||||
|
||||
// 管理员接口
|
||||
func (h *UserHandler) GetSettings(c *gin.Context) {
|
||||
rows, err := h.db.Query("SELECT key, value FROM settings")
|
||||
|
||||
@@ -71,6 +71,7 @@ func Setup(db *sql.DB, cfg *config.Config) *gin.Engine {
|
||||
auth.GET("/user/profile", userHandler.GetProfile)
|
||||
auth.PUT("/user/profile", userHandler.UpdateProfile)
|
||||
auth.PUT("/user/avatar", userHandler.UpdateAvatar)
|
||||
auth.GET("/user/:id", userHandler.GetUserInfo)
|
||||
|
||||
// 搜索
|
||||
auth.GET("/search", searchHandler.Search)
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user