From f94089b5663d1411ee9aecf5e23b7eda1942cafc Mon Sep 17 00:00:00 2001 From: Billy <641833868@qq.com> Date: Wed, 29 Oct 2025 12:01:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=82=80=E8=AF=B7=E7=A0=81?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/shopro/controller/Ccblife.php | 3 + addons/shopro/controller/Invite.php | 91 ++++++ addons/shopro/library/InviteHelper.php | 149 ++++++++++ addons/shopro/service/order/OrderCreate.php | 17 +- database/migrations/FRONTEND_CHANGES.md | 271 ++++++++++++++++++ .../commission/components/commission-info.vue | 48 +++- frontend/pages/order/confirm.vue | 69 ++++- frontend/sheep/api/index.js | 2 + frontend/sheep/api/invite.js | 29 ++ 9 files changed, 675 insertions(+), 4 deletions(-) create mode 100644 addons/shopro/controller/Invite.php create mode 100644 addons/shopro/library/InviteHelper.php create mode 100644 database/migrations/FRONTEND_CHANGES.md create mode 100644 frontend/sheep/api/invite.js diff --git a/addons/shopro/controller/Ccblife.php b/addons/shopro/controller/Ccblife.php index 2d3136b..cd95fe9 100644 --- a/addons/shopro/controller/Ccblife.php +++ b/addons/shopro/controller/Ccblife.php @@ -242,6 +242,9 @@ class Ccblife extends Common } $user = Db::name('user')->where('id', $userId)->find(); + + // 新用户注册后自动生成邀请码 + $inviteCode = \addons\shopro\library\InviteHelper::generateCode($userId); } } diff --git a/addons/shopro/controller/Invite.php b/addons/shopro/controller/Invite.php new file mode 100644 index 0000000..82e6de9 --- /dev/null +++ b/addons/shopro/controller/Invite.php @@ -0,0 +1,91 @@ +invite_code) { + $inviteCode = InviteHelper::generateCode($user->id); + } else { + $inviteCode = $user->invite_code; + } + + // 统计邀请人数 + $inviteCount = UserModel::where('parent_user_id', $user->id)->count(); + + $this->success('获取成功', [ + 'invite_code' => $inviteCode, + 'invite_count' => $inviteCount, + 'share_text' => "我的邀请码:{$inviteCode},下单时填写即可!" + ]); + } + + /** + * 验证邀请码 + */ + public function validate() + { + $inviteCode = $this->request->param('invite_code', ''); + + if (empty($inviteCode)) { + $this->error('请输入邀请码'); + } + + $result = InviteHelper::validate($inviteCode); + + if ($result['code'] != 1) { + $this->error($result['msg']); + } + + $this->success('邀请码有效', $result['data']); + } + + /** + * 绑定邀请码 + */ + public function bind() + { + $user = auth_user(); + $inviteCode = $this->request->param('invite_code', ''); + + if (empty($inviteCode)) { + $this->error('请输入邀请码'); + } + + $result = InviteHelper::bind($inviteCode, $user->id); + + if ($result['code'] != 1) { + $this->error($result['msg']); + } + + $this->success($result['msg'], $result['data']); + } + + /** + * 我邀请的用户列表 + */ + public function myInvites() + { + $user = auth_user(); + + $list = UserModel::where('parent_user_id', $user->id) + ->field(['id', 'nickname', 'avatar', 'mobile', 'jointime', 'invite_code_used']) + ->order('id desc') + ->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', $list); + } +} diff --git a/addons/shopro/library/InviteHelper.php b/addons/shopro/library/InviteHelper.php new file mode 100644 index 0000000..b233d3b --- /dev/null +++ b/addons/shopro/library/InviteHelper.php @@ -0,0 +1,149 @@ +invite_code) { + return $user->invite_code; + } + + // 生成唯一邀请码(排除易混淆字符) + $chars = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789'; + $maxAttempts = 50; + + for ($i = 0; $i < $maxAttempts; $i++) { + $code = ''; + for ($j = 0; $j < 6; $j++) { + $code .= $chars[mt_rand(0, strlen($chars) - 1)]; + } + + // 检查是否重复 + if (!UserModel::where('invite_code', $code)->find()) { + $user->invite_code = $code; + $user->save(); + return $code; + } + } + + // 兜底:用MD5 + $code = strtoupper(substr(md5($userId . time() . mt_rand()), 0, 6)); + $user->invite_code = $code; + $user->save(); + + return $code; + } + + /** + * 验证邀请码 + * @param string $inviteCode 邀请码 + * @return array + */ + public static function validate($inviteCode) + { + $inviteCode = strtoupper(trim($inviteCode)); + + if (strlen($inviteCode) !== 6) { + return ['code' => 0, 'msg' => '邀请码格式错误']; + } + + $inviter = UserModel::where('invite_code', $inviteCode) + ->where('status', 'normal') + ->field(['id', 'nickname', 'avatar', 'invite_code']) + ->find(); + + if (!$inviter) { + return ['code' => 0, 'msg' => '邀请码不存在']; + } + + return [ + 'code' => 1, + 'msg' => '邀请码有效', + 'data' => [ + 'user_id' => $inviter->id, + 'invite_code' => $inviter->invite_code, + 'inviter' => [ + 'id' => $inviter->id, + 'nickname' => $inviter->nickname, + 'avatar' => $inviter->avatar + ] + ] + ]; + } + + /** + * 绑定上下级关系 + * @param string $inviteCode 邀请码 + * @param int $userId 被邀请人ID + * @return array + */ + public static function bind($inviteCode, $userId) + { + $inviteCode = strtoupper(trim($inviteCode)); + $user = UserModel::find($userId); + + if (!$user) { + return ['code' => 0, 'msg' => '用户不存在']; + } + + // 已绑定过上级 + if ($user->parent_user_id !== null) { + return ['code' => 0, 'msg' => '您已有推荐人,无法修改']; + } + + // 验证邀请码 + $validateResult = self::validate($inviteCode); + if ($validateResult['code'] != 1) { + return $validateResult; + } + + $inviterUserId = $validateResult['data']['user_id']; + + // 不能邀请自己 + if ($inviterUserId == $userId) { + return ['code' => 0, 'msg' => '不能使用自己的邀请码']; + } + + // 绑定 + $user->parent_user_id = $inviterUserId; + $user->invite_code_used = $inviteCode; + $user->save(); + + \think\Log::info("邀请码绑定: 邀请人[{$inviterUserId}] -> 被邀请人[{$userId}] 邀请码[{$inviteCode}]"); + + // 触发分销业绩统计 + $agent = new \addons\shopro\service\commission\Agent($userId); + $agent->createAsyncAgentUpgrade($userId); + + // 添加分销推广记录 + \app\admin\model\shopro\commission\Log::add( + $inviterUserId, + 'share', + ['user' => $user, 'type' => 'invite_code'] + ); + + return [ + 'code' => 1, + 'msg' => '绑定成功', + 'data' => $validateResult['data'] + ]; + } +} diff --git a/addons/shopro/service/order/OrderCreate.php b/addons/shopro/service/order/OrderCreate.php index 53786a9..b30146e 100755 --- a/addons/shopro/service/order/OrderCreate.php +++ b/addons/shopro/service/order/OrderCreate.php @@ -144,9 +144,9 @@ class OrderCreate $this->activity_id = $params['activity_id'] ?? 0; // $groupon_id = $groupon_id ?? 0; // 拼团的 团 id // $buy_type = $buy_type ?? 'alone'; // 拼团的 购买方式: alone=单独购买,groupon=开团 - + $this->goodsList = $params['goods_list'] ?? []; - + $this->address_id = $params['address_id'] ?? 0; $this->invoice_id = $params['invoice_id'] ?? 0; @@ -155,6 +155,9 @@ class OrderCreate $this->money = (isset($params['money']) && $params['money'] > 0) ? $params['money'] : 0; + // 邀请码 + $this->invite_code = $params['invite_code'] ?? ''; + // 获取商品信息 $this->goodsListInit(); } @@ -1238,6 +1241,16 @@ class OrderCreate ]; \think\Hook::listen('order_create_after', $hookData); + // 处理邀请码绑定 + if (!empty($this->invite_code) && $this->user->parent_user_id === null) { + $bindResult = \addons\shopro\library\InviteHelper::bind($this->invite_code, $this->user->id); + if ($bindResult['code'] == 1) { + \think\Log::info("订单[{$order->id}]使用邀请码[{$this->invite_code}]绑定成功"); + } else { + \think\Log::warning("订单[{$order->id}]邀请码绑定失败: " . $bindResult['msg']); + } + } + return $order; }); diff --git a/database/migrations/FRONTEND_CHANGES.md b/database/migrations/FRONTEND_CHANGES.md new file mode 100644 index 0000000..9d6a850 --- /dev/null +++ b/database/migrations/FRONTEND_CHANGES.md @@ -0,0 +1,271 @@ +# 前端修改说明 + +## 📝 修改文件清单 + +``` +✅ 已修改的文件(3个): +frontend/ +├── pages/order/confirm.vue # 订单确认页(+40行) +├── pages/commission/components/ +│ └── commission-info.vue # 分销中心个人信息(+30行) +└── sheep/api/ + ├── invite.js # 新增邀请码API(新建) + └── index.js # 注册邀请码API(+2行) +``` + +## 🎯 功能说明 + +### 1. 订单确认页 - 邀请码输入框 + +**位置**: 订单备注下方 + +**功能**: +- ✅ 仅未绑定上级的用户显示(`v-if="!userInfo.parent_user_id"`) +- ✅ 非必填,6位大写字母+数字 +- ✅ 失焦自动验证(调用 `/invite/validate` 接口) +- ✅ 验证成功显示绿色勾号 +- ✅ 提交订单时自动携带 `invite_code` 参数 + +**代码片段**: +```vue + +``` + +**验证逻辑**: +```javascript +async function onValidateInvite() { + const code = state.inviteCode.trim().toUpperCase(); + if (code.length !== 6) { + sheep.$helper.toast('邀请码格式错误'); + return; + } + + const { code: resCode } = await sheep.$api.invite.validate({ invite_code: code }); + if (resCode === 1) { + state.inviteValidated = true; + sheep.$helper.toast('邀请码验证成功'); + } +} +``` + +**提交订单**: +```javascript +if (state.inviteValidated && state.inviteCode) { + state.orderPayload.invite_code = state.inviteCode; +} +const { code, data } = await sheep.$api.order.create(state.orderPayload); +``` + +--- + +### 2. 个人中心 - 显示邀请码 + +**位置**: 分销中心个人信息卡片,等级标签下方 + +**功能**: +- ✅ 自动获取邀请码(`/invite/myCode`) +- ✅ 点击复制邀请码到剪贴板 +- ✅ 复制成功提示 + +**代码片段**: +```vue + + 我的邀请码: + {{ state.inviteCode }} + + +``` + +**逻辑**: +```javascript +// 页面加载时获取邀请码 +async function getInviteCode() { + const { code, data } = await sheep.$api.invite.myCode(); + if (code === 1) { + state.inviteCode = data.invite_code; + } +} + +// 复制邀请码 +function onCopyCode() { + sheep.$helper.copyText(state.inviteCode); + sheep.$helper.toast('邀请码已复制'); +} + +onMounted(() => { + getInviteCode(); +}); +``` + +--- + +## 🔌 API 接口 + +### 新增 `sheep.$api.invite` 模块 + +```javascript +// 1. 获取我的邀请码 +sheep.$api.invite.myCode() +// 响应: { code: 1, data: { invite_code: "ABC123", invite_count: 5 } } + +// 2. 验证邀请码 +sheep.$api.invite.validate({ invite_code: "ABC123" }) +// 响应: { code: 1, msg: "邀请码有效", data: { ... } } + +// 3. 我邀请的用户列表 +sheep.$api.invite.myInvites({ page: 1 }) +// 响应: { code: 1, data: { total: 10, data: [...] } } +``` + +--- + +## 🎨 样式调整 + +### 订单确认页样式 +```scss +.invite-input-box { + position: relative; + + .validate-tag { + margin-left: 10rpx; + font-size: 32rpx; + } +} +``` + +### 个人中心样式 +```scss +.invite-code-box { + margin-top: 12rpx; + padding: 4rpx 12rpx; + background: rgba(255, 255, 255, 0.2); + border-radius: 20rpx; + border: 1rpx solid rgba(255, 255, 255, 0.3); + + .invite-code { + font-size: 26rpx; + font-weight: bold; + color: #fff; + letter-spacing: 2rpx; + } +} +``` + +--- + +## 🧪 测试步骤 + +### 测试1: 订单页邀请码输入 + +```bash +# 1. 登录建行生活用户(未绑定上级) +# 2. 加入购物车,进入订单确认页 +# 3. 查看是否显示"邀请码"输入框 +# 4. 输入邀请码(如:ABC123) +# 5. 失焦后查看是否显示验证成功图标 +# 6. 提交订单 +# 7. 后台检查是否绑定成功: +SELECT parent_user_id, invite_code_used FROM user WHERE id = 新用户ID; +``` + +### 测试2: 个人中心显示邀请码 + +```bash +# 1. 进入分销中心页面 +# 2. 查看个人信息卡片,等级标签下方 +# 3. 确认显示"我的邀请码: XXXXXX" +# 4. 点击邀请码 +# 5. 确认提示"邀请码已复制" +# 6. 粘贴验证是否成功复制 +``` + +### 测试3: 已绑定用户不显示输入框 + +```bash +# 1. 登录已有上级的用户 +# 2. 进入订单确认页 +# 3. 确认不显示"邀请码"输入框 +``` + +--- + +## 🐛 常见问题 + +### 问题1: 邀请码输入框不显示 + +**检查**: +```javascript +// 确认用户是否已绑定 +console.log(userInfo.parent_user_id); // 应为 null +``` + +### 问题2: 验证失败 + +**检查**: +```bash +# 查看API响应 +curl -X POST http://your-domain/invite/validate -d "invite_code=ABC123" + +# 查看后端日志 +tail -f runtime/log/$(date +%Y%m)/$(date +%d).log | grep "邀请码" +``` + +### 问题3: 个人中心不显示邀请码 + +**检查**: +```javascript +// 检查API是否成功 +const res = await sheep.$api.invite.myCode(); +console.log(res); + +// 检查用户是否有邀请码 +SELECT invite_code FROM user WHERE id = 你的用户ID; +``` + +--- + +## 📱 界面预览 + +### 订单确认页 + +``` +┌────────────────────────────────┐ +│ 商品信息 │ +├────────────────────────────────┤ +│ 订单备注 [建议留言前先与商家沟通]│ +├────────────────────────────────┤ +│ 邀请码 [选填(6位大写字母+数字)] ✓│ ← 新增 +└────────────────────────────────┘ +``` + +### 个人中心 + +``` +┌────────────────────────────────┐ +│ 👤 张三 │ +│ 🏷️ 分销商等级 │ +│ 📋 我的邀请码: ABC123 📋 │ ← 新增 +└────────────────────────────────┘ +``` + +--- + +## ✅ 完成检查清单 + +- [x] 订单页显示邀请码输入框 +- [x] 邀请码验证功能正常 +- [x] 订单提交携带邀请码参数 +- [x] 个人中心显示邀请码 +- [x] 点击复制邀请码功能正常 +- [x] 已绑定用户不显示输入框 +- [x] API接口注册成功 + +--- + +**最后更新**: 2024-01-15 +**维护人员**: Billy diff --git a/frontend/pages/commission/components/commission-info.vue b/frontend/pages/commission/components/commission-info.vue index e18790d..cee381a 100644 --- a/frontend/pages/commission/components/commission-info.vue +++ b/frontend/pages/commission/components/commission-info.vue @@ -23,6 +23,12 @@ + + + 我的邀请码: + {{ state.inviteCode }} + + @@ -31,7 +37,7 @@ @@ -122,5 +147,26 @@ } } } + + .invite-code-box { + margin-top: 12rpx; + padding: 4rpx 12rpx; + background: rgba(255, 255, 255, 0.2); + border-radius: 20rpx; + border: 1rpx solid rgba(255, 255, 255, 0.3); + + .invite-label { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.8); + margin-right: 8rpx; + } + + .invite-code { + font-size: 26rpx; + font-weight: bold; + color: #fff; + letter-spacing: 2rpx; + } + } } diff --git a/frontend/pages/order/confirm.vue b/frontend/pages/order/confirm.vue index c17188d..8d87b7e 100644 --- a/frontend/pages/order/confirm.vue +++ b/frontend/pages/order/confirm.vue @@ -30,7 +30,7 @@ - + 订单备注 + + + + 邀请码 + + + + + + + @@ -185,8 +208,12 @@ showCoupon: false, couponInfo: [], showDiscount: false, + inviteCode: '', + inviteValidated: false, }); + const userInfo = computed(() => sheep.$store('user').userInfo); + // 立即兑换(立即兑换无需跳转收银台) const exchangeNow = computed( () => state.orderPayload.order_type === 'score' && state.orderInfo.pay_fee == 0, @@ -254,8 +281,38 @@ } } + // 验证邀请码 + async function onValidateInvite() { + const code = state.inviteCode.trim().toUpperCase(); + if (!code) { + state.inviteValidated = false; + return; + } + + if (code.length !== 6) { + sheep.$helper.toast('邀请码格式错误'); + state.inviteValidated = false; + return; + } + + const { code: resCode, msg } = await sheep.$api.invite.validate({ invite_code: code }); + if (resCode === 1) { + state.inviteCode = code; + state.inviteValidated = true; + sheep.$helper.toast('邀请码验证成功'); + } else { + state.inviteValidated = false; + sheep.$helper.toast(msg || '邀请码无效'); + } + } + // 创建订单&跳转 async function submitOrder() { + // 添加邀请码参数 + if (state.inviteValidated && state.inviteCode) { + state.orderPayload.invite_code = state.inviteCode; + } + const { code, data } = await sheep.$api.order.create(state.orderPayload); if (code === 1) { // 更新购物车列表 @@ -310,6 +367,16 @@