Files
pj0235-eai_agentplatform/eai_agentplatform/backend-go/deploy/DELIVERY.md
T
eaiadminandClaude Code 14f303459e refactor: 后端仓库层收口(A1:课程/产品/素材)+ 收进工作区既有对象化重构
本提交含两部分。第一部分是本轮工作;第二部分是此前一直留在工作区、
从未提交的对象化重构,与第一部分在文件上互相咬合(internal/repository
整个包都是未跟踪状态,且 api 层已有文件引用它),无法拆成两个可编译的提交。

一、仓库层收口 A1 批(本轮工作)

把 api 层手写的 store.DB 查询收进具名仓库方法,只给真正获益的对象做方法,
不机械包裹全量。本批迁移 22 处裸查询(courses.go 9 / media.go 12 / products.go 1),
新增方法:

- MediaFileRepo.ListByBind / ListForAudit / MarkExtracted
- KnowledgeChunkRepo.CountByMediaFile
- ProductRepo.GetVisibleByID

两条业务口径改由仓库单点持有,避免各处手写漂移:
「只有 approved 素材出现在课程详情」与「已停用产品不在课程详情露出」。

修掉两个真实缺陷:
- ProductRepo.GetByID 缺 Where 条件。此前 GET /api/products/{id} 对任意 id 都返回
  第一条产品、对不存在的 id 返回 200,且 PUT /api/products/{id} 会覆盖第一条产品
  —— 数据损坏级。全仓扫描确认这是唯一一处同型写法。
- ProductRepo.Delete 写 status="deleted",而 DELETE 处理器文档与回包都声称
  "inactive",接口在说谎;管理员用 status=all 拉列表会看到前端不认识的状态。
  已对齐为 inactive(与 CourseRepo.Delete 一致)。

删除 8 个零调用且列名不存在的死方法(一调即 SQL 报错):
- media_file 上的 file_path / file_type / approval_status 三列并不存在,
  GetByPath / ListByType / UpdateStatus 全废
- knowledge_chunk 上的 space_id 列不存在(模型早已改为 knowledge_space_key),
  List / Total / ListBySpaceIDs / DeleteBySpace / SearchByVector 全废
取舍边界:能对当前 schema 跑通的死方法保留,跑不通的删或修。

CourseRepo.List 补齐 status=all 档(此前传给它会当作 status='all' 过滤出空列表)。
该方法此前零调用,现与产品列表语义对齐。

验证:go build ./... 与 go test ./... 全绿;另用真实 HTTP 请求验证 34 项
(课程 17 / 产品 3 / 素材 14),跑在数据库副本与独立 KB_DATA_DIR 上,
含 multipart 真上传 → 审批 → pdftotext 提取 → 分片入库的完整链路。

二、此前未提交的对象化重构(非本轮工作)

- 新增 internal/repository 仓库层、connectors、skills、specialists、xapps、jsonutil,
  model/task_record|task_run|task_artifact、api/task_runtime|action_definition|chat_message
- 删除 api/app_definition、connectors、my_app_center、notification、office_skill、
  export_docx|pptx|xlsx、official_account_* 等,随 XApp/Skill/Specialist/Connector
  可插拔打包方向(AR10/AR11)调整
- 资产目录归位:backend-go/knowledge_source → assets/knowledge/source、
  training_materials → assets/training/materials;README 内相对路径同步加深两级;
  deploy env 补 ASSET_ROOT_DIR 并改 KNOWLEDGE_SOURCE_DIR / TRAINING_MATERIALS_DIR
- 前端新增 skills/ specialists/ connectors/ xapps/ 目录与对应页面

验证:前端 npm run build 通过(7.26s)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-19 01:23:51 +08:00

7.5 KiB
Raw Blame History

交付手册(Go 单二进制 + Clonezilla 整盘克隆)

面向交付工程师。目标:把一台配置好的原型机整盘克隆到客户同型号机器, 客户拿到手即用,机器上无源代码、无开发痕迹、无测试数据、无默认口令。


0. 交付物清单

文件 说明
eai_agentplatform-server 单二进制(CGO 关闭,静态链接,约 38MB,无任何运行时依赖)
eai_agentplatform.service systemd 单元文件
eai_agentplatform.env 环境变量样例
clonezilla-cleanup.sh 克隆前清理脚本(DRY-RUN 默认)
data/eai_agentplatform.db SQLite 数据库(首启自动建表)
data/kb_data/ 已审批素材目录
assets/knowledge/source/ 知识源 Markdown(待入库,由管理员审批)
assets/training/materials/ 培训资料资产(课程种子、PDF、视频、脚本)

不需要:Go 运行时、Python、Docker、MySQL、任何 pip/npm 依赖。


1. 目录布局(客户机器最终形态)

/opt/eai_agentplatform/
├── eai_agentplatform-server     # 单二进制
├── .env                     # JWT 密钥等(交付前生成,勿提交源码库)
├── assets/
│   ├── knowledge/
│   │   └── source/          # 知识源 Markdown
│   └── training/
│       └── materials/       # 培训资料资产
├── data/
│   ├── eai_agentplatform.db     # SQLite(首启自动建)
│   ├── backups/             # 定期备份(服务自动维护,见第 7 节)
│   └── kb_data/             # 素材 + 提取缓存
└── (前端静态资源由 Nginx 托管,见 ../docs/deploy.md)

2. 交付前准备(原型机)

# 2.1 构建静态二进制(开发机执行,产物拷贝到原型机)
export PATH=$HOME/go-sdk/go/bin:$PATH CGO_ENABLED=0 GOPROXY=https://goproxy.cn,direct
go build -o bin/eai_agentplatform-server ./cmd/server
# 校验:file 输出应为 "statically linked",ldd 应为 "not a dynamic executable"

# 2.2 拷贝到原型机 + 建账号 + 装 systemd 单元
sudo install -m 0755 eai_agentplatform-server /opt/eai_agentplatform/eai_agentplatform-server
sudo useradd -r -s /usr/sbin/nologin eai_agentplatform
sudo mkdir -p /opt/eai_agentplatform/data/kb_data /opt/eai_agentplatform/assets/knowledge/source /opt/eai_agentplatform/assets/training/materials
sudo chown -R eai_agentplatform:eai_agentplatform /opt/eai_agentplatform
sudo install -m 0644 deploy/eai_agentplatform.service /etc/systemd/system/eai_agentplatform.service

# 2.3 生成 .env(含随机 JWT 密钥)
sudo -u eai_agentplatform cp deploy/eai_agentplatform.env /opt/eai_agentplatform/.env
NEW_SECRET=$(python3 -c "import secrets; print(secrets.token_urlsafe(48))")
sudo sed -i "s|^JWT_SECRET=.*|JWT_SECRET=$NEW_SECRET|" /opt/eai_agentplatform/.env

# 2.4 启动验证
sudo systemctl daemon-reload
sudo systemctl enable --now eai_agentplatform
curl http://127.0.0.1:8080/api/health   # {"status":"ok",...}

3. 克隆前清理(不可逆,务必先 DRY-RUN)

sudo bash clonezilla-cleanup.sh          # DRY-RUN,只列动作
sudo bash clonezilla-cleanup.sh --confirm # 确认后真实清理

脚本负责:

  • 删除所有 Go 源代码(cmd/ internal/ go.mod go.sum)
  • 清除 .git / 版本控制痕迹
  • 清空日志与 shell 历史

脚本不负责(需手工确认,见下):

手工清单(缺一不可)

# 动作 命令
A 重置管理员密码 sudo -u eai_agentplatform /opt/eai_agentplatform/eai_agentplatform-server -reset-admin '<强密码>'
B 确认 JWT 密钥随机 grep -c '__CHANGE_ME__' /opt/eai_agentplatform/.env(应为 0)
C 清空测试数据 Web 后台删除演示用户/素材/考试记录,或用 sqlite3(见第 4 节)

默认种子账号为 admin / admin123,克隆前必须改密(A 项),否则客户拿到默认口令。


4. 清空测试数据的 SQL 参考(可选)

# 若需彻底清库只保留结构 + admin:
sqlite3 /opt/eai_agentplatform/data/eai_agentplatform.db <<'SQL'
-- 先停服务
DELETE FROM exam_record;
DELETE FROM knowledge_chunk;
DELETE FROM knowledge_source;
DELETE FROM media_file;
DELETE FROM course;
DELETE FROM product;
DELETE FROM question;
DELETE FROM exam_paper;
DELETE FROM "user" WHERE username != 'admin';
DELETE FROM system_config WHERE config_key NOT IN ('company_intro','llm_base_url','llm_model','embed_model','llm_api_key');
SQL

表名以实际 schema 为准;无 sqlite3 时,用管理员 Web 界面逐项删除亦可。


5. Clonezilla 整盘克隆

  1. 原型机清理完成、复核 A/B/C 三项后关机。
  2. U 盘启动 Clonezilla → device-device(整盘复制)。
  3. 目标机为同型号机器,逐台克隆。
  4. 客户机器首启:systemd 自动拉起服务;Nginx 反代 8080。
  5. 交付验收:登录(新密码)→ 上传素材 → 审批 → 考试 → AI 问答(若接内网 LLM)。

6. 交付红线(每次克隆前过一遍)

  • ldd eai_agentplatform-server 输出 not a dynamic executable
  • find /opt/eai_agentplatform -name '*.go' -o -name 'go.mod' 无结果
  • /opt/eai_agentplatform/.env 无 __CHANGE_ME__
  • 管理员密码非 admin123
  • .bash_history 已清空、无 .git
  • 测试账号(zhangsan 等)、演示素材、演示考试记录已删除
  • data/backups/ 里没有原型机自己的数据备份(见第 7 节末)

7. 数据备份与恢复

SQLite 是单文件、没有主从副本,文件坏一份就是全丢,所以服务自带定期备份,默认开着。

怎么跑:进程启动时先检查一次(距上次备份够久就补一份),之后每小时醒一次看是否到期。 不需要外部 cron / systemd timer,备份逻辑在二进制里。间隔和保留份数由 .env 控制:

BACKUP_ENABLED=true          # 关掉就完全不备
BACKUP_DIR=data/backups      # 必须在 data/ 下(ProtectSystem=strict 只放开这里)
BACKUP_KEEP=7                # 保留最近几份,更旧的自动删
BACKUP_INTERVAL_HOURS=24     # 间隔

备份文件长这样:data/backups/eai_agentplatform-20260914-162401.db。 用 VACUUM INTO 产出,不是 cp 主库 —— 服务边跑边写时 cp 可能拷到写了一半的页; VACUUM INTO 出来的是已压实、内部一致的完整副本,且不用停服。产出的文件会校验 SQLite 文件头, 不是真库就删掉并报错,不会留下「看着像备份的废物」。

手工备一份(升级、迁移、动数据之前留个手边的副本):

sudo -u eai_agentplatform /opt/eai_agentplatform/eai_agentplatform-server -backup

恢复(会覆盖当前数据,先停服):

sudo systemctl stop eai_agentplatform
sudo -u eai_agentplatform cp data/backups/eai_agentplatform-20260914-162401.db data/eai_agentplatform.db
sudo systemctl start eai_agentplatform

备份只在本机 data/ 目录里,防的是误删/误改/写坏,防不了整盘损坏。 要防整盘,得把 data/backups/ 定期拷到机器之外(U 盘 / 内网文件服务器)。

克隆前注意:原型机的 data/backups/ 里会有原型机自己的历史数据(测试账号、演示素材), 整盘克隆会原样带到客户机器上。清理时一并删掉该目录内容,让客户机器从干净状态开始。

恢复之后想核对内容,直接看文件时间戳和大小即可 —— 文件名里的时间就是备份时刻。