从零开发Geo优化软件:数据模型与API设计

作者:技术拾光

时间:2026-06-05

浏览:394

GEO软件系统围绕内容资产设计数据模型,涵盖关键词、知识库、文章、发布记录等核心实体。API按应用作用域划分,支持多产品扩展。AI生成采用异步任务与配额预占机制,确保系统稳定可靠。

文章目录

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

这类GEO软件的核心流程其实并不复杂,完全可以拆成四个关键步骤:先批量拓词,建立选题池;再把企业介绍、服务说明、FAQ、案例这些资料塞进知识库;然后基于关键词和知识库生成文章;最后就是发布和分发。听起来像是个AI写作工具,但真正落到系统开发的时候,你会发现,重点反而不是AI本身,而是数据怎么沉淀下来、怎么复用。

从零开发Geo优化软件:数据模型与API设计

所以这一篇,专门聊数据模型和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、客户端模式和应用标识这些琐事了:

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层对应的就是 getKeywordGroups、createKeywordGroup、getKeywords、generateKeywords、batchCreateKeywords、moveKeywords 这些方法。

后端建表时,先保持这种粒度就足够了,不用一开始就把关键词表设计成很复杂的SEO大表:

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.vue 和 article-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

这里状态要区分 reserved、used、released。当任务失败或取消时,要释放额度;任务完成时,再确认扣减。这个设计能有效避免高并发下额度被重复消耗的问题。

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

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 响应规范

最后,是API响应格式的问题。项目里的axios拦截器,默认假设Lara vel的统一响应格式是这样的:

{
  "code": 0,
  "message": "success",
  "data": {}
}

如果code返回非0,前端会直接当成错误处理。如果返回401,前端就会清理token并跳转到登录页。后端在实现时,必须保持这个格式稳定不变,否则前端页面出问题的时候,排查起来会非常头疼。数据和接口的基础打好之后,下一步才适合深入AI生成链路——否则,生成出来的内容连系统都承接不住,那就更谈不上优化了。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章