|
|
@@ -0,0 +1,276 @@
|
|
|
+package com.zhentao.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.zhentao.entity.*;
|
|
|
+import com.zhentao.mapper.*;
|
|
|
+import com.zhentao.service.PointsMallService;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 积分商城服务实现类
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class PointsMallServiceImpl implements PointsMallService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private PointsProductMapper pointsProductMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private PointsOrderMapper pointsOrderMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private PointsDetailMapper pointsDetailMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private PointRuleMapper pointRuleMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private MatchmakerMapper matchmakerMapper;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> getProductList(Integer category, Integer pageNum, Integer pageSize) {
|
|
|
+ Page<PointsProduct> page = new Page<>(pageNum, pageSize);
|
|
|
+ LambdaQueryWrapper<PointsProduct> wrapper = new LambdaQueryWrapper<>();
|
|
|
+
|
|
|
+ // 只查询上架的商品
|
|
|
+ wrapper.eq(PointsProduct::getStatus, 1);
|
|
|
+
|
|
|
+ // 按分类筛选
|
|
|
+ if (category != null && category > 0) {
|
|
|
+ wrapper.eq(PointsProduct::getCategory, category);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按排序和创建时间排序
|
|
|
+ wrapper.orderByDesc(PointsProduct::getSortOrder)
|
|
|
+ .orderByDesc(PointsProduct::getCreateTime);
|
|
|
+
|
|
|
+ Page<PointsProduct> result = pointsProductMapper.selectPage(page, wrapper);
|
|
|
+
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("list", result.getRecords());
|
|
|
+ data.put("total", result.getTotal());
|
|
|
+ data.put("pageNum", pageNum);
|
|
|
+ data.put("pageSize", pageSize);
|
|
|
+ data.put("pages", result.getPages());
|
|
|
+
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<PointsProduct> getRecommendProducts(Integer limit) {
|
|
|
+ LambdaQueryWrapper<PointsProduct> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(PointsProduct::getStatus, 1)
|
|
|
+ .eq(PointsProduct::getIsRecommend, 1)
|
|
|
+ .orderByDesc(PointsProduct::getSortOrder)
|
|
|
+ .last("LIMIT " + (limit != null ? limit : 10));
|
|
|
+
|
|
|
+ return pointsProductMapper.selectList(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public PointsProduct getProductDetail(Long productId) {
|
|
|
+ return pointsProductMapper.selectById(productId);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Integer getPointsBalance(Long makerId) {
|
|
|
+ Matchmaker matchmaker = matchmakerMapper.selectById(makerId);
|
|
|
+ if (matchmaker == null) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ // 优先使用matchmakers表的points字段
|
|
|
+ if (matchmaker.getPoints() != null) {
|
|
|
+ return matchmaker.getPoints();
|
|
|
+ }
|
|
|
+ // 如果points字段为空,通过计算积分明细来获取余额
|
|
|
+ LambdaQueryWrapper<PointsDetail> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(PointsDetail::getMakerId, makerId);
|
|
|
+ List<PointsDetail> details = pointsDetailMapper.selectList(wrapper);
|
|
|
+
|
|
|
+ int balance = 0;
|
|
|
+ for (PointsDetail detail : details) {
|
|
|
+ balance += detail.getPoints();
|
|
|
+ }
|
|
|
+ return balance;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> getPointsRecords(Long makerId, Integer pageNum, Integer pageSize) {
|
|
|
+ Page<PointsDetail> page = new Page<>(pageNum, pageSize);
|
|
|
+ LambdaQueryWrapper<PointsDetail> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(PointsDetail::getMakerId, makerId)
|
|
|
+ .orderByDesc(PointsDetail::getCreateTime);
|
|
|
+
|
|
|
+ Page<PointsDetail> result = pointsDetailMapper.selectPage(page, wrapper);
|
|
|
+
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("list", result.getRecords());
|
|
|
+ data.put("total", result.getTotal());
|
|
|
+ data.put("pageNum", pageNum);
|
|
|
+ data.put("pageSize", pageSize);
|
|
|
+
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<PointRule> getPointRules() {
|
|
|
+ LambdaQueryWrapper<PointRule> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(PointRule::getStatus, 1)
|
|
|
+ .orderByAsc(PointRule::getRuleType);
|
|
|
+ return pointRuleMapper.selectList(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public PointsOrder exchangeProduct(Long makerId, Long productId, Integer quantity,
|
|
|
+ String receiverName, String receiverPhone, String receiverAddress) {
|
|
|
+ // 1. 检查商品是否存在且上架
|
|
|
+ PointsProduct product = pointsProductMapper.selectById(productId);
|
|
|
+ if (product == null || product.getStatus() != 1) {
|
|
|
+ throw new RuntimeException("商品不存在或已下架");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 检查库存
|
|
|
+ if (product.getStock() < quantity) {
|
|
|
+ throw new RuntimeException("商品库存不足");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 计算所需积分
|
|
|
+ int totalPoints = product.getPointsPrice() * quantity;
|
|
|
+
|
|
|
+ // 4. 检查积分余额
|
|
|
+ Integer balance = getPointsBalance(makerId);
|
|
|
+ if (balance < totalPoints) {
|
|
|
+ throw new RuntimeException("积分余额不足,当前积分: " + balance + ",需要: " + totalPoints);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 扣减库存
|
|
|
+ LambdaUpdateWrapper<PointsProduct> productUpdate = new LambdaUpdateWrapper<>();
|
|
|
+ productUpdate.eq(PointsProduct::getId, productId)
|
|
|
+ .ge(PointsProduct::getStock, quantity)
|
|
|
+ .setSql("stock = stock - " + quantity);
|
|
|
+ int updateCount = pointsProductMapper.update(null, productUpdate);
|
|
|
+ if (updateCount == 0) {
|
|
|
+ throw new RuntimeException("库存扣减失败,请重试");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 创建订单
|
|
|
+ PointsOrder order = new PointsOrder();
|
|
|
+ order.setOrderNo(generateOrderNo());
|
|
|
+ order.setMakerId(makerId);
|
|
|
+ order.setProductId(productId);
|
|
|
+ order.setProductName(product.getName());
|
|
|
+ order.setProductImage(product.getImageUrl());
|
|
|
+ order.setPointsPrice(product.getPointsPrice());
|
|
|
+ order.setQuantity(quantity);
|
|
|
+ order.setTotalPoints(totalPoints);
|
|
|
+ order.setStatus(0); // 待发货
|
|
|
+ order.setReceiverName(receiverName);
|
|
|
+ order.setReceiverPhone(receiverPhone);
|
|
|
+ order.setReceiverAddress(receiverAddress);
|
|
|
+ order.setCreateTime(LocalDateTime.now());
|
|
|
+ order.setUpdateTime(LocalDateTime.now());
|
|
|
+
|
|
|
+ pointsOrderMapper.insert(order);
|
|
|
+
|
|
|
+ // 7. 记录积分变动(扣减)
|
|
|
+ PointsDetail detail = new PointsDetail();
|
|
|
+ detail.setMakerId(makerId);
|
|
|
+ detail.setPoints(-totalPoints);
|
|
|
+ detail.setReason("兑换商品: " + product.getName() + " x" + quantity);
|
|
|
+ detail.setCreateTime(LocalDateTime.now());
|
|
|
+ detail.setUpdateTime(LocalDateTime.now());
|
|
|
+
|
|
|
+ pointsDetailMapper.insert(detail);
|
|
|
+
|
|
|
+ // 8. 更新红娘积分余额
|
|
|
+ LambdaUpdateWrapper<Matchmaker> makerUpdate = new LambdaUpdateWrapper<>();
|
|
|
+ makerUpdate.eq(Matchmaker::getMatchmakerId, makerId)
|
|
|
+ .setSql("points = IFNULL(points, 0) - " + totalPoints);
|
|
|
+ matchmakerMapper.update(null, makerUpdate);
|
|
|
+
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> getOrderList(Long makerId, Integer status, Integer pageNum, Integer pageSize) {
|
|
|
+ Page<PointsOrder> page = new Page<>(pageNum, pageSize);
|
|
|
+ LambdaQueryWrapper<PointsOrder> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(PointsOrder::getMakerId, makerId);
|
|
|
+
|
|
|
+ if (status != null && status >= 0) {
|
|
|
+ wrapper.eq(PointsOrder::getStatus, status);
|
|
|
+ }
|
|
|
+
|
|
|
+ wrapper.orderByDesc(PointsOrder::getCreateTime);
|
|
|
+
|
|
|
+ Page<PointsOrder> result = pointsOrderMapper.selectPage(page, wrapper);
|
|
|
+
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("list", result.getRecords());
|
|
|
+ data.put("total", result.getTotal());
|
|
|
+ data.put("pageNum", pageNum);
|
|
|
+ data.put("pageSize", pageSize);
|
|
|
+
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public PointsOrder getOrderDetail(String orderNo) {
|
|
|
+ LambdaQueryWrapper<PointsOrder> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(PointsOrder::getOrderNo, orderNo);
|
|
|
+ return pointsOrderMapper.selectOne(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Integer addPoints(Long makerId, Integer ruleType, String reason) {
|
|
|
+ // 1. 查询积分规则
|
|
|
+ LambdaQueryWrapper<PointRule> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(PointRule::getRuleType, ruleType)
|
|
|
+ .eq(PointRule::getStatus, 1);
|
|
|
+ PointRule rule = pointRuleMapper.selectOne(wrapper);
|
|
|
+
|
|
|
+ if (rule == null) {
|
|
|
+ throw new RuntimeException("积分规则不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ int pointValue = rule.getPointValue();
|
|
|
+
|
|
|
+ // 2. 记录积分变动
|
|
|
+ PointsDetail detail = new PointsDetail();
|
|
|
+ detail.setMakerId(makerId);
|
|
|
+ detail.setPoints(pointValue);
|
|
|
+ detail.setReason(reason != null ? reason : rule.getRuleName());
|
|
|
+ detail.setCreateTime(LocalDateTime.now());
|
|
|
+ detail.setUpdateTime(LocalDateTime.now());
|
|
|
+
|
|
|
+ pointsDetailMapper.insert(detail);
|
|
|
+
|
|
|
+ // 3. 更新红娘积分余额
|
|
|
+ LambdaUpdateWrapper<Matchmaker> makerUpdate = new LambdaUpdateWrapper<>();
|
|
|
+ makerUpdate.eq(Matchmaker::getMatchmakerId, makerId)
|
|
|
+ .setSql("points = IFNULL(points, 0) + " + pointValue);
|
|
|
+ matchmakerMapper.update(null, makerUpdate);
|
|
|
+
|
|
|
+ return pointValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成订单编号
|
|
|
+ */
|
|
|
+ private String generateOrderNo() {
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
|
|
+ String timestamp = LocalDateTime.now().format(formatter);
|
|
|
+ String random = String.format("%04d", new Random().nextInt(10000));
|
|
|
+ return "PO" + timestamp + random;
|
|
|
+ }
|
|
|
+}
|