Projects (GanjooRazor -> DiwanRazor, GanjoorService.sln -> DiwanService.sln), files, folders, classes, namespaces, settings, API routes (/api/diwan) and DB tables (Diwan*). External addresses (ganjoor.net, github.com/ganjoor) unchanged. Migrations renamed consistently (no pending model changes); requires a fresh database. Upstream RUNNING_LOCALLY/SEMANTIC_SEARCH docs archived unmodified under docs/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
225 lines
7.7 KiB
C#
225 lines
7.7 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using RMuseum.DbContext;
|
|
using RMuseum.Models.Diwan;
|
|
using RMuseum.Models.Diwan.ViewModels;
|
|
using RSecurityBackend.Models.Generic;
|
|
using RSecurityBackend.Models.Image;
|
|
using RSecurityBackend.Services;
|
|
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace RMuseum.Services.Implementation
|
|
{
|
|
/// <summary>
|
|
/// ganjoor.net banners service
|
|
/// </summary>
|
|
public class SiteBannersService : ISiteBannersService
|
|
{
|
|
/// <summary>
|
|
/// Add site banner
|
|
/// </summary>
|
|
/// <param name="imageStream"></param>
|
|
/// <param name="fileName"></param>
|
|
/// <param name="alternateText"></param>
|
|
/// <param name="targetUrl"></param>
|
|
/// <param name="active"></param>
|
|
/// <returns></returns>
|
|
public async Task<RServiceResult<DiwanSiteBannerViewModel>> AddSiteBanner(Stream imageStream, string fileName, string alternateText, string targetUrl, bool active)
|
|
{
|
|
RServiceResult<RImage> image = await _imageFileService.Add(null, imageStream, fileName, "SiteBanners");
|
|
|
|
if (!string.IsNullOrEmpty(image.ExceptionString))
|
|
{
|
|
return new RServiceResult<DiwanSiteBannerViewModel>(null, image.ExceptionString);
|
|
}
|
|
|
|
DiwanSiteBanner banner = new DiwanSiteBanner()
|
|
{
|
|
RImage = image.Result,
|
|
AlternateText = alternateText,
|
|
TargetUrl = targetUrl,
|
|
Active = active
|
|
};
|
|
|
|
_context.DiwanSiteBanners.Add(banner);
|
|
await _context.SaveChangesAsync();
|
|
|
|
await CleanBannersCache();
|
|
|
|
return new RServiceResult<DiwanSiteBannerViewModel>
|
|
(
|
|
new DiwanSiteBannerViewModel()
|
|
{
|
|
Id = banner.Id,
|
|
ImageUrl = $"api/rimages/{banner.RImageId}.jpg",
|
|
AlternateText = banner.AlternateText,
|
|
TargetUrl = banner.TargetUrl,
|
|
Active = banner.Active
|
|
}
|
|
);
|
|
}
|
|
|
|
/// <summary>
|
|
/// modify site banner
|
|
/// </summary>
|
|
/// <param name="id"></param>
|
|
/// <param name="alternateText"></param>
|
|
/// <param name="targetUrl"></param>
|
|
/// <param name="active"></param>
|
|
/// <returns></returns>
|
|
public async Task<RServiceResult<bool>> ModifySiteBanner(int id, string alternateText, string targetUrl, bool active)
|
|
{
|
|
DiwanSiteBanner target = await _context.DiwanSiteBanners.Where(b => b.Id == id).SingleOrDefaultAsync();
|
|
if (target == null)
|
|
return new RServiceResult<bool>(false);//not found
|
|
|
|
target.AlternateText = alternateText;
|
|
target.TargetUrl = targetUrl;
|
|
target.Active = active;
|
|
|
|
_context.DiwanSiteBanners.Update(target);
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
await CleanBannersCache();
|
|
|
|
return new RServiceResult<bool>(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// delete site banner
|
|
/// </summary>
|
|
/// <param name="id"></param>
|
|
/// <returns></returns>
|
|
public async Task<RServiceResult<bool>> DeleteSiteBanner(int id)
|
|
{
|
|
DiwanSiteBanner target = await _context.DiwanSiteBanners.Where(b => b.Id == id).SingleOrDefaultAsync();
|
|
if (target == null)
|
|
return new RServiceResult<bool>(false);//not found
|
|
|
|
_context.DiwanSiteBanners.Remove(target);
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
await CleanBannersCache();
|
|
|
|
return new RServiceResult<bool>(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// get site banners
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public async Task<RServiceResult<DiwanSiteBannerViewModel[]>> GetSiteBanners()
|
|
{
|
|
return new RServiceResult<DiwanSiteBannerViewModel[]>(
|
|
await _context.DiwanSiteBanners
|
|
.Select(b => new DiwanSiteBannerViewModel()
|
|
{
|
|
Id = b.Id,
|
|
ImageUrl = $"api/rimages/{b.RImageId}.jpg",
|
|
AlternateText = b.AlternateText,
|
|
TargetUrl = b.TargetUrl,
|
|
Active = b.Active
|
|
}).ToArrayAsync()
|
|
);
|
|
}
|
|
|
|
private const string _cacheKeyForIdSet = "SiteBannersService::GetARandomActiveSiteBanner::idSet";
|
|
|
|
private static string GetBannerCacheKey(int bannerId)
|
|
{
|
|
return $"SiteBannersService::BannerCacheKey::{bannerId}";
|
|
}
|
|
|
|
private async Task CleanBannersCache()
|
|
{
|
|
int[] idSet = await _context.DiwanSiteBanners.Select(b => b.Id).ToArrayAsync();
|
|
foreach (int id in idSet)
|
|
{
|
|
var cacheKey = GetBannerCacheKey(id);
|
|
if (_memoryCache.TryGetValue(id, out DiwanSiteBannerViewModel o))
|
|
{
|
|
_memoryCache.Remove(id);
|
|
}
|
|
}
|
|
if (_memoryCache.TryGetValue(_cacheKeyForIdSet, out idSet))
|
|
{
|
|
_memoryCache.Remove(_cacheKeyForIdSet);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// get a random site banner
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public async Task<RServiceResult<DiwanSiteBannerViewModel>> GetARandomActiveSiteBanner()
|
|
{
|
|
if (!_memoryCache.TryGetValue(_cacheKeyForIdSet, out int[] idSet))
|
|
{
|
|
idSet = await _context.DiwanSiteBanners.Where(b => b.Active == true).Select(b => b.Id).ToArrayAsync();
|
|
_memoryCache.Set(_cacheKeyForIdSet, idSet, TimeSpan.FromHours(1));
|
|
}
|
|
if (idSet.Length == 0)
|
|
return new RServiceResult<DiwanSiteBannerViewModel>(null);//no active banner
|
|
|
|
Random rnd = new Random(DateTime.Now.Millisecond);
|
|
int id = idSet[rnd.Next(0, idSet.Length)];
|
|
|
|
var cachKey = GetBannerCacheKey(id);
|
|
if (!_memoryCache.TryGetValue(cachKey, out DiwanSiteBannerViewModel banner))
|
|
{
|
|
banner =
|
|
await _context.DiwanSiteBanners
|
|
.Where(b => b.Id == id)
|
|
.Select(b => new DiwanSiteBannerViewModel()
|
|
{
|
|
Id = b.Id,
|
|
ImageUrl = $"api/rimages/{b.RImageId}.jpg",
|
|
AlternateText = b.AlternateText,
|
|
TargetUrl = b.TargetUrl,
|
|
Active = b.Active
|
|
})
|
|
.AsNoTracking()
|
|
.SingleAsync();
|
|
_memoryCache.Set(cachKey, banner, TimeSpan.FromHours(1));
|
|
}
|
|
|
|
return new RServiceResult<DiwanSiteBannerViewModel>(banner);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Database Context
|
|
/// </summary>
|
|
protected readonly RMuseumDbContext _context;
|
|
|
|
/// <summary>
|
|
/// Image File Service
|
|
/// </summary>
|
|
protected readonly IImageFileService _imageFileService;
|
|
|
|
/// <summary>
|
|
/// IMemoryCache
|
|
/// </summary>
|
|
protected readonly IMemoryCache _memoryCache;
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// constructor
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
/// <param name="imageFileService"></param>
|
|
/// <param name="memoryCache"></param>
|
|
public SiteBannersService(RMuseumDbContext context, IImageFileService imageFileService, IMemoryCache memoryCache)
|
|
{
|
|
_context = context;
|
|
_imageFileService = imageFileService;
|
|
_memoryCache = memoryCache;
|
|
}
|
|
}
|
|
}
|