从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
iduser_idapp_codenamedescriptionenabledsortcreated_atupdated_at
keywords
idgroup_iduser_idapp_codewordsourcecreated_atupdated_at
关键词页面已经把这些动作做成了日常运营入口:分组创建、关键词列表、AI 拓词、批量添加、批量删除、移动分组。API 层对应 getKeywordGroups、createKeywordGroup、getKeywords、generateKeywords、batchCreateKeywords、moveKeywords 等方法。
后端表结构可以先用这种粒度,不要一开始就把关键词扩展成很复杂的 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
iduser_idapp_codenamedescriptionenabledcreated_atupdated_at
knowledge_files
idknowledge_base_idnamefile_typefile_pathfile_sizecontentmetadatacreated_atupdated_at
项目 API 已经包含上传文件、添加文本、获取文件内容、更新内容、移动文件、删除文件等能力。开发时要注意图片和文本的处理方式不同:文本可以直接进入提示词,图片更适合作为素材引用,让模型在文章中输出占位符。
4. 转化目标和文章风格
转化目标解决「文章最终要为谁服务」的问题。文章风格解决「内容怎么写」的问题。不要把这两个概念混在一个 prompt 字段里。
conversion_targets
namecompany_nameofficial_websiteindustrydescription
article_styles
nametypepromptenabled
article-creation.vue 里把正文风格和标题风格拆开使用,并支持标题改写。这个设计很有必要:标题要负责点击和搜索匹配,正文要负责可信度和信息密度。
5. 文章模型
关键词、知识库、风格、转化目标最后都会汇聚到文章上。文章是系统的中心资产。建议字段:
iduser_idapp_codetitlecontentstatussourceword_countkeyword_idsknowledge_base_idsstyle_idtitle_style_idconversion_target_iderror_messagecreated_atupdated_at
状态建议至少包括:
createdmanualaigeneratingfailed
文章列表已经支持状态筛选、失败重试、发布、投放、导出 Word、发布记录展示。后端要把文章生成过程设计成任务化,避免页面等待大模型响应。
6. AI 配额和任务
数据资产之外,还要单独考虑 AI 任务和额度。项目里 keywords.vue 和 article-creation.vue 都会展示 AI 额度。关键词拓词调用后会轮询 getAiGenerationTask,这说明生成任务应该是异步的。
建议设计:
ai_generation_tasks
iduser_idapp_codetask_typeusage_typestatusrequest_payloadresult_payloaderror_messagestarted_atfinished_at
ai_quota_usages
iduser_idtask_idusage_typeamountperiod_keystatus
状态要区分 reserved、used、released。任务失败或取消时释放额度,任务完成时确认扣减。这个设计能避免高并发下额度被重复消耗。
任务和额度建议用"预占 -> 确认 -> 释放"的状态机,伪代码如下:
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 生成链路,否则生成出来的内容很难被系统承接。