独立站跑起来以后,第一个绕不开的问题不是"要不要做会员",而是"钱从哪来、成本压在哪"。这个项目选的路径很有意思:会员不是靠付费直接买,而是靠邀请好友升级——你邀请足够多的人,你就是会员;而会员的核心权益,是更大的 AI Token 配额。
为什么是 Token 配额?因为 a21 那篇讲过,AI 功能每次都真金白银地花 token——一个普通用户问 AI 十个问题,就可能消耗几百到上千个计量 token。AI 成本是独立站最真实、最不可忽视的开支。把它变成"会员权益"的锚点,既是成本控制,也是收入引擎。这篇就把这条"邀请 → 会员 → 配额 → 重置/降级"的经济链拆开。
会员体系的核心锚点是 AI Token 配额。配额不是拍脑袋定的,它是一张"分档表":
public enum AiTokenQuotaEnum {
// text, value, limit5h, limitWeek, imageGenWeek, imageSearchWeek, imageAnalysisWeek
NORMAL("普通用户", 0, 80000L, 300000L, 5L, 100L, 10L),
PRO ("Pro会员", 1, 180000L, 700000L, 15L, 500L, 30L),
PLUS ("Plus会员/管理员", 2, 400000L, 1500000L, 30L, 2000L, 100L);
// ...
}
这张表藏着整个配额体系的精髓。看列:limit5h(5 小时配额)和 limitWeek(每周配额)——对应 a21 那篇讲过的 Redis 滚动窗口;limitImageGenWeek(每周生图数)、limitImageSearchWeek(每周以图搜图次数)、limitImageAnalysisWeek(每周图像分析次数)——三个"按功能维度"的配额。
为什么要按功能拆配额,而不是只按"总 token"?因为不同 AI 功能的成本差异巨大:生成一张图可能顶几十次文本问答,以图搜图要调向量模型,图像分析要调多模态。如果只设一个总 token 池,用户会拿它干最贵的事(疯狂生图),把成本打爆。按功能拆配额,等于给每个功能单独划了一笔预算——成本控制要跟着"单位成本"走,不能一刀切。这是 AI 配额设计的第一课。
配额表的"数字"也不是随便填的,它背后是一次次成本核算:生一张图平均花多少 token、以图搜图调一次向量模型多少钱、图像分析一次多模态调用多少钱——每个功能单独算一笔账,再倒推出"每周给多少合理"。配额的上限,本质是"你能承担的成本上限"——普通用户的配额,大概就是"你愿意为一个免费用户承担的 AI 成本";会员的配额,就是"付费用户值得更大投入"。
还有那个"未知档位回落普通"的兜底,值得再品:它不只是防御非法输入,还是一个渐进放行的策略——新功能、新档位没配好,宁可少给不可多给。风控和成本场景的兜底,方向永远是"从严"。在成本系统里,所有未知都按最便宜的算——这是避免成本失控的保险丝。
最后,这张表和 a21 的"模型倍率"是配套的:倍率管"一次调用花多少",配额表管"一共能花多少"。一个管单价,一个管总量,两者合起来才是完整的成本控制。单价 × 总量,才是真实的 AI 成本——只控一个,另一个就会漏。
配额怎么落到具体请求上?回看 a21 那篇:get5hLimit 按会员类型读这张表:
private long get5hLimit(int memberType, boolean isAdmin) {
AiTokenQuotaEnum quotaEnum = AiTokenQuotaEnum.getEnumByValue(isAdmin ? 2 : memberType);
if (quotaEnum == null) quotaEnum = AiTokenQuotaEnum.NORMAL; // 未知档位默认普通
return quotaEnum.getLimit5h();
}
注意那个兜底:quotaEnum == null → NORMAL——就算传进来一个非法会员类型,也回落到普通用户,绝不因为"查不到配额"就放行。配额查询的兜底要"从严"——查不到就按最低档,宁可少给,不能多给。
配额表对应着三档会员:普通(NORMAL)、Pro、Plus。每档不只是"token 多一点",而是一整套权益包:
普通用户 —— 5小时 8万 token,每周 30万,生图每周 5 张
Pro 会员 —— 5小时 18万,每周 70万,生图 15 张
Plus 会员 —— 5小时 40万,每周 150万,生图 30 张
数字从 8 万到 40 万、从 5 张到 30 张,差异都是"倍数级"的——这给了用户清晰的"升级动力":普通用户用着用着撞到配额线,看到 Pro 的额度,自然会想升。会员的权益差异要大到"一眼可见",否则用户感觉不到升级的必要。而这三档之所以是"倍数级"而不是"多一点点",正是为了放大这个感知。
这里还有个容易被忽略的细节:普通用户的配额不是"0"——它足够日常轻度使用(每天问几个问题、生一两张图),只是不够重度使用。这个设计很关键:免费用户不能"完全不能用",否则他们永远感受不到 AI 的价值,也就没有升级动力。免费档给到"尝到甜头、但不够尽兴"的程度,用户才会为了"尽兴"去升级。给太少了用户直接走了,给太多了没人升级,中间这条线就是免费和付费的分界——而这条线,是要靠数据一点点试出来的,不是拍脑袋一拍就定的。
会员还有空间权益——SpaceLevelEnum 定义了不同档位的空间容量(存储上限、图片数上限)。普通用户的空间配额有限,会员更大。关键设计是:空间配额是"软性"的——用户超过了不删数据,只是不能继续上传(后面降级那节会细讲)。"超了不删、但停下"是内容产品最人道的配额策略——既保护用户的劳动成果,又守住成本。
前端有个 MemberMechanismModal(会员机制弹窗),把权益讲清楚:三档的配额、模型倍率(Qwen3.5-Plus 2.55 倍、DeepSeek-V4-Pro 16.36 倍)、升级条件。会员权益必须"讲得明白",用户才愿意升级——一个权益说不清楚的会员,等于没有会员。

三档会员还有个隐性的设计:它不是"付费直购",而是"行为获取"。普通用户不能直接花 30 块买 Pro,得靠邀请——这个"用行为换权益"的机制,比"花钱买"多了一层"投入感":用户不是"买"来的会员,是"挣"来的会员,会更珍惜、更有黏性。心理学上这叫"投入效应"——你为一样东西付出了努力,就会更看重它。用"挣"代替"买",是这个会员体系最聪明的地方之一——它把获客成本变成了用户的主动行为。
空间权益也值得展开:SpaceLevelEnum 定义了每档的空间容量(存储 MB + 图片数上限),会员档位越高空间越大。为什么空间也要分档?因为空间是实打实的存储成本——图片站每张图都占 COS 空间,会员给更多空间,既是权益也是成本分配。会员的每一档权益,背后都对应着一笔真实的成本——token 是算力成本,空间是存储成本,权益设计要"每个权益都有成本锚点",否则就是无本的承诺。
还有一个容易被忽略的点:会员档位的"进可升、退可降"——用户可以靠邀请升到 Pro,到期自动降回普通,然后再邀请再升。这个"上下流动"的设计,让会员不是"一次决定终身",而是"持续的行为回报"。用户每次看到自己的档位,都是在提醒自己"该拉新了"。会员体系的流动性,比固定身份更有激励——固定身份让人躺平,流动身份让人持续投入。
会员不是直接买的,是靠邀请升级的。整个链条从"注册时填邀请码"开始:
public long userRegister(String email, String userPassword, String checkPassword, String code, String inviteCode) {
Long inviterId = 0L;
if (StrUtil.isNotBlank(inviteCode)) {
// 用邀请码找到邀请人
QueryWrapper<User> inviterQuery = new QueryWrapper<>();
inviterQuery.eq("inviteCode", inviteCode);
User inviter = userService.getOne(inviterQuery);
if (inviter != null) inviterId = inviter.getId();
}
user.setInviterId(inviterId); // 记录谁邀请了我
if (inviterId > 0) {
// 生成一条邀请记录
InviteRecord inviteRecord = new InviteRecord();
inviteRecord.setInviterId(inviterId);
inviteRecord.setInviteCode(inviteCode);
// ...
inviteRecordMapper.insert(inviteRecord);
// 关键:邀请人可能因此升级会员
calculateAndUpgradeMember(inviterId);
}
return user.getId();
}
三个细节。inviterId——新用户绑定了邀请人,这一条"关系链"就建立了:新用户是邀请人的下线,邀请人因此获得"邀请记录"。inviteRecord——每次带邀请码的注册生成一条记录,状态先待确认,确认后(status=1)才算有效邀请。calculateAndUpgradeMember(inviterId)——这是最关键的一行:新用户注册成功,邀请人可能因此从普通用户升级成 Pro!
这个"邀请即升级"的设计,是这套会员体系的增长引擎:用户想要会员,不需要花钱,只需要拉人——每拉一个有效用户,离会员更近一步。这比"付费买会员"更贴合早期社区的冷启动——付费的门槛高,但邀请是人人能做的。用"邀请"换"权益",是独立站最便宜的增长杠杆——它同时激励了老用户拉新、新用户进来,还不用花一分钱。
邀请码本身的设计也值得说:每个用户注册时系统生成一个唯一的 inviteCode,在邀请页展示、一键复制。这个码是"用户的拉新名片"——老用户把码发给朋友,朋友注册时填上,关系链就建立了。邀请码是"把每个用户变成分销员"的工具——它让拉新不再依赖平台运营,而是每个用户自发的行为。
还有个细节:新用户注册时填了邀请码,绑定的是 inviterId——这个"关系"不仅在注册时有用,它还可能影响后续的推荐(a14 那篇的推荐系统可能用到社交关系)。一条邀请关系,可能在多个系统里产生价值——注册时它让邀请人升级,推荐时它可能是"朋友推荐"的信号。设计数据时多存一层关系,将来能复用的地方比你想的多。
邀请码本身还有个防泄露的设计:邀请码在注册页填,如果填了别人的码会怎样?后端校验这个码属于一个真实用户(userService.getOne),查不到就当作没填(inviterId=0),不会报错——非法的邀请码要"静默忽略",而不是报错打断注册。因为邀请码是可选的,填错了不该卡住一个本要注册的用户。一个用户因为邀请码填错就注册失败,那是最冤的流失。
calculateAndUpgradeMember 是升级的核心,它按"有效邀请数"决定邀请人的会员档位,还带防刷:
private void calculateAndUpgradeMember(Long userId) {
User user = this.getById(userId);
if (user == null || isAdmin(user)) return;
// 1. 拿到所有状态为 1(有效)的邀请记录
List<InviteRecord> allRecords = inviteRecordMapper.selectList(
new QueryWrapper<InviteRecord>()
.eq("inviterId", userId).eq("status", 1).eq("isDelete", 0)
.orderByAsc("confirmTime"));
// 2. 每天最多算 5 个,防刷
Map<String, Integer> dailyCountMap = new HashMap<>();
List<InviteRecord> validRecords = new ArrayList<>();
for (InviteRecord record : allRecords) {
String day = formatDay(record.getConfirmTime());
int count = dailyCountMap.getOrDefault(day, 0);
if (count < 5) { // 同一天最多算 5 个有效邀请
validRecords.add(record);
dailyCountMap.put(day, count + 1);
}
}
// 3. 按有效邀请数升级会员
// ... 邀请够 N 人 → Pro,够 M 人 → Plus
}
这里最值得讲的是防刷:每天最多算 5 个——为什么?因为"邀请升级"有个天然的作弊点:一个人注册一堆小号,用同一个邀请码互相邀请,一天刷出几十个"有效邀请",直接升级会员。限制"每天最多算 5 个有效邀请",把单日刷量压到最低——就算你刷,一天也就 5 个,要凑够升级数得刷很多天,成本就上来了。任何"行为换权益"的机制,都要防"批量刷"——邀请上限、每日封顶、异常检测,一样都不能少。
除了"每天 5 个"的上限,升级还有一层"按时间序"的细节:orderByAsc("confirmTime")——邀请记录按确认时间升序排,先到先算。为什么按时间序而不是随便排?因为"有效邀请"是有确认流程的(新用户注册后还要确认才算 status=1),按确认时间排能保证"先确认的先算",逻辑一致、可预测。涉及"计数"的逻辑,排序的确定性很重要——同一个数据集,两次跑出来的结果要一样,否则运营会看到诡异的变化。
升级门槛(邀几个到 Pro / 邀几个到 Plus)是这套体系的"定价"——它决定了"一个会员值几个新用户"。门槛太高,用户觉得遥不可及,放弃拉新;门槛太低,会员太容易拿,权益贬值。升级门槛是最需要运营调优的杠杆——它不是一次定死的,是要根据拉新数据反复调的。这也是为什么它要可配置、而不是写死。
升级还有个时机细节:calculateAndUpgradeMember 是在注册时同步调用的——新用户注册成功,邀请人当场升级,不用等。为什么要"即时"?因为升级是用户的"即时反馈":邀请人收到"你邀请的新用户注册成功,你升级为 Pro 了",这个即时感是拉新的持续动力。如果延迟到明天再算,用户早就忘了自己邀过谁,升级带来的激励就凉了。激励要即时兑现,延迟的激励等于没有激励——这和游戏里"任务完成立刻发奖"是同一个道理。
升级的门槛(邀几个到 Pro、邀几个到 Plus)是可配置的,前端会员机制弹窗里会展示"Pro:邀请 X 人、Plus:邀请 Y 人"。这个门槛也是运营的杠杆:早期门槛低(邀请 3 个就到 Pro),快速拉新;规模起来后门槛调高。增长杠杆要可调——写死门槛,运营想调就得发版。
邀请是"拉新"的引擎,签到是"留活"的引擎。/add/sign_in 每天签到一次,奖励 token:
@PostMapping("/add/sign_in")
@SaCheckLogin // 必须登录才能签到
public BaseResponse<Boolean> addSignIn(HttpServletRequest request) {
// 记录今天的签到,已签过就返回 false
// 签到成功,奖励一定量的 token
}
签到的设计意图很朴素:让用户"每天都来"。AI 功能的用户黏性其实不高——用完就走,走了不回。签到给了"每天来一次的理由":来了,签到,领 token,顺便看看新东西。这是用"小奖励"换"日活"——token 的成本低(普通用户签到一次才给一点),但换来的是用户每天打开 App。签到奖励的"锚点"要选对:奖励太贵(比如送一张生图额度),成本吃不消;太便宜(送 1 个 token),用户没动力。要卡在"让用户觉得值、但成本可控"的中间。
签到还和会员体系咬合:签到送的 token 也是消耗在 AI 功能上,而 AI 功能的配额由会员档位决定。签到是"引流",会员是"变现"——签到把用户吸引来用 AI,AI 用久了撞到配额线,用户就去升级会员。整个经济系统的闭环在这里初步成型。
签到还有一个工程细节:签到记录表(UserSignInRecord)和签到接口——/add/sign_in 每天调一次,后端查"今天签过没",签过返回 false,没签过记一条并奖励。这个"今天签没签"的判断,是签到功能的核心状态——它可以用数据库查,也可以用 Redis 的日 key(sign_in:{userId}:{yyyy-MM-dd})存一个标记。用 Redis 日 key 的好处是天然带过期(第二天自动没了),查起来 O(1),比查数据库快。"今天做过没"这种状态,是 Redis 的经典场景——一次性、当天有效、第二天自动失效。
签到奖励的"量"也是精心定的:太抠,用户觉得签到没意思,不来了;太大方,成本失控。这里选的是"一点 token"——够让用户觉得"来了不亏",但远不够覆盖他一天的 AI 消耗。签到奖励是"钩子"不是"主菜"——它的作用是让用户"来",不是让用户"够用"。
签到的"连续签到"设计也值得提:连续签到通常会有额外奖励(连签 7 天送更多)。为什么鼓励连续?因为"连续"制造了沉没成本——用户签了 6 天,第 7 天不签,之前的就断了,所以他会为了保住连续而来。"连续"是留活的最强钩子之一——它把"每天来"变成"不能断",比单日签到更有黏性。当然,连续签到的奖励梯度要设计好——连签的额外奖励要"值得保",但不至于"没它不行"。
配额不能"永远累积"——如果每周的 token 用不完能攒到下个月,用户的消耗会累积成一个大坑,成本不可控。所以配额是周期性重置的:
@Scheduled(cron = "0 0 0 * * MON") // 每周一零点
public void resetWeeklyAiTokens() {
// 清掉所有用户的周配额计数
stringRedisTemplate.delete(KEY_WEEK);
stringRedisTemplate.delete(KEY_IMAGE_GEN_WEEK);
}
每周一零点,把"每周 token"和"每周生图"的计数清空,用户拿到新一轮配额。这个设计有两个深意。成本可预测——每个用户每周最多消耗那么多,成本有上限;体验有节奏——每周一"配额刷新",用户感觉"这周又能用了",这个刷新本身就是一个让用户回访的钩子。
注意这里用的是"删 Redis 计数"而不是"改数据库里的配额值"——因为配额计数本来就是 Redis 滚动窗口(a21 那篇讲过),重置就是清掉窗口,简单、原子、不用碰数据库。周期性的资源,用"清计数"实现,比"改数值"干净得多——计数是状态,清掉就是重置,没有历史包袱。
重置的"节奏"也值得讲:为什么是每周而不是每天?每天重置,用户的消耗会被打散(今天用不完明天又有),成本压力大;每周重置,用户会在一周内"规划使用"(这周生图 15 张,得省着用),成本更可预测。配额的周期,决定了用户的使用心态——周期越长,用户越会"管理"自己的配额,平台的成本越可控。每周是一个"既够用、又可控"的平衡点。
还有个隐性的收益:每周重置本身就是一个"回访钩子"。用户周三把生图额度用完了,周五系统重置,他会想"这周又能生图了"——一个每周一次的"回访理由"。配额的周期性,不只是成本控制,还悄悄给了用户"每周来一次"的理由。经济系统的节拍,也是用户回访的节奏——设计配额的周期,其实是在设计用户的行为周期。
会员有到期的时候。到期那天,系统要自动把权益收回去——MemberDowngradeJob 每天零点检查:
@Scheduled(cron = "0 0 0 * * ?") // 每天零点
public void executeMemberDowngrade() {
// 查所有已过期的 Pro/Plus 用户
QueryWrapper<User> qw = new QueryWrapper<User>()
.in("memberType", 1, 2) // Pro 或 Plus
.isNotNull("memberExpire")
.ne("userRole", "admin") // 排除管理员
.lt("memberExpire", new Date()); // 已过期
List<User> expiredUsers = userService.list(qw);
for (User user : expiredUsers) {
// 降级:会员类型归 0,过期时间清空
userService.lambdaUpdate()
.set(User::getMemberType, 0)
.set(User::getMemberExpire, null)
.eq(User::getId, user.getId())
.update();
// 调整空间配额 + 发降级通知(后面细讲)
}
}
降级不是"删账号",是"收权益":会员类型归 0、过期时间清空、空间配额降到普通档、发一条"会员已到期"的系统通知。几个设计点:
定时任务 + 到期字段——降级靠的是"到期时间小于当前时间"这个判定,而不是"今天是谁的到期日"。这看起来一样,但"过期即降级"更健壮——就算任务晚跑一天,也只是晚降一天,不会漏降。降级判定用"过期时间"而不是"周期任务日",是更可靠的选型。
排除管理员——管理员的会员状态不受降级影响。特权账号不该被普通规则波及,这是运维的常识。
发通知——降级不是悄悄发生的,要告诉用户"你的 Plus 到期了,已降为普通用户"。权益的变化要主动告知,否则用户突然发现"生图额度没了"会一脸懵,还以为是 bug。
降级任务还有几个工程细节。它是每天零点跑一次,而不是"到期那一刻"——为什么?因为"到期那一刻"精确触发需要一个实时任务(可能要每个用户挂个定时器),成本高;每天零点批量扫一次,延迟最多一天,可接受。"批量 + 定时"是周期性业务的标准形态——实时精确到秒的降级没有意义,晚一天也没影响,但批量扫描的成本低一个量级。
降级通知用的是系统通知(SystemNotify),notifyType = "ACCOUNT_CHANGED"——这是一个"账号状态变化"类型的通知,会进用户的消息中心。为什么用系统通知而不是邮件?因为邮件可能进垃圾箱、可能被忽略,而站内系统通知是"用户一定能看到"的(只要打开 App)。重要的账号通知,要送到用户"一定看得见"的地方——站内通知比邮件可靠。
还有一个细节:降级时 set(User::getMemberExpire, null)——过期时间也清空了。为什么?因为降级后用户如果再邀请人升级(重新成为会员),不能让旧的过期时间残留干扰新会员的到期判定。降级要"清干净状态"——只改会员类型不清过期时间,下次判定就会错乱。
降级任务还有一个值得说的边界:空间降级不删数据,但新上传要拦。降级后空间配额变回普通档,用户已存的数据超了,前端在用户尝试上传时应该检查"当前空间配额已满",拦下上传并提示"空间配额不足,清理或升级"。这个"上传时校验配额"的逻辑,和降级任务的"调整配额"是配套的——一个管"配额改了",一个管"超额时拦上传"。降级要"收权 + 拦新"两手抓——只调配额不拦上传,用户照样能传,配额形同虚设。
降级最考验"人性化"的是空间配额的调整——用户是 Plus 时存了 10GB,降级后普通档只有 2GB,怎么办?直接删数据?那是灾难。这个项目的处理很成熟:
// 空间配额调到普通档
SpaceLevelEnum commonLevel = SpaceLevelEnum.COMMON;
StringBuilder detailMsg = new StringBuilder("您的 Plus 会员权益已到期,已自动降级为普通用户。\n");
detailMsg.append("空间配额已调整为:").append(commonLevel.getMaxStorage()).append("MB / ")
.append(commonLevel.getMaxCount()).append("张图片。\n");
// 检查每个空间是否超额,超了明确告知
for (Space sp : userSpaces) {
long overStorage = sp.getUsedStorage() - commonLevel.getMaxStorage();
long overCount = sp.getTotalCount() - commonLevel.getMaxCount();
if (overStorage > 0 || overCount > 0) {
detailMsg.append("「").append(sp.getSpaceName()).append("」超出存储/图片,")
.append("现有数据不会删除,但暂无法上传新图片。");
}
}
detailMsg.append("继续邀请好友即可重新获得会员权益。");
这段代码的每一句都值得品:"现有数据不会删除,但暂无法上传新图片"——这是内容产品配额策略的黄金法则:超了不删,只是停写。用户的劳动成果被保护,成本也被守住。"继续邀请好友即可重新获得会员权益"——降级不是终点,是重新拉新的起点,把"到期用户"重新变成"增长动力"。
还有那个"超额明细"——降级通知里会列出"「我的相册」超出存储 300MB",精确到每个空间超了多少。降级通知要"说清楚影响"——用户知道哪些空间超了、超了多少,才知道该怎么办(清理 or 重新升级)。一个笼统的"你的会员到期了"只会让用户更困惑。
"超了不删、只是停传"这个策略,还要讲一层它的成本逻辑:为什么不让超出的数据自动压缩或者拒绝降级?因为数据的价值在于用户,强制清理会激怒用户、造成流失——用户因为你是 Plus 才存了 10GB,到期你把他数据删了,他再也不会回来。而"停传"既守住了存储成本(不再增长),又保住了用户的信任(数据还在)。配额的"停"永远比"删"好——停是暂停,删是决裂,内容产品只能选停。
那个"继续邀请好友即可重新获得会员权益"的结尾,是整个降级流程的画龙点睛:它不是单纯的通知,而是一个"召回"——把到期用户重新推回增长循环。降级不是终点,是下一个邀请周期的起点——系统设计得好,连"用户流失"都能变成"增长机会"。
前端这边,会员体系的入口很完整:MyPage 的个人卡片显示当前会员档位和 badge,MemberMechanismModal 讲权益,InvitePage 给邀请码和排行榜。
InvitePage.vue 是拉新的主战场:
<div v-if="inviteCode" class="yuemu-code-display">
<span class="yuemu-code-text">{{ inviteCode }}</span>
<button class="yuemu-copy-btn" @click="copyCode">复制链接</button>
</div>
<!-- 排行榜 tab:看谁邀请得多 -->
<button :class="{ 'yuemu-active': activeTab === 'leaderboard' }"
@click="switchTab('leaderboard')">排行榜</button>
邀请页三件套:邀请码展示 + 一键复制(把邀请门槛降到最低——复制、分享、拉人)、邀请记录(用户看到自己邀了几个)、邀请排行榜(制造竞争感,谁邀请得多排前面)。排行榜是个聪明的激励——人都有比较心,看到别人邀请 20 个升到 Plus,自己才 3 个,就有了拉人的动力。排行榜是"行为激励"的放大器——它把"升级会员"这个私人目标,变成了"比朋友强"的社交目标。
MemberMechanismModal 把权益讲清楚:三档配额、模型倍率、升级条件。会员权益弹窗要"一次讲明白"——用户在犹豫"要不要升级"时看到的这一屏,决定了要不要行动。权益写得含糊,用户就关掉了。
前端还有个重要的入口:MyPage 上的会员卡片——显示当前档位(普通/Pro/Plus)和会员 badge,旁边一个"查看权益"的按钮。这个卡片的作用是"时刻提醒":用户每看一眼个人页,就看到自己的档位,看到"我是普通用户"、"Pro 的配额是我的三倍",升级的念头就被反复种下。会员体系的曝光度,决定了它的转化率——权益入口藏得深,等于没有会员体系。
还有"会员到期"的前端配合:降级通知进消息中心后,用户点开能看到"会员已到期,继续邀请即可恢复"。前端这里要让"恢复会员"的动作入口可见——通知里带一个"去邀请"的跳转,用户看完通知,一键回到邀请页。通知到行动的距离,决定了转化——降级通知不只是告知,还是下一个邀请行为的触发器。
这套经济系统的坑,踩多了以后我习惯把它们按"钱、人、节奏"三块来归类——比一条条记着,更容易看到规律。
钱:成本先漏,再堵。 签到奖励最初送得太豪爽,一次给一整天 AI 额度,高频用户靠签到白嫖所有功能,成本直接打爆——奖励的锚点必须算成本账,签到是获客成本,给的价值不能超过用户贡献。配额重置也栽过:每周重置只清了周 token 的 key,忘了清周生图的 key,生图配额一周不重置、一直累积。还有"消耗"和"重置"两个系统的缝隙——重置前最后一秒的消耗残留在重置后的计数里,用户能"多领"一点,后来统一了时间基准才干净。这三跤其实同一个根源:成本系统里的每一个"口子",都要在账本上被盯住,漏一个口子,钱就从那儿漏走。
人:任何行为换权益,都要防刷。 邀请升级最初没限每日数量,有人注册一批小号互刷,一天升到 Plus;有人用小号填自己的邀请码自邀;还有人刷排行榜冲名次。应对是一层层加的:每天最多算 5 个有效邀请、邀请码不能是自己的、排行榜只统计经过防刷过滤的有效邀请。而升级通知最初也和升级动作分开写,偶尔升了级用户却不知道——权益变化要"动作和通知一体",升级不告诉用户,这个权益就白给了。这一组坑的共同点:一旦权益可以"刷",它的激励就废了,还会把真实用户气走。
节奏:周期的业务,节拍要对齐。 空间超额的判定最初只比存储不比图片数,存储没超但图片超了两百张的情况漏掉了;降级任务最初只改数据库字段,用户完全不知道自己的权益变了,跑来问客服才发现会员到期——权益变化必须主动告知,不能让人自己猜;会员机制弹窗的文案和后台配额是两套,改了配额文案没改,用户看到的是错的。这些都不是"不会写",是"忘了把节拍对齐"——配额判定要覆盖所有受限维度,配置和展示要同源。
说到底,这套系统踩的坑,几乎全是同一个根源:做"钱"的事,忘了防"人";做"人"的事,忘了对"节拍"。把这三组关系想清楚,经济系统的坑能少踩一半——这也是我为什么后来把它归类着记,而不是一条条列。
把这条链收起来,你会发现它是一套完整的"增长 + 变现"循环:
用户注册(带邀请码)
→ 绑定邀请人 → 生成邀请记录 → 邀请人升级会员(防刷:每天5个)
→ 会员档位决定 AI Token 配额(5小时/周/生图/搜图/分析)
→ 用户用 AI → 消耗 token → 撞到配额线 → 想升级 → 去邀请/签到
→ 签到送 token(留活)→ 每周一重置配额(成本可控 + 回访钩子)
→ 会员到期 → 自动降级(收权益 + 空间调整 + 通知 + "继续邀请即可恢复")
每一环都在喂下一环:邀请拉新、新用户让邀请人升级、升级带来更大的 AI 消耗、消耗撞到配额、配额促使拉新和签到、签到留活、到期降级再把人拉回来邀请。这不是几个孤立的功能,是一个自我循环的经济系统——它同时解决了获客(邀请)、留活(签到)、成本(配额)、收入(升级动机)四个问题。
三句话总结这篇:
配额是锚,按成本分档。 AI 成本差异大,配额按功能维度拆(生图/搜图/分析各自独立),会员档位决定配额大小——成本控制跟着单位成本走。
邀请是增长引擎,签到是留活引擎。 邀请换会员是最便宜的增长杠杆(同时激励拉新和升级),但必须防刷(每天限 5 个);签到用低成本的 token 换日活。
重置和降级是经济的节拍。 每周一重置配额(成本可控 + 回访钩子),到期自动降级(收权益、调空间、发通知、"继续邀请即可恢复")——数据的节拍让整个系统有节奏地转。
最后说句实在的:独立站的"收入"没有捷径——它需要一套把成本、增长、留存串起来的系统。这个项目的答案是"邀请换会员 + AI 配额锚定成本",不一定适合所有站,但**"把真实成本变成用户能感知的权益"这个思路,是每个独立站都该有的**。希望这篇能给你一个参考的模板。
如果你也在做一个带 AI 功能的独立站,不妨照这条链检查:你的 AI 成本有配额锚吗?你的会员是"付费直购"还是"行为获取"?你的配额定期重置吗?到期会自动降级并通知吗?这四个问题,是"经济系统"从"随手做的会员"到"转起来的轮子"的分水岭。做经济系统,先别急着定价格,把这套循环的每一环想清楚——获客从哪来、成本怎么控、用户为什么留、到期怎么办——再动手。
当然,经济系统不是万能的——它挡不住所有问题,也不能替代产品本身的价值。但它给了一个独立站"活下去"的结构:获客、留存、成本、收入,四个齿轮互相咬着转。很多独立站死掉,不是因为产品不好,是因为没有这套齿轮——用户来了留不住,成本涨了控制不住,最后无以为继。把经济系统做对,是独立站"活下来"和"活得好"之间的那道门。希望你做独立站时,不只是把功能做出来,也把这套齿轮装上去——先让站转起来,再让钱转起来,站才算真正立住了。