diff --git a/src/main/java/com/example/building/controller/CategoryAttributeController.java b/src/main/java/com/example/building/controller/CategoryAttributeController.java deleted file mode 100644 index d36c6a1..0000000 --- a/src/main/java/com/example/building/controller/CategoryAttributeController.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.example.building.controller; - -import com.example.building.common.Result; -import com.example.building.entity.CategoryAttribute; -import com.example.building.service.CategoryAttributeService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -/** - * 种类属性控制器 - */ -@RestController -@RequestMapping("/api/v1/category-attributes") -public class CategoryAttributeController { - - @Autowired - private CategoryAttributeService categoryAttributeService; - - /** - * 获取种类的属性列表 - */ - @GetMapping("/category/{categoryId}") - public Result> getByCategoryId(@PathVariable String categoryId) { - return Result.success(categoryAttributeService.getByCategoryId(categoryId)); - } - - /** - * 保存种类属性 - */ - @PostMapping("/category/{categoryId}") - public Result saveAttributes(@PathVariable String categoryId, @RequestBody List attrs) { - categoryAttributeService.saveAttributes(categoryId, attrs); - return Result.success(); - } -} \ No newline at end of file diff --git a/src/main/java/com/example/building/controller/ProductController.java b/src/main/java/com/example/building/controller/ProductController.java index 4436046..6c7d673 100644 --- a/src/main/java/com/example/building/controller/ProductController.java +++ b/src/main/java/com/example/building/controller/ProductController.java @@ -3,11 +3,8 @@ package com.example.building.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.example.building.common.Result; import com.example.building.entity.Category; -import com.example.building.entity.CategoryAttribute; import com.example.building.entity.Product; import com.example.building.service.ProductService; -import com.example.building.service.CategoryAttributeService; -import com.example.building.service.ProductAttributeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -25,12 +22,6 @@ public class ProductController { @Autowired private ProductService productService; - @Autowired - private CategoryAttributeService categoryAttributeService; - - @Autowired - private ProductAttributeService productAttributeService; - /** * 获取分类列表 */ @@ -105,15 +96,7 @@ public class ProductController { if (!"admin".equals(role)) { return Result.error("只有管理员可以操作"); } - Product created = productService.createProduct(product); - // 如果有属性,一并保存 - if (product.getAttributes() != null && !product.getAttributes().isEmpty()) { - productAttributeService.saveProductAttributes( - created.getProductId(), - created.getCategoryId(), - product.getAttributes()); - } - return Result.success(created); + return Result.success(productService.createProduct(product)); } /** @@ -148,54 +131,4 @@ public class ProductController { public Result>> getStockAlerts() { return Result.success(productService.getStockAlerts()); } - - // ============ 属性相关 ============ - - /** - * 获取种类的属性定义 - */ - @GetMapping("/categories/{categoryId}/attributes") - public Result getCategoryAttributes(@PathVariable String categoryId) { - return Result.success(categoryAttributeService.getByCategoryId(categoryId)); - } - - /** - * 保存种类的属性定义 - */ - @PostMapping("/categories/{categoryId}/attributes") - public Result saveCategoryAttributes( - @PathVariable String categoryId, - @RequestBody List attrs, - @RequestHeader(value = "X-User-Role", required = false) String role) { - if (!"admin".equals(role)) { - return Result.error("只有管理员可以操作"); - } - categoryAttributeService.saveAttributes(categoryId, attrs); - return Result.success(); - } - - /** - * 获取商品的属性值 - */ - @GetMapping("/{productId}/attributes") - public Result getProductAttributes(@PathVariable String productId) { - return Result.success(productAttributeService.getByProductId(productId)); - } - - /** - * 保存商品的属性值 - */ - @PostMapping("/{productId}/attributes") - public Result saveProductAttributes( - @PathVariable String productId, - @RequestBody Map body, - @RequestHeader(value = "X-User-Role", required = false) String role) { - if (!"admin".equals(role)) { - return Result.error("只有管理员可以操作"); - } - String categoryId = (String) body.get("categoryId"); - List> attrs = (List>) body.get("attributes"); - productAttributeService.saveProductAttributes(productId, categoryId, attrs); - return Result.success(); - } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/building/entity/CategoryAttribute.java b/src/main/java/com/example/building/entity/CategoryAttribute.java deleted file mode 100644 index 392fec2..0000000 --- a/src/main/java/com/example/building/entity/CategoryAttribute.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.example.building.entity; - -import com.baomidou.mybatisplus.annotation.*; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * 种类属性定义实体 - */ -@Data -@TableName("category_attributes") -public class CategoryAttribute { - - @TableId(type = IdType.ASSIGN_UUID) - private String attrId; - - /** - * 种类ID - */ - private String categoryId; - - /** - * 属性名称 - */ - private String name; - - /** - * 属性类型: number=数字, text=文本, formula=公式 - */ - private String attrType; - - /** - * 单位 - */ - private String unit; - - /** - * 公式表达式(如:length*width) - */ - private String formula; - - /** - * 排序 - */ - private Integer sortOrder; - - /** - * 是否必填 - */ - private Integer required; - - @TableField(fill = FieldFill.INSERT) - private LocalDateTime createdAt; - - @TableField(fill = FieldFill.INSERT_UPDATE) - private LocalDateTime updatedAt; -} \ No newline at end of file diff --git a/src/main/java/com/example/building/entity/ProductAttribute.java b/src/main/java/com/example/building/entity/ProductAttribute.java deleted file mode 100644 index 1c31e82..0000000 --- a/src/main/java/com/example/building/entity/ProductAttribute.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.example.building.entity; - -import com.baomidou.mybatisplus.annotation.*; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * 商品属性值实体 - */ -@Data -@TableName("product_attributes") -public class ProductAttribute { - - @TableId(type = IdType.ASSIGN_UUID) - private String id; - - /** - * 商品ID - */ - private String productId; - - /** - * 属性定义ID - */ - private String attrId; - - /** - * 属性名称(冗余) - */ - private String attrName; - - /** - * 属性值 - */ - private String attrValue; - - @TableField(fill = FieldFill.INSERT) - private LocalDateTime createdAt; -} \ No newline at end of file diff --git a/src/main/java/com/example/building/mapper/CategoryAttributeMapper.java b/src/main/java/com/example/building/mapper/CategoryAttributeMapper.java deleted file mode 100644 index 04ff2fd..0000000 --- a/src/main/java/com/example/building/mapper/CategoryAttributeMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.example.building.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.example.building.entity.CategoryAttribute; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface CategoryAttributeMapper extends BaseMapper { -} \ No newline at end of file diff --git a/src/main/java/com/example/building/mapper/ProductAttributeMapper.java b/src/main/java/com/example/building/mapper/ProductAttributeMapper.java deleted file mode 100644 index bd3825f..0000000 --- a/src/main/java/com/example/building/mapper/ProductAttributeMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.example.building.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.example.building.entity.ProductAttribute; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface ProductAttributeMapper extends BaseMapper { -} \ No newline at end of file diff --git a/src/main/java/com/example/building/service/CategoryAttributeService.java b/src/main/java/com/example/building/service/CategoryAttributeService.java deleted file mode 100644 index e4b6765..0000000 --- a/src/main/java/com/example/building/service/CategoryAttributeService.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.example.building.service; - -import com.example.building.entity.CategoryAttribute; -import java.util.List; - -/** - * 种类属性服务接口 - */ -public interface CategoryAttributeService { - - /** - * 获取种类的所有属性 - */ - List getByCategoryId(String categoryId); - - /** - * 保存属性 - */ - void saveAttributes(String categoryId, List attrs); - - /** - * 删除种类的所有属性 - */ - void deleteByCategoryId(String categoryId); -} \ No newline at end of file diff --git a/src/main/java/com/example/building/service/ProductAttributeService.java b/src/main/java/com/example/building/service/ProductAttributeService.java deleted file mode 100644 index 9216919..0000000 --- a/src/main/java/com/example/building/service/ProductAttributeService.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.example.building.service; - -import com.example.building.entity.ProductAttribute; -import java.util.List; -import java.util.Map; - -/** - * 商品属性服务接口 - */ -public interface ProductAttributeService { - - /** - * 获取商品的属性值 - */ - List getByProductId(String productId); - - /** - * 保存商品属性值(自动计算公式属性) - * @param productId 商品ID - * @param categoryId 种类ID(用于获取公式定义) - * @param attrs 属性列表 - */ - void saveProductAttributes(String productId, String categoryId, List> attrs); - - /** - * 删除商品的所有属性 - */ - void deleteByProductId(String productId); -} \ No newline at end of file diff --git a/src/main/java/com/example/building/service/impl/CategoryAttributeServiceImpl.java b/src/main/java/com/example/building/service/impl/CategoryAttributeServiceImpl.java deleted file mode 100644 index 7abaa17..0000000 --- a/src/main/java/com/example/building/service/impl/CategoryAttributeServiceImpl.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.example.building.service.impl; - -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.example.building.entity.CategoryAttribute; -import com.example.building.mapper.CategoryAttributeMapper; -import com.example.building.service.CategoryAttributeService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -/** - * 种类属性服务实现 - */ -@Service -public class CategoryAttributeServiceImpl implements CategoryAttributeService { - - @Autowired - private CategoryAttributeMapper categoryAttributeMapper; - - @Override - public List getByCategoryId(String categoryId) { - return categoryAttributeMapper.selectList(new LambdaQueryWrapper() - .eq(CategoryAttribute::getCategoryId, categoryId) - .orderByAsc(CategoryAttribute::getSortOrder)); - } - - @Override - @Transactional - public void saveAttributes(String categoryId, List attrs) { - System.out.println("=== saveAttributes called ==="); - System.out.println("categoryId: " + categoryId); - System.out.println("attrs size: " + (attrs != null ? attrs.size() : 0)); - - // 删除旧属性 - categoryAttributeMapper.delete(new LambdaQueryWrapper() - .eq(CategoryAttribute::getCategoryId, categoryId)); - - // 保存新属性 - if (attrs != null && !attrs.isEmpty()) { - for (int i = 0; i < attrs.size(); i++) { - CategoryAttribute attr = attrs.get(i); - attr.setAttrId(UUID.randomUUID().toString()); - attr.setCategoryId(categoryId); - attr.setSortOrder(i); - - System.out.println(" attr: " + attr.getName() + ", type=" + attr.getAttrType() + ", unit=" + attr.getUnit()); - categoryAttributeMapper.insert(attr); - } - } - System.out.println("=== saveAttributes done ==="); - } - - @Override - @Transactional - public void deleteByCategoryId(String categoryId) { - categoryAttributeMapper.delete(new LambdaQueryWrapper() - .eq(CategoryAttribute::getCategoryId, categoryId)); - } -} \ No newline at end of file diff --git a/src/main/java/com/example/building/service/impl/ProductAttributeServiceImpl.java b/src/main/java/com/example/building/service/impl/ProductAttributeServiceImpl.java deleted file mode 100644 index 9b050d2..0000000 --- a/src/main/java/com/example/building/service/impl/ProductAttributeServiceImpl.java +++ /dev/null @@ -1,215 +0,0 @@ -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.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * 商品属性服务实现 - */ -@Service -public class ProductAttributeServiceImpl implements ProductAttributeService { - - @Autowired - private ProductAttributeMapper productAttributeMapper; - - @Autowired - private CategoryAttributeMapper categoryAttributeMapper; - - @Override - public List getByProductId(String productId) { - return productAttributeMapper.selectList(new LambdaQueryWrapper() - .eq(ProductAttribute::getProductId, productId)); - } - - @Override - @Transactional - 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)); - - // 保存新属性 - 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 - @Transactional - public void deleteByProductId(String productId) { - productAttributeMapper.delete(new LambdaQueryWrapper() - .eq(ProductAttribute::getProductId, productId)); - } -} \ No newline at end of file diff --git a/src/main/resources/db/migration/V7__add_category_attributes.sql b/src/main/resources/db/migration/V7__add_category_attributes.sql deleted file mode 100644 index 2b15473..0000000 --- a/src/main/resources/db/migration/V7__add_category_attributes.sql +++ /dev/null @@ -1,29 +0,0 @@ --- 种类属性定义表 -CREATE TABLE IF NOT EXISTS category_attributes ( - attr_id VARCHAR(36) PRIMARY KEY, - category_id VARCHAR(36) NOT NULL, - name VARCHAR(50) NOT NULL, - attr_type VARCHAR(20) DEFAULT 'number', - unit VARCHAR(20), - formula VARCHAR(200), - sort_order INTEGER DEFAULT 0, - required INTEGER DEFAULT 0, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (category_id) REFERENCES categories(category_id) ON DELETE CASCADE -); - --- 商品属性值表 -CREATE TABLE IF NOT EXISTS product_attributes ( - id VARCHAR(36) PRIMARY KEY, - product_id VARCHAR(36) NOT NULL, - attr_id VARCHAR(36) NOT NULL, - attr_name VARCHAR(50), - attr_value VARCHAR(200), - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE, - FOREIGN KEY (attr_id) REFERENCES category_attributes(attr_id) ON DELETE CASCADE -); - --- 商品表增加默认单位字段 -ALTER TABLE products ADD COLUMN default_unit VARCHAR(20); \ No newline at end of file diff --git a/src/main/resources/db/migration/V8__rollback_category_attributes.sql b/src/main/resources/db/migration/V8__rollback_category_attributes.sql new file mode 100644 index 0000000..812d7f2 --- /dev/null +++ b/src/main/resources/db/migration/V8__rollback_category_attributes.sql @@ -0,0 +1,9 @@ +-- 删除种类属性相关表和字段(回滚 V7) +-- 删除商品属性值表 +DROP TABLE IF EXISTS product_attributes; + +-- 删除种类属性定义表 +DROP TABLE IF EXISTS category_attributes; + +-- 删除商品表的 default_unit 字段 +ALTER TABLE products DROP COLUMN IF EXISTS default_unit; \ No newline at end of file