Initial commit: backend code

This commit is contained in:
Agent
2026-03-20 04:59:00 +00:00
commit e7c7f3b174
42 changed files with 2855 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
package com.example.building.service;
import java.util.Map;
/**
* 认证服务接口
*/
public interface AuthService {
/**
* 发送验证码
*/
void sendCode(String phone);
/**
* 手机号验证码登录
*/
Map<String, Object> phoneLogin(String phone, String code);
/**
* 微信扫码登录
*/
Map<String, Object> wechatLogin(String code);
/**
* 支付宝扫码登录
*/
Map<String, Object> alipayLogin(String code);
/**
* 刷新Token
*/
Map<String, Object> refreshToken(String refreshToken);
/**
* 获取当前用户信息
*/
Map<String, Object> getCurrentUser(String userId);
/**
* 退出登录
*/
void logout(String token);
}

View File

@@ -0,0 +1,35 @@
package com.example.building.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.building.entity.Customer;
/**
* 客户服务接口
*/
public interface CustomerService {
/**
* 客户列表
*/
Page<Customer> getCustomers(String keyword, Integer page, Integer pageSize);
/**
* 客户详情
*/
Customer getCustomer(String id);
/**
* 新增客户
*/
Customer createCustomer(Customer customer);
/**
* 修改客户
*/
Customer updateCustomer(String id, Customer customer);
/**
* 删除客户
*/
void deleteCustomer(String id);
}

View File

@@ -0,0 +1,48 @@
package com.example.building.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.building.dto.CreateOrderRequest;
import com.example.building.entity.Order;
import java.util.Map;
/**
* 订单服务接口
* 核心业务:订单创建、价格计算(原价/优惠/实付)
*/
public interface OrderService {
/**
* 创建订单
* 核心逻辑:
* 1. 计算订单原价(total_amount) = Σ(item.price × item.quantity)
* 2. 计算优惠金额(discount_amount) = total_amount × (100 - discount_rate) / 100
* 3. 计算实付金额(actual_amount) = total_amount - discount_amount
*/
Order createOrder(CreateOrderRequest request, String operatorId, String operatorName);
/**
* 获取订单列表
*/
Page<Order> getOrders(String customerId, Integer status, String startDate, String endDate, Integer page, Integer pageSize);
/**
* 获取订单详情(含明细)
*/
Map<String, Object> getOrderDetail(String orderId);
/**
* 取消订单
*/
void cancelOrder(String orderId, String operatorId);
/**
* 退款
*/
void refundOrder(String orderId, String operatorId);
/**
* 订单统计
*/
Map<String, Object> getStatistics(String startDate, String endDate);
}

View File

@@ -0,0 +1,64 @@
package com.example.building.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.building.entity.Category;
import com.example.building.entity.Product;
import java.util.List;
import java.util.Map;
/**
* 商品服务接口
*/
public interface ProductService {
/**
* 获取分类列表
*/
List<Category> getCategories();
/**
* 新增分类
*/
Category createCategory(Category category);
/**
* 修改分类
*/
Category updateCategory(String id, Category category);
/**
* 删除分类
*/
void deleteCategory(String id);
/**
* 获取商品列表
*/
Page<Product> getProducts(String categoryId, String keyword, Integer page, Integer pageSize);
/**
* 获取商品详情
*/
Product getProduct(String id);
/**
* 新增商品
*/
Product createProduct(Product product);
/**
* 修改商品
*/
Product updateProduct(String id, Product product);
/**
* 删除商品
*/
void deleteProduct(String id);
/**
* 获取库存预警商品
*/
List<Map<String, Object>> getStockAlerts();
}

View File

@@ -0,0 +1,40 @@
package com.example.building.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.building.entity.Stock;
import com.example.building.entity.StockFlow;
import java.util.List;
import java.util.Map;
/**
* 库存服务接口
* 核心业务:入库、库存查询、库存流水
*/
public interface StockService {
/**
* 入库
*/
Stock stockIn(String productId, Integer quantity, String remark, String operatorId);
/**
* 库存查询
*/
Page<Stock> getStockList(String keyword, Integer page, Integer pageSize);
/**
* 单商品库存
*/
Stock getStock(String productId);
/**
* 库存调整
*/
Stock adjustStock(String productId, Integer quantity, String remark, String operatorId);
/**
* 库存流水
*/
Page<StockFlow> getStockFlow(String productId, Integer type, Integer page, Integer pageSize);
}

View File

@@ -0,0 +1,189 @@
package com.example.building.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.building.common.JwtUtil;
import com.example.building.entity.User;
import com.example.building.mapper.UserMapper;
import com.example.building.service.AuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* 认证服务实现类
* 支持:手机号验证码登录、微信扫码登录、支付宝扫码登录
*/
@Service
public class AuthServiceImpl implements AuthService {
@Autowired
private UserMapper userMapper;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Value("${jwt.expiration:7200000}")
private Long expiration;
/**
* 发送验证码
* 实际生产中应调用阿里云短信服务
*/
@Override
public void sendCode(String phone) {
// 生成6位随机验证码
String code = String.format("%06d", (int) (Math.random() * 1000000));
// 存入Redis5分钟有效
redisTemplate.opsForValue().set("sms:code:" + phone, code, 5, TimeUnit.MINUTES);
// TODO: 调用短信服务发送验证码
System.out.println("验证码已发送: " + phone + " - " + code);
}
/**
* 手机号验证码登录
*/
@Override
public Map<String, Object> phoneLogin(String phone, String code) {
// 验证验证码
String savedCode = (String) redisTemplate.opsForValue().get("sms:code:" + phone);
if (savedCode == null || !savedCode.equals(code)) {
throw new RuntimeException("验证码错误或已过期");
}
// 查询用户,不存在则创建
User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
.eq(User::getPhone, phone));
if (user == null) {
user = new User();
user.setUserId(UUID.randomUUID().toString());
user.setPhone(phone);
user.setUsername("用户" + phone.substring(7));
user.setRole("sales");
user.setStatus(1);
userMapper.insert(user);
}
// 生成Token
return generateTokens(user);
}
/**
* 微信扫码登录
* 实际生产中需要调用微信API获取openid
*/
@Override
public Map<String, Object> wechatLogin(String code) {
// TODO: 调用微信API获取openid
// String openid = wechatService.getOpenId(code);
String openid = "wechat_" + code;
// 查询用户,不存在则创建
User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
.eq(User::getWechatOpenid, openid));
if (user == null) {
user = new User();
user.setUserId(UUID.randomUUID().toString());
user.setWechatOpenid(openid);
user.setUsername("微信用户");
user.setRole("sales");
user.setStatus(1);
userMapper.insert(user);
}
return generateTokens(user);
}
/**
* 支付宝扫码登录
* 实际生产中需要调用支付宝API获取openid
*/
@Override
public Map<String, Object> alipayLogin(String code) {
// TODO: 调用支付宝API获取openid
// String openid = alipayService.getOpenId(code);
String openid = "alipay_" + code;
// 查询用户,不存在则创建
User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
.eq(User::getAlipayOpenid, openid));
if (user == null) {
user = new User();
user.setUserId(UUID.randomUUID().toString());
user.setAlipayOpenid(openid);
user.setUsername("支付宝用户");
user.setRole("sales");
user.setStatus(1);
userMapper.insert(user);
}
return generateTokens(user);
}
/**
* 刷新Token
*/
@Override
public Map<String, Object> refreshToken(String refreshToken) {
if (!jwtUtil.validateToken(refreshToken)) {
throw new RuntimeException("刷新Token无效");
}
String userId = jwtUtil.getUserId(refreshToken);
User user = userMapper.selectById(userId);
if (user == null) {
throw new RuntimeException("用户不存在");
}
return generateTokens(user);
}
/**
* 获取当前用户信息
*/
@Override
public Map<String, Object> getCurrentUser(String userId) {
User user = userMapper.selectById(userId);
if (user == null) {
throw new RuntimeException("用户不存在");
}
Map<String, Object> result = new HashMap<>();
result.put("userId", user.getUserId());
result.put("username", user.getUsername());
result.put("phone", user.getPhone());
result.put("role", user.getRole());
return result;
}
/**
* 退出登录
*/
@Override
public void logout(String token) {
// 将token加入黑名单
redisTemplate.opsForValue().set("blacklist:" + token, "1", 2, TimeUnit.HOURS);
}
/**
* 生成Token和RefreshToken
*/
private Map<String, Object> generateTokens(User user) {
String token = jwtUtil.generateToken(user.getUserId(), user.getUsername(), user.getRole());
String refreshToken = jwtUtil.generateRefreshToken(user.getUserId());
Map<String, Object> result = new HashMap<>();
result.put("token", token);
result.put("refreshToken", refreshToken);
result.put("userId", user.getUserId());
result.put("username", user.getUsername());
result.put("role", user.getRole());
return result;
}
}

View File

@@ -0,0 +1,84 @@
package com.example.building.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.building.entity.Customer;
import com.example.building.mapper.CustomerMapper;
import com.example.building.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.util.UUID;
/**
* 客户服务实现类
*/
@Service
public class CustomerServiceImpl implements CustomerService {
@Autowired
private CustomerMapper customerMapper;
/**
* 客户列表
*/
@Override
public Page<Customer> getCustomers(String keyword, Integer page, Integer pageSize) {
Page<Customer> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<Customer> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.hasText(keyword)) {
wrapper.like(Customer::getName, keyword)
.or()
.like(Customer::getPhone, keyword);
}
wrapper.orderByDesc(Customer::getCreatedAt);
return customerMapper.selectPage(pageParam, wrapper);
}
/**
* 客户详情
*/
@Override
public Customer getCustomer(String id) {
Customer customer = customerMapper.selectById(id);
if (customer == null) {
throw new RuntimeException("客户不存在");
}
return customer;
}
/**
* 新增客户
*/
@Override
public Customer createCustomer(Customer customer) {
customer.setCustomerId(UUID.randomUUID().toString());
customer.setTotalAmount(BigDecimal.ZERO);
customerMapper.insert(customer);
return customer;
}
/**
* 修改客户
*/
@Override
public Customer updateCustomer(String id, Customer customer) {
Customer existing = customerMapper.selectById(id);
if (existing == null) {
throw new RuntimeException("客户不存在");
}
customer.setCustomerId(id);
customerMapper.updateById(customer);
return customer;
}
/**
* 删除客户
*/
@Override
public void deleteCustomer(String id) {
customerMapper.deleteById(id);
}
}

View File

@@ -0,0 +1,349 @@
package com.example.building.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.building.dto.CreateOrderRequest;
import com.example.building.entity.*;
import com.example.building.mapper.*;
import com.example.building.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* 订单服务实现类
* 核心业务逻辑:
* 1. 订单原价(total_amount) = Σ(item.price × item.quantity)
* 2. 优惠金额(discount_amount) = total_amount × (100 - discount_rate) / 100
* 3. 实付金额(actual_amount) = total_amount - discount_amount
*/
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Autowired
private ProductMapper productMapper;
@Autowired
private StockMapper stockMapper;
@Autowired
private StockFlowMapper stockFlowMapper;
@Autowired
private CustomerMapper customerMapper;
/**
* 创建订单
* 核心:价格计算、库存扣减、客户累计消费更新
*/
@Override
@Transactional
public Order createOrder(CreateOrderRequest request, String operatorId, String operatorName) {
// 1. 准备订单数据
Order order = new Order();
order.setOrderId(UUID.randomUUID().toString());
order.setOrderNo(generateOrderNo());
order.setOperatorId(operatorId);
order.setOperatorName(operatorName);
order.setDiscountRate(request.getDiscountRate() != null ? request.getDiscountRate() : new BigDecimal("100"));
order.setRemark(request.getRemark());
order.setPaymentMethod(request.getPaymentMethod());
order.setStatus(1); // 已完成
// 2. 查询客户信息(如果指定了客户)
if (request.getCustomerId() != null) {
Customer customer = customerMapper.selectById(request.getCustomerId());
if (customer != null) {
order.setCustomerId(customer.getCustomerId());
order.setCustomerName(customer.getName());
order.setCustomerPhone(customer.getPhone());
order.setCustomerWechat(customer.getWechatOpenid());
}
}
// 3. 计算订单金额
BigDecimal totalAmount = BigDecimal.ZERO; // 原价
List<OrderItem> orderItems = new ArrayList<>();
for (CreateOrderRequest.OrderItemDTO itemDTO : request.getItems()) {
// 查询商品信息
Product product = productMapper.selectById(itemDTO.getProductId());
if (product == null) {
throw new RuntimeException("商品不存在: " + itemDTO.getProductId());
}
// 使用用户指定价格或商品标价
BigDecimal price = itemDTO.getPrice() != null ? itemDTO.getPrice() : product.getPrice();
// 计算小计
BigDecimal subtotal = price.multiply(new BigDecimal(itemDTO.getQuantity()))
.setScale(2, RoundingMode.HALF_UP);
// 累加原价
totalAmount = totalAmount.add(subtotal);
// 构建订单明细
OrderItem item = new OrderItem();
item.setItemId(UUID.randomUUID().toString());
item.setOrderId(order.getOrderId());
item.setProductId(product.getProductId());
item.setProductName(product.getName());
item.setProductSpec(product.getSpec());
item.setUnit(product.getUnit());
item.setPrice(price);
item.setQuantity(itemDTO.getQuantity());
item.setSubtotal(subtotal);
orderItems.add(item);
// 4. 扣减库存
decreaseStock(product.getProductId(), itemDTO.getQuantity(), order.getOrderId(), operatorId);
}
// 设置订单原价
order.setTotalAmount(totalAmount);
// 5. 计算优惠金额和实付金额
BigDecimal discountRate = order.getDiscountRate();
BigDecimal discountAmount = totalAmount.multiply(new BigDecimal("100").subtract(discountRate))
.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
BigDecimal actualAmount = totalAmount.subtract(discountAmount);
order.setDiscountAmount(discountAmount);
order.setActualAmount(actualAmount);
// 6. 保存订单
orderMapper.insert(order);
// 7. 保存订单明细
for (OrderItem item : orderItems) {
orderItemMapper.insert(item);
}
// 8. 更新客户累计消费金额
if (request.getCustomerId() != null) {
Customer customer = customerMapper.selectById(request.getCustomerId());
if (customer != null) {
BigDecimal newTotal = customer.getTotalAmount().add(actualAmount);
customer.setTotalAmount(newTotal);
customerMapper.updateById(customer);
}
}
return order;
}
/**
* 获取订单列表
*/
@Override
public Page<Order> getOrders(String customerId, Integer status, String startDate, String endDate, Integer page, Integer pageSize) {
Page<Order> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
if (customerId != null) {
wrapper.eq(Order::getCustomerId, customerId);
}
if (status != null) {
wrapper.eq(Order::getStatus, status);
}
if (startDate != null) {
wrapper.ge(Order::getCreatedAt, startDate);
}
if (endDate != null) {
wrapper.le(Order::getCreatedAt, endDate);
}
wrapper.orderByDesc(Order::getCreatedAt);
return orderMapper.selectPage(pageParam, wrapper);
}
/**
* 获取订单详情(含明细)
*/
@Override
public Map<String, Object> getOrderDetail(String orderId) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
List<OrderItem> items = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>()
.eq(OrderItem::getOrderId, orderId));
Map<String, Object> result = new HashMap<>();
result.put("order", order);
result.put("items", items);
return result;
}
/**
* 取消订单
*/
@Override
@Transactional
public void cancelOrder(String orderId, String operatorId) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
if (order.getStatus() != 1) {
throw new RuntimeException("订单状态不允许取消");
}
// 恢复库存
List<OrderItem> items = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>()
.eq(OrderItem::getOrderId, orderId));
for (OrderItem item : items) {
increaseStock(item.getProductId(), item.getQuantity(), orderId, operatorId);
}
// 更新订单状态
order.setStatus(2); // 已取消
orderMapper.updateById(order);
}
/**
* 退款
*/
@Override
@Transactional
public void refundOrder(String orderId, String operatorId) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
if (order.getStatus() != 1) {
throw new RuntimeException("订单状态不允许退款");
}
// 恢复库存
List<OrderItem> items = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>()
.eq(OrderItem::getOrderId, orderId));
for (OrderItem item : items) {
increaseStock(item.getProductId(), item.getQuantity(), orderId, operatorId);
}
// 更新订单状态
order.setStatus(4); // 已退款
orderMapper.updateById(order);
}
/**
* 订单统计
*/
@Override
public Map<String, Object> getStatistics(String startDate, String endDate) {
LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Order::getStatus, 1);
if (startDate != null) {
wrapper.ge(Order::getCreatedAt, startDate);
}
if (endDate != null) {
wrapper.le(Order::getCreatedAt, endDate);
}
List<Order> orders = orderMapper.selectList(wrapper);
BigDecimal totalAmount = BigDecimal.ZERO; // 原价合计
BigDecimal actualAmount = BigDecimal.ZERO; // 实付合计
for (Order order : orders) {
totalAmount = totalAmount.add(order.getTotalAmount());
actualAmount = actualAmount.add(order.getActualAmount());
}
Map<String, Object> result = new HashMap<>();
result.put("orderCount", orders.size());
result.put("totalAmount", totalAmount);
result.put("actualAmount", actualAmount);
result.put("discountAmount", totalAmount.subtract(actualAmount));
return result;
}
/**
* 生成订单编号
* 规则: ORD + 年月日 + 6位序号
*/
private String generateOrderNo() {
String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String key = "order:no:" + date;
// 这里简化处理实际应使用Redis自增
int seq = (int) (Math.random() * 1000000);
return "ORD" + date + String.format("%06d", seq);
}
/**
* 扣减库存
*/
private void decreaseStock(String productId, Integer quantity, String relatedId, String operatorId) {
Stock stock = stockMapper.selectOne(new LambdaQueryWrapper<Stock>()
.eq(Stock::getProductId, productId));
if (stock == null) {
throw new RuntimeException("库存记录不存在");
}
if (stock.getQuantity() < quantity) {
throw new RuntimeException("库存不足");
}
int beforeQty = stock.getQuantity();
stock.setQuantity(beforeQty - quantity);
stockMapper.updateById(stock);
// 记录库存流水
saveStockFlow(productId, 2, -quantity, beforeQty, beforeQty - quantity, relatedId, "sale", operatorId);
}
/**
* 增加库存
*/
private void increaseStock(String productId, Integer quantity, String relatedId, String operatorId) {
Stock stock = stockMapper.selectOne(new LambdaQueryWrapper<Stock>()
.eq(Stock::getProductId, productId));
if (stock == null) {
stock = new Stock();
stock.setStockId(UUID.randomUUID().toString());
stock.setProductId(productId);
stock.setQuantity(0);
stock.setLockedQuantity(0);
stockMapper.insert(stock);
}
int beforeQty = stock.getQuantity();
stock.setQuantity(beforeQty + quantity);
stockMapper.updateById(stock);
// 记录库存流水
saveStockFlow(productId, 1, quantity, beforeQty, beforeQty + quantity, relatedId, "cancel", operatorId);
}
/**
* 保存库存流水
*/
private void saveStockFlow(String productId, Integer type, Integer quantity,
Integer beforeQty, Integer afterQty, String relatedId,
String relatedType, String operatorId) {
StockFlow flow = new StockFlow();
flow.setFlowId(UUID.randomUUID().toString());
flow.setProductId(productId);
flow.setType(type);
flow.setQuantity(quantity);
flow.setBeforeQuantity(beforeQty);
flow.setAfterQuantity(afterQty);
flow.setRelatedId(relatedId);
flow.setRelatedType(relatedType);
flow.setOperatorId(operatorId);
stockFlowMapper.insert(flow);
}
}

View File

@@ -0,0 +1,169 @@
package com.example.building.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.building.entity.Category;
import com.example.building.entity.Product;
import com.example.building.entity.Stock;
import com.example.building.mapper.CategoryMapper;
import com.example.building.mapper.ProductMapper;
import com.example.building.mapper.StockMapper;
import com.example.building.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.*;
/**
* 商品服务实现类
* 支持商品CRUD和分类管理
*/
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private StockMapper stockMapper;
/**
* 获取分类列表
*/
@Override
public List<Category> getCategories() {
return categoryMapper.selectList(new LambdaQueryWrapper<Category>()
.eq(Category::getStatus, 1)
.orderByAsc(Category::getSortOrder));
}
/**
* 新增分类
*/
@Override
public Category createCategory(Category category) {
category.setCategoryId(UUID.randomUUID().toString());
category.setStatus(1);
categoryMapper.insert(category);
return category;
}
/**
* 修改分类
*/
@Override
public Category updateCategory(String id, Category category) {
Category existing = categoryMapper.selectById(id);
if (existing == null) {
throw new RuntimeException("分类不存在");
}
category.setCategoryId(id);
categoryMapper.updateById(category);
return category;
}
/**
* 删除分类
*/
@Override
public void deleteCategory(String id) {
categoryMapper.deleteById(id);
}
/**
* 获取商品列表
*/
@Override
public Page<Product> getProducts(String categoryId, String keyword, Integer page, Integer pageSize) {
Page<Product> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Product::getStatus, 1);
if (StringUtils.hasText(categoryId)) {
wrapper.eq(Product::getCategoryId, categoryId);
}
if (StringUtils.hasText(keyword)) {
wrapper.like(Product::getName, keyword);
}
wrapper.orderByDesc(Product::getCreatedAt);
return productMapper.selectPage(pageParam, wrapper);
}
/**
* 获取商品详情
*/
@Override
public Product getProduct(String id) {
Product product = productMapper.selectById(id);
if (product == null || product.getStatus() == 0) {
throw new RuntimeException("商品不存在");
}
return product;
}
/**
* 新增商品
*/
@Override
public Product createProduct(Product product) {
product.setProductId(UUID.randomUUID().toString());
product.setStatus(1);
productMapper.insert(product);
return product;
}
/**
* 修改商品
*/
@Override
public Product updateProduct(String id, Product product) {
Product existing = productMapper.selectById(id);
if (existing == null) {
throw new RuntimeException("商品不存在");
}
product.setProductId(id);
productMapper.updateById(product);
return product;
}
/**
* 删除商品(软删)
*/
@Override
public void deleteProduct(String id) {
Product product = new Product();
product.setProductId(id);
product.setStatus(0);
productMapper.updateById(product);
}
/**
* 获取库存预警商品
* 查询库存低于预警值的商品
*/
@Override
public List<Map<String, Object>> getStockAlerts() {
// 查询所有商品及其库存
List<Product> products = productMapper.selectList(new LambdaQueryWrapper<Product>()
.eq(Product::getStatus, 1));
List<Map<String, Object>> alerts = new ArrayList<>();
for (Product product : products) {
Stock stock = stockMapper.selectOne(new LambdaQueryWrapper<Stock>()
.eq(Stock::getProductId, product.getProductId()));
int quantity = stock != null ? stock.getQuantity() : 0;
if (quantity < product.getStockAlert()) {
Map<String, Object> alert = new HashMap<>();
alert.put("productId", product.getProductId());
alert.put("productName", product.getName());
alert.put("stockAlert", product.getStockAlert());
alert.put("currentStock", quantity);
alerts.add(alert);
}
}
return alerts;
}
}

View File

@@ -0,0 +1,179 @@
package com.example.building.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.building.entity.Product;
import com.example.building.entity.Stock;
import com.example.building.entity.StockFlow;
import com.example.building.mapper.ProductMapper;
import com.example.building.mapper.StockFlowMapper;
import com.example.building.mapper.StockMapper;
import com.example.building.service.StockService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.UUID;
/**
* 库存服务实现类
* 核心业务:入库、库存查询、库存流水
*/
@Service
public class StockServiceImpl implements StockService {
@Autowired
private StockMapper stockMapper;
@Autowired
private StockFlowMapper stockFlowMapper;
@Autowired
private ProductMapper productMapper;
/**
* 入库
*/
@Override
@Transactional
public Stock stockIn(String productId, Integer quantity, String remark, String operatorId) {
// 验证商品存在
Product product = productMapper.selectById(productId);
if (product == null) {
throw new RuntimeException("商品不存在");
}
// 查询或创建库存记录
Stock stock = stockMapper.selectOne(new LambdaQueryWrapper<Stock>()
.eq(Stock::getProductId, productId));
int beforeQty = 0;
if (stock == null) {
stock = new Stock();
stock.setStockId(UUID.randomUUID().toString());
stock.setProductId(productId);
stock.setQuantity(0);
stock.setLockedQuantity(0);
stockMapper.insert(stock);
} else {
beforeQty = stock.getQuantity();
}
// 更新库存
stock.setQuantity(beforeQty + quantity);
stockMapper.updateById(stock);
// 记录库存流水 (类型1: 入库)
saveStockFlow(productId, 1, quantity, beforeQty, beforeQty + quantity,
null, "stock_in", operatorId, remark);
return stock;
}
/**
* 库存查询
*/
@Override
public Page<Stock> getStockList(String keyword, Integer page, Integer pageSize) {
Page<Stock> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<Stock> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.hasText(keyword)) {
wrapper.like(Stock::getProductId, keyword);
}
wrapper.orderByDesc(Stock::getUpdatedAt);
return stockMapper.selectPage(pageParam, wrapper);
}
/**
* 单商品库存
*/
@Override
public Stock getStock(String productId) {
Stock stock = stockMapper.selectOne(new LambdaQueryWrapper<Stock>()
.eq(Stock::getProductId, productId));
if (stock == null) {
stock = new Stock();
stock.setProductId(productId);
stock.setQuantity(0);
stock.setLockedQuantity(0);
}
return stock;
}
/**
* 库存调整
*/
@Override
@Transactional
public Stock adjustStock(String productId, Integer quantity, String remark, String operatorId) {
// 验证商品存在
Product product = productMapper.selectById(productId);
if (product == null) {
throw new RuntimeException("商品不存在");
}
// 查询或创建库存记录
Stock stock = stockMapper.selectOne(new LambdaQueryWrapper<Stock>()
.eq(Stock::getProductId, productId));
int beforeQty = 0;
if (stock == null) {
stock = new Stock();
stock.setStockId(UUID.randomUUID().toString());
stock.setProductId(productId);
stock.setQuantity(quantity);
stock.setLockedQuantity(0);
stockMapper.insert(stock);
beforeQty = 0;
} else {
beforeQty = stock.getQuantity();
stock.setQuantity(quantity);
stockMapper.updateById(stock);
}
// 记录库存流水 (类型3: 调整)
int changeQty = quantity - beforeQty;
saveStockFlow(productId, 3, changeQty, beforeQty, quantity,
null, "adjust", operatorId, remark);
return stock;
}
/**
* 库存流水
*/
@Override
public Page<StockFlow> getStockFlow(String productId, Integer type, Integer page, Integer pageSize) {
Page<StockFlow> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<StockFlow> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.hasText(productId)) {
wrapper.eq(StockFlow::getProductId, productId);
}
if (type != null) {
wrapper.eq(StockFlow::getType, type);
}
wrapper.orderByDesc(StockFlow::getCreatedAt);
return stockFlowMapper.selectPage(pageParam, wrapper);
}
/**
* 保存库存流水
*/
private void saveStockFlow(String productId, Integer type, Integer quantity,
Integer beforeQty, Integer afterQty, String relatedId,
String relatedType, String operatorId, String remark) {
StockFlow flow = new StockFlow();
flow.setFlowId(UUID.randomUUID().toString());
flow.setProductId(productId);
flow.setType(type);
flow.setQuantity(quantity);
flow.setBeforeQuantity(beforeQty);
flow.setAfterQuantity(afterQty);
flow.setRelatedId(relatedId);
flow.setRelatedType(relatedType);
flow.setOperatorId(operatorId);
flow.setRemark(remark);
stockFlowMapper.insert(flow);
}
}