管理端补上运营真正要用的那几件事

原来 107 个后台接口配的是一套只能看的界面:风险中心 6 行代码全是只读,
动态和消息只能一条一条点,运营想发一句公告没有任何入口。

公告与通知:按全体、会员、城市、喂养者、有宠物的用户或指定名单发系统通知,
发之前能看到会发给多少人,可同时走离线推送。撤回只删还没读的那些——已经
读过的假装收回去只会让统计说谎。

资金总览:把"平台账上哪些钱不是自己的"单独摆出来(任务托管中 + 喂养者余额 +
申诉冻结),旁边才是收入、退款和待打款,最后是按产品和按日的收款。

退款统一到一个入口:会员、代喂、保证金都能分几次退到退完,每笔记一行;
退完最后一分钱才撤销买到的东西,半份会员仍然是会员。

风险中心能动手了:按等级和状态筛,行内直接封禁、冻结、加白(必须写理由)、
跳用户详情,事件可以标记跟进完成。

批量处置:动态、消息、举报支持勾选后批量下架/恢复/驳回,一次最多 100 条。
处罚仍然一条一条来——批量封禁是错封人最快的方式。

敏感词统一词库:消息、动态、送养共用一套,命中是拦截还是转人工由运营定;
转人工的命中会在风险中心留一条,管理端还能拿真实文案先试一遍。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-07 13:07:58 +08:00
co-authored by Claude Opus 5
parent 3e6a4900e2
commit 0031f79eb3
13 changed files with 1589 additions and 23 deletions
+18 -1
View File
@@ -353,6 +353,17 @@ func (a *App) adminRoutes() []rest.Route {
{Method: http.MethodPost, Path: "/admin/v1/reports/:id/handle", Handler: permit("reports:handle", a.adminHandleReport)},
{Method: http.MethodGet, Path: "/admin/v1/risk/users", Handler: permit("risk:view", a.adminRiskUsers)},
{Method: http.MethodGet, Path: "/admin/v1/risk/events", Handler: permit("risk:view", a.adminRiskEvents)},
{Method: http.MethodPost, Path: "/admin/v1/risk/users/:id/whitelist", Handler: permit("risk:view", a.adminWhitelistRiskUser)},
{Method: http.MethodPost, Path: "/admin/v1/risk/events/:id/handle", Handler: permit("risk:view", a.adminHandleRiskEvent)},
{Method: http.MethodPost, Path: "/admin/v1/posts/batch", Handler: permit("content:manage", a.adminBatchPosts)},
{Method: http.MethodPost, Path: "/admin/v1/messages/batch", Handler: permit("messages:manage", a.adminBatchMessages)},
{Method: http.MethodPost, Path: "/admin/v1/reports/batch", Handler: permit("reports:handle", a.adminBatchReports)},
{Method: http.MethodPost, Path: "/admin/v1/pet/listings/batch", Handler: permit("users:manage", a.adminBatchPetListings)},
{Method: http.MethodGet, Path: "/admin/v1/moderation/keywords", Handler: permit("system:manage", a.adminKeywords)},
{Method: http.MethodPost, Path: "/admin/v1/moderation/keywords", Handler: permit("system:manage", a.adminCreateKeyword)},
{Method: http.MethodPut, Path: "/admin/v1/moderation/keywords/:id", Handler: permit("system:manage", a.adminUpdateKeyword)},
{Method: http.MethodDelete, Path: "/admin/v1/moderation/keywords/:id", Handler: permit("system:manage", a.adminDeleteKeyword)},
{Method: http.MethodPost, Path: "/admin/v1/moderation/keywords/test", Handler: permit("system:manage", a.adminTestKeyword)},
{Method: http.MethodGet, Path: "/admin/v1/membership/plans", Handler: permit("membership:manage", a.adminPlans)},
{Method: http.MethodPost, Path: "/admin/v1/membership/plans", Handler: permit("membership:manage", a.adminCreatePlan)},
{Method: http.MethodPatch, Path: "/admin/v1/membership/plans/:id", Handler: permit("membership:manage", a.adminUpdatePlan)},
@@ -363,7 +374,13 @@ func (a *App) adminRoutes() []rest.Route {
{Method: http.MethodDelete, Path: "/admin/v1/orders/:id", Handler: permit("orders:manage", a.adminDeleteOrder)},
{Method: http.MethodPost, Path: "/admin/v1/orders/:id/pay", Handler: permit("orders:manage", a.adminMarkOrderPaid)},
{Method: http.MethodPost, Path: "/admin/v1/orders/:id/close", Handler: permit("orders:manage", a.adminCloseOrder)},
{Method: http.MethodPost, Path: "/admin/v1/orders/:id/refund", Handler: permit("orders:manage", a.adminRefund)},
{Method: http.MethodPost, Path: "/admin/v1/orders/:id/refund", Handler: permit("orders:manage", a.adminRefundOrder)},
{Method: http.MethodGet, Path: "/admin/v1/orders/:id/refunds", Handler: permit("orders:view", a.adminOrderRefunds)},
{Method: http.MethodGet, Path: "/admin/v1/finance/overview", Handler: permit("orders:view", a.adminFinanceOverview)},
{Method: http.MethodGet, Path: "/admin/v1/notifications/broadcasts", Handler: permit("users:view", a.adminBroadcasts)},
{Method: http.MethodGet, Path: "/admin/v1/notifications/broadcasts/preview", Handler: permit("users:view", a.adminBroadcastPreview)},
{Method: http.MethodPost, Path: "/admin/v1/notifications/broadcasts", Handler: permit("users:manage", a.adminSendBroadcast)},
{Method: http.MethodPost, Path: "/admin/v1/notifications/broadcasts/:id/revoke", Handler: permit("users:manage", a.adminRevokeBroadcast)},
{Method: http.MethodGet, Path: "/admin/v1/messages", Handler: permit("messages:view", a.adminMessages)},
{Method: http.MethodPost, Path: "/admin/v1/messages/:id/moderate", Handler: permit("messages:manage", a.adminModerateMessage)},
{Method: http.MethodGet, Path: "/admin/v1/pets", Handler: permit("users:view", a.adminPets)},