e3mall项目第十六天——复习:商品详情页面的展示、购物车模块

商品详情页面的展示:

功能分析
在搜索结果页面点击商品图片或者商品标题,展示商品详情页面。
请求的url:/item/{itemId}
参数:商品id
返回值:String 逻辑视图
业务逻辑:
1、从url中取参数,商品id
2、根据商品id查询商品信息(tb_item)得到一个TbItem对象,缺少images属性,可以创建一个pojo继承TbItem,添加一个getImages方法。在e3-item-web工程中
3、根据商品id查询商品描述。
4、展示到页面。

pojo(item)

public class Item extends TbItem {
	
	public Item(TbItem tbItem) {
		this.setId(tbItem.getId());
		this.setTitle(tbItem.getTitle());
		this.setSellPoint(tbItem.getSellPoint());
		this.setPrice(tbItem.getPrice());
		this.setNum(tbItem.getNum());
		this.setBarcode(tbItem.getBarcode());
		this.setImage(tbItem.getImage());
		this.setCid(tbItem.getCid());
		this.setStatus(tbItem.getStatus());
		this.setCreated(tbItem.getCreated());
		this.setUpdated(tbItem.getUpdated());
	}

	public String[] getImages() {
		String image2 = this.getImage();
		if (image2 != null && !"".equals(image2)) {
			String[] strings = image2.split(",");
			return strings;
		}
		return null;
	}
}

Dao层
查询tb_item, tb_item_desc两个表,都是单表查询。可以使用逆向工程。

interface层:

TbItem getItemById(long itemId);
TbItemDesc getItemDescById(long itemId);

Service层:
1、根据商品id查询商品信息
参数:商品id
返回值:TbItem
设置查询条件,将TbItem转换成item
下面代码是添加redis缓存:

@Override
	public TbItem getItemById(long itemId) {
		//查询缓存
		try {
			String json = jedisClient.get(REDIS_ITEM_PRE + ":" + itemId + ":BASE");
			if(StringUtils.isNotBlank(json)) {
				TbItem tbItem = JsonUtils.jsonToPojo(json, TbItem.class);
				return tbItem;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		//缓存中没有,查询数据库
		//根据主键查询
		//TbItem tbItem = itemMapper.selectByPrimaryKey(itemId);
		TbItemExample example = new TbItemExample();
		Criteria criteria = example.createCriteria();
		//设置查询条件
		criteria.andIdEqualTo(itemId);
		//执行查询
		List<TbItem> list = itemMapper.selectByExample(example);
		if (list != null && list.size() > 0) {
			//把结果添加到缓存
			try {
				jedisClient.set(REDIS_ITEM_PRE + ":" + itemId + ":BASE", JsonUtils.objectToJson(list.get(0)));
				//设置过期时间
				jedisClient.expire(REDIS_ITEM_PRE + ":" + itemId + ":BASE", ITEM_CACHE_EXPIRE);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return list.get(0);
		}
		return null;
	}

2、根据商品id查询商品描述
参数:商品id
返回值:TbItemDesc
下面代码添加了缓存:

@Override
	public TbItemDesc getItemDescById(long itemId) {
		//查询缓存
		try {
			String json = jedisClient.get(REDIS_ITEM_PRE + ":" + itemId + ":DESC");
			if(StringUtils.isNotBlank(json)) {
				TbItemDesc tbItemDesc = JsonUtils.jsonToPojo(json, TbItemDesc.class);
				return tbItemDesc;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		TbItemDesc itemDesc = itemDescMapper.selectByPrimaryKey(itemId);
		//把结果添加到缓存
		try {
			jedisClient.set(REDIS_ITEM_PRE + ":" + itemId + ":DESC", JsonUtils.objectToJson(itemDesc));
			//设置过期时间
			jedisClient.expire(REDIS_ITEM_PRE + ":" + itemId + ":DESC", ITEM_CACHE_EXPIRE);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return itemDesc;
	}

controller层:
请求的url:/item/{itemId}
参数:商品id
返回值:String 逻辑视图

/**
 * 商品详情页面展示Controller
 * <p>Title: ItemController</p>
 * <p>Description: </p>
 * <p>Company: www.itcast.cn</p> 
 * @version 1.0
 */
@Controller
public class ItemController {

	@Autowired
	private ItemService itemService;
	
	@RequestMapping("/item/{itemId}")
	public String showItemInfo(@PathVariable Long itemId, Model model) {
		//调用服务取商品基本信息
		TbItem tbItem = itemService.getItemById(itemId);
		Item item = new Item(tbItem);
		//取商品描述信息
		TbItemDesc itemDesc = itemService.getItemDescById(itemId);
		//把信息传递给页面
		model.addAttribute("item", item);
		model.addAttribute("itemDesc", itemDesc);
		//返回逻辑视图

		return "item";
	}
}

购物车模块:

未登录状态下使用购物车
功能分析
在不登陆的情况下也可以添加购物车。把购物车信息写入cookie。

请求的url:/cart/add/{itemId}
参数:
1)商品id: Long itemId
2)商品数量: int num
业务逻辑:
1、从cookie中查询商品列表。
2、判断商品在商品列表中是否存在。
3、如果存在,商品数量相加。
4、不存在,根据商品id查询商品信息。
5、把商品添加到购车列表。
6、把购车商品列表写入cookie。
返回值:逻辑视图 “cartSuccess”

从cookie中取出购物车商品列表

/**
	 * 从cookie中取购物车列表的处理
	 * <p>Title: getCartListFromCookie</p>
	 * <p>Description: </p>
	 * @param request
	 * @return
	 */
	private List<TbItem> getCartListFromCookie(HttpServletRequest request) {
		String json = CookieUtils.getCookieValue(request, "cart", true);
		//判断json是否为空
		if (StringUtils.isBlank(json)) {
			return new ArrayList<>();
		}
		//把json转换成商品列表
		List<TbItem> list = JsonUtils.jsonToList(json, TbItem.class);
		return list;
	}

没有登入,将商品添加到购物车,写入cookie中

/**
	 * 未登录状态,将商品添加到购物车, 写入cookie中
	 * 已登入状态,保存到redis服务中
	 * <p>Title: addCart</p>
	 * <p>Description: </p>
	 * @param itemId
	 * @param num
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping("/cart/add/{itemId}")
	public String addCart(@PathVariable Long itemId, @RequestParam(defaultValue="1")Integer num,
			HttpServletRequest request, HttpServletResponse response) {
		//判断用户是否登录
		TbUser user = (TbUser) request.getAttribute("user");
		//如果是登录状态,把购物车写入redis
		if (user != null) {
			//保存到服务端
			cartService.addCart(user.getId(), itemId, num);
			//返回逻辑视图
			return "cartSuccess";
		}
		//如果未登录使用cookie
		//从cookie中取购物车列表
		List<TbItem> cartList = getCartListFromCookie(request);
		//判断商品在商品列表中是否存在
		boolean flag = false;
		for (TbItem tbItem : cartList) {
			//如果存在数量相加
			if (tbItem.getId() == itemId.longValue()) {
				flag = true;
				//找到商品,数量相加
				tbItem.setNum(tbItem.getNum() + num);
				//跳出循环
				break;
			}
		}
		//如果不存在
		if (!flag) {
			//根据商品id查询商品信息。得到一个TbItem对象
			TbItem tbItem = itemService.getItemById(itemId);
			//设置商品数量
			tbItem.setNum(num);
			//取一张图片
			String image = tbItem.getImage();
			if (StringUtils.isNotBlank(image)) {
				tbItem.setImage(image.split(",")[0]);
			}
			//把商品添加到商品列表
			cartList.add(tbItem);
		}
		//写入cookie
		CookieUtils.setCookie(request, response, "cart", JsonUtils.objectToJson(cartList), COOKIE_CART_EXPIRE, true);
		//返回添加成功页面
		return "cartSuccess";
	}

修改购物车商品数量:
3.3.1.功能分析
1、在页面中可以修改商品数量
2、重新计算小计和总计。
3、修改需要写入cookie。
4、每次修改都需要向服务端发送一个ajax请求,在服务端修改cookie中的商品数量。

请求的url:/cart/update/num/{itemId}/{num}
参数:long itemId、int num
业务逻辑:
1、接收两个参数
2、从cookie中取商品列表
3、遍历商品列表找到对应商品
4、更新商品数量
5、把商品列表写入cookie。
6、响应e3Result。Json数据。
返回值:
e3Result。Json数据

    /**
	 * 登录状态和未登入状态,更新购物车商品数量
	 */
	@RequestMapping("/cart/update/num/{itemId}/{num}")
	@ResponseBody
	public E3Result updateCartNum(@PathVariable Long itemId, @PathVariable Integer num
			, HttpServletRequest request ,HttpServletResponse response) {
		//判断用户是否为登录状态
		TbUser user = (TbUser) request.getAttribute("user");
		if (user != null) {
			cartService.updateCartNum(user.getId(), itemId, num);
			return E3Result.ok();
		}
		//从cookie中取购物车列表
		List<TbItem> cartList = getCartListFromCookie(request);
		//遍历商品列表找到对应的商品
		for (TbItem tbItem : cartList) {
			if (tbItem.getId().longValue() == itemId) {
				//更新数量
				tbItem.setNum(num);
				break;
			}
		}
		//把购物车列表写回cookie
		CookieUtils.setCookie(request, response, "cart", JsonUtils.objectToJson(cartList), COOKIE_CART_EXPIRE, true);
		//返回成功
		return E3Result.ok();
	}

删除购物车商品:
功能分析
请求的url:/cart/delete/{itemId}
参数:商品id
返回值:展示购物车列表页面。Url需要做redirect跳转。
业务逻辑:
1、从url中取商品id
2、从cookie中取购物车商品列表
3、遍历列表找到对应的商品
4、删除商品。
5、把商品列表写入cookie。
6、返回逻辑视图:在逻辑视图中做redirect跳转。

    /**
	 * 登入状态和为登入状态删除购物车商品
	 */
	@RequestMapping("/cart/delete/{itemId}")
	public String deleteCartItem(@PathVariable Long itemId, HttpServletRequest request,
			HttpServletResponse response) {
		//判断用户是否为登录状态
		TbUser user = (TbUser) request.getAttribute("user");
		if (user != null) {
			cartService.deleteCartItem(user.getId(), itemId);
			return "redirect:/cart/cart.html";
		}
		//从cookie中取购物车列表
		List<TbItem> cartList = getCartListFromCookie(request);
		//遍历列表,找到要删除的商品
		for (TbItem tbItem : cartList) {
			if (tbItem.getId().longValue() == itemId) {
				//删除商品
				cartList.remove(tbItem);
				//跳出循环
				break;
			}
		}
		//把购物车列表写入cookie
		CookieUtils.setCookie(request, response, "cart", JsonUtils.objectToJson(cartList), COOKIE_CART_EXPIRE, true);
		//返回逻辑视图
		return "redirect:/cart/cart.html";
	}

登录状态下的购物车处理:

功能分析
1、购物车数据保存的位置:
未登录状态下,把购物车数据保存到cookie中。
登录状态下,需要把购物车数据保存到服务端。需要永久保存,可以保存到数据库中。可以把购物车数据保存到redis中。
2、redis使用的数据类型
a)使用hash数据类型
b)Hash的key应该是用户id。Hash中的field是商品id,value可以把商品信息转换成json
3、添加购物车
登录状态下直接包商品数据保存到redis中。
未登录状态保存到cookie中。
4、如何判断是否登录?
a)从cookie中取token
b)取不到未登录
c)取到token,到redis中查询token是否过期。
d)如果过期,未登录状态
e)没过期登录状态。

判断用户是否登录:
功能分析
应该使用拦截器实现。
1、实现一个HandlerInterceptor接口。
2、在执行handler方法之前做业务处理
3、从cookie中取token。使用CookieUtils工具类实现。
4、没有取到token,用户未登录。放行
5、取到token,调用sso系统的服务,根据token查询用户信息。
6、没有返回用户信息。登录已经过期,未登录,放行。
7、返回用户信息。用户是登录状态。可以把用户对象保存到request中,在Controller中可以通过判断request中是否包含用户对象,确定是否为登录状态。
拦截器相关代码:

tokenService相关代码:

/**
 * 根据token取用户信息
 * <p>Title: TokenServiceImpl</p>
 * <p>Description: </p>
 * <p>Company: www.itcast.cn</p> 
 * @version 1.0
 */
@Service
public class TokenServiceImpl implements TokenService {

	@Autowired
	private JedisClient jedisClient;
	@Value("${SESSION_EXPIRE}")
	private Integer SESSION_EXPIRE;
	
	@Override
	public E3Result getUserByToken(String token) {
		//根据token到redis中取用户信息
		String json = jedisClient.get("SESSION:" + token);
		//取不到用户信息,登录已经过期,返回登录过期
		if (StringUtils.isBlank(json)) {
			return E3Result.build(201, "用户登录已经过期");
		}
		//取到用户信息更新token的过期时间
		jedisClient.expire("SESSION:" + token, SESSION_EXPIRE);
		//返回结果,E3Result其中包含TbUser对象
		TbUser user = JsonUtils.jsonToPojo(json, TbUser.class);
		return E3Result.ok(user);
	}

}
/**
 * 用户登录处理拦截器
 * <p>Title: LoginInterceptor</p>
 * <p>Description: </p>
 * <p>Company: www.itcast.cn</p> 
 * @version 1.0
 */
public class LoginInterceptor implements HandlerInterceptor {
	
	@Autowired
	private TokenService tokenService;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		// 前处理,执行handler之前执行此方法。
		//返回true,放行	false:拦截
		//1.从cookie中取token
		String token = CookieUtils.getCookieValue(request, "token");
		//2.如果没有token,未登录状态,直接放行
		if (StringUtils.isBlank(token)) {
			return true;
		}
		//3.取到token,需要调用sso系统的服务,根据token取用户信息
		E3Result e3Result = tokenService.getUserByToken(token);
		//4.没有取到用户信息。登录过期,直接放行。
		if (e3Result.getStatus() != 200) {
			return true;
		}
		//5.取到用户信息。登录状态。
		TbUser user = (TbUser) e3Result.getData();
		//6.把用户信息放到request中。只需要在Controller中判断request中是否包含user信息。放行
		request.setAttribute("user", user);
		return true;
	}
	
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
			throws Exception {
		//handler执行之后,返回ModeAndView之前
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		//完成处理,返回ModelAndView之后。
		//可以再此处理异常
	}

}

需要在Springmvc.xml配置拦截器:

<!-- 拦截器配置 -->
	<mvc:interceptors>
		<mvc:interceptor>
			<mvc:mapping path="/**"/>
			<bean class="cn.e3mall.cart.interceptor.LoginInterceptor"/>
		</mvc:interceptor>
	</mvc:interceptors>

添加购物车:

功能分析
登录状态下添加购物车,直接把数据保存到redis中。需要调用购物车服务,
使用redis的hash来保存数据。
Key:用户id
Field:商品id
Value:商品对象转换成json

参数:
1、用户id
2、商品id
3、商品数量

业务逻辑:
1、根据商品id查询商品信息
2、把商品信息保存到redis
a)判断购物车中是否有此商品
b)如果有,数量相加
c)如果没有,根据商品id查询商品信息。
d)把商品信息添加到购物车
3、返回值。E3Result

dao层:
根据商品id查询商品信息,单表查询。可以使用逆向工程

interface层:

E3Result addCart(long userId, long itemId, int num);

service层:

/**
	 * 向redis中添加购物车。
	 * <p>Title: addCart</p>
	 * <p>Description: </p>
	 * @param userId
	 * @param itemId
	 * @param num
	 * @return
	 * @see cn.e3mall.cart.service.CartService#addCart(long, long, int)
	 */
	@Override
	public E3Result addCart(long userId, long itemId, int num) {
		//向redis中添加购物车。
		//数据类型是hash key:用户id field:商品id value:商品信息
		//判断商品是否存在
		Boolean hexists = jedisClient.hexists(REDIS_CART_PRE + ":" + userId, itemId + "");
		//如果存在数量相加
		if (hexists) {
			String json = jedisClient.hget(REDIS_CART_PRE + ":" + userId, itemId + "");
			//把json转换成TbItem
			TbItem item = JsonUtils.jsonToPojo(json, TbItem.class);
			item.setNum(item.getNum() + num);
			//写回redis
			jedisClient.hset(REDIS_CART_PRE + ":" + userId, itemId + "", JsonUtils.objectToJson(item));
			return E3Result.ok();
		}
		//如果不存在,根据商品id取商品信息
		TbItem item = itemMapper.selectByPrimaryKey(itemId);
		//设置购物车数据量
		item.setNum(num);
		//取一张图片
		String image = item.getImage();
		if (StringUtils.isNotBlank(image)) {
			item.setImage(image.split(",")[0]);
		}
		//添加到购物车列表
		jedisClient.hset(REDIS_CART_PRE + ":" + userId, itemId + "", JsonUtils.objectToJson(item));
		return E3Result.ok();
	}

controller层:
相关代码:

/**
	 * 未登录状态,将商品添加到购物车, 写入cookie中
	 * 已登入状态,保存到redis服务中
	 * <p>Title: addCart</p>
	 * <p>Description: </p>
	 * @param itemId
	 * @param num
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping("/cart/add/{itemId}")
	public String addCart(@PathVariable Long itemId, @RequestParam(defaultValue="1")Integer num,
			HttpServletRequest request, HttpServletResponse response) {
		//判断用户是否登录
		TbUser user = (TbUser) request.getAttribute("user");
		//如果是登录状态,把购物车写入redis
		if (user != null) {
			//保存到服务端
			cartService.addCart(user.getId(), itemId, num);
			//返回逻辑视图
			return "cartSuccess";
		}

登录状态下访问购物车列表:

功能分析

1、未登录状态下购物车列表是从cookie中取。
2、登录状态下购物车应该是从服务端取。
3、如果cookie中有购物车数据,应该吧cookie中的购物车和服务端合并,
合并后删除cookie中的购物车数据。
4、合并购物车时,如果商品存在,数量相加,如果不存在,添加一个新的商品。
5、从服务端取购物车列表展示

Dao层

不需要访问数据库,只需要访问redis。

interface层:

E3Result mergeCart(long userId, List<TbItem> itemList);
List<TbItem> getCartList(long userId);

Service层:

1、合并购物车
参数:用户id
      List<TbItem>
返回值:E3Result
业务逻辑:
1)遍历商品列表
2)如果服务端有相同商品,数量相加
3)如果没有相同商品,添加一个新的商品

2、取购物车列表
参数:用户id
返回值:List<TbItem>
业务逻辑:
1)从hash中取所有商品数据
2)返回
@Override
	public E3Result mergeCart(long userId, List<TbItem> itemList) {
		//遍历商品列表
		//把列表添加到购物车。
		//判断购物车中是否有此商品
		//如果有,数量相加
		//如果没有添加新的商品
		for (TbItem tbItem : itemList) {
			addCart(userId, tbItem.getId(), tbItem.getNum());
		}
		//返回成功
		return E3Result.ok();
	}

	@Override
	public List<TbItem> getCartList(long userId) {
		//根据用户id查询购车列表
		List<String> jsonList = jedisClient.hvals(REDIS_CART_PRE + ":" + userId);
		List<TbItem> itemList = new ArrayList<>();
		for (String string : jsonList) {
			//创建一个TbItem对象
			TbItem item = JsonUtils.jsonToPojo(string, TbItem.class);
			//添加到列表
			itemList.add(item);
		}
		return itemList;
	}

controller层:

1、判断用户是否登录。
2、如果已经登录,判断cookie中是否有购物车信息
3、如果有合并购物车,并删除cookie中的购物车。
4、如果是登录状态,应从服务端取购物车列表。
5、如果是未登录状态,从cookie中取购物车列表
/**
	 * 从cookie中取购物车列表的处理
	 * <p>Title: getCartListFromCookie</p>
	 * <p>Description: </p>
	 * @param request
	 * @return
	 */
	private List<TbItem> getCartListFromCookie(HttpServletRequest request) {
		String json = CookieUtils.getCookieValue(request, "cart", true);
		//判断json是否为空
		if (StringUtils.isBlank(json)) {
			return new ArrayList<>();
		}
		//把json转换成商品列表
		List<TbItem> list = JsonUtils.jsonToList(json, TbItem.class);
		return list;
	}
	
	/**
	 * 展示购物车列表
	 * <p>Title: showCatList</p>
	 * <p>Description: </p>
	 * @param request
	 * @return
	 */
	@RequestMapping("/cart/cart")
	public String showCatList(HttpServletRequest request, HttpServletResponse response) {
		//从cookie中取购物车列表
		List<TbItem> cartList = getCartListFromCookie(request);
		//判断用户是否为登录状态
		TbUser user = (TbUser) request.getAttribute("user");
		//如果是登录状态
		if (user != null) {
			//从cookie中取购物车列表
			//如果不为空,把cookie中的购物车商品和服务端的购物车商品合并。
			cartService.mergeCart(user.getId(), cartList);
			//把cookie中的购物车删除
			CookieUtils.deleteCookie(request, response, "cart");
			//从服务端取购物车列表
			cartList = cartService.getCartList(user.getId());
			
		}
		//把列表传递给页面
		request.setAttribute("cartList", cartList);
		//返回逻辑视图
		return "cart";
	}

修改购物车数量:
dao层

只需要更新hash中商品的数量即可。
不需要对数据库进行操作,只需要对redis操作即可。

interface:

E3Result updateCartNum(long userId, long itemId, int num);

service层:

参数:
1、用户id
2、商品id
3、数量
返回值:
E3Result
业务逻辑:
1、根据商品id从hash中取商品信息。
2、把json转换成java对象
3、更新商品数量
4、把商品数据写回hash
@Override
	public E3Result updateCartNum(long userId, long itemId, int num) {
		//从redis中取商品信息
		String json = jedisClient.hget(REDIS_CART_PRE + ":" + userId, itemId + "");
		//更新商品数量
		TbItem tbItem = JsonUtils.jsonToPojo(json, TbItem.class);
		tbItem.setNum(num);
		//写入redis
		jedisClient.hset(REDIS_CART_PRE + ":" + userId, itemId + "", JsonUtils.objectToJson(tbItem));
		return E3Result.ok();
	}

controller层:

1、判断是否为登录状态
2、如果是登录状态,更新服务端商品数量
3、如果未登录,更新cookie中是商品数量
/**
	 * 登录状态和未登入状态,更新购物车商品数量
	 */
	@RequestMapping("/cart/update/num/{itemId}/{num}")
	@ResponseBody
	public E3Result updateCartNum(@PathVariable Long itemId, @PathVariable Integer num
			, HttpServletRequest request ,HttpServletResponse response) {
		//判断用户是否为登录状态
		TbUser user = (TbUser) request.getAttribute("user");
		if (user != null) {
			cartService.updateCartNum(user.getId(), itemId, num);
			return E3Result.ok();
		}

删除购物车商品:

业务逻辑

1、判断是否为登录状态
2、如果是登录状态,直接删除hash中的商品。
3、如果不是登录状态,对cookie中的购物车进行操作

interface:

E3Result deleteCartItem(long userId, long itemId);

Service

参数:用户id
      商品id
返回值:E3Result
业务逻辑:
根据商品id删除hash中对应的商品数据。
@Override
	public E3Result deleteCartItem(long userId, long itemId) {
		// 删除购物车商品
		jedisClient.hdel(REDIS_CART_PRE + ":" + userId, itemId + "");
		return E3Result.ok();
	}

controller:

1、判断用户登录状态
2、如果登录删除服务端
3、如果未登录删除cookie中的购物车商品
/**
	 * 登入状态和为登入状态删除购物车商品
	 */
	@RequestMapping("/cart/delete/{itemId}")
	public String deleteCartItem(@PathVariable Long itemId, HttpServletRequest request,
			HttpServletResponse response) {
		//判断用户是否为登录状态
		TbUser user = (TbUser) request.getAttribute("user");
		if (user != null) {
			cartService.deleteCartItem(user.getId(), itemId);
			return "redirect:/cart/cart.html";
		}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值