feat: add fnOS packaging, storage workflows and release pipeline

This commit is contained in:
2026-08-11 18:05:49 +08:00
parent c5922f9b08
commit 95932f0199
181 changed files with 24024 additions and 1164 deletions
+5 -5
View File
@@ -106,12 +106,12 @@ namespace dy.net.repository
/// <returns></returns>
public async Task<bool> UseTranAsync(Func<Task> action, Action<Exception> errorCallBack)
{
var res = Db.Ado.UseTranAsync(async () =>
{
await action();
}, errorCallBack: errorCallBack);
var res = await Db.Ado.UseTranAsync(async () =>
{
await action();
}, errorCallBack: errorCallBack);
return res.IsCompletedSuccessfully;
return res.IsSuccess;
}
/// <summary>
+39 -16
View File
@@ -18,20 +18,39 @@ namespace dy.net.repository
public async Task<List<DouyinFollowGroupDto>> GetDouyinFollowGroup()
{
var data = this.Db.Queryable<DouyinFollowed>()
.LeftJoin<DouyinCookie>((f, u) => f.mySelfId == u.MyUserId)
.Where((f,u)=>!string.IsNullOrEmpty(u.MyUserId)&&!string.IsNullOrEmpty(f.mySelfId))
//.Where((f, u) => u.Status == 1) // 注意:LeftJoin+u.Status==1 等价于 InnerJoinu必须存在)
.GroupBy((f, u) => f.mySelfId) // 按 mySelfId 分组
.Select((f, u) => new DouyinFollowGroupDto
{
Key = f.mySelfId, // 分组键(mySelfId
Name = u.UserName, // 取每组的 UserName(因分组唯一,用 First() 兼容 SQL
Total = SqlFunc.AggregateCount(f.Id) // 统计每组的记录数(用主表主键避免 null 影响)
})
.ToList(); // 执行查询
// 账号页签必须来自 Cookie 表,而不是关注表。否则一个刚配置、尚未同步过关注列表的
// 账号不会出现在“新增博主”的账号选择器里,也就无法添加第一位非关注博主。
var cookies = await Db.Queryable<DouyinCookie>()
.Where(x => !string.IsNullOrEmpty(x.MyUserId))
.OrderBy(x => x.UserName)
.ToListAsync();
var counts = await Db.Queryable<DouyinFollowed>()
.Where(x => !string.IsNullOrEmpty(x.mySelfId))
.GroupBy(x => x.mySelfId)
.Select(x => new DouyinFollowGroupDto
{
Key = x.mySelfId,
Total = SqlFunc.AggregateCount(x.Id)
})
.ToListAsync();
var countByUser = counts.ToDictionary(x => x.Key, x => x.Total, StringComparer.Ordinal);
return data;
return cookies
.GroupBy(x => x.MyUserId, StringComparer.Ordinal)
.Select(group => group
.OrderByDescending(x => x.Status == 1 && x.StatusCode == 0)
.First())
.Select(cookie => new DouyinFollowGroupDto
{
Key = cookie.MyUserId,
CookieId = cookie.Id,
Name = cookie.UserName,
Total = countByUser.GetValueOrDefault(cookie.MyUserId),
Status = cookie.Status,
StatusCode = cookie.StatusCode,
StatusMessage = cookie.StatusMsg
})
.ToList();
}
/// <summary>
@@ -157,7 +176,8 @@ namespace dy.net.repository
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 dyNoChanged = !string.Equals(existFollow.DouyinNo, newFollow.ShortId ?? "", StringComparison.Ordinal);
var douyinNo = ResolveDouyinNo(newFollow);
bool dyNoChanged = !string.Equals(existFollow.DouyinNo, douyinNo, StringComparison.Ordinal);
if (nameChanged || signatureChanged || uperAvatarChanged || enterpriseChanged|| dyNoChanged)
{
@@ -171,7 +191,7 @@ namespace dy.net.repository
Enterprise = newFollow.EnterpriseVerifyReason,
UperAvatar = newFollow.Avatar?.UrlList?.FirstOrDefault() ?? "",
LastSyncTime = DateTime.UtcNow,// 更新同步时间
DouyinNo = newFollow.ShortId
DouyinNo = douyinNo
};
if (string.IsNullOrWhiteSpace(existFollow.SavePath))
{
@@ -197,7 +217,7 @@ namespace dy.net.repository
Signature = follow.Signature,
UperId = follow.UperId,
SavePath = DouyinFileNameHelper.SanitizeLinuxFileName(follow.NickName, follow.UperId, true),
DouyinNo = follow.ShortId
DouyinNo = ResolveDouyinNo(follow)
};
bool batchAddSuccess = await BatchProcessAsync(toAddFollows, 200,
@@ -246,6 +266,9 @@ namespace dy.net.repository
}
}
private static string ResolveDouyinNo(FollowingsItem follow) =>
!string.IsNullOrWhiteSpace(follow?.UniqueId) ? follow.UniqueId : follow?.ShortId ?? string.Empty;
/// <summary>
/// 通用分批处理工具方法
+1
View File
@@ -43,6 +43,7 @@ namespace dy.net.repository
//.WhereIF(!string.IsNullOrWhiteSpace(title), x => x.VideoTitle.Contains(title))
.WhereIF(!string.IsNullOrWhiteSpace(dto.Title), x => x.VideoTitle.Contains(dto.Title))
.WhereIF(!string.IsNullOrWhiteSpace(dto.Author), x => x.Author.Contains(dto.Author))
.WhereIF(!string.IsNullOrWhiteSpace(dto.AuthorId), x => x.AuthorId == dto.AuthorId)
.WhereIF(!string.IsNullOrWhiteSpace(dto.Tag), x => x.Tag1 == dto.Tag)
.WhereIF(start.HasValue, x => x.SyncTime >= start.Value)
.WhereIF(end.HasValue, x => x.SyncTime <= end.Value)