refactor: BaseModel 主键列名统一 uid→id

- BaseModel 移除 gorm column:uid 和 json:uid 标签,统一为 id
- 更新 8 个 repository 文件中 28 处 raw SQL 列引用 (uid→id)
- 更新 3 个前端 JS 文件中 14 处 API 响应字段引用 (uid→id)
- 添加数据库迁移 SQL 脚本 (docs/migrations/001_uid_to_id.sql)
This commit is contained in:
2026-06-02 19:17:57 +08:00
parent 7fdea90ded
commit 5911cee01c
13 changed files with 70 additions and 43 deletions

View File

@ -0,0 +1,27 @@
-- Migration: 重命名 BaseModel 使用表的主键列 uid → id
-- 涉及表: users, audit_submissions, notifications, user_checkins, user_tasks
-- 执行前请备份数据库
BEGIN;
-- users 表主键及序列重命名
ALTER TABLE users RENAME COLUMN uid TO id;
ALTER SEQUENCE IF EXISTS users_uid_seq RENAME TO users_id_seq;
-- audit_submissions 表主键及序列重命名
ALTER TABLE audit_submissions RENAME COLUMN uid TO id;
ALTER SEQUENCE IF EXISTS audit_submissions_uid_seq RENAME TO audit_submissions_id_seq;
-- notifications 表主键及序列重命名
ALTER TABLE notifications RENAME COLUMN uid TO id;
ALTER SEQUENCE IF EXISTS notifications_uid_seq RENAME TO notifications_id_seq;
-- user_checkins 表主键及序列重命名
ALTER TABLE user_checkins RENAME COLUMN uid TO id;
ALTER SEQUENCE IF EXISTS user_checkins_uid_seq RENAME TO user_checkins_id_seq;
-- user_tasks 表主键及序列重命名
ALTER TABLE user_tasks RENAME COLUMN uid TO id;
ALTER SEQUENCE IF EXISTS user_tasks_uid_seq RENAME TO user_tasks_id_seq;
COMMIT;

View File

@ -8,7 +8,7 @@ import (
// BaseModel 所有模型的公共字段 // BaseModel 所有模型的公共字段
type BaseModel struct { type BaseModel struct {
ID uint `gorm:"primarykey;column:uid" json:"uid"` ID uint `gorm:"primarykey" json:"id"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`

View File

@ -35,7 +35,7 @@ func (r *CommentRepo) SearchUsersByLevel(keyword string, minLevel int, limit int
threshold := model.LevelThresholds[minLevel] threshold := model.LevelThresholds[minLevel]
err := r.db.Table("users"). err := r.db.Table("users").
Select("uid, username, exp"). Select("id, username, exp").
Where("username LIKE ? AND exp >= ? AND deleted_at IS NULL", "%"+keyword+"%", threshold). Where("username LIKE ? AND exp >= ? AND deleted_at IS NULL", "%"+keyword+"%", threshold).
Order("exp DESC"). Order("exp DESC").
Limit(limit). Limit(limit).
@ -59,7 +59,7 @@ func (r *CommentRepo) SearchUsersByLevel(keyword string, minLevel int, limit int
// GetUserUIDByID 通过 uint ID 获取 user 的 UID 字符串 // GetUserUIDByID 通过 uint ID 获取 user 的 UID 字符串
func (r *CommentRepo) GetUserUIDByID(userID uint) (string, error) { func (r *CommentRepo) GetUserUIDByID(userID uint) (string, error) {
var uid uint var uid uint
err := r.db.Table("users").Select("uid").Where("uid = ?", userID).Scan(&uid).Error err := r.db.Table("users").Select("id").Where("id = ?", userID).Scan(&uid).Error
if err != nil { if err != nil {
return "", err return "", err
} }
@ -69,7 +69,7 @@ func (r *CommentRepo) GetUserUIDByID(userID uint) (string, error) {
// GetUsernameByID 通过 userID 获取用户名(用于通知内容) // GetUsernameByID 通过 userID 获取用户名(用于通知内容)
func (r *CommentRepo) GetUsernameByID(userID uint) (string, error) { func (r *CommentRepo) GetUsernameByID(userID uint) (string, error) {
var username string var username string
err := r.db.Table("users").Select("username").Where("uid = ?", userID).Scan(&username).Error err := r.db.Table("users").Select("username").Where("id = ?", userID).Scan(&username).Error
return username, err return username, err
} }
@ -112,7 +112,7 @@ func (r *CommentRepo) AggregateCommentsByAuthor(userID uint, since string) ([]se
func (r *CommentRepo) ListAllComments(keyword string, showDeleted bool, offset, limit int) ([]model.AdminCommentRow, int64, error) { func (r *CommentRepo) ListAllComments(keyword string, showDeleted bool, offset, limit int) ([]model.AdminCommentRow, int64, error) {
var total int64 var total int64
q := r.db.Table("comments"). q := r.db.Table("comments").
Joins("LEFT JOIN users ON users.uid = comments.user_id"). Joins("LEFT JOIN users ON users.id = comments.user_id").
Joins("LEFT JOIN posts ON posts.id = comments.post_id") Joins("LEFT JOIN posts ON posts.id = comments.post_id")
if !showDeleted { if !showDeleted {

View File

@ -26,7 +26,7 @@ func (r *CommentRepo) FindByID(id uint) (*model.Comment, error) {
var c model.Comment var c model.Comment
err := r.db.Table("comments"). err := r.db.Table("comments").
Select("comments.*, users.username as author_name, users.avatar as author_avatar"). Select("comments.*, users.username as author_name, users.avatar as author_avatar").
Joins("LEFT JOIN users ON users.uid = comments.user_id"). Joins("LEFT JOIN users ON users.id = comments.user_id").
Where("comments.id = ?", id). Where("comments.id = ?", id).
First(&c).Error First(&c).Error
if err != nil { if err != nil {
@ -48,7 +48,7 @@ func (r *CommentRepo) FindRootComments(postID uint, offset, limit int) ([]model.
var list []model.Comment var list []model.Comment
err := base. err := base.
Select("comments.*, users.username as author_name, users.avatar as author_avatar"). Select("comments.*, users.username as author_name, users.avatar as author_avatar").
Joins("LEFT JOIN users ON users.uid = comments.user_id"). Joins("LEFT JOIN users ON users.id = comments.user_id").
Order("comments.created_at DESC"). Order("comments.created_at DESC").
Offset(offset).Limit(limit). Offset(offset).Limit(limit).
Find(&list).Error Find(&list).Error
@ -76,7 +76,7 @@ func (r *CommentRepo) FindReplies(rootID uint) ([]model.Comment, error) {
var list []model.Comment var list []model.Comment
err := r.db.Table("comments"). err := r.db.Table("comments").
Select("comments.*, users.username as author_name, users.avatar as author_avatar"). Select("comments.*, users.username as author_name, users.avatar as author_avatar").
Joins("LEFT JOIN users ON users.uid = comments.user_id"). Joins("LEFT JOIN users ON users.id = comments.user_id").
Where("comments.root_id = ? AND comments.parent_id IS NOT NULL AND comments.is_deleted = false", rootID). Where("comments.root_id = ? AND comments.parent_id IS NOT NULL AND comments.is_deleted = false", rootID).
Order("comments.created_at ASC"). Order("comments.created_at ASC").
Find(&list).Error Find(&list).Error
@ -101,8 +101,8 @@ func (r *CommentRepo) fillReplyToNames(list []model.Comment) {
} }
var rows []nameRow var rows []nameRow
r.db.Table("users"). r.db.Table("users").
Select("uid, username"). Select("id, username").
Where("uid IN (SELECT reply_to_uid FROM comments WHERE root_id IN (?) AND parent_id IS NOT NULL)", r.rootIDsFromList(list)). Where("id IN (SELECT reply_to_uid FROM comments WHERE root_id IN (?) AND parent_id IS NOT NULL)", r.rootIDsFromList(list)).
Find(&rows) Find(&rows)
m := make(map[string]string) m := make(map[string]string)

View File

@ -66,7 +66,7 @@ func (r *EnergyRepo) FindPostByID(postID uint) (*model.Post, error) {
// AddEnergy 原子增减用户域能余额amount 可为正负) // AddEnergy 原子增减用户域能余额amount 可为正负)
func (r *EnergyRepo) AddEnergy(userID uint, amount int) error { func (r *EnergyRepo) AddEnergy(userID uint, amount int) error {
return r.db.Model(&model.User{}). return r.db.Model(&model.User{}).
Where("uid = ?", userID). Where("id = ?", userID).
UpdateColumn("energy", gorm.Expr("energy + ?", amount)).Error UpdateColumn("energy", gorm.Expr("energy + ?", amount)).Error
} }

View File

@ -72,7 +72,7 @@ func (r *FollowRepo) ListFollowers(userID uint, offset, limit int) ([]model.User
var items []model.UserFollow var items []model.UserFollow
err := r.db.Table("user_follows"). err := r.db.Table("user_follows").
Select("user_follows.*, u.username AS follower_username, u.avatar AS follower_avatar, u.bio AS follower_bio"). Select("user_follows.*, u.username AS follower_username, u.avatar AS follower_avatar, u.bio AS follower_bio").
Joins("INNER JOIN users u ON u.uid = user_follows.follower_id"). Joins("INNER JOIN users u ON u.id = user_follows.follower_id").
Where("user_follows.followee_id = ?", userID). Where("user_follows.followee_id = ?", userID).
Order("user_follows.created_at DESC"). Order("user_follows.created_at DESC").
Offset(offset).Limit(limit). Offset(offset).Limit(limit).
@ -89,7 +89,7 @@ func (r *FollowRepo) ListFollowing(userID uint, offset, limit int) ([]model.User
var items []model.UserFollow var items []model.UserFollow
err := r.db.Table("user_follows"). err := r.db.Table("user_follows").
Select("user_follows.*, u.username AS followee_username, u.avatar AS followee_avatar, u.bio AS followee_bio"). Select("user_follows.*, u.username AS followee_username, u.avatar AS followee_avatar, u.bio AS followee_bio").
Joins("INNER JOIN users u ON u.uid = user_follows.followee_id"). Joins("INNER JOIN users u ON u.id = user_follows.followee_id").
Where("user_follows.follower_id = ?", userID). Where("user_follows.follower_id = ?", userID).
Order("user_follows.created_at DESC"). Order("user_follows.created_at DESC").
Offset(offset).Limit(limit). Offset(offset).Limit(limit).
@ -100,14 +100,14 @@ func (r *FollowRepo) ListFollowing(userID uint, offset, limit int) ([]model.User
// IncrFollowersCount 原子增减粉丝数 // IncrFollowersCount 原子增减粉丝数
func (r *FollowRepo) IncrFollowersCount(userID uint, delta int) error { func (r *FollowRepo) IncrFollowersCount(userID uint, delta int) error {
return r.db.Model(&model.User{}). return r.db.Model(&model.User{}).
Where("uid = ?", userID). Where("id = ?", userID).
UpdateColumn("followers_count", gorm.Expr("followers_count + ?", delta)).Error UpdateColumn("followers_count", gorm.Expr("followers_count + ?", delta)).Error
} }
// IncrFollowingCount 原子增减关注数 // IncrFollowingCount 原子增减关注数
func (r *FollowRepo) IncrFollowingCount(userID uint, delta int) error { func (r *FollowRepo) IncrFollowingCount(userID uint, delta int) error {
return r.db.Model(&model.User{}). return r.db.Model(&model.User{}).
Where("uid = ?", userID). Where("id = ?", userID).
UpdateColumn("following_count", gorm.Expr("following_count + ?", delta)).Error UpdateColumn("following_count", gorm.Expr("following_count + ?", delta)).Error
} }
@ -116,7 +116,7 @@ func (r *FollowRepo) GetFollowListPublic(userID uint) (bool, error) {
var listPublic bool var listPublic bool
err := r.db.Model(&model.User{}). err := r.db.Model(&model.User{}).
Select("follow_list_public"). Select("follow_list_public").
Where("uid = ?", userID). Where("id = ?", userID).
Scan(&listPublic).Error Scan(&listPublic).Error
return listPublic, err return listPublic, err
} }

View File

@ -31,7 +31,7 @@ func (r *LevelRepo) FindByID(id uint) (*model.User, error) {
// AddExp 原子增加用户经验值 // AddExp 原子增加用户经验值
func (r *LevelRepo) AddExp(userID uint, delta int) error { func (r *LevelRepo) AddExp(userID uint, delta int) error {
return r.db.Model(&model.User{}). return r.db.Model(&model.User{}).
Where("uid = ?", userID). Where("id = ?", userID).
UpdateColumn("exp", gorm.Expr("exp + ?", delta)).Error UpdateColumn("exp", gorm.Expr("exp + ?", delta)).Error
} }

View File

@ -50,7 +50,7 @@ func (r *NotificationRepo) CountUnread(userID uint) (int64, error) {
// MarkRead 标记单条消息为已读 // MarkRead 标记单条消息为已读
func (r *NotificationRepo) MarkRead(id, userID uint) error { func (r *NotificationRepo) MarkRead(id, userID uint) error {
return r.db.Model(&model.Notification{}). return r.db.Model(&model.Notification{}).
Where("uid = ? AND user_id = ?", id, userID). Where("id = ? AND user_id = ?", id, userID).
Update("is_read", true).Error Update("is_read", true).Error
} }
@ -109,7 +109,7 @@ func (r *NotificationRepo) GetUsernameByID(userID uint) (string, error) {
// GetNotifyPrefs 获取用户通知偏好NotifyPrefs 字段原始 JSON // GetNotifyPrefs 获取用户通知偏好NotifyPrefs 字段原始 JSON
func (r *NotificationRepo) GetNotifyPrefs(userID uint) (string, error) { func (r *NotificationRepo) GetNotifyPrefs(userID uint) (string, error) {
var prefs string var prefs string
err := r.db.Table("users").Select("notify_prefs").Where("uid = ?", userID).Scan(&prefs).Error err := r.db.Table("users").Select("notify_prefs").Where("id = ?", userID).Scan(&prefs).Error
return prefs, err return prefs, err
} }

View File

@ -36,7 +36,7 @@ func (r *PostRepo) FindByIDWithAuthor(id uint) (*model.Post, error) {
var post model.Post var post model.Post
err := r.db.Table("posts"). err := r.db.Table("posts").
Select("posts.*, users.username as author_name"). Select("posts.*, users.username as author_name").
Joins("LEFT JOIN users ON users.uid = posts.user_id"). Joins("LEFT JOIN users ON users.id = posts.user_id").
Where("posts.id = ?", id). Where("posts.id = ?", id).
First(&post).Error First(&post).Error
if err != nil { if err != nil {
@ -49,7 +49,7 @@ func (r *PostRepo) FindByIDWithAuthor(id uint) (*model.Post, error) {
func (r *PostRepo) buildQuery(keyword, status string) *gorm.DB { func (r *PostRepo) buildQuery(keyword, status string) *gorm.DB {
query := r.db.Table("posts"). query := r.db.Table("posts").
Select("posts.*, users.username as author_name"). Select("posts.*, users.username as author_name").
Joins("LEFT JOIN users ON users.uid = posts.user_id"). Joins("LEFT JOIN users ON users.id = posts.user_id").
Where("posts.deleted_at IS NULL") Where("posts.deleted_at IS NULL")
if keyword != "" { if keyword != "" {

View File

@ -69,7 +69,7 @@ func (r *UserRepo) ExistsByUsername(username string) (bool, error) {
func (r *UserRepo) ExistsByEmailExclude(email string, excludeUID uint) (bool, error) { func (r *UserRepo) ExistsByEmailExclude(email string, excludeUID uint) (bool, error) {
var count int64 var count int64
err := r.db.Model(&model.User{}). err := r.db.Model(&model.User{}).
Where("email = ? AND uid != ?", email, excludeUID). Where("email = ? AND id != ?", email, excludeUID).
Count(&count).Error Count(&count).Error
return count > 0, err return count > 0, err
} }
@ -77,7 +77,7 @@ func (r *UserRepo) ExistsByEmailExclude(email string, excludeUID uint) (bool, er
// FindByIDForAuth 认证专用查询:返回 uid/email/username/avatar/role/status // FindByIDForAuth 认证专用查询:返回 uid/email/username/avatar/role/status
func (r *UserRepo) FindByIDForAuth(userID uint) (*model.User, error) { func (r *UserRepo) FindByIDForAuth(userID uint) (*model.User, error) {
var user model.User var user model.User
err := r.db.Select("uid", "email", "username", "avatar", "role", "status", "exp"). err := r.db.Select("id", "email", "username", "avatar", "role", "status", "exp").
First(&user, userID).Error First(&user, userID).Error
if err != nil { if err != nil {
return nil, err return nil, err
@ -88,7 +88,7 @@ func (r *UserRepo) FindByIDForAuth(userID uint) (*model.User, error) {
// FindByIDUnscoped 管理后台专用:含软删除用户,用于解锁/封禁等操作 // FindByIDUnscoped 管理后台专用:含软删除用户,用于解锁/封禁等操作
func (r *UserRepo) FindByIDUnscoped(userID uint) (*model.User, error) { func (r *UserRepo) FindByIDUnscoped(userID uint) (*model.User, error) {
var user model.User var user model.User
err := r.db.Unscoped().Select("uid", "email", "username", "role", "status", "deleted_at"). err := r.db.Unscoped().Select("id", "email", "username", "role", "status", "deleted_at").
First(&user, userID).Error First(&user, userID).Error
if err != nil { if err != nil {
return nil, err return nil, err
@ -98,12 +98,12 @@ func (r *UserRepo) FindByIDUnscoped(userID uint) (*model.User, error) {
// UpdateStatus 更新用户状态含软删除用户locked 状态变更需要) // UpdateStatus 更新用户状态含软删除用户locked 状态变更需要)
func (r *UserRepo) UpdateStatus(uid uint, status string) error { func (r *UserRepo) UpdateStatus(uid uint, status string) error {
return r.db.Unscoped().Model(&model.User{}).Where("uid = ?", uid).Update("status", status).Error return r.db.Unscoped().Model(&model.User{}).Where("id = ?", uid).Update("status", status).Error
} }
// UpdateRole 更新用户角色(仅 owner 调用) // UpdateRole 更新用户角色(仅 owner 调用)
func (r *UserRepo) UpdateRole(uid uint, role string) error { func (r *UserRepo) UpdateRole(uid uint, role string) error {
return r.db.Model(&model.User{}).Where("uid = ?", uid).Update("role", role).Error return r.db.Model(&model.User{}).Where("id = ?", uid).Update("role", role).Error
} }
// Update 全量更新用户信息 // Update 全量更新用户信息
@ -113,12 +113,12 @@ func (r *UserRepo) Update(user *model.User) error {
// SoftDelete GORM 软删除(设 DeletedAt用于 locked 状态释放邮箱 // SoftDelete GORM 软删除(设 DeletedAt用于 locked 状态释放邮箱
func (r *UserRepo) SoftDelete(uid uint) error { func (r *UserRepo) SoftDelete(uid uint) error {
return r.db.Where("uid = ?", uid).Delete(&model.User{}).Error return r.db.Where("id = ?", uid).Delete(&model.User{}).Error
} }
// Restore 恢复软删除(清 DeletedAt // Restore 恢复软删除(清 DeletedAt
func (r *UserRepo) Restore(uid uint) error { func (r *UserRepo) Restore(uid uint) error {
return r.db.Unscoped().Model(&model.User{}).Where("uid = ?", uid).Update("deleted_at", nil).Error return r.db.Unscoped().Model(&model.User{}).Where("id = ?", uid).Update("deleted_at", nil).Error
} }
// buildSearchQuery 构建搜索/筛选的公共查询条件 // buildSearchQuery 构建搜索/筛选的公共查询条件
@ -141,7 +141,7 @@ func (r *UserRepo) buildSearchQuery(keyword, role, status string) *gorm.DB {
func (r *UserRepo) SearchUsers(keyword, role, status string, offset, limit int) ([]model.User, error) { func (r *UserRepo) SearchUsers(keyword, role, status string, offset, limit int) ([]model.User, error) {
var users []model.User var users []model.User
err := r.buildSearchQuery(keyword, role, status). err := r.buildSearchQuery(keyword, role, status).
Order("uid ASC").Offset(offset).Limit(limit).Find(&users).Error Order("id ASC").Offset(offset).Limit(limit).Find(&users).Error
return users, err return users, err
} }

View File

@ -931,7 +931,7 @@
let u = users[i]; let u = users[i];
var item = document.createElement('div'); var item = document.createElement('div');
item.className = 'mention-item'; item.className = 'mention-item';
item.innerHTML = '<span class="mention-username">' + escapeHtml(u.username) + '</span><span class="mention-uid">' + escapeHtml(u.uid) + '</span>'; item.innerHTML = '<span class="mention-username">' + escapeHtml(u.username) + '</span><span class="mention-uid">' + escapeHtml(u.id) + '</span>';
item.addEventListener('mousedown', function(e) { item.addEventListener('mousedown', function(e) {
e.preventDefault(); e.preventDefault();
selectMention(u); selectMention(u);

View File

@ -113,8 +113,8 @@ function renderUsernameRow(item) {
<td><strong>${escapeHtml(item.new_value)}</strong></td> <td><strong>${escapeHtml(item.new_value)}</strong></td>
<td>${formatTime(item.created_at)}</td> <td>${formatTime(item.created_at)}</td>
<td class="col-actions"> <td class="col-actions">
<button class="btn btn-primary btn-sm" onclick="approveAudit(${item.uid})">通过</button> <button class="btn btn-primary btn-sm" onclick="approveAudit(${item.id})">通过</button>
<button class="btn btn-outline btn-sm" onclick="openRejectModal(${item.uid})">拒绝</button> <button class="btn btn-outline btn-sm" onclick="openRejectModal(${item.id})">拒绝</button>
</td> </td>
</tr>`; </tr>`;
} }
@ -128,8 +128,8 @@ function renderBioRow(item) {
<td title="${escapeHtml(text)}">${escapeHtml(display)}</td> <td title="${escapeHtml(text)}">${escapeHtml(display)}</td>
<td>${formatTime(item.created_at)}</td> <td>${formatTime(item.created_at)}</td>
<td class="col-actions"> <td class="col-actions">
<button class="btn btn-primary btn-sm" onclick="approveAudit(${item.uid})">通过</button> <button class="btn btn-primary btn-sm" onclick="approveAudit(${item.id})">通过</button>
<button class="btn btn-outline btn-sm" onclick="openRejectModal(${item.uid})">拒绝</button> <button class="btn btn-outline btn-sm" onclick="openRejectModal(${item.id})">拒绝</button>
</td> </td>
</tr>`; </tr>`;
} }
@ -155,8 +155,8 @@ function renderAvatarCards(items) {
<img src="${escapeHtml(item.new_value)}" alt="新头像" onerror="this.src='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 80 80%22><rect fill=%22%23eee%22 width=%2280%22 height=%2280%22/><text x=%2240%22 y=%2245%22 text-anchor=%22middle%22 fill=%22%23999%22 font-size=%2212%22>加载失败</text></svg>'"> <img src="${escapeHtml(item.new_value)}" alt="新头像" onerror="this.src='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 80 80%22><rect fill=%22%23eee%22 width=%2280%22 height=%2280%22/><text x=%2240%22 y=%2245%22 text-anchor=%22middle%22 fill=%22%23999%22 font-size=%2212%22>加载失败</text></svg>'">
</div> </div>
<div class="card-actions"> <div class="card-actions">
<button class="btn btn-primary btn-sm" onclick="approveAudit(${item.uid})">通过</button> <button class="btn btn-primary btn-sm" onclick="approveAudit(${item.id})">通过</button>
<button class="btn btn-outline btn-sm" onclick="openRejectModal(${item.uid})">拒绝</button> <button class="btn btn-outline btn-sm" onclick="openRejectModal(${item.id})">拒绝</button>
</div> </div>
</div> </div>
`).join(''); `).join('');

View File

@ -56,7 +56,7 @@ function renderTable(users) {
} }
tbody.innerHTML = users.map(u => ` tbody.innerHTML = users.map(u => `
<tr> <tr>
<td class="col-uid">${u.uid}</td> <td class="col-uid">${u.id}</td>
<td class="col-username">${escapeHtml(u.username)}</td> <td class="col-username">${escapeHtml(u.username)}</td>
<td class="col-email">${escapeHtml(u.email)}</td> <td class="col-email">${escapeHtml(u.email)}</td>
<td class="col-role">${roleLabel(u.role)}</td> <td class="col-role">${roleLabel(u.role)}</td>
@ -73,27 +73,27 @@ function renderTable(users) {
function renderActions(u) { function renderActions(u) {
// 不可操作自己 // 不可操作自己
if (u.uid === window.__currentUid) return '—'; if (u.id === window.__currentUid) return '—';
let btns = ''; let btns = '';
const isOwner = window.__isOwner || false; const isOwner = window.__isOwner || false;
// 封禁/解封按钮 // 封禁/解封按钮
if (u.status === 'active') { if (u.status === 'active') {
btns += `<button class="btn btn-danger btn-sm" onclick="banUser(${u.uid}, '${escapeHtml(u.username)}')">封禁</button>`; btns += `<button class="btn btn-danger btn-sm" onclick="banUser(${u.id}, '${escapeHtml(u.username)}')">封禁</button>`;
} else if (u.status === 'banned') { } else if (u.status === 'banned') {
btns += `<button class="btn btn-outline btn-sm" onclick="unbanUser(${u.uid}, '${escapeHtml(u.username)}')">解封</button>`; btns += `<button class="btn btn-outline btn-sm" onclick="unbanUser(${u.id}, '${escapeHtml(u.username)}')">解封</button>`;
} }
// 强制下线按钮 // 强制下线按钮
if (u.status === 'active' || u.status === 'banned') { if (u.status === 'active' || u.status === 'banned') {
btns += `<button class="btn btn-outline btn-sm" onclick="resetToken(${u.uid}, '${escapeHtml(u.username)}')">踢下线</button>`; btns += `<button class="btn btn-outline btn-sm" onclick="resetToken(${u.id}, '${escapeHtml(u.username)}')">踢下线</button>`;
} }
// 删除按钮(仅 owner 可见,且目标不是 locked/deleted // 删除按钮(仅 owner 可见,且目标不是 locked/deleted
if (isOwner && u.status !== 'locked' && u.status !== 'deleted') { if (isOwner && u.status !== 'locked' && u.status !== 'deleted') {
btns += `<button class="btn btn-danger btn-sm" onclick="deleteUser(${u.uid}, '${escapeHtml(u.username)}')">删除</button>`; btns += `<button class="btn btn-danger btn-sm" onclick="deleteUser(${u.id}, '${escapeHtml(u.username)}')">删除</button>`;
} }
// 解锁按钮(仅 owner 可见,状态为 locked // 解锁按钮(仅 owner 可见,状态为 locked
if (isOwner && u.status === 'locked') { if (isOwner && u.status === 'locked') {
btns += `<button class="btn btn-outline btn-sm" onclick="unlockUser(${u.uid}, '${escapeHtml(u.username)}')">解锁</button>`; btns += `<button class="btn btn-outline btn-sm" onclick="unlockUser(${u.id}, '${escapeHtml(u.username)}')">解锁</button>`;
} }
return btns || '—'; return btns || '—';
} }