|
|
@@ -4,11 +4,37 @@ namespace Longyi\Wholesale\Http\Controllers;
|
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
use Illuminate\Routing\Controller;
|
|
|
+use Illuminate\Support\Facades\RateLimiter;
|
|
|
use Longyi\Wholesale\Helpers\ApiResponse;
|
|
|
use Longyi\Wholesale\Models\Message;
|
|
|
|
|
|
class WholesaleController extends Controller
|
|
|
{
|
|
|
+ /**
|
|
|
+ * 短窗口:同一 IP 允许的最大提交次数(60 秒内最多 1 次)。
|
|
|
+ */
|
|
|
+ const MAX_ATTEMPTS = 1;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 短窗口时长(秒)。
|
|
|
+ */
|
|
|
+ const DECAY_SECONDS = 60;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 长窗口:同一 IP 允许的最大提交次数(24 小时内最多 5 次)。
|
|
|
+ */
|
|
|
+ const DAILY_MAX_ATTEMPTS = 5;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 长窗口时长(秒):24 小时。
|
|
|
+ */
|
|
|
+ const DAILY_DECAY_SECONDS = 86400;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 重复提交去重窗口(秒):同一 IP 相同邮箱在该时间内只允许提交一次。
|
|
|
+ */
|
|
|
+ const DUPLICATE_SECONDS = 60;
|
|
|
+
|
|
|
/**
|
|
|
* Store a new wholesale enquiry.
|
|
|
*
|
|
|
@@ -16,6 +42,45 @@ class WholesaleController extends Controller
|
|
|
*/
|
|
|
public function store(Request $request)
|
|
|
{
|
|
|
+ $ip = $request->ip();
|
|
|
+
|
|
|
+ // 1. IP 级限流,防止高频恶意提交。
|
|
|
+ // 短窗口:60 秒内最多 1 次。
|
|
|
+ $throttleKey = 'wholesale-submit:'.$ip;
|
|
|
+
|
|
|
+ if (RateLimiter::tooManyAttempts($throttleKey, self::MAX_ATTEMPTS)) {
|
|
|
+ return ApiResponse::error(
|
|
|
+ trans('wholesale::app.shop.too-many-attempts'),
|
|
|
+ [],
|
|
|
+ 429
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 长窗口:24 小时内最多 5 次。
|
|
|
+ $dailyThrottleKey = 'wholesale-submit-daily:'.$ip;
|
|
|
+
|
|
|
+ if (RateLimiter::tooManyAttempts($dailyThrottleKey, self::DAILY_MAX_ATTEMPTS)) {
|
|
|
+ return ApiResponse::error(
|
|
|
+ trans('wholesale::app.shop.too-many-attempts'),
|
|
|
+ [],
|
|
|
+ 429
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ RateLimiter::hit($throttleKey, self::DECAY_SECONDS);
|
|
|
+ RateLimiter::hit($dailyThrottleKey, self::DAILY_DECAY_SECONDS);
|
|
|
+
|
|
|
+ // 2. 重复提交检测:同一 IP + 邮箱在窗口期内只允许提交一次。
|
|
|
+ $duplicateKey = 'wholesale-dup:'.md5($ip.'|'.strtolower((string) $request->input('email')));
|
|
|
+
|
|
|
+ if (cache()->has($duplicateKey)) {
|
|
|
+ return ApiResponse::error(
|
|
|
+ trans('wholesale::app.shop.duplicate-submission'),
|
|
|
+ [],
|
|
|
+ 429
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
try {
|
|
|
$validated = $request->validate([
|
|
|
'name' => 'required|string|max:200',
|
|
|
@@ -37,6 +102,9 @@ class WholesaleController extends Controller
|
|
|
'business_role' => $validated['business_role'] ?? '',
|
|
|
]);
|
|
|
|
|
|
+ // 记录去重标记,窗口期内重复提交会被拒绝。
|
|
|
+ cache()->put($duplicateKey, $message->message_id, self::DUPLICATE_SECONDS);
|
|
|
+
|
|
|
return ApiResponse::success([
|
|
|
'id' => $message->message_id,
|
|
|
], trans('wholesale::app.shop.success'));
|