model = new UserModel; } /** * 用户列表 */ public function index() { if (!$this->request->isAjax()) { return $this->view->fetch(); } $paginator = $this->model->sheepFilter() ->with(['parent_user', 'agent.level_info']) ->paginate($this->request->param('list_rows', 10)); $rate = (new \addons\shopro\service\partner\RateService())->currentRate(); $days = \addons\shopro\service\partner\PartnerConfig::activeDays(); $now = time(); $list = $paginator->items(); foreach ($list as &$user) { $agent = $user->agent; $partner = \addons\shopro\service\partner\IdentityService::attachProfile($agent, $rate, $user); foreach ($partner as $k => $v) { $user->$k = $v; } $user->has_agent = (bool) $agent; $user->madow_value = $partner['madow_value'] ?? '0.00'; if ($agent) { $user->is_venue = intval($agent->is_venue ?? 0); $user->is_county = intval($agent->is_county ?? 0); $user->is_partner = intval($agent->is_partner ?? 0); $user->direct_order_seq = intval($agent->direct_order_seq ?? 0); $user->voucher_total = \addons\shopro\service\partner\IdentityService::formatMoney($agent->voucher_total ?? 0); $user->voucher_released = \addons\shopro\service\partner\IdentityService::formatMoney($agent->voucher_released ?? 0); $user->voucher_used = \addons\shopro\service\partner\IdentityService::formatMoney($agent->voucher_used ?? 0); $user->voucher_available = \addons\shopro\service\partner\IdentityService::formatMoney(max(0, ($agent->voucher_released ?? 0) - ($agent->voucher_used ?? 0))); $user->last_self_order_time = intval($agent->last_self_order_time ?? 0); $user->last_self_order_text = $user->last_self_order_time ? date('Y-m-d H:i:s', $user->last_self_order_time) : '从未下单'; } else { $user->is_venue = 0; $user->is_county = 0; $user->is_partner = 0; $user->direct_order_seq = 0; $user->voucher_total = '0.00'; $user->voucher_released = '0.00'; $user->voucher_used = '0.00'; $user->voucher_available = '0.00'; $user->last_self_order_time = 0; $user->last_self_order_text = '从未下单'; } } $hasPartnerCol = false; try { $hasPartnerCol = in_array('is_partner', Db::name('shopro_commission_agent')->getTableFields()); } catch (\Exception $e) { $hasPartnerCol = false; } $stats = [ 'total_users' => UserModel::count(), 'maker_count' => Db::name('shopro_commission_agent')->where('level', '>=', 2)->count(), 'venue_count' => Db::name('shopro_commission_agent')->where('is_venue', 1)->count(), 'county_count' => Db::name('shopro_commission_agent')->where('is_county', 1)->count(), 'partner_count' => $hasPartnerCol ? Db::name('shopro_commission_agent')->where('is_partner', 1)->count() : 0, 'active_count' => Db::name('shopro_commission_agent')->where('last_self_order_time', '>=', $now - $days * 86400)->count(), ]; $data = $paginator->toArray(); $data['stats'] = $stats; $this->success('获取成功', null, $data); } /** * 用户详情 * * @param $id */ public function detail($id) { if (!$this->request->isAjax()) { return $this->view->fetch(); } $user = $this->model->with(['third_oauth', 'parent_user'])->where('id', $id)->find(); if (!$user) { $this->error(__('No Results were found')); } $agent = \addons\shopro\service\partner\IdentityService::findAgent($id); $partner = \addons\shopro\service\partner\IdentityService::attachProfile($agent, null, $user); $data = array_merge($user->toArray(), $partner); $data['has_agent'] = (bool) $agent; // 推三返一循环信息 $push3Amounts = \addons\shopro\service\partner\PartnerConfig::push3Amounts(); $seq = intval($agent->direct_order_seq ?? 0); $cycleCount = count($push3Amounts) > 0 ? count($push3Amounts) : 3; $nextAmount = $push3Amounts[$seq % $cycleCount] ?? 398; $data['direct_order_seq'] = $seq; $data['push3_cycle_index'] = ($seq % $cycleCount) + 1; $data['next_push3_amount'] = number_format((float) $nextAmount, 2, '.', ''); $data['next_push3_text'] = '第 ' . ($seq + 1) . ' 单将返 ¥' . $data['next_push3_amount']; // 30天自购活跃状态与倒计时 $lastOrder = intval($agent->last_self_order_time ?? 0); $activeDays = \addons\shopro\service\partner\PartnerConfig::activeDays(); $ago = $lastOrder ? intval(floor((time() - $lastOrder) / 86400)) : null; $isAdvanced = !empty($data['is_advanced']); $data['last_self_order_time'] = $lastOrder; $data['last_self_order_text'] = $lastOrder ? date('Y-m-d H:i:s', $lastOrder) : '从未下单'; $data['active_days_ago'] = $ago; $data['active_days_limit'] = $activeDays; if (!$isAdvanced) { $data['active_status_text'] = '免自购考核 (基础会员/品酒师)'; } else { $data['active_status_text'] = !empty($data['active_30d']) ? '正常领取(近30天有自购)' : ('已暂停领取' . ($ago !== null ? '(距今 ' . $ago . ' 天)' : '(从未下单)')); } // 累计现金收益统计(与 IdentityService 严格对齐,兼顾历史分销与收益中心) $agentTotalIncome = floatval($agent->total_income ?? 0); $cashTypes = \addons\shopro\service\partner\PartnerConfig::cashTypes(); $totalCash = \app\admin\model\shopro\partner\Reward::where('user_id', $id) ->whereIn('reward_type', $cashTypes) ->where('status', 1) ->where('amount', '>', 0) ->sum('amount'); $alignedTotalIncome = max($agentTotalIncome, (float) $totalCash); $push3Cash = \app\admin\model\shopro\partner\Reward::where('user_id', $id) ->where('status', 1) ->where('amount', '>', 0) ->where('reward_type', 'push3') ->sum('amount'); $partnerExclusiveCash = \app\admin\model\shopro\partner\Reward::where('user_id', $id) ->whereIn('reward_type', $cashTypes) ->where('status', 1) ->where('amount', '>', 0) ->where('reward_type', '<>', 'push3') ->sum('amount'); $data['income_total_cash'] = number_format($alignedTotalIncome, 2, '.', ''); $data['total_income'] = number_format($alignedTotalIncome, 2, '.', ''); $data['push3_cash_income'] = number_format((float) $push3Cash, 2, '.', ''); $data['partner_exclusive_cash'] = number_format((float) $partnerExclusiveCash, 2, '.', ''); $data['money'] = number_format(floatval($user->money ?? 0), 2, '.', ''); $data['commission'] = number_format(floatval($user->commission ?? 0), 2, '.', ''); // 兑换券计划资产统计 $voucherTotal = floatval($agent->voucher_total ?? 0); $voucherReleased = floatval($agent->voucher_released ?? 0); $voucherUsed = floatval($agent->voucher_used ?? 0); $data['voucher_total'] = number_format($voucherTotal, 2, '.', ''); $data['voucher_released'] = number_format($voucherReleased, 2, '.', ''); $data['voucher_used'] = number_format($voucherUsed, 2, '.', ''); $data['voucher_available'] = number_format(max(0, $voucherReleased - $voucherUsed), 2, '.', ''); $data['voucher_plans_count'] = \app\admin\model\shopro\partner\VoucherPlan::where('user_id', $id)->count(); // 团队统计 $data['direct_users_count'] = UserModel::where('parent_user_id', $id)->count(); if ($agent) { $data['agent_level'] = $agent->level; $data['agent_level_info'] = $agent->level_info; $data['is_venue'] = intval($agent->is_venue ?? 0); $data['is_county'] = intval($agent->is_county ?? 0); $data['is_partner'] = intval($agent->is_partner ?? 0); $data['county_code'] = (string) ($agent->county_code ?? ''); $data['upgrade_lock'] = intval($agent->upgrade_lock ?? 0); } $this->success('获取成功', null, $data); } /** * 详情动态:收益中心流水 */ public function rewards($id) { $showSkip = $this->request->param('show_skip', 0); $query = \app\admin\model\shopro\partner\Reward::sheepFilter() ->with(['buyer', 'order']) ->where('user_id', $id); if (!$showSkip) { $query->where(function ($q) { $q->where('amount', '<>', 0)->whereOr('madow', '<>', 0); }); } $list = $query->order('id', 'desc')->paginate($this->request->param('list_rows', 10)); $this->success('获取成功', null, $list); } /** * 详情动态:兑换券释放计划 */ public function voucherPlans($id) { $list = \app\admin\model\shopro\partner\VoucherPlan::sheepFilter() ->with(['order']) ->where('user_id', $id) ->order('id', 'desc') ->paginate($this->request->param('list_rows', 10)); $agent = \app\admin\model\shopro\commission\Agent::where('user_id', $id)->field('voucher_used,voucher_released')->find(); $used = (float) ($agent['voucher_used'] ?? 0); $released = (float) ($agent['voucher_released'] ?? 0); $available = number_format(max(0, $released - $used), 2, '.', ''); foreach ($list as $row) { $row->voucher_used = number_format($used, 2, '.', ''); $row->voucher_available = $available; } $this->success('获取成功', null, $list); } /** * 详情动态:团队成员列表 */ public function team($id) { $list = UserModel::with(['agent.level_info']) ->where('parent_user_id', $id) ->order('id', 'desc') ->paginate($this->request->param('list_rows', 10)); $rate = (new \addons\shopro\service\partner\RateService())->currentRate(); foreach ($list as $sub) { $agent = $sub->agent; $partner = \addons\shopro\service\partner\IdentityService::attachProfile($agent, $rate); foreach ($partner as $k => $v) { $sub->$k = $v; } $sub->has_agent = (bool) $agent; } $this->success('获取成功', null, $list); } /** * 更新用户 * * @param $id * @return \think\Response */ public function edit($id = null) { operate_filter(); $params = $this->request->only(['username', 'nickname', 'mobile', 'password', 'avatar', 'gender', 'email', 'status']); if (empty($params['password'])) unset($params['password']); if (empty($params['username'])) unset($params['username']); $params['id'] = $id; $this->svalidate($params, '.edit'); unset($params['id']); $user = $this->model->where('id', $id)->find(); $user->save($params); $this->success('更新成功', null, $user); } /** * 删除用户(已禁用) * * @param $id * @return \think\Response */ public function delete($id = null) { $this->error('会员数据涉及资产与团队层级关系,系统已禁用删除功能'); } /** * 删除用户(已禁用) * * @param $ids * @return \think\Response */ public function del($ids = null) { $this->error('会员数据涉及资产与团队层级关系,系统已禁用删除功能'); } public function recharge() { if (!$this->request->isAjax()) { return $this->view->fetch(); } $params = $this->request->only(['id', 'type', 'amount', 'memo']); $userId = intval($params['id'] ?? 0); $type = (string) ($params['type'] ?? 'money'); $amount = trim((string) ($params['amount'] ?? '')); $memo = trim((string) ($params['memo'] ?? '')); if (!$userId || !is_numeric($amount) || (float) $amount == 0) { $this->error('请输入合法的变更数值'); } $allowedTypes = ['money', 'score', 'madow', 'voucher']; if (!in_array($type, $allowedTypes)) { $this->error('不支持的资产类型'); } try { $result = Db::transaction(function () use ($userId, $type, $amount, $memo) { $user = UserModel::where('id', $userId)->lock(true)->find(); if (!$user) { error_stop('会员不存在'); } $oper = \addons\shopro\library\Operator::get(); $operDesc = ($oper['type'] ?? 'admin') . ($oper['id'] ? '#' . $oper['id'] : ''); if ($type === 'money') { // 余额充值或扣除 (保留2位小数) $amount = round((float) $amount, 2); if ($amount < 0 && bcadd((string) $user->money, (string) $amount, 2) < 0) { error_stop('当前余额不足,无法扣除'); } return WalletService::change($userId, 'money', $amount, 'admin_recharge', [], $memo ?: ''); } if ($type === 'score') { // 积分充值或扣除 (整数) $amount = intval($amount); if ($amount == 0) { error_stop('请输入有效的积分数值'); } if ($amount < 0 && ($user->score + $amount) < 0) { error_stop('当前积分不足,无法扣除'); } return WalletService::change($userId, 'score', $amount, 'admin_recharge', [], $memo ?: ''); } // 对于dmepa (madow) 与兑换券 (voucher),属于合伙人二开资产体系 $agent = \app\admin\model\shopro\commission\Agent::where('user_id', $userId)->lock(true)->find(); if (!$agent) { // 自动为用户初始化分销商记录,以便承载二开资产体系 $agent = (new \addons\shopro\service\commission\Agent($userId))->createNewAgent(); $agent = \app\admin\model\shopro\commission\Agent::where('user_id', $userId)->lock(true)->find(); if (!$agent) { $now = time(); \think\Db::name('shopro_commission_agent')->insert([ 'user_id' => $userId, 'level' => 1, 'status' => 'normal', 'madow' => '0.00000000', 'voucher_total' => '0.00', 'voucher_released' => '0.00', 'voucher_used' => '0.00', 'createtime' => $now, 'updatetime' => $now, ]); $agent = \app\admin\model\shopro\commission\Agent::where('user_id', $userId)->lock(true)->find(); } } // 生成全局唯一的负数 order_id(防止与商城真实订单冲突,并避免 uk_order_user_type 唯一索引冲突) $randOrderId = -intval(substr(time(), -6) . mt_rand(100, 999)); $identity = \addons\shopro\service\partner\IdentityService::build($agent); $role = !empty($identity['identities']) ? end($identity['identities']) : 'taster'; if ($type === 'madow') { // dmepa充值或扣除 (8位小数精度) $currentMadow = (string) ($agent->madow ?? '0.00000000'); $amountMadow = function_exists('bcadd') ? bcadd((string) $amount, '0', 8) : number_format((float) $amount, 8, '.', ''); $afterMadow = function_exists('bcadd') ? bcadd($currentMadow, $amountMadow, 8) : ($currentMadow + $amountMadow); if ((float) $afterMadow < 0) { error_stop('当前持仓dmepa不足,无法扣除'); } $rate = (new \addons\shopro\service\partner\RateService())->currentRate(); $agent->madow = $afterMadow; $agent->save(); \addons\shopro\service\partner\PartnerRewardService::write([ 'order_id' => $randOrderId, 'buyer_id' => $userId, 'user_id' => $userId, 'role' => $role, 'reward_type' => 'admin_madow', 'amount' => 0, 'madow' => $amountMadow, 'base_amount' => 0, 'rate' => $rate, 'settle_date' => null, // 设为 null,避免与每日均分的 uk_daily_user_type 发生索引冲突 'ext' => [ 'formula' => ($amount > 0 ? '管理员充值dmepa: +' : '管理员扣除dmepa: ') . $amountMadow, 'memo' => $memo ?: ($amount > 0 ? '管理员充值dmepa' : '管理员扣除dmepa'), 'operator' => $operDesc, 'before' => $currentMadow, 'after' => $afterMadow, ], 'status' => 1, ]); return true; } if ($type === 'voucher') { // 兑换券充值或扣除 (2位小数精度) $amountVoucher = round((float) $amount, 2); $currentReleased = (float) ($agent->voucher_released ?? 0); $currentUsed = (float) ($agent->voucher_used ?? 0); $currentTotal = (float) ($agent->voucher_total ?? 0); $available = max(0, round($currentReleased - $currentUsed, 2)); if ($amountVoucher < 0 && bccomp((string) $available, (string) abs($amountVoucher), 2) < 0) { error_stop('当前可用兑换券不足,无法扣除'); } if ($amountVoucher > 0) { // 充值增加可用兑换券:同时增加 released 与 total $newReleased = round($currentReleased + $amountVoucher, 2); $newTotal = round($currentTotal + $amountVoucher, 2); } else { // 扣除可用兑换券:减少 released 与 total $deduct = abs($amountVoucher); $newReleased = max(0, round($currentReleased - $deduct, 2)); $newTotal = max(0, round($currentTotal - $deduct, 2)); } $agent->voucher_released = $newReleased; $agent->voucher_total = $newTotal; $agent->save(); \addons\shopro\service\partner\PartnerRewardService::write([ 'order_id' => $randOrderId, 'buyer_id' => $userId, 'user_id' => $userId, 'role' => $role, 'reward_type' => 'admin_voucher', 'amount' => $amountVoucher, 'madow' => 0, 'base_amount' => abs($amountVoucher), 'rate' => 0, 'settle_date' => null, 'ext' => [ 'formula' => ($amountVoucher > 0 ? '管理员充值兑换券: +' : '管理员扣除兑换券: ') . $amountVoucher, 'memo' => $memo ?: ($amountVoucher > 0 ? '管理员充值兑换券' : '管理员扣除兑换券'), 'operator' => $operDesc, 'before_available' => number_format($available, 2, '.', ''), 'after_available' => number_format(max(0, $newReleased - $currentUsed), 2, '.', ''), ], 'status' => 1, ]); return true; } return false; }); } catch (\think\exception\HttpResponseException $e) { $data = $e->getResponse()->getData(); $msg = $data ? ($data['msg'] ?? '') : $e->getMessage(); $this->error($msg ?: '操作失败'); } catch (\Exception $e) { $this->error($e->getMessage() ?: '系统异常'); } if ($result) { $this->success('操作成功'); } $this->error('操作失败'); } /** * 用户优惠券列表 */ public function coupon($id) { $userCoupons = UserCouponModel::sheepFilter()->with('coupon')->where('user_id', $id) ->order('id', 'desc')->paginate($this->request->param('list_rows', 10)); $this->success('获取成功', null, $userCoupons); } public function select() { if (!$this->request->isAjax()) { return $this->view->fetch(); } $data = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); $this->success('获取成功', null, $data); } }