Gitea MCP 未向当前会话暴露,按仓库规则回退项目根目录安全配置与 Gitea API 维护本工单;凭据未写入工单、代码或日志。 2026-09-01 范围修订一(已确认):用户进一步要求去掉「任务状态必须为 failed」这道门槛——只要工人通过其他手机/网页验证过 PDD 商品确实失效,或单纯找到了更好的替代品,即便当前采集任务本身是成功的,也应该能从其任务详情发起替换。用户已确认第 1、2 点:去掉 failed 限制、确认弹窗文案追加「会取消该商品下尚未开始执行的采购任务」的明确提示。 2026-09-01 范围修订二(已确认):用户进一步要求,若该 PDD 商品名下存在正在采购(running/order_submit_started)的任务,不允许替换。核验后一并收紧为:running、order_submit_started、order_result_unknown(订单结果未知、需人工核对,同样敏感)三种状态命中任意一个即禁止替换,作为新增的硬门槛,不交给人工临场判断——这与范围修订一放宽的两道门槛(错误码、任务失败状态)性质不同:修订一放宽的是需要人工判断的软信号,本次收紧的是系统自身已经确定的硬事实(是否有采购正在执行或结果待核对)。以下正文已按此修订。
Gitea MCP 未向当前会话暴露,按仓库规则回退项目根目录安全配置与 Gitea API 维护本工单;凭据未写入工单、代码或日志。
2026-09-01 范围修订一(已确认):用户进一步要求去掉「任务状态必须为 failed」这道门槛——只要工人通过其他手机/网页验证过 PDD 商品确实失效,或单纯找到了更好的替代品,即便当前采集任务本身是成功的,也应该能从其任务详情发起替换。用户已确认第 1、2 点:去掉 failed 限制、确认弹窗文案追加「会取消该商品下尚未开始执行的采购任务」的明确提示。
failed
2026-09-01 范围修订二(已确认):用户进一步要求,若该 PDD 商品名下存在正在采购(running/order_submit_started)的任务,不允许替换。核验后一并收紧为:running、order_submit_started、order_result_unknown(订单结果未知、需人工核对,同样敏感)三种状态命中任意一个即禁止替换,作为新增的硬门槛,不交给人工临场判断——这与范围修订一放宽的两道门槛(错误码、任务失败状态)性质不同:修订一放宽的是需要人工判断的软信号,本次收紧的是系统自身已经确定的硬事实(是否有采购正在执行或结果待核对)。以下正文已按此修订。
running
order_submit_started
order_result_unknown
来源:用户于 2026-09-01 反馈采集任务 110 失败原因为「未找到 PDD 商品规格入口」(实际是链接失效被跳转到 PDD 首页),但任务详情里没有出现「采集替代商品」按钮。核验后确认根因是服务端按错误码白名单判断是否显示该按钮,白名单覆盖不了这类场景。用户明确要求:不应该用错误码白名单限制过多,任务失败了就应该显示该按钮,交给工人判断失败原因、决定是否需要替换商品;并要求单独建单,不并入 #165 或 #178。
目的:让「采集替代商品」的可见性既不依赖一份不完整的错误码白名单,也不强制要求当前任务本身处于失败状态,改为把"这个 PDD 商品是否值得替换"的判断完全交给人工——判断依据可以来自 Agent 自身的失败记录,也可以来自外部验证(其他手机、网页)或单纯发现了更好的商品;同时保留必要的状态机保护,针对新引入的行为(对健康商品发起替换会取消其未执行的采购任务)加强确认提示,并新增一道系统硬门槛:该商品名下有采购任务正在执行或结果待核对时,直接禁止替换,不留给人工临场判断,避免放宽准入扩大误触代价。
代码基线:4343ab5(2026-09-01)。核验日期 2026-09-01。功能来源工单 #130(33d4bb1 feat(#130): collect replacement products from Agent),后续 #132、#155、#157、#174 在同一路径上有过维护,本单是对 #130 既有准入条件的一次收窄反转。
4343ab5
33d4bb1 feat(#130): collect replacement products from Agent
server/app/goauto/replacement/origin.go:InspectOrigin
originType=="collection"
:37-46
task.PDDProductID != nil
:65-68
DisabledReason="原任务没有可替换的拼多多商品"
active
activeForOrigin
:80-83
DisabledReason="该商品已经发起替换"
:78-91
task.Status == failed
:104-106
DisabledReason="只有失败任务可以替换商品"
task.ErrorCode
PDD_LINK_INVALID
PDD_GOODS_SOLD_OUT
:108-110
ErrorPDDLinkInvalid
ErrorPDDGoodsSoldOut
origin.go:15-16
DisabledReason="当前失败原因不属于商品失效或售罄"
renderCollectionDetail
TaskHistoryFragment.kt
renderReplacementAction
InspectOrigin
TaskHistoryFragment.kt:92-100
ReplacementActionPolicy.presentation
eligible=false
mappingStatus
activationStatus
HIDDEN
:99
PDD_DETAIL_ENTRY_FAILED
replacement/activation.go:239-289
TaskHistoryFragment.kt:809-818
confirmReplacement
CorrectAndActivate
replacement/activation.go:70+
applyPurchaseReplacement
activation.go:309-330
pending
spec_probe_pending
SkippedRunningTasks
purchase/lifecycle.go
Claim
Start
Result
pdd.Status
activate()
disabled
activation.go:255-293
models.PurchaseTaskStatusRunning
PurchaseTaskStatusOrderSubmitStarted
PurchaseTaskStatusOrderResultUnknown
models/purchase.go:19-23
PDD 商品必须存在
该 PDD 商品当前无进行中的替换
任务状态为 failed
原任务没有可替换的拼多多商品
task.PDDProductID == nil
ErrorCode
originType=="purchase"
任务状态必须为 failed
replacement/origin.go
PDDProductID
result.Eligible = true
errorCode
task.Status
status
result.SourceProductID
purchase_task.pdd_product_id
result.DisabledReason = "该商品有采购任务正在执行或结果待核对,暂不能替换,请稍后重试"
detail
ReplacementPresentation.HIDDEN
现有按钮显示条件与确认弹窗文案的调整,不新增页面、不改变整体交互结构;确认弹窗文案变化需要标注截图交用户确认(放宽前/放宽后对比、新文案内容,需体现「取消未执行采购任务」和「有采购任务正在执行/结果待核对,暂不能替换」两句新增提示)。
场景一:可以替换时的确认弹窗(confirmReplacement)
标题:采集替代商品 正文: 用当前商品替换关联的拼多多商品?将会: · 把所有关联该商品的虾皮商品一并改指向新商品,原有规格映射清空,需重新匹配 · 取消该商品下尚未开始执行的采购任务 原商品会被停用,此操作暂不能自动撤销。 按钮:取消 / 确认替换
场景二:被拦截时(该商品名下存在 running/order_submit_started/order_result_unknown 采购任务)
不使用弹窗,比照 ReplacementPresentation.MANUAL_REQUIRED/MATCHING 的既有做法,新增枚举值(如 PURCHASE_IN_PROGRESS)原地渲染静态文字:
ReplacementPresentation.MANUAL_REQUIRED
MATCHING
PURCHASE_IN_PROGRESS
标题:暂不能替换 正文:该商品下有采购任务正在执行或订单结果待核对,任务结束后可再发起替换。
DisabledReason
有长期文档影响。 #130 相关的替换业务规则(含准入条件)若在 Wiki 业务规则页面有记录,需同步更新为「按状态而非错误码白名单判断,且新增采购执行中/结果待核对的硬性拦截」,并说明确认弹窗新增的批量影响提示与拦截提示。在线回读 revision 后完成一次 sync 和一次 sync --check,revision 回写本工单;若核实后发现相关内容此前未曾写入 Wiki,在工单说明并跳过同步。
sync
sync --check
待确认(2026-09-01 创建;同日三次修订与确认弹窗文案均已由用户确认:去除错误码白名单、去除 failed 状态要求、新增"无正在执行/结果待核对采购任务"硬门槛、确定两处提示文案。方案已完整,可进入实施。)
????(2026-09-01)???????? Gitea MCP,??????? Gitea API;????????????,????????????
????:c07c345????????:??????????/??????;?? running?order_submit_started?order_result_unknown ???????;Android ???????????????;????????????????????? Server/Android ???????,?? #185 ???????????????
c07c345
go test ./...
python dev_scripts/harness.py check --strict
verify -Component all
web/src/views/goauto/purchase-tasks/index.vue
3aab1f0 feat(goauto): 完善替代采集与颜色图片详情 (#184 #185)
36d7285beb3f0c8441e38b4e9e81e5cf7256b603
0f448673dbec00a351c397d30926f0c5090a1141
20260901-3aab1f0.zip
3aab1f0
/home/goauto/releases/20260901-102000-3aab1f0
/home/goauto/releases/20260901-090504-c07c345
goauto.service
/api/v1/health
40569d90bc6bcbd3b3edb14aca41f336a09c07f9a6b2dcadc1b06eca1184ca88
8612553a28524d72e095e8e855a5b6a9789db59f0ff9c521e8a37b551c64cb71
/api/agent/v1/app/latest
DEVICE_TOKEN_INVALID
agent_app_release
agent_app_release_setting
/tmp
用户在 Agent 0.9.35 点击“下载新版本”时收到“服务端处理失败”。根因确认:goauto.service 以 goauto:goauto 运行,但当前发布目录的 var/goauto-agent-releases 及 APK 属主为 root:root;目录 750、文件 640 导致服务进程无法 stat/open APK,版本检查可成功而下载接口返回 INTERNAL_ERROR。
goauto:goauto
var/goauto-agent-releases
root:root
750
640
stat/open
INTERNAL_ERROR
已修复当前发布 /home/goauto/releases/20260901-102000-3aab1f0:
current
goauto
未安装手机、未触发采购/订单/付款。等待用户在手机重新点击下载验证真实设备链路。
No dependencies set.
The note is not visible to the blocked user.
原始需求摘要
来源:用户于 2026-09-01 反馈采集任务 110 失败原因为「未找到 PDD 商品规格入口」(实际是链接失效被跳转到 PDD 首页),但任务详情里没有出现「采集替代商品」按钮。核验后确认根因是服务端按错误码白名单判断是否显示该按钮,白名单覆盖不了这类场景。用户明确要求:不应该用错误码白名单限制过多,任务失败了就应该显示该按钮,交给工人判断失败原因、决定是否需要替换商品;并要求单独建单,不并入 #165 或 #178。
目的:让「采集替代商品」的可见性既不依赖一份不完整的错误码白名单,也不强制要求当前任务本身处于失败状态,改为把"这个 PDD 商品是否值得替换"的判断完全交给人工——判断依据可以来自 Agent 自身的失败记录,也可以来自外部验证(其他手机、网页)或单纯发现了更好的商品;同时保留必要的状态机保护,针对新引入的行为(对健康商品发起替换会取消其未执行的采购任务)加强确认提示,并新增一道系统硬门槛:该商品名下有采购任务正在执行或结果待核对时,直接禁止替换,不留给人工临场判断,避免放宽准入扩大误触代价。
基线与已核实事实
代码基线:
4343ab5(2026-09-01)。核验日期 2026-09-01。功能来源工单 #130(33d4bb1 feat(#130): collect replacement products from Agent),后续 #132、#155、#157、#174 在同一路径上有过维护,本单是对 #130 既有准入条件的一次收窄反转。server/app/goauto/replacement/origin.go:InspectOrigin(采集来源originType=="collection"分支,:37-46):task.PDDProductID != nil(:65-68,否则DisabledReason="原任务没有可替换的拼多多商品")——本单保留;active状态的替换记录(activeForOrigin,:80-83,否则DisabledReason="该商品已经发起替换")——本单保留;:78-91)——本单保留;task.Status == failed(:104-106,否则DisabledReason="只有失败任务可以替换商品")——本单去除;task.ErrorCode精确等于PDD_LINK_INVALID或PDD_GOODS_SOLD_OUT(:108-110,ErrorPDDLinkInvalid/ErrorPDDGoodsSoldOut常量定义于origin.go:15-16,仅在此处被引用,无其他耦合);否则DisabledReason="当前失败原因不属于商品失效或售罄"——本单去除。renderCollectionDetail(TaskHistoryFragment.kt)当前对任意任务状态都无条件调用renderReplacementAction,能否显示按钮完全由服务端InspectOrigin的返回值决定;去掉第 4、5 两道门槛后,成功任务的详情页会自然出现该按钮,无需额外的前端改动。TaskHistoryFragment.kt:92-100(ReplacementActionPolicy.presentation):eligible=false且无mappingStatus/activationStatus时,呈现结果为HIDDEN,按钮直接不渲染,不显示任何理由文字(:99「ReplacementPresentation.HIDDEN -> Unit」)。这是造成用户「看不出为什么按钮不见了」的直接原因。PDD_DETAIL_ENTRY_FAILED一类),不在上述第 5 条白名单内,因此即便真实原因是商品失效,按钮也不出现。replacement/activation.go:239-289核实:批量把当前挂在该 PDD 商品下的所有虾皮商品改指向新商品并清空其规格映射、取消相关未开始的采购任务、把旧 PDD 商品标记停用,一个事务内批量完成。failed限制后新增的真实后果:此前"取消未开始执行的采购任务"这一步隐含的前提是"商品已经失败,挂在它上面的采购任务本来也成不了";一旦允许对状态正常、甚至采购中的健康商品发起替换,这一步会变成对着完全正常的商品静默取消其未执行的采购任务——这是本次放宽新引入的行为,此前被"必须先失败"这个前提掩盖,现有确认弹窗文案完全没有提及,必须在本单一并加强提示。TaskHistoryFragment.kt:809-818,confirmReplacement)文案仅为「用当前商品替换 #N 的失效商品?」,未提示上述批量影响范围。CorrectAndActivate(replacement/activation.go:70+)已存在,用于纠正误操作的替换记录,说明项目本身已经预期这类操作可能判断有误、需要纠正路径,但纠正本身仍是有成本的额外操作,不能作为放宽准入的免责理由。applyPurchaseReplacement(activation.go:309-330)已经区分处理:pending/spec_probe_pending取消;running/order_submit_started跳过不动(SkippedRunningTasks);其余终态保留不动。核对purchase/lifecycle.go的Claim/Start/Result全链路,均不重新校验pdd.Status,因此被跳过的正在执行任务不会被中途打断。但activate()仍会无条件把源 PDD 商品标记为disabled、把该商品下全部虾皮商品关联批量改到新商品(activation.go:255-293)——这一步和"是否有采购正在执行"完全无关,即便任务本身不受影响,也会在一笔采购正处于执行、尤其是刚越过order_submit_started这个最接近创建订单的不可逆边界时,制造一个货不对板的时间窗口:任务用旧商品的快照继续跑,但商品本身已经被标记废弃、关联已经改走,一旦该任务紧接着成功创建订单,会让审计和后续排查变得混乱。models.PurchaseTaskStatusRunning、PurchaseTaskStatusOrderSubmitStarted、PurchaseTaskStatusOrderResultUnknown均已在models/purchase.go:19-23定义,无需新增状态常量。判断边界
PDD 商品必须存在(task.PDDProductID != nil)、该 PDD 商品当前无进行中的替换——因为它们是纯状态机保护,不涉及对失败原因或商品价值的判断。任务状态为 failed不再作为门槛,用户已明确要求去除。failed限制后取消采购任务这一步可能作用于健康商品),因此确认弹窗文案必须加重,明确列出「会取消该商品下尚未开始执行的采购任务」,作为放宽准入的配套措施,不是可选项,用户已在 2026-09-01 确认此项。原任务没有可替换的拼多多商品(task.PDDProductID == nil)这一门槛是否也需要复核。经核实,采集任务从创建起就必须关联一个 PDD 商品(无论是新建还是复用),正常情况下该字段不会为空;本单不改变这一条。目标
ErrorCode是否命中白名单,也不再要求任务状态必须为failed;只要该 PDD 商品存在、当前没有进行中的替换、且没有正在执行或结果待核对的采购任务,任意状态(成功或失败)的采集任务详情都能发起替换,把"这个商品是否值得替换"的判断交给操作人工,同时用系统硬事实挡住最危险的时间窗口。task.PDDProductID != nil、无进行中替换这两条既有状态机保护,不再保留failed状态要求;新增第三条硬门槛:该 PDD 商品名下不存在running、order_submit_started、order_result_unknown状态的采购任务。originType=="purchase")分支是否同步放宽,本单需要用户单独确认(详见「非目标」)。running/order_submit_started/order_result_unknown被拦截时,明确告知"该商品有采购任务正在执行或结果待核对,暂不能替换,请稍后重试",与前两条放宽引入的提示(批量改关联/清映射、取消未执行采购任务)在同一处按场景分别展示,不混在一句话里。非目标
originType=="purchase")分支的准入条件。采购失败与采集失败的业务后果不同(采购失败可能已经产生过订单尝试),是否同步放宽需要单独评估,本单只处理采集来源分支,除非用户在确认阶段明确要求一并处理。任务状态必须为 failed不属于本条非目标,已按用户要求去除,见「目标」;新增的"无正在执行/结果待核对采购任务"门槛属于本单新增范围,见「目标」第 2 条。activate()的关联更新、映射清空、采购任务取消范围),只改变"按钮何时可见"。CorrectAndActivate纠正。前置依赖与并行性
固定实施方案
replacement/origin.go的InspectOrigin(originType=="collection"分支)删除错误码白名单判断(:108-110)和task.Status == failed判断(:104-106),改为:PDDProductID非空检查、无进行中替换检查通过后,进入第 2 步的采购状态检查,全部通过才result.Eligible = true;不再读取或比较errorCode,不再读取task.Status(status变量若因此无其他用途,一并清理)。result.SourceProductID关联purchase_task.pdd_product_id,检查是否存在状态为running、order_submit_started或order_result_unknown的记录(存在性查询即可,不需要拉全部字段)。命中任意一条,设置result.DisabledReason = "该商品有采购任务正在执行或结果待核对,暂不能替换,请稍后重试"并返回,不进入后续判断。该检查放在错误码/失败状态判断被移除后的位置,作为新的强制门槛。ErrorPDDLinkInvalid/ErrorPDDGoodsSoldOut常量因本单不再被任何逻辑引用,随本单一并删除,不保留死代码。confirmReplacement(TaskHistoryFragment.kt:809-818)确认弹窗文案更新,同时列出:批量影响范围(关联虾皮商品数量可从detail/接口已有信息中获取则一并展示,无法预知具体数量时至少说明"会影响该商品下全部关联的虾皮商品")**和「会取消该商品下尚未开始执行的采购任务」**这两条,后者不因原任务是否失败而省略;被第 2 步拦截时展示的独立提示文案不经过此弹窗(按钮不可用或点击后即提示拦截原因,不进入替换确认流程)。ReplacementPresentation.HIDDEN场景在放宽后是否仍会出现(应仅剩「无可替换 PDD 商品」「已有进行中替换」「有采购任务正在执行/结果待核对」三种),若这些情况下仍无任何提示文字,评估是否需要补充简短提示,提升可理解性;是否在本单一并做由用户在确认阶段决定,默认不做,作为相邻问题记录。设计证据
现有按钮显示条件与确认弹窗文案的调整,不新增页面、不改变整体交互结构;确认弹窗文案变化需要标注截图交用户确认(放宽前/放宽后对比、新文案内容,需体现「取消未执行采购任务」和「有采购任务正在执行/结果待核对,暂不能替换」两句新增提示)。
确定文案(2026-09-01 用户确认)
场景一:可以替换时的确认弹窗(
confirmReplacement)场景二:被拦截时(该商品名下存在
running/order_submit_started/order_result_unknown采购任务)不使用弹窗,比照
ReplacementPresentation.MANUAL_REQUIRED/MATCHING的既有做法,新增枚举值(如PURCHASE_IN_PROGRESS)原地渲染静态文字:验收标准
DisabledReason与既有文案一致。running、order_submit_started或order_result_unknown状态的采购任务,按钮不显示或点击后明确提示拦截原因,不进入替换确认流程;任务结束(成功/失败/取消)后按钮恢复可用(其余条件满足时)。PDDProductID为空时按钮仍不显示(既有保护不受影响)。originType=="purchase")分支行为未被改动,其既有测试通过。activate()批量替换的行为、范围、事务性均未改变;running/order_submit_started任务继续被跳过不动的既有行为不受影响(新门槛是提前拦截整个替换发起,不是改变activate()内部对这些任务的处理)。必测场景
failed),按钮正确出现(此前应不出现,本单行为反转,必须专门验证)。PDDProductID为空,按钮不出现。running采购任务的 PDD 商品发起替换:按钮不可用/点击即提示拦截原因,不能进入确认弹窗;该running任务不受影响,继续正常执行到结束。order_submit_started采购任务的 PDD 商品发起替换:同上,明确拦截。order_result_unknown采购任务的 PDD 商品发起替换:同上,明确拦截。running/order_submit_started任务跳过不动的既有行为不变)。风险与安全门禁
CorrectAndActivate可纠正),但纠正本身是额外人工成本,不作为放宽的免责理由。文档影响
有长期文档影响。 #130 相关的替换业务规则(含准入条件)若在 Wiki 业务规则页面有记录,需同步更新为「按状态而非错误码白名单判断,且新增采购执行中/结果待核对的硬性拦截」,并说明确认弹窗新增的批量影响提示与拦截提示。在线回读 revision 后完成一次
sync和一次sync --check,revision 回写本工单;若核实后发现相关内容此前未曾写入 Wiki,在工单说明并跳过同步。状态
待确认(2026-09-01 创建;同日三次修订与确认弹窗文案均已由用户确认:去除错误码白名单、去除
failed状态要求、新增"无正在执行/结果待核对采购任务"硬门槛、确定两处提示文案。方案已完整,可进入实施。)????(2026-09-01)???????? Gitea MCP,??????? Gitea API;????????????,????????????
????:
c07c345????????:??????????/??????;??running?order_submit_started?order_result_unknown???????;Android ???????????????;????????????????????? Server/Android ???????,?? #185 ???????????????实施完成,等待验收
实现
running、order_submit_started或order_result_unknown采购任务时,服务端拒绝发起,Android 明示“暂不能替换”。PDD_LINK_INVALID/PDD_GOODS_SOLD_OUT”规则保持不变。验证与证据
go test ./...与服务端构建通过;替代来源边界及采购来源回归测试通过。python dev_scripts/harness.py check --strict通过。verify -Component all的服务端阶段通过,但被本次未改动的web/src/views/goauto/purchase-tasks/index.vue存量 lint(制表符/空格混用,24 errors)阻断;Web 生产构建另行通过。3aab1f0 feat(goauto): 完善替代采集与颜色图片详情 (#184 #185)。36d7285beb3f0c8441e38b4e9e81e5cf7256b603;Android-Agent-API-Contract revision0f448673dbec00a351c397d30926f0c5090a1141;已完成一次sync和一次sync --check。发布与未验证项
20260901-3aab1f0.zip;线上切换、服务重启及设为当前 Agent 版本属于高风险操作,等待本次发布前人工确认。线上发布完成
3aab1f0。/home/goauto/releases/20260901-102000-3aab1f0;回滚目录:/home/goauto/releases/20260901-090504-c07c345,旧发布未删除。running、order_submit_started、order_result_unknown的采购任务均为 0。goauto.service为active;内部及外部/api/v1/health均为 HTTP 200,外部 Admin 首页为 HTTP 200;最近发布窗口无 service error 日志。40569d90bc6bcbd3b3edb14aca41f336a09c07f9a6b2dcadc1b06eca1184ca88。8612553a28524d72e095e8e855a5b6a9789db59f0ff9c521e8a37b551c64cb71,与本地构建一致。/api/agent/v1/app/latest无 Token 返回预期DEVICE_TOKEN_INVALID,证明外部路由进入业务鉴权层。agent_app_release/agent_app_release_setting现有服务契约完成登记与当前版本切换,并回读数据库及文件验证。/tmp上传包与解压暂存目录;正式发布目录、历史发布和 APK 版本均保留。线上 Agent APK 下载权限修复
用户在 Agent 0.9.35 点击“下载新版本”时收到“服务端处理失败”。根因确认:
goauto.service以goauto:goauto运行,但当前发布目录的var/goauto-agent-releases及 APK 属主为root:root;目录750、文件640导致服务进程无法stat/openAPK,版本检查可成功而下载接口返回INTERNAL_ERROR。已修复当前发布
/home/goauto/releases/20260901-102000-3aab1f0:current解析目标及目录内无符号链接;goauto:goauto;750、文件640,未放宽公开读取;goauto用户成功完整读取 versionCode 50 APK,SHA-256 为8612553a28524d72e095e8e855a5b6a9789db59f0ff9c521e8a37b551c64cb71;goauto.service为active,内部和外部健康接口均为 HTTP 200。未安装手机、未触发采购/订单/付款。等待用户在手机重新点击下载验证真实设备链路。