- Add GET /api/tenant/publish-records: brand-scoped paginated list that merges
in-flight (queued/publishing) records ahead of history (success/failed/cancelled),
enriched with article title and latest desktop task id; wire up
service/handler/router/swagger/router_test
- Rework PublishManagementView to consume publish-records instead of desktop
publish tasks; add publishRecordsApi + shared-types
(ListPublishRecordsParams, PublishRecordListResponse)
- Enterprise site Update: distinguish explicit null from missing PATCH fields via
EnterpriseSitePatchString, dedup site_url before write, verify RowsAffected,
and map unique-constraint violations to a clear conflict
- MediaView: enterprise site edit/delete flows with favicon fallback handling
- Localize enterprise site / PBootCMS error messages to Chinese across the
backend and the admin-web error map, plus legacy raw-message translation
- deploy: gate migration-coupled service rollouts behind RUN_MIGRATIONS unless
ALLOW_SKIP_MIGRATIONS_FOR_APP_ROLLOUT=true; handle empty SERVICES safely
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Added new fields to DesktopAccountInfo for tracking account session and task consumer online status, along with queued publish task count and oldest queued publish task timestamp.
- Updated DesktopDispatchEvent to include server time for better synchronization.
- Implemented separate presence management for task consumers, including marking presence and loading presence state.
- Enhanced DesktopAccountService to apply publish queue summaries and manage task consumer presence.
- Introduced new methods for replaying queued publish tasks in DesktopDispatchHandler.
- Updated tests to cover new presence management logic and ensure correct behavior of account session and task consumer online status.
Add a resource cache sync entrypoint (RunMediaResourceSyncOnce) driven
by the new scheduler job, and rework the backlink/sync worker around it.
Tune the meijiequan client and slow the default backlink sync interval
from 10m to 30m to reduce upstream pressure.
Trim unused public/transport surface (handler + router + swagger) and
simplify admin-web order management and ops-web media-supply views,
dropping stale shared-types fields.
- Extend DesktopClientReleaseCheckResponse with platform, arch, and channel fields.
- Introduce DesktopClientDownloadableRelease and DesktopClientDownloadableReleaseListResponse interfaces for managing downloadable releases.
- Implement ListDownloadable method in DesktopClientReleaseService to retrieve downloadable releases based on the specified channel.
- Update DesktopClientReleaseCheckResult to include platform, arch, and channel information.
- Add resolveInstallerDownloadURL method to handle download URL resolution for releases.
- Create new endpoint in DesktopReleaseHandler for listing downloadable releases.
- Update router to include the new downloadable releases route.
- Enhance tests to cover the new functionality for resolving download URLs and listing downloadable releases.
Introduce an end-to-end media-supply feature: tenant-side resource sync
service/worker backed by a Meijiequan supplier client, ops-side management
APIs, and admin/ops web views for resources, orders, favorites and
submission. Adds a shared digitocr helper, MediaSupply config blocks for
tenant and ops, shared types, and migrations for supplier media resources,
price overrides, customer visibility and order refunds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Consolidate per-platform cover/image fetch failure prompts into a single
PUBLISH_IMAGE_FETCH_FAILED_MESSAGE constant shared by baijiahao, bilibili,
dongchedi, qiehao, smzdm, and zol adapters, and generalize the normalizer
to match any *_image_fetch_failed / *_cover_fetch_failed code. Also split
desktop publish list loading into initial vs manual-refresh states so the
refresh button no longer blanks the table, and fix admin-web active-task
detection to cover all pending statuses.
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>
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>
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.
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>
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>
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>
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>
Unknown URLs previously rendered a blank page. Add a catch-all route
under AppShell that shows the requested path plus back / workspace
actions, with zh-CN and en-US copy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Shrink desktop presence TTL to 30s and heartbeat to 15s so unexpected
client exits surface within one TTL even if the explicit offline call
is missed; keep migration default aligned.
- Trust a known presence miss in resolveDesktopClientOnline instead of
falling back to the recent-heartbeat heuristic, so a still-cached
LastSeenAt cannot mask an offline client.
- Release the runtime session before clearing renderer desktop sessions
on shutdown to avoid leaving stale leases behind.
- Auto-refresh the MediaView account list every 5s and revamp card
layout (inline platform badge, status tags, UID row, meta box).
- Let the brand-question AI wizard accept multiple seed topics via a
tag-style input; candidates from each topic are merged and deduped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- add /questions/combination-fill endpoint with AI-driven, IP-region-aware matrix fill
- extract ip2region resolver from ops/app into shared/ipregion for cross-service use
- thread question_id filter through dashboard composite, citation summary, and collect-now
- switch template wizard from keyword inputs to brand-question selection (primary + supplemental)
- pass brand_question and supplemental_questions through assist/title/outline prompts
- add AiWaitingModal + 45s client timeout and request_timeout error mapping for long AI flows
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Implemented QuestionExpansionService for generating and materializing questions based on combinations and AI distillation.
- Added question metadata classification logic to infer intent and layer of questions.
- Created API handlers for question expansion operations including combination preview, AI distillation, metadata classification, and materialization.
- Introduced database migrations to support new question-related fields and constraints in brand_questions and brand_keywords tables.
- Added caching mechanism for AI distillation results to improve performance.
- Defined JSON schemas for question distillation responses to ensure data integrity.
Polling analyze/title/outline task results every 1.2s was over-eager
for AI tasks that typically run 15-30s. Bump interval to 3s and lower
max attempts to 40, extending total timeout from 72s to 120s while
roughly halving request volume.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Top-nav quota chip (会员到期 / 生成文章 / AI 点数) only refreshes when its
query is invalidated. Several mutations and stream-based flows that debit
points or article quota on the server were not invalidating it, so users
had to refresh the page to see updated balances.
- TemplateWizardView: invalidate `['workspace']` inside `endAssistTask()`,
covering analyze / title / outline assist tasks in one place.
- FreeCreateView: invalidate after `articlesApi.create` success.
- GenerateTaskDrawer: add `['workspace']` to the instant-generate
`Promise.all` alongside the existing `articles` / `instantTasks` keys.
- KolPromptEditor: invalidate in the `finally` of both `runAiAssistStream`
(generate) and `generateSelectionAiPreview` (optimize) so abort / error
paths also refresh.
- ArticleEditorCanvas: bring in `useQueryClient` and invalidate in the
selection-optimize stream `finally`.
Together with the existing invalidations in TemplateWizard.generate,
ImitationGenerate, KolGenerate, and article-regenerate, every quota-
debiting call site now feeds the cache refresh.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Carry `from=workspace` query from the workbench into the wizard so cancel
and post-generate redirects return to the workbench instead of always
landing on /articles/templates.
- AppShell now resolves an effective navKey from `route.query.from`, so the
sidebar highlight and breadcrumb track the entry source while inside the
wizard.
- Redesign the brand-info AI analyze CTA into a standalone banner with
template-aware copy (default / product_review / research_report) and add
the matching i18n keys.
- Drop the unused 批量生成文章 button and its styling from the templates
page.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Map dongchedi adapter failures (raw "server exception" or
dongchedi_platform_exception) to a Chinese prompt steering the user to
the dongchedi backend, so renderer/admin views and desktop-task error
payloads no longer surface the opaque platform message.
Hoists hasActiveGenerationStatus into the shared display helper so all
views agree on what counts as in-flight generation, shows the backend
generation_error_message inline on the imitation list and detail drawer
when status is failed, and drops the workspace/templates/imitation poll
interval from 10s to 3s (with a 5s fallback when streaming is enabled)
plus explicit query-cache invalidation so failed/completed states show
up promptly.
- findInteractiveByText now also accepts elements carrying business
attributes [dt-button-id]/[data-button-id]. The new yuanbao toolbar
renders the deep-think toggle as <div dt-button-id="deep_think"> rather
than a <button>, so the previous text-only locator missed it and probes
silently ran in the fast model — without inline citations.
- Lower <div data-idx-list="1,2,10"> placeholders to bare "[1][2][10]"
(previously "[citation:1] [citation:2] [citation:10]") and accept both
forms in YUANBAO_CITATION_MARKER_PATTERN. Final answer text runs through
normalizeCitationMarkers so any residual SSE [citation:N] is rewritten
to [N], matching DeepSeek's chip rendering on the SaaS side.
- searchGuid frames in the new protocol carry every reference inside
docs[] (each with a 1-based index that mirrors the answer markers) and
citations is now always null. Promote docs into the citations bucket so
citations[index-1] aligns with the inline marker, eliminating the
spurious yuanbao_incomplete_citations failures.
- Update the SaaS detail view: createYuanbaoCitationPattern accepts both
"[N]" and "[citation:N]" so yuanbao answers render the same clickable
superscript chips as DeepSeek.
Tests cover the new searchGuid.docs alignment, fast-cache answers (no
markers, no sources -> still succeed), legacy [citation:N] -> [N]
rewriting, and mixed-format marker extraction.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refactor readDeepseekAccountIdentity so the JSON payload parser is a pure
ts function (extractDeepseekIdentityFromPayload). The webContents IIFE now
just polls for userToken and returns the raw API JSON; payload picking lives
in ts and is unit-tested.
Tests lock the contract for both login types we observed in the wild:
- WeChat-bound account: id_profile.name = '阿白', picture present.
- Phone-only login: id_profile = null, mobile_number = '177******08',
email = '' (empty string, not null) — the case that originally regressed.
Plus regressions for the priority order (profile.name > mobile_number > email),
the all-empty fallback, and non-record payloads.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Poll the knowledge item list every 5s while any item is still pending
or processing so the UI reflects ingestion progress without manual
refresh, and exclude items that have no active chunks from group
counters so groups with no usable content do not appear populated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Intercept route changes and tab close when the template wizard has
unsubmitted content so users can save a draft, discard, or stay instead
of silently losing their progress.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Expose tenant-authenticated publish task list and retry endpoints so the
admin web can show desktop publish state without an Electron bridge, and
register a `shengxintui://` deep-link so the page can hand off to the
desktop client workbench.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move publish-state pill next to account name, surface device name in the
client-status tag with truncation + tooltip, and switch the card click
target from disabled to aria-disabled so tooltips remain interactive.
Also disable the keyword selector in the brand question form to prevent
mid-edit reassignment.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prettier with semi:false stripped the semicolons that previously separated
the two inline statements, leaving the Vue compiler unable to parse the
expression. Extract closeItemModal() and bind the handler by reference.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>