游乐游手机版
首页/前端开发/文章详情

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

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

GEO优化软件从零开发指南:数据模型与API设计详解

这套GEO软件的核心流程实际上并不复杂,完全可以拆解为四个关键步骤:首先是批量拓词,建立选题池;然后将企业介绍、服务说明、FAQ、案例等资料存入知识库;接着基于关键词和知识库生成文章;最后就是发布与分发。听起来像是一个AI写作工具,但真正落到系统开发时你会发现,重点反而不是AI本身,而是数据如何沉淀、如何复用。

从0开发一套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层对应的就是 getKeywordGroupscreateKeywordGroupgetKeywordsgenerateKeywordsbatchCreateKeywordsmoveKeywords 这些方法。

后端建表时,先保持这种粒度即可,不需要一开始就把关键词表设计成复杂的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.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。当任务失败或取消时,要释放额度;任务完成时,再确认扣减。这个设计能有效避免高并发下额度被重复消耗的问题。

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

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拦截器,默认假设Laravel的统一响应格式是这样的:

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

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

来源:https://juejin.cn/post/7646739818932125742
上一篇从零搭建Vue2项目完整教程 下一篇后端告别手绘!TinyVue流程图组件跨端定制指南
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Vue应用中异步更新性能问题的优化策略详解
前端开发 · 2026-07-03

Vue应用中异步更新性能问题的优化策略详解

先来看一个令许多开发者感到困惑的场景:明明修改了数据,DOM 却“毫无反应”,无法获取最新的高度,也无法计算正确的坐标。这并非 Vue 的缺陷,反而是它精心设计的性能优化策略。核心在于——你需要学会与它“异步更新”的特性协作,而非硬碰硬。 所谓的“异步更新性能问题”,本质上是一种认知偏差。Vue 的

如何避免原型对象挂载大体积动态数组内存污染
前端开发 · 2026-07-03

如何避免原型对象挂载大体积动态数组内存污染

原型链上的大数组:一个隐蔽的内存冲击波 先给个核心判断:直接在原型对象上挂载一个大体积动态数组,这既不是传统意义上的内存“污染”,也不是安全漏洞那种“污染”,而是一种相当隐蔽但后果严重的内存管理失当。它会导致所有实例共享同一份数据,而且正因为生命周期跟整个原型链绑定得太紧,垃圾回收器(GC)根本看不

利用堆栈信息精准定位显式绑定错误对象致未定义异常
前端开发 · 2026-07-03

利用堆栈信息精准定位显式绑定错误对象致未定义异常

深入追踪:显式绑定传错对象引发的未定义异常 说实话,这类问题在JavaScript开发中相当常见——显式绑定传错了对象,然后方法执行时静默失败、访问undefined、或者抛出TypeError。但真正的难点不在于“报了什么错”,而在于“到底是哪个对象被绑错了”。要解决它,需要跳出堆栈的表层报错信息

ES模块中默认导出和具名导出的执行上下文
前端开发 · 2026-07-03

ES模块中默认导出和具名导出的执行上下文

export default 与具名导出在 ES Module 中的行为机制截然不同,核心差异不在于“值如何传递”,而在于绑定如何建立以及导入时如何使用。先给出总结性结论,再逐一详细拆解。 export default 是一种语法糖,而非真正的变量声明 这种设计容易引起误解。实际上,export d

详解HTML中iframe标签loading=lazy属性实现嵌入内容懒加载方法
前端开发 · 2026-07-03

详解HTML中iframe标签loading=lazy属性实现嵌入内容懒加载方法

先聊聊 loading= "lazy " 这个属性——它本意是让 iframe 实现延迟加载,但实际落地时常常“失效”。这并非程序漏洞,而是浏览器内置的防御机制:只有所有条件同时触发,它才会真正推迟资源请求。比如 src 必须是跨域地址(类似 https: widget example com emb