You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

78 lines
5.6 KiB

<?php
namespace App\Services\SupplyDemand;
use App\Models\SupplyDemand;
use App\Models\Dialogue;
use App\Models\Message;
use Illuminate\Support\Facades\DB;
class Conversations
{
public static function key($demandId, $a, $b) { return (int) $demandId . ':' . min((int) $a, (int) $b) . ':' . max((int) $a, (int) $b); }
public static function resolve($viewer, array $input, $create = false) {
if (!empty($input['dialogue_id'])) {
return Dialogue::whereKey($input['dialogue_id'])->where(function ($q) use ($viewer) {
$q->where('user_id', $viewer)->orWhere('to_user_id', $viewer);
})->firstOrFail();
}
$demandId = (int) ($input['supply_demand_id'] ?? 0);
$other = (int) ($input['to_user_id'] ?? 0);
if ($demandId) {
$query = SupplyDemand::whereKey($demandId);
if ($create) $query->lockForUpdate();
$d = $query->firstOrFail();
// 接收人由需求归属确定,不能由请求伪造;发布人回复须指定已有会话。
if ((int) $d->user_id === (int) $viewer) {
if (!$other || $other === (int) $viewer) throw new \InvalidArgumentException('不能给自己发信息');
$existing = Dialogue::where('supply_demand_id', $demandId)->where('user_id', $other)->where('to_user_id', $viewer)->first();
if (!$existing) throw new \InvalidArgumentException('对接记录不存在');
return $existing;
}
if ((int) $d->status !== 1) throw new \InvalidArgumentException('该供需信息尚未发布或已下架');
if ($d->expire_time && \Illuminate\Support\Carbon::parse($d->expire_time)->isPast()) throw new \InvalidArgumentException('该信息已过期,不能发起对接');
if ((!$d->user_id && !$d->is_proxy) || ($d->user_id && !\App\Models\User::whereKey($d->user_id)->exists())) throw new \InvalidArgumentException('发布人资料暂不可用,请联系平台');
$other = (int) $d->user_id;
} elseif (!$other || $other === (int) $viewer || !\App\Models\User::whereKey($other)->exists()) {
throw new \InvalidArgumentException('请选择有效的对接人,不能给自己发信息');
}
$key = self::key($demandId, $viewer, $other);
$q = Dialogue::where('conversation_key', $key);
if (!$create) return $q->first();
return Dialogue::firstOrCreate(['conversation_key' => $key], ['user_id' => $viewer, 'to_user_id' => $other, 'supply_demand_id' => $demandId ?: null]);
}
public static function send($viewer, array $input) {
$content = trim((string) ($input['content'] ?? ''));
if ($content === '' || mb_strlen($content) > 2000) throw new \InvalidArgumentException('请输入1至2000字的对接内容');
return DB::transaction(function () use ($viewer, $input, $content) {
// 同一用户并发发送时串行检查每日限额。
\App\Models\User::whereKey($viewer)->lockForUpdate()->firstOrFail();
$dialogue = self::resolve($viewer, $input, true);
$dialogue = Dialogue::whereKey($dialogue->id)->lockForUpdate()->firstOrFail();
$other = (int) $dialogue->user_id === (int) $viewer ? (int) $dialogue->to_user_id : (int) $dialogue->user_id;
if ($other === (int) $viewer) throw new \InvalidArgumentException('不能给自己发信息');
$d = $dialogue->supplyDemand;
if ($d && $d->expire_time && \Illuminate\Support\Carbon::parse($d->expire_time)->isPast()) throw new \InvalidArgumentException('该信息已过期,不能发送对接');
$limit = \App\Models\Config::getValueByKey('message_limit');
if (is_numeric($limit) && $limit > 0 && Message::where('user_id', $viewer)->whereBetween('created_at', [now()->startOfDay(), now()->endOfDay()])->count() >= $limit) throw new \InvalidArgumentException('今天私信次数已达上限');
// 未回复前只允许一条,系统代管期间同样适用。
if ($dialogue->messages()->where('user_id', $viewer)->exists() && !$dialogue->messages()->where('user_id', $other)->exists()) throw new \InvalidArgumentException('对方回复以后才可以再次发送消息');
$first = !$dialogue->messages()->exists();
$m = Message::create(['dialogue_id' => $dialogue->id, 'user_id' => $viewer, 'to_user_id' => $other,
'supply_demand_id' => $dialogue->supply_demand_id ?: 0, 'content' => $content, 'is_read' => 0]);
$dialogue->last_content = $content; $dialogue->last_datetime = now()->toDateTimeString(); $dialogue->save();
if ($first && $d) $d->increment('contact_count');
if ($d && $other && (int) $d->user_id === $other) Notices::queue($other);
return ['id' => $m->id, 'dialogue_id' => $dialogue->id, 'is_managed' => $d && $d->is_proxy && !$d->user_id];
});
}
public static function summary(Dialogue $d, $viewer, $private = false) {
$d->loadMissing(['user', 'toUser', 'supplyDemand']);
$out = $d->attributesToArray();
$out['user'] = Profiles::user($d->user, $private);
$out['to_user'] = Profiles::user($d->toUser, $private);
$out['supply_demand'] = $d->supplyDemand ? Profiles::demand($d->supplyDemand, $private, $viewer) : null;
$out['unread_count'] = $d->messages()->where('to_user_id', $viewer)->where('is_read', 0)->count();
$out['other_profile'] = (int) $d->user_id === (int) $viewer ? $out['to_user'] : $out['user'];
if (!$out['other_profile'] && $d->supplyDemand) $out['other_profile'] = Profiles::publisher($d->supplyDemand, $private);
return $out;
}
}