add:
更新
This commit is contained in:
@@ -105,5 +105,11 @@
|
||||
// 3.9 会话相关错误(3100 ~ 3199)
|
||||
/// <summary>发送时异常</summary>
|
||||
public static CodeDefine CONVERSATION_NOT_FOUND = new CodeDefine(3100, "会话不存在");
|
||||
|
||||
// 3.9 文件相关错误(3200 ~ 3299)
|
||||
/// <summary>分片不存在异常</summary>
|
||||
public static CodeDefine CHUNKE_NOT_FOUND = new CodeDefine(3201, "分片不存在");
|
||||
/// <summary>分片合并异常</summary>
|
||||
public static CodeDefine CHUNKE_COMBINE_FAIL = new CodeDefine(3202, "分片合并失败");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
namespace IM_API.Tools
|
||||
{
|
||||
public static class ObjectNameGenerator
|
||||
{
|
||||
public static string Generate(ObjectNameContext ctx)
|
||||
{
|
||||
var ext = GetExtension(ctx.FileName, ctx.ContentType);
|
||||
var shortId = Guid.NewGuid().ToString("N")[..12];
|
||||
|
||||
var parts = new List<string>
|
||||
{
|
||||
ctx.Biz,
|
||||
ctx.Now.Year.ToString(),
|
||||
ctx.Now.Month.ToString("D2")
|
||||
};
|
||||
|
||||
if (ctx.UserId.HasValue)
|
||||
{
|
||||
parts.Add(ctx.UserId.Value.ToString());
|
||||
}
|
||||
|
||||
parts.Add($"{shortId}{ext}");
|
||||
|
||||
return string.Join("/", parts);
|
||||
}
|
||||
|
||||
private static string GetExtension(string fileName, string contentType)
|
||||
{
|
||||
var ext = Path.GetExtension(fileName);
|
||||
if (!string.IsNullOrWhiteSpace(ext))
|
||||
return ext.ToLowerInvariant();
|
||||
|
||||
return contentType switch
|
||||
{
|
||||
"image/jpeg" => ".jpg",
|
||||
"image/png" => ".png",
|
||||
"video/mp4" => ".mp4",
|
||||
_ => ".bin"
|
||||
};
|
||||
}
|
||||
}
|
||||
public class ObjectNameContext
|
||||
{
|
||||
public string Biz { get; init; } = "IM";
|
||||
public long? UserId { get; init; }
|
||||
public string FileName { get; init; } = default!;
|
||||
public string ContentType { get; init; } = default!;
|
||||
public DateTimeOffset Now { get; init; } = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,10 +2,13 @@
|
||||
{
|
||||
public static class RedisKeys
|
||||
{
|
||||
public static string GetUserinfoKey(string userId) => $"user::uinfo::{userId}";
|
||||
public static string GetUserinfoKeyByUsername(string username) => $"user::uinfobyid::{username}";
|
||||
public static string GetSequenceIdKey(string streamKey) => $"chat::seq::{streamKey}";
|
||||
public static string GetSequenceIdLockKey(string streamKey) => $"lock::seq::{streamKey}";
|
||||
public static string GetConnectionIdKey(string userId) => $"signalr::user::con::{userId}";
|
||||
public static string GetUserinfoKey(string userId) => $"user:uinfo:{userId}";
|
||||
public static string GetUserinfoKeyByUsername(string username) => $"user:uinfobyid:{username}";
|
||||
public static string GetSequenceIdKey(string streamKey) => $"chat:seq:{streamKey}";
|
||||
public static string GetSequenceIdLockKey(string streamKey) => $"lock:seq:{streamKey}";
|
||||
public static string GetConnectionIdKey(string userId) => $"signalr:user:con:{userId}";
|
||||
|
||||
public static string GetUploadPartKey(Guid taskId) => $"upload:task:{taskId}:parts";
|
||||
public static string MergeStatus(Guid taskId) => $"upload:task:{taskId}:merge";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
using SixLabors.ImageSharp;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace IM_API.Tools
|
||||
{
|
||||
public static class UrlTools
|
||||
{
|
||||
public static string GetFullUrl(string objectName, string provider, string? baseUrl)
|
||||
{
|
||||
return provider switch
|
||||
{
|
||||
"Local" => $"{baseUrl}/uploads/files/{objectName}",
|
||||
_ => "http://baidu.com",
|
||||
};
|
||||
}
|
||||
|
||||
public static async Task<(int width, int height)> GetImageWH(string url)
|
||||
{
|
||||
using var httpClient = new HttpClient();
|
||||
var stream = await httpClient.GetStreamAsync(url);
|
||||
var info = await Image.IdentifyAsync(stream);
|
||||
return (info.Width, info.Height);
|
||||
}
|
||||
|
||||
public static string ProcessMessageUrl(string contentJson, string? localBaseUrl)
|
||||
{
|
||||
// 1. 解析 JSON 文档(比反序列化快得多)
|
||||
using var doc = JsonDocument.Parse(contentJson);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// 2. 获取 Provider 字段
|
||||
string provider = root.GetProperty("provider").GetString();
|
||||
|
||||
// 3. 根据 Provider 决定前缀
|
||||
string prefix = GetFullUrl("", provider, localBaseUrl);
|
||||
|
||||
// 4. 重新组装(如果只是为了给前端看,建议直接返回带前缀的对象或字符串)
|
||||
// 这里推荐用 JsonNode 方便修改并返回字符串
|
||||
var node = JsonNode.Parse(contentJson);
|
||||
node["url"] = $"{prefix}{node["url"]}";
|
||||
node["thumb"] = $"{prefix}{node["thumb"]}";
|
||||
|
||||
return node.ToJsonString();
|
||||
}
|
||||
public static Stream Base64ToStream(string base64String)
|
||||
{
|
||||
if (string.IsNullOrEmpty(base64String))
|
||||
throw new ArgumentNullException(nameof(base64String));
|
||||
|
||||
// 1. 自动处理可能存在的 Base64 Data URL 前缀
|
||||
string base64Data = base64String.Contains(",")
|
||||
? base64String.Split(',')[1]
|
||||
: base64String;
|
||||
|
||||
// 2. 解码为字节数组
|
||||
byte[] bytes = Convert.FromBase64String(base64Data);
|
||||
|
||||
// 3. 包装进 MemoryStream
|
||||
// 注意:这里直接把 Position 设为 0,符合“方法a”产生即用的原则
|
||||
return new MemoryStream(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user