diff --git a/qy_lty/aiapp/views.py b/qy_lty/aiapp/views.py index 4707d8e..2b2c2f4 100644 --- a/qy_lty/aiapp/views.py +++ b/qy_lty/aiapp/views.py @@ -684,4 +684,63 @@ class CredentialSlotAdminView(APIView): # 重新读取 instance.access_token:serializer.save() 后 instance 已被同步刷新; # _build_response_data 内部会再次 dict(serializer.data) 拿最新 OrderedDict。 data = self._build_response_data(instance) - return success_response(data=data, message="凭据已更新") \ No newline at end of file + return success_response(data=data, message="凭据已更新") + + +# ====================================================================== +# Phase 3 — 通用凭据槽位客户端读取接口(CRED-05) +# 1:1 复刻 CredentialSlotAdminView 的 GET 部分,删 _ensure_admin / _build_response_data / PUT +# 关键差异:明文返回 access_token,不调 mask_token,不做 is_staff 二次校验 +# ====================================================================== + +# 客户端响应 data 子 schema:access_token 字段 description 显式标注「明文」 +_credential_slot_client_data_schema = openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'app_id': openapi.Schema( + type=openapi.TYPE_STRING, + description='第三方服务商分配的 APP ID(明文)', + ), + 'access_token': openapi.Schema( + type=openapi.TYPE_STRING, + description='明文 Access Token,供手机/设备端实际调用第三方服务(管理端同接口会脱敏返回末 4 位)', + ), + 'updated_at': openapi.Schema( + type=openapi.TYPE_STRING, + format='date-time', + description='最近一次更新时间(ISO 8601)', + ), + }, +) + + +class CredentialSlotClientView(APIView): + """通用凭据槽位客户端读取接口(user / admin token 鉴权,明文返回)。 + + GET: 返回明文 app_id + access_token,供手机/设备端实际调用第三方服务。 + + 与管理端 CredentialSlotAdminView 的关键差异(per CONTEXT.md D-Client-View): + - 不做 is_staff 二次校验:admin / user token 都允许(admin 用户是手机用户超集) + - 不脱敏:直接返回 serializer.data(明文返回) + - 仅 GET:客户端只读,不能写入 + """ + authentication_classes = [RedisTokenAuthentication] + permission_classes = [IsAuthenticated] + tags = ['通用凭据槽位(客户端)'] + + @swagger_auto_schema( + operation_description="读取通用凭据槽位(明文 access_token,供手机/设备端实际调用第三方服务)", + responses={ + 200: openapi.Response( + '读取成功', + get_standardized_response_schema(_credential_slot_client_data_schema), + ), + 401: openapi.Response('未提供有效 token', get_standardized_response_schema()), + }, + security=[{'Bearer': []}], + tags=['通用凭据槽位(客户端)'], + ) + def get(self, request): + instance = CredentialSlot.get_solo() + serializer = CredentialSlotSerializer(instance) + return success_response(data=serializer.data, message="读取成功")