diff --git a/mt-pay/ERP_USER_API.md b/mt-pay/ERP_USER_API.md new file mode 100644 index 0000000..2801440 --- /dev/null +++ b/mt-pay/ERP_USER_API.md @@ -0,0 +1,253 @@ +# ERP用户管理API文档 + +## 概述 + +ERP用户管理模块提供了用户注册、登录等基础功能,支持账号、密码、店铺号管理。 + +## 数据库表 + +### erp_user 表结构 + +执行以下SQL创建表: + +```sql +-- 文件位置:mt-pay/database/erp_user_schema.sql +``` + +表字段说明: +- `id`: 主键ID +- `username`: 账号(唯一,3-50个字符,只能包含字母、数字和下划线) +- `password`: 密码(MD5加密,6-20个字符) +- `nick_name`: 用户名称(可选,最大50个字符) +- `phone`: 手机号(可选,唯一,格式:1开头11位数字) +- `email`: 邮箱(可选,唯一,最大100个字符) +- `store_code`: 店铺号(最大50个字符) +- `status`: 状态(ACTIVE-激活,DISABLED-禁用) +- `last_login_time`: 最后登录时间 +- `last_login_ip`: 最后登录IP +- `create_time`: 创建时间 +- `update_time`: 更新时间 + +## API接口 + +### 1. 用户注册 + +**接口地址:** `POST /api/erp/user/register` + +**请求头:** +``` +Content-Type: application/json +``` + +**请求体:** +```json +{ + "username": "testuser", + "password": "123456", + "storeCode": "STORE001", + "nickName": "测试用户", + "phone": "13800138000", + "email": "test@example.com" +} +``` + +**参数说明:** +- `username` (必填): 账号,3-50个字符,只能包含字母、数字和下划线 +- `password` (必填): 密码,6-20个字符 +- `storeCode` (必填): 店铺号,最大50个字符 +- `nickName` (可选): 用户名称,最大50个字符 +- `phone` (可选): 手机号,格式:1开头11位数字(如:13800138000),必须唯一 +- `email` (可选): 邮箱,最大100个字符,必须唯一 + +**响应示例:** +```json +{ + "code": "0000", + "message": "注册成功", + "data": { + "id": 1, + "username": "testuser", + "nickName": "测试用户", + "phone": "13800138000", + "email": "test@example.com", + "storeCode": "STORE001", + "status": "ACTIVE", + "createTime": "2024-12-24T10:00:00" + }, + "timestamp": 1703412000000 +} +``` + +**错误响应:** +- 账号已存在:`{"code": "6000", "message": "账号已存在"}` +- 手机号已被注册:`{"code": "6000", "message": "手机号已被注册"}` +- 邮箱已被注册:`{"code": "6000", "message": "邮箱已被注册"}` +- 参数验证失败:`{"code": "4001", "message": "参数验证失败"}` + +--- + +### 2. 用户登录 + +**接口地址:** `POST /api/erp/user/login` + +**请求头:** +``` +Content-Type: application/json +``` + +**请求体:** +```json +{ + "username": "testuser", + "password": "123456" +} +``` + +**参数说明:** +- `username` (必填): 账号 +- `password` (必填): 密码 + +**响应示例:** +```json +{ + "code": "0000", + "message": "登录成功", + "data": { + "id": 1, + "username": "testuser", + "nickName": "测试用户", + "phone": "13800138000", + "email": "test@example.com", + "storeCode": "STORE001", + "status": "ACTIVE", + "token": "MTIzNDU2Nzg5MGFiY2RlZjoxMjM0NTY3ODkwYWJjZGVmOjE3MDM0MTIwMDAwMDA6YWJjZGVmMTIzNDU2Nzg5MA==", + "tokenExpireTime": 1704016800000, + "lastLoginTime": "2024-12-24T10:00:00", + "lastLoginIp": "192.168.1.100" + }, + "timestamp": 1703412000000 +} +``` + +**错误响应:** +- 账号或密码错误:`{"code": "4002", "message": "账号或密码错误"}` +- 账号已被禁用:`{"code": "4003", "message": "账号已被禁用"}` + +--- + +## Token使用说明 + +### Token生成 + +登录成功后,系统会返回一个`token`字段,该token的有效期为7天。 + +### Token验证 + +后续需要认证的接口,可以在请求头中携带token: + +``` +Authorization: Bearer {token} +``` + +或者使用自定义header: + +``` +X-Auth-Token: {token} +``` + +### Token验证方法 + +在需要认证的接口中,可以通过以下方式验证token: + +```java +@Autowired +private ErpUserService erpUserService; + +// 验证token +ErpUser user = erpUserService.validateToken(token); +if (user == null) { + // Token无效或已过期 + return Result.fail(ResultCode.TOKEN_INVALID); +} +``` + +--- + +## 错误码说明 + +| 错误码 | 说明 | +|--------|------| +| 0000 | 操作成功 | +| 4000 | 参数错误 | +| 4001 | 参数验证失败 | +| 4002 | 未授权(账号或密码错误) | +| 4003 | 禁止访问(账号被禁用) | +| 6000 | 业务错误(账号已存在等) | +| 7001 | 用户不存在 | +| 7002 | 用户已存在 | +| 7003 | 密码错误 | +| 7004 | Token无效或已过期 | + +--- + +## 安全说明 + +1. **密码加密**:密码使用MD5加密存储,生产环境建议使用BCrypt或Argon2等更安全的加密方式。 + +2. **Token安全**: + - 当前实现使用简单的MD5+Base64编码,生产环境建议使用JWT(JSON Web Token) + - Token包含用户ID、用户名、时间戳和签名 + - Token有效期为7天,过期后需要重新登录 + +3. **IP记录**:系统会记录用户最后登录的IP地址,可用于安全审计。 + +4. **账号状态**:支持账号禁用功能,禁用后的账号无法登录。 + +--- + +## 后续扩展建议 + +1. **JWT Token**:将当前简单的Token实现替换为标准的JWT +2. **密码策略**:添加密码复杂度要求、密码过期策略 +3. **登录限制**:添加登录失败次数限制、IP白名单等功能 +4. **权限管理**:添加角色和权限管理功能 +5. **操作日志**:记录用户的操作日志,便于审计 + +--- + +## 测试示例 + +### 使用curl测试注册接口 + +```bash +curl -X POST http://localhost:8080/api/erp/user/register \ + -H "Content-Type: application/json" \ + -d '{ + "username": "testuser", + "password": "123456", + "storeCode": "STORE001", + "nickName": "测试用户", + "phone": "13800138000", + "email": "test@example.com" + }' +``` + +### 使用curl测试登录接口 + +```bash +curl -X POST http://localhost:8080/api/erp/user/login \ + -H "Content-Type: application/json" \ + -d '{ + "username": "testuser", + "password": "123456" + }' +``` + +--- + +## 注意事项 + +1. 执行数据库表创建SQL后,才能使用注册和登录功能 +2. 生产环境建议修改`TokenUtils`中的`TOKEN_SECRET`,使用更复杂的密钥 +3. 建议在生产环境使用HTTPS协议,保护密码和Token传输安全 + diff --git a/mt-pay/database/erp_user_schema.sql b/mt-pay/database/erp_user_schema.sql new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/mt-pay/database/erp_user_schema.sql @@ -0,0 +1 @@ + diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/common/ResultCode.java b/mt-pay/src/main/java/com/mtkj/mtpay/common/ResultCode.java index 01eae7c..97446b0 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/common/ResultCode.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/common/ResultCode.java @@ -88,7 +88,27 @@ public enum ResultCode { /** * 业务错误 */ - BUSINESS_ERROR("6000", "业务错误"); + BUSINESS_ERROR("6000", "业务错误"), + + /** + * 用户不存在 + */ + USER_NOT_FOUND("7001", "用户不存在"), + + /** + * 用户已存在 + */ + USER_EXISTS("7002", "用户已存在"), + + /** + * 密码错误 + */ + PASSWORD_ERROR("7003", "密码错误"), + + /** + * Token无效或已过期 + */ + TOKEN_INVALID("7004", "Token无效或已过期"); /** * 响应码 diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/controller/ErpUserController.java b/mt-pay/src/main/java/com/mtkj/mtpay/controller/ErpUserController.java new file mode 100644 index 0000000..824e780 --- /dev/null +++ b/mt-pay/src/main/java/com/mtkj/mtpay/controller/ErpUserController.java @@ -0,0 +1,80 @@ +package com.mtkj.mtpay.controller; + +import com.mtkj.mtpay.common.Result; +import com.mtkj.mtpay.dto.request.ErpUserLoginRequestDTO; +import com.mtkj.mtpay.dto.request.ErpUserRegisterRequestDTO; +import com.mtkj.mtpay.dto.response.ErpUserLoginResponseDTO; +import com.mtkj.mtpay.dto.response.ErpUserRegisterResponseDTO; +import com.mtkj.mtpay.service.ErpUserService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +/** + * ERP用户控制器 + */ +@Slf4j +@RestController +@RequestMapping("/api/erp/user") +@RequiredArgsConstructor +public class ErpUserController { + + private final ErpUserService erpUserService; + + /** + * 用户注册 + */ + @PostMapping("/register") + public Result register(@Valid @RequestBody ErpUserRegisterRequestDTO request) { + log.info("用户注册请求,账号: {}", request.getUsername()); + ErpUserRegisterResponseDTO response = erpUserService.register(request); + return Result.success("注册成功", response); + } + + /** + * 用户登录 + */ + @PostMapping("/login") + public Result login(@Valid @RequestBody ErpUserLoginRequestDTO request, + HttpServletRequest httpRequest) { + log.info("用户登录请求,账号: {}", request.getUsername()); + + // 获取客户端IP + String loginIp = getClientIp(httpRequest); + + ErpUserLoginResponseDTO response = erpUserService.login(request, loginIp); + return Result.success("登录成功", response); + } + + /** + * 获取客户端IP地址 + */ + private String getClientIp(HttpServletRequest request) { + String ip = request.getHeader("X-Forwarded-For"); + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + + // 处理多个IP的情况,取第一个IP + if (ip != null && ip.contains(",")) { + ip = ip.split(",")[0].trim(); + } + + return ip; + } +} + diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/dto/request/ErpUserLoginRequestDTO.java b/mt-pay/src/main/java/com/mtkj/mtpay/dto/request/ErpUserLoginRequestDTO.java new file mode 100644 index 0000000..b1b02f0 --- /dev/null +++ b/mt-pay/src/main/java/com/mtkj/mtpay/dto/request/ErpUserLoginRequestDTO.java @@ -0,0 +1,24 @@ +package com.mtkj.mtpay.dto.request; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * ERP用户登录请求DTO + */ +@Data +public class ErpUserLoginRequestDTO { + + /** + * 账号 + */ + @NotBlank(message = "账号不能为空") + private String username; + + /** + * 密码 + */ + @NotBlank(message = "密码不能为空") + private String password; +} + diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/dto/request/ErpUserRegisterRequestDTO.java b/mt-pay/src/main/java/com/mtkj/mtpay/dto/request/ErpUserRegisterRequestDTO.java new file mode 100644 index 0000000..b8788e6 --- /dev/null +++ b/mt-pay/src/main/java/com/mtkj/mtpay/dto/request/ErpUserRegisterRequestDTO.java @@ -0,0 +1,56 @@ +package com.mtkj.mtpay.dto.request; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Data; + +/** + * ERP用户注册请求DTO + */ +@Data +public class ErpUserRegisterRequestDTO { + + /** + * 账号 + */ + @NotBlank(message = "账号不能为空") + @Size(min = 3, max = 50, message = "账号长度必须在3-50个字符之间") + @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "账号只能包含字母、数字和下划线") + private String username; + + /** + * 密码 + */ + @NotBlank(message = "密码不能为空") + @Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间") + private String password; + + /** + * 店铺号 + */ + @NotBlank(message = "店铺号不能为空") + @Size(max = 50, message = "店铺号长度不能超过50个字符") + private String storeCode; + + /** + * 用户名称 + */ + @Size(max = 50, message = "用户名称长度不能超过50个字符") + private String nickName; + + /** + * 手机号 + */ + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") + private String phone; + + /** + * 邮箱 + */ + @Email(message = "邮箱格式不正确") + @Size(max = 100, message = "邮箱长度不能超过100个字符") + private String email; +} + diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/dto/response/ErpUserLoginResponseDTO.java b/mt-pay/src/main/java/com/mtkj/mtpay/dto/response/ErpUserLoginResponseDTO.java new file mode 100644 index 0000000..62f2ffa --- /dev/null +++ b/mt-pay/src/main/java/com/mtkj/mtpay/dto/response/ErpUserLoginResponseDTO.java @@ -0,0 +1,74 @@ +package com.mtkj.mtpay.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * ERP用户登录响应DTO + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpUserLoginResponseDTO { + + /** + * 用户ID + */ + private Long id; + + /** + * 账号 + */ + private String username; + + /** + * 用户名称 + */ + private String nickName; + + /** + * 手机号 + */ + private String phone; + + /** + * 邮箱 + */ + private String email; + + /** + * 店铺号 + */ + private String storeCode; + + /** + * 状态 + */ + private String status; + + /** + * Token(用于后续接口认证) + */ + private String token; + + /** + * Token过期时间(毫秒时间戳) + */ + private Long tokenExpireTime; + + /** + * 最后登录时间 + */ + private LocalDateTime lastLoginTime; + + /** + * 最后登录IP + */ + private String lastLoginIp; +} + diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/dto/response/ErpUserRegisterResponseDTO.java b/mt-pay/src/main/java/com/mtkj/mtpay/dto/response/ErpUserRegisterResponseDTO.java new file mode 100644 index 0000000..ac9b94d --- /dev/null +++ b/mt-pay/src/main/java/com/mtkj/mtpay/dto/response/ErpUserRegisterResponseDTO.java @@ -0,0 +1,59 @@ +package com.mtkj.mtpay.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * ERP用户注册响应DTO + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpUserRegisterResponseDTO { + + /** + * 用户ID + */ + private Long id; + + /** + * 账号 + */ + private String username; + + /** + * 用户名称 + */ + private String nickName; + + /** + * 手机号 + */ + private String phone; + + /** + * 邮箱 + */ + private String email; + + /** + * 店铺号 + */ + private String storeCode; + + /** + * 状态 + */ + private String status; + + /** + * 创建时间 + */ + private LocalDateTime createTime; +} + diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/entity/ErpUser.java b/mt-pay/src/main/java/com/mtkj/mtpay/entity/ErpUser.java new file mode 100644 index 0000000..e33a033 --- /dev/null +++ b/mt-pay/src/main/java/com/mtkj/mtpay/entity/ErpUser.java @@ -0,0 +1,87 @@ +package com.mtkj.mtpay.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * ERP用户实体类 + */ +@TableName(value = "erp_user") +@Data +public class ErpUser { + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 账号(唯一) + */ + @TableField(value = "username", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR) + private String username; + + /** + * 密码(MD5加密) + */ + @TableField(value = "password", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR) + private String password; + + /** + * 用户名称 + */ + @TableField(value = "nick_name", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR) + private String nickName; + + /** + * 手机号 + */ + @TableField(value = "phone", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR) + private String phone; + + /** + * 邮箱 + */ + @TableField(value = "email", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR) + private String email; + + /** + * 店铺号 + */ + @TableField(value = "store_code", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR) + private String storeCode; + + /** + * 状态:ACTIVE-激活,DISABLED-禁用 + */ + @TableField(value = "status", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR) + private String status; + + /** + * 最后登录时间 + */ + @TableField(value = "last_login_time", jdbcType = org.apache.ibatis.type.JdbcType.TIMESTAMP) + private LocalDateTime lastLoginTime; + + /** + * 最后登录IP + */ + @TableField(value = "last_login_ip", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR) + private String lastLoginIp; + + /** + * 创建时间 + */ + @TableField(value = "create_time", fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; +} + diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/mapper/ErpUserMapper.java b/mt-pay/src/main/java/com/mtkj/mtpay/mapper/ErpUserMapper.java new file mode 100644 index 0000000..0fe615a --- /dev/null +++ b/mt-pay/src/main/java/com/mtkj/mtpay/mapper/ErpUserMapper.java @@ -0,0 +1,13 @@ +package com.mtkj.mtpay.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mtkj.mtpay.entity.ErpUser; +import org.apache.ibatis.annotations.Mapper; + +/** + * ERP用户Mapper接口 + */ +@Mapper +public interface ErpUserMapper extends BaseMapper { +} + diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/service/ErpUserService.java b/mt-pay/src/main/java/com/mtkj/mtpay/service/ErpUserService.java new file mode 100644 index 0000000..8077561 --- /dev/null +++ b/mt-pay/src/main/java/com/mtkj/mtpay/service/ErpUserService.java @@ -0,0 +1,42 @@ +package com.mtkj.mtpay.service; + +import com.mtkj.mtpay.dto.request.ErpUserLoginRequestDTO; +import com.mtkj.mtpay.dto.request.ErpUserRegisterRequestDTO; +import com.mtkj.mtpay.dto.response.ErpUserLoginResponseDTO; +import com.mtkj.mtpay.dto.response.ErpUserRegisterResponseDTO; + +/** + * ERP用户服务接口 + */ +public interface ErpUserService { + + /** + * 用户注册 + * @param request 注册请求 + * @return 注册响应 + */ + ErpUserRegisterResponseDTO register(ErpUserRegisterRequestDTO request); + + /** + * 用户登录 + * @param request 登录请求 + * @param loginIp 登录IP + * @return 登录响应 + */ + ErpUserLoginResponseDTO login(ErpUserLoginRequestDTO request, String loginIp); + + /** + * 根据用户名查询用户 + * @param username 用户名 + * @return 用户实体 + */ + com.mtkj.mtpay.entity.ErpUser getUserByUsername(String username); + + /** + * 验证Token + * @param token Token + * @return 用户实体,如果Token无效则返回null + */ + com.mtkj.mtpay.entity.ErpUser validateToken(String token); +} + diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/ErpUserServiceImpl.java b/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/ErpUserServiceImpl.java new file mode 100644 index 0000000..0d8ac70 --- /dev/null +++ b/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/ErpUserServiceImpl.java @@ -0,0 +1,190 @@ +package com.mtkj.mtpay.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.mtkj.mtpay.common.ResultCode; +import com.mtkj.mtpay.dto.request.ErpUserLoginRequestDTO; +import com.mtkj.mtpay.dto.request.ErpUserRegisterRequestDTO; +import com.mtkj.mtpay.dto.response.ErpUserLoginResponseDTO; +import com.mtkj.mtpay.dto.response.ErpUserRegisterResponseDTO; +import com.mtkj.mtpay.entity.ErpUser; +import com.mtkj.mtpay.exception.BusinessException; +import com.mtkj.mtpay.mapper.ErpUserMapper; +import com.mtkj.mtpay.service.ErpUserService; +import com.mtkj.mtpay.util.MD5; +import com.mtkj.mtpay.util.TokenUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.Map; + +/** + * ERP用户服务实现类 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ErpUserServiceImpl implements ErpUserService { + + private final ErpUserMapper erpUserMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public ErpUserRegisterResponseDTO register(ErpUserRegisterRequestDTO request) { + log.info("用户注册请求,账号: {}, 店铺号: {}", request.getUsername(), request.getStoreCode()); + + // 检查用户名是否已存在 + ErpUser existingUser = erpUserMapper.selectOne( + new LambdaQueryWrapper() + .eq(ErpUser::getUsername, request.getUsername()) + ); + + if (existingUser != null) { + throw new BusinessException(ResultCode.BUSINESS_ERROR.getCode(), "账号已存在"); + } + + // 检查手机号是否已存在(如果提供了手机号) + if (request.getPhone() != null && !request.getPhone().trim().isEmpty()) { + ErpUser existingPhone = erpUserMapper.selectOne( + new LambdaQueryWrapper() + .eq(ErpUser::getPhone, request.getPhone()) + ); + + if (existingPhone != null) { + throw new BusinessException(ResultCode.BUSINESS_ERROR.getCode(), "手机号已被注册"); + } + } + + // 检查邮箱是否已存在(如果提供了邮箱) + if (request.getEmail() != null && !request.getEmail().trim().isEmpty()) { + ErpUser existingEmail = erpUserMapper.selectOne( + new LambdaQueryWrapper() + .eq(ErpUser::getEmail, request.getEmail()) + ); + + if (existingEmail != null) { + throw new BusinessException(ResultCode.BUSINESS_ERROR.getCode(), "邮箱已被注册"); + } + } + + // 创建新用户 + ErpUser user = new ErpUser(); + user.setUsername(request.getUsername()); + // 密码使用MD5加密 + user.setPassword(MD5.md5(request.getPassword())); + user.setNickName(request.getNickName()); + user.setPhone(request.getPhone()); + user.setEmail(request.getEmail()); + user.setStoreCode(request.getStoreCode()); + user.setStatus("ACTIVE"); + + // 保存用户 + erpUserMapper.insert(user); + + log.info("用户注册成功,用户ID: {}, 账号: {}", user.getId(), user.getUsername()); + + // 构建响应 + ErpUserRegisterResponseDTO response = ErpUserRegisterResponseDTO.builder() + .id(user.getId()) + .username(user.getUsername()) + .nickName(user.getNickName()) + .phone(user.getPhone()) + .email(user.getEmail()) + .storeCode(user.getStoreCode()) + .status(user.getStatus()) + .createTime(user.getCreateTime()) + .build(); + + return response; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ErpUserLoginResponseDTO login(ErpUserLoginRequestDTO request, String loginIp) { + log.info("用户登录请求,账号: {}", request.getUsername()); + + // 查询用户 + ErpUser user = erpUserMapper.selectOne( + new LambdaQueryWrapper() + .eq(ErpUser::getUsername, request.getUsername()) + ); + + if (user == null) { + throw new BusinessException(ResultCode.UNAUTHORIZED.getCode(), "账号或密码错误"); + } + + // 检查用户状态 + if (!"ACTIVE".equals(user.getStatus())) { + throw new BusinessException(ResultCode.FORBIDDEN.getCode(), "账号已被禁用"); + } + + // 验证密码 + String encryptedPassword = MD5.md5(request.getPassword()); + if (!encryptedPassword.equals(user.getPassword())) { + throw new BusinessException(ResultCode.UNAUTHORIZED.getCode(), "账号或密码错误"); + } + + // 更新最后登录时间和IP + user.setLastLoginTime(LocalDateTime.now()); + user.setLastLoginIp(loginIp); + erpUserMapper.updateById(user); + + // 生成Token + String token = TokenUtils.generateToken(user.getId(), user.getUsername()); + Long tokenExpireTime = TokenUtils.getTokenExpireTime(token); + + log.info("用户登录成功,用户ID: {}, 账号: {}", user.getId(), user.getUsername()); + + // 构建响应 + ErpUserLoginResponseDTO response = ErpUserLoginResponseDTO.builder() + .id(user.getId()) + .username(user.getUsername()) + .nickName(user.getNickName()) + .phone(user.getPhone()) + .email(user.getEmail()) + .storeCode(user.getStoreCode()) + .status(user.getStatus()) + .token(token) + .tokenExpireTime(tokenExpireTime) + .lastLoginTime(user.getLastLoginTime()) + .lastLoginIp(user.getLastLoginIp()) + .build(); + + return response; + } + + @Override + public ErpUser getUserByUsername(String username) { + return erpUserMapper.selectOne( + new LambdaQueryWrapper() + .eq(ErpUser::getUsername, username) + ); + } + + @Override + public ErpUser validateToken(String token) { + if (token == null || token.trim().isEmpty()) { + return null; + } + + // 解析Token + Map userInfo = TokenUtils.parseToken(token); + if (userInfo == null) { + return null; + } + + // 查询用户 + Long userId = (Long) userInfo.get("userId"); + ErpUser user = erpUserMapper.selectById(userId); + + if (user == null || !"ACTIVE".equals(user.getStatus())) { + return null; + } + + return user; + } +} +