优化客服权限

This commit is contained in:
yml2213
2026-07-10 12:00:23 +08:00
parent 763f5034fd
commit ca5433ada4
18 changed files with 467 additions and 120 deletions
@@ -81,7 +81,7 @@ router.post(
router.post(
'/kuaishou-industry/vouchers/check-available',
requireAdminRoles(['admin', 'operator']),
requireAdminRoles(['admin', 'operator', 'support']),
createJsonHandler(
(req) =>
checkAdminKuaishouIndustryVoucherAvailable(
@@ -98,7 +98,7 @@ router.post(
router.post(
'/kuaishou-industry/vouchers/reverse',
requireAdminRoles(['admin', 'operator']),
requireAdminRoles(['admin', 'operator', 'support']),
createJsonHandler(
(req) =>
reverseAdminKuaishouIndustryVoucher(req.body as AdminKuaishouIndustryVoucherReverseRouteBody),
@@ -113,7 +113,7 @@ router.post(
router.post(
'/kuaishou-industry/vouchers/consume',
requireAdminRoles(['admin', 'operator']),
requireAdminRoles(['admin', 'operator', 'support']),
createJsonHandler(
(req) =>
consumeAdminKuaishouIndustryVoucherByCode(
@@ -130,7 +130,7 @@ router.post(
router.post(
'/kuaishou-industry/vouchers/resend-code',
requireAdminRoles(['admin', 'operator']),
requireAdminRoles(['admin', 'operator', 'support']),
createJsonHandler(
(req) =>
resendAdminKuaishouIndustryVoucherCode(
+1 -1
View File
@@ -34,7 +34,7 @@ router.get('/orders/:orderId', createJsonHandler(
router.post(
'/orders/:orderId/kuaishou-industry/resend-code',
requireAdminRoles(['admin', 'operator']),
requireAdminRoles(['admin', 'operator', 'support']),
createJsonHandler(
(req) =>
resendAdminOrderKuaishouIndustryVoucherCodes(
@@ -10,9 +10,9 @@ import fulfillmentRoutingRouter from "./platform-config/fulfillment-routing.js";
const router = Router();
router.use("/platform-config", kuaishouIndustryRouter);
router.use("/platform-config", requireAdminRoles(["admin"]));
router.use("/platform-config", notificationsRouter);
router.use("/platform-config", kuaishouIndustryRouter);
router.use("/platform-config", kuaishouFeifeiRouter);
router.use("/platform-config", ninetyoneRouter);
router.use("/platform-config", fulfillmentRoutingRouter);
@@ -10,13 +10,14 @@ import type {
AdminKuaishouIndustryAuthorizationCodeRouteBody,
AdminKuaishouIndustrySourceConfigRouteBody,
} from '../../../types/admin/route-inputs.js'
import { createJsonHandler } from '../session.js'
import { createJsonHandler, requireAdminRoles } from '../session.js'
import type { JsonRecord } from '../../../types/json.js'
const router = Router()
router.get(
'/kuaishou-industry-source',
requireAdminRoles(['admin', 'operator', 'support']),
createJsonHandler(() => getAdminKuaishouIndustrySourceConfig(), {
successMessage: 'ok',
errorMessage: '读取快手行业电子凭证配置失败',
@@ -26,6 +27,7 @@ router.get(
router.post(
'/kuaishou-industry-source',
requireAdminRoles(['admin']),
createJsonHandler(
(req) =>
updateAdminKuaishouIndustrySourceConfig(
@@ -57,6 +59,7 @@ router.post(
router.post(
'/kuaishou-industry-source/refresh-token',
requireAdminRoles(['admin']),
createJsonHandler((req) => refreshAdminKuaishouIndustryAccessToken(req.body as JsonRecord), {
successMessage: '快手行业电子凭证 accessToken 已刷新',
errorMessage: '刷新快手行业电子凭证 accessToken 失败',
@@ -80,6 +83,7 @@ router.post(
router.post(
'/kuaishou-industry-source/exchange-code',
requireAdminRoles(['admin']),
createJsonHandler(
(req) =>
exchangeAdminKuaishouIndustryAuthorizationCode(
+2 -2
View File
@@ -149,7 +149,7 @@ router.post(
router.post(
'/tasks/:taskId/kuaishou-industry/resend-code',
requireAdminRoles(['admin', 'operator']),
requireAdminRoles(['admin', 'operator', 'support']),
createJsonHandler(
(req) =>
resendAdminTaskKuaishouIndustryVoucherCode(getTaskId(req), req.adminSession || null),
@@ -164,7 +164,7 @@ router.post(
router.post(
'/tasks/:taskId/kuaishou-industry/consume',
requireAdminRoles(['admin', 'operator']),
requireAdminRoles(['admin', 'operator', 'support']),
createJsonHandler(
(req) =>
consumeAdminTaskKuaishouIndustryVoucher(
@@ -154,7 +154,7 @@ export async function getAdminOrderDetail(
kuaishouIndustryVouchers: kuaishouIndustryVouchers.map(mapAdminKuaishouIndustryVoucher),
operations: {
canResendKuaishouIndustryVoucherCode:
viewerContext.canManageTaskLifecycle && kuaishouIndustryVouchers.length > 0,
viewerContext.canOperateKuaishouIndustryVoucher && kuaishouIndustryVouchers.length > 0,
},
}
}
@@ -360,9 +360,9 @@ export async function getAdminTaskDetail(
String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' &&
canReturnKuaishouCloudFulfillmentStatus(task.task_status),
canResendKuaishouIndustryVoucherCode:
viewerContext.canManageTaskLifecycle && hasKuaishouIndustryVoucher,
viewerContext.canOperateKuaishouIndustryVoucher && hasKuaishouIndustryVoucher,
canConsumeKuaishouIndustryVoucher:
viewerContext.canManageTaskLifecycle &&
viewerContext.canOperateKuaishouIndustryVoucher &&
hasKuaishouIndustryVoucher &&
kuaishouIndustryVoucherStatus !== 'CONSUMED' &&
kuaishouIndustryVoucherSendCallbackStatus === 'success',
@@ -39,6 +39,13 @@ test('canViewerCloseTask allows support to close active task but not redeemed ta
assert.equal(canViewerCloseTask({ task_status: 'redeemed' }, viewerContext), false)
})
test('createAdminViewerContext lets support operate vouchers without lifecycle permissions', () => {
const viewerContext = createAdminViewerContext({ role: 'support' })
assert.equal(viewerContext.canOperateKuaishouIndustryVoucher, true)
assert.equal(viewerContext.canManageTaskLifecycle, false)
})
test('mapKuaishouCloudFulfillmentContext exposes cloud account display labels', () => {
const flow = mapKuaishouCloudFulfillmentContext(
{
@@ -25,6 +25,7 @@ export type AdminViewerContext = {
canViewSensitiveTaskData: boolean
canManageTaskLifecycle: boolean
canOperateAssistedTask: boolean
canOperateKuaishouIndustryVoucher: boolean
}
type RedeemResolutionAttempt = {
@@ -370,6 +371,7 @@ export function createAdminViewerContext(
canViewSensitiveTaskData: role === 'admin' || role === 'operator',
canManageTaskLifecycle: role === 'admin' || role === 'operator',
canOperateAssistedTask: role === 'admin' || role === 'operator' || role === 'support',
canOperateKuaishouIndustryVoucher: role === 'admin' || role === 'operator' || role === 'support',
}
}
@@ -369,7 +369,7 @@ export async function resendAdminTaskKuaishouIndustryVoucherCode(
const now = nowIso()
const viewerContext = createAdminViewerContext(session)
if (!viewerContext.canManageTaskLifecycle) {
if (!viewerContext.canOperateKuaishouIndustryVoucher) {
throw createHttpError('当前账号没有权限重发电子凭证发码回调', {
statusCode: 403,
errorCode: 'admin_task_kuaishou_industry_resend_forbidden',
@@ -446,7 +446,7 @@ export async function resendAdminOrderKuaishouIndustryVoucherCodes(
const viewerContext = createAdminViewerContext(session)
const now = nowIso()
if (!viewerContext.canManageTaskLifecycle) {
if (!viewerContext.canOperateKuaishouIndustryVoucher) {
throw createHttpError('当前账号没有权限重发电子凭证发码回调', {
statusCode: 403,
errorCode: 'admin_order_kuaishou_industry_resend_forbidden',
@@ -519,7 +519,7 @@ export async function consumeAdminTaskKuaishouIndustryVoucher(
const now = nowIso()
const viewerContext = createAdminViewerContext(session)
if (!viewerContext.canManageTaskLifecycle) {
if (!viewerContext.canOperateKuaishouIndustryVoucher) {
throw createHttpError('当前账号没有权限执行电子凭证核销', {
statusCode: 403,
errorCode: 'admin_task_kuaishou_industry_consume_forbidden',