Просмотр исходного кода

fix(openapi): 点号写法的数组元素字段折进 items;规格加入 batch-count

- 生成器 nestDotted():校验规则 / 文档注释里的 `targets.*.id`、`ids.*` 不再变成名字带点的
  顶层字段,而是父数组的 items(只处理一层)
- 重新生成规格:新增 POST /v3/discussions/batch-count,co_editor 清空规则写进 PATCH 说明

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
visuddhinanda 4 дней назад
Родитель
Сommit
502e255551

+ 2 - 0
openapi/public/assets/protocol/main.yaml

@@ -685,6 +685,8 @@ paths:
     $ref: ./resources/auto/v3-agent-grants.yaml
   /v3/discussions:
     $ref: ./resources/auto/v3-discussions.yaml
+  /v3/discussions/batch-count:
+    $ref: ./resources/auto/v3-discussions-batch-count.yaml
   /v3/discussions/summary:
     $ref: ./resources/auto/v3-discussions-summary.yaml
   '/v3/discussions/{discussion}':

+ 70 - 0
openapi/public/assets/protocol/resources/auto/v3-discussions-batch-count.yaml

@@ -0,0 +1,70 @@
+# 由 openapi/scripts/generate.php 生成,请勿手改;补充写到 overrides/v3-discussions-batch-count.yaml
+post:
+  summary: 批量取讨论角标
+  tags:
+    - discussions
+  operationId: post_api_v3_discussions_batch_count
+  description: |-
+    每个对象返回未关闭的评论线程数(total)与其中当前用户发起的数目(mine,未登录为 0),
+    按请求顺序、一个不少(没有讨论的为 0)。只数评论线程(discussion / qa / help / issue),
+    批注与已删除的不算;可见性与列表相同:未登录不数协作类话题,basic 角色在无权管理的
+    对象上只数自己发起的协作类话题。最多 500 个对象。
+    与 v2 的区别:入参是对象 uid 而不是句子坐标;只给计数不给明细;v2 不分类型、不管可见性。
+
+    实现:`V3\DiscussionBatchCountController@__invoke`
+  security:
+    - {  }
+    -
+      bearerAuth: []
+  requestBody:
+    required: true
+    content:
+      application/json:
+        schema:
+          type: object
+          properties:
+            targets:
+              type: array
+              description: '讨论对象列表,每项 {type, id},最多 500 个'
+              items:
+                type: object
+                properties:
+                  type:
+                    type: string
+                    description: 对象类型
+                    enum:
+                      - sentence
+                      - wbw
+                  id:
+                    type: string
+                    description: '对象 uid'
+                required:
+                  - type
+                  - id
+          required:
+            - targets
+  responses:
+    200:
+      description: 成功
+      content:
+        application/json:
+          schema:
+            type: object
+            properties:
+              data:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    target_type:
+                      type: string
+                    target_id:
+                      type: string
+                    total:
+                      type: integer
+                    mine:
+                      type: integer
+              meta:
+                $ref: '../../main.yaml#/components/schemas/PaginationMeta'
+    422:
+      $ref: '../../main.yaml#/components/responses/ProblemValidation'

+ 37 - 0
openapi/scripts/generate.php

@@ -354,6 +354,42 @@ function normalizeType(string $type): string
     };
 }
 
+/**
+ * 把点号写法的数组元素字段(`targets.*.id`、`ids.*`)折进父字段的 items。
+ *
+ * Laravel 的校验规则和文档注释都用这种写法;不折的话它们会变成名字里带点的顶层字段,
+ * 前端类型与生成的客户端都对不上。只处理一层(`a.*` / `a.*.b`),更深的嵌套原样留着。
+ *
+ * @param  array<string, array>  $props
+ * @param  list<string>  $required
+ * @return array{0: array<string, array>, 1: list<string>}
+ */
+function nestDotted(array $props, array $required): array
+{
+    foreach (array_keys($props) as $key) {
+        if (! preg_match('/^([^.]+)\.\*(?:\.([^.*]+))?$/', $key, $m)) {
+            continue;
+        }
+        [$parent, $child] = [$m[1], $m[2] ?? null];
+        $schema = $props[$key];
+        unset($props[$key]);
+        $props[$parent] = ($props[$parent] ?? []) + ['type' => 'array'];
+        $props[$parent]['type'] = 'array';
+        if ($child === null) {
+            $props[$parent]['items'] = $schema;
+        } else {
+            $props[$parent]['items'] ??= ['type' => 'object', 'properties' => []];
+            $props[$parent]['items']['properties'][$child] = $schema;
+            if (in_array($key, $required, true)) {
+                $props[$parent]['items']['required'][] = $child;
+            }
+        }
+        $required = array_values(array_filter($required, fn ($name) => $name !== $key));
+    }
+
+    return [$props, $required];
+}
+
 /** 验证规则数组 → [字段 => ['required'=>bool,'type'=>string,'enum'=>[]]] */
 function rulesFromArray(Node\Expr\Array_ $array): array
 {
@@ -831,6 +867,7 @@ function buildOperation(array $route, string $httpMethod): array
                 $required[] = $name;
             }
         }
+        [$props, $required] = nestDotted($props, $required);
         if ($props) {
             $body = ['type' => 'object', 'properties' => $props];
             if ($required) {