diff --git a/api/schemas/paths/users.yaml b/api/schemas/paths/users.yaml
index da741eb..897adad 100644
--- a/api/schemas/paths/users.yaml
+++ b/api/schemas/paths/users.yaml
@@ -216,23 +216,14 @@ user_me:
schema:
type: object
properties:
- username:
- type: string
- minLength: 3
- maxLength: 32
- display_name:
- type: string
- maxLength: 64
email:
type: string
format: email
current_password:
type: string
- description: 当修改密码时必填
new_password:
type: string
minLength: 8
- description: 新密码,如果要修改密码则必填
responses:
'200':
description: 用户信息更新成功
diff --git a/backend/internal/handler/user.go b/backend/internal/handler/user.go
index bd9f8fb..b96603b 100644
--- a/backend/internal/handler/user.go
+++ b/backend/internal/handler/user.go
@@ -11,7 +11,6 @@ import (
)
type UpdateCurrentUserRequest struct {
- Username string `json:"username,omitempty" binding:"omitempty,min=3,max=32"`
Email string `json:"email,omitempty" binding:"omitempty,email"`
CurrentPassword string `json:"current_password,omitempty"`
NewPassword string `json:"new_password,omitempty" binding:"omitempty,min=8"`
@@ -212,20 +211,12 @@ func (h *Handler) GetCurrentUser(c *gin.Context) {
// UpdateCurrentUser updates the current user's information
func (h *Handler) UpdateCurrentUser(c *gin.Context) {
// 从上下文中获取用户ID(由认证中间件设置)
- userIDStr, exists := c.Get("user_id")
+ userID, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
- // 将用户ID转换为整数
- userID, err := strconv.Atoi(userIDStr.(string))
- if err != nil {
- log.Error().Err(err).Msg("Failed to convert user ID to integer")
- c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID format"})
- return
- }
-
var req UpdateCurrentUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
@@ -240,25 +231,20 @@ func (h *Handler) UpdateCurrentUser(c *gin.Context) {
}
// 验证当前密码
- if err := h.service.VerifyPassword(c.Request.Context(), userID, req.CurrentPassword); err != nil {
+ if err := h.service.VerifyPassword(c.Request.Context(), userID.(int), req.CurrentPassword); err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid current password"})
return
}
}
// 更新用户信息
- user, err := h.service.UpdateUser(c.Request.Context(), userID, &types.UpdateUserInput{
- Username: req.Username,
+ user, err := h.service.UpdateUser(c.Request.Context(), userID.(int), &types.UpdateUserInput{
Email: req.Email,
Password: req.NewPassword,
DisplayName: req.DisplayName,
})
if err != nil {
log.Error().Err(err).Msg("Failed to update user")
- if err.Error() == "username already taken" {
- c.JSON(http.StatusConflict, gin.H{"error": "Username already taken"})
- return
- }
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update user information"})
return
}
diff --git a/backend/internal/service/user.go b/backend/internal/service/user.go
index 9d1d015..0ae658b 100644
--- a/backend/internal/service/user.go
+++ b/backend/internal/service/user.go
@@ -40,25 +40,6 @@ func (s *serviceImpl) UpdateUser(ctx context.Context, userID int, input *types.U
// Start building the update
update := s.client.User.UpdateOneID(userID)
- // Update username if provided
- if input.Username != "" {
- // Check if username is already taken
- exists, err := s.client.User.Query().
- Where(user.And(
- user.UsernameEQ(input.Username),
- user.IDNEQ(userID),
- )).
- Exist(ctx)
- if err != nil {
- log.Error().Err(err).Msg("Failed to check username availability")
- return nil, fmt.Errorf("failed to check username availability: %w", err)
- }
- if exists {
- return nil, fmt.Errorf("username already taken")
- }
- update.SetUsername(input.Username)
- }
-
// Update email if provided
if input.Email != "" {
update.SetEmail(input.Email)
diff --git a/backend/internal/types/user.go b/backend/internal/types/user.go
index 45a0b9e..1eef0b6 100644
--- a/backend/internal/types/user.go
+++ b/backend/internal/types/user.go
@@ -2,7 +2,6 @@ package types
// UpdateUserInput defines the input for updating a user
type UpdateUserInput struct {
- Username string
Email string
Password string
Role string
diff --git a/frontend/data/i18n/en.json b/frontend/data/i18n/en.json
index 06015e4..defb85f 100644
--- a/frontend/data/i18n/en.json
+++ b/frontend/data/i18n/en.json
@@ -14,23 +14,19 @@
"search": "Search"
},
"theme": {
- "light": "Light",
- "dark": "Dark",
- "system": "System"
+ "light": "Light Mode",
+ "dark": "Dark Mode",
+ "system": "System Mode"
},
"footer": {
"copyright": "TSS Rocks. All rights reserved."
},
"admin": {
"common": {
- "loading": "Loading...",
"search": "Search",
"create": "Create",
"edit": "Edit",
"delete": "Delete",
- "upload": "Upload",
- "save": "Save",
- "saving": "Saving...",
"status": "Status",
"actions": "Actions",
"published": "Published",
@@ -47,68 +43,19 @@
"lastLogin": "Last Login",
"joinDate": "Join Date",
"username": "Username",
- "logout": "Logout",
- "language": "Language",
- "theme": {
- "light": "Light Mode",
- "dark": "Dark Mode",
- "system": "System"
- }
- },
- "dashboard": {
- "totalPosts": "Total Posts",
- "totalCategories": "Total Categories",
- "totalUsers": "Total Users",
- "totalContributors": "Total Contributors"
- },
- "login": {
- "title": "Admin Login",
- "username": "Username",
- "password": "Password",
- "remember": "Remember me",
- "submit": "Sign in",
- "loading": "Signing in...",
- "error": {
- "failed": "Login failed",
- "retry": "Login failed, please try again later"
- }
+ "logout": "Logout"
},
"nav": {
"dashboard": "Dashboard",
"posts": "Posts",
- "daily": "Daily Quotes",
- "medias": "Media",
"categories": "Categories",
"users": "Users",
- "contributors": "Contributors",
- "settings": "Settings"
- },
- "daily": {
- "title": "Title",
- "publishDate": "Publish Date",
- "status": "Status"
- },
- "medias": {
- "name": "File Name",
- "type": "Type",
- "size": "Size",
- "uploadDate": "Upload Date"
+ "contributors": "Contributors"
},
"roles": {
"admin": "Administrator",
"user": "User",
"contributor": "Contributor"
- },
- "settings": {
- "username": "Username",
- "displayName": "Display Name",
- "email": "Email",
- "changePassword": "Change Password",
- "currentPassword": "Current Password",
- "newPassword": "New Password",
- "confirmPassword": "Confirm Password",
- "passwordMismatch": "Passwords do not match",
- "passwordTooShort": "Password must be at least 8 characters long"
}
}
}
diff --git a/frontend/data/i18n/zh-Hans.json b/frontend/data/i18n/zh-Hans.json
index 296d279..749c864 100644
--- a/frontend/data/i18n/zh-Hans.json
+++ b/frontend/data/i18n/zh-Hans.json
@@ -14,8 +14,8 @@
"search": "搜索"
},
"theme": {
- "light": "浅色",
- "dark": "深色",
+ "light": "浅色模式",
+ "dark": "深色模式",
"system": "跟随系统"
},
"footer": {
@@ -23,12 +23,10 @@
},
"admin": {
"common": {
- "loading": "正在加载...",
"search": "搜索",
"create": "新建",
"edit": "编辑",
"delete": "删除",
- "upload": "上传",
"status": "状态",
"actions": "操作",
"published": "已发布",
@@ -45,64 +43,19 @@
"lastLogin": "最后登录",
"joinDate": "加入时间",
"username": "用户名",
- "logout": "退出登录",
- "save": "保存",
- "saving": "保存中..."
- },
- "dashboard": {
- "totalPosts": "文章总数",
- "totalCategories": "分类总数",
- "totalUsers": "用户总数",
- "totalContributors": "贡献者总数"
- },
- "login": {
- "title": "管理员登录",
- "username": "用户名",
- "password": "密码",
- "remember": "记住我",
- "submit": "登录",
- "loading": "登录中...",
- "error": {
- "failed": "登录失败",
- "retry": "登录失败,请稍后重试"
- }
+ "logout": "退出"
},
"nav": {
- "dashboard": "仪表板",
- "posts": "文章管理",
- "daily": "每日一句",
- "medias": "媒体管理",
- "categories": "分类管理",
- "users": "用户管理",
- "contributors": "贡献者管理",
- "settings": "个人设置"
- },
- "daily": {
- "title": "标题",
- "publishDate": "发布日期",
- "status": "状态"
- },
- "medias": {
- "name": "文件名",
- "type": "类型",
- "size": "大小",
- "uploadDate": "上传日期"
+ "dashboard": "仪表盘",
+ "posts": "文章",
+ "categories": "分类",
+ "users": "用户",
+ "contributors": "作者"
},
"roles": {
"admin": "管理员",
"user": "普通用户",
"contributor": "作者"
- },
- "settings": {
- "username": "用户名",
- "displayName": "显示名称",
- "email": "邮箱",
- "changePassword": "修改密码",
- "currentPassword": "当前密码",
- "newPassword": "新密码",
- "confirmPassword": "确认密码",
- "passwordMismatch": "两次输入的密码不一致",
- "passwordTooShort": "密码长度不能少于8个字符"
}
}
}
diff --git a/frontend/data/i18n/zh-Hant.json b/frontend/data/i18n/zh-Hant.json
index 7a89534..6377964 100644
--- a/frontend/data/i18n/zh-Hant.json
+++ b/frontend/data/i18n/zh-Hant.json
@@ -14,8 +14,8 @@
"search": "搜尋"
},
"theme": {
- "light": "淺色",
- "dark": "深色",
+ "light": "淺色模式",
+ "dark": "深色模式",
"system": "跟隨系統"
},
"footer": {
@@ -23,12 +23,10 @@
},
"admin": {
"common": {
- "loading": "正在載入...",
"search": "搜尋",
"create": "新建",
"edit": "編輯",
"delete": "刪除",
- "upload": "上傳",
"status": "狀態",
"actions": "操作",
"published": "已發布",
@@ -45,70 +43,19 @@
"lastLogin": "最後登入",
"joinDate": "加入時間",
"username": "用戶名",
- "logout": "退出登錄",
- "language": "語言",
- "theme": {
- "light": "淺色模式",
- "dark": "深色模式",
- "system": "跟隨系統"
- },
- "save": "保存",
- "saving": "保存中..."
+ "logout": "退出"
},
"nav": {
"dashboard": "儀表板",
- "posts": "文章管理",
- "daily": "每日一句",
- "medias": "媒體管理",
- "categories": "分類管理",
- "users": "用戶管理",
- "contributors": "貢獻者管理",
- "settings": "個人設置"
- },
- "dashboard": {
- "totalPosts": "文章總數",
- "totalCategories": "分類總數",
- "totalUsers": "用戶總數",
- "totalContributors": "貢獻者總數"
- },
- "daily": {
- "title": "標題",
- "publishDate": "發布日期",
- "status": "狀態"
- },
- "medias": {
- "name": "檔案名",
- "type": "類型",
- "size": "大小",
- "uploadDate": "上傳日期"
- },
- "login": {
- "title": "管理員登錄",
- "username": "用戶名",
- "password": "密碼",
- "remember": "記住我",
- "submit": "登錄",
- "loading": "登錄中...",
- "error": {
- "failed": "登錄失敗",
- "retry": "登錄失敗,請稍後重試"
- }
+ "posts": "文章",
+ "categories": "分類",
+ "users": "用戶",
+ "contributors": "作者"
},
"roles": {
"admin": "管理員",
"user": "普通用戶",
"contributor": "作者"
- },
- "settings": {
- "username": "用戶名",
- "displayName": "顯示名稱",
- "email": "郵箱",
- "changePassword": "修改密碼",
- "currentPassword": "當前密碼",
- "newPassword": "新密碼",
- "confirmPassword": "確認密碼",
- "passwordMismatch": "兩次輸入的密碼不一致",
- "passwordTooShort": "密碼長度不能少於8個字符"
}
}
}
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 9fa788f..c5f2ca5 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,19 +1,63 @@
-import { RouterProvider } from 'react-router-dom';
-import { Suspense } from 'react';
+import React from 'react';
+import { BrowserRouter, Routes, Route } from 'react-router-dom';
+import { Header } from './components/layout/Header';
+import { Suspense, lazy } from 'react';
+import Footer from './components/Footer';
import { AuthProvider } from './contexts/AuthContext';
-import { UserProvider } from './contexts/UserContext';
-import router from './router';
-import LoadingSpinner from './components/LoadingSpinner';
+
+// Lazy load pages
+const Home = lazy(() => import('./pages/Home'));
+const Daily = lazy(() => import('./pages/Daily'));
+const Article = lazy(() => import('./pages/Article'));
+const AdminLayout = lazy(() => import('./pages/admin/layout/AdminLayout'));
+
+// 管理页面懒加载
+const PostsManagement = lazy(() => import('./pages/admin/posts/PostsManagement'));
+const CategoriesManagement = lazy(() => import('./pages/admin/categories/CategoriesManagement'));
+const UsersManagement = lazy(() => import('./pages/admin/users/UsersManagement'));
+const ContributorsManagement = lazy(() => import('./pages/admin/contributors/ContributorsManagement'));
function App() {
return (
-
{t('admin.contributors.name')} | -{t('admin.contributors.email')} | -{t('admin.contributors.role')} | -{t('admin.contributors.status')} | -{t('admin.contributors.postCount')} | +{t('admin.common.name')} | +{t('admin.common.bio')} | +{t('admin.common.articles')} | +{t('admin.common.joinDate')} | {t('admin.common.actions')} |
---|---|---|---|---|---|---|---|---|---|
{contributor.name} | -{contributor.email} | +{contributor.bio} | - - {t(`admin.roles.${contributor.role}`)} + + {contributor.articles} | -- - {t(`admin.common.${contributor.status}`)} - - | -{contributor.postCount} | +{contributor.joinDate} |