Metadata-Version: 2.1
Name: xj-study
Version: 0.1.0
Summary: 学习任务模块
Home-page: 
Author: 赵向明
Author-email: sieyoo@163.com
License: apache 3.0
Keywords: django russian study dictionary vocabulary
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Framework :: Django
Classifier: Framework :: Django :: 3.2
Classifier: Framework :: Django :: 4.0
Classifier: Framework :: Django :: 4.1
Requires-Python: >=3.7
Description-Content-Type: text/markdown

# 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`
```python
sys.path.append("D:/MyGitPkuac/xj-study")
```

**2. `config.ini` 的 `[main] app_packages`** —— 注册 Django App
```ini
app_packages = [
             ...
             xj_lexicon,
             xj_study,
             xj_comment,
              ]
```

**3. `config.ini` 的 `[main] main_url_patterns`** —— 注册路由前缀
```ini
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：

```json
{
  "err": 0,
  "data": { ... },
  "msg": "ok"
}
```

| 字段  | 含义                                                |
| ----- | --------------------------------------------------- |
| `err` | 错误码，`0` 表示成功；非 0 时 `data` 通常为空       |
| `data`| 数据体（列表接口为分页对象，详情接口为单条记录）     |
| `msg` | 错误信息；成功为 `ok`，失败为具体错误描述            |

列表接口的 `data` 结构：

```json
{
  "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` 装饰器兼容 `GET` query、`POST` form、`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. 学科列表

```bash
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"
```

响应：
```json
{
  "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. 学科详情（含关联阶段）

```bash
curl -G "http://127.0.0.1:8000/api/study/subject_item" --data "id=1"
```

响应：
```json
{
  "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. 学习阶段列表

```bash
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. 学习阶段详情（含关联单元）

```bash
curl -G "http://127.0.0.1:8000/api/study/stage_item" --data "id=1"
```

### 5. 学习单元列表

```bash
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. 学习单元详情（含关联任务）

```bash
curl -G "http://127.0.0.1:8000/api/study/unit_item" --data "id=10"
```

### 7. 单元任务列表

```bash
curl -G "http://127.0.0.1:8000/api/study/task_list" \
  --data "unit_id=10" --data "enable=true" --data "sort=-sort"
```

### 8. 单元任务详情

```bash
curl -G "http://127.0.0.1:8000/api/study/task_item" --data "id=100"
```

响应：
```json
{
  "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）

```bash
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
  }'
```

响应：
```json
{ "err": 0, "data": { "id": 101 }, "msg": "ok" }
```

### 10. 修改单元任务（PUT）

```bash
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 }'
```

响应：
```json
{ "err": 0, "data": { "rows": 1 }, "msg": "ok" }
```

### 11. 用户任务记录列表

```bash
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. 用户任务记录详情

```bash
curl -G "http://127.0.0.1:8000/api/study/user_task_item" --data "id=500"
```

### 13. 开启用户任务记录（POST）

学生进入作答时调用；若存在未完成记录将直接复用，不重复创建。

```bash
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
  }'
```

响应：
```json
{ "err": 0, "data": { "id": 500 }, "msg": "ok" }
```

### 14. 提交用户任务结果（POST，外部考核回调）

外部考核系统阅卷完成后回调本接口，写入得分、是否通过及 `task_snapshot` 快照，并自动记录 `finish_time`。

```bash
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
    }
  }'
```

响应：
```json
{ "err": 0, "data": { "rows": 1 }, "msg": "ok" }
```

---

## 七、典型业务流程

1. 管理员在后台配置 `StudySubject → StudyStage → StudyUnit → StudyTask`，并为每个任务填写 `exam_url`（外部考核回调地址）与 `thread_id`。
2. 学生打开单元，前端调 `GET /api/study/unit_item` 拿到 `task_list`。
3. 学生点击某任务，前端调 `POST /api/study/user_task_start` 创建/复用一条 `StudyUserTask` 记录，得到 `record_id`，并据 `exam_url` 跳转外部考核系统。
4. 外部考核系统阅卷完成，按约定回调 `POST /api/study/user_task_submit`（带 `record_id`、`result_score`、`is_pass`、`task_snapshot`）。
5. 前端轮询或拉取 `GET /api/study/user_task_item` 展示结果与快照。

---

## 八、数据库迁移

在宿主项目 `supreme-django-5.0` 根目录执行：

```bash
python manage.py makemigrations xj_study
python manage.py migrate
```

完成后可在 `/administrator/` 后台看到「Ⅳ 学习中心」应用及其 5 个模型的管理界面。
