增加接单工单客服备注
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
-- 061_work_order_admin_note.sql —— 后台客服工单备注。
|
||||
ALTER TABLE work_orders
|
||||
ADD COLUMN IF NOT EXISTS admin_note TEXT NOT NULL DEFAULT '';
|
||||
|
||||
COMMENT ON COLUMN work_orders.admin_note IS '后台客服维护的工单备注,不展示给打手';
|
||||
@@ -355,6 +355,7 @@ export type WorkOrderRow = {
|
||||
requirement_json: string | Record<string, unknown>
|
||||
acceptance_json: string | Record<string, unknown>
|
||||
draft_acceptance_json: string | Record<string, unknown>
|
||||
admin_note: string
|
||||
problem_note: string
|
||||
sharing_enabled: boolean
|
||||
sharing_total_quantity: number
|
||||
|
||||
@@ -167,6 +167,19 @@ export async function updateWorkOrderBasic(
|
||||
return getWorkOrderById(workOrderId)
|
||||
}
|
||||
|
||||
export async function updateWorkOrderAdminNote(
|
||||
workOrderId: number | string,
|
||||
note: string,
|
||||
updatedAt: string,
|
||||
): Promise<WorkOrderRow | null> {
|
||||
const result = await query<{ id: number }>(
|
||||
`UPDATE work_orders SET admin_note = $1, updated_at = $2 WHERE id = $3 RETURNING id`,
|
||||
[String(note || '').trim(), updatedAt, Number(workOrderId)],
|
||||
)
|
||||
if (!result.rows[0]) return null
|
||||
return getWorkOrderById(result.rows[0].id)
|
||||
}
|
||||
|
||||
export async function deleteWorkOrder(workOrderId: number | string): Promise<boolean> {
|
||||
const result = await query('DELETE FROM work_orders WHERE id = $1', [Number(workOrderId)])
|
||||
return Number(result.rowCount || 0) > 0
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
unassignAdminWorkOrder,
|
||||
unpublishAdminWorkOrder,
|
||||
updateAdminWorkOrder,
|
||||
updateAdminWorkOrderNote,
|
||||
updateAdminWorkOrderSharing,
|
||||
} from '../../../services/worker-platform/index.js'
|
||||
import {
|
||||
@@ -188,6 +189,30 @@ router.put(
|
||||
),
|
||||
)
|
||||
|
||||
router.put(
|
||||
'/worker-platform/orders/:workOrderId/note',
|
||||
requireAdminPermission('worker_order.edit'),
|
||||
createJsonHandler(
|
||||
(req) =>
|
||||
updateAdminWorkOrderNote(
|
||||
String(req.params.workOrderId || ''),
|
||||
req.body?.note,
|
||||
req.adminSession?.username || '',
|
||||
),
|
||||
{
|
||||
successMessage: '工单备注已保存',
|
||||
errorMessage: '保存工单备注失败',
|
||||
scope: '[admin/worker-platform/orders/:workOrderId/note]',
|
||||
audit: (req, data) => ({
|
||||
action: 'work_order_admin_note_updated',
|
||||
targetType: 'work_order',
|
||||
targetId: String(req.params.workOrderId || ''),
|
||||
data: data && typeof data === 'object' ? (data as Record<string, unknown>) : {},
|
||||
}),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
router.post(
|
||||
'/worker-platform/orders/:workOrderId/assign',
|
||||
requireAdminPermission('worker_order.assign'),
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
resolveProblemWorkOrder,
|
||||
updateWorkOrder,
|
||||
updateWorkOrderBasic,
|
||||
updateWorkOrderAdminNote,
|
||||
} from '../../repositories/worker-platform/index.js'
|
||||
import type { JsonObject } from '../../types/json.js'
|
||||
import { createHttpError } from '../../utils/http.js'
|
||||
@@ -111,6 +112,40 @@ export async function updateAdminWorkOrder(
|
||||
return { order: mapWorkOrderAdmin(updated || workOrder) }
|
||||
}
|
||||
|
||||
export async function updateAdminWorkOrderNote(
|
||||
workOrderId: number | string,
|
||||
note: unknown,
|
||||
actorName = '',
|
||||
) {
|
||||
const workOrder = await getRequiredWorkOrder(workOrderId)
|
||||
const normalizedNote = String(note ?? '').trim()
|
||||
if (normalizedNote.length > 2000) {
|
||||
throw createHttpError('备注不能超过 2000 个字符', {
|
||||
statusCode: 400,
|
||||
errorCode: 'work_order_admin_note_too_long',
|
||||
})
|
||||
}
|
||||
const now = nowIso()
|
||||
const updated = await updateWorkOrderAdminNote(workOrder.id, normalizedNote, now)
|
||||
if (!updated) {
|
||||
throw createHttpError('保存工单备注失败', {
|
||||
statusCode: 409,
|
||||
errorCode: 'work_order_admin_note_update_failed',
|
||||
})
|
||||
}
|
||||
await createWorkOrderEvent({
|
||||
workOrderId: workOrder.id,
|
||||
actorType: 'admin',
|
||||
actorId: actorName,
|
||||
eventType: 'admin_note_updated',
|
||||
fromStatus: workOrder.status,
|
||||
toStatus: workOrder.status,
|
||||
payloadJson: JSON.stringify({ note: normalizedNote }),
|
||||
now,
|
||||
})
|
||||
return { order: mapWorkOrderAdmin(updated) }
|
||||
}
|
||||
|
||||
export async function deleteAdminWorkOrder(workOrderId: number | string) {
|
||||
const workOrder = await getRequiredWorkOrder(workOrderId)
|
||||
if (!mapWorkOrderAdmin(workOrder).capabilities.canDelete) {
|
||||
|
||||
@@ -240,6 +240,7 @@ export function mapWorkOrderAdmin(workOrder: WorkOrderRow, shares?: WorkOrderSha
|
||||
},
|
||||
acceptance: mapAcceptanceForResponse(workOrder.acceptance_json),
|
||||
draftAcceptance: mapAcceptanceForResponse(workOrder.draft_acceptance_json),
|
||||
adminNote: workOrder.admin_note || '',
|
||||
problemNote: workOrder.problem_note || '',
|
||||
cancel:
|
||||
workOrder.status === WORK_ORDER_STATUS.CANCELLED
|
||||
@@ -286,6 +287,7 @@ export function mapWorkOrderForWorker(
|
||||
rawStatus: _rawStatus,
|
||||
hallAvailable: _hallAvailable,
|
||||
capabilities: _capabilities,
|
||||
adminNote: _adminNote,
|
||||
...visibleOrder
|
||||
} = mapped
|
||||
const personalStatus = resolveWorkerShareStatus(workOrder.status, myShare?.status)
|
||||
|
||||
Reference in New Issue
Block a user