diff --git a/src/main/java/icu/sunway/ai_spring_example/Common/Constant/MessageConstant.java b/src/main/java/icu/sunway/ai_spring_example/Common/Constant/MessageConstant.java index 2f34a9e..73c2c9d 100644 --- a/src/main/java/icu/sunway/ai_spring_example/Common/Constant/MessageConstant.java +++ b/src/main/java/icu/sunway/ai_spring_example/Common/Constant/MessageConstant.java @@ -24,4 +24,7 @@ public class MessageConstant { public static final String CART_ITEM_NOT_FOUND = "购物车记录不存在"; public static final String STOCK_INSUFFICIENT = "库存不足,无法添加"; public static final String NO_PERMISSION = "没有权限"; + public static final String CART_ITEM_EMPTY = "购物车为空"; + public static final String ADDRESS_ID_NULL = "地址ID不能为空"; + public static final String ADDRESS_NOT_FOUND = "地址不存在"; } diff --git a/src/main/java/icu/sunway/ai_spring_example/Controllers/OrderController.java b/src/main/java/icu/sunway/ai_spring_example/Controllers/OrderController.java new file mode 100644 index 0000000..2d2a913 --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/Controllers/OrderController.java @@ -0,0 +1,85 @@ +// OrderController.java +package icu.sunway.ai_spring_example.Controllers; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import icu.sunway.ai_spring_example.Common.Response.ResponseEntity; +import icu.sunway.ai_spring_example.Service.OrderService; +import icu.sunway.ai_spring_example.pojo.Dto.OrderDTO; +import icu.sunway.ai_spring_example.pojo.Dto.OrderStatusDTO; +import icu.sunway.ai_spring_example.pojo.Vo.OrderListVO; +import icu.sunway.ai_spring_example.pojo.Vo.OrderVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/order") +@Slf4j +public class OrderController { + + @Autowired + private OrderService orderService; + + /** + * 创建订单 + */ + @PostMapping + public ResponseEntity createOrder(@Validated @RequestBody OrderDTO orderDTO) { + log.info("创建订单: {}", orderDTO); + Long orderId = orderService.createOrder(orderDTO); + Map result = new HashMap<>(); + result.put("orderId", orderId); + return ResponseEntity.success("订单创建成功", result); + } + + /** + * 分页查询订单列表 + */ + @GetMapping("/page") + public ResponseEntity> getOrderPage( + @RequestParam(defaultValue = "1") Integer page, + @RequestParam(defaultValue = "10") Integer size, + @RequestParam Long userId, + @RequestParam(required = false) Integer status + ) { + log.info("查询订单列表: page={}, size={}, userId={}, status={}", + page, size, userId, status); + + Page orderPage = orderService.getOrderPage(page, size, userId, status); + + Map result = new HashMap<>(); + result.put("list", orderPage.getRecords()); + result.put("total", orderPage.getTotal()); + result.put("page", page); + result.put("size", size); + + return ResponseEntity.success(result); + } + + /** + * 查询订单详情 + */ + @GetMapping("/{id}") + public ResponseEntity getOrderDetail( + @PathVariable Long id, + @RequestParam Long userId + ) { + log.info("查询订单详情: id={}, userId={}", id, userId); + OrderVO orderVO = orderService.getOrderDetail(id, userId); + return ResponseEntity.success(orderVO); + } + + /** + * 更新订单状态 + */ + @PutMapping("/status") + public ResponseEntity updateOrderStatus(@Validated @RequestBody OrderStatusDTO statusDTO) { + log.info("更新订单状态: {}", statusDTO); + orderService.updateOrderStatus(statusDTO); + return ResponseEntity.success("订单状态更新成功"); + } +} \ No newline at end of file diff --git a/src/main/java/icu/sunway/ai_spring_example/Controllers/UserController.java b/src/main/java/icu/sunway/ai_spring_example/Controllers/UserController.java index dbf05c7..69cdb23 100644 --- a/src/main/java/icu/sunway/ai_spring_example/Controllers/UserController.java +++ b/src/main/java/icu/sunway/ai_spring_example/Controllers/UserController.java @@ -6,17 +6,22 @@ import icu.sunway.ai_spring_example.Common.Constant.StatusConstant; import icu.sunway.ai_spring_example.Common.Properties.JwtProperties; import icu.sunway.ai_spring_example.Common.Response.ResponseEntity; import icu.sunway.ai_spring_example.Common.Utils.JwtUtil; +import icu.sunway.ai_spring_example.Service.UserAddressService; import icu.sunway.ai_spring_example.Service.UserService; +import icu.sunway.ai_spring_example.pojo.Dto.UserAddressDTO; import icu.sunway.ai_spring_example.pojo.Dto.UserDTO; import icu.sunway.ai_spring_example.pojo.Entity.User; +import icu.sunway.ai_spring_example.pojo.Entity.UserAddress; import icu.sunway.ai_spring_example.pojo.Vo.UserLoginVO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.DigestUtils; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.HashMap; +import java.util.List; import java.util.Map; @RestController @@ -29,6 +34,9 @@ public class UserController { @Autowired private JwtProperties jwtProperties; + + @Autowired + private UserAddressService userAddressService; @PostMapping("/login") public ResponseEntity login(@RequestBody UserDTO userDTO){ log.info("用户登录:{}",userDTO); @@ -76,4 +84,64 @@ public class UserController { userService.update(userDTO); return ResponseEntity.success("更新成功"); } + + /** + * 添加地址 + */ + @PostMapping("/address") + public ResponseEntity addAddress(@Validated @RequestBody UserAddressDTO addressDTO) { + log.info("添加用户地址: {}", addressDTO); + userAddressService.addAddress(addressDTO); + return ResponseEntity.success("地址添加成功"); + } + + /** + * 更新地址 + */ + @PutMapping("/address") + public ResponseEntity updateAddress(@Validated @RequestBody UserAddressDTO addressDTO) { + log.info("更新用户地址: {}", addressDTO); + userAddressService.updateAddress(addressDTO); + return ResponseEntity.success("地址更新成功"); + } + + /** + * 删除地址 + */ + @DeleteMapping("/address/{id}") + public ResponseEntity deleteAddress(@PathVariable Long id, @RequestParam Long userId) { + log.info("删除用户地址: id={}, userId={}", id, userId); + userAddressService.deleteAddress(id, userId); + return ResponseEntity.success("地址删除成功"); + } + + /** + * 查询用户地址列表 + */ + @GetMapping("/address") + public ResponseEntity> getAddressList(@RequestParam Long userId) { + log.info("查询用户地址列表: userId={}", userId); + List addressList = userAddressService.getAddressListByUserId(userId); + return ResponseEntity.success(addressList); + } + + /** + * 查询地址详情 + */ + @GetMapping("/address/{id}") + public ResponseEntity getAddressDetail(@PathVariable Long id, @RequestParam Long userId) { + log.info("查询地址详情: id={}, userId={}", id, userId); + UserAddress address = userAddressService.getAddressById(id, userId); + return ResponseEntity.success(address); + } + + /** + * 设置默认地址 + */ + @PutMapping("/address/{id}/default") + public ResponseEntity setDefaultAddress(@PathVariable Long id, @RequestParam Long userId) { + log.info("设置默认地址: id={}, userId={}", id, userId); + userAddressService.setDefaultAddress(id, userId); + return ResponseEntity.success("默认地址设置成功"); + } } diff --git a/src/main/java/icu/sunway/ai_spring_example/Mapper/OrderInfoMapper.java b/src/main/java/icu/sunway/ai_spring_example/Mapper/OrderInfoMapper.java new file mode 100644 index 0000000..e1ec52f --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/Mapper/OrderInfoMapper.java @@ -0,0 +1,10 @@ +// OrderInfoMapper.java +package icu.sunway.ai_spring_example.Mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import icu.sunway.ai_spring_example.pojo.Entity.OrderInfo; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface OrderInfoMapper extends BaseMapper { +} \ No newline at end of file diff --git a/src/main/java/icu/sunway/ai_spring_example/Mapper/OrderItemMapper.java b/src/main/java/icu/sunway/ai_spring_example/Mapper/OrderItemMapper.java new file mode 100644 index 0000000..1218687 --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/Mapper/OrderItemMapper.java @@ -0,0 +1,10 @@ +// OrderItemMapper.java +package icu.sunway.ai_spring_example.Mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import icu.sunway.ai_spring_example.pojo.Entity.OrderItem; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface OrderItemMapper extends BaseMapper { +} \ No newline at end of file diff --git a/src/main/java/icu/sunway/ai_spring_example/Mapper/UserAddressMapper.java b/src/main/java/icu/sunway/ai_spring_example/Mapper/UserAddressMapper.java new file mode 100644 index 0000000..13be8a4 --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/Mapper/UserAddressMapper.java @@ -0,0 +1,11 @@ +// UserAddressMapper.java +package icu.sunway.ai_spring_example.Mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import icu.sunway.ai_spring_example.pojo.Entity.UserAddress; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface UserAddressMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/src/main/java/icu/sunway/ai_spring_example/Service/Impl/OrderServiceImpl.java b/src/main/java/icu/sunway/ai_spring_example/Service/Impl/OrderServiceImpl.java new file mode 100644 index 0000000..ed19a8a --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/Service/Impl/OrderServiceImpl.java @@ -0,0 +1,278 @@ +// OrderServiceImpl.java +package icu.sunway.ai_spring_example.Service.Impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import icu.sunway.ai_spring_example.Common.Exception.BusinessException; +import icu.sunway.ai_spring_example.Common.Constant.MessageConstant; +import icu.sunway.ai_spring_example.Mapper.*; +import icu.sunway.ai_spring_example.Service.OrderService; +import icu.sunway.ai_spring_example.pojo.Dto.OrderDTO; +import icu.sunway.ai_spring_example.pojo.Dto.OrderStatusDTO; +import icu.sunway.ai_spring_example.pojo.Entity.*; +import icu.sunway.ai_spring_example.pojo.Vo.OrderListVO; +import icu.sunway.ai_spring_example.pojo.Vo.OrderVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class OrderServiceImpl extends ServiceImpl implements OrderService { + + @Autowired + private OrderInfoMapper orderInfoMapper; + + @Autowired + private OrderItemMapper orderItemMapper; + + @Autowired + private ShoppingCartMapper shoppingCartMapper; + + @Autowired + private ProductSkuMapper productSkuMapper; + + + @Autowired + private ProductMapper productMapper; + /** + * 生成唯一订单编号 + */ + private String generateOrderNo() { + // 格式: 年月日时分秒 + 3位随机数 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); + String dateStr = LocalDateTime.now().format(formatter); + Random random = new Random(); + int randomNum = random.nextInt(900) + 100; // 100-999的随机数 + return "ORDER" + dateStr + randomNum; + } + + @Override + @Transactional + public Long createOrder(OrderDTO orderDTO) { + // 1. 查询用户购物车中已勾选的商品 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(ShoppingCart::getUserId, orderDTO.getUserId()) + .eq(ShoppingCart::getChecked, 1) // 只查询已勾选的商品 + .eq(ShoppingCart::getIsDeleted, 0); + + List cartList = shoppingCartMapper.selectList(queryWrapper); + if (cartList == null || cartList.isEmpty()) { + throw new BusinessException(MessageConstant.CART_ITEM_EMPTY); + } + + // 2. 验证库存并计算总金额 + BigDecimal totalAmount = BigDecimal.ZERO; + //TODO 改运费 + BigDecimal freightAmount = new BigDecimal("0.00"); // 假设运费0元 + + for (ShoppingCart cart : cartList) { + ProductSku sku = productSkuMapper.selectById(cart.getSkuId()); + if (sku == null) { + throw new BusinessException("商品规格不存在: " + cart.getSkuId()); + } + + if (sku.getStock() < cart.getCount()) { + throw new BusinessException("商品 " + sku.getSkuName() + " 库存不足"); + } + + // 计算商品总价 + BigDecimal itemTotal = sku.getPrice().multiply(new BigDecimal(cart.getCount())); + totalAmount = totalAmount.add(itemTotal); + } + + // 3. 创建订单主表记录 + OrderInfo orderInfo = new OrderInfo(); + BeanUtils.copyProperties(orderDTO, orderInfo); + String orderNo = generateOrderNo(); + orderInfo.setOrderNo(orderNo); + orderInfo.setTotalAmount(totalAmount); + orderInfo.setFreightAmount(freightAmount); + orderInfo.setPayAmount(totalAmount.add(freightAmount)); // 实付金额 = 商品总价 + 运费 + orderInfo.setStatus(0); // 初始状态:待付款 + orderInfo.setCreateTime(LocalDateTime.now()); + orderInfo.setUpdateTime(LocalDateTime.now()); + orderInfo.setIsDeleted(0); + + orderInfoMapper.insert(orderInfo); + + // 4. 创建订单项记录 + for (ShoppingCart cart : cartList) { + ProductSku sku = productSkuMapper.selectById(cart.getSkuId()); + Product product = productMapper.selectById(cart.getProductId()); + + OrderItem orderItem = new OrderItem(); + orderItem.setOrderId(orderInfo.getId()); + orderItem.setOrderNo(orderNo); + orderItem.setProductId(cart.getProductId()); + orderItem.setSkuId(cart.getSkuId()); + orderItem.setProductName(product.getName()); + orderItem.setSkuName(sku.getSkuName()); + orderItem.setProductImage(product.getMainImage()); + orderItem.setPrice(sku.getPrice()); + orderItem.setQuantity(cart.getCount()); + orderItem.setTotalPrice(sku.getPrice().multiply(new BigDecimal(cart.getCount()))); + orderItem.setCreateTime(LocalDateTime.now()); + orderItem.setUpdateTime(LocalDateTime.now()); + orderItem.setIsDeleted(0); + + orderItemMapper.insert(orderItem); + + // 5. 扣减库存 + sku.setStock(sku.getStock() - cart.getCount()); + productSkuMapper.updateById(sku); + } + + // 6. 清空购物车中已下单的商品 + shoppingCartMapper.delete(queryWrapper); + + log.info("创建订单成功: {}", orderNo); + return orderInfo.getId(); + } + + @Override + public Page getOrderPage(Integer page, Integer size, Long userId, Integer status) { + // 构造查询条件 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("is_deleted", 0); // 排除逻辑删除的记录 + + if (userId != null) { + queryWrapper.eq("user_id", userId); + } + + if (status != null) { + queryWrapper.eq("status", status); + } + + queryWrapper.orderByDesc("create_time"); + + // 使用MyBatis-Plus分页查询 + Page pageInfo = new Page<>(page, size); + Page orderPage = orderInfoMapper.selectPage(pageInfo, queryWrapper); + + // 转换为VO对象 + Page voPage = new Page<>(page, size, orderPage.getTotal()); + List voList = orderPage.getRecords().stream().map(orderInfo -> { + OrderListVO vo = new OrderListVO(); + BeanUtils.copyProperties(orderInfo, vo); + QueryWrapper itemQueryWrapper = new QueryWrapper<>(); + itemQueryWrapper.eq("order_id", orderInfo.getId()); + List itemList = orderItemMapper.selectList(itemQueryWrapper); + Integer count = 0; + for (OrderItem item : itemList) { + count += item.getQuantity(); + } + vo.setProductCount(count); + return vo; + }).collect(Collectors.toList()); + + voPage.setRecords(voList); + + // 转换状态码为文本 + voPage.getRecords().forEach(vo -> { + switch (vo.getStatus()) { + case 0: vo.setStatusText("待付款"); break; + case 1: vo.setStatusText("待发货"); break; + case 2: vo.setStatusText("待收货"); break; + case 3: vo.setStatusText("已完成"); break; + case 4: vo.setStatusText("已取消"); break; + default: vo.setStatusText("未知状态"); + } + }); + + return voPage; + } + + @Override + public OrderVO getOrderDetail(Long orderId, Long userId) { + // 查询订单主信息 + OrderInfo orderInfo = orderInfoMapper.selectById(orderId); + if (orderInfo == null || orderInfo.getIsDeleted() == 1) { + throw new BusinessException(MessageConstant.ORDER_NOT_FOUND); + } + + // 验证订单归属 + if (!orderInfo.getUserId().equals(userId)) { + throw new BusinessException(MessageConstant.NO_PERMISSION); + } + + // 查询订单项 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(OrderItem::getOrderId, orderId) + .eq(OrderItem::getIsDeleted, 0); + List orderItems = orderItemMapper.selectList(queryWrapper); + + // 转换为VO + OrderVO orderVO = new OrderVO(); + BeanUtils.copyProperties(orderInfo, orderVO); + orderVO.setOrderItems(orderItems); + + return orderVO; + } + + @Override + @Transactional + public void updateOrderStatus(OrderStatusDTO statusDTO) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("id", statusDTO.getOrderId()) + .eq("is_deleted", 0); + OrderInfo orderInfo = orderInfoMapper.selectOne(queryWrapper); + // 验证状态转换的合法性 + Integer currentStatus = orderInfo.getStatus(); + Integer newStatus = statusDTO.getStatus(); + + // 状态转换规则校验 + if (!isValidStatusTransition(currentStatus, newStatus)) { + throw new BusinessException("不允许从状态 " + currentStatus + " 转换到 " + newStatus); + } + + // 更新状态和相关时间 + orderInfo.setStatus(newStatus); + orderInfo.setUpdateTime(LocalDateTime.now()); + + // 根据状态设置相应的时间 + if (newStatus == 1) { // 待发货 -> 支付时间 + orderInfo.setPayTime(LocalDateTime.now()); + } else if (newStatus == 2) { // 待收货 -> 发货时间 + orderInfo.setDeliveryTime(LocalDateTime.now()); + } else if (newStatus == 3) { // 已完成 -> 收货时间 + orderInfo.setReceiveTime(LocalDateTime.now()); + } + + orderInfoMapper.updateById(orderInfo); + log.info("订单 {} 状态更新为: {}", orderInfo.getOrderNo(), newStatus); + } + + /** + * 验证状态转换是否合法 + */ + private boolean isValidStatusTransition(Integer currentStatus, Integer newStatus) { + // 状态转换规则: + // 0-待付款 -> 1-待发货 或 4-已取消 + // 1-待发货 -> 2-待收货 或 4-已取消 + // 2-待收货 -> 3-已完成 或 4-已取消 + // 3-已完成 -> 不能转换 + // 4-已取消 -> 不能转换 + + if (currentStatus == 0) { + return newStatus == 1 || newStatus == 4; + } else if (currentStatus == 1) { + return newStatus == 2 || newStatus == 4; + } else if (currentStatus == 2) { + return newStatus == 3 || newStatus == 4; + } else { // 3或4状态不能再转换 + return false; + } + } +} \ No newline at end of file diff --git a/src/main/java/icu/sunway/ai_spring_example/Service/Impl/UserAddressServiceImpl.java b/src/main/java/icu/sunway/ai_spring_example/Service/Impl/UserAddressServiceImpl.java new file mode 100644 index 0000000..b494e03 --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/Service/Impl/UserAddressServiceImpl.java @@ -0,0 +1,183 @@ +// UserAddressServiceImpl.java +package icu.sunway.ai_spring_example.Service.Impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import icu.sunway.ai_spring_example.Common.Exception.BusinessException; +import icu.sunway.ai_spring_example.Common.Constant.MessageConstant; +import icu.sunway.ai_spring_example.Mapper.UserAddressMapper; +import icu.sunway.ai_spring_example.Service.UserAddressService; +import icu.sunway.ai_spring_example.pojo.Dto.UserAddressDTO; +import icu.sunway.ai_spring_example.pojo.Entity.UserAddress; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +@Service +@Slf4j +public class UserAddressServiceImpl extends ServiceImpl implements UserAddressService { + + private final UserAddressMapper userAddressMapper; + + public UserAddressServiceImpl(UserAddressMapper userAddressMapper) { + this.userAddressMapper = userAddressMapper; + } + + @Override + @Transactional + public void addAddress(UserAddressDTO addressDTO) { + log.info("新增用户地址: {}", addressDTO); + UserAddress address = new UserAddress(); + BeanUtils.copyProperties(addressDTO, address); + + // 如果设置为默认地址,先取消该用户其他默认地址 + if (address.getIsDefault() != null && address.getIsDefault() == 1) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(UserAddress::getUserId, address.getUserId()) + .eq(UserAddress::getIsDefault, 1); + UserAddress defaultAddress = userAddressMapper.selectOne(queryWrapper); + if (defaultAddress != null) { + defaultAddress.setIsDefault(0); + userAddressMapper.updateById(defaultAddress); + } + } else { + // 如果是首次添加地址,默认设为默认地址 + LambdaQueryWrapper countWrapper = new LambdaQueryWrapper<>(); + countWrapper.eq(UserAddress::getUserId, address.getUserId()); + long count = userAddressMapper.selectCount(countWrapper); + if (count == 0) { + address.setIsDefault(1); + } else { + address.setIsDefault(0); // 非首次添加且未指定默认,设为非默认 + } + } + + userAddressMapper.insert(address); + } + + @Override + @Transactional + public void updateAddress(UserAddressDTO addressDTO) { + log.info("更新用户地址: {}", addressDTO); + if (addressDTO.getId() == null) { + throw new BusinessException(MessageConstant.ADDRESS_ID_NULL); + } + + // 验证地址是否存在且属于当前用户 + UserAddress address = userAddressMapper.selectById(addressDTO.getId()); + if (address == null || !address.getUserId().equals(addressDTO.getUserId())) { + throw new BusinessException(MessageConstant.ADDRESS_NOT_FOUND); + } + + BeanUtils.copyProperties(addressDTO, address); + + // 如果设置为默认地址,先取消该用户其他默认地址 + if (address.getIsDefault() != null && address.getIsDefault() == 1) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(UserAddress::getUserId, address.getUserId()) + .eq(UserAddress::getIsDefault, 1) + .ne(UserAddress::getId, address.getId()); // 排除当前地址 + UserAddress defaultAddress = userAddressMapper.selectOne(queryWrapper); + if (defaultAddress != null) { + defaultAddress.setIsDefault(0); + userAddressMapper.updateById(defaultAddress); + } + } + + address.setUpdateTime(LocalDateTime.now()); + userAddressMapper.updateById(address); + } + + @Override + @Transactional + public void deleteAddress(Long id, Long userId) { + log.info("删除用户地址: id={}, userId={}", id, userId); + if (id == null || userId == null) { + throw new BusinessException(MessageConstant.PARAM_ERROR); + } + + UserAddress address = userAddressMapper.selectById(id); + if (address == null || !address.getUserId().equals(userId)) { + throw new BusinessException(MessageConstant.ADDRESS_NOT_FOUND); + } + + // 如果删除的是默认地址,需要将其他地址设为默认 + if (address.getIsDefault() == 1) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(UserAddress::getUserId, userId) + .ne(UserAddress::getId, id) + .eq(UserAddress::getIsDeleted, 0); + List otherAddresses = userAddressMapper.selectList(queryWrapper); + if (!otherAddresses.isEmpty()) { + UserAddress newDefault = otherAddresses.get(0); + newDefault.setIsDefault(1); + userAddressMapper.updateById(newDefault); + } + } + + // 逻辑删除 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("id", id); + userAddressMapper.delete(queryWrapper); + } + + @Override + public List getAddressListByUserId(Long userId) { + log.info("查询用户地址列表: userId={}", userId); + if (userId == null) { + throw new BusinessException(MessageConstant.PARAM_ERROR); + } + QueryWrapper queryWrapper = new QueryWrapper<>(); + return userAddressMapper.selectList(queryWrapper); + } + + @Override + public UserAddress getAddressById(Long id, Long userId) { + log.info("查询地址详情: id={}, userId={}", id, userId); + if (id == null || userId == null) { + throw new BusinessException(MessageConstant.PARAM_ERROR); + } + + UserAddress address = userAddressMapper.selectById(id); + if (address == null || address.getIsDeleted() == 1 || !address.getUserId().equals(userId)) { + throw new BusinessException(MessageConstant.ADDRESS_NOT_FOUND); + } + return address; + } + + @Override + @Transactional + public void setDefaultAddress(Long id, Long userId) { + log.info("设置默认地址: id={}, userId={}", id, userId); + if (id == null || userId == null) { + throw new BusinessException(MessageConstant.PARAM_ERROR); + } + + // 验证地址是否存在且属于当前用户 + UserAddress address = userAddressMapper.selectById(id); + if (address == null || address.getIsDeleted() == 1 || !address.getUserId().equals(userId)) { + throw new BusinessException(MessageConstant.ADDRESS_NOT_FOUND); + } + + // 取消其他默认地址 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(UserAddress::getUserId, userId) + .eq(UserAddress::getIsDefault, 1) + .ne(UserAddress::getId, id); + UserAddress defaultAddress = userAddressMapper.selectOne(queryWrapper); + if (defaultAddress != null) { + defaultAddress.setIsDefault(0); + userAddressMapper.updateById(defaultAddress); + } + + // 设置当前地址为默认 + address.setIsDefault(1); + address.setUpdateTime(LocalDateTime.now()); + userAddressMapper.updateById(address); + } +} \ No newline at end of file diff --git a/src/main/java/icu/sunway/ai_spring_example/Service/OrderService.java b/src/main/java/icu/sunway/ai_spring_example/Service/OrderService.java new file mode 100644 index 0000000..c962745 --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/Service/OrderService.java @@ -0,0 +1,44 @@ +// OrderService.java +package icu.sunway.ai_spring_example.Service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import icu.sunway.ai_spring_example.pojo.Dto.OrderDTO; +import icu.sunway.ai_spring_example.pojo.Dto.OrderStatusDTO; +import icu.sunway.ai_spring_example.pojo.Entity.OrderInfo; +import icu.sunway.ai_spring_example.pojo.Vo.OrderListVO; +import icu.sunway.ai_spring_example.pojo.Vo.OrderVO; + +public interface OrderService extends IService { + + /** + * 创建订单 + * @param orderDTO 订单信息 + * @return 订单ID + */ + Long createOrder(OrderDTO orderDTO); + + /** + * 分页查询订单列表 + * @param page 页码 + * @param size 每页条数 + * @param userId 用户ID + * @param status 订单状态 + * @return 订单列表 + */ + Page getOrderPage(Integer page, Integer size, Long userId, Integer status); + + /** + * 查询订单详情 + * @param orderId 订单ID + * @param userId 用户ID + * @return 订单详情 + */ + OrderVO getOrderDetail(Long orderId, Long userId); + + /** + * 更新订单状态 + * @param statusDTO 订单状态信息 + */ + void updateOrderStatus(OrderStatusDTO statusDTO); +} \ No newline at end of file diff --git a/src/main/java/icu/sunway/ai_spring_example/Service/UserAddressService.java b/src/main/java/icu/sunway/ai_spring_example/Service/UserAddressService.java new file mode 100644 index 0000000..1e657dd --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/Service/UserAddressService.java @@ -0,0 +1,52 @@ +// UserAddressService.java +package icu.sunway.ai_spring_example.Service; + +import com.baomidou.mybatisplus.extension.service.IService; +import icu.sunway.ai_spring_example.pojo.Dto.UserAddressDTO; +import icu.sunway.ai_spring_example.pojo.Entity.UserAddress; + +import java.util.List; + +public interface UserAddressService extends IService { + + /** + * 添加用户地址 + * @param addressDTO 地址信息 + */ + void addAddress(UserAddressDTO addressDTO); + + /** + * 更新用户地址 + * @param addressDTO 地址信息 + */ + void updateAddress(UserAddressDTO addressDTO); + + /** + * 删除用户地址 + * @param id 地址ID + * @param userId 用户ID + */ + void deleteAddress(Long id, Long userId); + + /** + * 根据用户ID查询地址列表 + * @param userId 用户ID + * @return 地址列表 + */ + List getAddressListByUserId(Long userId); + + /** + * 根据地址ID查询地址详情 + * @param id 地址ID + * @param userId 用户ID + * @return 地址详情 + */ + UserAddress getAddressById(Long id, Long userId); + + /** + * 设置默认地址 + * @param id 地址ID + * @param userId 用户ID + */ + void setDefaultAddress(Long id, Long userId); +} \ No newline at end of file diff --git a/src/main/java/icu/sunway/ai_spring_example/pojo/Dto/OrderDTO.java b/src/main/java/icu/sunway/ai_spring_example/pojo/Dto/OrderDTO.java new file mode 100644 index 0000000..7910734 --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/pojo/Dto/OrderDTO.java @@ -0,0 +1,38 @@ +// OrderDTO.java +package icu.sunway.ai_spring_example.pojo.Dto; + +import lombok.Data; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +@Data +public class OrderDTO { + /** + * 用户ID + */ + @NotNull(message = "用户ID不能为空") + private Long userId; + + /** + * 收货人姓名 + */ + @NotBlank(message = "收货人姓名不能为空") + private String receiverName; + + /** + * 收货人手机号 + */ + @NotBlank(message = "收货人手机号不能为空") + private String receiverPhone; + + /** + * 收货人地址 + */ + @NotBlank(message = "收货人地址不能为空") + private String receiverAddress; + + /** + * 支付方式 0-未支付 1-微信 2-支付宝 + */ + private Integer payType = 0; +} \ No newline at end of file diff --git a/src/main/java/icu/sunway/ai_spring_example/pojo/Dto/OrderStatusDTO.java b/src/main/java/icu/sunway/ai_spring_example/pojo/Dto/OrderStatusDTO.java new file mode 100644 index 0000000..4d15893 --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/pojo/Dto/OrderStatusDTO.java @@ -0,0 +1,20 @@ +// OrderStatusDTO.java +package icu.sunway.ai_spring_example.pojo.Dto; + +import lombok.Data; +import jakarta.validation.constraints.NotNull; + +@Data +public class OrderStatusDTO { + /** + * 订单ID + */ + @NotNull(message = "订单ID不能为空") + private Long orderId; + + /** + * 订单状态 0-待付款 1-待发货 2-待收货 3-已完成 4-已取消 + */ + @NotNull(message = "订单状态不能为空") + private Integer status; +} \ No newline at end of file diff --git a/src/main/java/icu/sunway/ai_spring_example/pojo/Dto/UserAddressDTO.java b/src/main/java/icu/sunway/ai_spring_example/pojo/Dto/UserAddressDTO.java new file mode 100644 index 0000000..d8de185 --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/pojo/Dto/UserAddressDTO.java @@ -0,0 +1,34 @@ +// UserAddressDTO.java +package icu.sunway.ai_spring_example.pojo.Dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class UserAddressDTO { + private Long id; // 地址ID,新增时可不传,修改时必传 + + @NotNull(message = "用户ID不能为空") + private Long userId; // 用户ID + + @NotBlank(message = "收货人姓名不能为空") + private String receiverName; // 收货人姓名 + + @NotBlank(message = "收货人手机号不能为空") + private String receiverPhone; // 收货人手机号 + + @NotBlank(message = "省份不能为空") + private String province; // 省 + + @NotBlank(message = "城市不能为空") + private String city; // 市 + + @NotBlank(message = "区县不能为空") + private String district; // 区/县 + + @NotBlank(message = "详细地址不能为空") + private String detailAddress; // 详细地址 + + private Integer isDefault; // 是否默认(0-非默认,1-默认) +} \ No newline at end of file diff --git a/src/main/java/icu/sunway/ai_spring_example/pojo/Entity/UserAddress.java b/src/main/java/icu/sunway/ai_spring_example/pojo/Entity/UserAddress.java index 49c65cb..ce73c8f 100644 --- a/src/main/java/icu/sunway/ai_spring_example/pojo/Entity/UserAddress.java +++ b/src/main/java/icu/sunway/ai_spring_example/pojo/Entity/UserAddress.java @@ -7,7 +7,7 @@ import java.time.LocalDateTime; /** * 用户地址表实体类 * - * @author ZhiWei + * @author PuZhiWei * @since 2025-12-17 */ @Data diff --git a/src/main/java/icu/sunway/ai_spring_example/pojo/Vo/OrderListVO.java b/src/main/java/icu/sunway/ai_spring_example/pojo/Vo/OrderListVO.java new file mode 100644 index 0000000..6ef1e1e --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/pojo/Vo/OrderListVO.java @@ -0,0 +1,49 @@ +// OrderListVO.java +package icu.sunway.ai_spring_example.pojo.Vo; + +import lombok.Data; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +public class OrderListVO { + /** + * 订单ID + */ + private Long id; + + /** + * 订单编号 + */ + private String orderNo; + + /** + * 订单总金额 + */ + private BigDecimal totalAmount; + + /** + * 实付金额 + */ + private BigDecimal payAmount; + + /** + * 订单状态 0-待付款 1-待发货 2-待收货 3-已完成 4-已取消 + */ + private Integer status; + + /** + * 订单状态文本 + */ + private String statusText; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 商品数量 + */ + private Integer productCount; +} \ No newline at end of file diff --git a/src/main/java/icu/sunway/ai_spring_example/pojo/Vo/OrderVO.java b/src/main/java/icu/sunway/ai_spring_example/pojo/Vo/OrderVO.java new file mode 100644 index 0000000..b33ac5b --- /dev/null +++ b/src/main/java/icu/sunway/ai_spring_example/pojo/Vo/OrderVO.java @@ -0,0 +1,71 @@ +// OrderVO.java +package icu.sunway.ai_spring_example.pojo.Vo; + +import icu.sunway.ai_spring_example.pojo.Entity.OrderItem; +import lombok.Data; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Data +public class OrderVO { + /** + * 订单ID + */ + private Long id; + + /** + * 订单编号 + */ + private String orderNo; + + /** + * 用户ID + */ + private Long userId; + + /** + * 订单总金额 + */ + private BigDecimal totalAmount; + + /** + * 实付金额 + */ + private BigDecimal payAmount; + + /** + * 运费 + */ + private BigDecimal freightAmount; + + /** + * 支付方式 0-未支付 1-微信 2-支付宝 + */ + private Integer payType; + + /** + * 订单状态 0-待付款 1-待发货 2-待收货 3-已完成 4-已取消 + */ + private Integer status; + + /** + * 收货人信息 + */ + private String receiverName; + private String receiverPhone; + private String receiverAddress; + + /** + * 时间信息 + */ + private LocalDateTime payTime; + private LocalDateTime deliveryTime; + private LocalDateTime receiveTime; + private LocalDateTime createTime; + + /** + * 订单项列表 + */ + private List orderItems; +} \ No newline at end of file