In the admin publish modal, fetch the article's publish records, disable
accounts that already succeeded for the same article/platform, label
queued/publishing accounts inline, and route the success notification by
created vs existing task counts (with a dedicated warning for the
`desktop_publish_duplicate` server error).
Replace the "再次发送" mutation/popconfirm on both admin and desktop publish
management views with a "重新创建发布任务" action that calls the same Create
endpoint, only enables on failed/aborted/unknown rows, and reports created
vs existing task counts (with a tailored warning for `unknown` results
that may have already succeeded).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build a per-target dedup_key (tenant/workspace/article/platform/account) on
publish job creation, take an xact advisory lock on the keys, look up
already-active publish_records via the payload index, and skip creating
duplicate desktop_tasks. Surface created vs existing task ids on
CreatePublishJobResponse (and shared-types) so callers can tell the
difference, return `desktop_publish_duplicate` on unique-constraint races,
and expose desktop_account_id on PublishRecordResponse so the modal can
disable already-published accounts. Make the compliance ack consumer
accept a nullable job id since the dedup short-circuit no longer creates
one.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a unique index on desktop_tasks.dedup_key for active publish kinds
(queued/in_progress/succeeded/unknown) so duplicate publish submissions can
be rejected at the DB layer, plus supporting indexes on publish_records and
the desktop_tasks publish_record_id payload expression used by the new
dedup lookup query.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add SMZDM to the cover-required platform list on both client and server,
register a 800x800 native cover spec in the admin cover catalog, and
normalize the desktop-client `smzdm_image_fetch_failed` adapter error to a
user-friendly Chinese prompt across the publisher error pipeline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pull the recovered-task SELECT into a dedicated helper so the column list and
lease-expiry filter are testable, add a status column to the scan so logs
report the real task state, and log scan/iterate failures with the client and
recovery mode for observability.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rework the Doubao monitor adapter so success answers come exclusively from
CHUNK_DELTA SSE text and require an SSE_REPLY_END finish event — DOM fallbacks
and FULL_MSG_NOTIFY user echoes can no longer pose as the model reply. Also
auto-switch to the 思考 answer mode, repair mixed-mojibake fragments per run,
unwrap Doubao redirect URLs, scrub "unknown" source metadata, recover from
mid-query navigation by returning unknown for retry, and surface stream/finish
counters in diagnostics.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the standard search-results panel never opens, click the reasoning-chain
"搜索到 N 个网页" affordance to reveal the citation source list, and prefer the
revealed panel links over stale snapshot links so citations point at the right
URLs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Repair detached headings, missing list/heading spaces, and pipe-only tables in
answer text before rendering, and style the markdown preview so headings,
lists, tables, and emphasis match the rest of the detail view.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reject AI seed topics shorter than 2 characters on both client and server with
a dedicated invalid_seed_topic error code, and render an inline hint listing
the offending terms so users know exactly what to fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ship the Ops backstage 对象存储 page (browse / upload / move / delete /
folder ops) backed by a new object-storage service + handler, with the
shared storage client extended with List/Stat/Copy/Usage/DownloadURL so
both MinIO and Aliyun providers cover the new surface. Add
ForcePathStyle to the object-storage config and treat r2/s3/custom_s3
as external providers so Cloudflare R2 and other S3-compatibles can
share the MinIO client path without spinning up the bundled MinIO.
Also add CSV import/export + template download to the site-domain
mappings page, raise gin MaxMultipartMemory to 8 MiB for the new
multipart endpoints, register Ops swagger routes, and extend the
descriptions-parity test to cover the ops router.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Search terms like "全屋定制" or "GEO排名优化" were rejected as invalid because
both the Vue candidate validator and the Go question_metadata classifier
required interrogative cues (什么/如何/为什么/...). Lexicon entries are search
terms, not questions, so require only letters or digits plus the existing
4-char minimum. Rename helpers to searchTermLooksValid/questionLooksValid
to reflect the looser semantics, and relabel the navigation entry from
"公司和词库" to "品牌和词库" to match the actual domain.
Forbid URLs/domains/markdown links across prompt rules (runtime, platform
templates, outline/imitation/title flows) and rename "官网" placeholder to
"官网信息" with explicit instructions that links are background only.
Add sanitizeGeneratedArticleMarkdown to strip URLs, HTML images, and
"网站列表/官网列表" labels from LLM output, wired into article, prompt-rule,
template, and outline generation. Drop the site_list outline section seed
and filter blocked outline keys in the wizard so stored drafts cannot
resurrect it.
Route content/cover images through the desktop media-image asset
pipeline and abort before submitting when any body image was not
replaced with a Toutiao-hosted URL, so we don't publish articles
that reference our own private asset endpoints.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add cache-aside + singleflight to KOL marketplace list/detail, my-subscription
prompts, and generation schema lookups. Invalidate the shared public-facing
caches when subscriptions are added/revoked (ops) or KOL profile is updated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Turn the KOL prompt platform hint into a multi-select backed by the
media platforms API, persist hints as a 、-joined string, and add a
reusable KolPlatformTags component so manage/generate/package/template/
workspace views and the generate-task drawer all render the hints
consistently with truncation and overflow handling.
Also auto-select the first available subscription prompt in
GenerateTaskDrawer when switching to the subscription-prompt target so
users land on a usable option without an extra click.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add prompt_content columns to kol_prompts and kol_prompt_revisions so
prompt bodies live alongside their metadata, eliminating an extra round
trip to object storage for the common read path while keeping the old
asset key as a fallback for legacy rows.
Reads go through a singleflight-deduped, cache-backed loader that
prefers the database column, falls back to the asset key, and tolerates
missing objects via a new objectstorage.ErrObjectNotFound returned by
both the Aliyun OSS and MinIO clients on 404/NoSuchKey responses.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move question_hash and question_text out of the URL query into router
history state to keep tracking question links clean, and make the
tracking dashboard's business date follow the URL/today instead of the
previously persisted localStorage value.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Render markdown to sanitized HTML and write both text/html and text/plain
to the clipboard so pasted articles preserve formatting in rich editors,
falling back to selection-based copy and then plain text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Embed SourceHanSansSC and use native jsPDF text rendering for crisp,
selectable PDF output, replacing the canvas rasterization approach.
Also fix download menu item alignment.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extend schedule tasks to dispatch KOL subscription prompts in addition
to prompt rules, add daily/weekly scheduling with fixed or random time
windows, and track dispatch outcomes (last run, status, consecutive
failures).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add self-service password change that re-hashes the credential and bumps a
per-user session version so all existing access/refresh tokens are rejected.
Session version is tracked in Redis with an in-memory fallback and enforced in
middleware and refresh.
Scope prompt rules and rule groups to a brand: new brand_id column (migrated to
a "未归属" fallback brand for existing rows), brand-filtered queries/indexes, and
brand-aware admin-web tabs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the scheduler-polled KnowledgeDeletedCleanupWorker with an
event-driven KnowledgeDeletedCleanupEventWorker in tenant-api; cleanup
events are now enqueued transactionally at delete time instead of swept
periodically. Also fixes statement_timeout parameterization in
MonitoringRetentionWorker and propagates errors from cleanupDeletedKnowledgeItem.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Introduces Aliyun OSS as an alternative to MinIO; deploy scripts and CI
workflows now read object_storage.provider from config and conditionally
include or skip MinIO resources in both Docker Compose and k3s paths.
Also adds ops scheduler domain and its migration tables.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Remove industry, target_audience, content_goal, tone, and length_goal from the imitation form; brand_name is now mandatory with autocomplete from brand library, keywords are pre-populated from the selected brand's search keywords, and output length is hardcoded to ~2000 characters in the prompt.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
When a desktop monitoring task fails with a non-retryable authorization failure
(login expired, challenge required, risk control), all pending same-client/same-platform
tasks for the same business day are immediately bulk-failed as non-retryable, avoiding
wasted execution attempts. Adds preflight authorization checks before task execution,
enriches failure payloads with disposition metadata (code, category, retryable flags),
and triggers account health reports on auth state degradation.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>