diff --git a/backend/xt-server/src/main/java/cn/tianbaotong/fill/module/fill/controller/FillController.java b/backend/xt-server/src/main/java/cn/tianbaotong/fill/module/fill/controller/FillController.java
new file mode 100644
index 0000000..b96e7e5
--- /dev/null
+++ b/backend/xt-server/src/main/java/cn/tianbaotong/fill/module/fill/controller/FillController.java
@@ -0,0 +1,34 @@
+package cn.tianbaotong.fill.module.fill.controller;
+
+import cn.tianbaotong.fill.common.api.ApiResult;
+import cn.tianbaotong.fill.module.fill.dto.StudentFormVO;
+import cn.tianbaotong.fill.module.fill.service.FillService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 学生端填报(《03-接口设计》§9.x)。
+ *
+ *
要求已登录(学生),且必须已过门禁 —— 见 {@code FillService}。
+ */
+@Tag(name = "小程序/H5 · 填报")
+@RestController
+@RequestMapping("/api/v1/mp")
+@RequiredArgsConstructor
+public class FillController {
+
+ private final FillService fillService;
+
+ @GetMapping("/tasks/{id}/form")
+ @Operation(summary = "表单结构 + 当前值",
+ description = "字段三态见 §9.2:locked=true 只读、prefilled=true 标注「老师已填,请核对」。"
+ + "身份证号只回脱敏值。未过门禁时返回 40007(提示先核对身份)")
+ public ApiResult form(@PathVariable Long id) {
+ return ApiResult.ok(fillService.getForm(id));
+ }
+}
diff --git a/backend/xt-server/src/main/java/cn/tianbaotong/fill/module/fill/dto/StudentFormVO.java b/backend/xt-server/src/main/java/cn/tianbaotong/fill/module/fill/dto/StudentFormVO.java
new file mode 100644
index 0000000..a639ed9
--- /dev/null
+++ b/backend/xt-server/src/main/java/cn/tianbaotong/fill/module/fill/dto/StudentFormVO.java
@@ -0,0 +1,84 @@
+package cn.tianbaotong.fill.module.fill.dto;
+
+import cn.tianbaotong.fill.module.form.dto.FormFieldVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 学生端表单(《03-接口设计》§9.2)。
+ *
+ * 字段三态是这一屏的核心(§9.2 的表):
+ *
+ *
+ * | {@code locked} | {@code prefilled} | 学生端表现 |
+ * | true | — | 只读、灰底、右侧提示"如有误请联系老师" |
+ * | false | true | 可编辑,标注「老师已填,请核对」,改后留痕 |
+ * | false | false | 可编辑,正常样式 |
+ *
+ *
+ * ⚠️ 为什么 locked 必须是真的只读、而不是"前端灰一下":
+ * 「报考职业/工种/等级」由老师按机构的报考策略确定,学生不懂也不该改。
+ * 让它看起来可编辑(哪怕服务端会拒绝),只会让人白改一次 ——
+ * 而报错工种在机构那边是直接不受理,等发现时报名已经结束。
+ * 所以前端渲染成只读,服务端再拒绝一次(两层)。
+ */
+@Getter
+@Setter
+@Schema(description = "学生端表单")
+public class StudentFormVO {
+
+ private Long taskId;
+
+ private String taskName;
+
+ /** 表单版本(学生是按某一版填的,留痕用)。 */
+ private Integer formVersion;
+
+ /** 草稿单 id;没有草稿时为 null。 */
+ private Long submissionId;
+
+ /** 提交状态({@code 0} 未开始 / {@code 1} 草稿 / {@code 2} 待审 …)。 */
+ private Integer submissionStatus;
+
+ private List sections = new ArrayList<>();
+
+ /** 材料项(结构见 §3.4)。材料上传属下一段,这里先给出清单。 */
+ private List