# LoginUserUtil 工具类使用文档

## 概述

`LoginUserUtil` 是一个用于在web端商户平台（`alien-store-platform`）中获取当前登录用户信息的工具类。它封装了从JWT Token中提取用户信息以及从数据库查询完整用户信息的常用方法。

---

## 核心特性

- ✅ **静态方法**: 所有方法都是静态方法，可直接调用，无需实例化
- ✅ **自动注入**: 使用`@Autowired`自动注入`StoreUserMapper`
- ✅ **异常处理**: 所有方法都包含完善的异常处理，确保不会抛出异常
- ✅ **日志记录**: 详细的日志记录，便于调试和问题排查
- ✅ **多种获取方式**: 支持从Token获取基本信息，或从数据库查询完整信息

---

## 类结构

```java
@Slf4j
@Component
public class LoginUserUtil {
    // 核心方法
    public static Integer getCurrentUserId()           // 获取用户ID
    public static String getCurrentUserPhone()         // 获取手机号
    public static String getCurrentUserType()          // 获取用户类型
    public static JSONObject getCurrentUserInfo()      // 获取Token中的用户信息
    public static StoreUser getCurrentStoreUser()      // 获取完整用户信息（数据库）
    public static StoreUser getCurrentStoreUserByPhone() // 根据手机号获取用户信息
    
    // 辅助方法
    public static boolean isCurrentUser(Integer userId)  // 验证是否为当前用户
    public static boolean isLogin()                      // 验证是否已登录
    public static boolean isStorePlatformUser()          // 验证是否为商户平台用户
    
    // 便捷方法
    public static Integer getCurrentStoreId()            // 获取门店ID
    public static String getCurrentNickName()            // 获取昵称
    public static String getCurrentName()                // 获取真实姓名
}
```

---

## 方法详解

### 1. getCurrentUserId()

获取当前登录用户的ID（从Token中获取，无需查询数据库）

```java
/**
 * 获取当前登录用户的ID
 * @return 用户ID，如果未登录返回null
 */
public static Integer getCurrentUserId()
```

**使用示例**:
```java
Integer userId = LoginUserUtil.getCurrentUserId();
if (userId != null) {
    System.out.println("当前用户ID: " + userId);
} else {
    System.out.println("用户未登录");
}
```

**返回值**:
- 成功: 返回用户ID（Integer）
- 未登录: 返回`null`

**性能**: ⚡ 快速（只解析Token，不查询数据库）

---

### 2. getCurrentUserPhone()

获取当前登录用户的手机号（从Token中获取）

```java
/**
 * 获取当前登录用户的手机号
 * @return 手机号，如果未登录返回null
 */
public static String getCurrentUserPhone()
```

**使用示例**:
```java
String phone = LoginUserUtil.getCurrentUserPhone();
if (phone != null) {
    System.out.println("当前用户手机号: " + phone);
}
```

**返回值**:
- 成功: 返回手机号（String）
- 未登录: 返回`null`

**性能**: ⚡ 快速（只解析Token）

---

### 3. getCurrentUserType()

获取当前登录用户的用户类型（从Token中获取）

```java
/**
 * 获取当前登录用户的用户类型
 * @return 用户类型，如果未登录返回null
 */
public static String getCurrentUserType()
```

**使用示例**:
```java
String userType = LoginUserUtil.getCurrentUserType();
if ("storePlatform".equals(userType)) {
    System.out.println("商户平台用户");
}
```

**返回值**:
- 成功: 返回用户类型（String），如 `"storePlatform"`
- 未登录: 返回`null`

**性能**: ⚡ 快速（只解析Token）

---

### 4. getCurrentUserInfo()

获取当前登录用户的Token信息（JSONObject格式）

```java
/**
 * 获取当前登录用户的Token信息（JSONObject格式）
 * @return 用户信息JSONObject，包含userId、phone、userType等字段
 */
public static JSONObject getCurrentUserInfo()
```

**使用示例**:
```java
JSONObject userInfo = LoginUserUtil.getCurrentUserInfo();
if (userInfo != null) {
    Integer userId = userInfo.getInteger("userId");
    String phone = userInfo.getString("phone");
    String userType = userInfo.getString("userType");
    
    System.out.println("用户ID: " + userId);
    System.out.println("手机号: " + phone);
    System.out.println("用户类型: " + userType);
}
```

**返回值**:
- 成功: 返回包含用户信息的`JSONObject`
- 未登录: 返回`null`

**包含字段**:
- `userId`: 用户ID
- `phone`: 手机号
- `userType`: 用户类型

**性能**: ⚡ 快速（只解析Token）

---

### 5. getCurrentStoreUser()

获取当前登录用户的完整商户用户信息（从数据库查询）

```java
/**
 * 获取当前登录用户的完整商户用户信息（从数据库查询）
 * @return StoreUser对象，如果未登录或查询失败返回null
 */
public static StoreUser getCurrentStoreUser()
```

**使用示例**:
```java
StoreUser storeUser = LoginUserUtil.getCurrentStoreUser();
if (storeUser != null) {
    System.out.println("用户ID: " + storeUser.getId());
    System.out.println("手机号: " + storeUser.getPhone());
    System.out.println("昵称: " + storeUser.getNickName());
    System.out.println("真实姓名: " + storeUser.getName());
    System.out.println("门店ID: " + storeUser.getStoreId());
    System.out.println("账户余额: " + storeUser.getMoney());
}
```

**返回值**:
- 成功: 返回`StoreUser`对象（包含所有字段）
- 未登录: 返回`null`
- 查询失败: 返回`null`

**性能**: 🐢 较慢（需要查询数据库）

**注意**: 
- 适用于需要完整用户信息的场景
- 会产生数据库查询，建议在需要时才调用

---

### 6. getCurrentStoreUserByPhone()

根据手机号获取商户用户信息（从数据库查询）

```java
/**
 * 根据手机号获取商户用户信息
 * @return StoreUser对象，如果未登录或查询失败返回null
 */
public static StoreUser getCurrentStoreUserByPhone()
```

**使用示例**:
```java
StoreUser storeUser = LoginUserUtil.getCurrentStoreUserByPhone();
if (storeUser != null) {
    System.out.println("用户信息: " + storeUser);
}
```

**返回值**:
- 成功: 返回`StoreUser`对象
- 未登录: 返回`null`
- 查询失败: 返回`null`

**性能**: 🐢 较慢（需要查询数据库）

**与 getCurrentStoreUser() 的区别**:
- `getCurrentStoreUser()`: 根据用户ID查询（主键查询，更快）
- `getCurrentStoreUserByPhone()`: 根据手机号查询（索引查询，稍慢）

---

### 7. isCurrentUser(Integer userId)

验证当前登录用户是否为指定用户

```java
/**
 * 验证当前登录用户是否为指定用户
 * @param userId 要验证的用户ID
 * @return true-是当前用户, false-不是当前用户
 */
public static boolean isCurrentUser(Integer userId)
```

**使用示例**:
```java
Integer targetUserId = 12345;
if (LoginUserUtil.isCurrentUser(targetUserId)) {
    System.out.println("是当前用户，允许操作");
} else {
    System.out.println("不是当前用户，拒绝操作");
}
```

**使用场景**:
- 权限验证：确保用户只能修改自己的信息
- 安全检查：防止用户操作其他用户的数据

**返回值**:
- `true`: 是当前登录用户
- `false`: 不是当前登录用户或未登录

---

### 8. isLogin()

验证当前用户是否已登录

```java
/**
 * 验证当前用户是否已登录
 * @return true-已登录, false-未登录
 */
public static boolean isLogin()
```

**使用示例**:
```java
if (LoginUserUtil.isLogin()) {
    System.out.println("用户已登录");
} else {
    System.out.println("用户未登录");
}
```

**返回值**:
- `true`: 已登录
- `false`: 未登录

---

### 9. isStorePlatformUser()

验证当前用户类型是否为web端商户平台

```java
/**
 * 验证当前用户类型是否为web端商户平台
 * @return true-是商户平台用户, false-不是
 */
public static boolean isStorePlatformUser()
```

**使用示例**:
```java
if (LoginUserUtil.isStorePlatformUser()) {
    System.out.println("商户平台用户，允许访问");
} else {
    System.out.println("非商户平台用户，拒绝访问");
}
```

**返回值**:
- `true`: 用户类型为 `storePlatform`
- `false`: 用户类型不是 `storePlatform` 或未登录

---

### 10. getCurrentStoreId()

获取当前登录用户的门店ID

```java
/**
 * 获取当前登录用户的门店ID
 * @return 门店ID，如果未登录或用户无门店返回null
 */
public static Integer getCurrentStoreId()
```

**使用示例**:
```java
Integer storeId = LoginUserUtil.getCurrentStoreId();
if (storeId != null) {
    System.out.println("当前用户的门店ID: " + storeId);
}
```

**返回值**:
- 成功: 返回门店ID（Integer）
- 未登录: 返回`null`
- 用户无门店: 返回`null`

**性能**: 🐢 较慢（需要查询数据库）

---

### 11. getCurrentNickName()

获取当前登录用户的昵称

```java
/**
 * 获取当前登录用户的昵称
 * @return 昵称，如果未登录返回null
 */
public static String getCurrentNickName()
```

**使用示例**:
```java
String nickName = LoginUserUtil.getCurrentNickName();
if (nickName != null) {
    System.out.println("欢迎，" + nickName);
}
```

**返回值**:
- 成功: 返回昵称（String）
- 未登录: 返回`null`

**性能**: 🐢 较慢（需要查询数据库）

---

### 12. getCurrentName()

获取当前登录用户的真实姓名

```java
/**
 * 获取当前登录用户的真实姓名
 * @return 真实姓名，如果未登录返回null
 */
public static String getCurrentName()
```

**使用示例**:
```java
String name = LoginUserUtil.getCurrentName();
if (name != null) {
    System.out.println("真实姓名: " + name);
}
```

**返回值**:
- 成功: 返回真实姓名（String）
- 未登录: 返回`null`

**性能**: 🐢 较慢（需要查询数据库）

---

## 实际应用场景

### 场景1: 获取当前用户的基本信息

```java
@RestController
@RequestMapping("/user")
public class UserController {
    
    @GetMapping("/profile")
    public R<StoreUser> getUserProfile() {
        // 获取当前登录用户的完整信息
        StoreUser storeUser = LoginUserUtil.getCurrentStoreUser();
        if (storeUser == null) {
            return R.fail("请先登录");
        }
        return R.data(storeUser);
    }
}
```

---

### 场景2: 验证用户权限（只能操作自己的数据）

```java
@Service
public class StoreServiceImpl implements StoreService {
    
    @Override
    public boolean updateStore(Integer storeId, StoreInfo storeInfo) {
        // 获取当前用户的门店ID
        Integer currentStoreId = LoginUserUtil.getCurrentStoreId();
        
        // 验证用户只能修改自己的门店信息
        if (!storeId.equals(currentStoreId)) {
            throw new RuntimeException("您只能修改自己的门店信息");
        }
        
        // 执行更新操作
        return updateStoreInfo(storeInfo);
    }
}
```

---

### 场景3: 自动填充当前用户信息

```java
@Service
public class OrderServiceImpl implements OrderService {
    
    @Override
    public boolean createOrder(Order order) {
        // 自动填充当前用户信息
        Integer userId = LoginUserUtil.getCurrentUserId();
        Integer storeId = LoginUserUtil.getCurrentStoreId();
        
        order.setUserId(userId);
        order.setStoreId(storeId);
        order.setCreateTime(new Date());
        
        return orderMapper.insert(order) > 0;
    }
}
```

---

### 场景4: 根据用户类型返回不同数据

```java
@Service
public class DashboardServiceImpl implements DashboardService {
    
    @Override
    public JSONObject getDashboardData() {
        JSONObject result = new JSONObject();
        
        // 验证用户类型
        if (!LoginUserUtil.isStorePlatformUser()) {
            throw new RuntimeException("只有商户平台用户可以访问");
        }
        
        // 获取用户信息
        Integer storeId = LoginUserUtil.getCurrentStoreId();
        
        // 查询仪表盘数据
        result.put("todayIncome", getTodayIncome(storeId));
        result.put("todayOrderCount", getTodayOrderCount(storeId));
        
        return result;
    }
}
```

---

### 场景5: 日志记录（记录操作人）

```java
@Service
public class AuditLogServiceImpl implements AuditLogService {
    
    @Override
    public void logOperation(String action, String details) {
        AuditLog log = new AuditLog();
        
        // 记录操作人信息
        log.setUserId(LoginUserUtil.getCurrentUserId());
        log.setUserPhone(LoginUserUtil.getCurrentUserPhone());
        log.setUserName(LoginUserUtil.getCurrentName());
        log.setAction(action);
        log.setDetails(details);
        log.setOperateTime(new Date());
        
        auditLogMapper.insert(log);
    }
}
```

---

### 场景6: 条件查询（只查询当前用户的数据）

```java
@Service
public class OrderServiceImpl implements OrderService {
    
    @Override
    public List<Order> getMyOrders(Integer page, Integer size) {
        // 获取当前用户ID
        Integer userId = LoginUserUtil.getCurrentUserId();
        if (userId == null) {
            throw new RuntimeException("请先登录");
        }
        
        // 查询当前用户的订单
        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Order::getUserId, userId)
               .orderByDesc(Order::getCreateTime);
        
        return orderMapper.selectPage(new Page<>(page, size), wrapper).getRecords();
    }
}
```

---

### 场景7: 简化Controller代码

**之前的写法**:
```java
@PostMapping("/updateProfile")
public R<Boolean> updateProfile(@RequestBody StoreUser storeUser) {
    // 需要手动获取用户信息
    JSONObject userInfo = JwtUtil.getCurrentUserInfo();
    if (userInfo == null) {
        return R.fail("请先登录");
    }
    Integer userId = userInfo.getInteger("userId");
    storeUser.setId(userId);
    
    boolean success = storeUserService.updateById(storeUser);
    return success ? R.success("修改成功") : R.fail("修改失败");
}
```

**使用工具类后**:
```java
@PostMapping("/updateProfile")
public R<Boolean> updateProfile(@RequestBody StoreUser storeUser) {
    // 直接获取用户ID
    Integer userId = LoginUserUtil.getCurrentUserId();
    if (userId == null) {
        return R.fail("请先登录");
    }
    storeUser.setId(userId);
    
    boolean success = storeUserService.updateById(storeUser);
    return success ? R.success("修改成功") : R.fail("修改失败");
}
```

---

## 性能对比

| 方法 | 性能 | 是否查询数据库 | 适用场景 |
|------|------|---------------|----------|
| `getCurrentUserId()` | ⚡⚡⚡ 极快 | ❌ 否 | 只需要用户ID |
| `getCurrentUserPhone()` | ⚡⚡⚡ 极快 | ❌ 否 | 只需要手机号 |
| `getCurrentUserType()` | ⚡⚡⚡ 极快 | ❌ 否 | 验证用户类型 |
| `getCurrentUserInfo()` | ⚡⚡⚡ 极快 | ❌ 否 | 需要Token中的多个字段 |
| `getCurrentStoreUser()` | 🐢 较慢 | ✅ 是 | 需要完整用户信息 |
| `getCurrentStoreUserByPhone()` | 🐢 较慢 | ✅ 是 | 根据手机号查询 |
| `getCurrentStoreId()` | 🐢 较慢 | ✅ 是 | 只需要门店ID |
| `getCurrentNickName()` | 🐢 较慢 | ✅ 是 | 只需要昵称 |
| `getCurrentName()` | 🐢 较慢 | ✅ 是 | 只需要真实姓名 |

**优化建议**:
- ✅ 优先使用不查询数据库的方法（`getCurrentUserId()`、`getCurrentUserPhone()`等）
- ✅ 如果需要多个用户字段，使用`getCurrentStoreUser()`一次性获取，避免多次查询
- ✅ 不要在循环中调用需要查询数据库的方法

---

## 注意事项

### 1. 空值处理

所有方法在获取失败时都返回`null`，使用时需要判空：

```java
Integer userId = LoginUserUtil.getCurrentUserId();
if (userId != null) {
    // 处理业务逻辑
} else {
    // 处理未登录情况
}
```

### 2. 异常处理

工具类内部已处理所有异常，不会向外抛出异常，确保调用安全。

### 3. 静态方法限制

由于使用静态方法，需要通过`@Autowired`注入`StoreUserMapper`到静态字段。这是通过`@Component`和setter方法实现的。

### 4. 性能考虑

- **频繁调用**: 如果需要频繁获取用户信息，建议在Service层缓存
- **数据库查询**: 避免在循环中调用需要查询数据库的方法

### 5. 线程安全

工具类是线程安全的，可以在多线程环境下使用。

---

## 与 @LoginRequired 注解配合使用

`LoginUserUtil` 与 `@LoginRequired` 注解配合使用效果更佳：

```java
@RestController
@RequestMapping("/store")
public class StoreController {
    
    @LoginRequired  // AOP切面验证登录
    @GetMapping("/myStore")
    public R<StoreInfo> getMyStore() {
        // 登录验证已通过，直接获取门店ID
        Integer storeId = LoginUserUtil.getCurrentStoreId();
        StoreInfo storeInfo = storeService.getById(storeId);
        return R.data(storeInfo);
    }
}
```

---

## 配置要求

### 1. Spring Bean注册

工具类使用`@Component`注解，需要被Spring扫描到：

```java
@Component  // 必须
public class LoginUserUtil {
    // ...
}
```

### 2. 包扫描配置

确保`LoginUserUtil`所在的包被Spring Boot扫描：

```java
@SpringBootApplication
@ComponentScan(basePackages = {"shop.alien.storeplatform"})
public class Application {
    // ...
}
```

### 3. Mapper依赖

确保`StoreUserMapper`已注册为Spring Bean：

```java
@Mapper
public interface StoreUserMapper extends BaseMapper<StoreUser> {
}
```

---

## 常见问题

### Q1: 为什么有时返回null？

**原因**:
1. 用户未登录（Token不存在或无效）
2. Token已过期
3. 用户在数据库中不存在
4. 网络或数据库异常

**解决方案**:
- 使用前先判断是否已登录：`LoginUserUtil.isLogin()`
- 检查日志中的错误信息

---

### Q2: 如何在非Controller层使用？

**答案**: 工具类可以在任何Spring管理的Bean中使用：

```java
@Service
public class MyService {
    
    public void someMethod() {
        Integer userId = LoginUserUtil.getCurrentUserId();
        // 使用userId
    }
}
```

---

### Q3: 性能是否有问题？

**答案**: 
- Token解析方法（如`getCurrentUserId()`）性能很高，可以频繁调用
- 数据库查询方法（如`getCurrentStoreUser()`）性能较低，建议缓存结果

---

### Q4: 如何缓存用户信息？

**建议方案**:

```java
@Service
public class MyService {
    
    private StoreUser currentUser;
    
    public void processOrder() {
        // 第一次获取
        if (currentUser == null) {
            currentUser = LoginUserUtil.getCurrentStoreUser();
        }
        
        // 后续使用缓存的数据
        Integer storeId = currentUser.getStoreId();
        // ...
    }
}
```

或使用ThreadLocal缓存（适用于同一次请求）。

---

## 更新日志

### 2025-11-17

**新增工具类**:
- ✅ 创建`LoginUserUtil`工具类

**核心功能**:
- ✅ 获取当前登录用户ID、手机号、用户类型
- ✅ 获取Token中的用户信息（JSONObject）
- ✅ 从数据库查询完整用户信息
- ✅ 验证用户登录状态
- ✅ 验证用户类型
- ✅ 验证是否为当前用户
- ✅ 获取门店ID、昵称、姓名等便捷方法

**技术特点**:
- ✅ 静态方法，方便调用
- ✅ 完善的异常处理
- ✅ 详细的日志记录
- ✅ 支持多种获取方式

**开发人员**: ssk

---

**文档版本**: v1.0  
**最后更新**: 2025-11-17  
**维护人员**: ssk

