feat(#130): collect replacement products from Agent
This commit is contained in:
@@ -43,6 +43,7 @@ data class AgentTask(
|
||||
val leaseVersion: Long,
|
||||
val status: String,
|
||||
val source: String,
|
||||
val replacementOriginType: String,
|
||||
)
|
||||
|
||||
data class CurrentPageIdentity(
|
||||
@@ -100,6 +101,11 @@ data class CollectionHistoryDetail(
|
||||
val colorPrices: List<HistoryColorPrice>,
|
||||
val skus: List<HistorySku>,
|
||||
val missing: List<String>,
|
||||
val replacementEligible: Boolean,
|
||||
val replacementDisabledReason: String?,
|
||||
val replacementMappingStatus: String?,
|
||||
val replacementActivationStatus: String?,
|
||||
val replacementActivationErrorMessage: String?,
|
||||
)
|
||||
|
||||
data class CollectionResetResult(val taskId: Long, val status: String, val replayed: Boolean)
|
||||
@@ -132,7 +138,14 @@ data class PurchaseHistoryItem(
|
||||
val createdAt: String,
|
||||
)
|
||||
|
||||
data class PurchaseHistoryDetail(val task: PurchaseHistoryItem)
|
||||
data class PurchaseHistoryDetail(
|
||||
val task: PurchaseHistoryItem,
|
||||
val replacementEligible: Boolean,
|
||||
val replacementDisabledReason: String?,
|
||||
val replacementMappingStatus: String?,
|
||||
val replacementActivationStatus: String?,
|
||||
val replacementActivationErrorMessage: String?,
|
||||
)
|
||||
|
||||
class AgentApiException(
|
||||
val status: Int,
|
||||
@@ -184,8 +197,19 @@ class AgentApiClient(private val serverUrl: String) {
|
||||
return task(response.getJSONObject("data"))
|
||||
}
|
||||
|
||||
fun createCurrentPageCollectionTask(requestId: String, token: String): AgentTask {
|
||||
fun createCurrentPageCollectionTask(
|
||||
requestId: String,
|
||||
token: String,
|
||||
replacementOriginType: String? = null,
|
||||
replacementOriginTaskId: Long? = null,
|
||||
): AgentTask {
|
||||
val payload = JSONObject().put("requestId", requestId)
|
||||
if (!replacementOriginType.isNullOrBlank() && replacementOriginTaskId != null) {
|
||||
payload.put(
|
||||
"replacementOrigin",
|
||||
JSONObject().put("type", replacementOriginType).put("taskId", replacementOriginTaskId),
|
||||
)
|
||||
}
|
||||
return task(requireNotNull(request("POST", "/api/agent/v1/current-page-collection-tasks", payload, token)).getJSONObject("data"))
|
||||
}
|
||||
|
||||
@@ -275,6 +299,11 @@ class AgentApiClient(private val serverUrl: String) {
|
||||
HistorySku(specs, item.getLong("priceCent"), item.getBoolean("available"), item.getBoolean("complete"))
|
||||
},
|
||||
missing = data.getJSONArray("missing").strings(),
|
||||
replacementEligible = data.optBoolean("replacementEligible"),
|
||||
replacementDisabledReason = data.nullableString("replacementDisabledReason"),
|
||||
replacementMappingStatus = data.nullableString("replacementMappingStatus"),
|
||||
replacementActivationStatus = data.nullableString("replacementActivationStatus"),
|
||||
replacementActivationErrorMessage = data.nullableString("replacementActivationErrorMessage"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -294,7 +323,14 @@ class AgentApiClient(private val serverUrl: String) {
|
||||
|
||||
fun purchaseHistoryDetail(taskId: Long, token: String): PurchaseHistoryDetail {
|
||||
val data = requireNotNull(request("GET", "/api/agent/v1/purchase-tasks/$taskId", null, token)).getJSONObject("data")
|
||||
return PurchaseHistoryDetail(purchaseHistoryItem(data.getJSONObject("task")))
|
||||
return PurchaseHistoryDetail(
|
||||
task = purchaseHistoryItem(data.getJSONObject("task")),
|
||||
replacementEligible = data.optBoolean("replacementEligible"),
|
||||
replacementDisabledReason = data.nullableString("replacementDisabledReason"),
|
||||
replacementMappingStatus = data.nullableString("replacementMappingStatus"),
|
||||
replacementActivationStatus = data.nullableString("replacementActivationStatus"),
|
||||
replacementActivationErrorMessage = data.nullableString("replacementActivationErrorMessage"),
|
||||
)
|
||||
}
|
||||
|
||||
fun retryPurchaseTask(taskId: Long, requestId: String, token: String): PurchaseRetryResult {
|
||||
@@ -345,6 +381,7 @@ class AgentApiClient(private val serverUrl: String) {
|
||||
leaseVersion = data.getLong("leaseVersion"),
|
||||
status = data.getString("status"),
|
||||
source = data.optString("source", "admin"),
|
||||
replacementOriginType = data.optString("replacementOriginType"),
|
||||
)
|
||||
|
||||
private fun purchaseTask(data: JSONObject) = PurchaseAgentTask(
|
||||
|
||||
+52
-12
@@ -131,7 +131,11 @@ class AgentForegroundService : Service() {
|
||||
if (intent?.action == ACTION_RECONNECT) registeredThisProcess.set(false)
|
||||
if (intent?.action == ACTION_CHECK_NOW) manualCheckRequested.set(true)
|
||||
if (intent?.action == ACTION_CURRENT_PAGE_COLLECTION) {
|
||||
requestCurrentPageCollection(intent.getStringExtra(EXTRA_CURRENT_PAGE_REQUEST_ID).orEmpty())
|
||||
requestCurrentPageCollection(
|
||||
intent.getStringExtra(EXTRA_CURRENT_PAGE_REQUEST_ID).orEmpty(),
|
||||
intent.getStringExtra(EXTRA_REPLACEMENT_ORIGIN_TYPE),
|
||||
intent.getLongExtra(EXTRA_REPLACEMENT_ORIGIN_TASK_ID, 0L).takeIf { it > 0L },
|
||||
)
|
||||
return START_STICKY
|
||||
}
|
||||
triggerSync()
|
||||
@@ -291,18 +295,22 @@ class AgentForegroundService : Service() {
|
||||
})
|
||||
}
|
||||
|
||||
private fun requestCurrentPageCollection(requestId: String) {
|
||||
private fun requestCurrentPageCollection(
|
||||
requestId: String,
|
||||
replacementOriginType: String?,
|
||||
replacementOriginTaskId: Long?,
|
||||
) {
|
||||
if (requestId.isBlank()) {
|
||||
publishCurrentPageResult(0L, "采集请求无效,请重试。")
|
||||
publishCurrentPageResult(0L, "采集请求无效,请重试。", replacementOriginType, replacementOriginTaskId)
|
||||
return
|
||||
}
|
||||
activeCollectionCooldown()?.let { ticket ->
|
||||
val remaining = CollectionCooldownPolicy.remainingSeconds(System.currentTimeMillis(), ticket)
|
||||
publishCurrentPageResult(0L, "采集间隔中,还需 $remaining 秒。")
|
||||
publishCurrentPageResult(0L, "采集间隔中,还需 $remaining 秒。", replacementOriginType, replacementOriginTaskId)
|
||||
return
|
||||
}
|
||||
if (!taskMutex.tryAcquire(CURRENT_PAGE_RESERVATION_ID)) {
|
||||
publishCurrentPageResult(0L, "设备正在执行任务,请稍后再试。")
|
||||
publishCurrentPageResult(0L, "设备正在执行任务,请稍后再试。", replacementOriginType, replacementOriginTaskId)
|
||||
return
|
||||
}
|
||||
cancelIdleReturn("正在创建当前页面采集任务")
|
||||
@@ -318,7 +326,12 @@ class AgentForegroundService : Service() {
|
||||
throw TaskFailure("CURRENT_PDD_PAGE_NOT_FOUND", "请先在拼多多打开目标商品详情页")
|
||||
}
|
||||
val api = AgentApiClient(serverUrl)
|
||||
val task = api.createCurrentPageCollectionTask(requestId, credentials.token)
|
||||
val task = api.createCurrentPageCollectionTask(
|
||||
requestId,
|
||||
credentials.token,
|
||||
replacementOriginType,
|
||||
replacementOriginTaskId,
|
||||
)
|
||||
taskId = task.taskId
|
||||
check(taskMutex.transfer(CURRENT_PAGE_RESERVATION_ID, task.taskId)) {
|
||||
"当前页面采集本地占用状态不一致"
|
||||
@@ -329,15 +342,21 @@ class AgentForegroundService : Service() {
|
||||
publishCurrentPageResult(
|
||||
task.taskId,
|
||||
if (outcome.successful) {
|
||||
"临时采集任务 #${task.taskId} 已结束,请查看采集记录。"
|
||||
if (task.replacementOriginType.isNotBlank()) {
|
||||
"已替换,正在匹配规格"
|
||||
} else {
|
||||
"临时采集任务 #${task.taskId} 已结束,请查看采集记录。"
|
||||
}
|
||||
} else {
|
||||
currentPageUserMessage(outcome.code.orEmpty(), outcome.message.orEmpty())
|
||||
},
|
||||
replacementOriginType,
|
||||
replacementOriginTaskId,
|
||||
)
|
||||
} catch (error: TaskFailure) {
|
||||
publishCurrentPageResult(taskId, currentPageUserMessage(error.code, error.message ?: "采集失败"))
|
||||
publishCurrentPageResult(taskId, currentPageUserMessage(error.code, error.message ?: "采集失败"), replacementOriginType, replacementOriginTaskId)
|
||||
} catch (error: AgentApiException) {
|
||||
publishCurrentPageResult(taskId, currentPageUserMessage(error.code, error.message))
|
||||
publishCurrentPageResult(taskId, currentPageUserMessage(error.code, error.message), replacementOriginType, replacementOriginTaskId)
|
||||
} catch (error: Exception) {
|
||||
if (taskId > 0L) {
|
||||
val token = runCatching { identityStore.credentials()?.token }.getOrNull()
|
||||
@@ -353,7 +372,7 @@ class AgentForegroundService : Service() {
|
||||
}
|
||||
}
|
||||
}
|
||||
publishCurrentPageResult(taskId, "采集失败,请查看任务详情。")
|
||||
publishCurrentPageResult(taskId, "采集失败,请查看任务详情。", replacementOriginType, replacementOriginTaskId)
|
||||
} finally {
|
||||
if (taskId > 0L) {
|
||||
runningTaskId.compareAndSet(taskId, null)
|
||||
@@ -366,13 +385,20 @@ class AgentForegroundService : Service() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun publishCurrentPageResult(taskId: Long, message: String) {
|
||||
private fun publishCurrentPageResult(
|
||||
taskId: Long,
|
||||
message: String,
|
||||
replacementOriginType: String? = null,
|
||||
replacementOriginTaskId: Long? = null,
|
||||
) {
|
||||
currentPageResultNotificationUntil.set(SystemClock.elapsedRealtime() + CURRENT_PAGE_RESULT_NOTIFICATION_MILLIS)
|
||||
updateNotification(message, force = true)
|
||||
sendBroadcast(Intent(ACTION_CURRENT_PAGE_RESULT).apply {
|
||||
setPackage(packageName)
|
||||
putExtra(EXTRA_CURRENT_PAGE_TASK_ID, taskId)
|
||||
putExtra(EXTRA_CURRENT_PAGE_MESSAGE, message)
|
||||
replacementOriginType?.let { putExtra(EXTRA_REPLACEMENT_ORIGIN_TYPE, it) }
|
||||
replacementOriginTaskId?.let { putExtra(EXTRA_REPLACEMENT_ORIGIN_TASK_ID, it) }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -382,6 +408,7 @@ class AgentForegroundService : Service() {
|
||||
"CURRENT_PDD_PAGE_NOT_FOUND" -> "没有找到商品详情页,请重新打开商品后再试。"
|
||||
"PDD_SHARE_UNAVAILABLE", "PDD_COPY_LINK_UNAVAILABLE", "PDD_CLIPBOARD_UNAVAILABLE", "PDD_SHARE_LINK_INVALID" ->
|
||||
"无法识别商品链接,请确认商品页可以分享后再试。"
|
||||
"REPLACEMENT_ACTIVATION_FAILED" -> "采集成功,但替换生效失败,请稍后重试。"
|
||||
else -> fallback.ifBlank { "采集失败,请稍后重试。" }
|
||||
}
|
||||
|
||||
@@ -694,6 +721,10 @@ class AgentForegroundService : Service() {
|
||||
}
|
||||
TaskExecutionSummary(false, error.code, error.message)
|
||||
} catch (error: AgentApiException) {
|
||||
if (error.code == "REPLACEMENT_ACTIVATION_FAILED") {
|
||||
resultSafelySubmitted = true
|
||||
beginPostCollectionCooldowns()
|
||||
}
|
||||
stateStore.update("TASK_ERROR", "${error.code}:${error.message}", tokenStored = true)
|
||||
TaskExecutionSummary(false, error.code, error.message)
|
||||
} catch (error: Exception) {
|
||||
@@ -1001,6 +1032,8 @@ class AgentForegroundService : Service() {
|
||||
const val EXTRA_CURRENT_PAGE_REQUEST_ID = "current_page_request_id"
|
||||
const val EXTRA_CURRENT_PAGE_TASK_ID = "current_page_task_id"
|
||||
const val EXTRA_CURRENT_PAGE_MESSAGE = "current_page_message"
|
||||
const val EXTRA_REPLACEMENT_ORIGIN_TYPE = "replacement_origin_type"
|
||||
const val EXTRA_REPLACEMENT_ORIGIN_TASK_ID = "replacement_origin_task_id"
|
||||
const val MANUAL_EMPTY = "empty"
|
||||
const val MANUAL_COLLECTION_TASK = "collection_task"
|
||||
const val MANUAL_PURCHASE_TASK = "purchase_task"
|
||||
@@ -1044,10 +1077,17 @@ class AgentForegroundService : Service() {
|
||||
}
|
||||
}
|
||||
|
||||
fun collectCurrentPage(context: Context, requestId: String): CurrentPageLaunchResult {
|
||||
fun collectCurrentPage(
|
||||
context: Context,
|
||||
requestId: String,
|
||||
replacementOriginType: String? = null,
|
||||
replacementOriginTaskId: Long? = null,
|
||||
): CurrentPageLaunchResult {
|
||||
val intent = Intent(context, AgentForegroundService::class.java).apply {
|
||||
action = ACTION_CURRENT_PAGE_COLLECTION
|
||||
putExtra(EXTRA_CURRENT_PAGE_REQUEST_ID, requestId)
|
||||
replacementOriginType?.let { putExtra(EXTRA_REPLACEMENT_ORIGIN_TYPE, it) }
|
||||
replacementOriginTaskId?.let { putExtra(EXTRA_REPLACEMENT_ORIGIN_TASK_ID, it) }
|
||||
}
|
||||
return cn.ilapage.goauto.agent.automation.CurrentPageNavigationPolicy.launchBeforeOptionalBackground(
|
||||
launch = {
|
||||
|
||||
@@ -84,6 +84,19 @@ internal object PurchaseRetryPolicy {
|
||||
"系统会保留原任务,并创建一个新的采购任务。重试可能创建新的拼多多待付款订单,但系统不会支付。"
|
||||
}
|
||||
|
||||
internal enum class ReplacementPresentation { ACTION, ACTIVATION_FAILED, MATCHING, MATCHED, MANUAL_REQUIRED, HIDDEN }
|
||||
|
||||
internal object ReplacementActionPolicy {
|
||||
fun presentation(eligible: Boolean, mappingStatus: String?, activationStatus: String?): ReplacementPresentation = when {
|
||||
activationStatus == "failed" -> ReplacementPresentation.ACTIVATION_FAILED
|
||||
mappingStatus == "matching" -> ReplacementPresentation.MATCHING
|
||||
mappingStatus == "matched" -> ReplacementPresentation.MATCHED
|
||||
mappingStatus == "manual_required" -> ReplacementPresentation.MANUAL_REQUIRED
|
||||
eligible -> ReplacementPresentation.ACTION
|
||||
else -> ReplacementPresentation.HIDDEN
|
||||
}
|
||||
}
|
||||
|
||||
internal class TaskDetailState(restoredTaskId: Long? = null) {
|
||||
var taskId: Long? = restoredTaskId?.takeIf { it > 0L }
|
||||
private set
|
||||
@@ -113,18 +126,33 @@ class TaskHistoryFragment : Fragment() {
|
||||
private var currentPageReceiverRegistered = false
|
||||
private val currentPageReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent?.action != AgentForegroundService.ACTION_CURRENT_PAGE_RESULT || !collection) return
|
||||
if (intent?.action != AgentForegroundService.ACTION_CURRENT_PAGE_RESULT) return
|
||||
val message = intent.getStringExtra(AgentForegroundService.EXTRA_CURRENT_PAGE_MESSAGE).orEmpty()
|
||||
val taskId = intent.getLongExtra(AgentForegroundService.EXTRA_CURRENT_PAGE_TASK_ID, 0L)
|
||||
val originType = intent.getStringExtra(AgentForegroundService.EXTRA_REPLACEMENT_ORIGIN_TYPE)
|
||||
val originTaskId = intent.getLongExtra(AgentForegroundService.EXTRA_REPLACEMENT_ORIGIN_TASK_ID, 0L)
|
||||
val handlesResult = when (originType) {
|
||||
"collection" -> collection
|
||||
"purchase" -> !collection
|
||||
null -> collection
|
||||
else -> false
|
||||
}
|
||||
if (!handlesResult) return
|
||||
if (message.isNotBlank()) toast(message)
|
||||
if (taskId > 0L && isAdded) loadCollectionDetail(taskId) else if (isAdded) load()
|
||||
if (!isAdded) return
|
||||
when {
|
||||
originType == "collection" && collection && originTaskId > 0L -> loadCollectionDetail(originTaskId)
|
||||
originType == "purchase" && !collection && originTaskId > 0L -> loadPurchaseDetail(originTaskId)
|
||||
originType == null && collection && taskId > 0L -> loadCollectionDetail(taskId)
|
||||
originType == null && collection -> load()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(state: Bundle?) {
|
||||
super.onCreate(state)
|
||||
detailState = TaskDetailState(state?.getLong(STATE_DETAIL_TASK_ID)?.takeIf { it > 0L })
|
||||
if (collection) registerCurrentPageReceiver()
|
||||
registerCurrentPageReceiver()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
@@ -506,24 +534,20 @@ class TaskHistoryFragment : Fragment() {
|
||||
setOnClickListener { confirmReset(task) }
|
||||
}, collectionCardParams())
|
||||
}
|
||||
renderReplacementAction(
|
||||
eligible = detail.replacementEligible,
|
||||
mappingStatus = detail.replacementMappingStatus,
|
||||
activationStatus = detail.replacementActivationStatus,
|
||||
activationErrorMessage = detail.replacementActivationErrorMessage,
|
||||
originType = "collection",
|
||||
taskId = task.taskId,
|
||||
taskNo = "#${task.taskId}",
|
||||
)
|
||||
}
|
||||
|
||||
private fun confirmCurrentPageCollection() {
|
||||
collectionCooldownMessage()?.let { message ->
|
||||
showCurrentPageBlocked(message)
|
||||
return
|
||||
}
|
||||
val context = requireContext()
|
||||
val settings = AgentSettingsStore(context)
|
||||
val credentials = runCatching { SecureDeviceStore(context).credentials() }.getOrNull()
|
||||
val accessibility = GoAutoAccessibilityService.instance
|
||||
val problem = when {
|
||||
settings.serverUrl().isBlank() || credentials == null -> "设备尚未连接服务端,请先检查设置。"
|
||||
AccessibilityReadinessDetector.current(context) != AccessibilityReadiness.READY || accessibility == null -> "请先到“设置”开启采集采购助手。"
|
||||
AgentStateStore(context).read().currentTaskId != null -> "设备正在执行任务,请稍后再试。"
|
||||
!accessibility.hasRecentPddForeground() -> "请先在拼多多打开目标商品详情页,再切回 Agent。"
|
||||
else -> null
|
||||
}
|
||||
val problem = currentPageCollectionProblem()
|
||||
if (problem != null) {
|
||||
showCurrentPageBlocked(problem)
|
||||
return
|
||||
@@ -533,18 +557,38 @@ class TaskHistoryFragment : Fragment() {
|
||||
.setMessage("请确认拼多多已停留在目标商品详情页。Agent 将读取当前商品资料和分享链接;不会采购、创建订单或支付。")
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("开始采集") { _, _ ->
|
||||
toast("正在创建临时采集任务…")
|
||||
when (AgentForegroundService.collectCurrentPage(context, UUID.randomUUID().toString())) {
|
||||
CurrentPageLaunchResult.STARTED -> Unit
|
||||
CurrentPageLaunchResult.START_FAILED ->
|
||||
showCurrentPageBlocked("系统未允许启动采集服务,请保持 Agent 在前台后重试。")
|
||||
CurrentPageLaunchResult.BACKGROUND_FAILED ->
|
||||
showCurrentPageBlocked("无法将 Agent 切到后台,请手动返回拼多多后重试。")
|
||||
}
|
||||
launchCurrentPageCollection()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun currentPageCollectionProblem(): String? {
|
||||
collectionCooldownMessage()?.let { return it }
|
||||
val context = requireContext()
|
||||
val settings = AgentSettingsStore(context)
|
||||
val credentials = runCatching { SecureDeviceStore(context).credentials() }.getOrNull()
|
||||
val accessibility = GoAutoAccessibilityService.instance
|
||||
return when {
|
||||
settings.serverUrl().isBlank() || credentials == null -> "设备尚未连接服务端,请先检查设置。"
|
||||
AccessibilityReadinessDetector.current(context) != AccessibilityReadiness.READY || accessibility == null -> "请先到“设置”开启采集采购助手。"
|
||||
AgentStateStore(context).read().currentTaskId != null -> "设备正在执行任务,请稍后再试。"
|
||||
!accessibility.hasRecentPddForeground() -> "请先在拼多多打开目标商品详情页,再切回 Agent。"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchCurrentPageCollection(originType: String? = null, originTaskId: Long? = null) {
|
||||
val context = requireContext()
|
||||
toast(if (originType == null) "正在创建临时采集任务…" else "正在创建替代商品采集任务…")
|
||||
when (AgentForegroundService.collectCurrentPage(context, UUID.randomUUID().toString(), originType, originTaskId)) {
|
||||
CurrentPageLaunchResult.STARTED -> Unit
|
||||
CurrentPageLaunchResult.START_FAILED ->
|
||||
showCurrentPageBlocked("系统未允许启动采集服务,请保持 Agent 在前台后重试。")
|
||||
CurrentPageLaunchResult.BACKGROUND_FAILED ->
|
||||
showCurrentPageBlocked("无法将 Agent 切到后台,请手动返回拼多多后重试。")
|
||||
}
|
||||
}
|
||||
|
||||
private fun showCurrentPageBlocked(message: String) {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("暂时不能采集")
|
||||
@@ -665,6 +709,58 @@ class TaskHistoryFragment : Fragment() {
|
||||
} else {
|
||||
resultColumn.addView(context.centeredMessage("只读详情", "本页没有取消、修改订单或支付入口。"))
|
||||
}
|
||||
renderReplacementAction(
|
||||
eligible = detail.replacementEligible,
|
||||
mappingStatus = detail.replacementMappingStatus,
|
||||
activationStatus = detail.replacementActivationStatus,
|
||||
activationErrorMessage = detail.replacementActivationErrorMessage,
|
||||
originType = "purchase",
|
||||
taskId = task.taskId,
|
||||
taskNo = "CG-${task.taskId}",
|
||||
)
|
||||
}
|
||||
|
||||
private fun renderReplacementAction(
|
||||
eligible: Boolean,
|
||||
mappingStatus: String?,
|
||||
activationStatus: String?,
|
||||
activationErrorMessage: String?,
|
||||
originType: String,
|
||||
taskId: Long,
|
||||
taskNo: String,
|
||||
) {
|
||||
val context = requireContext()
|
||||
when (ReplacementActionPolicy.presentation(eligible, mappingStatus, activationStatus)) {
|
||||
ReplacementPresentation.ACTIVATION_FAILED -> resultColumn.addView(
|
||||
context.centeredMessage(
|
||||
"采集成功,但替换生效失败",
|
||||
activationErrorMessage?.takeIf(String::isNotBlank) ?: "请稍后重试。",
|
||||
),
|
||||
)
|
||||
ReplacementPresentation.MATCHING -> resultColumn.addView(context.centeredMessage("规格匹配中", "替代商品已提交,正在匹配规格。"))
|
||||
ReplacementPresentation.MATCHED -> resultColumn.addView(context.centeredMessage("规格已匹配", "可继续处理原任务。"))
|
||||
ReplacementPresentation.MANUAL_REQUIRED -> resultColumn.addView(context.centeredMessage("需要人工处理", "需在 Admin 人工匹配规格。"))
|
||||
ReplacementPresentation.ACTION -> resultColumn.addView(MaterialButton(context, null, com.google.android.material.R.attr.materialButtonOutlinedStyle).apply {
|
||||
text = "采集替代商品"
|
||||
minimumHeight = context.dp(48)
|
||||
contentDescription = "为任务 $taskNo 采集替代商品"
|
||||
setOnClickListener { confirmReplacement(originType, taskId, taskNo) }
|
||||
}, collectionCardParams())
|
||||
ReplacementPresentation.HIDDEN -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun confirmReplacement(originType: String, taskId: Long, taskNo: String) {
|
||||
currentPageCollectionProblem()?.let { problem ->
|
||||
showCurrentPageBlocked(problem)
|
||||
return
|
||||
}
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle("采集替代商品")
|
||||
.setMessage("用当前商品替换 $taskNo 的失效商品?")
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("替换") { _, _ -> launchCurrentPageCollection(originType, taskId) }
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun confirmPurchaseRetry(task: PurchaseHistoryItem) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package cn.ilapage.goauto.agent
|
||||
|
||||
import cn.ilapage.goauto.agent.ui.ReplacementActionPolicy
|
||||
import cn.ilapage.goauto.agent.ui.ReplacementPresentation
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class ReplacementActionPolicyTest {
|
||||
@Test
|
||||
fun serverEligibilityIsTheOnlyWayToShowTheAction() {
|
||||
assertEquals(ReplacementPresentation.ACTION, ReplacementActionPolicy.presentation(true, null, null))
|
||||
assertEquals(ReplacementPresentation.HIDDEN, ReplacementActionPolicy.presentation(false, null, null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun workflowStatesTakePriorityOverEligibility() {
|
||||
assertEquals(ReplacementPresentation.ACTIVATION_FAILED, ReplacementActionPolicy.presentation(true, "matching", "failed"))
|
||||
assertEquals(ReplacementPresentation.MATCHING, ReplacementActionPolicy.presentation(true, "matching", "activated"))
|
||||
assertEquals(ReplacementPresentation.MATCHED, ReplacementActionPolicy.presentation(false, "matched", "activated"))
|
||||
assertEquals(ReplacementPresentation.MANUAL_REQUIRED, ReplacementActionPolicy.presentation(false, "manual_required", "activated"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user