Skip to content

用户与分销 ​

本文面向运营 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 失败 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)。
  • 实名与业务的联动:
    1. 商品的 purchase_requires.require_verification 打开后,未实名用户下单被拒(40301「该商品需要实名认证后才能购买,请先完成实名认证」);同一组开关里的 require_phone 用于强制绑定手机号(40302)。
    2. 申请提现必须先实名(见第 7 节)。
    3. 充值是否必须实名由设置项 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 会被拒(「折扣率不能低于商品折扣组最低折扣率」)。
  • 解析顺序:
    1. 用户没有代理组,或代理组 status 不是 1 → 不打折;
    2. 商品挂了商品折扣组、该组 status 是 1,且矩阵里存在这个「代理组加商品折扣组」的组合 → 用矩阵里的 discount_rate;
    3. 以上都不成立时回退到代理组的 default_discount_rate;这一项为 null 表示不启用回退,仍按原价。
  • 成本价保护:折后金额不能低于成本金额(原价 × 商品折扣组的 cost_rate),否则报「代理折后金额不能低于成本价」。注意走第 3 条回退分支时成本率按 0 处理,该分支不会触发成本价校验。
  • 删除限制:代理组下还有用户 → 「当前代理组下仍有用户,无法删除」;商品折扣组下还有商品 → 「当前商品折扣组仍有商品,无法删除」;code 重复 → 「编码已存在」。
  • 给用户指定代理组:用户 → 用户详情 → 「编辑」弹窗 → 代理组 下拉(下拉里的 0 表示无代理分组,保存为 null),需要 user.manage 权限。

6. 折扣叠加规则 ​

这是最容易被问到的口径,结论是相乘与相减,不是二选一取最优。实际计算只发生在下单建账单时,顺序固定:

  1. 按商品配置与计费周期算出原价;
  2. 套用代理折扣:折后金额 = round(原价 × 代理折扣率 ÷ 100),折扣率取第 5 节解析出的值(没有代理关系时是 100,即不变);
  3. 在折后金额上计算优惠券抵扣;
  4. 账单应付额 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)的账号会被拒(「该客户账号已被禁用,无法代登录」)。这是一次显式的代操作,请只在自己确知需要时使用。
  • 不能做什么:
    • 不物理删除用户:用户模型使用软删除,删除动作不会让历史账单、流水与奖励记录消失。
    • 不直接改数据库里的用户状态或余额字段:前者绕过实名与风控链条,后者绕过乐观锁与流水写入。
    • 不把「驳回」当成日常的解除实名叫法:驳回等于解绑,会同时写入驳回原因,且只有当前已认证的用户才能操作。
    • 不改推荐关系:注册后绑定即固定,未在代码中确认存在后台改绑入口。
    • 不手工给奖励余额加数:奖励余额只能由返佣与提现两条链路变动;需要补差请走现金余额调整并写明原因。

关联文档 ​

基于 AGPL-3.0-or-later 发布