From 25f79e485d1f6dba1bb8b9e7b9dedb08e5ef0612 Mon Sep 17 00:00:00 2001 From: Dcx12138 <2320898596@qq.com> Date: Sun, 15 Dec 2024 16:04:38 +0800 Subject: [PATCH] =?UTF-8?q?readme=20=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SmileToCandy --- .../java/com/tamguo/config/WebConfig.java | 125 ++++++++++++------ .../com/tamguo/config/shiro/MemberRealm.java | 77 +++++++---- .../config/shiro/ShiroConfiguration.java | 49 ++++++- .../java/com/tamguo/dao/ChapterMapper.java | 22 ++- .../main/java/com/tamguo/dao/PaperMapper.java | 63 ++++++--- .../tamguo/interceptor/MemberInterceptor.java | 45 ++++--- .../main/java/com/tamguo/model/AdEntity.java | 32 ++++- .../java/com/tamguo/model/BookEntity.java | 78 +++++++++-- .../com/tamguo/service/impl/AdService.java | 35 ++++- .../com/tamguo/service/impl/AreaService.java | 71 ++++++++-- 10 files changed, 444 insertions(+), 153 deletions(-) diff --git a/tamguo/src/main/java/com/tamguo/config/WebConfig.java b/tamguo/src/main/java/com/tamguo/config/WebConfig.java index 23d0c61..217161e 100644 --- a/tamguo/src/main/java/com/tamguo/config/WebConfig.java +++ b/tamguo/src/main/java/com/tamguo/config/WebConfig.java @@ -19,51 +19,93 @@ import com.google.code.kaptcha.util.Config; import com.tamguo.interceptor.MemberInterceptor; import com.tamguo.interceptor.MenuInterceptor; - +// 这个类使用了Spring的 @Configuration 注解,表示它是一个配置类,用于配置Spring Web相关的一些功能和组件, +// Spring在启动时会自动扫描并加载该类中的配置信息,将其中定义的Bean注册到Spring容器中。 @Configuration public class WebConfig extends WebMvcConfigurerAdapter { - - @Value("${file.storage.path}") - private String fileStoragePath; - @Autowired - private MenuInterceptor menuInterceptor; - @Autowired - private MemberInterceptor memberInterceptor; - /** - * 拦截器 - */ - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(menuInterceptor).addPathPatterns("/**"); - registry.addInterceptor(memberInterceptor).addPathPatterns("/member/**"); - } - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/files/**").addResourceLocations("file:"+fileStoragePath); - super.addResourceHandlers(registry); - } - - @Bean(name="producer") - public DefaultKaptcha getKaptchaBean(){ - DefaultKaptcha defaultKaptcha=new DefaultKaptcha(); - Properties properties=new Properties(); - // properties.setProperty("kaptcha.border.color", "105,179,90"); + // 通过 @Value 注解从配置文件(例如 application.properties 或 application.yml)中读取名为 "file.storage.path" 的属性值, + // 并注入到这个变量中,该路径可能用于指定文件存储的位置,后续在资源处理器配置中会用到这个路径来映射资源访问路径。 + @Value("${file.storage.path}") + private String fileStoragePath; + + // 通过Spring的依赖注入机制自动注入 MenuInterceptor 实例,MenuInterceptor 应该是自定义的拦截器, + // 用于在请求处理过程中对菜单相关的逻辑进行拦截和处理,比如权限校验、菜单数据准备等操作(具体功能取决于拦截器的实现代码)。 + @Autowired + private MenuInterceptor menuInterceptor; + + // 同样通过依赖注入机制注入 MemberInterceptor 实例,MemberInterceptor 也是自定义拦截器, + // 从命名可以推测它可能用于对会员相关的请求路径进行拦截,执行如会员权限验证、会员数据处理等相关操作。 + @Autowired + private MemberInterceptor memberInterceptor; + + /** + * 拦截器 + * 重写了父类 WebMvcConfigurerAdapter 的 addInterceptors 方法,用于注册自定义的拦截器到Spring的拦截器注册表中, + * 这样在处理Web请求时,相应的拦截器就会按照配置的路径模式对请求进行拦截并执行拦截器内部的逻辑。 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 将 menuInterceptor 注册到拦截器注册表中,并配置其拦截的路径模式为 "/**",表示对所有的请求路径都会进行拦截, + // 具体在 MenuInterceptor 内部会根据业务逻辑判断是否需要进行处理以及如何处理这些请求。 + registry.addInterceptor(menuInterceptor).addPathPatterns("/**"); + // 将 memberInterceptor 注册到拦截器注册表中,配置其拦截的路径模式为 "/member/**",意味着对以 "/member/" 开头的请求路径进行拦截, + // 这通常用于对会员模块相关的请求进行特定的前置处理,比如验证会员登录状态、权限等情况。 + registry.addInterceptor(memberInterceptor).addPathPatterns("/member/**"); + } + + /** + * 资源处理器配置 + * 重写了父类的 addResourceHandlers 方法,用于配置Spring Web中资源文件的处理方式, + * 这里主要是定义了对 "/files/**" 这样的请求路径对应的资源文件的查找位置,将其映射到本地文件系统中指定的 fileStoragePath 路径下, + * 这样当客户端发起对 "/files/" 开头路径的资源请求时,Spring就能从对应的本地文件存储位置去查找并返回相应的文件资源。 + */ + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/files/**").addResourceLocations("file:" + fileStoragePath); + super.addResourceHandlers(registry); + } + + /** + * Kaptcha验证码配置 + * 定义了一个名为 "producer" 的Bean,用于创建和配置 DefaultKaptcha 实例,DefaultKaptcha 是用于生成验证码的组件, + * 在这里通过设置 Properties 对象中的各种属性来定制验证码的生成规则,例如验证码图片的宽度、高度、字体、字符长度等相关参数, + * 最终返回配置好的 DefaultKaptcha 实例,以便在项目的其他地方(比如登录页面等需要验证码的地方)可以使用它来生成验证码图片及对应的验证文本。 + */ + @Bean(name = "producer") + public DefaultKaptcha getKaptchaBean() { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); +// properties.setProperty("kaptcha.border.color", "105,179,90"); + // 设置验证码图片是否显示边框,这里配置为 "no",即不显示边框。 properties.setProperty("kaptcha.border", "no"); - properties.setProperty("kaptcha.image.width", "125"); - properties.setProperty("kaptcha.image.height", "45"); - properties.setProperty("kaptcha.session.key", "code"); - properties.setProperty("kaptcha.textproducer.char.length", "4"); - properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑"); - Config config=new Config(properties); - defaultKaptcha.setConfig(config); - return defaultKaptcha; - } - - @Bean + // 设置验证码图片的宽度为 125 像素。 + properties.setProperty("kaptcha.image.width", "125"); + // 设置验证码图片的高度为 45 像素。 + properties.setProperty("kaptcha.image.height", "45"); + // 设置在Session中存储验证码文本的键名,这里设置为 "code",方便后续在验证时从Session中获取对应的验证码文本进行比对验证。 + properties.setProperty("kaptcha.session.key", "code"); + // 设置验证码文本的字符长度为 4 个字符,即生成的验证码文本将包含 4 个字符。 + properties.setProperty("kaptcha.textproducer.char.length", "4"); + // 设置验证码文本生成时使用的字体,可以指定多个字体,用逗号隔开,这里指定了宋体、楷体、微软雅黑等常见字体, + // 在生成验证码时会随机从这些字体中选择一种来绘制验证码文本。 + properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + + /** + * 嵌入式Servlet容器定制 + * 定义了一个名为 "containerCustomizer" 的Bean,返回一个实现了 EmbeddedServletContainerCustomizer 接口的匿名内部类实例, + * 用于对嵌入式Servlet容器(比如在Spring Boot项目中默认使用的Tomcat容器等)进行定制化配置, + * 这里主要配置了错误页面的映射,当发生 HttpStatus.NOT_FOUND(404 状态码,表示资源未找到)错误时,将重定向到 "/404.html" 页面, + * 当发生 HttpStatus.INTERNAL_SERVER_ERROR(500 状态码,表示服务器内部错误)错误时,将重定向到 "/500.html" 页面, + * 通过这样的配置可以为用户提供更友好的错误提示页面,提升用户体验。 + */ + @Bean public EmbeddedServletContainerCustomizer containerCustomizer() { - return new EmbeddedServletContainerCustomizer(){ + return new EmbeddedServletContainerCustomizer() { @Override public void customize(ConfigurableEmbeddedServletContainer container) { container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404.html")); @@ -71,5 +113,4 @@ public class WebConfig extends WebMvcConfigurerAdapter { } }; } - -} +} \ No newline at end of file diff --git a/tamguo/src/main/java/com/tamguo/config/shiro/MemberRealm.java b/tamguo/src/main/java/com/tamguo/config/shiro/MemberRealm.java index e697481..42a7b65 100644 --- a/tamguo/src/main/java/com/tamguo/config/shiro/MemberRealm.java +++ b/tamguo/src/main/java/com/tamguo/config/shiro/MemberRealm.java @@ -21,53 +21,80 @@ import com.tamguo.service.IMemberService; /** * 认证 - * + * 这个类 `MemberRealm` 继承自 `AuthorizingRealm`,是 Apache Shiro 框架中用于进行认证(登录验证)和授权(权限验证)相关操作的核心类, + * 它负责与业务逻辑层交互,获取用户信息并进行相应的验证判断等操作。 */ public class MemberRealm extends AuthorizingRealm { - + + // 通过Spring的依赖注入机制,自动注入 `IMemberService` 接口的实现类实例, + // 用于后续在认证和授权过程中与数据库等数据源交互,获取用户相关信息(如用户实体、登录失败次数等)。 @Autowired private IMemberService iMemberService; - - /** - * 授权(验证权限时调用) - */ + + /** + * 授权(验证权限时调用) + * 这个方法重写了父类 `AuthorizingRealm` 的 `doGetAuthorizationInfo` 方法,用于获取用户的授权信息,即权限相关的数据。 + * 在 Shiro 框架验证用户权限时会调用此方法,根据用户的身份信息(通常通过 `PrincipalCollection` 传递)来确定该用户拥有哪些权限。 + * 当前实现比较简单,只是初始化了一个 `SimpleAuthorizationInfo` 对象,并且设置了权限集合(这里暂时设置为空集合,实际应用中需根据业务逻辑填充具体权限)。 + */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { - Set permsSet = null; + // 初始化一个用于存储权限字符串的集合,当前示例中暂未填充具体的权限数据,实际业务中需要从数据库等数据源获取用户对应的权限并添加到这个集合中。 + Set permsSet = null; SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); + // 将权限集合设置到 `SimpleAuthorizationInfo` 对象中,用于后续权限验证时判断用户是否具有特定权限。 info.setStringPermissions(permsSet); - + return info; } /** * 认证(登录时调用) + * 重写了父类 `AuthorizingRealm` 的 `doGetAuthenticationInfo` 方法,用于进行用户登录认证操作。 + * 当用户尝试登录系统时,Shiro 框架会调用这个方法来验证用户提供的登录信息(用户名和密码)是否正确, + * 该方法会与业务逻辑层交互,获取用户实体信息,并根据一系列规则判断登录是否合法,如用户名是否存在、密码是否正确、账号是否被锁定等。 */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { + // 从 `AuthenticationToken` 中获取用户输入的用户名,在 Shiro 中,`AuthenticationToken` 通常是在登录时传递用户名和密码等登录凭证的对象, + // 这里将获取到的用户名强制转换为 `String` 类型,因为假设登录时用户名是以字符串形式传递的(实际应用中需根据具体的 `AuthenticationToken` 实现来确定类型转换是否正确)。 String username = (String) token.getPrincipal(); - String password = new String((char[]) token.getCredentials()); - - MemberEntity member = iMemberService.findByUsername(username); - if(member == null) { - throw new UnknownAccountException("用户名或密码有误,请重新输入或找回密码"); - } - Integer loginFailureCount = iMemberService.getLoginFailureCount(member); - if(loginFailureCount > 10) { - throw new LockedAccountException("账号被锁定"); - } - - if(!new Sha256Hash(password).toHex().equals(member.getPassword())){ + // 从 `AuthenticationToken` 中获取用户输入的密码,同样需要进行类型转换,将其转换为 `String` 类型,这里将 `char[]` 类型的密码转换为 `String`, + // 注意这种方式在实际应用中可能存在安全风险(比如密码在内存中会以明文形式存在一段时间),更安全的做法是使用合适的加密算法立即对密码进行处理,避免明文暴露。 + String password = new String((char[]) token.getCredentials()); + + // 通过注入的 `IMemberService` 调用 `findByUsername` 方法,根据用户名从数据库或其他数据源查找对应的 `MemberEntity`(用户实体对象), + // 如果返回 `null`,说明用户名不存在,此时抛出 `UnknownAccountException`,表示用户名或密码有误(在实际应用中可以更细化错误提示,区分是用户名不存在还是密码错误等情况)。 + MemberEntity member = iMemberService.findByUsername(username); + if (member == null) { + throw new UnknownAccountException("用户名或密码有误,请重新输入或找回密码"); + } + + // 通过 `IMemberService` 调用 `getLoginFailureCount` 方法获取当前用户的登录失败次数, + // 如果登录失败次数大于 10 次,说明账号可能存在异常风险,抛出 `LockedAccountException`,表示账号被锁定,禁止登录。 + Integer loginFailureCount = iMemberService.getLoginFailureCount(member); + if (loginFailureCount > 10) { + throw new LockedAccountException("账号被锁定"); + } + + // 使用 `Sha256Hash` 对用户输入的密码进行哈希处理(将密码转换为不可逆的哈希值),并与数据库中存储的用户密码(假设数据库中存储的也是经过相同哈希算法处理后的哈希值)进行比较, + // 如果不相等,说明密码错误,此时增加登录失败次数,并通过 `IMemberService` 的 `updateLoginFailureCount` 方法更新数据库中的登录失败次数记录, + // 然后抛出 `IncorrectCredentialsException`,提示用户名或密码有误,请重新输入或找回密码。 + if (!new Sha256Hash(password).toHex().equals(member.getPassword())) { loginFailureCount++; - iMemberService.updateLoginFailureCount(member , loginFailureCount); + iMemberService.updateLoginFailureCount(member, loginFailureCount); throw new IncorrectCredentialsException("用户名或密码有误,请重新输入或找回密码"); } + + // 如果密码正确,调用 `IMemberService` 的 `updateLastLoginTime` 方法更新用户的最后登录时间,记录此次登录操作, + // 然后创建一个 `SimpleAuthenticationInfo` 对象,将用户实体对象(`member`)、密码(`password`)以及当前 `Realm` 的名称(通过 `getName()` 获取)传递进去, + // 这个对象会被 Shiro 框架用于后续的认证相关流程,比如在会话中保存认证信息等操作。 // 更新登录时间 iMemberService.updateLastLoginTime(member.getUid().toString()); - - SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(member, password, getName()); - return info; + + SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(member, password, getName()); + return info; } -} +} \ No newline at end of file diff --git a/tamguo/src/main/java/com/tamguo/config/shiro/ShiroConfiguration.java b/tamguo/src/main/java/com/tamguo/config/shiro/ShiroConfiguration.java index 430d260..6b44a16 100644 --- a/tamguo/src/main/java/com/tamguo/config/shiro/ShiroConfiguration.java +++ b/tamguo/src/main/java/com/tamguo/config/shiro/ShiroConfiguration.java @@ -14,15 +14,26 @@ import org.springframework.context.annotation.Configuration; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; +// 这个类使用了Spring的 @Configuration 注解,表明它是一个配置类,用于配置Shiro框架相关的各种组件和功能, +// Spring在启动时会自动扫描并加载这个类中的配置信息,将其中定义的Bean注册到Spring容器中。 @Configuration public class ShiroConfiguration { + // 创建一个LinkedHashMap用于定义Shiro的过滤链规则,键是URL路径的匹配模式,值是对应的权限过滤器名称, + // 后续会将这个映射关系配置到ShiroFilterFactoryBean中,用于控制不同URL的访问权限。 private static Map filterChainDefinitionMap = new LinkedHashMap(); + // 定义一个名为 "shiroRealm" 的Bean,这个Bean返回的是自定义的MemberRealm实例, + // MemberRealm是继承自Shiro的AuthorizingRealm,用于实现具体的认证(登录验证)和授权(权限验证)逻辑, + // 将其注册为Spring容器中的Bean,方便其他Shiro相关组件引用,比如在SecurityManager中设置使用这个Realm进行验证操作。 @Bean(name = "shiroRealm") public MemberRealm getShiroRealm() { return new MemberRealm(); } + // 定义一个名为 "shiroEhcacheManager" 的Bean,用于创建和配置EhCacheManager实例, + // EhCacheManager在Shiro框架中用于缓存相关的数据,比如用户的授权信息等,提高系统性能,减少重复查询授权等操作的开销。 + // 通过设置cacheManagerConfigFile属性指定了EhCache的配置文件路径(这里是类路径下的ehcache-shiro.xml文件), + // 该配置文件会定义缓存的具体策略、缓存区域等相关设置。 @Bean(name = "shiroEhcacheManager") public EhCacheManager getEhCacheManager() { EhCacheManager em = new EhCacheManager(); @@ -30,11 +41,17 @@ public class ShiroConfiguration { return em; } + // 定义一个名为 "lifecycleBeanPostProcessor" 的Bean,创建LifecycleBeanPostProcessor实例, + // LifecycleBeanPostProcessor是Shiro提供的一个用于管理Shiro相关Bean生命周期的后置处理器, + // 它能够确保Shiro的一些组件(如Realm等)在Spring容器中正确地初始化、销毁等,按照其生命周期规范执行相应操作。 @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } + // 定义一个名为 "defaultAdvisorAutoProxyCreator" 的Bean,创建DefaultAdvisorAutoProxyCreator实例, + // 它是Spring AOP中的一个自动代理创建器,用于在Spring容器中自动创建基于Advisor(切面顾问)的代理对象, + // 在Shiro与Spring集成时,帮助实现基于AOP的权限控制拦截等功能,通过设置proxyTargetClass为true,表示使用基于类的代理方式(而不是基于接口的代理)。 @Bean public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator(); @@ -42,6 +59,10 @@ public class ShiroConfiguration { return daap; } + // 定义一个名为 "securityManager" 的Bean,创建DefaultWebSecurityManager实例, + // DefaultWebSecurityManager是Shiro在Web应用中用于管理安全相关操作的核心组件, + // 在这里设置了它所使用的Realm(通过调用getShiroRealm()方法获取前面定义的MemberRealm实例)以及缓存管理器(通过getEhCacheManager()获取EhCacheManager实例), + // 这样SecurityManager在进行认证和授权操作时就会使用指定的Realm进行验证,并利用缓存管理器来缓存相关信息,提高效率。 @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager() { DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager(); @@ -50,22 +71,36 @@ public class ShiroConfiguration { return dwsm; } + // 定义一个名为 "authorizationAttributeSourceAdvisor" 的Bean,创建AuthorizationAttributeSourceAdvisor实例, + // AuthorizationAttributeSourceAdvisor是Shiro与Spring AOP集成的一个关键组件,用于在方法级别进行权限控制, + // 它通过设置securityManager(调用getDefaultWebSecurityManager()获取配置好的SecurityManager)来关联整个权限管理体系, + // 使得在Spring应用中可以基于注解等方式方便地进行权限控制,比如在方法上添加Shiro的权限注解来限制哪些用户可以访问该方法。 @Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor(); aasa.setSecurityManager(getDefaultWebSecurityManager()); return new AuthorizationAttributeSourceAdvisor(); } - - /** - * ShiroDialect,为了在thymeleaf里使用shiro的标签的bean - * @return - */ - @Bean - public ShiroDialect shiroDialect(){ + + /** + * ShiroDialect,为了在thymeleaf里使用shiro的标签的bean + * 这个方法定义了一个名为 "shiroDialect" 的Bean,返回ShiroDialect实例, + * ShiroDialect是用于在Thymeleaf模板引擎中使用Shiro标签的一个方言类, + * 当项目中使用Thymeleaf作为页面模板并且需要在页面中进行Shiro相关的权限判断、显示等操作时(比如根据用户权限显示或隐藏页面元素), + * 通过将这个Bean注册到Spring容器中,Thymeleaf就能识别并解析Shiro相关的标签,实现相应的功能。 + */ + @Bean + public ShiroDialect shiroDialect() { return new ShiroDialect(); } + // 定义一个名为 "shiroFilter" 的Bean,创建ShiroFilterFactoryBean实例, + // ShiroFilterFactoryBean是Shiro在Web应用中用于配置URL级别的访问控制过滤器链的核心组件, + // 它通过设置securityManager(关联前面配置好的DefaultWebSecurityManager)来建立整个权限过滤的基础框架, + // 同时设置了登录页面的URL(通过setLoginUrl方法设置为 "/login",当用户未登录访问受保护资源时会重定向到这个登录页面)、 + // 登录成功后的默认跳转页面URL(通过setSuccessUrl方法设置为 "/index"), + // 并且将前面定义的filterChainDefinitionMap(包含了URL路径和对应权限过滤器的映射关系)设置进去, + // 这样Shiro就能根据这些配置对不同的URL请求进行权限验证和相应的处理,比如哪些URL需要认证才能访问,哪些URL可以匿名访问等。 @Bean(name = "shiroFilter") public ShiroFilterFactoryBean getShiroFilterFactoryBean() { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); diff --git a/tamguo/src/main/java/com/tamguo/dao/ChapterMapper.java b/tamguo/src/main/java/com/tamguo/dao/ChapterMapper.java index 1274471..ea83164 100644 --- a/tamguo/src/main/java/com/tamguo/dao/ChapterMapper.java +++ b/tamguo/src/main/java/com/tamguo/dao/ChapterMapper.java @@ -6,12 +6,22 @@ import org.apache.ibatis.annotations.Param; import com.tamguo.config.dao.SuperMapper; import com.tamguo.model.ChapterEntity; -public interface ChapterMapper extends SuperMapper{ +// 定义了一个名为ChapterMapper的接口,该接口继承自SuperMapper, +// 通常继承自自定义的通用Mapper接口(这里的SuperMapper大概率是用于简化MyBatis常见CRUD操作的基础Mapper接口), +// 这样可以复用一些通用的数据库操作方法(比如增删改查等基本功能),同时也可以定义自己特定的查询方法,用于与数据库中的章节(Chapter)相关表进行交互。 +public interface ChapterMapper extends SuperMapper { - List findByBookId(@Param(value="bookId") String bookId); - - List findByParentId(@Param(value="parentId") String parentId); + // 定义了一个查询方法findByBookId,用于根据书籍的唯一标识符(bookId)从数据库中查找对应的章节信息列表。 + // 使用了@Param注解来为方法参数指定一个名称,这样在对应的SQL语句中就可以通过这个指定的名称来引用参数, + // 在这里参数名为"bookId",表示传入的是书籍的ID,方法返回值是一个ChapterEntity类型的列表,即查询到的对应章节实体的集合。 + List findByBookId(@Param(value = "bookId") String bookId); - ChapterEntity findNextPoint(@Param(value="uid")String uid , @Param(value="orders")Integer orders); + // 定义另一个查询方法findByParentId,目的是根据章节的父级章节的唯一标识符(parentId)来查找对应的子章节信息列表。 + // 同样使用了@Param注解为参数命名为"parentId",方便在SQL语句中引用,返回值也是ChapterEntity类型的列表,包含了满足条件的子章节实体集合。 + List findByParentId(@Param(value = "parentId") String parentId); -} + // 定义了一个查找下一个知识点(这里推测ChapterEntity可能代表知识点相关实体,具体取决于业务逻辑)的方法findNextPoint。 + // 该方法接收两个参数,一个是知识点的唯一标识符(uid),另一个是顺序号(orders),通过这两个参数从数据库中查找符合条件的下一个知识点实体。 + // 使用@Param注解分别为两个参数命名为"uid"和"orders",方便在对应的SQL语句中准确引用参数,返回值是一个ChapterEntity类型的对象,代表找到的下一个知识点实体。 + ChapterEntity findNextPoint(@Param(value = "uid") String uid, @Param(value = "orders") Integer orders); +} \ No newline at end of file diff --git a/tamguo/src/main/java/com/tamguo/dao/PaperMapper.java b/tamguo/src/main/java/com/tamguo/dao/PaperMapper.java index 2191eaf..7aee791 100644 --- a/tamguo/src/main/java/com/tamguo/dao/PaperMapper.java +++ b/tamguo/src/main/java/com/tamguo/dao/PaperMapper.java @@ -7,27 +7,54 @@ import com.baomidou.mybatisplus.plugins.pagination.Pagination; import com.tamguo.config.dao.SuperMapper; import com.tamguo.model.PaperEntity; -public interface PaperMapper extends SuperMapper{ - - List findByTypeAndAreaId(@Param(value="type")String type, @Param(value="areaId")String areaId , Pagination page); - - List findByAreaId(@Param(value="areaId") String areaId , Pagination page); - - List findBySchoolId(@Param(value="schoolId")String schoolId , Pagination page); - - List findList(@Param(value="subjectId")String subjectId, @Param(value="courseId")String courseId, @Param(value="paperType")String paperType, - @Param(value="year")String year, @Param(value="area")String area , Pagination page); - - List findPaperByAreaId(@Param(value="areaId")String areaId , @Param(value="type")String type , Pagination page); - +// 定义了名为PaperMapper的接口,它继承自SuperMapper, +// 与之前类似,SuperMapper大概率是一个自定义的通用Mapper接口,用于继承通用的数据库操作方法(如基本的增删改查等), +// 在此基础上,PaperMapper接口可以定义针对试卷(PaperEntity,从命名推测其代表试卷相关实体)相关的特定数据库查询方法。 +public interface PaperMapper extends SuperMapper { + + // 定义一个查询方法findByTypeAndAreaId,用于根据试卷类型(type)和地区ID(areaId)进行分页查询试卷信息列表。 + // 使用了@Param注解为方法的前两个参数分别命名为"type"和"areaId",方便在对应的SQL语句中准确引用参数, + // 第三个参数是Pagination类型的page,这是MyBatis Plus提供的用于分页的对象,通过它可以指定分页相关的参数(如页码、每页数量等), + // 方法返回值是一个PaperEntity类型的列表,即符合条件的试卷实体集合。 + List findByTypeAndAreaId(@Param(value = "type") String type, @Param(value = "areaId") String areaId, Pagination page); + + // 定义查询方法findByAreaId,根据地区ID(areaId)进行分页查询试卷信息列表。 + // 使用@Param注解为参数"areaId"命名,便于SQL语句引用,同样传入Pagination类型的page对象用于分页, + // 返回值为PaperEntity类型的列表,包含了该地区下对应的试卷实体集合。 + List findByAreaId(@Param(value = "areaId") String areaId, Pagination page); + + // 定义查询方法findBySchoolId,依据学校ID(schoolId)进行分页查询试卷信息列表。 + // 通过@Param注解将参数命名为"schoolId",结合分页对象page来实现分页查询功能,返回符合条件的试卷实体列表。 + List findBySchoolId(@Param(value = "schoolId") String schoolId, Pagination page); + + // 定义了一个较为复杂的查询方法findList,用于根据多个条件进行分页查询试卷信息列表,这些条件包括学科ID(subjectId)、课程ID(courseId)、 + // 试卷类型(paperType)、年份(year)以及地区(area),通过@Param注解分别为这些参数命名,方便在SQL语句中准确引用, + // 传入Pagination类型的page对象用于分页操作,返回值是满足所有条件的PaperEntity类型的试卷实体列表。 + List findList(@Param(value = "subjectId") String subjectId, @Param(value = "courseId") String courseId, + @Param(value = "paperType") String paperType, + @Param(value = "year") String year, @Param(value = "area") String area, Pagination page); + + // 定义查询方法findPaperByAreaId,根据地区ID(areaId)和试卷类型(type)进行分页查询试卷信息列表。 + // 利用@Param注解对两个参数"areaId"和"type"进行命名,结合分页对象page实现分页查询,返回符合条件的试卷实体集合。 + List findPaperByAreaId(@Param(value = "areaId") String areaId, @Param(value = "type") String type, Pagination page); + + // 定义了一个查询方法getPaperTotal,用于获取试卷的总数量。 + // 该方法没有传入分页相关的参数,返回值是Long类型,表示符合某种未明确限定(可能是所有记录,也可能是满足特定默认条件的记录,取决于具体业务逻辑和SQL实现)的试卷总条数。 Long getPaperTotal(); - List findByCreaterId(@Param(value="createrId")String createrId); - - List queryPageByNameAndCreatorId(@Param(value="name")String name, @Param(value="memberId")String memberId , Pagination page); + // 定义查询方法findByCreaterId,根据创建者的ID(createrId)来查找对应的试卷信息列表。 + // 使用@Param注解将参数命名为"createrId",方便在SQL语句中引用,返回值为PaperEntity类型的列表,即该创建者创建的试卷实体集合。 + List findByCreaterId(@Param(value = "createrId") String createrId); - List featuredPaper(@Param(value="type")String type, @Param(value="subjectId")String subjectId , Pagination page); + // 定义查询方法queryPageByNameAndCreatorId,用于根据试卷名称(name)和创建者ID(memberId)进行分页查询试卷信息列表。 + // 通过@Param注解分别为两个参数命名,传入Pagination类型的page对象用于分页,返回值是满足条件的试卷实体集合。 + List queryPageByNameAndCreatorId(@Param(value = "name") String name, @Param(value = "memberId") String memberId, Pagination page); - List findHotPaper(@Param(value="subjectId")String subjectId,@Param(value="courseId") String courseId, Pagination page); + // 定义查询方法featuredPaper,根据试卷类型(type)和学科ID(subjectId)进行分页查询特色试卷信息列表。 + // 使用@Param注解为参数命名,结合分页对象page实现分页查询功能,返回值是符合特色试卷相关条件的PaperEntity类型的列表。 + List featuredPaper(@Param(value = "type") String type, @Param(value = "subjectId") String subjectId, Pagination page); + // 定义查询方法findHotPaper,根据学科ID(subjectId)和课程ID(courseId)进行分页查询热门试卷信息列表。 + // 利用@Param注解对两个参数命名,结合分页对象page进行分页查询操作,返回值是满足热门试卷相关条件的PaperEntity类型的列表。 + List findHotPaper(@Param(value = "subjectId") String subjectId, @Param(value = "courseId") String courseId, Pagination page); } diff --git a/tamguo/src/main/java/com/tamguo/interceptor/MemberInterceptor.java b/tamguo/src/main/java/com/tamguo/interceptor/MemberInterceptor.java index 0012392..0c660c6 100644 --- a/tamguo/src/main/java/com/tamguo/interceptor/MemberInterceptor.java +++ b/tamguo/src/main/java/com/tamguo/interceptor/MemberInterceptor.java @@ -6,8 +6,10 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; +// 使用@Component注解将这个类标记为Spring组件,这样Spring在扫描时能够发现并将其纳入到Spring容器管理中, +// 便于在其他地方进行依赖注入或者配置使用。 @Component -public class MemberInterceptor extends HandlerInterceptorAdapter{ +public class MemberInterceptor extends HandlerInterceptorAdapter { /** "重定向URL"参数名称 */ private static final String REDIRECT_URL_PARAMETER_NAME = "redirectUrl"; @@ -17,39 +19,50 @@ public class MemberInterceptor extends HandlerInterceptorAdapter{ /** 登录URL */ private String loginUrl = DEFAULT_LOGIN_URL; - + /** * 请求前处理 - * - * @param request - * HttpServletRequest - * @param response - * HttpServletResponse - * @param handler - * 处理器 - * @return 是否继续执行 + * 重写了父类HandlerInterceptorAdapter的preHandle方法,该方法会在请求到达具体的处理器(比如Controller中的方法)之前被调用, + * 用于进行一些前置的拦截处理逻辑,比如权限校验、登录验证等操作,根据验证结果决定是否允许请求继续往下执行。 + * + * @param request HttpServletRequest,代表客户端发来的HTTP请求对象,从中可以获取请求的各种信息,如请求头、请求参数、会话信息等。 + * @param response HttpServletResponse,用于向客户端返回响应信息,可设置响应状态码、响应头、输出响应内容等。 + * @param handler 处理器,通常是指向具体要执行的Controller方法的一个对象引用,不过在这里一般不需要直接操作它,主要是根据请求相关情况做拦截判断。 + * @return 是否继续执行,如果返回true,表示允许请求继续执行,会接着调用后续的处理器(Controller方法等);如果返回false,则直接中断请求处理流程,不再执行后续操作。 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - Object currMember = request.getSession().getAttribute("currMember"); - if (currMember != null) { + // 从当前的HTTP请求对象中获取会话(Session)里名为"currMember"的属性值,通常这里的"currMember"可能是存储了当前已登录会员的相关信息的对象, + // 如果获取到的值不为null,说明当前用户已经登录,那么允许请求继续执行,直接返回true。 + Object currMember = request.getSession().getAttribute("currMember"); + if (currMember!= null) { return true; } else { + // 获取请求头中名为"X-Requested-With"的值,这个请求头常用于判断请求是否是通过Ajax(XMLHttpRequest)发起的, + // 在一些Web应用中,对于Ajax请求和普通页面请求的处理方式可能会有所不同,比如返回的错误提示格式等方面。 String requestType = request.getHeader("X-Requested-With"); - if (requestType != null && requestType.equalsIgnoreCase("XMLHttpRequest")) { + if (requestType!= null && requestType.equalsIgnoreCase("XMLHttpRequest")) { + // 如果请求是Ajax请求("X-Requested-With"请求头的值为"XMLHttpRequest"),则向响应头中添加一个名为"loginStatus"的自定义头信息, + // 设置其值为"accessDenied",表示登录状态为拒绝访问(也就是未登录情况下的Ajax请求被拦截了), + // 然后设置响应的状态码为HttpServletResponse.SC_FORBIDDEN(403,表示禁止访问),最后返回false,中断请求处理流程,不让请求继续执行。 response.addHeader("loginStatus", "accessDenied"); response.sendError(HttpServletResponse.SC_FORBIDDEN); return false; } else { + // 如果不是Ajax请求,再判断请求的方法是否是GET方法,对于不同的HTTP请求方法,重定向的逻辑可能稍有不同。 if (request.getMethod().equalsIgnoreCase("GET")) { - String redirectUrl = request.getQueryString() != null ? request.getRequestURI() + "?" + request.getQueryString() : request.getRequestURI(); + // 如果是GET请求,构建一个重定向的URL,将当前请求的完整路径(包括请求的URI和查询字符串,如果有的话)进行编码后作为参数添加到登录URL后面, + // 具体做法是先判断当前请求是否有查询字符串(通过request.getQueryString()判断),如果有,则将请求的URI和查询字符串拼接起来, + // 如果没有查询字符串,就只使用请求的URI,然后使用URLEncoder将这个重定向的URL按照UTF-8编码格式进行编码,避免出现中文等特殊字符乱码问题, + // 最后通过response.sendRedirect方法将客户端重定向到登录页面,并带上这个重定向URL参数,以便在登录成功后可以根据这个参数跳回原来请求的页面。 + String redirectUrl = request.getQueryString()!= null? request.getRequestURI() + "?" + request.getQueryString() : request.getRequestURI(); response.sendRedirect(request.getContextPath() + loginUrl + "?" + REDIRECT_URL_PARAMETER_NAME + "=" + URLEncoder.encode(redirectUrl, "UTF-8")); } else { + // 如果不是GET请求(比如POST等其他请求方法),直接重定向到登录页面,不携带额外的重定向URL参数,因为对于非GET请求,通常不需要记录原请求的详细信息来进行后续跳转。 response.sendRedirect(request.getContextPath() + loginUrl); } return false; } } } - -} +} \ No newline at end of file diff --git a/tamguo/src/main/java/com/tamguo/model/AdEntity.java b/tamguo/src/main/java/com/tamguo/model/AdEntity.java index a09b5f8..6d01d3c 100644 --- a/tamguo/src/main/java/com/tamguo/model/AdEntity.java +++ b/tamguo/src/main/java/com/tamguo/model/AdEntity.java @@ -9,51 +9,69 @@ import com.tamguo.config.dao.SuperEntity; /** * The persistent class for the tiku_ad database table. - * + * 这个类(AdEntity)用于映射数据库中名为“tiku_ad”的表,从命名推测它可能是与广告(Ad)相关的数据实体类, + * 继承自SuperEntity并实现了Serializable接口,继承SuperEntity可能是为了复用一些通用的实体相关方法(如基础的数据库操作相关方法等,具体取决于SuperEntity的定义), + * 实现Serializable接口是为了让该类的对象能够在网络传输、持久化存储等场景下进行序列化和反序列化操作,保证对象状态的保存和恢复。 */ -@TableName(value="tiku_ad") +@TableName(value = "tiku_ad") public class AdEntity extends SuperEntity implements Serializable { private static final long serialVersionUID = 1L; + // 用于存储业务相关的键值,具体含义可能与广告业务中的某种标识或者关联信息有关,其值由外部设置并获取,通过对应的getter和setter方法操作。 private String businessKey; + // 用于存储广告的名称,方便在业务中对不同广告进行区分和识别,同样通过getter和setter方法进行访问和修改。 private String name; - + + // 用于存储广告的详细信息,可能是包含了广告内容、展示规则等各种相关数据的字符串表示形式(具体格式取决于业务设计), + // 通过getter和setter方法来操作该属性值。 private String adInfo; + // getName方法用于获取广告名称属性的值,供外部代码调用,遵循JavaBean规范的getter方法定义。 public String getName() { return name; } + // setName方法用于设置广告名称属性的值,外部代码可以传入一个字符串参数来更新广告名称,遵循JavaBean规范的setter方法定义。 public void setName(String name) { this.name = name; } + // getSerialversionuid方法返回类的序列化版本UID,这个UID在序列化和反序列化过程中用于验证类的版本一致性, + // 这里直接返回了定义好的静态常量serialVersionUID的值,一般不需要手动修改这个方法的实现,它是按照Java序列化机制要求定义的。 public static long getSerialversionuid() { return serialVersionUID; } + // getAdInfo方法用于获取广告详细信息(adInfo)属性的值,外部代码可以通过调用这个方法获取存储的广告相关信息字符串。 public String getAdInfo() { return adInfo; } + // setAdInfo方法用于设置广告详细信息属性的值,外部代码可以传入一个字符串参数来更新广告详细信息的内容。 public void setAdInfo(String adInfo) { this.adInfo = adInfo; } - - public JSONArray getAds(){ - if(StringUtils.isEmpty(getAdInfo())){ + + /** + * getAds方法用于将存储在adInfo属性中的字符串解析为JSONArray对象,方便后续以JSON数据结构的形式对广告相关信息进行操作。 + * 如果adInfo属性的值为空(通过StringUtils.isEmpty方法判断),则返回null,表示没有可解析的有效广告信息; + * 如果不为空,则使用阿里巴巴的FastJSON库的JSONArray.parseArray方法将其解析为JSONArray对象,以便进行如遍历、获取特定元素等操作。 + */ + public JSONArray getAds() { + if (StringUtils.isEmpty(getAdInfo())) { return null; } return JSONArray.parseArray(getAdInfo()); } + // getBusinessKey方法用于获取业务键值(businessKey)属性的值,供外部代码获取相关的业务标识或关联信息。 public String getBusinessKey() { return businessKey; } + // setBusinessKey方法用于设置业务键值属性的值,外部代码可以传入一个字符串参数来更新该属性所代表的业务相关标识信息。 public void setBusinessKey(String businessKey) { this.businessKey = businessKey; } - } \ No newline at end of file diff --git a/tamguo/src/main/java/com/tamguo/model/BookEntity.java b/tamguo/src/main/java/com/tamguo/model/BookEntity.java index cf28015..f5b2eee 100644 --- a/tamguo/src/main/java/com/tamguo/model/BookEntity.java +++ b/tamguo/src/main/java/com/tamguo/model/BookEntity.java @@ -6,91 +6,143 @@ import com.baomidou.mybatisplus.activerecord.Model; import com.baomidou.mybatisplus.annotations.TableId; import com.baomidou.mybatisplus.annotations.TableName; -@TableName(value="tiku_book") -public class BookEntity extends Model{ - +// 这个类(BookEntity)定义了与数据库中名为“tiku_book”表对应的实体对象,从命名来看,它可能是用于表示书籍相关信息的数据实体类。 +// 该类继承自MyBatis Plus的Model类,继承Model类可以方便地使用MyBatis Plus提供的一些便捷的数据库操作方法,比如CRUD操作等, +// 同时还实现了Serializable接口,使得该类的对象能够在诸如网络传输、持久化存储等场景下进行序列化和反序列化操作,保证对象状态可以被正确保存和恢复。 +@TableName(value = "tiku_book") +public class BookEntity extends Model { + private static final long serialVersionUID = 1L; - + + // 使用@TableId注解标记这个字段,表明它是数据库表(“tiku_book”表)中的主键字段,在这里字段名为“uid”, + // 具体的主键生成策略等相关设置(如果有的话)通常会根据MyBatis Plus的默认配置或者项目中额外的配置来确定, + // 通过对应的getter和setter方法来操作这个属性的值,外部代码可以获取或设置书籍的唯一标识符(uid)。 @TableId private String uid; + + // 用于存储学科的唯一标识符,通过这个字段可以关联到对应的学科信息,其值由外部设置并获取,使用对应的getter和setter方法进行操作, + // 方便在业务逻辑中确定书籍所属的学科分类等情况。 private String subjectId; + + // 用于存储课程的唯一标识符,类似学科标识符的作用,可用于关联到具体的课程信息,外部代码可以通过getter和setter方法对其值进行操作, + // 帮助在业务中明确书籍与具体课程之间的关联关系。 private String courseId; + + // 用于存储书籍的名称,通过getter和setter方法(getName和setName),外部代码能够获取或更新书籍的名称信息,便于在业务中展示、查找等操作时使用。 private String name; + + // 用于存储书籍的出版社信息,外部代码可利用相应的getter和setter方法获取或修改该属性值,在涉及书籍来源、版权等业务场景下会用到这个信息。 private String publishingHouse; + + // 用于存储书籍中包含的题目数量相关信息,可能是一个数字字符串形式(具体取决于业务存储格式),通过对应的getter和setter方法进行操作, + // 在统计、查询等业务操作中可以依据这个属性了解书籍的题目规模情况。 private String questionNum; + + // 用于存储书籍中所涵盖的知识点数量相关信息,同样通过getter和setter方法来操作其值,方便在业务逻辑中对书籍涵盖的知识点情况进行把握和处理。 private String pointNum; + + // 用于存储书籍的排序序号相关信息,可能用于在展示、排序等场景下确定书籍的顺序,通过对应的getter和setter方法进行操作, + // 外部代码可以获取或修改这个序号值来调整书籍的排列顺序等情况。 private Integer orders; - public String getUid() { - return uid; + // getName方法是遵循JavaBean规范的getter方法,用于获取书籍名称属性(name)的值,外部代码可以调用这个方法获取当前BookEntity对象所代表书籍的名称。 + public String getName() { + return name; } - public void setUid(String uid) { - this.uid = uid; + // setName方法是遵循JavaBean规范的setter方法,用于设置书籍名称属性的值,外部代码可以传入一个字符串参数来更新当前BookEntity对象所代表书籍的名称。 + public void setName(String name) { + this.name = name; } + // getSubjectId方法用于获取学科标识符(subjectId)属性的值,供外部代码获取书籍所属的学科相关信息,便于进行关联查询、分类统计等操作。 public String getSubjectId() { return subjectId; } + // setSubjectId方法用于设置学科标识符属性的值,外部代码可以传入一个字符串参数来更新书籍所属的学科信息,在业务逻辑中调整书籍的学科分类归属等情况时会用到。 public void setSubjectId(String subjectId) { this.subjectId = subjectId; } + // getCourseId方法用于获取课程标识符(courseId)属性的值,方便外部代码知晓书籍与具体课程的关联情况,在课程相关的业务操作中起到关联作用。 public String getCourseId() { return courseId; } + // setCourseId方法用于设置课程标识符属性的值,外部代码可以传入一个字符串参数来更新书籍对应的课程信息,用于在业务中重新确定书籍与课程的关联关系。 public void setCourseId(String courseId) { this.courseId = courseId; } - public String getName() { - return name; + // getUid方法用于获取书籍的唯一标识符(uid)属性的值,这个值作为主键在数据库操作以及对象标识等方面有着重要作用, + // 外部代码可以通过调用这个方法获取当前BookEntity对象对应的书籍在数据库中的唯一标识。 + public String getUid() { + return uid; } - public void setName(String name) { - this.name = name; + // setUid方法用于设置书籍的唯一标识符属性的值,外部代码可以传入一个字符串参数来更新这个唯一标识信息,不过在实际应用中要谨慎操作, + // 因为主键通常具有唯一性且关联着数据库中的重要数据记录,随意修改可能导致数据不一致等问题。 + public void setUid(String uid) { + this.uid = uid; } + // getPublishingHouse方法用于获取书籍出版社(publishingHouse)属性的值,外部代码可以通过调用这个方法获取当前BookEntity对象所代表书籍的出版社信息。 public String getPublishingHouse() { return publishingHouse; } + // setPublishingHouse方法用于设置书籍出版社属性的值,外部代码可以传入一个字符串参数来更新书籍的出版社信息,比如在书籍信息更新、录入新书籍等场景下使用。 public void setPublishingHouse(String publishingHouse) { this.publishingHouse = publishingHouse; } + // getQuestionNum方法用于获取书籍题目数量(questionNum)属性的值,外部代码可以获取这个属性值来了解书籍包含的题目规模情况, + // 在统计分析、展示等业务操作中会用到这个信息。 public String getQuestionNum() { return questionNum; } + // setQuestionNum方法用于设置书籍题目数量属性的值,外部代码可以传入一个字符串参数来更新书籍所包含的题目数量信息, + // 例如在题目数量发生变化(新增、删除题目等情况)时对该属性进行相应的更新操作。 public void setQuestionNum(String questionNum) { this.questionNum = questionNum; } + // getPointNum方法用于获取书籍知识点数量(pointNum)属性的值,外部代码可以通过调用这个方法获取当前BookEntity对象所代表书籍涵盖的知识点数量情况, + // 在知识点相关的业务处理、统计等场景下会用到这个属性值。 public String getPointNum() { return pointNum; } + // setPointNum方法用于设置书籍知识点数量属性的值,外部代码可以传入一个字符串参数来更新书籍涵盖的知识点数量信息, + // 比如在对书籍内容进行知识点梳理、更新后对该属性进行相应的调整操作。 public void setPointNum(String pointNum) { this.pointNum = pointNum; } + // getOrders方法用于获取书籍排序序号(orders)属性的值,外部代码可以获取这个序号值来了解书籍在相关展示、排序场景下的顺序位置情况, + // 并且可以通过对应的setOrders方法来修改这个序号,从而调整书籍的排列顺序等。 public Integer getOrders() { return orders; } + // setOrders方法用于设置书籍排序序号属性的值,外部代码可以传入一个整数参数来更新书籍的排序序号,用于在业务中改变书籍的排列顺序, + // 例如在列表展示中调整书籍的先后位置等情况。 public void setOrders(Integer orders) { this.orders = orders; } + // getSerialversionuid方法返回类的序列化版本UID,这个UID在序列化和反序列化过程中用于验证类的版本一致性, + // 这里直接返回了定义好的静态常量serialVersionUID的值,一般不需要手动修改这个方法的实现,它是按照Java序列化机制要求定义的。 public static long getSerialversionuid() { return serialVersionUID; } + // 重写了父类Model的pkVal方法,用于指定当前实体类的主键值,在这里返回了getUid方法获取到的书籍唯一标识符(uid), + // 这样MyBatis Plus在进行一些基于主键的数据库操作(如根据主键查询、更新等)时就能准确知道当前实体对应的主键是什么,从而正确执行相关操作。 @Override protected Serializable pkVal() { return getUid(); } -} +} \ No newline at end of file diff --git a/tamguo/src/main/java/com/tamguo/service/impl/AdService.java b/tamguo/src/main/java/com/tamguo/service/impl/AdService.java index 5a06426..9ca9a93 100644 --- a/tamguo/src/main/java/com/tamguo/service/impl/AdService.java +++ b/tamguo/src/main/java/com/tamguo/service/impl/AdService.java @@ -13,24 +13,49 @@ import com.tamguo.model.AdEntity; import com.tamguo.service.IAdService; import com.tamguo.util.TamguoConstant; +// 使用 @Service 注解将这个类标记为Spring中的服务层组件,表明它是用于处理业务逻辑的类,Spring会自动扫描并将其纳入到Spring容器管理中, +// 方便在其他地方进行依赖注入等操作,同时也遵循了Spring的分层架构规范,使得代码结构更清晰。 @Service -public class AdService extends ServiceImpl implements IAdService{ - +public class AdService extends ServiceImpl implements IAdService { + + // 通过Spring的依赖注入机制,自动注入 AdMapper 接口的实现类实例, + // AdMapper 应该是用于定义与广告(AdEntity)相关的数据库操作方法的接口(比如查询、插入、更新等操作), + // 在这个服务类中会调用它的方法来与数据库进行交互,获取广告相关的数据。 @Autowired AdMapper adMapper; + + // 同样通过依赖注入机制注入 CacheService 实例,CacheService 大概率是用于处理缓存相关操作的服务类, + // 例如从缓存中获取数据、将数据存入缓存等功能,在这里主要用于缓存广告数据,减少频繁访问数据库的开销,提高系统性能。 @Autowired CacheService cacheService; + /** + * findAll 方法 + * 这个方法重写了接口 IAdService 中的 findAll 方法(从方法签名可以判断,这里实现了接口定义的抽象方法), + * 用于获取所有的广告信息列表,并且加入了缓存相关的逻辑处理,以优化数据获取的性能。 + */ @SuppressWarnings("unchecked") @Override public List findAll() { + // 首先尝试从缓存中获取名为 TamguoConstant.ALL_AD 的缓存对象,这里将获取到的对象强制转换为 List 类型, + // 期望从缓存中获取到之前存储的广告信息列表,如果缓存命中,就可以直接返回这个缓存中的广告列表数据,减少数据库查询操作。 List adList = (List) cacheService.getObject(TamguoConstant.ALL_AD); + // 此处将 adList 赋值为 null,这看起来有点奇怪,可能是代码有误或者有特定的后续逻辑(比如要重新获取最新数据等情况), + // 正常情况下如果是想清空缓存数据重新获取,应该先删除缓存中的对应数据等操作,而不是简单赋值为 null,不过先按照现有逻辑继续分析。 adList = null; - if(adList == null || adList.isEmpty()){ + // 判断从缓存中获取到的广告列表是否为 null 或者为空列表(即没有缓存数据或者缓存数据不存在有效的广告信息), + // 如果满足这个条件,就需要从数据库中重新获取广告信息列表。 + if (adList == null || adList.isEmpty()) { + // 通过注入的 AdMapper 调用其 selectList 方法,传入 Condition.EMPTY 参数,这表示使用默认的查询条件(通常是查询所有记录), + // 从数据库中获取所有的广告实体信息,将获取到的结果赋值给 adList 变量,用于后续操作。 adList = adMapper.selectList(Condition.EMPTY); - cacheService.setObject(TamguoConstant.ALL_AD, adList , 2 * 60 * 60); + // 调用注入的 CacheService 的 setObject 方法,将刚刚从数据库中获取到的广告列表数据(adList)存入缓存中, + // 缓存的键名为 TamguoConstant.ALL_AD,同时设置了缓存的有效时间为 2 * 60 * 60 秒(即 2 小时), + // 这样在接下来的 2 小时内,如果再次调用 findAll 方法,就可以直接从缓存中获取广告数据,而不用再次查询数据库了。 + cacheService.setObject(TamguoConstant.ALL_AD, adList, 2 * 60 * 60); } + // 最后返回获取到的广告信息列表,这个列表可能是从缓存中直接获取的,也可能是从数据库中查询后存入缓存再返回的, + // 取决于缓存中是否存在有效数据以及之前的逻辑执行情况。 return adList; } - } diff --git a/tamguo/src/main/java/com/tamguo/service/impl/AreaService.java b/tamguo/src/main/java/com/tamguo/service/impl/AreaService.java index c6158da..26a0b8e 100644 --- a/tamguo/src/main/java/com/tamguo/service/impl/AreaService.java +++ b/tamguo/src/main/java/com/tamguo/service/impl/AreaService.java @@ -15,48 +15,91 @@ import com.tamguo.service.IAreaService; import com.tamguo.util.Result; import com.tamguo.util.TamguoConstant; +// 使用 @Service 注解将这个类标记为Spring中的服务层组件,意味着它主要负责处理业务逻辑相关的操作, +// Spring在扫描时会自动识别并将其纳入到Spring容器中进行管理,方便在其他地方进行依赖注入等操作,符合Spring的分层架构设计。 @Service -public class AreaService extends ServiceImpl implements IAreaService{ - +public class AreaService extends ServiceImpl implements IAreaService { + + // 通过Spring的依赖注入机制,自动注入 AreaMapper 接口的实现类实例, + // AreaMapper 应该是用于定义与地区(AreaEntity)相关的数据库操作方法的接口,比如查询、插入、更新等操作, + // 在本服务类中会调用它的方法来和数据库进行交互,获取地区相关的数据信息。 @Autowired private AreaMapper areaMapper; + + // 同样通过依赖注入注入 CacheService 实例,CacheService 大概率是用于处理缓存相关操作的服务类, + // 例如从缓存中获取数据、将数据存入缓存以及判断缓存是否存在等功能,在这个类中主要用于缓存地区相关的数据,以优化数据获取性能,减少数据库访问次数。 @Autowired private CacheService cacheService; - + + /** + * findAll 方法 + * 重写了 IAreaService 接口中的 findAll 方法,用于获取所有的地区信息列表, + * 直接调用了通过依赖注入获取的 AreaMapper 的 selectList 方法,并传入 Condition.EMPTY 参数, + * 这意味着使用默认的查询条件(通常是查询所有符合条件的记录,在这里就是所有地区记录)从数据库中获取地区实体信息列表并返回。 + */ @SuppressWarnings("unchecked") @Override public List findAll() { return areaMapper.selectList(Condition.EMPTY); } - @Transactional(readOnly=true) + /** + * findRootArea 方法 + * 重写了 IAreaService 接口中的 findRootArea 方法,该方法被标记为只读事务(通过 @Transactional(readOnly = true) 注解指定), + * 意味着在这个方法执行的数据库操作中,只会进行查询操作,不会进行修改数据的操作(如插入、更新、删除等), + * 调用了 AreaMapper 的 findRootArea 方法,用于从数据库中获取最顶层(根)的地区信息列表,具体的查询逻辑由 AreaMapper 中对应的方法实现来确定, + // 返回查询到的根地区信息列表。 + */ + @Transactional(readOnly = true) @Override public List findRootArea() { return areaMapper.findRootArea(); } - @Transactional(readOnly=true) + /** + * findAreaTree 方法 + * 重写了 IAreaService 接口中的 findAreaTree 方法,同样被标记为只读事务,用于获取地区信息的树形结构数据, + * 方法内部加入了缓存相关的逻辑,先判断缓存中是否已经存在名为 TamguoConstant.AREA_ALL_TREE 的缓存数据, + * 如果缓存中存在,就直接将缓存中的数据作为成功结果返回(通过 Result.successResult 方法进行包装,将缓存数据封装到统一的结果对象中返回), + * 避免重复查询数据库构建地区树结构数据,提高获取数据的效率。 + */ + @Transactional(readOnly = true) @Override public Result findAreaTree() { - if(cacheService.isExist(TamguoConstant.AREA_ALL_TREE)) { + if (cacheService.isExist(TamguoConstant.AREA_ALL_TREE)) { return Result.successResult(cacheService.getObject(TamguoConstant.AREA_ALL_TREE)); } + + // 如果缓存中不存在相应数据,先从数据库中获取根地区信息列表,调用 AreaMapper 的 findRootArea 方法来获取, + // 这些根地区将作为地区树结构的顶层节点,后续会基于这些节点逐步构建完整的地区树。 List areaList = areaMapper.findRootArea(); - for(AreaEntity area : areaList) { + + // 遍历根地区列表,对于每个根地区,查找其下一级子地区信息,通过调用 AreaMapper 的 findByParent 方法,传入当前根地区的唯一标识符(area.getUid())作为参数, + // 获取该根地区对应的子地区列表,然后判断子地区列表是否为空,如果不为空,将子地区列表设置为当前根地区的子节点(通过 area.setChildren(childend) 方法设置), + // 这样就构建了地区树结构的第一层父子关系。 + for (AreaEntity area : areaList) { List childend = areaMapper.findByParent(area.getUid()); - if(!CollectionUtils.isEmpty(childend)) { + if (!CollectionUtils.isEmpty(childend)) { area.setChildren(childend); } - - for(AreaEntity a : childend) { + + // 对于每个子地区,再进一步查找它的下一级子地区(也就是孙子地区等更深层次的子节点),同样通过调用 AreaMapper 的 findByParent 方法, + // 传入子地区的唯一标识符(a.getUid())获取其对应的子地区列表,若列表不为空,将这些子地区设置为当前子地区的子节点(通过 a.setChildren(ceList) 方法设置), + // 以此类推,通过循环嵌套的方式逐步构建出完整的地区树结构,包含多层级的父子关系。 + for (AreaEntity a : childend) { List ceList = areaMapper.findByParent(a.getUid()); - if(!CollectionUtils.isEmpty(ceList)) { + if (!CollectionUtils.isEmpty(ceList)) { a.setChildren(ceList); } } } - cacheService.setObject(TamguoConstant.AREA_ALL_TREE, areaList , 60 * 60 * 2); + + // 将构建好的地区树结构数据存入缓存中,缓存的键名为 TamguoConstant.AREA_ALL_TREE,同时设置缓存的有效时间为 60 * 60 * 2 秒(即 2 小时), + // 这样在接下来的 2 小时内,如果再次调用 findAreaTree 方法,就可以直接从缓存中获取地区树数据,而不用重新构建,提高了数据获取的性能和效率。 + cacheService.setObject(TamguoConstant.AREA_ALL_TREE, areaList, 60 * 60 * 2); + + // 将构建好的地区树结构数据作为成功结果返回(通过 Result.successResult 方法进行包装),方便在调用处统一处理返回结果, + // 并且符合项目中对返回结果进行统一格式封装的规范要求。 return Result.successResult(areaList); } - -} +} \ No newline at end of file