从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 生成链路,否则生成出来的内容很难被系统承接。

相关推荐
李明卫杭州1 小时前
Web Components 完全指南:从 Custom Elements 到 Shadow DOM
前端
Darling噜啦啦1 小时前
BEM 命名规范 + CSS Reset 实战:从微信按钮页面看专业前端开发
前端·css·代码规范
用户34232323763171 小时前
数据模型与地址映射——为什么你读到的永远是错位的数据
后端
Dirty_Mouse1 小时前
基于langgraph + sentry的自动化前端性能监控日报 (直接上github链接)
前端
To_OC1 小时前
我调用 DeepSeek API 连踩 3 个坑,终于把 Node AIGC 开发的核心知识点捋顺了
后端·node.js·aigc
悟空瞎说1 小时前
React 项目一键部署至 GitHub Pages 实操教程
前端
在下赵铁柱1 小时前
Spring Boot 防重复提交:从按钮连点到重复下单,一个 AOP 注解真的够吗?
后端
To_OC1 小时前
写完这个微信风格按钮页面,我终于吃透了BEM命名+CSS重置
前端·css·html
万少1 小时前
如果你要自动化操作浏览器,Kimi-WebBridge可能适合你
前端·javascript·后端