从0开发一套geo优化软件:数据模型与API设计

从0开发一套geo优化软件:数据模型与API设计

这类 GEO 软件的核心流程并不复杂:先批量拓词,建立选题池;再把企业介绍、服务说明、FAQ、案例等资料放进知识库;然后基于关键词和知识库生成文章;最后发布和分发。听起来像是 AI 写作,但真正落到系统开发时,重点反而是数据怎么沉淀。

所以这一篇专门讲数据模型和 API。GEO 系统的数据模型要围绕「内容资产」设计,而不是围绕「AI 调用」设计。真正需要长期保存和复用的是关键词、知识库、文章、发布记录、账号、投放资源、配额和任务。模块拆分按这个方向做,系统会更像内容运营平台,而不是单点的写作工具。

1. 应用作用域

frontend/src/api/backend.js 中有一个关键设计:GEO 相关接口会自动挂到 /apps/geo_app 下。被纳入应用作用域的资源包括:

  • /keyword-groups
  • /keywords
  • /articles
  • /knowledge-bases
  • /conversion-targets
  • /article-styles
  • /platforms
  • /distribute
  • /social-accounts
  • /site-publish
  • /auto-operations
  • /soft-articles
  • /ai-quotas
  • /ai-generation-tasks

这意味着后端可以用同一套路由和权限框架服务多个产品,只要通过 X-App-Code 或 URL 前缀区分业务域。这里可以使用 geo_app 作为应用标识,后续如果扩展其他垂直产品,也不用重写整套后端。

前端请求拦截器可以这样做,所有业务页面都不用重复处理 token、客户端模式和应用标识:

js 复制代码
backendApi.interceptors.request.use(config => {
  const token = storage.get('token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }

  config.headers['X-App-Code'] = APP_CODE
  config.headers['X-Client-Mode'] =
    (window.require || window.electron) ? 'electron' : 'browser_web'
  config.url = withAppPrefix(config.url)

  return config
})

2. 关键词模型

有了应用作用域,接下来就要看第一类核心资产:关键词。关键词是 GEO 内容生产的入口。建议至少设计两个表:

keyword_groups

  • id
  • user_id
  • app_code
  • name
  • description
  • enabled
  • sort
  • created_at
  • updated_at

keywords

  • id
  • group_id
  • user_id
  • app_code
  • word
  • source
  • created_at
  • updated_at

关键词页面已经把这些动作做成了日常运营入口:分组创建、关键词列表、AI 拓词、批量添加、批量删除、移动分组。API 层对应 getKeywordGroupscreateKeywordGroupgetKeywordsgenerateKeywordsbatchCreateKeywordsmoveKeywords 等方法。

后端表结构可以先用这种粒度,不要一开始就把关键词扩展成很复杂的 SEO 表:

sql 复制代码
CREATE TABLE keyword_groups (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id BIGINT NOT NULL,
  app_code VARCHAR(64) NOT NULL,
  name VARCHAR(120) NOT NULL,
  description TEXT NULL,
  enabled TINYINT DEFAULT 1,
  sort INT DEFAULT 0,
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL
);

CREATE TABLE keywords (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id BIGINT NOT NULL,
  group_id BIGINT NOT NULL,
  app_code VARCHAR(64) NOT NULL,
  word VARCHAR(180) NOT NULL,
  source VARCHAR(40) DEFAULT 'manual',
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,
  INDEX idx_group_id (group_id),
  INDEX idx_user_app (user_id, app_code)
);

3. 知识库模型

只有关键词还不够。关键词告诉系统写什么方向,知识库则告诉系统哪些内容是真实可信的。知识库用于保证文章不是空泛生成,而是能围绕真实产品、服务和案例展开。建议设计:

knowledge_bases

  • id
  • user_id
  • app_code
  • name
  • description
  • enabled
  • created_at
  • updated_at

knowledge_files

  • id
  • knowledge_base_id
  • name
  • file_type
  • file_path
  • file_size
  • content
  • metadata
  • created_at
  • updated_at

项目 API 已经包含上传文件、添加文本、获取文件内容、更新内容、移动文件、删除文件等能力。开发时要注意图片和文本的处理方式不同:文本可以直接进入提示词,图片更适合作为素材引用,让模型在文章中输出占位符。

4. 转化目标和文章风格

转化目标解决「文章最终要为谁服务」的问题。文章风格解决「内容怎么写」的问题。不要把这两个概念混在一个 prompt 字段里。

conversion_targets

  • name
  • company_name
  • official_website
  • industry
  • description

article_styles

  • name
  • type
  • prompt
  • enabled

article-creation.vue 里把正文风格和标题风格拆开使用,并支持标题改写。这个设计很有必要:标题要负责点击和搜索匹配,正文要负责可信度和信息密度。

5. 文章模型

关键词、知识库、风格、转化目标最后都会汇聚到文章上。文章是系统的中心资产。建议字段:

  • id
  • user_id
  • app_code
  • title
  • content
  • status
  • source
  • word_count
  • keyword_ids
  • knowledge_base_ids
  • style_id
  • title_style_id
  • conversion_target_id
  • error_message
  • created_at
  • updated_at

状态建议至少包括:

  • created
  • manual
  • ai
  • generating
  • failed

文章列表已经支持状态筛选、失败重试、发布、投放、导出 Word、发布记录展示。后端要把文章生成过程设计成任务化,避免页面等待大模型响应。

6. AI 配额和任务

数据资产之外,还要单独考虑 AI 任务和额度。项目里 keywords.vuearticle-creation.vue 都会展示 AI 额度。关键词拓词调用后会轮询 getAiGenerationTask,这说明生成任务应该是异步的。

建议设计:

ai_generation_tasks

  • id
  • user_id
  • app_code
  • task_type
  • usage_type
  • status
  • request_payload
  • result_payload
  • error_message
  • started_at
  • finished_at

ai_quota_usages

  • id
  • user_id
  • task_id
  • usage_type
  • amount
  • period_key
  • status

状态要区分 reservedusedreleased。任务失败或取消时释放额度,任务完成时确认扣减。这个设计能避免高并发下额度被重复消耗。

任务和额度建议用"预占 -> 确认 -> 释放"的状态机,伪代码如下:

php 复制代码
DB::transaction(function () use ($user, $payload) {
    $quota = AiQuota::lockForUpdate()
        ->where('user_id', $user->id)
        ->where('usage_type', 'article_generation')
        ->firstOrFail();

    if ($quota->remaining <= 0) {
        throw new RuntimeException('AI额度不足');
    }

    $task = AiGenerationTask::create([
        'user_id' => $user->id,
        'app_code' => 'geo_app',
        'task_type' => 'article',
        'usage_type' => 'article_generation',
        'status' => 'pending',
        'request_payload' => $payload,
    ]);

    AiQuotaUsage::create([
        'user_id' => $user->id,
        'task_id' => $task->id,
        'usage_type' => 'article_generation',
        'amount' => 1,
        'status' => 'reserved',
    ]);
});

7. 发布和投放模型

发布分两类:账号发布和站点发布。

账号发布依赖 /social-accounts/distribute/records。站点发布依赖 /site-publish/site-publish/publish。自动运营服务会在生成文章后创建分发记录,再调用 RPA 或站点发布接口。

8. API 响应规范

项目里 axios 拦截器假设 Laravel 统一响应格式是:

json 复制代码
{
  "code": 0,
  "message": "success",
  "data": {}
}

非 0 会被前端当成错误。401 会清理 token 并跳转登录页。后端实现时必须保持这个格式稳定,否则前端页面会出现难排查的状态异常。数据和接口打好基础后,下一步才适合深入 AI 生成链路,否则生成出来的内容很难被系统承接。

相关推荐
AlienZHOU42 分钟前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
狗头大军之江苏分军3 小时前
《潮水漫过十七岁》开学了
后端
苏三说技术4 小时前
如何看待GPT-6在UP主众测中碾压夺冠?它是现在最强大模型吗?
后端
mldong4 小时前
一份 JSON,一条能跑的审批流:把报销流程送上工作流引擎
后端·架构
wno7044 小时前
Spring Boot WebFlux增删改查
java·spring boot·后端
Captaincc4 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
aramae5 小时前
模拟实现strlen()函数 (C语言)
c语言·开发语言·后端
计算机魔术师5 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen6 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒6 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端