From d9db2f5dcef1accfe271917b49286a5cb3de24e0 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 1 Aug 2026 15:11:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=8D=95=E5=B9=B3=E5=8F=B0=E7=AD=89?= =?UTF-8?q?=E7=BA=A7=20CRUD=20=E5=AE=8C=E5=96=84=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E9=BB=98=E8=AE=A4=E5=88=86=E7=B1=BB=E5=81=9C=E7=94=A8?= =?UTF-8?q?=E8=A2=AB=E9=87=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 等级面板支持编辑、启用/停用切换、删除(默认等级与使用中等级受保护) - 新增等级删除接口及使用量校验 - 修复默认分类停用后被 ensureWorkerPlatformDefaults 重置为启用的问题,默认分类仅不存在时创建 - 分类操作后改为显式刷新列表 --- .../src/repositories/worker-platform-repo.ts | 16 +++ .../src/routes/admin/worker-platform.ts | 17 +++ .../worker-platform-service.ts | 45 ++++++- .../pages/admin/AdminWorkerPlatformPage.tsx | 121 ++++++++++++++++-- .../src/services/admin/worker-platform.ts | 7 + 5 files changed, 187 insertions(+), 19 deletions(-) diff --git a/apps/backend/src/repositories/worker-platform-repo.ts b/apps/backend/src/repositories/worker-platform-repo.ts index e727ff53..360878e6 100644 --- a/apps/backend/src/repositories/worker-platform-repo.ts +++ b/apps/backend/src/repositories/worker-platform-repo.ts @@ -323,6 +323,22 @@ export async function upsertWorkerLevel(input: { return result.rows[0] || null } +export async function countWorkerLevelUsages(levelId: number | string): Promise { + const result = await query<{ total: number }>( + 'SELECT COUNT(*)::int AS total FROM worker_users WHERE level_id = $1', + [Number(levelId)], + ) + return Number(result.rows[0]?.total || 0) +} + +export async function deleteWorkerLevel(levelId: number | string): Promise<{ deleted: boolean }> { + const result = await query<{ id: number }>( + 'DELETE FROM worker_levels WHERE id = $1 RETURNING id', + [Number(levelId)], + ) + return { deleted: Boolean(result.rows[0]) } +} + export async function getWorkerUserByUsername(username: string): Promise { const result = await query( `${WORKER_USER_SELECT} WHERE wu.username = $1 LIMIT 1`, diff --git a/apps/backend/src/routes/admin/worker-platform.ts b/apps/backend/src/routes/admin/worker-platform.ts index 1216d9f4..4d986113 100644 --- a/apps/backend/src/routes/admin/worker-platform.ts +++ b/apps/backend/src/routes/admin/worker-platform.ts @@ -5,6 +5,7 @@ import { creditAdminWorkerWallet, createAdminMockWorkOrder, deleteAdminWorkCategory, + deleteAdminWorkerLevel, getAdminWorkerFinanceConfig, getAdminWorkerPlatformSummary, listAdminWorkerFinanceRequests, @@ -65,6 +66,22 @@ router.post( }), ) +router.delete( + '/worker-platform/levels/:levelId', + requireAdminRoles(['admin']), + createJsonHandler((req) => deleteAdminWorkerLevel(String(req.params.levelId || '')), { + successMessage: '接单等级已删除', + errorMessage: '删除接单等级失败', + scope: '[admin/worker-platform/levels/:levelId]', + audit: (_req, data) => ({ + action: 'worker_level_deleted', + targetType: 'worker_level', + targetId: String(_req.params.levelId || ''), + data: data && typeof data === 'object' ? (data as Record) : {}, + }), + }), +) + router.get( '/worker-platform/categories', requireAdminRoles(['admin', 'operator', 'support']), diff --git a/apps/backend/src/services/worker-platform/worker-platform-service.ts b/apps/backend/src/services/worker-platform/worker-platform-service.ts index d159deb4..c83e88f7 100644 --- a/apps/backend/src/services/worker-platform/worker-platform-service.ts +++ b/apps/backend/src/services/worker-platform/worker-platform-service.ts @@ -10,14 +10,17 @@ import { countWorkerAcceptedOrders, countWorkerActiveOrders, countWorkCategoryUsages, + countWorkerLevelUsages, createWorkOrder, createWorkOrderEvent, createWorkerFinanceRequest, createWorkerUser, deleteWorkCategory, + deleteWorkerLevel, getWorkerFinanceRequestById, getWorkerFinanceRequestSummary, getWorkCategoryById, + getWorkCategoryByKey, getWorkerLevelById, getWorkerLevelByKey, getWorkerUserByInviteCode, @@ -110,13 +113,16 @@ export async function ensureWorkerPlatformDefaults() { }), now, })) - const category = await upsertWorkCategory({ - categoryKey: DEFAULT_CATEGORY_KEY, - name: '默认分类', - sortOrder: 100, - status: 'active', - now, - }) + const existingCategory = await getWorkCategoryByKey(DEFAULT_CATEGORY_KEY) + const category = + existingCategory || + (await upsertWorkCategory({ + categoryKey: DEFAULT_CATEGORY_KEY, + name: '默认分类', + sortOrder: 100, + status: 'active', + now, + })) return { level, category } } @@ -933,6 +939,31 @@ export async function saveAdminWorkerLevel(payload: JsonObject = {}) { return { level: mapWorkerLevel(level) } } +export async function deleteAdminWorkerLevel(levelId: number | string) { + const level = await getWorkerLevelById(Number(levelId)) + if (!level) { + throw createHttpError('等级不存在', { + statusCode: 404, + errorCode: 'worker_level_not_found', + }) + } + if (level.level_key === DEFAULT_LEVEL_KEY) { + throw createHttpError('默认等级(VIP1)不可删除', { + statusCode: 409, + errorCode: 'worker_level_default_protected', + }) + } + const usageCount = await countWorkerLevelUsages(Number(levelId)) + if (usageCount > 0) { + throw createHttpError(`该等级正在被 ${usageCount} 名打手使用,请先调整打手等级后再删除`, { + statusCode: 409, + errorCode: 'worker_level_in_use', + }) + } + const { deleted } = await deleteWorkerLevel(Number(levelId)) + return { deleted } +} + export async function listAdminWorkProductRules(query: JsonObject = {}) { await ensureWorkerPlatformDefaults() const enabledValue = String(query.enabled ?? '').trim() diff --git a/apps/frontend/src/pages/admin/AdminWorkerPlatformPage.tsx b/apps/frontend/src/pages/admin/AdminWorkerPlatformPage.tsx index ef626f77..ca888042 100644 --- a/apps/frontend/src/pages/admin/AdminWorkerPlatformPage.tsx +++ b/apps/frontend/src/pages/admin/AdminWorkerPlatformPage.tsx @@ -40,6 +40,7 @@ import { createAdminMockWorkOrder, creditAdminWorkerWallet, deleteAdminWorkCategory, + deleteAdminWorkerLevel, fetchAdminWorkerFinanceConfig, fetchAdminWorkerFinanceRequests, fetchAdminWorkCategories, @@ -1043,9 +1044,7 @@ function CategoriesPanel() { }) async function refreshCategories() { - await queryClient.invalidateQueries({ - queryKey: ['admin-worker-platform-categories'], - }) + await categoriesQuery.refetch() } function startEdit(category: WorkCategory) { @@ -1880,6 +1879,28 @@ function LevelsPanel() { queryFn: () => fetchAdminWorkerLevels(), }) const [form] = Form.useForm() + const [editingLevel, setEditingLevel] = useState(null) + const [deletingLevel, setDeletingLevel] = useState(null) + + async function refreshLevels() { + await levelsQuery.refetch() + } + + function startEdit(level: WorkerLevel) { + setEditingLevel(level) + form.setFieldsValue({ + levelKey: level.levelKey, + name: level.name, + depositFreeAmount: level.depositFreeAmount, + maxActiveOrders: level.maxActiveOrders, + upgradeThreshold: level.upgradeThreshold, + }) + } + + function cancelEdit() { + setEditingLevel(null) + form.resetFields() + } async function saveLevel(values: { levelKey: string @@ -1890,15 +1911,47 @@ function LevelsPanel() { }) { try { await saveAdminWorkerLevel(values) - message.success('等级已保存') - await queryClient.invalidateQueries({ - queryKey: ['admin-worker-platform-levels'], - }) + message.success(editingLevel ? '等级已更新' : '等级已保存') + cancelEdit() + await refreshLevels() } catch (error) { message.error(error instanceof Error ? error.message : '保存失败') } } + async function toggleLevel(level: WorkerLevel) { + try { + await saveAdminWorkerLevel({ + levelKey: level.levelKey, + name: level.name, + depositFreeAmount: level.depositFreeAmount, + maxActiveOrders: level.maxActiveOrders, + upgradeThreshold: level.upgradeThreshold, + status: level.status === 'active' ? 'disabled' : 'active', + }) + message.success(level.status === 'active' ? '等级已停用' : '等级已启用') + await refreshLevels() + } catch (error) { + message.error(error instanceof Error ? error.message : '操作失败') + } + } + + async function submitDelete() { + if (!deletingLevel) return + try { + await deleteAdminWorkerLevel(deletingLevel.levelId) + message.success('等级已删除') + setDeletingLevel(null) + await refreshLevels() + await queryClient.invalidateQueries({ + queryKey: ['admin-worker-platform-workers'], + }) + } catch (error) { + message.error(error instanceof Error ? error.message : '删除失败') + setDeletingLevel(null) + } + } + const columns: TableColumnsType = [ { title: '等级', dataIndex: 'name' }, { title: '标识', dataIndex: 'levelKey' }, @@ -1908,12 +1961,40 @@ function LevelsPanel() { }, { title: '最大同时接单', dataIndex: 'maxActiveOrders' }, { title: '升级所需累计完成', dataIndex: 'upgradeThreshold' }, - { title: '状态', dataIndex: 'status' }, + { + title: '状态', + width: 110, + render: (_, row) => ( + + {row.status === 'active' ? '启用' : '停用'} + + ), + }, + { + title: '操作', + width: 240, + render: (_, row) => ( + + + + + + ), + }, ] return (
- +
- + + + {editingLevel ? : null} +
@@ -1969,6 +2053,19 @@ function LevelsPanel() { pagination={false} />
+ setDeletingLevel(null)} + okButtonProps={{ danger: true }} + okText="删除" + cancelText="取消" + > + + 确定删除等级「{deletingLevel?.name}」?正在使用该等级的打手将无法删除,需先调整。 + +
) } diff --git a/apps/frontend/src/services/admin/worker-platform.ts b/apps/frontend/src/services/admin/worker-platform.ts index e0132977..d5470c83 100644 --- a/apps/frontend/src/services/admin/worker-platform.ts +++ b/apps/frontend/src/services/admin/worker-platform.ts @@ -49,12 +49,19 @@ export function deleteAdminWorkCategory(categoryId: number) { ) } +export function deleteAdminWorkerLevel(levelId: number) { + return apiDelete<{ deleted: boolean }>( + `/api/v1/admin/worker-platform/levels/${levelId}`, + ) +} + export function saveAdminWorkerLevel(payload: { levelKey: string name: string depositFreeAmount?: number maxActiveOrders?: number upgradeThreshold?: number + status?: string }) { return apiPost<{ level: WorkerLevel }>( '/api/v1/admin/worker-platform/levels',