Files
29WxOrder/OrderSupportTicket/Services/WorkOrderCacheService.cs
T
2026-10-19 14:35:25 +08:00

149 lines
4.6 KiB
C#

using OrderSupportTicket.Models;
using OrderSupportTicket.Models.Api;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
namespace OrderSupportTicket.Services;
/// <summary>
/// 工单列表本地缓存的读取与未读比较。
/// </summary>
public sealed class WorkOrderCacheService
{
private const string ConfigName = "ordersupportticket";
private static readonly object SyncRoot = new object();
/// <summary>
/// 读取指定账号的工单列表缓存。
/// </summary>
public WorkOrderListCache Load(string userName)
{
if (string.IsNullOrWhiteSpace(userName))
return null;
try
{
lock (SyncRoot)
{
var path = NaiBa.Util.Helper.ConfigHelper.GetConfigUserPath(ConfigName);
if (!File.Exists(path))
return null;
var cache = NaiBa.Util.Helper.ConfigHelper
.LoadConfig<WorkOrderListCache>(ConfigName);
if (cache?.Items == null)
return null;
if (!string.Equals(cache.UserName, userName, StringComparison.Ordinal))
return null;
return cache;
}
}
catch
{
// 本地缓存损坏或读取失败时按无缓存处理。
return null;
}
}
/// <summary>
/// 保存工单列表缓存。
/// </summary>
public void Save(WorkOrderListCache cache)
{
if (cache == null)
throw new ArgumentNullException(nameof(cache));
if (string.IsNullOrWhiteSpace(cache.UserName))
throw new ArgumentException("缓存缺少登录账号。", nameof(cache));
lock (SyncRoot)
{
NaiBa.Util.Helper.ConfigHelper.SaveConfig(ConfigName, cache);
}
}
/// <summary>
/// 根据服务端工单列表和历史缓存计算每个工单的未读消息数。
/// 返回值的键为 "appId|wechatOrderId"。
/// </summary>
public Dictionary<string, int> BuildUnreadMap(
IEnumerable<WorkOrderSummaryDto> workOrders,
WorkOrderListCache cache)
{
var result = new Dictionary<string, int>(StringComparer.Ordinal);
var savedItems = (cache?.Items ?? new List<WorkOrderListCacheItem>())
.Where(item => item != null && !string.IsNullOrWhiteSpace(item.OrderId))
.GroupBy(item => item.OrderId, StringComparer.Ordinal)
.ToDictionary(
group => group.Key,
group => group.First(),
StringComparer.Ordinal);
var hasSavedCache = cache?.Items != null && cache.Items.Count > 0;
foreach (var dto in workOrders ?? Enumerable.Empty<WorkOrderSummaryDto>())
{
if (dto == null
|| string.IsNullOrWhiteSpace(dto.AppId)
|| string.IsNullOrWhiteSpace(dto.WechatOrderId))
{
continue;
}
var hasNewMessage = !hasSavedCache;
if (hasSavedCache)
{
if (savedItems.TryGetValue(dto.WechatOrderId, out var saved))
{
hasNewMessage = ParseTimestamp(dto.LastMessageAt) > saved.LastMessageAt;
}
else
{
// 缓存中没有该工单,说明是新增工单。
hasNewMessage = true;
}
}
if (!hasNewMessage)
continue;
// 有历史基线时,时间更新但服务端未读数为 0 也按 1 条提示;
// 首次运行没有基线时只信任服务端未读数,避免把历史工单全部标成未读。
var unreadCount = hasSavedCache
? Math.Max(1, dto.UnreadCount)
: dto.UnreadCount;
if (unreadCount <= 0)
continue;
result[BuildTicketKey(dto.AppId, dto.WechatOrderId)] = unreadCount;
}
return result;
}
/// <summary>
/// 构建工单实时通知使用的唯一键。
/// </summary>
public static string BuildTicketKey(string appId, string wechatOrderId)
{
return appId + "|" + wechatOrderId;
}
/// <summary>
/// 解析服务端 ISO 8601 时间字符串。
/// </summary>
public static DateTimeOffset ParseTimestamp(string value)
{
if (DateTimeOffset.TryParse(
value,
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal,
out var parsed))
{
return parsed;
}
return default;
}
}