using System.Text.RegularExpressions;
namespace OrderSupportTicket.TestServer;
///
/// 与客户端 OrderSupportTicket.Services.TicketApiService 保持一致的参数规则。
///
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 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);
}