From c983a0a398504697a777ffae48e59961b5ee2bd7 Mon Sep 17 00:00:00 2001
From: liujie <1793218484@qq.com>
Date: 星期二, 13 十月 2026 15:07:12 +0800
Subject: [PATCH] 系统管理模块 用户管理模块

---
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountUserController.java |  135 +++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 129 insertions(+), 6 deletions(-)

diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountUserController.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountUserController.java
index ba90416..b59f285 100644
--- a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountUserController.java
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountUserController.java
@@ -11,6 +11,7 @@
 import org.springframework.web.bind.annotation.PutMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import com.ruoyi.common.core.utils.StringUtils;
 import com.ruoyi.common.core.utils.poi.ExcelUtil;
@@ -21,6 +22,7 @@
 import com.ruoyi.common.log.enums.BusinessType;
 import com.ruoyi.common.security.annotation.RequiresPermissions;
 import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.user.domain.AccountAddress;
 import com.ruoyi.user.domain.AccountUser;
 import com.ruoyi.user.service.IAccountUserService;
 
@@ -42,7 +44,7 @@
     /**
      * 查询会员列表
      */
-    @RequiresPermissions("account:user:list")
+    @RequiresPermissions("member:user:list")
     @GetMapping("/list")
     public TableDataInfo list(AccountUser accountUser)
     {
@@ -54,7 +56,7 @@
     /**
      * 导出会员列表
      */
-    @RequiresPermissions("account:user:export")
+    @RequiresPermissions("member:user:export")
     @Log(title = "会员管理", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
     public void export(HttpServletResponse response, AccountUser accountUser)
@@ -67,7 +69,7 @@
     /**
      * 获取会员详细信息
      */
-    @RequiresPermissions("account:user:query")
+    @RequiresPermissions("member:user:query")
     @GetMapping(value = "/{userId}")
     public AjaxResult getInfo(@PathVariable Long userId)
     {
@@ -77,7 +79,7 @@
     /**
      * 新增会员
      */
-    @RequiresPermissions("account:user:add")
+    @RequiresPermissions("member:user:add")
     @Log(title = "会员管理", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@Validated @RequestBody AccountUser accountUser)
@@ -101,7 +103,7 @@
     /**
      * 修改会员
      */
-    @RequiresPermissions("account:user:edit")
+    @RequiresPermissions("member:user:edit")
     @Log(title = "会员管理", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@Validated @RequestBody AccountUser accountUser)
@@ -121,11 +123,132 @@
     /**
      * 删除会员
      */
-    @RequiresPermissions("account:user:remove")
+    @RequiresPermissions("member:user:remove")
     @Log(title = "会员管理", businessType = BusinessType.DELETE)
     @DeleteMapping("/{userIds}")
     public AjaxResult remove(@PathVariable Long[] userIds)
     {
         return toAjax(accountUserService.deleteAccountUserByIds(userIds));
     }
+
+    /**
+     * 获取会员详情(一次取回详情页各 Tab 的数据)
+     *
+     * 路径用 /detail/{userId} 而非 /{userId}/detail:后者在 Spring 的路径匹配下
+     * 会与 getInfo 的 /{userId} 就 "detail" 这个片段产生歧义。
+     */
+    @RequiresPermissions("member:user:query")
+    @GetMapping(value = "/detail/{userId}")
+    public AjaxResult getDetail(@PathVariable Long userId)
+    {
+        return success(accountUserService.selectAccountUserDetail(userId));
+    }
+
+    /**
+     * 修改推荐人/供货人关系
+     *
+     * 与 edit 分开的风险点:edit 走 @Validated 全量校验(userName 必填 + 用户名/手机号查重),
+     * 只传关系字段必然 400,故必须有独立入口。
+     *
+     * 只改供货人时 referrerId 也要原样带上:服务层的 updateUserRelation 对它无条件赋值,
+     * 漏传即为「解除推荐关系」。供货人的等级校验在服务层(searchSupplierCandidates 只给合法候选,
+     * 这里再兜一道,绕开页面直接打接口也拦得住)。
+     */
+    @RequiresPermissions("member:user:edit")
+    @Log(title = "会员管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/relation")
+    public AjaxResult relation(@RequestBody AccountUser accountUser)
+    {
+        Long userId = accountUser.getUserId();
+        if (StringUtils.isNull(userId))
+        {
+            return error("会员ID不能为空");
+        }
+        // 防自环:把自己设成自己的推荐人/供货人会让层级计算陷入死循环
+        if (userId.equals(accountUser.getReferrerId()) || userId.equals(accountUser.getSupplierId()))
+        {
+            return error("推荐人/供货人不能是会员本人");
+        }
+        accountUser.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(accountUserService.updateAccountUserRelation(accountUser));
+    }
+
+    /**
+     * 搜索可指定为供货人的会员(等级严格高于该会员),供「修改供货人」弹窗的下拉使用
+     *
+     * 路径是字面量 /supplierSearch,与上面的 GET /{userId} 同为单段。Spring 的路径匹配按
+     * 「字面量优先于变量」排序,故不会被 /{userId} 抢走(否则会因 "supplierSearch" 无法转
+     * Long 而报 500)。
+     *
+     * 权限与 relation 一致:能打开这个弹窗的人本来就有 member:user:edit。
+     */
+    @RequiresPermissions("member:user:edit")
+    @GetMapping("/supplierSearch")
+    public AjaxResult supplierSearch(@RequestParam(required = false) Long userId,
+                                     @RequestParam(required = false) String keyword)
+    {
+        if (StringUtils.isNull(userId))
+        {
+            return error("会员ID不能为空");
+        }
+        return success(accountUserService.searchSupplierCandidates(userId, keyword));
+    }
+
+    /**
+     * 修改收货地址
+     *
+     * 路径 /address 是字面量,与上面的 DELETE /{userIds} 不会打架:
+     * 后者只能匹配单段路径,/address/{ids} 是两段。
+     */
+    @RequiresPermissions("member:user:edit")
+    @Log(title = "会员收货地址", businessType = BusinessType.UPDATE)
+    @PutMapping("/address")
+    public AjaxResult editAddress(@RequestBody AccountAddress accountAddress)
+    {
+        if (StringUtils.isNull(accountAddress.getId()))
+        {
+            return error("地址ID不能为空");
+        }
+        // 置默认前服务层要按 userId 清旧默认标记,userId 缺失会误清或报错,故必填
+        if (StringUtils.isNull(accountAddress.getUserId()))
+        {
+            return error("会员ID不能为空");
+        }
+        accountAddress.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(accountUserService.updateAccountAddress(accountAddress));
+    }
+
+    /**
+     * 删除收货地址
+     */
+    @RequiresPermissions("member:user:edit")
+    @Log(title = "会员收货地址", businessType = BusinessType.DELETE)
+    @DeleteMapping("/address/{ids}")
+    public AjaxResult removeAddress(@PathVariable Long[] ids)
+    {
+        return toAjax(accountUserService.deleteAccountAddressByIds(ids));
+    }
+
+    /**
+     * 修改账户状态(禁用/启用)
+     *
+     * 只有「正常」与「禁用」能通过本接口互切;注销中/已注销由注销审核流程驱动,
+     * 服务层会拒绝这两态进出此接口。同样不能复用 PUT /user,原因见 relation 的注释。
+     */
+    @RequiresPermissions("member:user:edit")
+    @Log(title = "会员管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public AjaxResult changeStatus(@RequestBody AccountUser accountUser)
+    {
+        if (StringUtils.isNull(accountUser.getUserId()))
+        {
+            return error("会员ID不能为空");
+        }
+        if (StringUtils.isEmpty(accountUser.getStatus()))
+        {
+            return error("账户状态不能为空");
+        }
+        accountUser.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(accountUserService.changeStatus(accountUser));
+    }
 }

--
Gitblit v1.7.1