gengxin
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: 唐侦探独立章节存档
|
||||
version: 1.0.0
|
||||
description: >-
|
||||
使用原小程序 token,仅操作登录用户自己的 season-01 ID 存档。
|
||||
不接收健康回答、正文、快照或客户端用户身份。业务错误沿用 HTTP 200/code 约定。
|
||||
servers:
|
||||
- url: /api
|
||||
security:
|
||||
- MiniProgramToken: []
|
||||
paths:
|
||||
/tang/catalog:
|
||||
get:
|
||||
operationId: getTangDetectiveCatalog
|
||||
summary: 读取部署版本的 ID 白名单
|
||||
responses:
|
||||
'200':
|
||||
description: code=1 为目录,code=0/-1 为错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- type: object
|
||||
required: [code, show, msg, data]
|
||||
properties:
|
||||
code: {const: 1}
|
||||
show: {const: 0}
|
||||
msg: {type: string}
|
||||
data: {$ref: '#/components/schemas/Catalog'}
|
||||
- {$ref: '#/components/schemas/Failure'}
|
||||
/tang/progress:
|
||||
get:
|
||||
operationId: getTangDetectiveProgress
|
||||
summary: 只读当前登录用户存档,无存档返回 revision=0 的默认值
|
||||
responses:
|
||||
'200':
|
||||
$ref: '#/components/responses/ProgressResponse'
|
||||
/tang/saveProgress:
|
||||
post:
|
||||
operationId: saveTangDetectiveProgress
|
||||
summary: 版本匹配时替换整份 ID 投影或重新开始故事
|
||||
description: >-
|
||||
原始请求体最多 32768 字节。最后一次 request_id 的相同规范化内容可幂等重试。
|
||||
必须匹配 base_revision 和 story_generation;冲突不覆盖、不自动合并。
|
||||
reset_story 必须提交空故事字段,收藏为当前服务器与该请求的已验证卡 ID 并集。
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/SaveRequest'}
|
||||
responses:
|
||||
'200':
|
||||
$ref: '#/components/responses/ProgressResponse'
|
||||
components:
|
||||
securitySchemes:
|
||||
MiniProgramToken:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: token
|
||||
description: 沿用现有 LoginMiddleware;不是 Authorization,也不放入请求正文。
|
||||
responses:
|
||||
ProgressResponse:
|
||||
description: >-
|
||||
必须检查 code;code=1 的 data 是本人确认存档。
|
||||
此控制器的响应含 Cache-Control no-store。
|
||||
headers:
|
||||
Cache-Control:
|
||||
schema: {type: string, const: no-store}
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- {$ref: '#/components/schemas/ProgressSuccess'}
|
||||
- {$ref: '#/components/schemas/Failure'}
|
||||
schemas:
|
||||
ChapterId:
|
||||
type: string
|
||||
pattern: '^S01-C(0[1-9]|1[0-5])$'
|
||||
EventId:
|
||||
type: string
|
||||
pattern: '^S01-H(0[1-9]|[1-5][0-9]|60)$'
|
||||
PageId:
|
||||
type: string
|
||||
pattern: '^S01-C(0[1-9]|1[0-5])-P0[1-8]$'
|
||||
CardId:
|
||||
type: string
|
||||
pattern: '^S01-C(0[1-9]|1[0-5])-MC01$'
|
||||
Catalog:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [game_id, schema_version, content_version, max_body_bytes, chapters]
|
||||
properties:
|
||||
game_id: {const: tang-detective}
|
||||
schema_version: {const: 1}
|
||||
content_version: {const: season-01}
|
||||
max_body_bytes: {const: 32768}
|
||||
chapters:
|
||||
type: array
|
||||
minItems: 15
|
||||
maxItems: 15
|
||||
items:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [chapter_id, chapter_number, hotspot_ids, page_ids, memory_card_id]
|
||||
properties:
|
||||
chapter_id: {$ref: '#/components/schemas/ChapterId'}
|
||||
chapter_number: {type: integer, minimum: 1, maximum: 15}
|
||||
hotspot_ids:
|
||||
type: array
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
uniqueItems: true
|
||||
items: {$ref: '#/components/schemas/EventId'}
|
||||
page_ids:
|
||||
type: array
|
||||
minItems: 8
|
||||
maxItems: 8
|
||||
uniqueItems: true
|
||||
items: {$ref: '#/components/schemas/PageId'}
|
||||
memory_card_id: {$ref: '#/components/schemas/CardId'}
|
||||
Reader:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [currentPageId, completedEventIds, chapterFinished]
|
||||
properties:
|
||||
currentPageId: {$ref: '#/components/schemas/PageId'}
|
||||
completedEventIds:
|
||||
type: array
|
||||
maxItems: 4
|
||||
uniqueItems: true
|
||||
items: {$ref: '#/components/schemas/EventId'}
|
||||
chapterFinished: {type: boolean}
|
||||
description: >-
|
||||
事件必须是所属章节的连续前缀且与 completedHotspots 相同;
|
||||
chapterFinished 必须与 completedChapters 成员关系一致,为 true 时须有四事件。
|
||||
未 finished 仅允许 P01 至 P(03+事件数),最多 P07;P08 必须 finished。
|
||||
Progress:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [completedHotspots, completedChapters, lastChapter, collectedMemoryCards, comicReaderByChapter, lastPageId]
|
||||
properties:
|
||||
completedHotspots:
|
||||
type: object
|
||||
maxProperties: 15
|
||||
propertyNames: {$ref: '#/components/schemas/ChapterId'}
|
||||
additionalProperties:
|
||||
type: array
|
||||
maxItems: 4
|
||||
uniqueItems: true
|
||||
items: {$ref: '#/components/schemas/EventId'}
|
||||
completedChapters:
|
||||
type: array
|
||||
maxItems: 15
|
||||
uniqueItems: true
|
||||
items: {$ref: '#/components/schemas/ChapterId'}
|
||||
lastChapter: {type: integer, minimum: 1, maximum: 15}
|
||||
collectedMemoryCards:
|
||||
type: array
|
||||
maxItems: 15
|
||||
uniqueItems: true
|
||||
items: {$ref: '#/components/schemas/CardId'}
|
||||
comicReaderByChapter:
|
||||
type: object
|
||||
maxProperties: 15
|
||||
propertyNames: {$ref: '#/components/schemas/ChapterId'}
|
||||
additionalProperties: {$ref: '#/components/schemas/Reader'}
|
||||
lastPageId:
|
||||
oneOf:
|
||||
- {const: ''}
|
||||
- {$ref: '#/components/schemas/PageId'}
|
||||
description: >-
|
||||
空映射必须是 {},不是 []。有已完成事件或 finished 的章节必须有 reader。
|
||||
非空 lastPageId 必须等于 lastChapter 对应 reader.currentPageId。
|
||||
跨字段成员关系、顺序和解锁约束由 TangDetectiveProgress 验证。
|
||||
SaveRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [schema_version, content_version, base_revision, story_generation, request_id, operation, progress]
|
||||
properties:
|
||||
schema_version: {type: integer, const: 1}
|
||||
content_version: {const: season-01}
|
||||
base_revision: {type: integer, minimum: 0, maximum: 2147483646}
|
||||
story_generation: {type: integer, minimum: 0, maximum: 2147483646}
|
||||
request_id: {type: string, pattern: '^[A-Za-z0-9_-]{16,64}$'}
|
||||
operation: {enum: [replace, reset_story]}
|
||||
progress: {$ref: '#/components/schemas/Progress'}
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
operation: {const: reset_story}
|
||||
then:
|
||||
properties:
|
||||
progress:
|
||||
properties:
|
||||
completedHotspots: {maxProperties: 0}
|
||||
completedChapters: {maxItems: 0}
|
||||
lastChapter: {const: 1}
|
||||
comicReaderByChapter: {maxProperties: 0}
|
||||
lastPageId: {const: ''}
|
||||
State:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [user_id, schema_version, content_version, revision, story_generation, progress]
|
||||
properties:
|
||||
user_id: {type: integer, minimum: 1}
|
||||
schema_version: {const: 1}
|
||||
content_version: {const: season-01}
|
||||
revision: {type: integer, minimum: 0}
|
||||
story_generation: {type: integer, minimum: 0}
|
||||
progress: {$ref: '#/components/schemas/Progress'}
|
||||
idempotent:
|
||||
type: boolean
|
||||
description: 仅保存响应提供;true 表示重复确认最后一次提交,未重复写入。
|
||||
ProgressSuccess:
|
||||
type: object
|
||||
required: [code, show, msg, data]
|
||||
properties:
|
||||
code: {const: 1}
|
||||
show: {const: 0}
|
||||
msg: {type: string}
|
||||
data: {$ref: '#/components/schemas/State'}
|
||||
Failure:
|
||||
type: object
|
||||
required: [code, show, msg, data]
|
||||
properties:
|
||||
code: {type: integer, enum: [0, -1]}
|
||||
show: {type: integer, enum: [0, 1]}
|
||||
msg: {type: string}
|
||||
data:
|
||||
oneOf:
|
||||
- type: object
|
||||
additionalProperties: false
|
||||
required: [error_code]
|
||||
properties:
|
||||
error_code:
|
||||
enum: [INVALID_REQUEST, PAYLOAD_TOO_LARGE, UNSUPPORTED_CONTENT_VERSION, PROGRESS_CONFLICT, IDEMPOTENCY_CONFLICT, METHOD_NOT_ALLOWED, UNSUPPORTED_MEDIA_TYPE, AUTH_REQUIRED, STORAGE_UNAVAILABLE]
|
||||
- type: array
|
||||
maxItems: 0
|
||||
description: 原登录中间件可能提前返回空 data;登录过期 code=-1。
|
||||
Reference in New Issue
Block a user