feat(users): level integral XP schema + AKSRT_User_Level runtime

Rewrite /users/level to Zibll-like user_level_s/max/opt and
user_integral_opt. Runtime grants XP with day_max, resolves level,
hooks login/register/post/comment/like/favorite/follow. Profile
signals read integral-backed levels. audit errors=0.
This commit is contained in:
kate522 committed 2026-07-17 01:05:39 +08:00
1 parent e80d8ea45d
commit 1b0eed6ef7
8 files changed
+658 -141

No files matched your search

+62 -13
View File
@@ -1,33 +1,82 @@
/**
* User level + integral XP (Zibll user_level_s / user_level_opt / user_integral_opt).
*/
export const INTEGRAL_CATALOG = [
{ key: 'sign_up', label: '首次注册', default: 20, group: '用户' },
{ key: 'sign_in', label: '每日登录', default: 5, group: '用户' },
{ key: 'followed', label: '被关注', default: 5, group: '用户' },
{ key: 'post_new', label: '发布文章', default: 5, group: '文章' },
{ key: 'post_like', label: '文章获赞', default: 1, group: '文章' },
{ key: 'post_favorite', label: '文章被收藏', default: 2, group: '文章' },
{ key: 'comment_new', label: '发表评论', default: 2, group: '文章' },
{ key: 'comment_like', label: '评论获赞', default: 1, group: '文章' },
{ key: 'bbs_posts_new', label: '发布帖子', default: 3, group: '论坛' },
{ key: 'bbs_score_extra', label: '帖子被加分', default: 1, group: '论坛' },
{ key: 'bbs_essence', label: '帖子评为精华', default: 2, group: '论坛' },
{ key: 'bbs_posts_hot', label: '帖子成为热门', default: 2, group: '论坛' },
{ key: 'bbs_plate_new', label: '创建版块', default: 2, group: '论坛' },
{ key: 'bbs_plate_hot', label: '版块成为热门', default: 2, group: '论坛' },
{ key: 'bbs_adopt', label: '回答被采纳', default: 2, group: '论坛' },
{ key: 'bbs_comment_hot', label: '评论成为神评', default: 2, group: '论坛' },
]
export function defaultLevelOpt(max = 10) {
const opt = {}
for (let i = 1; i <= max; i++) {
// Rough Zibll-like curve: LV1=0, then grows
opt['upgrade_integral_' + i] = i === 1 ? 0 : Math.round(20 * Math.pow(i - 1, 1.6))
opt['name_' + i] = 'Lv.' + i
}
return opt
}
export function defaultIntegralOpt() {
const opt = { day_max: 100 }
for (const row of INTEGRAL_CATALOG) {
opt[row.key] = row.default
}
return opt
}
export const levelSchema = {
key: 'users.level',
title: '用户等级',
icon: 'fas fa-level-up-alt',
description: '配置等级展示前缀和等级阈值,当前可基于积分余额自动计算。',
description: '等级与经验值:最高等级、升级阈值、经验获取矩阵(对齐 Zibll user_level / user_integral)。',
defaults: {
user_level_enabled: false,
user_level_s: true,
user_level_public_display: true,
user_level_source: 'credits_balance',
user_level_prefix: 'Lv.',
user_level_thresholds: '1|0\n2|30\n3|100\n4|300',
user_level_max: 10,
user_level_opt: defaultLevelOpt(10),
user_integral_opt: defaultIntegralOpt(),
user_level_notify: true,
},
groups: [
{
key: 'display',
title: '等级显示',
icon: 'fas fa-signal',
fields: [
{ key: 'user_level_enabled', label: '启用等级系统', type: 'switch', default: false },
{ key: 'user_level_public_display', label: '公开页显示等级', type: 'switch', default: true, dependsOn: { key: 'user_level_enabled', value: true } },
{ key: 'user_level_prefix', label: '等级前缀', type: 'input', default: 'Lv.', dependsOn: { key: 'user_level_enabled', value: true } },
{ key: 'user_level_s', label: '启用用户等级', type: 'switch', default: true },
{ key: 'user_level_public_display', label: '公开页显示等级', type: 'switch', default: true, dependsOn: { key: 'user_level_s', value: true } },
{ key: 'user_level_prefix', label: '等级前缀', type: 'input', default: 'Lv.', dependsOn: { key: 'user_level_s', value: true } },
{ key: 'user_level_max', label: '最高等级', type: 'number', default: 10, min: 3, max: 30, dependsOn: { key: 'user_level_s', value: true } },
{ key: 'user_level_notify', label: '升级通知', type: 'switch', default: true, dependsOn: { key: 'user_level_s', value: true } },
],
},
{
key: 'rules',
title: '等级规则',
icon: 'fas fa-list-ol',
key: 'level_opt',
title: '等级升级阈值',
fields: [
{ key: 'user_level_source', label: '等级来源', type: 'radio', default: 'credits_balance', options: [{ label: '积分余额', value: 'credits_balance' }, { label: '手动 Meta', value: 'manual_meta' }], dependsOn: { key: 'user_level_enabled', value: true } },
{ key: 'user_level_thresholds', label: '等级阈值', type: 'textarea', default: '1|0\n2|30\n3|100\n4|300', help: '每行一个等级,格式:等级|所需积分,例如 3|100', dependsOn: { key: 'user_level_enabled', value: true } },
{ key: 'user_level_opt', label: '升级经验配置', type: 'json', default: defaultLevelOpt(10), help: 'upgrade_integral_N 为升到 N 级所需累计经验' },
],
},
{
key: 'integral',
title: '经验值得分',
fields: [
{ key: 'user_integral_opt', label: '经验值获取', type: 'json', default: defaultIntegralOpt(), help: 'day_max + 各行为得分;对应功能未开则将分值设 0' },
],
},
],