@ichinese/open-sdk 1.0.0-rc.2 → 1.0.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -56,7 +56,7 @@ export interface paths {
56
56
  * 两种码都接受:
57
57
  * - **网页免登的授权码**:应用网页在桌面端或手机 App 里调 JS SDK 的 `requestAuthCode()` 拿到,带区前缀、
58
58
  * 5 分钟、只能用一次、绑定这个应用和页面来源;不用传 `redirect_uri`。
59
- * - **标准 OIDC 的授权码**:普通浏览器里 JS SDK 退回签发方 `/authorize`(先 `prompt=none` 静默)拿到的码;
59
+ * - **标准 OIDC 的授权码**:普通浏览器里 JS SDK 退回签发方 `/auth`(先 `prompt=none` 静默)拿到的码;
60
60
  * 要传当时的 `redirect_uri`(和登记的一致),公开客户端用了 PKCE 时传 `code_verifier`。
61
61
  * 网关按码的区前缀转到用户的归属区。码过期返回 400 `details.reason = code_expired`,用过返回 400 `code_used`,
62
62
  * 不是这个应用的码返回 400 `code_invalid`。
@@ -263,6 +263,35 @@ export interface paths {
263
263
  /** 班级的课次(时间段内,最多 100 天) */
264
264
  get: operations["listClassSessions"];
265
265
  put?: never;
266
+ /**
267
+ * 给班级排课次(一次,或按周重复),在线或在本机构的教学点
268
+ * @description 和老师在班级课表里排课一样:查老师在各区的时间(撞了 409 `teacher_busy`),排好的课次进成员的课表、发提醒,
269
+ * 并推 `schedule.session_changed`(`change = scheduled`)。还没上架的班(草稿)排的课次也是草稿,上架时才进课表。
270
+ * 地点:`place` 不传跟着班级的默认地点,`null` 在线,给 `venue_id` 就在这个教学点(必须是班级所属机构的,`note` 写教室)。
271
+ * 教学点还没发布也能排,只是班级上架前要发布;关了或被下架的教学点 409 `venue_unavailable`。
272
+ * 个人老师的班、公开课只能在线(409 `online_only`)。
273
+ */
274
+ post: operations["createClassSessions"];
275
+ delete?: never;
276
+ options?: never;
277
+ head?: never;
278
+ patch?: never;
279
+ trace?: never;
280
+ };
281
+ "/classes/{class_id}/sessions/{session_id}/place": {
282
+ parameters: {
283
+ query?: never;
284
+ header?: never;
285
+ path?: never;
286
+ cookie?: never;
287
+ };
288
+ get?: never;
289
+ /**
290
+ * 改一次课(或这一次及同一系列以后的课)的地点
291
+ * @description 和班级课表里「改地点」一样:成员收到通知、不用回复,跟着这些课次的约课地点也改,推 `schedule.session_changed`(`change = place_changed`)。
292
+ * `place` 为 `null` 改成在线;地点的规则同 `createClassSessions`。已经结束、取消的课次 409 `not_editable`。
293
+ */
294
+ put: operations["setClassSessionPlace"];
266
295
  post?: never;
267
296
  delete?: never;
268
297
  options?: never;
@@ -654,12 +683,23 @@ export interface paths {
654
683
  path?: never;
655
684
  cookie?: never;
656
685
  };
657
- /** 试听时段(默认只列没结束的,按时间) */
686
+ /**
687
+ * 试听时间(教学点上的试听自己的时间;默认只列没结束的,按时间)
688
+ * @description 开课里「试听」的单独时间(这个试听专用的时间,可以几个人一起约)。老师的每周可约时间是所有 1v1 和试听共用的,不在这里列。
689
+ * 线上试听没有教学点,不在这里列。
690
+ */
658
691
  get: operations["listTrialSlots"];
659
692
  put?: never;
660
693
  /**
661
- * 排试听时段(可以按周重复);教学点要已发布
662
- * @description 409 `venue_not_published`;开始时间已过 400。老师在平台里指定(第一批不按 open_id 指定老师)。
694
+ * 开试听时间(可以按周重复):加到已有的试听,或开一份新的试听草稿
695
+ * @description 试听是平台「开课」里的一种课,学员在它的页面上约时间,老师或机构在「约课」里确认。这里直接读写同一套:
696
+ * - 给 `opening_id`(`GET /openings?kind=trial` 或之前返回的 `opening_id`):把时间加到这份试听上,老师是它的老师,`duration_minutes` 要等于它每节的时长(否则 400)。
697
+ * 它在招生中时,学员马上能约;已经结束或下架的 409 `opening_ended`。
698
+ * - 不给 `opening_id`:给 `venue_id`、`course_title`(可带 `teacher_open_id`)。同一个教学点、同一个名字、同一位老师、同样时长的试听还没结束时加到那份上,
699
+ * 否则开一份新的**试听草稿**。草稿和平台里开的一样,要机构在「开课」里上架(上架前检查老师、时间和教学点是否发布,照常审核),这里不能上架。
700
+ * 没给老师的草稿缺老师,用 `PUT /trial/openings/{opening_id}/teacher` 补上。
701
+ * 时间不能早于现在(400),同一位老师的时间不能重叠(409 `slot_overlap`)。教学点关了或被下架 409 `venue_unavailable`,
702
+ * 不是本机构的 404。`series_id` 一直是 null(新的约课没有「系列」)。
663
703
  */
664
704
  post: operations["createTrialSlots"];
665
705
  delete?: never;
@@ -677,7 +717,10 @@ export interface paths {
677
717
  };
678
718
  get?: never;
679
719
  put?: never;
680
- /** 取消一个试听时段(已约的人照常收到通知,预约随之取消) */
720
+ /**
721
+ * 关掉一个试听时间(不再能约;已有的约课不变,也不通知)
722
+ * @description 和老师在「约课」里关掉一个时间一样;已经约满的 409 `slot_taken`(要取消的是约课)。
723
+ */
681
724
  post: operations["cancelTrialSlot"];
682
725
  delete?: never;
683
726
  options?: never;
@@ -685,6 +728,27 @@ export interface paths {
685
728
  patch?: never;
686
729
  trace?: never;
687
730
  };
731
+ "/trial/openings/{opening_id}/teacher": {
732
+ parameters: {
733
+ query?: never;
734
+ header?: never;
735
+ path?: never;
736
+ cookie?: never;
737
+ };
738
+ get?: never;
739
+ /**
740
+ * 给一份试听草稿补上(或换)老师
741
+ * @description 只能改草稿(还没上架、没在审核):409 `not_draft`。老师要是本机构在机构所在区的老师(否则 400),他这些时间不能和他别的时间重叠(409 `slot_overlap`)。
742
+ * 这份试听的时间都换成这位老师的。也用来补以前用这组接口开的、缺老师的试听。
743
+ */
744
+ put: operations["setTrialTeacher"];
745
+ post?: never;
746
+ delete?: never;
747
+ options?: never;
748
+ head?: never;
749
+ patch?: never;
750
+ trace?: never;
751
+ };
688
752
  "/trial/bookings": {
689
753
  parameters: {
690
754
  query?: never;
@@ -692,7 +756,12 @@ export interface paths {
692
756
  path?: never;
693
757
  cookie?: never;
694
758
  };
695
- /** 机构各教学点的试听预约 */
759
+ /**
760
+ * 机构各教学点的试听预约
761
+ * @description 在本机构教学点上的试听课的约课(开课里开的「试听」商品,学员在商品页约时间),新约的在前。线上试听没有教学点,不在这里列出。
762
+ * 状态:`pending` 等老师确认;`reschedule_proposed` 老师换了时间、等学员同意;`confirmed` 约上了;`rejected` 老师没接受;
763
+ * `cancelled` 学员或机构取消(老师没来、退回的也算);`expired` 到时间还没确认;`attended` 上过了。
764
+ */
696
765
  get: operations["listTrialBookings"];
697
766
  put?: never;
698
767
  post?: never;
@@ -711,7 +780,10 @@ export interface paths {
711
780
  };
712
781
  get?: never;
713
782
  put?: never;
714
- /** 确认一个试听预约 */
783
+ /**
784
+ * 确认一个试听预约
785
+ * @description 和老师在「约课」里确认一样:排一节课次并告诉学员。不是等确认的 409 `not_requested`,已经到时间 409 `too_late`,老师那时有课 409 `teacher_busy`。
786
+ */
715
787
  post: operations["confirmTrialBooking"];
716
788
  delete?: never;
717
789
  options?: never;
@@ -719,6 +791,114 @@ export interface paths {
719
791
  patch?: never;
720
792
  trace?: never;
721
793
  };
794
+ "/venues": {
795
+ parameters: {
796
+ query?: never;
797
+ header?: never;
798
+ path?: never;
799
+ cookie?: never;
800
+ };
801
+ /**
802
+ * 机构的教学点(校区),新建的在前
803
+ * @description 所有状态的都列(草稿、等审核、已发布、停用、被下架);`shown` 为 true 的才在地图和发现里给人看。
804
+ */
805
+ get: operations["listVenues"];
806
+ put?: never;
807
+ /**
808
+ * 新建一个教学点(校区)的草稿;发布在平台里,照常审核
809
+ * @description 和机构后台「教学点」里新建一样,存成草稿(`status = draft`),不公开。要公开得由机构在平台里提交,按机构的审核规则发布;
810
+ * 照片、「咨询」转给谁也在平台里设。草稿就能用来排课(`createClassSessions` 的 `place`),只是班级上架前教学点要发布。
811
+ * 机构还没认证 403 `org_not_verified`。
812
+ */
813
+ post: operations["createVenue"];
814
+ delete?: never;
815
+ options?: never;
816
+ head?: never;
817
+ patch?: never;
818
+ trace?: never;
819
+ };
820
+ "/venues/{venue_id}": {
821
+ parameters: {
822
+ query?: never;
823
+ header?: never;
824
+ path?: never;
825
+ cookie?: never;
826
+ };
827
+ /** 一个教学点(只限本机构的) */
828
+ get: operations["getVenue"];
829
+ put?: never;
830
+ post?: never;
831
+ delete?: never;
832
+ options?: never;
833
+ head?: never;
834
+ patch?: never;
835
+ trace?: never;
836
+ };
837
+ "/openings": {
838
+ parameters: {
839
+ query?: never;
840
+ header?: never;
841
+ path?: never;
842
+ cookie?: never;
843
+ };
844
+ /**
845
+ * 开课:开的每一个班和课(多人班、1v1、公开课、试听、视频课),新开的在前
846
+ * @description 和平台「开课」列表同一套(只读)。机构令牌列本机构的;用户令牌只列这个人以个人老师身份开的课(不属于任何机构的),
847
+ * 他在机构里教的课要用那家机构的机构令牌读。状态:`draft` 草稿;`review` 老师交了、等机构审;`enrolling` 招生中;
848
+ * `running` 开始上了;`ended` 结束了或下架了。上架、下架、改课都在平台里做。
849
+ */
850
+ get: operations["listOpenings"];
851
+ put?: never;
852
+ post?: never;
853
+ delete?: never;
854
+ options?: never;
855
+ head?: never;
856
+ patch?: never;
857
+ trace?: never;
858
+ };
859
+ "/openings/{opening_id}": {
860
+ parameters: {
861
+ query?: never;
862
+ header?: never;
863
+ path?: never;
864
+ cookie?: never;
865
+ };
866
+ /** 一个开课 */
867
+ get: operations["getOpening"];
868
+ put?: never;
869
+ post?: never;
870
+ delete?: never;
871
+ options?: never;
872
+ head?: never;
873
+ patch?: never;
874
+ trace?: never;
875
+ };
876
+ "/insights": {
877
+ parameters: {
878
+ query?: never;
879
+ header?: never;
880
+ path?: never;
881
+ cookie?: never;
882
+ };
883
+ /**
884
+ * 机构看板的一页(只读):招生、教学、校区、收入、社区和活动
885
+ * @description 和机构工作台「数据」同一套数(M6.19 的每日汇总和实时的课次、订单),只给机构令牌、只限本机构。
886
+ * - 页(`tab`):`enrollment` 招生、`teaching` 教学、`venues` 校区、`income` 收入、`community` 社区和活动;不带时是 `enrollment`。
887
+ * 平台里的「老师」(每位老师的数)和「接待」(谁接了咨询)两页不开放:老师的数只在机构内部看、不排名;接待是内部流程。
888
+ * - **少于 5 人在接口里就压掉了**:人数是 1–4 时给 null、`withheld: true`;比例的分母、平均分的条数也要 ≥5。课次、小时、金额、次数不是人数,不压。
889
+ * - **金额按币种分开,不换算**:一页一种币种(`currency`,不带时是收入最多的那种),`filters.currencies` 列出有哪几种。
890
+ * - 时间:`range`(截止今天、含今天)或自定义 `from`、`to`(机构时区的本地日期,含两头,最多 366 天,不能晚于今天);`compare` 多给上一周期。
891
+ * - 没有任何一个人的 ID 或名字;按课程、校区、活动的行带它们的名字。别的区的学员只在汇总里,别的区某项不到 5 不给;有区没答上 `partial: true`。
892
+ */
893
+ get: operations["getInsights"];
894
+ put?: never;
895
+ post?: never;
896
+ delete?: never;
897
+ options?: never;
898
+ head?: never;
899
+ patch?: never;
900
+ trace?: never;
901
+ };
722
902
  "/trade/orders": {
723
903
  parameters: {
724
904
  query?: never;
@@ -736,6 +916,47 @@ export interface paths {
736
916
  patch?: never;
737
917
  trace?: never;
738
918
  };
919
+ "/bookings": {
920
+ parameters: {
921
+ query?: never;
922
+ header?: never;
923
+ path?: never;
924
+ cookie?: never;
925
+ };
926
+ /**
927
+ * 机构的约课(1v1 和试听,只读对账),上课时间晚的在前
928
+ * @description 学员在开课里约的每一节课,和平台「约课」同一套。确认、拒绝、换时间、取消都在平台里做(试听的确认见 `confirmTrialBooking`)。
929
+ * 状态:`requested` 等老师确认(`change.kind = teacher_proposed` 是老师换了时间、等学员同意);`confirmed` 约上了
930
+ * (`change.kind` 是 `teacher_moved` 或 `learner_requested` 时有一个改期等回复);`declined` 老师没接受;`cancelled` 取消了;
931
+ * `expired` 到时间还没确认;`completed` 上过了(`outcome` 写学员来没来);`missed` 老师没来,这一节退回了。
932
+ * 只给学员的 `open_id`,不给名字和留言。
933
+ */
934
+ get: operations["listBookings"];
935
+ put?: never;
936
+ post?: never;
937
+ delete?: never;
938
+ options?: never;
939
+ head?: never;
940
+ patch?: never;
941
+ trace?: never;
942
+ };
943
+ "/bookings/{booking_id}": {
944
+ parameters: {
945
+ query?: never;
946
+ header?: never;
947
+ path?: never;
948
+ cookie?: never;
949
+ };
950
+ /** 一节约课 */
951
+ get: operations["getBooking"];
952
+ put?: never;
953
+ post?: never;
954
+ delete?: never;
955
+ options?: never;
956
+ head?: never;
957
+ patch?: never;
958
+ trace?: never;
959
+ };
739
960
  "/reviews": {
740
961
  parameters: {
741
962
  query?: never;
@@ -865,9 +1086,7 @@ export interface paths {
865
1086
  patch?: never;
866
1087
  trace?: never;
867
1088
  };
868
- }
869
- export interface webhooks {
870
- event: {
1089
+ "/community/images": {
871
1090
  parameters: {
872
1091
  query?: never;
873
1092
  header?: never;
@@ -877,28 +1096,19 @@ export interface webhooks {
877
1096
  get?: never;
878
1097
  put?: never;
879
1098
  /**
880
- * 事件推送(平台 → 应用的请求地址)
881
- * @description 订阅的事件发生时,平台 POST 到应用在「事件与回调」里填的请求地址。
882
- * - **签名**:`X-Ich-Timestamp`(秒)、`X-Ich-Nonce`、`X-Ich-Signature: v1=<hex>`,
883
- * 其中 `hex = HMAC-SHA256(签名密钥, timestamp + "." + nonce + "." + 原始请求体)`。应用要验签,并拒绝
884
- * 时间戳和当前相差 5 分钟以上的请求。
885
- * - **加密(可选)**:填了加密密钥时请求体是 `{"encrypt": "<base64>"}`,内容是 AES-256-GCM 加密的事件 JSON:
886
- * 密钥 = SHA-256(加密密钥),base64 解出来依次是 12 字节 IV、密文、16 字节认证标签。签名对加密后的请求体算。
887
- * - **重试**:应用 5 秒内回 2xx 算成功;否则按 1、2、4、8 … 分钟退避重试,最多 12 次(约 3 天),每次都进
888
- * 事件推送日志,开发者后台能手动重推。同一个事件重试时 `event_id` 不变,应用按它去重(幂等)。
889
- * - **地址验证**:保存请求地址时平台先推一次 `{"type": "url_verification", "challenge": "…"}`,应用原样回
890
- * `{"challenge": "…"}`(同样签名)。
891
- * - 按机构订阅(机构令牌的事件)的,只推装了应用的机构的事件,`header.tenant_key` 是那个机构;个人数据只在
892
- * 用户授权了相应权限时才带(否则只带 `open_id`)。
1099
+ * 要一个图片上传地址(发帖时才重新编码、去掉 EXIF)
1100
+ * @description 用 `upload_url` 把图片 PUT 上去(`headers` 照带),发帖时在 `images` 里写 `key`。一天内没用上的会被删掉。
1101
+ * 图片最大 10 MB,jpeg、png、webp、gif。这个人每小时最多要 60 次(和他自己在 App 里传图算在一起)。
1102
+ * 只有要到地址的这个应用能把它放进帖子。
893
1103
  */
894
- post: operations["onEvent"];
1104
+ post: operations["createCommunityImageUpload"];
895
1105
  delete?: never;
896
1106
  options?: never;
897
1107
  head?: never;
898
1108
  patch?: never;
899
1109
  trace?: never;
900
1110
  };
901
- "card.action": {
1111
+ "/community/videos": {
902
1112
  parameters: {
903
1113
  query?: never;
904
1114
  header?: never;
@@ -908,64 +1118,315 @@ export interface webhooks {
908
1118
  get?: never;
909
1119
  put?: never;
910
1120
  /**
911
- * 卡片回调(用户点了机器人卡片上的按钮 → 应用的卡片回调地址)
912
- * @description 签名和加密同事件推送。应用 3 秒内可以回一张新卡片替换原来的(`{"card": {...}}`),或回空。
1121
+ * 要一个视频上传地址(最大 4 GB、1 小时)
1122
+ * @description 用 `upload.url` 把视频 PUT 上去,再 `POST /community/videos/{asset_id}/complete`。转码好了收到 `community.video_ready`,
1123
+ * 也可以查 `GET /community/videos/{asset_id}`。一周内没用上的会被删掉。这个应用每天的视频总大小有上限(`community_video_gb`)。
913
1124
  */
914
- post: operations["onCardAction"];
1125
+ post: operations["createCommunityVideoUpload"];
915
1126
  delete?: never;
916
1127
  options?: never;
917
1128
  head?: never;
918
1129
  patch?: never;
919
1130
  trace?: never;
920
1131
  };
921
- }
922
- export interface components {
923
- schemas: {
924
- PageMeta: {
925
- has_more: boolean;
926
- page_token?: string | null;
1132
+ "/community/videos/{asset_id}/complete": {
1133
+ parameters: {
1134
+ query?: never;
1135
+ header?: never;
1136
+ path?: never;
1137
+ cookie?: never;
927
1138
  };
928
- /**
929
- * @description 错误的细分原因(`error.details.reason`)
930
- * @enum {string}
931
- */
932
- OpenErrorReason: "invalid_credentials" | "invalid_token" | "token_expired" | "token_type" | "app_disabled" | "not_installed" | "not_available" | "scope_denied" | "scope_pending" | "scope_not_declared" | "gold_only" | "rate_limited" | "daily_quota_exceeded" | "code_expired" | "code_used" | "code_invalid" | "redirect_uri_mismatch" | "origin_not_trusted" | "ip_not_allowed";
933
- ErrorResponse: {
934
- error: {
935
- /** @enum {string} */
936
- code: "UNAUTHENTICATED" | "FORBIDDEN" | "NOT_FOUND" | "VALIDATION_FAILED" | "CONFLICT" | "RATE_LIMITED" | "PAYLOAD_TOO_LARGE" | "WRONG_REGION" | "SERVICE_UNAVAILABLE" | "INTERNAL";
937
- /** @description 按 Accept-Language 本地化的提示 */
938
- message: string;
939
- request_id: string;
940
- details?: {
941
- reason?: components["schemas"]["OpenErrorReason"];
942
- scope?: string;
943
- never_ask?: boolean;
944
- } & {
945
- [key: string]: unknown;
946
- };
947
- };
1139
+ get?: never;
1140
+ put?: never;
1141
+ /** 传完了,开始转码(重复调用没有副作用) */
1142
+ post: operations["completeCommunityVideoUpload"];
1143
+ delete?: never;
1144
+ options?: never;
1145
+ head?: never;
1146
+ patch?: never;
1147
+ trace?: never;
1148
+ };
1149
+ "/community/videos/{asset_id}": {
1150
+ parameters: {
1151
+ query?: never;
1152
+ header?: never;
1153
+ path?: never;
1154
+ cookie?: never;
948
1155
  };
949
- /** @description 一个人在一个应用里的 ID(同一个人在不同应用里不同,和 OIDC 的 `sub` 相同) */
950
- OpenId: string;
951
- /** @description 装了应用的机构(`<机构所在区>.<机构 ID>`) */
952
- TenantKey: string;
953
- /** @description App ID(`<应用所在区>.cli_…`),也是 OIDC 的 client_id */
954
- AppId: string;
955
- /** @description 权限码(目录见 `ScopeCode`;平台的 `GET /api/v1/open/scopes` 返回带说明的目录) */
956
- Scope: string;
957
- /** @enum {string} */
958
- ScopeCode: "openid" | "profile" | "org:member:readonly" | "org:member" | "class:readonly" | "class:member" | "schedule:readonly" | "schedule" | "attendance:readonly" | "coursework" | "grade:readonly" | "course" | "learning:progress:readonly" | "im:message:send_as_bot" | "im:chat:readonly" | "notify:send" | "live:recording:readonly" | "event" | "event:registration:readonly" | "job" | "job:candidate:readonly" | "trial" | "trade:order:readonly" | "review" | "profile:interest:readonly" | "profile:interest" | "profile:learning:readonly" | "report:create";
959
- /** @enum {string} */
960
- OrgRole: "owner" | "admin" | "academic" | "editor" | "content_reviewer" | "teacher" | "community" | "recruiter" | "member";
961
- AppCredentials: {
962
- app_id: components["schemas"]["AppId"];
963
- app_secret: string;
1156
+ /** 看转码状态(只限这个应用替这个人传的) */
1157
+ get: operations["getCommunityVideo"];
1158
+ put?: never;
1159
+ post?: never;
1160
+ delete?: never;
1161
+ options?: never;
1162
+ head?: never;
1163
+ patch?: never;
1164
+ trace?: never;
1165
+ };
1166
+ "/community/posts": {
1167
+ parameters: {
1168
+ query?: never;
1169
+ header?: never;
1170
+ path?: never;
1171
+ cookie?: never;
964
1172
  };
965
- AppAccessToken: {
966
- app_access_token: string;
967
- /** @description 秒 */
968
- expires_in: number;
1173
+ /** 这个应用替这个人发过的帖子(这个人自己发的、别的应用发的都看不到) */
1174
+ get: operations["listCommunityPosts"];
1175
+ put?: never;
1176
+ /**
1177
+ * 用这个人的名义发一条公开帖子(文字、最多 9 张图或 1 个视频、最多 10 个标签)
1178
+ * @description 规则和审核都和这个人自己发帖一样,帖子上写明「来自 <应用名>」。没有 `title` 时取正文第一句,没有文字时必须给 `title`。
1179
+ * `uuid` 是幂等键:同一个 `uuid` 一小时内只发一次,重复调用返回同一条帖子。
1180
+ *
1181
+ * 不能替人同意:这个人还没同意公开作者身份,或者撤回了同意,返回 409 `details.reason = consent_required`,带 `details.consent_url`,
1182
+ * 请这个人自己去点。视频没转好是 409 `video_not_ready`,转码失败是 409 `video_failed`。
1183
+ * 平台没有定时发、存草稿;被拒的帖子进这个人的草稿箱,应用收到 `community.post_reviewed`。
1184
+ *
1185
+ * 限额:这个人每小时 20 帖(和他自己发的算在一起,`post_rate_limited`);这个应用替这个人每天 10 帖(`app_user_daily_limit`);
1186
+ * 这个应用每天的总量(`daily_quota_exceeded`,`details.kind = community_posts`)。
1187
+ *
1188
+ * **机构令牌**(机构号发帖):用机构的社区账号发,帖子作者是机构(机构名、logo),同样写明「来自 <应用名>」、同样审核,审核结果记在应用头上。
1189
+ * 要机构装了这个应用、批了 `community:post`;机构的主页还没发布过、没有社区账号时 409 `no_org_account`。机构号每小时 20 帖、
1190
+ * 这个应用替这家机构每天 10 帖,和应用每天的总量算在一起。机构号的帖子没有 IP 属地和发布地。上传图片、视频,列出、查看、删除帖子
1191
+ * 也都用机构令牌,只碰这个应用替这家机构传的、发的。
1192
+ */
1193
+ post: operations["createCommunityPost"];
1194
+ delete?: never;
1195
+ options?: never;
1196
+ head?: never;
1197
+ patch?: never;
1198
+ trace?: never;
1199
+ };
1200
+ "/community/posts/{post_id}": {
1201
+ parameters: {
1202
+ query?: never;
1203
+ header?: never;
1204
+ path?: never;
1205
+ cookie?: never;
1206
+ };
1207
+ /** 一条帖子的状态(只限这个应用发的,别的都是 404) */
1208
+ get: operations["getCommunityPost"];
1209
+ put?: never;
1210
+ post?: never;
1211
+ /** 删帖(只限这个应用发的,别的都是 404) */
1212
+ delete: operations["deleteCommunityPost"];
1213
+ options?: never;
1214
+ head?: never;
1215
+ patch?: never;
1216
+ trace?: never;
1217
+ };
1218
+ "/community/categories": {
1219
+ parameters: {
1220
+ query?: never;
1221
+ header?: never;
1222
+ path?: never;
1223
+ cookie?: never;
1224
+ };
1225
+ /** 发帖能选的分类(本区开着的) */
1226
+ get: operations["listCommunityCategories"];
1227
+ put?: never;
1228
+ post?: never;
1229
+ delete?: never;
1230
+ options?: never;
1231
+ head?: never;
1232
+ patch?: never;
1233
+ trace?: never;
1234
+ };
1235
+ "/me/schedule": {
1236
+ parameters: {
1237
+ query?: never;
1238
+ header?: never;
1239
+ path?: never;
1240
+ cookie?: never;
1241
+ };
1242
+ /**
1243
+ * 这个人的课表:他的课次、报名的活动、确认的试听和考试(给日历类应用;只读)
1244
+ * @description 时间范围最长 100 天。别的区的课次也在里面(在它们所在的区读)。取消的课次也列出来(`cancelled`),方便日历把它去掉。
1245
+ */
1246
+ get: operations["getMySchedule"];
1247
+ put?: never;
1248
+ post?: never;
1249
+ delete?: never;
1250
+ options?: never;
1251
+ head?: never;
1252
+ patch?: never;
1253
+ trace?: never;
1254
+ };
1255
+ "/me/courses": {
1256
+ parameters: {
1257
+ query?: never;
1258
+ header?: never;
1259
+ path?: never;
1260
+ cookie?: never;
1261
+ };
1262
+ /** 这个人在学的课和进度(学完几课、最后学习时间;等级和目标是金牌专属的画像,不在这里) */
1263
+ get: operations["listMyCourses"];
1264
+ put?: never;
1265
+ post?: never;
1266
+ delete?: never;
1267
+ options?: never;
1268
+ head?: never;
1269
+ patch?: never;
1270
+ trace?: never;
1271
+ };
1272
+ "/me/words": {
1273
+ parameters: {
1274
+ query?: never;
1275
+ header?: never;
1276
+ path?: never;
1277
+ cookie?: never;
1278
+ };
1279
+ /** 这个人的生词本:字词、拼音、复习状态(不给释义,也改不了复习记录) */
1280
+ get: operations["listMyWords"];
1281
+ put?: never;
1282
+ /** 往这个人的生词本里加一个词(已经有时不变;来源记成这个应用) */
1283
+ post: operations["addMyWord"];
1284
+ delete?: never;
1285
+ options?: never;
1286
+ head?: never;
1287
+ patch?: never;
1288
+ trace?: never;
1289
+ };
1290
+ "/classes/{class_id}/exams": {
1291
+ parameters: {
1292
+ query?: never;
1293
+ header?: never;
1294
+ path?: never;
1295
+ cookie?: never;
1296
+ };
1297
+ /** 班级的考试(只限机构令牌、本机构) */
1298
+ get: operations["listClassExams"];
1299
+ put?: never;
1300
+ post?: never;
1301
+ delete?: never;
1302
+ options?: never;
1303
+ head?: never;
1304
+ patch?: never;
1305
+ trace?: never;
1306
+ };
1307
+ "/classes/{class_id}/exams/{exam_id}/results": {
1308
+ parameters: {
1309
+ query?: never;
1310
+ header?: never;
1311
+ path?: never;
1312
+ cookie?: never;
1313
+ };
1314
+ /** 一场考试每个学员的结果(公布以后才有分数;只限机构令牌、本机构) */
1315
+ get: operations["listClassExamResults"];
1316
+ put?: never;
1317
+ post?: never;
1318
+ delete?: never;
1319
+ options?: never;
1320
+ head?: never;
1321
+ patch?: never;
1322
+ trace?: never;
1323
+ };
1324
+ }
1325
+ export interface webhooks {
1326
+ event: {
1327
+ parameters: {
1328
+ query?: never;
1329
+ header?: never;
1330
+ path?: never;
1331
+ cookie?: never;
1332
+ };
1333
+ get?: never;
1334
+ put?: never;
1335
+ /**
1336
+ * 事件推送(平台 → 应用的请求地址)
1337
+ * @description 订阅的事件发生时,平台 POST 到应用在「事件与回调」里填的请求地址。
1338
+ * - **签名**:`X-Ich-Timestamp`(秒)、`X-Ich-Nonce`、`X-Ich-Signature: v1=<hex>`,
1339
+ * 其中 `hex = HMAC-SHA256(签名密钥, timestamp + "." + nonce + "." + 原始请求体)`。应用要验签,并拒绝
1340
+ * 时间戳和当前相差 5 分钟以上的请求。
1341
+ * - **加密(可选)**:填了加密密钥时请求体是 `{"encrypt": "<base64>"}`,内容是 AES-256-GCM 加密的事件 JSON:
1342
+ * 密钥 = SHA-256(加密密钥),base64 解出来依次是 12 字节 IV、密文、16 字节认证标签。签名对加密后的请求体算。
1343
+ * - **重试**:应用 5 秒内回 2xx 算成功;否则按 1、2、4、8 … 分钟退避重试,最多 12 次(约 3 天),每次都进
1344
+ * 事件推送日志,开发者后台能手动重推。同一个事件重试时 `event_id` 不变,应用按它去重(幂等)。
1345
+ * - **地址验证**:保存请求地址时平台先推一次 `{"type": "url_verification", "challenge": "…"}`,应用原样回
1346
+ * `{"challenge": "…"}`(同样签名)。
1347
+ * - 按机构订阅(机构令牌的事件)的,只推装了应用的机构的事件,`header.tenant_key` 是那个机构;个人数据只在
1348
+ * 用户授权了相应权限时才带(否则只带 `open_id`)。
1349
+ */
1350
+ post: operations["onEvent"];
1351
+ delete?: never;
1352
+ options?: never;
1353
+ head?: never;
1354
+ patch?: never;
1355
+ trace?: never;
1356
+ };
1357
+ "card.action": {
1358
+ parameters: {
1359
+ query?: never;
1360
+ header?: never;
1361
+ path?: never;
1362
+ cookie?: never;
1363
+ };
1364
+ get?: never;
1365
+ put?: never;
1366
+ /**
1367
+ * 卡片回调(用户点了机器人卡片上的按钮 → 应用的卡片回调地址)
1368
+ * @description 签名和加密同事件推送。应用 3 秒内可以回一张新卡片替换原来的(`{"card": {...}}`),或回空。
1369
+ */
1370
+ post: operations["onCardAction"];
1371
+ delete?: never;
1372
+ options?: never;
1373
+ head?: never;
1374
+ patch?: never;
1375
+ trace?: never;
1376
+ };
1377
+ }
1378
+ export interface components {
1379
+ schemas: {
1380
+ PageMeta: {
1381
+ has_more: boolean;
1382
+ page_token?: string | null;
1383
+ };
1384
+ /**
1385
+ * @description 错误的细分原因(`error.details.reason`)
1386
+ * @enum {string}
1387
+ */
1388
+ OpenErrorReason: "invalid_credentials" | "invalid_token" | "token_expired" | "token_type" | "app_disabled" | "not_installed" | "not_available" | "scope_denied" | "scope_pending" | "scope_not_declared" | "gold_only" | "rate_limited" | "daily_quota_exceeded" | "code_expired" | "code_used" | "code_invalid" | "redirect_uri_mismatch" | "origin_not_trusted" | "ip_not_allowed" | "org_not_installed" | "minor_not_allowed" | "birth_year_required" | "feature_disabled" | "app_post_stopped" | "consent_required" | "video_not_ready" | "video_failed" | "post_rate_limited" | "app_user_daily_limit" | "post_in_progress" | "no_org_account" | "teacher_busy" | "online_only" | "venue_unavailable" | "venue_not_found" | "slot_overlap" | "slot_taken" | "not_draft" | "not_editable" | "org_not_verified" | "opening_ended" | "opening_incomplete";
1389
+ ErrorResponse: {
1390
+ error: {
1391
+ /** @enum {string} */
1392
+ code: "UNAUTHENTICATED" | "FORBIDDEN" | "NOT_FOUND" | "VALIDATION_FAILED" | "CONFLICT" | "RATE_LIMITED" | "PAYLOAD_TOO_LARGE" | "WRONG_REGION" | "SERVICE_UNAVAILABLE" | "INTERNAL";
1393
+ /** @description 按 Accept-Language 本地化的提示 */
1394
+ message: string;
1395
+ request_id: string;
1396
+ details?: {
1397
+ reason?: components["schemas"]["OpenErrorReason"];
1398
+ scope?: string;
1399
+ never_ask?: boolean;
1400
+ /** @description `consent_required` 时给:请这个人打开它,自己点同意 */
1401
+ consent_url?: string;
1402
+ } & {
1403
+ [key: string]: unknown;
1404
+ };
1405
+ };
1406
+ };
1407
+ /**
1408
+ * @description 一个人在一个应用里的 ID(同一个人在不同应用里不同,和 OIDC 的 `sub` 相同)。同一个应用被几家机构安装时,同一个人的 `open_id`
1409
+ * 是同一个;开发者协议禁止用它跨机构比对。
1410
+ */
1411
+ OpenId: string;
1412
+ /** @description 装了应用的机构(`<机构所在区>.<机构 ID>`) */
1413
+ TenantKey: string;
1414
+ /** @description App ID(`<应用所在区>.cli_…`),也是 OIDC 的 client_id */
1415
+ AppId: string;
1416
+ /** @description 权限码(目录见 `ScopeCode`;平台的 `GET /api/v1/open/scopes` 返回带说明的目录) */
1417
+ Scope: string;
1418
+ /** @enum {string} */
1419
+ ScopeCode: "openid" | "profile" | "org:member:readonly" | "org:member" | "class:readonly" | "class:member" | "schedule:readonly" | "schedule" | "attendance:readonly" | "coursework" | "grade:readonly" | "course" | "learning:progress:readonly" | "im:message:send_as_bot" | "im:chat:readonly" | "notify:send" | "live:recording:readonly" | "event" | "event:registration:readonly" | "job" | "job:candidate:readonly" | "trial" | "trade:order:readonly" | "trade:booking:readonly" | "venue:readonly" | "venue" | "opening:readonly" | "review" | "profile:interest:readonly" | "profile:interest" | "profile:learning:readonly" | "report:create" | "community:post" | "me:schedule:readonly" | "me:learning:readonly" | "me:words:readonly" | "me:words" | "insights:readonly";
1420
+ /** @enum {string} */
1421
+ OrgRole: "owner" | "admin" | "academic" | "editor" | "content_reviewer" | "teacher" | "community" | "recruiter" | "member";
1422
+ AppCredentials: {
1423
+ app_id: components["schemas"]["AppId"];
1424
+ app_secret: string;
1425
+ };
1426
+ AppAccessToken: {
1427
+ app_access_token: string;
1428
+ /** @description 秒 */
1429
+ expires_in: number;
969
1430
  };
970
1431
  TenantAccessToken: {
971
1432
  tenant_access_token: string;
@@ -1016,6 +1477,11 @@ export interface components {
1016
1477
  /** Format: uuid */
1017
1478
  class_id: string;
1018
1479
  name: string;
1480
+ /**
1481
+ * @description class 班级;lecture 公开课;event 活动
1482
+ * @enum {string}
1483
+ */
1484
+ kind: "class" | "lecture" | "event";
1019
1485
  description?: string | null;
1020
1486
  student_count: number;
1021
1487
  /** Format: date-time */
@@ -1039,10 +1505,39 @@ export interface components {
1039
1505
  starts_at: string;
1040
1506
  /** Format: date-time */
1041
1507
  ends_at: string;
1042
- /** @enum {string} */
1043
- status: "scheduled" | "live" | "ended" | "cancelled";
1044
- /** @description 是不是在在线课堂里上(课次都是) */
1508
+ /**
1509
+ * @description draft 是还没上架的班的课次(只在排课的回应里出现,不进课表)
1510
+ * @enum {string}
1511
+ */
1512
+ status: "draft" | "scheduled" | "live" | "ended" | "cancelled";
1513
+ /** @description 在线课堂里上为 true;在教学点上(`place` 不为 null)为 false */
1045
1514
  live?: boolean;
1515
+ /** @description 在哪个教学点上;null 在线 */
1516
+ place?: components["schemas"]["OpenPlace"] | null;
1517
+ /** @description 上这次课的老师 */
1518
+ teacher_open_id?: components["schemas"]["OpenId"] | null;
1519
+ };
1520
+ OpenPlaceInput: {
1521
+ /**
1522
+ * Format: uuid
1523
+ * @description 本机构的教学点(`GET /venues`)
1524
+ */
1525
+ venue_id: string;
1526
+ /** @description 教室、楼层等,只给上课的人看 */
1527
+ note?: string | null;
1528
+ };
1529
+ /** @description 上课的地点:教学点公开的地址,加上教室 */
1530
+ OpenPlace: {
1531
+ /** Format: uuid */
1532
+ venue_id: string;
1533
+ name: string;
1534
+ address: string;
1535
+ city?: string | null;
1536
+ /** @description ISO 3166-1 两位代码 */
1537
+ country?: string | null;
1538
+ time_zone?: string | null;
1539
+ /** @description 教室 */
1540
+ note?: string | null;
1046
1541
  };
1047
1542
  AttendanceRecord: {
1048
1543
  open_id: components["schemas"]["OpenId"];
@@ -1266,13 +1761,25 @@ export interface components {
1266
1761
  booked: number;
1267
1762
  pending: number;
1268
1763
  course_title: string;
1764
+ /** @description 一直是 null(以前按周重复的一组时间共用一个) */
1269
1765
  series_id?: string | null;
1270
- /** @enum {string} */
1766
+ /**
1767
+ * @description open 还能约或已经约了;cancelled 关掉了
1768
+ * @enum {string}
1769
+ */
1271
1770
  status: "open" | "cancelled";
1771
+ /**
1772
+ * Format: uuid
1773
+ * @description 属于哪份试听(`GET /openings/{opening_id}`)
1774
+ */
1775
+ opening_id: string;
1776
+ /** @description 上这个时间的老师;试听还缺老师时为 null */
1777
+ teacher_open_id?: components["schemas"]["OpenId"] | null;
1272
1778
  };
1273
1779
  TrialBooking: {
1274
1780
  /** Format: uuid */
1275
1781
  booking_id: string;
1782
+ /** @description 上课的教学点(`/trial/venues` 的 `venue_id`) */
1276
1783
  venue_id?: string;
1277
1784
  open_id?: components["schemas"]["OpenId"];
1278
1785
  status: string;
@@ -1282,43 +1789,360 @@ export interface components {
1282
1789
  ends_at?: string | null;
1283
1790
  course_title?: string | null;
1284
1791
  };
1285
- TradeOrder: {
1286
- order_id: string;
1287
- status: string;
1288
- amount_minor: number;
1289
- currency: string;
1290
- item_title?: string | null;
1291
- open_id?: components["schemas"]["OpenId"];
1292
- /** Format: date-time */
1293
- created_at: string;
1294
- /** Format: date-time */
1295
- paid_at?: string | null;
1296
- };
1297
- OpenReview: {
1298
- review_id: string;
1299
- /** @description 评价对象的公开内容 ID */
1300
- target_id: string;
1301
- stars: number;
1302
- body?: string | null;
1303
- author_name?: string | null;
1304
- replied?: boolean;
1305
- /** Format: date-time */
1306
- created_at: string;
1307
- };
1308
- PublicContent: {
1309
- item_id: string;
1310
- kind: string;
1311
- title: string;
1312
- summary?: string | null;
1792
+ OpenVenue: {
1793
+ /** Format: uuid */
1794
+ venue_id: string;
1795
+ name: string;
1313
1796
  /**
1314
- * Format: uri
1315
- * @description 平台里的详情页
1797
+ * @description campus 校区;point 教学点;partner 合作点
1798
+ * @enum {string}
1316
1799
  */
1317
- url?: string;
1800
+ kind: "campus" | "point" | "partner";
1801
+ /**
1802
+ * @description 草稿、等本机构审核、被退回、已发布、停用、被语合中心下架
1803
+ * @enum {string}
1804
+ */
1805
+ status: "draft" | "pending" | "returned" | "published" | "closed" | "taken_down";
1806
+ /** @description 现在给不给人看(发布过,没停用、没被下架;改了还没再审时照旧显示上一次发布的) */
1807
+ shown: boolean;
1808
+ /** @description ISO 3166-1 两位代码 */
1809
+ country: string;
1810
+ city?: string | null;
1811
+ address: string;
1812
+ /** @description 地图上的点;没选点时为 null */
1813
+ location?: null | {
1814
+ lat: number;
1815
+ lng: number;
1816
+ /** @enum {string} */
1817
+ datum?: "wgs84" | "bd09";
1818
+ };
1819
+ time_zone: string;
1820
+ /** @description 发布过的公开内容 ID(`<区>:venue:<id>`) */
1821
+ item_id?: string | null;
1318
1822
  /** Format: date-time */
1319
- updated_at?: string | null;
1823
+ created_at: string;
1320
1824
  };
1321
- InterestProfile: {
1825
+ /** @description 和机构后台建教学点的请求体相同(`VenueInput`,见 openapi.yaml),但不收「咨询」转给谁和挂在这里的班级、商品、活动(这些在平台里设)。 */
1826
+ OpenVenueCreate: {
1827
+ name: string;
1828
+ /** @enum {string} */
1829
+ kind: "campus" | "point" | "partner";
1830
+ audiences?: ("children" | "adults" | "business")[];
1831
+ course_types?: ("hsk" | "yct" | "conversation" | "business" | "children" | "culture" | "other")[];
1832
+ description?: string;
1833
+ country: string;
1834
+ city?: string | null;
1835
+ address: string;
1836
+ /** @description 在地图上选的点;没给的教学点不上地图 */
1837
+ location?: null | {
1838
+ lat: number;
1839
+ lng: number;
1840
+ /**
1841
+ * @description 默认 wgs84;bd09 是百度地图的坐标
1842
+ * @enum {string}
1843
+ */
1844
+ datum?: "wgs84" | "bd09";
1845
+ };
1846
+ /** @description 交通说明 */
1847
+ transit?: string;
1848
+ /** @description IANA 时区 */
1849
+ time_zone: string;
1850
+ /** @description 每周的开放时间(教学点所在时区的本地时间,一天可以有几段) */
1851
+ hours?: {
1852
+ /** @description 1 星期一 … 7 星期日 */
1853
+ weekday: number;
1854
+ opens: string;
1855
+ /** @description 晚于 opens */
1856
+ closes: string;
1857
+ }[];
1858
+ facilities?: ("wifi" | "projector" | "library" | "lounge" | "parking" | "accessible" | "kids_area" | "recording_room")[];
1859
+ /** @description 公开的联系人 */
1860
+ contact_name?: string | null;
1861
+ /** @description 公开的电话 */
1862
+ contact_phone?: string | null;
1863
+ };
1864
+ OpenOpening: {
1865
+ /**
1866
+ * Format: uuid
1867
+ * @description 有班级的(多人班、公开课)是班级 ID,其余是商品 ID
1868
+ */
1869
+ opening_id: string;
1870
+ /**
1871
+ * @description group 多人班;one_on_one 1v1 课时包;lecture 公开课;trial 试听;video 视频课
1872
+ * @enum {string}
1873
+ */
1874
+ kind: "group" | "one_on_one" | "lecture" | "trial" | "video";
1875
+ /** @enum {string} */
1876
+ status: "draft" | "review" | "enrolling" | "running" | "ended";
1877
+ /** @description 招生中但名额满了 */
1878
+ full?: boolean;
1879
+ title: string;
1880
+ summary?: string;
1881
+ level?: number | null;
1882
+ languages?: string[];
1883
+ /**
1884
+ * Format: uuid
1885
+ * @description 班级(`/classes/{class_id}`);1v1、试听、视频课没有
1886
+ */
1887
+ class_id?: string | null;
1888
+ /** @description 上过架的商品的公开内容 ID(`<区>:offer:<id>`) */
1889
+ offer_item_id?: string | null;
1890
+ /**
1891
+ * Format: uuid
1892
+ * @description 教的课程(`/courses/{course_id}`)
1893
+ */
1894
+ course_id?: string | null;
1895
+ course_title?: string | null;
1896
+ teachers: {
1897
+ open_id: components["schemas"]["OpenId"];
1898
+ name: string;
1899
+ }[];
1900
+ /** @description 默认地点;null 在线 */
1901
+ place?: components["schemas"]["OpenPlace"] | null;
1902
+ /** @description 课次不都在同一个地方 */
1903
+ mixed_places?: boolean;
1904
+ /**
1905
+ * @description public 公开招生(商品上架);code 只凭加入码进
1906
+ * @enum {string}
1907
+ */
1908
+ enrollment?: "public" | "code";
1909
+ /** @description 商品在卖(招生中) */
1910
+ listed: boolean;
1911
+ /** @description 名额(多人班、公开课) */
1912
+ seats?: null | {
1913
+ max?: number | null;
1914
+ min?: number | null;
1915
+ taken: number;
1916
+ };
1917
+ price?: null | {
1918
+ amount_minor: number;
1919
+ currency: string;
1920
+ };
1921
+ /** @description 课次进度(有班级的) */
1922
+ sessions: {
1923
+ total: number;
1924
+ done: number;
1925
+ /** Format: date-time */
1926
+ first_starts_at?: string | null;
1927
+ next?: null | {
1928
+ /** Format: uuid */
1929
+ session_id: string;
1930
+ /** Format: date-time */
1931
+ starts_at: string;
1932
+ /** Format: date-time */
1933
+ ends_at: string;
1934
+ time_zone?: string;
1935
+ place?: components["schemas"]["OpenPlace"] | null;
1936
+ };
1937
+ };
1938
+ /** @description 1v1 一共几节 */
1939
+ lessons?: number | null;
1940
+ /** @description 1v1、试听每节多长 */
1941
+ lesson_minutes?: number | null;
1942
+ /**
1943
+ * Format: date-time
1944
+ * @description 1v1、试听在卖时最近能约的时间
1945
+ */
1946
+ next_available?: string | null;
1947
+ /** Format: date-time */
1948
+ created_at: string;
1949
+ /** Format: date-time */
1950
+ updated_at: string;
1951
+ };
1952
+ OpenBooking: {
1953
+ /** Format: uuid */
1954
+ booking_id: string;
1955
+ /** @enum {string} */
1956
+ kind: "one_on_one" | "trial";
1957
+ /**
1958
+ * Format: uuid
1959
+ * @description 约的是哪个开课(`GET /openings/{opening_id}`)
1960
+ */
1961
+ opening_id: string;
1962
+ title: string;
1963
+ open_id: components["schemas"]["OpenId"];
1964
+ teacher_open_id?: components["schemas"]["OpenId"];
1965
+ /** @enum {string} */
1966
+ status: "requested" | "confirmed" | "declined" | "cancelled" | "completed" | "expired" | "missed";
1967
+ /** @description 等回复的改期 */
1968
+ change?: null | {
1969
+ /** @enum {string} */
1970
+ kind: "teacher_moved" | "teacher_proposed" | "learner_requested";
1971
+ /** Format: date-time */
1972
+ to_starts_at: string;
1973
+ /** Format: date-time */
1974
+ to_ends_at: string;
1975
+ };
1976
+ /**
1977
+ * @description 上完以后的结果
1978
+ * @enum {string|null}
1979
+ */
1980
+ outcome?: "held" | "learner_absent" | "teacher_absent" | "both_absent" | null;
1981
+ /** @description 学员在规定时间内取消,这一节不退 */
1982
+ forfeited?: boolean;
1983
+ /** Format: date-time */
1984
+ starts_at: string;
1985
+ /** Format: date-time */
1986
+ ends_at: string;
1987
+ time_zone: string;
1988
+ /** @description 在哪上;null 在线 */
1989
+ place?: components["schemas"]["OpenPlace"] | null;
1990
+ /**
1991
+ * Format: uuid
1992
+ * @description 确认后排的课次
1993
+ */
1994
+ session_id?: string | null;
1995
+ /** @description 用的哪张订单的课时(免费试听为 null) */
1996
+ order_id?: string | null;
1997
+ /** Format: date-time */
1998
+ created_at: string;
1999
+ /** Format: date-time */
2000
+ updated_at?: string;
2001
+ };
2002
+ /** @description 一页看板。数字(`tiles`)和图(`cards`)都按 `key` 给,名字和说明由应用自己写(和平台三端一样按 key 取文案)。 */
2003
+ OpenInsightsPage: {
2004
+ tab: string;
2005
+ /** @description 这个接口能看的页 */
2006
+ tabs: string[];
2007
+ /** @enum {string} */
2008
+ range: "7d" | "30d" | "90d" | "365d" | "custom";
2009
+ /**
2010
+ * Format: date
2011
+ * @description 第一天
2012
+ */
2013
+ from: string;
2014
+ /**
2015
+ * Format: date
2016
+ * @description 最后一天(含)
2017
+ */
2018
+ to: string;
2019
+ /** Format: date */
2020
+ previous_from?: string | null;
2021
+ /** Format: date */
2022
+ previous_to?: string | null;
2023
+ /** @description 机构的时区(算「天」用的) */
2024
+ time_zone: string;
2025
+ /** @enum {string} */
2026
+ granularity: "day" | "week" | "month";
2027
+ /** @description 每个点从哪天开始 */
2028
+ points: string[];
2029
+ compare: boolean;
2030
+ /** Format: date-time */
2031
+ data_until: string;
2032
+ /**
2033
+ * Format: date-time
2034
+ * @description 每日汇总最后算到什么时候;还没算过为 null
2035
+ */
2036
+ summary_at: string | null;
2037
+ /** @description 有别的区没答上,跨区的汇总不全 */
2038
+ partial: boolean;
2039
+ /** @description 能筛的维度和选项 */
2040
+ filters: {
2041
+ courses?: components["schemas"]["OpenInsightsOption"][];
2042
+ venues?: components["schemas"]["OpenInsightsOption"][];
2043
+ currencies?: string[];
2044
+ };
2045
+ tiles: {
2046
+ /** @description 数字的名字 */
2047
+ key: string;
2048
+ unit: components["schemas"]["OpenInsightsUnit"];
2049
+ /** @description 没有分母或少于 5 人时为 null */
2050
+ value: number | null;
2051
+ /** @description 少于 5 人,不给 */
2052
+ withheld: boolean;
2053
+ /** @description 上一周期(`compare` 时才有) */
2054
+ previous?: number | null;
2055
+ /** @description 现在的数(不按时间段算) */
2056
+ current: boolean;
2057
+ currency?: string | null;
2058
+ trend?: (number | null)[];
2059
+ }[];
2060
+ cards: {
2061
+ /** @description 图的名字 */
2062
+ key: string;
2063
+ /** @enum {string} */
2064
+ kind: "line" | "stack" | "hbar" | "funnel" | "heat" | "table";
2065
+ unit: components["schemas"]["OpenInsightsUnit"];
2066
+ currency?: string | null;
2067
+ /** @description 整张图都是少于 5 人的 */
2068
+ withheld: boolean;
2069
+ series?: {
2070
+ key: string;
2071
+ label?: string | null;
2072
+ /** @description 和 `points` 一一对应;null 是没有数或压掉了 */
2073
+ values: (number | null)[];
2074
+ }[];
2075
+ previous?: (number | null)[];
2076
+ rows?: {
2077
+ key: string;
2078
+ label?: string | null;
2079
+ /** @description null:少于 5 人 */
2080
+ value: number | null;
2081
+ share?: number | null;
2082
+ }[];
2083
+ heat?: {
2084
+ rows?: string[];
2085
+ cols?: number[];
2086
+ values?: (number | null)[][];
2087
+ };
2088
+ table?: {
2089
+ columns?: string[];
2090
+ units?: components["schemas"]["OpenInsightsUnit"][];
2091
+ rows?: {
2092
+ /** @description 课程、活动等的 ID(不会是人) */
2093
+ id?: string | null;
2094
+ cells: (number | string | null)[];
2095
+ }[];
2096
+ total?: number;
2097
+ };
2098
+ }[];
2099
+ };
2100
+ OpenInsightsOption: {
2101
+ id: string;
2102
+ name: string;
2103
+ };
2104
+ /**
2105
+ * @description `ratio` 是 0–1 的比例;`money` 是最小货币单位(看 `currency`);`rating` 满分 5;`minutes` 分钟;其余是个数
2106
+ * @enum {string}
2107
+ */
2108
+ OpenInsightsUnit: "count" | "people" | "sessions" | "hours" | "ratio" | "minutes" | "rating" | "money" | "lessons";
2109
+ TradeOrder: {
2110
+ order_id: string;
2111
+ status: string;
2112
+ amount_minor: number;
2113
+ currency: string;
2114
+ item_title?: string | null;
2115
+ open_id?: components["schemas"]["OpenId"];
2116
+ /** Format: date-time */
2117
+ created_at: string;
2118
+ /** Format: date-time */
2119
+ paid_at?: string | null;
2120
+ };
2121
+ OpenReview: {
2122
+ review_id: string;
2123
+ /** @description 评价对象的公开内容 ID */
2124
+ target_id: string;
2125
+ stars: number;
2126
+ body?: string | null;
2127
+ author_name?: string | null;
2128
+ replied?: boolean;
2129
+ /** Format: date-time */
2130
+ created_at: string;
2131
+ };
2132
+ PublicContent: {
2133
+ item_id: string;
2134
+ kind: string;
2135
+ title: string;
2136
+ summary?: string | null;
2137
+ /**
2138
+ * Format: uri
2139
+ * @description 平台里的详情页
2140
+ */
2141
+ url?: string;
2142
+ /** Format: date-time */
2143
+ updated_at?: string | null;
2144
+ };
2145
+ InterestProfile: {
1322
2146
  /** @description 用户关了「个性化推荐」时为 false,其余为空 */
1323
2147
  personalized: boolean;
1324
2148
  topics: {
@@ -1344,7 +2168,153 @@ export interface components {
1344
2168
  * @description 可订阅的事件(取自通知目录里以机构为中心的事件)。`x-token` 说明按机构还是按用户订阅,`x-scope` 是要的权限。
1345
2169
  * @enum {string}
1346
2170
  */
1347
- EventType: "app.installed" | "app.uninstalled" | "app.scopes_changed" | "coursework.submitted" | "grade.published" | "schedule.session_changed" | "event.registered" | "job.candidate_applied" | "trade.order_paid" | "review.created" | "trial.booked" | "im.message_received";
2171
+ EventType: "app.installed" | "app.uninstalled" | "app.scopes_changed" | "coursework.submitted" | "grade.published" | "schedule.session_changed" | "event.registered" | "job.candidate_applied" | "trade.order_paid" | "review.created" | "trial.booked" | "im.message_received" | "class.member_changed" | "course.completed" | "exam.results_released" | "event.checked_in" | "live.recording_ready" | "trade.order_refunded" | "trial.confirmed" | "trial.cancelled" | "community.post_reviewed" | "community.video_ready" | "booking.changed";
2172
+ CommunityImageUpload: {
2173
+ /** @description 发帖时写进 `images[].key` */
2174
+ key: string;
2175
+ upload_url: string;
2176
+ /** @enum {string} */
2177
+ method: "PUT";
2178
+ headers: {
2179
+ [key: string]: string;
2180
+ };
2181
+ /** Format: date-time */
2182
+ expires_at: string;
2183
+ };
2184
+ CommunityVideo: {
2185
+ /** Format: uuid */
2186
+ asset_id: string;
2187
+ /**
2188
+ * @description ready 才能发帖;failed 换一个文件重传
2189
+ * @enum {string}
2190
+ */
2191
+ status: "uploading" | "queued" | "processing" | "ready" | "failed";
2192
+ duration_s?: number | null;
2193
+ width?: number | null;
2194
+ height?: number | null;
2195
+ /** @description failed 时的原因代码 */
2196
+ error?: string | null;
2197
+ };
2198
+ CommunityVideoUpload: {
2199
+ /** Format: uuid */
2200
+ asset_id: string;
2201
+ /** @enum {string} */
2202
+ status: "uploading" | "queued" | "processing" | "ready" | "failed";
2203
+ upload: {
2204
+ url: string;
2205
+ /** @enum {string} */
2206
+ method: "PUT";
2207
+ headers: {
2208
+ [key: string]: string;
2209
+ };
2210
+ /** Format: date-time */
2211
+ expires_at: string;
2212
+ };
2213
+ };
2214
+ /** @description 不接受 `consent`、`as_org_id`、`report_ref`、`city_id`,也不接受用户 IP;写了这些字段返回 400。 */
2215
+ CreateCommunityPost: {
2216
+ /** @description 幂等键,一小时内同一个 uuid 只发一次 */
2217
+ uuid?: string;
2218
+ /** @description 可不填(取正文第一句);没有文字时必须填 */
2219
+ title?: string;
2220
+ /** @description 正文里的 `#话题` 也算标签 */
2221
+ body?: string;
2222
+ /** @description 最多 9 张,不能和 `video` 同时给 */
2223
+ images?: {
2224
+ /** @description `POST /community/images` 给的 key */
2225
+ key: string;
2226
+ }[];
2227
+ /** @description 转好的视频,不能和 `images` 同时给 */
2228
+ video?: {
2229
+ /** Format: uuid */
2230
+ asset_id: string;
2231
+ };
2232
+ /** @description 标签,和正文里的 `#话题` 合起来最多 10 个;统一转小写,最长 30 字 */
2233
+ topics?: string[];
2234
+ /** @description `GET /community/categories` 的 key;不选时自动判断 */
2235
+ category?: string;
2236
+ /**
2237
+ * @description 只有文字时的背景,默认 paper
2238
+ * @enum {string}
2239
+ */
2240
+ cover_style?: "paper" | "grid" | "bamboo" | "mountains" | "dawn" | "dusk" | "ink" | "plain";
2241
+ };
2242
+ OpenCommunityPost: {
2243
+ /** @description `<区>:post:<uuid>` */
2244
+ post_id: string;
2245
+ /**
2246
+ * @description visible 已显示;in_review 审核中;rejected 被拒(在这个人的草稿箱);removed 被下架;deleted 这个人或应用删了
2247
+ * @enum {string}
2248
+ */
2249
+ status: "visible" | "in_review" | "rejected" | "removed" | "deleted";
2250
+ /** @description 被拒的原因类别,不给命中的词 */
2251
+ rejection_reason?: string | null;
2252
+ /** @description 帖子链接 */
2253
+ url: string;
2254
+ title: string;
2255
+ /** @description 最终的标签 */
2256
+ topics: string[];
2257
+ /** @description 最终的分类;还没判出来时是 null */
2258
+ category: string | null;
2259
+ /** Format: date-time */
2260
+ created_at: string;
2261
+ };
2262
+ MyScheduleEntry: {
2263
+ /** @enum {string} */
2264
+ kind: "session" | "event" | "trial" | "exam";
2265
+ id: string;
2266
+ title: string;
2267
+ /** Format: date-time */
2268
+ starts_at: string;
2269
+ /** Format: date-time */
2270
+ ends_at: string;
2271
+ cancelled: boolean;
2272
+ /** @description 打开这一项的页面 */
2273
+ url: string;
2274
+ };
2275
+ MyCourse: {
2276
+ /** @description 公开内容 ID(`<区>:course:<id>`) */
2277
+ course_id: string;
2278
+ title: string;
2279
+ completed_lessons: number;
2280
+ total_lessons: number;
2281
+ /** Format: date-time */
2282
+ last_studied_at: string | null;
2283
+ /** Format: date-time */
2284
+ completed_at: string | null;
2285
+ };
2286
+ MyWord: {
2287
+ /** @description 简体 */
2288
+ word: string;
2289
+ traditional?: string | null;
2290
+ pinyin?: string | null;
2291
+ /** @enum {string} */
2292
+ status: "learning" | "known";
2293
+ /** Format: date-time */
2294
+ added_at: string;
2295
+ /** @description 复习状态;还没进复习时为 null */
2296
+ review: null | {
2297
+ /** @enum {string} */
2298
+ state: "new" | "learning" | "review" | "relearning";
2299
+ /** Format: date-time */
2300
+ due_at: string | null;
2301
+ /** Format: date-time */
2302
+ last_reviewed_at: string | null;
2303
+ mastered: boolean;
2304
+ };
2305
+ };
2306
+ ClassExam: {
2307
+ /** Format: uuid */
2308
+ exam_id: string;
2309
+ /** Format: uuid */
2310
+ class_id: string;
2311
+ title: string;
2312
+ /** Format: date-time */
2313
+ closes_at: string | null;
2314
+ max_score: number | null;
2315
+ /** @description 结果公布了没有 */
2316
+ released: boolean;
2317
+ };
1348
2318
  EventEnvelope: {
1349
2319
  /** @enum {string} */
1350
2320
  schema: "1.0";
@@ -1436,7 +2406,8 @@ export interface components {
1436
2406
  /**
1437
2407
  * @description FORBIDDEN,`details.reason` 是 `scope_denied`(用户或机构管理员拒绝了,`details.scope`、`details.never_ask`)、
1438
2408
  * `scope_pending`(新版本加的权限还没问到)、`scope_not_declared`(应用没声明这个权限)、`gold_only`
1439
- * (金牌机构专属)或 `token_type`。应用要能降级。
2409
+ * (金牌机构专属)、`minor_not_allowed`(这个人是未成年人)、`org_not_installed`(用户令牌碰机构的班级或课程,
2410
+ * 那家机构没装这个应用,或没批同一项权限)或 `token_type`。应用要能降级。
1440
2411
  */
1441
2412
  ScopeDenied: {
1442
2413
  headers: {
@@ -1446,6 +2417,36 @@ export interface components {
1446
2417
  "application/json": components["schemas"]["ErrorResponse"];
1447
2418
  };
1448
2419
  };
2420
+ /**
2421
+ * @description FORBIDDEN,除了 `ScopeDenied` 的那些,`details.reason` 还可能是 `minor_not_allowed`(这个人是未成年人,不能授权代发)、
2422
+ * `birth_year_required`(这个人还没填出生年份,要他自己去填)、`app_post_stopped`(语合运营停了这个应用的发帖权限)。
2423
+ */
2424
+ PostForbidden: {
2425
+ headers: {
2426
+ [name: string]: unknown;
2427
+ };
2428
+ content: {
2429
+ "application/json": components["schemas"]["ErrorResponse"];
2430
+ };
2431
+ };
2432
+ /** @description SERVICE_UNAVAILABLE,`details.reason = feature_disabled`(本区暂时关了发帖,`details.feature = community_post`) */
2433
+ FeatureDisabled: {
2434
+ headers: {
2435
+ [name: string]: unknown;
2436
+ };
2437
+ content: {
2438
+ "application/json": components["schemas"]["ErrorResponse"];
2439
+ };
2440
+ };
2441
+ /** @description PAYLOAD_TOO_LARGE */
2442
+ PayloadTooLarge: {
2443
+ headers: {
2444
+ [name: string]: unknown;
2445
+ };
2446
+ content: {
2447
+ "application/json": components["schemas"]["ErrorResponse"];
2448
+ };
2449
+ };
1449
2450
  /** @description NOT_FOUND(没权限时同样返回 404) */
1450
2451
  NotFound: {
1451
2452
  headers: {
@@ -1464,7 +2465,7 @@ export interface components {
1464
2465
  "application/json": components["schemas"]["ErrorResponse"];
1465
2466
  };
1466
2467
  };
1467
- /** @description RATE_LIMITED(`details.reason`:`rate_limited` 或 `daily_quota_exceeded`) */
2468
+ /** @description RATE_LIMITED(`details.reason`:`rate_limited`、`daily_quota_exceeded`、`post_rate_limited` 或 `app_user_daily_limit`) */
1468
2469
  RateLimited: {
1469
2470
  headers: {
1470
2471
  "Retry-After"?: number;
@@ -1487,7 +2488,11 @@ export interface components {
1487
2488
  CourseId: string;
1488
2489
  EventId: string;
1489
2490
  JobIdPath: string;
2491
+ OpeningId: string;
2492
+ VenueId: string;
1490
2493
  BookingId: string;
2494
+ AssetId: string;
2495
+ PostId: string;
1491
2496
  OpenIdPath: components["schemas"]["OpenId"];
1492
2497
  };
1493
2498
  requestBodies: never;
@@ -1928,34 +2933,70 @@ export interface operations {
1928
2933
  404: components["responses"]["NotFound"];
1929
2934
  };
1930
2935
  };
1931
- cancelClassSession: {
2936
+ createClassSessions: {
1932
2937
  parameters: {
1933
2938
  query?: never;
1934
2939
  header?: never;
1935
2940
  path: {
1936
2941
  class_id: components["parameters"]["ClassId"];
1937
- session_id: components["parameters"]["SessionId"];
1938
2942
  };
1939
2943
  cookie?: never;
1940
2944
  };
1941
- requestBody?: never;
2945
+ requestBody: {
2946
+ content: {
2947
+ "application/json": {
2948
+ title: string;
2949
+ /**
2950
+ * Format: date-time
2951
+ * @description 第一次课的开始时间
2952
+ */
2953
+ starts_at: string;
2954
+ /**
2955
+ * Format: date-time
2956
+ * @description 第一次课的结束时间(最长 12 小时)
2957
+ */
2958
+ ends_at: string;
2959
+ /** @description IANA 时区 */
2960
+ time_zone: string;
2961
+ /** @description 上这些课的老师(必须是本班的老师);不填时用户令牌是本人,机构令牌是班里唯一的那位老师(有几位时必填) */
2962
+ teacher_open_id?: components["schemas"]["OpenId"];
2963
+ /** @description 按周重复;`until`(含当天)和 `count` 二选一。第一次课那天的星期几必须在 `weekdays` 里。 */
2964
+ recurrence?: {
2965
+ /** @description 星期几(1 = 周一 … 7 = 周日),按 `time_zone` 的当地日期 */
2966
+ weekdays: number[];
2967
+ /**
2968
+ * Format: date
2969
+ * @description 最后一天(当地日期,含当天)
2970
+ */
2971
+ until?: string;
2972
+ /** @description 一共几次 */
2973
+ count?: number;
2974
+ };
2975
+ /** @description 地点:不传跟着班级,`null` 在线 */
2976
+ place?: components["schemas"]["OpenPlaceInput"] | null;
2977
+ };
2978
+ };
2979
+ };
1942
2980
  responses: {
1943
- /** @description OK */
1944
- 200: {
2981
+ /** @description Created */
2982
+ 201: {
1945
2983
  headers: {
1946
2984
  [name: string]: unknown;
1947
2985
  };
1948
2986
  content: {
1949
- "application/json": components["schemas"]["OpenSession"];
2987
+ "application/json": {
2988
+ items: components["schemas"]["OpenSession"][];
2989
+ };
1950
2990
  };
1951
2991
  };
2992
+ 400: components["responses"]["ValidationFailed"];
1952
2993
  401: components["responses"]["Unauthenticated"];
1953
2994
  403: components["responses"]["ScopeDenied"];
1954
2995
  404: components["responses"]["NotFound"];
1955
2996
  409: components["responses"]["Conflict"];
1956
2997
  };
1957
2998
  };
1958
- rescheduleClassSession: {
2999
+ setClassSessionPlace: {
1959
3000
  parameters: {
1960
3001
  query?: never;
1961
3002
  header?: never;
@@ -1968,9 +3009,14 @@ export interface operations {
1968
3009
  requestBody: {
1969
3010
  content: {
1970
3011
  "application/json": {
1971
- /** Format: date-time */
1972
- starts_at: string;
1973
- duration_minutes?: number;
3012
+ /** @description 新地点;`null` 在线 */
3013
+ place: components["schemas"]["OpenPlaceInput"] | null;
3014
+ /**
3015
+ * @description this 只改这一次;following 这一次及同一系列以后的课次
3016
+ * @default this
3017
+ * @enum {string}
3018
+ */
3019
+ scope?: "this" | "following";
1974
3020
  };
1975
3021
  };
1976
3022
  };
@@ -1981,7 +3027,10 @@ export interface operations {
1981
3027
  [name: string]: unknown;
1982
3028
  };
1983
3029
  content: {
1984
- "application/json": components["schemas"]["OpenSession"];
3030
+ "application/json": {
3031
+ /** @description 改到了的课次 */
3032
+ items: components["schemas"]["OpenSession"][];
3033
+ };
1985
3034
  };
1986
3035
  };
1987
3036
  400: components["responses"]["ValidationFailed"];
@@ -1991,7 +3040,7 @@ export interface operations {
1991
3040
  409: components["responses"]["Conflict"];
1992
3041
  };
1993
3042
  };
1994
- getSessionAttendance: {
3043
+ cancelClassSession: {
1995
3044
  parameters: {
1996
3045
  query?: never;
1997
3046
  header?: never;
@@ -2009,22 +3058,85 @@ export interface operations {
2009
3058
  [name: string]: unknown;
2010
3059
  };
2011
3060
  content: {
2012
- "application/json": {
2013
- items: components["schemas"]["AttendanceRecord"][];
2014
- };
3061
+ "application/json": components["schemas"]["OpenSession"];
2015
3062
  };
2016
3063
  };
2017
3064
  401: components["responses"]["Unauthenticated"];
2018
3065
  403: components["responses"]["ScopeDenied"];
2019
3066
  404: components["responses"]["NotFound"];
3067
+ 409: components["responses"]["Conflict"];
2020
3068
  };
2021
3069
  };
2022
- listAssignments: {
3070
+ rescheduleClassSession: {
2023
3071
  parameters: {
2024
- query?: {
2025
- page_size?: components["parameters"]["PageSize"];
2026
- page_token?: components["parameters"]["PageToken"];
2027
- };
3072
+ query?: never;
3073
+ header?: never;
3074
+ path: {
3075
+ class_id: components["parameters"]["ClassId"];
3076
+ session_id: components["parameters"]["SessionId"];
3077
+ };
3078
+ cookie?: never;
3079
+ };
3080
+ requestBody: {
3081
+ content: {
3082
+ "application/json": {
3083
+ /** Format: date-time */
3084
+ starts_at: string;
3085
+ duration_minutes?: number;
3086
+ };
3087
+ };
3088
+ };
3089
+ responses: {
3090
+ /** @description OK */
3091
+ 200: {
3092
+ headers: {
3093
+ [name: string]: unknown;
3094
+ };
3095
+ content: {
3096
+ "application/json": components["schemas"]["OpenSession"];
3097
+ };
3098
+ };
3099
+ 400: components["responses"]["ValidationFailed"];
3100
+ 401: components["responses"]["Unauthenticated"];
3101
+ 403: components["responses"]["ScopeDenied"];
3102
+ 404: components["responses"]["NotFound"];
3103
+ 409: components["responses"]["Conflict"];
3104
+ };
3105
+ };
3106
+ getSessionAttendance: {
3107
+ parameters: {
3108
+ query?: never;
3109
+ header?: never;
3110
+ path: {
3111
+ class_id: components["parameters"]["ClassId"];
3112
+ session_id: components["parameters"]["SessionId"];
3113
+ };
3114
+ cookie?: never;
3115
+ };
3116
+ requestBody?: never;
3117
+ responses: {
3118
+ /** @description OK */
3119
+ 200: {
3120
+ headers: {
3121
+ [name: string]: unknown;
3122
+ };
3123
+ content: {
3124
+ "application/json": {
3125
+ items: components["schemas"]["AttendanceRecord"][];
3126
+ };
3127
+ };
3128
+ };
3129
+ 401: components["responses"]["Unauthenticated"];
3130
+ 403: components["responses"]["ScopeDenied"];
3131
+ 404: components["responses"]["NotFound"];
3132
+ };
3133
+ };
3134
+ listAssignments: {
3135
+ parameters: {
3136
+ query?: {
3137
+ page_size?: components["parameters"]["PageSize"];
3138
+ page_token?: components["parameters"]["PageToken"];
3139
+ };
2028
3140
  header?: never;
2029
3141
  path: {
2030
3142
  class_id: components["parameters"]["ClassId"];
@@ -2662,6 +3774,8 @@ export interface operations {
2662
3774
  parameters: {
2663
3775
  query?: {
2664
3776
  venue_id?: string;
3777
+ /** @description 只列这一个试听的时间 */
3778
+ opening_id?: string;
2665
3779
  include_past?: boolean;
2666
3780
  page_size?: components["parameters"]["PageSize"];
2667
3781
  page_token?: components["parameters"]["PageToken"];
@@ -2697,14 +3811,29 @@ export interface operations {
2697
3811
  requestBody: {
2698
3812
  content: {
2699
3813
  "application/json": {
2700
- /** Format: uuid */
2701
- venue_id: string;
3814
+ /**
3815
+ * Format: uuid
3816
+ * @description 加到这份试听上(不能和 `venue_id`、`course_title`、`teacher_open_id` 一起给)
3817
+ */
3818
+ opening_id?: string;
3819
+ /**
3820
+ * Format: uuid
3821
+ * @description 没有 `opening_id` 时必填
3822
+ */
3823
+ venue_id?: string;
3824
+ /** @description 没有 `opening_id` 时必填 */
3825
+ course_title?: string;
3826
+ /** @description 上试听的老师(本机构的老师,在机构所在区);不填时新开的草稿缺老师 */
3827
+ teacher_open_id?: components["schemas"]["OpenId"];
2702
3828
  /** Format: date-time */
2703
3829
  starts_at: string;
2704
3830
  duration_minutes: number;
3831
+ /** @description 每个时间几个人一起上 */
2705
3832
  capacity: number;
2706
- course_title: string;
2707
- /** @default 0 */
3833
+ /**
3834
+ * @description 再按周重复几次(按教学点所在时区的当地时间)
3835
+ * @default 0
3836
+ */
2708
3837
  repeat_weeks?: number;
2709
3838
  };
2710
3839
  };
@@ -2756,6 +3885,40 @@ export interface operations {
2756
3885
  401: components["responses"]["Unauthenticated"];
2757
3886
  403: components["responses"]["ScopeDenied"];
2758
3887
  404: components["responses"]["NotFound"];
3888
+ 409: components["responses"]["Conflict"];
3889
+ };
3890
+ };
3891
+ setTrialTeacher: {
3892
+ parameters: {
3893
+ query?: never;
3894
+ header?: never;
3895
+ path: {
3896
+ opening_id: components["parameters"]["OpeningId"];
3897
+ };
3898
+ cookie?: never;
3899
+ };
3900
+ requestBody: {
3901
+ content: {
3902
+ "application/json": {
3903
+ teacher_open_id: components["schemas"]["OpenId"];
3904
+ };
3905
+ };
3906
+ };
3907
+ responses: {
3908
+ /** @description OK */
3909
+ 200: {
3910
+ headers: {
3911
+ [name: string]: unknown;
3912
+ };
3913
+ content: {
3914
+ "application/json": components["schemas"]["OpenOpening"];
3915
+ };
3916
+ };
3917
+ 400: components["responses"]["ValidationFailed"];
3918
+ 401: components["responses"]["Unauthenticated"];
3919
+ 403: components["responses"]["ScopeDenied"];
3920
+ 404: components["responses"]["NotFound"];
3921
+ 409: components["responses"]["Conflict"];
2759
3922
  };
2760
3923
  };
2761
3924
  listTrialBookings: {
@@ -2812,10 +3975,10 @@ export interface operations {
2812
3975
  409: components["responses"]["Conflict"];
2813
3976
  };
2814
3977
  };
2815
- listTradeOrders: {
3978
+ listVenues: {
2816
3979
  parameters: {
2817
3980
  query?: {
2818
- status?: string;
3981
+ status?: "draft" | "pending" | "returned" | "published" | "closed" | "taken_down";
2819
3982
  page_size?: components["parameters"]["PageSize"];
2820
3983
  page_token?: components["parameters"]["PageToken"];
2821
3984
  };
@@ -2832,7 +3995,7 @@ export interface operations {
2832
3995
  };
2833
3996
  content: {
2834
3997
  "application/json": components["schemas"]["PageMeta"] & {
2835
- items: components["schemas"]["TradeOrder"][];
3998
+ items: components["schemas"]["OpenVenue"][];
2836
3999
  };
2837
4000
  };
2838
4001
  };
@@ -2840,9 +4003,65 @@ export interface operations {
2840
4003
  403: components["responses"]["ScopeDenied"];
2841
4004
  };
2842
4005
  };
2843
- listTenantReviews: {
4006
+ createVenue: {
4007
+ parameters: {
4008
+ query?: never;
4009
+ header?: never;
4010
+ path?: never;
4011
+ cookie?: never;
4012
+ };
4013
+ requestBody: {
4014
+ content: {
4015
+ "application/json": components["schemas"]["OpenVenueCreate"];
4016
+ };
4017
+ };
4018
+ responses: {
4019
+ /** @description Created */
4020
+ 201: {
4021
+ headers: {
4022
+ [name: string]: unknown;
4023
+ };
4024
+ content: {
4025
+ "application/json": components["schemas"]["OpenVenue"];
4026
+ };
4027
+ };
4028
+ 400: components["responses"]["ValidationFailed"];
4029
+ 401: components["responses"]["Unauthenticated"];
4030
+ 403: components["responses"]["ScopeDenied"];
4031
+ };
4032
+ };
4033
+ getVenue: {
4034
+ parameters: {
4035
+ query?: never;
4036
+ header?: never;
4037
+ path: {
4038
+ venue_id: components["parameters"]["VenueId"];
4039
+ };
4040
+ cookie?: never;
4041
+ };
4042
+ requestBody?: never;
4043
+ responses: {
4044
+ /** @description OK */
4045
+ 200: {
4046
+ headers: {
4047
+ [name: string]: unknown;
4048
+ };
4049
+ content: {
4050
+ "application/json": components["schemas"]["OpenVenue"];
4051
+ };
4052
+ };
4053
+ 401: components["responses"]["Unauthenticated"];
4054
+ 403: components["responses"]["ScopeDenied"];
4055
+ 404: components["responses"]["NotFound"];
4056
+ };
4057
+ };
4058
+ listOpenings: {
2844
4059
  parameters: {
2845
4060
  query?: {
4061
+ status?: "draft" | "review" | "enrolling" | "running" | "ended";
4062
+ kind?: "group" | "one_on_one" | "lecture" | "trial" | "video";
4063
+ /** @description 只列教这门课程的 */
4064
+ course_id?: string;
2846
4065
  page_size?: components["parameters"]["PageSize"];
2847
4066
  page_token?: components["parameters"]["PageToken"];
2848
4067
  };
@@ -2859,7 +4078,7 @@ export interface operations {
2859
4078
  };
2860
4079
  content: {
2861
4080
  "application/json": components["schemas"]["PageMeta"] & {
2862
- items: components["schemas"]["OpenReview"][];
4081
+ items: components["schemas"]["OpenOpening"][];
2863
4082
  };
2864
4083
  };
2865
4084
  };
@@ -2867,48 +4086,79 @@ export interface operations {
2867
4086
  403: components["responses"]["ScopeDenied"];
2868
4087
  };
2869
4088
  };
2870
- replyReview: {
4089
+ getOpening: {
2871
4090
  parameters: {
2872
4091
  query?: never;
2873
4092
  header?: never;
2874
4093
  path: {
2875
- review_id: string;
4094
+ opening_id: components["parameters"]["OpeningId"];
2876
4095
  };
2877
4096
  cookie?: never;
2878
4097
  };
2879
- requestBody: {
2880
- content: {
2881
- "application/json": {
2882
- body: string;
4098
+ requestBody?: never;
4099
+ responses: {
4100
+ /** @description OK */
4101
+ 200: {
4102
+ headers: {
4103
+ [name: string]: unknown;
4104
+ };
4105
+ content: {
4106
+ "application/json": components["schemas"]["OpenOpening"];
2883
4107
  };
2884
4108
  };
4109
+ 401: components["responses"]["Unauthenticated"];
4110
+ 403: components["responses"]["ScopeDenied"];
4111
+ 404: components["responses"]["NotFound"];
4112
+ };
4113
+ };
4114
+ getInsights: {
4115
+ parameters: {
4116
+ query?: {
4117
+ tab?: "enrollment" | "teaching" | "venues" | "income" | "community";
4118
+ /** @description 截止今天(含今天)往前几天;带了 `from`、`to` 时不看它 */
4119
+ range?: "7d" | "30d" | "90d" | "365d";
4120
+ /** @description 自定义时间的第一天(和 `to` 一起带) */
4121
+ from?: string;
4122
+ /** @description 自定义时间的最后一天(含) */
4123
+ to?: string;
4124
+ /** @description 和上一周期比 */
4125
+ compare?: boolean;
4126
+ /** @description 只看这个教学点(校区) */
4127
+ venue_id?: string;
4128
+ /** @description 只看这门课程 */
4129
+ course_id?: string;
4130
+ /** @description 收入页看哪种币种 */
4131
+ currency?: string;
4132
+ };
4133
+ header?: never;
4134
+ path?: never;
4135
+ cookie?: never;
2885
4136
  };
4137
+ requestBody?: never;
2886
4138
  responses: {
2887
- /** @description 已提交 */
2888
- 201: {
4139
+ /** @description OK */
4140
+ 200: {
2889
4141
  headers: {
2890
4142
  [name: string]: unknown;
2891
4143
  };
2892
- content?: never;
4144
+ content: {
4145
+ "application/json": components["schemas"]["OpenInsightsPage"];
4146
+ };
2893
4147
  };
2894
4148
  400: components["responses"]["ValidationFailed"];
2895
4149
  401: components["responses"]["Unauthenticated"];
2896
4150
  403: components["responses"]["ScopeDenied"];
2897
- 404: components["responses"]["NotFound"];
2898
- 409: components["responses"]["Conflict"];
2899
4151
  };
2900
4152
  };
2901
- listPublicContent: {
4153
+ listTradeOrders: {
2902
4154
  parameters: {
2903
4155
  query?: {
2904
- q?: string;
4156
+ status?: string;
2905
4157
  page_size?: components["parameters"]["PageSize"];
2906
4158
  page_token?: components["parameters"]["PageToken"];
2907
4159
  };
2908
4160
  header?: never;
2909
- path: {
2910
- kind: "courses" | "events" | "jobs";
2911
- };
4161
+ path?: never;
2912
4162
  cookie?: never;
2913
4163
  };
2914
4164
  requestBody?: never;
@@ -2916,23 +4166,32 @@ export interface operations {
2916
4166
  /** @description OK */
2917
4167
  200: {
2918
4168
  headers: {
2919
- /** @description 今天这一种还能取多少条 */
2920
- "X-Daily-Quota-Remaining"?: number;
2921
4169
  [name: string]: unknown;
2922
4170
  };
2923
4171
  content: {
2924
4172
  "application/json": components["schemas"]["PageMeta"] & {
2925
- items: components["schemas"]["PublicContent"][];
4173
+ items: components["schemas"]["TradeOrder"][];
2926
4174
  };
2927
4175
  };
2928
4176
  };
2929
4177
  401: components["responses"]["Unauthenticated"];
2930
- 429: components["responses"]["RateLimited"];
4178
+ 403: components["responses"]["ScopeDenied"];
2931
4179
  };
2932
4180
  };
2933
- getUserInterests: {
4181
+ listBookings: {
2934
4182
  parameters: {
2935
- query?: never;
4183
+ query?: {
4184
+ status?: "requested" | "confirmed" | "declined" | "cancelled" | "completed" | "expired" | "missed";
4185
+ kind?: "one_on_one" | "trial";
4186
+ /** @description 只列这一个开课的 */
4187
+ opening_id?: string;
4188
+ /** @description 上课时间不早于 */
4189
+ from?: string;
4190
+ /** @description 上课时间早于 */
4191
+ to?: string;
4192
+ page_size?: components["parameters"]["PageSize"];
4193
+ page_token?: components["parameters"]["PageToken"];
4194
+ };
2936
4195
  header?: never;
2937
4196
  path?: never;
2938
4197
  cookie?: never;
@@ -2945,53 +4204,47 @@ export interface operations {
2945
4204
  [name: string]: unknown;
2946
4205
  };
2947
4206
  content: {
2948
- "application/json": components["schemas"]["InterestProfile"];
4207
+ "application/json": components["schemas"]["PageMeta"] & {
4208
+ items: components["schemas"]["OpenBooking"][];
4209
+ };
2949
4210
  };
2950
4211
  };
4212
+ 400: components["responses"]["ValidationFailed"];
2951
4213
  401: components["responses"]["Unauthenticated"];
2952
4214
  403: components["responses"]["ScopeDenied"];
2953
4215
  };
2954
4216
  };
2955
- addInterestSignal: {
4217
+ getBooking: {
2956
4218
  parameters: {
2957
4219
  query?: never;
2958
4220
  header?: never;
2959
- path?: never;
2960
- cookie?: never;
2961
- };
2962
- requestBody: {
2963
- content: {
2964
- "application/json": {
2965
- /** @description 话题(和社区话题同一套写法,例如「商务中文」) */
2966
- topic: string;
2967
- /** @default 0.5 */
2968
- weight?: number;
2969
- /** @description 给用户看的来源说明,例如「在 xx 里常学商务中文」 */
2970
- reason: string;
2971
- };
4221
+ path: {
4222
+ booking_id: components["parameters"]["BookingId"];
2972
4223
  };
4224
+ cookie?: never;
2973
4225
  };
4226
+ requestBody?: never;
2974
4227
  responses: {
2975
4228
  /** @description OK */
2976
- 201: {
4229
+ 200: {
2977
4230
  headers: {
2978
4231
  [name: string]: unknown;
2979
4232
  };
2980
4233
  content: {
2981
- "application/json": {
2982
- accepted: boolean;
2983
- signal_id?: string;
2984
- };
4234
+ "application/json": components["schemas"]["OpenBooking"];
2985
4235
  };
2986
4236
  };
2987
- 400: components["responses"]["ValidationFailed"];
2988
4237
  401: components["responses"]["Unauthenticated"];
2989
4238
  403: components["responses"]["ScopeDenied"];
4239
+ 404: components["responses"]["NotFound"];
2990
4240
  };
2991
4241
  };
2992
- getUserLearningProfile: {
4242
+ listTenantReviews: {
2993
4243
  parameters: {
2994
- query?: never;
4244
+ query?: {
4245
+ page_size?: components["parameters"]["PageSize"];
4246
+ page_token?: components["parameters"]["PageToken"];
4247
+ };
2995
4248
  header?: never;
2996
4249
  path?: never;
2997
4250
  cookie?: never;
@@ -3004,20 +4257,166 @@ export interface operations {
3004
4257
  [name: string]: unknown;
3005
4258
  };
3006
4259
  content: {
3007
- "application/json": components["schemas"]["LearningProfile"];
4260
+ "application/json": components["schemas"]["PageMeta"] & {
4261
+ items: components["schemas"]["OpenReview"][];
4262
+ };
3008
4263
  };
3009
4264
  };
3010
4265
  401: components["responses"]["Unauthenticated"];
3011
4266
  403: components["responses"]["ScopeDenied"];
3012
4267
  };
3013
4268
  };
3014
- reportOnBehalf: {
4269
+ replyReview: {
3015
4270
  parameters: {
3016
4271
  query?: never;
3017
4272
  header?: never;
3018
- path?: never;
3019
- cookie?: never;
3020
- };
4273
+ path: {
4274
+ review_id: string;
4275
+ };
4276
+ cookie?: never;
4277
+ };
4278
+ requestBody: {
4279
+ content: {
4280
+ "application/json": {
4281
+ body: string;
4282
+ };
4283
+ };
4284
+ };
4285
+ responses: {
4286
+ /** @description 已提交 */
4287
+ 201: {
4288
+ headers: {
4289
+ [name: string]: unknown;
4290
+ };
4291
+ content?: never;
4292
+ };
4293
+ 400: components["responses"]["ValidationFailed"];
4294
+ 401: components["responses"]["Unauthenticated"];
4295
+ 403: components["responses"]["ScopeDenied"];
4296
+ 404: components["responses"]["NotFound"];
4297
+ 409: components["responses"]["Conflict"];
4298
+ };
4299
+ };
4300
+ listPublicContent: {
4301
+ parameters: {
4302
+ query?: {
4303
+ q?: string;
4304
+ page_size?: components["parameters"]["PageSize"];
4305
+ page_token?: components["parameters"]["PageToken"];
4306
+ };
4307
+ header?: never;
4308
+ path: {
4309
+ kind: "courses" | "events" | "jobs";
4310
+ };
4311
+ cookie?: never;
4312
+ };
4313
+ requestBody?: never;
4314
+ responses: {
4315
+ /** @description OK */
4316
+ 200: {
4317
+ headers: {
4318
+ /** @description 今天这一种还能取多少条 */
4319
+ "X-Daily-Quota-Remaining"?: number;
4320
+ [name: string]: unknown;
4321
+ };
4322
+ content: {
4323
+ "application/json": components["schemas"]["PageMeta"] & {
4324
+ items: components["schemas"]["PublicContent"][];
4325
+ };
4326
+ };
4327
+ };
4328
+ 401: components["responses"]["Unauthenticated"];
4329
+ 429: components["responses"]["RateLimited"];
4330
+ };
4331
+ };
4332
+ getUserInterests: {
4333
+ parameters: {
4334
+ query?: never;
4335
+ header?: never;
4336
+ path?: never;
4337
+ cookie?: never;
4338
+ };
4339
+ requestBody?: never;
4340
+ responses: {
4341
+ /** @description OK */
4342
+ 200: {
4343
+ headers: {
4344
+ [name: string]: unknown;
4345
+ };
4346
+ content: {
4347
+ "application/json": components["schemas"]["InterestProfile"];
4348
+ };
4349
+ };
4350
+ 401: components["responses"]["Unauthenticated"];
4351
+ 403: components["responses"]["ScopeDenied"];
4352
+ };
4353
+ };
4354
+ addInterestSignal: {
4355
+ parameters: {
4356
+ query?: never;
4357
+ header?: never;
4358
+ path?: never;
4359
+ cookie?: never;
4360
+ };
4361
+ requestBody: {
4362
+ content: {
4363
+ "application/json": {
4364
+ /** @description 话题(和社区话题同一套写法,例如「商务中文」) */
4365
+ topic: string;
4366
+ /** @default 0.5 */
4367
+ weight?: number;
4368
+ /** @description 给用户看的来源说明,例如「在 xx 里常学商务中文」 */
4369
+ reason: string;
4370
+ };
4371
+ };
4372
+ };
4373
+ responses: {
4374
+ /** @description OK */
4375
+ 201: {
4376
+ headers: {
4377
+ [name: string]: unknown;
4378
+ };
4379
+ content: {
4380
+ "application/json": {
4381
+ accepted: boolean;
4382
+ signal_id?: string;
4383
+ };
4384
+ };
4385
+ };
4386
+ 400: components["responses"]["ValidationFailed"];
4387
+ 401: components["responses"]["Unauthenticated"];
4388
+ 403: components["responses"]["ScopeDenied"];
4389
+ };
4390
+ };
4391
+ getUserLearningProfile: {
4392
+ parameters: {
4393
+ query?: never;
4394
+ header?: never;
4395
+ path?: never;
4396
+ cookie?: never;
4397
+ };
4398
+ requestBody?: never;
4399
+ responses: {
4400
+ /** @description OK */
4401
+ 200: {
4402
+ headers: {
4403
+ [name: string]: unknown;
4404
+ };
4405
+ content: {
4406
+ "application/json": components["schemas"]["LearningProfile"];
4407
+ };
4408
+ };
4409
+ 401: components["responses"]["Unauthenticated"];
4410
+ 403: components["responses"]["ScopeDenied"];
4411
+ };
4412
+ };
4413
+ reportOnBehalf: {
4414
+ parameters: {
4415
+ query?: never;
4416
+ header?: never;
4417
+ path?: never;
4418
+ cookie?: never;
4419
+ };
3021
4420
  requestBody: {
3022
4421
  content: {
3023
4422
  "application/json": {
@@ -3046,6 +4445,446 @@ export interface operations {
3046
4445
  404: components["responses"]["NotFound"];
3047
4446
  };
3048
4447
  };
4448
+ createCommunityImageUpload: {
4449
+ parameters: {
4450
+ query?: never;
4451
+ header?: never;
4452
+ path?: never;
4453
+ cookie?: never;
4454
+ };
4455
+ requestBody: {
4456
+ content: {
4457
+ "application/json": {
4458
+ /** @enum {string} */
4459
+ content_type: "image/jpeg" | "image/png" | "image/webp" | "image/gif";
4460
+ size_bytes: number;
4461
+ };
4462
+ };
4463
+ };
4464
+ responses: {
4465
+ /** @description Created */
4466
+ 201: {
4467
+ headers: {
4468
+ [name: string]: unknown;
4469
+ };
4470
+ content: {
4471
+ "application/json": components["schemas"]["CommunityImageUpload"];
4472
+ };
4473
+ };
4474
+ 400: components["responses"]["ValidationFailed"];
4475
+ 401: components["responses"]["Unauthenticated"];
4476
+ 403: components["responses"]["PostForbidden"];
4477
+ 429: components["responses"]["RateLimited"];
4478
+ 503: components["responses"]["FeatureDisabled"];
4479
+ };
4480
+ };
4481
+ createCommunityVideoUpload: {
4482
+ parameters: {
4483
+ query?: never;
4484
+ header?: never;
4485
+ path?: never;
4486
+ cookie?: never;
4487
+ };
4488
+ requestBody: {
4489
+ content: {
4490
+ "application/json": {
4491
+ filename: string;
4492
+ /** @description 比如 video/mp4 */
4493
+ content_type: string;
4494
+ size_bytes: number;
4495
+ };
4496
+ };
4497
+ };
4498
+ responses: {
4499
+ /** @description Created */
4500
+ 201: {
4501
+ headers: {
4502
+ [name: string]: unknown;
4503
+ };
4504
+ content: {
4505
+ "application/json": components["schemas"]["CommunityVideoUpload"];
4506
+ };
4507
+ };
4508
+ 400: components["responses"]["ValidationFailed"];
4509
+ 401: components["responses"]["Unauthenticated"];
4510
+ 403: components["responses"]["PostForbidden"];
4511
+ 413: components["responses"]["PayloadTooLarge"];
4512
+ 429: components["responses"]["RateLimited"];
4513
+ };
4514
+ };
4515
+ completeCommunityVideoUpload: {
4516
+ parameters: {
4517
+ query?: never;
4518
+ header?: never;
4519
+ path: {
4520
+ asset_id: components["parameters"]["AssetId"];
4521
+ };
4522
+ cookie?: never;
4523
+ };
4524
+ requestBody?: never;
4525
+ responses: {
4526
+ /** @description OK */
4527
+ 200: {
4528
+ headers: {
4529
+ [name: string]: unknown;
4530
+ };
4531
+ content: {
4532
+ "application/json": components["schemas"]["CommunityVideo"];
4533
+ };
4534
+ };
4535
+ 401: components["responses"]["Unauthenticated"];
4536
+ 403: components["responses"]["PostForbidden"];
4537
+ 404: components["responses"]["NotFound"];
4538
+ 409: components["responses"]["Conflict"];
4539
+ };
4540
+ };
4541
+ getCommunityVideo: {
4542
+ parameters: {
4543
+ query?: never;
4544
+ header?: never;
4545
+ path: {
4546
+ asset_id: components["parameters"]["AssetId"];
4547
+ };
4548
+ cookie?: never;
4549
+ };
4550
+ requestBody?: never;
4551
+ responses: {
4552
+ /** @description OK */
4553
+ 200: {
4554
+ headers: {
4555
+ [name: string]: unknown;
4556
+ };
4557
+ content: {
4558
+ "application/json": components["schemas"]["CommunityVideo"];
4559
+ };
4560
+ };
4561
+ 401: components["responses"]["Unauthenticated"];
4562
+ 403: components["responses"]["PostForbidden"];
4563
+ 404: components["responses"]["NotFound"];
4564
+ };
4565
+ };
4566
+ listCommunityPosts: {
4567
+ parameters: {
4568
+ query?: {
4569
+ page_size?: components["parameters"]["PageSize"];
4570
+ page_token?: components["parameters"]["PageToken"];
4571
+ };
4572
+ header?: never;
4573
+ path?: never;
4574
+ cookie?: never;
4575
+ };
4576
+ requestBody?: never;
4577
+ responses: {
4578
+ /** @description OK */
4579
+ 200: {
4580
+ headers: {
4581
+ [name: string]: unknown;
4582
+ };
4583
+ content: {
4584
+ "application/json": components["schemas"]["PageMeta"] & {
4585
+ items: components["schemas"]["OpenCommunityPost"][];
4586
+ };
4587
+ };
4588
+ };
4589
+ 401: components["responses"]["Unauthenticated"];
4590
+ 403: components["responses"]["PostForbidden"];
4591
+ };
4592
+ };
4593
+ createCommunityPost: {
4594
+ parameters: {
4595
+ query?: never;
4596
+ header?: never;
4597
+ path?: never;
4598
+ cookie?: never;
4599
+ };
4600
+ requestBody: {
4601
+ content: {
4602
+ "application/json": components["schemas"]["CreateCommunityPost"];
4603
+ };
4604
+ };
4605
+ responses: {
4606
+ /** @description 已发出(看 `status`);`uuid` 重复时返回的是原来那条 */
4607
+ 201: {
4608
+ headers: {
4609
+ [name: string]: unknown;
4610
+ };
4611
+ content: {
4612
+ "application/json": components["schemas"]["OpenCommunityPost"];
4613
+ };
4614
+ };
4615
+ 400: components["responses"]["ValidationFailed"];
4616
+ 401: components["responses"]["Unauthenticated"];
4617
+ 403: components["responses"]["PostForbidden"];
4618
+ 404: components["responses"]["NotFound"];
4619
+ 409: components["responses"]["Conflict"];
4620
+ 429: components["responses"]["RateLimited"];
4621
+ 503: components["responses"]["FeatureDisabled"];
4622
+ };
4623
+ };
4624
+ getCommunityPost: {
4625
+ parameters: {
4626
+ query?: never;
4627
+ header?: never;
4628
+ path: {
4629
+ post_id: components["parameters"]["PostId"];
4630
+ };
4631
+ cookie?: never;
4632
+ };
4633
+ requestBody?: never;
4634
+ responses: {
4635
+ /** @description OK */
4636
+ 200: {
4637
+ headers: {
4638
+ [name: string]: unknown;
4639
+ };
4640
+ content: {
4641
+ "application/json": components["schemas"]["OpenCommunityPost"];
4642
+ };
4643
+ };
4644
+ 401: components["responses"]["Unauthenticated"];
4645
+ 403: components["responses"]["PostForbidden"];
4646
+ 404: components["responses"]["NotFound"];
4647
+ };
4648
+ };
4649
+ deleteCommunityPost: {
4650
+ parameters: {
4651
+ query?: never;
4652
+ header?: never;
4653
+ path: {
4654
+ post_id: components["parameters"]["PostId"];
4655
+ };
4656
+ cookie?: never;
4657
+ };
4658
+ requestBody?: never;
4659
+ responses: {
4660
+ /** @description 已删除 */
4661
+ 204: {
4662
+ headers: {
4663
+ [name: string]: unknown;
4664
+ };
4665
+ content?: never;
4666
+ };
4667
+ 401: components["responses"]["Unauthenticated"];
4668
+ 403: components["responses"]["PostForbidden"];
4669
+ 404: components["responses"]["NotFound"];
4670
+ };
4671
+ };
4672
+ listCommunityCategories: {
4673
+ parameters: {
4674
+ query?: never;
4675
+ header?: never;
4676
+ path?: never;
4677
+ cookie?: never;
4678
+ };
4679
+ requestBody?: never;
4680
+ responses: {
4681
+ /** @description OK */
4682
+ 200: {
4683
+ headers: {
4684
+ [name: string]: unknown;
4685
+ };
4686
+ content: {
4687
+ "application/json": {
4688
+ items: {
4689
+ /** @description 发帖时写进 `category` */
4690
+ key: string;
4691
+ /** @description 按 Accept-Language 本地化的名称 */
4692
+ name: string;
4693
+ }[];
4694
+ };
4695
+ };
4696
+ };
4697
+ 401: components["responses"]["Unauthenticated"];
4698
+ 403: components["responses"]["PostForbidden"];
4699
+ };
4700
+ };
4701
+ getMySchedule: {
4702
+ parameters: {
4703
+ query: {
4704
+ from: string;
4705
+ to: string;
4706
+ page_size?: components["parameters"]["PageSize"];
4707
+ page_token?: components["parameters"]["PageToken"];
4708
+ };
4709
+ header?: never;
4710
+ path?: never;
4711
+ cookie?: never;
4712
+ };
4713
+ requestBody?: never;
4714
+ responses: {
4715
+ /** @description OK */
4716
+ 200: {
4717
+ headers: {
4718
+ [name: string]: unknown;
4719
+ };
4720
+ content: {
4721
+ "application/json": components["schemas"]["PageMeta"] & {
4722
+ items: components["schemas"]["MyScheduleEntry"][];
4723
+ };
4724
+ };
4725
+ };
4726
+ 400: components["responses"]["ValidationFailed"];
4727
+ 401: components["responses"]["Unauthenticated"];
4728
+ 403: components["responses"]["ScopeDenied"];
4729
+ };
4730
+ };
4731
+ listMyCourses: {
4732
+ parameters: {
4733
+ query?: {
4734
+ page_size?: components["parameters"]["PageSize"];
4735
+ page_token?: components["parameters"]["PageToken"];
4736
+ };
4737
+ header?: never;
4738
+ path?: never;
4739
+ cookie?: never;
4740
+ };
4741
+ requestBody?: never;
4742
+ responses: {
4743
+ /** @description OK */
4744
+ 200: {
4745
+ headers: {
4746
+ [name: string]: unknown;
4747
+ };
4748
+ content: {
4749
+ "application/json": components["schemas"]["PageMeta"] & {
4750
+ items: components["schemas"]["MyCourse"][];
4751
+ };
4752
+ };
4753
+ };
4754
+ 401: components["responses"]["Unauthenticated"];
4755
+ 403: components["responses"]["ScopeDenied"];
4756
+ };
4757
+ };
4758
+ listMyWords: {
4759
+ parameters: {
4760
+ query?: {
4761
+ page_size?: components["parameters"]["PageSize"];
4762
+ page_token?: components["parameters"]["PageToken"];
4763
+ };
4764
+ header?: never;
4765
+ path?: never;
4766
+ cookie?: never;
4767
+ };
4768
+ requestBody?: never;
4769
+ responses: {
4770
+ /** @description OK */
4771
+ 200: {
4772
+ headers: {
4773
+ [name: string]: unknown;
4774
+ };
4775
+ content: {
4776
+ "application/json": components["schemas"]["PageMeta"] & {
4777
+ items: components["schemas"]["MyWord"][];
4778
+ };
4779
+ };
4780
+ };
4781
+ 401: components["responses"]["Unauthenticated"];
4782
+ 403: components["responses"]["ScopeDenied"];
4783
+ };
4784
+ };
4785
+ addMyWord: {
4786
+ parameters: {
4787
+ query?: never;
4788
+ header?: never;
4789
+ path?: never;
4790
+ cookie?: never;
4791
+ };
4792
+ requestBody: {
4793
+ content: {
4794
+ "application/json": {
4795
+ /** @description 简体或繁体,存成简体 */
4796
+ word: string;
4797
+ };
4798
+ };
4799
+ };
4800
+ responses: {
4801
+ /** @description 已经在生词本里 */
4802
+ 200: {
4803
+ headers: {
4804
+ [name: string]: unknown;
4805
+ };
4806
+ content: {
4807
+ "application/json": components["schemas"]["MyWord"];
4808
+ };
4809
+ };
4810
+ /** @description 加进去了 */
4811
+ 201: {
4812
+ headers: {
4813
+ [name: string]: unknown;
4814
+ };
4815
+ content: {
4816
+ "application/json": components["schemas"]["MyWord"];
4817
+ };
4818
+ };
4819
+ 400: components["responses"]["ValidationFailed"];
4820
+ 401: components["responses"]["Unauthenticated"];
4821
+ 403: components["responses"]["ScopeDenied"];
4822
+ 409: components["responses"]["Conflict"];
4823
+ };
4824
+ };
4825
+ listClassExams: {
4826
+ parameters: {
4827
+ query?: {
4828
+ page_size?: components["parameters"]["PageSize"];
4829
+ page_token?: components["parameters"]["PageToken"];
4830
+ };
4831
+ header?: never;
4832
+ path: {
4833
+ class_id: components["parameters"]["ClassId"];
4834
+ };
4835
+ cookie?: never;
4836
+ };
4837
+ requestBody?: never;
4838
+ responses: {
4839
+ /** @description OK */
4840
+ 200: {
4841
+ headers: {
4842
+ [name: string]: unknown;
4843
+ };
4844
+ content: {
4845
+ "application/json": components["schemas"]["PageMeta"] & {
4846
+ items: components["schemas"]["ClassExam"][];
4847
+ };
4848
+ };
4849
+ };
4850
+ 401: components["responses"]["Unauthenticated"];
4851
+ 403: components["responses"]["ScopeDenied"];
4852
+ 404: components["responses"]["NotFound"];
4853
+ };
4854
+ };
4855
+ listClassExamResults: {
4856
+ parameters: {
4857
+ query?: {
4858
+ page_size?: components["parameters"]["PageSize"];
4859
+ page_token?: components["parameters"]["PageToken"];
4860
+ };
4861
+ header?: never;
4862
+ path: {
4863
+ class_id: components["parameters"]["ClassId"];
4864
+ exam_id: string;
4865
+ };
4866
+ cookie?: never;
4867
+ };
4868
+ requestBody?: never;
4869
+ responses: {
4870
+ /** @description OK */
4871
+ 200: {
4872
+ headers: {
4873
+ [name: string]: unknown;
4874
+ };
4875
+ content: {
4876
+ "application/json": components["schemas"]["PageMeta"] & {
4877
+ /** @description 结果公布了没有;没公布时分数和评语都是 null */
4878
+ released: boolean;
4879
+ items: components["schemas"]["OpenSubmission"][];
4880
+ };
4881
+ };
4882
+ };
4883
+ 401: components["responses"]["Unauthenticated"];
4884
+ 403: components["responses"]["ScopeDenied"];
4885
+ 404: components["responses"]["NotFound"];
4886
+ };
4887
+ };
3049
4888
  onEvent: {
3050
4889
  parameters: {
3051
4890
  query?: never;