- 新增 /api/admin/songs/batch-reject.post 批量驳回接口(事务清排期/投票/扣接纳数/黑名单去重/异步通知) - requireSongAdmin 三角色校验 + createApiError 统一错误包装 - scheduleDaysBefore/After 四字段排期可见范围限制(开放 API 与公开排期接口三处缓存路径 + DB 条件过滤) - submissionNoteAutoModeration 基于黑名单 KEYWORD/LANGUAGE/GENRE 自动判定留言 publicStatus,命中自动关闭 submissionNotePublic - 更新 songs/request.post 与 songs/[id]/update.put 接入自动审查逻辑
86 lines
2.4 KiB
TypeScript
86 lines
2.4 KiB
TypeScript
import { db, eq, userIdentities, users } from '~/drizzle/db'
|
|
import { getBeijingTime } from '~/utils/timeUtils'
|
|
import { resolveAvatarSource } from '~~/server/utils/user-avatar'
|
|
|
|
type DbTransaction = Parameters<Parameters<typeof db.transaction>[0]>[0]
|
|
|
|
export interface OAuthIdentityPayload {
|
|
provider: string
|
|
providerUserId: string
|
|
providerUsername?: string | null
|
|
avatar?: string | null
|
|
}
|
|
|
|
export interface ExistingOAuthIdentity {
|
|
id: number
|
|
avatar: string | null
|
|
}
|
|
|
|
export async function syncOAuthIdentityAvatar(
|
|
tx: DbTransaction,
|
|
user: { id: number; avatarProvider?: string | null; avatarProviderUserId?: string | null },
|
|
existing: ExistingOAuthIdentity | null | undefined,
|
|
payload: OAuthIdentityPayload
|
|
): Promise<{ created: boolean; avatarSourceSet: boolean }> {
|
|
const currentIdentities = await tx.query.userIdentities.findMany({
|
|
where: (t, { eq: eqUserId }) => eqUserId(t.userId, user.id),
|
|
columns: {
|
|
id: true,
|
|
provider: true,
|
|
providerUserId: true,
|
|
providerUsername: true,
|
|
avatar: true,
|
|
createdAt: true
|
|
}
|
|
})
|
|
const currentAvatarSource = resolveAvatarSource(user, currentIdentities)
|
|
|
|
let created = false
|
|
if (existing) {
|
|
if (payload.avatar && existing.avatar !== payload.avatar) {
|
|
await tx
|
|
.update(userIdentities)
|
|
.set({ avatar: payload.avatar })
|
|
.where(eq(userIdentities.id, existing.id))
|
|
}
|
|
} else {
|
|
await tx.insert(userIdentities).values({
|
|
userId: user.id,
|
|
provider: payload.provider,
|
|
providerUserId: payload.providerUserId,
|
|
providerUsername: payload.providerUsername,
|
|
avatar: payload.avatar || null,
|
|
createdAt: getBeijingTime()
|
|
})
|
|
created = true
|
|
}
|
|
|
|
let avatarSourceSet = false
|
|
if (!currentAvatarSource) {
|
|
const updatedIdentities = await tx.query.userIdentities.findMany({
|
|
where: (t, { eq: eqUserId }) => eqUserId(t.userId, user.id),
|
|
columns: {
|
|
id: true,
|
|
provider: true,
|
|
providerUserId: true,
|
|
providerUsername: true,
|
|
avatar: true,
|
|
createdAt: true
|
|
}
|
|
})
|
|
const nextAvatarSource = resolveAvatarSource(user, updatedIdentities)
|
|
if (nextAvatarSource) {
|
|
await tx
|
|
.update(users)
|
|
.set({
|
|
avatarProvider: nextAvatarSource.provider,
|
|
avatarProviderUserId: nextAvatarSource.providerUserId
|
|
})
|
|
.where(eq(users.id, user.id))
|
|
avatarSourceSet = true
|
|
}
|
|
}
|
|
|
|
return { created, avatarSourceSet }
|
|
}
|