fix(会员): 回灌后收敛充点商品目录并清理退役会员商品行

- profile.rs 新增 converge_default_profile_recharge_product_config,在存量库读取路径补齐缺失的 points_1280 / points_3280 档位。

- 同一步骤删除已退役的 membership 类型充点商品配置行,避免冲突迁移把旧 member_* 行原样带回后残留。

- 存量库充值商品目录因此幂等收敛到当前六档点卡,不再停留在旧四档 + 四行会员商品。
This commit is contained in:
2026-10-03 17:24:00 +08:00
parent 21f69ef13b
commit af0721fb78
@@ -8855,6 +8855,7 @@ pub fn init_profile_membership_plan_catalog(ctx: &ReducerContext) {
fn ensure_default_profile_recharge_product_config(ctx: &ReducerContext) {
if ctx.db.profile_recharge_product_config().count() > 0 {
migrate_legacy_default_profile_recharge_product_config(ctx);
converge_default_profile_recharge_product_config(ctx);
return;
}
@@ -8961,6 +8962,64 @@ fn migrate_legacy_default_profile_recharge_product_config(ctx: &ReducerContext)
}
}
/// 存量库 / 冲突迁移回灌后把充点商品收敛到当前六档目录。
///
/// 迁移目的:冲突迁移会清库发布再回灌旧 `profile_recharge_product_config`;旧库里的
/// `member_*` 会员商品行会被原样带回,而新增的 `points_1280` / `points_3280` 不在旧包里,
/// [`migrate_legacy_default_profile_recharge_product_config`] 又只归一旧行、不补缺档。
/// 这里在读取路径幂等补齐缺失档位并删除已退役的会员商品配置行。
fn converge_default_profile_recharge_product_config(ctx: &ReducerContext) {
let now = ctx.timestamp;
for (sort_order, product) in runtime_profile_recharge_point_products()
.into_iter()
.enumerate()
{
let product_id = product.product_id.clone();
if ctx
.db
.profile_recharge_product_config()
.product_id()
.find(&product_id)
.is_some()
{
continue;
}
ctx.db
.profile_recharge_product_config()
.insert(ProfileRechargeProductConfig {
product_id: product.product_id,
title: product.title,
price_cents: product.price_cents,
kind: product.kind,
points_amount: product.points_amount,
bonus_points: product.bonus_points,
duration_days: product.duration_days,
badge_label: product.badge_label,
description: product.description,
enabled: true,
sort_order: sort_order as i32,
created_by: PROFILE_RECHARGE_PRODUCT_SYSTEM_USER_ID.to_string(),
created_at: now,
updated_by: PROFILE_RECHARGE_PRODUCT_SYSTEM_USER_ID.to_string(),
updated_at: now,
});
}
let retired_membership_product_ids = ctx
.db
.profile_recharge_product_config()
.iter()
.filter(|row| row.kind == RuntimeProfileRechargeProductKind::Membership)
.map(|row| row.product_id)
.collect::<Vec<_>>();
for product_id in retired_membership_product_ids {
ctx.db
.profile_recharge_product_config()
.product_id()
.delete(&product_id);
}
}
fn profile_recharge_product_config_rows(
ctx: &ReducerContext,
include_disabled: bool,