恢复后台灰度发布控制面
Project CI / Frontend tests (push) Successful in 29s
Project CI / Repository checks (push) Successful in 39s
Project CI / Native shell tests (push) Successful in 2m10s
Project CI / Backend tests (push) Failing after 2m50s

恢复灰度发布导航、权限、前端契约与页面编译链
解除灰度页面对旧创作入口配置的依赖
补充路由、API、页面回归测试及当前架构文档
This commit is contained in:
2026-07-23 14:23:05 +08:00
parent a7d492eea6
commit b72e2163d7
17 changed files with 163 additions and 138 deletions
@@ -3,11 +3,13 @@ import { afterEach, expect, test, vi } from 'vitest';
import {
createAdminAccount,
executeAdminRechargeRefund,
getAdminFeatureGateConfig,
getAdminUserDetail,
listAdminRechargeOrders,
resolveAdminRechargeRefundManualReview,
updateAdminAccount,
uploadAdminEditorShowcaseCampaignImage,
upsertAdminFeatureGateConfig,
} from './adminApiClient';
afterEach(() => {
@@ -57,6 +59,52 @@ test('后台账号创建和更新携带 owner 会话与 Tab 权限', async () =>
);
});
test('灰度配置读写只使用通用 feature-gates 管理接口', async () => {
const fetchMock = vi.fn().mockImplementation(() =>
Promise.resolve(
new Response(JSON.stringify({ gates: [] }), {
status: 200,
headers: { 'content-type': 'application/json' },
}),
),
);
vi.stubGlobal('fetch', fetchMock);
await getAdminFeatureGateConfig('gray-token');
await upsertAdminFeatureGateConfig('gray-token', {
gateKey: 'image-editor:agent-sidebar',
enabled: true,
rolloutPercent: 25,
allowUserIds: ['user-1'],
allowUserTags: ['beta'],
denyUserIds: ['blocked-1'],
description: '画布 Agent 入口灰度',
});
expect(fetchMock.mock.calls[0]?.[0]).toBe('/admin/api/feature-gates');
expect(fetchMock.mock.calls[0]?.[1]).toEqual(
expect.objectContaining({
headers: expect.objectContaining({ Authorization: 'Bearer gray-token' }),
}),
);
expect(fetchMock.mock.calls[1]?.[0]).toBe('/admin/api/feature-gates');
expect(fetchMock.mock.calls[1]?.[1]).toEqual(
expect.objectContaining({
method: 'PUT',
headers: expect.objectContaining({ Authorization: 'Bearer gray-token' }),
body: JSON.stringify({
gateKey: 'image-editor:agent-sidebar',
enabled: true,
rolloutPercent: 25,
allowUserIds: ['user-1'],
allowUserTags: ['beta'],
denyUserIds: ['blocked-1'],
description: '画布 Agent 入口灰度',
}),
}),
);
});
test('活动卡图片上传成功后先确认正式私有对象再返回图片引用', async () => {
const closeBitmap = vi.fn();
vi.stubGlobal(
+19
View File
@@ -23,6 +23,7 @@ import type {
AdminEditorShowcaseListQuery,
AdminEditorShowcaseListResponse,
AdminEditorShowcaseReviewRequest,
AdminFeatureGateConfigResponse,
AdminLoginResponse,
AdminMeResponse,
AdminOverviewResponse,
@@ -41,6 +42,7 @@ import type {
AdminUpdateAccountResponse,
AdminUploadedEditorShowcaseCampaignImage,
AdminUpsertEditorShowcaseCampaignRequest,
AdminUpsertFeatureGateConfigRequest,
AdminUpsertProfileInviteCodeRequest,
AdminUpsertProfileRechargeProductRequest,
AdminUpsertProfileRedeemCodeRequest,
@@ -267,6 +269,23 @@ export function listAdminTrackingEventKeys(token: string) {
);
}
export function getAdminFeatureGateConfig(token: string) {
return request<AdminFeatureGateConfigResponse>('/admin/api/feature-gates', {
token,
});
}
export function upsertAdminFeatureGateConfig(
token: string,
payload: AdminUpsertFeatureGateConfigRequest,
) {
return request<AdminFeatureGateConfigResponse>('/admin/api/feature-gates', {
method: 'PUT',
token,
body: payload,
});
}
export function getAdminEditorGenerationPricing(token: string) {
return request<EditorGenerationPricingConfigPayload>(
'/admin/api/editor-generation-pricing',
+20
View File
@@ -286,6 +286,26 @@ export interface AdminTrackingEventListQuery {
exportAll?: boolean;
}
export interface AdminFeatureGateConfigPayload {
gateKey: string;
enabled: boolean;
rolloutPercent: number;
allowUserIds: string[];
allowUserTags: string[];
denyUserIds: string[];
description: string;
updatedAt: string;
}
export interface AdminFeatureGateConfigResponse {
gates: AdminFeatureGateConfigPayload[];
}
export type AdminUpsertFeatureGateConfigRequest = Omit<
AdminFeatureGateConfigPayload,
'updatedAt'
>;
/** 图片画布生成模型泥点定价配置。 */
export type EditorGenerationPricingUnitPayload = 'perGeneration' | 'perSecond';