39 changed files with 1258 additions and 15 deletions
@ -0,0 +1,33 @@ |
|||
package cn.nla.common.config; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; |
|||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
|||
|
|||
/** |
|||
* Redis配置:解决乱码问题 |
|||
*/ |
|||
@Configuration |
|||
public class RedisConfig { |
|||
@Bean |
|||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { |
|||
RedisTemplate<String, Object> template = new RedisTemplate<>(); |
|||
template.setConnectionFactory(redisConnectionFactory); |
|||
// 使用StringRedisSerializer序列化键
|
|||
template.setKeySerializer(new StringRedisSerializer()); |
|||
// 使用GenericJackson2JsonRedisSerializer序列化值
|
|||
ObjectMapper om = new ObjectMapper(); |
|||
// 可以自定义ObjectMapper配置,确保满足你项目的需求
|
|||
template.setValueSerializer(new GenericJackson2JsonRedisSerializer(om)); |
|||
// 设置hash的键和值的序列化器
|
|||
template.setHashKeySerializer(new StringRedisSerializer()); |
|||
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer(om)); |
|||
// 使序列化器生效
|
|||
template.afterPropertiesSet(); |
|||
return template; |
|||
} |
|||
} |
@ -0,0 +1,25 @@ |
|||
package cn.nla.common.model; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 分页响应对象 |
|||
*/ |
|||
@Data |
|||
@AllArgsConstructor |
|||
public class PageResult<T> { |
|||
|
|||
@JsonProperty("total_record") |
|||
private Long totalRecord; |
|||
|
|||
@JsonProperty("total_page") |
|||
private Long totalPage; |
|||
|
|||
@JsonProperty("current_data") |
|||
private List<T> currentData; |
|||
|
|||
} |
@ -0,0 +1,67 @@ |
|||
package cn.nla.coupon.model.VO; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonFormat; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* 个人领劵信息 |
|||
*/ |
|||
@Data |
|||
public class CouponRecordVO { |
|||
private Long id; |
|||
/** |
|||
* 优惠券id |
|||
*/ |
|||
@JsonProperty("coupon_id") |
|||
private Long couponId; |
|||
/** |
|||
* 使用状态 可用 NEW,已使用USED,过期 EXPIRED; |
|||
*/ |
|||
@JsonProperty("use_state") |
|||
private String useState; |
|||
/** |
|||
* 用户id |
|||
*/ |
|||
@JsonProperty("user_id") |
|||
private Long userId; |
|||
/** |
|||
* 用户昵称 |
|||
*/ |
|||
@JsonProperty("user_name") |
|||
private String userName; |
|||
/** |
|||
* 优惠券标题 |
|||
*/ |
|||
@JsonProperty("coupon_title") |
|||
private String couponTitle; |
|||
/** |
|||
* 开始时间 |
|||
*/ |
|||
@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss", locale = "zh", timezone = "GMT+8") |
|||
@JsonProperty("start_time") |
|||
private Date startTime; |
|||
/** |
|||
* 结束时间 |
|||
*/ |
|||
@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss", locale = "zh", timezone = "GMT+8") |
|||
@JsonProperty("end_time") |
|||
private Date endTime; |
|||
/** |
|||
* 订单id |
|||
*/ |
|||
@JsonProperty("order_id") |
|||
private Long orderId; |
|||
/** |
|||
* 抵扣价格 |
|||
*/ |
|||
private BigDecimal price; |
|||
/** |
|||
* 满多少才可以使用 |
|||
*/ |
|||
@JsonProperty("condition_price") |
|||
private BigDecimal conditionPrice; |
|||
} |
@ -0,0 +1,20 @@ |
|||
package cn.nla.coupon.model.request; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* 新用户注册发放优惠卷 |
|||
*/ |
|||
@ApiModel(value = "新用户注册发放优惠卷对象", description = "新用户注册发放优惠卷请求对象") |
|||
@Data |
|||
public class NewUserCouponRequest { |
|||
@ApiModelProperty(value = "用户Id", example = "19") |
|||
@JsonProperty("user_id") |
|||
private long userId; |
|||
@ApiModelProperty(value = "名称", example = "二当家") |
|||
@JsonProperty("name") |
|||
private String name; |
|||
} |
@ -0,0 +1,28 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<parent> |
|||
<artifactId>nla-shop</artifactId> |
|||
<groupId>cn.nla</groupId> |
|||
<version>1.0-SNAPSHOT</version> |
|||
</parent> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<artifactId>nla-product-service</artifactId> |
|||
<description>商品服务模块</description> |
|||
<properties> |
|||
<maven.compiler.source>11</maven.compiler.source> |
|||
<maven.compiler.target>11</maven.compiler.target> |
|||
</properties> |
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>cn.nla</groupId> |
|||
<artifactId>nla-common</artifactId> |
|||
<version>1.0-SNAPSHOT</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.projectlombok</groupId> |
|||
<artifactId>lombok</artifactId> |
|||
</dependency> |
|||
</dependencies> |
|||
</project> |
@ -0,0 +1,15 @@ |
|||
package cn.nla.product; |
|||
|
|||
import org.mybatis.spring.annotation.MapperScan; |
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
import org.springframework.context.annotation.ComponentScan; |
|||
|
|||
@SpringBootApplication |
|||
@MapperScan("cn.nla.*.mapper") |
|||
@ComponentScan(basePackages = {"cn.nla.*"}) |
|||
public class ProductApplication { |
|||
public static void main(String[] args) { |
|||
SpringApplication.run(ProductApplication.class, args); |
|||
} |
|||
} |
@ -0,0 +1,38 @@ |
|||
package cn.nla.product.controller; |
|||
|
|||
|
|||
import cn.nla.common.util.JsonData; |
|||
import cn.nla.product.service.BannerService; |
|||
import io.swagger.annotations.Api; |
|||
import io.swagger.annotations.ApiOperation; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.GetMapping; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
|
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import javax.annotation.Resource; |
|||
|
|||
/** |
|||
* <p> |
|||
* banner表 前端控制器 |
|||
* </p> |
|||
* |
|||
* @author YJs |
|||
* @since 2024-08-09 |
|||
*/ |
|||
@Api(tags = "轮播图控制器") |
|||
@RestController |
|||
@RequestMapping("/pdt/banner/v1") |
|||
public class BannerController { |
|||
|
|||
@Resource |
|||
private BannerService bannerService; |
|||
|
|||
@ApiOperation("轮播图列表接口") |
|||
@GetMapping("list") |
|||
public JsonData list() { |
|||
return JsonData.buildSuccess(bannerService.list()); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,56 @@ |
|||
package cn.nla.product.controller; |
|||
|
|||
import cn.nla.common.util.JsonData; |
|||
import cn.nla.product.model.request.CartItemRequest; |
|||
import cn.nla.product.service.CartService; |
|||
import io.swagger.annotations.Api; |
|||
import io.swagger.annotations.ApiOperation; |
|||
import io.swagger.annotations.ApiParam; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import javax.annotation.Resource; |
|||
|
|||
/** |
|||
* 购物车前端控制器 |
|||
*/ |
|||
@Api(tags = "购物车控制器") |
|||
@RestController |
|||
@RequestMapping("/pdt/cart/v1") |
|||
public class CartController { |
|||
@Resource |
|||
private CartService cartService; |
|||
|
|||
@ApiOperation("添加到购物车") |
|||
@PostMapping("add") |
|||
public JsonData addToCart(@ApiParam("购物项") @RequestBody CartItemRequest cartItemRequest) { |
|||
cartService.addToCart(cartItemRequest); |
|||
return JsonData.buildSuccess(); |
|||
} |
|||
|
|||
@ApiOperation("清空购物车") |
|||
@DeleteMapping("/clear") |
|||
public JsonData cleanMyCart() { |
|||
cartService.clear(); |
|||
return JsonData.buildSuccess(); |
|||
} |
|||
|
|||
@ApiOperation("查看我的购物车") |
|||
@GetMapping("/my-cart") |
|||
public JsonData findMyCart() { |
|||
return JsonData.buildSuccess(cartService.getMyCart()); |
|||
} |
|||
|
|||
@ApiOperation("修改购物车数量") |
|||
@PostMapping("change") |
|||
public JsonData changeItemNum(@ApiParam("购物项") @RequestBody CartItemRequest cartItemRequest) { |
|||
cartService.changeItemNum(cartItemRequest); |
|||
return JsonData.buildSuccess(); |
|||
} |
|||
|
|||
@ApiOperation("删除购物项") |
|||
@DeleteMapping("/delete/{product_id}") |
|||
public JsonData deleteItem(@ApiParam(value = "商品id", required = true) @PathVariable("product_id") long productId) { |
|||
cartService.deleteItem(productId); |
|||
return JsonData.buildSuccess(); |
|||
} |
|||
} |
@ -0,0 +1,49 @@ |
|||
package cn.nla.product.controller; |
|||
|
|||
|
|||
import cn.nla.common.util.JsonData; |
|||
import cn.nla.product.model.VO.ProductVO; |
|||
import cn.nla.product.service.ProductService; |
|||
import io.swagger.annotations.Api; |
|||
import io.swagger.annotations.ApiOperation; |
|||
import io.swagger.annotations.ApiParam; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import javax.annotation.Resource; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* <p> |
|||
* 商品表 前端控制器 |
|||
* </p> |
|||
* |
|||
* @author YJs |
|||
* @since 2024-08-09 |
|||
*/ |
|||
@Api(tags = "商品控制器") |
|||
@RestController |
|||
@RequestMapping("//pdt/product/v1") |
|||
public class ProductController { |
|||
|
|||
@Resource |
|||
private ProductService productService; |
|||
|
|||
@ApiOperation("分页查询商品列表") |
|||
@GetMapping("page") |
|||
public JsonData pageProductList( |
|||
@ApiParam(value = "当前页") @RequestParam(value = "page", defaultValue = "1") int page, |
|||
@ApiParam(value = "每页显示多少条") @RequestParam(value = "size", defaultValue = "10") int size) { |
|||
|
|||
return JsonData.buildSuccess(productService.page(page, size)); |
|||
} |
|||
|
|||
|
|||
@ApiOperation("商品详情") |
|||
@GetMapping("/detail/{product_id}") |
|||
public JsonData detail(@ApiParam(value = "商品id", required = true) @PathVariable("product_id") long productId) { |
|||
return JsonData.buildSuccess(productService.findDetailById(productId)); |
|||
} |
|||
|
|||
} |
|||
|
@ -0,0 +1,16 @@ |
|||
package cn.nla.product.mapper; |
|||
|
|||
import cn.nla.product.model.entity.BannerEntity; |
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
|
|||
/** |
|||
* <p> |
|||
* banner表 Mapper 接口 |
|||
* </p> |
|||
* |
|||
* @author YJs |
|||
* @since 2024-08-09 |
|||
*/ |
|||
public interface BannerMapper extends BaseMapper<BannerEntity> { |
|||
|
|||
} |
@ -0,0 +1,16 @@ |
|||
package cn.nla.product.mapper; |
|||
|
|||
import cn.nla.product.model.entity.ProductEntity; |
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
|
|||
/** |
|||
* <p> |
|||
* 商品表 Mapper 接口 |
|||
* </p> |
|||
* |
|||
* @author YJs |
|||
* @since 2024-08-09 |
|||
*/ |
|||
public interface ProductMapper extends BaseMapper<ProductEntity> { |
|||
|
|||
} |
@ -0,0 +1,42 @@ |
|||
package cn.nla.product.model.VO; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
|
|||
import java.math.BigDecimal; |
|||
|
|||
/** |
|||
* 购物项 |
|||
*/ |
|||
@Data |
|||
public class CartItemVO { |
|||
/** |
|||
* 商品id |
|||
*/ |
|||
@JsonProperty("product_id") |
|||
private Long productId; |
|||
/** |
|||
* 购买数量 |
|||
*/ |
|||
@JsonProperty("buy_num") |
|||
private Integer buyNum; |
|||
/** |
|||
* 商品标题 |
|||
*/ |
|||
@JsonProperty("product_title") |
|||
private String productTitle; |
|||
/** |
|||
* 图片 |
|||
*/ |
|||
@JsonProperty("product_img") |
|||
private String productImg; |
|||
/** |
|||
* 商品单价 |
|||
*/ |
|||
private BigDecimal amount; |
|||
/** |
|||
* 总价格,单价+数量 |
|||
*/ |
|||
@JsonProperty("total_amount") |
|||
private BigDecimal totalAmount; |
|||
} |
@ -0,0 +1,72 @@ |
|||
package cn.nla.product.model.VO; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 购物车 |
|||
*/ |
|||
@Data |
|||
public class CartVO { |
|||
/** |
|||
* 购物项 |
|||
*/ |
|||
@JsonProperty("cart_items") |
|||
private List<CartItemVO> cartItems; |
|||
/** |
|||
* 购买总件数 |
|||
*/ |
|||
@JsonProperty("total_num") |
|||
private Integer totalNum; |
|||
/** |
|||
* 购物车总价格 |
|||
*/ |
|||
@JsonProperty("total_price") |
|||
private BigDecimal totalPrice; |
|||
/** |
|||
* 购物车实际支付价格 |
|||
*/ |
|||
@JsonProperty("real_pay_price") |
|||
private BigDecimal realPayPrice; |
|||
|
|||
/** |
|||
* 总件数 |
|||
*/ |
|||
public Integer getTotalNum() { |
|||
if (this.cartItems != null) { |
|||
return cartItems.stream().mapToInt(CartItemVO::getBuyNum).sum(); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
/** |
|||
* 总价格 |
|||
*/ |
|||
public BigDecimal getTotalPrice() { |
|||
BigDecimal amount = new BigDecimal("0"); |
|||
if (this.cartItems != null) { |
|||
for (CartItemVO cartItemVO : cartItems) { |
|||
BigDecimal itemTotalAmount = cartItemVO.getTotalAmount(); |
|||
amount = amount.add(itemTotalAmount); |
|||
} |
|||
} |
|||
return amount; |
|||
} |
|||
|
|||
/** |
|||
* 购物车里面实际支付的价格 |
|||
*/ |
|||
public BigDecimal getRealPayPrice() { |
|||
BigDecimal amount = new BigDecimal("0"); |
|||
if (this.cartItems != null) { |
|||
for (CartItemVO cartItemVO : cartItems) { |
|||
BigDecimal itemTotalAmount = cartItemVO.getTotalAmount(); |
|||
amount = amount.add(itemTotalAmount); |
|||
} |
|||
} |
|||
return amount; |
|||
} |
|||
} |
@ -0,0 +1,37 @@ |
|||
package cn.nla.product.model.VO; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
|
|||
import java.math.BigDecimal; |
|||
|
|||
@Data |
|||
public class ProductVO { |
|||
private Long id; |
|||
/** |
|||
* 标题 |
|||
*/ |
|||
private String title; |
|||
/** |
|||
* 封面图 |
|||
*/ |
|||
@JsonProperty("cover_img") |
|||
private String coverImg; |
|||
/** |
|||
* 详情 |
|||
*/ |
|||
private String detail; |
|||
/** |
|||
* 老价格 |
|||
*/ |
|||
@JsonProperty("old_amount") |
|||
private BigDecimal oldAmount; |
|||
/** |
|||
* 新价格 |
|||
*/ |
|||
private BigDecimal amount; |
|||
/** |
|||
* 库存 |
|||
*/ |
|||
private Integer stock; |
|||
} |
@ -0,0 +1,44 @@ |
|||
package cn.nla.product.model.entity; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
import com.baomidou.mybatisplus.annotation.IdType; |
|||
import com.baomidou.mybatisplus.annotation.TableId; |
|||
import java.io.Serializable; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
|
|||
/** |
|||
* <p> |
|||
* banner表 |
|||
* </p> |
|||
* |
|||
* @author YJs |
|||
* @since 2024-08-09 |
|||
*/ |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = false) |
|||
@TableName("banner") |
|||
public class BannerEntity implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@TableId(value = "id", type = IdType.AUTO) |
|||
private Integer id; |
|||
|
|||
/** |
|||
* 图片 |
|||
*/ |
|||
private String img; |
|||
|
|||
/** |
|||
* 跳转地址 |
|||
*/ |
|||
private String url; |
|||
|
|||
/** |
|||
* 权重 |
|||
*/ |
|||
private Integer weight; |
|||
|
|||
|
|||
} |
@ -0,0 +1,71 @@ |
|||
package cn.nla.product.model.entity; |
|||
|
|||
import java.math.BigDecimal; |
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
import com.baomidou.mybatisplus.annotation.IdType; |
|||
import java.util.Date; |
|||
import com.baomidou.mybatisplus.annotation.TableId; |
|||
import java.io.Serializable; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
|
|||
/** |
|||
* <p> |
|||
* 商品表 |
|||
* </p> |
|||
* |
|||
* @author YJs |
|||
* @since 2024-08-09 |
|||
*/ |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = false) |
|||
@TableName("product") |
|||
public class ProductEntity implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@TableId(value = "id", type = IdType.AUTO) |
|||
private Long id; |
|||
|
|||
/** |
|||
* 标题 |
|||
*/ |
|||
private String title; |
|||
|
|||
/** |
|||
* 封面图 |
|||
*/ |
|||
private String coverImg; |
|||
|
|||
/** |
|||
* 详情 |
|||
*/ |
|||
private String detail; |
|||
|
|||
/** |
|||
* 老价格 |
|||
*/ |
|||
private BigDecimal oldPrice; |
|||
|
|||
/** |
|||
* 新价格 |
|||
*/ |
|||
private BigDecimal price; |
|||
|
|||
/** |
|||
* 库存 |
|||
*/ |
|||
private Integer stock; |
|||
|
|||
/** |
|||
* 创建时间 |
|||
*/ |
|||
private Date createTime; |
|||
|
|||
/** |
|||
* 锁定库存 |
|||
*/ |
|||
private Integer lockStock; |
|||
|
|||
|
|||
} |
@ -0,0 +1,20 @@ |
|||
package cn.nla.product.model.request; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* 添加购物车对象 |
|||
*/ |
|||
@ApiModel(value = "添加购物车对象", description = "添加购物车请求对象") |
|||
@Data |
|||
public class CartItemRequest { |
|||
@ApiModelProperty(value = "商品id", example = "11") |
|||
@JsonProperty("product_id") |
|||
private long productId; |
|||
@ApiModelProperty(value = "购买数量", example = "1") |
|||
@JsonProperty("buy_num") |
|||
private int buyNum; |
|||
} |
@ -0,0 +1,16 @@ |
|||
package cn.nla.product.service; |
|||
|
|||
import cn.nla.product.model.entity.BannerEntity; |
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
|
|||
/** |
|||
* <p> |
|||
* banner表 服务类 |
|||
* </p> |
|||
* |
|||
* @author YJs |
|||
* @since 2024-08-09 |
|||
*/ |
|||
public interface BannerService extends IService<BannerEntity> { |
|||
|
|||
} |
@ -0,0 +1,32 @@ |
|||
package cn.nla.product.service; |
|||
|
|||
|
|||
import cn.nla.product.model.VO.CartVO; |
|||
import cn.nla.product.model.request.CartItemRequest; |
|||
|
|||
public interface CartService { |
|||
/** |
|||
* 添加到购物车(一直缓存到redis中,没有创建购物车数据表) |
|||
*/ |
|||
void addToCart(CartItemRequest cartItemRequest); |
|||
|
|||
/** |
|||
* 清空购物车 |
|||
*/ |
|||
void clear(); |
|||
|
|||
/** |
|||
* 查询 |
|||
*/ |
|||
CartVO getMyCart(); |
|||
|
|||
/** |
|||
* 修改 |
|||
*/ |
|||
void changeItemNum(CartItemRequest cartItemRequest); |
|||
|
|||
/** |
|||
* 删除 |
|||
*/ |
|||
void deleteItem(long productId); |
|||
} |
@ -0,0 +1,33 @@ |
|||
package cn.nla.product.service; |
|||
|
|||
import cn.nla.common.model.PageResult; |
|||
import cn.nla.product.model.VO.ProductVO; |
|||
import cn.nla.product.model.entity.ProductEntity; |
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* <p> |
|||
* 商品表 服务类 |
|||
* </p> |
|||
* |
|||
* @author YJs |
|||
* @since 2024-08-09 |
|||
*/ |
|||
public interface ProductService extends IService<ProductEntity> { |
|||
/** |
|||
* 根据id找商品详情 |
|||
**/ |
|||
ProductVO findDetailById(Long productId); |
|||
|
|||
/** |
|||
* 分页查询商品列表 |
|||
**/ |
|||
PageResult<ProductVO> page(int page, int size); |
|||
|
|||
/** |
|||
* 批量查询 |
|||
*/ |
|||
List<ProductVO> findProductsByIdBatch(List<Long> productIdList); |
|||
} |
@ -0,0 +1,20 @@ |
|||
package cn.nla.product.service.impl; |
|||
|
|||
import cn.nla.product.model.entity.BannerEntity; |
|||
import cn.nla.product.mapper.BannerMapper; |
|||
import cn.nla.product.service.BannerService; |
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* <p> |
|||
* banner表 服务实现类 |
|||
* </p> |
|||
* |
|||
* @author YJs |
|||
* @since 2024-08-09 |
|||
*/ |
|||
@Service |
|||
public class BannerServiceImpl extends ServiceImpl<BannerMapper, BannerEntity> implements BannerService { |
|||
|
|||
} |
@ -0,0 +1,163 @@ |
|||
package cn.nla.product.service.impl; |
|||
|
|||
import cn.nla.common.constant.CacheKey; |
|||
import cn.nla.common.enums.BizCodeEnum; |
|||
import cn.nla.common.exception.BizException; |
|||
import cn.nla.common.interceptor.LoginInterceptor; |
|||
import cn.nla.common.model.LoginUser; |
|||
import cn.nla.product.model.VO.CartItemVO; |
|||
import cn.nla.product.model.VO.CartVO; |
|||
import cn.nla.product.model.VO.ProductVO; |
|||
import cn.nla.product.model.request.CartItemRequest; |
|||
import cn.nla.product.service.CartService; |
|||
import cn.nla.product.service.ProductService; |
|||
import com.alibaba.fastjson.JSON; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.data.redis.core.BoundHashOperations; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import javax.annotation.Resource; |
|||
import java.math.BigDecimal; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.function.Function; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* 购物车 |
|||
*/ |
|||
@Service |
|||
public class CartServiceImpl implements CartService { |
|||
@Resource |
|||
private ProductService productService; |
|||
@Resource |
|||
private RedisTemplate redisTemplate; |
|||
|
|||
@Override |
|||
public void addToCart(CartItemRequest cartItemRequest) { |
|||
long productId = cartItemRequest.getProductId(); |
|||
int buyNum = cartItemRequest.getBuyNum(); |
|||
//获取购物车
|
|||
BoundHashOperations<String, Object, Object> myCart = getMyCartOps(); |
|||
Object cacheObj = myCart.get(String.valueOf(productId)); |
|||
String result = ""; |
|||
if (cacheObj != null) { |
|||
result = (String) cacheObj; |
|||
} |
|||
if (StringUtils.isBlank(result)) { |
|||
//不存在则新建一个商品
|
|||
CartItemVO cartItemVO = new CartItemVO(); |
|||
ProductVO product = productService.findDetailById(productId); |
|||
if (product == null) { |
|||
throw new BizException(BizCodeEnum.CART_FAIL); |
|||
} |
|||
cartItemVO.setAmount(product.getAmount()); |
|||
cartItemVO.setBuyNum(buyNum); |
|||
cartItemVO.setProductId(productId); |
|||
cartItemVO.setProductImg(product.getCoverImg()); |
|||
cartItemVO.setProductTitle(product.getTitle()); |
|||
cartItemVO.setTotalAmount(product.getAmount().multiply(new BigDecimal(buyNum))); |
|||
myCart.put(String.valueOf(productId), JSON.toJSONString(cartItemVO)); |
|||
} else { |
|||
//存在商品,修改数量
|
|||
CartItemVO cartItem = JSON.parseObject(result, CartItemVO.class); |
|||
cartItem.setBuyNum(cartItem.getBuyNum() + buyNum); |
|||
myCart.put(String.valueOf(productId), JSON.toJSONString(cartItem)); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void clear() { |
|||
redisTemplate.delete(getCartKey()); |
|||
|
|||
} |
|||
|
|||
/** |
|||
* 删除购物项 |
|||
*/ |
|||
@Override |
|||
public void deleteItem(long productId) { |
|||
getMyCartOps().delete(String.valueOf(productId)); |
|||
} |
|||
|
|||
@Override |
|||
public void changeItemNum(CartItemRequest cartItemRequest) { |
|||
BoundHashOperations<String, Object, Object> mycart = getMyCartOps(); |
|||
Object cacheObj = mycart.get(String.valueOf(cartItemRequest.getProductId())); |
|||
if (cacheObj == null) { |
|||
throw new BizException(BizCodeEnum.CART_FAIL); |
|||
} |
|||
String obj = (String) cacheObj; |
|||
CartItemVO cartItemVO = JSON.parseObject(obj, CartItemVO.class); |
|||
cartItemVO.setBuyNum(cartItemRequest.getBuyNum()); |
|||
cartItemVO.setTotalAmount(cartItemVO.getAmount().multiply(new BigDecimal(cartItemVO.getBuyNum()))); |
|||
mycart.put(String.valueOf(cartItemRequest.getProductId()), JSON.toJSONString(cartItemVO)); |
|||
} |
|||
|
|||
@Override |
|||
public CartVO getMyCart() { |
|||
//获取全部购物项
|
|||
List<CartItemVO> cartItemVOList = buildCartItem(false); |
|||
CartVO cartVO = new CartVO(); |
|||
cartVO.setCartItems(cartItemVOList); |
|||
return cartVO; |
|||
} |
|||
|
|||
/** |
|||
* 获取最新的购物项, |
|||
* |
|||
* @param latestPrice 是否获取最新价格 |
|||
*/ |
|||
private List<CartItemVO> buildCartItem(boolean latestPrice) { |
|||
BoundHashOperations<String, Object, Object> myCart = getMyCartOps(); |
|||
List<Object> itemList = myCart.values(); |
|||
List<CartItemVO> cartItemVOList = new ArrayList<>(); |
|||
//拼接id列表查询最新价格
|
|||
List<Long> productIdList = new ArrayList<>(); |
|||
assert itemList != null; |
|||
for (Object item : itemList) { |
|||
CartItemVO cartItemVO = JSON.parseObject((String) item, CartItemVO.class); |
|||
cartItemVOList.add(cartItemVO); |
|||
productIdList.add(cartItemVO.getProductId()); |
|||
} |
|||
//查询最新的商品价格
|
|||
if (latestPrice) { |
|||
setProductLatestPrice(cartItemVOList, productIdList); |
|||
} |
|||
return cartItemVOList; |
|||
} |
|||
|
|||
/** |
|||
* 设置商品最新价格 |
|||
*/ |
|||
private void setProductLatestPrice(List<CartItemVO> cartItemVOList, List<Long> productIdList) { |
|||
//批量查询
|
|||
List<ProductVO> productVOList = productService.findProductsByIdBatch(productIdList); |
|||
//分组
|
|||
Map<Long, ProductVO> maps = productVOList.stream().collect(Collectors.toMap(ProductVO::getId, Function.identity())); |
|||
cartItemVOList.forEach(item -> { |
|||
ProductVO productVO = maps.get(item.getProductId()); |
|||
item.setProductTitle(productVO.getTitle()); |
|||
item.setProductImg(productVO.getCoverImg()); |
|||
item.setAmount(productVO.getAmount()); |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 抽取我的购物车,通用方法 |
|||
*/ |
|||
private BoundHashOperations<String, Object, Object> getMyCartOps() { |
|||
return redisTemplate.boundHashOps(getCartKey()); |
|||
} |
|||
|
|||
/** |
|||
* 购物车 key |
|||
*/ |
|||
private String getCartKey() { |
|||
LoginUser loginUser = LoginInterceptor.threadLocal.get(); |
|||
return String.format(CacheKey.CART_KEY, loginUser.getId()); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,56 @@ |
|||
package cn.nla.product.service.impl; |
|||
|
|||
import cn.nla.common.model.PageResult; |
|||
import cn.nla.product.model.VO.ProductVO; |
|||
import cn.nla.product.model.entity.ProductEntity; |
|||
import cn.nla.product.mapper.ProductMapper; |
|||
import cn.nla.product.service.ProductService; |
|||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|||
import com.baomidou.mybatisplus.core.metadata.IPage; |
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|||
import org.springframework.beans.BeanUtils; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* <p> |
|||
* 商品表 服务实现类 |
|||
* </p> |
|||
* |
|||
* @author YJs |
|||
* @since 2024-08-09 |
|||
*/ |
|||
@Service |
|||
public class ProductServiceImpl extends ServiceImpl<ProductMapper, ProductEntity> implements ProductService { |
|||
@Override |
|||
public ProductVO findDetailById(Long productId) { |
|||
return beanProcess(baseMapper.selectById(productId)); |
|||
} |
|||
|
|||
public PageResult<ProductVO> page(int page, int size) { |
|||
Page<ProductEntity> pageInfo = new Page<>(page, size); |
|||
IPage<ProductEntity> productPage = baseMapper.selectPage(pageInfo, null); |
|||
return new PageResult<>(productPage.getTotal(), productPage.getPages(), |
|||
productPage.getRecords().stream().map(this::beanProcess).collect(Collectors.toList())); |
|||
} |
|||
|
|||
@Override |
|||
public List<ProductVO> findProductsByIdBatch(List<Long> productIdList) { |
|||
return baseMapper.selectList(new QueryWrapper<ProductEntity>().in("id", productIdList)) |
|||
.stream().map(this::beanProcess).collect(Collectors.toList()); |
|||
} |
|||
|
|||
private ProductVO beanProcess(ProductEntity product) { |
|||
ProductVO result = new ProductVO(); |
|||
BeanUtils.copyProperties(product, result); |
|||
result.setAmount(product.getPrice()); |
|||
result.setStock(product.getStock() - product.getLockStock()); |
|||
return result; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,26 @@ |
|||
server: |
|||
port: 9003 |
|||
spring: |
|||
application: |
|||
name: nla-product-service |
|||
redis: |
|||
host: 127.0.0.1 |
|||
port: 6379 |
|||
database: 0 |
|||
password: yuan123456 |
|||
#数据库配置 |
|||
datasource: |
|||
driver-class-name: com.mysql.cj.jdbc.Driver |
|||
url: jdbc:mysql://117.72.43.105:3306/p_nla_product?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai |
|||
username: root |
|||
password: Yuan625621105. |
|||
#配置plus打印sql⽇志 |
|||
mybatis-plus: |
|||
configuration: |
|||
log-impl: |
|||
org.apache.ibatis.logging.stdout.StdOutImpl |
|||
#设置⽇志级别,ERROR/WARN/INFO/DEBUG,默认是INFO以上才显示 |
|||
#logging: |
|||
# level: |
|||
# root: INFO |
|||
|
@ -0,0 +1,71 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<configuration scan="true" scanPeriod="60 seconds" debug="false"> |
|||
<!-- 自定义配置: 日志存放路径 --> |
|||
<property name="log.path" value="./logs/nla-product-service"/> |
|||
<!-- 日志输出格式 --> |
|||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/> |
|||
<!-- 控制台输出 --> |
|||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> |
|||
<encoder> |
|||
<pattern>${log.pattern}</pattern> |
|||
</encoder> |
|||
</appender> |
|||
<!-- 系统日志输出 --> |
|||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>${log.path}/info.log</file> |
|||
<!-- 循环政策:基于时间创建日志文件 --> |
|||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
|||
<!-- 日志文件名格式 --> |
|||
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern> |
|||
<!-- 日志最大的历史 60天 --> |
|||
<maxHistory>60</maxHistory> |
|||
</rollingPolicy> |
|||
<encoder> |
|||
<pattern>${log.pattern}</pattern> |
|||
</encoder> |
|||
<filter class="ch.qos.logback.classic.filter.LevelFilter"> |
|||
<!-- 过滤的级别 --> |
|||
<level>INFO</level> |
|||
<!-- 匹配时的操作:接收(记录) --> |
|||
<onMatch>ACCEPT</onMatch> |
|||
<!-- 不匹配时的操作:拒绝(不记录) --> |
|||
<onMismatch>DENY</onMismatch> |
|||
</filter> |
|||
</appender> |
|||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>${log.path}/error.log</file> |
|||
<!-- 循环政策:基于时间创建日志文件 --> |
|||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
|||
<!-- 日志文件名格式 --> |
|||
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern> |
|||
<!-- 日志最大的历史 60天 --> |
|||
<maxHistory>60</maxHistory> |
|||
</rollingPolicy> |
|||
<encoder> |
|||
<pattern>${log.pattern}</pattern> |
|||
</encoder> |
|||
<filter class="ch.qos.logback.classic.filter.LevelFilter"> |
|||
<!-- 过滤的级别 --> |
|||
<level>ERROR</level> |
|||
<!-- 匹配时的操作:接收(记录) --> |
|||
<onMatch>ACCEPT</onMatch> |
|||
<!-- 不匹配时的操作:拒绝(不记录) --> |
|||
<onMismatch>DENY</onMismatch> |
|||
</filter> |
|||
</appender> |
|||
<!-- 自定义配置: 系统模块日志级别控制 --> |
|||
<logger name="cn.nla" level="info"/> |
|||
<!-- Spring日志级别控制 --> |
|||
<logger name="org.springframework" level="warn"/> |
|||
<logger name="io.lettuce.core.protocol" level="ERROR"/> |
|||
<!--自定义配置: mapper打印--> |
|||
<logger name="cn.nla.product.mapper" level="DEBUG"/> |
|||
<root level="info"> |
|||
<appender-ref ref="console"/> |
|||
</root> |
|||
<!--系统操作日志--> |
|||
<root level="info"> |
|||
<appender-ref ref="file_info"/> |
|||
<appender-ref ref="file_error"/> |
|||
</root> |
|||
</configuration> |
@ -0,0 +1,18 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
|||
<mapper namespace="cn.nla.product.mapper.BannerMapper"> |
|||
|
|||
<!-- 通用查询映射结果 --> |
|||
<resultMap id="BaseResultMap" type="cn.nla.product.model.entity.BannerEntity"> |
|||
<id column="id" property="id" /> |
|||
<result column="img" property="img" /> |
|||
<result column="url" property="url" /> |
|||
<result column="weight" property="weight" /> |
|||
</resultMap> |
|||
|
|||
<!-- 通用查询结果列 --> |
|||
<sql id="Base_Column_List"> |
|||
id, img, url, weight |
|||
</sql> |
|||
|
|||
</mapper> |
@ -0,0 +1,23 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
|||
<mapper namespace="cn.nla.product.mapper.ProductMapper"> |
|||
|
|||
<!-- 通用查询映射结果 --> |
|||
<resultMap id="BaseResultMap" type="cn.nla.product.model.entity.ProductEntity"> |
|||
<id column="id" property="id" /> |
|||
<result column="title" property="title" /> |
|||
<result column="cover_img" property="coverImg" /> |
|||
<result column="detail" property="detail" /> |
|||
<result column="old_price" property="oldPrice" /> |
|||
<result column="price" property="price" /> |
|||
<result column="stock" property="stock" /> |
|||
<result column="create_time" property="createTime" /> |
|||
<result column="lock_stock" property="lockStock" /> |
|||
</resultMap> |
|||
|
|||
<!-- 通用查询结果列 --> |
|||
<sql id="Base_Column_List"> |
|||
id, title, cover_img, detail, old_price, price, stock, create_time, lock_stock |
|||
</sql> |
|||
|
|||
</mapper> |
Loading…
Reference in new issue