2026-03-01 15:03:34 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文件功能:婚姻核心业务服务
|
|
|
|
|
|
*
|
|
|
|
|
|
* 处理求婚、接受、拒绝、离婚全流程。
|
|
|
|
|
|
* 所有金币/魅力变更通过 UserCurrencyService,
|
|
|
|
|
|
* 亲密度变更通过 MarriageIntimacyService,
|
|
|
|
|
|
* 参数通过 MarriageConfigService 读取。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ChatRoom Laravel
|
|
|
|
|
|
*
|
|
|
|
|
|
* @version 1.0.0
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
|
|
|
|
|
|
|
use App\Enums\CurrencySource;
|
|
|
|
|
|
use App\Enums\IntimacySource;
|
|
|
|
|
|
use App\Models\Marriage;
|
|
|
|
|
|
use App\Models\User;
|
|
|
|
|
|
use App\Models\UserPurchase;
|
|
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
|
|
|
|
|
|
|
|
class MarriageService
|
|
|
|
|
|
{
|
|
|
|
|
|
public function __construct(
|
|
|
|
|
|
private readonly UserCurrencyService $currency,
|
|
|
|
|
|
private readonly MarriageConfigService $config,
|
|
|
|
|
|
private readonly MarriageIntimacyService $intimacy,
|
|
|
|
|
|
) {}
|
|
|
|
|
|
|
|
|
|
|
|
// ──────────────────────────── 求婚 ────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 发起求婚。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param User $proposer 求婚方
|
|
|
|
|
|
* @param User $target 被求婚方
|
2026-03-01 17:53:43 +08:00
|
|
|
|
* @param int $weddingTierId 可选的婚礼档位 ID
|
2026-03-01 15:03:34 +08:00
|
|
|
|
* @return array{ok: bool, message: string, marriage_id: int|null}
|
|
|
|
|
|
*/
|
2026-03-01 17:53:43 +08:00
|
|
|
|
public function propose(User $proposer, User $target, int $ringPurchaseId, ?int $weddingTierId = null): array
|
2026-03-01 15:03:34 +08:00
|
|
|
|
{
|
|
|
|
|
|
// 不能向自己求婚
|
|
|
|
|
|
if ($proposer->id === $target->id) {
|
|
|
|
|
|
return ['ok' => false, 'message' => '不能向自己求婚!', 'marriage_id' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 16:04:32 +08:00
|
|
|
|
// 只允许异性之间求婚(sex 字段:1=男 2=女 0=未设置)
|
|
|
|
|
|
$validSexes = [1, 2];
|
2026-03-01 15:34:36 +08:00
|
|
|
|
if (
|
2026-03-01 16:04:32 +08:00
|
|
|
|
! in_array((int) $proposer->sex, $validSexes, true) ||
|
|
|
|
|
|
! in_array((int) $target->sex, $validSexes, true) ||
|
|
|
|
|
|
(int) $proposer->sex === (int) $target->sex
|
2026-03-01 15:34:36 +08:00
|
|
|
|
) {
|
|
|
|
|
|
return ['ok' => false, 'message' => '只有男女双方才能互相求婚,请确认双方性别设置。', 'marriage_id' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 15:03:34 +08:00
|
|
|
|
// 检查求婚方是否在冷静期
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
2026-03-01 17:53:43 +08:00
|
|
|
|
return DB::transaction(function () use ($proposer, $target, $ring, $expireHours, $weddingTierId) {
|
|
|
|
|
|
// 在求婚阶段同时进行婚礼设置(由求婚方一人出全资预扣,属于 "男方付 / scheduled冻结" 模式)
|
|
|
|
|
|
if ($weddingTierId) {
|
|
|
|
|
|
$tier = \App\Models\WeddingTier::find($weddingTierId);
|
|
|
|
|
|
if ($tier && $tier->is_active) {
|
|
|
|
|
|
if (($proposer->jjb ?? 0) < $tier->amount) {
|
2026-03-01 18:29:19 +08:00
|
|
|
|
return ['ok' => false, 'message' => "金币不足,该婚礼档位需要 {$tier->amount} 金币。", 'marriage_id' => null];
|
2026-03-01 17:53:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 15:03:34 +08:00
|
|
|
|
// 戒指状态改为占用中
|
|
|
|
|
|
$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,
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
2026-03-01 17:53:43 +08:00
|
|
|
|
if ($weddingTierId) {
|
|
|
|
|
|
$weddingService = app(WeddingService::class);
|
|
|
|
|
|
// 预扣冻结:payerType=groom, ceremonyType=scheduled
|
|
|
|
|
|
$setupRes = $weddingService->setup($marriage, $weddingTierId, 'groom', 'scheduled');
|
2026-03-01 18:29:19 +08:00
|
|
|
|
if (! $setupRes['ok']) {
|
2026-03-01 17:53:43 +08:00
|
|
|
|
throw new \Exception($setupRes['message']);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 15:03:34 +08:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
2026-03-01 17:53:43 +08:00
|
|
|
|
|
|
|
|
|
|
// 如果有预先设置的婚礼(随求婚一起设定的),则将冻结金币转为正式扣除,并触发红包
|
|
|
|
|
|
$ceremony = \App\Models\WeddingCeremony::where('marriage_id', $marriage->id)->where('status', 'pending')->first();
|
|
|
|
|
|
if ($ceremony) {
|
|
|
|
|
|
$weddingService = app(WeddingService::class);
|
|
|
|
|
|
$weddingService->confirmCeremony($ceremony); // 解冻扣除,转为 immediate
|
|
|
|
|
|
$weddingService->trigger($ceremony);
|
|
|
|
|
|
broadcast(new \App\Events\WeddingCelebration($ceremony, $marriage));
|
|
|
|
|
|
}
|
2026-03-01 15:03:34 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
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' => '无权操作此求婚。'];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 17:53:43 +08:00
|
|
|
|
return DB::transaction(function () use ($marriage) {
|
2026-03-01 15:03:34 +08:00
|
|
|
|
$marriage->update(['status' => 'rejected']);
|
2026-03-01 17:53:43 +08:00
|
|
|
|
|
|
|
|
|
|
// 检查是否有由于求婚冻结的婚礼金币,若有则退还
|
|
|
|
|
|
$ceremony = \App\Models\WeddingCeremony::where('marriage_id', $marriage->id)->where('status', 'pending')->first();
|
|
|
|
|
|
if ($ceremony) {
|
|
|
|
|
|
app(WeddingService::class)->cancelAndRefund($ceremony);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 戒指拒绝后遗失
|
2026-03-01 15:03:34 +08:00
|
|
|
|
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})");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 18:29:19 +08:00
|
|
|
|
return ['ok' => true, 'message' => '已拒绝求婚。'];
|
|
|
|
|
|
});
|
2026-03-01 15:03:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ──────────────────────────── 离婚 ──────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 申请离婚或发起强制离婚。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @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' => '协议离婚已完成。'];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 19:02:43 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 被申请方拒绝协议离婚申请(等同于强制离婚)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 规则:
|
|
|
|
|
|
* - 婚姻立即以 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} 枚金币给你,婚姻已解除。"];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 15:03:34 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 强制离婚(单方立即生效,发起方金币全转对方)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @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})");
|
|
|
|
|
|
}
|
2026-03-01 17:53:43 +08:00
|
|
|
|
|
|
|
|
|
|
// 退还当时冻结的婚礼金币
|
|
|
|
|
|
$ceremony = \App\Models\WeddingCeremony::where('marriage_id', $marriage->id)->where('status', 'pending')->first();
|
|
|
|
|
|
if ($ceremony) {
|
|
|
|
|
|
app(WeddingService::class)->cancelAndRefund($ceremony);
|
|
|
|
|
|
}
|
2026-03-01 15:03:34 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理协议离婚超时自动升级为强制(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()) {
|
2026-03-01 18:15:37 +08:00
|
|
|
|
// 取两者的完全相差天数,如果有部分不够一天的则向上取整为 1 天(例:还剩 2小时 = 1天)
|
|
|
|
|
|
$diffInHours = now()->diffInHours($cooldownEnds);
|
|
|
|
|
|
$remaining = max(1, (int) ceil($diffInHours / 24));
|
2026-03-01 15:03:34 +08:00
|
|
|
|
|
|
|
|
|
|
return "您还在离婚冷静期,还需 {$remaining} 天后才能再次结婚。";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|