收藏夹 服务

This commit is contained in:
jianzhichu
2026-01-19 14:11:23 +08:00
parent f5449f3ace
commit 65e6b48d8e
13 changed files with 401 additions and 15 deletions
+1 -1
View File
@@ -222,7 +222,7 @@ namespace dy.net.Controllers
/// 快速开启或停止 /// 快速开启或停止
/// </summary> /// </summary>
[HttpPost("switch")] [HttpPost("switch")]
public async Task<IActionResult> SwitchAsync([FromBody] DouyinCookieStopDto dto) public async Task<IActionResult> SwitchAsync([FromBody] DouyinCookieSwitchDto dto)
{ {
var result = await dyCookieService.Switch(dto); var result = await dyCookieService.Switch(dto);
if (result) if (result)
@@ -2,8 +2,8 @@
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. --> <!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<_PublishTargetUrl>E:\code\dysync\bin\Release\net6.0\publish\</_PublishTargetUrl> <_PublishTargetUrl>E:\gitea\dysync\bin\Release\net6.0\publish\</_PublishTargetUrl>
<History>True|2026-01-16T16:52:08.8180346Z||;False|2026-01-17T00:52:03.1829000+08:00||;True|2026-01-17T00:45:04.0498090+08:00||;True|2026-01-16T21:00:57.7239379+08:00||;True|2026-01-16T21:00:03.3912989+08:00||;True|2026-01-16T20:56:08.5505592+08:00||;True|2026-01-16T20:46:32.3067302+08:00||;True|2026-01-15T22:18:57.9835738+08:00||;True|2026-01-15T22:07:25.4753938+08:00||;True|2026-01-15T22:07:14.8243754+08:00||;True|2026-01-15T21:40:13.2613927+08:00||;True|2026-01-15T20:57:56.6291682+08:00||;True|2026-01-15T20:57:47.1363536+08:00||;True|2026-01-15T20:08:44.4710883+08:00||;True|2026-01-15T20:04:51.8284314+08:00||;False|2026-01-15T20:04:45.3258155+08:00||;True|2026-01-15T19:59:11.7907672+08:00||;True|2026-01-15T01:10:48.9066941+08:00||;True|2026-01-15T01:03:23.6216975+08:00||;True|2026-01-15T01:03:16.3364591+08:00||;False|2026-01-15T01:03:10.5654436+08:00||;True|2026-01-15T00:49:21.6559622+08:00||;True|2026-01-15T00:10:42.7288439+08:00||;True|2026-01-15T00:06:28.4241682+08:00||;True|2026-01-13T22:34:15.2372411+08:00||;True|2026-01-13T22:26:46.4522947+08:00||;True|2026-01-13T22:15:56.9735179+08:00||;True|2026-01-13T22:15:51.8250677+08:00||;False|2026-01-13T22:15:46.6608690+08:00||;True|2026-01-13T22:03:06.4683237+08:00||;False|2026-01-13T22:03:01.2036027+08:00||;True|2026-01-13T21:36:05.8776209+08:00||;True|2026-01-13T21:33:02.0766289+08:00||;False|2026-01-13T21:32:57.1976141+08:00||;True|2026-01-13T19:14:35.2519439+08:00||;True|2026-01-13T15:07:50.0836745+08:00||;False|2026-01-13T15:06:35.5929685+08:00||;True|2026-01-13T10:21:41.6012776+08:00||;True|2026-01-13T09:57:25.4788847+08:00||;True|2026-01-13T09:53:14.4900360+08:00||;True|2026-01-13T09:23:53.6465295+08:00||;True|2026-01-12T22:04:14.5886955+08:00||;False|2026-01-12T22:04:08.2008101+08:00||;True|2026-01-12T21:53:46.9947176+08:00||;True|2026-01-12T21:11:15.8358024+08:00||;True|2026-01-12T21:09:51.8663228+08:00||;True|2026-01-11T21:25:07.5052845+08:00||;False|2026-01-11T21:24:27.5744557+08:00||;True|2026-01-11T19:59:15.4734611+08:00||;False|2026-01-11T19:59:04.9543339+08:00||;True|2026-01-11T18:51:30.6649269+08:00||;True|2026-01-08T21:07:57.5094466+08:00||;True|2026-01-08T14:51:18.2266231+08:00||;True|2026-01-08T14:31:55.1137120+08:00||;True|2026-01-08T00:16:24.4451490+08:00||;True|2026-01-08T00:14:20.7430793+08:00||;True|2026-01-08T00:07:46.7228194+08:00||;True|2026-01-07T23:52:13.3290082+08:00||;True|2026-01-07T23:49:28.4838650+08:00||;True|2026-01-07T23:47:45.1936189+08:00||;True|2026-01-07T23:35:09.7818611+08:00||;True|2026-01-07T23:20:43.7462863+08:00||;True|2026-01-07T23:04:39.5140429+08:00||;False|2026-01-07T23:04:36.5367611+08:00||;True|2026-01-07T22:53:14.6013449+08:00||;True|2026-01-07T22:49:59.9549006+08:00||;True|2026-01-07T22:36:40.0254856+08:00||;False|2026-01-07T22:36:28.8275903+08:00||;True|2026-01-07T21:59:51.4355171+08:00||;True|2026-01-03T21:38:51.4307034+08:00||;True|2026-01-02T19:09:16.9668906+08:00||;False|2026-01-02T19:09:11.7496369+08:00||;True|2026-01-02T15:42:08.2215697+08:00||;True|2026-01-02T09:46:56.1654861+08:00||;True|2026-01-02T09:35:28.0211225+08:00||;True|2026-01-01T23:07:01.9022045+08:00||;False|2026-01-01T23:06:56.0537216+08:00||;True|2026-01-01T22:16:10.2974067+08:00||;True|2026-01-01T22:16:06.2123787+08:00||;False|2026-01-01T22:15:33.3626979+08:00||;True|2026-01-01T22:01:30.7161900+08:00||;True|2026-01-01T21:10:19.3664263+08:00||;True|2026-01-01T21:09:38.6071080+08:00||;True|2025-12-30T11:45:54.5543034+08:00||;True|2025-12-30T09:19:08.3178124+08:00||;True|2025-12-29T18:57:29.7032246+08:00||;True|2025-12-27T14:52:24.4780776+08:00||;False|2025-12-27T14:52:19.5635794+08:00||;True|2025-12-27T14:48:01.6252748+08:00||;False|2025-12-27T14:47:55.7976192+08:00||;True|2025-12-27T14:38:23.9723838+08:00||;True|2025-12-27T13:00:29.8583858+08:00||;True|2025-12-26T22:18:42.4015637+08:00||;True|2025-12-26T22:10:58.5274572+08:00||;True|2025-12-26T22:06:26.3129600+08:00||;True|2025-12-26T22:03:55.6718618+08:00||;False|2025-12-26T22:03:48.3809954+08:00||;True|2025-12-26T22:02:33.1840390+08:00||;True|2025-12-26T22:01:16.1660100+08:00||;True|2025-12-25T00:50:26.1116465+08:00||;</History> <History>True|2026-01-19T02:19:44.8997385Z||;True|2026-01-19T10:11:10.2305307+08:00||;True|2026-01-17T00:52:08.8180346+08:00||;False|2026-01-17T00:52:03.1829000+08:00||;True|2026-01-17T00:45:04.0498090+08:00||;True|2026-01-16T21:00:57.7239379+08:00||;True|2026-01-16T21:00:03.3912989+08:00||;True|2026-01-16T20:56:08.5505592+08:00||;True|2026-01-16T20:46:32.3067302+08:00||;True|2026-01-15T22:18:57.9835738+08:00||;True|2026-01-15T22:07:25.4753938+08:00||;True|2026-01-15T22:07:14.8243754+08:00||;True|2026-01-15T21:40:13.2613927+08:00||;True|2026-01-15T20:57:56.6291682+08:00||;True|2026-01-15T20:57:47.1363536+08:00||;True|2026-01-15T20:08:44.4710883+08:00||;True|2026-01-15T20:04:51.8284314+08:00||;False|2026-01-15T20:04:45.3258155+08:00||;True|2026-01-15T19:59:11.7907672+08:00||;True|2026-01-15T01:10:48.9066941+08:00||;True|2026-01-15T01:03:23.6216975+08:00||;True|2026-01-15T01:03:16.3364591+08:00||;False|2026-01-15T01:03:10.5654436+08:00||;True|2026-01-15T00:49:21.6559622+08:00||;True|2026-01-15T00:10:42.7288439+08:00||;True|2026-01-15T00:06:28.4241682+08:00||;True|2026-01-13T22:34:15.2372411+08:00||;True|2026-01-13T22:26:46.4522947+08:00||;True|2026-01-13T22:15:56.9735179+08:00||;True|2026-01-13T22:15:51.8250677+08:00||;False|2026-01-13T22:15:46.6608690+08:00||;True|2026-01-13T22:03:06.4683237+08:00||;False|2026-01-13T22:03:01.2036027+08:00||;True|2026-01-13T21:36:05.8776209+08:00||;True|2026-01-13T21:33:02.0766289+08:00||;False|2026-01-13T21:32:57.1976141+08:00||;True|2026-01-13T19:14:35.2519439+08:00||;True|2026-01-13T15:07:50.0836745+08:00||;False|2026-01-13T15:06:35.5929685+08:00||;True|2026-01-13T10:21:41.6012776+08:00||;True|2026-01-13T09:57:25.4788847+08:00||;True|2026-01-13T09:53:14.4900360+08:00||;True|2026-01-13T09:23:53.6465295+08:00||;True|2026-01-12T22:04:14.5886955+08:00||;False|2026-01-12T22:04:08.2008101+08:00||;True|2026-01-12T21:53:46.9947176+08:00||;True|2026-01-12T21:11:15.8358024+08:00||;True|2026-01-12T21:09:51.8663228+08:00||;True|2026-01-11T21:25:07.5052845+08:00||;False|2026-01-11T21:24:27.5744557+08:00||;True|2026-01-11T19:59:15.4734611+08:00||;False|2026-01-11T19:59:04.9543339+08:00||;True|2026-01-11T18:51:30.6649269+08:00||;True|2026-01-08T21:07:57.5094466+08:00||;True|2026-01-08T14:51:18.2266231+08:00||;True|2026-01-08T14:31:55.1137120+08:00||;True|2026-01-08T00:16:24.4451490+08:00||;True|2026-01-08T00:14:20.7430793+08:00||;True|2026-01-08T00:07:46.7228194+08:00||;True|2026-01-07T23:52:13.3290082+08:00||;True|2026-01-07T23:49:28.4838650+08:00||;True|2026-01-07T23:47:45.1936189+08:00||;True|2026-01-07T23:35:09.7818611+08:00||;True|2026-01-07T23:20:43.7462863+08:00||;True|2026-01-07T23:04:39.5140429+08:00||;False|2026-01-07T23:04:36.5367611+08:00||;True|2026-01-07T22:53:14.6013449+08:00||;True|2026-01-07T22:49:59.9549006+08:00||;True|2026-01-07T22:36:40.0254856+08:00||;False|2026-01-07T22:36:28.8275903+08:00||;True|2026-01-07T21:59:51.4355171+08:00||;True|2026-01-03T21:38:51.4307034+08:00||;True|2026-01-02T19:09:16.9668906+08:00||;False|2026-01-02T19:09:11.7496369+08:00||;True|2026-01-02T15:42:08.2215697+08:00||;True|2026-01-02T09:46:56.1654861+08:00||;True|2026-01-02T09:35:28.0211225+08:00||;True|2026-01-01T23:07:01.9022045+08:00||;False|2026-01-01T23:06:56.0537216+08:00||;True|2026-01-01T22:16:10.2974067+08:00||;True|2026-01-01T22:16:06.2123787+08:00||;False|2026-01-01T22:15:33.3626979+08:00||;True|2026-01-01T22:01:30.7161900+08:00||;True|2026-01-01T21:10:19.3664263+08:00||;True|2026-01-01T21:09:38.6071080+08:00||;True|2025-12-30T11:45:54.5543034+08:00||;True|2025-12-30T09:19:08.3178124+08:00||;True|2025-12-29T18:57:29.7032246+08:00||;True|2025-12-27T14:52:24.4780776+08:00||;False|2025-12-27T14:52:19.5635794+08:00||;True|2025-12-27T14:48:01.6252748+08:00||;False|2025-12-27T14:47:55.7976192+08:00||;True|2025-12-27T14:38:23.9723838+08:00||;True|2025-12-27T13:00:29.8583858+08:00||;True|2025-12-26T22:18:42.4015637+08:00||;True|2025-12-26T22:10:58.5274572+08:00||;True|2025-12-26T22:06:26.3129600+08:00||;True|2025-12-26T22:03:55.6718618+08:00||;False|2025-12-26T22:03:48.3809954+08:00||;True|2025-12-26T22:02:33.1840390+08:00||;</History>
<LastFailureDetails /> <LastFailureDetails />
</PropertyGroup> </PropertyGroup>
</Project> </Project>
+12 -2
View File
@@ -8,8 +8,6 @@
<SpaRoot>app\</SpaRoot> <SpaRoot>app\</SpaRoot>
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
<GenerateDocumentationFile>True</GenerateDocumentationFile> <GenerateDocumentationFile>True</GenerateDocumentationFile>
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection> <ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
<ServerGarbageCollection>false</ServerGarbageCollection> <ServerGarbageCollection>false</ServerGarbageCollection>
<RetainVMGarbageCollection>true</RetainVMGarbageCollection> <RetainVMGarbageCollection>true</RetainVMGarbageCollection>
@@ -19,6 +17,9 @@
<TieredCompilation>true</TieredCompilation> <TieredCompilation>true</TieredCompilation>
<InvariantGlobalization>true</InvariantGlobalization> <InvariantGlobalization>true</InvariantGlobalization>
<DockerfileContext>.</DockerfileContext> <DockerfileContext>.</DockerfileContext>
<!-- 2. 强制禁用默认编译项(旧版VS关键配置) -->
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<EnableDefaultContentItems>false</EnableDefaultContentItems>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -40,10 +41,19 @@
<None Remove="logs\**" /> <None Remove="logs\**" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Include="**/*.cs" Exclude="app\**;db\**;expand\**;logs\**;obj\**;bin\**" />
<Content Include="**/*.json;**/*.xml;**/*.config" Exclude="app\**;db\**;expand\**;logs\**;obj\**;bin\**" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Remove="wwwroot\index.html" /> <Content Remove="wwwroot\index.html" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ClockSnowFlake" Version="1.1.0" /> <PackageReference Include="ClockSnowFlake" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.16" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.16" />
+1 -1
View File
@@ -2,7 +2,7 @@
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<ActiveDebugProfile>tiny.ddns</ActiveDebugProfile> <ActiveDebugProfile>tiny.ddns</ActiveDebugProfile>
<NameOfLastUsedPublishProfile>E:\code\dysync\Properties\PublishProfiles\docker.pubxml</NameOfLastUsedPublishProfile> <NameOfLastUsedPublishProfile>E:\gitea\dysync\Properties\PublishProfiles\docker.pubxml</NameOfLastUsedPublishProfile>
<Controller_SelectedScaffolderID>ApiControllerEmptyScaffolder</Controller_SelectedScaffolderID> <Controller_SelectedScaffolderID>ApiControllerEmptyScaffolder</Controller_SelectedScaffolderID>
<Controller_SelectedScaffolderCategoryPath>root/Common/Api</Controller_SelectedScaffolderCategoryPath> <Controller_SelectedScaffolderCategoryPath>root/Common/Api</Controller_SelectedScaffolderCategoryPath>
</PropertyGroup> </PropertyGroup>
+124
View File
@@ -0,0 +1,124 @@
using dy.net.model.entity;
using dy.net.model.response;
using dy.net.service;
using Quartz;
using System.Collections.Generic;
namespace dy.net.job
{
[DisallowConcurrentExecution] // 禁止并发执行,确保同一时间只有一个实例在运行
public class DouyinCollectCatesSyncJob : IJob
{
/// <summary>
/// 抖音Cookie服务,用于获取和管理用户Cookie
/// </summary>
protected readonly DouyinCookieService _dyCookieService;
/// <summary>
/// 抖音HTTP客户端服务,用于发送HTTP请求
/// </summary>
protected readonly DouyinHttpClientService _douyinService;
/// <summary>
/// 抖音关注服务,用于管理关注数据
/// </summary>
protected readonly DouyinFollowService _followService;
protected readonly DouyinCommonService douyinCommonService;
public DouyinCollectCatesSyncJob(DouyinCookieService dyCookieService, DouyinHttpClientService douyinService, DouyinFollowService followService, DouyinCommonService douyinCommonService)
{
_dyCookieService = dyCookieService;
_douyinService = douyinService;
_followService = followService;
this.douyinCommonService = douyinCommonService;
}
public async Task Execute(IJobExecutionContext context)
{
var cookies = await _dyCookieService.GetAllOpendAsync();
if (cookies != null && cookies.Any())
{
AppConfig conf = douyinCommonService.GetConfig();
foreach (var ck in cookies)
{
if (string.IsNullOrWhiteSpace(ck.SecUserId))
{
Serilog.Log.Debug($"同步关注列表博主[{ck.UserName}]-未设置secuserid,跳过.");
continue;
}
string count = "20";
string offset = "0";
bool hasmore = true;
int total= 0;
List<FollowingsItem> follows = new List<FollowingsItem>();
while (hasmore)
{
try
{
var data = await _douyinService.SyncMyFollows(count, offset, ck.SecUserId, ck.Cookies, async err =>
{
if (err.StatusCode != 0)
{
Serilog.Log.Error($"同步关注列表博主[{ck.UserName}]关注列表时发生错误,错误信息:{err.StatusMsg},状态码:{err.StatusCode}");
}
// 如果是未登录错误,则跳出循环
if (err.StatusCode == 8)
{
hasmore = false;
}
ck.StatusMsg = err.StatusCode == 8 ? "无效" : "正常";
ck.StatusCode = err.StatusCode;
await _dyCookieService.UpdateAsync(ck);
});
if (data != null)
{
// 绑定我的用户ID--之前没有这个字段
if (string.IsNullOrWhiteSpace(ck.MyUserId))
{
ck.MyUserId = data.MySelfUserId;
await _dyCookieService.UpdateAsync(ck);
}
total = data.Total;
hasmore = data.HasMore;
offset = data.Offset.ToString();
if (data.Followings != null && data.Followings.Count > 0)
{
follows.AddRange(data.Followings);
}
}
// 只有第一次启动时,才进行全部同步
if (!conf.IsFirstRunning)
{
hasmore = false;
}
}
catch (Exception ex)
{
Serilog.Log.Error($"关注列表同步失败,{ex.Message}");
hasmore = false;
break;
}
}
if (follows.Count > 0)
{
var (add, update, succ) = await _followService.Sync(follows, ck);
if (!succ) hasmore = false;
else
{
hasmore = add > 100|| update > 100;//一次最多100条数据
}
}
}
await douyinCommonService.SetConfigNotFirstRunning();
}
}
}
}
+8 -1
View File
@@ -1,9 +1,16 @@
namespace dy.net.model.dto namespace dy.net.model.dto
{ {
public class DouyinCookieStopDto public class DouyinCookieSwitchDto
{ {
public string Id { get; set; } public string Id { get; set; }
public int Status { get; set; } public int Status { get; set; }
} }
public class DouyinCollectCateSwitchDto
{
public string Id { get; set; }
public bool Sync { get; set; }
}
} }
+8
View File
@@ -52,4 +52,12 @@ namespace dy.net.model.dto
public string UperId { get; set; } public string UperId { get; set; }
} }
public class DouyinCollectCateRequestDto : PageRequestDto
{
public string cookieId { get; set; }
}
} }
+6 -2
View File
@@ -18,7 +18,7 @@ namespace dy.net.model.entity
/// <summary> /// <summary>
/// 关联cookieId /// 关联cookieId
/// </summary> /// </summary>
public long CookieId { get; set; } public string CookieId { get; set; }
/// <summary> /// <summary>
/// 收藏夹名称 /// 收藏夹名称
@@ -47,6 +47,10 @@ namespace dy.net.model.entity
/// <summary> /// <summary>
/// 收藏夹类型(0:自定义收藏夹,1:合集,2:短剧) /// 收藏夹类型(0:自定义收藏夹,1:合集,2:短剧)
/// </summary> /// </summary>
public int CollectType { get; set; } public int CateType { get; set; }
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
} }
} }
+157
View File
@@ -0,0 +1,157 @@
using ClockSnowFlake;
using dy.net.model.dto;
using dy.net.model.entity;
using SqlSugar;
using System.Linq.Expressions;
namespace dy.net.repository
{
public class DouyinCollectCateRepository : BaseRepository<DouyinCollectCate>
{
// 注入SQLSugar客户端
public DouyinCollectCateRepository(ISqlSugarClient db) : base(db)
{
}
/// <summary>
/// 分页查询
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<(List<DouyinCollectCate> list, int totalCount)> GetPagedAsync(DouyinCollectCateRequestDto dto)
{
var where = this.Db.Queryable<DouyinCollectCate>()
.WhereIF(!string.IsNullOrWhiteSpace(dto.cookieId), x => x.CookieId == dto.cookieId);
var totalCount = await where.CountAsync();
var list = await where.Skip((dto.PageIndex - 1) * dto.PageSize).Take(dto.PageSize).ToListAsync();
return (list, totalCount);
}
/// <summary>
/// 批量修改状态
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<bool> SwitchBatchAsync(List<DouyinCollectCateSwitchDto> dto)
{
// 空值/空列表防护 + 提取ID列表
var ids = dto?.Select(x => x.Id).ToList() ?? new List<string>();
if (!ids.Any()) return false;
// 1. 查询需要更新的分类数据
var cates = await GetListAsync(x => ids.Contains(x.Id));
// 2. 用字典优化查找(避免循环内FirstOrDefault,提升性能)
var dtoDict = dto.ToDictionary(x => x.Id);
// 3. 批量更新状态(简化循环写法)
cates.ForEach(item =>
{
if (dtoDict.TryGetValue(item.Id, out var dtocate))
{
item.Sync = dtocate.Sync;
}
});
// 4. 执行更新并返回结果
return await Db.Updateable(cates).ExecuteCommandAsync() > 0;
}
internal async Task<(int add, int update, int delete, bool succ)> Sync(List<DouyinCollectCate> cates, string ckId,int cateType)
{
// 初始化返回结果
int addCount = 0;
int updateCount = 0;
int deleteCount = 0;
bool isSuccess = false;
try
{
// 1. 参数校验
if (string.IsNullOrWhiteSpace(ckId))
{
throw new ArgumentNullException(nameof(ckId), "CookieId信息不能为空");
}
if (cates == null)
{
cates = new List<DouyinCollectCate>(); // 空列表则执行删除所有操作
}
// 2. 开启SqlSugar事务
await Db.Ado.BeginTranAsync();
// 3. 查询数据库中当前用户的所有分类
var dbCates = await Db.Queryable<DouyinCollectCate>()
.Where(c => c.CookieId == ckId && c.CateType == cateType)
.ToListAsync();
// 4. 处理新增和更新逻辑
var cateIds = cates.Select(c => c.Id).ToList();
foreach (var cate in cates)
{
cate.UpdateTime = DateTime.Now; // 更新时间戳
// 检查是否已存在
if (string.IsNullOrWhiteSpace(cate.Id))
{
cate.CreateTime = DateTime.Now;
cate.Id = IdGener.GetLong().ToString();
// 新增
await Db.Insertable(cate).ExecuteCommandAsync();
addCount++;
}
else
{
var existCate = dbCates.FirstOrDefault(x => x.Id == cate.Id);
if (existCate == null)
{
Serilog.Log.Error($" cate {cate.Id} id 不存在");
continue;
}
// 对比字段是否有变化(根据实际业务字段调整)
bool isChanged = !string.Equals(existCate.Name, cate.Name, StringComparison.Ordinal);
if (isChanged)
{
// 更新(只更新有变化的字段,或全量更新)
await Db.Updateable(cate)
.IgnoreColumns(ignoreAllNullColumns: true) // 忽略空值字段
.Where(c => c.Id == cate.Id)
.ExecuteCommandAsync();
updateCount++;
}
}
}
// 5. 处理删除逻辑:数据库中有但传入列表中没有的分类
var deleteCates = dbCates.Where(c => !cateIds.Contains(c.Id)).ToList();
if (deleteCates.Any())
{
var deleteIds = deleteCates.Select(c => c.Id).ToList();
deleteCount = await Db.Deleteable<DouyinCollectCate>()
.Where(c => deleteIds.Contains(c.Id) && c.CookieId == ckId)
.ExecuteCommandAsync();
}
// 6. 提交事务
await Db.Ado.CommitTranAsync();
isSuccess = true;
}
catch (Exception ex)
{
// 异常时回滚事务
await Db.Ado.RollbackTranAsync();
isSuccess = false;
// throw;
Serilog.Log.Error($"同步收藏夹:类型-{cateType}异常,{ex.StackTrace}" );
}
// 7. 返回最终结果
return (addCount, updateCount, deleteCount, isSuccess);
}
}
}
+1 -1
View File
@@ -44,7 +44,7 @@ namespace dy.net.repository
} }
public async Task<bool> SwitchAsync(DouyinCookieStopDto dto) public async Task<bool> SwitchAsync(DouyinCookieSwitchDto dto)
{ {
var res =await Db.Updateable<DouyinCookie>().SetColumns(x => new DouyinCookie { Status = dto.Status }).Where(x => x.Id == dto.Id).ExecuteCommandAsync(); var res =await Db.Updateable<DouyinCookie>().SetColumns(x => new DouyinCookie { Status = dto.Status }).Where(x => x.Id == dto.Id).ExecuteCommandAsync();
+5
View File
@@ -107,6 +107,8 @@ namespace dy.net.repository
try try
{ {
// 2. 开启SqlSugar事务
await Db.Ado.BeginTranAsync();
// 1. 提取当前批次的SecUid集合(去重) // 1. 提取当前批次的SecUid集合(去重)
HashSet<string> currentSecUids = followInfos.Select(x => x.SecUid).ToHashSet(); HashSet<string> currentSecUids = followInfos.Select(x => x.SecUid).ToHashSet();
if (!currentSecUids.Any()) if (!currentSecUids.Any())
@@ -203,12 +205,15 @@ namespace dy.net.repository
} }
} }
// 6. 提交事务
await Db.Ado.CommitTranAsync();
// 【重要】删除逻辑已移除:增量场景下不能通过批次对比删除,需单独设计取消关注逻辑 // 【重要】删除逻辑已移除:增量场景下不能通过批次对比删除,需单独设计取消关注逻辑
Serilog.Log.Debug($"dy_followed_users{ck.UserName})关注列表同步完成:新增{toAddFollows.Count}条,更新{toUpdateFollows.Count}条"); Serilog.Log.Debug($"dy_followed_users{ck.UserName})关注列表同步完成:新增{toAddFollows.Count}条,更新{toUpdateFollows.Count}条");
return (toAddFollows.Count, toUpdateFollows.Count, true); return (toAddFollows.Count, toUpdateFollows.Count, true);
} }
catch (Exception ex) catch (Exception ex)
{ {
await Db.Ado.RollbackTranAsync();
Serilog.Log.Error(ex, $"同步关注列表失败({ck.UserName}):{ex.Message}"); Serilog.Log.Error(ex, $"同步关注列表失败({ck.UserName}):{ex.Message}");
return (0, 0, false); return (0, 0, false);
+71
View File
@@ -0,0 +1,71 @@
using ClockSnowFlake;
using dy.net.model.dto;
using dy.net.model.entity;
using dy.net.model.response;
using dy.net.repository;
using SqlSugar;
using System.Linq.Expressions;
namespace dy.net.service
{
public class DouyinCollectCateService
{
private readonly DouyinCollectCateRepository douyinCollectCateRepository;
public DouyinCollectCateService(DouyinCollectCateRepository douyinCollectCateRepository)
{
douyinCollectCateRepository = douyinCollectCateRepository;
}
/// <summary>
///
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<(List<DouyinCollectCate> list, int totalCount)> GetPagedAsync(DouyinCollectCateRequestDto dto)
{
return await douyinCollectCateRepository.GetPagedAsync(dto);
}
/// <summary>
///
/// </summary>
/// <param name="cates"></param>
/// <param name="ckId"></param>
/// <returns></returns>
public async Task<(int add, int update,int delete, bool succ)> Sync(List<DouyinCollectCate> cates, string ckId)
{
return await douyinCollectCateRepository.Sync(cates, ckId);
}
/// <summary>
/// 打开或关闭同步
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<bool> BatchSwitchSync(List<DouyinCollectCateSwitchDto> dto)
{
return await douyinCollectCateRepository.SwitchBatchAsync(dto);
}
/// <summary>
/// 获取需要同步的合集、短剧、收藏夹
/// </summary>
/// <returns></returns>
public async Task<List<DouyinCollectCate>> GetSyncCates(string cookieId,int cateType)
{
return await douyinCollectCateRepository.GetListAsync(x => x.CookieId == cookieId && x.CateType == cateType);
}
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ namespace dy.net.service
{ {
return await _cookieRepository.InsertAsync(dyUserCookies); return await _cookieRepository.InsertAsync(dyUserCookies);
} }
public async Task<bool> Switch(DouyinCookieStopDto dto) public async Task<bool> Switch(DouyinCookieSwitchDto dto)
{ {
return await _cookieRepository.SwitchAsync(dto); return await _cookieRepository.SwitchAsync(dto);
} }