导入成绩工作台(dsh-fuulea-import)
DeepSeek Harness 工作台插件:从辅立码课 / 智慧教学云学校站点选择已发布的「平常考试」任务, 把成绩文件(xlsx / xls / csv,或打包成 zip 的多个文件)拖进工作台,自动识别姓名列、题号列与总分列, 下载全通教育「班级对照表」模板并自动填充,预检(空格 / 名单一致性)通过后一键导入,并核对平台回执。
- 第一版范围:只做「平常考试」 —— 任务已在平台发布(老师照常发布任务即可); 工作台负责 下模板 → 填充 → 预检 → 上传 → 核回执。不做 绑卷 / 发布考试任务 / 阅卷分配 / 收尾。
- 支持学校绑定与切换:账号只在本机保存一次(
~/.fuulea-import/bindings.json,权限 0600), 之后打开工作台自动登录,可在已绑定的多所学校之间切换。
安装
- 仓库:https://github.com/100759/dsh-fuulea-import
- 装到你的 DSH profile(示例用
desktop;web 端就换成--profile web):
# 从 GitHub 安装(推荐,同事直接复用这一条)
dsh plugin --profile desktop add github:100759/dsh-fuulea-import
# 本地源码调试(link 安装)
dsh plugin --profile desktop add link:/path/to/dsh-fuulea-import
# 或本地打包目录安装
dsh plugin --profile desktop add /path/to/dsh-fuulea-import
插件自带 cordis.patch.yml,安装后自动在 profile 里插入 fuulea-import 这一行;
若插件管理里显示为未启用,手动启用一次即可。重启 DeepSeek Harness 后,工作台左侧出现「导入成绩」入口。
用
github:安装时,每次执行该命令都会拉到最新提交;更新后需要重启 DeepSeek Harness 生效。
版本与更新
页面右上角显示当前版本(如 v1.1.0)。只要本机是按 github: 源安装的,就有两条更新途径:
| 方式 | 操作 |
|---|---|
| 页面内一键更新(推荐) | 有新版本时徽标变红并带跳动红点,点它展开面板 →「立即更新」 |
| 命令行 | dsh plugin --profile <你的 profile> add github:100759/dsh-fuulea-import |
- Host 每 10 分钟最多查一次 GitHub 上的最新版本(
GET /update-check);页面打开时查一次, 面板上的「检查更新」可强制立即查。 - 有新版本时,版本面板会自动展开并显示「有新版本可用:vX」;点「立即更新」会在本机 profile 里执行
pnpm add github:100759/dsh-fuulea-import(即重新拉到最新提交)。 - 更新完成后提示重启 DeepSeek Harness;重启前版本面板会显示「已更新到 vX,重启 Harness 后生效」, 红点同时消失(不会因为还没重启就一直提示有新版本)。
- 版本比较用「磁盘上已装的版本」而不是「正在运行的版本」,所以更新成功后红点立刻消失。
无法一键更新的情况,面板会说明原因:
| 情况 | 面板提示 |
|---|---|
link: 本地目录安装 |
更新请直接改源码目录,或改从 GitHub 安装 |
| npm / 其它来源 | 给出可复制的 dsh plugin --profile <p> add github:... 命令 |
| 内网无法访问 GitHub | 显示「检查更新失败:…」,不影响导入成绩等主流程 |
关闭联网检查(内网或合规环境)—— 在 cordis.patch.yml 的 config 里加:
- insert:
- id: fuulea-import
name: 'dsh-fuulea-import'
config:
checkUpdates: false # 不发起任何版本检查请求
repository: owner/repo # 换仓库地址(默认 100759/dsh-fuulea-import)
发版流程(维护者)
别人能不能「更新到你的最新版」,取决于 package.json 的 version 有没有变大:
- 改代码,把
package.json的version提高(如1.1.0→1.2.0)。 CHANGELOG.md补一段该版本的说明。git push到main(并建议打 tag:git tag v1.2.0 && git push origin v1.2.0)。
推上去之后,其他人下次打开工作台(或点「检查更新」)就会看到红点,点「立即更新」即可。
只推代码不升
version,别人的红点不会亮 —— 版本号是唯一的比较依据。
依赖
- Node ≥ 22.19(插件本身);
python3+openpyxl(填充与预检)。 - 读老式
.xls需xlrd(第一版对.xls提示另存为.xlsx)。
python3 -m pip install openpyxl # 必需
python3 -m pip install xlrd # 可选:要直接读 .xls 时
自测
node scripts/client-smoke.mjs # Client 半边渲染/交互冒烟,含版本徽标与更新面板(不联网)
node scripts/test-ai.mjs # AI 映射输出约束(不联网)
node scripts/test-updater.mjs # 版本比较、安装来源识别、/update-check 与 /update 的拦截分支(不联网、不跑 pnpm)
使用
- 左侧进入「导入成绩」。
- 顶部选学校;下拉里会列出本工作台已绑定的学校 + 「教学数据报告」工作台已绑定的学校
(两处共享,换工作台不用重输账号)。没有要用的学校就点「+ 绑定学校」,填子域名 + 账号密码,
验证通过后账号只存本机(
~/.fuulea-import/bindings.json,0600)。 - 点「加载任务」,列出该账号可见的任务(含作业 / 周考 / 考试等各类,不按类型过滤)。
- 默认只显示最近 90 天(太久远的任务通常不用导),工具栏「时间范围」可切 7 天 / 30 天 / 90 天 / 一年 / 全部; 被隐藏的条数会提示「已隐藏 N 个较早任务」。
- 任务按发布时间倒序,最新的在最前面。
- 任务多时先选科目(下拉只列实际有任务的科目并带数量),再用搜索框按「任务名 / 班级」筛选 (空格分隔多个关键词需全部命中),最后选择目标任务。
- 把成绩文件拖进 ① 区(支持多文件与 zip),点「解析文件」(自动识别姓名列 / 题号列 / 总分列)。
- 依次点 ② 区的「填充模板 → 预检 → 导入 → 核对回执」。
⚠️ 任务列表的三个坑(2026-10-26 实测,test.fuulea.com): ①
GET /v2/tasks/必须带gradeId,不带时静默返回 0 条(不报错,看起来像"没有任务"); ② 不能用type过滤。type是任务来源(1=周考/考试、2=普通任务、6/7=资料…), 不是"作业/考试"的分类标签;用type=2会漏掉周考、考试类任务。工作台一律用type=0取全部; ③ 不能只遍历"某场考试里出现过的学科"。实测任务会落在账号学科之外(账号 subjectId=8 历史, 而考试里只取到 6/7/1/3/2/5),只遍历那几个会漏掉历史任务。 工作台的做法:先不带subjectId拉一遍(账号自己的学科),再按 1..12 逐学科补齐、合并去重。 个别任务在列表接口里连subjectId都不带,这类会再查一次任务详情、用exercise.subjectName补全学科。
导入是后台异步的,接口返回 {} 只代表已提交,唯一可信的“收下”证据是平台消息回执
(工作台「核对回执」步骤自动读取,或看平台右上角铃铛 → 系统消息 → 【云平台通知】导入任务数据)。
⚠️ 回执里出现
学生xxx:N 的分数大于题目分数这类错误,说明源表分值超过了该题满分, 平台会整份拒收。工作台的预检已内置这项校验(见下),会在上传前就拦下,不用等回执。
模板题号:用「叶子题号」,父题不单独占列
实测(test.fuulea.com,taskId 17643391「10月26日任务」):试卷是 3 道单选 + 1 道大题(该大题下挂 2 个子题),
而模板 小题分 表的题号列是 1, 2, 3, 4.1, 4.2 共 5 列 —— 父题 4 本身不占列,只列它的子题 4.1 / 4.2。
因此源表也要按同样的题号列写(1,2,3,4.1,4.2),工作台按题号文本对齐,不按列序号硬对。
⚠️ 题号解析必须支持小数:只认整数会把 5 列数成 3 列,导致题数校验失败、填充报错。 工作台已在 JS(
lib/template.js、lib/parse.js)和 Python(fill_template.py、precheck.py)两侧统一支持1 / 4.1 / 第4.1题 / T4.1。
批量体检(批量体检)
一堆文件里往往只有几个能用。点「批量体检」会逐个判定并说明原因,只把能用的留出来:
| 判定项 | 说明 |
|---|---|
| 格式 | 不是表格文件(如 .txt/.docx)→ 跳过 |
| 姓名列 | 表头没有「姓名」时,会按数据特征推断(该列是文本、每行都有值且互不相同);推断不出才跳过 |
| 题号列 | 没有题号列(只有总分/缺逐题分)→ 跳过 |
| 学生数 | 没有数据行 → 跳过 |
| 名单匹配 | 学生一个都不在模板「班级对照表」里 → 跳过(传了平台也会拒) |
- 支持拖 zip:自动解压展开,与单独拖入的文件自动去重(同名同大小只留一份)。
- 每份可用文件后面有「用这份」,点了它就作为后续「分析题号映射 / 填充」的源文件。
- 体检是只读的:只下载模板用于比对名单,不写任何平台数据。
实测(test 站 17643391,5 份杂文件):1 份可用、4 份跳过, 原因分别为「不是表格文件」「找不到姓名列」「没有题号列」「学生都不在模板的班级对照表里」。
AI 题号映射(分析题号映射)
老师给的成绩表往往很乱:题号写法五花八门、父题子题结构对不上、还夹着无关列。 工作台在填充前会做一次题号映射分析,把「源表题号列」对齐到「模板题号列」。
铁律:AI 只判对应关系,分数一律由程序搬运
- AI 只输出「源表题号 → 模板题号」的对应关系,不产生、不修改任何分值。
- 程序按映射做取值/求和:
copy(一列搬过去)、sum(多列相加,如 4.1+4.2 → 4)。 - 每一条 AI 建议都标
needsConfirm,必须勾选确认后才会填充;未确认时「填充模板」会被拦下。
规则优先,AI 只在歧义时兜底
| 情况 | 处理 |
|---|---|
| 源表与模板题号完全一致 | 直接对应,无需确认 |
| 源表是小题(4.1/4.2)、模板是父题(4) | 程序求和合并,标「小题合并」需确认 |
| 源表只有父题(4)、模板要小题(4.1/4.2) | 不自动填。程序无法还原各小题真实得分,标记「无法精确对应」,由人工处理(不做均摊) |
题组:源表同时有 4 和 4.1/4.2 |
4 是合计列(=4.1+4.2),自动忽略,只把 4.1/4.2 精确对应;会提示「源表的 4 是合计列,已忽略该列」 |
| 规则判不了(写法太怪) | 请 AI 给建议,全部标需确认 |
| 模板有、源表没有的题号 | 明确提示「这些格会留空,平台会整份拒收」 |
采纳的拆分策略:一律不自动填,全部人工确认(用户 2026-10-26 确认)。
题号写法支持(乱文件也能认)
1 / 01 / 4.1 / 第4题 / 4题 / T4 / 一 / 二 / 十二 / 四(1) / 四(2) / 4(1) / 4-1
全部会标准化为 1、4.1 这类统一题号后再比对。中文数字(一~九十九)也支持。
预检做了什么
预检在上传前逐项检查平台会整份拒收的条件,全部通过才建议导入:
| 检查项 | 说明 |
|---|---|
| 空格 | 小题分 表凡有姓名的行,题号格不能为空(空格数必须为 0) |
| 名单一致 | 小题分 里的学生必须都在 班级对照表 里,否则报「学生班级未找到」 |
| 无小题分 | 题号列为 0 时直接判不可传(删行也无效,缺的是源表逐题分) |
| 分值上限 | 任一格分数 > 该题满分即拦下。满分取自任务所绑试卷的 questions[].score,按题号与模板题号列对应 |
分值上限的实测依据(test.fuulea.com,taskId 16401145):试卷 11 道小题 no=1..11,
score 为 1.5×10 + 60 = 75,与 totalScore 一致、与模板题号列一一对应。
传第 1 题 3 分(满分 1.5)时平台回执 {"学生student01":"1 的分数大于题目分数"} —— 整份拒收,
现在预检会直接报「分值超上限 1 格」并列出题号、满分、超限人数。
若任务没绑试卷或满分读不到,预检会跳过分值校验并明确提示(不静默放行,也不阻塞其余检查)。
已实测走通的链路(2026-10-26,测试站 test.fuulea.com)
加载任务 → 下载班级对照表模板 → 解析源成绩表(10人×11题) → 填充 → 预检(空格=0) → 导入 → 核对回执
→ 回执 {"语文-任务16401145-语文.xlsx":"导入学生: 10"},与填充人数逐一相等。
同时验证了拒收识别:分值越界时回执为
{"学生student01":"1 的分数大于题目分数"},工作台会把该条标为「失败」并显示原因,
不会因为接口返回 {} 就误判成功。
配置项(cordis.patch.yml 的 config,全部可省略)
| 项 | 默认 | 说明 |
|---|---|---|
defaultSchool |
lyyz |
首次默认学校子域名 |
bindingsFile |
~/.fuulea-import/bindings.json |
学校绑定存储 |
credentialsFile |
~/.workbuddy/skills/fuulea-teaching-cloud/scripts/.credentials |
兼容旧账号密码文件 |
outputDir |
~/.fuulea-import/output |
填好的模板输出目录 |
pythonPath |
python3 |
python3 可执行文件 |
requestTimeoutMs |
90000 |
单次请求超时 |
repository |
100759/dsh-fuulea-import |
版本检查与一键更新用的 GitHub 仓库(owner/repo) |
checkUpdates |
true |
是否联网检查新版本;false 时完全不发请求,也不允许一键更新 |
数据接口(只读 + 一个写接口)
| 接口 | 用途 |
|---|---|
POST /v2/auth/login/ |
账号密码登录,取 JWT |
GET /v2/tasks/ |
任务列表(必须带 gradeId;用 type=0 取全部,遍历各学科) |
GET /v2/tasks/<taskId>/class-list/ |
下载全通教育「班级对照表」模板(xlsx) |
POST /v2/tasks/import/data/ |
导入成绩(source=qt,后台异步,写操作) |
GET /v2/inbox/trainer/ |
读取导入回执(判断是否真的收下) |
平台 /v2 接口需要 Authorization: jwt <token>、uuid、fl-sec-sign 三个请求头,签名算法是
murmurhash3_x64_128(见 lib/murmur3.js、lib/api.js)。
本机接口(/fuulea-import/api/*)
工作台页面调用的是本机 Host 路由,不是平台接口:
| 接口 | 用途 |
|---|---|
GET defaults |
已绑定学校、默认学校、输出目录、当前版本 |
POST bind / POST unbind |
绑定 / 解绑学校(账号只写本机) |
POST tasks |
列出可导入的任务 |
POST template / POST parse / POST batch-scan / POST analyze |
下模板、解析源表、批量体检、题号映射分析 |
POST fill / POST precheck |
填充模板、预检 |
POST import / POST receipt |
导入、核对回执 |
GET update-check |
检查 GitHub 上是否有新版本(?force=1 跳过缓存) |
POST update |
从 GitHub 重新安装到本机 profile(仅 github: 源安装可用) |
POST open |
用系统默认应用打开输出目录里的文件 |
安全
- 生产站写操作(导入)会真实修改学生成绩;请谨慎执行,只在自己的账号权限范围内操作。
- 学生名单与成绩只在本机处理,不上传对话或共享位置。
- 仓库里不含任何账号、密码、token、学生名单或成绩数据:绑定信息只写在本机
~/.fuulea-import/bindings.json(0600),该文件与输出目录都不在版本控制内。
给同事复用
- 确认本机有 Node ≥ 22.19 和
python3+openpyxl。 dsh plugin --profile <你的 profile> add github:100759/dsh-fuulea-import- 重启 DeepSeek Harness → 左侧「导入成绩」→ 「+ 绑定学校」填自己的学校子域名与账号。 绑定只存在各自机器上,同事之间不共享账号。
- 每个账号只能操作自己权限范围内的任务;
defaultSchool只是首次默认值,可在cordis.patch.yml的 config 里改成自己学校的子域名。 - 之后不用再记安装命令:维护者推了新版本后,页面右上角版本徽标会变红, 点它 →「立即更新」→ 重启 Harness 即可。详见「版本与更新」。
许可证
MIT,见 LICENSE。可自由修改与再分发,保留版权声明即可。
No comments yet. Be the first to write one.