refactor(chat): remove legacy unlock response fields

This commit is contained in:
2026-07-15 15:06:09 +08:00
parent 684ae4bf5d
commit 078774f9b9
5 changed files with 116 additions and 62 deletions
@@ -25,15 +25,14 @@ describe("UnlockPrivateResponse", () => {
expect(response.shortfallCredits).toBe(0);
});
it("normalizes backend ids, audio aliases, and unlocked images", () => {
it("parses canonical ids, audio, and unlocked images", () => {
const response = UnlockPrivateResponse.from({
unlocked: true,
message_id: "backend-message-1",
messageId: "backend-message-1",
clientLockId: "promotion-1",
lockType: "image_paywall",
content: null,
reply: "Unlocked reply",
audio_url: "https://example.com/audio.mp3",
content: "Unlocked reply",
audioUrl: "https://example.com/audio.mp3",
image: {
type: "promotion",
url: "https://example.com/image.jpg",
@@ -41,18 +40,20 @@ describe("UnlockPrivateResponse", () => {
});
expect(response.messageId).toBe("backend-message-1");
expect(response.clientLockId).toBe("promotion-1");
expect(response.lockType).toBe("image_paywall");
expect(response.content).toBe("Unlocked reply");
expect(response.audioUrl).toBe("https://example.com/audio.mp3");
expect(response.image.url).toBe("https://example.com/image.jpg");
expect(response.toJson()).toMatchObject({
clientLockId: "promotion-1",
lockType: "image_paywall",
});
});
it("parses an insufficient-balance unlock response", () => {
it("parses an insufficient-credits unlock response", () => {
const response = UnlockPrivateResponse.from({
unlocked: false,
content: "",
reason: "insufficient_balance",
reason: "insufficient_credits",
creditBalance: 5,
creditsCharged: 0,
requiredCredits: 10,
@@ -60,7 +61,7 @@ describe("UnlockPrivateResponse", () => {
});
expect(response.unlocked).toBe(false);
expect(response.reason).toBe("insufficient_balance");
expect(response.reason).toBe("insufficient_credits");
expect(response.creditBalance).toBe(5);
expect(response.creditsCharged).toBe(0);
expect(response.requiredCredits).toBe(10);
@@ -72,7 +73,7 @@ describe("UnlockPrivateResponse", () => {
unlocked: false,
content: null,
audioUrl: null,
reason: "insufficient_balance",
reason: "insufficient_credits",
});
expect(response.content).toBe("");
@@ -83,7 +84,7 @@ describe("UnlockPrivateResponse", () => {
const response = UnlockPrivateResponse.from({
unlocked: false,
content: "",
reason: "insufficient_balance",
reason: "insufficient_credits",
creditBalance: null,
creditsCharged: null,
requiredCredits: null,
@@ -112,4 +113,20 @@ describe("UnlockPrivateResponse", () => {
expect(response.toJson()).not.toHaveProperty("lockDetail");
expect(response).not.toHaveProperty("lockDetail");
});
it("does not fall back to removed response aliases", () => {
const response = UnlockPrivateResponse.fromJson({
unlocked: true,
message_id: "legacy-message",
reply: "Legacy reply",
audio_url: "https://example.com/legacy.mp3",
});
expect(response.messageId).toBe("");
expect(response.content).toBe("");
expect(response.audioUrl).toBe("");
expect(response.toJson()).not.toHaveProperty("message_id");
expect(response.toJson()).not.toHaveProperty("reply");
expect(response.toJson()).not.toHaveProperty("audio_url");
});
});
@@ -4,7 +4,7 @@ import {
type UnlockPrivateResponseData,
type UnlockPrivateResponseInput,
} from "@/data/schemas/chat/response/unlock_private_response";
import type { ChatImageData, ChatLockType } from "@/data/schemas/chat";
import type { ChatImageData } from "@/data/schemas/chat";
/**
* 单条历史付费 / 私密消息解锁响应 DTO。
@@ -12,8 +12,6 @@ import type { ChatImageData, ChatLockType } from "@/data/schemas/chat";
export class UnlockPrivateResponse {
declare readonly unlocked: boolean;
declare readonly messageId: string;
declare readonly clientLockId: string | null;
declare readonly lockType: ChatLockType | null;
declare readonly content: string;
declare readonly audioUrl: string;
declare readonly image: ChatImageData;
@@ -14,30 +14,20 @@ import { ChatLockTypeSchema } from "../chat_lock_type";
*/
export const UnlockPrivateReasonSchema = stringOrEmpty;
export const UnlockPrivateResponseSchema = z
.object({
unlocked: booleanOrFalse,
content: stringOrEmpty,
reply: stringOrEmpty,
messageId: stringOrEmpty,
message_id: stringOrEmpty,
clientLockId: stringOrNull,
lockType: ChatLockTypeSchema.nullable().default(null),
audioUrl: stringOrEmpty,
audio_url: stringOrEmpty,
image: ChatImageSchema,
reason: UnlockPrivateReasonSchema,
creditBalance: numberOrZero,
creditsCharged: numberOrZero,
requiredCredits: numberOrZero,
shortfallCredits: numberOrZero,
})
.transform(({ reply, message_id, audio_url, ...data }) => ({
...data,
content: data.content || reply,
messageId: data.messageId || message_id,
audioUrl: data.audioUrl || audio_url,
}));
export const UnlockPrivateResponseSchema = z.object({
unlocked: booleanOrFalse,
content: stringOrEmpty,
messageId: stringOrEmpty,
clientLockId: stringOrNull,
lockType: ChatLockTypeSchema.nullable().default(null),
audioUrl: stringOrEmpty,
image: ChatImageSchema,
reason: UnlockPrivateReasonSchema,
creditBalance: numberOrZero,
creditsCharged: numberOrZero,
requiredCredits: numberOrZero,
shortfallCredits: numberOrZero,
});
export type UnlockPrivateReason = z.output<typeof UnlockPrivateReasonSchema>;
export type UnlockPrivateResponseInput = z.input<