UserController.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. package modules.user;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.jdcloud.sdk.service.sms.model.BatchSendResponse;
  4. import com.jfinal.aop.Before;
  5. import com.jfinal.aop.Inject;
  6. import com.jfinal.kit.HashKit;
  7. import com.jfinal.kit.StrKit;
  8. import common.interceptor.LoginInterceptor;
  9. import common.interceptor.empty.EmptyInterface;
  10. import common.interceptor.role.RequiredRoleInterface;
  11. import common.model.User;
  12. import common.utils.http.MyController;
  13. import common.utils.http.MyRet;
  14. import common.utils.http.VerifyCode;
  15. import common.utils.hyg.AESUtils;
  16. import common.utils.jdcloud.SMS;
  17. import java.util.HashMap;
  18. import java.util.List;
  19. import java.util.Map;
  20. public class UserController extends MyController {
  21. // 验证码相关的常量
  22. private static final long SEND_VERIFY_CODE_DELAY = 60 * 1000; // 验证码发送间隔
  23. private static final long VERIFY_CODE_EXPIRATION_TIME = 5 * 60 * 1000; // 验证码有效期,5 分钟 (300 秒)
  24. // 角色相关常量
  25. public static final int ROLE_SUPER_ADMIN = 0; // 超级管理员
  26. public static final int ROLE_CHECK_ADMIN = 1; // 审核管理员
  27. public static final int ROLE_USER = 2; // 普通用户
  28. @Inject
  29. private UserService service;
  30. public void hello() {
  31. renderJson(MyRet.ok(service.hello()));
  32. }
  33. @EmptyInterface({"mobile_number"})
  34. public void sendVerifyCode() {
  35. // --- 核心修改部分 ---
  36. // 通过 MyController 获取解析后的 JSON 对象,拦截器也使用了这个方法
  37. JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
  38. // 从 JSON 对象中获取 mobile_number
  39. String mobileNumber = requestBodyJson.getString("mobile_number");
  40. // 2. 校验发送间隔
  41. // 从 Session 中获取上次发送验证码的时间戳
  42. Long lastSendTime = getSessionAttr("last_send_verify_code_time");
  43. if (lastSendTime != null) {
  44. long currentTime = System.currentTimeMillis();
  45. if (currentTime - lastSendTime < SEND_VERIFY_CODE_DELAY) {
  46. // 计算还需要等待多少秒
  47. long remainingSeconds = (SEND_VERIFY_CODE_DELAY - (currentTime - lastSendTime)) / 1000 + 1; // +1 确保显示完整秒数
  48. renderJson(MyRet.fail("验证码发送过于频繁,请 " + remainingSeconds + " 秒后再试"));
  49. return;
  50. }
  51. }
  52. // 3. 生成验证码
  53. String verifyCode = VerifyCode.randomVerifyCode();
  54. // 4. 将验证码和发送时间戳存入 Session
  55. setSessionAttr("verify_code", verifyCode); // 验证码本身
  56. setSessionAttr("last_send_verify_code_time", System.currentTimeMillis()); // 记录本次发送时间戳
  57. // 5. 实际发送验证码(这里是模拟发送)
  58. BatchSendResponse response = SMS.sendVerifyCodeByMobileNumber(mobileNumber, verifyCode);
  59. String jsonResponse = JSONObject.toJSONString(response);
  60. boolean isSentSuccessfully = response.getResult().getStatus();
  61. if (isSentSuccessfully) {
  62. renderJson(MyRet.ok("验证码已发送,请注意查收。").setData(jsonResponse));
  63. } else {
  64. // 短信服务商返回发送失败的情况处理
  65. renderJson(MyRet.fail("验证码发送失败,请稍后再试").setData(jsonResponse));
  66. }
  67. }
  68. @EmptyInterface({"mobile_number", "pwd_md5", "repeat_pwd_md5", "verify_code"})
  69. public void register() {
  70. // --- 核心修改部分:从 JSON 请求体中获取参数 ---
  71. JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
  72. // 因为 EmptyInterceptor 已经保证了这些字段不为空,这里可以直接获取
  73. String mobileNumber = requestBodyJson.getString("mobile_number");
  74. String pwd = requestBodyJson.getString("pwd_md5");
  75. String repeatPwd = requestBodyJson.getString("repeat_pwd_md5");
  76. String userVerifyCode = requestBodyJson.getString("verify_code");
  77. // 2. 密码重复校验
  78. if (!pwd.equals(repeatPwd)) {
  79. renderJson(MyRet.fail("两次输入密码不一致"));
  80. return;
  81. }
  82. // 3. 验证码校验
  83. MyRet verifyCodeRet = checkVerifyCode(userVerifyCode);
  84. if (!verifyCodeRet.isOk()) {
  85. renderJson(verifyCodeRet);
  86. return;
  87. }
  88. // 4. 构建 User 对象并处理业务逻辑
  89. // 因为 getPara("mobile_number") 已经获取了手机号,这里可以手动设置
  90. // 或者优化 getModel 的用法,让它只获取数据库字段,然后手动设置 mobileNumber。
  91. // 但为了保持原先 getModel 的语义,这里继续使用。
  92. User user = getModel(User.class, "", true);
  93. // 确保 mobile_number 填充正确
  94. if (StrKit.isBlank(user.getStr("mobile_number"))) {
  95. // 如果 getModel 没能获取到 mobile_number,则手动设置,确保数据完整性
  96. user.set("mobile_number", mobileNumber);
  97. }
  98. // 参数补充
  99. user.set("role", 2); // 例如:2表示普通用户
  100. // 密码加密并存储
  101. user.set("pwd_md5_md5", HashKit.md5(pwd));
  102. user.set("integral", 0);
  103. user.set("is_deleted", 0); // 0表示未禁用
  104. // 5. 调用服务层进行用户保存
  105. renderJson(service.saveUser(user));
  106. }
  107. @EmptyInterface({"mobile_number"})
  108. public void login() {
  109. // --- 从 JSON 请求体中获取参数 ---
  110. JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
  111. // 因为 EmptyInterceptor 已经保证了这些字段不为空,这里可以直接获取
  112. String mobileNumber = requestBodyJson.getString("mobile_number");
  113. String pwdMd5 = requestBodyJson.getString("pwd_md5");
  114. String verifyCode = requestBodyJson.getString("verify_code");
  115. // 1. 校验手机号是否被注册
  116. if (!service.isUserExists(mobileNumber)) {
  117. renderJson(MyRet.fail("该手机号未注册。"));
  118. return;
  119. }
  120. // 2.判断是使用密码还是验证码登录
  121. MyRet ret = MyRet.fail("参数缺失,需要pwd_md5或verify_code").setData(requestBodyJson);
  122. // 如果传入了密码优先使用密码登录
  123. if (StrKit.notBlank(pwdMd5)) {
  124. ret = service.login(mobileNumber, HashKit.md5(pwdMd5));
  125. }
  126. // 如果有验证码传入进行验证码校验
  127. if (StrKit.notBlank(verifyCode)) {
  128. MyRet verifyCodeRet = checkVerifyCode(verifyCode);
  129. if (!verifyCodeRet.isOk()) {
  130. renderJson(verifyCodeRet);
  131. return;
  132. }
  133. ret = service.verifyCodeLogin(mobileNumber);
  134. }
  135. if (ret.isOk()) {
  136. String token = createToken("dl-token");
  137. // 更新用户登录时间
  138. service.updateUserLoginTime(mobileNumber);
  139. // 更新用户token
  140. service.updateToken(mobileNumber, token);
  141. // 将token传回前端
  142. ret.set("token", token);
  143. // 其它参数的封装
  144. setSessionAttr("user_id", ((User)ret.get("data")).getStr("id"));
  145. setSessionAttr("mobile_number", ((User)ret.get("data")).getStr("mobile_number"));
  146. setSessionAttr("role", ((User)ret.get("data")).getStr("role"));
  147. }
  148. renderJson(ret);
  149. }
  150. // 慧用工免登请求地址,用来换取手机号
  151. public void tokenToMobileNumber() {
  152. String token = getPara("token");
  153. // 顶层 Map 用于封装整个 JSON 结构
  154. Map<String, Object> responseJson = new HashMap<>();
  155. // data Map 用于封装 workerMobile 等信息
  156. Map<String, String> data = new HashMap<>();
  157. if (StrKit.notBlank(token)) {
  158. String mobileNumber = service.findMobileNumberByToken(token);
  159. if (StrKit.notBlank(mobileNumber)) {
  160. // 成功获取手机号
  161. responseJson.put("statusCode", "000000");
  162. responseJson.put("statusText", "获取手机号成功");
  163. data.put("workerMobile", mobileNumber);
  164. data.put("workerName", ""); // 示例中为空,根据实际业务填充
  165. data.put("idCard", ""); // 示例中为空,根据实际业务填充
  166. responseJson.put("data", data);
  167. } else {
  168. responseJson.put("statusCode", "100001"); // 或 STATUS_CODE_TOKEN_EXPIRED
  169. responseJson.put("statusText", "token无效或已过期,未找到匹配的手机号");
  170. data.put("workerMobile", "");
  171. data.put("workerName", "");
  172. data.put("idCard", "");
  173. responseJson.put("data", data);
  174. }
  175. renderText(service.findMobileNumberByToken(token));
  176. } else {
  177. // token 为空
  178. responseJson.put("statusCode", "100001");
  179. responseJson.put("statusText", "token不能为空");
  180. data.put("workerMobile", "");
  181. data.put("workerName", "");
  182. data.put("idCard", "");
  183. responseJson.put("data", data);
  184. }
  185. renderJson(responseJson);
  186. }
  187. // 慧用工签约回调地址
  188. public void hygSign() {
  189. JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
  190. String businessBodyEncryptedHex = requestBodyJson.getString("businessBody");
  191. try {
  192. String body = AESUtils.decryptByHex(businessBodyEncryptedHex);
  193. JSONObject businessBody = JSONObject.parseObject(body);
  194. String workerId = businessBody.getString("workerId");
  195. String agreeState = businessBody.getString("agreeState");
  196. String workerMobile = businessBody.getString("workerMobile");
  197. User user = service.findUserByMobileNumber(workerMobile);
  198. // 签约成功,则赋予用户workerId
  199. if (agreeState.equals("2")) {
  200. user.setHygWorkerId(workerId);
  201. }
  202. // 无论成功与否,签约结果都放到数据库,方便维护
  203. user.setHygSignRst(businessBody.toJSONString());
  204. // 对user进行升级
  205. user.update();
  206. System.out.println(businessBody.toJSONString());
  207. Map<String, Object> responseJson = new HashMap<>();
  208. responseJson.put("statusCode", "000000");
  209. responseJson.put("statusText", "回调成功");
  210. renderJson(responseJson);
  211. } catch (Exception e) {
  212. Map<String, Object> responseJson = new HashMap<>();
  213. responseJson.put("statusCode", "100000");
  214. responseJson.put("statusText", "回调失败,解密出现错误 " + e.getMessage());
  215. System.out.println("回调失败,解密出现错误 " + e.getMessage());
  216. renderJson(responseJson);
  217. }
  218. }
  219. @Before(LoginInterceptor.class)
  220. public void getUserInfo() {
  221. User user = service.findUserByMobileNumber(this.<String>getSessionAttr("mobile_number"));
  222. if (user == null) {
  223. removeSessionAttr("dl-token");
  224. removeSessionAttr("role");
  225. removeSessionAttr("mobile_number");
  226. renderJson(MyRet.fail("登录信息失效,请重新登录。").setCode(MyRet.CODE_NO_LOGIN));
  227. } else {
  228. setSessionAttr("role", user.getRole().toString());
  229. renderJson(MyRet.ok("获取成功").setData(user));
  230. }
  231. }
  232. public void logout() {
  233. removeSessionAttr("user_id");
  234. removeSessionAttr("mobile_number");
  235. removeSessionAttr("dl-token");
  236. removeSessionAttr("role");
  237. renderJson(MyRet.ok("已成功登出。"));
  238. }
  239. /**
  240. * 更新用户的方法,传什么更新什么,不传就不更新,id必须传
  241. */
  242. @Before(LoginInterceptor.class)
  243. @RequiredRoleInterface({UserController.ROLE_SUPER_ADMIN})
  244. @EmptyInterface({"id"})
  245. public void updateByAdmin() {
  246. JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
  247. String id = requestBodyJson.getString("id");
  248. User user = service.findUserById(id);
  249. // 检查用户合法性
  250. if (user == null) {
  251. renderJson(MyRet.fail("用户获取不合法,该id对应的用户不存在,不要乱传参数。id: " + id));
  252. return;
  253. }
  254. // 校验昵称是否需要修改
  255. String nickname = requestBodyJson.getString("nickname");
  256. if (StrKit.notBlank(nickname)) {
  257. user.set("nickname", nickname);
  258. }
  259. // 校验手机号是否需要修改
  260. String mobileNumber = requestBodyJson.getString("mobile_number");
  261. if (StrKit.notBlank(mobileNumber)) {
  262. user.set("mobile_number", mobileNumber);
  263. }
  264. // 密码修改
  265. String pwdMd5 = requestBodyJson.getString("pwd_md5");
  266. if (StrKit.notBlank(pwdMd5)) {
  267. user.set("pwd_md5_md5", HashKit.md5(pwdMd5));
  268. }
  269. // 权限修改
  270. String roleStr = requestBodyJson.getString("role");
  271. if (StrKit.notBlank(roleStr)) {
  272. try {
  273. int roleInt = Integer.parseInt(roleStr); // 转换为 int 类型
  274. if (roleInt == ROLE_SUPER_ADMIN) {
  275. renderJson(MyRet.fail("为了安全考虑!不能在该接口设置超级管理员!请联系开发者处理!"));
  276. return;
  277. } else if (user.getInt("role") == 0) {
  278. renderJson(MyRet.fail("为了安全考虑!不能在该接口将超级管理员降级!请联系开发者处理!"));
  279. return;
  280. } else {
  281. user.set("role", roleInt);
  282. }
  283. } catch (Exception e) {
  284. // 处理转换失败的情况,例如记录日志,或者返回错误信息给前端
  285. renderJson(MyRet.fail("role 格式不正确" + e.getMessage()));
  286. return; // 中断后续操作
  287. }
  288. }
  289. // 推荐人修改
  290. String referrerIdStr = requestBodyJson.getString("referrer_id");
  291. if (StrKit.notBlank(referrerIdStr)) {
  292. try {
  293. long referrerIdLong = Long.parseLong(referrerIdStr); // 或者 int,根据数据库字段大小
  294. user.set("referrer_id", referrerIdLong);
  295. } catch (Exception e) {
  296. renderJson(MyRet.fail("referrer_id 格式不正确: " + e.getMessage()));
  297. return;
  298. }
  299. }
  300. // 积分修改
  301. String integralStr = requestBodyJson.getString("integral");
  302. if (StrKit.notBlank(integralStr)) {
  303. try {
  304. int integralInt = Integer.parseInt(integralStr);
  305. user.set("integral", integralInt);
  306. } catch (Exception e) {
  307. renderJson(MyRet.fail("integral 格式不正确: " + e.getMessage()));
  308. return;
  309. }
  310. }
  311. // 更新时间
  312. user.set("update_time", System.currentTimeMillis());
  313. // 执行更新业务
  314. if (user.update()) {
  315. renderJson(MyRet.ok("用户更新成功").setData(service.findUserByMobileNumber(user.getMobileNumber())));
  316. } else {
  317. renderJson(MyRet.fail("用户更新失败,请将此日志复制给开发者" + user));
  318. }
  319. }
  320. @Before(LoginInterceptor.class)
  321. @RequiredRoleInterface({UserController.ROLE_SUPER_ADMIN})
  322. public void count() {
  323. renderJson(MyRet.ok("查询成功").setData(service.count(null)));
  324. }
  325. @Before(LoginInterceptor.class)
  326. @RequiredRoleInterface({UserController.ROLE_SUPER_ADMIN})
  327. @EmptyInterface({"page_size", "page_number"})
  328. public void users() {
  329. JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
  330. // 页面大小
  331. String pageSizeStr = requestBodyJson.getString("page_size");
  332. int pageSizeInt;
  333. try {
  334. pageSizeInt = Integer.parseInt(pageSizeStr);
  335. if (pageSizeInt <= 0) {
  336. renderJson(MyRet.fail("页面大小(page_size)期待是正整数,你传的是: " + pageSizeStr));
  337. return;
  338. }
  339. } catch (Exception e) {
  340. renderJson(MyRet.fail("页面大小(page_size)格式不正确: " + e.getMessage()));
  341. return;
  342. }
  343. // 页码
  344. String pageNumberStr = requestBodyJson.getString("page_number");
  345. int pageNumberInt;
  346. try {
  347. pageNumberInt = Integer.parseInt(pageNumberStr);
  348. if (pageNumberInt <= 0) {
  349. renderJson(MyRet.fail("页码(page_number)期待是正整数,你传的是: " + pageNumberStr));
  350. return;
  351. }
  352. } catch (Exception e) {
  353. renderJson(MyRet.fail("页码(page_number)格式不正确: " + e.getMessage()));
  354. return;
  355. }
  356. // 查询条件
  357. String keywords = requestBodyJson.getString("keywords");
  358. // 最终结果封装
  359. List<User> users = service.users(pageNumberInt, pageSizeInt, keywords);
  360. Integer totalUsers = service.count(keywords);
  361. Map<String, Object> response = new HashMap<>();
  362. response.put("list", users);
  363. response.put("total_row", users.toArray().length);
  364. response.put("total_page", 1 + (users.toArray().length / pageSizeInt));
  365. response.put("page_size", pageSizeInt);
  366. response.put("page_number", pageNumberInt);
  367. response.put("total_user_count", totalUsers);
  368. renderJson(MyRet.ok("查询成功").setData(response));
  369. }
  370. /*
  371. *
  372. ### 验证码修改密码
  373. POST {{ baseUrl }}/user/updatePwd
  374. Content-Type: application/json
  375. {
  376. "mobile_number": "17781855864",
  377. "new_pwd_md5": "e10adc3949ba59abbe56e057f20f883e",
  378. "verify_code": "9119"
  379. }
  380. *
  381. * */
  382. // @EmptyInterface(keyArray = {"mobile_number", "new_pwd_md5", "verify_code"})
  383. // public void updatePwd() {
  384. // // --- 核心修改部分:从 JSON 请求体中获取参数 ---
  385. // JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
  386. //
  387. // // 因为 EmptyInterceptor 已经保证了这些字段不为空,这里可以直接获取
  388. // String mobileNumber = requestBodyJson.getString("mobile_number");
  389. // String pwdMd5 = requestBodyJson.getString("new_pwd_md5");
  390. // String verifyCode = requestBodyJson.getString("verify_code");
  391. // }
  392. private MyRet checkVerifyCode(String userVerifyCode) {
  393. // 3. 验证码校验
  394. String storedVerifyCode = getSessionAttr("verify_code");
  395. Long sendTime = getSessionAttr("last_send_verify_code_time");
  396. if (StrKit.isBlank(storedVerifyCode) || sendTime == null) {
  397. // Session中没有验证码或发送时间,可能从未发送过,或Session已失效/过期
  398. return MyRet.fail("请先获取验证码或验证码已失效");
  399. }
  400. // 校验有效期
  401. long currentTime = System.currentTimeMillis();
  402. if (currentTime - sendTime > VERIFY_CODE_EXPIRATION_TIME) {
  403. // 验证码已过期
  404. // 可以在这里清除Session中的验证码,防止不必要的存储
  405. removeSessionAttr("verify_code");
  406. removeSessionAttr("last_send_verify_code_time");
  407. return MyRet.fail("验证码已过期,请重新获取");
  408. }
  409. // 校验用户输入的验证码是否与Session中存储的一致
  410. if (!userVerifyCode.equals(storedVerifyCode)) {
  411. renderJson(MyRet.fail("验证码不正确"));
  412. return MyRet.fail("验证码已过期,请重新获取");
  413. }
  414. removeSessionAttr("verify_code");
  415. return MyRet.ok("验证码校验通过");
  416. }
  417. }