设备申请模块 — 后端和数据库实现步骤指南
| 说明 |
内容 |
| 文档版本 |
v1.0 |
| 适用对象 |
具备基础 SQL、Python、FastAPI 知识 |
| 任务范围 |
仅后端 + 数据库;前端交给 AI 按第 7 章接口契约对接 |
| 需求依据 |
设备申请模块 — 产品需求文档(PRD) |
| 预计工时 |
10 ~ 14 天(边做边学) |
使用说明
按章节顺序做。尤其先做「管理端设备录入」,再做「用户申请」——没有设备数据,后面无法测试。
没有提供完整代码,只提供步骤说明和伪代码。
第 0 章 开始前必读
0.1 要做成什么
用一句话概括:
管理员在后台录入设备 → 师生在用户端看到设备并提交借用申请 → 管理员审核 → 用户查看结果 / 撤回待审申请。
当前缺口:
| 已有 |
缺失 |
| 用户端设备申请 UI(但是 Mock 数据) |
设备相关后端 API |
devices、device_bookings 数据库表 |
device_bookings 部分字段 |
管理端 /admin/devices、/admin/device-bookings 菜单 |
两个页面都是占位,无 API |
0.2 项目后端结构(先建立整体印象)
1 2 3 4 5 6 7 8 9 10 11 12 13
| backend/ ├── main.py # 入口:注册路由 └── app/ ├── api/routes/ │ ├── appointments.py # ★ 预约模块:主要参照模板 │ ├── labs.py # ★ 简单列表查询参照 │ ├── devices.py # ☆ 要新建:设备台账 │ └── device_bookings.py # ☆ 要新建:借用申请 ├── models/ │ ├── device.py # 设备表 ORM │ └── device_booking.py # 申请表 ORM(需补字段) └── core/ └── database.py # 数据库连接、建表、补列
|
0.3 必读参照文件
这一步我觉得比较重要,动手前先浏览一遍现有文件,包括机房预约:appointments.py,labs.py。以及设备申请相关的初始代码(看懂大致流程即可。不必每行都懂,看不懂的地方可以问TRAE,或者问我):
| 文件 |
文件说明 |
学什么 |
backend/app/api/routes/appointments.py |
实验室预约的 HTTP API 路由,处理前端/管理端的预约请求。 |
列表分页、POST 创建、PATCH 审核 |
backend/app/api/routes/labs.py |
实验室信息的 只读 API,给预约页展示「有哪些实验室可以约」。 |
最简单的 GET 列表 |
backend/app/models/device.py |
定义数据库表 devices(设备台账)的 Python 模型。描述「系统里有哪些设备、库存多少、什么状态」。 |
设备表有哪些字段
|
backend/app/models/device_booking.py |
定义数据库表 device_bookings(设备借用申请单)的 Python 模型。用户申请借设备时,每条申请存一张「工单」。 |
申请表现有字段 |
backend/app/models/appointment.py |
定义数据库表 appointments(实验室预约记录)的 Python 模型,告诉 SQLAlchemy:预约一条记录要存哪些字段、什么类型。 |
对照 admin_remark、reject_reason 怎么定义 |
0.4 实施顺序总览
1 2 3 4 5 6 7
| 第1章 补全 device_bookings 表字段 第2章 设备台账 API(管理端录入)← 数据源头,最先做 第3章 用户端设备列表 GET 第4章 提交借用申请 POST 第5章 申请列表 + 审核 + 撤回 第6章 注册路由 + 完整流程自测 第7章 交给 AI 的前端对接说明
|
0.5 文件改动 Checklist(做完打勾)
第 1 章 数据库:补全 device_bookings 表字段
1.1 为什么要改表
用户端申请表单要填:学号、姓名、班级、专业、电话、教师、事由等。
当前 device_bookings 表只有 user_name,不够用。
审核还需要 admin_remark(管理员备注)和 reject_reason(拒绝原因),参照 appointments 表。
1.2 需要新增的字段
打开 backend/app/models/device_booking.py,在 DeviceBooking 类中增加以下列定义(具体 Column 写法参照 appointment.py):
| 字段名 |
类型建议 |
说明 |
| device_name |
String(128) |
设备名称 |
| serial_number |
String(64) |
设备编号 |
| student_id |
String(64) |
学号 |
| class_name |
String(64) |
班级 |
| major |
String(64) |
专业 |
| phone |
String(32) |
电话 |
| teacher_id |
String(64) |
指导教师 ID |
| teacher_name |
String(64) |
指导教师姓名 |
| admin_remark |
Text |
管理员备注 |
| reject_reason |
Text |
拒绝原因 |
同时确认:user_name 用来存申请人姓名(对应表单 name 字段)。下面举几个例子说明代码具体如何写:橙色高亮的代码就是新增代码。例如根据表格新增device_name,类型是String(128),说明是设备名称,所以增加的代码就是device_name = Column(String(128), nullable=False, comment=”设备名字”)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| """设备申请表/设备借用工单表模型""" from sqlalchemy import Column, Integer, String, Text, SmallInteger, DateTime, Date from sqlalchemy.sql import func
from app.core.database import Base
class DeviceBooking(Base): __tablename__ = "device_bookings"
id = Column(Integer, primary_key=True, autoincrement=True) device_id = Column(Integer, nullable=False, comment="关联设备id") device_name = Column(String(128), nullable=False, comment="设备名字") serial_number = Column(String(64), nullable=True, comment="设备序列号") quantity = Column(Integer, nullable=False, default=1, comment="申请借用数量") start_date = Column(Date, nullable=False, comment="开始日期") end_date = Column(Date, nullable=False, comment="结束日期") reason = Column(Text, nullable=False, comment="借用/使用原因") special_requirement = Column(Text, nullable=True, comment="申请特殊需要,可为空") user_id = Column(Integer, nullable=True, comment="用户ID") user_name = Column(String(64), nullable=False, default="", comment="用户姓名") status = Column(SmallInteger, nullable=False, default=0, comment="工单状态: 0待审核 1已通过 2已拒绝 3已取消") created_at = Column(DateTime(timezone=True), server_default=func.now(), comment="记录创建时间") openid = Column(String(128), nullable=False, default="", comment="用户openid") class_name = Column(String(64), nullable=False, comment="班级") major = Column(String(64), nullable=False, comment="专业") phone = Column(String(32), nullable=False, comment="手机号") teacher_id = Column(String(64), nullable=True, comment="教师ID") teacher_name = Column(String(64), nullable=False, comment="教室名字") admin_remark = Column(Text, nullable=True, comment="审核员的评论") reject_reason = Column(Text, nullable=True, comment="审核员输入的拒绝原因")
|
1.3 让数据库真正拥有这些列
本项目启动时会执行 init_db()。按照下面流程来操作:
改好 device_booking.py 模型
用数据库客户端删除 device_bookings 表
重启后端,create_all 会按新模型建表
1.4 本章自测
| 检查项 |
怎么做 |
期望 |
| 模型无语法错误 |
重启后端不报错 |
终端正常启动 |
| 表结构正确 |
SQL:DESCRIBE device_bookings; |
能看到新增列 |
1.5 常见错误
| 现象 |
原因 |
处理 |
| 启动报 Unknown column |
模型加了字段但表没更新 |
删表重建或写补列函数 |
| 字段名对不上 |
Python 用下划线,JSON 用驼峰 |
后面 API 层做映射,本章只管数据库 |
第 2 章 设备台账 API(管理端录入)★ 优先完成
本章是整个模块的数据源头。 管理员录入的设备,才会出现在用户端供师生申请。
2.1 新建路由文件
创建文件:backend/app/api/routes/devices.py
文件开头的代码(这是伪代码,可以把伪代码转换成python代码):
1 2 3 4 5 6 7
| 从 fastapi 导入 APIRouter, Depends, HTTPException, Query 从 pydantic 导入 BaseModel, Field 从 sqlalchemy.orm 导入 Session 从 app.core.database 导入 get_db 从 app.models 导入 Device
router = APIRouter(prefix="/devices", tags=["devices"])
|
2.2 编写「设备对象转字典」辅助函数
前端需要的字段名和数据库不完全一样,需要一个转换函数。
伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 函数 device_to_dict(device行, 是否管理端模式): 计算对外状态 status_text: 如果 device.status != 0 → "maintenance" 或 "retired"(按 1/2 区分) 否则如果 available_quantity <= 0 → "unavailable" 否则 → "available"
返回字典: id = device.id name = device.name code = device.serial_number # 注意:DB 叫 serial_number,前端叫 code spec = device.description 或截断后的描述 category = device.category manufacturer = device.manufacturer model = device.model location = device.location department = device.department total_quantity = device.total_quantity available_quantity = device.available_quantity status = status_text # 对外字符串,不是数字 0/1/2 manager = device.manager icon = device.icon
|
2.3 定义 Pydantic 请求体(录入 / 编辑用)
伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 类 DeviceCreateBody: name: 字符串,必填,最少1字 serialNumber: 字符串,必填 category: 整数,0~3 manufacturer: 字符串,默认空 model: 字符串,默认空 description: 字符串,默认空 location: 字符串,默认空 department: 字符串,默认空 totalQuantity: 整数,默认1,>=1 availableQuantity: 整数,可选;若不传则等于 totalQuantity status: 整数,默认0(0正常 1维修 2报废) manager: 字符串,默认空 icon: 字符串,默认空
类 DeviceUpdateBody: 字段与 DeviceCreateBody 相同(或全部可选,P0 可以要求全量更新)
|
说明:前端 JSON 常用驼峰(serialNumber),数据库用下划线(serial_number),在写入时做转换。
2.4 实现 GET /devices 接口(列表)
伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| GET /devices 参数: category: 可选整数 q: 可选搜索关键字 page: 默认1 page_size: 默认10,最大100 scope: 可选,"public" 或 "admin",默认 "public"
函数 list_devices(db, category, q, page, page_size, scope): query = db.query(Device)
# 用户端只看待借设备 如果 scope != "admin": query = query.filter(Device.status == 0)
如果 category 有值: query = query.filter(Device.category == category)
如果 q 非空: like = "%" + q + "%" query = query.filter( Device.name 像 like 或 Device.serial_number 像 like 或 Device.description 像 like 或 Device.model 像 like )
total = query.count() rows = query.order_by(Device.id.desc()) .offset((page-1)*page_size) .limit(page_size) .all()
返回 { data: [device_to_dict(r, scope=="admin") for r in rows], pagination: { page, page_size, total, total_pages } }
|
2.5 实现 GET /devices/{id}(详情)
伪代码:
1 2 3 4 5
| GET /devices/{device_id}
row = 按 id 查询 Device 如果不存在 → HTTP 404 "未找到该设备" 返回 device_to_dict(row)
|
2.6 实现 POST /devices(管理端新增录入)★
伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| POST /devices 请求体: DeviceCreateBody
函数 create_device(body, db): # 校验 如果 body.name 去空格后为空 → 400 如果 body.serialNumber 去空格后为空 → 400 如果 body.totalQuantity < 1 → 400
avail = body.availableQuantity 如果 avail 未传或为 None: avail = body.totalQuantity 如果 avail < 0 或 avail > body.totalQuantity → 400
如果 body.category 不在 0~3 → 400 如果 body.status 不在 0~2 → 400
# 编号唯一(建议) 已存在 = db.query(Device).filter(serial_number == body.serialNumber).first() 如果已存在 → 400 "设备编号已存在"
# 写入 row = Device( name = body.name.strip(), serial_number = body.serialNumber.strip(), category = body.category, manufacturer = body.manufacturer.strip(), model = body.model.strip(), description = body.description.strip(), location = body.location.strip(), department = body.department.strip(), total_quantity = body.totalQuantity, available_quantity = avail, status = body.status, manager = body.manager.strip(), icon = body.icon.strip(), ) db.add(row) db.commit() db.refresh(row) 返回 { ok: true, id: row.id }
|
2.7 实现 PUT /devices/{id}(管理端编辑)
伪代码:
1 2 3 4 5 6 7 8 9 10 11 12
| PUT /devices/{device_id} 请求体: DeviceUpdateBody(字段同创建)
row = 按 id 查询,不存在 → 404
重复校验 serialNumber 唯一(排除自身 id)
重复数量校验 available <= total
更新 row 各字段(从 body 赋值) db.commit() 返回 { ok: true, data: device_to_dict(row) }
|
2.8 管理端「下架」怎么做(P0 不做 DELETE)-- 先不用做
PRD 建议用软下架,不物理删除记录:
用户端 scope=public 时自然看不到或显示不可借。
如果一定要做 DELETE 接口(可选):
1 2 3 4
| DELETE /devices/{id} 检查 device_bookings 有没有该 device_id 且 status 为 0 或 1 的记录 如果有 → 400 "存在进行中申请,不能删除" 否则 db.delete(row); commit
|
2.9 在 main.py 注册 devices 路由
伪代码(在 main.py):
1 2
| import devices 路由模块 app.include_router(devices.router, prefix=API_PREFIX)
|
按顺序测:
| 步骤 |
操作 |
期望 |
| 2a |
POST /api/devices 录入一台笔记本电脑 category=0 |
返回 { ok: true, id: N } |
| 2b |
POST 再录入一台实验器材 category=2 |
成功 |
| 2c |
用相同 serialNumber 再 POST |
400 编号重复 |
| 2d |
GET /api/devices?scope=admin |
能看到全部,含 status |
| 2e |
GET /api/devices(默认 public) |
只看到 status=0 的设备 |
| 2f |
GET /api/devices?category=2 |
只有实验器材 |
| 2g |
PUT /api/devices/{id} 改 available_quantity=0 |
成功 |
| 2h |
GET public 列表 |
该设备 status 为 unavailable |
| 2i |
SQL:SELECT * FROM devices; |
与 API 一致 |
2.11 常见错误
| 现象 |
原因 |
| 404 |
路由未在 main.py 注册 |
| 422 |
请求 JSON 字段名或类型与 Pydantic 不一致 |
| 500 数据库错误 |
模型字段与表结构不一致 |
第 3 章 用户端设备列表(只读,已在第 2 章实现了)
第 2 章的 GET /api/devices(scope=public)已经服务用户端。本章确认行为符合 PRD:
| 规则 |
说明 |
| 只展示 status=0 |
维修中、报废不展示给用户 |
| 可借判定 |
available_quantity > 0 → 前端显示「选择」 |
| 类别映射 |
category 0/1/2/3 对应 UI 四个 Tab |
自测: 不传 scope 调用 GET,应看不到 status=2 的报废设备。
第 4 章 设备借用申请 API(POST 创建)
4.1 新建路由文件
创建:backend/app/api/routes/device_bookings.py
结构参照 appointments.py:
1
| router = APIRouter(prefix="/device-bookings", tags=["device-bookings"])
|
4.2 定义请求体
伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 类 DeviceBookingCreateBody: deviceId: 整数,必填 quantity: 整数,默认 1 studentId: 字符串,默认空 name: 字符串,默认空 # 存入 user_name className: 字符串,默认空 major: 字符串,默认空 phone: 字符串,必填 teacherId: 字符串,默认空 teacherName: 字符串,默认空 borrowStart: 字符串,日期 YYYY-MM-DD,必填 borrowEnd: 字符串,日期 YYYY-MM-DD,必填 reason: 字符串,必填
|
4.3 实现 POST /device-bookings
伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| POST /device-bookings
函数 create_device_booking(body, db): # 1. 查设备 device = db.query(Device).filter(id == body.deviceId).first() 如果不存在 → 404 "设备不存在"
# 2. 可借校验 如果 device.status != 0 → 400 "设备当前不可借" 如果 device.available_quantity < body.quantity → 400 "可借数量不足"
# 3. 日期校验 如果 body.borrowEnd < body.borrowStart → 400 "归还日期不能早于开始日期"
# 4. 必填校验 如果 body.phone 为空 → 400 如果 body.reason 为空 → 400
# 5. 创建申请记录 row = DeviceBooking( device_id = device.id, device_name = device.name, serial_number = device.serial_number, quantity = body.quantity, start_date = 解析 body.borrowStart 为 Date, end_date = 解析 body.borrowEnd 为 Date, reason = body.reason.strip(), student_id = body.studentId.strip(), user_name = body.name.strip(), class_name = body.className.strip(), major = body.major.strip(), phone = body.phone.strip(), teacher_id = body.teacherId.strip(), teacher_name = body.teacherName.strip(), status = 0, admin_remark = "", reject_reason = "", openid = "", user_id = None, ) db.add(row) db.commit() db.refresh(row) 返回 { ok: true, id: row.id }
|
说明:
4.4 本章自测
| 步骤 |
操作 |
期望 |
| 4a |
用第 2 章录入的设备 id 提交申请 |
200,device_bookings 新增一行 status=0 |
| 4b |
用不存在的 deviceId |
404 |
| 4c |
borrowEnd 早于 borrowStart |
400 |
| 4d |
把设备 available_quantity 改为 0 后再提交 |
400 库存不足 |
第 5 章 申请列表、审核、撤回
5.1 实现 GET /device-bookings(列表)
强烈建议: 打开 appointments.py 的 list_appointments,对照改写。
伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| GET /device-bookings 参数: q: 搜索关键字,可选 status: 整数 0/1/2/3,可选 date_from, date_to: 可选,按 start_date 或 created_at 筛选 page, page_size: 分页
函数 list_device_bookings(...): query = db.query(DeviceBooking)
如果 q 非空: like = "%" + q + "%" query = query.filter( device_name 像 like 或 user_name 像 like 或 student_id 像 like 或 reason 像 like 或 serial_number 像 like )
如果 status 参数有值: query = query.filter(DeviceBooking.status == status)
如果 date_from 有值: query = query.filter(start_date >= date_from) 如果 date_to 有值: query = query.filter(start_date <= date_to)
total = query.count() rows = query.order_by(id.desc()).offset(...).limit(...).all()
返回 { data: [ 每条记录转成字典,字段名用前端习惯的驼峰或下划线,与 appointments 保持一致 ], pagination: {...} }
|
列表每项建议包含:
id, device_id, device_name, serial_number, quantity, start_date, end_date, reason, student_id, name, class_name, major, phone, teacher_id, teacher_name, status, admin_remark, reject_reason, created_at
5.2 实现 PATCH /device-bookings/{id}/review(审核)
伪代码(与 review_appointment 几乎相同):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 类 DeviceBookingReviewBody: action: 字符串,"approve" 或 "reject" adminRemark: 字符串,默认空 rejectReason: 字符串,默认空
PATCH /device-bookings/{appointment_id}/review
row = 按 id 查 DeviceBooking,不存在 → 404
action = body.action 转小写
如果 action == "approve": row.status = 1 row.admin_remark = body.adminRemark.strip() row.reject_reason = "" 否则如果 action == "reject": 如果 body.rejectReason 为空 → 400 "拒绝时必须填写拒绝原因" row.status = 2 row.reject_reason = body.rejectReason.strip() 否则: 400 "action 仅支持 approve/reject"
db.commit() 返回 { ok: true, data: { id, status, admin_remark, reject_reason } }
|
5.3 实现 PATCH /device-bookings/{id}/cancel(用户撤回)
伪代码:
1 2 3 4 5 6 7 8 9 10
| PATCH /device-bookings/{id}/cancel
row = 按 id 查询,不存在 → 404
如果 row.status != 0: 400 "仅待审核状态可撤回"
row.status = 3 db.commit() 返回 { ok: true, data: { id, status: 3 } }
|
5.4 状态码速查表(贴在桌面)
| status |
含义 |
用户端文案 |
| 0 |
待审核 |
待审批 |
| 1 |
已通过 |
已通过 |
| 2 |
已拒绝 |
已拒绝 |
| 3 |
已取消 |
已取消 |
5.5 本章自测
| 步骤 |
操作 |
期望 |
| 5a |
GET /api/device-bookings |
能看到第 4 章提交的记录 |
| 5b |
PATCH review action=approve |
status 变 1 |
| 5c |
再提交一条,PATCH reject 不带原因 |
400 |
| 5d |
PATCH reject 带原因 |
status=2,reject_reason 有值 |
| 5e |
再提交一条,PATCH cancel |
status=3 |
第 6 章 注册路由与完整流程自测
6.1 确认 main.py 注册了两个路由
伪代码:
1 2 3 4
| from app.api.routes import devices, device_bookings
app.include_router(devices.router, prefix=API_PREFIX) app.include_router(device_bookings.router, prefix=API_PREFIX)
|
保存后重启后端,打开 /docs 应能看到 devices 和 device-bookings 两组接口。
6.2 完整业务剧本(端到端)
按此顺序在 Swagger 中走一遍,全部通过即后端 P0 完成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| 【设备录入 — 管理端数据源头】 1. POST /api/devices 录入:名称="测试笔记本", serialNumber="LAP-TEST-001", category=0, totalQuantity=3, availableQuantity=3, status=0 → 记下返回的 id
2. POST /api/devices 再录入一台 category=2 的实验器材(可选,测筛选)
3. GET /api/devices?scope=admin → 确认两台都在
【用户浏览 — 公开列表】 4. GET /api/devices → 能看到 status=0 的设备,code 字段等于 serialNumber
5. GET /api/devices?category=0 → 只有笔记本
【用户申请】 6. POST /api/device-bookings deviceId=步骤1的id, name="测试学生", phone="138...", reason="课程实验", borrowStart=今天, borrowEnd=七天后 → 返回 ok, id
7. GET /api/device-bookings → 有一条 status=0
【管理员审核】 8. PATCH /api/device-bookings/{id}/review { "action": "approve", "adminRemark": "同意" } → status=1
9. 再 POST 一条申请 → PATCH reject(带 rejectReason)→ 确认 status=2
10. 再 POST 一条申请 → PATCH cancel → 确认 status=3
【库存与下架】 11. PUT /api/devices/{id} 设 availableQuantity=0 12. POST 新申请该设备 → 应 400 库存不足
13. PUT /api/devices/{id} 设 status=2(报废) 14. GET /api/devices(public)→ 不应再展示该设备
|
6.3 最终验收对照 PRD
| 验收编号 |
怎么证明完成了 |
| AC-00 |
步骤 1~3:管理端可录入 |
| AC-01 |
步骤 4:用户端列表来自 API |
| AC-02 |
步骤 6:申请落库 |
| AC-05/06 |
步骤 8~9:审核 |
| AC-07 |
步骤 10:撤回 |
| AC-08 |
步骤 11~12:不可借 |
| AC-10 |
步骤 13~14:下架后不可见 |
| AC-11 |
重复 serialNumber POST 失败 |
第 7 章 交给 AI 的前端对接说明(先不需要手写,让TRAE来实现前端)
后端完成后,将以下内容交给 AI 实现前端(用户端 + 管理端)。
7.1 用户端需改的文件
| 文件 |
改动 |
frontend/src/api/index.js |
新增 getDevices, submitDeviceBooking, getDeviceBookings, cancelDeviceBooking |
frontend/src/views/EquipmentView.vue |
删除 Mock;列表调 getDevices;提交调 submitDeviceBooking;我的申请调 getDeviceBookings;撤回绑 cancelDeviceBooking |
7.2 管理端需改的文件
| 文件 |
改动 |
admin/src/router/index.js |
devices → AdminDevicesView;device-bookings → AdminDeviceBookingsView |
新建 admin/src/views/AdminDevicesView.vue |
设备列表 + 新增/编辑表单,调 devices CRUD API |
新建 admin/src/views/AdminDeviceBookingsView.vue |
参照 AdminAppointmentsView,调 device-bookings API |
7.3 接口契约摘要(给 AI)
设备台账:
| 方法 |
路径 |
说明 |
| GET |
/api/devices?category=&q=&page=&page_size= |
用户端公开列表 |
| GET |
/api/devices?scope=admin |
管理端全量列表 |
| GET |
/api/devices/{id} |
详情 |
| POST |
/api/devices |
管理端录入 |
| PUT |
/api/devices/{id} |
管理端编辑 |
设备申请:
| 方法 |
路径 |
说明 |
| GET |
/api/device-bookings |
申请列表 |
| POST |
/api/device-bookings |
提交申请 |
| PATCH |
/api/device-bookings/{id}/review |
审核 |
| PATCH |
/api/device-bookings/{id}/cancel |
撤回 |
类别映射: 0 电脑 / 1 手机 / 2 实验器材 / 3 软件 → UI 四个 Tab。
设备可借: 响应 status 为 "available" 时可点击申请。
附录 A 概念速查
| 概念 |
在本项目里干什么 |
APIRouter |
把一组 URL 接口组织在一个文件里 |
Depends(get_db) |
每个请求自动拿一个数据库连接,用完关闭 |
BaseModel |
定义请求/响应 JSON 的格式和校验规则 |
db.add(row) |
把新对象放入待提交队列 |
db.commit() |
真正写入数据库 |
db.refresh(row) |
提交后刷新对象(拿到数据库生成的 id 等) |
HTTPException(400, detail="...") |
告诉前端请求不合法 |
附录 B 常见错误排查表
| 现象 |
排查顺序 |
Swagger 看不到新接口
|
main.py 是否 import 并 include_router;是否重启后端 |
| 404 Not Found |
URL 前缀是否为 /api/devices;router 的 prefix 是否写对 |
| 422 Unprocessable |
请求 JSON 字段名是否与 Pydantic 一致(驼峰 vs 下划线) |
| 500 Internal Server Error |
看终端完整报错;多半是缺列或日期格式转换失败 |
| 列表为空 |
是否先用 POST 录入了设备;public scope 是否过滤掉了维修/报废 |
| 申请成功但列表看不到 |
GET 列表的排序/分页;是否查错表 |
favorite
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!