跳转至

Excel 文件自动判分功能 · 技术设计文档

适用场景:信息技术上机练习 / 作业 / 可信局域网环境的 Excel 操作题自动判分(计算机二级风格)。 定位:在现有「局域网答题」平台(FastAPI + SQLite + 原生 JS)上新增一个独立的 Excel 判分模块,复用其账号 / 房间 / Web 框架,但判分逻辑自包含。 本文档同时面向开发者(架构、数据结构、接口、测试用例)与非技术人员(功能说明、用例),第 1 节与第 5 节偏业务,其余偏技术。


0. 关键澄清与默认设计(先对齐 5 个问题)

# 澄清点 本文采用的设计
1 判分对象 ① 学生提交的 Excel 文件(作业/练习);② 教师提交的标准答案 Excel,用于预先提取考点规则。两者都是普通 .xls/.xlsx。支持「单文件多 Sheet 判分」与「批量多文件判分(整班)」。
2 判分规则 四类全支持:固定单元格值比对 / 公式文本校验 / 公式结果校验 / 格式·图表·批注等。规则可由教师自由组合并编排进对应「小题」(一道小题 = 多条规则,多条小题 = 一份试卷),每条规则配分值,支持部分得分。
3 输入/输出 输入:多个 Excel 文件(批量)或单个文件内多个 Sheet。输出:总分汇总表(每生总分)+ 逐文件逐规则明细(每条规则 对/错、期望/实际)+ 错误项标注(定位到单元格/工作表)+ 导出 CSV
4 技术环境 前端:原生 HTML/JS + ExcelJS/SheetJS(读文件)+ HyperFormula(算公式结果)。后端:Python(FastAPI + SQLite + pandas 导出)。必须 Web 界面;兼容 .xls.xlsx
5 文档读者 开发者看第 2–6 节(架构/数据结构/接口/测试);非技术看第 1、5 节(功能与用例)。

判分执行位置(最重要技术决策):本场景为练习 / 作业 / 可信局域网环境,采用 「前端判分」 架构——判分引擎跑在学生浏览器本机,交卷即出分,不依赖服务器算力,服务端也无需安装 MS Office / WPS / LibreOffice。后端(FastAPI+SQLite)只负责存规则、收文件、存成绩、出汇总信任前端回传的分数(可信环境,不防蓄意造假)。 公式结果计算:浏览器端用 HyperFormula 直接按 Excel 语义计算公式结果(等效于把「LibreOffice 无头重算」搬到前端,且无进程管理负担);读文件用 ExcelJS(样式/公式齐全)+ SheetJS(兼容 .xls 读值)。这套 JS 引擎也可在 Node 后端复用,作为将来若需「权威重判」时的同一份真相源(当前可信场景不启用)。


1. 功能需求(非技术人员也能看懂)

1.1 背景

信息技术上机练习、计算机二级都有大量 Excel 操作题(求和、函数、格式、图表、透视表)。传统人工阅卷慢且易错,需要一个提交即判分的系统:学生交 Excel 文件,系统对照教师预设的考点规则,逐条核对,自动算出分数并指出错在哪。本方案面向练习/作业/可信局域网,学生本机算分、即时反馈,服务器只做存档与汇总。

1.2 角色与用例

角色 核心用例
教师(出题人) 上传标准答案 Excel → 前端自动提取候选考点,或教师手工录入/勾选规则 → 把规则编排成若干「小题」并定分 → 发布判分任务
学生 在 Web 页上传自己的 Excel 作业(支持批量:整班一次交多份)→ 本机立即判分,看到总分与逐条明细、错在哪
教务/管理员 查看整班成绩汇总、导出 CSV、可下载学生原文件复核

1.3 功能清单(FR)

  • FR1 标准答案规则提取:上传标准答案 .xlsx/.xls,前端解析后给出可勾选的考点候选(单元格值、公式、格式等),教师确认即生成规则库。
  • FR2 规则定义与组合:支持单元格值、公式文本、公式结果、数字格式、字体、填充、边框、对齐、工作表名/隐藏、图表、透视表、批注等类型;教师可改期望值、运算符、分值、容差;多条规则组合进一个「小题」。
  • FR3 文件提交:单文件多 Sheet、批量多文件(按文件名/学号匹配学生)两种入口;前端即时判分
  • FR4 前端自动判分引擎:按规则逐条读取学生文件并校验,支持数值容差、文本包含/正则、部分得分;公式结果由 HyperFormula 计算。
  • FR5 结果输出:总分汇总、逐文件逐规则明细、错误项标注(单元格级)、导出 CSV/打印;教师可下载学生原文件复核。
  • FR6 格式兼容.xls.xlsx 均支持(.xls 经 SheetJS 读值/公式,样式题建议用 .xlsx)。
  • FR7 Web 界面:原生 HTML/JS,无需安装客户端。

1.4 非功能需求(NFR)

  • 算力:判分在客户端完成,服务端几乎零计算压力,天然支持整班并发提交。
  • 跨平台:后端 Python 可跑 Linux/Windows;前端依赖现代浏览器(File API、可选 Web Worker)。
  • 精度:公式结果以 HyperFormula 计算为准;与微软官方存在细微差异时,在配置中标注。
  • 安全:后端仍做文件类型/大小校验;可信环境不校验收到的分数,但存原始文件供教师抽查复核。

2. 总体架构

2.1 分层(判分在客户端,后端只存)

学生浏览器(本机判分,零服务器算力)
┌──────────────────────────────────────────┐
│  Web 前端(原生 HTML/JS)                    │
│   · 教师:定义/编排规则(规则存后端)          │
│   · 学生:上传 Excel → 前端判分引擎           │
│        ExcelJS / SheetJS 读文件              │
│        HyperFormula 算公式结果               │
│        逐规则校验 + 计分(即时反馈)          │
│        POST { 原始文件, 总分, 逐规则明细 }     │
└───────────────────┬──────────────────────┘
                    │ HTTP / JSON (FastAPI, Python)
┌───────────────────▼──────────────────────┐
│  后端服务层(FastAPI + SQLite)              │
│   · 规则 CRUD / 下发                         │
│   · 接收提交:文件落盘 + 成绩入库(不重算)     │
│   · 汇总 / 导出 CSV(pandas)                │
│   ※ 信任前端回传分数(可信局域网场景)          │
└───────────────┬──────────────────────────┘
┌───────────────▼──────────────────────────┐
│  存储层(SQLite,复用现有)                   │
│  exam / question / rule /                  │
│  submission / grade / grade_detail          │
│  文件:学生原始 Excel(磁盘目录)              │
└──────────────────────────────────────────┘

2.2 判分主流程

教师:上传标准答案
   └─> [前端解析] 提取单元格/公式/格式候选
          └─> [教师确认/编排] 规则存后端(excel_rule,挂到小题→试卷)
学生:打开提交页 → 拉取本题规则 → 选 Excel 上传
   └─> [前端] ExcelJS/SheetJS 读文件 + HyperFormula 算结果
          └─> [前端判分引擎] 逐规则校验、计分(即时看到对错与总分)
                 └─> POST 原始文件 + 总分 + 逐规则明细 到后端
后端:存文件 + 写 grade/grade_detail + 汇总
教师:结果报表看整班汇总、导出 CSV、可下载学生原文件复核

2.3 前端公式计算策略(HyperFormula)

环节 实现 说明
读文件 ExcelJS.xlsx 样式/公式齐全)/ SheetJS.xls 读值+公式文本) 浏览器内纯 JS,无需上传即可解析
算公式结果 HyperFormula 按 Excel 语义重算整表,拿到真实 .Value,规避 openpyxl「只读到缓存值」的坑
降级 无 HyperFormula 覆盖的函数 该类规则改判「公式文本比对」(检查函数名/$ 绝对引用)并提示

后端不参与公式重算;因此服务端完全不依赖 Office/LibreOffice,也不存在 COM 进程泄漏问题。


3. 数据结构

3.1 SQLite 表(复用现有连接与账号/房间隔离)

-- 判分任务(一份试卷/一次作业)
CREATE TABLE excel_exam (
  id         INTEGER PRIMARY KEY,
  title      TEXT NOT NULL,
  desc       TEXT,
  school_id  INTEGER,          -- 复用多学校隔离
  room_code  TEXT,             -- 复用房间隔离(可选)
  created_by INTEGER,
  status     TEXT DEFAULT 'draft',  -- draft|published|closed
  created_at TEXT
);

-- 小题(可挂父题做材料/大题)
CREATE TABLE excel_question (
  id       INTEGER PRIMARY KEY,
  exam_id  INTEGER,
  parent_id INTEGER,           -- 材料大题头行
  no       INTEGER,            -- 小题序号
  title    TEXT,
  score    REAL                -- 小题总分 = 其下规则分之和(冗余存储)
);

-- 考点规则(判分最小单元;后端存储、前端拉取执行)
CREATE TABLE excel_rule (
  id          INTEGER PRIMARY KEY,
  exam_id     INTEGER,
  qid         INTEGER,         -- 所属小题
  sheet       TEXT,            -- 工作表名;"*" 表示任意/全部表
  addr        TEXT,            -- 单元格或区域,如 D3 / A2:A10
  target      TEXT,            -- cell|range|sheet|workbook
  check       TEXT,            -- 见 3.2 类型枚举
  expected    TEXT,            -- 期望值(JSON 字符串,按 check 类型解释)
  op          TEXT DEFAULT 'eq', -- eq|ne|contains|regex|in|gte|lte
  score       REAL,
  tolerance   REAL DEFAULT 0,  -- 数值容差
  must_recalc INTEGER DEFAULT 0,-- 1=依赖公式结果(前端用 HyperFormula 算)
  desc        TEXT,            -- 考点说明(展示给学生)
  order_no    INTEGER
);

-- 学生提交(一次提交 = 一份文件;成绩来自前端回传)
CREATE TABLE excel_submission (
  id          INTEGER PRIMARY KEY,
  exam_id     INTEGER,
  user_id     INTEGER,         -- 复用账号体系
  filename    TEXT,
  raw_path    TEXT,            -- 学生原始文件(落盘,供复核)
  status      TEXT DEFAULT 'graded',
  submitted_at TEXT
);

-- 判分结果(一次提交一场;total/passed 来自前端回传,后端存证)
CREATE TABLE excel_grade (
  id           INTEGER PRIMARY KEY,
  submission_id INTEGER,
  total        REAL,           -- 前端回传总分
  passed       INTEGER,        -- 命中规则数
  total_rules  INTEGER,
  created_at   TEXT
);

-- 逐规则明细(错误标注来源;前端回传,后端存证)
CREATE TABLE excel_grade_detail (
  id           INTEGER PRIMARY KEY,
  grade_id     INTEGER,
  rule_id      INTEGER,
  qid          INTEGER,
  pass         INTEGER,
  actual       TEXT,           -- 学生文件实际值(JSON)
  expected     TEXT,           -- 期望(冗余,便于展示)
  score_got    REAL,
  note         TEXT             -- 错误标注,如 “D3 期望 SUMIFS,实际为空”
);

3.2 规则 JSON Schema(核心,前端引擎直接消费)

{
  "rule_id": "r001",
  "qid": 1,
  "sheet": "销售汇总",
  "addr": "D3",
  "target": "cell",
  "check": "formula_text",
  "expected": "=SUMIFS($B$2:$B$100,C2:C100,\"已完成\")",
  "op": "contains",
  "score": 2,
  "tolerance": 0,
  "must_recalc": false,
  "desc": "D3 使用 SUMIFS 按状态汇总已完成金额"
}

check 类型枚举与取值:

check 读取内容 expected 解释
cell_value 单元格值 文本/数值;op 支持 eq/contains/regex/gte/lte;数值带 tolerance
formula_text 公式源码 文本;op contains 可查函数名/$ 绝对引用
formula_result HyperFormula 计算结果 cell_value,但 must_recalc=1
number_format 数字格式串 0.00%yyyy/mm/dd
font 字体对象 JSON:{name,size,bold,color} 逐字段比对
fill 填充色 JSON:{color}
border 边框 JSON:{style,color}(四边)
alignment 对齐 JSON:{horizontal,vertical,wrap}
sheet_name 工作表名 文本 eq/contains
sheet_hidden 隐藏状态 bool
chart 图表 JSON:{type,data_range}
pivot 透视表 JSON:{fields,rows}(ExcelJS 解析能力有限,部分属性降级)
comment 批注 文本(批注内容)

3.3 文件存储

  • 学生原始文件:uploads/excel/{exam_id}/{user_id}_{sub_id}_{filename}(落盘供教师复核;前端判分用同一份文件,无需服务端再读)。

4. 接口设计

4.1 页面路由

页面 路由 说明
判分工作台(教师) /excel 建考试、传标准答案、编排规则、看汇总
学生提交页 /excel/submit?exam=ID 上传作业(单/批量),前端即时判分
结果报表 /excel/result?exam=ID 汇总 + 逐生明细 + 导出 + 下载原文件复核

4.2 REST API

方法 路径 说明
POST /api/excel/exams 新建判分任务(标题/描述)
POST /api/excel/exams/{id}/answer 上传标准答案文件(后端仅落盘存档;候选考点由前端解析供教师勾选)
PUT /api/excel/exams/{id}/rules 保存规则库(教师确认/编排后的完整 JSON,前端组装后回传)
GET /api/excel/exams/{id}/rules 学生端拉取本题规则用于本机判分
POST /api/excel/submit 学生提交:multipart 文件 + JSON {total, details:[{rule_id,pass,actual,expected,score_got,note}]};后端只存不重算
GET /api/excel/grade/{submission_id} 单份明细
GET /api/excel/exams/{id}/summary 整班汇总(每生总分、各小题均分)
GET /api/excel/exams/{id}/export?fmt=csv 导出成绩 CSV(pandas)

信任模型/api/excel/submit 回传的 total/details 直接入库作为正式成绩(可信局域网/练习场景)。后端仅做文件落盘与结构校验;如需将来升级为「考试防作弊」,可改为后端用同一 JS 引擎(Node)重判,接口不变。

4.3 前端判分引擎接口(浏览器内 JS 模块)

// 主入口:读文件 → 逐规则校验 → 返回总分与明细
async function gradeWorkbook(file, rules) {
  const wb = await readWorkbook(file);      // ExcelJS / SheetJS
  const hf = buildHyperFormula(wb);          // 建计算模型,拿公式结果
  const details = rules.map(r => checkRule(wb, hf, r));
  const total = details.reduce((s, d) => s + d.score_got, 0);
  return { total, details };
}

// 单条规则校验
function checkRule(wb, hf, rule) {
  // 定位 sheet/addr → 按 check 取 actual → 按 op/tolerance 比对 expected
  // 返回 { pass, actual, score_got, note }
}

// 公式结果:HyperFormula 计算(替代 LibreOffice 重算)
function cellResult(hf, sheet, addr) { return hf.getCellValue(sheet, addr); }

同一套 gradeWorkbook / checkRule 逻辑若日后需在服务端跑(权威重判),可原样迁移到 Node,实现前后端单一真相源。


5. 判分引擎详细设计(业务 + 技术)

5.1 逐规则校验逻辑(运行在浏览器)

  1. 定位工作表:按 sheet(支持 * 通配 / 按序号);找不到表 → 该规则判 0 分并标注「工作表缺失」。
  2. 读取目标:按 target/addr 用 ExcelJS 读值或公式或样式对象;formula_result 走 HyperFormula 取计算值。
  3. 比较:按 op 比较 actualexpected
  4. 数值:abs(actual-expected) <= tolerance 视为相等;
  5. 文本:eq/contains/regex/in
  6. 公式文本:op=contains 常用于「是否用了 SUMIFS」「是否有 $ 绝对引用」。
  7. 得分:pass 则该规则得 score,否则 0;小题分 = Σ规则分;总分 = Σ小题分。
  8. 错误标注:写 note,如「D3 期望含 SUMIFS,实际为空」「字体期望微软雅黑,实际宋体」。

5.2 容错与降级

  • 文件损坏/打不开:前端提示,不允许提交。
  • HyperFormula 不支持的某函数:该 formula_result 规则降级为 formula_text 比对并提示。
  • .xls 样式不全:格式类规则在 .xls 下可能读不到,建议格式题要求 .xlsx,或在规则设计时为 .xls 提供降级(仅校验值/公式文本)。

5.3 批量匹配

  • 批量上传时按文件名解析学号/姓名(如 20260001_张三.xlsx),匹配账号;匹配不上则按文件名展示,教师可事后补认领。

6. 测试用例

6.1 单元测试(各规则类型,用样例文件,浏览器内跑)

用例 输入 期望
单元格值相等 D3=1256.32,规则 cell_value eq 1256.32 pass
数值容差 D3=1256.3,tolerance=0.1 pass
公式文本 D3==SUM($B$2:$B$9),规则 contains SUM 且 contains $ pass
公式结果 HyperFormula 算得 D3=1256.32,规则 formula_result eq 1256.32 pass
字体 A1 微软雅黑14加粗,规则 font 匹配 pass
数字格式 D3 0.00%,规则匹配 pass
图表 存在簇状柱形图数据源 A2:C10 pass
工作表名 表名「销售汇总」 pass
否定 实际为空,期望非空 fail,note 标注

6.2 集成测试(端到端)

  1. 教师上传标准答案 → 前端返回候选考点 → 教师勾选 5 条规则编排成 2 小题(小题1=3分,小题2=4分)→ 发布。
  2. 学生 A 上传全对文件 → 前端判分总分 7,明细全 pass,POST 后端。
  3. 学生 B 上传部分错文件(1 条值错、1 条格式错) → 前端总分 4.5,明细标注错项与单元格,POST 后端。
  4. 教师结果报表:汇总显示 A=7、B=4.5,可下载两人原文件复核。
  5. 导出 CSV → 含 学号/姓名/总分/各小题分。
  6. .xls 文件提交 → SheetJS 读值正常判分(样式题按降级处理)。

6.3 二级样题示例

  • 样题:在「销售汇总」表 D3 用 SUMIFS 按状态汇总「已完成」金额,要求绝对引用,A1 设为微软雅黑 14 加粗,插入簇状柱形图数据源 A2:C10。
  • 规则formula_text contains SUMIFSformula_text contains $font {name:微软雅黑,size:14,bold:true}chart {type:柱形图,range:A2:C10}
  • 判分:前端逐条核对,满分如 6 分,缺图表扣 2 分并标注「未检测到源 A2:C10 的柱形图」。

7. 风险与注意事项(前端判分版)

  1. 信任前端分数:可信局域网/练习可接受;若升级为正式考试,必须后端权威重判(预留 Node 复用同一引擎),否则分数可被伪造。
  2. HyperFormula 公式覆盖率:与微软 Excel 存在细微差异(尤其财务/数组函数),复杂题需实测;其优势是直接计算,规避 openpyxl「缓存值空」问题。
  3. .xls 样式:SheetJS 社区版读 .xls 样式不全,格式类考点建议要求 .xlsx;值/公式文本可用。
  4. 浏览器兼容与性能:需现代浏览器;大 Excel 解析可能卡顿,可用 Web Worker 放到后台线程避免界面冻结。
  5. 文件落盘:后端仍做类型/大小校验并保存原文件,供教师抽查复核(虽不自动重判,但保留证据)。
  6. 图表/透视表解析:ExcelJS 对图表、透视表的读取能力有限,相关考点设计时需实测,必要时降级为部分分或仅校验存在性。

8. 后续规划(可选)

  • 标准答案「一键提取 + 教师微调」降低出题成本(前端解析候选)。
  • 规则模板市场(二级常考考点预置模板)。
  • 与现有「整卷测验」打通:Excel 判分作为整卷里的一种题型。
  • 若需防作弊升级:后端起 Node 服务复用同一 JS 引擎做权威重判,接口不变。

本文档为设计稿,已按「练习/作业/可信局域网」场景定为前端判分、后端只存。编码前请就第 0 节的 5 项默认设计进行评审确认。