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:
27
docs/migrations/001_uid_to_id.sql
Normal file
27
docs/migrations/001_uid_to_id.sql
Normal 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;
|
||||
@ -8,7 +8,7 @@ import (
|
||||
|
||||
// BaseModel 所有模型的公共字段
|
||||
type BaseModel struct {
|
||||
ID uint `gorm:"primarykey;column:uid" json:"uid"`
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
@ -35,7 +35,7 @@ func (r *CommentRepo) SearchUsersByLevel(keyword string, minLevel int, limit int
|
||||
threshold := model.LevelThresholds[minLevel]
|
||||
|
||||
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).
|
||||
Order("exp DESC").
|
||||
Limit(limit).
|
||||
@ -59,7 +59,7 @@ func (r *CommentRepo) SearchUsersByLevel(keyword string, minLevel int, limit int
|
||||
// GetUserUIDByID 通过 uint ID 获取 user 的 UID 字符串
|
||||
func (r *CommentRepo) GetUserUIDByID(userID uint) (string, error) {
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
@ -69,7 +69,7 @@ func (r *CommentRepo) GetUserUIDByID(userID uint) (string, error) {
|
||||
// GetUsernameByID 通过 userID 获取用户名(用于通知内容)
|
||||
func (r *CommentRepo) GetUsernameByID(userID uint) (string, error) {
|
||||
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
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
var total int64
|
||||
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")
|
||||
|
||||
if !showDeleted {
|
||||
|
||||
@ -26,7 +26,7 @@ func (r *CommentRepo) FindByID(id uint) (*model.Comment, error) {
|
||||
var c model.Comment
|
||||
err := r.db.Table("comments").
|
||||
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).
|
||||
First(&c).Error
|
||||
if err != nil {
|
||||
@ -48,7 +48,7 @@ func (r *CommentRepo) FindRootComments(postID uint, offset, limit int) ([]model.
|
||||
var list []model.Comment
|
||||
err := base.
|
||||
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").
|
||||
Offset(offset).Limit(limit).
|
||||
Find(&list).Error
|
||||
@ -76,7 +76,7 @@ func (r *CommentRepo) FindReplies(rootID uint) ([]model.Comment, error) {
|
||||
var list []model.Comment
|
||||
err := r.db.Table("comments").
|
||||
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).
|
||||
Order("comments.created_at ASC").
|
||||
Find(&list).Error
|
||||
@ -101,8 +101,8 @@ func (r *CommentRepo) fillReplyToNames(list []model.Comment) {
|
||||
}
|
||||
var rows []nameRow
|
||||
r.db.Table("users").
|
||||
Select("uid, username").
|
||||
Where("uid IN (SELECT reply_to_uid FROM comments WHERE root_id IN (?) AND parent_id IS NOT NULL)", r.rootIDsFromList(list)).
|
||||
Select("id, username").
|
||||
Where("id IN (SELECT reply_to_uid FROM comments WHERE root_id IN (?) AND parent_id IS NOT NULL)", r.rootIDsFromList(list)).
|
||||
Find(&rows)
|
||||
|
||||
m := make(map[string]string)
|
||||
|
||||
@ -66,7 +66,7 @@ func (r *EnergyRepo) FindPostByID(postID uint) (*model.Post, error) {
|
||||
// AddEnergy 原子增减用户域能余额(amount 可为正负)
|
||||
func (r *EnergyRepo) AddEnergy(userID uint, amount int) error {
|
||||
return r.db.Model(&model.User{}).
|
||||
Where("uid = ?", userID).
|
||||
Where("id = ?", userID).
|
||||
UpdateColumn("energy", gorm.Expr("energy + ?", amount)).Error
|
||||
}
|
||||
|
||||
|
||||
@ -72,7 +72,7 @@ func (r *FollowRepo) ListFollowers(userID uint, offset, limit int) ([]model.User
|
||||
var items []model.UserFollow
|
||||
err := r.db.Table("user_follows").
|
||||
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).
|
||||
Order("user_follows.created_at DESC").
|
||||
Offset(offset).Limit(limit).
|
||||
@ -89,7 +89,7 @@ func (r *FollowRepo) ListFollowing(userID uint, offset, limit int) ([]model.User
|
||||
var items []model.UserFollow
|
||||
err := r.db.Table("user_follows").
|
||||
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).
|
||||
Order("user_follows.created_at DESC").
|
||||
Offset(offset).Limit(limit).
|
||||
@ -100,14 +100,14 @@ func (r *FollowRepo) ListFollowing(userID uint, offset, limit int) ([]model.User
|
||||
// IncrFollowersCount 原子增减粉丝数
|
||||
func (r *FollowRepo) IncrFollowersCount(userID uint, delta int) error {
|
||||
return r.db.Model(&model.User{}).
|
||||
Where("uid = ?", userID).
|
||||
Where("id = ?", userID).
|
||||
UpdateColumn("followers_count", gorm.Expr("followers_count + ?", delta)).Error
|
||||
}
|
||||
|
||||
// IncrFollowingCount 原子增减关注数
|
||||
func (r *FollowRepo) IncrFollowingCount(userID uint, delta int) error {
|
||||
return r.db.Model(&model.User{}).
|
||||
Where("uid = ?", userID).
|
||||
Where("id = ?", userID).
|
||||
UpdateColumn("following_count", gorm.Expr("following_count + ?", delta)).Error
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ func (r *FollowRepo) GetFollowListPublic(userID uint) (bool, error) {
|
||||
var listPublic bool
|
||||
err := r.db.Model(&model.User{}).
|
||||
Select("follow_list_public").
|
||||
Where("uid = ?", userID).
|
||||
Where("id = ?", userID).
|
||||
Scan(&listPublic).Error
|
||||
return listPublic, err
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ func (r *LevelRepo) FindByID(id uint) (*model.User, error) {
|
||||
// AddExp 原子增加用户经验值
|
||||
func (r *LevelRepo) AddExp(userID uint, delta int) error {
|
||||
return r.db.Model(&model.User{}).
|
||||
Where("uid = ?", userID).
|
||||
Where("id = ?", userID).
|
||||
UpdateColumn("exp", gorm.Expr("exp + ?", delta)).Error
|
||||
}
|
||||
|
||||
|
||||
@ -50,7 +50,7 @@ func (r *NotificationRepo) CountUnread(userID uint) (int64, error) {
|
||||
// MarkRead 标记单条消息为已读
|
||||
func (r *NotificationRepo) MarkRead(id, userID uint) error {
|
||||
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
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@ func (r *NotificationRepo) GetUsernameByID(userID uint) (string, error) {
|
||||
// GetNotifyPrefs 获取用户通知偏好(NotifyPrefs 字段原始 JSON)
|
||||
func (r *NotificationRepo) GetNotifyPrefs(userID uint) (string, error) {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ func (r *PostRepo) FindByIDWithAuthor(id uint) (*model.Post, error) {
|
||||
var post model.Post
|
||||
err := r.db.Table("posts").
|
||||
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).
|
||||
First(&post).Error
|
||||
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 {
|
||||
query := r.db.Table("posts").
|
||||
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")
|
||||
|
||||
if keyword != "" {
|
||||
|
||||
@ -69,7 +69,7 @@ func (r *UserRepo) ExistsByUsername(username string) (bool, error) {
|
||||
func (r *UserRepo) ExistsByEmailExclude(email string, excludeUID uint) (bool, error) {
|
||||
var count int64
|
||||
err := r.db.Model(&model.User{}).
|
||||
Where("email = ? AND uid != ?", email, excludeUID).
|
||||
Where("email = ? AND id != ?", email, excludeUID).
|
||||
Count(&count).Error
|
||||
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
|
||||
func (r *UserRepo) FindByIDForAuth(userID uint) (*model.User, error) {
|
||||
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
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -88,7 +88,7 @@ func (r *UserRepo) FindByIDForAuth(userID uint) (*model.User, error) {
|
||||
// FindByIDUnscoped 管理后台专用:含软删除用户,用于解锁/封禁等操作
|
||||
func (r *UserRepo) FindByIDUnscoped(userID uint) (*model.User, error) {
|
||||
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
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -98,12 +98,12 @@ func (r *UserRepo) FindByIDUnscoped(userID uint) (*model.User, error) {
|
||||
|
||||
// UpdateStatus 更新用户状态(含软删除用户,locked 状态变更需要)
|
||||
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 调用)
|
||||
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 全量更新用户信息
|
||||
@ -113,12 +113,12 @@ func (r *UserRepo) Update(user *model.User) error {
|
||||
|
||||
// SoftDelete GORM 软删除(设 DeletedAt),用于 locked 状态释放邮箱
|
||||
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)
|
||||
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 构建搜索/筛选的公共查询条件
|
||||
@ -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) {
|
||||
var users []model.User
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@ -931,7 +931,7 @@
|
||||
let u = users[i];
|
||||
var item = document.createElement('div');
|
||||
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) {
|
||||
e.preventDefault();
|
||||
selectMention(u);
|
||||
|
||||
@ -113,8 +113,8 @@ function renderUsernameRow(item) {
|
||||
<td><strong>${escapeHtml(item.new_value)}</strong></td>
|
||||
<td>${formatTime(item.created_at)}</td>
|
||||
<td class="col-actions">
|
||||
<button class="btn btn-primary btn-sm" onclick="approveAudit(${item.uid})">通过</button>
|
||||
<button class="btn btn-outline btn-sm" onclick="openRejectModal(${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.id})">拒绝</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
@ -128,8 +128,8 @@ function renderBioRow(item) {
|
||||
<td title="${escapeHtml(text)}">${escapeHtml(display)}</td>
|
||||
<td>${formatTime(item.created_at)}</td>
|
||||
<td class="col-actions">
|
||||
<button class="btn btn-primary btn-sm" onclick="approveAudit(${item.uid})">通过</button>
|
||||
<button class="btn btn-outline btn-sm" onclick="openRejectModal(${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.id})">拒绝</button>
|
||||
</td>
|
||||
</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>'">
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<button class="btn btn-primary btn-sm" onclick="approveAudit(${item.uid})">通过</button>
|
||||
<button class="btn btn-outline btn-sm" onclick="openRejectModal(${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.id})">拒绝</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
@ -56,7 +56,7 @@ function renderTable(users) {
|
||||
}
|
||||
tbody.innerHTML = users.map(u => `
|
||||
<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-email">${escapeHtml(u.email)}</td>
|
||||
<td class="col-role">${roleLabel(u.role)}</td>
|
||||
@ -73,27 +73,27 @@ function renderTable(users) {
|
||||
|
||||
function renderActions(u) {
|
||||
// 不可操作自己
|
||||
if (u.uid === window.__currentUid) return '—';
|
||||
if (u.id === window.__currentUid) return '—';
|
||||
|
||||
let btns = '';
|
||||
const isOwner = window.__isOwner || false;
|
||||
// 封禁/解封按钮
|
||||
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') {
|
||||
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') {
|
||||
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)
|
||||
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)
|
||||
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 || '—';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user