{
  "input": "{\"openapi\": \"3.0.3\", \"info\": {\"title\": \"江苏国信微信小程序 H5 免登\", \"version\": \"1.0.0\", \"description\": \"增量同步到 Apifox：江苏国信微信小程序通过手机号申请一次性 ticket，H5 消费 ticket 换取智慧食堂 Access-Token。\"}, \"servers\": [{\"url\": \"https://zhctdev.yyangpt.cn/aizhct\", \"description\": \"开发环境\"}], \"tags\": [{\"name\": \"用户认证模块/H5免登\", \"description\": \"第三方小程序嵌入 H5 免登接口\"}], \"paths\": {\"/api/sso/ticket/create\": {\"post\": {\"tags\": [\"用户认证模块/H5免登\"], \"summary\": \"江苏国信小程序申请 H5 免登 ticket\", \"description\": \"小程序方后端在用户已登录后，使用可信手机号向智慧食堂后端申请一次性 ticket。该接口必须由小程序后端调用，不允许在小程序前端调用，也不能把签名密钥下发到前端。\\n\\n业务规则：\\n- provider 固定为 jsgx_wechat_applet_001。\\n- mobile 必须是小程序后端登录态中的可信手机号。\\n- 后端校验 provider、timestamp、nonce、sign 后查询智慧食堂账号。\\n- 手机号不存在时返回手机号不存在。\\n- ticket 默认 300 秒有效，只能消费一次。\\n- redirect_url 由数据库 yoshop_sso_provider.redirect_domain 生成，形如 https://{redirect_domain}/web/#/home?ticket={ticket}。\\n\\n签名规则：\\n- 参与签名字段：provider、mobile、timestamp、nonce。\\n- 按字段名 ASCII 升序排序。\\n- 使用 RFC3986 URL 编码拼接查询串。\\n- 使用双方线下约定 secret 计算 HMAC-SHA256，输出小写 hex。\\n- 示例待签名字符串：mobile=13800000000&nonce=random_abc123&provider=jsgx_wechat_applet_001&timestamp=1710000000。\", \"operationId\": \"createJsgxWechatAppletSsoTicket\", \"x-apifox-folder\": \"用户认证模块/H5免登\", \"requestBody\": {\"required\": true, \"content\": {\"application/x-www-form-urlencoded\": {\"schema\": {\"$ref\": \"#/components/schemas/CreateSsoTicketRequest\"}, \"examples\": {\"default\": {\"summary\": \"申请 ticket\", \"value\": {\"provider\": \"jsgx_wechat_applet_001\", \"mobile\": \"13800000000\", \"timestamp\": 1710000000, \"nonce\": \"random_abc123\", \"sign\": \"0f6e000000000000000000000000000000000000000000000000000000000000\"}}}}}}, \"responses\": {\"200\": {\"description\": \"申请成功\", \"content\": {\"application/json\": {\"schema\": {\"$ref\": \"#/components/schemas/CreateSsoTicketSuccessResponse\"}, \"examples\": {\"success\": {\"summary\": \"成功响应\", \"value\": {\"status\": 200, \"message\": \"ok\", \"data\": {\"ticket\": \"sso_随机字符串\", \"expires_in\": 300, \"redirect_url\": \"https://jsgx.yyangpt.cn/web/#/home?ticket=sso_%E9%9A%8F%E6%9C%BA%E5%AD%97%E7%AC%A6%E4%B8%B2\"}}}}}}}, \"400\": {\"description\": \"请求参数错误\", \"content\": {\"application/json\": {\"schema\": {\"$ref\": \"#/components/schemas/CommonErrorResponse\"}, \"examples\": {\"badMobile\": {\"summary\": \"手机号格式错误\", \"value\": {\"status\": 400, \"message\": \"手机号格式错误\", \"data\": []}}, \"missingNonce\": {\"summary\": \"缺少 nonce\", \"value\": {\"status\": 400, \"message\": \"缺少nonce\", \"data\": []}}}}}}, \"401\": {\"description\": \"签名或时间戳错误\", \"content\": {\"application/json\": {\"schema\": {\"$ref\": \"#/components/schemas/CommonErrorResponse\"}, \"examples\": {\"expired\": {\"summary\": \"请求已过期\", \"value\": {\"status\": 401, \"message\": \"请求已过期\", \"data\": []}}, \"badSign\": {\"summary\": \"签名错误\", \"value\": {\"status\": 401, \"message\": \"签名错误\", \"data\": []}}}}}}, \"403\": {\"description\": \"provider 不支持或未启用\", \"content\": {\"application/json\": {\"schema\": {\"$ref\": \"#/components/schemas/CommonErrorResponse\"}, \"examples\": {\"providerUnsupported\": {\"summary\": \"provider 不支持\", \"value\": {\"status\": 403, \"message\": \"provider不支持\", \"data\": []}}, \"providerDisabled\": {\"summary\": \"provider 未启用\", \"value\": {\"status\": 403, \"message\": \"provider未启用\", \"data\": []}}}}}}, \"404\": {\"description\": \"手机号没有对应智慧食堂账号\", \"content\": {\"application/json\": {\"schema\": {\"$ref\": \"#/components/schemas/CommonErrorResponse\"}, \"examples\": {\"mobileNotFound\": {\"summary\": \"手机号不存在\", \"value\": {\"status\": 404, \"message\": \"手机号不存在\", \"data\": []}}}}}}, \"409\": {\"description\": \"重复 nonce 或手机号不唯一\", \"content\": {\"application/json\": {\"schema\": {\"$ref\": \"#/components/schemas/CommonErrorResponse\"}, \"examples\": {\"nonceUsed\": {\"summary\": \"nonce 已使用\", \"value\": {\"status\": 409, \"message\": \"nonce已使用\", \"data\": []}}, \"duplicateMobile\": {\"summary\": \"手机号对应多个账号\", \"value\": {\"status\": 409, \"message\": \"手机号对应多个账号，无法自动登录\", \"data\": []}}}}}}, \"500\": {\"description\": \"服务端 SSO provider 配置缺失\", \"content\": {\"application/json\": {\"schema\": {\"$ref\": \"#/components/schemas/CommonErrorResponse\"}, \"examples\": {\"missingSecret\": {\"summary\": \"未配置密钥\", \"value\": {\"status\": 500, \"message\": \"SSO provider未配置密钥\", \"data\": []}}, \"missingRedirectDomain\": {\"summary\": \"未配置跳转域名\", \"value\": {\"status\": 500, \"message\": \"SSO provider未配置跳转域名\", \"data\": []}}}}}}}}}, \"/api/sso/ticket/consume\": {\"post\": {\"tags\": [\"用户认证模块/H5免登\"], \"summary\": \"H5 消费免登 ticket 换取 Access-Token\", \"description\": \"H5 打开 https://{redirect_domain}/web/#/home?ticket={ticket} 后自动调用该接口，消费一次性 ticket 并换取智慧食堂现有 Access-Token。小程序方一般不需要直接调用。\\n\\n业务规则：\\n- provider 固定为 jsgx_wechat_applet_001。\\n- ticket 只能使用一次，消费后立即失效。\\n- ticket 过期、已消费或不存在时返回 ticket 已失效，请重新从小程序进入。\\n- 成功后 H5 清理 URL 中的 ticket，并写入现有登录态。\", \"operationId\": \"consumeJsgxWechatAppletSsoTicket\", \"x-apifox-folder\": \"用户认证模块/H5免登\", \"requestBody\": {\"required\": true, \"content\": {\"application/x-www-form-urlencoded\": {\"schema\": {\"$ref\": \"#/components/schemas/ConsumeSsoTicketRequest\"}, \"examples\": {\"default\": {\"summary\": \"消费 ticket\", \"value\": {\"provider\": \"jsgx_wechat_applet_001\", \"ticket\": \"sso_随机字符串\"}}}}}}, \"responses\": {\"200\": {\"description\": \"消费成功\", \"content\": {\"application/json\": {\"schema\": {\"$ref\": \"#/components/schemas/ConsumeSsoTicketSuccessResponse\"}, \"examples\": {\"success\": {\"summary\": \"成功响应\", \"value\": {\"status\": 200, \"message\": \"ok\", \"data\": {\"token\": \"智慧食堂Access-Token\", \"system_user_id\": 123, \"user_name\": \"张三\", \"department_name\": \"某学校\", \"mobile\": \"138****0000\"}}}}}}}, \"400\": {\"description\": \"缺少 ticket\", \"content\": {\"application/json\": {\"schema\": {\"$ref\": \"#/components/schemas/CommonErrorResponse\"}, \"examples\": {\"missingTicket\": {\"summary\": \"缺少 ticket\", \"value\": {\"status\": 400, \"message\": \"缺少ticket\", \"data\": []}}}}}}, \"401\": {\"description\": \"ticket 无效或已失效\", \"content\": {\"application/json\": {\"schema\": {\"$ref\": \"#/components/schemas/CommonErrorResponse\"}, \"examples\": {\"ticketExpired\": {\"summary\": \"ticket 已失效\", \"value\": {\"status\": 401, \"message\": \"ticket已失效，请重新从小程序进入\", \"data\": []}}, \"ticketInvalid\": {\"summary\": \"ticket 无效\", \"value\": {\"status\": 401, \"message\": \"ticket无效\", \"data\": []}}}}}}, \"403\": {\"description\": \"provider 不支持\", \"content\": {\"application/json\": {\"schema\": {\"$ref\": \"#/components/schemas/CommonErrorResponse\"}, \"examples\": {\"providerUnsupported\": {\"summary\": \"provider 不支持\", \"value\": {\"status\": 403, \"message\": \"provider不支持\", \"data\": []}}}}}}, \"404\": {\"description\": \"用户不存在\", \"content\": {\"application/json\": {\"schema\": {\"$ref\": \"#/components/schemas/CommonErrorResponse\"}, \"examples\": {\"userNotFound\": {\"summary\": \"用户不存在\", \"value\": {\"status\": 404, \"message\": \"用户不存在\", \"data\": []}}}}}}}}}}, \"components\": {\"schemas\": {\"CreateSsoTicketRequest\": {\"type\": \"object\", \"required\": [\"provider\", \"mobile\", \"timestamp\", \"nonce\", \"sign\"], \"properties\": {\"provider\": {\"type\": \"string\", \"description\": \"固定值：jsgx_wechat_applet_001\", \"example\": \"jsgx_wechat_applet_001\"}, \"mobile\": {\"type\": \"string\", \"description\": \"小程序后端从登录态取得的可信手机号\", \"example\": \"13800000000\"}, \"timestamp\": {\"type\": \"integer\", \"description\": \"Unix 秒级时间戳，默认允许误差 300 秒\", \"example\": 1710000000}, \"nonce\": {\"type\": \"string\", \"description\": \"随机字符串，同一 provider 下不可重复\", \"example\": \"random_abc123\"}, \"sign\": {\"type\": \"string\", \"description\": \"HMAC-SHA256 签名，小写 hex\", \"example\": \"0f6e000000000000000000000000000000000000000000000000000000000000\"}}}, \"ConsumeSsoTicketRequest\": {\"type\": \"object\", \"required\": [\"provider\", \"ticket\"], \"properties\": {\"provider\": {\"type\": \"string\", \"description\": \"固定值：jsgx_wechat_applet_001\", \"example\": \"jsgx_wechat_applet_001\"}, \"ticket\": {\"type\": \"string\", \"description\": \"申请 ticket 接口返回的一次性 ticket\", \"example\": \"sso_随机字符串\"}}}, \"CreateSsoTicketSuccessResponse\": {\"type\": \"object\", \"required\": [\"status\", \"message\", \"data\"], \"properties\": {\"status\": {\"type\": \"integer\", \"example\": 200}, \"message\": {\"type\": \"string\", \"example\": \"ok\"}, \"data\": {\"type\": \"object\", \"required\": [\"ticket\", \"expires_in\", \"redirect_url\"], \"properties\": {\"ticket\": {\"type\": \"string\", \"example\": \"sso_随机字符串\"}, \"expires_in\": {\"type\": \"integer\", \"description\": \"ticket 剩余有效秒数\", \"example\": 300}, \"redirect_url\": {\"type\": \"string\", \"description\": \"小程序前端最终打开的 H5 地址，域名来自 yoshop_sso_provider.redirect_domain\", \"example\": \"https://jsgx.yyangpt.cn/web/#/home?ticket=sso_%E9%9A%8F%E6%9C%BA%E5%AD%97%E7%AC%A6%E4%B8%B2\"}}}}}, \"ConsumeSsoTicketSuccessResponse\": {\"type\": \"object\", \"required\": [\"status\", \"message\", \"data\"], \"properties\": {\"status\": {\"type\": \"integer\", \"example\": 200}, \"message\": {\"type\": \"string\", \"example\": \"ok\"}, \"data\": {\"type\": \"object\", \"required\": [\"token\", \"system_user_id\", \"user_name\", \"department_name\", \"mobile\"], \"properties\": {\"token\": {\"type\": \"string\", \"description\": \"智慧食堂 Access-Token\", \"example\": \"智慧食堂Access-Token\"}, \"system_user_id\": {\"type\": \"integer\", \"description\": \"智慧食堂用户 ID\", \"example\": 123}, \"user_name\": {\"type\": \"string\", \"example\": \"张三\"}, \"department_name\": {\"type\": \"string\", \"example\": \"某学校\"}, \"mobile\": {\"type\": \"string\", \"description\": \"脱敏手机号\", \"example\": \"138****0000\"}}}}}, \"CommonErrorResponse\": {\"type\": \"object\", \"required\": [\"status\", \"message\", \"data\"], \"properties\": {\"status\": {\"type\": \"integer\", \"example\": 400}, \"message\": {\"type\": \"string\", \"example\": \"手机号格式错误\"}, \"data\": {\"type\": \"array\", \"items\": {}, \"example\": []}}}}}}",
  "options": {
    "targetEndpointFolderId": 52798662,
    "targetSchemaFolderId": 0,
    "endpointOverwriteBehavior": "OVERWRITE_EXISTING",
    "schemaOverwriteBehavior": "KEEP_EXISTING",
    "updateFolderOfChangedEndpoint": true,
    "prependBasePath": false
  }
}