From 75e996aa3050f119664ba90efc7469a9b5784962 Mon Sep 17 00:00:00 2001 From: Agent Date: Mon, 30 Mar 2026 13:45:07 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=85=AC=E5=BC=8F?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E8=87=AA=E5=8A=A8=E8=AE=A1=E7=AE=97=EF=BC=88?= =?UTF-8?q?=E5=A6=82=E9=9D=A2=E7=A7=AF=3D=E9=95=BF*=E5=AE=BD=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ProductController.java | 16 +- .../com/example/building/entity/Product.java | 5 + .../service/ProductAttributeService.java | 7 +- .../impl/ProductAttributeServiceImpl.java | 186 ++++++++++++++++-- 4 files changed, 195 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/example/building/controller/ProductController.java b/src/main/java/com/example/building/controller/ProductController.java index c3f5e18..4436046 100644 --- a/src/main/java/com/example/building/controller/ProductController.java +++ b/src/main/java/com/example/building/controller/ProductController.java @@ -105,7 +105,15 @@ public class ProductController { if (!"admin".equals(role)) { return Result.error("只有管理员可以操作"); } - return Result.success(productService.createProduct(product)); + Product created = productService.createProduct(product); + // 如果有属性,一并保存 + if (product.getAttributes() != null && !product.getAttributes().isEmpty()) { + productAttributeService.saveProductAttributes( + created.getProductId(), + created.getCategoryId(), + product.getAttributes()); + } + return Result.success(created); } /** @@ -180,12 +188,14 @@ public class ProductController { @PostMapping("/{productId}/attributes") public Result saveProductAttributes( @PathVariable String productId, - @RequestBody List> attrs, + @RequestBody Map body, @RequestHeader(value = "X-User-Role", required = false) String role) { if (!"admin".equals(role)) { return Result.error("只有管理员可以操作"); } - productAttributeService.saveProductAttributes(productId, attrs); + String categoryId = (String) body.get("categoryId"); + List> attrs = (List>) body.get("attributes"); + productAttributeService.saveProductAttributes(productId, categoryId, attrs); return Result.success(); } } diff --git a/src/main/java/com/example/building/entity/Product.java b/src/main/java/com/example/building/entity/Product.java index e7588df..8367861 100644 --- a/src/main/java/com/example/building/entity/Product.java +++ b/src/main/java/com/example/building/entity/Product.java @@ -5,6 +5,8 @@ import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; /** * 商品实体类 @@ -71,6 +73,9 @@ public class Product { */ private Integer status; + @TableField(exist = false) + private List> attributes; + @TableField(fill = FieldFill.INSERT) private LocalDateTime createdAt; diff --git a/src/main/java/com/example/building/service/ProductAttributeService.java b/src/main/java/com/example/building/service/ProductAttributeService.java index e47d4d6..9216919 100644 --- a/src/main/java/com/example/building/service/ProductAttributeService.java +++ b/src/main/java/com/example/building/service/ProductAttributeService.java @@ -15,9 +15,12 @@ public interface ProductAttributeService { List getByProductId(String productId); /** - * 保存商品属性值 + * 保存商品属性值(自动计算公式属性) + * @param productId 商品ID + * @param categoryId 种类ID(用于获取公式定义) + * @param attrs 属性列表 */ - void saveProductAttributes(String productId, List> attrs); + void saveProductAttributes(String productId, String categoryId, List> attrs); /** * 删除商品的所有属性 diff --git a/src/main/java/com/example/building/service/impl/ProductAttributeServiceImpl.java b/src/main/java/com/example/building/service/impl/ProductAttributeServiceImpl.java index fcb1dae..9b050d2 100644 --- a/src/main/java/com/example/building/service/impl/ProductAttributeServiceImpl.java +++ b/src/main/java/com/example/building/service/impl/ProductAttributeServiceImpl.java @@ -1,16 +1,18 @@ package com.example.building.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.example.building.entity.CategoryAttribute; import com.example.building.entity.ProductAttribute; +import com.example.building.mapper.CategoryAttributeMapper; import com.example.building.mapper.ProductAttributeMapper; import com.example.building.service.ProductAttributeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * 商品属性服务实现 @@ -21,6 +23,9 @@ public class ProductAttributeServiceImpl implements ProductAttributeService { @Autowired private ProductAttributeMapper productAttributeMapper; + @Autowired + private CategoryAttributeMapper categoryAttributeMapper; + @Override public List getByProductId(String productId) { return productAttributeMapper.selectList(new LambdaQueryWrapper() @@ -29,23 +34,176 @@ public class ProductAttributeServiceImpl implements ProductAttributeService { @Override @Transactional - public void saveProductAttributes(String productId, List> attrs) { + public void saveProductAttributes(String productId, String categoryId, List> attrs) { + // 获取种类的属性定义(包括公式) + List categoryAttrs = categoryAttributeMapper.selectList( + new LambdaQueryWrapper() + .eq(CategoryAttribute::getCategoryId, categoryId) + .orderByAsc(CategoryAttribute::getSortOrder)); + + // 构建属性名->属性定义的映射 + Map attrDefMap = new HashMap<>(); + for (CategoryAttribute ca : categoryAttrs) { + attrDefMap.put(ca.getName(), ca); + } + + // 先把输入的属性保存到 Map 中(用于公式计算) + Map attrValues = new HashMap<>(); + List toSave = new ArrayList<>(); + + // 第一遍:保存所有普通属性 + for (Map attr : attrs) { + String attrName = (String) attr.get("attrName"); + String attrValue = String.valueOf(attr.get("attrValue")); + + // 尝试转换为数字 + try { + attrValues.put(attrName, Double.parseDouble(attrValue)); + } catch (NumberFormatException e) { + // 非数字类型不做计算 + } + + ProductAttribute pa = new ProductAttribute(); + pa.setId(UUID.randomUUID().toString()); + pa.setProductId(productId); + pa.setAttrId((String) attr.get("attrId")); + pa.setAttrName(attrName); + pa.setAttrValue(attrValue); + toSave.add(pa); + } + + // 第二遍:处理公式属性 + for (CategoryAttribute ca : categoryAttrs) { + if ("formula".equals(ca.getAttrType()) && ca.getFormula() != null && !ca.getFormula().isEmpty()) { + String formula = ca.getFormula(); + try { + double result = evaluateFormula(formula, attrValues); + // 保留两位小数 + result = Math.round(result * 100.0) / 100.0; + + ProductAttribute pa = new ProductAttribute(); + pa.setId(UUID.randomUUID().toString()); + pa.setProductId(productId); + pa.setAttrId(ca.getAttrId()); + pa.setAttrName(ca.getName()); + pa.setAttrValue(String.valueOf(result)); + toSave.add(pa); + + System.out.println("公式计算: " + formula + " = " + result); + } catch (Exception e) { + System.out.println("公式计算失败: " + formula + ", error: " + e.getMessage()); + } + } + } + // 删除旧属性 productAttributeMapper.delete(new LambdaQueryWrapper() .eq(ProductAttribute::getProductId, productId)); - + // 保存新属性 - if (attrs != null && !attrs.isEmpty()) { - for (Map attr : attrs) { - ProductAttribute pa = new ProductAttribute(); - pa.setId(UUID.randomUUID().toString()); - pa.setProductId(productId); - pa.setAttrId((String) attr.get("attrId")); - pa.setAttrName((String) attr.get("attrName")); - pa.setAttrValue(String.valueOf(attr.get("attrValue"))); - productAttributeMapper.insert(pa); + for (ProductAttribute pa : toSave) { + productAttributeMapper.insert(pa); + } + } + + /** + * 解析并计算公式 + * 支持四则运算 + - * / 和括号 () + * 变量名用实际值替换 + */ + private double evaluateFormula(String formula, Map variables) { + // 替换变量为数值 + String expr = formula; + Pattern pattern = Pattern.compile("[a-zA-Z_]+"); + Matcher matcher = pattern.matcher(formula); + while (matcher.find()) { + String varName = matcher.group(); + if (variables.containsKey(varName)) { + expr = expr.replace(varName, String.valueOf(variables.get(varName))); } } + + // 使用 JavaScript 引擎计算表达式 + return evalExpression(expr); + } + + /** + * 简单的表达式计算器(支持 + - * / 和括号) + */ + private double evalExpression(String expr) { + // 去除空格 + expr = expr.replace(" ", ""); + + return parseExpression(expr, new int[]{0}); + } + + private double parseExpression(String expr, int[] pos) { + double result = parseTerm(expr, pos); + + while (pos[0] < expr.length()) { + char op = expr.charAt(pos[0]); + if (op != '+' && op != '-') break; + pos[0]++; + double term = parseTerm(expr, pos); + if (op == '+') { + result += term; + } else { + result -= term; + } + } + return result; + } + + private double parseTerm(String expr, int[] pos) { + double result = parseFactor(expr, pos); + + while (pos[0] < expr.length()) { + char op = expr.charAt(pos[0]); + if (op != '*' && op != '/') break; + pos[0]++; + double factor = parseFactor(expr, pos); + if (op == '*') { + result *= factor; + } else { + if (factor != 0) { + result /= factor; + } + } + } + return result; + } + + private double parseFactor(String expr, int[] pos) { + // 处理括号 + if (pos[0] < expr.length() && expr.charAt(pos[0]) == '(') { + pos[0]++; // 跳过 ( + double result = parseExpression(expr, pos); + if (pos[0] < expr.length() && expr.charAt(pos[0]) == ')') { + pos[0]++; // 跳过 ) + } + return result; + } + + // 处理负数 + boolean negative = false; + if (pos[0] < expr.length() && expr.charAt(pos[0]) == '-') { + negative = true; + pos[0]++; + } + + // 解析数字 + double result = 0; + while (pos[0] < expr.length()) { + char c = expr.charAt(pos[0]); + if ((c >= '0' && c <= '9') || c == '.') { + result = result * 10 + (c - '0'); + pos[0]++; + } else { + break; + } + } + + return negative ? -result : result; } @Override