Merge branch 'dev1023' into dev

解决 9 个冲突文件,并修复合并后遗留问题:

- 大厅候选集:采用 FIFO 窗口 + 全部置顶单 UNION(列表与抢单/拼单复核同源)
- 拼单份额:采用多次参与模型(每次参与 INSERT 新份额、requestId 幂等),
  取消后可重新参与;移除 HEAD 已废弃的单行 rejoin 逻辑与对应测试
- 工单迁移与成本中心 103–108 编号撞车:合并重写为
  109_work_order_share_participation / 110_work_order_hall_and_shop_indexes;
  临时库全量执行与重跑幂等验证通过(10 列、唯一键已删、7 个索引)
- 实时补丁:保留个性化字段、置顶重排、分页总数同步;补回 orderTimelineVisible 类型
- 打手大厅:保留 memo 组件化,移植「再拼一次」多份额按钮逻辑
- 全项目表格统一 PercentTable:WorkerOrderShareTable 改造

验证:backend typecheck/lint/check:sql/test(833 pass)、
frontend typecheck/lint/test(172 pass)/build 全绿。
This commit is contained in:
yml2213
2026-10-26 12:10:53 +08:00
94 changed files with 8991 additions and 744 deletions
@@ -892,6 +892,41 @@ export function cancelAdminWorkOrderShare(
}>(`/api/v1/admin/worker-platform/orders/${workOrderId}/shares/${shareId}/cancel`, payload)
}
/** 按份数撤销某个打手的部分份额:减少的份数回到大厅,押金保持原冻结额。 */
export function reduceAdminWorkOrderShareQuantity(
workOrderId: number,
shareId: number,
payload: { reduceQuantity: number; note?: string },
) {
return apiPost<{ order: WorkOrder; share: WorkOrderShare }>(
`/api/v1/admin/worker-platform/orders/${workOrderId}/shares/${shareId}/reduce-quantity`,
payload,
)
}
/** 按份额标记问题单:只影响该份额持有人,母单状态不变。备注选填。 */
export function markAdminWorkOrderShareProblem(workOrderId: number, shareId: number, note = '') {
return apiPost<{ order: WorkOrder; share: WorkOrderShare }>(
`/api/v1/admin/worker-platform/orders/${workOrderId}/shares/${shareId}/problem`,
{ note },
)
}
/** 处置份额级问题单:方式与母单处置一致,但只作用于该份额。 */
export function disposeAdminWorkOrderShareProblem(
workOrderId: number,
shareId: number,
payload: {
action: 'return_to_worker' | 'reopen' | 'cancel_release' | 'cancel_deduct'
note: string
},
) {
return apiPost<{ order: WorkOrder; share: WorkOrderShare; depositAmount: number }>(
`/api/v1/admin/worker-platform/orders/${workOrderId}/shares/${shareId}/problem/dispose`,
payload,
)
}
export function cancelAdminWorkOrderSharing(
workOrderId: number,
payload: {
+13
View File
@@ -18,3 +18,16 @@ export async function uploadFile(file: File, scene: string, scope: UploadScope)
const response = await apiPostForm<{ file: UploadedFile }>(endpoint, form)
return response.data.file
}
/**
* 验收视频上传:视频已在浏览器内压好,这里不再做图片压缩。
* 带上 durationSeconds 供服务端在容器解析不到时长时兜底(见 resolveVideoDuration)。
*/
export async function uploadVideoFile(file: File, scene: string, durationSeconds: number) {
const form = new FormData()
form.append('file', file)
form.append('scene', scene)
form.append('durationSeconds', String(durationSeconds))
const response = await apiPostForm<{ file: UploadedFile }>('/api/v1/worker/files/upload', form)
return response.data.file
}
+36 -17
View File
@@ -9,6 +9,8 @@ import type {
WorkerCancelRequest,
WorkerCancelRequestRisk,
WorkerAnnouncementConfig,
WorkerAcceptanceConfig,
WorkerAcceptanceVideo,
WorkerOrderFeedback,
WorkerAfterSalesCase,
WorkerHallCategoriesResponse,
@@ -144,6 +146,11 @@ export function fetchWorkerAnnouncement() {
return apiGet<WorkerAnnouncementConfig>('/api/v1/worker/announcement')
}
/** 验收视频配置(后台可改):时长上限、目标高度、码率等,压缩前读取。 */
export function fetchWorkerAcceptanceConfig() {
return apiGet<WorkerAcceptanceConfig>('/api/v1/worker/acceptance/config')
}
export function fetchWorkerWalletLedgers(params?: Record<string, unknown>) {
return apiGet<WorkerListResponse<WorkerWalletLedger>>(
'/api/v1/worker/profile/wallet-ledgers',
@@ -221,12 +228,18 @@ export function fetchWorkerHallLeaderboard(period: WorkerLeaderboardPeriod = 'al
return apiGet<WorkerLeaderboardResponse>('/api/v1/worker/hall/leaderboard', { period })
}
export function grabWorkerOrder(workOrderId: number) {
return apiPost<{ order: WorkOrder }>(`/api/v1/worker/hall/orders/${workOrderId}/grab`)
export function grabWorkerOrder(workOrderId: number, payload: { requestId?: string } = {}) {
return apiPost<{ order: WorkOrder; duplicated?: boolean }>(
`/api/v1/worker/hall/orders/${workOrderId}/grab`,
payload,
)
}
export function joinWorkerSharingOrder(workOrderId: number, payload: { quantity: number }) {
return apiPost<{ share: WorkOrderShare }>(
export function joinWorkerSharingOrder(
workOrderId: number,
payload: { quantity: number; requestId?: string },
) {
return apiPost<{ share: WorkOrderShare; duplicated?: boolean }>(
`/api/v1/worker/hall/orders/${workOrderId}/join-sharing`,
payload,
)
@@ -250,7 +263,10 @@ export function fetchWorkerCancelRequests() {
)
}
export function submitWorkerCancelRequest(workOrderId: number, payload: { reason: string }) {
export function submitWorkerCancelRequest(
workOrderId: number,
payload: { reason: string; shareId?: number | null },
) {
return apiPost<{ request: WorkerCancelRequest; risk: WorkerCancelRequestRisk }>(
`/api/v1/worker/orders/${workOrderId}/cancel-requests`,
payload,
@@ -293,11 +309,17 @@ export function saveWorkerOrderNote(workOrderId: number, note: string) {
return apiPost<{ order: WorkOrder }>(`/api/v1/worker/orders/${workOrderId}/note`, { note })
}
export function submitWorkerAcceptance(
workOrderId: number,
payload: { note?: string; imageUrls?: string[]; files?: UploadedFile[] },
) {
return apiPost<{ order: WorkOrder }>(
export type WorkerAcceptancePayload = {
note?: string
imageUrls?: string[]
files?: UploadedFile[]
/** 验收视频(非必填);null 在草稿阶段表示删除,已提交阶段服务端只接受替换 */
video?: WorkerAcceptanceVideo | null
shareId?: number
}
export function submitWorkerAcceptance(workOrderId: number, payload: WorkerAcceptancePayload) {
return apiPost<{ order: WorkOrder; share?: WorkOrderShare }>(
`/api/v1/worker/orders/${workOrderId}/submit-acceptance`,
payload,
)
@@ -320,11 +342,8 @@ export function remindWorkerAcceptance(workOrderId: number) {
)
}
export function saveWorkerAcceptanceDraft(
workOrderId: number,
payload: { note?: string; imageUrls?: string[]; files?: UploadedFile[] },
) {
return apiPost<{ order: WorkOrder }>(
export function saveWorkerAcceptanceDraft(workOrderId: number, payload: WorkerAcceptancePayload) {
return apiPost<{ order: WorkOrder; share?: WorkOrderShare }>(
`/api/v1/worker/orders/${workOrderId}/acceptance-draft`,
payload,
)
@@ -332,9 +351,9 @@ export function saveWorkerAcceptanceDraft(
export function supplementWorkerAcceptedOrderEvidence(
workOrderId: number,
payload: { files: UploadedFile[] },
payload: { files: UploadedFile[]; video?: WorkerAcceptanceVideo | null; shareId?: number },
) {
return apiPost<{ order: WorkOrder }>(
return apiPost<{ order: WorkOrder; share?: WorkOrderShare }>(
`/api/v1/worker/orders/${workOrderId}/acceptance-evidence`,
payload,
)