157 lines
4.5 KiB
C#
157 lines
4.5 KiB
C#
using System.Text.RegularExpressions;
|
||
|
||
namespace OrderSupportTicket.TestServer;
|
||
|
||
/// <summary>
|
||
/// 与客户端 OrderSupportTicket.Services.TicketApiService 保持一致的参数规则。
|
||
/// </summary>
|
||
public static class ContractValidator
|
||
{
|
||
private static readonly Regex AppIdRegex =
|
||
new(@"^wx[0-9a-f]{16}$", RegexOptions.Compiled | RegexOptions.CultureInvariant);
|
||
|
||
private static readonly Regex OrderIdRegex =
|
||
new(@"^[0-9]{10,64}$", RegexOptions.Compiled | RegexOptions.CultureInvariant);
|
||
|
||
private static readonly Regex PurchaseAccountRegex =
|
||
new(@"^[\p{L}\p{N}_.@-]{2,120}$", RegexOptions.Compiled);
|
||
|
||
private static readonly Regex OperationIdRegex =
|
||
new(@"^[A-Za-z0-9_.:-]{1,128}$", RegexOptions.Compiled | RegexOptions.CultureInvariant);
|
||
|
||
private static readonly HashSet<string> Statuses =
|
||
new(StringComparer.OrdinalIgnoreCase)
|
||
{
|
||
"waiting_reply",
|
||
"replied",
|
||
"completed"
|
||
};
|
||
|
||
public static string PurchaseAccount(string? value)
|
||
{
|
||
var normalized = value?.Trim();
|
||
if (string.IsNullOrWhiteSpace(normalized) || !PurchaseAccountRegex.IsMatch(normalized))
|
||
{
|
||
throw BadRequest(
|
||
"purchaseAccount 必填,且只能为 2~120 位中英文、数字或 _.@-。",
|
||
"invalid_purchase_account");
|
||
}
|
||
|
||
return normalized;
|
||
}
|
||
|
||
public static string AppId(string? value)
|
||
{
|
||
var normalized = value?.Trim();
|
||
if (string.IsNullOrWhiteSpace(normalized) || !AppIdRegex.IsMatch(normalized))
|
||
{
|
||
throw BadRequest(
|
||
"appId 必须为 wx 加 16 位小写十六进制字符。",
|
||
"invalid_app_id");
|
||
}
|
||
|
||
return normalized;
|
||
}
|
||
|
||
public static string WechatOrderId(string? value)
|
||
{
|
||
var normalized = value?.Trim();
|
||
if (string.IsNullOrWhiteSpace(normalized) || !OrderIdRegex.IsMatch(normalized))
|
||
{
|
||
throw BadRequest(
|
||
"wechatOrderId 必须保持为 10~64 位数字字符串。",
|
||
"invalid_wechat_order_id");
|
||
}
|
||
|
||
return normalized;
|
||
}
|
||
|
||
public static string Description(string? value)
|
||
{
|
||
var normalized = value?.Trim();
|
||
if (string.IsNullOrWhiteSpace(normalized) || normalized.Length > 2000)
|
||
{
|
||
throw BadRequest(
|
||
"description 必须为 1~2000 字。",
|
||
"invalid_description");
|
||
}
|
||
|
||
return normalized;
|
||
}
|
||
|
||
public static string MessageText(string? value)
|
||
{
|
||
var normalized = value?.Trim();
|
||
if (string.IsNullOrWhiteSpace(normalized) || normalized.Length > 4000)
|
||
{
|
||
throw BadRequest(
|
||
"text 必须为 1~4000 字。",
|
||
"invalid_message_text");
|
||
}
|
||
|
||
return normalized;
|
||
}
|
||
|
||
public static string? OptionalOperationId(string? value, string parameterName)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(value))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
return OperationId(value, parameterName);
|
||
}
|
||
|
||
public static string OperationId(string? value, string parameterName)
|
||
{
|
||
var normalized = value?.Trim();
|
||
if (string.IsNullOrWhiteSpace(normalized) || !OperationIdRegex.IsMatch(normalized))
|
||
{
|
||
throw BadRequest(
|
||
parameterName + " 必须为 1~128 位字母、数字或 _.:-。",
|
||
"invalid_operation_id");
|
||
}
|
||
|
||
return normalized;
|
||
}
|
||
|
||
public static string? Status(string? value)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(value))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
var normalized = value.Trim().ToLowerInvariant();
|
||
if (!Statuses.Contains(normalized))
|
||
{
|
||
throw BadRequest(
|
||
"status 只能为 waiting_reply、replied 或 completed。",
|
||
"invalid_status");
|
||
}
|
||
|
||
return normalized;
|
||
}
|
||
|
||
public static int Limit(int value, int fallback)
|
||
{
|
||
if (value == 0)
|
||
{
|
||
value = fallback;
|
||
}
|
||
|
||
if (value is < 1 or > 100)
|
||
{
|
||
throw BadRequest("limit 必须为 1~100。", "invalid_limit");
|
||
}
|
||
|
||
return value;
|
||
}
|
||
|
||
public static ApiException BadRequest(string message, string code) =>
|
||
new(StatusCodes.Status400BadRequest, message, code);
|
||
|
||
public static ApiException NotFound(string message, string code = "work_order_not_found") =>
|
||
new(StatusCodes.Status404NotFound, message, code);
|
||
}
|