## GitHub 同步说明 | 项 | 内容 | |:---|:---| | 仓库 | https://github.com/fnvtk/cunkebao_v3 | | 分支 | develop | | 方向 | 本地 → GitHub(单向 push) | | 是否拉取远程 | 否(未 fetch / pull / merge) | | 是否改本地源文件 | 否(仅 git add/commit,未编辑业务/文档正文) | | 远程基准 | origin/develop @b5b40e8b| | 本批工作区变更 | 999 files, +47622 / -4007 lines | ## 一并推送的本地已有 commit(此前未 push) 1.3aa7ecf8deploy: 官网 ckb.quwanzhi.com 宝塔上线 DNS+SSL+验收文档 2.16a70045fix(官网): 桌面导航仅保留一个留资 CTA 并重新上线 3.dbe7b7aafix(官网): Hero 改为主 CTA 按钮并去掉重复 1200+ 数据条 4.ac82726echore(部署): 五端上线验收46项并统一留资与缓存版本 ## 本 commit 目录统计 | 目录 | 文件数 | 说明 | |:---|---:|:---| | 开发文档/ | 639 | 10、项目管理、未完成项跨仓汇总、五端需求/部署/接口 | | Server/ | 129 | 后端 API、迁移、部署相关 | | Touchkebao/ | 80 | PC 工作台、获客、算力 | | 官网/ | 61 | ckb.quwanzhi.com 静态站与 CTA 迭代 | | Cunkebao/ | 49 | 移动端场景/设置/流量池 | | SuperAdmin/ | 26 | 超管后台 | | .cursor/ | 11 | 规则与 Skill | | 其他 | 14 | docker-compose、.obsidian、.codegraph 等 | ## 重点文件(本地为准) - 开发文档/1、需求/修改/未完成项与跨仓验收汇总.md - 开发文档/10、项目管理/账号密码与登录环境一览.md - 开发文档/10、项目管理/(新建项目管理目录与截图) - 官网/ 全站 + 宝塔上线文档 - 五端上线验收 46 项(见 ac82726e) ## 刻意未上传 - node_modules/、.DS_Store、.smart-env/ - .开发文档_nested_git_backup/ - **/__pycache__/ ## 验收 - 提交页(含本说明): https://github.com/fnvtk/cunkebao_v3/commit/HEAD - 分支树: https://github.com/fnvtk/cunkebao_v3/tree/develop Co-authored-by: Cursor <cursoragent@cursor.com>
133 lines
4.0 KiB
TypeScript
133 lines
4.0 KiB
TypeScript
import { apiRequest } from "./api-utils"
|
||
|
||
/** SEO 运营中心 API 客户端(REQ-WEB-41/42 · /v1/admin/seo/*) */
|
||
|
||
export interface SeoOverview {
|
||
pages: number
|
||
keywords: number
|
||
keywordsLayouted: number
|
||
internalLinks: number
|
||
internalLinksPending: number
|
||
externalSites: number
|
||
indexLogs: number
|
||
thresholds: { pages: number; keywords: number; links: number; sites: number }
|
||
}
|
||
|
||
export interface SeoPage {
|
||
id: number
|
||
path: string
|
||
title: string
|
||
description: string
|
||
keywords: string
|
||
canonical: string
|
||
ogImage: string
|
||
schemaJson: string
|
||
status: number
|
||
updatedBy: string
|
||
updateTime: string
|
||
}
|
||
|
||
export interface SeoKeyword {
|
||
id: number
|
||
word: string
|
||
type: "core" | "scene" | "longtail" | "brand"
|
||
targetPath: string
|
||
priority: number
|
||
layouted: number
|
||
}
|
||
|
||
export interface SeoInternalLink {
|
||
id: number
|
||
fromPath: string
|
||
toPath: string
|
||
anchor: string
|
||
status: "pending" | "applied" | "ignored"
|
||
source: "manual" | "crawl"
|
||
}
|
||
|
||
export interface SeoExternalSite {
|
||
id: number
|
||
name: string
|
||
baseUrl: string
|
||
relType: "dofollow" | "nofollow"
|
||
note: string
|
||
enabled: number
|
||
}
|
||
|
||
export interface SeoIndexLog {
|
||
id: number
|
||
engine: "baidu" | "google" | "bing"
|
||
action: string
|
||
target: string
|
||
result: string
|
||
status: "pending" | "ok" | "fail"
|
||
createTime: string
|
||
}
|
||
|
||
export const KEYWORD_TYPE_LABEL: Record<string, string> = {
|
||
core: "核心词",
|
||
scene: "场景词",
|
||
longtail: "长尾词",
|
||
brand: "品牌词",
|
||
}
|
||
|
||
const BASE = "/v1/admin/seo"
|
||
|
||
export const getSeoOverview = () => apiRequest(`${BASE}/overview`)
|
||
|
||
export const getSeoPages = () => apiRequest(`${BASE}/pages`)
|
||
export const saveSeoPage = (p: Partial<SeoPage>) => apiRequest(`${BASE}/pages`, "POST", p)
|
||
export const deleteSeoPage = (id: number) => apiRequest(`${BASE}/pages/${id}`, "DELETE")
|
||
export const exportSeoPages = () => apiRequest(`${BASE}/pages/export`)
|
||
|
||
export const getSeoKeywords = (type = "") =>
|
||
apiRequest(`${BASE}/keywords${type ? `?type=${encodeURIComponent(type)}` : ""}`)
|
||
export const saveSeoKeyword = (p: Partial<SeoKeyword>) => apiRequest(`${BASE}/keywords`, "POST", p)
|
||
export const deleteSeoKeyword = (id: number) => apiRequest(`${BASE}/keywords/${id}`, "DELETE")
|
||
export const importSeoKeywords = () => apiRequest(`${BASE}/keywords/import`, "POST", {})
|
||
|
||
export const getSeoInternalLinks = (status = "") =>
|
||
apiRequest(`${BASE}/internal-links${status ? `?status=${encodeURIComponent(status)}` : ""}`)
|
||
export const saveSeoInternalLink = (p: Partial<SeoInternalLink>) =>
|
||
apiRequest(`${BASE}/internal-links`, "POST", p)
|
||
export const deleteSeoInternalLink = (id: number) =>
|
||
apiRequest(`${BASE}/internal-links/${id}`, "DELETE")
|
||
|
||
export const getSeoExternalSites = () => apiRequest(`${BASE}/external-sites`)
|
||
export const getSeoMatrixSnippet = () => apiRequest(`${BASE}/external-sites/snippet`)
|
||
export const saveSeoExternalSite = (p: Partial<SeoExternalSite>) =>
|
||
apiRequest(`${BASE}/external-sites`, "POST", p)
|
||
export const deleteSeoExternalSite = (id: number) =>
|
||
apiRequest(`${BASE}/external-sites/${id}`, "DELETE")
|
||
|
||
export const getSeoIndexLogs = () => apiRequest(`${BASE}/index-logs`)
|
||
export const saveSeoIndexLog = (p: Partial<SeoIndexLog>) => apiRequest(`${BASE}/index-logs`, "POST", p)
|
||
|
||
export interface SeoPublishTask {
|
||
id: number
|
||
channel: "zhihu" | "baijiahao" | "mp" | "video" | "other"
|
||
title: string
|
||
bodyMd: string
|
||
targetUrl: string
|
||
utmCampaign: string
|
||
utmUrl: string
|
||
publishStatus: "draft" | "published"
|
||
publishedAt: number
|
||
updateTime: number
|
||
}
|
||
|
||
export const PUBLISH_CHANNEL_LABEL: Record<string, string> = {
|
||
zhihu: "知乎",
|
||
baijiahao: "百家号",
|
||
mp: "公众号",
|
||
video: "视频号",
|
||
other: "其它",
|
||
}
|
||
|
||
export const getSeoPublishTasks = (channel = "") =>
|
||
apiRequest(`${BASE}/publish-tasks${channel ? `?channel=${encodeURIComponent(channel)}` : ""}`)
|
||
export const saveSeoPublishTask = (p: Partial<SeoPublishTask>) =>
|
||
apiRequest(`${BASE}/publish-tasks`, "POST", p)
|
||
export const deleteSeoPublishTask = (id: number) =>
|
||
apiRequest(`${BASE}/publish-tasks/${id}`, "DELETE")
|