Files
StoreWeb/Plugins/Store.ImageCache/Services/ImageCacheService.cs
T

173 lines
4.5 KiB
C#

using MAX.Core.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Store.ImageCache.Dtos;
using Store.ImageCache.Entities;
using Store.ImageCache.Mapper;
using System;
using System.Collections.Generic;
using System.Text;
namespace Store.ImageCache.Services
{
public class ImageCacheService : ISingletonDependency
{
private const int Md5Length = 32;
private const int PlatformUrlMaxLength = 1000;
private readonly IFreeSql _freeSql;
private ImageCacheMapper mapper;
public ImageCacheService([FromKeyedServices("ImageCacheConnection")] IFreeSql freeSql)
{
mapper = new ImageCacheMapper();
if (freeSql == null)
{
throw new ArgumentNullException("freeSql");
}
_freeSql = freeSql;
}
/// <summary>
/// 按 MD5 查询图片缓存;platform 为空时返回该 MD5 在所有平台的缓存。
/// </summary>
public ImageCacheDto GetByMd5(string sourceUrlMd5, byte? platform = null)
{
var md5 = NormalizeMd5(sourceUrlMd5);
return GetByMd5sInternal(new List<string> { md5 }, platform).FirstOrDefault();
}
/// <summary>
/// 批量按 MD5 查询图片缓存。MD5 会按首字符分组,只查询对应的 images_0 到 images_f 分表。
/// </summary>
public List<ImageCacheDto> GetByMd5s(IEnumerable<string> sourceUrlMd5s, byte? platform = null)
{
return GetByMd5sInternal(NormalizeMd5s(sourceUrlMd5s), platform);
}
/// <summary>
/// 按 MD5 和平台直接修改平台图片 URL,未找到时返回 false。
/// </summary>
public bool AddOrUpdate(string sourceUrlMd5, byte platform, string platformUrl)
{
var md5 = NormalizeMd5(sourceUrlMd5);
ValidatePlatformUrl(platformUrl);
var tableName = GetTableName(md5[0]);
var existing = _freeSql.Select<ImageCacheEntity>()
.AsTable((type, oldName) => tableName)
.Where(a => a.source_url_md5 == sourceUrlMd5 && a.platform == platform)
.First();
var now = DateTime.Now;
if (existing != null)
{
var affected = _freeSql.Update<ImageCacheEntity>()
.AsTable(tableName)
.Set(a => a.platform_url, platformUrl)
.Set(a => a.update_time, now)
.Where(a => a.source_url_md5 == md5 && a.platform == platform)
.ExecuteAffrows();
return affected > 0;
}
else
{
ImageCacheEntity imageCache = new ImageCacheEntity();
imageCache.id = 0;
imageCache.source_url_md5 = md5;
imageCache.platform = platform;
imageCache.platform_url = platformUrl;
imageCache.create_time = now;
imageCache.update_time = now;
imageCache.id = _freeSql.Insert(imageCache)
.AsTable(tableName)
.ExecuteIdentity();
return imageCache.id > 0;
}
}
private List<ImageCacheDto> GetByMd5sInternal(List<string> md5s, byte? platform)
{
var result = new List<ImageCacheDto>();
if (md5s.Count == 0)
{
return result;
}
foreach (var shard in md5s.GroupBy(a => a[0]))
{
var tableName = GetTableName(shard.Key);
var md5sInTable = shard.ToList();
var query = _freeSql.Select<ImageCacheEntity>()
.AsTable((type, oldName) => tableName)
.Where(a => md5sInTable.Contains(a.source_url_md5));
if (platform.HasValue)
{
var platformValue = platform.Value;
query = query.Where(a => a.platform == platformValue);
}
result.AddRange(query.ToList().Select(mapper.ToDto));
}
return result;
}
private List<string> NormalizeMd5s(IEnumerable<string> sourceUrlMd5s)
{
if (sourceUrlMd5s == null)
{
throw new ArgumentNullException("sourceUrlMd5s");
}
var result = new List<string>();
foreach (var sourceUrlMd5 in sourceUrlMd5s)
{
var md5 = NormalizeMd5(sourceUrlMd5);
if (!result.Contains(md5))
{
result.Add(md5);
}
}
return result;
}
private string NormalizeMd5(string sourceUrlMd5)
{
if (string.IsNullOrWhiteSpace(sourceUrlMd5))
{
throw new ArgumentException("MD5 不能为空", "sourceUrlMd5");
}
var md5 = sourceUrlMd5.Trim().ToLowerInvariant();
if (md5.Length != Md5Length || md5.Any(a => !Uri.IsHexDigit(a)))
{
throw new ArgumentException("MD5 必须是 32 位十六进制字符串", "sourceUrlMd5");
}
return md5;
}
private string GetTableName(char shard)
{
return "images_" + shard;
}
private void ValidatePlatformUrl(string platformUrl)
{
if (string.IsNullOrWhiteSpace(platformUrl))
{
throw new ArgumentException("平台图片 URL 不能为空", "platformUrl");
}
if (platformUrl.Length > PlatformUrlMaxLength)
{
throw new ArgumentException("平台图片 URL 长度不能超过 1000", "platformUrl");
}
}
}
}