package modules.user; import com.alibaba.fastjson.JSONObject; import com.jdcloud.sdk.service.sms.model.BatchSendResponse; import com.jfinal.aop.Before; import com.jfinal.aop.Inject; import com.jfinal.kit.HashKit; import com.jfinal.kit.StrKit; import common.interceptor.LoginInterceptor; import common.interceptor.empty.EmptyInterface; import common.interceptor.role.RequiredRoleInterface; import common.model.User; import common.utils.http.MyController; import common.utils.http.MyRet; import common.utils.http.VerifyCode; import common.utils.hyg.AESUtils; import common.utils.jdcloud.SMS; import java.util.HashMap; import java.util.List; import java.util.Map; public class UserController extends MyController { // 验证码相关的常量 private static final long SEND_VERIFY_CODE_DELAY = 60 * 1000; // 验证码发送间隔 private static final long VERIFY_CODE_EXPIRATION_TIME = 5 * 60 * 1000; // 验证码有效期,5 分钟 (300 秒) // 角色相关常量 public static final int ROLE_SUPER_ADMIN = 0; // 超级管理员 public static final int ROLE_CHECK_ADMIN = 1; // 审核管理员 public static final int ROLE_USER = 2; // 普通用户 @Inject private UserService service; public void hello() { renderJson(MyRet.ok(service.hello())); } @EmptyInterface({"mobile_number"}) public void sendVerifyCode() { // --- 核心修改部分 --- // 通过 MyController 获取解析后的 JSON 对象,拦截器也使用了这个方法 JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class); // 从 JSON 对象中获取 mobile_number String mobileNumber = requestBodyJson.getString("mobile_number"); // 2. 校验发送间隔 // 从 Session 中获取上次发送验证码的时间戳 Long lastSendTime = getSessionAttr("last_send_verify_code_time"); if (lastSendTime != null) { long currentTime = System.currentTimeMillis(); if (currentTime - lastSendTime < SEND_VERIFY_CODE_DELAY) { // 计算还需要等待多少秒 long remainingSeconds = (SEND_VERIFY_CODE_DELAY - (currentTime - lastSendTime)) / 1000 + 1; // +1 确保显示完整秒数 renderJson(MyRet.fail("验证码发送过于频繁,请 " + remainingSeconds + " 秒后再试")); return; } } // 3. 生成验证码 String verifyCode = VerifyCode.randomVerifyCode(); // 4. 将验证码和发送时间戳存入 Session setSessionAttr("verify_code", verifyCode); // 验证码本身 setSessionAttr("last_send_verify_code_time", System.currentTimeMillis()); // 记录本次发送时间戳 // 5. 实际发送验证码(这里是模拟发送) BatchSendResponse response = SMS.sendVerifyCodeByMobileNumber(mobileNumber, verifyCode); String jsonResponse = JSONObject.toJSONString(response); boolean isSentSuccessfully = response.getResult().getStatus(); if (isSentSuccessfully) { renderJson(MyRet.ok("验证码已发送,请注意查收。").setData(jsonResponse)); } else { // 短信服务商返回发送失败的情况处理 renderJson(MyRet.fail("验证码发送失败,请稍后再试").setData(jsonResponse)); } } @EmptyInterface({"mobile_number", "pwd_md5", "repeat_pwd_md5", "verify_code"}) public void register() { // --- 核心修改部分:从 JSON 请求体中获取参数 --- JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class); // 因为 EmptyInterceptor 已经保证了这些字段不为空,这里可以直接获取 String mobileNumber = requestBodyJson.getString("mobile_number"); String pwd = requestBodyJson.getString("pwd_md5"); String repeatPwd = requestBodyJson.getString("repeat_pwd_md5"); String userVerifyCode = requestBodyJson.getString("verify_code"); // 2. 密码重复校验 if (!pwd.equals(repeatPwd)) { renderJson(MyRet.fail("两次输入密码不一致")); return; } // 3. 验证码校验 MyRet verifyCodeRet = checkVerifyCode(userVerifyCode); if (!verifyCodeRet.isOk()) { renderJson(verifyCodeRet); return; } // 4. 构建 User 对象并处理业务逻辑 // 因为 getPara("mobile_number") 已经获取了手机号,这里可以手动设置 // 或者优化 getModel 的用法,让它只获取数据库字段,然后手动设置 mobileNumber。 // 但为了保持原先 getModel 的语义,这里继续使用。 User user = getModel(User.class, "", true); // 确保 mobile_number 填充正确 if (StrKit.isBlank(user.getStr("mobile_number"))) { // 如果 getModel 没能获取到 mobile_number,则手动设置,确保数据完整性 user.set("mobile_number", mobileNumber); } // 参数补充 user.set("role", 2); // 例如:2表示普通用户 // 密码加密并存储 user.set("pwd_md5_md5", HashKit.md5(pwd)); user.set("integral", 0); user.set("is_deleted", 0); // 0表示未禁用 // 5. 调用服务层进行用户保存 renderJson(service.saveUser(user)); } @EmptyInterface({"mobile_number"}) public void login() { // --- 从 JSON 请求体中获取参数 --- JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class); // 因为 EmptyInterceptor 已经保证了这些字段不为空,这里可以直接获取 String mobileNumber = requestBodyJson.getString("mobile_number"); String pwdMd5 = requestBodyJson.getString("pwd_md5"); String verifyCode = requestBodyJson.getString("verify_code"); // 1. 校验手机号是否被注册 if (!service.isUserExists(mobileNumber)) { renderJson(MyRet.fail("该手机号未注册。")); return; } // 2.判断是使用密码还是验证码登录 MyRet ret = MyRet.fail("参数缺失,需要pwd_md5或verify_code").setData(requestBodyJson); // 如果传入了密码优先使用密码登录 if (StrKit.notBlank(pwdMd5)) { ret = service.login(mobileNumber, HashKit.md5(pwdMd5)); } // 如果有验证码传入进行验证码校验 if (StrKit.notBlank(verifyCode)) { MyRet verifyCodeRet = checkVerifyCode(verifyCode); if (!verifyCodeRet.isOk()) { renderJson(verifyCodeRet); return; } ret = service.verifyCodeLogin(mobileNumber); } if (ret.isOk()) { String token = createToken("dl-token"); // 更新用户登录时间 service.updateUserLoginTime(mobileNumber); // 更新用户token service.updateToken(mobileNumber, token); // 将token传回前端 ret.set("token", token); // 其它参数的封装 setSessionAttr("user_id", ((User)ret.get("data")).getStr("id")); setSessionAttr("mobile_number", ((User)ret.get("data")).getStr("mobile_number")); setSessionAttr("role", ((User)ret.get("data")).getStr("role")); } renderJson(ret); } // 慧用工免登请求地址,用来换取手机号 public void tokenToMobileNumber() { String token = getPara("token"); // 顶层 Map 用于封装整个 JSON 结构 Map responseJson = new HashMap<>(); // data Map 用于封装 workerMobile 等信息 Map data = new HashMap<>(); if (StrKit.notBlank(token)) { String mobileNumber = service.findMobileNumberByToken(token); if (StrKit.notBlank(mobileNumber)) { // 成功获取手机号 responseJson.put("statusCode", "000000"); responseJson.put("statusText", "获取手机号成功"); data.put("workerMobile", mobileNumber); data.put("workerName", ""); // 示例中为空,根据实际业务填充 data.put("idCard", ""); // 示例中为空,根据实际业务填充 responseJson.put("data", data); } else { responseJson.put("statusCode", "100001"); // 或 STATUS_CODE_TOKEN_EXPIRED responseJson.put("statusText", "token无效或已过期,未找到匹配的手机号"); data.put("workerMobile", ""); data.put("workerName", ""); data.put("idCard", ""); responseJson.put("data", data); } renderText(service.findMobileNumberByToken(token)); } else { // token 为空 responseJson.put("statusCode", "100001"); responseJson.put("statusText", "token不能为空"); data.put("workerMobile", ""); data.put("workerName", ""); data.put("idCard", ""); responseJson.put("data", data); } renderJson(responseJson); } // 慧用工签约回调地址 public void hygSign() { JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class); String businessBodyEncryptedHex = requestBodyJson.getString("businessBody"); try { String body = AESUtils.decryptByHex(businessBodyEncryptedHex); JSONObject businessBody = JSONObject.parseObject(body); String workerId = businessBody.getString("workerId"); String agreeState = businessBody.getString("agreeState"); String workerMobile = businessBody.getString("workerMobile"); User user = service.findUserByMobileNumber(workerMobile); // 签约成功,则赋予用户workerId if (agreeState.equals("2")) { user.setHygWorkerId(workerId); } // 无论成功与否,签约结果都放到数据库,方便维护 user.setHygSignRst(businessBody.toJSONString()); // 对user进行升级 user.update(); System.out.println(businessBody.toJSONString()); Map responseJson = new HashMap<>(); responseJson.put("statusCode", "000000"); responseJson.put("statusText", "回调成功"); renderJson(responseJson); } catch (Exception e) { Map responseJson = new HashMap<>(); responseJson.put("statusCode", "100000"); responseJson.put("statusText", "回调失败,解密出现错误 " + e.getMessage()); System.out.println("回调失败,解密出现错误 " + e.getMessage()); renderJson(responseJson); } } @Before(LoginInterceptor.class) public void getUserInfo() { User user = service.findUserByMobileNumber(this.getSessionAttr("mobile_number")); if (user == null) { removeSessionAttr("dl-token"); removeSessionAttr("role"); removeSessionAttr("mobile_number"); renderJson(MyRet.fail("登录信息失效,请重新登录。").setCode(MyRet.CODE_NO_LOGIN)); } else { setSessionAttr("role", user.getRole().toString()); renderJson(MyRet.ok("获取成功").setData(user)); } } public void logout() { removeSessionAttr("user_id"); removeSessionAttr("mobile_number"); removeSessionAttr("dl-token"); removeSessionAttr("role"); renderJson(MyRet.ok("已成功登出。")); } /** * 更新用户的方法,传什么更新什么,不传就不更新,id必须传 */ @Before(LoginInterceptor.class) @RequiredRoleInterface({UserController.ROLE_SUPER_ADMIN}) @EmptyInterface({"id"}) public void updateByAdmin() { JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class); String id = requestBodyJson.getString("id"); User user = service.findUserById(id); // 检查用户合法性 if (user == null) { renderJson(MyRet.fail("用户获取不合法,该id对应的用户不存在,不要乱传参数。id: " + id)); return; } // 校验昵称是否需要修改 String nickname = requestBodyJson.getString("nickname"); if (StrKit.notBlank(nickname)) { user.set("nickname", nickname); } // 校验手机号是否需要修改 String mobileNumber = requestBodyJson.getString("mobile_number"); if (StrKit.notBlank(mobileNumber)) { user.set("mobile_number", mobileNumber); } // 密码修改 String pwdMd5 = requestBodyJson.getString("pwd_md5"); if (StrKit.notBlank(pwdMd5)) { user.set("pwd_md5_md5", HashKit.md5(pwdMd5)); } // 权限修改 String roleStr = requestBodyJson.getString("role"); if (StrKit.notBlank(roleStr)) { try { int roleInt = Integer.parseInt(roleStr); // 转换为 int 类型 if (roleInt == ROLE_SUPER_ADMIN) { renderJson(MyRet.fail("为了安全考虑!不能在该接口设置超级管理员!请联系开发者处理!")); return; } else if (user.getInt("role") == 0) { renderJson(MyRet.fail("为了安全考虑!不能在该接口将超级管理员降级!请联系开发者处理!")); return; } else { user.set("role", roleInt); } } catch (Exception e) { // 处理转换失败的情况,例如记录日志,或者返回错误信息给前端 renderJson(MyRet.fail("role 格式不正确" + e.getMessage())); return; // 中断后续操作 } } // 推荐人修改 String referrerIdStr = requestBodyJson.getString("referrer_id"); if (StrKit.notBlank(referrerIdStr)) { try { long referrerIdLong = Long.parseLong(referrerIdStr); // 或者 int,根据数据库字段大小 user.set("referrer_id", referrerIdLong); } catch (Exception e) { renderJson(MyRet.fail("referrer_id 格式不正确: " + e.getMessage())); return; } } // 积分修改 String integralStr = requestBodyJson.getString("integral"); if (StrKit.notBlank(integralStr)) { try { int integralInt = Integer.parseInt(integralStr); user.set("integral", integralInt); } catch (Exception e) { renderJson(MyRet.fail("integral 格式不正确: " + e.getMessage())); return; } } // 更新时间 user.set("update_time", System.currentTimeMillis()); // 执行更新业务 if (user.update()) { renderJson(MyRet.ok("用户更新成功").setData(service.findUserByMobileNumber(user.getMobileNumber()))); } else { renderJson(MyRet.fail("用户更新失败,请将此日志复制给开发者" + user)); } } @Before(LoginInterceptor.class) @RequiredRoleInterface({UserController.ROLE_SUPER_ADMIN}) public void count() { renderJson(MyRet.ok("查询成功").setData(service.count(null))); } @Before(LoginInterceptor.class) @RequiredRoleInterface({UserController.ROLE_SUPER_ADMIN}) @EmptyInterface({"page_size", "page_number"}) public void users() { JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class); // 页面大小 String pageSizeStr = requestBodyJson.getString("page_size"); int pageSizeInt; try { pageSizeInt = Integer.parseInt(pageSizeStr); if (pageSizeInt <= 0) { renderJson(MyRet.fail("页面大小(page_size)期待是正整数,你传的是: " + pageSizeStr)); return; } } catch (Exception e) { renderJson(MyRet.fail("页面大小(page_size)格式不正确: " + e.getMessage())); return; } // 页码 String pageNumberStr = requestBodyJson.getString("page_number"); int pageNumberInt; try { pageNumberInt = Integer.parseInt(pageNumberStr); if (pageNumberInt <= 0) { renderJson(MyRet.fail("页码(page_number)期待是正整数,你传的是: " + pageNumberStr)); return; } } catch (Exception e) { renderJson(MyRet.fail("页码(page_number)格式不正确: " + e.getMessage())); return; } // 查询条件 String keywords = requestBodyJson.getString("keywords"); // 最终结果封装 List users = service.users(pageNumberInt, pageSizeInt, keywords); Integer totalUsers = service.count(keywords); Map response = new HashMap<>(); response.put("list", users); response.put("total_row", users.toArray().length); response.put("total_page", 1 + (users.toArray().length / pageSizeInt)); response.put("page_size", pageSizeInt); response.put("page_number", pageNumberInt); response.put("total_user_count", totalUsers); renderJson(MyRet.ok("查询成功").setData(response)); } /* * ### 验证码修改密码 POST {{ baseUrl }}/user/updatePwd Content-Type: application/json { "mobile_number": "17781855864", "new_pwd_md5": "e10adc3949ba59abbe56e057f20f883e", "verify_code": "9119" } * * */ // @EmptyInterface(keyArray = {"mobile_number", "new_pwd_md5", "verify_code"}) // public void updatePwd() { // // --- 核心修改部分:从 JSON 请求体中获取参数 --- // JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class); // // // 因为 EmptyInterceptor 已经保证了这些字段不为空,这里可以直接获取 // String mobileNumber = requestBodyJson.getString("mobile_number"); // String pwdMd5 = requestBodyJson.getString("new_pwd_md5"); // String verifyCode = requestBodyJson.getString("verify_code"); // } private MyRet checkVerifyCode(String userVerifyCode) { // 3. 验证码校验 String storedVerifyCode = getSessionAttr("verify_code"); Long sendTime = getSessionAttr("last_send_verify_code_time"); if (StrKit.isBlank(storedVerifyCode) || sendTime == null) { // Session中没有验证码或发送时间,可能从未发送过,或Session已失效/过期 return MyRet.fail("请先获取验证码或验证码已失效"); } // 校验有效期 long currentTime = System.currentTimeMillis(); if (currentTime - sendTime > VERIFY_CODE_EXPIRATION_TIME) { // 验证码已过期 // 可以在这里清除Session中的验证码,防止不必要的存储 removeSessionAttr("verify_code"); removeSessionAttr("last_send_verify_code_time"); return MyRet.fail("验证码已过期,请重新获取"); } // 校验用户输入的验证码是否与Session中存储的一致 if (!userVerifyCode.equals(storedVerifyCode)) { renderJson(MyRet.fail("验证码不正确")); return MyRet.fail("验证码已过期,请重新获取"); } removeSessionAttr("verify_code"); return MyRet.ok("验证码校验通过"); } }