1、修复关注同步时报https证书错误问题
2、关注列表点头像跳抖音博主首页
This commit is contained in:
+1
-1
@@ -197,7 +197,7 @@ ____/ _|_)_____/ _| _| \_|\____|
|
|||||||
// 重置博主作品同步状态为未同步
|
// 重置博主作品同步状态为未同步
|
||||||
commonService.UpdateAllCookieSyncedToZero();
|
commonService.UpdateAllCookieSyncedToZero();
|
||||||
|
|
||||||
if (!isDevelopment)
|
//if (!isDevelopment)
|
||||||
{
|
{
|
||||||
// 启动定时任务
|
// 启动定时任务
|
||||||
var quartzJobService = services.GetRequiredService<DouyinQuartzJobService>();
|
var quartzJobService = services.GetRequiredService<DouyinQuartzJobService>();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<_PublishTargetUrl>E:\code\dysync\bin\Release\net6.0\publish\</_PublishTargetUrl>
|
<_PublishTargetUrl>E:\code\dysync\bin\Release\net6.0\publish\</_PublishTargetUrl>
|
||||||
<History>True|2025-12-04T00:22:47.9617427Z||;False|2025-12-04T08:22:41.3759733+08:00||;True|2025-12-04T08:14:02.2478893+08:00||;True|2025-12-04T07:57:00.2208139+08:00||;True|2025-12-03T23:58:13.3693303+08:00||;True|2025-12-03T23:57:40.4257893+08:00||;True|2025-12-03T23:56:38.8717769+08:00||;True|2025-12-03T23:56:24.0663568+08:00||;False|2025-12-03T23:55:36.1902974+08:00||;True|2025-12-03T23:51:30.6688504+08:00||;True|2025-12-03T23:34:17.1648971+08:00||;False|2025-12-03T23:34:07.7649161+08:00||;True|2025-12-03T22:32:56.2435003+08:00||;True|2025-12-03T22:27:45.6059666+08:00||;True|2025-12-03T15:55:28.0186597+08:00||;False|2025-12-03T15:54:17.5032724+08:00||;True|2025-12-03T15:53:10.2273509+08:00||;True|2025-12-02T22:11:37.4645042+08:00||;False|2025-12-02T22:10:47.1320259+08:00||;True|2025-12-02T14:39:58.8932130+08:00||;True|2025-12-02T14:36:37.1529072+08:00||;True|2025-12-02T12:58:22.0951548+08:00||;True|2025-12-02T09:30:33.7066474+08:00||;True|2025-12-02T09:30:14.5481844+08:00||;True|2025-12-02T09:30:00.3123364+08:00||;False|2025-12-02T09:29:54.4615520+08:00||;True|2025-12-02T09:13:01.7995414+08:00||;False|2025-12-02T09:12:55.8360281+08:00||;True|2025-12-02T09:12:31.0156791+08:00||;True|2025-12-02T08:55:11.3773395+08:00||;True|2025-12-02T08:53:21.3927713+08:00||;False|2025-12-02T08:48:55.8638037+08:00||;True|2025-12-02T07:29:25.2192447+08:00||;False|2025-12-02T07:29:10.2504665+08:00||;True|2025-12-02T07:28:29.0769286+08:00||;True|2025-12-01T21:53:07.6474834+08:00||;True|2025-12-01T21:44:28.1674315+08:00||;True|2025-12-01T21:18:47.2119609+08:00||;True|2025-12-01T20:51:19.9948301+08:00||;True|2025-12-01T20:50:36.9904697+08:00||;True|2025-12-01T20:44:10.1695142+08:00||;True|2025-12-01T19:27:58.0479456+08:00||;True|2025-12-01T19:16:02.2288214+08:00||;False|2025-12-01T19:15:56.0372115+08:00||;True|2025-12-01T19:15:16.4854821+08:00||;False|2025-12-01T19:15:06.7001019+08:00||;True|2025-12-01T17:10:38.9739466+08:00||;False|2025-12-01T17:10:18.8928139+08:00||;True|2025-12-01T17:03:59.3171589+08:00||;True|2025-12-01T17:03:12.5681656+08:00||;True|2025-12-01T16:42:51.4415025+08:00||;True|2025-12-01T01:29:59.6975567+08:00||;True|2025-12-01T01:29:45.1898652+08:00||;False|2025-12-01T01:29:33.1787721+08:00||;True|2025-12-01T01:27:33.1297605+08:00||;True|2025-12-01T01:27:27.3976862+08:00||;False|2025-12-01T01:27:20.5193052+08:00||;True|2025-12-01T01:11:11.0210582+08:00||;True|2025-12-01T01:04:04.3307782+08:00||;True|2025-12-01T00:55:49.8018864+08:00||;True|2025-12-01T00:27:25.1323561+08:00||;True|2025-11-30T23:58:20.7284116+08:00||;True|2025-11-30T23:57:58.3027622+08:00||;True|2025-11-30T23:57:43.0829614+08:00||;False|2025-11-30T23:57:33.5606716+08:00||;True|2025-11-30T23:54:29.6203368+08:00||;True|2025-11-30T23:54:27.5383120+08:00||;False|2025-11-30T23:54:17.0823982+08:00||;True|2025-11-30T22:41:11.4538820+08:00||;True|2025-11-30T22:38:19.7064566+08:00||;True|2025-11-30T22:37:35.2889186+08:00||;True|2025-11-30T21:42:31.1408314+08:00||;True|2025-11-30T21:42:19.5385333+08:00||;False|2025-11-30T21:42:13.3290237+08:00||;True|2025-11-30T21:33:28.0806806+08:00||;True|2025-11-30T21:16:56.7088027+08:00||;False|2025-11-30T21:16:46.1444944+08:00||;True|2025-11-30T20:21:24.4059664+08:00||;False|2025-11-30T20:21:17.6006098+08:00||;True|2025-11-30T20:20:08.2030141+08:00||;False|2025-11-30T20:16:12.3608968+08:00||;True|2025-11-30T20:15:35.6048313+08:00||;True|2025-11-30T19:45:18.7229552+08:00||;True|2025-11-30T19:45:08.6083368+08:00||;False|2025-11-30T19:44:54.7824905+08:00||;True|2025-11-30T19:44:17.0179491+08:00||;True|2025-11-30T19:44:06.7073484+08:00||;False|2025-11-30T19:43:58.2896556+08:00||;True|2025-11-30T19:42:08.2261927+08:00||;True|2025-11-30T19:28:15.7115077+08:00||;False|2025-11-30T19:28:10.2280725+08:00||;True|2025-11-30T19:23:31.7859158+08:00||;True|2025-11-30T19:23:20.7788277+08:00||;True|2025-11-30T19:17:11.8583296+08:00||;False|2025-11-30T19:10:41.8369574+08:00||;True|2025-11-30T19:02:08.8184758+08:00||;False|2025-11-30T19:01:59.4711045+08:00||;True|2025-11-30T18:16:01.8372242+08:00||;True|2025-11-30T14:50:31.9356543+08:00||;True|2025-11-30T14:48:27.1833064+08:00||;</History>
|
<History>True|2025-12-04T13:58:29.4112766Z||;True|2025-12-04T21:58:10.0382218+08:00||;True|2025-12-04T21:57:55.3894059+08:00||;True|2025-12-04T21:10:38.8943797+08:00||;True|2025-12-04T21:09:31.5024569+08:00||;True|2025-12-04T08:22:47.9617427+08:00||;False|2025-12-04T08:22:41.3759733+08:00||;True|2025-12-04T08:14:02.2478893+08:00||;True|2025-12-04T07:57:00.2208139+08:00||;True|2025-12-03T23:58:13.3693303+08:00||;True|2025-12-03T23:57:40.4257893+08:00||;True|2025-12-03T23:56:38.8717769+08:00||;True|2025-12-03T23:56:24.0663568+08:00||;False|2025-12-03T23:55:36.1902974+08:00||;True|2025-12-03T23:51:30.6688504+08:00||;True|2025-12-03T23:34:17.1648971+08:00||;False|2025-12-03T23:34:07.7649161+08:00||;True|2025-12-03T22:32:56.2435003+08:00||;True|2025-12-03T22:27:45.6059666+08:00||;True|2025-12-03T15:55:28.0186597+08:00||;False|2025-12-03T15:54:17.5032724+08:00||;True|2025-12-03T15:53:10.2273509+08:00||;True|2025-12-02T22:11:37.4645042+08:00||;False|2025-12-02T22:10:47.1320259+08:00||;True|2025-12-02T14:39:58.8932130+08:00||;True|2025-12-02T14:36:37.1529072+08:00||;True|2025-12-02T12:58:22.0951548+08:00||;True|2025-12-02T09:30:33.7066474+08:00||;True|2025-12-02T09:30:14.5481844+08:00||;True|2025-12-02T09:30:00.3123364+08:00||;False|2025-12-02T09:29:54.4615520+08:00||;True|2025-12-02T09:13:01.7995414+08:00||;False|2025-12-02T09:12:55.8360281+08:00||;True|2025-12-02T09:12:31.0156791+08:00||;True|2025-12-02T08:55:11.3773395+08:00||;True|2025-12-02T08:53:21.3927713+08:00||;False|2025-12-02T08:48:55.8638037+08:00||;True|2025-12-02T07:29:25.2192447+08:00||;False|2025-12-02T07:29:10.2504665+08:00||;True|2025-12-02T07:28:29.0769286+08:00||;True|2025-12-01T21:53:07.6474834+08:00||;True|2025-12-01T21:44:28.1674315+08:00||;True|2025-12-01T21:18:47.2119609+08:00||;True|2025-12-01T20:51:19.9948301+08:00||;True|2025-12-01T20:50:36.9904697+08:00||;True|2025-12-01T20:44:10.1695142+08:00||;True|2025-12-01T19:27:58.0479456+08:00||;True|2025-12-01T19:16:02.2288214+08:00||;False|2025-12-01T19:15:56.0372115+08:00||;True|2025-12-01T19:15:16.4854821+08:00||;False|2025-12-01T19:15:06.7001019+08:00||;True|2025-12-01T17:10:38.9739466+08:00||;False|2025-12-01T17:10:18.8928139+08:00||;True|2025-12-01T17:03:59.3171589+08:00||;True|2025-12-01T17:03:12.5681656+08:00||;True|2025-12-01T16:42:51.4415025+08:00||;True|2025-12-01T01:29:59.6975567+08:00||;True|2025-12-01T01:29:45.1898652+08:00||;False|2025-12-01T01:29:33.1787721+08:00||;True|2025-12-01T01:27:33.1297605+08:00||;True|2025-12-01T01:27:27.3976862+08:00||;False|2025-12-01T01:27:20.5193052+08:00||;True|2025-12-01T01:11:11.0210582+08:00||;True|2025-12-01T01:04:04.3307782+08:00||;True|2025-12-01T00:55:49.8018864+08:00||;True|2025-12-01T00:27:25.1323561+08:00||;True|2025-11-30T23:58:20.7284116+08:00||;True|2025-11-30T23:57:58.3027622+08:00||;True|2025-11-30T23:57:43.0829614+08:00||;False|2025-11-30T23:57:33.5606716+08:00||;True|2025-11-30T23:54:29.6203368+08:00||;True|2025-11-30T23:54:27.5383120+08:00||;False|2025-11-30T23:54:17.0823982+08:00||;True|2025-11-30T22:41:11.4538820+08:00||;True|2025-11-30T22:38:19.7064566+08:00||;True|2025-11-30T22:37:35.2889186+08:00||;True|2025-11-30T21:42:31.1408314+08:00||;True|2025-11-30T21:42:19.5385333+08:00||;False|2025-11-30T21:42:13.3290237+08:00||;True|2025-11-30T21:33:28.0806806+08:00||;True|2025-11-30T21:16:56.7088027+08:00||;False|2025-11-30T21:16:46.1444944+08:00||;True|2025-11-30T20:21:24.4059664+08:00||;False|2025-11-30T20:21:17.6006098+08:00||;True|2025-11-30T20:20:08.2030141+08:00||;False|2025-11-30T20:16:12.3608968+08:00||;True|2025-11-30T20:15:35.6048313+08:00||;True|2025-11-30T19:45:18.7229552+08:00||;True|2025-11-30T19:45:08.6083368+08:00||;False|2025-11-30T19:44:54.7824905+08:00||;True|2025-11-30T19:44:17.0179491+08:00||;True|2025-11-30T19:44:06.7073484+08:00||;False|2025-11-30T19:43:58.2896556+08:00||;True|2025-11-30T19:42:08.2261927+08:00||;True|2025-11-30T19:28:15.7115077+08:00||;False|2025-11-30T19:28:10.2280725+08:00||;True|2025-11-30T19:23:31.7859158+08:00||;True|2025-11-30T19:23:20.7788277+08:00||;True|2025-11-30T19:17:11.8583296+08:00||;False|2025-11-30T19:10:41.8369574+08:00||;</History>
|
||||||
<LastFailureDetails />
|
<LastFailureDetails />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ Cookie 及 `sec_user_id` 是同步功能的核心,需严格按步骤获取,
|
|||||||
|
|
||||||
| 镜像标签 | 架构 |
|
| 镜像标签 | 架构 |
|
||||||
| ----------------- | -------------- |
|
| ----------------- | -------------- |
|
||||||
| `beta_1.7.5` | x86_64 (amd64) |
|
| `beta_1.7.6` | x86_64 (amd64) |
|
||||||
| `arm_1.7.5` | ARM64 |
|
| `arm_1.7.6` | ARM64 |
|
||||||
|
|
||||||
### 最新镜像查看
|
### 最新镜像查看
|
||||||
[镜像列表](http://nas.synology2023.online:10108/api/docker/dysync/1)
|
[镜像列表](http://nas.synology2023.online:10108/api/docker/dysync/1)
|
||||||
@@ -116,7 +116,7 @@ docker run -d --restart=always \
|
|||||||
-v /opt/dysync/uper:/app/uper \
|
-v /opt/dysync/uper:/app/uper \
|
||||||
-p 10103:10101 \
|
-p 10103:10101 \
|
||||||
--name dysync2025 \
|
--name dysync2025 \
|
||||||
ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_1.7.5
|
ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_1.7.6
|
||||||
# 注意:-p 后面的容器端口,可以用环境变量类似:ASPNETCORE_URLS = http://+:10108 指定
|
# 注意:-p 后面的容器端口,可以用环境变量类似:ASPNETCORE_URLS = http://+:10108 指定
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ version: '3.8'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
dysync:
|
dysync:
|
||||||
image: ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_1.7.5
|
image: ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_1.7.6
|
||||||
container_name: dysync2025 # 容器名称
|
container_name: dysync2025 # 容器名称
|
||||||
restart: unless-stopped # 始终重启容器,除非容器被手动停止或Docker服务停止
|
restart: unless-stopped # 始终重启容器,除非容器被手动停止或Docker服务停止
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-main-content">
|
<div class="card-main-content">
|
||||||
<div class="avatar-wrapper">
|
<div class="avatar-wrapper" @click="goDouyinUp(item)">
|
||||||
<a-avatar shape="circle" size="large" :src="item.uperAvatar" v-if="item.uperAvatar" />
|
<a-avatar shape="circle" size="large" :src="item.uperAvatar" v-if="item.uperAvatar" />
|
||||||
<a-avatar shape="circle" size="large" v-else class="avatar-placeholder">
|
<a-avatar shape="circle" size="large" v-else class="avatar-placeholder">
|
||||||
{{ item.uperName.charAt(0) }}
|
{{ item.uperName.charAt(0) }}
|
||||||
@@ -667,6 +667,10 @@ const handleDeleteItem = (item: FollowItem) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const goDouyinUp = (item) => {
|
||||||
|
window.open('https://www.douyin.com/user/' + item.secUid, '_blank', 'noopener noreferrer');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"dbconn": "",
|
"dbconn": "",
|
||||||
"dbtype": "Sqlite",
|
"dbtype": "Sqlite",
|
||||||
"tagName": "beta_1.7.5"
|
"tagName": "beta_1.7.6",
|
||||||
|
//"tagName": "dev_1.2"
|
||||||
}
|
}
|
||||||
+107
-31
@@ -18,6 +18,7 @@ using Swashbuckle.AspNetCore.SwaggerGen;
|
|||||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Net.Security;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@@ -171,51 +172,126 @@ namespace dy.net.extension
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//public static void AddHttpClients(this IServiceCollection services)
|
||||||
|
//{
|
||||||
|
// services.AddHttpClient("dy_collect", client =>
|
||||||
|
// {
|
||||||
|
// client.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
|
||||||
|
// client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36");
|
||||||
|
// client.DefaultRequestHeaders.Add("Referer", "https://www.douyin.com");
|
||||||
|
// });
|
||||||
|
|
||||||
|
// services.AddHttpClient("dy_follow", client =>
|
||||||
|
// {
|
||||||
|
// client.DefaultRequestHeaders.Referrer = new Uri("https://www.douyin.com/user/self?showTab=like");
|
||||||
|
// });
|
||||||
|
|
||||||
|
// services.AddHttpClient("dy_favorite", client =>
|
||||||
|
// {
|
||||||
|
// client.DefaultRequestHeaders.Referrer = new Uri("https://www.douyin.com/user/self?showTab=like");
|
||||||
|
// });
|
||||||
|
|
||||||
|
// services.AddHttpClient("dy_uper", client =>
|
||||||
|
// {
|
||||||
|
// client.DefaultRequestHeaders.Referrer = new Uri("https://www.douyin.com/user/MS4wLjABAAAA1zFIBhWG-3qS8MiggDMhyCpqDnvfGYf_mtsdgtBzV7A?from_tab_name=main&showTab=post&vid=7576282367263807451");
|
||||||
|
// });
|
||||||
|
// // 配置HttpClient(Startup.cs或Program.cs)
|
||||||
|
// services.AddHttpClient("dy_download")
|
||||||
|
// .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
|
||||||
|
// {
|
||||||
|
// // 控制并发连接数(根据服务器承受能力调整,建议5-20)
|
||||||
|
// MaxConnectionsPerServer = 5,
|
||||||
|
// // 禁用代理自动检测(减少不必要的延迟)
|
||||||
|
// UseProxy = false,
|
||||||
|
// // 连接超时(建立连接的超时时间)
|
||||||
|
// ConnectTimeout = TimeSpan.FromSeconds(60)
|
||||||
|
// })
|
||||||
|
// .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
|
||||||
|
// {
|
||||||
|
// ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
|
||||||
|
// })
|
||||||
|
// // 配置客户端默认请求头
|
||||||
|
// .ConfigureHttpClient(client =>
|
||||||
|
// {
|
||||||
|
// client.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
|
||||||
|
// client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36");
|
||||||
|
// client.DefaultRequestHeaders.Add("Referer", "https://www.douyin.com");
|
||||||
|
// });
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void AddHttpClients(this IServiceCollection services)
|
public static void AddHttpClients(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddHttpClient("dy_collect", client =>
|
// 通用的忽略SSL证书验证的HttpMessageHandler配置
|
||||||
|
Func<HttpMessageHandler> ignoreSslHandlerFactory = () =>
|
||||||
{
|
{
|
||||||
client.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
|
var handler = new SocketsHttpHandler
|
||||||
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36");
|
|
||||||
client.DefaultRequestHeaders.Add("Referer", "https://www.douyin.com");
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddHttpClient("dy_follow", client =>
|
|
||||||
{
|
|
||||||
client.DefaultRequestHeaders.Referrer = new Uri("https://www.douyin.com/user/self?showTab=like");
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddHttpClient("dy_favorite", client =>
|
|
||||||
{
|
|
||||||
client.DefaultRequestHeaders.Referrer = new Uri("https://www.douyin.com/user/self?showTab=like");
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddHttpClient("dy_uper", client =>
|
|
||||||
{
|
|
||||||
client.DefaultRequestHeaders.Referrer = new Uri("https://www.douyin.com/user/MS4wLjABAAAA1zFIBhWG-3qS8MiggDMhyCpqDnvfGYf_mtsdgtBzV7A?from_tab_name=main&showTab=post&vid=7576282367263807451");
|
|
||||||
});
|
|
||||||
// 配置HttpClient(Startup.cs或Program.cs)
|
|
||||||
services.AddHttpClient("dy_download")
|
|
||||||
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
|
|
||||||
{
|
{
|
||||||
// 控制并发连接数(根据服务器承受能力调整,建议5-20)
|
// 控制并发连接数(根据服务器承受能力调整,建议5-20)
|
||||||
MaxConnectionsPerServer = 5,
|
MaxConnectionsPerServer = 5,
|
||||||
// 禁用代理自动检测(减少不必要的延迟)
|
// 禁用代理自动检测(减少不必要的延迟)
|
||||||
UseProxy = false,
|
UseProxy = false,
|
||||||
// 连接超时(建立连接的超时时间)
|
// 连接超时(建立连接的超时时间)
|
||||||
ConnectTimeout = TimeSpan.FromSeconds(60)
|
ConnectTimeout = TimeSpan.FromSeconds(60),
|
||||||
})
|
// 忽略HTTPS证书验证
|
||||||
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
|
SslOptions = new SslClientAuthenticationOptions
|
||||||
{
|
{
|
||||||
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
|
RemoteCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
|
||||||
})
|
}
|
||||||
// 配置客户端默认请求头
|
};
|
||||||
|
|
||||||
|
return handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 抖音采集客户端
|
||||||
|
services.AddHttpClient("dy_collect", client =>
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
|
||||||
|
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36");
|
||||||
|
client.DefaultRequestHeaders.Add("Referer", "https://www.douyin.com");
|
||||||
|
}).ConfigurePrimaryHttpMessageHandler(ignoreSslHandlerFactory);
|
||||||
|
|
||||||
|
// 抖音关注客户端
|
||||||
|
services.AddHttpClient("dy_follow", client =>
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Referrer = new Uri("https://www.douyin.com/user/self?showTab=like");
|
||||||
|
}).ConfigurePrimaryHttpMessageHandler(ignoreSslHandlerFactory);
|
||||||
|
|
||||||
|
// 抖音收藏客户端
|
||||||
|
services.AddHttpClient("dy_favorite", client =>
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Referrer = new Uri("https://www.douyin.com/user/self?showTab=like");
|
||||||
|
}).ConfigurePrimaryHttpMessageHandler(ignoreSslHandlerFactory);
|
||||||
|
|
||||||
|
// 抖音UP主客户端
|
||||||
|
services.AddHttpClient("dy_uper", client =>
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Referrer = new Uri("https://www.douyin.com/user/MS4wLjABAAAA1zFIBhWG-3qS8MiggDMhyCpqDnvfGYf_mtsdgtBzV7A?from_tab_name=main&showTab=post&vid=7576282367263807451");
|
||||||
|
}).ConfigurePrimaryHttpMessageHandler(ignoreSslHandlerFactory);
|
||||||
|
|
||||||
|
// 抖音下载客户端(单独配置,保持原有特性)
|
||||||
|
services.AddHttpClient("dy_download")
|
||||||
|
.ConfigurePrimaryHttpMessageHandler(ignoreSslHandlerFactory)
|
||||||
.ConfigureHttpClient(client =>
|
.ConfigureHttpClient(client =>
|
||||||
{
|
{
|
||||||
client.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
|
client.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
|
||||||
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36");
|
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36");
|
||||||
client.DefaultRequestHeaders.Add("Referer", "https://www.douyin.com");
|
client.DefaultRequestHeaders.Add("Referer", "https://www.douyin.com");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 如果你需要兼容旧版.NET(使用HttpClientHandler而非SocketsHttpHandler),可以使用以下配置:
|
||||||
|
// Func<HttpMessageHandler> legacyIgnoreSslHandlerFactory = () =>
|
||||||
|
// {
|
||||||
|
// var handler = new HttpClientHandler
|
||||||
|
// {
|
||||||
|
// ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true,
|
||||||
|
// MaxConnectionsPerServer = 5,
|
||||||
|
// UseProxy = false,
|
||||||
|
// ConnectTimeout = TimeSpan.FromSeconds(60)
|
||||||
|
// };
|
||||||
|
// return handler;
|
||||||
|
// };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -26,11 +26,14 @@ namespace dy.net.job
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly DouyinFollowService _followService;
|
protected readonly DouyinFollowService _followService;
|
||||||
|
|
||||||
public DouyinFollowedUsersSyncJob(DouyinCookieService dyCookieService, DouyinHttpClientService douyinService, DouyinFollowService followService)
|
protected readonly DouyinCommonService douyinCommonService;
|
||||||
|
|
||||||
|
public DouyinFollowedUsersSyncJob(DouyinCookieService dyCookieService, DouyinHttpClientService douyinService, DouyinFollowService followService, DouyinCommonService douyinCommonService)
|
||||||
{
|
{
|
||||||
_dyCookieService = dyCookieService;
|
_dyCookieService = dyCookieService;
|
||||||
_douyinService = douyinService;
|
_douyinService = douyinService;
|
||||||
_followService = followService;
|
_followService = followService;
|
||||||
|
this.douyinCommonService = douyinCommonService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Execute(IJobExecutionContext context)
|
public async Task Execute(IJobExecutionContext context)
|
||||||
@@ -38,6 +41,7 @@ namespace dy.net.job
|
|||||||
var cookies = await _dyCookieService.GetAllOpendAsync();
|
var cookies = await _dyCookieService.GetAllOpendAsync();
|
||||||
if (cookies != null && cookies.Any())
|
if (cookies != null && cookies.Any())
|
||||||
{
|
{
|
||||||
|
AppConfig conf = douyinCommonService.GetConfig();
|
||||||
|
|
||||||
foreach (var ck in cookies)
|
foreach (var ck in cookies)
|
||||||
{
|
{
|
||||||
@@ -48,37 +52,52 @@ namespace dy.net.job
|
|||||||
List<FollowingsItem> follows = new List<FollowingsItem>();
|
List<FollowingsItem> follows = new List<FollowingsItem>();
|
||||||
while (hasmore)
|
while (hasmore)
|
||||||
{
|
{
|
||||||
var data = await _douyinService.SyncMyFollows(count, offset, ck.SecUserId, ck.Cookies, async err =>
|
try
|
||||||
{
|
{
|
||||||
if (err.StatusCode != 0)
|
var data = await _douyinService.SyncMyFollows(count, offset, ck.SecUserId, ck.Cookies, async err =>
|
||||||
{
|
{
|
||||||
Serilog.Log.Error($"同步用户[{ck.UserName}]关注列表时发生错误,错误信息:{err.StatusMsg},状态码:{err.StatusCode}");
|
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 (err.StatusCode == 8)
|
// 只有第一次启动时,才进行全部同步
|
||||||
|
if (!conf.IsFirstRunning)
|
||||||
{
|
{
|
||||||
hasmore = false;
|
hasmore = false;
|
||||||
}
|
}
|
||||||
ck.StatusMsg = err.StatusCode == 8 ? "已过期" : "正 常";
|
}
|
||||||
ck.StatusCode = err.StatusCode;
|
catch (Exception ex)
|
||||||
await _dyCookieService.UpdateAsync(ck);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (data != null)
|
|
||||||
{
|
{
|
||||||
// 绑定我的用户ID--之前没有这个字段
|
Serilog.Log.Error($"关注列表同步失败,{ex.Message}");
|
||||||
if (string.IsNullOrWhiteSpace(ck.MyUserId))
|
hasmore = false;
|
||||||
{
|
break;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,8 +106,8 @@ namespace dy.net.job
|
|||||||
await _followService.Sync(follows, ck );
|
await _followService.Sync(follows, ck );
|
||||||
}
|
}
|
||||||
|
|
||||||
//Serilog.Log.Debug($"当前Cookie-[{ck.UserName}],本次同步关注列表完成,当前共关注{total}人。");
|
|
||||||
}
|
}
|
||||||
|
await douyinCommonService.SetConfigNotFirstRunning();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -8,7 +8,10 @@ namespace dy.net.model
|
|||||||
[SugarTable(TableName = "dy_app_config")]
|
[SugarTable(TableName = "dy_app_config")]
|
||||||
public class AppConfig
|
public class AppConfig
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是否是程序启动时...
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFirstRunning { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -87,17 +87,18 @@ namespace dy.net.repository
|
|||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 同步关注列表(新增名字和签名变更检测)
|
/// 同步关注列表(新增名字和签名变更检测)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="followInfos"></param>
|
/// <param name="followInfos"></param>
|
||||||
/// <param name="myselfUserId"></param>
|
/// <param name="ck"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<bool> Sync(List<FollowingsItem> followInfos, DouyinCookie ck)
|
public async Task<bool> Sync(List<FollowingsItem> followInfos, DouyinCookie ck)
|
||||||
{
|
{
|
||||||
// 基础参数校验
|
// 基础参数校验
|
||||||
if (followInfos == null) followInfos = new List<FollowingsItem>();
|
if (followInfos == null) followInfos = new List<FollowingsItem>();
|
||||||
if (ck==null || string.IsNullOrWhiteSpace(ck.MyUserId))
|
if (ck == null || string.IsNullOrWhiteSpace(ck.MyUserId))
|
||||||
{
|
{
|
||||||
Serilog.Log.Error("同步关注列表失败:当前用户ID为空");
|
Serilog.Log.Error("同步关注列表失败:当前用户ID为空");
|
||||||
return false;
|
return false;
|
||||||
@@ -105,51 +106,51 @@ namespace dy.net.repository
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 1. 查询现有关注列表
|
// 1. 提取当前批次的SecUid集合(去重)
|
||||||
|
HashSet<string> currentSecUids = followInfos.Select(x => x.SecUid).ToHashSet();
|
||||||
|
if (!currentSecUids.Any())
|
||||||
|
{
|
||||||
|
Serilog.Log.Debug($"同步关注列表:当前批次无有效数据({ck.UserName}),直接返回成功");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 查询当前批次对应的现有记录(仅查需要对比的,减少数据量)
|
||||||
List<DouyinFollowed> existFollows = await Db.Queryable<DouyinFollowed>()
|
List<DouyinFollowed> existFollows = await Db.Queryable<DouyinFollowed>()
|
||||||
.Where(x => x.mySelfId == ck.MyUserId)
|
.Where(x => x.mySelfId == ck.MyUserId)
|
||||||
.Where(x=>!x.IsNoFollowed) //排除手动添加但未关注的用户
|
.Where(x => !x.IsNoFollowed)
|
||||||
|
.Where(x => currentSecUids.Contains(x.SecUid)) // 仅查当前批次的SecUid
|
||||||
.ToListAsync() ?? new List<DouyinFollowed>();
|
.ToListAsync() ?? new List<DouyinFollowed>();
|
||||||
|
|
||||||
// 2. 提取现有和当前的SecUid集合(去重优化)
|
// 3. 拆分:新增(当前批次有,数据库无) + 更新(当前批次有,数据库也有且字段变化)
|
||||||
HashSet<string> existSecUids = existFollows.Select(x => x.SecUid).ToHashSet();
|
HashSet<string> existSecUids = existFollows.Select(x => x.SecUid).ToHashSet();
|
||||||
HashSet<string> currentSecUids = followInfos.Select(x => x.SecUid).ToHashSet();
|
|
||||||
|
|
||||||
// 3. 计算新增、待删除和需要更新的记录
|
|
||||||
var toAddFollows = followInfos.Where(x => !existSecUids.Contains(x.SecUid)).ToList();
|
var toAddFollows = followInfos.Where(x => !existSecUids.Contains(x.SecUid)).ToList();
|
||||||
var toRemoveFollows = existFollows.Where(x => !currentSecUids.Contains(x.SecUid)).ToList();
|
|
||||||
|
|
||||||
// 3.1 筛选需要更新的记录(SecUid存在但名字或签名有变更)
|
|
||||||
var toUpdateFollows = new List<DouyinFollowed>();
|
var toUpdateFollows = new List<DouyinFollowed>();
|
||||||
|
|
||||||
|
// 3.1 筛选需要更新的记录
|
||||||
foreach (var existFollow in existFollows)
|
foreach (var existFollow in existFollows)
|
||||||
{
|
{
|
||||||
var newFollow = followInfos.FirstOrDefault(x => x.SecUid == existFollow.SecUid);
|
var newFollow = followInfos.FirstOrDefault(x => x.SecUid == existFollow.SecUid);
|
||||||
if (newFollow == null) continue;
|
if (newFollow == null) continue;
|
||||||
|
|
||||||
// 检查名字或签名是否变更(精确匹配,区分大小写和空格)
|
// 检查字段是否变更(精确匹配)
|
||||||
bool nameChanged = !string.Equals(existFollow.UperName, newFollow.NickName, StringComparison.Ordinal);
|
bool nameChanged = !string.Equals(existFollow.UperName, newFollow.NickName, StringComparison.Ordinal);
|
||||||
bool signatureChanged = !string.Equals(existFollow.Signature, newFollow.Signature, StringComparison.Ordinal);
|
bool signatureChanged = !string.Equals(existFollow.Signature, newFollow.Signature, StringComparison.Ordinal);
|
||||||
bool enterpriseChanged = !string.Equals(existFollow.Enterprise, newFollow.EnterpriseVerifyReason, StringComparison.Ordinal);
|
bool enterpriseChanged = !string.Equals(existFollow.Enterprise, newFollow.EnterpriseVerifyReason, StringComparison.Ordinal);
|
||||||
bool uperAvatarChanged = !string.Equals(existFollow.UperAvatar, newFollow.Avatar.UrlList?.FirstOrDefault()??"", StringComparison.Ordinal);
|
bool uperAvatarChanged = !string.Equals(existFollow.UperAvatar, newFollow.Avatar?.UrlList?.FirstOrDefault() ?? "", StringComparison.Ordinal);
|
||||||
//bool uperIdChanged = !string.Equals(existFollow.UperId, newFollow.UperId, StringComparison.Ordinal);
|
|
||||||
|
|
||||||
if (nameChanged || signatureChanged|| uperAvatarChanged|| enterpriseChanged)
|
if (nameChanged || signatureChanged || uperAvatarChanged || enterpriseChanged)
|
||||||
{
|
{
|
||||||
// 构造更新实体(仅赋值变更字段和必要字段)
|
toUpdateFollows.Add(new DouyinFollowed
|
||||||
var updateEntity = new DouyinFollowed
|
|
||||||
{
|
{
|
||||||
Id = existFollow.Id, // 主键必须保留,用于匹配
|
Id = existFollow.Id, // 主键用于匹配
|
||||||
mySelfId = existFollow.mySelfId,
|
mySelfId = ck.MyUserId,
|
||||||
SecUid = existFollow.SecUid,
|
SecUid = existFollow.SecUid,
|
||||||
UperName = newFollow.NickName, // 新名字
|
UperName = newFollow.NickName,
|
||||||
Signature = newFollow.Signature, // 新签名
|
Signature = newFollow.Signature,
|
||||||
UperId = newFollow.UperId, // 新的UperId
|
Enterprise = newFollow.EnterpriseVerifyReason,
|
||||||
UperAvatar = newFollow.Avatar?.UrlList?.FirstOrDefault() ?? "", // 新头像
|
UperAvatar = newFollow.Avatar?.UrlList?.FirstOrDefault() ?? "",
|
||||||
Enterprise = newFollow.EnterpriseVerifyReason, // 新企业认证
|
|
||||||
LastSyncTime = DateTime.UtcNow // 更新同步时间
|
LastSyncTime = DateTime.UtcNow // 更新同步时间
|
||||||
// 其他字段(如Enterprise、UperAvatar、OpenSync)保留原有值,无需赋值
|
});
|
||||||
};
|
|
||||||
toUpdateFollows.Add(updateEntity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,19 +181,17 @@ namespace dy.net.repository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 分批处理更新(适配 SQLSugar 语法:UpdateColumns + WhereColumns)
|
// 5. 分批处理更新(适配SQLSugar语法)
|
||||||
if (toUpdateFollows.Any())
|
if (toUpdateFollows.Any())
|
||||||
{
|
{
|
||||||
bool batchUpdateSuccess = await BatchProcessAsync(toUpdateFollows, 200,
|
bool batchUpdateSuccess = await BatchProcessAsync(toUpdateFollows, 200,
|
||||||
async batch =>
|
async batch =>
|
||||||
{
|
{
|
||||||
// SQLSugar 正确用法:实体集合更新 + UpdateColumns(指定更新字段) + WhereColumns(指定匹配字段/主键)
|
int affectedRows = await Db.Updateable(batch)
|
||||||
int affectedRows = await Db.Updateable(batch) // 传入更新实体集合
|
.UpdateColumns(x => new { x.UperName, x.Signature, x.LastSyncTime, x.Enterprise, x.UperAvatar })
|
||||||
.UpdateColumns(x => new { x.UperName, x.Signature, x.LastSyncTime,x.Enterprise,x.UperAvatar}) // 仅更新这3个字段
|
.WhereColumns(x => x.Id) // 按主键匹配
|
||||||
.WhereColumns(x => x.Id) // 按主键Id匹配现有记录
|
|
||||||
.ExecuteCommandAsync();
|
.ExecuteCommandAsync();
|
||||||
|
|
||||||
// 受影响行数 > 0 或 批次无数据(正常),返回true;否则返回false
|
|
||||||
return affectedRows >= 0;
|
return affectedRows >= 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -203,25 +202,8 @@ namespace dy.net.repository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 分批处理删除(单批200条)
|
// 【重要】删除逻辑已移除:增量场景下不能通过批次对比删除,需单独设计取消关注逻辑
|
||||||
if (toRemoveFollows.Any())
|
Serilog.Log.Debug($"dy_followed_users({ck.UserName})关注列表同步完成:新增{toAddFollows.Count}条,更新{toUpdateFollows.Count}条");
|
||||||
{
|
|
||||||
bool batchDeleteSuccess = await BatchProcessAsync(toRemoveFollows, 200,
|
|
||||||
async batch =>
|
|
||||||
{
|
|
||||||
var secUids = batch.Select(x => x.SecUid).ToList();
|
|
||||||
await DeleteAsync(x => x.mySelfId == ck.MyUserId && secUids.Contains(x.SecUid));
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!batchDeleteSuccess)
|
|
||||||
{
|
|
||||||
Serilog.Log.Error("同步关注列表失败:取消关注分批删除异常");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Serilog.Log.Debug($"dy_followed_users({ck.UserName})关注列表同步完成:新增{toAddFollows.Count}条,更新{toUpdateFollows.Count}条,删除{toRemoveFollows.Count}条");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -230,6 +212,144 @@ namespace dy.net.repository
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//public async Task<bool> Sync(List<FollowingsItem> followInfos, DouyinCookie ck)
|
||||||
|
//{
|
||||||
|
// // 基础参数校验
|
||||||
|
// if (followInfos == null) followInfos = new List<FollowingsItem>();
|
||||||
|
// if (ck==null || string.IsNullOrWhiteSpace(ck.MyUserId))
|
||||||
|
// {
|
||||||
|
// Serilog.Log.Error("同步关注列表失败:当前用户ID为空");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// // 1. 查询现有关注列表
|
||||||
|
// List<DouyinFollowed> existFollows = await Db.Queryable<DouyinFollowed>()
|
||||||
|
// .Where(x => x.mySelfId == ck.MyUserId)
|
||||||
|
// .Where(x=>!x.IsNoFollowed) //排除手动添加但未关注的用户
|
||||||
|
// .ToListAsync() ?? new List<DouyinFollowed>();
|
||||||
|
|
||||||
|
// // 2. 提取现有和当前的SecUid集合(去重优化)
|
||||||
|
// HashSet<string> existSecUids = existFollows.Select(x => x.SecUid).ToHashSet();
|
||||||
|
// HashSet<string> currentSecUids = followInfos.Select(x => x.SecUid).ToHashSet();
|
||||||
|
|
||||||
|
// // 3. 计算新增、待删除和需要更新的记录
|
||||||
|
// var toAddFollows = followInfos.Where(x => !existSecUids.Contains(x.SecUid)).ToList();
|
||||||
|
// var toRemoveFollows = existFollows.Where(x => !currentSecUids.Contains(x.SecUid)).ToList();
|
||||||
|
|
||||||
|
// // 3.1 筛选需要更新的记录(SecUid存在但名字或签名有变更)
|
||||||
|
// var toUpdateFollows = new List<DouyinFollowed>();
|
||||||
|
// foreach (var existFollow in existFollows)
|
||||||
|
// {
|
||||||
|
// var newFollow = followInfos.FirstOrDefault(x => x.SecUid == existFollow.SecUid);
|
||||||
|
// if (newFollow == null) continue;
|
||||||
|
|
||||||
|
// // 检查名字或签名是否变更(精确匹配,区分大小写和空格)
|
||||||
|
// bool nameChanged = !string.Equals(existFollow.UperName, newFollow.NickName, StringComparison.Ordinal);
|
||||||
|
// bool signatureChanged = !string.Equals(existFollow.Signature, newFollow.Signature, StringComparison.Ordinal);
|
||||||
|
// bool enterpriseChanged = !string.Equals(existFollow.Enterprise, newFollow.EnterpriseVerifyReason, StringComparison.Ordinal);
|
||||||
|
// bool uperAvatarChanged = !string.Equals(existFollow.UperAvatar, newFollow.Avatar.UrlList?.FirstOrDefault()??"", StringComparison.Ordinal);
|
||||||
|
// //bool uperIdChanged = !string.Equals(existFollow.UperId, newFollow.UperId, StringComparison.Ordinal);
|
||||||
|
|
||||||
|
// if (nameChanged || signatureChanged|| uperAvatarChanged|| enterpriseChanged)
|
||||||
|
// {
|
||||||
|
// // 构造更新实体(仅赋值变更字段和必要字段)
|
||||||
|
// var updateEntity = new DouyinFollowed
|
||||||
|
// {
|
||||||
|
// Id = existFollow.Id, // 主键必须保留,用于匹配
|
||||||
|
// mySelfId = existFollow.mySelfId,
|
||||||
|
// SecUid = existFollow.SecUid,
|
||||||
|
// UperName = newFollow.NickName, // 新名字
|
||||||
|
// Signature = newFollow.Signature, // 新签名
|
||||||
|
// UperId = newFollow.UperId, // 新的UperId
|
||||||
|
// UperAvatar = newFollow.Avatar?.UrlList?.FirstOrDefault() ?? "", // 新头像
|
||||||
|
// Enterprise = newFollow.EnterpriseVerifyReason, // 新企业认证
|
||||||
|
// LastSyncTime = DateTime.UtcNow // 更新同步时间
|
||||||
|
// // 其他字段(如Enterprise、UperAvatar、OpenSync)保留原有值,无需赋值
|
||||||
|
// };
|
||||||
|
// toUpdateFollows.Add(updateEntity);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 4. 分批处理新增(单批200条)
|
||||||
|
// if (toAddFollows.Any())
|
||||||
|
// {
|
||||||
|
// Func<FollowingsItem, DouyinFollowed> mapToDouyinFollowed = follow => new DouyinFollowed
|
||||||
|
// {
|
||||||
|
// Id = IdGener.GetLong().ToString(),
|
||||||
|
// Enterprise = follow.EnterpriseVerifyReason,
|
||||||
|
// LastSyncTime = DateTime.UtcNow,
|
||||||
|
// mySelfId = ck.MyUserId,
|
||||||
|
// SecUid = follow.SecUid,
|
||||||
|
// OpenSync = false,
|
||||||
|
// UperAvatar = follow.Avatar?.UrlList?.FirstOrDefault() ?? "",
|
||||||
|
// UperName = follow.NickName,
|
||||||
|
// Signature = follow.Signature,
|
||||||
|
// UperId = follow.UperId
|
||||||
|
// };
|
||||||
|
|
||||||
|
// bool batchAddSuccess = await BatchProcessAsync(toAddFollows, 200,
|
||||||
|
// async batch => await BatchInsert(batch.Select(mapToDouyinFollowed).ToList()));
|
||||||
|
|
||||||
|
// if (!batchAddSuccess)
|
||||||
|
// {
|
||||||
|
// Serilog.Log.Error("同步关注列表失败:新增关注分批插入异常");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 5. 分批处理更新(适配 SQLSugar 语法:UpdateColumns + WhereColumns)
|
||||||
|
// if (toUpdateFollows.Any())
|
||||||
|
// {
|
||||||
|
// bool batchUpdateSuccess = await BatchProcessAsync(toUpdateFollows, 200,
|
||||||
|
// async batch =>
|
||||||
|
// {
|
||||||
|
// // SQLSugar 正确用法:实体集合更新 + UpdateColumns(指定更新字段) + WhereColumns(指定匹配字段/主键)
|
||||||
|
// int affectedRows = await Db.Updateable(batch) // 传入更新实体集合
|
||||||
|
// .UpdateColumns(x => new { x.UperName, x.Signature, x.LastSyncTime,x.Enterprise,x.UperAvatar}) // 仅更新这3个字段
|
||||||
|
// .WhereColumns(x => x.Id) // 按主键Id匹配现有记录
|
||||||
|
// .ExecuteCommandAsync();
|
||||||
|
|
||||||
|
// // 受影响行数 > 0 或 批次无数据(正常),返回true;否则返回false
|
||||||
|
// return affectedRows >= 0;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (!batchUpdateSuccess)
|
||||||
|
// {
|
||||||
|
// Serilog.Log.Error("同步关注列表失败:关注信息更新异常");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 6. 分批处理删除(单批200条)
|
||||||
|
// if (toRemoveFollows.Any())
|
||||||
|
// {
|
||||||
|
// bool batchDeleteSuccess = await BatchProcessAsync(toRemoveFollows, 200,
|
||||||
|
// async batch =>
|
||||||
|
// {
|
||||||
|
// var secUids = batch.Select(x => x.SecUid).ToList();
|
||||||
|
// await DeleteAsync(x => x.mySelfId == ck.MyUserId && secUids.Contains(x.SecUid));
|
||||||
|
// return true;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (!batchDeleteSuccess)
|
||||||
|
// {
|
||||||
|
// Serilog.Log.Error("同步关注列表失败:取消关注分批删除异常");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Serilog.Log.Debug($"dy_followed_users({ck.UserName})关注列表同步完成:新增{toAddFollows.Count}条,更新{toUpdateFollows.Count}条,删除{toRemoveFollows.Count}条");
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// Serilog.Log.Error(ex, $"同步关注列表失败({ck.UserName}):{ex.Message}");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通用分批处理工具方法
|
/// 通用分批处理工具方法
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ namespace dy.net.service
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化并返回配置
|
/// 初始化并返回配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="downLoadImage"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public AppConfig InitConfig()
|
public AppConfig InitConfig()
|
||||||
{
|
{
|
||||||
@@ -34,9 +33,10 @@ namespace dy.net.service
|
|||||||
if (string.IsNullOrWhiteSpace(conf.PriorityLevel))
|
if (string.IsNullOrWhiteSpace(conf.PriorityLevel))
|
||||||
{
|
{
|
||||||
conf.PriorityLevel = "[{\"id\":1,\"name\":\"喜欢的视频\",\"sort\":1},{\"id\":2,\"name\":\"收藏的视频\",\"sort\":2},{\"id\":3,\"name\":\"关注的视频\",\"sort\":3}]";
|
conf.PriorityLevel = "[{\"id\":1,\"name\":\"喜欢的视频\",\"sort\":1},{\"id\":2,\"name\":\"收藏的视频\",\"sort\":2},{\"id\":3,\"name\":\"关注的视频\",\"sort\":3}]";
|
||||||
sqlSugarClient.Updateable(conf).ExecuteCommand();
|
|
||||||
//兼容旧版本
|
|
||||||
}
|
}
|
||||||
|
conf.IsFirstRunning = true;//标记为程序刚启动第一次运行
|
||||||
|
sqlSugarClient.Updateable(conf).ExecuteCommand();
|
||||||
|
//兼容旧版本
|
||||||
return conf;
|
return conf;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -57,7 +57,8 @@ namespace dy.net.service
|
|||||||
FullFollowedTitleTemplate = "",
|
FullFollowedTitleTemplate = "",
|
||||||
FollowedTitleSeparator = "",
|
FollowedTitleSeparator = "",
|
||||||
AutoDistinct = true,//默认开启
|
AutoDistinct = true,//默认开启
|
||||||
PriorityLevel= "[{\"id\":1,\"name\":\"喜欢的视频\",\"sort\":1},{\"id\":2,\"name\":\"收藏的视频\",\"sort\":2},{\"id\":3,\"name\":\"关注的视频\",\"sort\":3}]"
|
PriorityLevel = "[{\"id\":1,\"name\":\"喜欢的视频\",\"sort\":1},{\"id\":2,\"name\":\"收藏的视频\",\"sort\":2},{\"id\":3,\"name\":\"关注的视频\",\"sort\":3}]",
|
||||||
|
IsFirstRunning = true
|
||||||
};
|
};
|
||||||
sqlSugarClient.Insertable(config).ExecuteCommand();
|
sqlSugarClient.Insertable(config).ExecuteCommand();
|
||||||
return config;
|
return config;
|
||||||
@@ -66,6 +67,25 @@ namespace dy.net.service
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新AppConfig的IsFirstRunning属性为fasle
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task SetConfigNotFirstRunning()
|
||||||
|
{
|
||||||
|
//更新IsFirstRunning为false
|
||||||
|
await sqlSugarClient.Updateable<AppConfig>()
|
||||||
|
.SetColumns(x => x.IsFirstRunning == false)
|
||||||
|
.Where(x => x.IsFirstRunning == true)
|
||||||
|
.ExecuteCommandAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新配置
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
|
||||||
public async Task<bool> UpdateConfig(AppConfig config)
|
public async Task<bool> UpdateConfig(AppConfig config)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -293,25 +293,18 @@ namespace dy.net.service
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Serilog.Log.Error($"SyncMyFollows error: {ex.Message}");
|
Serilog.Log.Error($"SyncMyFollows error: {ex.Message}");
|
||||||
return null;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<bool> SyncReDown(string awemeId,string cookie)
|
//
|
||||||
{
|
|
||||||
return false;
|
|
||||||
//有空再研究把.a_bogus算法有问题
|
|
||||||
}
|
|
||||||
|
|
||||||
//AI优化2
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 下载文件并保存到本地(支持重试机制,优化批量下载稳定性)
|
/// 下载文件并保存到本地(支持重试机制,优化批量下载稳定性)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="videoUrl">文件地址</param>
|
/// <param name="videoUrl">文件地址</param>
|
||||||
/// <param name="savePath">保存路径</param>
|
/// <param name="savePath">保存路径</param>
|
||||||
/// <param name="cookie">请求Cookie</param>
|
/// <param name="cookie">请求Cookie</param>
|
||||||
/// <param name="httpclientName">HttpClient名称(默认"dy_down1")</param>
|
|
||||||
/// <param name="cancellationToken">取消令牌(用于终止任务)</param>
|
/// <param name="cancellationToken">取消令牌(用于终止任务)</param>
|
||||||
/// <param name="streamTimeout">流读取超时时间(默认60秒)</param>
|
/// <param name="streamTimeout">流读取超时时间(默认60秒)</param>
|
||||||
/// <param name="maxRetryCount">最大重试次数(默认3次)</param>
|
/// <param name="maxRetryCount">最大重试次数(默认3次)</param>
|
||||||
|
|||||||
@@ -102,15 +102,15 @@ namespace dy.net.service
|
|||||||
|
|
||||||
// 3. 只启动第一个任务(collect),后续任务由 Listener 自动触发
|
// 3. 只启动第一个任务(collect),后续任务由 Listener 自动触发
|
||||||
var firstJobConfigKey = "collect";
|
var firstJobConfigKey = "collect";
|
||||||
var startSuccess = await StartJobAsync(firstJobConfigKey, expression);
|
var startSuccess = await StartJobAsync(firstJobConfigKey, expression);
|
||||||
|
|
||||||
if (startSuccess)
|
if (startSuccess)
|
||||||
{
|
{
|
||||||
Log.Debug("【任务服务】任务链条启动成功!执行顺序:collect → favorite → followed → follow_user", expression);
|
Log.Debug($"任务开始执行:collect → favorite → followed → follow_user-->每{expression}分钟执行一次...");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.Error("【任务服务】任务链条启动失败(第一个任务 {FirstJob} 启动失败)", firstJobConfigKey);
|
Log.Error($"任务执行失败(任务 {firstJobConfigKey} 启动失败)");
|
||||||
}
|
}
|
||||||
//启动follow_user--这个与其他几个任务没有依赖关系,所以单独启动
|
//启动follow_user--这个与其他几个任务没有依赖关系,所以单独启动
|
||||||
await StartJobAsync("follow_user", expression);
|
await StartJobAsync("follow_user", expression);
|
||||||
|
|||||||
Reference in New Issue
Block a user