diff --git a/src/Jiaowu.Api/Controllers/PersonnelController.cs b/src/Jiaowu.Api/Controllers/PersonnelController.cs index 2c9d3e1..7640429 100644 --- a/src/Jiaowu.Api/Controllers/PersonnelController.cs +++ b/src/Jiaowu.Api/Controllers/PersonnelController.cs @@ -437,6 +437,9 @@ public sealed class PersonnelController( await cache.RemoveByTagAsync( AppCacheTags.Timetables, cancellationToken); + await cache.RemoveByTagAsync( + AppCacheTags.Analytics, + cancellationToken); return Created(string.Empty, new { id }); } catch (DbUpdateException) @@ -453,6 +456,9 @@ public sealed class PersonnelController( await cache.RemoveByTagAsync( AppCacheTags.Timetables, cancellationToken); + await cache.RemoveByTagAsync( + AppCacheTags.Analytics, + cancellationToken); return NoContent(); } catch (DbUpdateException) diff --git a/src/Jiaowu.Api/Controllers/PersonnelExcelController.cs b/src/Jiaowu.Api/Controllers/PersonnelExcelController.cs index ca57c56..b21c978 100644 --- a/src/Jiaowu.Api/Controllers/PersonnelExcelController.cs +++ b/src/Jiaowu.Api/Controllers/PersonnelExcelController.cs @@ -147,6 +147,9 @@ public sealed class PersonnelExcelController( await cache.RemoveByTagAsync( AppCacheTags.Timetables, cancellationToken); + await cache.RemoveByTagAsync( + AppCacheTags.Analytics, + cancellationToken); return Ok(result); } catch (DbUpdateException) diff --git a/src/Jiaowu.Api/Controllers/StatisticsController.cs b/src/Jiaowu.Api/Controllers/StatisticsController.cs index 8f53669..b712558 100644 --- a/src/Jiaowu.Api/Controllers/StatisticsController.cs +++ b/src/Jiaowu.Api/Controllers/StatisticsController.cs @@ -966,7 +966,11 @@ public sealed class StatisticsController( source.Value.GetType()); }, AppCacheProfile.Analytics, - [AppCacheTags.Analytics], + [ + AppCacheTags.Analytics, + AppCacheTags.BaseData, + AppCacheTags.Timetables + ], cancellationToken); return value; } diff --git a/src/Jiaowu.Api/Infrastructure/Caching/AppCache.cs b/src/Jiaowu.Api/Infrastructure/Caching/AppCache.cs index c81a7f9..2c2286e 100644 --- a/src/Jiaowu.Api/Infrastructure/Caching/AppCache.cs +++ b/src/Jiaowu.Api/Infrastructure/Caching/AppCache.cs @@ -179,7 +179,7 @@ public static class AppCacheKeys var filterPart = filters.Length == 0 ? "all" : string.Join(':', filters.Select(Normalize)); - return $"statistics:{Normalize(area)}:scope:{Normalize(dataScope)}:" + + return $"statistics:v2:{Normalize(area)}:scope:{Normalize(dataScope)}:" + $"college:{effectiveCollegeId?.ToString("N") ?? "all"}:{filterPart}"; } } diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/DemoDataSeeder.cs b/src/Jiaowu.Api/Infrastructure/Persistence/DemoDataSeeder.cs index a59c781..639f605 100644 --- a/src/Jiaowu.Api/Infrastructure/Persistence/DemoDataSeeder.cs +++ b/src/Jiaowu.Api/Infrastructure/Persistence/DemoDataSeeder.cs @@ -1,11 +1,13 @@ using System.Data; using Jiaowu.Api.Domain.Academic; +using Jiaowu.Api.Infrastructure.Caching; using Microsoft.EntityFrameworkCore; namespace Jiaowu.Api.Infrastructure.Persistence; public sealed class DemoDataSeeder( AppDbContext db, + IAppCache cache, ILogger logger) { // MySql.EntityFrameworkCore 10 cannot type-map parameterized primitive @@ -47,6 +49,10 @@ public sealed class DemoDataSeeder( await LogSummaryAsync(cancellationToken); await transaction.CommitAsync(cancellationToken); }); + + await cache.RemoveByTagAsync(AppCacheTags.BaseData, cancellationToken); + await cache.RemoveByTagAsync(AppCacheTags.Timetables, cancellationToken); + await cache.RemoveByTagAsync(AppCacheTags.Analytics, cancellationToken); } private async Task EnsureDatabaseIsEmptyAsync(CancellationToken cancellationToken) diff --git a/tests/Jiaowu.Api.Tests/DemoDataSeederTests.cs b/tests/Jiaowu.Api.Tests/DemoDataSeederTests.cs index 6fde9fd..61ade12 100644 --- a/tests/Jiaowu.Api.Tests/DemoDataSeederTests.cs +++ b/tests/Jiaowu.Api.Tests/DemoDataSeederTests.cs @@ -1,4 +1,5 @@ using Jiaowu.Api.Domain.Academic; +using Jiaowu.Api.Infrastructure.Caching; using Jiaowu.Api.Infrastructure.Persistence; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; @@ -18,6 +19,7 @@ public sealed class DemoDataSeederTests var seeder = new DemoDataSeeder( db, + NoOpAppCache.Instance, NullLogger.Instance); await seeder.SeedAsync(); @@ -46,6 +48,7 @@ public sealed class DemoDataSeederTests var seeder = new DemoDataSeeder( db, + NoOpAppCache.Instance, NullLogger.Instance); var exception = await Assert.ThrowsAsync( @@ -70,6 +73,7 @@ public sealed class DemoDataSeederTests var seeder = new DemoDataSeeder( db, + NoOpAppCache.Instance, NullLogger.Instance); await Assert.ThrowsAsync(() => seeder.SeedAsync()); diff --git a/tests/Jiaowu.Api.Tests/StatisticsControllerTests.cs b/tests/Jiaowu.Api.Tests/StatisticsControllerTests.cs index 61b9949..e720ccc 100644 --- a/tests/Jiaowu.Api.Tests/StatisticsControllerTests.cs +++ b/tests/Jiaowu.Api.Tests/StatisticsControllerTests.cs @@ -52,6 +52,14 @@ public sealed class StatisticsControllerTests null, CancellationToken.None); Assert.Equal(1, TotalCourses(first)); + Assert.Equal( + new[] + { + AppCacheTags.Analytics, + AppCacheTags.BaseData, + AppCacheTags.Timetables + }, + cache.Tags.Single()); db.Courses.Add(CreateCourse("C003", firstCollege, category)); await db.SaveChangesAsync(); @@ -115,6 +123,7 @@ public sealed class StatisticsControllerTests public int SourceCalls { get; private set; } public IReadOnlyCollection Keys => values.Keys; + public List> Tags { get; } = []; public async Task GetOrCreateAsync( string key, @@ -127,6 +136,7 @@ public sealed class StatisticsControllerTests return (T)value; SourceCalls++; + Tags.Add(tags.ToArray()); var loaded = await factory(cancellationToken); values[key] = loaded!; return loaded;