id === $target->id) { return ['ok' => false, 'message' => '不能向自己求婚!', 'marriage_id' => null]; } // 检查求婚方是否在冷静期 if ($cooldownMsg = $this->checkCooldown($proposer)) { return ['ok' => false, 'message' => $cooldownMsg, 'marriage_id' => null]; } // 检查双方是否已有进行中的婚姻 if (Marriage::currentFor($proposer->id)) { return ['ok' => false, 'message' => '您已有进行中的婚姻或求婚,无法重复求婚。', 'marriage_id' => null]; } if (Marriage::currentFor($target->id)) { return ['ok' => false, 'message' => '对方已有婚姻关系,无法向其求婚。', 'marriage_id' => null]; } // 验证戒指 $ring = UserPurchase::query() ->where('id', $ringPurchaseId) ->where('user_id', $proposer->id) ->where('status', 'active') ->whereHas('item', fn ($q) => $q->where('type', 'ring')) ->with('item') ->first(); if (! $ring) { return ['ok' => false, 'message' => '未找到有效的戒指,请先到商城购买。', 'marriage_id' => null]; } $expireHours = $this->config->get('proposal_expire_hours', 48); return DB::transaction(function () use ($proposer, $target, $ring, $expireHours) { // 戒指状态改为占用中 $ring->update(['status' => 'used_pending']); // 创建婚姻记录 $marriage = Marriage::create([ 'user_id' => $proposer->id, 'partner_id' => $target->id, 'ring_item_id' => $ring->item_id, 'ring_purchase_id' => $ring->id, 'status' => 'pending', 'proposed_at' => now(), 'expires_at' => now()->addHours($expireHours), // 旧字段兼容 'hyname' => $proposer->username, 'hyname1' => $target->username, ]); return ['ok' => true, 'message' => '求婚成功,等待对方回应。', 'marriage_id' => $marriage->id]; }); } // ──────────────────────────── 接受求婚 ────────────────────────────── /** * 接受求婚,正式结婚。 * * @param Marriage $marriage 婚姻记录(必须为 pending 状态) * @param User $acceptor 接受方(必须为 partner) * @return array{ok: bool, message: string} */ public function accept(Marriage $marriage, User $acceptor): array { if ($marriage->status !== 'pending') { return ['ok' => false, 'message' => '该求婚已失效。']; } if ($marriage->partner_id !== $acceptor->id) { return ['ok' => false, 'message' => '无权操作此求婚。']; } if ($marriage->expires_at && $marriage->expires_at->isPast()) { $this->expireProposal($marriage); return ['ok' => false, 'message' => '求婚已超时失效,戒指已消失。']; } $ringItem = $marriage->ringItem; DB::transaction(function () use ($marriage, $ringItem) { // 正式结婚 $marriage->update([ 'status' => 'married', 'married_at' => now(), 'hytime' => now(), ]); // 戒指标记已使用 UserPurchase::where('id', $marriage->ring_purchase_id)->update(['status' => 'used']); // 双方各获魅力加成(通过戒指 slug 查配置) if ($ringItem) { $slug = $ringItem->slug; $charmKey = 'ring_'.str_replace('ring_', '', $slug).'_charm'; $intimacyKey = 'ring_'.str_replace('ring_', '', $slug).'_intimacy'; $charm = $this->config->get($charmKey, 50); $initIntimacy = $this->config->get($intimacyKey, 10); $proposer = $marriage->user; $partner = $marriage->partner; $ringName = $ringItem->name; $this->currency->change($proposer, 'charm', $charm, CurrencySource::MARRY_CHARM, "结婚魅力加成({$ringName})"); $this->currency->change($partner, 'charm', $charm, CurrencySource::MARRY_CHARM, "结婚魅力加成({$ringName})"); // 初始亲密度 $this->intimacy->add($marriage, $initIntimacy, IntimacySource::WEDDING_BONUS, "结婚戒指初始亲密度({$ringName})", true); } }); return ['ok' => true, 'message' => '恭喜!你们已正式结婚!']; } // ──────────────────────────── 拒绝求婚 ────────────────────────────── /** * 拒绝求婚(戒指消失,不退还)。 * * @param Marriage $marriage 婚姻记录 * @param User $rejector 拒绝方 * @return array{ok: bool, message: string} */ public function reject(Marriage $marriage, User $rejector): array { if ($marriage->status !== 'pending') { return ['ok' => false, 'message' => '该求婚已失效。']; } if ($marriage->partner_id !== $rejector->id) { return ['ok' => false, 'message' => '无权操作此求婚。']; } DB::transaction(function () use ($marriage) { $marriage->update(['status' => 'rejected']); // 戒指消失(lost = 不退还) UserPurchase::where('id', $marriage->ring_purchase_id)->update(['status' => 'lost']); // 记录戒指消失流水(amount=0,仅存档) if ($proposer = $marriage->user) { $this->currency->change($proposer, 'gold', 0, CurrencySource::RING_LOST, "求婚被拒,戒指消失({$marriage->ringItem?->name})"); } }); return ['ok' => true, 'message' => '已拒绝求婚。']; } // ──────────────────────────── 离婚 ────────────────────────────────── /** * 申请离婚或发起强制离婚。 * * @param Marriage $marriage 婚姻记录(必须为 married 状态) * @param User $initiator 发起方 * @param string $type 'mutual'=协议 | 'forced'=强制 * @return array{ok: bool, message: string} */ public function divorce(Marriage $marriage, User $initiator, string $type = 'mutual'): array { if ($marriage->status !== 'married') { return ['ok' => false, 'message' => '当前没有有效的婚姻关系。']; } if (! $marriage->involves($initiator->id)) { return ['ok' => false, 'message' => '无权操作此婚姻。']; } if ($type === 'forced') { return $this->forceDissolve($marriage, $initiator); } // 协议离婚:标记申请,等待对方确认(72h 后 Horizon 自动升级) $marriage->update([ 'divorce_type' => 'mutual', 'divorcer_id' => $initiator->id, 'divorce_requested_at' => now(), ]); return ['ok' => true, 'message' => '离婚申请已发送,等待对方确认(72小时内未回应将自动解除)。']; } /** * 确认协议离婚(被申请方接受)。 * * @param Marriage $marriage 婚姻记录 * @param User $confirmer 确认方(必须不是发起方) * @return array{ok: bool, message: string} */ public function confirmDivorce(Marriage $marriage, User $confirmer): array { if ($marriage->status !== 'married' || $marriage->divorce_type !== 'mutual') { return ['ok' => false, 'message' => '没有待确认的离婚申请。']; } if ($marriage->divorcer_id === $confirmer->id) { return ['ok' => false, 'message' => '不能确认自己发起的离婚申请,如需强制离婚请另行操作。']; } if (! $marriage->involves($confirmer->id)) { return ['ok' => false, 'message' => '无权操作此婚姻。']; } DB::transaction(function () use ($marriage) { $penalty = $this->config->get('divorce_mutual_charm', 100); // 双方扣魅力 $this->currency->change($marriage->user, 'charm', -$penalty, CurrencySource::DIVORCE_CHARM, '协议离婚魅力惩罚'); $this->currency->change($marriage->partner, 'charm', -$penalty, CurrencySource::DIVORCE_CHARM, '协议离婚魅力惩罚'); // 更新婚姻状态 $marriage->update([ 'status' => 'divorced', 'divorce_type' => 'mutual', 'divorced_at' => now(), 'intimacy' => 0, 'level' => 1, ]); }); return ['ok' => true, 'message' => '协议离婚已完成。']; } /** * 强制离婚(单方立即生效,发起方金币全转对方)。 * * @param Marriage $marriage 婚姻记录 * @param User $initiator 强制发起方 * @return array{ok: bool, message: string} */ public function forceDissolve(Marriage $marriage, User $initiator, bool $byAdmin = false): array { if (! $byAdmin) { // 检查强制离婚间隔限制 $limitDays = $this->config->get('forced_divorce_limit_days', 60); $recentForced = Marriage::query() ->where('divorcer_id', $initiator->id) ->where('divorce_type', 'forced') ->where('divorced_at', '>=', now()->subDays($limitDays)) ->exists(); if ($recentForced) { return ['ok' => false, 'message' => "您每{$limitDays}天内只能强制离婚1次,冷静期未满。"]; } } $victim = $marriage->user_id === $initiator->id ? $marriage->partner : $marriage->user; DB::transaction(function () use ($marriage, $initiator, $victim, $byAdmin) { if (! $byAdmin) { $penalty = $this->config->get('divorce_forced_charm', 300); // 强制方扣魅力 $this->currency->change($initiator, 'charm', -$penalty, CurrencySource::DIVORCE_CHARM, '强制离婚魅力惩罚'); // 金币全转对方 $initiatorJjb = $initiator->fresh()->jjb ?? 0; if ($initiatorJjb > 0) { $this->currency->change($initiator, 'gold', -$initiatorJjb, CurrencySource::FORCED_DIVORCE_TRANSFER, "强制离婚,财产转让给{$victim->username}"); $this->currency->change($victim, 'gold', $initiatorJjb, CurrencySource::FORCED_DIVORCE_TRANSFER, "强制离婚,获得{$initiator->username}全部财产"); } } $marriage->update([ 'status' => 'divorced', 'divorce_type' => $byAdmin ? 'admin' : 'forced', 'divorcer_id' => $initiator->id, 'divorced_at' => now(), 'intimacy' => 0, 'level' => 1, ]); }); $msg = $byAdmin ? '管理员已强制解除婚姻关系。' : "强制离婚已完成,{$initiator->username} 的全部金币已转给 {$victim->username}。"; return ['ok' => true, 'message' => $msg]; } // ──────────────────────────── 内部工具 ──────────────────────────── /** * 处理超时的求婚记录(Horizon Job 调用)。 */ public function expireProposal(Marriage $marriage): void { if ($marriage->status !== 'pending') { return; } DB::transaction(function () use ($marriage) { $marriage->update(['status' => 'expired']); UserPurchase::where('id', $marriage->ring_purchase_id)->update(['status' => 'lost']); if ($proposer = $marriage->user) { $this->currency->change($proposer, 'gold', 0, CurrencySource::RING_LOST, "求婚超时,戒指消失({$marriage->ringItem?->name})"); } }); } /** * 处理协议离婚超时自动升级为强制(Horizon Job 调用)。 */ public function autoExpireDivorce(Marriage $marriage): void { $divorcer = User::find($marriage->divorcer_id); if (! $divorcer) { return; } $penalty = $this->config->get('divorce_auto_charm', 150); DB::transaction(function () use ($marriage, $divorcer, $penalty) { $this->currency->change($divorcer, 'charm', -$penalty, CurrencySource::DIVORCE_CHARM, '离婚申请超时,自动解除'); $marriage->update([ 'status' => 'divorced', 'divorce_type' => 'auto', 'divorced_at' => now(), 'intimacy' => 0, 'level' => 1, ]); }); } /** * 检查用户是否在冷静期,返回错误提示文字;无冷静期返回 null。 */ private function checkCooldown(User $user): ?string { // 查找最近一次离婚记录 $lastDivorce = Marriage::query() ->where('status', 'divorced') ->where(function ($q) use ($user) { $q->where('user_id', $user->id)->orWhere('partner_id', $user->id); }) ->orderByDesc('divorced_at') ->first(); if (! $lastDivorce) { return null; } $type = $lastDivorce->divorce_type ?? 'mutual'; // 强制方 $isForcer = $lastDivorce->divorcer_id === $user->id && in_array($type, ['forced', 'auto']); $cooldownKey = match (true) { $isForcer && $type === 'forced' => 'divorce_forced_cooldown', $isForcer && $type === 'auto' => 'divorce_auto_cooldown', default => 'divorce_mutual_cooldown', }; $cooldownDays = $this->config->get($cooldownKey, 70); $cooldownEnds = $lastDivorce->divorced_at?->addDays($cooldownDays); if ($cooldownEnds && $cooldownEnds->isFuture()) { $remaining = now()->diffInDays($cooldownEnds, false); return "您还在离婚冷静期,还需 {$remaining} 天后才能再次结婚。"; } return null; } }