ソースを参照

创建了答题卡模块相应的后端api

yourname 6 ヶ月 前
コミット
24d29bb379

+ 195 - 0
server/routes_classroom_data.ts

@@ -0,0 +1,195 @@
+import { Hono } from 'hono'
+import type { Variables, WithAuth } from "./middlewares.ts";
+
+export function createClassroomDataRoutes(withAuth: WithAuth) {
+  const classroomDataRoutes = new Hono<{ Variables: Variables }>()
+  
+  // 获取课堂数据列表
+  classroomDataRoutes.get('/', withAuth, async (c) => {
+    try {
+      const apiClient = c.get('apiClient')
+      
+      const page = Number(c.req.query('page')) || 1
+      const pageSize = Number(c.req.query('pageSize')) || 10
+      const offset = (page - 1) * pageSize
+      
+      const search = c.req.query('search') || ''
+      const classroomId = c.req.query('classroomId')
+      
+      let query = apiClient.database.table('classroom_data')
+        .orderBy('id', 'desc')
+      
+      if (search) {
+        query = query.where('title', 'like', `%${search}%`)
+      }
+      
+      if (classroomId) {
+        query = query.where('classroom_id', classroomId)
+      }
+
+      const total = await query.clone().count()
+      const data = await query.select('*')
+        .limit(pageSize).offset(offset)
+      
+      return c.json({
+        data,
+        pagination: {
+          total: Number(total),
+          current: page,
+          pageSize
+        }
+      })
+    } catch (error) {
+      console.error('获取课堂数据列表失败:', error)
+      return c.json({ error: '获取课堂数据列表失败' }, 500)
+    }
+  })
+  
+  // 获取单个课堂数据详情
+  classroomDataRoutes.get('/:id', withAuth, async (c) => {
+    try {
+      const id = Number(c.req.param('id'))
+      
+      if (!id || isNaN(id)) {
+        return c.json({ error: '无效的课堂数据ID' }, 400)
+      }
+      
+      const apiClient = c.get('apiClient')
+      const data = await apiClient.database.table('classroom_data')
+        .where('id', id)
+        .first()
+      
+      if (!data) {
+        return c.json({ error: '课堂数据不存在' }, 404)
+      }
+      
+      return c.json({
+        data,
+        message: '获取课堂数据详情成功'
+      })
+    } catch (error) {
+      console.error('获取课堂数据详情失败:', error)
+      return c.json({ error: '获取课堂数据详情失败' }, 500)
+    }
+  })
+
+  // 创建课堂数据
+  classroomDataRoutes.post('/', withAuth, async (c) => {
+    try {
+      const apiClient = c.get('apiClient')
+      const body = await c.req.json()
+      
+      // 验证必填字段
+      const { classroom_id, title, content } = body
+      if (!classroom_id || !title || !content) {
+        return c.json({ error: '缺少必要的课堂数据信息' }, 400)
+      }
+
+      // 创建课堂数据
+      const [id] = await apiClient.database.table('classroom_data').insert({
+        classroom_id,
+        title,
+        content,
+        created_at: new Date(),
+        updated_at: new Date()
+      })
+
+      const newData = await apiClient.database.table('classroom_data')
+        .where('id', id)
+        .first()
+
+      return c.json({
+        data: newData,
+        message: '创建课堂数据成功'
+      })
+    } catch (error) {
+      console.error('创建课堂数据失败:', error)
+      return c.json({ error: '创建课堂数据失败' }, 500)
+    }
+  })
+
+  // 更新课堂数据
+  classroomDataRoutes.put('/:id', withAuth, async (c) => {
+    try {
+      const id = Number(c.req.param('id'))
+      if (!id || isNaN(id)) {
+        return c.json({ error: '无效的课堂数据ID' }, 400)
+      }
+
+      const apiClient = c.get('apiClient')
+      const body = await c.req.json()
+      
+      // 验证必填字段
+      const { title, content } = body
+      if (!title || !content) {
+        return c.json({ error: '缺少必要的课堂数据信息' }, 400)
+      }
+
+      // 检查数据是否存在
+      const existingData = await apiClient.database.table('classroom_data')
+        .where('id', id)
+        .first()
+      
+      if (!existingData) {
+        return c.json({ error: '课堂数据不存在' }, 404)
+      }
+
+      // 更新数据
+      await apiClient.database.table('classroom_data')
+        .where('id', id)
+        .update({
+          title,
+          content,
+          updated_at: new Date()
+        })
+
+      const updatedData = await apiClient.database.table('classroom_data')
+        .where('id', id)
+        .first()
+
+      return c.json({
+        data: updatedData,
+        message: '更新课堂数据成功'
+      })
+    } catch (error) {
+      console.error('更新课堂数据失败:', error)
+      return c.json({ error: '更新课堂数据失败' }, 500)
+    }
+  })
+
+  // 删除课堂数据
+  classroomDataRoutes.delete('/:id', withAuth, async (c) => {
+    try {
+      const id = Number(c.req.param('id'))
+      if (!id || isNaN(id)) {
+        return c.json({ error: '无效的课堂数据ID' }, 400)
+      }
+
+      const apiClient = c.get('apiClient')
+      
+      // 检查数据是否存在
+      const existingData = await apiClient.database.table('classroom_data')
+        .where('id', id)
+        .first()
+      
+      if (!existingData) {
+        return c.json({ error: '课堂数据不存在' }, 404)
+      }
+
+      // 删除数据
+      await apiClient.database.table('classroom_data')
+        .where('id', id)
+        .delete()
+
+      return c.json({
+        message: '删除课堂数据成功',
+        id
+      })
+    } catch (error) {
+      console.error('删除课堂数据失败:', error)
+      return c.json({ error: '删除课堂数据失败' }, 500)
+    }
+  })
+
+  return classroomDataRoutes
+}

+ 168 - 0
server/routes_date_notes.ts

@@ -0,0 +1,168 @@
+import { Hono } from 'hono'
+import type { Variables, WithAuth } from "./middlewares.ts";
+
+export function createDateNotesRoutes(withAuth: WithAuth) {
+  const dateNotesRoutes = new Hono<{ Variables: Variables }>()
+
+  // 创建日期笔记
+  dateNotesRoutes.post('/', withAuth, async (c) => {
+    try {
+      const apiClient = c.get('apiClient')
+      const { date, title, content } = await c.req.json()
+
+      if (!date || !title || !content) {
+        return c.json({ error: '缺少必要参数' }, 400)
+      }
+
+      const user = c.get('user')
+      if (!user) return c.json({ error: '未授权访问' }, 401)
+      
+      const [noteId] = await apiClient.database.table('date_notes').insert({
+        date,
+        title,
+        content,
+        user_id: user.id,
+        created_at: apiClient.database.fn.now(),
+        updated_at: apiClient.database.fn.now()
+      })
+
+      return c.json({
+        message: '日期笔记创建成功',
+        data: { id: noteId }
+      }, 201)
+    } catch (error) {
+      console.error('创建日期笔记失败:', error)
+      return c.json({ error: '创建日期笔记失败' }, 500)
+    }
+  })
+
+  // 获取日期笔记列表(支持日期范围查询)
+  dateNotesRoutes.get('/', withAuth, async (c) => {
+    try {
+      const apiClient = c.get('apiClient')
+      
+      const page = Number(c.req.query('page')) || 1
+      const pageSize = Number(c.req.query('pageSize')) || 20
+      const startDate = c.req.query('startDate')
+      const endDate = c.req.query('endDate')
+
+      const user = c.get('user')
+      if (!user) return c.json({ error: '未授权访问' }, 401)
+      
+      const query = apiClient.database.table('date_notes')
+        .where('user_id', user.id)
+        .orderBy('date', 'desc')
+        .limit(pageSize)
+        .offset((page - 1) * pageSize)
+
+      // 日期范围查询
+      if (startDate) query.where('date', '>=', startDate)
+      if (endDate) query.where('date', '<=', endDate)
+
+      const countQuery = query.clone()
+      const notes = await query
+      
+      // 获取总数用于分页
+      const total = await countQuery.count()
+      const totalCount = Number(total)
+      const totalPages = Math.ceil(totalCount / pageSize)
+
+      return c.json({
+        data: notes,
+        pagination: {
+          total: totalCount,
+          current: page,
+          pageSize,
+          totalPages
+        }
+      })
+    } catch (error) {
+      console.error('获取日期笔记列表失败:', error)
+      return c.json({ error: '获取日期笔记列表失败' }, 500)
+    }
+  })
+
+  // 获取单个日期笔记详情
+  dateNotesRoutes.get('/:id', withAuth, async (c) => {
+    try {
+      const apiClient = c.get('apiClient')
+      
+      const noteId = c.req.param('id')
+
+      const user = c.get('user')
+      if (!user) return c.json({ error: '未授权访问' }, 401)
+      
+      const note = await apiClient.database.table('date_notes')
+        .where('id', noteId)
+        .where('user_id', user.id)
+        .first()
+
+      if (!note) {
+        return c.json({ error: '日期笔记不存在或无权访问' }, 404)
+      }
+
+      return c.json({
+        message: '获取日期笔记成功',
+        data: note
+      })
+    } catch (error) {
+      console.error('获取日期笔记详情失败:', error)
+      return c.json({ error: '获取日期笔记详情失败' }, 500)
+    }
+  })
+
+  // 更新日期笔记
+  dateNotesRoutes.put('/:id', withAuth, async (c) => {
+    try {
+      const apiClient = c.get('apiClient')
+      const { date, title, content } = await c.req.json()
+
+      if (!date || !title || !content) {
+        return c.json({ error: '缺少必要参数' }, 400)
+      }
+
+      const user = c.get('user')
+      if (!user) return c.json({ error: '未授权访问' }, 401)
+      
+      const noteId = c.req.param('id')
+
+      await apiClient.database.table('date_notes')
+        .where('id', noteId)
+        .where('user_id', user.id)
+        .update({
+          date,
+          title,
+          content,
+          updated_at: apiClient.database.fn.now()
+        })
+
+      return c.json({ message: '日期笔记更新成功' })
+    } catch (error) {
+      console.error('更新日期笔记失败:', error)
+      return c.json({ error: '更新日期笔记失败' }, 500)
+    }
+  })
+
+  // 删除日期笔记
+  dateNotesRoutes.delete('/:id', withAuth, async (c) => {
+    try {
+      const apiClient = c.get('apiClient')
+      const user = c.get('user')
+      if (!user) return c.json({ error: '未授权访问' }, 401)
+      
+      const noteId = c.req.param('id')
+
+      await apiClient.database.table('date_notes')
+        .where('id', noteId)
+        .where('user_id', user.id)
+        .delete()
+
+      return c.json({ message: '日期笔记已删除' })
+    } catch (error) {
+      console.error('删除日期笔记失败:', error)
+      return c.json({ error: '删除日期笔记失败' }, 500)
+    }
+  })
+
+  return dateNotesRoutes
+}

+ 314 - 0
server/routes_submission_records.ts

@@ -0,0 +1,314 @@
+import { Hono } from "hono";
+import debug from "debug";
+import type {
+  SubmissionRecord,
+} from "../client/share/types_stock.ts";
+import {
+  SubmissionRecordStatus,
+} from "../client/share/types_stock.ts";
+
+import {
+  DeleteStatus,
+  EnableStatus,
+} from "../client/share/types.ts";
+
+import type { Variables, WithAuth } from "./middlewares.ts";
+
+const log = {
+  api: debug("api:submission"),
+};
+
+// 创建提交记录路由
+export function createSubmissionRoutes(withAuth: WithAuth) {
+  const submissionRoutes = new Hono<{ Variables: Variables }>();
+
+  // 创建提交记录
+  submissionRoutes.post("/", withAuth, async (c) => {
+    try {
+      const recordData = (await c.req.json()) as Partial<SubmissionRecord>;
+      const user = c.get("user");
+
+      // 验证必填字段
+      if (!recordData.code || !recordData.training_date) {
+        return c.json({ error: "股票代码和训练日期不能为空" }, 400);
+      }
+
+      const apiClient = c.get('apiClient');
+
+      // 设置提交者信息
+      if (user) {
+        recordData.user_id = user.id;
+        recordData.nickname = user.nickname || user.username;
+      }
+
+      // 设置默认状态
+      recordData.status = SubmissionRecordStatus.PENDING;
+
+      // 插入提交记录
+      const [id] = await apiClient.database.table("submission_records").insert({
+        ...recordData,
+        is_disabled: EnableStatus.ENABLED,
+        is_deleted: DeleteStatus.NOT_DELETED,
+        created_at: apiClient.database.fn.now(),
+        updated_at: apiClient.database.fn.now(),
+      });
+
+      // 获取插入的数据
+      const [insertedRecord] = await apiClient.database
+        .table("submission_records")
+        .where("id", id);
+
+      return c.json({
+        message: "提交记录创建成功",
+        data: insertedRecord,
+      });
+    } catch (error) {
+      log.api("创建提交记录失败:", error);
+      return c.json({ error: "创建提交记录失败" }, 500);
+    }
+  });
+
+  // 获取提交记录列表
+  submissionRoutes.get("/list", withAuth, async (c) => {
+    try {
+      const page = Number(c.req.query("page")) || 1;
+      const pageSize = Number(c.req.query("pageSize")) || 10;
+      const status = c.req.query("status");
+      const keyword = c.req.query("keyword");
+
+      const apiClient = c.get('apiClient');
+      let query = apiClient.database
+        .table("submission_records")
+        .where("is_deleted", DeleteStatus.NOT_DELETED)
+        .orderBy("created_at", "desc");
+
+      // 应用过滤条件
+      if (status) {
+        query = query.where("status", status);
+      }
+
+      if (keyword) {
+        query = query.where((builder) => {
+          builder
+            .where("code", "like", `%${keyword}%`)
+            .orWhere("training_date", "like", `%${keyword}%`);
+        });
+      }
+
+      // 获取总数
+      const total = await query.clone().count();
+
+      // 分页查询
+      const records = await query.limit(pageSize).offset((page - 1) * pageSize);
+
+      return c.json({
+        message: "获取提交记录列表成功",
+        data: {
+          list: records,
+          pagination: {
+            current: page,
+            pageSize,
+            total: Number(total),
+          },
+        },
+      });
+    } catch (error) {
+      log.api("获取提交记录列表失败:", error);
+      return c.json({ error: "获取提交记录列表失败" }, 500);
+    }
+  });
+
+  // 获取单个提交记录
+  submissionRoutes.get("/:id", withAuth, async (c) => {
+    try {
+      const id = Number(c.req.param("id"));
+
+      if (!id || isNaN(id)) {
+        return c.json({ error: "无效的提交记录ID" }, 400);
+      }
+
+      const apiClient = c.get('apiClient');
+      const record = await apiClient.database
+        .table("submission_records")
+        .where("id", id)
+        .where("is_deleted", DeleteStatus.NOT_DELETED)
+        .first();
+
+      if (!record) {
+        return c.json({ error: "提交记录不存在" }, 404);
+      }
+
+      return c.json({
+        message: "获取提交记录成功",
+        data: record,
+      });
+    } catch (error) {
+      log.api("获取提交记录失败:", error);
+      return c.json({ error: "获取提交记录失败" }, 500);
+    }
+  });
+
+  // 更新提交记录
+  submissionRoutes.put("/:id", withAuth, async (c) => {
+    try {
+      const id = Number(c.req.param("id"));
+      const recordData = (await c.req.json()) as Partial<SubmissionRecord>;
+
+      if (!id || isNaN(id)) {
+        return c.json({ error: "无效的提交记录ID" }, 400);
+      }
+
+      const apiClient = c.get('apiClient');
+
+      // 查询记录是否存在
+      const record = await apiClient.database
+        .table("submission_records")
+        .where("id", id)
+        .where("is_deleted", DeleteStatus.NOT_DELETED)
+        .first();
+
+      if (!record) {
+        return c.json({ error: "提交记录不存在" }, 404);
+      }
+
+      // 更新记录
+      await apiClient.database
+        .table("submission_records")
+        .where("id", id)
+        .update({
+          ...recordData,
+          updated_at: apiClient.database.fn.now(),
+        });
+
+      // 获取更新后的数据
+      const [updatedRecord] = await apiClient.database
+        .table("submission_records")
+        .where("id", id);
+
+      return c.json({
+        message: "更新提交记录成功",
+        data: updatedRecord,
+      });
+    } catch (error) {
+      log.api("更新提交记录失败:", error);
+      return c.json({ error: "更新提交记录失败" }, 500);
+    }
+  });
+
+  // 删除提交记录
+  submissionRoutes.delete("/:id", withAuth, async (c) => {
+    try {
+      const id = Number(c.req.param("id"));
+
+      if (!id || isNaN(id)) {
+        return c.json({ error: "无效的提交记录ID" }, 400);
+      }
+
+      const apiClient = c.get('apiClient');
+
+      // 查询记录是否存在
+      const record = await apiClient.database
+        .table("submission_records")
+        .where("id", id)
+        .where("is_deleted", DeleteStatus.NOT_DELETED)
+        .first();
+
+      if (!record) {
+        return c.json({ error: "提交记录不存在" }, 404);
+      }
+
+      // 软删除记录
+      await apiClient.database.table("submission_records").where("id", id).update({
+        is_deleted: DeleteStatus.DELETED,
+        updated_at: apiClient.database.fn.now(),
+      });
+
+      return c.json({
+        message: "提交记录删除成功",
+      });
+    } catch (error) {
+      log.api("删除提交记录失败:", error);
+      return c.json({ error: "删除提交记录失败" }, 500);
+    }
+  });
+
+  // 获取提交状态
+  submissionRoutes.get("/:id/status", withAuth, async (c) => {
+    try {
+      const id = Number(c.req.param("id"));
+
+      if (!id || isNaN(id)) {
+        return c.json({ error: "无效的提交记录ID" }, 400);
+      }
+
+      const apiClient = c.get('apiClient');
+      const record = await apiClient.database
+        .table("submission_records")
+        .where("id", id)
+        .where("is_deleted", DeleteStatus.NOT_DELETED)
+        .select("status")
+        .first();
+
+      if (!record) {
+        return c.json({ error: "提交记录不存在" }, 404);
+      }
+
+      return c.json({
+        message: "获取提交状态成功",
+        data: {
+          status: record.status,
+        },
+      });
+    } catch (error) {
+      log.api("获取提交状态失败:", error);
+      return c.json({ error: "获取提交状态失败" }, 500);
+    }
+  });
+
+  // 更新提交状态
+  submissionRoutes.put("/:id/status", withAuth, async (c) => {
+    try {
+      const id = Number(c.req.param("id"));
+      const { status } = (await c.req.json()) as { status: SubmissionRecordStatus };
+
+      if (!id || isNaN(id)) {
+        return c.json({ error: "无效的提交记录ID" }, 400);
+      }
+
+      if (!status) {
+        return c.json({ error: "状态不能为空" }, 400);
+      }
+
+      const apiClient = c.get('apiClient');
+
+      // 查询记录是否存在
+      const record = await apiClient.database
+        .table("submission_records")
+        .where("id", id)
+        .where("is_deleted", DeleteStatus.NOT_DELETED)
+        .first();
+
+      if (!record) {
+        return c.json({ error: "提交记录不存在" }, 404);
+      }
+
+      // 更新状态
+      await apiClient.database
+        .table("submission_records")
+        .where("id", id)
+        .update({
+          status,
+          updated_at: apiClient.database.fn.now(),
+        });
+
+      return c.json({
+        message: "更新提交状态成功",
+      });
+    } catch (error) {
+      log.api("更新提交状态失败:", error);
+      return c.json({ error: "更新提交状态失败" }, 500);
+    }
+  });
+
+  return submissionRoutes;
+}

+ 305 - 0
server/routes_xunlian_codes.ts

@@ -0,0 +1,305 @@
+import { Hono } from 'hono'
+import type { Variables, WithAuth } from "./middlewares.ts";
+
+export function createXunlianCodesRoutes(withAuth: WithAuth) {
+  const xunlianCodesRoutes = new Hono<{ Variables: Variables }>()
+  
+  // 获取训练码列表
+  xunlianCodesRoutes.get('/', withAuth, async (c) => {
+    try {
+      const apiClient = c.get('apiClient')
+      
+      const page = Number(c.req.query('page')) || 1
+      const pageSize = Number(c.req.query('pageSize')) || 10
+      const offset = (page - 1) * pageSize
+      
+      const search = c.req.query('search') || ''
+      
+      let query = apiClient.database.table('xunlian_codes')
+        .orderBy('id', 'desc')
+      
+      if (search) {
+        query = query.where('code', 'like', `%${search}%`)
+      }
+      
+      const total = await query.clone().count()
+      const codes = await query.select('id', 'code', 'status', 'created_at', 'updated_at')
+        .limit(pageSize).offset(offset)
+      
+      return c.json({
+        data: codes,
+        pagination: {
+          total: Number(total),
+          current: page,
+          pageSize
+        }
+      })
+    } catch (error) {
+      console.error('获取训练码列表失败:', error)
+      return c.json({ error: '获取训练码列表失败' }, 500)
+    }
+  })
+  
+  // 获取单个训练码详情
+  xunlianCodesRoutes.get('/:id', withAuth, async (c) => {
+    try {
+      const id = Number(c.req.param('id'))
+      
+      if (!id || isNaN(id)) {
+        return c.json({ error: '无效的训练码ID' }, 400)
+      }
+      
+      const apiClient = c.get('apiClient')
+      const code = await apiClient.database.table('xunlian_codes')
+        .where('id', id)
+        .select('id', 'code', 'status', 'created_at', 'updated_at')
+        .first()
+      
+      if (!code) {
+        return c.json({ error: '训练码不存在' }, 404)
+      }
+      
+      return c.json({
+        data: code,
+        message: '获取训练码详情成功'
+      })
+    } catch (error) {
+      console.error('获取训练码详情失败:', error)
+      return c.json({ error: '获取训练码详情失败' }, 500)
+    }
+  })
+
+  // 创建训练码
+  xunlianCodesRoutes.post('/', withAuth, async (c) => {
+    try {
+      const apiClient = c.get('apiClient')
+      const body = await c.req.json()
+      
+      // 验证必填字段
+      const { code } = body
+      if (!code) {
+        return c.json({ error: '缺少训练码' }, 400)
+      }
+
+      // 检查训练码是否已存在
+      const existingCode = await apiClient.database.table('xunlian_codes')
+        .where('code', code)
+        .first()
+      
+      if (existingCode) {
+        return c.json({ error: '训练码已存在' }, 400)
+      }
+
+      // 创建训练码
+      const [id] = await apiClient.database.table('xunlian_codes').insert({
+        code,
+        status: 'active', // 默认激活状态
+        created_at: new Date(),
+        updated_at: new Date()
+      })
+
+      const newCode = await apiClient.database.table('xunlian_codes')
+        .where('id', id)
+        .select('id', 'code', 'status', 'created_at')
+        .first()
+
+      return c.json({
+        data: newCode,
+        message: '创建训练码成功'
+      })
+    } catch (error) {
+      console.error('创建训练码失败:', error)
+      return c.json({ error: '创建训练码失败' }, 500)
+    }
+  })
+
+  // 更新训练码
+  xunlianCodesRoutes.put('/:id', withAuth, async (c) => {
+    try {
+      const id = Number(c.req.param('id'))
+      if (!id || isNaN(id)) {
+        return c.json({ error: '无效的训练码ID' }, 400)
+      }
+
+      const apiClient = c.get('apiClient')
+      const body = await c.req.json()
+      
+      // 验证必填字段
+      const { code } = body
+      if (!code) {
+        return c.json({ error: '缺少训练码' }, 400)
+      }
+
+      // 检查训练码是否存在
+      const existingCode = await apiClient.database.table('xunlian_codes')
+        .where('id', id)
+        .first()
+      
+      if (!existingCode) {
+        return c.json({ error: '训练码不存在' }, 404)
+      }
+
+      // 如果修改了训练码,检查新训练码是否已被使用
+      if (code !== existingCode.code) {
+        const codeWithSameName = await apiClient.database.table('xunlian_codes')
+          .where('code', code)
+          .whereNot('id', id.toString())
+          .first()
+        
+        if (codeWithSameName) {
+          return c.json({ error: '训练码已存在' }, 400)
+        }
+      }
+
+      // 更新训练码信息
+      const updateData = {
+        code,
+        updated_at: new Date()
+      }
+
+      await apiClient.database.table('xunlian_codes')
+        .where('id', id)
+        .update(updateData)
+
+      const updatedCode = await apiClient.database.table('xunlian_codes')
+        .where('id', id)
+        .select('id', 'code', 'status', 'created_at')
+        .first()
+
+      return c.json({
+        data: updatedCode,
+        message: '更新训练码成功'
+      })
+    } catch (error) {
+      console.error('更新训练码失败:', error)
+      return c.json({ error: '更新训练码失败' }, 500)
+    }
+  })
+
+  // 删除训练码
+  xunlianCodesRoutes.delete('/:id', withAuth, async (c) => {
+    try {
+      const id = Number(c.req.param('id'))
+      if (!id || isNaN(id)) {
+        return c.json({ error: '无效的训练码ID' }, 400)
+      }
+
+      const apiClient = c.get('apiClient')
+      
+      // 检查训练码是否存在
+      const existingCode = await apiClient.database.table('xunlian_codes')
+        .where('id', id)
+        .first()
+      
+      if (!existingCode) {
+        return c.json({ error: '训练码不存在' }, 404)
+      }
+
+      // 删除训练码
+      await apiClient.database.table('xunlian_codes')
+        .where('id', id)
+        .delete()
+
+      return c.json({
+        message: '删除训练码成功',
+        id
+      })
+    } catch (error) {
+      console.error('删除训练码失败:', error)
+      return c.json({ error: '删除训练码失败' }, 500)
+    }
+  })
+
+  // 批量生成训练码
+  xunlianCodesRoutes.post('/batch-generate', withAuth, async (c) => {
+    try {
+      const apiClient = c.get('apiClient')
+      const body = await c.req.json()
+      
+      const { count, prefix } = body
+      if (!count || count <= 0) {
+        return c.json({ error: '无效的生成数量' }, 400)
+      }
+
+      const codes = []
+      const now = new Date()
+      
+      for (let i = 0; i < count; i++) {
+        const code = prefix ? `${prefix}-${generateRandomCode(8)}` : generateRandomCode(10)
+        codes.push({
+          code,
+          status: 'active',
+          created_at: now,
+          updated_at: now
+        })
+      }
+
+      // 批量插入
+      await apiClient.database.table('xunlian_codes').insert(codes)
+
+      return c.json({
+        message: `成功生成${count}个训练码`,
+        count
+      })
+    } catch (error) {
+      console.error('批量生成训练码失败:', error)
+      return c.json({ error: '批量生成训练码失败' }, 500)
+    }
+  })
+
+  // 更新训练码状态
+  xunlianCodesRoutes.put('/:id/status', withAuth, async (c) => {
+    try {
+      const id = Number(c.req.param('id'))
+      if (!id || isNaN(id)) {
+        return c.json({ error: '无效的训练码ID' }, 400)
+      }
+
+      const apiClient = c.get('apiClient')
+      const body = await c.req.json()
+      
+      const { status } = body
+      if (!status || !['active', 'inactive', 'used'].includes(status)) {
+        return c.json({ error: '无效的状态值' }, 400)
+      }
+
+      // 检查训练码是否存在
+      const existingCode = await apiClient.database.table('xunlian_codes')
+        .where('id', id)
+        .first()
+      
+      if (!existingCode) {
+        return c.json({ error: '训练码不存在' }, 404)
+      }
+
+      // 更新状态
+      await apiClient.database.table('xunlian_codes')
+        .where('id', id)
+        .update({
+          status,
+          updated_at: new Date()
+        })
+
+      return c.json({
+        message: '更新训练码状态成功',
+        id,
+        status
+      })
+    } catch (error) {
+      console.error('更新训练码状态失败:', error)
+      return c.json({ error: '更新训练码状态失败' }, 500)
+    }
+  })
+
+  return xunlianCodesRoutes
+}
+
+// 生成随机训练码
+function generateRandomCode(length: number) {
+  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
+  let result = ''
+  for (let i = 0; i < length; i++) {
+    result += chars.charAt(Math.floor(Math.random() * chars.length))
+  }
+  return result
+}

+ 1 - 0
版本迭代需求.md

@@ -1,5 +1,6 @@
 2025.05.15 0.1.1
 
+创建了答题卡模块相应的后端api
 答题功能现已完全集成到移动端应用,可通过导航菜单访问所有相关功能
 答题卡模块socket.io重构,创建了routes_io_exam.ts路由文件,实现了考试相关socket事件处理
 迁移入答题卡页面