核心功能开发
1. 用户服务开发
1.1 实体类
java
@Entity
@Table(name = "users")
@EntityListeners(AuditingEntityListener.class)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 50)
private String username;
@Column(nullable = false)
private String password;
@Column(unique = true, length = 100)
private String email;
@Column(length = 20)
private String phone;
@Enumerated(EnumType.STRING)
private UserStatus status = UserStatus.ACTIVE;
@CreatedDate
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<UserAddress> addresses = new ArrayList<>();
}
public enum UserStatus {
ACTIVE, INACTIVE, LOCKED
}1.2 Repository
java
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);
boolean existsByUsername(String username);
boolean existsByEmail(String email);
}1.3 Service
java
@Service
@Transactional
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public User create(UserRequest request) {
if (userRepository.existsByUsername(request.username())) {
throw new BusinessException("用户名已存在");
}
if (userRepository.existsByEmail(request.email())) {
throw new BusinessException("邮箱已被注册");
}
User user = new User();
user.setUsername(request.username());
user.setPassword(passwordEncoder.encode(request.password()));
user.setEmail(request.email());
user.setPhone(request.phone());
return userRepository.save(user);
}
@Transactional(readOnly = true)
public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("用户不存在"));
}
@Transactional(readOnly = true)
public Page<User> findAll(int page, int size) {
return userRepository.findAll(PageRequest.of(page, size));
}
public User update(Long id, UserRequest request) {
User user = findById(id);
if (!user.getUsername().equals(request.username()) &&
userRepository.existsByUsername(request.username())) {
throw new BusinessException("用户名已存在");
}
user.setUsername(request.username());
user.setEmail(request.email());
user.setPhone(request.phone());
return userRepository.save(user);
}
public void delete(Long id) {
User user = findById(id);
user.setStatus(UserStatus.INACTIVE);
userRepository.save(user);
}
}1.4 Controller
java
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
@PostMapping
public ResponseEntity<ApiResponse<User>> create(@Valid @RequestBody UserRequest request) {
User user = userService.create(request);
return ResponseEntity.status(HttpStatus.CREATED)
.body(ApiResponse.success("创建成功", user));
}
@GetMapping("/{id}")
public ApiResponse<User> findById(@PathVariable Long id) {
return ApiResponse.success(userService.findById(id));
}
@GetMapping
public ApiResponse<Page<User>> findAll(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return ApiResponse.success(userService.findAll(page, size));
}
@PutMapping("/{id}")
public ApiResponse<User> update(@PathVariable Long id,
@Valid @RequestBody UserRequest request) {
return ApiResponse.success(userService.update(id, request));
}
@DeleteMapping("/{id}")
public ApiResponse<Void> delete(@PathVariable Long id) {
userService.delete(id);
return ApiResponse.success(null);
}
}2. 商品服务开发
2.1 实体类
java
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 200)
private String name;
@Column(columnDefinition = "TEXT")
private String description;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal price;
@Column(nullable = false)
private Integer stock = 0;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private Category category;
@Column(name = "image_url", length = 500)
private String imageUrl;
@Enumerated(EnumType.STRING)
private ProductStatus status = ProductStatus.ACTIVE;
@CreatedDate
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}
public enum ProductStatus {
ACTIVE, INACTIVE, OUT_OF_STOCK
}2.2 商品搜索
java
@Service
public class ProductService {
private final ProductRepository productRepository;
public Page<Product> search(ProductQuery query, int page, int size) {
Specification<Product> spec = (root, q, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (query.name() != null && !query.name().isEmpty()) {
predicates.add(cb.like(root.get("name"),
"%" + query.name() + "%"));
}
if (query.categoryId() != null) {
predicates.add(cb.equal(root.get("category").get("id"),
query.categoryId()));
}
if (query.minPrice() != null) {
predicates.add(cb.greaterThanOrEqualTo(
root.get("price"), query.minPrice()));
}
if (query.maxPrice() != null) {
predicates.add(cb.lessThanOrEqualTo(
root.get("price"), query.maxPrice()));
}
if (query.status() != null) {
predicates.add(cb.equal(root.get("status"), query.status()));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
return productRepository.findAll(spec, PageRequest.of(page, size));
}
}3. 订单服务开发
3.1 实体类
java
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "order_no", nullable = false, unique = true, length = 50)
private String orderNo;
@Column(name = "user_id", nullable = false)
private Long userId;
@Column(name = "total_amount", nullable = false, precision = 10, scale = 2)
private BigDecimal totalAmount;
@Enumerated(EnumType.STRING)
private OrderStatus status = OrderStatus.PENDING;
@Enumerated(EnumType.STRING)
@Column(name = "payment_status")
private PaymentStatus paymentStatus = PaymentStatus.UNPAID;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> items = new ArrayList<>();
@CreatedDate
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public void calculateTotalAmount() {
this.totalAmount = items.stream()
.map(OrderItem::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
@Entity
@Table(name = "order_items")
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id", nullable = false)
private Order order;
@Column(name = "product_id", nullable = false)
private Long productId;
@Column(name = "product_name", nullable = false, length = 200)
private String productName;
@Column(nullable = false)
private Integer quantity;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal price;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal amount;
@PrePersist
public void calculateAmount() {
this.amount = price.multiply(BigDecimal.valueOf(quantity));
}
}3.2 订单创建
java
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final ProductClient productClient;
private final InventoryClient inventoryClient;
@GlobalTransactional(name = "create-order")
public Order create(OrderRequest request) {
// 生成订单号
String orderNo = generateOrderNo();
// 创建订单
Order order = new Order();
order.setOrderNo(orderNo);
order.setUserId(request.getUserId());
// 添加订单项
for (OrderItemRequest item : request.getItems()) {
Product product = productClient.getProduct(item.getProductId());
OrderItem orderItem = new OrderItem();
orderItem.setOrder(order);
orderItem.setProductId(product.getId());
orderItem.setProductName(product.getName());
orderItem.setQuantity(item.getQuantity());
orderItem.setPrice(product.getPrice());
order.getItems().add(orderItem);
// 扣减库存
inventoryClient.deductStock(item.getProductId(), item.getQuantity());
}
// 计算总金额
order.calculateTotalAmount();
return orderRepository.save(order);
}
private String generateOrderNo() {
String timestamp = LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
String random = String.format("%04d", new Random().nextInt(10000));
return timestamp + random;
}
}4. 库存服务开发
4.1 库存扣减
java
@Service
public class InventoryService {
private final InventoryRepository inventoryRepository;
private final RedisTemplate<String, Object> redisTemplate;
private static final String STOCK_KEY_PREFIX = "stock:";
@Transactional
public boolean deductStock(Long productId, Integer quantity) {
String key = STOCK_KEY_PREFIX + productId;
// 使用 Redis 原子操作扣减库存
Long remaining = redisTemplate.opsForValue().decrement(key, quantity);
if (remaining < 0) {
// 库存不足,回滚
redisTemplate.opsForValue().increment(key, quantity);
throw new BusinessException("库存不足");
}
// 更新数据库
Inventory inventory = inventoryRepository.findByProductId(productId);
inventory.setStock(inventory.getStock() - quantity);
inventoryRepository.save(inventory);
return true;
}
@Transactional
public void restoreStock(Long productId, Integer quantity) {
String key = STOCK_KEY_PREFIX + productId;
redisTemplate.opsForValue().increment(key, quantity);
Inventory inventory = inventoryRepository.findByProductId(productId);
inventory.setStock(inventory.getStock() + quantity);
inventoryRepository.save(inventory);
}
}5. 小结
本章完成了核心功能开发:
| 内容 | 要点 |
|---|---|
| 用户服务 | CRUD、注册登录 |
| 商品服务 | 商品管理、搜索 |
| 订单服务 | 订单创建、状态管理 |
| 库存服务 | 库存扣减、恢复 |
下一章将学习服务集成。