feat: 抖音扫码登录,自动填写 Cookie 和 SecUserId
部署到群晖 / deploy (push) Successful in 32s

新增 DyQrCodeService,调用 sso.douyin.com 二维码接口;
ConfigController 新增 qrcode/qrstatus/qrbind 三个接口;
前端账号列表每行增加"扫码登录"按钮,展示二维码弹窗并自动轮询绑定。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
yuming
2026-04-27 08:56:56 +08:00
parent 6803b32b1b
commit 0848ebc272
4 changed files with 360 additions and 3 deletions
+81 -1
View File
@@ -18,12 +18,14 @@ namespace dy.net.Controllers
private readonly CommonService commonService;
private readonly QuartzJobService quartzJobService;
private readonly DyQrCodeService _qrCodeService;
public ConfigController(DyCookieService dyCookieService, CommonService commonService,QuartzJobService quartzJobService)
public ConfigController(DyCookieService dyCookieService, CommonService commonService, QuartzJobService quartzJobService, DyQrCodeService qrCodeService)
{
this.dyCookieService = dyCookieService;
this.commonService = commonService;
this.quartzJobService = quartzJobService;
_qrCodeService = qrCodeService;
}
@@ -198,6 +200,84 @@ namespace dy.net.Controllers
}
/// <summary>
/// 获取抖音扫码登录二维码
/// </summary>
[HttpGet("qrcode")]
[Authorize]
public async Task<IActionResult> GetQrCode()
{
try
{
var (token, qrBase64) = await _qrCodeService.GetQrCodeAsync();
return Ok(new { code = 0, data = new { token, qrBase64 } });
}
catch (Exception ex)
{
Serilog.Log.Error($"获取二维码失败: {ex.Message}");
return Ok(new { code = -1, error = "获取二维码失败:" + ex.Message });
}
}
/// <summary>
/// 轮询扫码状态(前端每2秒调一次)
/// status: 1=待扫码, 2=已扫码待确认, 3=登录成功, -1=已过期
/// </summary>
[HttpGet("qrstatus")]
[Authorize]
public async Task<IActionResult> GetQrStatus([FromQuery] string token)
{
try
{
var (status, cookies, secUserId) = await _qrCodeService.CheckStatusAsync(token);
return Ok(new { code = 0, data = new { status, hasCookies = !string.IsNullOrEmpty(cookies), secUserId } });
}
catch (Exception ex)
{
Serilog.Log.Error($"查询扫码状态失败: {ex.Message}");
return Ok(new { code = -1, error = "查询失败:" + ex.Message });
}
}
/// <summary>
/// 扫码成功后,将 Cookie 绑定到指定账号
/// </summary>
[HttpPost("qrbind")]
[Authorize]
public async Task<IActionResult> BindQrCode([FromQuery] string token, [FromQuery] string id)
{
try
{
var (cookies, secUserId) = _qrCodeService.GetSessionResult(token);
if (string.IsNullOrEmpty(cookies))
return Ok(new { code = -1, error = "扫码尚未完成或已过期" });
var account = await dyCookieService.GetByIdAsync(id);
if (account == null)
return Ok(new { code = -1, error = "账号不存在" });
account.Cookies = cookies;
if (!string.IsNullOrEmpty(secUserId))
account.SecUserId = secUserId;
account.Status = 1;
var result = await dyCookieService.UpdateAsync(account);
_qrCodeService.RemoveSession(token);
if (result)
{
await ReStartJob();
return Ok(new { code = 0, data = new { secUserId } });
}
return Ok(new { code = -1, error = "保存失败" });
}
catch (Exception ex)
{
Serilog.Log.Error($"绑定Cookie失败: {ex.Message}");
return Ok(new { code = -1, error = "绑定失败:" + ex.Message });
}
}
private async Task ReStartJob() {
var config= commonService.GetConfig();