项目 6 数据存储:仿冒 SqlClient(T-SQL 转写成 SQLite)、数据库页面、DataGridView、DateTimePicker
- 每个学生一套数据库,长期保存(data/databases/<用户编号>/库名.db);学生程序运行时挂进沙箱可读写 - Sim.Data:SqlConnection / SqlCommand / SqlDataReader / SqlDataAdapter / SqlCommandBuilder / SqlParameter; 连接字符串只按库名打开学生自己的库;ScriptDom 解析 T-SQL 后按白名单转写成 SQLite 语句, STRICT 表 + 元数据表还原 SQL Server 的列类型,报错编号和中文信息与 SQL Server 一致; INSERT 不写列名时跳过自增列,日期统一格式(书上 DateTime.ToString() 拼接的日期能正确比较) - 常驻 SQL 服务(Sim.Runner sql-server):数据库页面的查询、对象资源管理器,评分前按实验 database.sql 建库 - 数据库页面(代替 SSMS):对象资源管理器、查询窗口、编辑前 200 行、表设计器、恢复实验初始数据 - DataGridView(绑定 DataView/DataTable/DataSet,单元格修改写回 DataTable)、DateTimePicker;设计器工具箱加入 - 评分:每个检查项一份新建的实验库;新增点 / 修改表格单元格、选日期、表格里有 / 没有某行的检查 - 示例:项目 6 任务 2、3;时间格式改成和中文 Windows 一致(9:05:03);异常堆栈不暴露服务器路径 - 测试:数据库端到端、接口(每人只看到自己的库、恢复实验数据)、评分出错情况、T-SQL 生成 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6b088240da
commit
670abe5921
72 files changed
+7253
-51
No files matched your search
+9
-4
@@ -7,7 +7,9 @@ import { languageService } from './language-service'
|
||||
import { config } from './config'
|
||||
import { openDatabase } from './db'
|
||||
import { createGradingQueue } from './grading/queue'
|
||||
import { userDatabaseDir } from './databases'
|
||||
import { cleanupStaleRuns, RunSession } from './run-session'
|
||||
import { sqlService } from './sql-service'
|
||||
import { checkSandbox, sandboxError } from './sandbox'
|
||||
|
||||
await cleanupStaleRuns()
|
||||
@@ -20,6 +22,7 @@ const aiQueue = createAiQueue(db)
|
||||
const app = createApp(db, createGradingQueue(db, (id) => aiQueue.enqueue(id)), aiQueue)
|
||||
|
||||
interface SocketData {
|
||||
userId?: number
|
||||
session?: RunSession
|
||||
}
|
||||
|
||||
@@ -29,8 +32,9 @@ const server = Bun.serve({
|
||||
const url = new URL(req.url)
|
||||
if (url.pathname === '/ws') {
|
||||
// 浏览器的 WebSocket 不能带请求头,令牌放在网址参数里
|
||||
if (!(await tokenUser(db, url.searchParams.get('token')))) return new Response('请先登录', { status: 401 })
|
||||
if (server.upgrade(req, { data: {} })) return
|
||||
const user = await tokenUser(db, url.searchParams.get('token'))
|
||||
if (!user) return new Response('请先登录', { status: 401 })
|
||||
if (server.upgrade(req, { data: { userId: user.id } })) return
|
||||
return new Response('WebSocket 升级失败', { status: 400 })
|
||||
}
|
||||
return app.fetch(req, server)
|
||||
@@ -38,7 +42,8 @@ const server = Bun.serve({
|
||||
websocket: {
|
||||
data: {} as SocketData,
|
||||
open(ws) {
|
||||
ws.data.session = new RunSession((message) => ws.send(JSON.stringify(message)))
|
||||
const userId = ws.data.userId!
|
||||
ws.data.session = new RunSession((message) => ws.send(JSON.stringify(message)), () => userDatabaseDir(userId))
|
||||
},
|
||||
async message(ws, raw) {
|
||||
let message: ClientMessage
|
||||
@@ -72,5 +77,5 @@ const server = Bun.serve({
|
||||
})
|
||||
|
||||
console.log(`服务器已启动:http://localhost:${server.port}`)
|
||||
await Promise.all([compileService.start(), languageService.start()])
|
||||
await Promise.all([compileService.start(), languageService.start(), sqlService.start()])
|
||||
console.log('C# 编译服务已就绪')
|
||||
Reference in new issue
Block a user