From 289adf5c36ce1e7a33c32e6e00276b0927008db3 Mon Sep 17 00:00:00 2001 From: chen Date: Thu, 17 Sep 2026 16:55:19 +0800 Subject: [PATCH] =?UTF-8?q?feat(user):=20=E9=87=8D=E6=9E=84=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E4=BC=9A=E5=91=98=E5=88=97=E8=A1=A8=E4=B8=8E=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E4=B8=BA=E5=85=A8=E5=B1=8F=E9=80=89=E9=A1=B9=E5=8D=A1?= =?UTF-8?q?=E5=B9=B6=E8=A1=A5=E9=BD=90=E4=BA=8C=E5=BC=80=E6=9D=83=E7=9B=8A?= =?UTF-8?q?=E8=B5=84=E4=BA=A7=E4=B8=AD=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/shopro/filter/user/UserFilter.php | 165 +++- .../admin/controller/shopro/user/User.php | 189 +++- .../admin/view/shopro/user/user/detail.html | 903 ++++++++++++------ .../admin/view/shopro/user/user/index.html | 174 +++- .../shopro/components/sa-user-profile.js | 13 +- public/assets/js/backend/shopro/user/user.js | 215 ++++- tests/verify_user_list_and_detail_spec.js | 124 +++ 7 files changed, 1453 insertions(+), 330 deletions(-) create mode 100644 tests/verify_user_list_and_detail_spec.js diff --git a/addons/shopro/filter/user/UserFilter.php b/addons/shopro/filter/user/UserFilter.php index 6a4f921..907ca24 100755 --- a/addons/shopro/filter/user/UserFilter.php +++ b/addons/shopro/filter/user/UserFilter.php @@ -3,12 +3,175 @@ namespace addons\shopro\filter\user; use addons\shopro\filter\BaseFilter; +use addons\shopro\service\partner\PartnerConfig; use think\db\Query; +use app\admin\model\shopro\commission\Agent as AgentModel; +use app\admin\model\shopro\user\User as UserModel; /** - * 用户管理 + * 用户管理 Filter 过滤器 */ class UserFilter extends BaseFilter { protected $keywordFields = ['id', 'username', 'nickname', 'mobile', 'email']; + + /** + * 身份筛选:品酒师 / 创客 / 酒道馆 / 区县代理 / 合伙股东 + */ + public function identity($query, $data) + { + $value = $this->plainValue($data); + if (!$value) { + return $query; + } + + $current = $query->getTable(); + $agentTable = (new AgentModel())->getQuery()->getTable(); + + if ($value === 'taster') { + return $query->where(function (Query $q) use ($current, $agentTable) { + $q->whereExists(function ($sub) use ($current, $agentTable) { + $sub->table($agentTable)->where($agentTable . '.user_id=' . $current . '.id')->where('level', 1); + })->whereOrNotExists(function ($sub) use ($current, $agentTable) { + $sub->table($agentTable)->where($agentTable . '.user_id=' . $current . '.id'); + }); + }); + } + if ($value === 'maker') { + return $query->whereExists(function ($sub) use ($current, $agentTable) { + $sub->table($agentTable)->where($agentTable . '.user_id=' . $current . '.id')->where('level', '>=', 2); + }); + } + if ($value === 'venue') { + return $query->whereExists(function ($sub) use ($current, $agentTable) { + $sub->table($agentTable)->where($agentTable . '.user_id=' . $current . '.id')->where('is_venue', 1); + }); + } + if ($value === 'county') { + return $query->whereExists(function ($sub) use ($current, $agentTable) { + $sub->table($agentTable)->where($agentTable . '.user_id=' . $current . '.id')->where('is_county', 1); + }); + } + if ($value === 'partner') { + return $query->whereExists(function ($sub) use ($current, $agentTable) { + $sub->table($agentTable)->where($agentTable . '.user_id=' . $current . '.id')->where('is_partner', 1); + }); + } + return $query; + } + + /** + * 创客等级筛选:V1 ~ V6 (对应 level = 2 ~ 7) + */ + public function makerLevel($query, $data) + { + $value = $this->plainValue($data); + if ($value === '' || $value === null) { + return $query; + } + $level = intval($value); + if ($level < 1 || $level > 7) { + return $query; + } + // 如果传入 1~6(即 V1~V6),映射为系统 level = 2~7;若传入 2~7,直接使用 + $targetLevel = ($level <= 6) ? ($level + 1) : $level; + + $current = $query->getTable(); + $agentTable = (new AgentModel())->getQuery()->getTable(); + + return $query->whereExists(function ($sub) use ($current, $agentTable, $targetLevel) { + $sub->table($agentTable)->where($agentTable . '.user_id=' . $current . '.id')->where('level', $targetLevel); + }); + } + + /** + * 30天自购活跃状态筛选 + */ + public function active30d($query, $data) + { + $value = $this->plainValue($data); + if ($value === '' || $value === null) { + return $query; + } + $days = PartnerConfig::activeDays(); + $since = time() - $days * 86400; + + $current = $query->getTable(); + $agentTable = (new AgentModel())->getQuery()->getTable(); + + if ($value === '1' || $value === 1 || $value === 'yes') { + return $query->whereExists(function ($sub) use ($current, $agentTable, $since) { + $sub->table($agentTable)->where($agentTable . '.user_id=' . $current . '.id') + ->where('last_self_order_time', '>=', $since); + }); + } else { + return $query->where(function (Query $q) use ($current, $agentTable, $since) { + $q->whereNotExists(function ($sub) use ($current, $agentTable) { + $sub->table($agentTable)->where($agentTable . '.user_id=' . $current . '.id'); + })->whereOrExists(function ($sub) use ($current, $agentTable, $since) { + $sub->table($agentTable)->where($agentTable . '.user_id=' . $current . '.id') + ->where(function (Query $sq) use ($since) { + $sq->whereNull('last_self_order_time')->whereOr('last_self_order_time', '<', $since); + }); + }); + }); + } + } + + /** + * 是否具备分销商/收益档案 + */ + public function hasAgent($query, $data) + { + $value = $this->plainValue($data); + if ($value === '' || $value === null) { + return $query; + } + $current = $query->getTable(); + $agentTable = (new AgentModel())->getQuery()->getTable(); + + if ($value === '1' || $value === 1 || $value === 'yes') { + return $query->whereExists(function ($sub) use ($current, $agentTable) { + $sub->table($agentTable)->where($agentTable . '.user_id=' . $current . '.id'); + }); + } else { + return $query->whereNotExists(function ($sub) use ($current, $agentTable) { + $sub->table($agentTable)->where($agentTable . '.user_id=' . $current . '.id'); + }); + } + } + + /** + * 推荐人检索 + */ + public function parentUser($query, $data) + { + $value = $this->plainValue($data); + if ($value === '' || $value === null) { + return $query; + } + if (is_numeric($value)) { + return $query->where('parent_user_id', intval($value)); + } + + $current = $query->getTable(); + $userTable = (new UserModel())->getQuery()->getTable(); + + return $query->whereExists(function ($sub) use ($current, $userTable, $value) { + $sub->table($userTable)->where($userTable . '.id=' . $current . '.parent_user_id') + ->where(function (Query $q) use ($value) { + $q->where('nickname', 'like', "%{$value}%") + ->whereOr('mobile', 'like', "%{$value}%") + ->whereOr('username', 'like', "%{$value}%"); + }); + }); + } + + protected function plainValue($data) + { + if (is_array($data)) { + return $data[0] ?? ''; + } + return $data; + } } diff --git a/application/admin/controller/shopro/user/User.php b/application/admin/controller/shopro/user/User.php index ab16d9a..bfa7d24 100644 --- a/application/admin/controller/shopro/user/User.php +++ b/application/admin/controller/shopro/user/User.php @@ -12,7 +12,7 @@ class User extends Common { protected $model = null; - protected $noNeedRight = ['select']; + protected $noNeedRight = ['select', 'rewards', 'voucherPlans', 'team']; public function _initialize() { @@ -29,7 +29,60 @@ class User extends Common return $this->view->fetch(); } - $data = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $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); + 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 = '从未下单'; + } + } + + $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' => Db::name('shopro_commission_agent')->where('is_partner', 1)->count(), + '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); } @@ -54,9 +107,141 @@ class User extends Common $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 . ' 天)' : '(从未下单)')); + } + + // 累计现金收益统计 + $totalCash = \app\admin\model\shopro\partner\Reward::where('user_id', $id) + ->where('status', 1) + ->where('amount', '>', 0) + ->sum('amount'); + $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) + ->where('status', 1) + ->where('amount', '>', 0) + ->where('reward_type', '<>', 'push3') + ->sum('amount'); + + $data['income_total_cash'] = number_format((float) $totalCash, 2, '.', ''); + $data['push3_cash_income'] = number_format((float) $push3Cash, 2, '.', ''); + $data['partner_exclusive_cash'] = number_format((float) $partnerExclusiveCash, 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); + } + /** * 更新用户 * diff --git a/application/admin/view/shopro/user/user/detail.html b/application/admin/view/shopro/user/user/detail.html index f21577b..1205dbc 100644 --- a/application/admin/view/shopro/user/user/detail.html +++ b/application/admin/view/shopro/user/user/detail.html @@ -1,48 +1,125 @@ {include file="/shopro/common/script" /}
- - - -
基本信息
-
-
-
- -
-
- {{ state.detail.nickname }} + + +
+
+ 返回 + + 会员详情 - {{ state.detail.nickname || state.detail.username || '会员' }} (#{{ state.detail.id }}) + + + {{ state.detail.status == 'normal' ? '正常' : '已禁用' }} + + + {{ state.detail.identity_text }} + +
+
+ + 收益中心分销商档案 + + {if $auth->check('shopro/user/user/recharge')} + 账户充值 + {/if} + +
+
+ + + + + + +
基本信息
+
+
+
+ +
+
+ {{ state.detail.nickname }} +
+
#{{ state.detail.id }}
-
#{{ state.detail.id }}
+ {if $auth->check('shopro/user/user/edit')} + 更换头像 + {/if}
- {if $auth->check('shopro/user/user/edit')} - 更换头像 - {/if} -
-
- - -
昵称
-
- -
-
- -
性别
-
- - 女 - 男 - -
-
- -
用户名
-
- - - -
-
- -
密码
-
- - - -
-
- -
手机号
-
- - - -
-
- -
电子邮箱
-
- - - -
-
- -
状态
-
- - 正常 - 禁用 - -
-
- -
-
- 重置 - {if $auth->check('shopro/user/user/edit')} - 保存 - {/if} -
-
-
-
-
- - - - -
账户信息
-
-
- - -
推荐人
-
- - {if $is_pro} - - 更换 - {/if} -
-
- -
佣金
-
{{ state.detail.commission }}
-
- -
身份
-
{{ state.detail.identity_text || '尚未生成分销身份' }}
-
- -
马豆
-
{{ state.detail.madow || '0' }}
-
- -
可用兑换券
-
- {{ state.detail.voucher_available || '0.00' }} - - 分销商详情 - -
-
- -
余额
-
- {{ state.detail.money }} - {if $auth->check('shopro/user/user/recharge')} - 充值 - {/if} -
-
- -
上次登陆时间
-
{{ state.detail.logintime }}
-
- -
积分
-
- {{ state.detail.score }} - {if $auth->check('shopro/user/user/recharge')} - 充值 - {/if} -
-
- -
登录IP
-
{{ state.detail.loginip }}
-
- -
总计消费
-
- {{ state.detail.total_consume }} -
-
- -
注册时间
-
{{ state.detail.createtime }}
-
-
-
-
-
- -
第三方账号
-
- - -
-
- - - {{ platform[item.provider][item.platform] }} - -
- -
-
-
登录次数:
-
{{ item.login_num }}
-
-
-
Openid:
-
{{ item.openid }}
-
-
-
Unionid:
-
{{ item.unionid }}
-
-
-
更新时间:
-
{{ item.updatetime }}
-
-
-