谷粒商城(新增商品、商品管理、仓库管理)思路详解
谷粒商城基础分布式总结
- 1.新增商品
- 1.调试member服务
- 2.查询分类下的所有品牌
- 2.获取分类下面带属性的属性分组。
- 3、保存设定好的spu和sku信息
- 2.商品管理
- 1.spu展示查询(spuinfo/list)
- 2.商品展示
- 3.仓库管理
- 1.初始化ware获取仓库列表
- 2.查询库存&创建采购需求
- 3.合并采购需求
- 4.领取采购单
- 5.完成采购
- 6.展示规格,修改规格
- 4.基础分布式总结
- 1.分布式:
- 2.基础开发:
- 3.环境:
- 4.开发规范
1.新增商品
1.调试member服务
出现问题:
①路由规则配置问题/api/member/错,改成/api(原因在找)
②在最后一步没有显示会员的优惠信息。这一栏如果没有显示原因就是你设置了多个默认会员。导致错误。可以看看数据库或者是看看会员等级这个部分。
- id: gulimall_member_routeuri: lb://gulimall-memberpredicates:- Path=/api/member/**filters:- RewritePath=/api/(?<segment>.*),/$\{segment}
2.查询分类下的所有品牌
思路
①CategoryBrandCon中写brands/list接口,接收catId,并且封装返回的BrandVo对象
②CateBrandSer根据分类id查询分类品牌的关联关系,然后通过关系对象获取所有的品牌id。
③根据品牌id再次获取品牌对象,封装到list返回
CateBrandSer
@Overridepublic List<BrandEntity> getBrandsByCatId(Long catId) {//1.根据分类id查询关联的List<CategoryBrandRelationEntity> relationEntities = categoryBrandRelationDao.selectList(new QueryWrapper<CategoryBrandRelationEntity>().eq("catelog_id", catId));//2.根据品牌id查询品牌。(查询的是分类下面关联的品牌。)List<BrandEntity> brandEntities = relationEntities.stream().map(item -> {Long brandId = item.getBrandId();BrandEntity byId = brandService.getById(brandId);return byId;}).collect(Collectors.toList());return brandEntities;}
CateBrandCon
@GetMapping("brands/list")public R getBrandsRelation(@RequestParam(value = "catId",required = true)Long catId ){//service获取对应的brandentityList<BrandEntity> list=categoryBrandRelationService.getBrandsByCatId(catId);List<BrandVo> vos= list.stream().map(item -> {BrandVo brandVo = new BrandVo();brandVo.setBrandId(item.getBrandId());brandVo.setBrandName(item.getName());return brandVo;}).collect(Collectors.toList());return R.ok().put("data",vos);}
2.获取分类下面带属性的属性分组。
思路
①需要一个vo对象来存储处理group对象的属性还要有属性集合
②先根据分类id获取所有分组
③根据分组id通过之前写过的getRelationAttr方法获取分组下所有的属性,存入对应的vo对象
AttrService
/*** 查询分类下所有属性* @param catelogId* @return*/@Overridepublic List<AttrGroupWithAttrsVo> getGroupWithAttrsByCatId(Long catelogId) {//1.查询所有分组List<AttrGroupEntity> group= attrGroupDao.selectList(new QueryWrapper<AttrGroupEntity>().eq("catelog_id", catelogId));//2.获取查询分组下所有的属性List<AttrGroupWithAttrsVo> attrGroupWithAttrsVos = group.stream().map(item -> {//3.把group放入vo对象AttrGroupWithAttrsVo attrGroupWithAttrsVo = new AttrGroupWithAttrsVo();BeanUtils.copyProperties(item, attrGroupWithAttrsVo);Long attrGroupId = item.getAttrGroupId();//4.根据groupid查询关联关系List<AttrEntity> attrs = attrService.getRelationAttr(attrGroupId);attrGroupWithAttrsVo.setAttrs(attrs);return attrGroupWithAttrsVo;}).collect(Collectors.toList());return attrGroupWithAttrsVos;}
3、保存设定好的spu和sku信息
思路
①JSON转换成java类
②理清楚所有要保存的属性。
③第一步保存Spu的所有信息
④接着就是保存sku的信息。如果发现需要转换对象那么就可以通过stream来达到转换的效果。
拓展与踩坑
①保存spu的bound的时候需要远程调用,服务之间的远程参数可以放到common的TO对象中。而且TO对象在传输的时候会变成requestBody也就是json字符串的请求体,也就是只需要消费者对象和提供者对象的json字符串相容就能够传输。(相容指的是他们的属性名是否相同,结构是否相似。)
②第一个是关于getCode的问题,获取的本来就是一个Integer无法转换成String。那么直接强转Object为Integer就可以
③第二个即使desc的问题,其实问题就是字段sku_id不应该是自增而是我们设置进去的。不然程序设置的时候会发现sku_id已经有默认值而无法自增。
④解决会员价<=0和折扣<=0的情况,还有就是sku_imgurl为空的情况。
⑤set session transaction isolation level read uncommitted;通过这个设置会话为读未提交,能够有虚拟数据加入。但是中途如果自己断开连接,这些数据会保存下来。
⑥compound能够把微服务一起开启,并且可以设置-Xml100m在VM-option中限制他们运行的内存
SpuInfoServiceImpl
@Transactional@Overridepublic void saveSpuSaveVo(SpuSaveVo vo) {//1.保存Spu的基本信息spu_infoSpuInfoEntity spuInfoEntity=new SpuInfoEntity();BeanUtils.copyProperties(vo,spuInfoEntity);spuInfoEntity.setCreateTime(new Date());spuInfoEntity.setUpdateTime(new Date());this.saveSpuInfo(spuInfoEntity);//2.保存spu的图片集spu_imgList<String> images = vo.getImages();spuImagesService.saveImages(spuInfoEntity.getId(),images);//3.保存spu的介绍图片集spu_desList<String> decript = vo.getDecript();SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();spuInfoDescEntity.setSpuId(spuInfoEntity.getId());spuInfoDescEntity.setDecript(String.join(",",decript));spuInfoDescService.saveSpuDesc(spuInfoDescEntity);//4.保存spu的规格参数spu_product_attr_valueList<BaseAttrs> baseAttrs = vo.getBaseAttrs();//转换成productattrvalueList<ProductAttrValueEntity> attrValueEntities = baseAttrs.stream().map(attr -> {ProductAttrValueEntity productAttrValueEntity = new ProductAttrValueEntity();productAttrValueEntity.setAttrId(attr.getAttrId());productAttrValueEntity.setQuickShow(attr.getShowDesc());//根据attrId获取属性->nameAttrEntity attrEntity = attrService.getById(attr.getAttrId());productAttrValueEntity.setAttrName(attrEntity.getAttrName());productAttrValueEntity.setAttrValue(attr.getAttrValues());return productAttrValueEntity;}).collect(Collectors.toList());attrValueService.saveProductAttrValueList(attrValueEntities);//5.保存spu的积分信息spu_boundsBounds bounds = vo.getBounds();R r1 = couponFeignService.saveBound(bounds);if(r1.getCode()!=0){log.error("保存spu积分信息失败");}//6.保存skuList<Skus> skusList = vo.getSkus();//判空操作if(skusList!=null&&skusList.size()>0){//遍历skufor(Skus skuvo:skusList){//遍历图片,找到defalutURLString defaultImg="";List<Images> skuImages = skuvo.getImages();for(Images skuImage:skuImages){if(skuImage.getDefaultImg()==1){defaultImg=skuImage.getImgUrl();}}//vo->po转换SkuInfoEntity skuInfoEntity = new SkuInfoEntity();BeanUtils.copyProperties(skuvo,skuInfoEntity);skuInfoEntity.setBrandId(spuInfoEntity.getBrandId());skuInfoEntity.setCatalogId(spuInfoEntity.getCatalogId());skuInfoEntity.setSaleCount(0L);skuInfoEntity.setSpuId(spuInfoEntity.getId());skuInfoEntity.setSkuDefaultImg(defaultImg);//6.1、sku基本信息skuInfoService.saveSkuInfo(skuInfoEntity);//处理图片集List<SkuImagesEntity> skuImagesEntityList = skuImages.stream().map(img -> {//存入SkuImage下SkuImagesEntity skuImagesEntity = new SkuImagesEntity();BeanUtils.copyProperties(img, skuImagesEntity);skuImagesEntity.setSkuId(skuInfoEntity.getSkuId());return skuImagesEntity;}).collect(Collectors.toList());//6.2、sku图片集skuImagesService.saveBatch(skuImagesEntityList);//6.3、sku销售属性List<Attr> attrList = skuvo.getAttr();List<SkuSaleAttrValueEntity> skuSaleAttrValueEntityList = attrList.stream().map(attr -> {SkuSaleAttrValueEntity skuSaleAttrValueEntity = new SkuSaleAttrValueEntity();BeanUtils.copyProperties(attr, skuSaleAttrValueEntity);skuSaleAttrValueEntity.setSkuId(skuInfoEntity.getSkuId());return skuSaleAttrValueEntity;}).collect(Collectors.toList());skuSaleAttrValueService.saveBatch(skuSaleAttrValueEntityList);//6.4、sku优惠属性,满减、会员价、打折信息SkuReductionTo skuReductionTo = new SkuReductionTo();skuReductionTo.setSkuId(skuInfoEntity.getSkuId());BeanUtils.copyProperties(skuvo,skuReductionTo);R r = couponFeignService.saveSkuReduction(skuReductionTo);if(r.getCode()!=0){log.error("保存sku优惠信息失败");}}}}
SpuBoundsCon
/*** 保存*/@RequestMapping("/save")//@RequiresPermissions("coupon:spubounds:save")public R save(@RequestBody SpuBoundsEntity spuBounds){spuBoundsService.save(spuBounds);return R.ok();}
CouponFeignService
@FeignClient("gulimall-coupon")
public interface CouponFeignService {@RequestMapping("coupon/spubounds/save")R saveBound(Bounds bounds);@PostMapping("coupon/skufullreduction/saveInfo")R saveSkuReduction(SkuReductionTo skuReductionTo);
}
2.商品管理
1.spu展示查询(spuinfo/list)
思路
①重写service方法qeuryForPageByCondition
②对属性值进行判空,然后增加条件查询。
拓展
①如果发现最后显示的时间格式不好那么可以在application.yml中配置date-formate.(json)
SpuInfoSer
@Overridepublic PageUtils queryPageByCondition(Map<String, Object> params) {QueryWrapper<SpuInfoEntity> wrapper = new QueryWrapper<>();//1.判断keyString key = (String) params.get("key");if(!StringUtils.isEmpty(key)){wrapper.and(w->{w.eq("id",key).or().like("spu_name",key);});}//2.statusString status = (String) params.get("status");if(!StringUtils.isEmpty(status)){wrapper.eq("publish_status",status);}//3.catelog_idString catelogId = (String) params.get("catelogId");if(!StringUtils.isEmpty(catelogId )){wrapper.eq("catalog_id",catelogId);}//4.brandIdString brandId = (String) params.get("brandId");if(!StringUtils.isEmpty(brandId)){wrapper.eq("brand_id",brandId);}IPage<SpuInfoEntity> page = this.page(new Query<SpuInfoEntity>().getPage(params),wrapper);return new PageUtils(page);}
2.商品展示
思路
①还是在对应的skuinfo/list中增加条件查询
拓展与问题
①在这个地方会发现catelog_id和brand_id如果等于0的时候就会查询不到,那么就要做一个判断处理,如果是0那么就不增加查询条件。
②max需要放入decimal检测是否是数字,然后还需要看是不是>0如果是才加入条件。
③条件查询的连接可以用到and(w->{})继续连接查询。或者是eq。
SkuInoSer
@Overridepublic PageUtils queryPageByCondition(Map<String, Object> params) {QueryWrapper<SkuInfoEntity> wrapper = new QueryWrapper<>();/*** key:* catelogId: 0* brandId: 0* min: 0* max: 0*/String key = (String) params.get("key");if(!StringUtils.isEmpty(key)){wrapper.and(w->{w.eq("sku_id",key).or().like("sku_name",key);});}String brandId = (String) params.get("brandId");if(!StringUtils.isEmpty(brandId)&&!"0".equalsIgnoreCase(brandId)){wrapper.eq("brand_id",brandId);}String catelogId = (String) params.get("catelogId");if(!StringUtils.isEmpty(catelogId)&&!"0".equalsIgnoreCase(catelogId)){wrapper.eq("catalog_id",catelogId);}String min = (String) params.get("min");if(!StringUtils.isEmpty(min)){wrapper.ge("price",min);}String max = (String) params.get("max");if(!StringUtils.isEmpty(max)){//判断是否为0try {BigDecimal maxBigDecimal = new BigDecimal(max);if(maxBigDecimal.compareTo(new BigDecimal("0"))==1){wrapper.le("price",max);}}catch (Exception e){e.printStackTrace();}}IPage<SkuInfoEntity> page = this.page(new Query<SkuInfoEntity>().getPage(params),wrapper);return new PageUtils(page);}
3.仓库管理
1.初始化ware获取仓库列表
思路
①写好gateway
②服务注册,name,开启事务。
2.查询库存&创建采购需求
思路和上面的一致都是判空然后添加条件。
3.合并采购需求
需求描述:把采购需求合并到一个采购单中。分解出来其实就是把采购单id与对应的采购需求绑定在一起,所以只需要更新采购需求的绑定采购单id,如果没有采购单就创建采购单.
思路
①搜索合并采购单,并且查询那些未领取和新建的,处理status条件即可
②创建constant类来记录这些采购状态。
③主要是编写merge接口
PurchaseSer
/*** 合并菜单* @param mergeVo*/@Overridepublic void merge(MergeVo mergeVo) {//1.如果没有订单那么就创建一个Long purchaseId = mergeVo.getPurchaseId();if(purchaseId==null){PurchaseEntity purchaseEntity=new PurchaseEntity();purchaseEntity.setStatus(WareConstant.PurchaseStatusEnum.CREATE.getCode());purchaseEntity.setCreateTime(new Date());purchaseEntity.setUpdateTime(new Date());this.save(purchaseEntity);}//2.更新需求信息List<Long> items = mergeVo.getItems();List<PurchaseDetailEntity> detailEntities = items.stream().map(i -> {PurchaseDetailEntity purchaseDetailEntity = new PurchaseDetailEntity();//绑定到的订单号purchaseDetailEntity.setPurchaseId(purchaseId);purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailEnum.ASSIGNED.getCode());//根据自己的id来更新purchaseDetailEntity.setId(i);return purchaseDetailEntity;}).collect(Collectors.toList());//更新detailService.updateBatchById(detailEntities);}
4.领取采购单
思路
①相当于就是修改采购单和采购需求的状态。
②先确定采购单的状态
③修改采购单状态->通过id获取采购单->过滤状态不对的采购单->修改状态
④修改采购需求状态->根据采购单id获取对应需求->修改状态
@Overridepublic void receivedPurchase(List<Long> ids) {//1.确认采购单的状态是0或者是1List<PurchaseEntity> purchaseEntities = ids.stream().map(id -> {//查询信息PurchaseEntity purchaseEntity = this.getById(id);return purchaseEntity;}).filter(entity -> {//过滤if (entity.getStatus() == WareConstant.PurchaseStatusEnum.ASSIGNED.getCode() ||entity.getStatus() == WareConstant.PurchaseStatusEnum.CREATE.getCode()) {return true;}return false;}).map(entity -> {//改变状态entity.setStatus(WareConstant.PurchaseStatusEnum.RECEIVE.getCode());return entity;}).collect(Collectors.toList());//2.修改采购单状态this.updateBatchById(purchaseEntities);//3.修改采购需求状态purchaseEntities.forEach(item->{Long purchaseId = item.getId();//查询采购单的需求List<PurchaseDetailEntity> detailEntities=detailService.getDetailByPurchaseId(purchaseId);//遍历需求,修改状态List<PurchaseDetailEntity> updateDetailList = detailEntities.stream().map(entity -> {PurchaseDetailEntity purchaseDetailEntity = new PurchaseDetailEntity();purchaseDetailEntity.setId(entity.getId());purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailEnum.BUYING.getCode());return purchaseDetailEntity;}).collect(Collectors.toList());//更新detailService.updateBatchById(updateDetailList);});
5.完成采购
需求分析
完成采购谁需要改变?很明显采购单需要状态改变,第二个就是采购项状态需要改变。那么完成了采购是不是相应的库存也应该有所增加?也就是说库存也需要进行改变。那么思路就是purchaseSer完成状态改变->wareSkuSer完成库存改变。库存其实就是商品-仓库-商品数量之间的一个对应关系。
思路
①需要两个vo对象,一个是采购单的,一个是采购项的。采购单中有id和多个采购项,采购项里就是采购项的id和采购状态还有造成这个状态的采购原因。其实就是购买出现了什么状况
②然后就是PurchaseCon写好done接口,->交给ser先完成采购项的状态更新,并且留一个flag判断是否采购成功,因为一个采购项出现问题,那么采购单的采购也是有问题的。
③更新采购单状态。
④接着就是改变库存。
拓展与踩坑
①flag只需要一个失败的采购项就是一直是false,不需要在成功的时候改成true
②注意json对象与vo对象的兼容
WareSkuSer
@Overridepublic void addStock(PurchaseDetailEntity entity) {//不存在这个库存位置List<WareSkuEntity> list = this.list(new QueryWrapper<WareSkuEntity>().eq("sku_id", entity.getSkuId()).eq("ware_id", entity.getWareId()));if(list!=null&&list.size()>0){//如果存在的话baseMapper.addStock(entity.getSkuId(),entity.getWareId(),entity.getSkuNum());}else{//如果不存在这个库存,那么就创建一条记录。//仓库——商品(数量)WareSkuEntity wareSkuEntity = new WareSkuEntity();wareSkuEntity.setSkuId(entity.getSkuId());wareSkuEntity.setStock(entity.getSkuNum());wareSkuEntity.setWareId(entity.getWareId());wareSkuEntity.setStockLocked(0);//远程调用添加商品名字try {R info = productFeignService.info(entity.getSkuId());if(info.getCode()==0){Map<String,Object> skuInfo = (Map<String, Object>) info.get("skuInfo");wareSkuEntity.setSkuName((String) skuInfo.get("skuName"));}} catch (Exception e) {e.printStackTrace();}baseMapper.insert(wareSkuEntity);}}
PurchaseSer
@Transactional@Overridepublic void done(PurChaseDoneVo purChaseDoneVo) {boolean flag=true;//1.修改采购项的状态List<PurChaseItemVo> itemVos = purChaseDoneVo.getItems();//创建一个detailList方便修改List<PurchaseDetailEntity> purchaseDetailEntities=new ArrayList<>();for(PurChaseItemVo itemVo:itemVos){//新创建detail对象PurchaseDetailEntity purchaseDetailEntity = new PurchaseDetailEntity();//获取状态并且判断Integer itemStatus = itemVo.getStatus();if(itemStatus== WareConstant.PurchaseDetailEnum.HASERROR.getCode()){//失败//主要修改状态purchaseDetailEntity.setStatus(itemStatus);flag=false;}else{//1.1修改库存信息purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailEnum.FINISHED.getCode());//1.2获取项信息,并修改库存。PurchaseDetailEntity byIdDetailEntity = detailService.getById(itemVo.getItemId());//1.3修改库存wareSkuService.addStock(byIdDetailEntity);}purchaseDetailEntity.setId(itemVo.getItemId());purchaseDetailEntities.add(purchaseDetailEntity);}detailService.updateBatchById(purchaseDetailEntities);//2.修改采购单的状态Long purchaseId = purChaseDoneVo.getId();//获取订单对象PurchaseEntity updatePurchase = new PurchaseEntity();updatePurchase.setId(purchaseId);System.out.println(flag);updatePurchase.setStatus(flag?WareConstant.PurchaseStatusEnum.FINISHED.getCode():WareConstant.PurchaseStatusEnum.HASERROR.getCode());updatePurchase.setUpdateTime(new Date());this.updateById(updatePurchase);}
6.展示规格,修改规格
思路
①直接展示spu_id相关的规格参数,条件查询
②修改规格,接收参数,删除spuId相关的规格参数之后再重新插入。
拓展与踩坑
①如果发现点击规格出现404,先去gulimall-admin的数据库中插入信息INSERT INTO sys_menu (menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (76, 37, ‘规格维护’, ‘product/attrupdate’, null,2, ‘log’, 0);,接着还是会出现404,那么就去到src/router/index.js下的mainRoutes的children:[]里面加入路径{ path: ‘/product-attrupdate’, component: _import(‘modules/product/attrupdate’), name: ‘attr-update’, meta: { title: ‘规格维护’, isTab: true } }
ProductAttrValueSer
/*** 查询产品规格参数* @param spuId* @return*/@Overridepublic List<ProductAttrValueEntity> listForSpu(Long spuId) {List<ProductAttrValueEntity> productAttrValueEntityList = this.baseMapper.selectList(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id", spuId));return productAttrValueEntityList;}/*** 更新规格参数* @param spuId* @param updateProductAttrList*/@Overridepublic void updateProductAttrList(Long spuId, List<ProductAttrValueEntity> updateProductAttrList) {//1.删除所有关于spuId的规格参数this.baseMapper.delete(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id",spuId));//2.给修改的集合加上spuId然后插入修改List<ProductAttrValueEntity> productAttrValueEntities = updateProductAttrList.stream().map(item -> {item.setSpuId(spuId);return item;}).collect(Collectors.toList());this.saveBatch(productAttrValueEntities);}
4.基础分布式总结
1.分布式:
①注册中心:服务注册
②配置中心:配置文件注册
③Feign:完成远程调用和负载均衡
④远程调用
⑤微服务
2.基础开发:
①Springboot2
②mybatis-plus
③springcloud
④vue
⑤阿里云对象存储
3.环境:
①linux
②redis、mysql
③逆向工程和人人开源
④vagrant虚拟机
4.开发规范
①vo(前端数据)与to(服务之间传输的对象)还有po(数据库表保存对象)的区别
②JSR303数据校验
③全局跨域、异常映射、统一返回
④枚举状态、业务枚举
⑤lombok
总结:对于自己来说相比起尚筹网和谷粒学院这个项目逻辑更复杂,一开始我并不适应,后来跟着一步步练习,然后找到对象之间的关系。如果大家也很觉得难,那么可以尝试先把思路写在纸上,然后画出类和表之间的逻辑关系,相互调用的逻辑关系,以及分析需求的求解方案,这样能够梳理清楚业务逻辑,然后加以总结。接下来就进入到谷粒商城的第二个阶段了。
谷粒商城(新增商品、商品管理、仓库管理)思路详解相关推荐
- 企业项目权限管理设计思路详解
任何系统都离不开权限的管理,有一个好的权限管理模块,不仅使我们的系统操作自如,管理方便,也为系统添加亮点. l 不同职责的人员,对于系统操作的权限应该是不同的.优秀的业务系统,这是最基本 ...
- ElasticSearch最全详细使用教程:入门、索引管理、映射详解、索引别名、分词器、文档管理、路由、搜索详解...
墨墨导读:之前我们分享了ElasticSearch最全详细使用教程:入门.索引管理.映射详解,本文详细介绍ElasticSearch的索引别名.分词器.文档管理.路由.搜索详解. 一.索引别名 1. ...
- 【直播】陈安东,但扬:CNN模型搭建、训练以及LSTM模型思路详解
CNN模型搭建.训练以及LSTM模型思路详解 目前 Datawhale第24期组队学习 正在如火如荼的进行中.为了大家更好的学习"零基础入门语音识别(食物声音识别)"的课程设计者 ...
- Java编程配置思路详解
Java编程配置思路详解 SpringBoot虽然提供了很多优秀的starter帮助我们快速开发,可实际生产环境的特殊性,我们依然需要对默认整合配置做自定义操作,提高程序的可控性,虽然你配的不一定比官 ...
- ElasticSearch最全详细使用教程:入门、索引管理、映射详解
墨墨导读:本文介绍了ElasticSearch的必备知识:从入门.索引管理到映射详解. 一.快速入门 1. 查看集群的健康状况http://localhost:9200/_cat http://loc ...
- 【云原生之k8s】k8s管理工具kubectl详解
[云原生之k8s]k8s管理工具kubectl详解 前言 一.陈述式管理 (1)陈述式资源管理方法 (2)k8s相关信息查看 ①查看版本信息 ②查看节点信息 ③查看资源对象简写 ④查看集群信息 ⑤配置 ...
- Pinterest 3.0 for iOS设计过程——升级iOS7设计思路详解
Pinterest 3.0 for iOS设计过程--升级iOS7设计思路详解 时间2013-12-11 11:39:31 苹果开发中文站 原文 http://www.cocoachina.com ...
- elasticsearch最全详细使用教程:入门、索引管理、映射详解、索引别名、分词器、文档管理、路由、搜索详解
一.快速入门 1. 查看集群的健康状况 http://localhost:9200/_cat http://localhost:9200/_cat/health?v 说明:v是用来要求在结果中返回表头 ...
- JAVA抖音潜艇挑战_Android 实现抖音小游戏潜艇大挑战的思路详解
<潜水艇大挑战>是抖音上的一款小游戏,以面部识别来驱动潜艇通过障碍物,最近特别火爆,相信很多人都玩过. 一时兴起自己用Android自定义View也撸了一个,发现只要有好的创意,不用高深的 ...
最新文章
- android+apk+反编译和再签名打包,Android:apk反编译步骤,打包、签名和逆向工程经验总结...
- vue.js框架原理浅析
- 2020-2021年度第二届全国大学生算法设计与编程挑战赛 (春季赛)- 天才的操作(线段树+主席树+树上倍增)
- 基于Sbo SDK的Add-on插件开发实例
- vs设计窗口不见了_碳纤维的巅峰:VS沛纳海616V3
- vscode无法打开源文件iostream_C++的iostream标准库介绍(1)
- bae php微信配置,使用BAE3.0搭建微信开发环境
- 在Linux下安装LaTeX+CJK+中文字体的方法 [转]
- Eclipse中使用GIT将文件还原至上一版本
- Java中的数据结构之常见的五种数据结构
- iMC iNode客户端上岗证
- unity物理引擎详解
- 怎样设置阿里云Web应用攻击防护?
- 一堂难忘的计算机课作文,一节电脑课作文600字
- Stack Ball 堆栈球小游戏unity3d开发教程
- excel一列数字前面批量加个逗号
- Robert Sedgewick左倾红黑树论文翻译
- 为什么一场比赛进3球叫帽子戏法?
- 你从未见过的“地狱级”烂项目
- react 生命周期
热门文章
- 浅谈卓智达公网集群通信系统在大型赛事中的应用
- [Datasheet] PHY LAN8720网络芯片解读
- 华为Ascend众智计划项目--3DMPPE_ROOTNET--Pytorch模型迁移至NPU(二)
- 整数规划(线性)matlab实现
- 使用MegaCli监控Linux硬盘
- BZOJ2073: [POI2004]PRZ
- 创客教育对学生的意义
- 每天和琦琦学点新知识_爬虫篇002_Python正则表达式
- PCIe热插拔机制(详细)总结-PCIe专题知识(五)
- Verilog基础入门