diff --git a/sql/mysql/optinal/crm_20240114.sql b/sql/mysql/optinal/crm_20240114.sql new file mode 100644 index 000000000..24f25d4ef --- /dev/null +++ b/sql/mysql/optinal/crm_20240114.sql @@ -0,0 +1,55 @@ + +-- ---------------------------- +-- Table structure for crm_business_product +-- ---------------------------- +DROP TABLE IF EXISTS `crm_business_product`; +CREATE TABLE `crm_business_product` ( + `id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键', + `business_id` bigint(0) NOT NULL COMMENT '商机ID', + `product_id` bigint(0) NOT NULL COMMENT '产品ID', + `price` decimal(18, 2) NOT NULL COMMENT '产品单价', + `sales_price` decimal(18, 2) NULL DEFAULT NULL COMMENT '销售价格', + `num` int(0) NULL DEFAULT NULL COMMENT '数量', + `discount` decimal(10, 2) NULL DEFAULT NULL COMMENT '折扣', + `subtotal` decimal(18, 2) NULL DEFAULT NULL COMMENT '小计(折扣后价格)', + `unit` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '单位', + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `tenant_id` bigint(0) NOT NULL DEFAULT 1 COMMENT '租户编号', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 29 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商机产品关联表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for crm_business_status +-- ---------------------------- +DROP TABLE IF EXISTS `crm_business_status`; +CREATE TABLE `crm_business_status` ( + `id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `type_id` bigint(0) NOT NULL COMMENT '状态类型编号', + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '状态名', + `percent` bigint(0) NULL DEFAULT NULL COMMENT '赢单率', + `sort` int(0) NULL DEFAULT NULL COMMENT '排序', + `tenant_id` bigint(0) NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商机状态' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for crm_business_status_type +-- ---------------------------- +DROP TABLE IF EXISTS `crm_business_status_type`; +CREATE TABLE `crm_business_status_type` ( + `id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '状态类型名', + `dept_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '使用的部门编号', + `status` int(0) NOT NULL DEFAULT 1 COMMENT '开启状态', + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', + `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `tenant_id` bigint(0) NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商机状态类型' ROW_FORMAT = Dynamic; diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java index 6200f3845..7df94068e 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java @@ -19,6 +19,7 @@ public interface ErrorCodeConstants { // ========== 商机管理 1-020-002-000 ========== ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_002_000, "商机不存在"); + ErrorCode BUSINESS_CONTRACT_EXISTS = new ErrorCode(1_020_002_001, "商机已关联合同,不能删除"); // TODO @lilleo:商机状态、商机类型,都单独错误码段 diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/business/CrmBizEndStatus.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/business/CrmBizEndStatus.java new file mode 100644 index 000000000..f84610e8d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/business/CrmBizEndStatus.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.crm.enums.business; + +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * @author lzxhqs + * @version 1.0 + * @title CrmBizEndStatus + * @description + * @create 2024/1/12 + */ +@RequiredArgsConstructor +@Getter +public enum CrmBizEndStatus implements IntArrayValuable { + WIN(1, "赢单"), + LOSE(2, "输单"), + INVALID(3, "无效"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmBizEndStatus::getStatus).toArray(); + + public static boolean isWin(Integer status) { + return ObjectUtil.equal(WIN.getStatus(), status); + } + + public static boolean isLose(Integer status) { + return ObjectUtil.equal(LOSE.getStatus(), status); + } + + public static boolean isInvalid(Integer status) { + return ObjectUtil.equal(INVALID.getStatus(), status); + } + + /** + * 场景类型 + */ + private final Integer status; + /** + * 场景名称 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } +} diff --git a/yudao-module-crm/yudao-module-crm-biz/pom.xml b/yudao-module-crm/yudao-module-crm-biz/pom.xml index 9e1a9e152..1d74efbef 100644 --- a/yudao-module-crm/yudao-module-crm-biz/pom.xml +++ b/yudao-module-crm/yudao-module-crm-biz/pom.xml @@ -22,6 +22,11 @@ yudao-module-system-api ${revision} + + cn.iocoder.boot + yudao-module-system-biz + ${revision} + cn.iocoder.boot yudao-module-crm-api diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java index cd4a60e81..9f2e3fbf1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; @@ -101,11 +102,12 @@ public class CrmBusinessStatusTypeController { PageResult pageResult = businessStatusTypeService.getBusinessStatusTypePage(pageReqVO); // 处理部门回显 // TODO @ljlleo:可以使用 CollectionUtils.convertSet 替代常用的 stream 操作,更简洁一点;下面几个也是哈; - Set deptIds = pageResult.getList().stream() - .map(CrmBusinessStatusTypeDO::getDeptIds) - .filter(Objects::nonNull) - .flatMap(Collection::stream) - .collect(Collectors.toSet()); +// Set deptIds = pageResult.getList().stream() +// .map(CrmBusinessStatusTypeDO::getDeptIds) +// .filter(Objects::nonNull) +// .flatMap(Collection::stream) +// .collect(Collectors.toSet()); + Set deptIds = CollectionUtils.convertSetByFlatMap(pageResult.getList(), CrmBusinessStatusTypeDO::getDeptIds,Collection::stream); List deptList = deptApi.getDeptList(deptIds); return success(CrmBusinessStatusTypeConvert.INSTANCE.convertPage(pageResult, deptList)); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java index f01f50c00..b615211a2 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java @@ -1,5 +1,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.product.CrmBusinessProductSaveReqVO; +import cn.iocoder.yudao.module.crm.enums.business.CrmBizEndStatus; import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction; import com.mzt.logapi.starter.annotation.DiffLogField; import io.swagger.v3.oas.annotations.media.Schema; @@ -9,6 +12,8 @@ import org.springframework.format.annotation.DateTimeFormat; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -66,7 +71,17 @@ public class CrmBusinessSaveReqVO { @Schema(description = "备注", example = "随便") @DiffLogField(name = "备注") private String remark; - // TODO @ljileo:修改的时候,应该可以传递添加的产品; + @Schema(description = "联系人编号", example = "110") + @NotNull(message = "联系人编号不能为空") + private Long contactId; + + @Schema(description = "1赢单2输单3无效", example = "1") + @InEnum(CrmBizEndStatus.class) + private Integer endStatus; + + @Schema(description = "商机产品列表", example = "") + private List products = new ArrayList<>(); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductPageReqVO.java new file mode 100644 index 000000000..fcf406b5d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductPageReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.product; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + + +/** + * @author lzxhqs + */ +@Schema(description = "管理后台 - 商机产品分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CrmBusinessProductPageReqVO extends PageParam { +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductRespVO.java new file mode 100644 index 000000000..4d24bd3c5 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductRespVO.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.product; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @author lzxhqs + */ +@Schema(description = "管理后台 - 商机产品关联 Response VO") +@Data +@ExcelIgnoreUnannotated +public class CrmBusinessProductRespVO { +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductSaveReqVO.java new file mode 100644 index 000000000..ddcc603c9 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/product/CrmBusinessProductSaveReqVO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.product; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author lzxhqs + */ +@Schema(description = "管理后台 - CRM 商机产品关联表 创建/更新 Request VO") +@Data +public class CrmBusinessProductSaveReqVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129") + private Long id; + + @Schema(description = "商机ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") + @NotNull(message = "商机ID不能为空") + private Integer businessId; + + @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") + @NotNull(message = "产品ID不能为空") + private Integer productId; + + @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") + @NotNull(message = "产品单价不能为空") + private BigDecimal price; + + @Schema(description = "销售价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") + @NotNull(message = "销售价格不能为空") + private BigDecimal salesPrice; + + @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") + @NotNull(message = "数量不能为空") + private BigDecimal num; + + @Schema(description = "折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") + @NotNull(message = "折扣不能为空") + private BigDecimal discount; + + @Schema(description = "小计(折扣后价格)", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") + @NotNull(message = "小计(折扣后价格)不能为空") + private BigDecimal subtotal; + + @Schema(description = "单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") + @NotNull(message = "单位不能为空") + private String unit; +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeSaveReqVO.java index 096b42e1f..3d61bed1d 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeSaveReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeSaveReqVO.java @@ -24,6 +24,6 @@ public class CrmBusinessStatusTypeSaveReqVO { // TODO @ljlleo VO 里面,我们不使用默认值哈。这里 Lists.newArrayList() 看看怎么去掉。上面 deptIds 也是类似噢 @Schema(description = "商机状态集合", requiredMode = Schema.RequiredMode.REQUIRED) - private List statusList = Lists.newArrayList(); + private List statusList; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessproduct/CrmBusinessProductConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessproduct/CrmBusinessProductConvert.java new file mode 100644 index 000000000..c3b84d31f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessproduct/CrmBusinessProductConvert.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.crm.convert.businessproduct; + +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.product.CrmBusinessProductSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author lzxhqs + * @version 1.0 + * @title CrmBusinessProductConvert + * @description + * @create 2024/1/12 + */ +@Mapper +public interface CrmBusinessProductConvert { + CrmBusinessProductConvert INSTANCE = Mappers.getMapper(CrmBusinessProductConvert.class); + + CrmBusinessProductDO convert(CrmBusinessProductSaveReqVO product); +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessstatustype/CrmBusinessStatusTypeConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessstatustype/CrmBusinessStatusTypeConvert.java index 0bad40148..301feb0d7 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessstatustype/CrmBusinessStatusTypeConvert.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/businessstatustype/CrmBusinessStatusTypeConvert.java @@ -39,9 +39,9 @@ public interface CrmBusinessStatusTypeConvert { default CrmBusinessStatusTypeRespVO convert(CrmBusinessStatusTypeDO bean, List statusList) { // TODO @ljlleo 可以链式赋值,简化成一行; - CrmBusinessStatusTypeRespVO result = convert(bean); - result.setStatusList(statusList); - return result; +// CrmBusinessStatusTypeRespVO result = convert(bean); +// result.setStatusList(statusList); + return convert(bean).setStatusList(statusList); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java index f1d5d4431..68b5f087b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.business; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -52,6 +54,7 @@ public class CrmBusinessDO extends BaseDO { * 客户编号 * * TODO @ljileo:这个字段,后续要写下关联的实体哈 + * 关联 {@link CrmCustomerDO#getId()} */ private Long customerId; /** @@ -101,6 +104,7 @@ public class CrmBusinessDO extends BaseDO { * 负责人的用户编号 * * 关联 AdminUserDO 的 id 字段 + * {@link AdminUserDO#getId()} */ private Long ownerUserId; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java new file mode 100644 index 000000000..3f146c3b3 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.business; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.math.BigDecimal; + +/** + * 商机产品关联表 DO + * + * @author lzxhqs + */ +@TableName("crm_business_product") +@KeySequence("crm_business_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmBusinessProductDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + + /** + * 商机ID + * 关联 {@link CrmBusinessDO#getId()} + */ + private Long businessId; + + /** + * 产品ID + * 关联{@link CrmProductDO#getId()} + */ + private Long productId; + + /** + * 产品单价 + */ + private BigDecimal price; + + /** + * 销售价格 + */ + private BigDecimal salesPrice; + + /** + * 数量 + */ + private BigDecimal num; + + /** + * 折扣 + */ + private BigDecimal discount; + + /** + * 小计(折扣后价格) + */ + private BigDecimal subtotal; + + /** + * 单位 + */ + private String unit; +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusDO.java index f83d0fb27..40c99936b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusDO.java @@ -39,7 +39,7 @@ public class CrmBusinessStatusDO { * * TODO 这里是不是改成 Integer 存储,百分比 * 100 ; */ - private String percent; + private Integer percent; /** * 排序 */ diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusTypeDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusTypeDO.java index d0d2f11f2..75f0094a1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusTypeDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusTypeDO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.business; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; import com.baomidou.mybatisplus.annotation.KeySequence; @@ -43,6 +44,7 @@ public class CrmBusinessStatusTypeDO extends BaseDO { * 开启状态 * * TODO 改成 Integer,关联 CommonStatus + * {@link CommonStatusEnum} */ private Boolean status; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java new file mode 100644 index 000000000..37a193f1f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.business; + + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 商机产品 Mapper + * @author lzxhqs + */ +@Mapper +public interface CrmBusinessProductMapper extends BaseMapperX { + default void deleteByBusinessId(Long id) { + delete(CrmBusinessProductDO::getBusinessId, id); + } + + default CrmBusinessProductDO selectByBusinessId(Long id) { + return selectOne(CrmBusinessProductDO::getBusinessId, id); + } +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusTypeMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusTypeMapper.java index af10bf8c7..9b90549a1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusTypeMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusTypeMapper.java @@ -28,4 +28,15 @@ public interface CrmBusinessStatusTypeMapper extends BaseMapperX { default Long selectCountByContactId(Long contactId) { return selectCount(CrmContractDO::getContactId, contactId); } + default CrmContractDO selectByBizId(Long businessId) { + return selectOne(CrmContractDO::getBusinessId, businessId); + } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java index 03c29557c..5d2ed6853 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java @@ -7,10 +7,18 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.product.CrmBusinessProductSaveReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO; import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert; +import cn.iocoder.yudao.module.crm.convert.businessproduct.CrmBusinessProductConvert; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper; +import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper; +import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessMapper; +import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; @@ -25,11 +33,14 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; +import java.math.BigDecimal; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_CONTRACT_EXISTS; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS; import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; @@ -45,6 +56,13 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { @Resource private CrmBusinessMapper businessMapper; + @Resource + private CrmBusinessProductMapper businessProductMapper; + @Resource + private CrmContractMapper contractMapper; + + @Resource + private CrmContactBusinessMapper contactBusinessMapper; @Resource private CrmPermissionService permissionService; @Resource @@ -61,17 +79,65 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { .setOwnerUserId(userId); businessMapper.insert(business); // TODO 商机待定:插入商机与产品的关联表;校验商品存在 - + verifyCrmBusinessProduct(business.getId()); + if (!createReqVO.getProducts().isEmpty()) { + createBusinessProducts(createReqVO.getProducts(), business.getId()); + } // TODO 商机待定:在联系人的详情页,如果直接【新建商机】,则需要关联下。这里要搞个 CrmContactBusinessDO 表 + createContactBusiness(business.getId(), createReqVO.getContactId()); // 2. 创建数据权限 + // 设置当前操作的人为负责人 permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()) - .setBizId(business.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人 + .setBizId(business.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 4. 记录操作日志上下文 LogRecordContext.putVariable("business", business); return business.getId(); } + /** + * @param businessId 商机id + * @param contactId 联系人id + * @throws + * @description 联系人与商机的关联 + * @author lzxhqs + */ + private void createContactBusiness(Long businessId, Long contactId) { + CrmContactBusinessDO contactBusiness = new CrmContactBusinessDO(); + contactBusiness.setBusinessId(businessId); + contactBusiness.setContactId(contactId); + contactBusinessMapper.insert(contactBusiness); + + } + + /** + * @param products 产品集合 + * @description 插入商机产品关联表 + * @author lzxhqs + */ + private void createBusinessProducts(List products, Long businessId) { + List list = new ArrayList<>(); + for (CrmBusinessProductSaveReqVO product : products) { + CrmBusinessProductDO businessProductDO = CrmBusinessProductConvert.INSTANCE.convert(product); + businessProductDO.setBusinessId(businessId); + list.add(businessProductDO); + } + businessProductMapper.insertBatch(list); + } + + /** + * @param id businessId + * @description 校验管理的产品存在则删除 + * @author lzxhqs + */ + private void verifyCrmBusinessProduct(Long id) { + CrmBusinessProductDO businessProductDO = businessProductMapper.selectByBusinessId(id); + if (businessProductDO != null) { + //通过商机Id删除 + businessProductMapper.deleteByBusinessId(id); + } + + } @Override @Transactional(rollbackFor = Exception.class) @@ -86,6 +152,10 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class); businessMapper.updateById(updateObj); // TODO 商机待定:插入商机与产品的关联表;校验商品存在 + verifyCrmBusinessProduct(updateReqVO.getId()); + if (!updateReqVO.getProducts().isEmpty()) { + createBusinessProducts(updateReqVO.getProducts(), updateReqVO.getId()); + } // TODO @商机待定:如果状态发生变化,插入商机状态变更记录表 // 3. 记录操作日志上下文 @@ -102,6 +172,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { // 校验存在 CrmBusinessDO business = validateBusinessExists(id); // TODO @商机待定:需要校验有没关联合同。CrmContractDO 的 businessId 字段 + validateContractExists(id); // 删除 businessMapper.deleteById(id); @@ -112,6 +183,19 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { LogRecordContext.putVariable("businessName", business.getName()); } + /** + * @param businessId 商机id + * @throws + * @description 删除校验合同是关联合同 + * @author lzxhqs + */ + private void validateContractExists(Long businessId) { + CrmContractDO contract = contractMapper.selectByBizId(businessId); + if(contract != null) { + throw exception(BUSINESS_CONTRACT_EXISTS); + } + } + private CrmBusinessDO validateBusinessExists(Long id) { CrmBusinessDO crmBusiness = businessMapper.selectById(id); if (crmBusiness == null) { diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeServiceImpl.java index 0ebcda87c..d9845976b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeServiceImpl.java @@ -95,14 +95,18 @@ public class CrmBusinessStatusTypeServiceImpl implements CrmBusinessStatusTypeSe // TODO @ljlleo 这个方法,这个参考 validateDeptNameUnique 实现。 private void validateBusinessStatusTypeExists(String name, Long id) { - LambdaQueryWrapper wrapper = new LambdaQueryWrapperX<>(); - if(null != id) { - wrapper.ne(CrmBusinessStatusTypeDO::getId, id); - } - long cnt = businessStatusTypeMapper.selectCount(wrapper.eq(CrmBusinessStatusTypeDO::getName, name)); - if (cnt > 0) { + CrmBusinessStatusTypeDO businessStatusTypeDO = businessStatusTypeMapper.selectByIdAndName(id, name); + if (businessStatusTypeDO != null) { throw exception(BUSINESS_STATUS_TYPE_NAME_EXISTS); } +// LambdaQueryWrapper wrapper = new LambdaQueryWrapperX<>(); +// if(null != id) { +// wrapper.ne(CrmBusinessStatusTypeDO::getId, id); +// } +// long cnt = businessStatusTypeMapper.selectCount(wrapper.eq(CrmBusinessStatusTypeDO::getName, name)); +// if (cnt > 0) { +// throw exception(BUSINESS_STATUS_TYPE_NAME_EXISTS); +// } } @Override