refactor: Bot频道全局共享(共用分类与频道)
This commit is contained in:
1 parent
efbe67791d
commit
7d8f93b4c8
23 files changed
+1362
-76
No files matched your search
@@ -10,17 +10,20 @@ public class BotManagementService
|
||||
private readonly IBotRepository _botRepository;
|
||||
private readonly IBotChannelRepository _botChannelRepository;
|
||||
private readonly IBotChannelCategoryRepository _categoryRepository;
|
||||
private readonly IBotChannelMemberRepository _memberRepository;
|
||||
private readonly ILogger<BotManagementService> _logger;
|
||||
|
||||
public BotManagementService(
|
||||
IBotRepository botRepository,
|
||||
IBotChannelRepository botChannelRepository,
|
||||
IBotChannelCategoryRepository categoryRepository,
|
||||
IBotChannelMemberRepository memberRepository,
|
||||
ILogger<BotManagementService> logger)
|
||||
{
|
||||
_botRepository = botRepository;
|
||||
_botChannelRepository = botChannelRepository;
|
||||
_categoryRepository = categoryRepository;
|
||||
_memberRepository = memberRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -96,28 +99,25 @@ public class BotManagementService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<BotChannelCategory>> GetCategoriesAsync(int botId)
|
||||
public async Task<IEnumerable<BotChannelCategory>> GetCategoriesAsync()
|
||||
{
|
||||
return await _categoryRepository.GetForBotAsync(botId);
|
||||
return await _categoryRepository.GetAllOrderedAsync();
|
||||
}
|
||||
|
||||
public async Task<BotChannelCategory> CreateCategoryAsync(int botId, string name, string? description = null)
|
||||
public async Task<BotChannelCategory> CreateCategoryAsync(string name, string? description = null)
|
||||
{
|
||||
name = (name ?? string.Empty).Trim();
|
||||
description = string.IsNullOrWhiteSpace(description) ? null : description.Trim();
|
||||
|
||||
if (botId <= 0)
|
||||
throw new ArgumentException("botId 无效", nameof(botId));
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
throw new ArgumentException("分类名称不能为空", nameof(name));
|
||||
|
||||
var existing = await _categoryRepository.GetByNameAsync(botId, name);
|
||||
var existing = await _categoryRepository.GetByNameAsync(name);
|
||||
if (existing != null)
|
||||
throw new InvalidOperationException("分类名称已存在");
|
||||
|
||||
var cat = new BotChannelCategory
|
||||
{
|
||||
BotId = botId,
|
||||
Name = name,
|
||||
Description = description,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
@@ -173,15 +173,22 @@ public class BotManagementService
|
||||
return await _botChannelRepository.GetByTelegramIdAsync(botId, telegramId);
|
||||
}
|
||||
|
||||
public async Task<BotChannel> UpsertChannelAsync(BotChannel channel)
|
||||
public async Task<BotChannel> UpsertChannelAsync(int botId, BotChannel channel)
|
||||
{
|
||||
if (channel.BotId <= 0)
|
||||
throw new ArgumentException("BotId 无效", nameof(channel));
|
||||
if (botId <= 0)
|
||||
throw new ArgumentException("BotId 无效", nameof(botId));
|
||||
if (channel.TelegramId == 0)
|
||||
throw new ArgumentException("TelegramId 无效", nameof(channel));
|
||||
|
||||
var existing = await _botChannelRepository.GetByTelegramIdAsync(channel.BotId, channel.TelegramId);
|
||||
if (existing != null)
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var existing = await _botChannelRepository.GetGlobalByTelegramIdAsync(channel.TelegramId);
|
||||
if (existing == null)
|
||||
{
|
||||
channel.SyncedAt = now;
|
||||
existing = await _botChannelRepository.AddAsync(channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
existing.Title = channel.Title;
|
||||
existing.Username = channel.Username;
|
||||
@@ -191,23 +198,27 @@ public class BotManagementService
|
||||
existing.AccessHash = channel.AccessHash;
|
||||
if (channel.CreatedAt.HasValue)
|
||||
existing.CreatedAt = channel.CreatedAt;
|
||||
existing.SyncedAt = DateTime.UtcNow;
|
||||
existing.SyncedAt = now;
|
||||
|
||||
await _botChannelRepository.UpdateAsync(existing);
|
||||
return existing;
|
||||
}
|
||||
|
||||
channel.SyncedAt = DateTime.UtcNow;
|
||||
return await _botChannelRepository.AddAsync(channel);
|
||||
await _memberRepository.UpsertAsync(botId, existing.Id, now);
|
||||
return existing;
|
||||
}
|
||||
|
||||
public async Task DeleteChannelByTelegramIdAsync(int botId, long telegramId)
|
||||
{
|
||||
var existing = await _botChannelRepository.GetByTelegramIdAsync(botId, telegramId);
|
||||
if (existing == null)
|
||||
var ch = await _botChannelRepository.GetByTelegramIdAsync(botId, telegramId);
|
||||
if (ch == null)
|
||||
return;
|
||||
|
||||
await _botChannelRepository.DeleteAsync(existing);
|
||||
await _memberRepository.DeleteAsync(botId, ch.Id);
|
||||
|
||||
// 清理无任何 Bot 关联的“孤儿频道”
|
||||
var remains = await _memberRepository.CountForChannelAsync(ch.Id);
|
||||
if (remains == 0)
|
||||
await _botChannelRepository.DeleteAsync(ch);
|
||||
}
|
||||
|
||||
public async Task SetChannelCategoryAsync(int botChannelId, int? categoryId)
|
||||
|
||||
@@ -256,9 +256,8 @@ public class BotTelegramService
|
||||
// left/kicked 或降权为 member 等,直接从列表移除(否则导出邀请会失败且列表不准)。
|
||||
if (change.Status is "administrator" or "creator")
|
||||
{
|
||||
await _botManagement.UpsertChannelAsync(new BotChannel
|
||||
await _botManagement.UpsertChannelAsync(bot.Id, new BotChannel
|
||||
{
|
||||
BotId = bot.Id,
|
||||
TelegramId = chatId,
|
||||
Title = string.IsNullOrWhiteSpace(change.Chat.Title) ? $"频道 {chatId}" : change.Chat.Title,
|
||||
Username = string.IsNullOrWhiteSpace(change.Chat.Username) ? null : change.Chat.Username.Trim().TrimStart('@'),
|
||||
|
||||
@@ -23,6 +23,7 @@ public class AppDbContext : DbContext
|
||||
public DbSet<Bot> Bots => Set<Bot>();
|
||||
public DbSet<BotChannel> BotChannels => Set<BotChannel>();
|
||||
public DbSet<BotChannelCategory> BotChannelCategories => Set<BotChannelCategory>();
|
||||
public DbSet<BotChannelMember> BotChannelMembers => Set<BotChannelMember>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@@ -166,12 +167,7 @@ public class AppDbContext : DbContext
|
||||
entity.Property(e => e.Name).IsRequired().HasMaxLength(100);
|
||||
entity.Property(e => e.Description).HasMaxLength(500);
|
||||
|
||||
entity.HasIndex(e => new { e.BotId, e.Name }).IsUnique();
|
||||
|
||||
entity.HasOne(e => e.Bot)
|
||||
.WithMany(b => b.Categories)
|
||||
.HasForeignKey(e => e.BotId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
entity.HasIndex(e => e.Name).IsUnique();
|
||||
});
|
||||
|
||||
// BotChannel配置
|
||||
@@ -182,19 +178,33 @@ public class AppDbContext : DbContext
|
||||
entity.Property(e => e.Username).HasMaxLength(100);
|
||||
entity.Property(e => e.About).HasMaxLength(1000);
|
||||
|
||||
entity.HasIndex(e => new { e.BotId, e.TelegramId }).IsUnique();
|
||||
entity.HasIndex(e => e.TelegramId).IsUnique();
|
||||
entity.HasIndex(e => e.Username);
|
||||
entity.HasIndex(e => e.CategoryId);
|
||||
|
||||
entity.HasOne(e => e.Bot)
|
||||
.WithMany(b => b.Channels)
|
||||
.HasForeignKey(e => e.BotId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasOne(e => e.Category)
|
||||
.WithMany(c => c.Channels)
|
||||
.HasForeignKey(e => e.CategoryId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
// BotChannelMember 配置
|
||||
modelBuilder.Entity<BotChannelMember>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => new { e.BotId, e.BotChannelId }).IsUnique();
|
||||
entity.HasIndex(e => e.BotId);
|
||||
entity.HasIndex(e => e.BotChannelId);
|
||||
|
||||
entity.HasOne(e => e.Bot)
|
||||
.WithMany(b => b.ChannelMembers)
|
||||
.HasForeignKey(e => e.BotId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasOne(e => e.BotChannel)
|
||||
.WithMany(c => c.Members)
|
||||
.HasForeignKey(e => e.BotChannelId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
||||
namespace TelegramPanel.Data;
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 dotnet-ef 在设计时创建 DbContext(不影响运行时)。
|
||||
/// </summary>
|
||||
public sealed class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
|
||||
{
|
||||
public AppDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<AppDbContext>()
|
||||
.UseSqlite("Data Source=design-time.db")
|
||||
.Options;
|
||||
|
||||
return new AppDbContext(options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,5 @@ public class Bot
|
||||
public DateTime? LastSyncAt { get; set; }
|
||||
public long? LastUpdateId { get; set; }
|
||||
|
||||
public ICollection<BotChannelCategory> Categories { get; set; } = new List<BotChannelCategory>();
|
||||
public ICollection<BotChannel> Channels { get; set; } = new List<BotChannel>();
|
||||
public ICollection<BotChannelMember> ChannelMembers { get; set; } = new List<BotChannelMember>();
|
||||
}
|
||||
@@ -6,7 +6,6 @@ namespace TelegramPanel.Data.Entities;
|
||||
public class BotChannel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int BotId { get; set; }
|
||||
public long TelegramId { get; set; }
|
||||
public long? AccessHash { get; set; }
|
||||
public string Title { get; set; } = null!;
|
||||
@@ -19,7 +18,6 @@ public class BotChannel
|
||||
|
||||
public int? CategoryId { get; set; }
|
||||
|
||||
public Bot? Bot { get; set; }
|
||||
public BotChannelCategory? Category { get; set; }
|
||||
public ICollection<BotChannelMember> Members { get; set; } = new List<BotChannelMember>();
|
||||
}
|
||||
|
||||
@@ -6,12 +6,9 @@ namespace TelegramPanel.Data.Entities;
|
||||
public class BotChannelCategory
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int BotId { get; set; }
|
||||
public string Name { get; set; } = null!;
|
||||
public string? Description { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public Bot? Bot { get; set; }
|
||||
public ICollection<BotChannel> Channels { get; set; } = new List<BotChannel>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace TelegramPanel.Data.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Bot - 频道/群 成员关系(用于记录“哪个 Bot 在哪个聊天里”)。
|
||||
/// </summary>
|
||||
public class BotChannelMember
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int BotId { get; set; }
|
||||
public int BotChannelId { get; set; }
|
||||
|
||||
public DateTime SyncedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public Bot Bot { get; set; } = null!;
|
||||
public BotChannel BotChannel { get; set; } = null!;
|
||||
}
|
||||
|
||||
+620
@@ -0,0 +1,620 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using TelegramPanel.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace TelegramPanel.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20260103094343_GlobalizeBotChannels")]
|
||||
partial class GlobalizeBotChannels
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.22");
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Account", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ApiHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ApiId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("CategoryId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("LastLoginAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastSyncAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Nickname")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SessionPath")
|
||||
.IsRequired()
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("TelegramStatusCheckedAtUtc")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TelegramStatusDetails")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool?>("TelegramStatusOk")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TelegramStatusSummary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TwoFactorPassword")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.HasIndex("Phone")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.AccountCategory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Color")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("ExcludeFromOperations")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("AccountCategories");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.AccountChannel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AccountId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsAdmin")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsCreator")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("SyncedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ChannelId");
|
||||
|
||||
b.HasIndex("AccountId", "ChannelId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("AccountChannels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BatchTask", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Completed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("CompletedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Config")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Failed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("StartedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TaskType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Total")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedAt");
|
||||
|
||||
b.HasIndex("Status");
|
||||
|
||||
b.ToTable("BatchTasks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Bot", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("LastSyncAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long?>("LastUpdateId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Username");
|
||||
|
||||
b.ToTable("Bots");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("About")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long?>("AccessHash")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("CategoryId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsBroadcast")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MemberCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("SyncedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("TelegramId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.HasIndex("TelegramId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Username");
|
||||
|
||||
b.ToTable("BotChannels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannelCategory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("BotChannelCategories");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannelMember", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("BotChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("BotId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("SyncedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("BotChannelId");
|
||||
|
||||
b.HasIndex("BotId");
|
||||
|
||||
b.HasIndex("BotId", "BotChannelId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("BotChannelMembers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Channel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("About")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long?>("AccessHash")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("CreatorAccountId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("GroupId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsBroadcast")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MemberCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("SyncedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("TelegramId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatorAccountId");
|
||||
|
||||
b.HasIndex("GroupId");
|
||||
|
||||
b.HasIndex("TelegramId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Username");
|
||||
|
||||
b.ToTable("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.ChannelGroup", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ChannelGroups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Group", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("About")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long?>("AccessHash")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("CreatorAccountId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MemberCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("SyncedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("TelegramId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatorAccountId");
|
||||
|
||||
b.HasIndex("TelegramId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Username");
|
||||
|
||||
b.ToTable("Groups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Account", b =>
|
||||
{
|
||||
b.HasOne("TelegramPanel.Data.Entities.AccountCategory", "Category")
|
||||
.WithMany("Accounts")
|
||||
.HasForeignKey("CategoryId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Category");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.AccountChannel", b =>
|
||||
{
|
||||
b.HasOne("TelegramPanel.Data.Entities.Account", "Account")
|
||||
.WithMany("AccountChannels")
|
||||
.HasForeignKey("AccountId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("TelegramPanel.Data.Entities.Channel", "Channel")
|
||||
.WithMany("AccountChannels")
|
||||
.HasForeignKey("ChannelId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Account");
|
||||
|
||||
b.Navigation("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannel", b =>
|
||||
{
|
||||
b.HasOne("TelegramPanel.Data.Entities.BotChannelCategory", "Category")
|
||||
.WithMany("Channels")
|
||||
.HasForeignKey("CategoryId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Category");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannelMember", b =>
|
||||
{
|
||||
b.HasOne("TelegramPanel.Data.Entities.BotChannel", "BotChannel")
|
||||
.WithMany("Members")
|
||||
.HasForeignKey("BotChannelId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("TelegramPanel.Data.Entities.Bot", "Bot")
|
||||
.WithMany("ChannelMembers")
|
||||
.HasForeignKey("BotId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Bot");
|
||||
|
||||
b.Navigation("BotChannel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Channel", b =>
|
||||
{
|
||||
b.HasOne("TelegramPanel.Data.Entities.Account", "CreatorAccount")
|
||||
.WithMany("Channels")
|
||||
.HasForeignKey("CreatorAccountId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("TelegramPanel.Data.Entities.ChannelGroup", "Group")
|
||||
.WithMany("Channels")
|
||||
.HasForeignKey("GroupId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CreatorAccount");
|
||||
|
||||
b.Navigation("Group");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Group", b =>
|
||||
{
|
||||
b.HasOne("TelegramPanel.Data.Entities.Account", "CreatorAccount")
|
||||
.WithMany("Groups")
|
||||
.HasForeignKey("CreatorAccountId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("CreatorAccount");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Account", b =>
|
||||
{
|
||||
b.Navigation("AccountChannels");
|
||||
|
||||
b.Navigation("Channels");
|
||||
|
||||
b.Navigation("Groups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.AccountCategory", b =>
|
||||
{
|
||||
b.Navigation("Accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Bot", b =>
|
||||
{
|
||||
b.Navigation("ChannelMembers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannel", b =>
|
||||
{
|
||||
b.Navigation("Members");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannelCategory", b =>
|
||||
{
|
||||
b.Navigation("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Channel", b =>
|
||||
{
|
||||
b.Navigation("AccountChannels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.ChannelGroup", b =>
|
||||
{
|
||||
b.Navigation("Channels");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace TelegramPanel.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class GlobalizeBotChannels : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// 将“每个 Bot 一套 BotChannels/BotChannelCategories”的旧结构,升级为:
|
||||
// - BotChannels:全局唯一(TelegramId 唯一)
|
||||
// - BotChannelCategories:全局唯一(Name 唯一)
|
||||
// - BotChannelMembers:Bot-频道关系(哪个 Bot 在哪个频道里)
|
||||
//
|
||||
// 分类合并策略(用户选择 A):同一 TelegramId 在多 Bot 下若存在不同分类,
|
||||
// 取“出现次数最多”的分类作为全局分类。
|
||||
|
||||
migrationBuilder.Sql("ALTER TABLE BotChannels RENAME TO BotChannels_Old;");
|
||||
migrationBuilder.Sql("ALTER TABLE BotChannelCategories RENAME TO BotChannelCategories_Old;");
|
||||
|
||||
// SQLite:表重命名后索引名不会自动变更;为避免与新表的同名索引冲突,这里先删旧索引(数据复制不依赖它们)。
|
||||
migrationBuilder.Sql("DROP INDEX IF EXISTS IX_BotChannels_BotId_TelegramId;");
|
||||
migrationBuilder.Sql("DROP INDEX IF EXISTS IX_BotChannels_Username;");
|
||||
migrationBuilder.Sql("DROP INDEX IF EXISTS IX_BotChannels_CategoryId;");
|
||||
migrationBuilder.Sql("DROP INDEX IF EXISTS IX_BotChannelCategories_BotId_Name;");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BotChannelCategories",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Name = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
|
||||
Description = table.Column<string>(type: "TEXT", maxLength: 500, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BotChannelCategories", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BotChannels",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
TelegramId = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
AccessHash = table.Column<long>(type: "INTEGER", nullable: true),
|
||||
Title = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
|
||||
Username = table.Column<string>(type: "TEXT", maxLength: 100, nullable: true),
|
||||
IsBroadcast = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
MemberCount = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
About = table.Column<string>(type: "TEXT", maxLength: 1000, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
SyncedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
CategoryId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BotChannels", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_BotChannels_BotChannelCategories_CategoryId",
|
||||
column: x => x.CategoryId,
|
||||
principalTable: "BotChannelCategories",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BotChannelMembers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
BotId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
BotChannelId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SyncedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BotChannelMembers", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_BotChannelMembers_BotChannels_BotChannelId",
|
||||
column: x => x.BotChannelId,
|
||||
principalTable: "BotChannels",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_BotChannelMembers_Bots_BotId",
|
||||
column: x => x.BotId,
|
||||
principalTable: "Bots",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BotChannelCategories_Name",
|
||||
table: "BotChannelCategories",
|
||||
column: "Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BotChannels_TelegramId",
|
||||
table: "BotChannels",
|
||||
column: "TelegramId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BotChannels_Username",
|
||||
table: "BotChannels",
|
||||
column: "Username");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BotChannels_CategoryId",
|
||||
table: "BotChannels",
|
||||
column: "CategoryId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BotChannelMembers_BotId_BotChannelId",
|
||||
table: "BotChannelMembers",
|
||||
columns: new[] { "BotId", "BotChannelId" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BotChannelMembers_BotId",
|
||||
table: "BotChannelMembers",
|
||||
column: "BotId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BotChannelMembers_BotChannelId",
|
||||
table: "BotChannelMembers",
|
||||
column: "BotChannelId");
|
||||
|
||||
// 1) 迁移分类(去重:Name 全局唯一)
|
||||
migrationBuilder.Sql(@"
|
||||
INSERT INTO BotChannelCategories (Name, Description, CreatedAt)
|
||||
SELECT Name, MAX(Description), MIN(CreatedAt)
|
||||
FROM BotChannelCategories_Old
|
||||
GROUP BY Name;
|
||||
");
|
||||
|
||||
// 2) 迁移频道(TelegramId 全局唯一;取 SyncedAt 最新的一条作为全局信息)
|
||||
migrationBuilder.Sql(@"
|
||||
INSERT INTO BotChannels (TelegramId, AccessHash, Title, Username, IsBroadcast, MemberCount, About, CreatedAt, SyncedAt, CategoryId)
|
||||
SELECT bc.TelegramId, bc.AccessHash, bc.Title, bc.Username, bc.IsBroadcast, bc.MemberCount, bc.About, bc.CreatedAt, bc.SyncedAt, NULL
|
||||
FROM BotChannels_Old bc
|
||||
JOIN (
|
||||
SELECT TelegramId, MAX(SyncedAt) AS MaxSyncedAt
|
||||
FROM BotChannels_Old
|
||||
GROUP BY TelegramId
|
||||
) m ON m.TelegramId = bc.TelegramId AND m.MaxSyncedAt = bc.SyncedAt
|
||||
JOIN (
|
||||
SELECT TelegramId, SyncedAt, MAX(Id) AS MaxId
|
||||
FROM BotChannels_Old
|
||||
GROUP BY TelegramId, SyncedAt
|
||||
) m2 ON m2.TelegramId = bc.TelegramId AND m2.SyncedAt = bc.SyncedAt AND m2.MaxId = bc.Id;
|
||||
");
|
||||
|
||||
// 3) 迁移 Bot-频道关系(同一 bot + 同一频道去重,SyncedAt 取最大)
|
||||
migrationBuilder.Sql(@"
|
||||
INSERT INTO BotChannelMembers (BotId, BotChannelId, SyncedAt)
|
||||
SELECT old.BotId, now.Id AS BotChannelId, MAX(old.SyncedAt) AS SyncedAt
|
||||
FROM BotChannels_Old old
|
||||
JOIN BotChannels now ON now.TelegramId = old.TelegramId
|
||||
GROUP BY old.BotId, now.Id;
|
||||
");
|
||||
|
||||
// 4) 合并分类:同一 TelegramId 在多 bot 下若存在不同分类,取“出现次数最多”的分类(方案 A)
|
||||
migrationBuilder.Sql(@"
|
||||
WITH CatNames AS (
|
||||
SELECT bc.TelegramId AS TelegramId, c.Name AS CatName
|
||||
FROM BotChannels_Old bc
|
||||
JOIN BotChannelCategories_Old c ON c.Id = bc.CategoryId
|
||||
WHERE bc.CategoryId IS NOT NULL
|
||||
),
|
||||
Counts AS (
|
||||
SELECT TelegramId, CatName, COUNT(*) AS Cnt
|
||||
FROM CatNames
|
||||
GROUP BY TelegramId, CatName
|
||||
),
|
||||
Pick AS (
|
||||
SELECT TelegramId, CatName
|
||||
FROM (
|
||||
SELECT TelegramId, CatName, Cnt,
|
||||
ROW_NUMBER() OVER (PARTITION BY TelegramId ORDER BY Cnt DESC, CatName ASC) AS rn
|
||||
FROM Counts
|
||||
)
|
||||
WHERE rn = 1
|
||||
)
|
||||
UPDATE BotChannels
|
||||
SET CategoryId = (
|
||||
SELECT catNew.Id
|
||||
FROM Pick p
|
||||
JOIN BotChannelCategories catNew ON catNew.Name = p.CatName
|
||||
WHERE p.TelegramId = BotChannels.TelegramId
|
||||
)
|
||||
WHERE TelegramId IN (SELECT TelegramId FROM Pick);
|
||||
");
|
||||
|
||||
// 5) 清理旧表
|
||||
migrationBuilder.Sql("DROP TABLE BotChannels_Old;");
|
||||
migrationBuilder.Sql("DROP TABLE BotChannelCategories_Old;");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// 降级不做数据回填(不可逆):仅恢复旧结构的空表,避免 Down 失败阻塞回滚链路。
|
||||
migrationBuilder.DropTable(name: "BotChannelMembers");
|
||||
migrationBuilder.DropTable(name: "BotChannels");
|
||||
migrationBuilder.DropTable(name: "BotChannelCategories");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BotChannelCategories",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
BotId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Name = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
|
||||
Description = table.Column<string>(type: "TEXT", maxLength: 500, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BotChannelCategories", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_BotChannelCategories_Bots_BotId",
|
||||
column: x => x.BotId,
|
||||
principalTable: "Bots",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BotChannels",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
BotId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
TelegramId = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
AccessHash = table.Column<long>(type: "INTEGER", nullable: true),
|
||||
Title = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
|
||||
Username = table.Column<string>(type: "TEXT", maxLength: 100, nullable: true),
|
||||
IsBroadcast = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
MemberCount = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
About = table.Column<string>(type: "TEXT", maxLength: 1000, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
SyncedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
CategoryId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BotChannels", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_BotChannels_Bots_BotId",
|
||||
column: x => x.BotId,
|
||||
principalTable: "Bots",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_BotChannels_BotChannelCategories_CategoryId",
|
||||
column: x => x.CategoryId,
|
||||
principalTable: "BotChannelCategories",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BotChannelCategories_BotId_Name",
|
||||
table: "BotChannelCategories",
|
||||
columns: new[] { "BotId", "Name" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BotChannels_BotId_TelegramId",
|
||||
table: "BotChannels",
|
||||
columns: new[] { "BotId", "TelegramId" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BotChannels_CategoryId",
|
||||
table: "BotChannels",
|
||||
column: "CategoryId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BotChannels_Username",
|
||||
table: "BotChannels",
|
||||
column: "Username");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// <auto-generated />
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
@@ -15,7 +15,7 @@ namespace TelegramPanel.Data.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.0");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.22");
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Account", b =>
|
||||
{
|
||||
@@ -64,16 +64,17 @@ namespace TelegramPanel.Data.Migrations
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TelegramStatusDetails")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool?>("TelegramStatusOk")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TelegramStatusSummary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TwoFactorPassword")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
@@ -113,6 +114,7 @@ namespace TelegramPanel.Data.Migrations
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("ExcludeFromOperations")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
@@ -129,6 +131,37 @@ namespace TelegramPanel.Data.Migrations
|
||||
b.ToTable("AccountCategories");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.AccountChannel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AccountId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsAdmin")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsCreator")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("SyncedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ChannelId");
|
||||
|
||||
b.HasIndex("AccountId", "ChannelId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("AccountChannels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BatchTask", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -175,6 +208,153 @@ namespace TelegramPanel.Data.Migrations
|
||||
b.ToTable("BatchTasks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Bot", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("LastSyncAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long?>("LastUpdateId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Username");
|
||||
|
||||
b.ToTable("Bots");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("About")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long?>("AccessHash")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("CategoryId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsBroadcast")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MemberCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("SyncedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("TelegramId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.HasIndex("TelegramId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Username");
|
||||
|
||||
b.ToTable("BotChannels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannelCategory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("BotChannelCategories");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannelMember", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("BotChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("BotId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("SyncedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("BotChannelId");
|
||||
|
||||
b.HasIndex("BotId");
|
||||
|
||||
b.HasIndex("BotId", "BotChannelId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("BotChannelMembers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Channel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -191,7 +371,7 @@ namespace TelegramPanel.Data.Migrations
|
||||
b.Property<DateTime?>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("CreatorAccountId")
|
||||
b.Property<int?>("CreatorAccountId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("GroupId")
|
||||
@@ -317,13 +497,60 @@ namespace TelegramPanel.Data.Migrations
|
||||
b.Navigation("Category");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.AccountChannel", b =>
|
||||
{
|
||||
b.HasOne("TelegramPanel.Data.Entities.Account", "Account")
|
||||
.WithMany("AccountChannels")
|
||||
.HasForeignKey("AccountId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("TelegramPanel.Data.Entities.Channel", "Channel")
|
||||
.WithMany("AccountChannels")
|
||||
.HasForeignKey("ChannelId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Account");
|
||||
|
||||
b.Navigation("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannel", b =>
|
||||
{
|
||||
b.HasOne("TelegramPanel.Data.Entities.BotChannelCategory", "Category")
|
||||
.WithMany("Channels")
|
||||
.HasForeignKey("CategoryId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Category");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannelMember", b =>
|
||||
{
|
||||
b.HasOne("TelegramPanel.Data.Entities.BotChannel", "BotChannel")
|
||||
.WithMany("Members")
|
||||
.HasForeignKey("BotChannelId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("TelegramPanel.Data.Entities.Bot", "Bot")
|
||||
.WithMany("ChannelMembers")
|
||||
.HasForeignKey("BotId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Bot");
|
||||
|
||||
b.Navigation("BotChannel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Channel", b =>
|
||||
{
|
||||
b.HasOne("TelegramPanel.Data.Entities.Account", "CreatorAccount")
|
||||
.WithMany("Channels")
|
||||
.HasForeignKey("CreatorAccountId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("TelegramPanel.Data.Entities.ChannelGroup", "Group")
|
||||
.WithMany("Channels")
|
||||
@@ -348,6 +575,8 @@ namespace TelegramPanel.Data.Migrations
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Account", b =>
|
||||
{
|
||||
b.Navigation("AccountChannels");
|
||||
|
||||
b.Navigation("Channels");
|
||||
|
||||
b.Navigation("Groups");
|
||||
@@ -358,6 +587,26 @@ namespace TelegramPanel.Data.Migrations
|
||||
b.Navigation("Accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Bot", b =>
|
||||
{
|
||||
b.Navigation("ChannelMembers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannel", b =>
|
||||
{
|
||||
b.Navigation("Members");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.BotChannelCategory", b =>
|
||||
{
|
||||
b.Navigation("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.Channel", b =>
|
||||
{
|
||||
b.Navigation("AccountChannels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TelegramPanel.Data.Entities.ChannelGroup", b =>
|
||||
{
|
||||
b.Navigation("Channels");
|
||||
|
||||
@@ -9,21 +9,19 @@ public class BotChannelCategoryRepository : Repository<BotChannelCategory>, IBot
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<BotChannelCategory>> GetForBotAsync(int botId)
|
||||
public async Task<IEnumerable<BotChannelCategory>> GetAllOrderedAsync()
|
||||
{
|
||||
return await _dbSet
|
||||
.Where(x => x.BotId == botId)
|
||||
.OrderBy(x => x.Name)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<BotChannelCategory?> GetByNameAsync(int botId, string name)
|
||||
public async Task<BotChannelCategory?> GetByNameAsync(string name)
|
||||
{
|
||||
name = (name ?? string.Empty).Trim();
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return null;
|
||||
|
||||
return await _dbSet.FirstOrDefaultAsync(x => x.BotId == botId && x.Name == name);
|
||||
return await _dbSet.FirstOrDefaultAsync(x => x.Name == name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TelegramPanel.Data.Entities;
|
||||
|
||||
namespace TelegramPanel.Data.Repositories;
|
||||
|
||||
public class BotChannelMemberRepository : Repository<BotChannelMember>, IBotChannelMemberRepository
|
||||
{
|
||||
public BotChannelMemberRepository(AppDbContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<BotChannelMember?> GetAsync(int botId, int botChannelId)
|
||||
{
|
||||
return await _dbSet.FirstOrDefaultAsync(x => x.BotId == botId && x.BotChannelId == botChannelId);
|
||||
}
|
||||
|
||||
public async Task UpsertAsync(int botId, int botChannelId, DateTime syncedAt)
|
||||
{
|
||||
var existing = await GetAsync(botId, botChannelId);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.SyncedAt = syncedAt;
|
||||
_dbSet.Update(existing);
|
||||
await _context.SaveChangesAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
await _dbSet.AddAsync(new BotChannelMember
|
||||
{
|
||||
BotId = botId,
|
||||
BotChannelId = botChannelId,
|
||||
SyncedAt = syncedAt
|
||||
});
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int botId, int botChannelId)
|
||||
{
|
||||
var existing = await GetAsync(botId, botChannelId);
|
||||
if (existing == null)
|
||||
return;
|
||||
|
||||
_dbSet.Remove(existing);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<int> CountForChannelAsync(int botChannelId)
|
||||
{
|
||||
return await _dbSet.CountAsync(x => x.BotChannelId == botChannelId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public class BotChannelRepository : Repository<BotChannel>, IBotChannelRepositor
|
||||
var query = _dbSet
|
||||
.AsNoTracking()
|
||||
.Include(x => x.Category)
|
||||
.Where(x => x.BotId == botId);
|
||||
.Where(x => x.Members.Any(m => m.BotId == botId));
|
||||
|
||||
// 约定:categoryId = -1 表示“未分类”(CategoryId == null)
|
||||
if (categoryId.HasValue)
|
||||
@@ -44,23 +44,36 @@ public class BotChannelRepository : Repository<BotChannel>, IBotChannelRepositor
|
||||
{
|
||||
return await _dbSet
|
||||
.Include(x => x.Category)
|
||||
.FirstOrDefaultAsync(x => x.BotId == botId && x.TelegramId == telegramId);
|
||||
.FirstOrDefaultAsync(x => x.TelegramId == telegramId && x.Members.Any(m => m.BotId == botId));
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<BotChannel>> GetForBotAsync(int botId, int? categoryId = null)
|
||||
{
|
||||
var query = _dbSet
|
||||
.Include(x => x.Category)
|
||||
.Where(x => x.BotId == botId);
|
||||
.Where(x => x.Members.Any(m => m.BotId == botId));
|
||||
|
||||
// 约定:categoryId = -1 表示“未分类”(CategoryId == null)
|
||||
if (categoryId.HasValue)
|
||||
query = query.Where(x => x.CategoryId == categoryId.Value);
|
||||
{
|
||||
if (categoryId.Value == -1)
|
||||
query = query.Where(x => x.CategoryId == null);
|
||||
else
|
||||
query = query.Where(x => x.CategoryId == categoryId.Value);
|
||||
}
|
||||
|
||||
return await query
|
||||
.OrderByDescending(x => x.SyncedAt)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<BotChannel?> GetGlobalByTelegramIdAsync(long telegramId)
|
||||
{
|
||||
return await _dbSet
|
||||
.Include(x => x.Category)
|
||||
.FirstOrDefaultAsync(x => x.TelegramId == telegramId);
|
||||
}
|
||||
|
||||
public async Task<(IReadOnlyList<BotChannel> Items, int TotalCount)> QueryPagedAsync(
|
||||
int botId,
|
||||
int? categoryId,
|
||||
|
||||
@@ -31,10 +31,8 @@ public class BotRepository : Repository<Bot>, IBotRepository
|
||||
{
|
||||
// 只用于列表显示统计信息,避免一次性加载全部频道详情
|
||||
return await _dbSet
|
||||
.Include(x => x.Channels)
|
||||
.Include(x => x.Categories)
|
||||
.Include(x => x.ChannelMembers)
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace TelegramPanel.Data.Repositories;
|
||||
|
||||
public interface IBotChannelCategoryRepository : IRepository<BotChannelCategory>
|
||||
{
|
||||
Task<IEnumerable<BotChannelCategory>> GetForBotAsync(int botId);
|
||||
Task<BotChannelCategory?> GetByNameAsync(int botId, string name);
|
||||
Task<IEnumerable<BotChannelCategory>> GetAllOrderedAsync();
|
||||
Task<BotChannelCategory?> GetByNameAsync(string name);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using TelegramPanel.Data.Entities;
|
||||
|
||||
namespace TelegramPanel.Data.Repositories;
|
||||
|
||||
public interface IBotChannelMemberRepository : IRepository<BotChannelMember>
|
||||
{
|
||||
Task<BotChannelMember?> GetAsync(int botId, int botChannelId);
|
||||
Task UpsertAsync(int botId, int botChannelId, DateTime syncedAt);
|
||||
Task DeleteAsync(int botId, int botChannelId);
|
||||
Task<int> CountForChannelAsync(int botChannelId);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ public interface IBotChannelRepository : IRepository<BotChannel>
|
||||
{
|
||||
Task<BotChannel?> GetByTelegramIdAsync(int botId, long telegramId);
|
||||
Task<IEnumerable<BotChannel>> GetForBotAsync(int botId, int? categoryId = null);
|
||||
Task<BotChannel?> GetGlobalByTelegramIdAsync(long telegramId);
|
||||
|
||||
Task<(IReadOnlyList<BotChannel> Items, int TotalCount)> QueryPagedAsync(
|
||||
int botId,
|
||||
|
||||
@@ -30,6 +30,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<IBotRepository, BotRepository>();
|
||||
services.AddScoped<IBotChannelRepository, BotChannelRepository>();
|
||||
services.AddScoped<IBotChannelCategoryRepository, BotChannelCategoryRepository>();
|
||||
services.AddScoped<IBotChannelMemberRepository, BotChannelMemberRepository>();
|
||||
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
||||
|
||||
return services;
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
@code
|
||||
{
|
||||
[CascadingParameter] private MudDialogInstance MudDialog { get; set; } = default!;
|
||||
[Parameter] public int BotId { get; set; }
|
||||
[Parameter] public BotChannelCategory? Category { get; set; }
|
||||
|
||||
private bool saving;
|
||||
@@ -71,7 +70,7 @@
|
||||
if (Category == null)
|
||||
{
|
||||
// 新增模式:使用现有的 CreateCategoryAsync 方法
|
||||
var category = await BotManagement.CreateCategoryAsync(BotId, name, description);
|
||||
var category = await BotManagement.CreateCategoryAsync(name, description);
|
||||
Snackbar.Add("分类创建成功", Severity.Success);
|
||||
MudDialog.Close(DialogResult.Ok(category));
|
||||
}
|
||||
|
||||
@@ -124,9 +124,8 @@
|
||||
var username = chatInfo?.Username ?? dbChannel?.Username;
|
||||
var memberCount = chatInfo?.MemberCount ?? dbChannel?.MemberCount ?? 0;
|
||||
|
||||
await BotManagement.UpsertChannelAsync(new BotChannel
|
||||
await BotManagement.UpsertChannelAsync(BotId, new BotChannel
|
||||
{
|
||||
BotId = BotId,
|
||||
TelegramId = ChannelTelegramId,
|
||||
Title = title,
|
||||
Username = username,
|
||||
|
||||
@@ -252,7 +252,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
categories = (await BotManagement.GetCategoriesAsync(selectedBotId)).ToList();
|
||||
categories = (await BotManagement.GetCategoriesAsync()).ToList();
|
||||
await ReloadTable();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -364,10 +364,7 @@
|
||||
if (selectedBotId <= 0)
|
||||
return;
|
||||
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
["BotId"] = selectedBotId
|
||||
};
|
||||
var parameters = new DialogParameters();
|
||||
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Small, FullWidth = true };
|
||||
var dialog = DialogService.Show<BotChannelCategoryDialog>("新建分类", parameters, options);
|
||||
var result = await dialog.Result;
|
||||
|
||||
@@ -40,8 +40,7 @@
|
||||
<HeaderContent>
|
||||
<MudTh>名称</MudTh>
|
||||
<MudTh>用户名</MudTh>
|
||||
<MudTh>频道数</MudTh>
|
||||
<MudTh>分类数</MudTh>
|
||||
<MudTh>可管理聊天数</MudTh>
|
||||
<MudTh>最后同步</MudTh>
|
||||
<MudTh>状态</MudTh>
|
||||
<MudTh>操作</MudTh>
|
||||
@@ -49,8 +48,7 @@
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="名称">@context.Name</MudTd>
|
||||
<MudTd DataLabel="用户名">@(!string.IsNullOrWhiteSpace(context.Username) ? $"@{context.Username}" : "-")</MudTd>
|
||||
<MudTd DataLabel="频道数">@context.Channels.Count(x => x.IsBroadcast)</MudTd>
|
||||
<MudTd DataLabel="分类数">@context.Categories.Count</MudTd>
|
||||
<MudTd DataLabel="可管理聊天数">@context.ChannelMembers.Count</MudTd>
|
||||
<MudTd DataLabel="最后同步"><PanelTime Value="context.LastSyncAt" /></MudTd>
|
||||
<MudTd DataLabel="状态">
|
||||
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
||||
@@ -151,7 +149,7 @@
|
||||
{
|
||||
bool? result = await DialogService.ShowMessageBox(
|
||||
"确认删除",
|
||||
$"确定要删除机器人 {bot.Name} 吗?该机器人加入的频道与分类也会一并删除。",
|
||||
$"确定要删除机器人 {bot.Name} 吗?该机器人加入的频道关联也会一并删除。",
|
||||
yesText: "删除", cancelText: "取消");
|
||||
|
||||
if (result != true)
|
||||
|
||||
Reference in new issue
Block a user