|  |  |  | @ -25,288 +25,637 @@ import java.util.Date; | 
			
		
	
		
			
				
					|  |  |  |  | import java.util.List; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  |  |  * ProductServiceImpl类实现了IProductService接口,是整个项目中处理商品相关业务逻辑的核心服务类, | 
			
		
	
		
			
				
					|  |  |  |  |  * 它整合了数据持久层(通过ProductMapper)、缓存操作(通过CommonCacheUtil)以及与其他服务的交互(通过CategoryClient)等功能, | 
			
		
	
		
			
				
					|  |  |  |  |  * 为前端(包括后台管理系统前端和前台门户前端)提供了丰富的商品操作服务,如获取商品列表、查询商品详情、更新商品状态、新增/更新商品等功能, | 
			
		
	
		
			
				
					|  |  |  |  |  * 并且在操作过程中注重数据的格式转换、缓存的合理使用以及与其他模块的协同工作,确保商品业务逻辑的完整性和高效性。 | 
			
		
	
		
			
				
					|  |  |  |  |  * | 
			
		
	
		
			
				
					|  |  |  |  |  * @Author swg. | 
			
		
	
		
			
				
					|  |  |  |  |  * @Date 2019/1/2 17:36 | 
			
		
	
		
			
				
					|  |  |  |  |  * @CONTACT 317758022@qq.com | 
			
		
	
		
			
				
					|  |  |  |  |  * @DESC | 
			
		
	
		
			
				
					|  |  |  |  |  */ | 
			
		
	
		
			
				
					|  |  |  |  | @Service | 
			
		
	
		
			
				
					|  |  |  |  | // @Service注解用于标记该类是Spring框架中的服务层组件,意味着这个类会被Spring容器管理,其他地方(如控制层)可以通过依赖注入的方式使用这个类的实例,
 | 
			
		
	
		
			
				
					|  |  |  |  | // 同时Spring会对其进行相关的生命周期管理以及一些配置和增强操作(如AOP切面等,若有配置的话),方便业务逻辑的组织和复用。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | @Slf4j | 
			
		
	
		
			
				
					|  |  |  |  | // 使用Lombok的 @Slf4j注解自动生成名为log的SLF4J日志记录器,用于在商品业务逻辑处理的各个步骤中记录关键信息(如操作的参数、执行结果等)以及出现异常情况时记录详细的错误信息,
 | 
			
		
	
		
			
				
					|  |  |  |  | // 方便后续查看日志来了解商品业务的执行情况,排查可能出现的问题,例如商品查询失败的原因、更新状态不成功的原因等情况。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | public class ProductServiceImpl implements IProductService{ | 
			
		
	
		
			
				
					|  |  |  |  |     @Autowired | 
			
		
	
		
			
				
					|  |  |  |  |     private ProductMapper productMapper; | 
			
		
	
		
			
				
					|  |  |  |  |     // 通过Spring的依赖注入机制注入ProductMapper接口的实现类实例,用于与数据库进行交互,执行如查询商品记录、更新商品信息等持久化操作,
 | 
			
		
	
		
			
				
					|  |  |  |  |     // ProductMapper中定义了一系列针对商品数据表的操作方法,这个实例在本类的多个业务方法中都会被调用,以获取或更新商品相关的数据。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @Autowired | 
			
		
	
		
			
				
					|  |  |  |  |     private CategoryClient categoryClient; | 
			
		
	
		
			
				
					|  |  |  |  |     // 注入CategoryClient实例,CategoryClient通常是基于Feign框架实现的用于调用商品分类相关服务的客户端接口,
 | 
			
		
	
		
			
				
					|  |  |  |  |     // 通过它可以远程调用其他服务(可能是独立的商品分类服务微服务)提供的接口,获取商品分类的详细信息等内容,在一些需要涉及商品分类信息处理的业务方法中会用到它。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @Autowired | 
			
		
	
		
			
				
					|  |  |  |  |     private CommonCacheUtil commonCacheUtil; | 
			
		
	
		
			
				
					|  |  |  |  | // 注入CommonCacheUtil实例,用于操作缓存(通常是Redis缓存等情况),实现如缓存商品数据、从缓存中获取商品数据、删除缓存中的商品相关键值对等功能,
 | 
			
		
	
		
			
				
					|  |  |  |  |     // 在整个商品业务逻辑中,通过合理使用缓存可以提高数据获取的效率,减少对数据库的频繁访问,提升系统的整体性能。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * 后台获取产品分页列表的业务方法实现,用于获取后台管理系统中展示的商品分页列表信息。 | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @param pageNum 表示要获取的商品列表所在的页码,是一个整数类型参数,用于指定获取第几页的商品列表,例如传入1表示获取第一页的商品列表, | 
			
		
	
		
			
				
					|  |  |  |  |      *                通常默认从第一页开始展示商品列表,调用者(如后台管理系统的控制器)可以根据用户操作或者业务需求传入不同的页码值来获取相应页的商品信息。 | 
			
		
	
		
			
				
					|  |  |  |  |      * @param pageSize 表示每页显示的商品数量,也是整数类型参数,用于控制每页展示的商品个数,例如传入10表示每页展示10个商品, | 
			
		
	
		
			
				
					|  |  |  |  |      *                 调用者可以根据页面布局、用户查看习惯等因素传入合适的每页数量值。 | 
			
		
	
		
			
				
					|  |  |  |  |      * @return ServerResponse 返回一个ServerResponse类型的对象,该对象用于包装获取商品分页列表操作的结果, | 
			
		
	
		
			
				
					|  |  |  |  |      *                         若操作成功,返回的ServerResponse对象中会包含表示成功的状态码以及分页后的商品列表数据等信息,方便调用者展示商品列表, | 
			
		
	
		
			
				
					|  |  |  |  |      *                         若操作失败(如数据库查询异常、参数错误等原因),则会包含相应的错误信息和表示失败的状态码,供调用者进行相应的错误处理,例如提示用户获取列表失败等操作。 | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @Override | 
			
		
	
		
			
				
					|  |  |  |  |     public ServerResponse list(int pageNum, int pageSize) { | 
			
		
	
		
			
				
					|  |  |  |  |         //1.pagehelper对下一行取出的集合进行分页
 | 
			
		
	
		
			
				
					|  |  |  |  |         PageHelper.startPage(pageNum,pageSize); | 
			
		
	
		
			
				
					|  |  |  |  |         // 使用PageHelper插件启动分页功能,它会拦截后续执行的查询语句(通过ProductMapper进行的查询),并根据传入的页码和每页数量参数对查询结果进行分页处理,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 例如,如果查询到的总商品数量为100条,传入pageNum为2,pageSize为10,那么将会获取到第11 - 20条商品记录,方便在后台展示分页的商品列表。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         List<Product> productList = productMapper.selectList(); | 
			
		
	
		
			
				
					|  |  |  |  |         // 通过ProductMapper的selectList方法从数据库中获取所有的商品记录列表,这里获取到的是原始的Product实体对象列表,后续需要对其进行一些格式转换等操作,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 以适配返回给前端展示的需求,例如将其转换为包含部分商品属性的视图对象列表等情况。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         List<ProductListVo> productListVoList = Lists.newArrayList(); | 
			
		
	
		
			
				
					|  |  |  |  |         for(Product product:productList){ | 
			
		
	
		
			
				
					|  |  |  |  |             ProductListVo productListVo = assembleProductListVo(product); | 
			
		
	
		
			
				
					|  |  |  |  |             productListVoList.add(productListVo); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         // 遍历从数据库获取到的商品列表,调用assembleProductListVo方法将每个Product实体对象转换为ProductListVo视图对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // ProductListVo对象可能只包含了前端展示所需的部分商品属性,这样可以减少返回给前端的数据量,同时保证展示的信息是前端关心的关键内容,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 将转换后的视图对象依次添加到productListVoList列表中,用于后续构建包含分页信息的返回结果。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         //2.返回给前端的还需要一些其他的分页信息,为了不丢失这些信息,需要进行下面的处理
 | 
			
		
	
		
			
				
					|  |  |  |  |         PageInfo pageInfo = new PageInfo(productList); | 
			
		
	
		
			
				
					|  |  |  |  |         pageInfo.setList(productListVoList); | 
			
		
	
		
			
				
					|  |  |  |  |         // 创建PageInfo对象,它会自动封装从数据库查询到的商品列表的分页相关信息(如总记录数、总页数、当前页数据列表等),
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 然后将转换后的视图对象列表(productListVoList)设置到PageInfo对象中,替换掉原本的包含所有商品属性的列表,确保返回给前端的分页信息是准确且符合展示需求的,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 最后将包含分页商品列表信息的PageInfo对象包装在ServerResponse对象中返回给调用者(后台管理系统前端)。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         return ServerResponse.createBySuccess(pageInfo); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * 后台的搜索,根据id或者name模糊查询的业务方法实现,用于在后台管理系统中实现商品的搜索功能,并返回分页的搜索结果。 | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @param productName 表示要搜索的商品名称,是一个字符串类型参数,用于进行模糊查询,若传入具体的商品名称关键字, | 
			
		
	
		
			
				
					|  |  |  |  |      *                    实现类需要在数据库中查找名称包含该关键字的商品记录(常见的是使用 LIKE '%关键字%' 这样的方式进行模糊匹配), | 
			
		
	
		
			
				
					|  |  |  |  |      *                    若不传该参数或者传入空字符串,则在搜索时可视为不限制商品名称条件,仅依据其他参数(如商品ID等)进行查询,方便实现多种搜索场景,例如只按ID搜索或者结合名称和ID进行搜索等情况。 | 
			
		
	
		
			
				
					|  |  |  |  |      * @param productId 表示商品的唯一标识符,即商品ID,是一个整数类型参数,用于精确查找特定的商品,若传入具体的商品ID值, | 
			
		
	
		
			
				
					|  |  |  |  |      *                  实现类会优先根据该ID查找对应的商品记录,若不传该参数或者传入null值,则在搜索时可视为不限制商品ID条件, | 
			
		
	
		
			
				
					|  |  |  |  |      *                  可以结合商品名称等其他参数进行商品查找,通过与productName等参数的灵活组合,实现不同条件下的商品搜索功能。 | 
			
		
	
		
			
				
					|  |  |  |  |      * @param pageNum 表示要获取的商品列表所在的页码,整数类型,用于指定获取搜索结果中第几页的商品列表,其作用与在list方法中的类似, | 
			
		
	
		
			
				
					|  |  |  |  |      *                调用者可以传入具体页码值获取对应页的搜索到的商品信息,实现类需要根据这个页码值进行分页查询操作,获取相应页的符合搜索条件的商品数据。 | 
			
		
	
		
			
				
					|  |  |  |  |      * @param pageSize 表示每页显示的商品数量,整数类型,用于控制每页展示的搜索到的商品个数,其作用同list方法中的pageSize参数, | 
			
		
	
		
			
				
					|  |  |  |  |      *                 调用者可传入期望的每页数量值,实现类依据此参数对搜索到的商品数据进行分页处理,确保返回的商品列表符合每页显示数量的要求。 | 
			
		
	
		
			
				
					|  |  |  |  |      * @return ServerResponse<PageInfo> 返回一个ServerResponse类型的对象,其泛型参数为PageInfo, | 
			
		
	
		
			
				
					|  |  |  |  |      *                                 ServerResponse用于包装业务操作的结果,PageInfo是用于包装分页相关信息(如总记录数、总页数、当前页数据列表等)以及查询到的商品列表数据的对象, | 
			
		
	
		
			
				
					|  |  |  |  |      *                                 若搜索操作成功,返回的ServerResponse对象中会包含表示成功的状态码以及有效的PageInfo对象(包含符合条件的商品列表和分页信息)等信息,方便调用者分页展示搜索结果, | 
			
		
	
		
			
				
					|  |  |  |  |      *                                 若搜索失败(如数据库查询异常、参数错误等原因),则会包含相应的错误信息和表示失败的状态码,供调用者进行相应的错误处理,例如提示用户搜索失败等操作。 | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     @Override | 
			
		
	
		
			
				
					|  |  |  |  |     public ServerResponse<PageInfo> search(String productName, Integer productId, int pageNum, int pageSize) { | 
			
		
	
		
			
				
					|  |  |  |  |         //开始准备分页
 | 
			
		
	
		
			
				
					|  |  |  |  |         PageHelper.startPage(pageNum,pageSize); | 
			
		
	
		
			
				
					|  |  |  |  |         // 同样使用PageHelper插件启动分页功能,为后续的商品搜索结果进行分页准备,后续查询到的符合条件的商品记录将会按照传入的页码和每页数量参数进行分页处理。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         //如果有内容,可以先在这里封装好,直接传到sql中去
 | 
			
		
	
		
			
				
					|  |  |  |  |         if(StringUtils.isNotBlank(productName)){ | 
			
		
	
		
			
				
					|  |  |  |  |             productName = new StringBuilder().append("%").append(productName).append("%").toString(); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果传入的商品名称参数不为空字符串,对其进行模糊查询格式的处理,通过在名称前后添加 "%" 符号,将其转换为 LIKE 语句中可用的模糊查询格式,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 例如,原本传入的商品名称为 "手机",处理后变为 "%手机%",这样在数据库查询时就能查找名称中包含 "手机" 关键字的所有商品记录了。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         List<Product> productList = productMapper.selectProductByNameAndId(productName,productId); | 
			
		
	
		
			
				
					|  |  |  |  |         // 通过ProductMapper的selectProductByNameAndId方法,传入处理后的商品名称和商品ID参数,从数据库中查询符合条件的商品记录列表,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 这个方法内部会根据传入的参数构建相应的SQL查询语句(基于MyBatis的映射机制),执行数据库查询操作,获取满足条件的商品记录。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         //转换一下传给前端显示
 | 
			
		
	
		
			
				
					|  |  |  |  |         List<ProductListVo> productListVoList = Lists.newArrayList(); | 
			
		
	
		
			
				
					|  |  |  |  |         for(Product product:productList){ | 
			
		
	
		
			
				
					|  |  |  |  |             ProductListVo productListVo = assembleProductListVo(product); | 
			
		
	
		
			
				
					|  |  |  |  |             productListVoList.add(productListVo); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         // 遍历查询到的商品列表,调用assembleProductListVo方法将每个Product实体对象转换为ProductListVo视图对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 目的同样是为了提取前端展示所需的部分商品属性,减少返回给前端的数据量,将转换后的视图对象依次添加到productListVoList列表中,用于后续构建返回结果。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         PageInfo pageInfo = new PageInfo(productList); | 
			
		
	
		
			
				
					|  |  |  |  |         pageInfo.setList(productListVoList); | 
			
		
	
		
			
				
					|  |  |  |  |         // 创建PageInfo对象来封装分页相关信息,将包含商品列表的PageInfo对象的原始商品列表替换为转换后的视图对象列表,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 确保返回给前端的分页信息中包含的是经过处理、符合展示需求的商品数据,最后将包含分页商品列表信息的PageInfo对象包装在ServerResponse对象中返回给调用者(后台管理系统前端)。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         return ServerResponse.createBySuccess(pageInfo); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * 后台查看商品详情的业务方法实现,用于在后台管理系统中获取指定商品的详细信息。 | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @param productId 表示要查看详情的商品的唯一标识符,是一个整数类型参数,通过传入这个参数,实现类能够定位到数据库中对应的商品记录, | 
			
		
	
		
			
				
					|  |  |  |  |      *                  进而获取该商品的详细信息,如商品名称、描述、价格、库存等各种属性信息,调用者需要确保传入准确的商品ID值, | 
			
		
	
		
			
				
					|  |  |  |  |      *                  若传入的ID对应的商品不存在等情况,实现类需要在返回的ServerResponse对象中包含相应的错误信息和表示失败的状态码,方便调用者进行相应的处理,例如提示用户商品不存在等操作。 | 
			
		
	
		
			
				
					|  |  |  |  |      * @return ServerResponse<ProductDetailVo> 返回一个ServerResponse类型的对象,其泛型参数为ProductDetailVo, | 
			
		
	
		
			
				
					|  |  |  |  |      *                                         ServerResponse用于包装业务操作的结果,ProductDetailVo是一个包含商品详细信息的视图对象(VO,View Object), | 
			
		
	
		
			
				
					|  |  |  |  |      *                                         若查询操作成功,返回的ServerResponse对象中会包含表示成功的状态码以及有效的ProductDetailVo对象(包含丰富的商品详细属性信息)等信息,方便调用者展示商品详情, | 
			
		
	
		
			
				
					|  |  |  |  |      *                                         若查询失败(如商品不存在、数据库查询异常等原因),则会包含相应的错误信息和表示失败的状态码,供调用者进行相应的错误处理,例如提示用户查看详情失败等操作。 | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     @Override | 
			
		
	
		
			
				
					|  |  |  |  |     public ServerResponse<ProductDetailVo> detail(Integer productId) { | 
			
		
	
		
			
				
					|  |  |  |  |         //1,校验参数
 | 
			
		
	
		
			
				
					|  |  |  |  |         if(productId == null){ | 
			
		
	
		
			
				
					|  |  |  |  |             throw new SnailmallException("参数不正确"); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         // 首先对传入的商品ID参数进行校验,如果参数为null,说明传入的参数不合法,抛出SnailmallException异常,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 这个异常可能会在更上层(如控制层)被捕获并处理,最终给前端返回相应的错误提示信息,告知用户参数不正确,无法进行商品详情查询操作。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         //1-在售 2-下架 3-删除
 | 
			
		
	
		
			
				
					|  |  |  |  |         Product product = productMapper.selectByPrimaryKey(productId); | 
			
		
	
		
			
				
					|  |  |  |  |         if(product == null){ | 
			
		
	
		
			
				
					|  |  |  |  |             return ServerResponse.createByErrorMessage("商品不存在"); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         // 通过ProductMapper的selectByPrimaryKey方法,根据传入的商品ID从数据库中查询对应的商品记录,如果查询结果为null,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 表示数据库中不存在该ID对应的商品,此时返回一个包含错误信息的ServerResponse对象,告知前端商品不存在,调用者(如后台管理系统前端)可以根据返回结果进行相应的提示展示。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         ProductDetailVo productDetailVo = assembleProductDetailVo(product); | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果查询到了商品记录,调用assembleProductDetailVo方法,将查询到的Product实体对象转换为ProductDetailVo视图对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // ProductDetailVo对象包含了更丰富、更适合前端展示的商品详细属性信息,用于后续返回给前端展示商品详情。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         return ServerResponse.createBySuccess(productDetailVo); | 
			
		
	
		
			
				
					|  |  |  |  |         // 最后,将包含转换好的ProductDetailVo视图对象的ServerResponse对象以成功状态返回,这个响应对象会被传递到前端(如后台管理系统的对应界面),
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 前端可以从ServerResponse中获取到表示成功的状态码以及ProductDetailVo对象中的详细商品信息,进而在页面上进行商品详情的展示,完成整个查看商品详情的业务操作流程,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 向用户提供了期望的商品详细信息展示功能,同时通过前面的参数校验和商品存在性判断等操作,保证了整个流程的健壮性和对各种情况的合理处理。
 | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @Override | 
			
		
	
		
			
				
					|  |  |  |  |     public ServerResponse<String> set_sale_status(Integer productId, Integer status) { | 
			
		
	
		
			
				
					|  |  |  |  |         //1.校验参数
 | 
			
		
	
		
			
				
					|  |  |  |  |         if(productId == null || status == null){ | 
			
		
	
		
			
				
					|  |  |  |  |         // 1.校验参数
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (productId == null || status == null) { | 
			
		
	
		
			
				
					|  |  |  |  |             return ServerResponse.createByErrorMessage("参数不正确"); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         //2.更新状态
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 首先对传入的参数进行合法性校验,商品ID(productId)和要设置的销售状态(status)在这个业务操作中都是必不可少的。
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果其中任何一个参数为null,说明传入的参数不符合要求,无法进行后续的商品销售状态更新操作,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 此时直接返回一个包含错误提示信息(“参数不正确”)的ServerResponse对象,告知调用者(可能是前端页面或者其他调用该服务的模块)参数存在问题,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 这样可以保证业务操作在正确的参数输入前提下进行,避免因参数缺失或错误导致的异常情况,提高系统的健壮性。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 2.更新状态
 | 
			
		
	
		
			
				
					|  |  |  |  |         Product product = new Product(); | 
			
		
	
		
			
				
					|  |  |  |  |         product.setId(productId); | 
			
		
	
		
			
				
					|  |  |  |  |         product.setStatus(status); | 
			
		
	
		
			
				
					|  |  |  |  |         //3.删除该商品缓存
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 创建一个新的Product对象,并通过Setter方法设置其ID和状态属性。这里的目的是构建一个只包含要更新的关键信息(商品ID和新的销售状态)的商品对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 用于后续传递给数据持久层(通过productMapper)进行数据库更新操作,采用这种方式可以灵活地指定要更新的字段,而不需要获取整个商品对象的所有属性再去更新,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 尤其在只需要更新部分字段(这里就是状态字段)的场景下,更加高效且符合业务逻辑,减少不必要的数据操作。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 3.删除该商品缓存
 | 
			
		
	
		
			
				
					|  |  |  |  |         commonCacheUtil.delKey(Constants.PRODUCT_TOKEN_PREFIX); | 
			
		
	
		
			
				
					|  |  |  |  |         // 在更新商品销售状态之前,先调用commonCacheUtil的delKey方法删除与商品相关的缓存。
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 原因是商品状态发生了改变,之前缓存中的商品信息可能已经不符合最新情况了,为了避免后续读取缓存时获取到旧的、不准确的商品状态数据,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 需要先将对应的缓存数据删除,使得下次获取该商品信息时能够从数据库重新加载最新的数据并更新缓存,保证数据的一致性和准确性。
 | 
			
		
	
		
			
				
					|  |  |  |  |         // Constants.PRODUCT_TOKEN_PREFIX应该是一个定义好的常量字符串,用于标识商品相关缓存的键值的前缀部分,通过这个前缀可以定位到所有与商品相关的缓存项,进行统一的删除操作。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         int rowCount = productMapper.updateByPrimaryKeySelective(product); | 
			
		
	
		
			
				
					|  |  |  |  |         if(rowCount > 0){ | 
			
		
	
		
			
				
					|  |  |  |  |             commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX+productId,JsonUtil.obj2String(product),Constants.PRODUCT_EXPIRE_TIME); | 
			
		
	
		
			
				
					|  |  |  |  |         // 通过productMapper(数据持久层接口,通常基于MyBatis等框架实现)的updateByPrimaryKeySelective方法,将构建好的包含新状态信息的Product对象传递进去,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 执行根据商品主键(这里就是productId)更新商品记录中指定字段(这里就是状态字段,因为使用的是updateByPrimaryKeySelective方法,只会更新传入对象中非空的字段)的操作,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 该方法会返回受影响的行数(即实际更新的商品记录行数),如果返回值大于0,表示数据库中对应的商品记录的状态字段已成功更新,否则表示更新操作未成功执行。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (rowCount > 0) { | 
			
		
	
		
			
				
					|  |  |  |  |             commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX + productId, JsonUtil.obj2String(product), Constants.PRODUCT_EXPIRE_TIME); | 
			
		
	
		
			
				
					|  |  |  |  |             return ServerResponse.createBySuccessMessage("更新产品状态成功"); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果受影响的行数大于0,说明商品状态在数据库中更新成功了,接下来需要重新将更新后的商品信息缓存起来,方便后续快速获取。
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 调用commonCacheUtil的cacheNxExpire方法,以商品ID(productId)拼接上之前定义的商品缓存键值前缀(Constants.PRODUCT_TOKEN_PREFIX)作为缓存的键,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 将更新后的商品对象转换为字符串(通过JsonUtil的obj2String方法,可能是将Java对象序列化为JSON字符串形式,方便存储在缓存中)作为缓存的值,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 并设置缓存的过期时间为Constants.PRODUCT_EXPIRE_TIME(应该是一个预定义的表示缓存有效时长的常量),确保缓存数据在一定时间后会自动失效,避免缓存数据长期占用内存且过时的问题。
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 最后返回一个包含成功提示信息(“更新产品状态成功”)的ServerResponse对象,表示商品销售状态更新操作成功完成,调用者(如前端页面)可以根据这个响应进行相应的提示展示等操作。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         return ServerResponse.createByErrorMessage("更新产品状态失败"); | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果数据库更新操作中受影响的行数不大于0,即更新商品状态失败了,返回一个包含错误提示信息(“更新产品状态失败”)的ServerResponse对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 告知调用者商品销售状态更新操作没有成功,方便调用者(比如前端页面可以给用户展示相应的提示,告知用户状态更新失败的情况)进行相应的处理,保证业务流程的完整性以及对操作结果的合理反馈。
 | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @Override | 
			
		
	
		
			
				
					|  |  |  |  |     public ServerResponse<String> saveOrUpdateProduct(Product product) { | 
			
		
	
		
			
				
					|  |  |  |  |         //1.校验参数
 | 
			
		
	
		
			
				
					|  |  |  |  |         if(product == null || product.getCategoryId()==null){ | 
			
		
	
		
			
				
					|  |  |  |  |         // 1.校验参数
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (product == null || product.getCategoryId() == null) { | 
			
		
	
		
			
				
					|  |  |  |  |             return ServerResponse.createByErrorMessage("参数不正确"); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         //2.设置一下主图,主图为子图的第一个图
 | 
			
		
	
		
			
				
					|  |  |  |  |         if(StringUtils.isNotBlank(product.getSubImages())){ | 
			
		
	
		
			
				
					|  |  |  |  |         // 首先进行参数的合法性校验。在这里,传入的Product对象以及其中的商品分类ID(categoryId)是关键参数,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果Product对象为null,说明没有接收到有效的商品信息,或者商品分类ID为null,意味着缺少必要的商品分类关联信息,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 这两种情况都不符合业务逻辑要求,无法进行后续的保存或更新操作,所以直接返回一个包含错误提示信息(“参数不正确”)的ServerResponse对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 告知调用者(可能是前端页面或者其他调用该服务的模块)参数存在问题,以此保证业务操作在正确的参数输入前提下进行,避免因参数缺失或错误导致的异常情况,增强系统的健壮性。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 2.设置一下主图,主图为子图的第一个图
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (StringUtils.isNotBlank(product.getSubImages())) { | 
			
		
	
		
			
				
					|  |  |  |  |             String[] subImages = product.getSubImages().split(","); | 
			
		
	
		
			
				
					|  |  |  |  |             if(subImages.length > 0){ | 
			
		
	
		
			
				
					|  |  |  |  |             if (subImages.length > 0) { | 
			
		
	
		
			
				
					|  |  |  |  |                 product.setMainImage(subImages[0]); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         //3.看前端传过来的产品id是否存在,存在则为更新,否则为新增
 | 
			
		
	
		
			
				
					|  |  |  |  |         if(product.getId() != null){ | 
			
		
	
		
			
				
					|  |  |  |  |             //删除该商品缓存
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 这段代码的目的是处理商品主图的设置逻辑。如果传入的商品对象中的子图信息(subImages)不为空字符串,说明存在子图相关信息,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 首先通过逗号将子图信息字符串分割为字符串数组(假设子图信息是以逗号分隔的多个图片路径等情况),然后判断数组长度是否大于0,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果大于0,表示存在至少一个子图,按照业务规则,将第一个子图作为商品的主图,通过设置商品对象的主图属性(setMainImage方法)来完成主图的赋值操作,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 这样可以保证商品在展示等场景下有合理的主图信息,同时体现了一种常见的业务逻辑处理方式,即根据已有的子图来确定主图。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 3.看前端传过来的产品id是否存在,存在则为更新,否则为新增
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (product.getId()!= null) { | 
			
		
	
		
			
				
					|  |  |  |  |             // 删除该商品缓存
 | 
			
		
	
		
			
				
					|  |  |  |  |             commonCacheUtil.delKey(Constants.PRODUCT_TOKEN_PREFIX); | 
			
		
	
		
			
				
					|  |  |  |  |             int rowCount = productMapper.updateByPrimaryKeySelective(product); | 
			
		
	
		
			
				
					|  |  |  |  |             if(rowCount > 0){ | 
			
		
	
		
			
				
					|  |  |  |  |                 commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX+product.getId(),JsonUtil.obj2String(product),Constants.PRODUCT_EXPIRE_TIME); | 
			
		
	
		
			
				
					|  |  |  |  |             if (rowCount > 0) { | 
			
		
	
		
			
				
					|  |  |  |  |                 commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX + product.getId(), JsonUtil.obj2String(product), Constants.PRODUCT_EXPIRE_TIME); | 
			
		
	
		
			
				
					|  |  |  |  |                 return ServerResponse.createBySuccessMessage("更新产品成功"); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             return ServerResponse.createByErrorMessage("更新产品失败"); | 
			
		
	
		
			
				
					|  |  |  |  |         }else { | 
			
		
	
		
			
				
					|  |  |  |  |             //新增
 | 
			
		
	
		
			
				
					|  |  |  |  |         } else { | 
			
		
	
		
			
				
					|  |  |  |  |             // 新增
 | 
			
		
	
		
			
				
					|  |  |  |  |             product.setCreateTime(new Date());//这两句可能多余,因为xml中已经保证了,就先放这里
 | 
			
		
	
		
			
				
					|  |  |  |  |             product.setUpdateTime(new Date()); | 
			
		
	
		
			
				
					|  |  |  |  |             int rowCount = productMapper.insert(product); | 
			
		
	
		
			
				
					|  |  |  |  |             if(rowCount > 0){ | 
			
		
	
		
			
				
					|  |  |  |  |                 commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX+product.getId(),JsonUtil.obj2String(product),Constants.PRODUCT_EXPIRE_TIME); | 
			
		
	
		
			
				
					|  |  |  |  |             if (rowCount > 0) { | 
			
		
	
		
			
				
					|  |  |  |  |                 commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX + product.getId(), JsonUtil.obj2String(product), Constants.PRODUCT_EXPIRE_TIME); | 
			
		
	
		
			
				
					|  |  |  |  |                 return ServerResponse.createBySuccessMessage("新增产品成功"); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             return ServerResponse.createByErrorMessage("新增产品失败"); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         // 这部分代码通过判断传入商品对象的ID是否存在来区分是执行更新操作还是新增操作:
 | 
			
		
	
		
			
				
					|  |  |  |  |         // - 如果商品ID(product.getId())不为null,说明前端传递过来的是已存在商品的相关信息,要执行更新操作。
 | 
			
		
	
		
			
				
					|  |  |  |  |         //   - 首先调用commonCacheUtil的delKey方法删除该商品对应的缓存,原因是商品信息即将被更新,之前缓存中的商品数据已经过时,
 | 
			
		
	
		
			
				
					|  |  |  |  |         //     为了保证后续获取商品信息时能获取到最新的数据,需要先清除旧的缓存,Constants.PRODUCT_TOKEN_PREFIX是用于标识商品缓存键的前缀常量,通过它可以定位并删除对应的缓存项。
 | 
			
		
	
		
			
				
					|  |  |  |  |         //   - 接着通过productMapper的updateByPrimaryKeySelective方法,将传入的商品对象传递进去执行更新操作,该方法会根据商品的主键(即商品ID)更新数据库中对应的商品记录,
 | 
			
		
	
		
			
				
					|  |  |  |  |         //     并且只会更新传入对象中非空的字段(这种方式更灵活,可以只更新部分需要修改的字段),然后获取实际更新的行数(rowCount)。
 | 
			
		
	
		
			
				
					|  |  |  |  |         //   - 如果rowCount大于0,表示数据库中商品记录更新成功,此时需要将更新后的商品信息重新缓存起来,方便后续快速获取。
 | 
			
		
	
		
			
				
					|  |  |  |  |         //     通过调用commonCacheUtil的cacheNxExpire方法,以商品ID拼接上缓存键前缀作为缓存的键,将商品对象转换为字符串(通过JsonUtil的obj2String方法,可能是序列化为JSON字符串方便存储在缓存中)作为缓存的值,
 | 
			
		
	
		
			
				
					|  |  |  |  |         //     并设置缓存的过期时间为Constants.PRODUCT_EXPIRE_TIME,最后返回一个包含成功提示信息(“更新产品成功”)的ServerResponse对象,表示商品更新操作成功完成,调用者(如前端页面)可据此进行相应提示展示等操作。
 | 
			
		
	
		
			
				
					|  |  |  |  |         //   - 如果rowCount不大于0,说明商品更新操作失败了,返回一个包含错误提示信息(“更新产品失败”)的ServerResponse对象,告知调用者更新操作未成功,方便前端进行相应提示告知用户情况。
 | 
			
		
	
		
			
				
					|  |  |  |  |         // - 如果商品ID(product.getId())为null,说明前端传递的是一个新的商品信息,要执行新增操作。
 | 
			
		
	
		
			
				
					|  |  |  |  |         //   - 先设置商品的创建时间(createTime)和更新时间(updateTime)为当前时间(虽然代码中备注了这两句可能多余,因为在对应的xml配置中可能已经做了相关时间的默认设置,但这里先保留设置操作)。
 | 
			
		
	
		
			
				
					|  |  |  |  |         //   - 然后通过productMapper的insert方法将商品对象插入到数据库中,获取实际插入的行数(rowCount)。
 | 
			
		
	
		
			
				
					|  |  |  |  |         //   - 如果rowCount大于0,表示商品新增操作成功,同样需要将新插入的商品信息缓存起来,操作方式与更新成功后的缓存设置类似,通过commonCacheUtil的cacheNxExpire方法进行缓存设置,
 | 
			
		
	
		
			
				
					|  |  |  |  |         //     最后返回一个包含成功提示信息(“新增产品成功”)的ServerResponse对象,告知调用者商品新增操作已成功,方便前端进行相应展示等操作。
 | 
			
		
	
		
			
				
					|  |  |  |  |         //   - 如果rowCount不大于0,说明商品新增操作失败了,返回一个包含错误提示信息(“新增产品失败”)的ServerResponse对象,告知调用者新增操作未成功,以便前端提示用户相应情况。
 | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @Override | 
			
		
	
		
			
				
					|  |  |  |  |     public ServerResponse<ProductDetailVo> getPortalProductDetail(Integer productId) { | 
			
		
	
		
			
				
					|  |  |  |  |         if(productId == null){ | 
			
		
	
		
			
				
					|  |  |  |  |         // 首先进行参数校验,判断传入的商品ID是否为null。在前台门户获取商品详情的业务场景中,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 商品ID是定位特定商品的关键依据,如果传入的商品ID为null,就无法明确要查询详情的具体商品,不符合正常的业务逻辑,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 所以直接返回一个包含错误提示信息(“参数不正确”)的ServerResponse对象,告知调用者(可能是前台页面或者其他调用该服务的模块)参数存在问题,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 保证业务操作在参数合法有效的前提下进行,避免因错误参数引发后续不必要的操作和异常情况,增强系统的健壮性。
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (productId == null) { | 
			
		
	
		
			
				
					|  |  |  |  |             return ServerResponse.createByErrorMessage("参数不正确"); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         Product product = productMapper.selectByPrimaryKey(productId); | 
			
		
	
		
			
				
					|  |  |  |  |         if(product == null){ | 
			
		
	
		
			
				
					|  |  |  |  |         // 通过ProductMapper接口的selectByPrimaryKey方法,依据传入的商品ID从数据库中精确查询对应的商品记录。
 | 
			
		
	
		
			
				
					|  |  |  |  |         // ProductMapper通常是基于数据持久层框架(如MyBatis)生成的接口,其定义了与数据库中商品表交互的相关方法,selectByPrimaryKey方法就是按照主键(即商品ID)来查找一条商品记录,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 这一步是获取商品详情的核心数据库查询操作,只有先从数据库获取到商品的原始数据,才能进行后续的判断以及数据转换等工作,为最终返回适合前台展示的商品详情信息做准备。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (product == null) { | 
			
		
	
		
			
				
					|  |  |  |  |             return ServerResponse.createByErrorMessage("商品不存在"); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         if(product.getStatus() != Constants.Product.PRODUCT_ON){ | 
			
		
	
		
			
				
					|  |  |  |  |         // 对从数据库查询到的商品记录进行判断,如果返回的product对象为null,说明在数据库中并没有找到与传入的productId对应的商品,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 这种情况下,直接返回一个包含“商品不存在”错误提示信息的ServerResponse对象,告知调用者(比如前台页面)当前要查看详情的商品不存在,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 使得前台可以根据这个响应结果向用户展示相应的提示内容,保证业务流程在面对商品不存在这种情况时能合理反馈,维持系统的完整性。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (product.getStatus()!= Constants.Product.PRODUCT_ON) { | 
			
		
	
		
			
				
					|  |  |  |  |             return ServerResponse.createByErrorMessage("产品已下架或删除"); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         // 当查询到商品记录(即product对象不为null)后,进一步检查商品的状态。Constants.Product.PRODUCT_ON应该是一个预定义的表示商品上架状态的常量,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果商品的状态不等于这个上架状态常量,意味着商品可能已经下架或者被删除了,此时不符合在前台门户展示商品详情的业务需求,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 所以返回一个包含“产品已下架或删除”错误提示信息的ServerResponse对象,告知调用者(例如前台页面)该商品不能展示详情,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 让前台能够相应地提示用户,避免展示不可用的商品信息给用户,确保前台展示的商品信息都是有效的、可供查看的。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         ProductDetailVo productDetailVo = assembleProductDetailVo(product); | 
			
		
	
		
			
				
					|  |  |  |  |         // 当商品存在(product不为null)且处于上架状态(product.getStatus()符合要求)时,调用assembleProductDetailVo方法对查询到的Product实体对象进行处理,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // assembleProductDetailVo方法的作用是将从数据库获取的Product实体类对象转换为ProductDetailVo视图对象。ProductDetailVo作为视图对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 它会提取、封装那些适合在前台门户展示给用户查看商品详情的属性信息,可能会对Product实体类中的一些属性进行筛选、格式转换或者添加一些方便前台展示的辅助信息等操作,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 例如对日期类型的属性进行格式化处理,使其更符合前台展示的格式要求,以此来优化展示给用户的商品详情内容,提升用户体验。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         return ServerResponse.createBySuccess(productDetailVo); | 
			
		
	
		
			
				
					|  |  |  |  |         // 最后,将包含转换好的ProductDetailVo视图对象的ServerResponse对象以成功状态返回,这个ServerResponse对象会传递到前台(比如前台门户的对应页面),
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 前台可以从中获取到表示成功的状态码以及ProductDetailVo对象里详细的商品信息,进而在页面上展示商品详情给用户查看,完成整个前台门户获取商品详情的业务操作流程,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 为用户提供了准确且符合业务要求的商品详情展示功能,同时通过前面的参数校验、商品存在性判断以及状态检查等操作,保障了整个流程的健壮性和对各种情况的合理处理。
 | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @Override | 
			
		
	
		
			
				
					|  |  |  |  |     public ServerResponse<PageInfo> portalList(String keyword, Integer categoryId, String orderBy, int pageNum, int pageSize) { | 
			
		
	
		
			
				
					|  |  |  |  |         //准备盛放categoryIds
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 准备盛放categoryIds
 | 
			
		
	
		
			
				
					|  |  |  |  |         List<Integer> categoryIdList = Lists.newArrayList(); | 
			
		
	
		
			
				
					|  |  |  |  |         //如果categoryId不为空
 | 
			
		
	
		
			
				
					|  |  |  |  |         if(categoryId != null){ | 
			
		
	
		
			
				
					|  |  |  |  |             //对于这里,直接强转出错了,所以我就序列化处理了一下
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 创建一个空的整数列表categoryIdList,用于后续存放商品分类ID相关的数据,它的作用是在根据分类筛选商品时,存储符合条件的多个分类ID,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 比如当查询某个父分类下的所有商品(包括子分类商品)时,这个列表会存储该父分类及其所有子分类的ID,方便后续进行基于分类的商品查询操作。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果categoryId不为空
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (categoryId!= null) { | 
			
		
	
		
			
				
					|  |  |  |  |             // 对于这里,直接强转出错了,所以我就序列化处理了一下
 | 
			
		
	
		
			
				
					|  |  |  |  |             ServerResponse response = categoryClient.getCategoryDetail(categoryId); | 
			
		
	
		
			
				
					|  |  |  |  |             // 通过categoryClient(通常是基于Feign等远程调用框架实现的用于与商品分类服务进行交互的客户端)的getCategoryDetail方法,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 根据传入的categoryId尝试获取对应的商品分类详细信息,返回的是一个ServerResponse对象,它包装了操作结果(成功与否以及相关的数据等信息)。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |             Object object = response.getData(); | 
			
		
	
		
			
				
					|  |  |  |  |             String objStr = JsonUtil.obj2String(object); | 
			
		
	
		
			
				
					|  |  |  |  |             Category category = JsonUtil.Str2Obj(objStr,Category.class); | 
			
		
	
		
			
				
					|  |  |  |  |             if(category == null && StringUtils.isBlank(keyword)){ | 
			
		
	
		
			
				
					|  |  |  |  |                 ////直接返回空
 | 
			
		
	
		
			
				
					|  |  |  |  |                 PageHelper.startPage(pageNum,pageSize); | 
			
		
	
		
			
				
					|  |  |  |  |             Category category = JsonUtil.Str2Obj(objStr, Category.class); | 
			
		
	
		
			
				
					|  |  |  |  |             // 从ServerResponse对象中获取返回的数据部分(getData方法),这个数据可能是一个对象,先将其转换为字符串(通过JsonUtil的obj2String方法,可能是序列化为JSON字符串格式,方便后续处理),
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 然后再将这个字符串反序列化为Category类型的对象(通过JsonUtil的Str2Obj方法,用于将JSON字符串转换回Java对象),这样就能获取到具体的商品分类对象了,方便后续判断和使用。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |             if (category == null && StringUtils.isBlank(keyword)) { | 
			
		
	
		
			
				
					|  |  |  |  |                 // 如果根据categoryId获取到的商品分类对象为null,并且传入的关键词(keyword)也是空字符串,意味着既没有有效的分类信息,也没有关键词信息来筛选商品,
 | 
			
		
	
		
			
				
					|  |  |  |  |                 // 在这种情况下,直接返回一个空的商品列表分页信息。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |                 PageHelper.startPage(pageNum, pageSize); | 
			
		
	
		
			
				
					|  |  |  |  |                 List<ProductListVo> productListVoList = Lists.newArrayList(); | 
			
		
	
		
			
				
					|  |  |  |  |                 PageInfo pageInfo = new PageInfo(productListVoList); | 
			
		
	
		
			
				
					|  |  |  |  |                 return ServerResponse.createBySuccess(pageInfo); | 
			
		
	
		
			
				
					|  |  |  |  |                 // 首先使用PageHelper启动分页功能,传入当前页码(pageNum)和每页显示数量(pageSize),虽然此时没有实际的商品数据,但分页相关的设置还是需要进行的,
 | 
			
		
	
		
			
				
					|  |  |  |  |                 // 然后创建一个空的ProductListVo类型的列表(ProductListVo是用于前台展示的商品列表视图对象类型,包含部分商品属性),将其封装到PageInfo对象中,
 | 
			
		
	
		
			
				
					|  |  |  |  |                 // 最后通过ServerResponse的createBySuccess方法将包含空列表的PageInfo对象包装起来,以成功状态返回,表示没有符合条件的商品数据,调用者(如前台页面)可以根据这个响应进行相应的展示,比如显示一个空的商品列表页面。
 | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             //说明category还是存在的
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 说明category还是存在的
 | 
			
		
	
		
			
				
					|  |  |  |  |             categoryIdList = (List<Integer>) categoryClient.getDeepCategory(categoryId).getData(); | 
			
		
	
		
			
				
					|  |  |  |  |             // 如果获取到的商品分类对象不为null,说明categoryId对应的分类是存在的,此时调用categoryClient的getDeepCategory方法,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 这个方法可能是用于获取指定分类及其所有子分类的ID列表(根据业务逻辑推测),从返回的ServerResponse对象中获取数据部分,并强转成List<Integer>类型,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 赋值给categoryIdList,后续就可以基于这个包含多个分类ID的列表去数据库中查询属于这些分类的商品了。
 | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         //如果keyword不为空
 | 
			
		
	
		
			
				
					|  |  |  |  |         if(StringUtils.isNotBlank(keyword)){ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果keyword不为空
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (StringUtils.isNotBlank(keyword)) { | 
			
		
	
		
			
				
					|  |  |  |  |             keyword = new StringBuilder().append("%").append(keyword).append("%").toString(); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         //如果orderBy不为空
 | 
			
		
	
		
			
				
					|  |  |  |  |         if(StringUtils.isNotBlank(orderBy)){ | 
			
		
	
		
			
				
					|  |  |  |  |             if(Constants.ProductListOrderBy.PRICE_ASC_DESC.contains(orderBy)){ | 
			
		
	
		
			
				
					|  |  |  |  |         // 当传入的关键词(keyword)不为空字符串时,对其进行模糊查询格式的处理,通过在关键词前后添加 "%" 符号,将其转换为 LIKE 语句中可用的模糊查询格式,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 例如,原本传入的关键词为 "手机",处理后变为 "%手机%",这样在数据库查询时就能查找名称中包含 "手机" 关键字的所有商品记录了,方便实现根据关键词模糊搜索商品的功能。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果orderBy不为空
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (StringUtils.isNotBlank(orderBy)) { | 
			
		
	
		
			
				
					|  |  |  |  |             if (Constants.ProductListOrderBy.PRICE_ASC_DESC.contains(orderBy)) { | 
			
		
	
		
			
				
					|  |  |  |  |                 String[] orderByArray = orderBy.split("_"); | 
			
		
	
		
			
				
					|  |  |  |  |                 //特定的格式
 | 
			
		
	
		
			
				
					|  |  |  |  |                 PageHelper.orderBy(orderByArray[0]+" "+orderByArray[1]); | 
			
		
	
		
			
				
					|  |  |  |  |                 // 特定的格式
 | 
			
		
	
		
			
				
					|  |  |  |  |                 PageHelper.orderBy(orderByArray[0] + " " + orderByArray[1]); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         PageHelper.startPage(pageNum,pageSize); | 
			
		
	
		
			
				
					|  |  |  |  |         //模糊查询
 | 
			
		
	
		
			
				
					|  |  |  |  |         List<Product> productList = productMapper.selectByNameAndCategoryIds(StringUtils.isBlank(keyword)?null:keyword, | 
			
		
	
		
			
				
					|  |  |  |  |                                                                              categoryIdList.size()==0?null:categoryIdList); | 
			
		
	
		
			
				
					|  |  |  |  |         //封装返回对象
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 当传入的排序规则(orderBy)字符串不为空时,先进行合法性校验,判断它是否在预定义的合法排序规则集合(Constants.ProductListOrderBy.PRICE_ASC_DESC,应该是定义了允许的排序规则内容的常量集合)中,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果在集合中,说明是合法的排序规则,将orderBy字符串按照下划线("_")进行分割,得到一个包含排序字段和排序方式(如 "price" 和 "asc" 或者 "price" 和 "desc" 等情况)的字符串数组,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 然后通过PageHelper的orderBy方法,按照特定格式(排序字段 + " " + 排序方式)传入参数,设置后续数据库查询结果的排序规则,例如按照价格升序或者降序排列商品列表等,以满足前台展示商品列表时不同的排序需求。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         PageHelper.startPage(pageNum, pageSize); | 
			
		
	
		
			
				
					|  |  |  |  |         // 使用PageHelper启动分页功能,传入当前页码(pageNum)和每页显示数量(pageSize)参数,这样后续通过ProductMapper进行的商品查询操作将会按照设定的分页规则返回对应页的商品数据,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 便于在前台分页展示商品列表,提升用户查看商品时的体验,避免一次性返回大量数据影响性能和展示效果。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 模糊查询
 | 
			
		
	
		
			
				
					|  |  |  |  |         List<Product> productList = productMapper.selectByNameAndCategoryIds(StringUtils.isBlank(keyword)? null : keyword, | 
			
		
	
		
			
				
					|  |  |  |  |                 categoryIdList.size() == 0? null : categoryIdList); | 
			
		
	
		
			
				
					|  |  |  |  |         // 通过ProductMapper的selectByNameAndCategoryIds方法进行模糊查询,根据处理后的关键词(如果keyword为空则传入null)以及分类ID列表(如果categoryIdList为空则传入null),
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 从数据库中查询符合条件的商品记录列表,这个方法内部会根据传入的参数构建相应的SQL查询语句(基于MyBatis的映射机制),执行数据库查询操作,获取满足条件的商品记录,实现根据关键词和分类筛选商品的功能。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 封装返回对象
 | 
			
		
	
		
			
				
					|  |  |  |  |         List<ProductListVo> productListVoList = Lists.newArrayList(); | 
			
		
	
		
			
				
					|  |  |  |  |         for(Product product : productList){ | 
			
		
	
		
			
				
					|  |  |  |  |         for (Product product : productList) { | 
			
		
	
		
			
				
					|  |  |  |  |             ProductListVo productListVo = assembleProductListVo(product); | 
			
		
	
		
			
				
					|  |  |  |  |             productListVoList.add(productListVo); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         //返回
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 遍历查询到的商品列表,调用assembleProductListVo方法将每个Product实体对象转换为ProductListVo视图对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // ProductListVo对象是专门为了前台展示而设计的,它可能只包含了部分商品属性(如商品名称、主图、价格等前台关心的关键信息),通过这样的转换可以减少返回给前台的数据量,同时保证展示的信息是符合前台展示需求的,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 将转换后的视图对象依次添加到productListVoList列表中,用于后续构建包含分页信息的返回结果。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 返回
 | 
			
		
	
		
			
				
					|  |  |  |  |         PageInfo pageInfo = new PageInfo(productList); | 
			
		
	
		
			
				
					|  |  |  |  |         pageInfo.setList(productListVoList); | 
			
		
	
		
			
				
					|  |  |  |  |         return ServerResponse.createBySuccess(pageInfo); | 
			
		
	
		
			
				
					|  |  |  |  |         // 创建PageInfo对象来封装分页相关信息(如总记录数、总页数、当前页数据列表等),它会根据传入的原始商品列表(productList)自动填充这些信息,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 然后将包含商品列表的PageInfo对象的原始商品列表替换为转换后的视图对象列表(productListVoList),确保返回给前台的分页信息中包含的是经过处理、符合展示需求的商品数据,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 最后将包含分页商品列表信息的PageInfo对象包装在ServerResponse对象中,通过createBySuccess方法以成功状态返回,调用者(如前台页面)可以接收到这个响应,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 从中获取分页的商品列表信息并展示在页面上,完成整个前台门户获取商品分页列表的业务操作流程,为用户提供了可根据关键词、分类、排序规则分页查看商品列表的功能。
 | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @Override | 
			
		
	
		
			
				
					|  |  |  |  |     public ServerResponse queryProduct(Integer productId) { | 
			
		
	
		
			
				
					|  |  |  |  |         //1.校验参数
 | 
			
		
	
		
			
				
					|  |  |  |  |         if(productId == null){ | 
			
		
	
		
			
				
					|  |  |  |  |         // 1.校验参数
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (productId == null) { | 
			
		
	
		
			
				
					|  |  |  |  |             return ServerResponse.createByErrorMessage("参数错误"); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         //2.去redis中查询,没有则把商品重新添加进redis中
 | 
			
		
	
		
			
				
					|  |  |  |  |         String redisProductStr = commonCacheUtil.getCacheValue(Constants.PRODUCT_TOKEN_PREFIX+productId); | 
			
		
	
		
			
				
					|  |  |  |  |         if (redisProductStr == null){ | 
			
		
	
		
			
				
					|  |  |  |  |         // 首先进行参数校验,判断传入的商品ID(productId)是否为null。在查询商品的业务场景中,商品ID是定位特定商品的关键依据,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果传入的商品ID为null,就无法明确要查询的具体商品,不符合正常的业务逻辑,所以直接返回一个包含“参数错误”提示信息的ServerResponse对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 告知调用者(可能是其他业务模块或者前端页面等)参数存在问题,以此保证业务操作在正确的参数输入前提下进行,避免因错误参数引发后续不必要的操作和异常情况,增强系统的健壮性。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 2.去redis中查询,没有则把商品重新添加进redis中
 | 
			
		
	
		
			
				
					|  |  |  |  |         String redisProductStr = commonCacheUtil.getCacheValue(Constants.PRODUCT_TOKEN_PREFIX + productId); | 
			
		
	
		
			
				
					|  |  |  |  |         // 通过commonCacheUtil(这应该是一个用于操作缓存的工具类实例)的getCacheValue方法,尝试从Redis缓存中获取指定键(由Constants.PRODUCT_TOKEN_PREFIX与传入的商品ID拼接而成的字符串作为键)对应的缓存值,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 缓存值在这里预期是存储的商品相关信息(可能是经过序列化后的字符串形式),将获取到的缓存值赋值给redisProductStr变量,后续根据这个值来判断商品信息是否已存在于缓存中。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (redisProductStr == null) { | 
			
		
	
		
			
				
					|  |  |  |  |             Product product = productMapper.selectByPrimaryKey(productId); | 
			
		
	
		
			
				
					|  |  |  |  |             if(product == null){ | 
			
		
	
		
			
				
					|  |  |  |  |             if (product == null) { | 
			
		
	
		
			
				
					|  |  |  |  |                 return ServerResponse.createByErrorMessage("商品不存在"); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             if(product.getStatus() != Constants.Product.PRODUCT_ON){ | 
			
		
	
		
			
				
					|  |  |  |  |             // 如果从缓存中获取到的值为null,说明缓存中不存在该商品的信息,此时需要从数据库中查询商品信息。
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 通过ProductMapper的selectByPrimaryKey方法,依据传入的商品ID从数据库中精确查询对应的商品记录,这是获取商品原始数据的关键步骤,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 如果查询到的product对象为null,意味着数据库中也不存在该商品,直接返回一个包含“商品不存在”提示信息的ServerResponse对象,告知调用者商品不存在,方便调用者(如前端页面)进行相应的提示展示等操作。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |             if (product.getStatus()!= Constants.Product.PRODUCT_ON) { | 
			
		
	
		
			
				
					|  |  |  |  |                 return ServerResponse.createByErrorMessage("商品已经下架或者删除"); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX+productId,JsonUtil.obj2String(product),Constants.PRODUCT_EXPIRE_TIME); | 
			
		
	
		
			
				
					|  |  |  |  |             // 当从数据库中查询到商品记录(即product不为null)后,进一步检查商品的状态。Constants.Product.PRODUCT_ON应该是一个预定义的表示商品上架状态的常量,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 如果商品的状态不等于这个上架状态常量,意味着商品已经下架或者被删除了,此时不符合正常查询并返回可用商品信息的业务需求,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 所以返回一个包含“商品已经下架或者删除”提示信息的ServerResponse对象,告知调用者该商品不可用,让调用者(例如前端页面)能够相应地提示用户,避免返回无效的商品信息。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |             commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX + productId, JsonUtil.obj2String(product), Constants.PRODUCT_EXPIRE_TIME); | 
			
		
	
		
			
				
					|  |  |  |  |             // 当商品存在(product不为null)且处于上架状态(product.getStatus()符合要求)时,需要将从数据库获取到的商品信息缓存到Redis中,方便后续查询时能够直接从缓存获取,提高查询效率。
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 通过调用commonCacheUtil的cacheNxExpire方法,以商品ID拼接上缓存键前缀(Constants.PRODUCT_TOKEN_PREFIX)作为缓存的键,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 将商品对象转换为字符串(通过JsonUtil的obj2String方法,可能是将Java对象序列化为JSON字符串形式,方便存储在缓存中)作为缓存的值,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 并设置缓存的过期时间为Constants.PRODUCT_EXPIRE_TIME(应该是一个预定义的表示缓存有效时长的常量),确保缓存数据在一定时间后会自动失效,避免缓存数据长期占用内存且过时的问题。
 | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         //2.获取商品
 | 
			
		
	
		
			
				
					|  |  |  |  |         Product product = JsonUtil.Str2Obj(commonCacheUtil.getCacheValue(Constants.PRODUCT_TOKEN_PREFIX+productId),Product.class); | 
			
		
	
		
			
				
					|  |  |  |  |         // 2.获取商品
 | 
			
		
	
		
			
				
					|  |  |  |  |         Product product = JsonUtil.Str2Obj(commonCacheUtil.getCacheValue(Constants.PRODUCT_TOKEN_PREFIX + productId), Product.class); | 
			
		
	
		
			
				
					|  |  |  |  |         // 无论之前是从缓存中获取到了商品信息(即redisProductStr不为null的情况),还是刚刚将数据库中查询到的商品信息缓存后,这里都需要从缓存中获取商品信息并转换为Product对象。
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 通过JsonUtil的Str2Obj方法,将从缓存中获取到的缓存值(通过commonCacheUtil的getCacheValue方法再次获取,确保获取到最新的缓存内容)转换为Product类型的Java对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 这样就能得到适合后续业务处理或返回给调用者的商品对象形式了。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         return ServerResponse.createBySuccess(product); | 
			
		
	
		
			
				
					|  |  |  |  |         // 最后,将包含查询到的商品对象的ServerResponse对象以成功状态返回,这个ServerResponse对象会被传递给调用者(比如其他业务模块或者前端页面等),
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 调用者可以从中获取到表示成功的状态码以及商品对象中的详细商品信息,进而进行相应的业务处理或展示操作,完成整个查询商品的业务操作流程,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 实现了先从缓存查询商品信息,缓存不存在时从数据库获取并缓存,最终返回可用商品信息的功能,提高了商品查询的效率以及数据的可用性。
 | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     private ProductListVo assembleProductListVo(Product product) { | 
			
		
	
		
			
				
					|  |  |  |  |         // 创建一个新的ProductListVo对象,ProductListVo是专门设计用于在前端展示商品列表时使用的视图对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 它包含了部分商品属性,这些属性是经过筛选的,符合前端展示商品列表时通常所需要展示的关键信息,通过将Product实体对象转换为ProductListVo对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 可以减少传递给前端的数据量,同时保证前端获取到的是真正需要展示给用户查看的信息,提升性能以及用户体验。
 | 
			
		
	
		
			
				
					|  |  |  |  |         ProductListVo productListVo = new ProductListVo(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productListVo.setId(product.getId()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 将传入的Product实体对象的ID属性赋值给ProductListVo对象的ID属性,商品ID是唯一标识商品的关键信息,在前端展示商品列表时,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 通常需要展示每个商品对应的ID,方便后续进行一些与商品相关的操作(如查看详情、编辑等操作时可以通过ID来定位具体商品)。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productListVo.setSubtitle(product.getSubtitle()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 把Product实体对象中的副标题(subtitle)属性赋值给ProductListVo对象的副标题属性,副标题可以对商品进行进一步的补充说明,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 在商品列表展示中,能够帮助用户更详细地了解商品的一些特点或者额外信息,丰富展示内容。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productListVo.setMainImage(product.getMainImage()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 将Product实体对象的主图(mainImage)属性赋值给ProductListVo对象的主图属性,主图是商品在列表中最直观展示给用户的视觉元素,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 用户可以通过主图快速识别商品的大致外观等信息,所以在商品列表视图对象中需要包含主图信息,方便前端进行展示。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productListVo.setPrice(product.getPrice()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 把Product实体对象的价格(price)属性传递给ProductListVo对象的价格属性,价格是用户在浏览商品列表时非常关注的信息之一,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 展示价格能够让用户快速了解商品的价值情况,便于用户进行比较和筛选商品。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productListVo.setCategoryId(product.getCategoryId()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 将Product实体对象的商品分类ID(categoryId)属性赋值给ProductListVo对象的分类ID属性,商品分类信息有助于用户了解商品所属的类别,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 在一些有分类筛选或者导航功能的前端页面中,展示分类ID可以方便后续基于分类进行相关操作(如筛选同一分类下的其他商品等)。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productListVo.setName(product.getName()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 把Product实体对象的商品名称(name)属性赋值给ProductListVo对象的商品名称属性,商品名称是最基本且重要的展示信息,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 用户主要通过商品名称来识别和区分不同的商品,所以必须包含在用于前端展示的视图对象中。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productListVo.setStatus(product.getStatus()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 将Product实体对象的商品状态(status)属性传递给ProductListVo对象的商品状态属性,商品状态信息(例如是否上架等状态)可以让用户了解商品当前的可用性,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 方便用户知晓哪些商品是可以进行购买等操作的,哪些是不可用的。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productListVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix","http://image.snail.com/")); | 
			
		
	
		
			
				
					|  |  |  |  |         // 通过PropertiesUtil工具类的getProperty方法获取图片服务器的HTTP前缀地址(默认值为 "http://image.snail.com/"),
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 并将其赋值给ProductListVo对象的ImageHost属性。这个属性的作用是与商品的图片路径(如主图、子图等路径)相结合,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 构成完整的图片URL地址,方便前端能够正确地展示商品的图片,因为在存储图片路径时可能只是相对路径或者部分路径,需要加上这个前缀才能形成可访问的完整URL。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         return productListVo; | 
			
		
	
		
			
				
					|  |  |  |  |         // 最后返回组装好的ProductListVo对象,这个对象包含了适合前端展示商品列表的关键属性信息,可供后续在业务逻辑中返回给前端进行展示使用。
 | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     private ProductDetailVo assembleProductDetailVo(Product product){ | 
			
		
	
		
			
				
					|  |  |  |  |     private ProductDetailVo assembleProductDetailVo(Product product) { | 
			
		
	
		
			
				
					|  |  |  |  |         ProductDetailVo productDetailVo = new ProductDetailVo(); | 
			
		
	
		
			
				
					|  |  |  |  |         // 创建一个新的ProductDetailVo对象,ProductDetailVo是用于在前端展示商品详细信息时使用的视图对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 它相比ProductListVo包含了更丰富、更全面的商品属性信息,旨在为用户提供查看商品所有重要细节的功能,同样通过将Product实体对象转换为这个视图对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 可以对数据进行整理和格式转换,使其更符合前端展示详细信息的需求。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setId(product.getId()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 将传入的Product实体对象的ID属性赋值给ProductDetailVo对象的ID属性,商品ID用于唯一标识商品,在展示商品详细信息时,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 依然需要展示这个关键标识,方便用户在查看详情页面与其他相关页面(如列表页返回等操作)进行关联和定位该商品。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setSubtitle(product.getSubtitle()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 把Product实体对象中的副标题(subtitle)属性赋值给ProductDetailVo对象的副标题属性,副标题能进一步补充说明商品的特点等信息,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 在详细信息页面展示可以让用户更全面地了解商品情况。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setMainImage(product.getMainImage()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 将Product实体对象的主图(mainImage)属性赋值给ProductDetailVo对象的主图属性,主图在商品详情页面也是重要的展示元素,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 方便用户直观地看到商品外观,辅助用户了解商品。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setSubImages(product.getSubImages()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 把Product实体对象的子图(subImages)属性赋值给ProductDetailVo对象的子图属性,子图可以从更多角度展示商品的外观、细节等情况,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 在商品详情页面完整展示商品图片信息能够让用户更全面地了解商品的样子,所以需要包含子图信息在视图对象中。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setPrice(product.getPrice()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 把Product实体对象的价格(price)属性传递给ProductDetailVo对象的价格属性,价格是用户在查看商品详情时关注的重要信息之一,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 明确商品价格有助于用户决定是否购买该商品。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setCategoryId(product.getCategoryId()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 将Product实体对象的商品分类ID(categoryId)属性赋值给ProductDetailVo对象的分类ID属性,展示商品所属分类信息,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 能让用户了解商品在整个商品体系中的归类情况,同时方便用户通过分类导航查看同类的其他商品等操作。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setDetail(product.getDetail()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 把Product实体对象的详细描述(detail)属性赋值给ProductDetailVo对象的详细描述属性,详细描述通常包含了商品的各种详细规格、功能特点、使用说明等内容,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 在商品详情页面展示这些详细信息能够满足用户深入了解商品的需求。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setName(product.getName()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 把Product实体对象的商品名称(name)属性赋值给ProductDetailVo对象的商品名称属性,商品名称始终是重要的展示信息,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 用户通过名称来快速识别商品,在详情页面同样需要展示明确的商品名称。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setStatus(product.getStatus()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 将Product实体对象的商品状态(status)属性传递给ProductDetailVo对象的商品状态属性,展示商品当前的状态(如是否上架等),
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 让用户清楚该商品是否可进行购买等操作,提供必要的商品可用性信息。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setStock(product.getStock()); | 
			
		
	
		
			
				
					|  |  |  |  |         // 把Product实体对象的库存(stock)属性赋值给ProductDetailVo对象的库存属性,库存信息对于用户来说也是比较关注的内容,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 特别是在一些限量销售或者用户考虑购买数量的场景下,了解商品的库存情况有助于用户做出购买决策。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix","http://image.snail.com/")); | 
			
		
	
		
			
				
					|  |  |  |  |         //返回给前端还需要一下该商品所处品类的父品类id,所以需要去品类服务中去查询一下,这里就要用到Feign
 | 
			
		
	
		
			
				
					|  |  |  |  |         if(categoryClient.getCategoryDetail(product.getCategoryId()).isSuccess()){ | 
			
		
	
		
			
				
					|  |  |  |  |         // 通过PropertiesUtil工具类获取图片服务器的HTTP前缀地址(默认值为 "http://image.snail.com/"),并赋值给ProductDetailVo对象的ImageHost属性,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 作用与在ProductListVo中类似,是为了与商品的图片路径结合形成完整可访问的图片URL,确保前端能够正确展示商品的所有图片。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 返回给前端还需要一下该商品所处品类的父品类id,所以需要去品类服务中去查询一下,这里就要用到Feign
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (categoryClient.getCategoryDetail(product.getCategoryId()).isSuccess()) { | 
			
		
	
		
			
				
					|  |  |  |  |             ServerResponse response = categoryClient.getCategoryDetail(product.getCategoryId()); | 
			
		
	
		
			
				
					|  |  |  |  |             Object object = response.getData(); | 
			
		
	
		
			
				
					|  |  |  |  |             String objStr = JsonUtil.obj2String(object); | 
			
		
	
		
			
				
					|  |  |  |  |             Category category = (Category) JsonUtil.Str2Obj(objStr,Category.class); | 
			
		
	
		
			
				
					|  |  |  |  |             Category category = (Category) JsonUtil.Str2Obj(objStr, Category.class); | 
			
		
	
		
			
				
					|  |  |  |  |             productDetailVo.setParentCategoryId(category.getParentId()); | 
			
		
	
		
			
				
					|  |  |  |  |         }else { | 
			
		
	
		
			
				
					|  |  |  |  |         } else { | 
			
		
	
		
			
				
					|  |  |  |  |             productDetailVo.setParentCategoryId(0); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         // 通过categoryClient(通常是基于Feign框架实现的用于调用商品分类服务的客户端)的getCategoryDetail方法,根据商品的分类ID去查询对应的商品分类详细信息,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果查询操作成功(通过isSuccess方法判断),从返回的ServerResponse对象中获取数据部分,先将其转换为字符串(通过JsonUtil的obj2String方法),
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 再将字符串反序列化为Category类型的对象(通过JsonUtil的Str2Obj方法),然后获取该分类对象的父品类ID,并赋值给ProductDetailVo对象的ParentCategoryId属性,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 这样在前端展示商品详情时就能展示商品所属品类的父品类信息了,方便用户了解商品在分类层级中的位置等情况。
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 如果查询失败,将ProductDetailVo对象的ParentCategoryId属性设置为0(这里0可能是一个表示默认或者无父品类的约定值),保证该属性有一个合理的默认值,避免出现空值等异常情况。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setCreateTime(DateTimeUtil.dateToStr(product.getCreateTime())); | 
			
		
	
		
			
				
					|  |  |  |  |         // 使用DateTimeUtil工具类的dateToStr方法将Product实体对象中的创建时间(createTime,通常是Date类型)转换为字符串格式,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 并赋值给ProductDetailVo对象的CreateTime属性,将日期类型转换为字符串是为了方便前端直接展示时间信息,符合前端展示的格式要求,让用户能直观地看到商品的创建时间。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         productDetailVo.setUpdateTime(DateTimeUtil.dateToStr(product.getUpdateTime())); | 
			
		
	
		
			
				
					|  |  |  |  |         // 同样地,通过DateTimeUtil工具类将Product实体对象中的更新时间(updateTime,通常是Date类型)转换为字符串格式,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 赋值给ProductDetailVo对象的UpdateTime属性,使得前端能够展示商品信息的最后更新时间,方便用户了解商品信息的时效性。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         return productDetailVo; | 
			
		
	
		
			
				
					|  |  |  |  |         // 最后返回组装好的ProductDetailVo对象,这个对象包含了丰富且适合前端展示商品详细信息的各种属性,可供后续在业务逻辑中返回给前端进行商品详情展示使用。
 | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @Override | 
			
		
	
		
			
				
					|  |  |  |  |     public ServerResponse preInitProductStcokToRedis() { | 
			
		
	
		
			
				
					|  |  |  |  |         // 通过productMapper(数据持久层接口,通常基于MyBatis等框架实现,用于与数据库中的商品表进行交互)的selectList方法,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 从数据库中查询获取所有的商品记录列表,这个列表包含了系统中所有商品的相关信息(以Product实体对象形式存在),后续将基于这个列表来筛选并缓存商品的库存信息到Redis中。
 | 
			
		
	
		
			
				
					|  |  |  |  |         List<Product> productList = productMapper.selectList(); | 
			
		
	
		
			
				
					|  |  |  |  |         for(Product product:productList){ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         for (Product product : productList) { | 
			
		
	
		
			
				
					|  |  |  |  |             Integer productId = product.getId(); | 
			
		
	
		
			
				
					|  |  |  |  |             Integer stock = product.getStock(); | 
			
		
	
		
			
				
					|  |  |  |  |             if(productId != null && stock != null && product.getStatus().equals(Constants.Product.PRODUCT_ON)){ | 
			
		
	
		
			
				
					|  |  |  |  |                 commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_STOCK_PREFIX+String.valueOf(productId),String.valueOf(stock),Constants.PRODUCT_EXPIRE_TIME); | 
			
		
	
		
			
				
					|  |  |  |  |             // 遍历从数据库获取到的商品列表,对于每一个商品对象,获取其商品ID(productId)和库存(stock)属性值,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 商品ID用于唯一标识商品,是后续在Redis缓存中构建缓存键以及关联商品相关信息的关键,库存信息则是要缓存到Redis中的核心数据内容,方便后续业务快速获取商品库存情况。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |             if (productId!= null && stock!= null && product.getStatus().equals(Constants.Product.PRODUCT_ON)) { | 
			
		
	
		
			
				
					|  |  |  |  |                 commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_STOCK_PREFIX + String.valueOf(productId), String.valueOf(stock), Constants.PRODUCT_EXPIRE_TIME); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             // 对每个商品进行条件判断,如果商品ID不为null,库存也不为null,并且商品的状态(通过Constants.Product.PRODUCT_ON常量来判断,该常量应该表示商品处于上架等可用状态)符合要求,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 说明这个商品的库存信息是有效的、需要缓存到Redis中的。此时调用commonCacheUtil(这是一个用于操作Redis缓存的工具类实例)的cacheNxExpire方法,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 以特定的缓存键(由Constants.PRODUCT_TOKEN_STOCK_PREFIX与商品ID的字符串形式拼接而成,Constants.PRODUCT_TOKEN_STOCK_PREFIX应该是一个预定义的用于标识商品库存缓存键前缀的常量字符串)作为键,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 将库存值转换为字符串形式(通过String.valueOf方法)作为缓存的值,同时设置缓存的过期时间为Constants.PRODUCT_EXPIRE_TIME(这也是一个预定义的表示缓存有效时长的常量),
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 这样就把商品的库存信息缓存到了Redis中,并且在过期时间后缓存会自动失效,避免缓存数据长期占用内存且过时的问题,方便后续业务场景(如商品销售、库存查询等操作)能快速从缓存获取准确的库存信息,提高系统性能。
 | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         return ServerResponse.createBySuccessMessage("预置库存成功"); | 
			
		
	
		
			
				
					|  |  |  |  |         // 当完成对所有符合条件商品的库存信息缓存操作后,返回一个包含成功提示信息(“预置库存成功”)的ServerResponse对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 告知调用者(可能是其他业务模块或者系统管理相关的操作触发了这个方法调用)库存信息预置到Redis缓存的操作已顺利完成,方便调用者根据这个返回结果进行相应的后续处理(如记录日志、提示用户操作成功等)。
 | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @Override | 
			
		
	
		
			
				
					|  |  |  |  |     public ServerResponse preInitProductListToRedis() { | 
			
		
	
		
			
				
					|  |  |  |  |         // 同样先通过productMapper的selectList方法从数据库中获取所有的商品记录列表,这个列表包含了系统中全部商品的详细信息,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 后续将基于这个列表来筛选并缓存商品的详细信息(以Product实体对象形式表示的各种属性信息)到Redis中,方便后续业务场景快速获取商品的完整信息,减少数据库查询压力,提升系统响应速度。
 | 
			
		
	
		
			
				
					|  |  |  |  |         List<Product> productList = productMapper.selectList(); | 
			
		
	
		
			
				
					|  |  |  |  |         for(Product product:productList){ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         for (Product product : productList) { | 
			
		
	
		
			
				
					|  |  |  |  |             Integer productId = product.getId(); | 
			
		
	
		
			
				
					|  |  |  |  |             if(productId != null  && product.getStatus().equals(Constants.Product.PRODUCT_ON)){ | 
			
		
	
		
			
				
					|  |  |  |  |                 commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX+String.valueOf(productId),JsonUtil.obj2String(product),Constants.PRODUCT_EXPIRE_TIME); | 
			
		
	
		
			
				
					|  |  |  |  |             // 遍历商品列表,获取每个商品的商品ID(productId),商品ID用于唯一标识商品,在缓存操作中是构建缓存键以及关联商品对应信息的关键依据,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 通过它可以准确地在Redis中定位和存储、获取特定商品的详细信息。
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |             if (productId!= null && product.getStatus().equals(Constants.Product.PRODUCT_ON)) { | 
			
		
	
		
			
				
					|  |  |  |  |                 commonCacheUtil.cacheNxExpire(Constants.PRODUCT_TOKEN_PREFIX + String.valueOf(productId), JsonUtil.obj2String(product), Constants.PRODUCT_EXPIRE_TIME); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             // 对每个商品进行条件判断,如果商品ID不为null,并且商品的状态(依据Constants.Product.PRODUCT_ON常量判断是否处于可用状态)符合要求,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 说明这个商品的详细信息是需要缓存到Redis中的。此时调用commonCacheUtil的cacheNxExpire方法,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 以特定的缓存键(由Constants.PRODUCT_TOKEN_PREFIX与商品ID的字符串形式拼接而成,Constants.PRODUCT_TOKEN_PREFIX是用于标识商品相关缓存键前缀的常量字符串)作为键,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 将整个商品对象转换为字符串形式(通过JsonUtil的obj2String方法,可能是将Java对象序列化为JSON字符串形式,方便存储在缓存中)作为缓存的值,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 同时设置缓存的过期时间为Constants.PRODUCT_EXPIRE_TIME,以此将商品的详细信息缓存到Redis中,确保缓存数据在一定时间后会自动失效,维持缓存数据的时效性和内存占用的合理性,
 | 
			
		
	
		
			
				
					|  |  |  |  |             // 方便后续如商品详情查询等业务操作能优先从缓存获取信息,提升系统的整体性能和响应效率。
 | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         return ServerResponse.createBySuccessMessage("预置商品信息成功"); | 
			
		
	
		
			
				
					|  |  |  |  |         // 当完成对所有符合条件商品的详细信息缓存操作后,返回一个包含成功提示信息(“预置商品信息成功”)的ServerResponse对象,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 告知调用者(例如系统初始化过程中触发此操作或者管理员手动执行预置信息操作等情况)商品详细信息预置到Redis缓存的操作已成功完成,
 | 
			
		
	
		
			
				
					|  |  |  |  |         // 调用者可以根据这个返回结果进行相应的后续处理(如记录日志、展示操作成功提示给用户等),保证业务流程的完整性以及对操作结果的合理反馈。
 | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | 
 |