From 84a4b42f31ace01d695fc2bbe2948cd8825028cc Mon Sep 17 00:00:00 2001 From: lkddi Date: Sun, 1 Mar 2026 19:02:43 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A6=BB=E5=A9=9A=E6=B5=81=E7=A8=8B=E5=85=A8?= =?UTF-8?q?=E9=9D=A2=E5=8D=87=E7=BA=A7=EF=BC=9A=E2=91=A0=E5=8F=91=E8=B5=B7?= =?UTF-8?q?=E6=96=B9=E4=B8=93=E5=B1=9E=E7=A1=AE=E8=AE=A4=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=EF=BC=88=E5=90=AB=E5=AF=B9=E6=96=B9=E6=8B=92=E7=BB=9D=E5=90=8E?= =?UTF-8?q?=E6=9E=9C+=E9=AD=85=E5=8A=9B/=E9=87=91=E5=B8=81=E6=83=A9?= =?UTF-8?q?=E7=BD=9A=E5=AE=9E=E6=97=B6=E5=80=BC=EF=BC=89=E2=91=A1=E8=A2=AB?= =?UTF-8?q?=E7=94=B3=E8=AF=B7=E6=96=B9=E4=B8=89=E9=80=89=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=EF=BC=88=E5=90=8C=E6=84=8F/=E4=B8=8D=E5=90=8C=E6=84=8F/?= =?UTF-8?q?=E7=A8=8D=E5=90=8E=EF=BC=89=E2=91=A2=E4=B8=8D=E5=90=8C=E6=84=8F?= =?UTF-8?q?=3D=E5=BC=BA=E5=88=B6=E7=A6=BB=E5=A9=9A=E7=94=B3=E8=AF=B7?= =?UTF-8?q?=E4=BA=BA=E8=B5=94=E4=B8=80=E5=8D=8A=E9=87=91=E5=B8=81=E2=91=A3?= =?UTF-8?q?=E6=89=80=E6=9C=89=E6=83=A9=E7=BD=9A=E6=95=B0=E5=80=BC=E4=BB=8E?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E5=AE=9E=E6=97=B6=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Events/MarriageDivorceRequested.php | 3 + app/Http/Controllers/MarriageController.php | 32 ++ app/Services/MarriageService.php | 63 ++++ resources/views/chat/frame.blade.php | 2 + .../chat/partials/marriage-modals.blade.php | 352 ++++++++++++++++-- .../chat/partials/user-actions.blade.php | 39 +- routes/web.php | 4 + 7 files changed, 444 insertions(+), 51 deletions(-) diff --git a/app/Events/MarriageDivorceRequested.php b/app/Events/MarriageDivorceRequested.php index ac45755..6892d09 100644 --- a/app/Events/MarriageDivorceRequested.php +++ b/app/Events/MarriageDivorceRequested.php @@ -58,6 +58,8 @@ class MarriageDivorceRequested implements ShouldBroadcastNow // 读取协议离婚魅力惩罚供前端展示 $penalty = (int) \App\Models\MarriageConfig::where('key', 'divorce_mutual_charm')->value('value'); + // 读取强制离婚魅力惩罚(被拒=强制离婚时申请方受此惩罚) + $forcedPenalty = (int) \App\Models\MarriageConfig::where('key', 'divorce_forced_charm')->value('value'); return [ 'marriage_id' => $this->marriage->id, @@ -66,6 +68,7 @@ class MarriageDivorceRequested implements ShouldBroadcastNow 'timeout_hours' => 72, 'requested_at' => $this->marriage->divorce_requested_at, 'mutual_charm_penalty' => $penalty, // 协议离婚双方各扣魅力 + 'forced_charm_penalty' => $forcedPenalty, // 不同意→强制,申请方受此惩罚 ]; } diff --git a/app/Http/Controllers/MarriageController.php b/app/Http/Controllers/MarriageController.php index 876f4ba..bb678a5 100644 --- a/app/Http/Controllers/MarriageController.php +++ b/app/Http/Controllers/MarriageController.php @@ -20,6 +20,7 @@ use App\Events\MarriageProposed; use App\Events\MarriageRejected; use App\Models\Marriage; use App\Models\UserPurchase; +use App\Services\MarriageConfigService; use App\Services\MarriageService; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -28,8 +29,23 @@ class MarriageController extends Controller { public function __construct( private readonly MarriageService $marriage, + private readonly MarriageConfigService $config, ) {} + /** + * 获取离婚相关惩罚配置(供前端展示风险提示)。 + * 返回协议离婚魅力惩罚、强制离婚魅力惩罚及各冷静期天数。 + */ + public function divorceConfig(): JsonResponse + { + return response()->json([ + 'mutual_charm_penalty' => (int) $this->config->get('divorce_mutual_charm', 100), + 'forced_charm_penalty' => (int) $this->config->get('divorce_forced_charm', 300), + 'mutual_cooldown_days' => (int) $this->config->get('divorce_mutual_cooldown', 70), + 'forced_cooldown_days' => (int) $this->config->get('divorce_forced_cooldown', 90), + ]); + } + /** * 获取当前用户的婚姻状态(名片/用户列表用)。 */ @@ -221,4 +237,20 @@ class MarriageController extends Controller return response()->json($result); } + + /** + * 拒绝协议离婚申请(被申请方选择不同意 = 视为强制离婚)。 + * 申请人赔偿一半金币给对方,婚姻以 forced 类型解除。 + */ + public function rejectDivorce(Request $request, Marriage $marriage): JsonResponse + { + $result = $this->marriage->rejectDivorce($marriage, $request->user()); + + if ($result['ok']) { + $marriage->refresh(); + broadcast(new MarriageDivorced($marriage, 'forced')); + } + + return response()->json($result); + } } diff --git a/app/Services/MarriageService.php b/app/Services/MarriageService.php index 94a8b00..e75ed59 100644 --- a/app/Services/MarriageService.php +++ b/app/Services/MarriageService.php @@ -304,6 +304,69 @@ class MarriageService return ['ok' => true, 'message' => '协议离婚已完成。']; } + /** + * 被申请方拒绝协议离婚申请(等同于强制离婚)。 + * + * 规则: + * - 婚姻立即以 forced 类型解除 + * - 申请人(divorcer)被扣除 divorce_forced_charm 点魅力作为惩罚 + * - 申请人一半金币赔偿给被申请方 + * - 申请人进入强制离婚冷静期 + * + * @param Marriage $marriage 婚姻记录 + * @param User $respondent 被申请方(拒绝者) + * @return array{ok: bool, message: string} + */ + public function rejectDivorce(Marriage $marriage, User $respondent): array + { + if ($marriage->status !== 'married' || $marriage->divorce_type !== 'mutual') { + return ['ok' => false, 'message' => '没有待处理的离婚申请。']; + } + if ($marriage->divorcer_id === $respondent->id) { + return ['ok' => false, 'message' => '不能拒绝自己发起的离婚申请。']; + } + if (! $marriage->involves($respondent->id)) { + return ['ok' => false, 'message' => '无权操作此婚姻。']; + } + + // 申请方(被拒的一方) + $divorcer = $marriage->user_id === $marriage->divorcer_id + ? $marriage->user + : $marriage->partner; + + DB::transaction(function () use ($marriage, $divorcer, $respondent) { + $charmPenalty = $this->config->get('divorce_forced_charm', 300); + + // 申请人扣魅力 + $this->currency->change($divorcer, 'charm', -$charmPenalty, CurrencySource::DIVORCE_CHARM, '拒绝离婚时视为强制离婚,扣除魅力惩罚'); + + // 申请人一半金币赔偿给对方 + $divorcerJjb = $divorcer->fresh()->jjb ?? 0; + if ($divorcerJjb > 0) { + $half = (int) floor($divorcerJjb / 2); + if ($half > 0) { + $this->currency->change($divorcer, 'gold', -$half, CurrencySource::FORCED_DIVORCE_TRANSFER, "协议离婚被拒,赔偿一半金币给 {$respondent->username}"); + $this->currency->change($respondent, 'gold', $half, CurrencySource::FORCED_DIVORCE_TRANSFER, "对方协议离婚被拒,获得 {$divorcer->username} 一半金币赔偿"); + } + } + + // 更新婚姻为强制离婚 + $marriage->update([ + 'status' => 'divorced', + 'divorce_type' => 'forced', + 'divorcer_id' => $divorcer->id, + 'divorced_at' => now(), + 'intimacy' => 0, + 'level' => 1, + ]); + }); + + $divorcerJjb = $divorcer->fresh()->jjb ?? 0; + $half = (int) floor(($divorcer->jjb ?? 0) / 2); + + return ['ok' => true, 'message' => "你已拒绝离婚申请,视为强制离婚,{$divorcer->username} 赔偿了 {$half} 枚金币给你,婚姻已解除。"]; + } + /** * 强制离婚(单方立即生效,发起方金币全转对方)。 * diff --git a/resources/views/chat/frame.blade.php b/resources/views/chat/frame.blade.php index 14f0b7d..5d0c13b 100644 --- a/resources/views/chat/frame.blade.php +++ b/resources/views/chat/frame.blade.php @@ -74,6 +74,8 @@ rejectUrl: (id) => `/marriage/${id}/reject`, divorceUrl: (id) => `/marriage/${id}/divorce`, confirmDivorceUrl: (id) => `/marriage/${id}/confirm-divorce`, + rejectDivorceUrl: (id) => `/marriage/${id}/reject-divorce`, + divorceConfigUrl: '/marriage/divorce-config', weddingTiersUrl: "/wedding/tiers", weddingSetupUrl: (id) => `/wedding/${id}/setup`, claimEnvelopeUrl: (id, ceremonyId) => `/wedding/${id}/claim`, diff --git a/resources/views/chat/partials/marriage-modals.blade.php b/resources/views/chat/partials/marriage-modals.blade.php index 1c523a5..0361119 100644 --- a/resources/views/chat/partials/marriage-modals.blade.php +++ b/resources/views/chat/partials/marriage-modals.blade.php @@ -340,6 +340,181 @@ +{{-- ═══════════ 3.5. 发起离婚确认弹窗(发起方专用 + 后果说明) ═══════════ --}} +
+
+
+ + {{-- 头部 --}} +
+
📄
+
发起协议离婚申请
+
请仔细阅读以下后果,确认后再提交申请
+
+ +
+ {{-- 对方同意:协议离婚 --}} +
+
✅ 若对方同意(协议离婚) +
+
    +
  • 婚姻关系立即解除,亲密度清零
  • +
  • 双方各被扣除
  • +
  • 双方进入 + 离婚冷静期,期间无法再次结婚
  • +
+
+ + {{-- 对方拒绝:强制离婚,自己赔钱 --}} +
+
⚠️ + 若对方拒绝(视为强制离婚,后果由你承担)
+
    +
  • 婚姻关系立即强制解除
  • +
  • 你将被扣除 + 点魅力值作为惩罚
  • +
  • 你当前一半的金币将赔偿给对方
  • +
  • 你进入 + 强制离婚冷静期,期间无法再次结婚
  • +
+
+ + {{-- 操作按钮 --}} +
+ + +
+ +
+
+
+
+
+ +{{-- ═══════════ 3.6. 离婚申请通知弹窗(三选按钮 + 后果说明) ═══════════ --}} +
+
+
+ + {{-- 头部 --}} +
+
💔
+
收到离婚申请
+
+
+
请仔细阅读以下后果说明,做出你的选择:
+
+ + {{-- 后果说明区 --}} +
+ + {{-- 同意后果 --}} +
+
+ + 同意离婚 +
+
    +
  • 婚姻关系立即解除,所有亲密度清零
  • +
  • 双方各被扣除 ? 点魅力值 +
  • +
  • 双方都将进入离婚冷静期,期间无法再次结婚
  • +
+
+ + {{-- 拒绝后果 --}} +
+
+ + 不同意(强制离婚) +
+
    +
  • 视同强制离婚,婚姻立即解除
  • +
  • 申请方被扣除大量魅力值作为惩罚
  • +
  • 申请方当前一半金币赔偿给你(入账到你账户)
  • +
  • 申请方进入强制离婚冷静期,期间无法再次结婚
  • +
+
+ + {{-- 取消(稍后处理) --}} +
+
+ + 稍后决定 +
+
    +
  • 关闭此弹窗,暂不做决定
  • +
  • 下次登录或刷新页面时仍会再次弹出提示
  • +
  • 72 小时内若不处理,系统将自动视为对方强制离婚
  • +
+
+ + {{-- 三个按钮 --}} +
+ {{-- 同意 --}} + + {{-- 拒绝 --}} + + {{-- 取消 --}} + +
+ +
+
+
+
+
+ {{-- ═══════════ 4. 婚礼设置弹窗 ═══════════ --}}
{ - const { - initiator_name, - marriage_id, - mutual_charm_penalty - } = e.detail; - const penaltyTip = mutual_charm_penalty > 0 ? - `\n\n⚠️ 双方各将被扣除 ${mutual_charm_penalty} 点魅力值作为惩罚。` : - ''; - window.chatDialog?.confirm( - `${initiator_name} 申请与你协议离婚,是否同意?${penaltyTip}`, - '离婚申请 💔' - ).then(ok => { - if (!ok) return; - fetch(window.chatContext.marriage.confirmDivorceUrl(marriage_id), { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content - }, - body: JSON.stringify({ - room_id: window.chatContext.roomId - }) - }).then(r => r.json()).then(data => { - window.chatDialog?.alert(data.message, data.status === 'success' ? '操作完成' : - '失败', data.status === 'success' ? '#6b7280' : '#cc4444'); - }); - }); + const detail = e.detail; + const el = document.getElementById('divorce-request-modal'); + if (el) Alpine.$data(el).open(detail); }); + + /** 红包领取成功通知(私人频道) */ window.addEventListener('chat:envelope-claimed', (e) => { const { diff --git a/resources/views/chat/partials/user-actions.blade.php b/resources/views/chat/partials/user-actions.blade.php index 5049fb9..d395919 100644 --- a/resources/views/chat/partials/user-actions.blade.php +++ b/resources/views/chat/partials/user-actions.blade.php @@ -172,40 +172,37 @@ return; }, - /** 发起协议离婚 */ + /** 发起协议离婚(先拉惩罚配置,再弹专属全屏确认弹窗) */ async doDivorce(marriageId) { if (!marriageId) return; this.showUserModal = false; - const confirmed = await window.chatDialog?.confirm( - '提出离婚后,对方将在私人频道收到通知。\n\n如果对方在 24 小时内未作决定,此申请将自动升级为“强制离婚”,并扣除发起方一定的魅力值作为单方面解除契约的惩罚。\n\n您确定要继续发起离婚申请吗?', - '发起离婚', - '#475569' - ); - - if (!confirmed) return; - - window.chatDialog?.alert('请稍候…', '发送申请中', '#9ca3af'); + // 从后台实时拉取最新惩罚配置 + let divorceConfig = { + mutual_charm_penalty: 0, + forced_charm_penalty: 0, + mutual_cooldown_days: 0, + forced_cooldown_days: 0 + }; try { - const res = await fetch(window.chatContext.marriage.divorceUrl(marriageId), { - method: 'POST', + const cfgRes = await fetch(window.chatContext.marriage.divorceConfigUrl, { headers: { 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content + 'X-Requested-With': 'XMLHttpRequest' } }); - const data = await res.json(); - if (data.ok) { - window.chatDialog?.alert(data.message, '已发出', '#6b7280'); - } else { - window.chatDialog?.alert(data.message || '操作失败', '错误', '#dc2626'); - } + if (cfgRes.ok) divorceConfig = await cfgRes.json(); } catch (e) { - window.chatDialog?.alert('网络请求失败', '错误', '#dc2626'); + /* 网络异常则使用默认值 */ } + + // 打开专属离婚确认弹窗 + const modal = document.getElementById('divorce-confirm-modal'); + if (modal && window.Alpine) { + Alpine.$data(modal).open(marriageId, divorceConfig); } }, + /** 获取用户资料 */ async fetchUser(username) { try { diff --git a/routes/web.php b/routes/web.php index 8a19cb8..456dfa4 100644 --- a/routes/web.php +++ b/routes/web.php @@ -87,8 +87,12 @@ Route::middleware(['chat.auth'])->group(function () { Route::post('/{marriage}/reject', [\App\Http\Controllers\MarriageController::class, 'reject'])->name('reject'); // 申请离婚(type: mutual|forced) Route::post('/{marriage}/divorce', [\App\Http\Controllers\MarriageController::class, 'divorce'])->name('divorce'); + // 离婚惩罚配置查询(前端展示风险提示用) + Route::get('/divorce-config', [\App\Http\Controllers\MarriageController::class, 'divorceConfig'])->name('divorce-config'); // 确认协议离婚 Route::post('/{marriage}/confirm-divorce', [\App\Http\Controllers\MarriageController::class, 'confirmDivorce'])->name('confirm-divorce'); + // 拒绝离婚申请(被申请方选择不同意→弹强制离婚) + Route::post('/{marriage}/reject-divorce', [\App\Http\Controllers\MarriageController::class, 'rejectDivorce'])->name('reject-divorce'); }); // ── 婚礼系统(前台)──────────────────────────────────────────────