Преглед на файлове

Merge pull request #2488 from visuddhinanda/development

Development
visuddhinanda преди 22 часа
родител
ревизия
d93e0a505a
променени са 100 файла, в които са добавени 1723 реда и са изтрити 171 реда
  1. 1 0
      CLAUDE.md
  2. 47 0
      api-test/cases/07_discussions.php
  3. 89 0
      api-test/cases/09_tipitaka_discussions.php
  4. 6 0
      api-test/client/.openapi-generator/FILES
  5. 1 1
      api-test/client/lib/Api/AgentGrantsApi.php
  6. 1 1
      api-test/client/lib/Api/DiscussionsApi.php
  7. 1 1
      api-test/client/lib/Api/EmailCertificationsApi.php
  8. 1 1
      api-test/client/lib/Api/HeartbeatApi.php
  9. 1 1
      api-test/client/lib/Api/InvitesApi.php
  10. 1 1
      api-test/client/lib/Api/MeApi.php
  11. 1 1
      api-test/client/lib/Api/PasswordResetsApi.php
  12. 1 1
      api-test/client/lib/Api/ProgressApi.php
  13. 1 1
      api-test/client/lib/Api/ReactionsApi.php
  14. 1 1
      api-test/client/lib/Api/SearchApi.php
  15. 1 1
      api-test/client/lib/Api/SearchSuggestApi.php
  16. 1 1
      api-test/client/lib/Api/SessionsApi.php
  17. 636 0
      api-test/client/lib/Api/TipitakaDiscussionsApi.php
  18. 1 1
      api-test/client/lib/Api/TipitakaReadingApi.php
  19. 1 1
      api-test/client/lib/Api/TipitakaRelatedParagraphsApi.php
  20. 1 1
      api-test/client/lib/Api/UpgradeApi.php
  21. 1 1
      api-test/client/lib/Api/UsersApi.php
  22. 1 1
      api-test/client/lib/ApiException.php
  23. 2 2
      api-test/client/lib/Configuration.php
  24. 1 1
      api-test/client/lib/FormDataProcessor.php
  25. 1 1
      api-test/client/lib/HeaderSelector.php
  26. 1 1
      api-test/client/lib/Model/GetApiV3Discussions200Response.php
  27. 35 1
      api-test/client/lib/Model/GetApiV3Discussions200ResponseDataInner.php
  28. 1 1
      api-test/client/lib/Model/GetApiV3Discussions200ResponseDataInnerAbilities.php
  29. 1 1
      api-test/client/lib/Model/GetApiV3Discussions200ResponseDataInnerEditor.php
  30. 512 0
      api-test/client/lib/Model/GetApiV3Discussions200ResponseDataInnerSentence.php
  31. 1 1
      api-test/client/lib/Model/GetApiV3DiscussionsSummary200Response.php
  32. 1 1
      api-test/client/lib/Model/GetApiV3DiscussionsSummary200ResponseData.php
  33. 1 1
      api-test/client/lib/Model/GetApiV3DiscussionsSummary200ResponseDataStatusCounts.php
  34. 1 1
      api-test/client/lib/Model/GetApiV3Heartbeat200Response.php
  35. 1 1
      api-test/client/lib/Model/GetApiV3Heartbeat200ResponseData.php
  36. 1 1
      api-test/client/lib/Model/GetApiV3Me200Response.php
  37. 1 1
      api-test/client/lib/Model/GetApiV3Me200ResponseData.php
  38. 1 1
      api-test/client/lib/Model/GetApiV3MeReactions200Response.php
  39. 1 1
      api-test/client/lib/Model/GetApiV3MeReactions200ResponseDataInner.php
  40. 1 1
      api-test/client/lib/Model/GetApiV3PasswordResetsToken200Response.php
  41. 1 1
      api-test/client/lib/Model/GetApiV3PasswordResetsToken200ResponseData.php
  42. 1 1
      api-test/client/lib/Model/GetApiV3Progress200Response.php
  43. 1 1
      api-test/client/lib/Model/GetApiV3Progress200ResponseDataInner.php
  44. 1 1
      api-test/client/lib/Model/GetApiV3ReactionsTally200Response.php
  45. 1 1
      api-test/client/lib/Model/GetApiV3ReactionsTally200ResponseDataInner.php
  46. 1 1
      api-test/client/lib/Model/GetApiV3Search200Response.php
  47. 1 1
      api-test/client/lib/Model/GetApiV3SearchSearch200Response.php
  48. 1 1
      api-test/client/lib/Model/GetApiV3TipitakaReadingChannel200Response.php
  49. 1 1
      api-test/client/lib/Model/GetApiV3TipitakaReadingChannel200ResponseDataInner.php
  50. 1 1
      api-test/client/lib/Model/GetApiV3TipitakaReadingChannel200ResponseMeta.php
  51. 1 1
      api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphs200Response.php
  52. 1 1
      api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphs200ResponseDataInner.php
  53. 1 1
      api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphs200ResponseDataInnerPathInner.php
  54. 1 1
      api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphs200ResponseMeta.php
  55. 1 1
      api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphsAggregate200Response.php
  56. 1 1
      api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphsAggregate200ResponseDataInner.php
  57. 1 1
      api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphsAggregateBookName200Response.php
  58. 1 1
      api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphsAggregateBookName200ResponseDataInner.php
  59. 1 1
      api-test/client/lib/Model/ModelInterface.php
  60. 1 1
      api-test/client/lib/Model/PaginationMeta.php
  61. 8 6
      api-test/client/lib/Model/PatchApiV3DiscussionsDiscussionRequest.php
  62. 1 1
      api-test/client/lib/Model/PatchApiV3PasswordResetsTokenRequest.php
  63. 1 1
      api-test/client/lib/Model/PostApiV3AgentGrants200Response.php
  64. 1 1
      api-test/client/lib/Model/PostApiV3AgentGrants200ResponseData.php
  65. 1 1
      api-test/client/lib/Model/PostApiV3AgentGrantsRequest.php
  66. 1 1
      api-test/client/lib/Model/PostApiV3Discussions200Response.php
  67. 1 1
      api-test/client/lib/Model/PostApiV3DiscussionsBatchCount200Response.php
  68. 1 1
      api-test/client/lib/Model/PostApiV3DiscussionsBatchCount200ResponseDataInner.php
  69. 1 1
      api-test/client/lib/Model/PostApiV3DiscussionsBatchCountRequest.php
  70. 1 1
      api-test/client/lib/Model/PostApiV3DiscussionsBatchCountRequestTargetsInner.php
  71. 1 1
      api-test/client/lib/Model/PostApiV3DiscussionsDiscussionRepliesRequest.php
  72. 9 7
      api-test/client/lib/Model/PostApiV3DiscussionsRequest.php
  73. 1 1
      api-test/client/lib/Model/PostApiV3EmailCertificationsRequest.php
  74. 1 1
      api-test/client/lib/Model/PostApiV3Invites200Response.php
  75. 1 1
      api-test/client/lib/Model/PostApiV3Invites200ResponseData.php
  76. 1 1
      api-test/client/lib/Model/PostApiV3InvitesRequest.php
  77. 1 1
      api-test/client/lib/Model/PostApiV3MeReactions200Response.php
  78. 1 1
      api-test/client/lib/Model/PostApiV3MeReactionsRequest.php
  79. 1 1
      api-test/client/lib/Model/PostApiV3PasswordResetsRequest.php
  80. 1 1
      api-test/client/lib/Model/PostApiV3SearchRequest.php
  81. 1 1
      api-test/client/lib/Model/PostApiV3Sessions200Response.php
  82. 1 1
      api-test/client/lib/Model/PostApiV3Sessions200ResponseData.php
  83. 1 1
      api-test/client/lib/Model/PostApiV3SessionsRequest.php
  84. 1 1
      api-test/client/lib/Model/PostApiV3Users200Response.php
  85. 1 1
      api-test/client/lib/Model/PostApiV3Users200ResponseData.php
  86. 1 1
      api-test/client/lib/Model/PostApiV3UsersRequest.php
  87. 1 1
      api-test/client/lib/Model/ProblemDetails.php
  88. 1 1
      api-test/client/lib/ObjectSerializer.php
  89. 73 19
      api-test/config.local.example.php
  90. 17 18
      api-test/config.php
  91. 24 3
      api-test/run.php
  92. 2 2
      api-v13/app/Http/Controllers/V3/DiscussionBatchCountController.php
  93. 25 19
      api-v13/app/Http/Controllers/V3/DiscussionController.php
  94. 1 1
      api-v13/app/Http/Controllers/V3/DiscussionSummaryController.php
  95. 61 0
      api-v13/app/Http/Controllers/V3/TipitakaDiscussionController.php
  96. 48 0
      api-v13/app/Http/Requests/V3/IndexTipitakaDiscussionRequest.php
  97. 10 4
      api-v13/app/Http/Requests/V3/StoreDiscussionRequest.php
  98. 1 1
      api-v13/app/Http/Requests/V3/UpdateDiscussionRequest.php
  99. 12 2
      api-v13/app/Http/Resources/V3/DiscussionResource.php
  100. 26 8
      api-v13/app/Models/Discussion.php

+ 1 - 0
CLAUDE.md

@@ -352,6 +352,7 @@ Resource / FormRequest / 测试同理;URL 只换版本前缀
 | `email-certification` + `invite/{id}`(读)+ `sign-up` / `EmailCertificationController` + `InviteController@show` + `SignUpController@store` | `email-certifications` + `invites` (+`/{invite}`) + `users` / `V3\EmailCertificationController` + `V3\InviteController` + `V3\UserController` | 注册。验证码改在服务端比对:v2 的 `GET email-certification/{id}`(把码交给前端)在 v3 没有对应端点,换成 `POST invites {email, code}` 签发 invite;自助与邀请注册都凭 invite 走 `POST users`。验证码 6 位、30 分钟、试错 5 次作废;验证通过前不写 invites 表。studio 发邀请(`POST /v2/invite`)不在此列,将来迁成 `studios/{studio}/invites` |
 | `email-certification` + `invite/{id}`(读)+ `sign-up` / `EmailCertificationController` + `InviteController@show` + `SignUpController@store` | `email-certifications` + `invites` (+`/{invite}`) + `users` / `V3\EmailCertificationController` + `V3\InviteController` + `V3\UserController` | 注册。验证码改在服务端比对:v2 的 `GET email-certification/{id}`(把码交给前端)在 v3 没有对应端点,换成 `POST invites {email, code}` 签发 invite;自助与邀请注册都凭 invite 走 `POST users`。验证码 6 位、30 分钟、试错 5 次作废;验证通过前不写 invites 表。studio 发邀请(`POST /v2/invite`)不在此列,将来迁成 `studios/{studio}/invites` |
 | `discussion`(列表、单条、新建、修改、删除)/ `DiscussionController` | `discussions` (+`/summary`、`/{d}/replies`) / `V3\DiscussionController` + `V3\DiscussionSummaryController` + `V3\DiscussionReplyController` | 管评论线程(type `discussion` / `qa` / `help` / `issue`)与批注(`note` 边注、`commentary` 义注 / 复注对应——巴利注释书,不是普通评论)。批注只挂句子、只有该句管理者能建(AI 凭授权代写按授权人判)、带锚点 `pos_*`/`quote_*`、可回复、无开关、新建不发通知;列表与 summary 不带 `type` 时只算评论线程。写入支持 agent 代写(`X-Agent-Grant`),新增 `co_editor`(人亲自改内容时清空)。字段改名 `res_type`/`res_id`→`target_type`/`target_id`、`parent`→`parent_id`、`children_count`→`reply_count`;`view=question/res_id/question-by-topic`→列表,`view=answer`+带 parent 的 POST→`replies`,响应里的 `active`/`close`/`can_create`→`summary`;每条带 `abilities`;只有 PATCH;软删除(2026-10-15 起 v2 也是),有回复的话题留作墓碑。`view=topic-by-user`/`all`、课程参数、`discussion-anchor`、`sent-discussion-tree` 未迁;`discussion-count` 拆开:讨论角标迁成 `POST discussions/batch-count`(只给计数,入参改为对象 uid,套 v3 可见性与类型口径),标签部分仍走 v2、待迁标签时处理;课程模式的「学生的讨论」待课程迁移后补(TODO 在 `DiscussionService::batchCount`) |
 | `discussion`(列表、单条、新建、修改、删除)/ `DiscussionController` | `discussions` (+`/summary`、`/{d}/replies`) / `V3\DiscussionController` + `V3\DiscussionSummaryController` + `V3\DiscussionReplyController` | 管评论线程(type `discussion` / `qa` / `help` / `issue`)与批注(`note` 边注、`commentary` 义注 / 复注对应——巴利注释书,不是普通评论)。批注只挂句子、只有该句管理者能建(AI 凭授权代写按授权人判)、带锚点 `pos_*`/`quote_*`、可回复、无开关、新建不发通知;列表与 summary 不带 `type` 时只算评论线程。写入支持 agent 代写(`X-Agent-Grant`),新增 `co_editor`(人亲自改内容时清空)。字段改名 `res_type`/`res_id`→`target_type`/`target_id`、`parent`→`parent_id`、`children_count`→`reply_count`;`view=question/res_id/question-by-topic`→列表,`view=answer`+带 parent 的 POST→`replies`,响应里的 `active`/`close`/`can_create`→`summary`;每条带 `abilities`;只有 PATCH;软删除(2026-10-15 起 v2 也是),有回复的话题留作墓碑。`view=topic-by-user`/`all`、课程参数、`discussion-anchor`、`sent-discussion-tree` 未迁;`discussion-count` 拆开:讨论角标迁成 `POST discussions/batch-count`(只给计数,入参改为对象 uid,套 v3 可见性与类型口径),标签部分仍走 v2、待迁标签时处理;课程模式的「学生的讨论」待课程迁移后补(TODO 在 `DiscussionService::batchCount`) |
 | `access-token`(agent 用途)/ `AccessTokenController@store` | `agent-grants` / `V3\AgentGrantController` | 人类为自己名下的 AI 模型签 channel 写入授权。载荷由服务端决定(v2 原样签客户端 payload)、绑定模型(act)与签发人(grantor)、带模型 token_version、独立密钥;写入时放 `X-Agent-Grant` 头而不是请求体 |
 | `access-token`(agent 用途)/ `AccessTokenController@store` | `agent-grants` / `V3\AgentGrantController` | 人类为自己名下的 AI 模型签 channel 写入授权。载荷由服务端决定(v2 原样签客户端 payload)、绑定模型(act)与签发人(grantor)、带模型 token_version、独立密钥;写入时放 `X-Agent-Grant` 头而不是请求体 |
+| `sent-discussion-tree`(部分)/ `DiscussionController@discussion_tree` | `tipitaka-discussions/{channel}` / `V3\TipitakaDiscussionController` | 新能力为主:按经文范围(channel 必填;`book` + `chapter` 或 `para`/`to`,不给 book 即整个 channel)列挂在句子上的话题,每条带 `sentence` 坐标,按经文顺序排;`type` 可逗号多选,默认只列话题,可见性同 `/v3/discussions`。v2 那条要调用方逐句列出坐标、只回标题与回复数,未迁也不迁(只有 v4 在用)。与 `/v3/discussions`(单个对象)必填参数不同,按规范第 1 条分成两个端点 |
 | `sign-in` + `auth/current` / `AuthController@signIn` + `@getUserInfoByToken` | `sessions` + `me` / `V3\SessionController` + `V3\MeController` | 登录一次返回 `{token, user}`,不再二次查询;`login` 字段收用户名或邮箱(邮箱不区分大小写),失败统一 422 `errors.login`;按账号 + IP 限流。`me` 的字段沿用 v2 的驼峰用户摘要(`UserService::profile()`),`roles` 恒为数组,另加 `email`,不再回显 token。token 与 v2 完全相同、两边互认 |
 | `sign-in` + `auth/current` / `AuthController@signIn` + `@getUserInfoByToken` | `sessions` + `me` / `V3\SessionController` + `V3\MeController` | 登录一次返回 `{token, user}`,不再二次查询;`login` 字段收用户名或邮箱(邮箱不区分大小写),失败统一 422 `errors.login`;按账号 + IP 限流。`me` 的字段沿用 v2 的驼峰用户摘要(`UserService::profile()`),`roles` 恒为数组,另加 `email`,不再回显 token。token 与 v2 完全相同、两边互认 |
 
 
 **注意**:`tipitaka-reading` **不是** v2 `paragraph-content` / `chapter-content` 的替代品。
 **注意**:`tipitaka-reading` **不是** v2 `paragraph-content` / `chapter-content` 的替代品。

+ 47 - 0
api-test/cases/07_discussions.php

@@ -222,3 +222,50 @@ test('discussions 一轮:建话题 → 回复 → 关闭后回复 409 → 重
     $gone = raw('GET', "/v3/discussions/{$id}", null, null, $token);
     $gone = raw('GET', "/v3/discussions/{$id}", null, null, $token);
     assert_status(404, $gone, '删后再查');
     assert_status(404, $gone, '删后再查');
 });
 });
+
+/* ---- 术语讨论(glossary)与锚点 ---- */
+
+test('glossary 一轮:带锚点建 → 公开可读 → 不收锚点的类型传锚点 422 → 改成 discussion 锚点保留 → 删', function () {
+    $token = required_token();
+    $stamp = 'api-test-glossary-' . date('Ymd-His');
+
+    $create = raw('POST', '/v3/discussions', null, [
+        'target_type' => 'sentence', 'target_id' => discussion_target(), 'type' => 'glossary',
+        'title' => 'dukkha', 'content' => $stamp, 'pos_start' => 0, 'pos_end' => 3, 'quote_exact' => 'abc',
+        'notify' => false,
+    ], $token);
+    assert_status(201, $create, '建 glossary');
+    $id = $create['body']['data']['id'] ?? null;
+    if (! $id) {
+        fail('建 glossary:应返回 id');
+    }
+
+    try {
+        assert_eq('dukkha', $create['body']['data']['title'] ?? null, 'glossary title 是词头');
+        assert_eq(3, $create['body']['data']['pos_end'] ?? null, 'glossary pos_end');
+
+        // 公开可见:匿名也读得到
+        assert_status(200, raw('GET', "/v3/discussions/{$id}"), '匿名读 glossary');
+
+        // glossary 缺 title / 挂在句子以外 → 422
+        $noTitle = raw('POST', '/v3/discussions', null, [
+            'target_type' => 'sentence', 'target_id' => discussion_target(), 'type' => 'glossary', 'notify' => false,
+        ], $token);
+        assert_field_error('title', $noTitle, 'glossary 缺 title');
+
+        // discussion 不收锚点
+        $anchored = raw('POST', '/v3/discussions', null, [
+            'target_type' => 'sentence', 'target_id' => discussion_target(), 'type' => 'discussion',
+            'title' => $stamp, 'pos_start' => 1, 'notify' => false,
+        ], $token);
+        assert_field_error('pos_start', $anchored, 'discussion 带锚点');
+
+        // 改成 discussion:锚点保留(不显示,改回可带锚点的类型时恢复)
+        $converted = raw('PATCH', "/v3/discussions/{$id}", null, ['type' => 'discussion'], $token);
+        assert_status(200, $converted, 'glossary → discussion');
+        assert_eq(3, $converted['body']['data']['pos_end'] ?? null, '改类型后 pos_end');
+    } finally {
+        $del = raw('DELETE', "/v3/discussions/{$id}", null, null, $token);
+    }
+    assert_status(200, $del, '删 glossary');
+});

+ 89 - 0
api-test/cases/09_tipitaka_discussions.php

@@ -0,0 +1,89 @@
+<?php
+
+use \WikipaliApi\Api\TipitakaDiscussionsApi;
+
+/*
+ * 按经文范围列讨论:GET /v3/tipitaka-discussions/{channel}?book=&chapter=|para=&to=&type=…
+ *
+ * 只读端点。完整一轮借 discussions 的写接口在 fixtures 的 discussion_sentence 上建一条
+ * issue(测试账号自己的句子,notify=false),按它所在的段落列出来核对,最后删掉。
+ * 权限矩阵(匿名 / basic 可见性)、排序、墓碑由 api-v13 的 TipitakaDiscussionV3Test 覆盖。
+ */
+
+/** fixture 句子的 book / paragraph(从 v2 单句接口取,各服务器的 fixture 不同) */
+function tipitaka_discussion_coord(): array
+{
+    $res = raw('GET', '/v2/sentence/' . fixtures()['discussion_sentence']);
+    assert_status(200, $res, '取 fixture 句子');
+    $data = $res['body']['data'] ?? [];
+
+    return [(int) ($data['book'] ?? 0), (int) ($data['paragraph'] ?? 0)];
+}
+
+test('GET /v3/tipitaka-discussions/{channel} → 200,列表 + 分页', function () {
+    $res = request(fn () => api(TipitakaDiscussionsApi::class)
+        ->getApiV3TipitakaDiscussionsChannelWithHttpInfo(fixtures()['discussion_channel']));
+    assert_status(200, $res, 'tipitaka-discussions');
+    assert_has_key('data', $res['body'], 'tipitaka-discussions');
+    assert_has_key('meta', $res['body'], 'tipitaka-discussions');
+});
+
+test('GET /v3/tipitaka-discussions/{channel}(chapter 不带 book)→ 422', function () {
+    $res = raw('GET', '/v3/tipitaka-discussions/' . fixtures()['discussion_channel'], ['chapter' => 1]);
+    assert_status(422, $res, 'chapter 不带 book');
+    assert_field_error('book', $res, 'chapter 不带 book');
+});
+
+test('GET /v3/tipitaka-discussions/{channel}(非法 type)→ 422', function () {
+    $res = raw('GET', '/v3/tipitaka-discussions/' . fixtures()['discussion_channel'], ['type' => 'issue,bogus']);
+    assert_status(422, $res, '非法 type');
+    assert_field_error('type', $res, '非法 type');
+});
+
+test('GET /v3/tipitaka-discussions/{非 uuid} → 404', function () {
+    assert_status(404, raw('GET', '/v3/tipitaka-discussions/not-a-uuid'), '非 uuid 的 channel');
+});
+
+test('tipitaka-discussions 一轮:在 fixture 句子上建 issue → 按段落列出、带句子坐标 → 按类型过滤 → 删', function () {
+    $token = required_token();
+    [$book, $para] = tipitaka_discussion_coord();
+    $stamp = 'api-test-tipitaka-discussions-' . date('Ymd-His');
+
+    $create = raw('POST', '/v3/discussions', null, [
+        'target_type' => 'sentence', 'target_id' => fixtures()['discussion_sentence'],
+        'type' => 'issue', 'severity' => 'suggestion', 'title' => $stamp,
+        'content' => '由 api-test 建立,测完即删', 'notify' => false,
+    ], $token);
+    assert_status(201, $create, '建 issue');
+    $id = $create['body']['data']['id'] ?? null;
+    if (! $id) {
+        fail('建 issue:应返回 id');
+    }
+
+    try {
+        $client = api(TipitakaDiscussionsApi::class, $token);
+        $list = request(fn () => $client->getApiV3TipitakaDiscussionsChannelWithHttpInfo(
+            fixtures()['discussion_channel'], 200, $book, null, $para, null, 'issue'));
+        assert_status(200, $list, '按段落列 issue');
+        $hit = null;
+        foreach ($list['body']['data'] ?? [] as $row) {
+            if (($row['id'] ?? null) === $id) {
+                $hit = $row;
+            }
+        }
+        if (! $hit) {
+            fail('按段落列 issue:应包含刚建的那条');
+        }
+        assert_eq($book, $hit['sentence']['book'] ?? null, '句子坐标 book');
+        assert_eq($para, $hit['sentence']['paragraph'] ?? null, '句子坐标 paragraph');
+
+        // 按别的类型过滤就不该出现
+        $glossary = request(fn () => $client->getApiV3TipitakaDiscussionsChannelWithHttpInfo(
+            fixtures()['discussion_channel'], 200, $book, null, $para, null, 'glossary'));
+        assert_status(200, $glossary, '按 glossary 过滤');
+        assert_eq(false, in_array($id, array_column($glossary['body']['data'] ?? [], 'id'), true), '按 glossary 过滤不含 issue');
+    } finally {
+        $del = raw('DELETE', "/v3/discussions/{$id}", null, null, $token);
+    }
+    assert_status(200, $del, '删 issue');
+});

+ 6 - 0
api-test/client/.openapi-generator/FILES

@@ -16,6 +16,7 @@ docs/Api/ReactionsApi.md
 docs/Api/SearchApi.md
 docs/Api/SearchApi.md
 docs/Api/SearchSuggestApi.md
 docs/Api/SearchSuggestApi.md
 docs/Api/SessionsApi.md
 docs/Api/SessionsApi.md
+docs/Api/TipitakaDiscussionsApi.md
 docs/Api/TipitakaReadingApi.md
 docs/Api/TipitakaReadingApi.md
 docs/Api/TipitakaRelatedParagraphsApi.md
 docs/Api/TipitakaRelatedParagraphsApi.md
 docs/Api/UpgradeApi.md
 docs/Api/UpgradeApi.md
@@ -24,6 +25,7 @@ docs/Model/GetApiV3Discussions200Response.md
 docs/Model/GetApiV3Discussions200ResponseDataInner.md
 docs/Model/GetApiV3Discussions200ResponseDataInner.md
 docs/Model/GetApiV3Discussions200ResponseDataInnerAbilities.md
 docs/Model/GetApiV3Discussions200ResponseDataInnerAbilities.md
 docs/Model/GetApiV3Discussions200ResponseDataInnerEditor.md
 docs/Model/GetApiV3Discussions200ResponseDataInnerEditor.md
+docs/Model/GetApiV3Discussions200ResponseDataInnerSentence.md
 docs/Model/GetApiV3DiscussionsSummary200Response.md
 docs/Model/GetApiV3DiscussionsSummary200Response.md
 docs/Model/GetApiV3DiscussionsSummary200ResponseData.md
 docs/Model/GetApiV3DiscussionsSummary200ResponseData.md
 docs/Model/GetApiV3DiscussionsSummary200ResponseDataStatusCounts.md
 docs/Model/GetApiV3DiscussionsSummary200ResponseDataStatusCounts.md
@@ -93,6 +95,7 @@ lib/Api/ReactionsApi.php
 lib/Api/SearchApi.php
 lib/Api/SearchApi.php
 lib/Api/SearchSuggestApi.php
 lib/Api/SearchSuggestApi.php
 lib/Api/SessionsApi.php
 lib/Api/SessionsApi.php
+lib/Api/TipitakaDiscussionsApi.php
 lib/Api/TipitakaReadingApi.php
 lib/Api/TipitakaReadingApi.php
 lib/Api/TipitakaRelatedParagraphsApi.php
 lib/Api/TipitakaRelatedParagraphsApi.php
 lib/Api/UpgradeApi.php
 lib/Api/UpgradeApi.php
@@ -105,6 +108,7 @@ lib/Model/GetApiV3Discussions200Response.php
 lib/Model/GetApiV3Discussions200ResponseDataInner.php
 lib/Model/GetApiV3Discussions200ResponseDataInner.php
 lib/Model/GetApiV3Discussions200ResponseDataInnerAbilities.php
 lib/Model/GetApiV3Discussions200ResponseDataInnerAbilities.php
 lib/Model/GetApiV3Discussions200ResponseDataInnerEditor.php
 lib/Model/GetApiV3Discussions200ResponseDataInnerEditor.php
+lib/Model/GetApiV3Discussions200ResponseDataInnerSentence.php
 lib/Model/GetApiV3DiscussionsSummary200Response.php
 lib/Model/GetApiV3DiscussionsSummary200Response.php
 lib/Model/GetApiV3DiscussionsSummary200ResponseData.php
 lib/Model/GetApiV3DiscussionsSummary200ResponseData.php
 lib/Model/GetApiV3DiscussionsSummary200ResponseDataStatusCounts.php
 lib/Model/GetApiV3DiscussionsSummary200ResponseDataStatusCounts.php
@@ -176,12 +180,14 @@ test/Api/ReactionsApiTest.php
 test/Api/SearchApiTest.php
 test/Api/SearchApiTest.php
 test/Api/SearchSuggestApiTest.php
 test/Api/SearchSuggestApiTest.php
 test/Api/SessionsApiTest.php
 test/Api/SessionsApiTest.php
+test/Api/TipitakaDiscussionsApiTest.php
 test/Api/TipitakaReadingApiTest.php
 test/Api/TipitakaReadingApiTest.php
 test/Api/TipitakaRelatedParagraphsApiTest.php
 test/Api/TipitakaRelatedParagraphsApiTest.php
 test/Api/UpgradeApiTest.php
 test/Api/UpgradeApiTest.php
 test/Api/UsersApiTest.php
 test/Api/UsersApiTest.php
 test/Model/GetApiV3Discussions200ResponseDataInnerAbilitiesTest.php
 test/Model/GetApiV3Discussions200ResponseDataInnerAbilitiesTest.php
 test/Model/GetApiV3Discussions200ResponseDataInnerEditorTest.php
 test/Model/GetApiV3Discussions200ResponseDataInnerEditorTest.php
+test/Model/GetApiV3Discussions200ResponseDataInnerSentenceTest.php
 test/Model/GetApiV3Discussions200ResponseDataInnerTest.php
 test/Model/GetApiV3Discussions200ResponseDataInnerTest.php
 test/Model/GetApiV3Discussions200ResponseTest.php
 test/Model/GetApiV3Discussions200ResponseTest.php
 test/Model/GetApiV3DiscussionsSummary200ResponseDataStatusCountsTest.php
 test/Model/GetApiV3DiscussionsSummary200ResponseDataStatusCountsTest.php

+ 1 - 1
api-test/client/lib/Api/AgentGrantsApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/DiscussionsApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/EmailCertificationsApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/HeartbeatApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/InvitesApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/MeApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/PasswordResetsApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/ProgressApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/ReactionsApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/SearchApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/SearchSuggestApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/SessionsApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 636 - 0
api-test/client/lib/Api/TipitakaDiscussionsApi.php

@@ -0,0 +1,636 @@
+<?php
+/**
+ * TipitakaDiscussionsApi
+ * PHP version 8.1
+ *
+ * @category Class
+ * @package  WikipaliApi
+ * @author   OpenAPI Generator team
+ * @link     https://openapi-generator.tech
+ */
+
+/**
+ * MINT API
+ *
+ * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
+ *
+ * The version of the OpenAPI document: 2026.10.21
+ * Generated by: https://openapi-generator.tech
+ * Generator version: 7.25.0
+ */
+
+/**
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+namespace WikipaliApi\Api;
+
+use GuzzleHttp\Client;
+use GuzzleHttp\ClientInterface;
+use GuzzleHttp\Exception\ConnectException;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Psr7\MultipartStream;
+use GuzzleHttp\Psr7\Request;
+use GuzzleHttp\RequestOptions;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use WikipaliApi\ApiException;
+use WikipaliApi\Configuration;
+use WikipaliApi\FormDataProcessor;
+use WikipaliApi\HeaderSelector;
+use WikipaliApi\ObjectSerializer;
+
+/**
+ * TipitakaDiscussionsApi Class Doc Comment
+ *
+ * @category Class
+ * @package  WikipaliApi
+ * @author   OpenAPI Generator team
+ * @link     https://openapi-generator.tech
+ */
+class TipitakaDiscussionsApi
+{
+    /**
+     * @var ClientInterface
+     */
+    protected $client;
+
+    /**
+     * @var Configuration
+     */
+    protected $config;
+
+    /**
+     * @var HeaderSelector
+     */
+    protected $headerSelector;
+
+    /**
+     * @var int Host index
+     */
+    protected $hostIndex;
+
+    /** @var string[] $contentTypes **/
+    public const contentTypes = [
+        'getApiV3TipitakaDiscussionsChannel' => [
+            'application/json',
+        ],
+    ];
+
+    /**
+     * @param ClientInterface $client
+     * @param Configuration   $config
+     * @param HeaderSelector  $selector
+     * @param int             $hostIndex (Optional) host index to select the list of hosts if defined in the OpenAPI spec
+     */
+    public function __construct(
+        ?ClientInterface $client = null,
+        ?Configuration $config = null,
+        ?HeaderSelector $selector = null,
+        int $hostIndex = 0
+    ) {
+        $this->client = $client ?: new Client();
+        $this->config = $config ?: Configuration::getDefaultConfiguration();
+        $this->headerSelector = $selector ?: new HeaderSelector();
+        $this->hostIndex = $hostIndex;
+    }
+
+    /**
+     * Set the host index
+     *
+     * @param int $hostIndex Host index (required)
+     */
+    public function setHostIndex($hostIndex): void
+    {
+        $this->hostIndex = $hostIndex;
+    }
+
+    /**
+     * Get the host index
+     *
+     * @return int Host index
+     */
+    public function getHostIndex()
+    {
+        return $this->hostIndex;
+    }
+
+    /**
+     * @return Configuration
+     */
+    public function getConfig()
+    {
+        return $this->config;
+    }
+
+    /**
+     * Operation getApiV3TipitakaDiscussionsChannel
+     *
+     * 列出一段经文里的话题
+     *
+     * @param  string $channel 句子所在 channel 的 uuid (required)
+     * @param  int|null $per_page 每页数量,最大 200 (optional, default to 15)
+     * @param  int|null $book 书号;与 chapter / para / to 一起用时必填,不传则是整个 channel (optional)
+     * @param  int|null $chapter 章节起始段落号,服务端按章节长度展开成段落区间;不能与 para / to 同用 (optional)
+     * @param  int|null $para 起始段落号 (optional)
+     * @param  int|null $to 结束段落号(含),缺省同 para (optional)
+     * @param  string|null $type 类型,逗号分隔多选;不传则只列评论线程 (optional)
+     * @param  string|null $status 话题状态,不传则两种都要 (optional)
+     * @param  string|null $severity 严重程度,逗号分隔多选 (optional)
+     * @param  int|null $page 页码 (optional, default to 1)
+     * @param  string $contentType The value for the Content-Type header. Check self::contentTypes['getApiV3TipitakaDiscussionsChannel'] to see the possible values for this operation
+     *
+     * @throws \WikipaliApi\ApiException on non-2xx response or if the response body is not in the expected format
+     * @throws \InvalidArgumentException
+     * @return \WikipaliApi\Model\GetApiV3Discussions200Response|\WikipaliApi\Model\ProblemDetails|\WikipaliApi\Model\ProblemDetails
+     */
+    public function getApiV3TipitakaDiscussionsChannel($channel, $per_page = 15, $book = null, $chapter = null, $para = null, $to = null, $type = null, $status = null, $severity = null, $page = 1, string $contentType = self::contentTypes['getApiV3TipitakaDiscussionsChannel'][0])
+    {
+        list($response) = $this->getApiV3TipitakaDiscussionsChannelWithHttpInfo($channel, $per_page, $book, $chapter, $para, $to, $type, $status, $severity, $page, $contentType);
+        return $response;
+    }
+
+    /**
+     * Operation getApiV3TipitakaDiscussionsChannelWithHttpInfo
+     *
+     * 列出一段经文里的话题
+     *
+     * @param  string $channel 句子所在 channel 的 uuid (required)
+     * @param  int|null $per_page 每页数量,最大 200 (optional, default to 15)
+     * @param  int|null $book 书号;与 chapter / para / to 一起用时必填,不传则是整个 channel (optional)
+     * @param  int|null $chapter 章节起始段落号,服务端按章节长度展开成段落区间;不能与 para / to 同用 (optional)
+     * @param  int|null $para 起始段落号 (optional)
+     * @param  int|null $to 结束段落号(含),缺省同 para (optional)
+     * @param  string|null $type 类型,逗号分隔多选;不传则只列评论线程 (optional)
+     * @param  string|null $status 话题状态,不传则两种都要 (optional)
+     * @param  string|null $severity 严重程度,逗号分隔多选 (optional)
+     * @param  int|null $page 页码 (optional, default to 1)
+     * @param  string $contentType The value for the Content-Type header. Check self::contentTypes['getApiV3TipitakaDiscussionsChannel'] to see the possible values for this operation
+     *
+     * @throws \WikipaliApi\ApiException on non-2xx response or if the response body is not in the expected format
+     * @throws \InvalidArgumentException
+     * @return array of \WikipaliApi\Model\GetApiV3Discussions200Response|\WikipaliApi\Model\ProblemDetails|\WikipaliApi\Model\ProblemDetails, HTTP status code, HTTP response headers (array of strings)
+     */
+    public function getApiV3TipitakaDiscussionsChannelWithHttpInfo($channel, $per_page = 15, $book = null, $chapter = null, $para = null, $to = null, $type = null, $status = null, $severity = null, $page = 1, string $contentType = self::contentTypes['getApiV3TipitakaDiscussionsChannel'][0])
+    {
+        $request = $this->getApiV3TipitakaDiscussionsChannelRequest($channel, $per_page, $book, $chapter, $para, $to, $type, $status, $severity, $page, $contentType);
+
+        try {
+            $options = $this->createHttpClientOption();
+            try {
+                $response = $this->client->send($request, $options);
+            } catch (RequestException $e) {
+                throw new ApiException(
+                    "[{$e->getCode()}] {$e->getMessage()}",
+                    (int) $e->getCode(),
+                    $e->getResponse() ? $e->getResponse()->getHeaders() : null,
+                    $e->getResponse() ? (string) $e->getResponse()->getBody() : null
+                );
+            } catch (ConnectException $e) {
+                throw new ApiException(
+                    "[{$e->getCode()}] {$e->getMessage()}",
+                    (int) $e->getCode(),
+                    null,
+                    null
+                );
+            }
+
+            $statusCode = $response->getStatusCode();
+
+
+            switch($statusCode) {
+                case 200:
+                    return $this->handleResponseWithDataType(
+                        '\WikipaliApi\Model\GetApiV3Discussions200Response',
+                        $request,
+                        $response,
+                    );
+                case 404:
+                    return $this->handleResponseWithDataType(
+                        '\WikipaliApi\Model\ProblemDetails',
+                        $request,
+                        $response,
+                    );
+                case 422:
+                    return $this->handleResponseWithDataType(
+                        '\WikipaliApi\Model\ProblemDetails',
+                        $request,
+                        $response,
+                    );
+            }
+
+            
+
+            if ($statusCode < 200 || $statusCode > 299) {
+                throw new ApiException(
+                    sprintf(
+                        '[%d] Error connecting to the API (%s)',
+                        $statusCode,
+                        (string) $request->getUri()
+                    ),
+                    $statusCode,
+                    $response->getHeaders(),
+                    (string) $response->getBody()
+                );
+            }
+
+            return $this->handleResponseWithDataType(
+                '\WikipaliApi\Model\GetApiV3Discussions200Response',
+                $request,
+                $response,
+            );
+        } catch (ApiException $e) {
+            switch ($e->getCode()) {
+                case 200:
+                    $data = ObjectSerializer::deserialize(
+                        $e->getResponseBody(),
+                        '\WikipaliApi\Model\GetApiV3Discussions200Response',
+                        $e->getResponseHeaders()
+                    );
+                    $e->setResponseObject($data);
+                    throw $e;
+                case 404:
+                    $data = ObjectSerializer::deserialize(
+                        $e->getResponseBody(),
+                        '\WikipaliApi\Model\ProblemDetails',
+                        $e->getResponseHeaders()
+                    );
+                    $e->setResponseObject($data);
+                    throw $e;
+                case 422:
+                    $data = ObjectSerializer::deserialize(
+                        $e->getResponseBody(),
+                        '\WikipaliApi\Model\ProblemDetails',
+                        $e->getResponseHeaders()
+                    );
+                    $e->setResponseObject($data);
+                    throw $e;
+            }
+        
+
+            throw $e;
+        }
+    }
+
+    /**
+     * Operation getApiV3TipitakaDiscussionsChannelAsync
+     *
+     * 列出一段经文里的话题
+     *
+     * @param  string $channel 句子所在 channel 的 uuid (required)
+     * @param  int|null $per_page 每页数量,最大 200 (optional, default to 15)
+     * @param  int|null $book 书号;与 chapter / para / to 一起用时必填,不传则是整个 channel (optional)
+     * @param  int|null $chapter 章节起始段落号,服务端按章节长度展开成段落区间;不能与 para / to 同用 (optional)
+     * @param  int|null $para 起始段落号 (optional)
+     * @param  int|null $to 结束段落号(含),缺省同 para (optional)
+     * @param  string|null $type 类型,逗号分隔多选;不传则只列评论线程 (optional)
+     * @param  string|null $status 话题状态,不传则两种都要 (optional)
+     * @param  string|null $severity 严重程度,逗号分隔多选 (optional)
+     * @param  int|null $page 页码 (optional, default to 1)
+     * @param  string $contentType The value for the Content-Type header. Check self::contentTypes['getApiV3TipitakaDiscussionsChannel'] to see the possible values for this operation
+     *
+     * @throws \InvalidArgumentException
+     * @return \GuzzleHttp\Promise\PromiseInterface
+     */
+    public function getApiV3TipitakaDiscussionsChannelAsync($channel, $per_page = 15, $book = null, $chapter = null, $para = null, $to = null, $type = null, $status = null, $severity = null, $page = 1, string $contentType = self::contentTypes['getApiV3TipitakaDiscussionsChannel'][0])
+    {
+        return $this->getApiV3TipitakaDiscussionsChannelAsyncWithHttpInfo($channel, $per_page, $book, $chapter, $para, $to, $type, $status, $severity, $page, $contentType)
+            ->then(
+                function ($response) {
+                    return $response[0];
+                }
+            );
+    }
+
+    /**
+     * Operation getApiV3TipitakaDiscussionsChannelAsyncWithHttpInfo
+     *
+     * 列出一段经文里的话题
+     *
+     * @param  string $channel 句子所在 channel 的 uuid (required)
+     * @param  int|null $per_page 每页数量,最大 200 (optional, default to 15)
+     * @param  int|null $book 书号;与 chapter / para / to 一起用时必填,不传则是整个 channel (optional)
+     * @param  int|null $chapter 章节起始段落号,服务端按章节长度展开成段落区间;不能与 para / to 同用 (optional)
+     * @param  int|null $para 起始段落号 (optional)
+     * @param  int|null $to 结束段落号(含),缺省同 para (optional)
+     * @param  string|null $type 类型,逗号分隔多选;不传则只列评论线程 (optional)
+     * @param  string|null $status 话题状态,不传则两种都要 (optional)
+     * @param  string|null $severity 严重程度,逗号分隔多选 (optional)
+     * @param  int|null $page 页码 (optional, default to 1)
+     * @param  string $contentType The value for the Content-Type header. Check self::contentTypes['getApiV3TipitakaDiscussionsChannel'] to see the possible values for this operation
+     *
+     * @throws \InvalidArgumentException
+     * @return \GuzzleHttp\Promise\PromiseInterface
+     */
+    public function getApiV3TipitakaDiscussionsChannelAsyncWithHttpInfo($channel, $per_page = 15, $book = null, $chapter = null, $para = null, $to = null, $type = null, $status = null, $severity = null, $page = 1, string $contentType = self::contentTypes['getApiV3TipitakaDiscussionsChannel'][0])
+    {
+        $returnType = '\WikipaliApi\Model\GetApiV3Discussions200Response';
+        $request = $this->getApiV3TipitakaDiscussionsChannelRequest($channel, $per_page, $book, $chapter, $para, $to, $type, $status, $severity, $page, $contentType);
+
+        return $this->client
+            ->sendAsync($request, $this->createHttpClientOption())
+            ->then(
+                function ($response) use ($returnType) {
+                    if ($returnType === '\SplFileObject') {
+                        $content = $response->getBody(); //stream goes to serializer
+                    } else {
+                        $content = (string) $response->getBody();
+                        if ($returnType !== 'string') {
+                            $content = json_decode($content);
+                        }
+                    }
+
+                    return [
+                        ObjectSerializer::deserialize($content, $returnType, []),
+                        $response->getStatusCode(),
+                        $response->getHeaders()
+                    ];
+                },
+                function ($exception) {
+                    $response = $exception->getResponse();
+                    $statusCode = $response->getStatusCode();
+                    throw new ApiException(
+                        sprintf(
+                            '[%d] Error connecting to the API (%s)',
+                            $statusCode,
+                            $exception->getRequest()->getUri()
+                        ),
+                        $statusCode,
+                        $response->getHeaders(),
+                        (string) $response->getBody()
+                    );
+                }
+            );
+    }
+
+    /**
+     * Create request for operation 'getApiV3TipitakaDiscussionsChannel'
+     *
+     * @param  string $channel 句子所在 channel 的 uuid (required)
+     * @param  int|null $per_page 每页数量,最大 200 (optional, default to 15)
+     * @param  int|null $book 书号;与 chapter / para / to 一起用时必填,不传则是整个 channel (optional)
+     * @param  int|null $chapter 章节起始段落号,服务端按章节长度展开成段落区间;不能与 para / to 同用 (optional)
+     * @param  int|null $para 起始段落号 (optional)
+     * @param  int|null $to 结束段落号(含),缺省同 para (optional)
+     * @param  string|null $type 类型,逗号分隔多选;不传则只列评论线程 (optional)
+     * @param  string|null $status 话题状态,不传则两种都要 (optional)
+     * @param  string|null $severity 严重程度,逗号分隔多选 (optional)
+     * @param  int|null $page 页码 (optional, default to 1)
+     * @param  string $contentType The value for the Content-Type header. Check self::contentTypes['getApiV3TipitakaDiscussionsChannel'] to see the possible values for this operation
+     *
+     * @throws \InvalidArgumentException
+     * @return \GuzzleHttp\Psr7\Request
+     */
+    public function getApiV3TipitakaDiscussionsChannelRequest($channel, $per_page = 15, $book = null, $chapter = null, $para = null, $to = null, $type = null, $status = null, $severity = null, $page = 1, string $contentType = self::contentTypes['getApiV3TipitakaDiscussionsChannel'][0])
+    {
+
+        // verify the required parameter 'channel' is set
+        if ($channel === null || (is_array($channel) && count($channel) === 0)) {
+            throw new \InvalidArgumentException(
+                'Missing the required parameter $channel when calling getApiV3TipitakaDiscussionsChannel'
+            );
+        }
+
+
+
+
+
+
+
+
+
+
+
+        $resourcePath = '/v3/tipitaka-discussions/{channel}';
+        $formParams = [];
+        $queryParams = [];
+        $headerParams = [];
+        $httpBody = '';
+        $multipart = false;
+
+        // query params
+        $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue(
+            $per_page,
+            'per_page', // param base name
+            'integer', // openApiType
+            'form', // style
+            true, // explode
+            false // required
+        ) ?? []);
+        // query params
+        $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue(
+            $book,
+            'book', // param base name
+            'integer', // openApiType
+            'form', // style
+            true, // explode
+            false // required
+        ) ?? []);
+        // query params
+        $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue(
+            $chapter,
+            'chapter', // param base name
+            'integer', // openApiType
+            'form', // style
+            true, // explode
+            false // required
+        ) ?? []);
+        // query params
+        $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue(
+            $para,
+            'para', // param base name
+            'integer', // openApiType
+            'form', // style
+            true, // explode
+            false // required
+        ) ?? []);
+        // query params
+        $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue(
+            $to,
+            'to', // param base name
+            'integer', // openApiType
+            'form', // style
+            true, // explode
+            false // required
+        ) ?? []);
+        // query params
+        $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue(
+            $type,
+            'type', // param base name
+            'string', // openApiType
+            'form', // style
+            true, // explode
+            false // required
+        ) ?? []);
+        // query params
+        $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue(
+            $status,
+            'status', // param base name
+            'string', // openApiType
+            'form', // style
+            true, // explode
+            false // required
+        ) ?? []);
+        // query params
+        $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue(
+            $severity,
+            'severity', // param base name
+            'string', // openApiType
+            'form', // style
+            true, // explode
+            false // required
+        ) ?? []);
+        // query params
+        $queryParams = array_merge($queryParams, ObjectSerializer::toQueryValue(
+            $page,
+            'page', // param base name
+            'integer', // openApiType
+            'form', // style
+            true, // explode
+            false // required
+        ) ?? []);
+
+
+        // path params
+        if ($channel !== null) {
+            $resourcePath = str_replace(
+                '{channel}',
+                ObjectSerializer::toPathValue($channel),
+                $resourcePath
+            );
+        }
+
+
+        $headers = $this->headerSelector->selectHeaders(
+            ['application/json', 'application/problem+json', ],
+            $contentType,
+            $multipart
+        );
+
+        // for model (json/xml)
+        if (count($formParams) > 0) {
+            if ($multipart) {
+                $multipartContents = [];
+                foreach ($formParams as $formParamName => $formParamValue) {
+                    $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue];
+                    foreach ($formParamValueItems as $formParamValueItem) {
+                        $multipartContents[] = [
+                            'name' => $formParamName,
+                            'contents' => $formParamValueItem
+                        ];
+                    }
+                }
+                // for HTTP post (form)
+                $httpBody = new MultipartStream($multipartContents);
+
+            } elseif (stripos($headers['Content-Type'], 'application/json') !== false) {
+                # if Content-Type contains "application/json", json_encode the form parameters
+                try {
+                    $httpBody = json_encode($formParams, JSON_THROW_ON_ERROR);
+                } catch (\JsonException $e) {
+                    throw new \InvalidArgumentException('json_encode error: ' . $e->getMessage(), 0, $e);
+                }
+            } else {
+                // for HTTP post (form)
+                $httpBody = ObjectSerializer::buildQuery($formParams);
+            }
+        }
+
+        // this endpoint requires Bearer authentication (access token)
+        if (!empty($this->config->getAccessToken())) {
+            $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken();
+        }
+
+        $defaultHeaders = [];
+        if ($this->config->getUserAgent()) {
+            $defaultHeaders['User-Agent'] = $this->config->getUserAgent();
+        }
+
+        $headers = array_merge(
+            $defaultHeaders,
+            $headerParams,
+            $headers
+        );
+
+        $operationHost = $this->config->getHost();
+        $query = ObjectSerializer::buildQuery($queryParams);
+        return new Request(
+            'GET',
+            $operationHost . $resourcePath . ($query ? "?{$query}" : ''),
+            $headers,
+            $httpBody
+        );
+    }
+
+    /**
+     * Create http client option
+     *
+     * @throws \RuntimeException on file opening failure
+     * @return array of http client options
+     */
+    protected function createHttpClientOption()
+    {
+        $options = [];
+        if ($this->config->getDebug()) {
+            $options[RequestOptions::DEBUG] = fopen($this->config->getDebugFile(), 'a');
+            if (!$options[RequestOptions::DEBUG]) {
+                throw new \RuntimeException('Failed to open the debug file: ' . $this->config->getDebugFile());
+            }
+        }
+
+        if ($this->config->getCertFile()) {
+            $options[RequestOptions::CERT] = $this->config->getCertFile();
+        }
+
+        if ($this->config->getKeyFile()) {
+            $options[RequestOptions::SSL_KEY] = $this->config->getKeyFile();
+        }
+
+        return $options;
+    }
+
+    private function handleResponseWithDataType(
+        string $dataType,
+        RequestInterface $request,
+        ResponseInterface $response
+    ): array {
+        if ($dataType === '\SplFileObject') {
+            $content = $response->getBody(); //stream goes to serializer
+        } else {
+            $content = (string) $response->getBody();
+            if ($dataType !== 'string') {
+                try {
+                    $content = json_decode($content, false, 512, JSON_THROW_ON_ERROR);
+                } catch (\JsonException $exception) {
+                    throw new ApiException(
+                        sprintf(
+                            'Error JSON decoding server response (%s)',
+                            $request->getUri()
+                        ),
+                        $response->getStatusCode(),
+                        $response->getHeaders(),
+                        $content
+                    );
+                }
+            }
+        }
+
+        return [
+            ObjectSerializer::deserialize($content, $dataType, []),
+            $response->getStatusCode(),
+            $response->getHeaders()
+        ];
+    }
+
+    private function responseWithinRangeCode(
+        string $rangeCode,
+        int $statusCode
+    ): bool {
+        $left = (int) ($rangeCode[0].'00');
+        $right = (int) ($rangeCode[0].'99');
+
+        return $statusCode >= $left && $statusCode <= $right;
+    }
+}

+ 1 - 1
api-test/client/lib/Api/TipitakaReadingApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/TipitakaRelatedParagraphsApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/UpgradeApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Api/UsersApi.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/ApiException.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 2 - 2
api-test/client/lib/Configuration.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */
@@ -489,7 +489,7 @@ class Configuration
         $report  = 'PHP SDK (WikipaliApi) Debug Report:' . PHP_EOL;
         $report  = 'PHP SDK (WikipaliApi) Debug Report:' . PHP_EOL;
         $report .= '    OS: ' . php_uname() . PHP_EOL;
         $report .= '    OS: ' . php_uname() . PHP_EOL;
         $report .= '    PHP Version: ' . PHP_VERSION . PHP_EOL;
         $report .= '    PHP Version: ' . PHP_VERSION . PHP_EOL;
-        $report .= '    The version of the OpenAPI document: 2026.10.15' . PHP_EOL;
+        $report .= '    The version of the OpenAPI document: 2026.10.21' . PHP_EOL;
         $report .= '    SDK Package Version: 1.0.0' . PHP_EOL;
         $report .= '    SDK Package Version: 1.0.0' . PHP_EOL;
         $report .= '    Temp Folder Path: ' . self::getDefaultConfiguration()->getTempFolderPath() . PHP_EOL;
         $report .= '    Temp Folder Path: ' . self::getDefaultConfiguration()->getTempFolderPath() . PHP_EOL;
 
 

+ 1 - 1
api-test/client/lib/FormDataProcessor.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/HeaderSelector.php

@@ -14,7 +14,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3Discussions200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 35 - 1
api-test/client/lib/Model/GetApiV3Discussions200ResponseDataInner.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */
@@ -76,6 +76,7 @@ class GetApiV3Discussions200ResponseDataInner implements ModelInterface, ArrayAc
         'quote_prefix' => 'string',
         'quote_prefix' => 'string',
         'quote_suffix' => 'string',
         'quote_suffix' => 'string',
         'reply_count' => 'int',
         'reply_count' => 'int',
+        'sentence' => '\WikipaliApi\Model\GetApiV3Discussions200ResponseDataInnerSentence',
         'editor' => '\WikipaliApi\Model\GetApiV3Discussions200ResponseDataInnerEditor',
         'editor' => '\WikipaliApi\Model\GetApiV3Discussions200ResponseDataInnerEditor',
         'co_editor' => '\WikipaliApi\Model\GetApiV3Discussions200ResponseDataInnerEditor',
         'co_editor' => '\WikipaliApi\Model\GetApiV3Discussions200ResponseDataInnerEditor',
         'abilities' => '\WikipaliApi\Model\GetApiV3Discussions200ResponseDataInnerAbilities',
         'abilities' => '\WikipaliApi\Model\GetApiV3Discussions200ResponseDataInnerAbilities',
@@ -111,6 +112,7 @@ class GetApiV3Discussions200ResponseDataInner implements ModelInterface, ArrayAc
         'quote_prefix' => null,
         'quote_prefix' => null,
         'quote_suffix' => null,
         'quote_suffix' => null,
         'reply_count' => null,
         'reply_count' => null,
+        'sentence' => null,
         'editor' => null,
         'editor' => null,
         'co_editor' => null,
         'co_editor' => null,
         'abilities' => null,
         'abilities' => null,
@@ -144,6 +146,7 @@ class GetApiV3Discussions200ResponseDataInner implements ModelInterface, ArrayAc
         'quote_prefix' => true,
         'quote_prefix' => true,
         'quote_suffix' => true,
         'quote_suffix' => true,
         'reply_count' => false,
         'reply_count' => false,
+        'sentence' => false,
         'editor' => true,
         'editor' => true,
         'co_editor' => true,
         'co_editor' => true,
         'abilities' => false,
         'abilities' => false,
@@ -257,6 +260,7 @@ class GetApiV3Discussions200ResponseDataInner implements ModelInterface, ArrayAc
         'quote_prefix' => 'quote_prefix',
         'quote_prefix' => 'quote_prefix',
         'quote_suffix' => 'quote_suffix',
         'quote_suffix' => 'quote_suffix',
         'reply_count' => 'reply_count',
         'reply_count' => 'reply_count',
+        'sentence' => 'sentence',
         'editor' => 'editor',
         'editor' => 'editor',
         'co_editor' => 'co_editor',
         'co_editor' => 'co_editor',
         'abilities' => 'abilities',
         'abilities' => 'abilities',
@@ -290,6 +294,7 @@ class GetApiV3Discussions200ResponseDataInner implements ModelInterface, ArrayAc
         'quote_prefix' => 'setQuotePrefix',
         'quote_prefix' => 'setQuotePrefix',
         'quote_suffix' => 'setQuoteSuffix',
         'quote_suffix' => 'setQuoteSuffix',
         'reply_count' => 'setReplyCount',
         'reply_count' => 'setReplyCount',
+        'sentence' => 'setSentence',
         'editor' => 'setEditor',
         'editor' => 'setEditor',
         'co_editor' => 'setCoEditor',
         'co_editor' => 'setCoEditor',
         'abilities' => 'setAbilities',
         'abilities' => 'setAbilities',
@@ -323,6 +328,7 @@ class GetApiV3Discussions200ResponseDataInner implements ModelInterface, ArrayAc
         'quote_prefix' => 'getQuotePrefix',
         'quote_prefix' => 'getQuotePrefix',
         'quote_suffix' => 'getQuoteSuffix',
         'quote_suffix' => 'getQuoteSuffix',
         'reply_count' => 'getReplyCount',
         'reply_count' => 'getReplyCount',
+        'sentence' => 'getSentence',
         'editor' => 'getEditor',
         'editor' => 'getEditor',
         'co_editor' => 'getCoEditor',
         'co_editor' => 'getCoEditor',
         'abilities' => 'getAbilities',
         'abilities' => 'getAbilities',
@@ -407,6 +413,7 @@ class GetApiV3Discussions200ResponseDataInner implements ModelInterface, ArrayAc
         $this->setIfExists('quote_prefix', $data ?? [], null);
         $this->setIfExists('quote_prefix', $data ?? [], null);
         $this->setIfExists('quote_suffix', $data ?? [], null);
         $this->setIfExists('quote_suffix', $data ?? [], null);
         $this->setIfExists('reply_count', $data ?? [], null);
         $this->setIfExists('reply_count', $data ?? [], null);
+        $this->setIfExists('sentence', $data ?? [], null);
         $this->setIfExists('editor', $data ?? [], null);
         $this->setIfExists('editor', $data ?? [], null);
         $this->setIfExists('co_editor', $data ?? [], null);
         $this->setIfExists('co_editor', $data ?? [], null);
         $this->setIfExists('abilities', $data ?? [], null);
         $this->setIfExists('abilities', $data ?? [], null);
@@ -1040,6 +1047,33 @@ class GetApiV3Discussions200ResponseDataInner implements ModelInterface, ArrayAc
         return $this;
         return $this;
     }
     }
 
 
+    /**
+     * Gets sentence
+     *
+     * @return \WikipaliApi\Model\GetApiV3Discussions200ResponseDataInnerSentence|null
+     */
+    public function getSentence()
+    {
+        return $this->container['sentence'];
+    }
+
+    /**
+     * Sets sentence
+     *
+     * @param \WikipaliApi\Model\GetApiV3Discussions200ResponseDataInnerSentence|null $sentence sentence
+     *
+     * @return self
+     */
+    public function setSentence($sentence)
+    {
+        if (is_null($sentence)) {
+            throw new \InvalidArgumentException('non-nullable sentence cannot be null');
+        }
+        $this->container['sentence'] = $sentence;
+
+        return $this;
+    }
+
     /**
     /**
      * Gets editor
      * Gets editor
      *
      *

+ 1 - 1
api-test/client/lib/Model/GetApiV3Discussions200ResponseDataInnerAbilities.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3Discussions200ResponseDataInnerEditor.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 512 - 0
api-test/client/lib/Model/GetApiV3Discussions200ResponseDataInnerSentence.php

@@ -0,0 +1,512 @@
+<?php
+/**
+ * GetApiV3Discussions200ResponseDataInnerSentence
+ *
+ * PHP version 8.1
+ *
+ * @category Class
+ * @package  WikipaliApi
+ * @author   OpenAPI Generator team
+ * @link     https://openapi-generator.tech
+ */
+
+/**
+ * MINT API
+ *
+ * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
+ *
+ * The version of the OpenAPI document: 2026.10.21
+ * Generated by: https://openapi-generator.tech
+ * Generator version: 7.25.0
+ */
+
+/**
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+namespace WikipaliApi\Model;
+
+use \ArrayAccess;
+use \WikipaliApi\ObjectSerializer;
+
+/**
+ * GetApiV3Discussions200ResponseDataInnerSentence Class Doc Comment
+ *
+ * @category Class
+ * @description 仅在部分查询口径下返回。
+ * @package  WikipaliApi
+ * @author   OpenAPI Generator team
+ * @link     https://openapi-generator.tech
+ * @implements \ArrayAccess<string, mixed>
+ */
+class GetApiV3Discussions200ResponseDataInnerSentence implements ModelInterface, ArrayAccess, \JsonSerializable
+{
+    public const DISCRIMINATOR = null;
+
+    /**
+     * The original name of the model.
+     *
+     * @var string
+     */
+    protected static $openAPIModelName = 'get_api_v3_discussions_200_response_data_inner_sentence';
+
+    /**
+     * Array of property to type mappings. Used for (de)serialization
+     *
+     * @var string[]
+     */
+    protected static $openAPITypes = [
+        'book' => 'int',
+        'paragraph' => 'int',
+        'word_start' => 'int',
+        'word_end' => 'int'
+    ];
+
+    /**
+     * Array of property to format mappings. Used for (de)serialization
+     *
+     * @var string[]
+     * @phpstan-var array<string, string|null>
+     * @psalm-var array<string, string|null>
+     */
+    protected static $openAPIFormats = [
+        'book' => null,
+        'paragraph' => null,
+        'word_start' => null,
+        'word_end' => null
+    ];
+
+    /**
+     * Array of nullable properties. Used for (de)serialization
+     *
+     * @var boolean[]
+     */
+    protected static array $openAPINullables = [
+        'book' => false,
+        'paragraph' => false,
+        'word_start' => false,
+        'word_end' => false
+    ];
+
+    /**
+     * If a nullable field gets set to null, insert it here
+     *
+     * @var boolean[]
+     */
+    protected array $openAPINullablesSetToNull = [];
+
+    /**
+     * Array of property to type mappings. Used for (de)serialization
+     *
+     * @return array
+     */
+    public static function openAPITypes()
+    {
+        return self::$openAPITypes;
+    }
+
+    /**
+     * Array of property to format mappings. Used for (de)serialization
+     *
+     * @return array
+     */
+    public static function openAPIFormats()
+    {
+        return self::$openAPIFormats;
+    }
+
+    /**
+     * Array of nullable properties
+     *
+     * @return array
+     */
+    protected static function openAPINullables(): array
+    {
+        return self::$openAPINullables;
+    }
+
+    /**
+     * Array of nullable field names deliberately set to null
+     *
+     * @return boolean[]
+     */
+    private function getOpenAPINullablesSetToNull(): array
+    {
+        return $this->openAPINullablesSetToNull;
+    }
+
+    /**
+     * Setter - Array of nullable field names deliberately set to null
+     *
+     * @param boolean[] $openAPINullablesSetToNull
+     */
+    private function setOpenAPINullablesSetToNull(array $openAPINullablesSetToNull): void
+    {
+        $this->openAPINullablesSetToNull = $openAPINullablesSetToNull;
+    }
+
+    /**
+     * Checks if a property is nullable
+     *
+     * @param string $property
+     * @return bool
+     */
+    public static function isNullable(string $property): bool
+    {
+        return self::openAPINullables()[$property] ?? false;
+    }
+
+    /**
+     * Checks if a nullable property is set to null.
+     *
+     * @param string $property
+     * @return bool
+     */
+    public function isNullableSetToNull(string $property): bool
+    {
+        return in_array($property, $this->getOpenAPINullablesSetToNull(), true);
+    }
+
+    /**
+     * Array of attributes where the key is the local name,
+     * and the value is the original name
+     *
+     * @var string[]
+     */
+    protected static $attributeMap = [
+        'book' => 'book',
+        'paragraph' => 'paragraph',
+        'word_start' => 'word_start',
+        'word_end' => 'word_end'
+    ];
+
+    /**
+     * Array of attributes to setter functions (for deserialization of responses)
+     *
+     * @var string[]
+     */
+    protected static $setters = [
+        'book' => 'setBook',
+        'paragraph' => 'setParagraph',
+        'word_start' => 'setWordStart',
+        'word_end' => 'setWordEnd'
+    ];
+
+    /**
+     * Array of attributes to getter functions (for serialization of requests)
+     *
+     * @var string[]
+     */
+    protected static $getters = [
+        'book' => 'getBook',
+        'paragraph' => 'getParagraph',
+        'word_start' => 'getWordStart',
+        'word_end' => 'getWordEnd'
+    ];
+
+    /**
+     * Array of attributes where the key is the local name,
+     * and the value is the original name
+     *
+     * @return array
+     */
+    public static function attributeMap()
+    {
+        return self::$attributeMap;
+    }
+
+    /**
+     * Array of attributes to setter functions (for deserialization of responses)
+     *
+     * @return array
+     */
+    public static function setters()
+    {
+        return self::$setters;
+    }
+
+    /**
+     * Array of attributes to getter functions (for serialization of requests)
+     *
+     * @return array
+     */
+    public static function getters()
+    {
+        return self::$getters;
+    }
+
+    /**
+     * The original name of the model.
+     *
+     * @return string
+     */
+    public function getModelName()
+    {
+        return self::$openAPIModelName;
+    }
+
+
+    /**
+     * Associative array for storing property values
+     *
+     * @var mixed[]
+     */
+    protected $container = [];
+
+    /**
+     * Constructor
+     *
+     * @param mixed[]|null $data Associated array of property values
+     *                      initializing the model
+     */
+    public function __construct(?array $data = null)
+    {
+        $this->setIfExists('book', $data ?? [], null);
+        $this->setIfExists('paragraph', $data ?? [], null);
+        $this->setIfExists('word_start', $data ?? [], null);
+        $this->setIfExists('word_end', $data ?? [], null);
+    }
+
+    /**
+     * Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName
+     * is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the
+     * $this->openAPINullablesSetToNull array
+     *
+     * @param string $variableName
+     * @param array  $fields
+     * @param mixed  $defaultValue
+     */
+    private function setIfExists(string $variableName, array $fields, $defaultValue): void
+    {
+        if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) {
+            $this->openAPINullablesSetToNull[] = $variableName;
+        }
+
+        $this->container[$variableName] = $fields[$variableName] ?? $defaultValue;
+    }
+
+    /**
+     * Show all the invalid properties with reasons.
+     *
+     * @return array invalid properties with reasons
+     */
+    public function listInvalidProperties()
+    {
+        $invalidProperties = [];
+
+        return $invalidProperties;
+    }
+
+    /**
+     * Validate all the properties in the model
+     * return true if all passed
+     *
+     * @return bool True if all properties are valid
+     */
+    public function valid()
+    {
+        return count($this->listInvalidProperties()) === 0;
+    }
+
+
+    /**
+     * Gets book
+     *
+     * @return int|null
+     */
+    public function getBook()
+    {
+        return $this->container['book'];
+    }
+
+    /**
+     * Sets book
+     *
+     * @param int|null $book book
+     *
+     * @return self
+     */
+    public function setBook($book)
+    {
+        if (is_null($book)) {
+            throw new \InvalidArgumentException('non-nullable book cannot be null');
+        }
+        $this->container['book'] = $book;
+
+        return $this;
+    }
+
+    /**
+     * Gets paragraph
+     *
+     * @return int|null
+     */
+    public function getParagraph()
+    {
+        return $this->container['paragraph'];
+    }
+
+    /**
+     * Sets paragraph
+     *
+     * @param int|null $paragraph paragraph
+     *
+     * @return self
+     */
+    public function setParagraph($paragraph)
+    {
+        if (is_null($paragraph)) {
+            throw new \InvalidArgumentException('non-nullable paragraph cannot be null');
+        }
+        $this->container['paragraph'] = $paragraph;
+
+        return $this;
+    }
+
+    /**
+     * Gets word_start
+     *
+     * @return int|null
+     */
+    public function getWordStart()
+    {
+        return $this->container['word_start'];
+    }
+
+    /**
+     * Sets word_start
+     *
+     * @param int|null $word_start word_start
+     *
+     * @return self
+     */
+    public function setWordStart($word_start)
+    {
+        if (is_null($word_start)) {
+            throw new \InvalidArgumentException('non-nullable word_start cannot be null');
+        }
+        $this->container['word_start'] = $word_start;
+
+        return $this;
+    }
+
+    /**
+     * Gets word_end
+     *
+     * @return int|null
+     */
+    public function getWordEnd()
+    {
+        return $this->container['word_end'];
+    }
+
+    /**
+     * Sets word_end
+     *
+     * @param int|null $word_end word_end
+     *
+     * @return self
+     */
+    public function setWordEnd($word_end)
+    {
+        if (is_null($word_end)) {
+            throw new \InvalidArgumentException('non-nullable word_end cannot be null');
+        }
+        $this->container['word_end'] = $word_end;
+
+        return $this;
+    }
+    /**
+     * Returns true if offset exists. False otherwise.
+     *
+     * @param integer|string $offset Offset
+     *
+     * @return boolean
+     */
+    public function offsetExists(mixed $offset): bool
+    {
+        return isset($this->container[$offset]);
+    }
+
+    /**
+     * Gets offset.
+     *
+     * @param integer|string $offset Offset
+     *
+     * @return mixed|null
+     */
+    #[\ReturnTypeWillChange]
+    public function offsetGet(mixed $offset)
+    {
+        return $this->container[$offset] ?? null;
+    }
+
+    /**
+     * Sets value based on offset.
+     *
+     * @param int|null $offset Offset
+     * @param mixed    $value  Value to be set
+     *
+     * @return void
+     */
+    public function offsetSet($offset, $value): void
+    {
+        if (is_null($offset)) {
+            $this->container[] = $value;
+        } else {
+            $this->container[$offset] = $value;
+        }
+    }
+
+    /**
+     * Unsets offset.
+     *
+     * @param integer|string $offset Offset
+     *
+     * @return void
+     */
+    public function offsetUnset(mixed $offset): void
+    {
+        unset($this->container[$offset]);
+    }
+
+    /**
+     * Serializes the object to a value that can be serialized natively by json_encode().
+     * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php
+     *
+     * @return mixed Returns data which can be serialized by json_encode(), which is a value
+     * of any type other than a resource.
+     */
+    #[\ReturnTypeWillChange]
+    public function jsonSerialize()
+    {
+       return ObjectSerializer::sanitizeForSerialization($this);
+    }
+
+    /**
+     * Gets the string presentation of the object
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return json_encode(
+            ObjectSerializer::sanitizeForSerialization($this),
+            JSON_PRETTY_PRINT
+        );
+    }
+
+    /**
+     * Gets a header-safe presentation of the object
+     *
+     * @return string
+     */
+    public function toHeaderValue()
+    {
+        return json_encode(ObjectSerializer::sanitizeForSerialization($this));
+    }
+}
+
+

+ 1 - 1
api-test/client/lib/Model/GetApiV3DiscussionsSummary200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3DiscussionsSummary200ResponseData.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3DiscussionsSummary200ResponseDataStatusCounts.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3Heartbeat200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3Heartbeat200ResponseData.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3Me200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3Me200ResponseData.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3MeReactions200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3MeReactions200ResponseDataInner.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3PasswordResetsToken200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3PasswordResetsToken200ResponseData.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3Progress200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3Progress200ResponseDataInner.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3ReactionsTally200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3ReactionsTally200ResponseDataInner.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3Search200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3SearchSearch200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3TipitakaReadingChannel200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3TipitakaReadingChannel200ResponseDataInner.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3TipitakaReadingChannel200ResponseMeta.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphs200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphs200ResponseDataInner.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphs200ResponseDataInnerPathInner.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphs200ResponseMeta.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphsAggregate200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphsAggregate200ResponseDataInner.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphsAggregateBookName200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/GetApiV3TipitakaRelatedParagraphsAggregateBookName200ResponseDataInner.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/ModelInterface.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PaginationMeta.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 8 - 6
api-test/client/lib/Model/PatchApiV3DiscussionsDiscussionRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */
@@ -288,6 +288,7 @@ class PatchApiV3DiscussionsDiscussionRequest implements ModelInterface, ArrayAcc
     public const TYPE_QA = 'qa';
     public const TYPE_QA = 'qa';
     public const TYPE_HELP = 'help';
     public const TYPE_HELP = 'help';
     public const TYPE_ISSUE = 'issue';
     public const TYPE_ISSUE = 'issue';
+    public const TYPE_GLOSSARY = 'glossary';
     public const TYPE_NOTE = 'note';
     public const TYPE_NOTE = 'note';
     public const TYPE_COMMENTARY = 'commentary';
     public const TYPE_COMMENTARY = 'commentary';
     public const SEVERITY_ERROR = 'error';
     public const SEVERITY_ERROR = 'error';
@@ -321,6 +322,7 @@ class PatchApiV3DiscussionsDiscussionRequest implements ModelInterface, ArrayAcc
             self::TYPE_QA,
             self::TYPE_QA,
             self::TYPE_HELP,
             self::TYPE_HELP,
             self::TYPE_ISSUE,
             self::TYPE_ISSUE,
+            self::TYPE_GLOSSARY,
             self::TYPE_NOTE,
             self::TYPE_NOTE,
             self::TYPE_COMMENTARY,
             self::TYPE_COMMENTARY,
         ];
         ];
@@ -616,7 +618,7 @@ class PatchApiV3DiscussionsDiscussionRequest implements ModelInterface, ArrayAcc
     /**
     /**
      * Sets pos_start
      * Sets pos_start
      *
      *
-     * @param int|null $pos_start 锚点起始偏移(只限批注)
+     * @param int|null $pos_start 锚点起始偏移(只限 note / commentary / issue / glossary)
      *
      *
      * @return self
      * @return self
      */
      */
@@ -643,7 +645,7 @@ class PatchApiV3DiscussionsDiscussionRequest implements ModelInterface, ArrayAcc
     /**
     /**
      * Sets pos_end
      * Sets pos_end
      *
      *
-     * @param int|null $pos_end 锚点结束偏移(只限批注)
+     * @param int|null $pos_end 锚点结束偏移(只限 note / commentary / issue / glossary)
      *
      *
      * @return self
      * @return self
      */
      */
@@ -670,7 +672,7 @@ class PatchApiV3DiscussionsDiscussionRequest implements ModelInterface, ArrayAcc
     /**
     /**
      * Sets quote_exact
      * Sets quote_exact
      *
      *
-     * @param string|null $quote_exact 锚定的原文片段(只限批注)
+     * @param string|null $quote_exact 锚定的原文片段(只限 note / commentary / issue / glossary)
      *
      *
      * @return self
      * @return self
      */
      */
@@ -697,7 +699,7 @@ class PatchApiV3DiscussionsDiscussionRequest implements ModelInterface, ArrayAcc
     /**
     /**
      * Sets quote_prefix
      * Sets quote_prefix
      *
      *
-     * @param string|null $quote_prefix 锚定片段前的上下文(只限批注)
+     * @param string|null $quote_prefix 锚定片段前的上下文(只限 note / commentary / issue / glossary)
      *
      *
      * @return self
      * @return self
      */
      */
@@ -724,7 +726,7 @@ class PatchApiV3DiscussionsDiscussionRequest implements ModelInterface, ArrayAcc
     /**
     /**
      * Sets quote_suffix
      * Sets quote_suffix
      *
      *
-     * @param string|null $quote_suffix 锚定片段后的上下文(只限批注)
+     * @param string|null $quote_suffix 锚定片段后的上下文(只限 note / commentary / issue / glossary)
      *
      *
      * @return self
      * @return self
      */
      */

+ 1 - 1
api-test/client/lib/Model/PatchApiV3PasswordResetsTokenRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3AgentGrants200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3AgentGrants200ResponseData.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3AgentGrantsRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3Discussions200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3DiscussionsBatchCount200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3DiscussionsBatchCount200ResponseDataInner.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3DiscussionsBatchCountRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3DiscussionsBatchCountRequestTargetsInner.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3DiscussionsDiscussionRepliesRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 9 - 7
api-test/client/lib/Model/PostApiV3DiscussionsRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */
@@ -309,6 +309,7 @@ class PostApiV3DiscussionsRequest implements ModelInterface, ArrayAccess, \JsonS
     public const TYPE_QA = 'qa';
     public const TYPE_QA = 'qa';
     public const TYPE_HELP = 'help';
     public const TYPE_HELP = 'help';
     public const TYPE_ISSUE = 'issue';
     public const TYPE_ISSUE = 'issue';
+    public const TYPE_GLOSSARY = 'glossary';
     public const TYPE_NOTE = 'note';
     public const TYPE_NOTE = 'note';
     public const TYPE_COMMENTARY = 'commentary';
     public const TYPE_COMMENTARY = 'commentary';
     public const SEVERITY_ERROR = 'error';
     public const SEVERITY_ERROR = 'error';
@@ -345,6 +346,7 @@ class PostApiV3DiscussionsRequest implements ModelInterface, ArrayAccess, \JsonS
             self::TYPE_QA,
             self::TYPE_QA,
             self::TYPE_HELP,
             self::TYPE_HELP,
             self::TYPE_ISSUE,
             self::TYPE_ISSUE,
+            self::TYPE_GLOSSARY,
             self::TYPE_NOTE,
             self::TYPE_NOTE,
             self::TYPE_COMMENTARY,
             self::TYPE_COMMENTARY,
         ];
         ];
@@ -585,7 +587,7 @@ class PostApiV3DiscussionsRequest implements ModelInterface, ArrayAccess, \JsonS
     /**
     /**
      * Sets title
      * Sets title
      *
      *
-     * @param string|null $title 标题,最长 256 字符;评论线程必填,批注可省
+     * @param string|null $title 标题,最长 256 字符;评论线程必填(glossary 填术语词头),批注可省
      *
      *
      * @return self
      * @return self
      */
      */
@@ -676,7 +678,7 @@ class PostApiV3DiscussionsRequest implements ModelInterface, ArrayAccess, \JsonS
     /**
     /**
      * Sets pos_start
      * Sets pos_start
      *
      *
-     * @param int|null $pos_start 批注锚点在句子文本中的起始偏移(只限批注)
+     * @param int|null $pos_start 锚点在句子文本中的起始偏移(只限 note / commentary / issue / glossary)
      *
      *
      * @return self
      * @return self
      */
      */
@@ -703,7 +705,7 @@ class PostApiV3DiscussionsRequest implements ModelInterface, ArrayAccess, \JsonS
     /**
     /**
      * Sets pos_end
      * Sets pos_end
      *
      *
-     * @param int|null $pos_end 批注锚点的结束偏移,批注插在这里(只限批注)
+     * @param int|null $pos_end 锚点的结束偏移,批注插在这里(只限 note / commentary / issue / glossary)
      *
      *
      * @return self
      * @return self
      */
      */
@@ -730,7 +732,7 @@ class PostApiV3DiscussionsRequest implements ModelInterface, ArrayAccess, \JsonS
     /**
     /**
      * Sets quote_exact
      * Sets quote_exact
      *
      *
-     * @param string|null $quote_exact 锚定的原文片段(只限批注)
+     * @param string|null $quote_exact 锚定的原文片段(只限 note / commentary / issue / glossary)
      *
      *
      * @return self
      * @return self
      */
      */
@@ -757,7 +759,7 @@ class PostApiV3DiscussionsRequest implements ModelInterface, ArrayAccess, \JsonS
     /**
     /**
      * Sets quote_prefix
      * Sets quote_prefix
      *
      *
-     * @param string|null $quote_prefix 锚定片段前的上下文,用于重新定位(只限批注)
+     * @param string|null $quote_prefix 锚定片段前的上下文,用于重新定位(只限 note / commentary / issue / glossary)
      *
      *
      * @return self
      * @return self
      */
      */
@@ -784,7 +786,7 @@ class PostApiV3DiscussionsRequest implements ModelInterface, ArrayAccess, \JsonS
     /**
     /**
      * Sets quote_suffix
      * Sets quote_suffix
      *
      *
-     * @param string|null $quote_suffix 锚定片段后的上下文,用于重新定位(只限批注)
+     * @param string|null $quote_suffix 锚定片段后的上下文,用于重新定位(只限 note / commentary / issue / glossary)
      *
      *
      * @return self
      * @return self
      */
      */

+ 1 - 1
api-test/client/lib/Model/PostApiV3EmailCertificationsRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3Invites200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3Invites200ResponseData.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3InvitesRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3MeReactions200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3MeReactionsRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3PasswordResetsRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3SearchRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3Sessions200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3Sessions200ResponseData.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3SessionsRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3Users200Response.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3Users200ResponseData.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/PostApiV3UsersRequest.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/Model/ProblemDetails.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 1 - 1
api-test/client/lib/ObjectSerializer.php

@@ -15,7 +15,7 @@
  *
  *
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  * International Academy Of Pali Tipitaka(国际巴利三藏学院)  由 `php openapi/scripts/generate.php` 依据 api-v13 的路由与控制器源码生成。 响应统一封装为 `{ok, message, data}`;`ok=false` 时 `message` 为错误信息。
  *
  *
- * The version of the OpenAPI document: 2026.10.15
+ * The version of the OpenAPI document: 2026.10.21
  * Generated by: https://openapi-generator.tech
  * Generated by: https://openapi-generator.tech
  * Generator version: 7.25.0
  * Generator version: 7.25.0
  */
  */

+ 73 - 19
api-test/config.local.example.php

@@ -5,27 +5,81 @@
  *
  *
  *   cp config.local.example.php config.local.php
  *   cp config.local.example.php config.local.php
  *
  *
- * 优先级:命令行参数 > 环境变量(API_TEST_*)> 本文件 > config.php 的默认值。
+ * 切 server 两种方式(账号 / 种子数据都会跟着走):
+ *   1. 改 `$server` 默认值;
+ *   2. `php run.php --server=<alias>` 临时切,alias 见 config.php 的 servers 列表。
+ * 每个 server 一套账号 + 种子数据,集中列在 `$servers`,互不干扰。
+ * 优先级:命令行参数 > 环境变量(API_TEST_*)> 本文件 > config.php 默认值。
  * 不需要的键删掉或留 null 即可。
  * 不需要的键删掉或留 null 即可。
  */
  */
 
 
-return [
-    // 默认服务器 alias(见 config.php 的 servers),或完整 base URL
-    'default_server' => 'local',
-
-    // 测试账号:调 POST /v3/sessions 换 token。也可以直接给 token(优先)
-    'username' => 'test161',
-    'password' => null,
-    'token' => null,
-
-    // 覆盖 fixtures.php 里的种子数据(换环境时用),只写要改的键
-    'fixtures' => [
-        // 讨论写用例挂的句子:必须在测试账号自己的 channel 里、由测试账号编辑过
-        // 'discussion_sentence' => 'ee5259be-5d1e-4408-a2a6-566a5773eef1',
-        // 'discussion_channel' => '1721b7fa-e471-46fd-b860-64b9e79d370e',
-        // agent 代写用例:归测试账号所有的 AI 模型 uid;不给则 SKIP
-        // 'agent_model' => null,
-        // email-certifications「已注册邮箱」用例;不给则 SKIP
-        // 'registered_email' => 'someone@example.com',
+$server = 'local';   // local | staging | next | prod | next-cn | prod-cn
+
+$servers = [
+    'local' => [
+        'username' => 'test',
+        'password' => null,
+        'token' => null,                       // 填了就直接用,跳过登录
+        'fixtures' => [
+            // 讨论写用例挂的句子:必须在测试账号自己的 channel 里、由测试账号编辑过
+            // 'discussion_sentence' => 'ee5259be-5d1e-4408-a2a6-566a5773eef1',
+            // discussion_sentence 所在的 channel(agent 授权的范围)
+            // 'discussion_channel' => '1721b7fa-e471-46fd-b860-64b9e79d370e',
+            // agent 代写用例:归测试账号所有的 AI 模型 uid;不给则 SKIP
+            // 'agent_model' => null,
+            // email-certifications「已注册邮箱」用例;不给则 SKIP
+            // 'registered_email' => 'someone@example.com',
+        ],
+    ],
+
+    'staging' => [
+        'username' => null,
+        'password' => null,
+        'token' => null,
+        'fixtures' => [
+            // 'discussion_sentence' => '',
+            // 'discussion_channel' => '',
+            // 'agent_model' => null,
+            // 'registered_email' => null,
+        ],
+    ],
+
+    'next' => [
+        'username' => null,
+        'password' => null,
+        'token' => null,
+        'fixtures' => [],
+    ],
+
+    'prod' => [
+        'username' => null,
+        'password' => null,
+        'token' => null,
+        'fixtures' => [],
     ],
     ],
+
+    'next-cn' => [
+        'username' => null,
+        'password' => null,
+        'token' => null,
+        'fixtures' => [],
+    ],
+
+    'prod-cn' => [
+        'username' => null,
+        'password' => null,
+        'token' => null,
+        'fixtures' => [],
+    ],
+];
+
+if (! isset($servers[$server])) {
+    throw new RuntimeException(
+        'config.local.php: 未知 server "' . $server . '",可选:' . implode(' / ', array_keys($servers))
+    );
+}
+
+return [
+    'default_server' => $server,
+    'servers' => $servers,
 ];
 ];

+ 17 - 18
api-test/config.php

@@ -6,24 +6,23 @@
  * 服务器切换的优先级(高 → 低):
  * 服务器切换的优先级(高 → 低):
  *   1. 命令行 `php run.php --server=<alias|完整 base URL>`
  *   1. 命令行 `php run.php --server=<alias|完整 base URL>`
  *   2. 环境变量 `API_TEST_BASE`(完整 base URL,覆盖 alias)
  *   2. 环境变量 `API_TEST_BASE`(完整 base URL,覆盖 alias)
- *   3. 本文件的 `default_server`
+ *   3. config.local.php 的 `default_server`
  *
  *
  * alias 对齐 openapi/public/assets/protocol/main.yaml 里的 servers。
  * alias 对齐 openapi/public/assets/protocol/main.yaml 里的 servers。
  * 第一个 `/api` 是同源地址,CLI 客户端用不到,故略。
  * 第一个 `/api` 是同源地址,CLI 客户端用不到,故略。
  *
  *
  * 本机的账号、默认服务器、种子数据覆盖写在 config.local.php(模板
  * 本机的账号、默认服务器、种子数据覆盖写在 config.local.php(模板
- * config.local.example.php,已 gitignore)。
+ * config.local.example.php,已 gitignore)。config.local.php 两种写法都认:
+ *   - 新分组:顶层 `servers` 按 alias 分组,run.php 按实际 server 选对应那套(推荐)
+ *   - 旧扁平:顶层直接写 username / token / fixtures(一套,切环境要手动改)
  *
  *
- * token(需登录的用例用)优先级:
+ * token(需登录的用例用)优先级(在 run.php 里按序套用):
  *   1. `php run.php --token=...`
  *   1. `php run.php --token=...`
  *   2. 环境变量 `API_TEST_TOKEN`
  *   2. 环境变量 `API_TEST_TOKEN`
- *   3. config.local.php 的 `token`
- *   4. 都没有时,用下面的 username/password 调 `POST /v3/sessions` 现换一个 token
+ *   3. 实际 server 对应的 `servers[alias].token`
+ *   4. 都没有时,用 username/password 调 `POST /v3/sessions` 现换一个 token
  *
  *
- * username/password(登录换 token 用)优先级:
- *   1. `php run.php --username=... --password=...`
- *   2. 环境变量 `API_TEST_USERNAME` / `API_TEST_PASSWORD`
- *   3. config.local.php
+ * username/password 同理:--username/--password > API_TEST_USERNAME/PASSWORD > servers[alias]
  *
  *
  * 写操作(POST/DELETE)在**所有服务器上都真实执行**,包括 prod——会产生垃圾数据,
  * 写操作(POST/DELETE)在**所有服务器上都真实执行**,包括 prod——会产生垃圾数据,
  * 由测试账号定期清理。
  * 由测试账号定期清理。
@@ -35,7 +34,7 @@ return [
     // alias => 完整 base URL(含 /api 前缀)
     // alias => 完整 base URL(含 /api 前缀)
     'servers' => [
     'servers' => [
         'local'   => 'http://127.0.0.1:8000/api',
         'local'   => 'http://127.0.0.1:8000/api',
-        'staging' => 'https://staging.wikipali.org/api',
+        'staging' => 'https://staging.wikipali.cc/api',
         'next'    => 'https://next.wikipali.org/api',
         'next'    => 'https://next.wikipali.org/api',
         'prod'    => 'https://www.wikipali.org/api',
         'prod'    => 'https://www.wikipali.org/api',
         'next-cn' => 'https://next.wikipali.cc/api',
         'next-cn' => 'https://next.wikipali.cc/api',
@@ -43,15 +42,15 @@ return [
     ],
     ],
     'default_server' => $local['default_server'] ?? 'local',
     'default_server' => $local['default_server'] ?? 'local',
 
 
-    // bearer token;没有则 /v3/me/* 的“带 token”用例会 FAIL(不跳过)。
-    'token' => getenv('API_TEST_TOKEN') ?: ($local['token'] ?? null),
-
-    // 登录凭据:sign-in 用 username(也可以是 email)+ password。
-    'username' => getenv('API_TEST_USERNAME') ?: ($local['username'] ?? null),
-    'password' => getenv('API_TEST_PASSWORD') ?: ($local['password'] ?? null),
-
-    // 覆盖 fixtures.php 的种子数据
+    // 旧扁平结构兜底(新分组的 config.local.php 顶层没有这些键,留空即可)
+    'token' => $local['token'] ?? null,
+    'username' => $local['username'] ?? null,
+    'password' => $local['password'] ?? null,
     'fixtures' => $local['fixtures'] ?? [],
     'fixtures' => $local['fixtures'] ?? [],
 
 
+    // 新分组结构:per-server 配置块(username/password/token/fixtures),
+    // run.php 解析出实际 server 后从这里选对应那套。
+    'local_servers' => $local['servers'] ?? null,
+
     'timeout' => 20,
     'timeout' => 20,
 ];
 ];

+ 24 - 3
api-test/run.php

@@ -43,25 +43,46 @@ $servers = $config['servers'];
 $serverArg = is_string($opts['server'] ?? null) ? $opts['server'] : null;
 $serverArg = is_string($opts['server'] ?? null) ? $opts['server'] : null;
 $envBase = getenv('API_TEST_BASE') ?: null;
 $envBase = getenv('API_TEST_BASE') ?: null;
 
 
+// 除了 base_url,还要记下对应的 alias($serverKey),好去 config.local.php 的
+// servers[alias] 里选账号 / token / 种子数据。完整 URL 会反查 alias。
+$serverKey = null;
 if ($envBase !== null) {
 if ($envBase !== null) {
     $config['base_url'] = rtrim($envBase, '/');
     $config['base_url'] = rtrim($envBase, '/');
+    $serverKey = array_search($config['base_url'], $servers, true) ?: null;
 } elseif ($serverArg !== null && isset($servers[$serverArg])) {
 } elseif ($serverArg !== null && isset($servers[$serverArg])) {
     $config['base_url'] = $servers[$serverArg];
     $config['base_url'] = $servers[$serverArg];
+    $serverKey = $serverArg;
 } elseif ($serverArg !== null && preg_match('#^https?://#', $serverArg)) {
 } elseif ($serverArg !== null && preg_match('#^https?://#', $serverArg)) {
     $config['base_url'] = rtrim($serverArg, '/');
     $config['base_url'] = rtrim($serverArg, '/');
+    $serverKey = array_search($config['base_url'], $servers, true) ?: null;
 } elseif (isset($servers[$config['default_server']])) {
 } elseif (isset($servers[$config['default_server']])) {
     $config['base_url'] = $servers[$config['default_server']];
     $config['base_url'] = $servers[$config['default_server']];
+    $serverKey = $config['default_server'];
 } else {
 } else {
     // config.local.php 的 default_server 也可以直接写完整 base URL
     // config.local.php 的 default_server 也可以直接写完整 base URL
     $config['base_url'] = rtrim($config['default_server'], '/');
     $config['base_url'] = rtrim($config['default_server'], '/');
+    $serverKey = array_search($config['base_url'], $servers, true) ?: null;
 }
 }
 
 
-// token 覆盖
+// 按实际 server 选 config.local.php 的 per-server 块(账号 / token / 种子数据跟着 server 走)
+$localServers = $config['local_servers'] ?? null;
+if (is_array($localServers) && $serverKey !== null && isset($localServers[$serverKey])) {
+    $block = $localServers[$serverKey];
+    $config['token'] = $block['token'] ?? $config['token'];
+    $config['username'] = $block['username'] ?? $config['username'];
+    $config['password'] = $block['password'] ?? $config['password'];
+    $config['fixtures'] = $block['fixtures'] ?? $config['fixtures'];
+}
+
+// 环境变量覆盖(高于 config.local.php,低于命令行)
+$config['token'] = getenv('API_TEST_TOKEN') ?: $config['token'];
+$config['username'] = getenv('API_TEST_USERNAME') ?: $config['username'];
+$config['password'] = getenv('API_TEST_PASSWORD') ?: $config['password'];
+
+// 命令行覆盖(最高优先级)
 if (is_string($opts['token'] ?? null) && $opts['token'] !== '') {
 if (is_string($opts['token'] ?? null) && $opts['token'] !== '') {
     $config['token'] = $opts['token'];
     $config['token'] = $opts['token'];
 }
 }
-
-// username/password 覆盖
 if (is_string($opts['username'] ?? null) && $opts['username'] !== '') {
 if (is_string($opts['username'] ?? null) && $opts['username'] !== '') {
     $config['username'] = $opts['username'];
     $config['username'] = $opts['username'];
 }
 }

+ 2 - 2
api-v13/app/Http/Controllers/V3/DiscussionBatchCountController.php

@@ -25,8 +25,8 @@ class DiscussionBatchCountController extends Controller
      *
      *
      * 每个对象返回未关闭的评论线程数(total)与其中当前用户发起的数目(mine,未登录为 0),
      * 每个对象返回未关闭的评论线程数(total)与其中当前用户发起的数目(mine,未登录为 0),
      * 按请求顺序、一个不少(没有讨论的为 0)。只数评论线程(discussion / qa / help / issue),
      * 按请求顺序、一个不少(没有讨论的为 0)。只数评论线程(discussion / qa / help / issue),
-     * 批注与已删除的不算;可见性与列表相同:未登录不数协作类话题,basic 角色在无权管理的
-     * 对象上只数自己发起的协作类话题。最多 500 个对象。
+     * 批注与已删除的不算;可见性与列表相同:未登录不数不公开的话题(discussion),basic 角色在
+     * 无权管理的对象上只数自己发起的不公开话题。最多 500 个对象。
      *
      *
      * 与 v2 的区别:入参是对象 uid 而不是句子坐标;只给计数不给明细;v2 不分类型、不管可见性。
      * 与 v2 的区别:入参是对象 uid 而不是句子坐标;只给计数不给明细;v2 不分类型、不管可见性。
      *
      *

+ 25 - 19
api-v13/app/Http/Controllers/V3/DiscussionController.php

@@ -41,15 +41,16 @@ class DiscussionController extends Controller
      * 列出某个对象上的话题
      * 列出某个对象上的话题
      *
      *
      * 只列话题(不含回复),删除了但还有回复的话题以墓碑出现(deleted_at 有值、内容为空)。
      * 只列话题(不含回复),删除了但还有回复的话题以墓碑出现(deleted_at 有值、内容为空)。
-     * 未登录看不到协作类话题(discussion / issue);basic 角色在自己无权管理的对象上只看得到
-     * 自己发起的协作类话题。各状态的数量与「能发起哪些类型」见 `GET /v3/discussions/summary`。
-     * 不传 type 时只列评论线程(discussion / qa / help / issue),批注要显式 type=note / commentary。
+     * 未登录看不到不公开的话题(discussion);basic 角色在自己无权管理的对象上只看得到
+     * 自己发起的不公开话题。各状态的数量与「能发起哪些类型」见 `GET /v3/discussions/summary`。
+     * 不传 type 时只列评论线程(discussion / qa / help / issue / glossary),批注要显式 type=note / commentary。
+     * 要一段经文(一章、一段段落区间)里的全部话题,用 `GET /v3/tipitaka-discussions/{channel}`。
      *
      *
      * @unauthenticated
      * @unauthenticated
      *
      *
      * @queryParam target_type string required 挂靠对象的类型。Enum: sentence,wbw,article,term,task
      * @queryParam target_type string required 挂靠对象的类型。Enum: sentence,wbw,article,term,task
      * @queryParam target_id string required 挂靠对象的 uuid
      * @queryParam target_id string required 挂靠对象的 uuid
-     * @queryParam type string 类型,不传则只列评论线程。Enum: discussion,qa,help,issue,note,commentary
+     * @queryParam type string 类型,不传则只列评论线程。Enum: discussion,qa,help,issue,glossary,note,commentary
      * @queryParam status string 话题状态,不传则两种都要。Enum: active,close
      * @queryParam status string 话题状态,不传则两种都要。Enum: active,close
      * @queryParam severity string 严重程度,逗号分隔多选。Example: error,warning
      * @queryParam severity string 严重程度,逗号分隔多选。Example: error,warning
      * @queryParam sort string 排序,前缀 - 表示倒序。Enum: created_at,-created_at,updated_at,-updated_at Default: -created_at
      * @queryParam sort string 排序,前缀 - 表示倒序。Enum: created_at,-created_at,updated_at,-updated_at Default: -created_at
@@ -74,22 +75,26 @@ class DiscussionController extends Controller
      *
      *
      * 批注(note / commentary)只能加在句子上,且只有该句所在 channel 的编辑者能加;
      * 批注(note / commentary)只能加在句子上,且只有该句所在 channel 的编辑者能加;
      * 标题可省、正文必填,commentary 的正文必须是义注句子模板 `{{book-para-start-end}}`
      * 标题可省、正文必填,commentary 的正文必须是义注句子模板 `{{book-para-start-end}}`
-     * (可多句并列);可带锚点 pos_* / quote_*(评论线程传了 422);不发通知。
+     * (可多句并列);可带锚点 pos_* / quote_*;不发通知。
+     *
+     * glossary(外挂术语:不改译文,给锚点处的译文挂上术语)是评论线程,title 必填、填术语词头;
+     * 与批注一样只能挂在句子上、只有该句所在 channel 的编辑者能发,对所有人公开可见。
+     * 锚点只有 note / commentary / issue / glossary 收,其它类型传了 422。
      *
      *
      * @responseStatus 201 创建成功
      * @responseStatus 201 创建成功
      * @responseStatus 403 当前用户不能在这个对象上发起这种类型的话题
      * @responseStatus 403 当前用户不能在这个对象上发起这种类型的话题
      *
      *
      * @bodyParam target_type string required 挂靠对象的类型。Enum: sentence,wbw,article,term,task
      * @bodyParam target_type string required 挂靠对象的类型。Enum: sentence,wbw,article,term,task
      * @bodyParam target_id string required 挂靠对象的 uuid
      * @bodyParam target_id string required 挂靠对象的 uuid
-     * @bodyParam type string 类型。Enum: discussion,qa,help,issue,note,commentary Default: discussion
-     * @bodyParam title string 标题,最长 256 字符;评论线程必填,批注可省
+     * @bodyParam type string 类型。Enum: discussion,qa,help,issue,glossary,note,commentary Default: discussion
+     * @bodyParam title string 标题,最长 256 字符;评论线程必填(glossary 填术语词头),批注可省
      * @bodyParam content string 正文(markdown);批注必填
      * @bodyParam content string 正文(markdown);批注必填
      * @bodyParam severity string 严重程度。Enum: error,warning,suggestion,urgent,normal
      * @bodyParam severity string 严重程度。Enum: error,warning,suggestion,urgent,normal
-     * @bodyParam pos_start integer 批注锚点在句子文本中的起始偏移(只限批注)
-     * @bodyParam pos_end integer 批注锚点的结束偏移,批注插在这里(只限批注)
-     * @bodyParam quote_exact string 锚定的原文片段(只限批注)
-     * @bodyParam quote_prefix string 锚定片段前的上下文,用于重新定位(只限批注)
-     * @bodyParam quote_suffix string 锚定片段后的上下文,用于重新定位(只限批注)
+     * @bodyParam pos_start integer 锚点在句子文本中的起始偏移(只限 note / commentary / issue / glossary)
+     * @bodyParam pos_end integer 锚点的结束偏移,批注插在这里(只限 note / commentary / issue / glossary)
+     * @bodyParam quote_exact string 锚定的原文片段(只限 note / commentary / issue / glossary)
+     * @bodyParam quote_prefix string 锚定片段前的上下文,用于重新定位(只限 note / commentary / issue / glossary)
+     * @bodyParam quote_suffix string 锚定片段后的上下文,用于重新定位(只限 note / commentary / issue / glossary)
      * @bodyParam tpl_id string 话题模板(文章)的 uuid
      * @bodyParam tpl_id string 话题模板(文章)的 uuid
      * @bodyParam notify boolean 是否发通知(批注不发)。Default: true
      * @bodyParam notify boolean 是否发通知(批注不发)。Default: true
      */
      */
@@ -126,7 +131,8 @@ class DiscussionController extends Controller
      *
      *
      * 只改传了的字段。title / status / type / severity 只对话题有效,回复传了 422。
      * 只改传了的字段。title / status / type / severity 只对话题有效,回复传了 422。
      * 作者或对象的管理者(句子、逐词所在 channel 的编辑者,文章的编辑者)可改;
      * 作者或对象的管理者(句子、逐词所在 channel 的编辑者,文章的编辑者)可改;
-     * 改 type 时回复跟着改,新类型不收原来的 severity 时 severity 清空。
+     * 改 type 时回复跟着改,新类型不收原来的 severity 时 severity 清空(锚点保留,改回可带锚点的类型时恢复);
+     * 改成 glossary 须挂在句子上、且是该句的管理者。
      * 批注:没有 status,type 只能在 note / commentary 之间改(不能与评论线程互转),
      * 批注:没有 status,type 只能在 note / commentary 之间改(不能与评论线程互转),
      * 正文不能清空,可改锚点。
      * 正文不能清空,可改锚点。
      * 改了内容(标题、正文、锚点)时 co_editor 跟着这次写入走:人亲自改则清空,AI 代改则换成该模型;
      * 改了内容(标题、正文、锚点)时 co_editor 跟着这次写入走:人亲自改则清空,AI 代改则换成该模型;
@@ -139,13 +145,13 @@ class DiscussionController extends Controller
      * @bodyParam title string 标题(只限话题)
      * @bodyParam title string 标题(只限话题)
      * @bodyParam content string 正文(markdown)
      * @bodyParam content string 正文(markdown)
      * @bodyParam status string 状态(只限话题)。Enum: active,close
      * @bodyParam status string 状态(只限话题)。Enum: active,close
-     * @bodyParam type string 类型(只限话题;评论线程与批注不能互转)。Enum: discussion,qa,help,issue,note,commentary
+     * @bodyParam type string 类型(只限话题;评论线程与批注不能互转)。Enum: discussion,qa,help,issue,glossary,note,commentary
      * @bodyParam severity string 严重程度(只限话题),null 表示清空。Enum: error,warning,suggestion,urgent,normal
      * @bodyParam severity string 严重程度(只限话题),null 表示清空。Enum: error,warning,suggestion,urgent,normal
-     * @bodyParam pos_start integer 锚点起始偏移(只限批注)
-     * @bodyParam pos_end integer 锚点结束偏移(只限批注)
-     * @bodyParam quote_exact string 锚定的原文片段(只限批注)
-     * @bodyParam quote_prefix string 锚定片段前的上下文(只限批注)
-     * @bodyParam quote_suffix string 锚定片段后的上下文(只限批注)
+     * @bodyParam pos_start integer 锚点起始偏移(只限 note / commentary / issue / glossary)
+     * @bodyParam pos_end integer 锚点结束偏移(只限 note / commentary / issue / glossary)
+     * @bodyParam quote_exact string 锚定的原文片段(只限 note / commentary / issue / glossary)
+     * @bodyParam quote_prefix string 锚定片段前的上下文(只限 note / commentary / issue / glossary)
+     * @bodyParam quote_suffix string 锚定片段后的上下文(只限 note / commentary / issue / glossary)
      */
      */
     public function update(UpdateDiscussionRequest $request, Discussion $discussion)
     public function update(UpdateDiscussionRequest $request, Discussion $discussion)
     {
     {

+ 1 - 1
api-v13/app/Http/Controllers/V3/DiscussionSummaryController.php

@@ -30,7 +30,7 @@ class DiscussionSummaryController extends Controller
      *
      *
      * @queryParam target_type string required 挂靠对象的类型。Enum: sentence,wbw,article,term,task
      * @queryParam target_type string required 挂靠对象的类型。Enum: sentence,wbw,article,term,task
      * @queryParam target_id string required 挂靠对象的 uuid
      * @queryParam target_id string required 挂靠对象的 uuid
-     * @queryParam type string 只数这种类型,不传则只数评论线程。Enum: discussion,qa,help,issue,note,commentary
+     * @queryParam type string 只数这种类型,不传则只数评论线程。Enum: discussion,qa,help,issue,glossary,note,commentary
      */
      */
     public function __invoke(SummaryDiscussionRequest $request)
     public function __invoke(SummaryDiscussionRequest $request)
     {
     {

+ 61 - 0
api-v13/app/Http/Controllers/V3/TipitakaDiscussionController.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace App\Http\Controllers\V3;
+
+use App\Http\Controllers\Concerns\ResolvesCurrentUser;
+use App\Http\Controllers\Controller;
+use App\Http\Requests\V3\IndexTipitakaDiscussionRequest;
+use App\Http\Resources\V3\DiscussionResource;
+use App\Services\V3\DiscussionService;
+
+/**
+ * 按经文范围列讨论:某个 channel 的一段经文(整个 channel / 一本书 / 一章 / 段落区间)里,
+ * 挂在句子上的全部话题。给前端的章节讨论面板、agent 的「这一章还有哪些未关闭的 issue」用。
+ *
+ * 与 `GET /v3/discussions` 是两个契约:那边按单个对象查(target_type + target_id 必填),
+ * 这边按 channel + 经文范围查,必填参数不同,所以分成两个端点(v3 规范第 1 条)。
+ * 没有对应的 v2 端点;最接近的是 `POST /v2/sent-discussion-tree`(调用方逐句列出句子坐标,
+ * 只回标题与回复数),v2 那份原样保留。
+ */
+class TipitakaDiscussionController extends Controller
+{
+    use ResolvesCurrentUser;
+
+    public function __construct(private readonly DiscussionService $discussions) {}
+
+    /**
+     * 列出一段经文里的话题
+     *
+     * 只列挂在该 channel 句子上的话题(不含回复),按经文顺序(书、段、句)排列,同一句里按时间;
+     * 每条带 `sentence`(所在句子的坐标)。删除了但还有回复的话题以墓碑出现。
+     * 不传 type 时只列评论线程(discussion / qa / help / issue / glossary),批注要显式
+     * type=note / commentary。可见性同 `GET /v3/discussions`:未登录看不到 discussion;
+     * basic 角色在自己不能编辑的 channel 上只看得到自己发起的 discussion。
+     * channel 不存在或范围内没有讨论都返回空列表;chapter 不存在 404。
+     *
+     * @unauthenticated
+     *
+     * @responseStatus 404 chapter 不是本书存在的段落
+     *
+     * @urlParam channel string required 句子所在 channel 的 uuid
+     *
+     * @queryParam book integer 书号;与 chapter / para / to 一起用时必填,不传则是整个 channel。Example: 169
+     * @queryParam chapter integer 章节起始段落号,服务端按章节长度展开成段落区间;不能与 para / to 同用。Example: 893
+     * @queryParam para integer 起始段落号。Example: 895
+     * @queryParam to integer 结束段落号(含),缺省同 para。Example: 896
+     * @queryParam type string 类型,逗号分隔多选;不传则只列评论线程。Example: issue,glossary
+     * @queryParam status string 话题状态,不传则两种都要。Enum: active,close
+     * @queryParam severity string 严重程度,逗号分隔多选。Example: error,warning
+     * @queryParam page integer 页码。Default: 1
+     * @queryParam per_page integer 每页数量,最大 200。Default: 15
+     */
+    public function __invoke(IndexTipitakaDiscussionRequest $request, string $channel)
+    {
+        return DiscussionResource::collection($this->discussions->listInChannel(
+            $channel,
+            $request->validated(),
+            $this->currentUserUid($request),
+            $request->integer('per_page', 15),
+        ));
+    }
+}

+ 48 - 0
api-v13/app/Http/Requests/V3/IndexTipitakaDiscussionRequest.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace App\Http\Requests\V3;
+
+use App\Models\Discussion;
+use Closure;
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Validation\Rule;
+
+/**
+ * `GET /v3/tipitaka-discussions/{channel}` 的入参。
+ *
+ * **只有 channel 是必填的,而且它在路径上。** 这里全是可选的范围、过滤与分页参数:
+ * 不带 book 就是整个 channel;`book` 只在跟 `chapter` / `para` 一起用时才必填
+ * (与 `tipitaka-reading` 同一口径)。
+ */
+class IndexTipitakaDiscussionRequest extends FormRequest
+{
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    /**
+     * @return array<string, array<int, mixed>>
+     */
+    public function rules(): array
+    {
+        return [
+            'book' => ['integer', 'min:1', 'required_with:chapter,para,to'],
+            // 章节起始段落号;服务端按 pali_texts 的 chapter_len 展开成段落区间
+            'chapter' => ['integer', 'min:1', 'prohibits:para,to'],
+            'para' => ['integer', 'min:1'],
+            'to' => ['integer', 'min:1'],
+            // 逗号分隔多选,如 issue,glossary;不传只列话题(评论线程)
+            'type' => ['sometimes', 'string', 'regex:/^[a-z]+(,[a-z]+)*$/', function (string $attribute, mixed $value, Closure $fail) {
+                if (array_diff(explode(',', (string) $value), Discussion::COMMENT_TYPES) !== []) {
+                    $fail(__('validation.in', ['attribute' => $attribute]));
+                }
+            }],
+            'status' => ['sometimes', 'string', Rule::in(Discussion::STATUSES)],
+            // 逗号分隔多选,如 error,warning
+            'severity' => ['sometimes', 'string', 'regex:/^[a-z]+(,[a-z]+)*$/'],
+            'page' => ['integer', 'min:1'],
+            'per_page' => ['integer', 'min:1', 'max:200'],
+        ];
+    }
+}

+ 10 - 4
api-v13/app/Http/Requests/V3/StoreDiscussionRequest.php

@@ -13,7 +13,8 @@ use Illuminate\Validation\Rule;
  * severity 的取值随 type 而定,见 {@see Discussion::SEVERITIES};不收 severity 的类型传了就 422。
  * severity 的取值随 type 而定,见 {@see Discussion::SEVERITIES};不收 severity 的类型传了就 422。
  *
  *
  * 批注(note / commentary)与评论线程的规则不同:只能挂在句子上、标题可省、正文必填,
  * 批注(note / commentary)与评论线程的规则不同:只能挂在句子上、标题可省、正文必填,
- * commentary 的正文必须是义注句子模板;锚点(pos_* / quote_*)只有批注收。
+ * commentary 的正文必须是义注句子模板。glossary 也只能挂在句子上。
+ * 锚点(pos_* / quote_*)只有 {@see Discussion::ANCHORED_TYPES} 收。
  */
  */
 class StoreDiscussionRequest extends FormRequest
 class StoreDiscussionRequest extends FormRequest
 {
 {
@@ -32,11 +33,14 @@ class StoreDiscussionRequest extends FormRequest
         $type = $this->input('type', 'discussion');
         $type = $this->input('type', 'discussion');
         $severities = Discussion::SEVERITIES[$type] ?? [];
         $severities = Discussion::SEVERITIES[$type] ?? [];
         $isAnnotation = in_array($type, Discussion::INJECTED_TYPES, true);
         $isAnnotation = in_array($type, Discussion::INJECTED_TYPES, true);
-        // 锚点只有批注收:评论线程传了 422,免得静默丢掉
-        $anchor = fn (array $rules) => $isAnnotation ? ['sometimes', 'nullable', ...$rules] : ['prohibited'];
+        $sentenceOnly = in_array($type, Discussion::SENTENCE_ONLY_TYPES, true);
+        // 不收锚点的类型传了 422,免得静默丢掉
+        $anchor = fn (array $rules) => in_array($type, Discussion::ANCHORED_TYPES, true)
+            ? ['sometimes', 'nullable', ...$rules]
+            : ['prohibited'];
 
 
         return [
         return [
-            'target_type' => ['required', 'string', Rule::in($isAnnotation ? ['sentence'] : Discussion::TARGET_TYPES)],
+            'target_type' => ['required', 'string', Rule::in($sentenceOnly ? ['sentence'] : Discussion::TARGET_TYPES)],
             'target_id' => ['required', 'uuid'],
             'target_id' => ['required', 'uuid'],
             'type' => ['sometimes', 'string', Rule::in(Discussion::COMMENT_TYPES)],
             'type' => ['sometimes', 'string', Rule::in(Discussion::COMMENT_TYPES)],
             'title' => $isAnnotation
             'title' => $isAnnotation
@@ -71,6 +75,8 @@ class StoreDiscussionRequest extends FormRequest
         ];
         ];
         if (in_array($this->input('type'), Discussion::INJECTED_TYPES, true)) {
         if (in_array($this->input('type'), Discussion::INJECTED_TYPES, true)) {
             $messages['target_type.in'] = __('messages.discussion_annotation_target');
             $messages['target_type.in'] = __('messages.discussion_annotation_target');
+        } elseif ($this->input('type') === Discussion::TYPE_GLOSSARY) {
+            $messages['target_type.in'] = __('messages.discussion_glossary_target');
         }
         }
         foreach (self::ANCHOR_FIELDS as $field) {
         foreach (self::ANCHOR_FIELDS as $field) {
             $messages["{$field}.prohibited"] = __('messages.discussion_anchor_thread');
             $messages["{$field}.prohibited"] = __('messages.discussion_anchor_thread');

+ 1 - 1
api-v13/app/Http/Requests/V3/UpdateDiscussionRequest.php

@@ -11,7 +11,7 @@ use Illuminate\Validation\Rule;
  *
  *
  * title / status / type / severity 只对话题有效,回复传了 422——这一条与
  * title / status / type / severity 只对话题有效,回复传了 422——这一条与
  * severity 是否匹配最终 type,都要看库里那条记录,在 Service 里判。
  * severity 是否匹配最终 type,都要看库里那条记录,在 Service 里判。
- * 批注相关的规则(锚点只有批注收、commentary 正文格式、不能与评论线程互转、没有 status)
+ * 批注相关的规则(锚点只有可锚定的类型收、commentary 正文格式、不能与评论线程互转、没有 status)
  * 同样取决于那条记录的类型,也在 Service 里判。
  * 同样取决于那条记录的类型,也在 Service 里判。
  */
  */
 class UpdateDiscussionRequest extends FormRequest
 class UpdateDiscussionRequest extends FormRequest

+ 12 - 2
api-v13/app/Http/Resources/V3/DiscussionResource.php

@@ -10,8 +10,8 @@ use Illuminate\Http\Request;
 /**
 /**
  * 一条评论:话题(parent_id 为 null)或回复;或一条批注(note / commentary)。
  * 一条评论:话题(parent_id 为 null)或回复;或一条批注(note / commentary)。
  *
  *
- * 批注带锚点(pos_start / pos_end / quote_*),指明它挂在句子文本的哪一段;
- * 评论线程的这几个字段恒为 null。
+ * 批注与 issue / glossary 可带锚点(pos_start / pos_end / quote_*),指明它挂在句子文本的
+ * 哪一段;其它评论线程的这几个字段为 null。glossary 的 title 是术语词头。
  *
  *
  * 删除了但还挂着回复的话题以「墓碑」出现:deleted_at 有值,title / content 为 null,
  * 删除了但还挂着回复的话题以「墓碑」出现:deleted_at 有值,title / content 为 null,
  * 回复照常可读(与 Google Drive 的 comments.deleted、GitHub 的 deletedAt 同一做法)。
  * 回复照常可读(与 Google Drive 的 comments.deleted、GitHub 的 deletedAt 同一做法)。
@@ -22,6 +22,9 @@ use Illuminate\Http\Request;
  * **本类不查库。** 编辑者从预加载的 `user` / `aiModel` / `coEditor` 关系上取;权限与正文渲染
  * **本类不查库。** 编辑者从预加载的 `user` / `aiModel` / `coEditor` 关系上取;权限与正文渲染
  * 由 {@see DiscussionService} 算好挂在模型的 `abilities` / `rendered` 属性上。
  * 由 {@see DiscussionService} 算好挂在模型的 `abilities` / `rendered` 属性上。
  *
  *
+ * `sentence` 只在按经文范围列出时有(`GET /v3/tipitaka-discussions/{channel}`):所在句子的坐标,
+ * 由查询顺带取出(`sentence_*` 属性),本类不另查。
+ *
  * 字段名改了两处(v2 → v3):res_type / res_id → target_type / target_id,
  * 字段名改了两处(v2 → v3):res_type / res_id → target_type / target_id,
  * parent → parent_id;children_count → reply_count(只有话题有)。
  * parent → parent_id;children_count → reply_count(只有话题有)。
  *
  *
@@ -53,6 +56,7 @@ class DiscussionResource extends BaseResource
      *     quote_prefix: string|null,
      *     quote_prefix: string|null,
      *     quote_suffix: string|null,
      *     quote_suffix: string|null,
      *     reply_count?: int,
      *     reply_count?: int,
+     *     sentence?: array{book: int, paragraph: int, word_start: int, word_end: int},
      *     editor: array{id: string, nickName: string, userName: string, realName: string, sn: int, avatar: string|null, roles: array|null}|null,
      *     editor: array{id: string, nickName: string, userName: string, realName: string, sn: int, avatar: string|null, roles: array|null}|null,
      *     co_editor: array{id: string, nickName: string, userName: string, realName: string, sn: int, avatar: string|null, roles: array|null}|null,
      *     co_editor: array{id: string, nickName: string, userName: string, realName: string, sn: int, avatar: string|null, roles: array|null}|null,
      *     abilities: array{update: bool, delete: bool, close: bool, reply: bool},
      *     abilities: array{update: bool, delete: bool, close: bool, reply: bool},
@@ -85,6 +89,12 @@ class DiscussionResource extends BaseResource
             'quote_prefix' => $tombstone ? null : $this->quote_prefix,
             'quote_prefix' => $tombstone ? null : $this->quote_prefix,
             'quote_suffix' => $tombstone ? null : $this->quote_suffix,
             'quote_suffix' => $tombstone ? null : $this->quote_suffix,
             'reply_count' => $this->when($this->parent === null, fn () => (int) $this->reply_count),
             'reply_count' => $this->when($this->parent === null, fn () => (int) $this->reply_count),
+            'sentence' => $this->when(isset($this->sentence_book), fn () => [
+                'book' => (int) $this->sentence_book,
+                'paragraph' => (int) $this->sentence_paragraph,
+                'word_start' => (int) $this->sentence_word_start,
+                'word_end' => (int) $this->sentence_word_end,
+            ]),
             'editor' => $this->actor($this->user, $this->aiModel),
             'editor' => $this->actor($this->user, $this->aiModel),
             // AI 代写时的模型;人类自己写的、存量数据都为 null
             // AI 代写时的模型;人类自己写的、存量数据都为 null
             'co_editor' => $this->coEditor ? $this->actor(null, $this->coEditor) : null,
             'co_editor' => $this->coEditor ? $this->actor(null, $this->coEditor) : null,

+ 26 - 8
api-v13/app/Models/Discussion.php

@@ -68,17 +68,35 @@ class Discussion extends Model
     /** 普通边注:content 就是注解正文,没有出处 */
     /** 普通边注:content 就是注解正文,没有出处 */
     public const TYPE_NOTE = 'note';
     public const TYPE_NOTE = 'note';
 
 
-    /** 会被注入阅读页的批注类型 */
+    /** 批注类型:作为边注注入阅读页,没有回复以外的线程属性(开关、severity) */
     public const INJECTED_TYPES = [self::TYPE_COMMENTARY, self::TYPE_NOTE];
     public const INJECTED_TYPES = [self::TYPE_COMMENTARY, self::TYPE_NOTE];
 
 
+    /**
+     * 外挂术语:不改译文原文,给锚点处的一段译文挂上术语(title 是术语词头),阅读页把那段
+     * 原文渲染成 `[[title#原文]]`。带锚点、只能挂在句子上。与术语表(dhamma terms)是两回事。
+     */
+    public const TYPE_GLOSSARY = 'glossary';
+
     /** 评论线程:话题 + 一层回复,有开关状态 */
     /** 评论线程:话题 + 一层回复,有开关状态 */
-    public const THREAD_TYPES = ['discussion', 'qa', 'help', 'issue'];
+    public const THREAD_TYPES = ['discussion', 'qa', 'help', 'issue', self::TYPE_GLOSSARY];
+
+    /** 可以带锚点(pos_* / quote_*)、定位到句子文本某一段的类型:批注 + issue / glossary */
+    public const ANCHORED_TYPES = [self::TYPE_COMMENTARY, self::TYPE_NOTE, 'issue', self::TYPE_GLOSSARY];
+
+    /**
+     * 会被注入阅读页的全部类型({@see PaliContentService::injectAnnotationNotes()}):
+     * 批注插边注,glossary 换成术语,issue 高亮。issue / glossary 只注入未关闭的。
+     */
+    public const READING_TYPES = [self::TYPE_COMMENTARY, self::TYPE_NOTE, 'issue', self::TYPE_GLOSSARY];
+
+    /** 只能挂在句子上的类型:批注 + glossary */
+    public const SENTENCE_ONLY_TYPES = [self::TYPE_COMMENTARY, self::TYPE_NOTE, self::TYPE_GLOSSARY];
 
 
     /**
     /**
      * `/v3/discussions` 管的全部类型:评论线程({@see self::THREAD_TYPES})+ 批注
      * `/v3/discussions` 管的全部类型:评论线程({@see self::THREAD_TYPES})+ 批注
      * ({@see self::INJECTED_TYPES})。批注锚定在句子的某个位置、注入阅读页,没有回复和开关。
      * ({@see self::INJECTED_TYPES})。批注锚定在句子的某个位置、注入阅读页,没有回复和开关。
      */
      */
-    public const COMMENT_TYPES = ['discussion', 'qa', 'help', 'issue', 'note', 'commentary'];
+    public const COMMENT_TYPES = ['discussion', 'qa', 'help', 'issue', 'glossary', 'note', 'commentary'];
 
 
     /** 各类型允许的 severity 取值;不在表里的类型不允许设 severity */
     /** 各类型允许的 severity 取值;不在表里的类型不允许设 severity */
     public const SEVERITIES = [
     public const SEVERITIES = [
@@ -145,15 +163,15 @@ class Discussion extends Model
     }
     }
 
 
     /**
     /**
-     * 这两类批注会被注入到所挂句子的阅读页里(PaliContentService::
-     * injectAnnotationNotes),阅读页按 (book, para, channel) 缓存,所以增改删它们
-     * 都要清掉那一段的缓存。改之前是、改之后不是(或反过来)的也要清。
+     * 这几类会被注入到所挂句子的阅读页里({@see self::READING_TYPES}),阅读页按
+     * (book, para, channel) 缓存,所以增改删它们(含关闭 / 重开)都要清掉那一段的缓存。
+     * 改之前是、改之后不是(或反过来)的也要清。
      */
      */
     protected static function booted(): void
     protected static function booted(): void
     {
     {
         $forget = function (Discussion $discussion) {
         $forget = function (Discussion $discussion) {
-            $wasInjected = in_array($discussion->getOriginal('type'), self::INJECTED_TYPES, true);
-            if (! in_array($discussion->type, self::INJECTED_TYPES, true) && ! $wasInjected) {
+            $wasInjected = in_array($discussion->getOriginal('type'), self::READING_TYPES, true);
+            if (! in_array($discussion->type, self::READING_TYPES, true) && ! $wasInjected) {
                 return;
                 return;
             }
             }
             if ($discussion->res_type !== 'sentence' || empty($discussion->res_id)) {
             if ($discussion->res_type !== 'sentence' || empty($discussion->res_id)) {

Някои файлове не бяха показани, защото твърде много файлове са промени