From d1506d24e56967a6d12454a0caec95e3e3866d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E6=98=8A=E5=A4=A9?= <15984976+n2319_0@user.noreply.gitee.com> Date: Thu, 18 Jun 2026 18:19:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E5=85=B3=E8=81=94=E4=BA=A7?= =?UTF-8?q?=E5=93=81=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GearCustomerProductController.java | 75 ++++++++++ .../gear/oa/domain/GearCustomerProduct.java | 46 +++++++ .../oa/domain/bo/GearCustomerProductBo.java | 40 ++++++ .../oa/domain/vo/GearCustomerProductVo.java | 47 +++++++ .../oa/mapper/GearCustomerProductMapper.java | 24 ++++ .../service/IGearCustomerProductService.java | 33 +++++ .../impl/GearCustomerProductServiceImpl.java | 81 +++++++++++ .../mapper/oa/GearCustomerProductMapper.xml | 59 ++++++++ gear-ui3/src/api/oa/product.js | 25 ++++ gear-ui3/src/api/oms/customer.js | 25 ++++ gear-ui3/src/views/oms/customer/index.vue | 121 +++++++++++++++- gear-ui3/src/views/product/info/index.vue | 129 +++++++++++++++++- .../2026-06-16_add_gear_customer_product.sql | 15 ++ 13 files changed, 717 insertions(+), 3 deletions(-) create mode 100644 gear-oa/src/main/java/com/gear/oa/controller/GearCustomerProductController.java create mode 100644 gear-oa/src/main/java/com/gear/oa/domain/GearCustomerProduct.java create mode 100644 gear-oa/src/main/java/com/gear/oa/domain/bo/GearCustomerProductBo.java create mode 100644 gear-oa/src/main/java/com/gear/oa/domain/vo/GearCustomerProductVo.java create mode 100644 gear-oa/src/main/java/com/gear/oa/mapper/GearCustomerProductMapper.java create mode 100644 gear-oa/src/main/java/com/gear/oa/service/IGearCustomerProductService.java create mode 100644 gear-oa/src/main/java/com/gear/oa/service/impl/GearCustomerProductServiceImpl.java create mode 100644 gear-oa/src/main/resources/mapper/oa/GearCustomerProductMapper.xml create mode 100644 script/sql/mysql/update/2026-06-16_add_gear_customer_product.sql diff --git a/gear-oa/src/main/java/com/gear/oa/controller/GearCustomerProductController.java b/gear-oa/src/main/java/com/gear/oa/controller/GearCustomerProductController.java new file mode 100644 index 0000000..cabc0cf --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/controller/GearCustomerProductController.java @@ -0,0 +1,75 @@ +package com.gear.oa.controller; + +import com.gear.common.annotation.Log; +import com.gear.common.annotation.RepeatSubmit; +import com.gear.common.core.controller.BaseController; +import com.gear.common.core.domain.R; +import com.gear.common.core.validate.AddGroup; +import com.gear.common.enums.BusinessType; +import com.gear.oa.domain.bo.GearCustomerProductBo; +import com.gear.oa.domain.vo.GearCustomerProductVo; +import com.gear.oa.service.IGearCustomerProductService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; + +/** + * 客户-产品关联 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/oa/customerProduct") +public class GearCustomerProductController extends BaseController { + + private final IGearCustomerProductService iGearCustomerProductService; + + /** + * 查询客户关联产品列表 + */ + @GetMapping("/customer/{customerId}") + public R> listByCustomer(@NotNull(message = "客户ID不能为空") + @PathVariable Long customerId) { + return R.ok(iGearCustomerProductService.queryByCustomerId(customerId)); + } + + /** + * 查询产品关联客户列表 + */ + @GetMapping("/product/{productId}") + public R> listByProduct(@NotNull(message = "产品ID不能为空") + @PathVariable Long productId) { + return R.ok(iGearCustomerProductService.queryByProductId(productId)); + } + + /** + * 新增客户关联产品 + */ + @Log(title = "客户关联产品", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping + public R add(@Validated(AddGroup.class) @RequestBody GearCustomerProductBo bo) { + return toAjax(iGearCustomerProductService.insertByBo(bo)); + } + + /** + * 删除客户关联产品 + */ + @Log(title = "客户关联产品", businessType = BusinessType.DELETE) + @DeleteMapping("/{relationIds}") + public R remove(@NotEmpty(message = "关联ID不能为空") + @PathVariable Long[] relationIds) { + return toAjax(iGearCustomerProductService.deleteWithValidByIds(Arrays.asList(relationIds), true)); + } +} diff --git a/gear-oa/src/main/java/com/gear/oa/domain/GearCustomerProduct.java b/gear-oa/src/main/java/com/gear/oa/domain/GearCustomerProduct.java new file mode 100644 index 0000000..276239d --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/domain/GearCustomerProduct.java @@ -0,0 +1,46 @@ +package com.gear.oa.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.gear.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 客户-产品关联对象 gear_customer_product + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("gear_customer_product") +public class GearCustomerProduct extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 关联ID + */ + @TableId(value = "relation_id") + private Long relationId; + + /** + * 客户ID + */ + private Long customerId; + + /** + * 产品ID + */ + private Long productId; + + /** + * 备注 + */ + private String remark; + + /** + * 删除标志(0=正常,1=已删除) + */ + @TableLogic + private Integer delFlag; +} diff --git a/gear-oa/src/main/java/com/gear/oa/domain/bo/GearCustomerProductBo.java b/gear-oa/src/main/java/com/gear/oa/domain/bo/GearCustomerProductBo.java new file mode 100644 index 0000000..576fdac --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/domain/bo/GearCustomerProductBo.java @@ -0,0 +1,40 @@ +package com.gear.oa.domain.bo; + +import com.gear.common.core.domain.BaseEntity; +import com.gear.common.core.validate.AddGroup; +import com.gear.common.core.validate.EditGroup; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotNull; + +/** + * 客户-产品关联业务对象 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class GearCustomerProductBo extends BaseEntity { + + /** + * 关联ID + */ + @NotNull(message = "关联ID不能为空", groups = EditGroup.class) + private Long relationId; + + /** + * 客户ID + */ + @NotNull(message = "客户ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long customerId; + + /** + * 产品ID + */ + @NotNull(message = "产品ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long productId; + + /** + * 备注 + */ + private String remark; +} diff --git a/gear-oa/src/main/java/com/gear/oa/domain/vo/GearCustomerProductVo.java b/gear-oa/src/main/java/com/gear/oa/domain/vo/GearCustomerProductVo.java new file mode 100644 index 0000000..b4debf6 --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/domain/vo/GearCustomerProductVo.java @@ -0,0 +1,47 @@ +package com.gear.oa.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 客户-产品关联视图对象 + */ +@Data +@ExcelIgnoreUnannotated +public class GearCustomerProductVo { + + private static final long serialVersionUID = 1L; + + @ExcelProperty(value = "关联ID") + private Long relationId; + + @ExcelProperty(value = "客户ID") + private Long customerId; + + @ExcelProperty(value = "客户名称") + private String customerName; + + @ExcelProperty(value = "产品ID") + private Long productId; + + @ExcelProperty(value = "产品编号") + private String productCode; + + @ExcelProperty(value = "产品名称") + private String productName; + + @ExcelProperty(value = "负责人") + private String owner; + + @ExcelProperty(value = "单位") + private String unit; + + @ExcelProperty(value = "备注") + private String remark; + + @ExcelProperty(value = "创建时间") + private Date createTime; +} diff --git a/gear-oa/src/main/java/com/gear/oa/mapper/GearCustomerProductMapper.java b/gear-oa/src/main/java/com/gear/oa/mapper/GearCustomerProductMapper.java new file mode 100644 index 0000000..271d59e --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/mapper/GearCustomerProductMapper.java @@ -0,0 +1,24 @@ +package com.gear.oa.mapper; + +import com.gear.common.core.mapper.BaseMapperPlus; +import com.gear.oa.domain.GearCustomerProduct; +import com.gear.oa.domain.vo.GearCustomerProductVo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 客户-产品关联Mapper接口 + */ +public interface GearCustomerProductMapper extends BaseMapperPlus { + + /** + * 按客户查询关联产品 + */ + List selectByCustomerId(@Param("customerId") Long customerId); + + /** + * 按产品查询关联客户 + */ + List selectByProductId(@Param("productId") Long productId); +} diff --git a/gear-oa/src/main/java/com/gear/oa/service/IGearCustomerProductService.java b/gear-oa/src/main/java/com/gear/oa/service/IGearCustomerProductService.java new file mode 100644 index 0000000..a3135ae --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/service/IGearCustomerProductService.java @@ -0,0 +1,33 @@ +package com.gear.oa.service; + +import com.gear.oa.domain.bo.GearCustomerProductBo; +import com.gear.oa.domain.vo.GearCustomerProductVo; + +import java.util.Collection; +import java.util.List; + +/** + * 客户-产品关联Service接口 + */ +public interface IGearCustomerProductService { + + /** + * 按客户查询关联产品 + */ + List queryByCustomerId(Long customerId); + + /** + * 按产品查询关联客户 + */ + List queryByProductId(Long productId); + + /** + * 新增客户-产品关联 + */ + Boolean insertByBo(GearCustomerProductBo bo); + + /** + * 批量删除客户-产品关联 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/gear-oa/src/main/java/com/gear/oa/service/impl/GearCustomerProductServiceImpl.java b/gear-oa/src/main/java/com/gear/oa/service/impl/GearCustomerProductServiceImpl.java new file mode 100644 index 0000000..3185aa6 --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/service/impl/GearCustomerProductServiceImpl.java @@ -0,0 +1,81 @@ +package com.gear.oa.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.gear.common.exception.ServiceException; +import com.gear.oa.domain.GearCustomerProduct; +import com.gear.oa.domain.bo.GearCustomerProductBo; +import com.gear.oa.domain.vo.GearCustomerProductVo; +import com.gear.oa.mapper.GearCustomerMapper; +import com.gear.oa.mapper.GearCustomerProductMapper; +import com.gear.oa.mapper.GearProductMapper; +import com.gear.oa.service.IGearCustomerProductService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +/** + * 客户-产品关联Service业务层处理 + */ +@RequiredArgsConstructor +@Service +public class GearCustomerProductServiceImpl implements IGearCustomerProductService { + + private final GearCustomerProductMapper baseMapper; + private final GearCustomerMapper customerMapper; + private final GearProductMapper productMapper; + + @Override + public List queryByCustomerId(Long customerId) { + return baseMapper.selectByCustomerId(customerId); + } + + @Override + public List queryByProductId(Long productId) { + return baseMapper.selectByProductId(productId); + } + + @Override + public Boolean insertByBo(GearCustomerProductBo bo) { + validEntityBeforeSave(bo); + GearCustomerProduct add = BeanUtil.toBean(bo, GearCustomerProduct.class); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setRelationId(add.getRelationId()); + } + return flag; + } + + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid && (ids == null || ids.isEmpty())) { + throw new ServiceException("关联ID不能为空"); + } + return baseMapper.deleteBatchIds(ids) > 0; + } + + private void validEntityBeforeSave(GearCustomerProductBo bo) { + if (bo.getCustomerId() == null) { + throw new ServiceException("客户不能为空"); + } + if (bo.getProductId() == null) { + throw new ServiceException("产品不能为空"); + } + if (customerMapper.selectById(bo.getCustomerId()) == null) { + throw new ServiceException("客户不存在"); + } + if (productMapper.selectById(bo.getProductId()) == null) { + throw new ServiceException("产品不存在"); + } + Long count = baseMapper.selectCount( + Wrappers.lambdaQuery() + .eq(GearCustomerProduct::getCustomerId, bo.getCustomerId()) + .eq(GearCustomerProduct::getProductId, bo.getProductId()) + ); + if (count != null && count > 0) { + throw new ServiceException("该客户已关联此产品,请勿重复添加"); + } + } +} diff --git a/gear-oa/src/main/resources/mapper/oa/GearCustomerProductMapper.xml b/gear-oa/src/main/resources/mapper/oa/GearCustomerProductMapper.xml new file mode 100644 index 0000000..a7620a3 --- /dev/null +++ b/gear-oa/src/main/resources/mapper/oa/GearCustomerProductMapper.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/gear-ui3/src/api/oa/product.js b/gear-ui3/src/api/oa/product.js index e39ec06..45988eb 100644 --- a/gear-ui3/src/api/oa/product.js +++ b/gear-ui3/src/api/oa/product.js @@ -42,3 +42,28 @@ export function delProduct(productId) { method: 'delete' }) } + +// 查询产品关联客户 +export function listProductCustomers(productId) { + return request({ + url: '/oa/customerProduct/product/' + productId, + method: 'get' + }) +} + +// 新增产品关联客户 +export function addProductCustomer(data) { + return request({ + url: '/oa/customerProduct', + method: 'post', + data: data + }) +} + +// 删除产品关联客户 +export function delProductCustomer(relationId) { + return request({ + url: '/oa/customerProduct/' + relationId, + method: 'delete' + }) +} diff --git a/gear-ui3/src/api/oms/customer.js b/gear-ui3/src/api/oms/customer.js index 7704bb7..4d3aebf 100644 --- a/gear-ui3/src/api/oms/customer.js +++ b/gear-ui3/src/api/oms/customer.js @@ -42,3 +42,28 @@ export function delCustomer(customerId) { method: 'delete' }) } + +// 查询客户关联产品 +export function listCustomerProducts(customerId) { + return request({ + url: '/oa/customerProduct/customer/' + customerId, + method: 'get' + }) +} + +// 新增客户关联产品 +export function addCustomerProduct(data) { + return request({ + url: '/oa/customerProduct', + method: 'post', + data: data + }) +} + +// 删除客户关联产品 +export function delCustomerProduct(relationId) { + return request({ + url: '/oa/customerProduct/' + relationId, + method: 'delete' + }) +} diff --git a/gear-ui3/src/views/oms/customer/index.vue b/gear-ui3/src/views/oms/customer/index.vue index 57dc7f2..9cb881a 100644 --- a/gear-ui3/src/views/oms/customer/index.vue +++ b/gear-ui3/src/views/oms/customer/index.vue @@ -94,6 +94,7 @@ + @@ -214,6 +215,50 @@ +
+
+
+
关联产品
+
+ 新增关联 + 刷新 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
@@ -383,7 +428,7 @@ + + diff --git a/script/sql/mysql/update/2026-06-16_add_gear_customer_product.sql b/script/sql/mysql/update/2026-06-16_add_gear_customer_product.sql new file mode 100644 index 0000000..60253e5 --- /dev/null +++ b/script/sql/mysql/update/2026-06-16_add_gear_customer_product.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS `gear_customer_product` ( + `relation_id` bigint NOT NULL COMMENT '关联ID', + `customer_id` bigint NOT NULL COMMENT '客户ID', + `product_id` bigint NOT NULL COMMENT '产品ID', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + `create_by` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `del_flag` tinyint DEFAULT 0 COMMENT '删除标志(0=正常,1=已删除)', + PRIMARY KEY (`relation_id`), + KEY `idx_customer_product_customer` (`customer_id`), + KEY `idx_customer_product_product` (`product_id`), + KEY `idx_customer_product_del_flag` (`del_flag`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='客户-产品关联表';