EternalAI/docs/business-processes.md

51 KiB
Raw Permalink Blame History

Eternal AI 业务流程文档

本文档详细描述 Eternal AI 平台的系统架构、数据模型、API 端点清单以及全部业务流程,供新成员快速上手开发与维护。

  • 项目名称EternalAI
  • 版本1.0.0
  • 描述AI 陪伴平台 — 人设创作者设定发布,生成 Hermes agent 配置文件
  • 作者chigulong
  • 许可证MIT

目录


一、系统架构概述

1.1 技术栈

层级 技术 说明
前端 原生 HTML / CSS / JavaScript (IIFE) 单页应用SPA无框架通过 view 切换实现路由
后端 Node.js + Express 5.x RESTful API提供认证与角色 CRUD
数据库 PostgreSQL 通过 Prisma ORM 访问
ORM Prisma 5.22 Schema 位于 prisma/schema.prisma
认证 JWT (jsonwebtoken) + bcryptjs Token 有效期 7 天,密码使用 bcrypt 哈希salt rounds = 10
配置 dotenv 通过 .env 注入环境变量
跨域 cors 全局启用 CORS
测试 Jest + Playwright 1.54 npm test 运行 Jest

1.2 文件结构

EternalAI/
├── index.html              # 单页应用 HTML包含全部视图view
├── app.js                  # 前端全部逻辑状态管理、视图路由、API 调用、表单处理
├── styles.css              # 样式表
├── server.js               # Express 服务器入口:中间件、路由挂载、静态文件、启动
├── package.json            # 依赖与脚本
├── prisma/
│   └── schema.prisma       # 数据库模型定义User / Role / Order
├── src/
│   ├── lib/
│   │   ├── auth.js         # JWT 与密码工具hashPassword / verifyPassword / signToken / verifyToken / authMiddleware
│   │   └── prisma.js       # PrismaClient 单例
│   └── routes/
│       ├── auth.js         # 认证路由register / login / me / settings
│       └── roles.js        # 角色路由:列表 / 详情 / 我的角色 / 发布 / 编辑 / 完整信息
└── docs/
    └── business-processes.md  # 本文档

1.3 数据流图

┌─────────────────────────────────────────────────────────────────┐
│                         浏览器(前端)                            │
│                                                                 │
│  index.html (9 个 view)  ←─DOM─→  app.js (IIFE)                 │
│                                                                 │
│  app.js 内部模块:                                               │
│  ┌──────────────┐  ┌──────────────┐  ┌────────────────────┐    │
│  │ 状态管理      │  │ 视图路由      │  │ API 封装 (api())   │    │
│  │ (state +     │  │ (showView +  │  │ fetch + JWT header │    │
│  │  localStorage)│  │  viewHistory)│  │                    │    │
│  └──────┬───────┘  └──────┬───────┘  └─────────┬──────────┘    │
│         │                 │                     │               │
│         └─────────────────┴─────────────────────┘               │
│                           │                                     │
│                  localStorage (2 个键)                          │
│           eternal_ai_token (JWT)  +  eternal_ai_state (状态)    │
└───────────────────────────┼─────────────────────────────────────┘
                            │  HTTP (fetch, /api/*)
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Express 服务器 (server.js)                    │
│                                                                 │
│  中间件: cors → express.json → 路由 → 静态文件 → 主页            │
│                                                                 │
│  ┌─────────────────────┐       ┌──────────────────────────┐    │
│  │ /api/auth           │       │ /api/roles               │    │
│  │ (src/routes/auth.js)│       │ (src/routes/roles.js)    │    │
│  │                     │       │                          │    │
│  │ POST /register      │       │ GET    /                 │    │
│  │ POST /login         │       │ GET    /:id              │    │
│  │ GET  /me            │       │ GET    /my/roles         │    │
│  │ PUT  /settings      │       │ POST   /                 │    │
│  │                     │       │ PUT    /:id              │    │
│  │ 依赖: authMiddleware│       │ GET    /:id/full         │    │
│  └──────────┬──────────┘       └────────────┬─────────────┘    │
│             │                                │                  │
│             └────────────┬───────────────────┘                  │
│                          ▼                                      │
│              ┌────────────────────────┐                         │
│              │  PrismaClient 单例      │                         │
│              │  (src/lib/prisma.js)    │                         │
│              └────────────┬───────────┘                         │
└───────────────────────────┼─────────────────────────────────────┘
                            │  SQL
                            ▼
                   ┌──────────────────┐
                   │   PostgreSQL     │
                   │                  │
                   │  User  Role  Order│
                   └──────────────────┘

关键说明

  • 前端为单页应用9 个 view 通过 .active class 切换显示,viewHistory 数组维护返回栈。
  • 所有 API 请求经 api() 封装,自动注入 Authorization: Bearer <token> 头部。
  • JWT Token 与前端状态分别存储在 localStorage 的两个键中(见 5.2)。
  • authMiddleware 用于需要登录的端点,从 Token 解析 userId 挂载到 req.userId

二、数据模型

数据库 Schema 定义于 prisma/schema.prisma,使用 PostgreSQL包含三个模型。

2.1 User用户

字段 类型 约束 默认值 说明
id String @id uuid() 用户唯一 ID
account String @unique 登录账号(手机号或用户名)
password String bcrypt 哈希后的密码
isCreator Boolean false 是否为创作者
creatorName String? 可空 创作者笔名
libraryName String? 可空 角色库名称(首页「我的 XXX」显示文字
boundCreator String? 可空 绑定的创作者 ID普通用户通过专属链接绑定
createdAt DateTime now() 创建时间
updatedAt DateTime @updatedAt 更新时间

关联roles Role[](一对多,用户创建的角色)、orders Order[](一对多,用户的订单)。

2.2 Role角色

字段 类型 约束 默认值 说明
id String @id uuid() 角色唯一 ID
creatorId String 外键 → User.id 创建者 ID
displayName String 显示名称
gender String "unknown" 性别unknown/female/male/nonbinary
age String? 可空 年龄
relationship String? 可空 与使用者的关系
personality String 必填 性格标签(逗号分隔)
background String 必填 背景故事
speechStyle String 必填 说话风格
likes String? 可空 喜好
dislikes String? 可空 厌恶 / 底线
memories String? 可空 共同记忆 / 关键事件
secrets String? 可空 秘密或未说出口的话
greeting String 必填 开场白
systemPrompt String? 可空 系统提示词(可自动生成)
model String "gpt-4o" 模型名称
temperature Float 0.8 温度参数
maxTokens Int 2048 最大 Token 数
enableMemory Boolean true 是否启用长期记忆
enableTools Boolean false 是否启用外部工具
agentId String? 可空 角色代号(英文/数字/下划线,用于 config.yaml
soulMd String? 可空 生成的 Soul.md 内容
configYaml String? 可空 生成的 config.yaml 内容
avatar String? 可空 头像 URL
desc String? 可空 角色简介
price Float 0 订阅价格(元/月)
status String "running" 状态running/stopped
createdAt DateTime now() 创建时间
updatedAt DateTime @updatedAt 更新时间

关联creator User(多对一)、orders Order[](一对多)。

2.3 Order订单

字段 类型 约束 默认值 说明
id String @id uuid() 订单唯一 ID
userId String 外键 → User.id 下单用户 ID
roleId String 外键 → Role.id 订阅角色 ID
amount Float 订单金额
status String "paid" 订单状态
createdAt DateTime now() 创建时间

⚠️ 注意Order 模型已在 Schema 中定义,但当前后端路由(src/routes/*.js未实现任何订单相关端点。角色付款流程目前为前端 Mock4.12)。

2.4 模型关系

User (1) ──────< (N) Role
  │                   │
  │                   │
  └────< (N) Order >──┘
  • 一个 User 可创建多个 RolecreatorId)。
  • 一个 User 可有多个 Order一个 Role 可对应多个 Order。
  • Order 同时关联 User下单者与 Role被订阅角色

三、API 端点清单

所有 API 前缀为 /api。认证端点挂载于 /api/auth,角色端点挂载于 /api/roles

认证端点(/api/auth

方法 路径 鉴权 请求体 响应 说明
POST /api/auth/register { account: string, password: string } { token: string, user: { id, account, isCreator, creatorName, libraryName } } 注册新用户,返回 JWT
POST /api/auth/login { account: string, password: string } { token: string, user: { id, account, isCreator, creatorName, libraryName } } 登录,返回 JWT
GET /api/auth/me { user: { id, account, isCreator, creatorName, libraryName, boundCreator } } 获取当前登录用户信息
PUT /api/auth/settings { creatorName?, libraryName?, isCreator? } { user: { id, account, isCreator, creatorName, libraryName } } 更新用户设置(仅传入字段被更新)

角色端点(/api/roles

方法 路径 鉴权 请求体 / 参数 响应 说明
GET /api/roles { roles: [{ id, displayName, avatar, desc, price, status }] } 获取所有已上架status=running角色按创建时间倒序
GET /api/roles/:id id(路径) { role: { id, displayName, avatar, desc, price, status, gender, age, relationship, personality, background, speechStyle, greeting, creatorId } } 获取角色详情(公开字段)
GET /api/roles/my/roles { roles: [Role 完整字段] } 获取当前用户创建的全部角色
POST /api/roles 角色完整数据(见下) { role: Role } 发布新角色
PUT /api/roles/:id id(路径)+ 角色数据 { role: Role } 编辑角色(仅创建者可编辑)
GET /api/roles/:id/full id(路径) { role: Role 完整字段 } 获取角色完整信息(含 soulMd、configYaml仅创建者可访问

POST /api/roles 请求体(必填字段:displayNamegreetingpersonalitybackgroundspeechStyle

{
  "displayName": "云朵",
  "gender": "female",
  "age": "24",
  "relationship": "前任",
  "personality": "温柔, 敏感",
  "background": "你们是如何相遇的...",
  "speechStyle": "常用语气词...",
  "likes": "喜欢的事物",
  "dislikes": "讨厌什么",
  "memories": "共同记忆",
  "secrets": "秘密",
  "greeting": "ta 第一次开口会对你说什么?",
  "systemPrompt": "(留空则自动生成)",
  "model": "hermes-3-llama-3.1-70b",
  "temperature": 0.85,
  "maxTokens": 1024,
  "enableMemory": true,
  "enableTools": false,
  "agentId": "eternal_lover_001",
  "soulMd": "# Soul of ...(生成的 Markdown",
  "configYaml": "# Hermes Agent Config...(生成的 YAML",
  "avatar": "https://...",
  "desc": "角色简介",
  "price": 29.9
}

四、业务流程

4.1 用户注册流程

流程名称:用户注册流程

参与角色:未登录访客

前置条件

  • 用户处于未登录状态。
  • 数据库可正常访问。

详细步骤

  1. 用户进入首页(landing 视图),点击「我的 [XXX]」卡片(data-action="open-characters")。
  2. 前端检测 state.isLoggedIn === false,调用 switchAuthTab('login') 切换到登录 TabshowView('auth') 进入认证视图。
  3. 用户点击「注册」Tabdata-tab="register"),切换到注册表单(#register-form)。
  4. 用户填写表单:
    • account(手机号 / 用户名,必填)
    • password(密码,必填,minlength="6"
    • confirmPassword(确认密码,必填)
  5. 用户点击「注册」按钮提交表单。
  6. 前端 submit 事件触发:
    • 调用 validatePasswordMatch() 校验两次密码是否一致,不一致则 setCustomValidity('两次输入的密码不一致') 并阻止提交。
    • 通过 api('/auth/register', { method: 'POST', body: { account, password } }) 发起注册请求。
  7. 后端 POST /api/auth/register 处理:
    • 校验 accountpassword 非空,密码长度 ≥ 6。
    • 查询 prisma.user.findUnique({ where: { account } }),若已存在返回 409 { error: '该账号已注册' }
    • 使用 hashPassword(password)bcryptsalt rounds = 10哈希密码。
    • prisma.user.create() 创建用户,默认 isCreator = falselibraryName = '我的角色库'
    • signToken(user.id) 生成 JWT有效期 7 天),返回 { token, user }
  8. 前端收到响应:
    • setToken(result.token) 将 JWT 存入 localStorage。
    • applyUserData(result.user) 更新 statesaveState()
  9. 注册后自动成为创作者:前端额外发起 PUT /api/auth/settings,请求体 { isCreator: true, creatorName: data.account }
    • 后端更新用户 isCreator = truecreatorName = account,返回更新后的 user。
  10. 前端更新 state.isCreator = truestate.creatorName = data.accountsaveState() + updateLandingCard()
  11. 由于 state.isCreatortrue,前端 showView('creator-center')renderCreatorCenter(),进入创作者管理中心。

后置条件

  • 数据库新增一条 User 记录,isCreator = true
  • localStorage 存有 JWT 与用户状态。
  • 用户进入创作者管理中心。

异常处理

  • 账号或密码为空 → 后端返回 400 { error: '账号和密码不能为空' },前端 alert(err.message)
  • 密码 < 6 位 → 后端返回 400 { error: '密码至少 6 位' }
  • 账号已注册 → 后端返回 409 { error: '该账号已注册' }
  • 两次密码不一致 → 前端阻止提交并提示。
  • 网络错误 → api() 抛出 无法连接服务器,请检查网络
  • 服务器异常 → 后端返回 500 { error: '注册失败,请稍后重试' }

涉及的 API 端点

  • POST /api/auth/register
  • PUT /api/auth/settings(注册成功后自动调用以开启创作者身份)

4.2 用户登录流程

流程名称:用户登录流程

参与角色:已注册用户

前置条件

  • 用户已有账号。
  • 处于未登录状态。

详细步骤

  1. 用户进入首页,点击「我的 [XXX]」卡片(data-action="open-characters")。
  2. 前端检测未登录,switchAuthTab('login') + showView('auth')
  3. 用户在登录表单(#login-form)填写:
    • account(手机号 / 用户名,必填)
    • password(密码,必填)
  4. 用户点击「登录」提交。
  5. 前端 submit 事件触发:
    • validatePasswordMatch()(登录表单无 confirmPassword直接返回 true)。
    • api('/auth/login', { method: 'POST', body: { account, password } })
  6. 后端 POST /api/auth/login 处理:
    • 校验 accountpassword 非空。
    • prisma.user.findUnique({ where: { account } }) 查询用户。
    • 用户不存在或 verifyPassword(password, user.password) 失败 → 返回 401 { error: '账号或密码错误' }
    • 验证通过,signToken(user.id) 生成 JWT返回 { token, user }
  7. 前端收到响应:
    • setToken(result.token) 存 Token。
    • applyUserData(result.user) 更新状态。
  8. 根据 isCreator 跳转
    • state.isCreator === trueshowView('creator-center') + renderCreatorCenter()
    • 否则 → renderRoleLibrary() + showView('role-library'),进入角色库。

后置条件

  • localStorage 存有 JWT 与用户状态。
  • 用户进入创作者管理中心或角色库。

异常处理

  • 账号或密码为空 → 400 { error: '账号和密码不能为空' }
  • 账号或密码错误 → 401 { error: '账号或密码错误' }
  • 服务器异常 → 500 { error: '登录失败,请稍后重试' }
  • 所有错误均通过前端 alert(err.message) 提示。

涉及的 API 端点

  • POST /api/auth/login

4.3 登录态持久化流程

流程名称:登录态持久化流程

参与角色:已登录用户(页面刷新场景)

前置条件

  • localStorage 中存在 eternal_ai_tokenJWT

详细步骤

  1. 页面加载时,app.js 末尾的 IIFE立即执行函数自动执行
    const token = getToken();  // 读取 localStorage['eternal_ai_token']
    if (!token) return;        // 无 Token 则跳过
    
  2. 若存在 Token调用 api('/auth/me')GET 请求,自动携带 Authorization: Bearer <token>)。
  3. 后端 GET /api/auth/me 处理:
    • authMiddleware 解析 Token提取 userId,无效则返回 401
    • prisma.user.findUnique({ where: { id: req.userId } }) 查询用户。
    • 用户不存在 → 404 { error: '用户不存在' }
    • 返回 { user: { id, account, isCreator, creatorName, libraryName, boundCreator } }
  4. 前端收到响应:
    • applyUserData(user) 恢复 state.isLoggedIn = true 及各项用户字段。
    • saveState() 持久化状态。
    • updateLandingCard() 更新首页卡片显示。
  5. 同时,页面初始化阶段已执行 loadState() 从 localStorage 恢复 state(含 isLoggedIn 等),保证首屏渲染时即显示登录态。

后置条件

  • 用户登录态恢复,首页卡片显示对应内容(创作者显示「进入管理中心」,普通用户显示「进入角色库」)。

异常处理

  • Token 过期或无效 → api('/auth/me') 抛错,catch 块执行 setToken('') 清除 Token用户需重新登录
  • 网络错误 → Token 保留,下次刷新重试。

涉及的 API 端点

  • GET /api/auth/me

4.4 用户登出流程

流程名称:用户登出流程

参与角色:已登录用户(创作者)

前置条件

  • 用户已登录并处于创作者管理中心。

详细步骤

  1. 用户在创作者管理中心点击「我的」Tabdata-center-tab="settings"),切换到设置面板。
  2. 用户点击「退出登录」按钮(data-action="logout")。
  3. 前端 logout() 函数执行:
    • 重置 stateisLoggedIn = falseisCreator = falseaccount = nulluserId = nullboundCreator = nullroles = []income = { balance: 0, records: [] }
    • setToken('') 清除 localStorage 中的 JWT。
    • saveState() 持久化重置后的状态。
    • updateLandingCard() 更新首页卡片为未登录态(显示「登录 / 注册」)。
    • showView('landing') 返回首页。

后置条件

  • localStorage 中 JWT 被清除。
  • 前端状态重置为默认值。
  • 用户返回首页,卡片显示「登录 / 注册」。

异常处理

  • 此流程为纯前端操作,无 API 调用,无异常风险。

涉及的 API 端点

  • 无(纯前端状态清理)

4.5 角色库浏览流程

流程名称:角色库浏览流程

参与角色:已登录的普通用户(非创作者)

前置条件

  • 用户已登录且 isCreator === false

详细步骤

  1. 用户在首页点击「我的 [XXX]」卡片(data-action="open-characters")。
  2. 前端检测 state.isLoggedIn === truestate.isCreator === false,执行:
    • renderRoleLibrary() 渲染角色库。
    • showView('role-library') 切换视图。
  3. renderRoleLibrary() 执行:
    • 设置标题为 state.libraryName || '我的角色库'
    • 列表区域显示「加载中…」。
    • 调用 api('/roles') 获取角色列表。
  4. 后端 GET /api/roles 处理:
    • prisma.role.findMany({ where: { status: 'running' }, orderBy: { createdAt: 'desc' }, select: { id, displayName, avatar, desc, price, status } })
    • 返回 { roles: [...] }
  5. 前端渲染:
    • roles.length === 0 → 清空列表,显示空状态提示(#library-empty:「你还没有绑定专属创作者」)。
    • 否则,将每个角色渲染为 .role-card,包含头像、名称、简介、价格,卡片带 data-role-id 属性。
  6. 用户点击任意角色卡片 → 进入 角色详情查看流程

后置条件

  • 角色库视图展示所有上架角色卡片。

异常处理

  • API 失败 → 列表区域显示「加载失败:{err.message}」。

涉及的 API 端点

  • GET /api/roles

4.6 角色详情查看流程

流程名称:角色详情查看流程

参与角色:已登录的普通用户

前置条件

  • 用户处于角色库视图,角色列表已加载。

详细步骤

  1. 用户在角色库点击角色卡片(.role-card,含 data-role-id)。
  2. 前端事件委托捕获点击:
    • 调用 renderRoleDetail(roleId)
    • showView('role-detail') 切换到角色详情视图。
  3. renderRoleDetail(roleId) 执行:
    • 调用 api('/roles/${roleId}') 获取角色详情。
  4. 后端 GET /api/roles/:id 处理:
    • prisma.role.findUnique({ where: { id }, select: { id, displayName, avatar, desc, price, status, gender, age, relationship, personality, background, speechStyle, greeting, creatorId } })
    • 角色不存在 → 404 { error: '角色不存在' }
    • 返回 { role }
  5. 前端渲染详情:
    • currentRole = role(保存当前角色,供付款流程使用)。
    • 设置详情头部名称(#detail-name)。
    • 设置 hero 背景图(#detail-hero,使用 role.avatar)。
    • 设置角色名称(#detail-role-name)、描述(#detail-role-desc,取 desc || personality)。
    • 设置价格(#detail-price,显示「¥{price} / 月」)。
    • 显示「立即订阅」按钮(#detail-actions-pre),隐藏已付款区域(#detail-paid)。
  6. 用户可点击「立即订阅」→ 进入 角色付款流程
  7. 用户可点击返回按钮(data-action="back-to-library")→ renderRoleLibrary() + showView('role-library') 返回角色库。

后置条件

  • 角色详情视图展示角色头像、描述、价格、订阅按钮。

异常处理

  • 加载失败 → alert('加载角色详情失败:' + err.message) + goBack() 返回上一视图。

涉及的 API 端点

  • GET /api/roles/:id

4.7 新建角色流程4 步表单)

流程名称:新建角色流程

参与角色:创作者(isCreator === true

前置条件

  • 用户已登录且为创作者。
  • 处于创作者管理中心。

详细步骤

  1. 用户在创作者管理中心「我的角色」Tab点击「+ 新建角色」按钮(data-action="new-role")。
  2. 前端执行 resetCreator()
    • 重置表单(form.reset())。
    • 显示表单、隐藏结果面板。
    • 清空 generatedSoulgeneratedConfigeditingRoleId = null
    • updateStep(0) 回到第一步。
    • updateSystemPromptPreview() 刷新系统提示词预览。
  3. showView('creator') 切换到角色编辑视图。
  4. Step 1 — 基础身份data-step="0"
    • agentId:角色代号(必填,pattern="[a-zA-Z0-9_]+",用于 config.yaml
    • displayName:显示名称(必填)。
    • gender性别select默认 unknown
    • age:年龄(选填)。
    • 点击「下一步」(data-action="next")→ validateStep(0) 校验 → updateStep(1)
  5. Step 2 — 灵魂设定 Soul.mddata-step="1"
    • background背景故事必填textarea
    • personality:性格标签(必填,逗号分隔)。
    • speechStyle说话风格必填textarea
    • likes:喜好(选填)。
    • dislikes:厌恶 / 底线(选填)。
    • 「上一步」/「下一步」。
  6. Step 3 — 关系与记忆data-step="2"
    • relationship:与使用者的关系(选填)。
    • memories:共同记忆 / 关键事件选填textarea
    • secrets:秘密或未说出口的话(选填)。
    • greeting开场白必填textarea
    • 「上一步」/「下一步」。
  7. Step 4 — 运行配置 config.yamldata-step="3"
    • model:模型(默认 hermes-3-llama-3.1-70b)。
    • temperature温度number0~2默认 0.85)。
    • maxTokens:最大 Tokennumber默认 1024
    • systemPrompt系统提示词textarea留空自动生成
    • enableMemory启用长期记忆checkbox默认勾选
    • enableTools启用外部工具checkbox默认不勾选
    • 点击「生成并发布」(data-action="publish")。
  8. publish() 执行(详见 4.9 角色发布数据生成流程
    • validateStep(currentStep) 校验当前步骤。
    • getFormData() 收集表单数据。
    • systemPrompt 为空 → buildSystemPrompt(data) 自动生成。
    • generateSoulMd(data) 生成 Soul.md。
    • generateConfigYaml(data) 生成 config.yaml。
    • 构建 payload补充 descpriceavatartemperaturemaxTokens 等)。
    • 由于 editingRoleId === null,调用 api('/roles', { method: 'POST', body: payload })
  9. 后端 POST /api/roles 处理:
    • 校验必填字段(displayNamegreetingpersonalitybackgroundspeechStyle)。
    • prisma.role.create() 创建角色,creatorId = req.userIdstatus = 'running'
    • 返回 { role }
  10. 前端收到响应:
    • form.hidden = true 隐藏表单。
    • resultPanel.hidden = false 显示结果面板。
    • renderPreview() 渲染预览代码(默认显示 Soul.md
  11. 结果面板展示:
    • 「角色已蒸馏完成」标题。
    • Soul.md 与 config.yaml 下载按钮(data-download="soul" / data-download="config")。
    • 预览 Tab 切换Soul.md / config.yaml
    • 「再创建一个」(data-action="reset")与「返回管理中心」(data-action="back-to-center")按钮。

后置条件

  • 数据库新增一条 Role 记录,status = 'running'creatorId 为当前用户。
  • 前端显示结果面板,可下载 Soul.md 与 config.yaml。

异常处理

  • 必填字段缺失 → 后端 400 { error: '必填字段缺失' },前端 alert('保存失败:' + err.message)
  • 步骤校验失败 → validateStep() 调用 input.reportValidity() 显示浏览器原生校验提示,阻止进入下一步。
  • 服务器异常 → 500 { error: '发布失败' }

涉及的 API 端点

  • POST /api/roles

4.8 编辑角色流程

流程名称:编辑角色流程

参与角色:创作者(角色创建者)

前置条件

  • 用户已登录且为创作者。
  • 创作者管理中心「我的角色」列表已加载。

详细步骤

  1. 用户在创作者管理中心「我的角色」Tab角色列表由 renderCreatorRoles() 渲染(调用 GET /api/roles/my/roles)。
  2. 每个角色卡片含「编辑」按钮(data-action="edit-role"data-role-id="{id}")。
  3. 用户点击「编辑」按钮。
  4. 前端事件委托捕获:
    • await loadRoleForEdit(roleId)
    • showView('creator')
  5. loadRoleForEdit(roleId) 执行:
    • 调用 api('/roles/${roleId}/full') 获取角色完整信息。
  6. 后端 GET /api/roles/:id/full 处理:
    • authMiddleware 验证登录。
    • prisma.role.findUnique({ where: { id } }) 查询角色(含全部字段)。
    • 角色不存在 → 404 { error: '角色不存在' }
    • role.creatorId !== req.userId403 { error: '无权查看' }(仅创建者可访问)。
    • 返回 { role }
  7. 前端填充表单:
    • editingRoleId = roleId(标记为编辑模式)。
    • 显示表单、隐藏结果面板。
    • 逐字段填充:displayNamegenderagerelationshippersonalitybackgroundspeechStylelikesdislikesmemoriessecretsgreetingsystemPromptmodeltemperature(转 StringmaxTokens(转 Stringprice(转 String
    • 设置 enableMemoryenableTools checkbox 状态。
    • updateStep(0) 回到第一步。
    • updateSystemPromptPreview() 刷新预览。
  8. 用户编辑各步骤字段(与新建流程一致的 4 步表单)。
  9. 用户点击「生成并发布」(data-action="publish")。
  10. publish() 执行:
    • 由于 editingRoleId 不为 null,调用 api('/roles/${editingRoleId}', { method: 'PUT', body: payload })
  11. 后端 PUT /api/roles/:id 处理:
    • prisma.role.findUnique({ where: { id } }) 查询角色。
    • 角色不存在 → 404 { error: '角色不存在' }
    • existing.creatorId !== req.userId403 { error: '无权编辑他人角色' }
    • prisma.role.update() 更新字段(使用 ?? 运算符,未传入字段保留原值)。
    • 返回 { role }
  12. 前端显示结果面板(同新建流程)。

后置条件

  • 数据库中对应 Role 记录被更新。
  • 前端显示结果面板,可下载更新后的 Soul.md 与 config.yaml。

异常处理

  • 角色不存在 → 404
  • 非创建者编辑 → 403 { error: '无权编辑他人角色' }
  • 加载角色数据失败 → alert('加载角色数据失败:' + err.message)
  • 保存失败 → alert('保存失败:' + err.message)

涉及的 API 端点

  • GET /api/roles/:id/full(加载完整数据)
  • PUT /api/roles/:id(提交编辑)

4.9 角色发布数据生成流程

流程名称:角色发布数据生成流程

参与角色:创作者

前置条件

  • 创作者已完成 4 步表单填写并点击「生成并发布」。

详细步骤

  1. publish() 被调用,首先 validateStep(currentStep) 校验当前步骤。
  2. getFormData() 收集表单数据:
    • 使用 FormData + Object.fromEntries() 获取所有字段。
    • 单独处理 enableMemoryenableToolscheckbox.checked)。
  3. data.systemPrompt.trim() 为空,调用 buildSystemPrompt(data) 自动生成系统提示词:
    • personality 按中英文逗号分割为标签数组,用「、」连接。
    • 组装包含基本设定、性格、背景、说话风格、喜好、厌恶、共同记忆、内心秘密的中文提示词。
    • 末尾强调「请始终保持角色一致性……像一个真实、有记忆、有情绪的人一样陪伴对方」。
  4. generateSoulMd(data) 生成 Soul.mdMarkdown 格式):
    • 标题 # Soul of {displayName}
    • 包含 Identity、Background、Personality、Speech Style、Likes、Dislikes、Shared Memories、Secrets & Inner Voice、First Greeting 等章节。
    • 性格标签用 | 分隔。
  5. generateConfigYaml(data) 生成 config.yamlYAML 格式):
    • systemPrompt 为空,使用 buildSystemPrompt(data) 的结果。
    • escapeYaml() 对含特殊字符(:#、换行、")的值进行转义。
    • 包含 agentid、name、versionmodelname、temperature、max_tokenssystem_promptmemoryenabled、storage、recall_depthcharactersoul_file、greeting
    • enableTools 为 true追加 toolssearch_memory、save_memory
  6. 构建 payload补充派生字段
    • soulMd = generatedSoulconfigYaml = generatedConfig
    • desc:取 personality 前两个标签用「,」连接。
    • priceparseFloat(data.price) || 29.9(表单无 price 字段,默认 29.9)。
    • avatardata.avatar || 自动生成 URL基于 displayName 的 text_to_image 接口)。
    • temperatureparseFloat(data.temperature) || 0.8
    • maxTokensparseInt(data.maxTokens) || 2048
  7. 根据 editingRoleId 决定请求方式:
    • nullPOST /api/roles(新建)。
    • nullPUT /api/roles/:id(编辑)。
  8. 请求成功后:
    • form.hidden = trueresultPanel.hidden = false
    • renderPreview()generatedSoulgeneratedConfig 写入 <pre><code> 预览区。
  9. 用户可在结果面板:
    • 切换预览 TabSoul.md / config.yaml
    • 点击「下载」按钮下载对应文件(download() 通过 Blob + <a download> 实现)。

后置条件

  • Soul.md 与 config.yaml 内容已生成并持久化到数据库(soulMdconfigYaml 字段)。
  • 前端结果面板展示预览,支持下载。

异常处理

  • API 失败 → alert('保存失败:' + err.message),表单保持显示,用户可修正后重试。

涉及的 API 端点

  • POST /api/roles(新建)或 PUT /api/roles/:id(编辑)

4.10 创作者中心管理流程

流程名称:创作者中心管理流程

参与角色:创作者

前置条件

  • 用户已登录且 isCreator === true

详细步骤

  1. 用户进入创作者管理中心(登录后自动跳转,或通过首页卡片 / TabBar「我的」进入
  2. renderCreatorCenter() 被调用,依次执行:
    • renderCreatorRoles()渲染「我的角色」Tab。
    • renderIncome()渲染「收入」Tab。
    • renderSettings()渲染「我的」Tab。
  3. 三个 Tab 通过 switchCenterTab(tab) 切换(data-center-tab 属性):
    • roles(我的角色,默认激活):
      • renderCreatorRoles() 调用 GET /api/roles/my/roles 获取当前用户创建的全部角色。
      • 渲染角色卡片(含头像、名称、运行状态、编辑按钮)。
      • 列表底部有「+ 新建角色」按钮。
      • 空列表显示「还没有创建角色,点击「新建角色」开始」。
    • income(收入):
      • renderIncome() 读取 state.income,若 balance === 0 则使用 mockIncomeMock 数据:余额 ¥1280.503 条流水记录)。
      • 显示可提现余额、流水明细列表。
      • 包含「申请提现」表单(收款方式 select + 金额 input提交时校验金额不超过余额alert 提示「提现申请已提交」Mock无 API
    • settings(我的):
      • renderSettings()state.creatorNamestate.libraryName 填入设置表单。
      • 设置表单提交 → 进入 设置保存流程
      • 「退出登录」按钮 → 进入 用户登出流程
  4. Tab 切换时更新 #center-tab-label 文本与 aria-selected 属性。

后置条件

  • 创作者中心三个 Tab 均可正常切换与展示数据。

异常处理

  • GET /api/roles/my/roles 失败 → 角色列表区域显示「加载失败:{err.message}」。
  • 提现金额超过余额 → alert('提现金额超过可提现余额')

涉及的 API 端点

  • GET /api/roles/my/roles(角色列表)
  • PUT /api/auth/settings(设置保存,见 4.11

4.11 设置保存流程

流程名称:设置保存流程

参与角色:创作者

前置条件

  • 用户在创作者管理中心「我的」Tab。

详细步骤

  1. 用户在设置表单(#settings-form)填写:
    • creatorName:创作者名字(笔名)。
    • libraryName:角色库名称(首页「我的 XXX」显示文字
  2. 用户点击「保存设置」按钮提交。
  3. 前端 submit 事件触发:
    • FormData 收集 creatorNamelibraryName
    • api('/auth/settings', { method: 'PUT', body: { creatorName, libraryName } })
  4. 后端 PUT /api/auth/settings 处理:
    • authMiddleware 验证登录。
    • prisma.user.update({ where: { id: req.userId }, data: { creatorName, libraryName } })(仅更新传入字段)。
    • 返回 { user: { id, account, isCreator, creatorName, libraryName } }
  5. 前端收到响应:
    • 更新 state.creatorNamestate.libraryName(若 libraryName 为空则回退为 '我的 [XXX]')。
    • saveState() 持久化。
    • updateLandingCard() 更新首页卡片显示。
    • alert('设置已保存') 提示成功。

后置条件

  • 数据库 User 记录的 creatorNamelibraryName 字段更新。
  • 前端状态与首页卡片同步更新。

异常处理

  • 未登录 → 401 { error: '未登录' }401 { error: '登录已过期,请重新登录' }
  • 服务器异常 → 500 { error: '更新失败' },前端 alert('保存失败:' + err.message)

涉及的 API 端点

  • PUT /api/auth/settings

4.12 角色付款流程

流程名称:角色付款流程

参与角色:已登录的普通用户

前置条件

  • 用户处于角色详情视图,currentRole 已加载。

详细步骤

  1. 用户在角色详情视图点击「立即订阅」按钮(data-action="pay")。
  2. 前端 payRole() 执行(当前为 Mock 实现,无 API 调用、无订单创建
    • 隐藏「立即订阅」按钮区域(#detail-actions-pre)。
    • 显示已付款区域(#detail-paid)。
    • #detail-qr 显示二维码占位符「扫码连接 AI 角色」。
    • #detail-avatar 设置为 currentRole.avatar 背景图。
  3. 已付款区域显示提示文案:「扫码添加后,请将下方头像保存并设置为该联系人的备注头像,获得更完整的体验」。
  4. 用户可点击「下载角色头像」按钮(data-action="download-avatar"
    • 调用 download(currentRole.name + '_avatar.png', '')(内容为空)。
    • window.open(currentRole.avatar, '_blank') 在新窗口打开头像图片。

后置条件

  • 详情视图切换为「已付款」状态,显示二维码占位与头像。

异常处理

  • 此流程为纯前端 Mock无异常处理逻辑。

⚠️ 注意:当前付款流程为前端 Mock未调用后端 API未创建 Order 记录。Order 数据模型已定义但未投入使用。未来需对接真实支付并创建订单。

涉及的 API 端点

  • Mock 实现)

4.13 导航流程

流程名称:导航流程

参与角色:所有用户

前置条件

  • 应用已加载。

详细步骤

A. 视图View系统

应用包含 9 个视图,通过 .active class 控制显示:

视图 ID 标签 说明
landing 首页 落地页,含两张卡片与底部链接
auth 登录 / 注册 认证视图,含登录与注册两个 Tab
role-library 角色库 角色列表
role-detail 角色详情 单个角色详情
distill 蒸馏前任 蒸馏服务介绍页
about 关于 Eternal AI 平台介绍与 FAQ
onboarding 创作者入驻 创作者合作说明
creator-center 创作者管理中心 三 Tab 管理面板
creator 角色编辑 4 步角色创建/编辑表单

B. 视图切换与历史栈

  • showView(name, trackHistory = true)
    • 切换所有视图的 .active class。
    • trackHistorytrue 且新视图与栈顶不同,将 name 压入 viewHistory 数组。
    • 滚动到顶部。
    • updateTabBar(name) 同步底部 TabBar 高亮。
    • 无障碍:将焦点移至新视图,通过 #sr-announce live region 播报视图名称。
  • goBack()
    • 弹出 viewHistory 栈顶,显示前一个视图(trackHistory = false 避免重复压栈)。
    • 若栈中仅剩一个元素,返回 landing

C. 首页卡片导航data-action

动作 行为
open-characters 未登录 → auth 视图;创作者 → creator-center普通用户 → role-library
open-distill → distill 视图
open-about → about 视图
open-onboarding → onboarding 视图

D. 底部 TabBardata-tab-action

Tab 动作 行为
tab-home → landing 视图
tab-distill → distill 视图
tab-mine 未登录 → auth创作者 → creator-center普通用户 → role-library

updateTabBar(viewName) 根据当前视图映射高亮对应 Tablanding→homedistill→distillrole-library/creator-center→mine

E. 返回按钮data-action

动作 行为
back goBack() 返回历史栈上一视图
back-to-library 重新渲染角色库并切换到 role-library
back-to-center 切换到 creator-center 并重新渲染

后置条件

  • 视图正确切换,历史栈与 TabBar 状态同步。

异常处理

  • 无异常风险(纯前端导航)。

涉及的 API 端点


4.14 表单验证流程

流程名称:表单验证流程

参与角色:所有需要填写表单的用户

前置条件

  • 用户处于含表单的视图。

详细步骤

A. 注册表单验证#register-form

字段 验证规则 实现方式
account 必填 HTML required + checkValidity()
password 必填,最少 6 位 HTML required + minlength="6"
confirmPassword 必填,且与 password 一致 validatePasswordMatch():比较两次值,不一致则 setCustomValidity('两次输入的密码不一致') + reportValidity()
  • 提交时先调用 validatePasswordMatch(authForm),返回 false 则阻止提交。
  • 后端二次校验:accountpassword 非空、password.length >= 6

B. 登录表单验证#login-form

字段 验证规则
account 必填
password 必填
  • validatePasswordMatch() 检测到无 confirmPassword 字段,直接返回 true
  • 后端校验 accountpassword 非空。

C. 角色编辑表单验证#character-form4 步)

每步通过 validateStep(index) 校验:遍历该步骤内所有 inputtextareaselect,调用 input.checkValidity(),不通过则 input.reportValidity() 显示浏览器原生提示。

步骤 字段 验证规则
Step 1 agentId 必填,pattern="[a-zA-Z0-9_]+"(仅英文/数字/下划线)
Step 1 displayName 必填
Step 1 gender select默认 unknown,无需校验
Step 1 age 选填
Step 2 background 必填
Step 2 personality 必填
Step 2 speechStyle 必填
Step 2 likes / dislikes 选填
Step 3 relationship 选填
Step 3 memories / secrets 选填
Step 3 greeting 必填
Step 4 model 默认值,无需校验
Step 4 temperature type="number"min="0" max="2" step="0.05"
Step 4 maxTokens type="number"min="1"
Step 4 systemPrompt 选填(留空自动生成)
  • 「下一步」按钮(data-action="next"):先 validateStep(currentStep),通过才 updateStep(currentStep + 1)
  • 「生成并发布」(data-action="publish"):先 validateStep(currentStep) 校验当前(第 4步。
  • 后端 POST /api/roles 二次校验必填字段:displayNamegreetingpersonalitybackgroundspeechStyle,缺失返回 400 { error: '必填字段缺失' }

D. 设置表单验证#settings-form

字段 验证规则
creatorName 选填(type="text"
libraryName 选填(type="text"
  • 无前端必填校验,直接提交。
  • 后端 PUT /api/auth/settings 仅更新传入字段。

E. 提现表单验证#withdraw-form

字段 验证规则
method requiredselectwechat/alipay
amount requiredtype="number" min="1" step="0.01"
  • 提交时前端额外校验 amount 不超过可提现余额(state.income.balancemockIncome.balance 取较大者比较),超过则 alert('提现金额超过可提现余额')

后置条件

  • 校验通过则继续后续流程;不通过则阻止提交并提示用户。

异常处理

  • 前端校验失败:浏览器原生 reportValidity() 提示或 alert
  • 后端校验失败:返回 4xx + { error },前端 alert 显示错误信息。

涉及的 API 端点

  • 所有涉及表单提交的端点(注册、登录、角色发布/编辑、设置保存)。

五、附录

5.1 前端状态结构

前端状态 state 存储于 app.js,默认结构如下:

const defaultState = {
  isLoggedIn: false,      // 是否已登录
  isCreator: false,       // 是否为创作者
  account: null,          // 登录账号
  userId: null,           // 用户 ID
  boundCreator: null,     // 绑定的创作者
  libraryName: '我的 [XXX]',  // 角色库名称
  creatorName: '',        // 创作者笔名
  roles: [],              // 创作者的角色列表
  income: { balance: 0, records: [] },  // 收入数据
};
  • loadState():从 localStorage 读取并与 defaultState 合并。
  • saveState():将 state 序列化写入 localStorage。
  • applyUserData(user):根据后端返回的 user 对象更新 statesaveState()

5.2 localStorage 键名约定

键名 内容 说明
eternal_ai_token JWT 字符串 登录凭证,由 setToken() / getToken() 管理
eternal_ai_state JSON 序列化的 state 对象 前端状态持久化

5.3 已知限制与待办

  1. 付款流程为 MockOrder 模型已定义但未使用,角色付款(4.12)为纯前端模拟,未对接真实支付、未创建订单记录。
  2. 收入数据为 MockrenderIncome()state.income.balance === 0 时回退到 mockIncome(硬编码数据),无后端收入 API。
  3. 提现流程为 Mock:提现表单提交仅 alert 提示,无后端处理。
  4. 角色表单无 priceavatar 输入框publish()data.pricedata.avatarundefined,分别回退到默认值 29.9 与自动生成的头像 URL。
  5. mockRoles 未使用app.js 中定义了 mockRoles 数组,但 renderRoleLibrary() 始终调用 GET /api/roles 获取真实数据,该数组未被引用。
  6. JWT Secret 默认值src/lib/auth.jsJWT_SECRET 默认值为 'eternalai_jwt_secret_2026_change_in_prod',生产环境必须通过 .envJWT_SECRET 覆盖。
  7. 无角色删除功能:后端未提供 DELETE /api/roles/:id 端点,前端无删除入口。
  8. 无角色上下架切换status 字段支持 running/stopped,但前端无切换入口,新建角色默认 running
  9. 蒸馏前任服务为展示页distill 视图为静态介绍页,「立即下单」按钮仅 alert 提示,无实际下单流程。