用户与分销
本文面向运营 TuraIDC 的站长,说明用户怎么进来、账号怎么保护、实名怎么审、会员等级与代理折扣怎么配、返佣怎么发、提现怎么批。字段名与默认值取自后端代码,后台路径取自管理端路由。
1. 注册与账号归属
- 注册字段:
account(必填,邮箱或手机号,识别不出类型时报「请输入正确的邮箱或手机号」,最长 100)、code(必填,6 位验证码)、password(必填,至少 8 位,需带确认字段)、nickname(可选,最长 50)、email/phone(可选)、referral_code(可选,最长 24)。 - 必须验证码:按
account判定走短信码还是邮箱码,失败分别提示「短信验证码错误或已过期」「邮箱验证码错误或已过期」。 - 注册同时建号与推荐码:写入
users记录(status默认1正常)与 6 位大写字母数字的referral_code;余额账户user_accounts按需创建,各金额默认0.00(后台建用户会立即建好账户行)。注册时还会写入last_login_at与last_login_ip。 - 推荐关系在注册环节绑定一次:与建号在同一个事务里完成。推荐码为空、推荐人不存在、推荐人就是自己时静默放行(不报错也不绑定);绑定成功会写一条
client.register.referral_bound操作日志。推荐功能总开关关闭时同样不绑定。 - 后台建用户:入口是用户列表的「新增用户」,表单字段为 邮箱、昵称、手机号、密码;服务层另支持初始
balance与credit_limit(默认0),当前表单没有暴露这两项。邮箱重复报「邮箱已存在」,手机号有唯一性校验。
2. 登录与安全策略
- 两种登录方式:密码登录与验证码登录(邮箱码或短信码)。两种方式下
users.status !== 1都会被拒,报 40300「账号已被禁用」。 - 账号状态只有一个开关:
users.status,1正常、0禁用,在用户详情的「编辑」弹窗里切换。 - Token 有效期:默认
1440分钟(24 小时)过期、10800秒(3 小时)闲置超时,任一到达即失效。管理员给用户改密会立即吊销该用户全部已签发 token——这是处置盗号的主入口。 - 人机验证场景开关:入口在 系统 → 插件管理 → 人机验证(
/admin/integration-plugins/captcha)。可关的是「用户登录」与「管理员登录」;强制开启、不可关闭的是「用户注册」「发送邮箱验证码」「发送手机验证码」。未保存过的开关按「默认开启」处理。 - 应急通道:
php artisan captcha:scene --list查看各场景状态,php artisan captcha:scene admin_login off关闭管理员登录的人机验证,php artisan captcha:scene --disable-plugin整体停用验证码插件(一次显式的全站决定)。强制场景不接受配置关闭,改库也无效。 - 失败次数软锁定(仅在验证码插件未启用时生效,作为兜底):
- 账号加 IP 维度:失败达到
5次即锁定,计数窗口 900 秒; - 账号维度:需要
3个以上不同 IP 且账号失败达到10次才锁定,计数窗口 1800 秒。加这条限制是为了防止第三方从单一 IP 反复尝试目标账号名就能锁死他人账号; - 登录成功会把上述计数全部清零。
- 账号加 IP 维度:失败达到
- 软锁定不落库:它保存在限流计数里,所以后台没有「解锁」按钮。要么等窗口过期,要么让用户在验证码通道成功登录一次(验证码通道与密码通道的计数互相独立)。
- 验证码触发阈值(插件启用时):账号加 IP 失败
1次即要求、账号失败1次即要求、单 IP 失败8次即要求。管理端与客户端使用互相隔离的计数作用域,同名管理员与客户不会互相累加,也不会互相解锁。
3. 实名认证
- 字段:
is_verified、verification_status、verification_message、verification_certify_id、real_name、id_card、verified_at。 - 判定通过只有一条口径:
is_verified等于1,或verification_status等于2。 - 状态取值:
0未认证、1待认证、2已认证、3认证失败、4待认证、5已驳回。5由管理员在后台「解绑」产生,落库时同时写入驳回原因(缺省「管理员驳回」),界面上显示为「已驳回」,数据效果是解除绑定。 - 管理端入口:用户 → 实名列表(
/admin/users/verification)与实名管理(/admin/users/verification/manage),权限verification.list。驳回/解绑需要额外权限verification.unbind,驳回原因必填;人工订正资料走PUT /v2/admin/verifications/{user}接口。 - 认证插件:目录
backend/plugins/certification/,内置alipay_certify(支付宝)、baidu_face、leaf_face(人脸)、smapi、stay33、demo_verification。启用与配置入口:系统 → 插件管理 → 实名认证(/admin/integration-plugins/verification)。 - 实名与业务的联动:
- 商品的
purchase_requires.require_verification打开后,未实名用户下单被拒(40301「该商品需要实名认证后才能购买,请先完成实名认证」);同一组开关里的require_phone用于强制绑定手机号(40302)。 - 申请提现必须先实名(见第 7 节)。
- 充值是否必须实名由设置项
finance.recharge_requires_verification决定,默认开启,入口在设置页「账户与财务」标签页,见财务与账单。
- 商品的
4. 会员等级与返利比例
会员等级不影响商品售价,它只决定推荐人拿多少返佣。
- 表结构
member_levels:name、code、sales_amount_min、sales_amount_max、reward_rate、status、sort_order、remark。 - 管理端入口:营销 → 会员等级(
/admin/member-levels),权限member_level.list/member_level.manage。 - 字段与限制:
等级名称(不能为空、不能重名)、等级编码(留空时由名称生成,同样不能重复)、累计销售额下限(默认0,不能小于 0)、累计销售额上限(留空表示不封顶)、返利比例(%)(0–100)、排序值、状态(默认启用)。 - 等级归属:按用户累计销售额落在哪个启用等级的区间内判定,命中多个时取
sales_amount_min最大的那个。区间不允许重叠,保存时会校验并报「等级销售额区间与现有等级重叠」。 - 销售额什么时候涨:推荐人的下级完成一笔新购时,把订单实付额累加进推荐人的
total_sales_amount,再用累加后的金额解析等级并取该等级的reward_rate。升级与返佣在同一笔交易里一起发生。 - 改动规则的代价:保存等级后会派发一个全局唯一的异步任务重算存量用户等级(分批,每批 200 个用户)。任务幂等,结果只由累计销售额与当前规则决定,中断后重跑即可收敛。
- 删除限制:等级下仍有用户时删除被拒(「当前等级下仍有用户,无法删除」)。等级列表缓存 5 分钟,保存后自动失效。
5. 代理分组与折扣矩阵
代理折扣按「代理组 × 商品折扣组」的矩阵配置,是给渠道客户降价的主要手段。
- 表结构:
agent_groups:name、code、status、default_discount_rate、sort_order、remark;用户通过users.agent_group_id归属某个组。product_discount_groups:name、code、min_discount_rate(最低折扣率,下限保护)、cost_rate(成本率)、status、sort_order。agent_group_discounts:agent_group_id、product_discount_group_id、discount_rate三列构成的矩阵。
- 管理端入口:营销 → 代理折扣(
/admin/agent-discounts),三个标签页依次是 代理组、商品折扣组、折扣矩阵;权限agent_discount.list/agent_discount.manage。 - 折扣率是百分比,不是折数:
100是原价,90是九折。取值必须在0–100之间,低于商品折扣组的min_discount_rate会被拒(「折扣率不能低于商品折扣组最低折扣率」)。 - 解析顺序:
- 用户没有代理组,或代理组
status不是1→ 不打折; - 商品挂了商品折扣组、该组
status是1,且矩阵里存在这个「代理组加商品折扣组」的组合 → 用矩阵里的discount_rate; - 以上都不成立时回退到代理组的
default_discount_rate;这一项为null表示不启用回退,仍按原价。
- 用户没有代理组,或代理组
- 成本价保护:折后金额不能低于成本金额(原价 × 商品折扣组的
cost_rate),否则报「代理折后金额不能低于成本价」。注意走第 3 条回退分支时成本率按0处理,该分支不会触发成本价校验。 - 删除限制:代理组下还有用户 → 「当前代理组下仍有用户,无法删除」;商品折扣组下还有商品 → 「当前商品折扣组仍有商品,无法删除」;
code重复 → 「编码已存在」。 - 给用户指定代理组:用户 → 用户详情 → 「编辑」弹窗 →
代理组下拉(下拉里的0表示无代理分组,保存为null),需要user.manage权限。
6. 折扣叠加规则
这是最容易被问到的口径,结论是相乘与相减,不是二选一取最优。实际计算只发生在下单建账单时,顺序固定:
- 按商品配置与计费周期算出原价;
- 套用代理折扣:折后金额 =
round(原价 × 代理折扣率 ÷ 100),折扣率取第 5 节解析出的值(没有代理关系时是100,即不变); - 在折后金额上计算优惠券抵扣;
- 账单应付额
amount=max(折后金额 − 券抵扣, 0),discount记录券抵扣额。
因此三者同时存在时的公式是:应付 = 原价 × 代理折扣率 ÷ 100 − 优惠券抵扣。会员等级的 reward_rate 不参与这条计算,只影响推荐人的返佣比例。
改价的窗口在下单前:报价凭证 quote_token 绑定折后金额与券后应付额,有效期 600 秒;提交时服务端重新校验,客户端改不动价格。
7. 返佣、冻结与提现审批
返佣怎么算:
- 前提是返利总开关
referral.enabled为开(默认开启)、订单类型是新购、订单状态属于 已付款 / 开通中 / 已完成、买家有推荐人。 - 计算基数是订单实付额(
paid_amount,为空时取amount),不是商品原价。 - 比例取推荐人按累加后累计销售额命中的等级
reward_rate;没有命中任何等级时用全局配置referral.reward_rate(设置页默认10,范围 0–100)。 - 奖励先落到
user_accounts.referral_frozen_balance,并写一条referral_rewards(status为0冻结中),available_at由referral.reward_freeze_days决定(设置页默认4天)。 - 到期由
php artisan referral:release-rewards把冻结转为可提现(status变1已释放),用户申请提现时也会先触发一次释放。 - 没有订单的账单走另一条发放路径,只认类型为
new或normal且状态为已付的账单。 - 退款会把奖励反向冲减(
status变2已回退),分别从冻结余额或可提现余额扣回;若奖励已释放而可提现余额不足,系统记 warning 而不是静默吞掉。 - 管理端入口:营销 → 推广概览(
/admin/referral)与奖励记录(/admin/referral/rewards),权限referral.list。 - 绑定不可更改:推荐关系在注册时绑定一次,已有推荐人时再传推荐码不会覆盖。推荐人与被推荐人最近登录 IP 相同时绑定被拒,并写
referral.bind.blocked_same_ip操作日志。
提现怎么批:
- 用户申请入口是控制台
/client/referral,字段amount、method、remark;method只有alipay(支付宝,也是默认值)与balance(提到站内余额)两个值。 - 提交门槛按顺序校验:必须已实名 → 金额大于
0→ 不低于referral.withdraw_min_amount(设置页默认20,提示「奖励满 X 元才可提现」)→ 可提现余额足够 → 不能已有一笔待处理申请(「已有待处理提现申请,请等待审核完成」)。选支付宝时还必须已绑定支付宝,否则「请先绑定支付宝」。 - 申请时钱就动过了:可提现余额减少、提现中余额等额增加,并写一条
withdraw_apply流水;提现单状态为0待处理。 - 审批入口:营销 → 提现审核(
/admin/referral/withdrawals),权限referral_withdrawal.list。只有待处理的单据带「通过」与「拒绝」两个按钮,弹窗里通过备注可留空、拒绝原因必填。- 通过:提现中余额转为已提取余额,状态变
1已通过。若方式是balance,同一事务里还会把等额金额加到cash_balance,并写一条account_type为cash、event_type为referral_withdraw_approved的现金流水。 - 拒绝:金额退回可提现余额,状态变
2已拒绝。
- 通过:提现中余额转为已提取余额,状态变
- 打款确认:后端提供
POST /v2/admin/referral-withdrawals/{withdrawal}/payment-confirmations,只接受已通过(状态1)且方式不是balance的单据,必须填写打款单号,成功后状态推进为3已打款并回填打款时间。管理端当前只有通过/拒绝两个按钮,未在代码中确认存在打款确认入口,需要时请按接口直接调用并留痕。 - 用户侧四个奖励余额的含义:
referral_frozen_balance冻结中、referral_available_balance可提现、referral_pending_withdrawal_balance提现中、referral_withdrawn_balance已提取。用户详情的「推荐信息」标签页展示推荐码、当前等级、直接推荐人数、累计奖励与可提现额。
8. 管理端用户操作与边界
- 用户列表(
/admin/users,权限user.list):列为 ID、账号、昵称、余额、已开通服务数、代理组、状态、注册时间;筛选支持关键词与状态。 - 用户详情(
/admin/users/:id,权限user.detail)标签页:基本信息、推荐信息、产品/服务、账单、资金流水、工单、操作日志、通知记录。顶部余额卡片旁的按钮直接打开「资金管理」弹窗,规则与风险见财务与账单。 - 可编辑字段:
nickname、status(正常/禁用)、phone、password(留空不改,至少 8 位)、agent_group_id(需user.manage)。手机号回显为脱敏值时按「未修改」处理,不会被写坏。 - 代登录:用户详情页可对目标用户发起「登录为用户」,被禁用(
status不等于1)的账号会被拒(「该客户账号已被禁用,无法代登录」)。这是一次显式的代操作,请只在自己确知需要时使用。 - 不能做什么:
- 不物理删除用户:用户模型使用软删除,删除动作不会让历史账单、流水与奖励记录消失。
- 不直接改数据库里的用户状态或余额字段:前者绕过实名与风控链条,后者绕过乐观锁与流水写入。
- 不把「驳回」当成日常的解除实名叫法:驳回等于解绑,会同时写入驳回原因,且只有当前已认证的用户才能操作。
- 不改推荐关系:注册后绑定即固定,未在代码中确认存在后台改绑入口。
- 不手工给奖励余额加数:奖励余额只能由返佣与提现两条链路变动;需要补差请走现金余额调整并写明原因。