diff --git a/contracts/admin-openapi.json b/contracts/admin-openapi.json index f4423aa..bd5b8ac 100644 --- a/contracts/admin-openapi.json +++ b/contracts/admin-openapi.json @@ -15,7 +15,17 @@ "operationId": "listAchievementDefinitions", "responses": { "200": { - "$ref": "#/components/responses/EmptyResponse" + "description": "批量创建的礼物", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object" + } + } + } + } } }, "x-permission": "achievement:view", @@ -25,7 +35,17 @@ "operationId": "createAchievementDefinition", "responses": { "200": { - "$ref": "#/components/responses/EmptyResponse" + "description": "批量删除的资源", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object" + } + } + } + } } }, "x-permission": "achievement:create", @@ -2353,6 +2373,21 @@ "x-permissions": ["gift:create"] } }, + "/admin/gifts/batch": { + "post": { + "operationId": "batchCreateGifts", + "requestBody": { + "$ref": "#/components/requestBodies/BatchCreateGiftsRequest" + }, + "responses": { + "200": { + "$ref": "#/components/responses/EmptyResponse" + } + }, + "x-permission": "gift:create", + "x-permissions": ["gift:create"] + } + }, "/admin/gifts/{gift_id}": { "put": { "operationId": "updateGift", @@ -4205,6 +4240,21 @@ "x-permissions": ["resource:create"] } }, + "/admin/resources/batch-delete": { + "post": { + "operationId": "batchDeleteResources", + "requestBody": { + "$ref": "#/components/requestBodies/BatchDeleteResourcesRequest" + }, + "responses": { + "200": { + "$ref": "#/components/responses/EmptyResponse" + } + }, + "x-permission": "resource:delete", + "x-permissions": ["resource:delete"] + } + }, "/admin/resources/{resource_id}": { "get": { "operationId": "getResource", @@ -5955,6 +6005,42 @@ } }, "requestBodies": { + "BatchCreateGiftsRequest": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["items"], + "properties": { + "items": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + } + } + }, + "BatchDeleteResourcesRequest": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["resourceIds"], + "properties": { + "resourceIds": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + } + } + }, "BatchStatusRequest": { "content": { "application/json": { diff --git a/src/features/resources/api.test.ts b/src/features/resources/api.test.ts index 489d8c8..04e36a9 100644 --- a/src/features/resources/api.test.ts +++ b/src/features/resources/api.test.ts @@ -1,6 +1,8 @@ import { afterEach, expect, test, vi } from "vitest"; import { setAccessToken } from "@/shared/api/request"; import { + batchCreateGifts, + batchDeleteResources, createEmojiPack, createGift, createResource, @@ -85,6 +87,7 @@ test("resource APIs use generated admin paths", async () => { status: "active", }); await deleteResource(11); + await batchDeleteResources([11, 12]); await listGifts({ page: 1, page_size: 10, region_id: 1001 }); await createGift({ chargeAssetType: "COIN", @@ -104,6 +107,26 @@ test("resource APIs use generated admin paths", async () => { sortOrder: 0, status: "active", }); + await batchCreateGifts([ + { + chargeAssetType: "COIN", + coinPrice: 20, + cpRelationType: "", + effectiveAtMs: 1777766400000, + effectiveFromMs: 0, + effectiveToMs: 0, + effectTypes: ["animation"], + giftId: "ring", + giftTypeCode: "normal", + name: "Ring", + presentationJson: "{}", + priceVersion: "default", + regionIds: [0], + resourceId: 12, + sortOrder: 1, + status: "active", + }, + ]); await enableResource(11); await disableResource(11); await createResourceGroup({ @@ -180,23 +203,25 @@ test("resource APIs use generated admin paths", async () => { const [createUrl, createInit] = vi.mocked(fetch).mock.calls[1]; const [updateUrl, updateInit] = vi.mocked(fetch).mock.calls[2]; const [deleteResourceUrl, deleteResourceInit] = vi.mocked(fetch).mock.calls[3]; - const [giftListUrl, giftListInit] = vi.mocked(fetch).mock.calls[4]; - const [createGiftUrl, createGiftInit] = vi.mocked(fetch).mock.calls[5]; - const [enableUrl, enableInit] = vi.mocked(fetch).mock.calls[6]; - const [disableUrl, disableInit] = vi.mocked(fetch).mock.calls[7]; - const [createGroupUrl, createGroupInit] = vi.mocked(fetch).mock.calls[8]; - const [updateGroupUrl, updateGroupInit] = vi.mocked(fetch).mock.calls[9]; - const [enableGroupUrl, enableGroupInit] = vi.mocked(fetch).mock.calls[10]; - const [disableGroupUrl, disableGroupInit] = vi.mocked(fetch).mock.calls[11]; - const [updateGiftUrl, updateGiftInit] = vi.mocked(fetch).mock.calls[12]; - const [enableGiftUrl, enableGiftInit] = vi.mocked(fetch).mock.calls[13]; - const [disableGiftUrl, disableGiftInit] = vi.mocked(fetch).mock.calls[14]; - const [deleteGiftUrl, deleteGiftInit] = vi.mocked(fetch).mock.calls[15]; - const [grantListUrl, grantListInit] = vi.mocked(fetch).mock.calls[16]; - const [grantTargetUrl, grantTargetInit] = vi.mocked(fetch).mock.calls[17]; - const [grantResourceUrl, grantResourceInit] = vi.mocked(fetch).mock.calls[18]; - const [grantGroupUrl, grantGroupInit] = vi.mocked(fetch).mock.calls[19]; - const [revokeGrantUrl, revokeGrantInit] = vi.mocked(fetch).mock.calls[20]; + const [batchDeleteResourceUrl, batchDeleteResourceInit] = vi.mocked(fetch).mock.calls[4]; + const [giftListUrl, giftListInit] = vi.mocked(fetch).mock.calls[5]; + const [createGiftUrl, createGiftInit] = vi.mocked(fetch).mock.calls[6]; + const [batchCreateGiftUrl, batchCreateGiftInit] = vi.mocked(fetch).mock.calls[7]; + const [enableUrl, enableInit] = vi.mocked(fetch).mock.calls[8]; + const [disableUrl, disableInit] = vi.mocked(fetch).mock.calls[9]; + const [createGroupUrl, createGroupInit] = vi.mocked(fetch).mock.calls[10]; + const [updateGroupUrl, updateGroupInit] = vi.mocked(fetch).mock.calls[11]; + const [enableGroupUrl, enableGroupInit] = vi.mocked(fetch).mock.calls[12]; + const [disableGroupUrl, disableGroupInit] = vi.mocked(fetch).mock.calls[13]; + const [updateGiftUrl, updateGiftInit] = vi.mocked(fetch).mock.calls[14]; + const [enableGiftUrl, enableGiftInit] = vi.mocked(fetch).mock.calls[15]; + const [disableGiftUrl, disableGiftInit] = vi.mocked(fetch).mock.calls[16]; + const [deleteGiftUrl, deleteGiftInit] = vi.mocked(fetch).mock.calls[17]; + const [grantListUrl, grantListInit] = vi.mocked(fetch).mock.calls[18]; + const [grantTargetUrl, grantTargetInit] = vi.mocked(fetch).mock.calls[19]; + const [grantResourceUrl, grantResourceInit] = vi.mocked(fetch).mock.calls[20]; + const [grantGroupUrl, grantGroupInit] = vi.mocked(fetch).mock.calls[21]; + const [revokeGrantUrl, revokeGrantInit] = vi.mocked(fetch).mock.calls[22]; expect(String(listUrl)).toContain("/api/v1/admin/resources?"); expect(String(listUrl)).toContain("resource_type=gift"); @@ -222,12 +247,18 @@ test("resource APIs use generated admin paths", async () => { }); expect(String(deleteResourceUrl)).toContain("/api/v1/admin/resources/11"); expect(deleteResourceInit?.method).toBe("DELETE"); + expect(String(batchDeleteResourceUrl)).toContain("/api/v1/admin/resources/batch-delete"); + expect(batchDeleteResourceInit?.method).toBe("POST"); + expect(JSON.parse(String(batchDeleteResourceInit?.body))).toEqual({ resourceIds: [11, 12] }); expect(String(giftListUrl)).toContain("/api/v1/admin/gifts?"); expect(String(giftListUrl)).toContain("region_id=1001"); expect(giftListInit?.method).toBe("GET"); expect(String(createGiftUrl)).toContain("/api/v1/admin/gifts"); expect(createGiftInit?.method).toBe("POST"); expect(JSON.parse(String(createGiftInit?.body))).toMatchObject({ giftId: "rose", resourceId: 11 }); + expect(String(batchCreateGiftUrl)).toContain("/api/v1/admin/gifts/batch"); + expect(batchCreateGiftInit?.method).toBe("POST"); + expect(JSON.parse(String(batchCreateGiftInit?.body))).toMatchObject({ items: [{ giftId: "ring", resourceId: 12 }] }); expect(String(enableUrl)).toContain("/api/v1/admin/resources/11/enable"); expect(enableInit?.method).toBe("POST"); expect(String(disableUrl)).toContain("/api/v1/admin/resources/11/disable"); diff --git a/src/features/resources/api.ts b/src/features/resources/api.ts index 28bd42c..f407fab 100644 --- a/src/features/resources/api.ts +++ b/src/features/resources/api.ts @@ -224,6 +224,10 @@ export interface GiftPayload { regionIds: number[]; } +export interface BatchCreateGiftsPayload { + items: GiftPayload[]; +} + export interface GiftTypePayload { displayName: string; tabName: string; @@ -337,6 +341,10 @@ export interface ResourceShopItemsPayload { items: ResourceShopItemPayload[]; } +export interface BatchDeleteResourcesPayload { + resourceIds: EntityId[]; +} + export function listResources(query: PageQuery = {}): Promise> { const endpoint = API_ENDPOINTS.listResources; return apiRequest>(apiEndpointPath(API_OPERATIONS.listResources), { @@ -371,6 +379,17 @@ export function deleteResource(resourceId: EntityId): Promise { }); } +export function batchDeleteResources(resourceIds: EntityId[]): Promise { + const endpoint = API_ENDPOINTS.batchDeleteResources; + return apiRequest( + apiEndpointPath(API_OPERATIONS.batchDeleteResources), + { + body: { resourceIds }, + method: endpoint.method, + }, + ); +} + export function updateResourceMp4Layouts(items: ResourceMp4LayoutUpdatePayload[]): Promise { return apiRequest("/v1/admin/resources/mp4-layouts/batch", { body: { items }, @@ -548,6 +567,14 @@ export function createGift(payload: GiftPayload): Promise { }); } +export function batchCreateGifts(payloads: GiftPayload[]): Promise { + const endpoint = API_ENDPOINTS.batchCreateGifts; + return apiRequest(apiEndpointPath(API_OPERATIONS.batchCreateGifts), { + body: { items: payloads }, + method: endpoint.method, + }); +} + export function updateGift(giftId: string, payload: GiftPayload): Promise { const endpoint = API_ENDPOINTS.updateGift; return apiRequest(apiEndpointPath(API_OPERATIONS.updateGift, { gift_id: giftId }), { diff --git a/src/features/resources/hooks/useGiftListPageBatchCreate.test.jsx b/src/features/resources/hooks/useGiftListPageBatchCreate.test.jsx new file mode 100644 index 0000000..c78bd04 --- /dev/null +++ b/src/features/resources/hooks/useGiftListPageBatchCreate.test.jsx @@ -0,0 +1,117 @@ +import { act, renderHook, waitFor } from "@testing-library/react"; +import { afterEach, beforeEach, expect, test, vi } from "vitest"; +import { useGiftListPage } from "./useResourcePages.js"; + +const mocks = vi.hoisted(() => ({ + batchCreateGifts: vi.fn(), + can: vi.fn(), + createGift: vi.fn(), + listGifts: vi.fn(), + listGiftTypes: vi.fn(), + listResources: vi.fn(), + reload: vi.fn(), + showToast: vi.fn(), + usePaginatedQuery: vi.fn(), +})); + +vi.mock("@/app/auth/AuthProvider.jsx", () => ({ + useAuth: () => ({ can: mocks.can }), +})); + +vi.mock("@/shared/hooks/usePaginatedQuery.js", () => ({ + usePaginatedQuery: mocks.usePaginatedQuery, +})); + +vi.mock("@/shared/hooks/useRegionOptions.js", () => ({ + useRegionOptions: () => ({ loadingRegions: false, regionOptions: [{ label: "全球", value: "0" }] }), +})); + +vi.mock("@/shared/ui/ConfirmProvider.jsx", () => ({ + useConfirm: () => vi.fn(), +})); + +vi.mock("@/shared/ui/ToastProvider.jsx", () => ({ + useToast: () => ({ showToast: mocks.showToast }), +})); + +vi.mock("@/features/resources/api", async (importOriginal) => ({ + ...(await importOriginal()), + batchCreateGifts: mocks.batchCreateGifts, + createGift: mocks.createGift, + listGifts: mocks.listGifts, + listGiftTypes: mocks.listGiftTypes, + listResources: mocks.listResources, +})); + +beforeEach(() => { + mocks.can.mockReturnValue(true); + mocks.batchCreateGifts.mockResolvedValue([]); + mocks.createGift.mockResolvedValue({}); + mocks.listGifts.mockResolvedValue({ items: [], page: 1, pageSize: 100, total: 0 }); + mocks.listGiftTypes.mockResolvedValue([]); + mocks.listResources.mockResolvedValue({ items: [], page: 1, pageSize: 100, total: 0 }); + mocks.reload.mockResolvedValue(undefined); + mocks.usePaginatedQuery.mockReturnValue({ + data: { items: [], page: 1, pageSize: 50, total: 0 }, + error: null, + loading: false, + reload: mocks.reload, + }); +}); + +afterEach(() => { + vi.clearAllMocks(); +}); + +test("creating multiple gift drafts calls the batch create endpoint once", async () => { + const { result } = renderHook(() => useGiftListPage()); + + await act(async () => { + result.current.openCreateGift(); + }); + await waitFor(() => expect(result.current.activeAction).toBe("create")); + + await act(async () => { + result.current.setForm({ + ...giftDraft({ giftId: "rose", name: "Rose", resourceId: "11" }), + items: [ + giftDraft({ giftId: "rose", name: "Rose", resourceId: "11" }), + giftDraft({ giftId: "ring", name: "Ring", resourceId: "12" }), + ], + resourceId: "11", + }); + }); + + await act(async () => { + await result.current.submitGift({ preventDefault: vi.fn() }); + }); + + expect(mocks.batchCreateGifts).toHaveBeenCalledTimes(1); + expect(mocks.batchCreateGifts).toHaveBeenCalledWith([ + expect.objectContaining({ giftId: "rose", name: "Rose", resourceId: 11 }), + expect.objectContaining({ giftId: "ring", name: "Ring", resourceId: 12 }), + ]); + expect(mocks.createGift).not.toHaveBeenCalled(); + expect(mocks.reload).toHaveBeenCalledTimes(1); +}); + +function giftDraft(patch = {}) { + return { + chargeAssetType: "COIN", + coinPrice: "10", + cpRelationType: "", + effectTypes: ["animation"], + effectiveFrom: "", + effectiveTo: "", + enabled: true, + giftId: "gift", + giftTypeCode: "normal", + name: "Gift", + presentationJson: "{}", + priceVersion: "default", + regionIds: ["0"], + resourceId: "1", + sortOrder: "0", + ...patch, + }; +} diff --git a/src/features/resources/hooks/useResourceListPageDelete.test.jsx b/src/features/resources/hooks/useResourceListPageDelete.test.jsx index 418777a..c49862e 100644 --- a/src/features/resources/hooks/useResourceListPageDelete.test.jsx +++ b/src/features/resources/hooks/useResourceListPageDelete.test.jsx @@ -3,6 +3,7 @@ import { afterEach, beforeEach, expect, test, vi } from "vitest"; import { useResourceListPage } from "./useResourcePages.js"; const mocks = vi.hoisted(() => ({ + batchDeleteResources: vi.fn(), can: vi.fn(), confirm: vi.fn(), deleteResource: vi.fn(), @@ -29,12 +30,14 @@ vi.mock("@/shared/ui/ToastProvider.jsx", () => ({ vi.mock("@/features/resources/api", async (importOriginal) => ({ ...(await importOriginal()), + batchDeleteResources: mocks.batchDeleteResources, deleteResource: mocks.deleteResource, })); beforeEach(() => { mocks.can.mockReturnValue(true); mocks.confirm.mockResolvedValue(true); + mocks.batchDeleteResources.mockResolvedValue([]); mocks.deleteResource.mockResolvedValue({}); mocks.reload.mockResolvedValue(undefined); mocks.usePaginatedQuery.mockReturnValue({ @@ -76,14 +79,14 @@ test("batch resource delete hides successful rows without reloading the list", a }); expect(deletedResourceIds).toEqual([11, 12]); - expect(mocks.deleteResource).toHaveBeenNthCalledWith(1, 11); - expect(mocks.deleteResource).toHaveBeenNthCalledWith(2, 12); + expect(mocks.batchDeleteResources).toHaveBeenCalledWith([11, 12]); + expect(mocks.deleteResource).not.toHaveBeenCalled(); expect(mocks.reload).not.toHaveBeenCalled(); expect(result.current.data.items).toEqual([]); expect(mocks.showToast).toHaveBeenCalledWith("已删除 2 个资源", "success"); }); -test("batch resource delete continues after an item fails and hides successful rows", async () => { +test("failed batch resource delete keeps all rows visible", async () => { mocks.usePaginatedQuery.mockReturnValue({ data: { items: [ @@ -99,9 +102,7 @@ test("batch resource delete continues after an item fails and hides successful r loading: false, reload: mocks.reload, }); - mocks.deleteResource.mockImplementation((resourceId) => - resourceId === 12 ? Promise.reject(new Error("delete failed")) : Promise.resolve({}), - ); + mocks.batchDeleteResources.mockRejectedValueOnce(new Error("delete failed")); const { result } = renderHook(() => useResourceListPage()); let deletedResourceIds = []; @@ -113,12 +114,11 @@ test("batch resource delete continues after an item fails and hides successful r ]); }); - expect(deletedResourceIds).toEqual([11, 13]); - expect(mocks.deleteResource).toHaveBeenNthCalledWith(1, 11); - expect(mocks.deleteResource).toHaveBeenNthCalledWith(2, 12); - expect(mocks.deleteResource).toHaveBeenNthCalledWith(3, 13); - expect(result.current.data.items.map((resource) => resource.resourceId)).toEqual([12]); - expect(mocks.showToast).toHaveBeenCalledWith("已删除 2 个资源,1 个删除失败:delete failed", "error"); + expect(deletedResourceIds).toEqual([]); + expect(mocks.batchDeleteResources).toHaveBeenCalledWith([11, 12, 13]); + expect(mocks.deleteResource).not.toHaveBeenCalled(); + expect(result.current.data.items.map((resource) => resource.resourceId)).toEqual([11, 12, 13]); + expect(mocks.showToast).toHaveBeenCalledWith("delete failed", "error"); }); test("failed resource delete keeps the row visible", async () => { diff --git a/src/features/resources/hooks/useResourcePages.js b/src/features/resources/hooks/useResourcePages.js index 9b49ac6..18ed122 100644 --- a/src/features/resources/hooks/useResourcePages.js +++ b/src/features/resources/hooks/useResourcePages.js @@ -6,6 +6,8 @@ import { useConfirm } from "@/shared/ui/ConfirmProvider.jsx"; import { useToast } from "@/shared/ui/ToastProvider.jsx"; import { emptyUserIdentityFilter, userIdentityFilterParams } from "@/shared/ui/userIdentityFilter.js"; import { + batchCreateGifts, + batchDeleteResources as requestBatchDeleteResources, createEmojiPack, createGift, createResource, @@ -593,32 +595,23 @@ export function useResourceListPage() { } const deletedResourceIds = []; - const failedDeletes = []; setLoadingAction( targetResources.length > 1 ? "resource-delete-batch" : `resource-delete-${targetResources[0].resourceId}`, ); try { - for (const resource of targetResources) { - try { - await requestDeleteResource(resource.resourceId); - deletedResourceIds.push(resource.resourceId); - } catch (err) { - failedDeletes.push({ err, resource }); - } + if (targetResources.length > 1) { + await requestBatchDeleteResources(targetResources.map((resource) => resource.resourceId)); + deletedResourceIds.push(...targetResources.map((resource) => resource.resourceId)); + } else { + await requestDeleteResource(targetResources[0].resourceId); + deletedResourceIds.push(targetResources[0].resourceId); } hideResourceRows(deletedResourceIds); - if (failedDeletes.length) { - const firstError = failedDeletes[0].err?.message || "删除资源失败"; - showToast( - deletedResourceIds.length - ? `已删除 ${deletedResourceIds.length} 个资源,${failedDeletes.length} 个删除失败:${firstError}` - : firstError, - "error", - ); - return deletedResourceIds; - } showToast(targetResources.length > 1 ? `已删除 ${deletedResourceIds.length} 个资源` : "资源已删除", "success"); return deletedResourceIds; + } catch (err) { + showToast(err.message || "删除资源失败", "error"); + return []; } finally { setLoadingAction(""); } @@ -1510,9 +1503,12 @@ export function useGiftListPage() { draft, payload: buildGiftPayload(parseForm(giftFormSchema, draft)), })); - for (const { draft, payload } of payloads) { - await createGift(payload); - createdDrafts.push(draft); + if (payloads.length > 1) { + await batchCreateGifts(payloads.map(({ payload }) => payload)); + createdDrafts.push(...payloads.map(({ draft }) => draft)); + } else { + await createGift(payloads[0].payload); + createdDrafts.push(payloads[0].draft); } } showToast(editing ? "礼物已更新" : `已创建 ${createDrafts.length} 个礼物`, "success"); diff --git a/src/shared/api/generated/endpoints.ts b/src/shared/api/generated/endpoints.ts index 014f5f6..889eb78 100644 --- a/src/shared/api/generated/endpoints.ts +++ b/src/shared/api/generated/endpoints.ts @@ -26,6 +26,8 @@ export const API_OPERATIONS = { appSetPassword: "appSetPassword", appUnbanUser: "appUnbanUser", appUpdateUser: "appUpdateUser", + batchCreateGifts: "batchCreateGifts", + batchDeleteResources: "batchDeleteResources", batchUpdateUserStatus: "batchUpdateUserStatus", cancelRoomPin: "cancelRoomPin", changePassword: "changePassword", @@ -436,6 +438,20 @@ export const API_ENDPOINTS: Record = { permission: "app-user:update", permissions: ["app-user:update"] }, + batchCreateGifts: { + method: "POST", + operationId: API_OPERATIONS.batchCreateGifts, + path: "/v1/admin/gifts/batch", + permission: "gift:create", + permissions: ["gift:create"] + }, + batchDeleteResources: { + method: "POST", + operationId: API_OPERATIONS.batchDeleteResources, + path: "/v1/admin/resources/batch-delete", + permission: "resource:delete", + permissions: ["resource:delete"] + }, batchUpdateUserStatus: { method: "POST", operationId: API_OPERATIONS.batchUpdateUserStatus, diff --git a/src/shared/api/generated/schema.d.ts b/src/shared/api/generated/schema.d.ts index a7005f7..0319813 100644 --- a/src/shared/api/generated/schema.d.ts +++ b/src/shared/api/generated/schema.d.ts @@ -1620,6 +1620,22 @@ export interface paths { patch?: never; trace?: never; }; + "/admin/gifts/batch": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["batchCreateGifts"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/admin/gifts/{gift_id}": { parameters: { query?: never; @@ -2660,6 +2676,22 @@ export interface paths { patch?: never; trace?: never; }; + "/admin/resources/batch-delete": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["batchDeleteResources"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/admin/resources/{resource_id}": { parameters: { query?: never; @@ -4601,6 +4633,20 @@ export interface components { Status: string; }; requestBodies: { + BatchCreateGiftsRequest: { + content: { + "application/json": { + items: Record[]; + }; + }; + }; + BatchDeleteResourcesRequest: { + content: { + "application/json": { + resourceIds: number[]; + }; + }; + }; BatchStatusRequest: { content: { "application/json": { @@ -4729,7 +4775,15 @@ export interface operations { }; requestBody?: never; responses: { - 200: components["responses"]["EmptyResponse"]; + /** @description 批量创建的礼物 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": Record[]; + }; + }; }; }; createAchievementDefinition: { @@ -4741,7 +4795,15 @@ export interface operations { }; requestBody?: never; responses: { - 200: components["responses"]["EmptyResponse"]; + /** @description 批量删除的资源 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": Record[]; + }; + }; }; }; updateAchievementDefinition: { @@ -6631,6 +6693,18 @@ export interface operations { 200: components["responses"]["EmptyResponse"]; }; }; + batchCreateGifts: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: components["requestBodies"]["BatchCreateGiftsRequest"]; + responses: { + 200: components["responses"]["EmptyResponse"]; + }; + }; updateGift: { parameters: { query?: never; @@ -7873,6 +7947,18 @@ export interface operations { 200: components["responses"]["EmptyResponse"]; }; }; + batchDeleteResources: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: components["requestBodies"]["BatchDeleteResourcesRequest"]; + responses: { + 200: components["responses"]["EmptyResponse"]; + }; + }; getResource: { parameters: { query?: never;