新增群积分通货膨胀策略

1. 新增群聊插件积分真实消耗统计,区分成员持有积分与 SYSTEM 沉淀积分。

2. 为 plugin_points_cost 注解接入按群动态倍率,积分消耗偏低时自动提高实际扣费。

3. 优化积分不足与扣费成功提示,展示基础价、实际价与当前通胀倍率。
This commit is contained in:
liuwei
2026-04-27 13:35:13 +08:00
parent 59db63937d
commit 66ac0a7e89
2 changed files with 249 additions and 13 deletions

View File

@@ -610,6 +610,90 @@ class PointsDBOperator(BaseDBOperator):
self.LOG.error(f"获取群组积分统计信息失败: {e}")
return stats
def get_group_plugin_consumption_stats(self, group_id: str, lookback_hours: int = 72) -> Dict[str, Any]:
"""
获取群聊插件积分消耗统计信息。
这里专门为“积分通货膨胀”策略提供数据支撑,只统计真正会销毁积分总量的插件扣费:
1. 用户之间转账、打劫、保释这类行为,本质上只是积分在用户之间流转,不算真实消耗;
2. `plugin` 来源的 `spend` 流水,才代表群积分池被真正消耗掉了;
3. 因此通胀策略应基于“当前群积分存量”与“最近一段时间插件真实消耗量”的关系来判断。
Args:
group_id: 群ID
lookback_hours: 统计最近多少小时的插件消耗
Returns:
包含群积分存量、系统沉淀积分、最近插件消耗量等字段的统计字典
"""
normalized_hours = max(1, int(lookback_hours or 72))
stats = {
"group_id": group_id,
"lookback_hours": normalized_hours,
"total_users": 0,
"active_points_total": 0,
"system_points_total": 0,
"plugin_spend_points": 0,
"plugin_spend_count": 0,
"plugin_spend_ratio": 0.0,
}
if not group_id:
return stats
try:
# 用户积分存量统计中要把 SYSTEM 单独拆出来:
# SYSTEM 账户通常用于承接抽水、保释金等沉淀积分,
# 这些积分已经不在普通成员手里流通,所以需要单独记录,避免干扰真实“群成员持有积分”。
points_result = self.execute_query(
"""
SELECT
SUM(CASE WHEN user_id <> 'SYSTEM' THEN 1 ELSE 0 END) AS total_users,
SUM(CASE WHEN user_id <> 'SYSTEM' THEN total_points ELSE 0 END) AS active_points_total,
SUM(CASE WHEN user_id = 'SYSTEM' THEN total_points ELSE 0 END) AS system_points_total
FROM t_user_points
WHERE group_id = %s
""",
(group_id,),
fetch_one=True,
) or {}
# 最近插件消耗采用时间窗口统计。
# 只看 plugin 来源的 spend能更准确反映“带注解功能”对积分池的真实消耗效率。
time_boundary = datetime.now() - timedelta(hours=normalized_hours)
spend_result = self.execute_query(
"""
SELECT
SUM(CASE WHEN transaction_type = 'spend' AND source = %s THEN ABS(points) ELSE 0 END)
AS plugin_spend_points,
SUM(CASE WHEN transaction_type = 'spend' AND source = %s THEN 1 ELSE 0 END)
AS plugin_spend_count
FROM t_point_transactions
WHERE group_id = %s
AND created_at >= %s
""",
(PointSource.PLUGIN.value, PointSource.PLUGIN.value, group_id, time_boundary),
fetch_one=True,
) or {}
stats["total_users"] = int(points_result.get("total_users") or 0)
stats["active_points_total"] = int(points_result.get("active_points_total") or 0)
stats["system_points_total"] = int(points_result.get("system_points_total") or 0)
stats["plugin_spend_points"] = int(spend_result.get("plugin_spend_points") or 0)
stats["plugin_spend_count"] = int(spend_result.get("plugin_spend_count") or 0)
active_points_total = stats["active_points_total"]
if active_points_total > 0:
stats["plugin_spend_ratio"] = round(
stats["plugin_spend_points"] / active_points_total,
6,
)
return stats
except Exception as e:
self.LOG.error(f"获取群聊插件积分消耗统计失败: {e}")
return stats
def imprison_user(self, user_id: str, group_id: str, hours: int = 24, reason: str = None) -> bool:
"""关押用户
Args: