feat: “出问题了”三栏说清问题,交作品时写一句反思,外部提示词至少 8 个字
Deploy / deploy (build, debian, 22) (push) Canceled after 0s
Deploy / deploy (build, debian, 22) (push) Canceled after 0s
- 对话框加“出问题了”:做了什么 / 想看到什么 / 实际看到了什么拼成一句发给 AI; 只说“还是不行”这类的提醒一次,可以直接发 - “交这一版”先弹框问最满意的改动和理由(可以不写),存在 submission_reflection, 批改面板显示为“学生说”,只有老师和作者本人看得到 - 粘贴外部 AI 代码时提示词至少 8 个字,可以把几轮都贴进来 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5bdacc438d
commit
e9127318cb
12 files changed
+351
-63
No files matched your search
@@ -51,6 +51,9 @@ CREATE INDEX IF NOT EXISTS "prompt_message_prompt_level_e461b596" ON "prompt_mes
|
||||
CREATE INDEX IF NOT EXISTS "prompt_message_chain" ON "prompt_message" ("conversation_id", "created", "id", "role", "source", "prompt_level");
|
||||
CREATE INDEX IF NOT EXISTS "prompt_message_submission" ON "prompt_message" ("submission_id", "conversation_id", "created", "source") WHERE "submission_id" IS NOT NULL;
|
||||
|
||||
-- 学生交作品时写的一句反思(最满意的改动和理由);可以不写。删提交时跟着删
|
||||
CREATE TABLE IF NOT EXISTS "submission_reflection" ("submission_id" char(32) NOT NULL PRIMARY KEY REFERENCES "submission_submission" ("id") ON DELETE CASCADE, "content" text NOT NULL, "modified" datetime NOT NULL);
|
||||
|
||||
-- 登录用 JWT:access token 不落库,refresh token 只存 sha256
|
||||
DROP TABLE IF EXISTS "auth_session";
|
||||
CREATE TABLE IF NOT EXISTS "auth_refresh_token" ("token_hash" text NOT NULL PRIMARY KEY, "user_id" bigint NOT NULL REFERENCES "account_user" ("id") ON DELETE CASCADE, "expire_at" datetime NOT NULL);
|
||||
|
||||
@@ -120,6 +120,7 @@ router.post("/", async (c) => {
|
||||
js: z.string().nullable().optional(),
|
||||
prompt: z.string().nullable().optional(),
|
||||
message_id: z.number().int().nullable().optional(), // 关联的 assistant message
|
||||
reflection: z.string().max(1000).nullable().optional(), // 交的时候写的一句反思,可以不写
|
||||
}),
|
||||
)
|
||||
const task = orNotFound(
|
||||
@@ -131,6 +132,8 @@ router.post("/", async (c) => {
|
||||
|
||||
const code = { html: p.html ?? null, css: p.css ?? null, js: p.js ?? null }
|
||||
|
||||
const reflection = p.reflection?.trim() || null
|
||||
|
||||
const { submissionId, newUserMsgId } = tx(() => {
|
||||
let manualAsstMsgId: number | null = null
|
||||
let linkedMsg: MessageRow | null = null
|
||||
@@ -203,6 +206,15 @@ router.post("/", async (c) => {
|
||||
return { submissionId: id, newUserMsgId }
|
||||
})
|
||||
|
||||
// 同一版再交时没写就留着上次写的
|
||||
if (reflection) {
|
||||
run(
|
||||
`INSERT INTO submission_reflection (submission_id, content, modified) VALUES ($id, $content, $ts)
|
||||
ON CONFLICT (submission_id) DO UPDATE SET content = excluded.content, modified = excluded.modified`,
|
||||
{ id: submissionId, content: reflection, ts: now() },
|
||||
)
|
||||
}
|
||||
|
||||
// 只对新增的提示词做层级分类
|
||||
if (
|
||||
newUserMsgId !== null &&
|
||||
@@ -540,7 +552,13 @@ router.get("/:id", (c) => {
|
||||
const id = uuidParam(c, "id")
|
||||
requireView(user, id)
|
||||
const s = orNotFound(get(`${SUBMISSION_SELECT} ${SUBMISSION_FROM} WHERE s.id = $id`, { me: user.id, id }))
|
||||
const out = submissionOut(s)
|
||||
// 反思只给老师和作者本人看
|
||||
const reflection =
|
||||
isStaff(user) || s.user_id === user.id
|
||||
? (get<{ content: string }>(`SELECT content FROM submission_reflection WHERE submission_id = $id`, { id })
|
||||
?.content ?? null)
|
||||
: null
|
||||
const out = { ...submissionOut(s), reflection }
|
||||
// 和同学作品一样:学生给同学打分之前不告诉作者和得分(作品展里的本来就署名)
|
||||
if (!isStaff(user) && s.user_id !== user.id && !out.my_score && !inShowcase(id)) {
|
||||
return c.json({ ...out, userid: null, username: null, score: null })
|
||||
|
||||
@@ -276,6 +276,34 @@ describe("提交与删除", () => {
|
||||
expect(all(`SELECT COUNT(*) AS n FROM submission_submission WHERE id != $id`, { id: otherSub })).toEqual([{ n: 1 }])
|
||||
})
|
||||
|
||||
test("交的时候写的反思:再交没写就保留,只有老师和作者能看,删提交时一起删", async () => {
|
||||
makeUser("stu")
|
||||
makeUser("mate")
|
||||
makeUser("teacher", "admin")
|
||||
const task = makeTask("T")
|
||||
const stu = await client("stu")
|
||||
const edit = await stu("POST", "/api/prompt/versions", { task_id: task, html: "<p>1", css: "", js: "" })
|
||||
const sub = await stu("POST", "/api/submission/", {
|
||||
task_id: task,
|
||||
html: "<p>1",
|
||||
message_id: edit.json.message_id,
|
||||
reflection: " 按钮改成三个,因为五个太乱 ",
|
||||
})
|
||||
const id = sub.json.id
|
||||
await stu("POST", "/api/submission/", { task_id: task, html: "<p>1!", message_id: edit.json.message_id, reflection: "" })
|
||||
expect((await stu("GET", `/api/submission/${id}`)).json.reflection).toBe("按钮改成三个,因为五个太乱")
|
||||
expect((await (await client("teacher"))("GET", `/api/submission/${id}`)).json.reflection).toBe("按钮改成三个,因为五个太乱")
|
||||
|
||||
const mate = await client("mate")
|
||||
await mate("POST", "/api/submission/", { task_id: task, html: "<p>m" })
|
||||
const peer = await mate("GET", `/api/submission/${id}`)
|
||||
expect(peer.status).toBe(200)
|
||||
expect(peer.json.reflection).toBeNull()
|
||||
|
||||
expect((await stu("DELETE", `/api/submission/${id}`)).status).toBe(200)
|
||||
expect(all(`SELECT * FROM submission_reflection`)).toEqual([])
|
||||
})
|
||||
|
||||
test("被评分的提交只有超管能删", async () => {
|
||||
const author = makeUser("author")
|
||||
const other = makeUser("other")
|
||||
|
||||
Reference in new issue
Block a user