diff --git a/src/Api/TakeoutSaaS.AdminApi/Contracts/Responses/TencentMapScriptResponse.cs b/src/Api/TakeoutSaaS.AdminApi/Contracts/Responses/TencentMapScriptResponse.cs new file mode 100644 index 0000000..11748a1 --- /dev/null +++ b/src/Api/TakeoutSaaS.AdminApi/Contracts/Responses/TencentMapScriptResponse.cs @@ -0,0 +1,3 @@ +namespace TakeoutSaaS.AdminApi.Contracts.Responses; + +public sealed record TencentMapScriptResponse(string ScriptUrl); diff --git a/src/Api/TakeoutSaaS.AdminApi/Controllers/TencentMapsController.cs b/src/Api/TakeoutSaaS.AdminApi/Controllers/TencentMapsController.cs new file mode 100644 index 0000000..0745246 --- /dev/null +++ b/src/Api/TakeoutSaaS.AdminApi/Controllers/TencentMapsController.cs @@ -0,0 +1,90 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Security.Cryptography; +using System.Text; +using TakeoutSaaS.AdminApi.Contracts.Responses; +using TakeoutSaaS.Shared.Abstractions.Constants; +using TakeoutSaaS.Shared.Abstractions.Results; +using TakeoutSaaS.Shared.Web.Api; + +namespace TakeoutSaaS.AdminApi.Controllers; + +/// +/// 腾讯地图脚本签名服务。 +/// +[ApiVersion("1.0")] +[Authorize] +[Route("api/admin/v{version:apiVersion}/maps")] +public sealed class TencentMapsController(IConfiguration configuration) : BaseApiController +{ + private const string DefaultLibraries = "visualization,geometry,vector,tools"; + private const string DefaultCallback = "initGLMap"; + private const string DefaultVersion = "1.exp"; + + /// + /// 获取腾讯地图 JS 脚本地址(含签名)。 + /// + [HttpGet("tencent-js")] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status400BadRequest)] + public ApiResponse GetTencentJsScript( + [FromQuery] string? libraries, + [FromQuery] string? callback, + [FromQuery] string? v) + { + // 1. 读取配置 + var key = configuration.GetValue("TencentMap:Key"); + var sk = configuration.GetValue("TencentMap:Sk"); + if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(sk)) + { + return ApiResponse.Error(ErrorCodes.BadRequest, "未配置腾讯地图 Key/SK"); + } + + // 2. 规范化参数 + var resolvedLibraries = string.IsNullOrWhiteSpace(libraries) ? DefaultLibraries : libraries; + var resolvedCallback = string.IsNullOrWhiteSpace(callback) ? DefaultCallback : callback; + var resolvedVersion = string.IsNullOrWhiteSpace(v) ? DefaultVersion : v; + + // 3. 构建签名 + var parameters = new SortedDictionary(StringComparer.Ordinal) + { + ["callback"] = resolvedCallback, + ["key"] = key, + ["libraries"] = resolvedLibraries, + ["v"] = resolvedVersion + }; + var queryString = BuildQueryString(parameters); + var signature = ComputeSignature("/api/gljs", queryString, sk); + var scriptUrl = $"https://map.qq.com/api/gljs?{queryString}&sig={signature}"; + + return ApiResponse.Ok(new TencentMapScriptResponse(scriptUrl)); + } + + private static string BuildQueryString(IEnumerable> parameters) + { + var builder = new StringBuilder(); + foreach (var (key, value) in parameters) + { + if (builder.Length > 0) + { + builder.Append('&'); + } + builder.Append(key); + builder.Append('='); + builder.Append(Uri.EscapeDataString(value)); + } + return builder.ToString(); + } + + private static string ComputeSignature(string path, string queryString, string sk) + { + var raw = $"{path}?{queryString}{sk}"; + var hash = MD5.HashData(Encoding.UTF8.GetBytes(raw)); + var builder = new StringBuilder(hash.Length * 2); + foreach (var b in hash) + { + builder.Append(b.ToString("x2")); + } + return builder.ToString(); + } +} diff --git a/src/Api/TakeoutSaaS.AdminApi/appsettings.Development.json b/src/Api/TakeoutSaaS.AdminApi/appsettings.Development.json index 6256ff4..dbce292 100644 --- a/src/Api/TakeoutSaaS.AdminApi/appsettings.Development.json +++ b/src/Api/TakeoutSaaS.AdminApi/appsettings.Development.json @@ -139,6 +139,10 @@ "AntiLeechTokenSecret": "ReplaceWithARandomToken" } }, + "TencentMap": { + "Key": "DGIBZ-YUM64-5ONUT-F4RIA-MPUP2-XFFXZ", + "Sk": "6ztzMqwtuOyaJLuBs1koRDyqNpnVyda8" + }, "Sms": { "Provider": "Tencent", "DefaultSignName": "外卖SaaS", diff --git a/src/Api/TakeoutSaaS.AdminApi/appsettings.Production.json b/src/Api/TakeoutSaaS.AdminApi/appsettings.Production.json index 3eb75e2..98295d9 100644 --- a/src/Api/TakeoutSaaS.AdminApi/appsettings.Production.json +++ b/src/Api/TakeoutSaaS.AdminApi/appsettings.Production.json @@ -139,6 +139,10 @@ "AntiLeechTokenSecret": "ReplaceWithARandomToken" } }, + "TencentMap": { + "Key": "DGIBZ-YUM64-5ONUT-F4RIA-MPUP2-XFFXZ", + "Sk": "6ztzMqwtuOyaJLuBs1koRDyqNpnVyda8" + }, "Sms": { "Provider": "Tencent", "DefaultSignName": "外卖SaaS",