数据模型:SystemSettings 新增 kugouApiBaseUrl / kugouAdminCookie / kugouAdminToken / kugouAdminUser / kugouAdminBoundAt 5 字段;新建 KuGouPlaylistSync 同步历史表(12 字段 + playDate 倒序索引)。迁移脚本:app/drizzle/migrations/20260919143000_add_kugou_integration.sql 后端 Server:新增 server/services/kugouService.ts 封装 KuGouMusicApi sidecar 转发(自动拼 Cookie / Token header);新增 6 个 admin/kugou 路由:status / qr-create / qr-check / unbind / sync-schedule-playlists / sync-history。同步核心:逐日出非草稿排期 → kgHash 直取优先 → 失败用 cloudsearch/search 按标题+歌手三级召回 → /playlist/create + /playlist/tracks op=add 每 100 首分批;写同步历史(status、added/missing、歌单 URL)。 后台 UI:新建 Admin/KuGouImporter.client.vue 三栏布局——左栏 sidecar 健康卡+部署说明+扫码绑定/解绑弹窗;中栏多选排期日期+逐日出排期预览卡+按日同步;下栏最近 100 条同步历史(7 种状态 badge + 歌单链接跳转)。dashboard.vue L105 playlist-card 挂载点由 PlaylistCardPage 替换为 KuGouImporter;权限 permissions.js 5 个 ADMIN 族角色开放 playlist-card 页面访问(接口内部绑定/同步再强校验 ADMIN/SUPER_ADMIN)。 侧car要求:部署 KuGouMusicApi(参考文档 https://kugoumusicapi-docs.4everland.app),默认本机 http://127.0.0.1:3000,可通过 SystemSettings.kugouApiBaseUrl 或环境变量 KUGOU_API_BASE 覆盖。
65 lines
2.3 KiB
TypeScript
65 lines
2.3 KiB
TypeScript
import { eq } from 'drizzle-orm'
|
|
import { db } from '~/drizzle/db'
|
|
import { systemSettings } from '~/drizzle/schema'
|
|
import { CacheService } from '~~/server/services/cacheService'
|
|
import { kuGouQrLoginCheck } from '~~/server/services/kugouService'
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const user = event.context.user
|
|
if (!user) throw createError({ statusCode: 401, message: '未登录' })
|
|
if (!['ADMIN', 'SUPER_ADMIN'].includes(user.role))
|
|
throw createError({ statusCode: 403, message: '仅管理员可绑定酷狗账号' })
|
|
|
|
const body = await readBody(event)
|
|
const { qr_id, login_id, key, cookie } = body || {}
|
|
if (!(qr_id || login_id || key)) {
|
|
throw createError({ statusCode: 400, message: '缺少 qr_id 或 key' })
|
|
}
|
|
|
|
const res = await kuGouQrLoginCheck({ qr_id, login_id, key, cookie })
|
|
const state = Number(res?.data?.status ?? 0)
|
|
const token = (res as any)?.data?.token || (res as any)?.token || ''
|
|
const kugouCookie = (res as any)?.data?.cookie || (res as any)?.cookie || cookie || ''
|
|
const kugouUser = (res as any)?.data?.userinfo || (res as any)?.userinfo || null
|
|
|
|
let bound = false
|
|
if (state === 1 && (token || kugouCookie)) {
|
|
const rows = await db.select().from(systemSettings).limit(1)
|
|
const patch: Record<string, any> = {
|
|
kugouAdminToken: token || null,
|
|
kugouAdminCookie: kugouCookie || null,
|
|
kugouAdminUser: kugouUser || null,
|
|
kugouAdminBoundAt: new Date(),
|
|
updatedAt: new Date(),
|
|
}
|
|
if (!rows[0]) {
|
|
await db.insert(systemSettings).values(patch as any)
|
|
} else {
|
|
await db.update(systemSettings).set(patch).where(eq(systemSettings.id, rows[0].id))
|
|
}
|
|
try {
|
|
const cs = CacheService.getInstance()
|
|
await cs.setSystemSettings?.({ ...(rows[0] || {}), ...patch })
|
|
} catch {}
|
|
bound = true
|
|
}
|
|
|
|
return {
|
|
ok: true,
|
|
state, // 0 未扫码 / 1 已完成 / 2 已扫码未确认 / -1 过期
|
|
bound,
|
|
user: kugouUser,
|
|
hint:
|
|
state === 0
|
|
? '请用酷狗 App 扫二维码'
|
|
: state === 2
|
|
? '扫码成功,请在手机端确认登录'
|
|
: state === 1
|
|
? '绑定成功'
|
|
: state === -1
|
|
? '二维码已过期,请刷新重试'
|
|
: res?.error || '等待中',
|
|
raw: res,
|
|
}
|
|
})
|