购物车服务

一、环境搭建

  • springboot初始化

  • 新增本地域名cart.achangmall.com

  • 修改对应pom配置,与此次服务间版本对应

    • 引入公共依赖
    • 修改springboot版本
    • 修改springcloud版本

  • 将提供的静态资源导入nginx服务,做到动静分离

  • 修改页面对应的资源地址,修改只选nginx的静态地址,如:

  • 主启动类com.achang.achangmall.cart.AchangmallCartApplication
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableFeignClients
@EnableDiscoveryClient
public class AchangmallCartApplication {public static void main(String[] args) {SpringApplication.run(AchangmallCartApplication.class, args);}
}
  • 配置文件achangmall-cart/src/main/resources/application.yaml
spring:cloud:loadbalancer:ribbon:enabled: falsenacos:discovery:server-addr: localhost:8848thymeleaf:cache: falseredis:host: 192.168.109.101port: 6379application:name: achangmall-cartserver:port: 30000
  • 网关配置路由映射achangmall-gateway/src/main/resources/application.yml
        - id: cart_routeuri: lb://achangmall-cartpredicates:- Host=cart.achangmall.com
  • 启动服务测试,访问:http://cart.achangmall.com/


二、业务

  • com.achang.achangmall.cart.vo.CartVo
/*** 购物车1级*/
public class CartVo {/*** 购物车子项信息*/List<CartItemVo> items;/*** 商品数量*/private Integer countNum;/*** 商品类型数量*/private Integer countType;/*** 商品总价*/private BigDecimal totalAmount;/*** 减免价格*/private BigDecimal reduce = new BigDecimal("0.00");;public List<CartItemVo> getItems() {return items;}public void setItems(List<CartItemVo> items) {this.items = items;}public Integer getCountNum() {int count = 0;if (items != null && items.size() > 0) {for (CartItemVo item : items) {count += item.getCount();}}return count;}public Integer getCountType() {int count = 0;if (items != null && items.size() > 0) {for (CartItemVo item : items) {count += 1;}}return count;}public BigDecimal getTotalAmount() {BigDecimal amount = new BigDecimal("0");// 计算购物项总价if (!CollectionUtils.isEmpty(items)) {for (CartItemVo cartItem : items) {if (cartItem.getCheck()) {amount = amount.add(cartItem.getTotalPrice());}}}// 计算优惠后的价格return amount.subtract(getReduce());}public BigDecimal getReduce() {return reduce;}public void setReduce(BigDecimal reduce) {this.reduce = reduce;}
}
  • com.achang.achangmall.cart.vo.CartItemVo
/*** 购物车2级--购物项*/
public class CartItemVo {private Long skuId;private Boolean check = true;private String title;private String image;/*** 商品套餐属性*/private List<String> skuAttrValues;private BigDecimal price;private Integer count;private BigDecimal totalPrice;public Long getSkuId() {return skuId;}public void setSkuId(Long skuId) {this.skuId = skuId;}public Boolean getCheck() {return check;}public void setCheck(Boolean check) {this.check = check;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getImage() {return image;}public void setImage(String image) {this.image = image;}public List<String> getSkuAttrValues() {return skuAttrValues;}public void setSkuAttrValues(List<String> skuAttrValues) {this.skuAttrValues = skuAttrValues;}public BigDecimal getPrice() {return price;}public void setPrice(BigDecimal price) {this.price = price;}public Integer getCount() {return count;}public void setCount(Integer count) {this.count = count;}/*** 计算当前购物项总价* @return*/public BigDecimal getTotalPrice() {return this.price.multiply(new BigDecimal("" + this.count));}public void setTotalPrice(BigDecimal totalPrice) {this.totalPrice = totalPrice;}
}
  • com.achang.achangmall.cart.vo.SkuInfoVo
@Data
public class SkuInfoVo {private Long skuId;/*** spuId*/private Long spuId;/*** sku名称*/private String skuName;/*** sku介绍描述*/private String skuDesc;/*** 所属分类id*/private Long catalogId;/*** 品牌id*/private Long brandId;/*** 默认图片*/private String skuDefaultImg;/*** 标题*/private String skuTitle;/*** 副标题*/private String skuSubtitle;/*** 价格*/private BigDecimal price;/*** 销量*/private Long saleCount;
}
  • 引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 整合springsession -->
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId>
</dependency>
  • com.achang.achangmall.cart.config.AchangmallSessionConfig
@Configuration
@EnableRedisHttpSession
public class AchangmallSessionConfig {@Beanpublic CookieSerializer cookieSerializer() {DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();//放大作用域cookieSerializer.setDomainName("achangmall.com");cookieSerializer.setCookieName("ACHANGSESSION");return cookieSerializer;}@Beanpublic RedisSerializer<Object> springSessionDefaultRedisSerializer() {//使用json存储redis,而不是默认的序列化存储return new GenericJackson2JsonRedisSerializer();}
}
  • com.achang.achangmall.cart.config.MyThreadConfig
@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {@Beanpublic ThreadPoolExecutor executor(ThreadPoolConfigProperties pool) {return new ThreadPoolExecutor(pool.getCoreSize(),pool.getMaxSize(),pool.getKeepAliveTime(),TimeUnit.SECONDS,new LinkedBlockingDeque<>(100000),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());}
}
  • com.achang.achangmall.cart.config.ThreadPoolConfigProperties
@ConfigurationProperties(prefix = "achangmall.thread")
@Data
public class ThreadPoolConfigProperties {private Integer coreSize;private Integer maxSize;private Integer keepAliveTime;
}
  • com.achang.achangmall.cart.exception.RuntimeExceptionHandler
@ControllerAdvice
public class RuntimeExceptionHandler {/*** 全局统一异常处理*/@ExceptionHandler(RuntimeException.class)@ResponseBodypublic R handler(RuntimeException exception) {return R.error(exception.getMessage());}@ExceptionHandler(CartExceptionHandler.class)public R userHandler(CartExceptionHandler exception) {return R.error("购物车无此商品");}
}
  • com.achang.achangmall.cart.exception.CartExceptionHandler
public class CartExceptionHandler extends RuntimeException {}
  • com.achang.achangmall.cart.feign.ProductFeignService
@FeignClient("achangmall-product")
public interface ProductFeignService {/*** 根据skuId查询sku信息*/@RequestMapping("/product/skuinfo/info/{skuId}")R getInfo(@PathVariable("skuId") Long skuId);/*** 根据skuId查询pms_sku_sale_attr_value表中的信息*/@GetMapping(value = "/product/skusaleattrvalue/stringList/{skuId}")List<String> getSkuSaleAttrValues(@PathVariable("skuId") Long skuId);/*** 根据skuId查询当前商品的最新价格*/@GetMapping(value = "/product/skuinfo/{skuId}/price")BigDecimal getPrice(@PathVariable("skuId") Long skuId);
}
  • com.achang.achangmall.cart.intercept.CartIntercept
@Data
public class UserInfoTo {private Long userId;private String userKey;//一定存在/*** 是否临时用户*/private Boolean tempUser = false;
}
  • 拦截器com.achang.achangmall.cart.intercept.CartIntercept
@Component
public class CartIntercept implements HandlerInterceptor {public static ThreadLocal<UserInfoTo> toThreadLocal = new ThreadLocal<>();//在目标方法执行之前@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {UserInfoTo userInfoTo = new UserInfoTo();HttpSession session = request.getSession();MemberResponseVo memberResponseVo = (MemberResponseVo) session.getAttribute(LOGIN_USER);if (memberResponseVo != null) {//用户登录了userInfoTo.setUserId(memberResponseVo.getId());}Cookie[] cookies = request.getCookies();if (cookies != null && cookies.length > 0) {for (Cookie cookie : cookies) {//user-keyString name = cookie.getName();if (name.equals(TEMP_USER_COOKIE_NAME)) {userInfoTo.setUserKey(cookie.getValue());//标记为已是临时用户userInfoTo.setTempUser(true);}}}//如果没有临时用户一定分配一个临时用户if (StringUtils.isEmpty(userInfoTo.getUserKey())) {String uuid = UUID.randomUUID().toString();userInfoTo.setUserKey(uuid);}//目标方法执行之前toThreadLocal.set(userInfoTo);return true;}//业务执行之后,分配临时用户来浏览器保存@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {//获取当前用户的值UserInfoTo userInfoTo = toThreadLocal.get();//如果没有临时用户一定保存一个临时用户if (!userInfoTo.getTempUser()) {//创建一个cookieCookie cookie = new Cookie(TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());//扩大作用域cookie.setDomain("achangmall.com");//设置过期时间cookie.setMaxAge(TEMP_USER_COOKIE_TIMEOUT);response.addCookie(cookie);}toThreadLocal.remove();}
}
  • com.achang.achangmall.cart.to.UserInfoTo
@Data
public class UserInfoTo {private Long userId;private String userKey;//一定存在/*** 是否临时用户*/private Boolean tempUser = false;
}
  • com.achang.common.constant.CartConstant
public class CartConstant {public final static String TEMP_USER_COOKIE_NAME = "user-key";public final static int TEMP_USER_COOKIE_TIMEOUT = 60*60*24*30;public final static String CART_PREFIX = "achangmall:cart:";
}
  • com.achang.achangmall.cart.intercept.AchangmallWebConfig配置拦截器,让其生效
@Configuration
public class AchangmallWebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new CartIntercept())//注册拦截器.addPathPatterns("/**");}
}
  • achangmall-product/src/main/resources/mapper/product/SkuSaleAttrValueDao.xml
<select id="getSkuSaleAttrValuesAsStringList" resultType="java.lang.String">SELECTCONCAT( attr_name, ":", attr_value )FROMpms_sku_sale_attr_valueWHEREsku_id = #{skuId}
</select>
  • com.achang.achangmall.product.service.impl.SkuSaleAttrValueServiceImpl
@Override
public List<String> getSkuSaleAttrValuesAsStringList(Long skuId) {SkuSaleAttrValueDao baseMapper = this.baseMapper;List<String> stringList = baseMapper.getSkuSaleAttrValuesAsStringList(skuId);return stringList;
}
- com.achang.achangmall.product.app.SkuSaleAttrValueController
@GetMapping(value = "/stringList/{skuId}")
public List<String> getSkuSaleAttrValues(@PathVariable("skuId") Long skuId) {List<String> stringList = skuSaleAttrValueService.getSkuSaleAttrValuesAsStringList(skuId);return stringList;
}
  • com.achang.achangmall.cart.controller.CartController
@Controller
public class CartController {@Resourceprivate CartService cartService;/*** 获取当前用户的购物车商品项* @return*/@GetMapping(value = "/currentUserCartItems")@ResponseBodypublic List<CartItemVo> getCurrentCartItems() {List<CartItemVo> cartItemVoList = cartService.getUserCartItems();return cartItemVoList;}/*** 去购物车页面的请求* 浏览器有一个cookie:user-key 标识用户的身份,一个月过期* 如果第一次使用jd的购物车功能,都会给一个临时的用户身份:* 浏览器以后保存,每次访问都会带上这个cookie;** 登录:session有* 没登录:按照cookie里面带来user-key来做* 第一次,如果没有临时用户,自动创建一个临时用户**/@GetMapping(value = "/cart.html")public String cartListPage(Model model) throws ExecutionException, InterruptedException {//快速得到用户信息:id,user-key// UserInfoTo userInfoTo = CartInterceptor.toThreadLocal.get();CartVo cartVo = cartService.getCart();model.addAttribute("cart",cartVo);return "cartList";}/*** 添加商品到购物车* attributes.addFlashAttribute():将数据放在session中,可以在页面中取出,但是只能取一次* attributes.addAttribute():将数据放在url后面*/@GetMapping(value = "/addCartItem")public String addCartItem(@RequestParam("skuId") Long skuId,@RequestParam("num") Integer num,RedirectAttributes attributes) throws ExecutionException, InterruptedException {cartService.addToCart(skuId,num);attributes.addAttribute("skuId",skuId);return "redirect:http://cart.achangmall.com/addToCartSuccessPage.html";}/*** 跳转到添加购物车成功页面*/@GetMapping(value = "/addToCartSuccessPage.html")public String addToCartSuccessPage(@RequestParam("skuId") Long skuId,Model model) {//重定向到成功页面。再次查询购物车数据即可CartItemVo cartItemVo = cartService.getCartItem(skuId);model.addAttribute("cartItem",cartItemVo);return "success";}/*** 商品是否选中*/@GetMapping(value = "/checkItem")public String checkItem(@RequestParam(value = "skuId") Long skuId,@RequestParam(value = "checked") Integer checked) {cartService.checkItem(skuId,checked);return "redirect:http://cart.achangmall.com/cart.html";}/*** 改变商品数量*/@GetMapping(value = "/countItem")public String countItem(@RequestParam(value = "skuId") Long skuId,@RequestParam(value = "num") Integer num) {cartService.changeItemCount(skuId,num);return "redirect:http://cart.achangmall.com/cart.html";}/*** 删除商品信息*/@GetMapping(value = "/deleteItem")public String deleteItem(@RequestParam("skuId") Integer skuId) {cartService.deleteIdCartInfo(skuId);return "redirect:http://cart.achangmall.com/cart.html";}
}
  • com.achang.achangmall.cart.service.impl.CartServiceImpl
@Slf4j
@Service("cartService")
public class CartServiceImpl implements CartService {@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate ProductFeignService productFeignService;@Autowiredprivate ThreadPoolExecutor executor;@Overridepublic CartItemVo addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {//拿到要操作的购物车信息BoundHashOperations<String, Object, Object> cartOps = getCartOps();//判断Redis是否有该商品的信息String productRedisValue = (String) cartOps.get(skuId.toString());//如果没有就添加数据if (StringUtils.isEmpty(productRedisValue)) {//2、添加新的商品到购物车(redis)CartItemVo cartItemVo = new CartItemVo();//开启第一个异步任务CompletableFuture<Void> getSkuInfoFuture = CompletableFuture.runAsync(() -> {//1、远程查询当前要添加商品的信息R productSkuInfo = productFeignService.getInfo(skuId);SkuInfoVo skuInfo = productSkuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {});//数据赋值操作cartItemVo.setSkuId(skuInfo.getSkuId());cartItemVo.setTitle(skuInfo.getSkuTitle());cartItemVo.setImage(skuInfo.getSkuDefaultImg());cartItemVo.setPrice(skuInfo.getPrice());cartItemVo.setCount(num);}, executor);//开启第二个异步任务CompletableFuture<Void> getSkuAttrValuesFuture = CompletableFuture.runAsync(() -> {//2、远程查询skuAttrValues组合信息List<String> skuSaleAttrValues = productFeignService.getSkuSaleAttrValues(skuId);cartItemVo.setSkuAttrValues(skuSaleAttrValues);}, executor);//等待所有的异步任务全部完成CompletableFuture.allOf(getSkuInfoFuture, getSkuAttrValuesFuture).get();String cartItemJson = JSON.toJSONString(cartItemVo);cartOps.put(skuId.toString(), cartItemJson);return cartItemVo;} else {//购物车有此商品,修改数量即可CartItemVo cartItemVo = JSON.parseObject(productRedisValue, CartItemVo.class);cartItemVo.setCount(cartItemVo.getCount() + num);//修改redis的数据String cartItemJson = JSON.toJSONString(cartItemVo);cartOps.put(skuId.toString(),cartItemJson);return cartItemVo;}}@Overridepublic CartItemVo getCartItem(Long skuId) {//拿到要操作的购物车信息BoundHashOperations<String, Object, Object> cartOps = getCartOps();String redisValue = (String) cartOps.get(skuId.toString());CartItemVo cartItemVo = JSON.parseObject(redisValue, CartItemVo.class);return cartItemVo;}/*** 获取用户登录或者未登录购物车里所有的数据*/@Overridepublic CartVo getCart() throws ExecutionException, InterruptedException {CartVo cartVo = new CartVo();UserInfoTo userInfoTo = CartIntercept.toThreadLocal.get();if (userInfoTo.getUserId() != null) {//1、登录String cartKey = CART_PREFIX + userInfoTo.getUserId();//临时购物车的键String temptCartKey = CART_PREFIX + userInfoTo.getUserKey();//2、如果临时购物车的数据还未进行合并List<CartItemVo> tempCartItems = getCartItems(temptCartKey);if (tempCartItems != null) {//临时购物车有数据需要进行合并操作for (CartItemVo item : tempCartItems) {addToCart(item.getSkuId(),item.getCount());}//清除临时购物车的数据clearCartInfo(temptCartKey);}//3、获取登录后的购物车数据【包含合并过来的临时购物车的数据和登录后购物车的数据】List<CartItemVo> cartItems = getCartItems(cartKey);cartVo.setItems(cartItems);} else {//没登录String cartKey = CART_PREFIX + userInfoTo.getUserKey();//获取临时购物车里面的所有购物项List<CartItemVo> cartItems = getCartItems(cartKey);cartVo.setItems(cartItems);}return cartVo;}/*** 获取到我们要操作的购物车*/private BoundHashOperations<String, Object, Object> getCartOps() {//先得到当前用户信息UserInfoTo userInfoTo = CartIntercept.toThreadLocal.get();String cartKey = "";if (userInfoTo.getUserId() != null) {//gulimall:cart:1cartKey = CART_PREFIX + userInfoTo.getUserId();} else {cartKey = CART_PREFIX + userInfoTo.getUserKey();}//绑定指定的key操作RedisBoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);return operations;}/*** 获取购物车里面的数据*/private List<CartItemVo> getCartItems(String cartKey) {//获取购物车里面的所有商品BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);List<Object> values = operations.values();if (values != null && values.size() > 0) {List<CartItemVo> cartItemVoStream = values.stream().map((obj) -> {String str = (String) obj;CartItemVo cartItem = JSON.parseObject(str, CartItemVo.class);return cartItem;}).collect(Collectors.toList());return cartItemVoStream;}return null;}@Overridepublic void clearCartInfo(String cartKey) {redisTemplate.delete(cartKey);}@Overridepublic void checkItem(Long skuId, Integer check) {//查询购物车里面的商品CartItemVo cartItem = getCartItem(skuId);//修改商品状态cartItem.setCheck(check == 1?true:false);//序列化存入redis中String redisValue = JSON.toJSONString(cartItem);BoundHashOperations<String, Object, Object> cartOps = getCartOps();cartOps.put(skuId.toString(),redisValue);}/*** 修改购物项数量*/@Overridepublic void changeItemCount(Long skuId, Integer num) {//查询购物车里面的商品CartItemVo cartItem = getCartItem(skuId);cartItem.setCount(num);BoundHashOperations<String, Object, Object> cartOps = getCartOps();//序列化存入redis中String redisValue = JSON.toJSONString(cartItem);cartOps.put(skuId.toString(),redisValue);}/*** 删除购物项*/@Overridepublic void deleteIdCartInfo(Integer skuId) {BoundHashOperations<String, Object, Object> cartOps = getCartOps();cartOps.delete(skuId.toString());}@Overridepublic List<CartItemVo> getUserCartItems() {List<CartItemVo> cartItemVoList = new ArrayList<>();//获取当前用户登录的信息UserInfoTo userInfoTo = CartIntercept.toThreadLocal.get();//如果用户未登录直接返回nullif (userInfoTo.getUserId() == null) {return null;} else {//获取购物车项String cartKey = CART_PREFIX + userInfoTo.getUserId();//获取所有的List<CartItemVo> cartItems = getCartItems(cartKey);if (cartItems == null) {throw new CartExceptionHandler();}//筛选出选中的cartItemVoList = cartItems.stream().filter(items -> items.getCheck()).map(item -> {//更新为最新的价格(查询数据库)BigDecimal price = productFeignService.getPrice(item.getSkuId());item.setPrice(price);return item;}).collect(Collectors.toList());}return cartItemVoList;}
}

Day425426.购物车服务 -谷粒商城相关推荐

  1. Day429430431.订单服务 -谷粒商城

    订单服务 一.环境搭建 因为我们做的是动静分离,将静态资源上传到nginx上 配置本地域名:‪C:\Windows\System32\drivers\etc\hosts 配置服务网关:achangma ...

  2. Day437438439.秒杀服务 -谷粒商城

    秒杀服务 一.定时任务-Quartz Cron表达式 执行定时任务需要给一个时间计划,这个时间计划可以用 Cron 表达式来编写 官方文档 Cron 表达式是一个字符串,是用空格分割的六到七个属性. ...

  3. Day400401402403404405406.商品服务 -谷粒商城

    商品服务 一.品牌管理 1.效果优化与快速显示开关 将逆向工程product得到的resources\src\views\modules\product文件拷贝到achangmall/renren-f ...

  4. 谷粒商城-分布式高级篇【业务编写】

    谷粒商城-分布式基础篇[环境准备] 谷粒商城-分布式基础[业务编写] 谷粒商城-分布式高级篇[业务编写]持续更新 谷粒商城-分布式高级篇-ElasticSearch 谷粒商城-分布式高级篇-分布式锁与 ...

  5. 谷粒商城二十订单服务

    rabbitmq相关知识 // 静态页面的引入,静态资源上传nginx等192.168.56.10 gulimall.com 192.168.56.10 search.gulimall.com 192 ...

  6. 谷粒商城笔记+踩坑(18)——购物车

    导航: 谷粒商城笔记+踩坑汇总篇 目录 一.环境搭建 1.1.购物车模块初始化 1.2.动静资源处理 1.3.页面跳转配置 二.数据模型分析 2.1.购物车需求 2.1.1.离线购物车和在线购物车需求 ...

  7. 谷粒商城--订单服务--高级篇笔记十一

    1.页面环境搭建 1.1 静态资源导入nginx 等待付款 --------->detail 订单页 --------->list 结算页 --------->confirm 收银页 ...

  8. 谷粒商城检索服务(三十)

    谷粒商城检索服务(三十) 173.商城业务-检索服务-搭建页面环境 - 192.商城业务-检索服务-条件筛选联动 敲完代码,感觉ES的复杂查询还不是很熟练,不过应用的也比较简单,权重和分词都没有应用到 ...

  9. 【谷粒商城 -秒杀服务】

    谷粒商城–秒杀服务–高级篇笔记十二 1.后台添加秒杀商品 未配置秒杀服务相关网关 1.1 配置网关 - id: coupon_routeuri: lb://gulimall-couponpredica ...

最新文章

  1. 将数组A中的内容和数组B中的内容进行交换(数组一样大)
  2. C语言打印100-200之间的素数
  3. TFBOYS饭票上线引热议,骗局之外,区块链技术能重构娱乐产业吗?
  4. java axis2 教程_webservice的Axis2入门教程java版
  5. 当贝显示服务器生病,智能电视一直显示“缓冲中”是什么问题?当贝市场为你解答...
  6. Java 实验5 T3 将十进制转换为二进制
  7. 基于MicroPython:TPYBoard心率监测器
  8. 本科计算机相关课程设计,计算机科学及技术专业本科生大学课程设计.doc
  9. 2021第十届小美赛-“认证杯”数学中国数学建模国际赛
  10. 归并排序(MergeSort)
  11. html保护眼睛背景色,保护眼睛的颜色和各种背景颜色设置方法
  12. html微信分享图标设置,微信分享之设置分享title和icon
  13. 重症监护病人心电导联信号质量评估、SQI
  14. 小丁是怎样入门git的
  15. 用Q-learning算法实现自动走迷宫机器人
  16. 你还在纠结用什么库写 Python 命令行程序?看这一篇就够了
  17. 生成帮助文档(html或chm格式)
  18. python连接不上数据库_python连接各种数据库
  19. hdu 1907 & hdu 2509(Nim博弈)
  20. MyBatis 与 MyBatis-Plus 的区别

热门文章

  1. 《More Effective C++》读书笔记-技术(二)
  2. 突然断网?专业分析宽带拨号和自动获取ip哪个好?
  3. 最新虚幻5引擎(UE5)游戏性能的影响详解
  4. s7-200使用高速计数器时,出现看门狗错误-L48
  5. 【哈工大SCIR】八篇长文被ACL 2020录用
  6. LPS和RAS坐标系
  7. 如何修改java运行环境版本_安卓修改大师配置Java运行环境教程 教你快速使用
  8. [电子设计]基于MA8601的USB_HUB
  9. GIS与地质灾害评价——缓冲区分析
  10. 水泵综合性能测试系统软件,中小型水泵厂水泵综合性能测试系统技术方案