xj-study 学习中心模块
基于 Django 的学习中心微服务模块,采用 Model-Services-APIs 三层架构设计,与 xj-lexicon、xj-exam 等模块保持完全一致的工程约定。本模块管理「学科 → 学习阶段 → 学习单元 → 单元任务 → 用户任务记录」五级学习层级,并支持通过外部考核接口回调记录学生测试结果。
一、目录结构
xj-study/
├── xj_study/
│ ├── apis/ # API 层(视图 + 路由参数解析)
│ │ ├── study_subject_api.py 学科列表 / 详情
│ │ ├── study_stage_api.py 学习阶段列表 / 详情
│ │ ├── study_unit_api.py 学习单元列表 / 详情
│ │ ├── study_task_api.py 单元任务 列表 / 详情 / 新增 / 修改
│ │ └── study_user_task_api.py 用户任务记录 列表 / 详情 / 开启 / 提交
│ ├── services/ # 业务逻辑层(查询/分页/过滤/写库)
│ │ ├── study_subject_service.py
│ │ ├── study_stage_service.py
│ │ ├── study_unit_service.py
│ │ ├── study_task_service.py
│ │ └── study_user_task_service.py
│ ├── utils/ # 工具层(与 xj-lexicon 同步)
│ │ ├── custom_response.py util_response 统一响应封装
│ │ ├── custom_tool.py format_params_handle 字段过滤
│ │ ├── j_transform_type.py JTransformType 类型强制转换
│ │ ├── request_params_wrapper.py request_params_wrapper 参数解析装饰器
│ │ └── parse_json.py JSON 字符串解析
│ ├── __init__.py
│ ├── apps.py # AppConfig:name='xj_study',sort=14
│ ├── admin.py # 后台注册(5 个模型)
│ ├── models.py # 数据模型定义
│ └── urls.py # 子路由分发
├── __init__.py
└── README.md
三层职责
| 层 | 职责 | 约定 |
|---|---|---|
| Model | 仅定义数据结构与字段 | 不写业务方法 |
| Service | 全部业务逻辑:参数校验、过滤白名单、分页、时间格式化、关联嵌套查询、写库 | @staticmethod,统一返回 (result, error) 元组;不直接接触 request/response |
| API | 视图层:解析请求参数、调 Service、统一响应 | 继承 APIView;@request_params_wrapper 注入 request_params;util_response() 返回 |
二、数据模型层级
StudySubject(学科)
└── StudyStage(学习阶段/册) FK→subject
└── StudyUnit(学习单元) FK→stage
└── StudyTask(单元任务) FK→unit,含 exam_url / thread_id 外部考核回调字段
└── StudyUserTask(用户任务记录)FK→task,user_id/user_uuid 不挂用户外键
StudyUserTask与用户系统不使用外键,仅以user_id+user_uuid关联外部用户服务,便于跨服务解耦。task_snapshot字段以 JSON 存储学生测试原始数据。
三、宿主项目注册
本模块作为独立可安装包,挂载到宿主项目 supreme-django-5.0。需在宿主完成以下三处配置(已完成):
1. main/settings.py —— 把模块根目录加入 sys.path
sys.path.append("D:/MyGitPkuac/xj-study")
2. config.ini 的 [main] app_packages —— 注册 Django App
app_packages = [
...
xj_lexicon,
xj_study,
xj_comment,
]
3. config.ini 的 [main] main_url_patterns —— 注册路由前缀
main_url_patterns = [
...
api/lexicon[/_] => xj_lexicon.urls,
api/study[/_] => xj_study.urls,
api/comment[/_] => xj_comment.urls,
]
注册后,所有接口统一以 /api/study/ 为前缀([/_] 同时兼容 /api/study_xxx 写法)。
四、统一响应协议
所有接口返回 util_response() 封装的 JSON:
{
"err": 0,
"data": { ... },
"msg": "ok"
}
| 字段 | 含义 |
|---|---|
err |
错误码,0 表示成功;非 0 时 data 通常为空 |
data |
数据体(列表接口为分页对象,详情接口为单条记录) |
msg |
错误信息;成功为 ok,失败为具体错误描述 |
列表接口的 data 结构:
{
"page": 1,
"size": 20,
"total": 35,
"list": [ ... ],
"query": "SELECT ..." // 实际执行的 SQL,便于调试
}
五、API 路由表
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/api/study/subject_list |
学科列表 |
GET |
/api/study/subject_item |
学科详情(含阶段列表) |
GET |
/api/study/stage_list |
学习阶段列表 |
GET |
/api/study/stage_item |
学习阶段详情(含单元列表) |
GET |
/api/study/unit_list |
学习单元列表 |
GET |
/api/study/unit_item |
学习单元详情(含任务列表) |
GET |
/api/study/task_list |
单元任务列表 |
GET |
/api/study/task_item |
单元任务详情 |
POST |
/api/study/task_item |
新增单元任务 |
PUT |
/api/study/task_item |
修改单元任务 |
GET |
/api/study/user_task_list |
用户任务记录列表 |
GET |
/api/study/user_task_item |
用户任务记录详情 |
POST |
/api/study/user_task_start |
开启一条用户任务记录 |
POST |
/api/study/user_task_submit |
提交用户任务结果(回调) |
@request_params_wrapper装饰器兼容GETquery、POSTform、application/json、text/plain多种请求格式,下列示例统一用 JSON Body 演示(GET 接口建议用 query string)。
六、API 调用示例
以下示例假设服务运行在
http://127.0.0.1:8000。GET请求参数既可走 query string,也可走 body;POST/PUT走 JSON Body。
1. 学科列表
curl -G "http://127.0.0.1:8000/api/study/subject_list" \
--data "page=1" --data "size=20" --data "sort=-sort" \
--data "search=俄" --data "enable=true"
响应:
{
"err": 0,
"data": {
"page": 1, "size": 20, "total": 2,
"list": [
{ "id": 1, "name": "俄语", "enable": true, "subject_score": 0, "icon": "", "sort": 1, "create_time": "2026-08-31 10:00:00", "update_time": "2026-08-31 10:00:00" }
],
"query": "SELECT ..."
},
"msg": "ok"
}
2. 学科详情(含关联阶段)
curl -G "http://127.0.0.1:8000/api/study/subject_item" --data "id=1"
响应:
{
"err": 0,
"data": {
"id": 1, "name": "俄语", "enable": true, "subject_score": 0, "icon": "", "sort": 1,
"create_time": "2026-08-31 10:00:00", "update_time": "2026-08-31 10:00:00",
"stage_count": 2,
"stage_list": [
{ "id": 1, "name": "第一册", "enable": true, "course_book": "...", "stage_score": 0, "icon": "", "sort": 1, "create_time": "...", "update_time": "..." }
]
},
"msg": "ok"
}
3. 学习阶段列表
curl -G "http://127.0.0.1:8000/api/study/stage_list" \
--data "subject_id=1" --data "enable=true" --data "page=1" --data "size=20"
4. 学习阶段详情(含关联单元)
curl -G "http://127.0.0.1:8000/api/study/stage_item" --data "id=1"
5. 学习单元列表
curl -G "http://127.0.0.1:8000/api/study/unit_list" \
--data "stage_id=1" --data "search=语音" --data "page=1" --data "size=20"
6. 学习单元详情(含关联任务)
curl -G "http://127.0.0.1:8000/api/study/unit_item" --data "id=10"
7. 单元任务列表
curl -G "http://127.0.0.1:8000/api/study/task_list" \
--data "unit_id=10" --data "enable=true" --data "sort=-sort"
8. 单元任务详情
curl -G "http://127.0.0.1:8000/api/study/task_item" --data "id=100"
响应:
{
"err": 0,
"data": {
"id": 100, "unit_id": 10, "name": "A1 听力测试",
"exam_url": "https://exam.example.com/api/check", "thread_id": "th_abc123",
"enable": true, "task_score": 100, "time_limit": 1800,
"open_time": "2026-09-01 09:00:00", "close_time": "2026-09-01 11:00:00",
"publish_time": "2026-08-31 18:00:00", "icon": "", "sort": 1,
"create_time": "...", "update_time": "..."
},
"msg": "ok"
}
9. 新增单元任务(POST)
curl -X POST "http://127.0.0.1:8000/api/study/task_item" \
-H "Content-Type: application/json" \
-d '{
"unit_id": 10,
"name": "A1 听力测试",
"exam_url": "https://exam.example.com/api/check",
"thread_id": "th_abc123",
"enable": true,
"task_score": 100,
"time_limit": 1800,
"sort": 1
}'
响应:
{ "err": 0, "data": { "id": 101 }, "msg": "ok" }
10. 修改单元任务(PUT)
curl -X PUT "http://127.0.0.1:8000/api/study/task_item" \
-H "Content-Type: application/json" \
-d '{ "id": 101, "time_limit": 2400, "task_score": 120 }'
响应:
{ "err": 0, "data": { "rows": 1 }, "msg": "ok" }
11. 用户任务记录列表
curl -G "http://127.0.0.1:8000/api/study/user_task_list" \
--data "user_id=10086" --data "task_id=100" --data "is_pass=false"
12. 用户任务记录详情
curl -G "http://127.0.0.1:8000/api/study/user_task_item" --data "id=500"
13. 开启用户任务记录(POST)
学生进入作答时调用;若存在未完成记录将直接复用,不重复创建。
curl -X POST "http://127.0.0.1:8000/api/study/user_task_start" \
-H "Content-Type: application/json" \
-d '{
"user_id": 10086,
"user_uuid": "u-10086-xxxx",
"task_id": 100
}'
响应:
{ "err": 0, "data": { "id": 500 }, "msg": "ok" }
14. 提交用户任务结果(POST,外部考核回调)
外部考核系统阅卷完成后回调本接口,写入得分、是否通过及 task_snapshot 快照,并自动记录 finish_time。
curl -X POST "http://127.0.0.1:8000/api/study/user_task_submit" \
-H "Content-Type: application/json" \
-d '{
"id": 500,
"result_score": 85,
"user_task_score": 15,
"is_pass": true,
"task_snapshot": {
"questions": [
{ "qid": "Q1", "answer": "А", "correct": true },
{ "qid": "Q2", "answer": "Б", "correct": false }
],
"duration": 1620
}
}'
响应:
{ "err": 0, "data": { "rows": 1 }, "msg": "ok" }
七、典型业务流程
- 管理员在后台配置
StudySubject → StudyStage → StudyUnit → StudyTask,并为每个任务填写exam_url(外部考核回调地址)与thread_id。 - 学生打开单元,前端调
GET /api/study/unit_item拿到task_list。 - 学生点击某任务,前端调
POST /api/study/user_task_start创建/复用一条StudyUserTask记录,得到record_id,并据exam_url跳转外部考核系统。 - 外部考核系统阅卷完成,按约定回调
POST /api/study/user_task_submit(带record_id、result_score、is_pass、task_snapshot)。 - 前端轮询或拉取
GET /api/study/user_task_item展示结果与快照。
八、数据库迁移
在宿主项目 supreme-django-5.0 根目录执行:
python manage.py makemigrations xj_study
python manage.py migrate
完成后可在 /administrator/ 后台看到「Ⅳ 学习中心」应用及其 5 个模型的管理界面。
Release files for xj-study 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| xj_study-0.1.0.tar.gz | 37.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xj_study-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 92.7 kB
Release files / xj_study-0.1.0.tar.gz
| Download URL | xj_study-0.1.0.tar.gz |
|---|---|
| Size | 37.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
622305a4efa46d30396d2066ee098dc525079bacb7a3713687165e672f55e50b
|
|
BLAKE2b-256 checksum How to use checksums |
89ec545481852a41caeb2f188b9edee9df62b83ec2a165d686a18cd1128ca42c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.11
|
Release files / xj_study-0.1.0-py3-none-any.whl
| Download URL | xj_study-0.1.0-py3-none-any.whl |
|---|---|
| Size | 54.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ae6d499e74d66f4ea651abcffa4234618e58461e9e24b445b0f4b6d84d5eafc7
|
|
BLAKE2b-256 checksum How to use checksums |
167397360a1b5cecab4eb5bfa13b5952a485aecc65ede33e8bfe175b2d41e41b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.11
|