feat(chat): add support action cards and live entitlements
Docker Image / Build and Push Docker Image (push) Successful in 2m2s

This commit is contained in:
Codex
2026-07-24 17:59:57 +08:00
parent 17236bd14e
commit 30ab2c2c97
52 changed files with 1151 additions and 40 deletions
@@ -60,6 +60,26 @@ describe("multi-character API contract", () => {
});
});
it("reports an idempotent chat action event", async () => {
const body = {
eventId: "4b223884-02ec-4fd2-aacf-e84ee8ca3adb",
actionId: "action-1",
characterId: CHARACTER_ID,
eventType: "opened" as const,
};
httpClientMock.mockResolvedValue({
success: true,
data: { ...body, duplicate: false },
});
await new ChatApi().recordActionEvent(body);
expect(httpClientMock).toHaveBeenCalledWith("/api/chat/action-events", {
method: "POST",
body,
});
});
it("persists the character opening message with the canonical contract", async () => {
httpClientMock.mockResolvedValue({
success: true,
+1
View File
@@ -19,6 +19,7 @@
"paymentTipMessage": { "method": "post", "path": "/api/payment/tip-message" },
"paymentCommercialOfferAccept": { "method": "post", "path": "/api/payment/commercial-offers/{offerId}/accept" },
"chatSend": { "method": "post", "path": "/api/chat/send" },
"chatActionEvents": { "method": "post", "path": "/api/chat/action-events" },
"chatOpeningMessage": { "method": "post", "path": "/api/chat/opening-message" },
"chatHistory": { "method": "get", "path": "/api/chat/history" },
"chatUnlockPrivate": { "method": "post", "path": "/api/chat/unlock-private" },
+3
View File
@@ -78,6 +78,9 @@ export class ApiPath {
/** 发送消息 */
static readonly chatSend = apiContract.chatSend.path;
/** 记录角色聊天操作卡漏斗事件 */
static readonly chatActionEvents = apiContract.chatActionEvents.path;
/** 幂等保存角色开场白 */
static readonly chatOpeningMessage = apiContract.chatOpeningMessage.path;
+19
View File
@@ -11,6 +11,10 @@ import {
ChatPreviewsResponseSchema,
ChatSendResponse,
ChatSendResponseSchema,
ChatActionEventRequest,
ChatActionEventRequestSchema,
ChatActionEventResponse,
ChatActionEventResponseSchema,
OpeningMessageRequest,
OpeningMessageResponse,
OpeningMessageResponseSchema,
@@ -42,6 +46,21 @@ export class ChatApi {
return ChatSendResponseSchema.parse(unwrap(env) as Record<string, unknown>);
}
/** 上报白名单聊天操作卡事件;eventId 用于安全重试。 */
async recordActionEvent(
body: ChatActionEventRequest,
): Promise<ChatActionEventResponse> {
const request = ChatActionEventRequestSchema.parse(body);
const env = await httpClient<ApiEnvelope<unknown>>(
ApiPath.chatActionEvents,
{
method: "POST",
body: request,
},
);
return ChatActionEventResponseSchema.parse(unwrap(env));
}
/** 幂等保存当前角色的首次开场白。 */
async saveOpeningMessage(
body: OpeningMessageRequest,