- 支付设置:支付宝/微信各自支持正式/沙箱网关环境,凭据字段补全(支付宝公钥、微信商户证书私钥/平台证书 PEM),密钥可直接粘贴 PEM 文本(yansongda 支持字符串) - 修复关键 bug:yansongda 配置结构错误(app_id 等在 default 租户外,真实网关拿不到配置),现按 alipay.default / wechat.default 正确传入,并用 _force 保证每次调用生效 - 后台 AI 设置页新增 WebSocket 实时审核进度(ai.moderation.* 事件),未连接时降级提示 - 部署文档:明确 queue:work 与 workerman:serve 等价、dev:watch 用法 - 测试:支付宝/微信配置结构回归 + AI 设置页加载
88 lines
4.0 KiB
PHP
88 lines
4.0 KiB
PHP
<x-filament-panels::page>
|
|
<form wire:submit="save">
|
|
{{ $this->form }}
|
|
<div class="mt-6">
|
|
<x-filament::button type="submit" color="primary">保存设置</x-filament::button>
|
|
</div>
|
|
</form>
|
|
|
|
<x-filament::section heading="AI 润色文章">
|
|
<div class="space-y-2">
|
|
@foreach(\App\Models\Post::query()->latest()->limit(20)->get(['id','title']) as $post)
|
|
<div class="flex items-center justify-between rounded-lg border border-gray-200 p-3 dark:border-white/10">
|
|
<span class="text-sm">{{ $post->title }}</span>
|
|
<div class="flex gap-2">
|
|
<x-filament::button size="sm" color="primary" wire:click="polish({{ $post->id }})">AI 润色</x-filament::button>
|
|
@if($post->meta['ai_polished'] ?? null)
|
|
<x-filament::button size="sm" color="success" wire:click="acceptPolish({{ $post->id }})">采纳</x-filament::button>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
|
|
@if($polishResult)
|
|
<div class="mt-4 rounded-lg border border-gray-300 bg-gray-50 p-4 dark:border-white/10 dark:bg-white/5">
|
|
<h3 class="mb-2 text-sm font-semibold">润色预览(已暂存,点击采纳写回文章)</h3>
|
|
<pre class="whitespace-pre-wrap text-sm">{{ Str::limit($polishResult, 3000) }}</pre>
|
|
</div>
|
|
@endif
|
|
</x-filament::section>
|
|
|
|
<x-filament::section heading="AI 审核实时进度"
|
|
:description="'WebSocket :'.config('workerman.websocket_port', 8787).'(需 workerman:serve 运行;未连接时结果仍会落库,可在评论列表查看)'">
|
|
<div id="ai-moderation-feed" class="space-y-1 text-sm" style="max-height:260px;overflow-y:auto;">
|
|
<p class="text-gray-500">等待事件…</p>
|
|
</div>
|
|
</x-filament::section>
|
|
</x-filament-panels::page>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
var port = {{ (int) config('workerman.websocket_port', 8787) }};
|
|
var host = location.hostname || 'localhost';
|
|
var feed = document.getElementById('ai-moderation-feed');
|
|
if (! feed) return;
|
|
|
|
var ws;
|
|
try {
|
|
ws = new WebSocket('ws://' + host + ':' + port);
|
|
} catch (e) { return; }
|
|
|
|
ws.onopen = function () {
|
|
feed.innerHTML = '<p class="text-gray-400">已连接,等待 AI 审核事件…</p>';
|
|
};
|
|
ws.onclose = function () {
|
|
feed.innerHTML = '<p class="text-gray-500">WebSocket 未连接(workerman 未运行或端口不通)</p>';
|
|
};
|
|
ws.onmessage = function (e) {
|
|
var msg;
|
|
try { msg = JSON.parse(e.data); } catch (err) { return; }
|
|
if (! msg.event || msg.event.indexOf('ai.moderation') !== 0) return;
|
|
|
|
var d = msg.data || {};
|
|
var item = document.createElement('div');
|
|
var text = '[' + new Date().toLocaleTimeString() + '] 评论 #' + (d.comment_id || '?') + ' ';
|
|
var cls = 'border-gray-300 bg-gray-50 dark:border-white/10';
|
|
|
|
if (msg.event === 'ai.moderation.started') {
|
|
text += '开始审核…';
|
|
cls = 'border-blue-300 bg-blue-50 dark:border-blue-500/30';
|
|
} else if (msg.event === 'ai.moderation.finished') {
|
|
var labels = { approved: '已通过', rejected: '已拒绝', spam: '垃圾评论' };
|
|
text += '审核完成:' + (labels[d.verdict] || d.verdict);
|
|
cls = d.verdict === 'approved' ? 'border-green-300 bg-green-50 dark:border-green-500/30'
|
|
: (d.verdict === 'spam' ? 'border-red-300 bg-red-50 dark:border-red-500/30'
|
|
: 'border-amber-300 bg-amber-50 dark:border-amber-500/30');
|
|
} else if (msg.event === 'ai.moderation.failed') {
|
|
text += '审核失败:' + (d.error || '未知错误');
|
|
cls = 'border-red-300 bg-red-50 dark:border-red-500/30';
|
|
}
|
|
|
|
item.className = 'rounded-md border px-2 py-1 ' + cls;
|
|
item.textContent = text;
|
|
feed.prepend(item);
|
|
};
|
|
});
|
|
</script>
|