diff --git a/cash-api/product-server/src/main/java/com/czg/controller/admin/ConsInfoController.java b/cash-api/product-server/src/main/java/com/czg/controller/admin/ConsInfoController.java index 870b2ac1..c6743c62 100644 --- a/cash-api/product-server/src/main/java/com/czg/controller/admin/ConsInfoController.java +++ b/cash-api/product-server/src/main/java/com/czg/controller/admin/ConsInfoController.java @@ -1,10 +1,13 @@ package com.czg.controller; +import com.czg.enums.CrudEnum; import com.czg.log.annotation.OperationLog; import com.czg.product.dto.ConsInfoDTO; +import com.czg.product.param.ConsSubUnitParam; import com.czg.product.service.ConsInfoService; import com.czg.resp.CzgResult; import com.czg.utils.AssertUtil; +import com.czg.validator.ValidatorUtil; import com.czg.validator.group.DefaultGroup; import com.czg.validator.group.InsertGroup; import com.czg.validator.group.UpdateGroup; @@ -146,4 +149,19 @@ public class ConsInfoController { consInfoService.onOffConsInfo(id, isStock); return CzgResult.success(); } + + /** + * 编辑副单位 + */ + @PostMapping("modifySubUnit") + @OperationLog("耗材信息-编辑副单位") + //@SaAdminCheckPermission("consInfo:update") + public CzgResult modifySubUnit(@RequestBody ConsSubUnitParam param) { + ValidatorUtil.validateEntity(param, DefaultGroup.class); + if (CrudEnum.ADD.value().equals(param.getModifyFlag())) { + ValidatorUtil.validateEntity(param, InsertGroup.class); + } + consInfoService.modifySubUnit(param); + return CzgResult.success(); + } } \ No newline at end of file diff --git a/cash-api/product-server/src/main/java/com/czg/controller/admin/ProductController.java b/cash-api/product-server/src/main/java/com/czg/controller/admin/ProductController.java index 2fbeeb45..d1d379c1 100644 --- a/cash-api/product-server/src/main/java/com/czg/controller/admin/ProductController.java +++ b/cash-api/product-server/src/main/java/com/czg/controller/admin/ProductController.java @@ -1,10 +1,12 @@ package com.czg.controller.admin; import com.czg.log.annotation.OperationLog; +import com.czg.product.dto.ProdConsBindDTO; import com.czg.product.dto.ProdSkuDTO; import com.czg.product.dto.ProductDTO; import com.czg.product.param.ProductIsSaleParam; import com.czg.product.param.ProductIsSoldOutParam; +import com.czg.product.service.ProdConsRelationService; import com.czg.product.service.ProductService; import com.czg.resp.CzgResult; import com.czg.utils.AssertUtil; @@ -31,6 +33,7 @@ import java.util.List; @RequestMapping("/admin/product") public class ProductController { private final ProductService productService; + private final ProdConsRelationService prodConsRelationService; @GetMapping("page") @OperationLog("商品-分页") @@ -90,7 +93,7 @@ public class ProductController { /** * 商品-上下架 */ - @PostMapping("/on-off") + @PostMapping("on-off") @OperationLog("商品-上下架") //@SaAdminCheckPermission("product:on-off") public CzgResult onOffProduct(@RequestBody @Validated({DefaultGroup.class}) ProductIsSaleParam param) { @@ -101,7 +104,7 @@ public class ProductController { /** * 商品-标记售罄 */ - @PostMapping("/markIsSoldOut") + @PostMapping("markIsSoldOut") @OperationLog("商品-标记售罄") //@SaAdminCheckPermission("product:markIsSoldOut") public CzgResult markIsSoldOutProduct(@RequestBody @Validated({DefaultGroup.class}) ProductIsSoldOutParam param) { @@ -109,4 +112,15 @@ public class ProductController { return CzgResult.success(); } + /** + * 商品-绑定耗材 + */ + @PostMapping("bind") + @OperationLog("商品-绑定耗材") + //@SaAdminCheckPermission("product:bind") + public CzgResult bindCons(@RequestBody @Validated({DefaultGroup.class}) ProdConsBindDTO param) { + prodConsRelationService.saveProdConsRelation(param); + return CzgResult.success(); + } + } \ No newline at end of file diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/dto/ProdConsBindDTO.java b/cash-common/cash-common-service/src/main/java/com/czg/product/dto/ProdConsBindDTO.java new file mode 100644 index 00000000..add07418 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/dto/ProdConsBindDTO.java @@ -0,0 +1,33 @@ +package com.czg.product.dto; + +import com.czg.validator.group.DefaultGroup; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 商品耗材绑定 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 1.0 2025-02-20 + */ +@Data +public class ProdConsBindDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 商品id + */ + @NotNull(message = "商品id不能为空", groups = DefaultGroup.class) + private Long id; + /** + * 耗材列表 + */ + private List consList; + +} \ No newline at end of file diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/dto/ProdConsRelationDTO.java b/cash-common/cash-common-service/src/main/java/com/czg/product/dto/ProdConsRelationDTO.java new file mode 100644 index 00000000..6c48e21f --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/dto/ProdConsRelationDTO.java @@ -0,0 +1,54 @@ +package com.czg.product.dto; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.czg.validator.group.DefaultGroup; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 商品耗材绑定关系 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 1.0 2025-02-20 + */ +@Data +public class ProdConsRelationDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + private Long id; + /** + * 店铺id + */ + private Long shopId; + /** + * 商品id + */ + @NotNull(message = "商品id不能为空", groups = DefaultGroup.class) + private Long productId; + /** + * 耗材id + */ + @NotNull(message = "耗材id不能为空", groups = DefaultGroup.class) + private Long consInfoId; + /** + * 单位消耗值 + */ + @NotNull(message = "使用数量不能为空", groups = DefaultGroup.class) + private BigDecimal surplusStock; + /** + * 创建时间 + */ + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/entity/ProdConsRelation.java b/cash-common/cash-common-service/src/main/java/com/czg/product/entity/ProdConsRelation.java new file mode 100644 index 00000000..c6e8a7bd --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/entity/ProdConsRelation.java @@ -0,0 +1,53 @@ +package com.czg.product.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.annotation.Table; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 商品耗材绑定关系 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 1.0 2025-02-20 + */ +@Data +@Table("tb_prod_cons_relation") +public class ProdConsRelation implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id(keyType = KeyType.Auto) + private Long id; + /** + * 店铺id + */ + private Long shopId; + /** + * 商品id + */ + private Long productId; + /** + * 耗材id + */ + private Long consInfoId; + /** + * 单位消耗值 + */ + private BigDecimal surplusStock; + /** + * 创建时间 + */ + @Column(onInsertValue = "now()") + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/param/ConsSubUnitParam.java b/cash-common/cash-common-service/src/main/java/com/czg/product/param/ConsSubUnitParam.java new file mode 100644 index 00000000..cf7ca1e5 --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/param/ConsSubUnitParam.java @@ -0,0 +1,49 @@ +package com.czg.product.param; + +import com.czg.validator.group.DefaultGroup; +import com.czg.validator.group.InsertGroup; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 耗材副单位编辑入参 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 1.0 2025-02-20 + */ +@Data +public class ConsSubUnitParam implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + /** + * id + */ + @NotNull(message = "ID不能为空", groups = DefaultGroup.class) + private Long id; + /** + * 耗材分组id + */ + @NotNull(message = "副单位不能为空", groups = InsertGroup.class) + private String conUnitTwo; + /** + * 转换数量 (等同于多少主单位) + */ + @NotNull(message = "转换数量不能为空", groups = InsertGroup.class) + private BigDecimal conUnitTwoConvert; + /** + * 默认入库单位 + */ + @NotNull(message = "默认入库单位不能为空", groups = InsertGroup.class) + private String defaultUnit; + /** + * 修改标志 add-新增 delete-删除 + */ + @NotNull(message = "修改标志不能为空", groups = DefaultGroup.class) + private String modifyFlag; + +} \ No newline at end of file diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/service/ConsInfoService.java b/cash-common/cash-common-service/src/main/java/com/czg/product/service/ConsInfoService.java index ccbf0547..68c67ae3 100644 --- a/cash-common/cash-common-service/src/main/java/com/czg/product/service/ConsInfoService.java +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/service/ConsInfoService.java @@ -2,6 +2,7 @@ package com.czg.product.service; import com.czg.product.dto.ConsInfoDTO; import com.czg.product.entity.ConsInfo; +import com.czg.product.param.ConsSubUnitParam; import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.service.IService; @@ -32,4 +33,6 @@ public interface ConsInfoService extends IService { boolean onOffConsInfo(Long id, Integer isStock); + void modifySubUnit(ConsSubUnitParam param); + } \ No newline at end of file diff --git a/cash-common/cash-common-service/src/main/java/com/czg/product/service/ProdConsRelationService.java b/cash-common/cash-common-service/src/main/java/com/czg/product/service/ProdConsRelationService.java new file mode 100644 index 00000000..4cd3a5cb --- /dev/null +++ b/cash-common/cash-common-service/src/main/java/com/czg/product/service/ProdConsRelationService.java @@ -0,0 +1,22 @@ +package com.czg.product.service; + +import com.czg.product.dto.ProdConsBindDTO; +import com.czg.product.dto.ProdConsRelationDTO; +import com.czg.product.entity.ProdConsRelation; +import com.mybatisflex.core.service.IService; + +import java.util.List; + +/** + * 商品耗材绑定关系 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 1.0 2025-02-20 + */ +public interface ProdConsRelationService extends IService { + + List getProdConsRelationList(ProdConsRelationDTO param); + + boolean saveProdConsRelation(ProdConsBindDTO dto); + +} \ No newline at end of file diff --git a/cash-common/cash-common-tools/src/main/java/com/czg/enums/CrudEnum.java b/cash-common/cash-common-tools/src/main/java/com/czg/enums/CrudEnum.java new file mode 100644 index 00000000..e884b728 --- /dev/null +++ b/cash-common/cash-common-tools/src/main/java/com/czg/enums/CrudEnum.java @@ -0,0 +1,36 @@ +package com.czg.enums; + +/** + * 数据操作枚举 + * + * @author tankaikai + * @since 2025-02-20 16:59 + */ +public enum CrudEnum { + /** + * 新增 + */ + ADD("add"), + /** + * 修改 + */ + UPDATE("update"), + /** + * 查询 + */ + QUERY("query"), + /** + * 查询 + */ + DELETE("delete"); + + private String value; + + CrudEnum(String value) { + this.value = value; + } + + public String value() { + return this.value; + } +} diff --git a/cash-common/cash-common-tools/src/main/java/com/czg/validator/ValidatorUtil.java b/cash-common/cash-common-tools/src/main/java/com/czg/validator/ValidatorUtil.java index 790ca981..3d16bd80 100644 --- a/cash-common/cash-common-tools/src/main/java/com/czg/validator/ValidatorUtil.java +++ b/cash-common/cash-common-tools/src/main/java/com/czg/validator/ValidatorUtil.java @@ -9,6 +9,7 @@ import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.validation.beanvalidation.MessageSourceResourceBundleLocator; +import java.util.List; import java.util.Locale; import java.util.Set; @@ -30,9 +31,10 @@ public class ValidatorUtil { /** * 校验对象 - * @param object 待校验对象 - * @param groups 待校验的组 - * @throws CzgException 校验不通过,则报CzgException异常 + * + * @param object 待校验对象 + * @param groups 待校验的组 + * @throws CzgException 校验不通过,则报CzgException异常 */ public static void validateEntity(Object object, Class... groups) throws CzgException { @@ -46,4 +48,26 @@ public class ValidatorUtil { throw new CzgException(constraint.getMessage()); } } + + /** + * 校验对象集合 + * + * @param objects 待校验对象集合 + * @param groups 待校验的组 + * @throws CzgException 校验不通过,则报CzgException异常 + */ + public static void validateEntityList(List objects, Class... groups) + throws CzgException { + Locale.setDefault(LocaleContextHolder.getLocale()); + Validator validator = Validation.byDefaultProvider().configure().messageInterpolator( + new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(getMessageSource()))) + .buildValidatorFactory().getValidator(); + for (Object object : objects) { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) { + ConstraintViolation constraint = constraintViolations.iterator().next(); + throw new CzgException(constraint.getMessage()); + } + } + } } \ No newline at end of file diff --git a/cash-service/product-service/src/main/java/com/czg/service/product/mapper/ProdConsRelationMapper.java b/cash-service/product-service/src/main/java/com/czg/service/product/mapper/ProdConsRelationMapper.java new file mode 100644 index 00000000..1e2ec700 --- /dev/null +++ b/cash-service/product-service/src/main/java/com/czg/service/product/mapper/ProdConsRelationMapper.java @@ -0,0 +1,16 @@ +package com.czg.service.product.mapper; + +import com.czg.product.entity.ProdConsRelation; +import com.mybatisflex.core.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 商品耗材绑定关系 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 1.0 2025-02-20 + */ +@Mapper +public interface ProdConsRelationMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ConsInfoServiceImpl.java b/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ConsInfoServiceImpl.java index bfed24b0..2dd92c51 100644 --- a/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ConsInfoServiceImpl.java +++ b/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ConsInfoServiceImpl.java @@ -3,12 +3,14 @@ package com.czg.service.product.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; +import com.czg.enums.CrudEnum; import com.czg.enums.StatusEnum; import com.czg.enums.YesNoEnum; import com.czg.exception.CzgException; import com.czg.product.dto.ConsInfoDTO; import com.czg.product.entity.ConsGroupRelation; import com.czg.product.entity.ConsInfo; +import com.czg.product.param.ConsSubUnitParam; import com.czg.product.service.ConsInfoService; import com.czg.sa.StpKit; import com.czg.service.product.mapper.ConsGroupRelationMapper; @@ -153,4 +155,26 @@ public class ConsInfoServiceImpl extends ServiceImpl i return false; } + @Override + public void modifySubUnit(ConsSubUnitParam param) { + Long shopId = StpKit.USER.getShopId(0L); + if (CrudEnum.ADD.value().equals(param.getModifyFlag())) { + UpdateChain.of(ConsInfo.class) + .set(ConsInfo::getConUnitTwo, param.getConUnitTwo()) + .set(ConsInfo::getConUnitTwoConvert, param.getConUnitTwoConvert()) + .set(ConsInfo::getDefaultUnit, param.getDefaultUnit()) + .eq(ConsInfo::getId, param.getId()) + .eq(ConsInfo::getShopId, shopId) + .update(); + } else if (CrudEnum.DELETE.value().equals(param.getModifyFlag())) { + UpdateChain.of(ConsInfo.class) + .set(ConsInfo::getConUnitTwo, null) + .set(ConsInfo::getConUnitTwoConvert, null) + .set(ConsInfo::getDefaultUnit, null) + .eq(ConsInfo::getId, param.getId()) + .eq(ConsInfo::getShopId, shopId) + .update(); + } + } + } \ No newline at end of file diff --git a/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ProdConsRelationServiceImpl.java b/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ProdConsRelationServiceImpl.java new file mode 100644 index 00000000..e58b0352 --- /dev/null +++ b/cash-service/product-service/src/main/java/com/czg/service/product/service/impl/ProdConsRelationServiceImpl.java @@ -0,0 +1,79 @@ +package com.czg.service.product.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import com.czg.exception.CzgException; +import com.czg.product.dto.ProdConsBindDTO; +import com.czg.product.dto.ProdConsRelationDTO; +import com.czg.product.entity.ProdConsRelation; +import com.czg.product.entity.Product; +import com.czg.product.service.ProdConsRelationService; +import com.czg.sa.StpKit; +import com.czg.service.product.mapper.ProdConsRelationMapper; +import com.czg.service.product.mapper.ProductMapper; +import com.czg.utils.PageUtil; +import com.czg.validator.ValidatorUtil; +import com.czg.validator.group.DefaultGroup; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 商品耗材绑定关系 + * + * @author Tankaikai tankaikai@aliyun.com + * @since 1.0 2025-02-20 + */ +@Service +@AllArgsConstructor +public class ProdConsRelationServiceImpl extends ServiceImpl implements ProdConsRelationService { + + private final ProductMapper productMapper; + + private QueryWrapper buildQueryWrapper(ProdConsRelationDTO param) { + QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper(); + Long shopId = StpKit.USER.getLoginIdAsLong(); + queryWrapper.eq(ProdConsRelation::getShopId, shopId); + queryWrapper.orderBy(ProdConsRelation::getId, false); + return queryWrapper; + } + + @Override + public List getProdConsRelationList(ProdConsRelationDTO param) { + QueryWrapper queryWrapper = buildQueryWrapper(param); + return super.listAs(queryWrapper, ProdConsRelationDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean saveProdConsRelation(ProdConsBindDTO dto) { + Long shopId = StpKit.USER.getLoginIdAsLong(); + long count = productMapper.selectCountByQuery(query().eq(Product::getShopId, shopId).eq(Product::getId, dto.getId())); + if (count == 0) { + throw new CzgException("商品不存在"); + } + super.remove(query().eq(ProdConsRelation::getShopId, shopId).eq(ProdConsRelation::getProductId, dto.getId())); + if (CollUtil.isEmpty(dto.getConsList())) { + return true; + } + ValidatorUtil.validateEntityList(dto.getConsList(), DefaultGroup.class); + List consList = dto.getConsList(); + for (ProdConsRelationDTO con : consList) { + con.setShopId(shopId); + } + List list = consList.stream().map(ProdConsRelationDTO::getProductId).distinct().toList(); + if (CollUtil.isNotEmpty(list) && list.size() > 1) { + throw new CzgException("只能绑定同一商品下的耗材"); + } + if (!dto.getId().equals(list.getFirst())) { + throw new CzgException("非法操作,绑定关系商品不一致"); + } + List entityList = BeanUtil.copyToList(consList, ProdConsRelation.class); + return super.saveBatch(entityList); + } + +} \ No newline at end of file diff --git a/cash-service/product-service/src/main/resources/mapper/ProdConsRelationMapper.xml b/cash-service/product-service/src/main/resources/mapper/ProdConsRelationMapper.xml new file mode 100644 index 00000000..cde3b96a --- /dev/null +++ b/cash-service/product-service/src/main/resources/mapper/ProdConsRelationMapper.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file