diff --git a/apps/backend/src/services/worker-platform/index.ts b/apps/backend/src/services/worker-platform/index.ts index 51f20c73..33844664 100644 --- a/apps/backend/src/services/worker-platform/index.ts +++ b/apps/backend/src/services/worker-platform/index.ts @@ -14,6 +14,7 @@ export * from './worker-order-timeout-service.js' export * from './worker-order-realtime-service.js' export * from './worker-collect-service.js' export * from './worker-acceptance-service.js' +export * from './worker-my-order-service.js' export * from './worker-platform-defaults.js' export * from './work-order-timeout-policy.js' export * from './after-sales-service.js' diff --git a/apps/backend/src/services/worker-platform/worker-my-order-service.ts b/apps/backend/src/services/worker-platform/worker-my-order-service.ts new file mode 100644 index 00000000..834fab6e --- /dev/null +++ b/apps/backend/src/services/worker-platform/worker-my-order-service.ts @@ -0,0 +1,348 @@ +import { runtimeConfig } from '../../config/runtime.js' +import { + createWorkerCancelRequest, + createWorkerOrderFeedback, + getWorkOrderShare, + getWorkerCancelRequestRisk, + getWorkerOrderOverview, + listWorkerCancelRequests, + listWorkerOrderFeedbacks, + listWorkerSharesByWorker, + listWorkerWorkOrderNotes, + listWorkerWorkOrderViews, + listWorkOrderEventsByOrderId, + listWorkOrders, + markWorkerWorkOrderViewed, + upsertWorkerWorkOrderNote, +} from '../../repositories/worker-platform/index.js' +import type { JsonObject } from '../../types/json.js' +import { createHttpError } from '../../utils/http.js' +import { nowIso } from '../../utils/time.js' +import { normalizePage, normalizePageSize } from '../admin/admin-query-utils.js' +import { getWorkerAnnouncementConfig } from './worker-announcement-config-service.js' +import { + createWorkerCancelRequestAdminNotification, + createWorkerOrderFeedbackAdminNotification, +} from '../admin/admin-notification-service.js' +import { publishWorkOrderRealtimeChange } from '../realtime/realtime-event-service.js' +import { + mapWorkOrderEvents, + mapWorkOrderForWorker, + mapWorkerCancelRequest, + mapWorkerOrderFeedback, + normalizeBoolean, + normalizeStatuses, + resolveWorkerPermissions, +} from './mappers.js' +import { + getRequiredWorkOrder, + getRequiredWorker, + requireActiveWorkerSession, + type WorkerSession, +} from './worker-session-context-service.js' +import { settleOverdueWorkOrders } from './worker-order-timeout-service.js' + +export async function listWorkerMyOrders(query: JsonObject = {}, session: WorkerSession) { + requireActiveWorkerSession(session) + const worker = await getRequiredWorker(session.workerId) + const page = normalizePage(query.page) + const pageSize = normalizePageSize(query.pageSize) + const vipEvidencePending = normalizeBoolean(query.vipEvidencePending, false) + const { items, total } = await listWorkOrders({ + page, + pageSize, + statuses: normalizeStatuses(query.status), + keyword: String(query.keyword || '').trim(), + workerSharingId: worker.id, + vipEvidencePending, + sort: 'worker_claimed_at_desc', + }) + const permissions = resolveWorkerPermissions(worker) + const myShares = await listWorkerSharesByWorker(worker.id) + const myShareByOrderId = new Map(myShares.map((share) => [Number(share.work_order_id), share])) + const workOrderIds = items.map((item) => Number(item.id)) + const [notes, views] = await Promise.all([ + listWorkerWorkOrderNotes(worker.id, workOrderIds), + listWorkerWorkOrderViews(worker.id, workOrderIds), + ]) + const noteByOrderId = new Map( + notes.map((item) => [Number(item.work_order_id), String(item.note || '')]), + ) + const viewedAtByOrderId = new Map( + views.map((item) => [Number(item.work_order_id), String(item.viewed_at || '')]), + ) + return { + items: items.map((item) => + mapWorkOrderForWorker( + item, + permissions, + myShareByOrderId.get(Number(item.id)), + noteByOrderId.get(Number(item.id)) || '', + viewedAtByOrderId.get(Number(item.id)) || null, + ), + ), + pagination: { page, pageSize, total }, + } +} + +/** 打手订单标签概览:订单数与预计或已结算金额。 */ +export async function getWorkerMyOrderOverview(session: WorkerSession) { + requireActiveWorkerSession(session) + const worker = await getRequiredWorker(session.workerId) + await settleOverdueWorkOrders({ workerId: worker.id, limit: 50 }) + const rows = await getWorkerOrderOverview(worker.id) + const items = rows.map((row) => ({ + status: row.status, + orderCount: row.orderCount, + settlementAmount: row.settlementAmount, + })) + const vipEvidencePending = rows.reduce( + (summary, row) => ({ + orderCount: summary.orderCount + row.vipEvidencePendingCount, + settlementAmount: summary.settlementAmount + row.vipEvidencePendingSettlementAmount, + }), + { orderCount: 0, settlementAmount: 0 }, + ) + const all = items.reduce( + (summary, item) => ({ + orderCount: summary.orderCount + item.orderCount, + settlementAmount: summary.settlementAmount + item.settlementAmount, + }), + { orderCount: 0, settlementAmount: 0 }, + ) + return { + items: [ + ...items, + { status: 'vip_evidence_pending', ...vipEvidencePending }, + { status: 'all', ...all }, + ], + } +} + +export async function getWorkerMyOrderEvents(workOrderId: number | string, session: WorkerSession) { + requireActiveWorkerSession(session) + if (runtimeConfig.worker.orderTimelineVisible !== true) { + throw createHttpError('流转记录暂未对打手开放', { + statusCode: 403, + errorCode: 'work_order_events_worker_disabled', + }) + } + const workOrder = await getRequiredWorkOrder(workOrderId) + const workerId = Number(session.workerId) + const sharingShare = await getWorkOrderShare(workOrder.id, workerId) + const isOrderOwner = + Number(workOrder.assigned_worker_id || 0) === workerId || + Number(workOrder.last_assigned_worker_id || 0) === workerId + if (!sharingShare && !isOrderOwner) { + throw createHttpError('只能查看自己订单的流转记录', { + statusCode: 403, + errorCode: 'work_order_events_owner_required', + }) + } + const events = await listWorkOrderEventsByOrderId(workOrderId) + return { items: await mapWorkOrderEvents(events) } +} + +export async function getWorkerCancelRequestSummary(session: WorkerSession) { + requireActiveWorkerSession(session) + const [risk, requests] = await Promise.all([ + getWorkerCancelRequestRisk(session.workerId, nowIso()), + listWorkerCancelRequests(session.workerId), + ]) + return { risk, items: requests.map(mapWorkerCancelRequest) } +} + +export async function getWorkerAnnouncement(session: WorkerSession) { + requireActiveWorkerSession(session) + return getWorkerAnnouncementConfig() +} + +export async function getWorkerOrderFeedbackSummary(session: WorkerSession) { + requireActiveWorkerSession(session) + return { items: (await listWorkerOrderFeedbacks(session.workerId)).map(mapWorkerOrderFeedback) } +} + +export async function submitWorkerOrderFeedback( + workOrderId: number | string, + payload: JsonObject = {}, + session: WorkerSession, +) { + requireActiveWorkerSession(session) + const content = String(payload.content || '').trim() + if (!content) { + throw createHttpError('请填写问题反馈内容', { + statusCode: 400, + errorCode: 'worker_order_feedback_content_required', + }) + } + if (content.length > 500) { + throw createHttpError('问题反馈不能超过 500 个字符', { + statusCode: 400, + errorCode: 'worker_order_feedback_content_too_long', + }) + } + const result = await createWorkerOrderFeedback({ + workOrderId: Number(workOrderId), + workerId: Number(session.workerId), + content, + now: nowIso(), + }) + if (!result.feedback) { + throw createHttpError('只能反馈自己的订单问题', { + statusCode: 403, + errorCode: 'worker_order_feedback_owner_required', + }) + } + const worker = await getRequiredWorker(session.workerId) + await createWorkerOrderFeedbackAdminNotification({ + feedbackId: Number(result.feedback.id), + workOrderId: Number(result.feedback.work_order_id), + workOrderNo: result.feedback.work_order_no || '', + productName: result.feedback.product_name || '', + workerName: worker.display_name || worker.username, + content, + }) + publishWorkOrderRealtimeChange({ + workOrderId: Number(result.feedback.work_order_id), + workerIds: [Number(session.workerId)], + }) + return { feedback: mapWorkerOrderFeedback(result.feedback) } +} + +export async function submitWorkerCancelRequest( + workOrderId: number | string, + payload: JsonObject = {}, + session: WorkerSession, +) { + requireActiveWorkerSession(session) + const reason = String(payload.reason || '').trim() + if (!reason) { + throw createHttpError('请填写申请取消原因', { + statusCode: 400, + errorCode: 'worker_cancel_request_reason_required', + }) + } + if (reason.length > 300) { + throw createHttpError('申请取消原因不能超过 300 个字符', { + statusCode: 400, + errorCode: 'worker_cancel_request_reason_too_long', + }) + } + const now = nowIso() + const result = await createWorkerCancelRequest({ + workOrderId: Number(workOrderId), + workerId: Number(session.workerId), + reason, + now, + }) + if (result.failureReason === 'daily_limit_reached') { + throw createHttpError('今日最多申请取消 5 次,请明日再试', { + statusCode: 429, + errorCode: 'worker_cancel_request_daily_limit', + }) + } + if (result.failureReason === 'request_pending') { + throw createHttpError('该订单已有待审核的取消申请', { + statusCode: 409, + errorCode: 'worker_cancel_request_pending', + }) + } + if (result.failureReason === 'share_not_cancellable') { + throw createHttpError('当前拼单份额不能申请取消', { + statusCode: 409, + errorCode: 'worker_cancel_request_share_status_invalid', + }) + } + if (!result.request) { + throw createHttpError('当前订单不能申请取消', { + statusCode: 409, + errorCode: 'worker_cancel_request_status_invalid', + }) + } + const worker = await getRequiredWorker(session.workerId) + await createWorkerCancelRequestAdminNotification({ + requestId: Number(result.request.id), + workOrderId: Number(result.request.work_order_id), + platformOrderId: result.request.platform_order_id || '', + productName: result.request.product_name || '', + workerName: worker.display_name || worker.username, + reason, + }) + const risk = await getWorkerCancelRequestRisk(session.workerId, now) + publishWorkOrderRealtimeChange({ + workOrderId: Number(result.request.work_order_id), + workerIds: [Number(session.workerId)], + }) + return { request: mapWorkerCancelRequest(result.request), risk } +} + +export async function saveWorkerOrderNote( + workOrderId: number | string, + payload: JsonObject = {}, + session: WorkerSession, +) { + requireActiveWorkerSession(session) + const workOrder = await getRequiredWorkOrder(workOrderId) + const workerId = Number(session.workerId) + const note = String(payload.note || '').trim() + if (note.length > 100) { + throw createHttpError('备注不能超过 100 个字符', { + statusCode: 400, + errorCode: 'work_order_note_too_long', + }) + } + + const worker = await getRequiredWorker(workerId) + const sharingShare = await getWorkOrderShare(workOrder.id, workerId) + const isOrderOwner = + Number(workOrder.assigned_worker_id || 0) === workerId || + Number(workOrder.last_assigned_worker_id || 0) === workerId + if (!sharingShare && !isOrderOwner) { + throw createHttpError('只能修改自己的订单备注', { + statusCode: 403, + errorCode: 'work_order_note_owner_required', + }) + } + const savedNote = await upsertWorkerWorkOrderNote({ + workOrderId: Number(workOrder.id), + workerId, + note, + now: nowIso(), + }) + return { + order: mapWorkOrderForWorker( + workOrder, + resolveWorkerPermissions(worker), + sharingShare, + savedNote, + ), + } +} + +/** 标记打手本人已查看工单,首次操作后不再展示“新”标识。 */ +export async function markWorkerOrderRead(workOrderId: number | string, session: WorkerSession) { + requireActiveWorkerSession(session) + const workOrder = await getRequiredWorkOrder(workOrderId) + const workerId = Number(session.workerId) + const sharingShare = await getWorkOrderShare(workOrder.id, workerId) + const isOrderOwner = + Number(workOrder.assigned_worker_id || 0) === workerId || + Number(workOrder.last_assigned_worker_id || 0) === workerId + if (!sharingShare && !isOrderOwner) { + throw createHttpError('只能标记自己的订单为已读', { + statusCode: 403, + errorCode: 'work_order_read_owner_required', + }) + } + + const viewedAt = await markWorkerWorkOrderViewed({ + workOrderId: Number(workOrder.id), + workerId, + now: nowIso(), + }) + publishWorkOrderRealtimeChange({ + workOrderId: Number(workOrder.id), + workerIds: [workerId], + }) + return { viewedAt, isUnread: false } +} diff --git a/apps/backend/src/services/worker-platform/worker-service.ts b/apps/backend/src/services/worker-platform/worker-service.ts index 00f27f10..b249400e 100644 --- a/apps/backend/src/services/worker-platform/worker-service.ts +++ b/apps/backend/src/services/worker-platform/worker-service.ts @@ -1,8 +1,5 @@ -import { runtimeConfig } from '../../config/runtime.js' import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js' import { - createWorkerCancelRequest, - createWorkerOrderFeedback, countWorkerActiveOrders, consumeWorkerSmsCode, createWorkOrderEvent, @@ -13,8 +10,6 @@ import { getWorkerUserByUsername, getWorkerUserByDisplayName, getWorkOrderById, - getWorkOrderShare, - getWorkerOrderOverview, grabWorkOrder, createWorkerSessionRecord, getWorkerSessionBySessionId, @@ -22,24 +17,12 @@ import { revokeWorkerSession, touchWorkerSession, joinWorkOrderShare, - listWorkerCancelRequests, - listWorkerOrderFeedbacks, - listWorkerSharesByWorker, - listWorkerWorkOrderNotes, - listWorkerWorkOrderViews, - listWorkOrders, - listWorkOrderEventsByOrderId, - getWorkerCancelRequestRisk, - upsertWorkerWorkOrderNote, - markWorkerWorkOrderViewed, type WorkOrderRow, type WorkerSessionRow, } from '../../repositories/worker-platform/index.js' import type { JsonObject } from '../../types/json.js' import { createHttpError } from '../../utils/http.js' import { nowIso } from '../../utils/time.js' -import { normalizePage, normalizePageSize } from '../admin/admin-query-utils.js' -import { getWorkerAnnouncementConfig } from './worker-announcement-config-service.js' import { getWorkerHallConfig } from './worker-hall-config-service.js' import { getRequiredWorkOrder, @@ -47,10 +30,6 @@ import { requireActiveWorkerSession, type WorkerSession, } from './worker-session-context-service.js' -import { - createWorkerCancelRequestAdminNotification, - createWorkerOrderFeedbackAdminNotification, -} from '../admin/admin-notification-service.js' import { publishWorkerWalletRealtimeChange, publishWorkOrderRealtimeChange, @@ -58,14 +37,8 @@ import { import { mapWorkOrderForWorker, - mapWorkOrderEvents, mapWorkOrderShare, - mapWorkerCancelRequest, - mapWorkerOrderFeedback, - normalizeBoolean, - normalizeOptionalId, normalizePositiveInteger, - normalizeStatuses, resolveFreezeDepositAmount, resolveWorkerPermissions, throwGrabWorkOrderFailure, @@ -99,7 +72,6 @@ import { } from './worker-session-auth-service.js' import { normalizeWorkOrderTimeoutPolicy } from './work-order-timeout-policy.js' import { publishWorkOrderParticipantChange } from './worker-order-realtime-service.js' -import { settleOverdueWorkOrders } from './worker-order-timeout-service.js' export { getRequiredWorkOrder, @@ -255,310 +227,18 @@ export { settleDueDepositUnfreezes } from './worker-profile-service.js' export { settleOverdueWorkOrders } from './worker-order-timeout-service.js' -export async function listWorkerMyOrders(query: JsonObject = {}, session: WorkerSession) { - requireActiveWorkerSession(session) - const worker = await getRequiredWorker(session.workerId) - const page = normalizePage(query.page) - const pageSize = normalizePageSize(query.pageSize) - const vipEvidencePending = normalizeBoolean(query.vipEvidencePending, false) - const { items, total } = await listWorkOrders({ - page, - pageSize, - statuses: normalizeStatuses(query.status), - keyword: String(query.keyword || '').trim(), - workerSharingId: worker.id, - vipEvidencePending, - sort: 'worker_claimed_at_desc', - }) - const permissions = resolveWorkerPermissions(worker) - const myShares = await listWorkerSharesByWorker(worker.id) - const myShareByOrderId = new Map(myShares.map((share) => [Number(share.work_order_id), share])) - const workOrderIds = items.map((item) => Number(item.id)) - const [notes, views] = await Promise.all([ - listWorkerWorkOrderNotes(worker.id, workOrderIds), - listWorkerWorkOrderViews(worker.id, workOrderIds), - ]) - const noteByOrderId = new Map( - notes.map((item) => [Number(item.work_order_id), String(item.note || '')]), - ) - const viewedAtByOrderId = new Map( - views.map((item) => [Number(item.work_order_id), String(item.viewed_at || '')]), - ) - return { - items: items.map((item) => - mapWorkOrderForWorker( - item, - permissions, - myShareByOrderId.get(Number(item.id)), - noteByOrderId.get(Number(item.id)) || '', - viewedAtByOrderId.get(Number(item.id)) || null, - ), - ), - pagination: { page, pageSize, total }, - } -} - -/** 打手订单标签概览:订单数与预计/已结算金额。 */ -export async function getWorkerMyOrderOverview(session: WorkerSession) { - requireActiveWorkerSession(session) - const worker = await getRequiredWorker(session.workerId) - await settleOverdueWorkOrders({ workerId: worker.id, limit: 50 }) - const rows = await getWorkerOrderOverview(worker.id) - const items = rows.map((row) => ({ - status: row.status, - orderCount: row.orderCount, - settlementAmount: row.settlementAmount, - })) - const vipEvidencePending = rows.reduce( - (summary, row) => ({ - orderCount: summary.orderCount + row.vipEvidencePendingCount, - settlementAmount: summary.settlementAmount + row.vipEvidencePendingSettlementAmount, - }), - { orderCount: 0, settlementAmount: 0 }, - ) - const all = items.reduce( - (summary, item) => ({ - orderCount: summary.orderCount + item.orderCount, - settlementAmount: summary.settlementAmount + item.settlementAmount, - }), - { orderCount: 0, settlementAmount: 0 }, - ) - return { - items: [ - ...items, - { status: 'vip_evidence_pending', ...vipEvidencePending }, - { status: 'all', ...all }, - ], - } -} - -export async function getWorkerMyOrderEvents(workOrderId: number | string, session: WorkerSession) { - requireActiveWorkerSession(session) - if (runtimeConfig.worker.orderTimelineVisible !== true) { - throw createHttpError('流转记录暂未对打手开放', { - statusCode: 403, - errorCode: 'work_order_events_worker_disabled', - }) - } - const workOrder = await getRequiredWorkOrder(workOrderId) - const workerId = Number(session.workerId) - const sharingShare = await getWorkOrderShare(workOrder.id, workerId) - const isOrderOwner = - Number(workOrder.assigned_worker_id || 0) === workerId || - Number(workOrder.last_assigned_worker_id || 0) === workerId - if (!sharingShare && !isOrderOwner) { - throw createHttpError('只能查看自己订单的流转记录', { - statusCode: 403, - errorCode: 'work_order_events_owner_required', - }) - } - const events = await listWorkOrderEventsByOrderId(workOrderId) - return { items: await mapWorkOrderEvents(events) } -} - -export async function getWorkerCancelRequestSummary(session: WorkerSession) { - requireActiveWorkerSession(session) - const [risk, requests] = await Promise.all([ - getWorkerCancelRequestRisk(session.workerId, nowIso()), - listWorkerCancelRequests(session.workerId), - ]) - return { risk, items: requests.map(mapWorkerCancelRequest) } -} - -export async function getWorkerAnnouncement(session: WorkerSession) { - requireActiveWorkerSession(session) - return getWorkerAnnouncementConfig() -} - -export async function getWorkerOrderFeedbackSummary(session: WorkerSession) { - requireActiveWorkerSession(session) - return { items: (await listWorkerOrderFeedbacks(session.workerId)).map(mapWorkerOrderFeedback) } -} - -export async function submitWorkerOrderFeedback( - workOrderId: number | string, - payload: JsonObject = {}, - session: WorkerSession, -) { - requireActiveWorkerSession(session) - const content = String(payload.content || '').trim() - if (!content) { - throw createHttpError('请填写问题反馈内容', { - statusCode: 400, - errorCode: 'worker_order_feedback_content_required', - }) - } - if (content.length > 500) { - throw createHttpError('问题反馈不能超过 500 个字符', { - statusCode: 400, - errorCode: 'worker_order_feedback_content_too_long', - }) - } - const result = await createWorkerOrderFeedback({ - workOrderId: Number(workOrderId), - workerId: Number(session.workerId), - content, - now: nowIso(), - }) - if (!result.feedback) { - throw createHttpError('只能反馈自己的订单问题', { - statusCode: 403, - errorCode: 'worker_order_feedback_owner_required', - }) - } - const worker = await getRequiredWorker(session.workerId) - await createWorkerOrderFeedbackAdminNotification({ - feedbackId: Number(result.feedback.id), - workOrderId: Number(result.feedback.work_order_id), - workOrderNo: result.feedback.work_order_no || '', - productName: result.feedback.product_name || '', - workerName: worker.display_name || worker.username, - content, - }) - publishWorkOrderRealtimeChange({ - workOrderId: Number(result.feedback.work_order_id), - workerIds: [Number(session.workerId)], - }) - return { feedback: mapWorkerOrderFeedback(result.feedback) } -} - -export async function submitWorkerCancelRequest( - workOrderId: number | string, - payload: JsonObject = {}, - session: WorkerSession, -) { - requireActiveWorkerSession(session) - const reason = String(payload.reason || '').trim() - if (!reason) { - throw createHttpError('请填写申请取消原因', { - statusCode: 400, - errorCode: 'worker_cancel_request_reason_required', - }) - } - if (reason.length > 300) { - throw createHttpError('申请取消原因不能超过 300 个字符', { - statusCode: 400, - errorCode: 'worker_cancel_request_reason_too_long', - }) - } - const now = nowIso() - const result = await createWorkerCancelRequest({ - workOrderId: Number(workOrderId), - workerId: Number(session.workerId), - reason, - now, - }) - if (result.failureReason === 'daily_limit_reached') { - throw createHttpError('今日最多申请取消 5 次,请明日再试', { - statusCode: 429, - errorCode: 'worker_cancel_request_daily_limit', - }) - } - if (result.failureReason === 'request_pending') { - throw createHttpError('该订单已有待审核的取消申请', { - statusCode: 409, - errorCode: 'worker_cancel_request_pending', - }) - } - if (result.failureReason === 'share_not_cancellable') { - throw createHttpError('当前拼单份额不能申请取消', { - statusCode: 409, - errorCode: 'worker_cancel_request_share_status_invalid', - }) - } - if (!result.request) { - throw createHttpError('当前订单不能申请取消', { - statusCode: 409, - errorCode: 'worker_cancel_request_status_invalid', - }) - } - const worker = await getRequiredWorker(session.workerId) - await createWorkerCancelRequestAdminNotification({ - requestId: Number(result.request.id), - workOrderId: Number(result.request.work_order_id), - platformOrderId: result.request.platform_order_id || '', - productName: result.request.product_name || '', - workerName: worker.display_name || worker.username, - reason, - }) - const risk = await getWorkerCancelRequestRisk(session.workerId, now) - publishWorkOrderRealtimeChange({ - workOrderId: Number(result.request.work_order_id), - workerIds: [Number(session.workerId)], - }) - return { request: mapWorkerCancelRequest(result.request), risk } -} - -export async function saveWorkerOrderNote( - workOrderId: number | string, - payload: JsonObject = {}, - session: WorkerSession, -) { - requireActiveWorkerSession(session) - const workOrder = await getRequiredWorkOrder(workOrderId) - const workerId = Number(session.workerId) - const note = String(payload.note || '').trim() - if (note.length > 100) { - throw createHttpError('备注不能超过 100 个字符', { - statusCode: 400, - errorCode: 'work_order_note_too_long', - }) - } - - const worker = await getRequiredWorker(workerId) - const sharingShare = await getWorkOrderShare(workOrder.id, workerId) - const isOrderOwner = - Number(workOrder.assigned_worker_id || 0) === workerId || - Number(workOrder.last_assigned_worker_id || 0) === workerId - if (!sharingShare && !isOrderOwner) { - throw createHttpError('只能修改自己的订单备注', { - statusCode: 403, - errorCode: 'work_order_note_owner_required', - }) - } - const savedNote = await upsertWorkerWorkOrderNote({ - workOrderId: Number(workOrder.id), - workerId, - note, - now: nowIso(), - }) - return { - order: mapWorkOrderForWorker( - workOrder, - resolveWorkerPermissions(worker), - sharingShare, - savedNote, - ), - } -} - -/** 标记打手本人已查看工单,首次操作后不再展示“新”标识。 */ -export async function markWorkerOrderRead(workOrderId: number | string, session: WorkerSession) { - requireActiveWorkerSession(session) - const workOrder = await getRequiredWorkOrder(workOrderId) - const workerId = Number(session.workerId) - const sharingShare = await getWorkOrderShare(workOrder.id, workerId) - const isOrderOwner = - Number(workOrder.assigned_worker_id || 0) === workerId || - Number(workOrder.last_assigned_worker_id || 0) === workerId - if (!sharingShare && !isOrderOwner) { - throw createHttpError('只能标记自己的订单为已读', { - statusCode: 403, - errorCode: 'work_order_read_owner_required', - }) - } - - const viewedAt = await markWorkerWorkOrderViewed({ - workOrderId: Number(workOrder.id), - workerId, - now: nowIso(), - }) - publishWorkOrderRealtimeChange({ - workOrderId: Number(workOrder.id), - workerIds: [workerId], - }) - return { viewedAt, isUnread: false } -} +export { + getWorkerAnnouncement, + getWorkerCancelRequestSummary, + getWorkerMyOrderEvents, + getWorkerMyOrderOverview, + getWorkerOrderFeedbackSummary, + listWorkerMyOrders, + markWorkerOrderRead, + saveWorkerOrderNote, + submitWorkerCancelRequest, + submitWorkerOrderFeedback, +} from './worker-my-order-service.js' export async function joinWorkerSharingOrder( workOrderId: number | string,