locked($id, $version); $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.DefinitionImpl', null); $config->set('URI.AllowedSchemes', ['http' => true, 'https' => true, 'mailto' => true]); $html = (new HTMLPurifier($config))->purify($input['content_html'] ?? ''); $news->fill([ 'title' => $input['title'], 'summary' => $input['summary'] ?? null, 'content_html' => $html, 'content_source' => 'manual', 'content_updated_by' => $actor, 'content_updated_at' => now(), 'is_visible' => false, 'visible_at' => null, 'version' => $version + 1, ])->save(); $this->log($news, $actor, 'edit', ['version' => $news->version, 'content_hash' => hash('sha256', $html)]); return $news; }); } public function review(int $id, int $version, string $status, ?string $note, ?int $actor): CompanyNews { return DB::transaction(function () use ($id, $version, $status, $note, $actor) { $news = $this->locked($id, $version); $from = $news->review_status; $news->fill([ 'review_status' => $status, 'review_note' => $note, 'reviewed_by' => $actor, 'reviewed_at' => now(), 'is_visible' => false, 'visible_at' => null, 'version' => $version + 1, ])->save(); $this->log($news, $actor, 'review', ['from' => $from, 'to' => $status, 'note' => $note]); return $news; }); } private function locked(int $id, int $version): CompanyNews { $news = CompanyNews::whereKey($id)->lockForUpdate()->first(); if (!$news) { throw new RuntimeException('新闻不存在'); } if ($news->version !== $version) { throw new RuntimeException('新闻已被其他管理员修改,请重新打开后操作'); } return $news; } private function log(CompanyNews $news, ?int $actor, string $action, array $detail): void { DB::table('company_news_logs')->insert([ 'news_id' => $news->id, 'actor_id' => $actor, 'action' => $action, 'detail' => json_encode($detail, JSON_UNESCAPED_UNICODE), 'created_at' => now(), ]); } }