Compare commits

...

6 Commits

9 changed files with 712 additions and 270 deletions

View File

@ -0,0 +1,333 @@
package org.jeecg.modules.demo.shop.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.demo.shop.entity.Order;
import org.jeecg.modules.demo.shop.entity.Product;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* 商品管理模拟接口
* 用于AI Agent通过工具帮助用户查询商品并采购的业务演示
* @Author: chenrui
* @Date: 2025-11-06
*/
@Tag(name = "商品管理Demo")
@RestController
@RequestMapping("/demo/shop")
@Slf4j
public class ShopController {
/**
* 商品数据存储(内存)
*/
private final Map<String, Product> productStore = new ConcurrentHashMap<>();
/**
* 订单数据存储(内存)
*/
private final Map<String, Order> orderStore = new ConcurrentHashMap<>();
/**
* 订单ID生成器
*/
private final AtomicInteger orderIdGenerator = new AtomicInteger(1000);
/**
* 初始化商品数据
*
* @author chenrui
* @date 2025/11/6 14:30
*/
@PostConstruct
public void initProducts() {
// 电子产品
productStore.put("P001", new Product("P001", "iPhone 15 Pro", new BigDecimal("7999.00"), "电子产品", "Apple最新旗舰手机,6.1英寸屏幕,钛金属边框", 50));
productStore.put("P002", new Product("P002", "MacBook Pro 14", new BigDecimal("14999.00"), "电子产品", "M3 Pro芯片,16GB内存,512GB存储", 30));
productStore.put("P003", new Product("P003", "AirPods Pro 2", new BigDecimal("1899.00"), "电子产品", "主动降噪无线耳机,支持空间音频", 100));
productStore.put("P004", new Product("P004", "iPad Air", new BigDecimal("4799.00"), "电子产品", "10.9英寸液晶显示屏,M1芯片", 60));
// 图书
productStore.put("B001", new Product("B001", "Java核心技术卷I", new BigDecimal("119.00"), "图书", "Java编程经典教材,适合初学者和进阶开发者", 200));
productStore.put("B002", new Product("B002", "深入理解计算机系统", new BigDecimal("139.00"), "图书", "CSAPP经典教材,计算机系统必读书籍", 150));
productStore.put("B003", new Product("B003", "设计模式", new BigDecimal("89.00"), "图书", "软件设计经典著作,GoF四人组著作", 180));
// 生活用品
productStore.put("L001", new Product("L001", "小米电动牙刷", new BigDecimal("199.00"), "生活用品", "声波震动,IPX7防水,续航18天", 300));
productStore.put("L002", new Product("L002", "戴森吹风机", new BigDecimal("2990.00"), "生活用品", "快速干发,智能温控,保护头发", 80));
productStore.put("L003", new Product("L003", "膳魔师保温杯", new BigDecimal("259.00"), "生活用品", "真空保温,304不锈钢,保温12小时", 500));
// 食品
productStore.put("F001", new Product("F001", "三只松鼠坚果礼盒", new BigDecimal("159.00"), "食品", "混合坚果大礼包,1500g装", 400));
productStore.put("F002", new Product("F002", "茅台飞天53度", new BigDecimal("2899.00"), "食品", "贵州茅台酒,500ml", 20));
productStore.put("F003", new Product("F003", "星巴克咖啡豆", new BigDecimal("128.00"), "食品", "中度烘焙,派克市场,250g", 250));
log.info("商品数据初始化完成,共{}个商品", productStore.size());
}
/**
* 查询商品列表
*
* @param category 商品分类(可选)
* @param keyword 搜索关键词(可选)
* @return 商品列表
* @author chenrui
* @date 2025/11/6 14:30
*/
@Operation(summary = "查询商品列表", description = "支持按分类和关键词搜索")
@GetMapping("/products")
public Result<List<Product>> getProducts(
@Parameter(description = "商品分类") @RequestParam(required = false) String category,
@Parameter(description = "搜索关键词") @RequestParam(required = false) String keyword) {
log.info("查询商品列表 - 分类: {}, 关键词: {}", category, keyword);
List<Product> products = new ArrayList<>(productStore.values());
// 按分类过滤
if (category != null && !category.trim().isEmpty()) {
products = products.stream()
.filter(p -> category.equals(p.getCategory()))
.collect(Collectors.toList());
}
// 按关键词过滤(搜索商品名称和描述)
if (keyword != null && !keyword.trim().isEmpty()) {
String searchKey = keyword.toLowerCase();
products = products.stream()
.filter(p -> p.getName().toLowerCase().contains(searchKey)
|| p.getDescription().toLowerCase().contains(searchKey))
.collect(Collectors.toList());
}
// 按价格排序
products.sort(Comparator.comparing(Product::getPrice));
log.info("查询到{}个商品", products.size());
return Result.OK(products);
}
/**
* 查询商品库存
*
* @param productId 商品ID
* @return 库存信息
* @author chenrui
* @date 2025/11/6 14:30
*/
@Operation(summary = "查询商品库存", description = "根据商品ID查询库存数量")
@GetMapping("/stock")
public Result<Map<String, Object>> getStock(
@Parameter(description = "商品ID", required = true) @RequestParam String productId) {
log.info("查询商品库存 - 商品ID: {}", productId);
Product product = productStore.get(productId);
if (product == null) {
return Result.error("商品不存在: " + productId);
}
Map<String, Object> stockInfo = new HashMap<>();
stockInfo.put("productId", product.getId());
stockInfo.put("productName", product.getName());
stockInfo.put("stock", product.getStock());
stockInfo.put("available", product.getStock() > 0);
return Result.OK(stockInfo);
}
/**
* 购买商品(下单)
*
* @param productId 商品ID
* @param quantity 购买数量
* @param userId 用户ID(可选)
* @return 订单信息
* @author chenrui
* @date 2025/11/6 14:30
*/
@Operation(summary = "购买商品", description = "创建订单,但不立即扣减库存")
@PostMapping("/purchase")
public Result<Order> purchase(
@Parameter(description = "商品ID", required = true) @RequestParam String productId,
@Parameter(description = "购买数量", required = true) @RequestParam Integer quantity,
@Parameter(description = "用户ID") @RequestParam(required = false) String userId) {
log.info("购买商品 - 商品ID: {}, 数量: {}, 用户: {}", productId, quantity, userId);
// 参数校验
if (quantity == null || quantity <= 0) {
return Result.error("购买数量必须大于0");
}
// 查询商品
Product product = productStore.get(productId);
if (product == null) {
return Result.error("商品不存在: " + productId);
}
// 检查库存
if (product.getStock() < quantity) {
return Result.error("库存不足,当前库存: " + product.getStock());
}
// 创建订单
String orderId = "O" + orderIdGenerator.incrementAndGet();
BigDecimal totalAmount = product.getPrice().multiply(new BigDecimal(quantity));
Order order = new Order();
order.setId(orderId);
order.setProductId(productId);
order.setProductName(product.getName());
order.setQuantity(quantity);
order.setUnitPrice(product.getPrice());
order.setTotalAmount(totalAmount);
order.setStatus("pending");
order.setCreateTime(new Date());
order.setUserId(userId);
orderStore.put(orderId, order);
log.info("订单创建成功 - 订单ID: {}, 总金额: {}", orderId, totalAmount);
return Result.OK(order);
}
/**
* 扣减商品库存
*
* @param orderId 订单ID
* @return 扣减结果
* @author chenrui
* @date 2025/11/6 14:30
*/
@Operation(summary = "扣减商品库存", description = "根据订单ID扣减对应商品库存")
@PostMapping("/stock/deduct")
public Result<Map<String, Object>> deductStock(
@Parameter(description = "订单ID", required = true) @RequestParam String orderId) {
log.info("扣减库存 - 订单ID: {}", orderId);
// 查询订单
Order order = orderStore.get(orderId);
if (order == null) {
return Result.error("订单不存在: " + orderId);
}
// 检查订单状态
if ("paid".equals(order.getStatus())) {
return Result.error("订单已支付,库存已扣减");
}
if ("cancelled".equals(order.getStatus())) {
return Result.error("订单已取消");
}
// 查询商品
Product product = productStore.get(order.getProductId());
if (product == null) {
return Result.error("商品不存在: " + order.getProductId());
}
// 检查库存
synchronized (product) {
if (product.getStock() < order.getQuantity()) {
return Result.error("库存不足,当前库存: " + product.getStock() + ", 需要: " + order.getQuantity());
}
// 扣减库存
int newStock = product.getStock() - order.getQuantity();
product.setStock(newStock);
// 更新订单状态
order.setStatus("paid");
log.info("库存扣减成功 - 商品: {}, 扣减数量: {}, 剩余库存: {}",
product.getName(), order.getQuantity(), newStock);
}
Map<String, Object> result = new HashMap<>();
result.put("orderId", orderId);
result.put("productId", product.getId());
result.put("productName", product.getName());
result.put("deductedQuantity", order.getQuantity());
result.put("remainingStock", product.getStock());
result.put("orderStatus", order.getStatus());
return Result.OK(result);
}
/**
* 查询订单详情
*
* @param orderId 订单ID
* @return 订单详情
* @author chenrui
* @date 2025/11/6 14:30
*/
@Operation(summary = "查询订单详情", description = "根据订单ID查询订单信息")
@GetMapping("/order")
public Result<Order> getOrder(
@Parameter(description = "订单ID", required = true) @RequestParam String orderId) {
log.info("查询订单 - 订单ID: {}", orderId);
Order order = orderStore.get(orderId);
if (order == null) {
return Result.error("订单不存在: " + orderId);
}
return Result.OK(order);
}
/**
* 获取所有商品分类
*
* @return 分类列表
* @author chenrui
* @date 2025/11/6 14:30
*/
@Operation(summary = "获取商品分类", description = "获取所有商品的分类列表")
@GetMapping("/categories")
public Result<List<String>> getCategories() {
Set<String> categories = productStore.values().stream()
.map(Product::getCategory)
.collect(Collectors.toSet());
List<String> categoryList = new ArrayList<>(categories);
categoryList.sort(String::compareTo);
return Result.OK(categoryList);
}
/**
* 重置所有数据(仅用于测试)
*
* @return 重置结果
* @author chenrui
* @date 2025/11/6 14:30
*/
@Operation(summary = "重置数据", description = "清空所有订单并重置商品库存(仅用于测试)")
@PostMapping("/reset")
public Result<String> reset() {
log.info("重置商品和订单数据");
orderStore.clear();
orderIdGenerator.set(1000);
productStore.clear();
initProducts();
return Result.OK("数据重置成功");
}
}

View File

@ -0,0 +1,64 @@
package org.jeecg.modules.demo.shop.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.Date;
/**
* 订单实体
* @Author: chenrui
* @Date: 2025-11-06
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
/**
* 订单ID
*/
private String id;
/**
* 商品ID
*/
private String productId;
/**
* 商品名称
*/
private String productName;
/**
* 购买数量
*/
private Integer quantity;
/**
* 单价
*/
private BigDecimal unitPrice;
/**
* 总金额
*/
private BigDecimal totalAmount;
/**
* 订单状态: pending-待支付, paid-已支付, cancelled-已取消
*/
private String status;
/**
* 创建时间
*/
private Date createTime;
/**
* 用户信息(可选)
*/
private String userId;
}

View File

@ -0,0 +1,48 @@
package org.jeecg.modules.demo.shop.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* 商品实体
* @Author: chenrui
* @Date: 2025-11-06
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
/**
* 商品ID
*/
private String id;
/**
* 商品名称
*/
private String name;
/**
* 商品价格
*/
private BigDecimal price;
/**
* 商品分类
*/
private String category;
/**
* 商品描述
*/
private String description;
/**
* 库存数量
*/
private Integer stock;
}

View File

@ -82,14 +82,13 @@ public class JeecgBizToolsProvider implements JeecgToolsProvider {
"\n\n - 你应该提前判断用户的输入是否合法,比如用户名是否符合规范,手机号和邮箱是否正确等." +
"\n\n - 提前使用用户名查询用户是否存在,如果存在则不能添加." +
"\n\n - 添加成功后返回成功消息,如果失败则返回失败原因." +
"\n\n - 用户名,工号,邮箱,手机号均要求唯一,提前通过查询用户工具确认唯一性." )
"\n\n - 用户名,邮箱,手机号均要求唯一,提前通过查询用户工具确认唯一性." )
.parameters(
JsonObjectSchema.builder()
.addStringProperty("username", "用户名,必填,只允许使用字母、数字、下划线,且必须以字母开头,唯一")
.addStringProperty("password", "用户密码,必填")
.addStringProperty("realname", "真实姓名,必填")
.addStringProperty("workNo", "工号,必填,唯一")
.addStringProperty("email", "邮箱,必填,唯一")
//.addStringProperty("email", "邮箱,必填,唯一")
.addStringProperty("phone", "手机号,必填,唯一")
.required("username","password","realname","workNo","email","phone")
.build()
@ -140,14 +139,13 @@ public class JeecgBizToolsProvider implements JeecgToolsProvider {
private JeecgLlmTools queryUserTool() {
ToolSpecification toolSpecification = ToolSpecification.builder()
.name("query_user_by_name")
.description("查询用户详细信息返回json数组。支持用户名、真实姓名、邮箱、手机号、工号多字段组合查询,用户名、真实姓名、邮箱、手机号均为模糊查询,工号为精确查询。无条件则返回全部用户。")
.description("查询用户详细信息返回json数组。支持用户名、真实姓名、邮箱、手机号 多字段组合查询,用户名、真实姓名、邮箱、手机号均为模糊查询。无条件则返回全部用户。")
.parameters(
JsonObjectSchema.builder()
.addStringProperty("username", "用户名")
.addStringProperty("realname", "真实姓名")
.addStringProperty("email", "电子邮件")
.addStringProperty("phone", "手机号")
.addStringProperty("workNo", "工号")
.build()
)
.build();

File diff suppressed because one or more lines are too long

View File

@ -23,8 +23,8 @@
"husky:install": "husky install"
},
"dependencies": {
"@jeecg/online": "3.9.0-beta",
"@jeecg/aiflow": "3.9.0-beta",
"@jeecg/online": "3.9.0-beta2",
"@jeecg/aiflow":"3.9.0-beta2",
"@logicflow/core": "^2.0.10",
"@logicflow/extension": "^2.0.14",
"@logicflow/vue-node-registry": "^1.0.12",

File diff suppressed because it is too large Load Diff

View File

@ -53,7 +53,6 @@ if errorlevel 1 (
if %errorlevel% neq 0 (
echo [错误] 设置 hosts 文件失败,请检查权限!
pause
exit /b 1
)
echo [3/5] 编译后端项目...

View File

@ -53,7 +53,6 @@ if errorlevel 1 (
if %errorlevel% neq 0 (
echo [错误] 设置 hosts 文件失败,请检查权限!
pause
exit /b 1
)
echo [3/5] 编译后端项目...