免责声明:是的,我确实使用了 Claude 来帮助我修复这个问题:
当前运行的 Discourse 核心版本为提交 5ee8e24(2026-08-19)。Patreon 同步过程没有报错,但同步的用户数为零:
Patreon sync complete: 63 pledges, 0 users synced
API 响应均为 status=200,因此请求本身是正常的。在 Rails 控制台中进行追踪时,我发现插件构建的成员请求是正确的,并且确实在 member 资源中请求了 email 字段:
fields[member]=full_name,last_charge_date,last_charge_status,currently_entitled_amount_cents,patron_status,email
使用正确范围的创作者令牌(已授予 campaigns.members[email] 权限)直接调用该端点时,email 字段存在于 member 对象中,而不是 included 中的 user 对象上。对单个成员的获取也证实了这一点:
data.attributes.email => "patron@example.com" # 存在
included[].attributes.email => (不存在) # user 对象仅包含 full_name
因此,在 campaign 成员列表中,member.attributes.email 是有值的(我的 63 名成员中有 46 名;其余人限制了共享或为免费关注者),而 included 中的 user 条目根本没有 email 字段。
问题出在 Patreon::ApiVersion::V2.extract(lib/api_version/v2.rb)中。它从 included 中的 user 对象构建 users 映射:
ruby
(member_data["included"] || []).each do |entry|
if entry["type"] == "user" && entry["attributes"]["email"].present?
users[entry["id"]] = entry["attributes"]["email"].downcase
end
end
由于这些 user 对象从未携带 email,映射结果为空,导致每个成员的 email 匹配都失败,因此同步数为 0。
改为从 member 条目中读取 email(使用 member.relationships.user.data.id 中已有的 user id 作为键,这与用于 pledges 的 patron_id 相同)修复了我安装环境中的问题:同步数从 0 变为 46 users synced。我应用的更改如下:
diff
+ users[patron_id] = attrs["email"].downcase if attrs["email"].present?
pledges[patron_id] = attrs["currently_entitled_amount_cents"]
declines[patron_id] = attrs["last_charge_date"] if attrs["last_charge_status"] == "Declined"
end
-
- (member_data["included"] || []).each do |entry|
- if entry["type"] == "user" && entry["attributes"]["email"].present?
- users[entry["id"]] = entry["attributes"]["email"].downcase
- end
- end
我不确定完全移除 included 循环是否适用于所有情况——如果某些配置确实在 user 对象上接收 email,你可能希望保留它作为后备方案,而不是替换它。在此发布观察到的行为以及在此处有效的最小更改,希望这对大家有用,或者能指出正确的修复方向。