diff --git a/snailmall-user-service/src/main/java/com/njupt/swg/controller/UserController.java b/snailmall-user-service/src/main/java/com/njupt/swg/controller/UserController.java index 2079642..f0f7a31 100644 --- a/snailmall-user-service/src/main/java/com/njupt/swg/controller/UserController.java +++ b/snailmall-user-service/src/main/java/com/njupt/swg/controller/UserController.java @@ -29,215 +29,189 @@ import javax.servlet.http.HttpSession; */ //TODO 先全部开放GET请求 +// 使用@RequestMapping注解,将该类下所有的请求处理方法的请求路径都映射到以"/user"开头的路径下, +// 这样便于对用户相关的接口进行统一的路径管理,使得接口的结构更加清晰,符合RESTful风格的接口设计规范。 @RequestMapping("user") +// @RestController注解是一个组合注解,相当于同时使用了@Controller和@ResponseBody注解。 +// 表示这个类是一个Spring MVC的控制器类,并且类中所有方法的返回值都会直接作为响应体(ResponseBody)返回给客户端, +// 通常返回的数据格式为JSON等,方便与前端进行数据交互,处理客户端发送的关于用户相关的各种请求。 @RestController +// 使用lombok的@Slf4j注解,用于自动生成日志记录相关的代码,方便在类中的各个方法执行过程中记录日志信息, +// 比如记录用户操作的相关情况、接口调用的状态等,有助于后续进行问题排查、系统监控以及数据分析等工作。 @Slf4j -// 表示标识这个类是swagger的资源 +// @Api注解用于标识这个类是Swagger的资源,在Swagger生成的接口文档中对这个控制器类进行描述。 +// 通过设置value属性指定类在文档中的显示名称,tags属性设置相关的标签,便于对用户服务相关的接口在文档中进行分类展示, +// 让接口的使用者(如前端开发人员、测试人员等)能够直观地了解接口所属的功能模块以及大致用途等信息。 @Api(value = "UserController", tags = {"用户服务接口"}) public class UserController { + // 通过Spring的依赖注入机制,使用@Autowired注解自动注入IUserService类型的实例。 + // IUserService应该是一个定义了与用户业务逻辑相关操作的接口,其具体的实现类由Spring容器根据配置进行实例化并注入到此处。 + // 通过这个接口可以调用诸如用户登录、注册、信息查询、修改等各种业务方法,以此实现具体的用户相关功能处理。 @Autowired private IUserService userService; + // 同样使用@Autowired注解注入CommonCacheUtil类型的实例,CommonCacheUtil类通常是用于缓存操作的工具类, + // 在这里主要负责与Redis等缓存系统进行交互,例如在用户登录后将用户信息缓存到Redis中,或者从Redis中获取缓存的用户信息等操作, + // 方便对用户相关的数据进行缓存管理,从而提高系统的性能,减少对数据库的频繁访问,提升数据获取的效率。 @Autowired private CommonCacheUtil commonCacheUtil; /** - * 用户登陆:验证参数、登陆、写到cookie中并且写到redis中 - * 用户登陆以后,点击其他需要登陆才能看的页面时,先判断是否前端是否有这个key,没有则提示需要登陆 + * 此方法用于处理用户登录的请求,实现了包括验证登录参数、执行用户登录逻辑、将登录相关信息写到Cookie中以及缓存到Redis中等一系列操作, + * 以此完成用户登录的功能,并最终返回登录后的相关结果给客户端。 + * 当用户登录成功后,在访问其他需要登录权限才能查看的页面时,前端可以先判断是否存在代表登录状态的特定key(存储在Cookie中), + * 如果不存在则提示用户需要登录,以此来控制页面的访问权限,确保系统的安全性和数据的保密性。 + * + * @param session HttpSession对象,它代表了用户与服务器之间的一次会话,服务器可以通过它来存储用户在本次会话期间的相关信息, + * 例如在这里可以获取会话的唯一标识(sessionId),并将其作为用户登录状态的标识,存储到Cookie和Redis中,方便后续的验证和操作。 + * @param response HttpServletResponse对象,用于向客户端发送响应,通过这个对象可以将一些信息(如登录成功后生成的Cookie)发送给客户端浏览器, + * 让浏览器进行保存,以便在后续的请求中携带这些信息,用于服务器端验证用户的登录状态等操作。 + * @param username 用户名,作为登录请求的必传参数,由客户端传入,不能为空,它与密码一起用于在登录验证时核对用户的身份, + * 是判断用户是否合法登录系统的关键输入信息之一。 + * @param password 用户密码,同样是登录请求的必传参数,不能为空,它和用户名共同构成了验证用户身份的要素,确保只有合法的用户能够登录系统。 + * @return 返回一个ServerResponse类型的响应对象,其中ServerResponse是一个自定义的通用响应类, + * 它包含了登录操作的结果状态(如成功或失败,通过isSuccess方法判断)以及登录成功后用户相关的详细信息(通过UserResVO对象进行封装), + * 方便客户端根据返回的结果判断登录是否成功,并获取相应的用户信息进行后续的展示、处理等操作,例如在前端页面展示用户的基本信息等。 */ - @ApiOperation(value="用户登陆", notes="输入用户名,密码,不能为空") + @ApiOperation(value = "用户登陆", notes = "输入用户名,密码,不能为空") @ApiImplicitParams({ @ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String"), @ApiImplicitParam(name = "password", value = "用户密码", required = true, dataType = "String") }) @RequestMapping("/login.do") - public ServerResponse login(HttpSession session, HttpServletResponse response, String username, String password){ - log.info("【用户{}开始登陆】",username); - ServerResponse userVOServerResponse = userService.login(username,password); - if(userVOServerResponse.isSuccess()){ - //登陆成功,那么需要在redis中存储,并且将代表用户的sessionId写到前端浏览器的cookie中 - log.info("【用户{}cookie开始写入】",username); - CookieUtil.writeLoginToken(response,session.getId()); - //写到redis中,将用户信息序列化,设置过期时间为30分钟 - log.info("【用户{}redis开始写入】",username); + public ServerResponse login(HttpSession session, HttpServletResponse response, String username, String password) { + // 使用日志记录用户开始登录的信息,通过占位符{}的方式将用户名动态地记录到日志中,方便后续查看具体是哪个用户在什么时间发起了登录请求, + // 有助于排查登录相关的问题,例如某个用户频繁登录失败等情况时,可以通过日志分析原因。 + log.info("【用户{}开始登陆】", username); + // 调用userService的login方法,传入用户名和密码参数,由userService的具体实现类来执行用户登录验证等相关的业务逻辑处理, + // 例如可能会在数据库中查询用户信息,验证用户名和密码是否匹配等操作,最终返回一个ServerResponse类型的对象, + // 包含了登录操作的结果以及登录成功后对应的用户相关信息(如果登录成功)。 + ServerResponse userVOServerResponse = userService.login(username, password); + if (userVOServerResponse.isSuccess()) { + // 如果登录成功,那么需要将用户的登录状态信息存储到Redis中,并且把代表用户的sessionId写到前端浏览器的Cookie中, + // 这样后续用户访问其他页面时,服务器可以通过验证Cookie中的sessionId以及Redis中的缓存信息来确认用户的登录状态。 + log.info("【用户{}cookie开始写入】", username); + // 调用CookieUtil工具类的writeLoginToken方法,将sessionId写入到响应的Cookie中,传递给客户端浏览器保存, + // 使得浏览器在后续的请求中会自动带上这个Cookie,方便服务器识别用户身份。 + CookieUtil.writeLoginToken(response, session.getId()); + // 调用commonCacheUtil的cacheNxExpire方法,先将用户信息通过JsonUtil的obj2String方法序列化为JSON字符串格式, + // 然后将其存储到Redis中,并设置过期时间为30分钟(通过Constants.RedisCacheExtime.REDIS_SESSION_EXTIME来指定), + // 这样可以在一定时间内缓存用户信息,避免频繁查询数据库获取相同的用户信息,提高系统性能,同时在过期后自动清除缓存数据,保证数据的时效性。 + log.info("【用户{}redis开始写入】", username); commonCacheUtil.cacheNxExpire(session.getId(), JsonUtil.obj2String(userVOServerResponse.getData()), Constants.RedisCacheExtime.REDIS_SESSION_EXTIME); } - log.info("【用户{}登陆成功】",username); + // 使用日志记录用户登录成功的信息,同样通过占位符记录用户名,方便后续统计登录情况、排查登录相关问题等, + // 例如可以统计每日登录成功的用户数量等信息,通过日志进行辅助分析。 + log.info("【用户{}登陆成功】", username); return userVOServerResponse; } /** - * 用户注册,要判断用户名和邮箱是否重复,这里用了分布式锁来防止用户名和邮箱可能出现重复 + * 该方法用于处理用户注册的请求,在用户注册的过程中,需要判断用户名和邮箱是否已经在系统中被其他用户使用,为了防止在高并发场景下出现用户名或邮箱重复注册的问题, + * 这里使用了分布式锁机制来保证用户名和邮箱的唯一性,确保注册信息的合法性以及系统数据的一致性,最后返回注册操作的结果给客户端。 + * + * @param user 一个User类型的对象,作为注册请求的参数,由客户端传入,它包含了用户注册时填写的详细信息,如用户名、邮箱、密码等, + * 这些信息需要符合系统定义的用户实体结构要求,是进行用户注册操作的关键输入数据,将用于后续在数据库中插入新用户记录等操作。 + * @return 返回一个ServerResponse类型的响应对象,该对象包含了用户注册操作的结果状态(如成功或失败)以及可能的相关提示信息等, + * 方便客户端根据返回的结果判断注册是否成功,并知晓注册失败的原因等情况,进而采取相应的后续操作,例如提示用户重新填写注册信息等。 */ - @ApiOperation(value="创建用户", notes="根据User对象创建用户") + @ApiOperation(value = "创建用户", notes = "根据User对象创建用户") @ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User") @RequestMapping("/register.do") - public ServerResponse register(User user){ + public ServerResponse register(User user) { log.info("【开始注册】"); - //这里模拟高并发的注册场景,防止用户名字注册重复,所以需要加上分布式锁 + // 在这里模拟高并发的注册场景,由于在多用户同时进行注册操作时,可能会出现并发访问导致对数据库中用户名或邮箱唯一性验证出现问题,进而造成重复注册的情况, + // 所以需要使用分布式锁来保证关键操作(如验证用户名和邮箱的唯一性以及向数据库中插入新用户记录等操作)的原子性,避免并发冲突。 + // 调用userService的register方法,由其具体实现类来执行用户注册相关的业务逻辑,例如验证用户输入信息的合法性、检查用户名和邮箱是否重复、将新用户信息插入数据库等操作, + // 最终返回一个ServerResponse类型的对象,包含注册结果以及相关的提示信息等内容。 ServerResponse response = userService.register(user); log.info("【用户注册成功】"); return response; } /** - * 判断用户名和邮箱是否重复 + * 此方法用于处理验证用户名和邮箱是否重复的请求,通过传入的参数来判断指定的用户名或邮箱在系统中是否已经被其他用户使用, + * 然后返回相应的验证结果给客户端,以此辅助用户在进行注册、修改信息等操作时确保输入的用户名和邮箱的唯一性。 + * + * @param str 一个字符串类型的输入参数,由客户端传入,它用于传递要验证的用户名或邮箱等具体信息, + * 根据另一个参数type的取值来确定其具体代表的是用户名还是邮箱,是验证操作的关键输入数据之一。 + * @param type 同样是一个字符串类型的参数,由客户端传入,用于指定str参数所代表的类型,其取值应该是预定义的表示用户名或邮箱等类型的常量(例如在Constants类中定义的相关常量), + * 通过这个参数明确要验证的是用户名还是邮箱是否重复,从而决定验证操作的具体逻辑走向。 + * @return 返回一个ServerResponse类型的响应对象,该对象包含了验证操作的结果状态(如重复或不重复)以及相关的提示信息等, + * 方便客户端根据返回结果判断输入的用户名或邮箱是否可用,进而采取相应的后续操作,例如提示用户重新输入等。 */ - @ApiOperation(value="验证用户名和邮箱是否重复", notes="用户名和邮箱都不能用已经存在的") + @ApiOperation(value = "验证用户名和邮箱是否重复", notes = "用户名和邮箱都不能用已经存在的") @ApiImplicitParams({ @ApiImplicitParam(name = "str", value = "输入参数", required = true, dataType = "String"), @ApiImplicitParam(name = "type", value = "参数类型", required = true, dataType = "String") }) @RequestMapping("/check_valid.do") public ServerResponse checkValid(@RequestParam("str") String str, - @RequestParam("type") String type){ + @RequestParam("type") String type) { log.info("【开始验证用户名和邮箱是否重复】"); - ServerResponse response = userService.checkValid(str,type); + ServerResponse response = userService.checkValid(str, type); return response; } /** - * 获取登陆状态用户信息 - * 本地测试的时候,由于cookie是写到oursnai.cn域名下面的,所以需要在hosts文件中添加127.0.0.1 oursnail.cn这个解析 - * 在浏览器中测试的时候,将login方法暂时开放为GET请求,然后请求路径为:http://oursnail.cn:8081/user/login.do?username=admin&password=123456 - * 同样地,在测试获取登陆用户信息接口,也要按照域名来请求,否则拿不到token:http://oursnail.cn:8081/user/get_user_info.do + * 这个方法用于处理获取登录状态用户信息的请求,在本地进行测试时,由于Cookie是写到"oursnai.cn"域名下面的, + * 所以需要在本地的hosts文件中添加"127.0.0.1 oursnail.cn"这个解析记录,使得本地测试环境能够模拟真实的域名访问情况,从而正确地获取到Cookie信息。 + * 在浏览器中进行测试时,需要将login方法暂时开放为GET请求(原本可能是POST等其他请求方式,这里只是为了方便测试进行的临时调整), + * 然后按照指定的请求路径(如http://oursnail.cn:8081/user/login.do?username=admin&password=123456)进行登录操作,获取到登录状态的Cookie信息后, + * 再通过同样的域名(http://oursnail.cn:8081/user/get_user_info.do)来请求这个获取用户信息的接口,否则可能因为域名不匹配等原因无法获取到代表登录状态的token,进而不能获取用户信息。 + * 最终会根据用户的登录状态返回相应的用户个人信息给客户端,如果用户未登录则返回相应的提示信息告知客户端无法获取信息。 + * + * @param request HttpServletRequest对象,用于接收客户端发送的请求,通过它可以获取请求中的各种信息, + * 在这里主要是用于获取客户端发送过来的包含在请求中的Cookie信息,以便从中读取代表登录状态的token,进而获取登录用户的相关信息。 + * @return 返回一个ServerResponse类型的响应对象,该对象包含了获取用户信息操作的结果状态(如成功获取到信息或用户未登录等情况)以及如果成功获取到的登录用户的个人信息(通过相关的用户信息对象表示), + * 方便客户端根据返回结果判断是否成功获取到用户信息,并进行相应的展示等后续操作,例如在前端页面展示用户的基本信息等。 */ - @ApiOperation(value="获取用户个人信息", notes="登陆状态下获取") + @ApiOperation(value = "获取用户个人信息", notes = "登陆状态下获取") @RequestMapping("/get_user_info.do") - public ServerResponse getUserInfo(HttpServletRequest request){ + public ServerResponse getUserInfo(HttpServletRequest request) { + // 调用CookieUtil工具类的readLoginToken方法,从请求对象中读取代表登录状态的token(该token存储在Cookie中), + // 如果能够读取到则说明用户可能处于登录状态,若读取不到则表示用户未登录,后续需要进行相应的处理。 String loginToken = CookieUtil.readLoginToken(request); - if(StringUtils.isEmpty(loginToken)){ + if (StringUtils.isEmpty(loginToken)) { log.info("【用户未登录,无法获取当前用户信息】"); return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息"); } + // 调用commonCacheUtil的getCacheValue方法,根据读取到的登录token从缓存(如Redis)中获取对应的用户信息字符串, + // 如果获取到的用户信息字符串为null,则可能是缓存过期或者用户未登录等情况,同样需要进行相应的处理。 String userStr = commonCacheUtil.getCacheValue(loginToken); - if(userStr == null){ + if (userStr == null) { log.info("【用户未登录,无法获取当前用户信息】"); return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息"); } - User currentUser = JsonUtil.Str2Obj(userStr,User.class); + // 调用JsonUtil的Str2Obj方法,将从缓存中获取到的用户信息字符串反序列化为User类型的对象,以便后续进行业务逻辑处理, + // 例如可以调用userService的相关方法进一步获取数据库中的详细用户信息等操作。 + User currentUser = JsonUtil.Str2Obj(userStr, User.class); UserResVO userResVO = userService.getUserInfoFromDB(currentUser.getId()); - return ServerResponse.createBySuccess("登陆用户获取自身信息成功",userResVO); + return ServerResponse.createBySuccess("登陆用户获取自身信息成功", userResVO); } /** - * 根据用户名去拿到对应的问题 + * 此方法用于处理根据用户名去获取对应的问题的请求,通常在用户忘记密码时,首先需要根据用户名去获取之前设置的用于找回密码的相关问题, + * 然后将获取到的问题信息返回给客户端,以便用户进行后续的密码找回操作。 + * + * @param username 用户名,作为请求的必传参数,由客户端传入,不能为空,用于在系统中查找对应的用户设置的找回密码相关问题, + * 是获取问题操作的关键输入信息,通过这个用户名来定位具体用户的相关设置信息。 + * @return 返回一个ServerResponse类型的响应对象,该对象包含了获取问题操作的结果状态(如成功获取到问题或未找到对应的用户等情况)以及如果成功获取到的相关问题信息, + * 方便客户端根据返回结果判断是否成功获取到问题,并进行相应的展示等后续操作,例如在前端页面展示问题让用户回答等。 */ - @ApiOperation(value="根据用户名去拿到对应的问题", notes="忘记密码时首先根据用户名去获取设置的问题") + @ApiOperation(value = "根据用户名去拿到对应的问题", notes = "忘记密码时首先根据用户名去获取设置的问题") @ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String") @RequestMapping("/forget_get_question.do") - public ServerResponse forgetGetQuestion(String username){ - log.info("【用户{}忘记密码,点击忘记密码输入用户名】",username); + public ServerResponse forgetGetQuestion(String username) { + log.info("【用户{}忘记密码,点击忘记密码输入用户名】", username); ServerResponse response = userService.getQuestionByUsername(username); return response; } - /** - * 校验答案是否正确 - */ - @ApiOperation(value="校验答案是否正确", notes="忘记密码时输入正确的用户名之后就可以获取到问题,此时就可以输入答案") - @ApiImplicitParams({ - @ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String"), - @ApiImplicitParam(name = "question", value = "设置的问题", required = true, dataType = "String"), - @ApiImplicitParam(name = "answer", value = "提交的答案", required = true, dataType = "String") - }) - @RequestMapping("/forget_check_answer.do") - public ServerResponse forgetCheckAnswer(String username,String question,String answer){ - log.info("【用户{}忘记密码,提交问题答案】",username); - ServerResponse response = userService.checkAnswer(username,question,answer); - return response; - } - - - /** - * 忘记密码的重置密码 - */ - @ApiOperation(value="忘记密码的重置密码", notes="输入新的密码,要进行token的校验") - @ApiImplicitParams({ - @ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String"), - @ApiImplicitParam(name = "passwordNew", value = "新密码", required = true, dataType = "String"), - @ApiImplicitParam(name = "forgetToken", value = "前端保存的token", required = true, dataType = "String") - }) - @RequestMapping("/forget_reset_password.do") - public ServerResponse forgetResetPasswd(String username,String passwordNew,String forgetToken){ - log.info("【用户{}忘记密码,输入新密码】",username); - ServerResponse response = userService.forgetResetPasswd(username,passwordNew,forgetToken); - return response; - } - - /** - * 登陆状态的重置密码 - */ - @ApiOperation(value="登陆状态的重置密码", notes="登陆的时候只需要输入老的密码和新密码即可") - @ApiImplicitParams({ - @ApiImplicitParam(name = "passwordOld", value = "老密码", required = true, dataType = "String"), - @ApiImplicitParam(name = "passwordNew", value = "新密码", required = true, dataType = "String") - }) - @RequestMapping("/reset_password.do") - public ServerResponse resetPasswd(String passwordOld,String passwordNew,HttpServletRequest request){ - //1.读取cookie - String loginToken = CookieUtil.readLoginToken(request); - if(StringUtils.isEmpty(loginToken)){ - return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息"); - } - //2.从redis中获取用户信息 - String userStr = commonCacheUtil.getCacheValue(loginToken); - if(userStr == null){ - return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息"); - } - User currentUser = JsonUtil.Str2Obj(userStr,User.class); - log.info("【用户{}重置密码】",currentUser); - - ServerResponse response = userService.resetPasswd(passwordOld,passwordNew,currentUser.getId()); - return response; - } - - /** - * 更新当前登陆用户信息 - */ - @ApiOperation(value="更新当前登陆用户信息", notes="更新用户信息") - @ApiImplicitParams({ - @ApiImplicitParam(name = "email", value = "邮箱", required = true, dataType = "String"), - @ApiImplicitParam(name = "phone", value = "电话", required = true, dataType = "String"), - @ApiImplicitParam(name = "question", value = "问题", required = true, dataType = "String"), - @ApiImplicitParam(name = "answer", value = "答案", required = true, dataType = "String") - }) - @RequestMapping("/update_information.do") - public ServerResponse updateInformation(String email,String phone,String question,String answer,HttpServletRequest request){ - //1.读取cookie - String loginToken = CookieUtil.readLoginToken(request); - if(StringUtils.isEmpty(loginToken)){ - return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息"); - } - //2.从redis中获取用户信息 - String userStr = commonCacheUtil.getCacheValue(loginToken); - if(userStr == null){ - return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息"); - } - User currentUser = JsonUtil.Str2Obj(userStr,User.class); - - ServerResponse response = userService.updateInfomation(email,phone,question,answer,currentUser.getId()); - return response; - } - - /** - * 登出,删除cookie和redis即可 - */ - @ApiOperation(value="登出", notes="退出登陆,删除cookie和redis缓存") - @RequestMapping("/logout.do") - public ServerResponse logout(HttpServletRequest request,HttpServletResponse response){ - log.info("【用户删除cookie】"); - //1.删除cookie - String loginToken = CookieUtil.readLoginToken(request); - CookieUtil.delLoginToken(request,response); - log.info("【用户删除redis缓存】"); - //2.删除redis中缓存记录 - commonCacheUtil.delKey(loginToken); - return ServerResponse.createBySuccess(); - } - - - - -} +/** + * 这个方法用于处理校验答案是否正确的请求,在用户忘记密码的流程中,当输入正确的用户名之后可以获取到对应的问题, + * 此时用户输入答案后,通过该方法来校验用户提交的答案是否与系统中之前设置的答案一致,然后返回相应的校验结果给客户端, + * 以此来决定是否允许用户进行后续的密码重置操作。 + * + * @ \ No newline at end of file