feat(bpm): 新增合同审批功能

- 添加合同审批相关的 API 接口和实现类
- 创建合同审批的请求和响应对象
- 实现合同审批的创建、查询等功能
- 添加合同统计相关接口和实现
- 优化合同审批页面列表展示
This commit is contained in:
furongxin 2024-12-08 13:36:06 +08:00
parent c26f8f723f
commit b1e7f9a9f6
119 changed files with 3072 additions and 562 deletions

View File

@ -42,6 +42,10 @@
<artifactId>spring-cloud-starter-openfeign</artifactId> <artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-protection</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;
import cn.iocoder.yudao.module.bpm.api.oa.vo.contract.BpmOAContractDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.contract.BpmOAContractVO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.contract.ContractStatisticsDTO;
import cn.iocoder.yudao.module.bpm.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.time.LocalDateTime;
import java.util.List;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 流程实例")
public interface BpmOAContractApi {
String PREFIX = ApiConstants.PREFIX + "/oa/contract";
@PostMapping(PREFIX + "/create")
@Operation(summary = "创建合同审批流程")
@Idempotent(timeout = 10)
CommonResult<Long> create(@RequestBody BpmOAContractDTO createReqVO);
@GetMapping(PREFIX + "/get-contract-count")
@Operation(summary = "获得当日合同统计 | 新增数量、新增金额")
CommonResult<ContractStatisticsDTO> getContractCount(@RequestParam("relation") String relation);
@GetMapping(PREFIX + "/get-contract-statistics")
@Operation(summary = "获得指定用户的合同统计信息")
CommonResult<List<ContractStatisticsDTO>> getContractStatistics(@RequestParam("userId") List<Long> userIds,
@RequestParam(name = "createTime", required = false) LocalDateTime[] createTime);
@PostMapping(PREFIX + "/getList")
@Operation(summary = "获得合同列表")
CommonResult<List<BpmOAContractDTO>> getContractList(@RequestBody BpmOAContractVO respVO);
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.bpm.api.oa; package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.BpmOAEntryDTO; import cn.iocoder.yudao.module.bpm.api.oa.vo.entry.BpmOAEntryDTO;
import cn.iocoder.yudao.module.bpm.enums.ApiConstants; import cn.iocoder.yudao.module.bpm.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.invoice.BpmOAInvoiceDTO;
import cn.iocoder.yudao.module.bpm.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 流程实例")
public interface BpmOAInvoiceApi {
String PREFIX = ApiConstants.PREFIX + "/oa/invoice";
@PostMapping(PREFIX + "/create")
@Operation(summary = "创建开票申请流程")
CommonResult<Long> create(@RequestBody BpmOAInvoiceDTO createReqVO);
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.bpm.api.oa; package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.BpmOALeaveRpcVO; import cn.iocoder.yudao.module.bpm.api.oa.vo.leave.BpmOALeaveRpcVO;
import cn.iocoder.yudao.module.bpm.enums.ApiConstants; import cn.iocoder.yudao.module.bpm.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.bpm.api.oa; package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.BpmOARefundDTO; import cn.iocoder.yudao.module.bpm.api.oa.vo.refund.BpmOARefundDTO;
import cn.iocoder.yudao.module.bpm.enums.ApiConstants; import cn.iocoder.yudao.module.bpm.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;

View File

@ -0,0 +1,97 @@
package cn.iocoder.yudao.module.bpm.api.oa.vo.contract;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
/**
* 合同审批 创建 Request VO
*
* @author 符溶馨
*/
@Schema(description = "管理后台 - 合同审批创建 Request VO")
@Data
public class BpmOAContractDTO {
@Schema(description = "合同类型 | 字典值 bpm_oa_contract_type", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "合同类型不能为空")
private Integer contractType;
@Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "合同名称不能为空")
private String contractName;
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "合同编号不能为空")
private String contractNo;
@Schema(description = "签约日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "签约日期不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate signingDate;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Long customerId;
@Schema(description = "客户名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String customerName;
@Schema(description = "客户签约人联系人ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Long contactsId;
@Schema(description = "客户签约人(联系人)名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String contactsName;
@Schema(description = "签约公司编号", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "签约公司不能为空")
private Long companyId;
@Schema(description = "签约人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Long signatoryId;
@Schema(description = "合同金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal contractMoney;
@Schema(description = "已收/已付金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal returnMoney;
@Schema(description = "已开票金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal invoiceMoney;
@Schema(description = "产品总金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal totalPrice;
@Schema(description = "整单优惠金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal discountRate;
@Schema(description = "商机ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Long businessId;
@Schema(description = "合同开始时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate startDate;
@Schema(description = "合同结束时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate endDate;
@Schema(description = "合同状态 | 1已签约 2过期", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Integer status;
@Schema(description = "流程实例编号")
private String processInstanceId;
@Schema(description = "状态-参见 bpm_process_instance_result 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer result;
@Schema(description = "上传文件", requiredMode = Schema.RequiredMode.REQUIRED)
private List<UploadUserFile> fileItems;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.bpm.api.oa.vo.contract;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Schema(description = "BpmOA合同审批流程的VO对象")
@Data
public class BpmOAContractVO {
/**
* 合同类型
* 1 销售合同
*/
public static final Integer SALE_CONTRACT = 1;
@Schema(description = "用户编号")
private List<Long> userId;
@Schema(description = "合同类型")
private Integer contractType;
@Schema(description = "开始时间")
private Date starTime;
@Schema(description = "结束时间")
private Date endTime;
}

View File

@ -0,0 +1,38 @@
package cn.iocoder.yudao.module.bpm.api.oa.vo.contract;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 合同审批 请求Request VO")
@Data
public class ContractStatisticsDTO {
@Schema(description = "用户编号")
private Long userId;
@Schema(description = "今日新增合同数")
private Integer todayAdditions;
@Schema(description = "昨日新增合同数")
private Integer yesterdayAdditions;
@Schema(description = "较昨日百分比 | 数量")
private BigDecimal countPercentage;
@Schema(description = "今日新增合同金额")
private BigDecimal todayAddMoney;
@Schema(description = "昨日新增合同金额")
private BigDecimal yesterdayAddMoney;
@Schema(description = "较昨日百分比 | 金额")
private BigDecimal moneyPercentage;
@Schema(description = "合同总数")
private Integer count;
@Schema(description = "合同总金额")
private BigDecimal money;
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.bpm.api.oa.vo; package cn.iocoder.yudao.module.bpm.api.oa.vo.entry;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.bpm.api.oa.vo; package cn.iocoder.yudao.module.bpm.api.oa.vo.evection;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile; import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;

View File

@ -0,0 +1,88 @@
package cn.iocoder.yudao.module.bpm.api.oa.vo.invoice;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
@Schema(description = "管理后台 - 开票申请创建 Request VO")
@Data
@EqualsAndHashCode()
@ToString(callSuper = true)
public class BpmOAInvoiceDTO {
@Schema(description = "计划开票日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "计划开票日期不能为空")
private LocalDate invoiceDate;
@Schema(description = "关联合同业务编号", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "合同编号不能为空")
private Long contractId;
@Schema(description = "关联合同流程实例编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String contractInstanceId;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Long customerId;
@Schema(description = "开票主体", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "开票主体不能为空")
private String invoiceBody;
@Schema(description = "发票抬头", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "发票抬头不能为空")
private String invoiceName;
@Schema(description = "抬头类型 | 1企业2个人3事业单位", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "抬头类型不能为空")
private Integer invoiceIssue;
@Schema(description = "发票类型 | 1普票 2专票", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "发票类型不能为空")
private Integer invoiceType;
@Schema(description = "统一社会信用代码", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "统一社会信用代码不能为空")
private String registerNo;
@Schema(description = "开票金额", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "开票金额不能为空")
private BigDecimal amount;
@Schema(description = "开票内容", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "开票内容不能为空")
private String content;
@Schema(description = "开票单位地址")
private String unitAddress;
@Schema(description = "开票单位电话")
private String unitPhone;
@Schema(description = "开票单位开户银行")
private String unitBankName;
@Schema(description = "开票单位银行账号")
private String unitBankNo;
@Schema(description = "开票单位邮箱")
private String email;
@Schema(description = "开票状态", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Integer status;
@Schema(description = "流程实例编号")
private String processInstanceId;
@Schema(description = "状态-参见 bpm_process_instance_result 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer result;
@Schema(description = "上传文件", requiredMode = Schema.RequiredMode.REQUIRED)
private List<UploadUserFile> fileItems ;
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.bpm.api.oa.vo; package cn.iocoder.yudao.module.bpm.api.oa.vo.leave;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.bpm.api.oa.vo; package cn.iocoder.yudao.module.bpm.api.oa.vo.refund;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile; import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;

View File

@ -59,7 +59,7 @@ public interface ErrorCodeConstants {
ErrorCode OA_REPLACEMENT_CARD_NOT_EXISTS = new ErrorCode(1_009_001_122, "补卡申请不存在"); ErrorCode OA_REPLACEMENT_CARD_NOT_EXISTS = new ErrorCode(1_009_001_122, "补卡申请不存在");
ErrorCode OA_REFUND_NOT_EXISTS = new ErrorCode(1_009_001_123, "退款申请不存在"); ErrorCode OA_REFUND_NOT_EXISTS = new ErrorCode(1_009_001_123, "退款申请不存在");
ErrorCode OA_PROJECT_NOT_EXISTS = new ErrorCode(1_009_001_124, "项目申请不存在"); ErrorCode OA_PROJECT_NOT_EXISTS = new ErrorCode(1_009_001_124, "项目申请不存在");
ErrorCode OA_CONTRACT_INVOICE_AMOUNT_LACK = new ErrorCode(1_009_001_125, "该合同的开票余额不足!");
// ========== 流程模型 1-009-002-000 ========== // ========== 流程模型 1-009-002-000 ==========
ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, "已经存在流程标识为【{}】的流程"); ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, "已经存在流程标识为【{}】的流程");

View File

@ -118,6 +118,22 @@
<groupId>cn.iocoder.cloud</groupId> <groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-flowable</artifactId> <artifactId>yudao-spring-boot-starter-flowable</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-product-api</artifactId>
<version>2.0.0-jdk8-snapshot</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-crm-api</artifactId>
<version>2.0.0-jdk8-snapshot</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-protection</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.oa.vo.contract.BpmOAContractDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.contract.BpmOAContractVO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.contract.ContractStatisticsDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.ContractStatisticsRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAContractService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* Flowable 流程实例 Api 实现类
*/
@RestController
@Validated
public class BpmOAContractApiImpl implements BpmOAContractApi{
@Resource
private BpmOAContractService contractService;
@Override
public CommonResult<Long> create(BpmOAContractDTO createReqVO) {
return success(contractService.createContract(getLoginUserId(), BeanUtils.toBean(createReqVO, BpmOAContractCreateReqVO.class)));
}
@Override
public CommonResult<ContractStatisticsDTO> getContractCount(String relation) {
ContractStatisticsRespVO respVO = contractService.getContractStatistics(getLoginUserId(), relation);
return success(BeanUtils.toBean(respVO, ContractStatisticsDTO.class));
}
@Override
public CommonResult<List<ContractStatisticsDTO>> getContractStatistics(List<Long> userIds, LocalDateTime[] createTime) {
List<ContractStatisticsRespVO> respVOS = contractService.getContractStatisticsListByUserIds(userIds, createTime);
return success(BeanUtils.toBean(respVOS, ContractStatisticsDTO.class));
}
@Override
public CommonResult<List<BpmOAContractDTO>> getContractList(BpmOAContractVO respVO) {
List<BpmOAContractDO> contractDO = contractService.getContractList(respVO);
return success(BeanUtils.toBean(contractDO, BpmOAContractDTO.class));
}
}

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.oa.vo.BpmOAEntryDTO; import cn.iocoder.yudao.module.bpm.api.oa.vo.entry.BpmOAEntryDTO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAEntryDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAEntryDO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAEntryService; import cn.iocoder.yudao.module.bpm.service.oa.BpmOAEntryService;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.oa.vo.invoice.BpmOAInvoiceDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceCreateReqVO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAInvoiceService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* Flowable 流程实例 Api 实现类
*/
@RestController
@Validated
public class BpmOAInvoiceApiImpl implements BpmOAInvoiceApi{
@Resource
private BpmOAInvoiceService invoiceService;
@Override
public CommonResult<Long> create(BpmOAInvoiceDTO createReqVO) {
return success(invoiceService.createInvoice(getLoginUserId(), BeanUtils.toBean(createReqVO, BpmOAInvoiceCreateReqVO.class)));
}
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.bpm.api.oa; package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.BpmOALeaveRpcVO; import cn.iocoder.yudao.module.bpm.api.oa.vo.leave.BpmOALeaveRpcVO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOALeaveService; import cn.iocoder.yudao.module.bpm.service.oa.BpmOALeaveService;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.oa.vo.BpmOARefundDTO; import cn.iocoder.yudao.module.bpm.api.oa.vo.refund.BpmOARefundDTO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOARefundDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOARefundDO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOARefundService; import cn.iocoder.yudao.module.bpm.service.oa.BpmOARefundService;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;

View File

@ -1,13 +1,23 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa; package cn.iocoder.yudao.module.bpm.controller.admin.oa;
import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.ContractStatisticsRespVO;
import cn.iocoder.yudao.module.bpm.convert.oa.BpmOAContractConvert; import cn.iocoder.yudao.module.bpm.convert.oa.BpmOAContractConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAContractService; import cn.iocoder.yudao.module.bpm.service.oa.BpmOAContractService;
import cn.iocoder.yudao.module.hrm.api.crmbusiness.BusinessApi;
import cn.iocoder.yudao.module.hrm.api.crmbusiness.dto.CrmBusinessDTO;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@ -16,7 +26,9 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@ -39,8 +51,15 @@ public class BpmOAContractController {
@Resource @Resource
private AdminUserApi userApi; private AdminUserApi userApi;
@Resource
private DeptApi deptApi;
@Resource
private BusinessApi businessApi;
@PostMapping("/create") @PostMapping("/create")
@Operation(summary = "创建请求申请") @Operation(summary = "创建请求申请")
@Idempotent(timeout = 10)
public CommonResult<Long> createContract(@Valid @RequestBody BpmOAContractCreateReqVO createReqVO) { public CommonResult<Long> createContract(@Valid @RequestBody BpmOAContractCreateReqVO createReqVO) {
return success(contractService.createContract(getLoginUserId(), createReqVO)); return success(contractService.createContract(getLoginUserId(), createReqVO));
} }
@ -51,8 +70,31 @@ public class BpmOAContractController {
public CommonResult<BpmOAContractRespVO> getContract(@RequestParam("id") Long id) { public CommonResult<BpmOAContractRespVO> getContract(@RequestParam("id") Long id) {
BpmOAContractDO contract = contractService.getContract(id); BpmOAContractDO contract = contractService.getContract(id);
BpmOAContractRespVO respVO = BpmOAContractConvert.INSTANCE.convert(contract);
if (respVO != null) {
// 获取用户和签约人信息
List<Long> userIds = Arrays.asList(contract.getUserId(), contract.getSignatoryId());
Map<Long, AdminUserRespDTO> userMap = userApi.getUserMap(userIds);
if (CollectionUtil.isNotEmpty(userMap)) {
// 设置创建人签约人名称
respVO.setSignatoryName(userMap.get(contract.getSignatoryId()).getNickname());
respVO.setCreateName(userMap.get(contract.getUserId()).getNickname());
}
return success(BpmOAContractConvert.INSTANCE.convert(contract)); // 获取公司信息
DeptRespDTO dto = deptApi.getDept(contract.getCompanyId()).getCheckedData();
if (dto != null) {
respVO.setCompanyName(dto.getName());
}
if (respVO.getBusinessId() != null) {
// 获取商机信息
CrmBusinessDTO businessDTO = businessApi.getBusiness(respVO.getBusinessId()).getCheckedData();
respVO.setBusinessName(businessDTO != null ? businessDTO.getName() : null);
}
}
return success(respVO);
} }
@GetMapping("/getByProcessInstanceId") @GetMapping("/getByProcessInstanceId")
@ -61,12 +103,42 @@ public class BpmOAContractController {
public CommonResult<BpmOAContractRespVO> getContractByProcessInstanceId(@RequestParam("processInstanceId") String processInstanceId) { public CommonResult<BpmOAContractRespVO> getContractByProcessInstanceId(@RequestParam("processInstanceId") String processInstanceId) {
BpmOAContractDO contract = contractService.getByProcessInstanceId(processInstanceId); BpmOAContractDO contract = contractService.getByProcessInstanceId(processInstanceId);
BpmOAContractRespVO respVO = BpmOAContractConvert.INSTANCE.convert(contract);
if (respVO != null) {
// 获取用户和签约人信息
List<Long> userIds = Arrays.asList(contract.getUserId(), contract.getSignatoryId());
Map<Long, AdminUserRespDTO> userMap = userApi.getUserMap(userIds);
if (CollectionUtil.isNotEmpty(userMap)) {
// 设置创建人签约人名称
respVO.setSignatoryName(userMap.get(contract.getSignatoryId()).getNickname());
respVO.setCreateName(userMap.get(contract.getUserId()).getNickname());
}
return success(BpmOAContractConvert.INSTANCE.convert(contract)); // 获取公司信息
DeptRespDTO dto = deptApi.getDept(contract.getCompanyId()).getCheckedData();
if (dto != null) {
respVO.setCompanyName(dto.getName());
}
if (respVO.getBusinessId() != null) {
// 获取商机信息
CrmBusinessDTO businessDTO = businessApi.getBusiness(respVO.getBusinessId()).getCheckedData();
respVO.setBusinessName(businessDTO != null ? businessDTO.getName() : null);
}
}
return success(respVO);
}
@GetMapping("/page")
@Operation(summary = "获得合同审批分页列表")
public CommonResult<PageResult<BpmOAContractRespVO>> getPage(@Valid BpmOAContractPageReqVO pageReqVO) {
return success(contractService.getContractPage(pageReqVO));
} }
@GetMapping("/getListByDeptId") @GetMapping("/getListByDeptId")
@Operation(summary = "获得同部门的合同审批") @Operation(summary = "获得同部门的销售合同")
public CommonResult<List<BpmOAContractRespVO>> getListByDeptId() { public CommonResult<List<BpmOAContractRespVO>> getListByDeptId() {
// 获取同部门所有用户id // 获取同部门所有用户id
@ -76,4 +148,16 @@ public class BpmOAContractController {
return success(BeanUtils.toBean(contracts, BpmOAContractRespVO.class)); return success(BeanUtils.toBean(contracts, BpmOAContractRespVO.class));
} }
@GetMapping("/get-contract-statistics")
@Operation(summary = "获得当日合同统计 | 新增数量、新增金额")
@Parameter(name = "relation", description = "查询类型 my我的 sub下属| ", required = true)
public CommonResult<ContractStatisticsRespVO> getListByDeptId(@RequestParam("relation") String relation) {
ContractStatisticsRespVO respVO = contractService.getContractStatistics(getLoginUserId(), relation);
return success(respVO);
}
} }

View File

@ -1,8 +1,10 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa; package cn.iocoder.yudao.module.bpm.controller.admin.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoicePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAInvoiceDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAInvoiceDO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAInvoiceService; import cn.iocoder.yudao.module.bpm.service.oa.BpmOAInvoiceService;
@ -61,4 +63,11 @@ public class BpmOAInvoiceController {
BpmOAInvoiceRespVO bpmOAInvoiceRespVO = BeanUtils.toBean(invoice, BpmOAInvoiceRespVO.class); BpmOAInvoiceRespVO bpmOAInvoiceRespVO = BeanUtils.toBean(invoice, BpmOAInvoiceRespVO.class);
return success(bpmOAInvoiceRespVO); return success(bpmOAInvoiceRespVO);
} }
@GetMapping("/page")
@Operation(summary = "获得开票申请分页列表")
public CommonResult<PageResult<BpmOAInvoiceRespVO>> getPage(@Valid BpmOAInvoicePageReqVO pageReqVO) {
return success(invoiceService.getInvoicePage(pageReqVO));
}
} }

View File

@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile; import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
@ -16,32 +18,78 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
* @author 符溶馨 * @author 符溶馨
*/ */
@Schema(description = "管理后台 - 合同审批创建 Request VO") @Schema(description = "管理后台 - 合同审批创建 Request VO")
@Data
public class BpmOAContractCreateReqVO { public class BpmOAContractCreateReqVO {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Long id;
@Schema(description = "合同类型 | 字典值 bpm_oa_contract_type", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "合同类型不能为空")
private Integer contractType;
@Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "合同名称不能为空") @NotNull(message = "合同名称不能为空")
private String contractName; private String contractName;
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@NotNull(message = "合同编号不能为空") private String contractNo;
private String contractNumber;
@Schema(description = "签约日期", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "签约日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "签约日期不能为空") @NotNull(message = "签约日期不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate signingDate; private LocalDate signingDate;
@Schema(description = "我方公司名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String mCompanyName; private Long customerId;
@Schema(description = "我方负责人", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String mHeadName; private String customerName;
@Schema(description = "对方公司名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @Schema(description = "客户签约人联系人ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String oCompanyName; private Long contactsId;
@Schema(description = "对方负责人", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @Schema(description = "客户签约人(联系人)名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String oHeadName; private String contactsName;
@Schema(description = "签约公司编号", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "签约公司不能为空")
private Long companyId;
@Schema(description = "签约人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Long signatoryId;
@Schema(description = "合同金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal contractMoney;
@Schema(description = "已收/已付金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal returnMoney;
@Schema(description = "已开票金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal invoiceMoney;
@Schema(description = "产品总金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal totalPrice;
@Schema(description = "整单优惠金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal discountRate;
@Schema(description = "商机ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Long businessId;
@Schema(description = "合同开始时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate startDate;
@Schema(description = "合同结束时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate endDate;
@Schema(description = "合同状态 | 1已签约 2过期", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Integer status;
@Schema(description = "合同产品关系列表")
private List<CrmContractProductVO> contractProducts;
@Schema(description = "流程实例编号") @Schema(description = "流程实例编号")
private String processInstanceId; private String processInstanceId;
@ -51,103 +99,4 @@ public class BpmOAContractCreateReqVO {
@Schema(description = "上传文件", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "上传文件", requiredMode = Schema.RequiredMode.REQUIRED)
private List<UploadUserFile> fileItems; private List<UploadUserFile> fileItems;
public BpmOAContractCreateReqVO() {
}
public BpmOAContractCreateReqVO(String contractName, String contractNumber, LocalDate signingDate,
String mCompanyName, String mHeadName, String oCompanyName,
String oHeadName, String processInstanceId, List<UploadUserFile> fileItems,
Integer result) {
this.contractName = contractName;
this.contractNumber = contractNumber;
this.signingDate = signingDate;
this.mCompanyName = mCompanyName;
this.mHeadName = mHeadName;
this.oCompanyName = oCompanyName;
this.oHeadName = oHeadName;
this.processInstanceId = processInstanceId;
this.result = result;
this.fileItems = fileItems;
}
public Integer getResult() {
return result;
}
public void setResult(Integer result) {
this.result = result;
}
public String getContractName() {
return contractName;
}
public String getProcessInstanceId() {
return processInstanceId;
}
public void setProcessInstanceId(String processInstanceId) {
this.processInstanceId = processInstanceId;
}
public void setContractName(String contractName) {
this.contractName = contractName;
}
public String getContractNumber() {
return contractNumber;
}
public void setContractNumber(String contractNumber) {
this.contractNumber = contractNumber;
}
public LocalDate getSigningDate() {
return signingDate;
}
public void setSigningDate(LocalDate signingDate) {
this.signingDate = signingDate;
}
public String getmCompanyName() {
return mCompanyName;
}
public void setmCompanyName(String mCompanyName) {
this.mCompanyName = mCompanyName;
}
public String getmHeadName() {
return mHeadName;
}
public void setmHeadName(String mHeadName) {
this.mHeadName = mHeadName;
}
public String getoCompanyName() {
return oCompanyName;
}
public void setoCompanyName(String oCompanyName) {
this.oCompanyName = oCompanyName;
}
public String getoHeadName() {
return oHeadName;
}
public void setoHeadName(String oHeadName) {
this.oHeadName = oHeadName;
}
public List<UploadUserFile> getFileItems() {
return fileItems;
}
public void setFileItems(List<UploadUserFile> fileItems) {
this.fileItems = fileItems;
}
} }

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Schema(description = "管理后台 - 合同审批分页 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class BpmOAContractPageReqVO extends PageParam {
@Schema(description = "合同类型 | 字典值 bpm_oa_contract_type", example = "1")
private Integer contractType;
@Schema(description = "客户名称,模糊匹配")
private String customerName;
@Schema(description = "合同名称,模糊匹配")
private String contractName;
@Schema(description = "合同编号")
private String contractNo;
@Schema(description = "审批结果")
private Integer result;
@Schema(description = "合同状态", example = "1")
private Integer status;
@Schema(description = "签约人名称")
private String signatoryName;
@Schema(description = "创建人名称")
private String createName;
@Schema(description = "查询模式")
private String relation;
}

View File

@ -1,13 +1,12 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract; package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOABaseRespVO;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile; import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOABaseRespVO;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull; import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
@ -19,31 +18,81 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
@Data @Data
public class BpmOAContractRespVO extends BpmOABaseRespVO { public class BpmOAContractRespVO extends BpmOABaseRespVO {
@Schema(description = "发起人用户编号", requiredMode = Schema.RequiredMode.REQUIRED)
private Long userId;
@Schema(description = "合同类型 | 字典值 bpm_oa_contract_type", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer contractType;
@Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "合同名称不能为空")
private String contractName; private String contractName;
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "合同编号不能为空") private String contractNo;
private String contractNumber;
@Schema(description = "签约日期", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "签约日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "签约日期不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate signingDate; private String signingDate;
@Schema(description = "我方公司名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String mCompanyName; private Long customerId;
@Schema(description = "我方负责人", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String mHeadName; private String customerName;
@Schema(description = "对方公司名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @Schema(description = "客户签约人联系人ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String oCompanyName; private Long contactsId;
@Schema(description = "对方负责人", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @Schema(description = "客户签约人(联系人)名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String oHeadName; private String contactsName;
@Schema(description = "签约公司编号", requiredMode = Schema.RequiredMode.REQUIRED)
private Long companyId;
@Schema(description = "签约公司名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String companyName;
@Schema(description = "签约人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Long signatoryId;
@Schema(description = "签约人名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String signatoryName;
@Schema(description = "合同金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal contractMoney;
@Schema(description = "已收/已付金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal returnMoney;
@Schema(description = "已开票金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal invoiceMoney;
@Schema(description = "产品总金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal totalPrice;
@Schema(description = "整单优惠金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private BigDecimal discountRate;
@Schema(description = "商机ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Long businessId;
@Schema(description = "商机名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String businessName;
@Schema(description = "合同开始时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private String startDate;
@Schema(description = "合同结束时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private String endDate;
@Schema(description = "合同状态 | 1已签约 2过期", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Integer status;
@Schema(description = "上传文件", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "上传文件", requiredMode = Schema.RequiredMode.REQUIRED)
private List<UploadUserFile> fileItems; private List<UploadUserFile> fileItems;
@Schema(description = "创建人名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String createName;
} }

View File

@ -0,0 +1,38 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 合同审批 请求Request VO")
@Data
public class ContractStatisticsRespVO {
@Schema(description = "用户编号")
private Long userId;
@Schema(description = "今日新增合同数")
private Integer todayAdditions;
@Schema(description = "昨日新增合同数")
private Integer yesterdayAdditions;
@Schema(description = "较昨日百分比 | 数量")
private BigDecimal countPercentage;
@Schema(description = "今日新增合同金额")
private BigDecimal todayAddMoney;
@Schema(description = "昨日新增合同金额")
private BigDecimal yesterdayAddMoney;
@Schema(description = "较昨日百分比 | 金额")
private BigDecimal moneyPercentage;
@Schema(description = "合同总数")
private Integer count;
@Schema(description = "合同总金额")
private BigDecimal money;
}

View File

@ -0,0 +1,38 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 合同产品关联 Response VO")
@Data
public class CrmContractProductVO {
@Schema(description = "合同编号")
private Long contractId;
@Schema(description = "产品编号")
private Long productId;
@Schema(description = "产品名称")
private String name;
@Schema(description = "产品属性")
private String productAttrUnique;
@Schema(description = "产品单价")
private BigDecimal price;
@Schema(description = "数量")
private Integer nums;
@Schema(description = "折扣")
private BigDecimal discount;
@Schema(description = "小计")
private BigDecimal subtotal;
@Schema(description = "备注")
private String remarks;
}

View File

@ -21,8 +21,35 @@ public class BpmOAInvoiceCreateReqVO {
@NotNull(message = "计划开票日期不能为空") @NotNull(message = "计划开票日期不能为空")
private LocalDate invoiceDate; private LocalDate invoiceDate;
@Schema(description = "项目编号") @Schema(description = "关联合同业务编号", requiredMode = Schema.RequiredMode.REQUIRED)
private Long projectId; @NotNull(message = "合同编号不能为空")
private Long contractId;
@Schema(description = "关联合同流程实例编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String contractInstanceId;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Long customerId;
@Schema(description = "开票主体", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "开票主体不能为空")
private String invoiceBody;
@Schema(description = "发票抬头", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "发票抬头不能为空")
private String invoiceName;
@Schema(description = "抬头类型 | 1企业2个人3事业单位", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "抬头类型不能为空")
private Integer invoiceIssue;
@Schema(description = "发票类型 | 1普票 2专票", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "发票类型不能为空")
private Integer invoiceType;
@Schema(description = "统一社会信用代码", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "统一社会信用代码不能为空")
private String registerNo;
@Schema(description = "开票金额", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "开票金额", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "开票金额不能为空") @NotNull(message = "开票金额不能为空")
@ -32,14 +59,6 @@ public class BpmOAInvoiceCreateReqVO {
@NotNull(message = "开票内容不能为空") @NotNull(message = "开票内容不能为空")
private String content; private String content;
@Schema(description = "开票单位名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "开票单位名称不能为空")
private String unitName;
@Schema(description = "开票单位纳税人识别号", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "开票单位纳税人识别号不能为空")
private String unitTin;
@Schema(description = "开票单位地址") @Schema(description = "开票单位地址")
private String unitAddress; private String unitAddress;
@ -52,8 +71,11 @@ public class BpmOAInvoiceCreateReqVO {
@Schema(description = "开票单位银行账号") @Schema(description = "开票单位银行账号")
private String unitBankNo; private String unitBankNo;
@Schema(description = "关联合同业务流程编号") @Schema(description = "开票单位邮箱")
private String contractInstanceId; private String email;
@Schema(description = "开票状态", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Integer status;
@Schema(description = "流程实例编号") @Schema(description = "流程实例编号")
private String processInstanceId; private String processInstanceId;

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Schema(description = "管理后台 - 合同审批分页 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class BpmOAInvoicePageReqVO extends PageParam {
@Schema(description = "合同名称")
private String contractName;
@Schema(description = "客户名称,模糊匹配")
private String customerName;
@Schema(description = "审批结果")
private Integer result;
@Schema(description = "开票状态", example = "1")
private Integer status;
@Schema(description = "查询模式")
private String relation;
}

View File

@ -20,8 +20,35 @@ public class BpmOAInvoiceRespVO extends BpmOABaseRespVO {
@Schema(description = "计划开票日期", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "计划开票日期", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDate invoiceDate; private LocalDate invoiceDate;
@Schema(description = "项目编号") @Schema(description = "关联合同业务编号", requiredMode = Schema.RequiredMode.REQUIRED)
private Long projectId; private Long contractId;
@Schema(description = "合同名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String contractName;
@Schema(description = "关联合同流程实例编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String contractInstanceId;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Long customerId;
@Schema(description = "客户名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String customerName;
@Schema(description = "开票主体", requiredMode = Schema.RequiredMode.REQUIRED)
private String invoiceBody;
@Schema(description = "发票抬头", requiredMode = Schema.RequiredMode.REQUIRED)
private String invoiceName;
@Schema(description = "抬头类型 | 1企业2个人3事业单位", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer invoiceIssue;
@Schema(description = "发票类型 | 1普票 2专票", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer invoiceType;
@Schema(description = "统一社会信用代码", requiredMode = Schema.RequiredMode.REQUIRED)
private String registerNo;
@Schema(description = "开票金额", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "开票金额", requiredMode = Schema.RequiredMode.REQUIRED)
private BigDecimal amount; private BigDecimal amount;
@ -29,12 +56,6 @@ public class BpmOAInvoiceRespVO extends BpmOABaseRespVO {
@Schema(description = "开票内容", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "开票内容", requiredMode = Schema.RequiredMode.REQUIRED)
private String content; private String content;
@Schema(description = "开票单位名称", requiredMode = Schema.RequiredMode.REQUIRED)
private String unitName;
@Schema(description = "开票单位纳税人识别号", requiredMode = Schema.RequiredMode.REQUIRED)
private String unitTin;
@Schema(description = "开票单位地址") @Schema(description = "开票单位地址")
private String unitAddress; private String unitAddress;
@ -47,9 +68,12 @@ public class BpmOAInvoiceRespVO extends BpmOABaseRespVO {
@Schema(description = "开票单位银行账号") @Schema(description = "开票单位银行账号")
private String unitBankNo; private String unitBankNo;
@Schema(description = "关联合同业务流程编号") @Schema(description = "开票单位邮箱")
private String contractInstanceId; private String email;
@Schema(description = "上传文件") @Schema(description = "开票状态", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private Integer status;
@Schema(description = "上传文件", requiredMode = Schema.RequiredMode.REQUIRED)
private List<UploadUserFile> fileItems ; private List<UploadUserFile> fileItems ;
} }

View File

@ -5,17 +5,25 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmHistoryProcessInstanceDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmHistoryProcessInstanceDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAContractService;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAInvoiceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmHistoryProcessInstanceService; import cn.iocoder.yudao.module.bpm.service.task.BpmHistoryProcessInstanceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.flowable.task.api.history.HistoricTaskInstance; import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -36,6 +44,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLogi
@RestController @RestController
@RequestMapping("/bpm/task") @RequestMapping("/bpm/task")
@Validated @Validated
@Slf4j
@DataPermission(enable = false) @DataPermission(enable = false)
public class BpmTaskController { public class BpmTaskController {
@ -51,9 +60,16 @@ public class BpmTaskController {
@Resource @Resource
private DeptApi deptApi; private DeptApi deptApi;
@Resource
private BpmOAContractService bpmOAContractService;
@Resource
private BpmOAInvoiceService bpmOAInvoiceService;
/** /**
* add by yaojun 2024.1.3 * add by yaojun 2024.1.3
* 流程审核添加了抄送用户多用户的功能 * 流程审核添加了抄送用户多用户的功能
*
* @param pageReqVO * @param pageReqVO
* @return * @return
*/ */
@ -213,7 +229,7 @@ public class BpmTaskController {
if (CollUtil.isEmpty(userIds)) { if (CollUtil.isEmpty(userIds)) {
return success(userRespDTO); return success(userRespDTO);
} }
Map<Long, AdminUserRespDTO> userMap = userApi.getUserMap(userIds.stream().map(Long::parseLong).collect(Collectors.toList())); Map<Long, AdminUserRespDTO> userMap = userApi.getUserMap(userIds.stream().map(Long::parseLong).collect(Collectors.toList()));
return success(convertList(userIds, id -> userMap.get(Long.valueOf(id)))); return success(convertList(userIds, id -> userMap.get(Long.valueOf(id))));
} }
@ -236,9 +252,43 @@ public class BpmTaskController {
@GetMapping("getCurrentToDoProcessInstacePreAndNex-page") @GetMapping("getCurrentToDoProcessInstacePreAndNex-page")
@Operation(summary = "获取当前待处理流程实例带查询条件上一个流程和下一个流程的实例ID") @Operation(summary = "获取当前待处理流程实例带查询条件上一个流程和下一个流程的实例ID")
public CommonResult<Map<String,String>> getCurrentToDoProcessInstacePreAndNex(@RequestParam("processInstanceId") String processInstanceId,@Valid BpmTaskTodoPageReqVO pageVO) { public CommonResult<Map<String, String>> getCurrentToDoProcessInstacePreAndNex(@RequestParam("processInstanceId") String processInstanceId, @Valid BpmTaskTodoPageReqVO pageVO) {
return success(taskService.getCurrentToDoProcessInstacePreAndNex(getLoginUserId(), processInstanceId, pageVO)); return success(taskService.getCurrentToDoProcessInstacePreAndNex(getLoginUserId(), processInstanceId, pageVO));
} }
@GetMapping("/get-crm-task")
@Operation(summary = "获得CRM审批任务")
@Parameter(name = "processDefinitionId", description = "流程定义编号", required = true, example = "1024")
@Parameter(name = "isTodo", description = "是否待办", required = true)
public CommonResult<PageResult<Map<String, Object>>> getCrmTask(BpmCrmTaskPageReqVO pageReqVO) {
List<Map<String, Object>> respVOs = new ArrayList<>();
PageResult<BpmTaskExtDO> tasks = taskService.getCrmTask(getLoginUserId(), pageReqVO);
// 获取流程实例编号
List<String> processInstanceIds = tasks.getList().stream().map(BpmTaskExtDO::getProcessInstanceId).distinct().collect(Collectors.toList());
try {
ObjectMapper objectMapper = new ObjectMapper();
switch (pageReqVO.getProcessDefinitionId()) {
case "oa_contract":
List<BpmOAContractRespVO> contractRespVOS = bpmOAContractService.getListByProcessInstanceIds(processInstanceIds);
respVOs = contractRespVOS.stream()
.map(item -> objectMapper.convertValue(item, new TypeReference<Map<String, Object>>(){}))
.collect(Collectors.toList());
break;
case "oa_invoice":
List<BpmOAInvoiceRespVO> invoiceRespVOS = bpmOAInvoiceService.getListByProcessInstanceIds(processInstanceIds);
respVOs = invoiceRespVOS.stream()
.map(item -> objectMapper.convertValue(item, new TypeReference<Map<String, Object>>(){}))
.collect(Collectors.toList());
break;
}
return success(new PageResult<>(respVOs, tasks.getTotal()));
} catch (Exception e) {
log.error("json parse err,json:{}", processInstanceIds, e);
return success(null);
}
}
} }

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
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;
@Schema(description = "管理后台 - CRM流程任务的分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmCrmTaskPageReqVO extends PageParam {
@Schema(description = "流程定义编号", example = "oa_contract")
private String processDefinitionId;
@Schema(description = "是否是待办", example = "true")
private Boolean isTodo;
}

View File

@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.*; import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
@ -21,13 +22,12 @@ import java.util.List;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true) @ToString(callSuper = true)
//@Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class BpmOAContractDO extends BaseDO { public class BpmOAContractDO extends BaseDO {
/** /**
* 出差表单主键 * 合同审批表单主键
*/ */
@TableId @TableId
private Long id; private Long id;
@ -39,6 +39,11 @@ public class BpmOAContractDO extends BaseDO {
*/ */
private Long userId; private Long userId;
/**
* 合同类型 | 字典值 bpm_oa_contract_type
*/
private Integer contractType;
/** /**
* 合同名称 * 合同名称
*/ */
@ -47,7 +52,7 @@ public class BpmOAContractDO extends BaseDO {
/** /**
* 合同编号 * 合同编号
*/ */
private String contractNumber; private String contractNo;
/** /**
* 签约日期 * 签约日期
@ -55,28 +60,82 @@ public class BpmOAContractDO extends BaseDO {
private LocalDate signingDate; private LocalDate signingDate;
/** /**
* 我方公司名称 * 客户编号
*/ */
private String mCompanyName; private Long customerId;
/** /**
* 我方负责人 * 客户名称
*/ */
private String mHeadName; private String customerName;
/** /**
* 对方公司名称 * 客户签约人联系人ID
*/ */
private String oCompanyName; private Long contactsId;
/** /**
* 对方负责人 * 客户签约人(联系人)名称
*/ */
private String oHeadName; private String contactsName;
/** /**
* 用章的结果 * 签约公司编号
* */
private Long companyId;
/**
* 签约人用户编号
*/
private Long signatoryId;
/**
* 合同金额
*/
private BigDecimal contractMoney;
/**
* 已收/已付金额
*/
private BigDecimal returnMoney;
/**
* 已开票金额
*/
private BigDecimal invoiceMoney;
/**
* 产品总金额
*/
private BigDecimal totalPrice;
/**
* 整单优惠金额
*/
private BigDecimal discountRate;
/**
* 商机ID
*/
private Long businessId;
/**
* 合同有效期开始时间
*/
private LocalDate startDate;
/**
* 合同有效期结束时间
*/
private LocalDate endDate;
/**
* 合同状态 | 1已签约 2过期
*/
private Integer status;
/**
* 审批结果
* 枚举 {@link BpmProcessInstanceResultEnum} * 枚举 {@link BpmProcessInstanceResultEnum}
* 考虑到简单所以直接复用了 BpmProcessInstanceResultEnum 枚举也可以自己定义一个枚举哈 * 考虑到简单所以直接复用了 BpmProcessInstanceResultEnum 枚举也可以自己定义一个枚举哈
*/ */

View File

@ -44,9 +44,44 @@ public class BpmOAInvoiceDO extends BaseDO {
private LocalDate invoiceDate; private LocalDate invoiceDate;
/** /**
* 项目编号 * 关联合同业务编号
*/ */
private Long projectId; private Long contractId;
/**
* 关联合同业务的流程实例编号
*/
private String contractInstanceId;
/**
* 客户编号
*/
private Long customerId;
/**
* 开票主体
*/
private String invoiceBody;
/**
* 发票抬头
*/
private String invoiceName;
/**
* 抬头类型 | 1企业2个人3事业单位
*/
private Integer invoiceIssue;
/**
* 发票类型 | 1普票 2专票
*/
private Integer invoiceType;
/**
* 统一社会信用代码
*/
private String registerNo;
/** /**
* 开票金额 * 开票金额
@ -58,16 +93,6 @@ public class BpmOAInvoiceDO extends BaseDO {
*/ */
private String content; private String content;
/**
* 开票单位名称
*/
private String unitName;
/**
* 开票单位纳税人识别号
*/
private String unitTin;
/** /**
* 开票单位地址 * 开票单位地址
*/ */
@ -89,9 +114,14 @@ public class BpmOAInvoiceDO extends BaseDO {
private String unitBankNo; private String unitBankNo;
/** /**
* 关联合同业务流程编号 * 接收发票邮箱
*/ */
private String contractInstanceId; private String email;
/**
* 开票状态 | 0未开票 1已开票
*/
private Integer status;
/** /**
* 申请的结果 * 申请的结果

View File

@ -1,8 +1,18 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.oa; package cn.iocoder.yudao.module.bpm.dal.mysql.oa;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.ContractStatisticsRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
/** /**
* 合同审批 Mapper * 合同审批 Mapper
@ -12,4 +22,50 @@ import org.apache.ibatis.annotations.Mapper;
*/ */
@Mapper @Mapper
public interface BpmOAContractMapper extends BaseMapperX<BpmOAContractDO> { public interface BpmOAContractMapper extends BaseMapperX<BpmOAContractDO> {
default PageResult<BpmOAContractRespVO> selectPage(BpmOAContractPageReqVO pageReqVO,
Long userId) {
MPJLambdaWrapperX<BpmOAContractDO> query = new MPJLambdaWrapperX<>();
query.selectAll(BpmOAContractDO.class);
query.selectAs("u.nickname", BpmOAContractRespVO::getSignatoryName);
query.selectAs("u1.nickname", BpmOAContractRespVO::getCreateName);
query.selectAs("d.name", BpmOAContractRespVO::getCompanyName);
query.leftJoin("system_users u on u.id = t.signatory_id");
query.leftJoin("system_users u1 on u1.id = t.creator");
query.leftJoin("system_dept d on d.id = t.company_id");
query.eqIfPresent(BpmOAContractDO::getContractType, pageReqVO.getContractType());
query.likeIfPresent(BpmOAContractDO::getContractName, pageReqVO.getContractName());
query.likeIfPresent(BpmOAContractDO::getContractNo, pageReqVO.getContractNo());
query.likeIfPresent(BpmOAContractDO::getCustomerName, pageReqVO.getCustomerName());
query.eqIfPresent(BpmOAContractDO::getStatus, pageReqVO.getStatus());
query.eqIfPresent(BpmOAContractDO::getResult, pageReqVO.getResult());
query.apply(Objects.nonNull(pageReqVO.getSignatoryName()),"u.nickname Like CONCAT('%', {0}, '%')", pageReqVO.getSignatoryName());
query.apply(Objects.nonNull(pageReqVO.getCreateName()),"u1.nickname Like CONCAT('%', {0}, '%')", pageReqVO.getCreateName());
if ("my".equals(pageReqVO.getRelation())) {
query.eq(BpmOAContractDO::getUserId, userId);
}else if ("sub".equals(pageReqVO.getRelation())){
query.innerJoin("(" +
"\tSELECT DISTINCT\n" +
"\t\tu.id \n" +
"\tFROM\n" +
"\t\tsystem_users u\n" +
"\t\tINNER JOIN system_dept d ON d.leader_user_id = "+ userId +"\n" +
"\t\tINNER JOIN system_dept d1 ON d1.flag LIKE CONCAT( '%', d.id, '-' ) \n" +
"\t\tOR d1.flag LIKE CONCAT( '%-', d.id, '-%' ) \n" +
"\t\tOR d1.flag LIKE CONCAT( '-', d.id, '%' ) \n" +
"\tWHERE\n" +
"\t\tu.dept_id = d1.id \n" +
"\t\tAND u.STATUS = 0 \n" +
"\t) subordinate ON subordinate.id = t.user_id");
}
query.orderByDesc(BpmOAContractDO::getCreateTime);
return selectJoinPage(pageReqVO, BpmOAContractRespVO.class, query);
}
ContractStatisticsRespVO selectContractStatistics(@Param("userId") Long userId,
@Param("relation") String relation);
List<ContractStatisticsRespVO> selectContractStatisticsListByUserIds(@Param("userIds") List<Long> userIds,
@Param("createTime") LocalDateTime[] createTime);
} }

View File

@ -1,9 +1,61 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.oa; package cn.iocoder.yudao.module.bpm.dal.mysql.oa;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoicePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAInvoiceDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAInvoiceDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Objects;
@Mapper @Mapper
public interface BpmOAInvoiceMapper extends BaseMapperX<BpmOAInvoiceDO> { public interface BpmOAInvoiceMapper extends BaseMapperX<BpmOAInvoiceDO> {
default PageResult<BpmOAInvoiceRespVO> selectPage(BpmOAInvoicePageReqVO pageReqVO,
Long userId) {
MPJLambdaWrapperX<BpmOAInvoiceDO> query = new MPJLambdaWrapperX<>();
query.selectAll(BpmOAInvoiceDO.class);
query.selectAs(BpmOAContractDO::getContractName, BpmOAInvoiceRespVO::getContractName);
query.selectAs(BpmOAContractDO::getCustomerName, BpmOAInvoiceRespVO::getCustomerName);
query.leftJoin(BpmOAContractDO.class, "c", BpmOAContractDO::getId, BpmOAInvoiceDO::getContractId);
query.apply(Objects.nonNull(pageReqVO.getContractName()), "c.contract_name LIKE CONCAT('%', {0}, '%')", pageReqVO.getContractName());
query.apply(Objects.nonNull(pageReqVO.getCustomerName()), "c.customer_name LIKE CONCAT('%', {0}, '%')", pageReqVO.getCustomerName());
if ("my".equals(pageReqVO.getRelation())) {
query.eq(BpmOAContractDO::getUserId, userId);
}else if ("sub".equals(pageReqVO.getRelation())){
query.innerJoin("(" +
"\tSELECT DISTINCT\n" +
"\t\tu.id \n" +
"\tFROM\n" +
"\t\tsystem_users u\n" +
"\t\tINNER JOIN system_dept d ON d.leader_user_id = "+ userId +"\n" +
"\t\tINNER JOIN system_dept d1 ON d1.flag LIKE CONCAT( '%', d.id, '-' ) \n" +
"\t\tOR d1.flag LIKE CONCAT( '%-', d.id, '-%' ) \n" +
"\t\tOR d1.flag LIKE CONCAT( '-', d.id, '%' ) \n" +
"\tWHERE\n" +
"\t\tu.dept_id = d1.id \n" +
"\t\tAND u.STATUS = 0 \n" +
"\t) subordinate ON subordinate.id = t.user_id");
}
query.orderByDesc(BpmOAInvoiceDO::getCreateTime);
return selectJoinPage(pageReqVO, BpmOAInvoiceRespVO.class, query);
}
default List<BpmOAInvoiceRespVO> selectListByProcessInstanceIds(List<String> processInstanceIds) {
MPJLambdaWrapperX<BpmOAInvoiceDO> query = new MPJLambdaWrapperX<>();
query.selectAll(BpmOAInvoiceDO.class);
query.selectAs(BpmOAContractDO::getContractName, BpmOAInvoiceRespVO::getContractName);
query.selectAs(BpmOAContractDO::getCustomerName, BpmOAInvoiceRespVO::getCustomerName);
query.leftJoin(BpmOAContractDO.class, BpmOAContractDO::getId, BpmOAInvoiceDO::getContractId);
query.inIfPresent(BpmOAInvoiceDO::getProcessInstanceId, processInstanceIds);
return selectJoinList(BpmOAInvoiceRespVO.class, query);
}
} }

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.oa; package cn.iocoder.yudao.module.bpm.dal.mysql.oa;
import cn.iocoder.yudao.module.bpm.api.oa.vo.BpmOALeaveRpcVO; import cn.iocoder.yudao.module.bpm.api.oa.vo.leave.BpmOALeaveRpcVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.leave.BpmOALeavePageReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.leave.BpmOALeavePageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;

View File

@ -1,7 +1,9 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.task; package cn.iocoder.yudao.module.bpm.dal.mysql.task;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmCrmTaskPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
@ -11,6 +13,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -61,4 +64,15 @@ public interface BpmTaskExtMapper extends BaseMapperX<BpmTaskExtDO> {
List<Long> selectTodoTaskDept(@Param("reqVO") BpmTaskTodoPageReqVO pageVO, List<Long> selectTodoTaskDept(@Param("reqVO") BpmTaskTodoPageReqVO pageVO,
@Param("userId") Long userId); @Param("userId") Long userId);
default PageResult<BpmTaskExtDO> selectCrmPage(Long userId, BpmCrmTaskPageReqVO pageReqVO) {
List<Integer> approveOrReject = Arrays.asList(BpmProcessInstanceResultEnum.APPROVE.getResult(), BpmProcessInstanceResultEnum.REJECT.getResult());
return selectPage(pageReqVO, new LambdaQueryWrapperX<BpmTaskExtDO>()
.eq(BpmTaskExtDO::getAssigneeUserId, userId)
.likeIfPresent(BpmTaskExtDO::getProcessDefinitionId, pageReqVO.getProcessDefinitionId())
.eq(pageReqVO.getIsTodo(), BpmTaskExtDO::getResult, BpmProcessInstanceResultEnum.PROCESS.getResult())
.in(!pageReqVO.getIsTodo(), BpmTaskExtDO::getResult, approveOrReject));
}
} }

View File

@ -1,7 +1,12 @@
package cn.iocoder.yudao.module.bpm.framework.rpc.config; package cn.iocoder.yudao.module.bpm.framework.rpc.config;
import cn.iocoder.yudao.module.hrm.api.crmbusiness.BusinessApi;
import cn.iocoder.yudao.module.hrm.api.crmcontract.ContractApi;
import cn.iocoder.yudao.module.hrm.api.crmcustomer.CrmCustomerApi;
import cn.iocoder.yudao.module.infra.api.config.ConfigApi; import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
import cn.iocoder.yudao.module.infra.api.file.FileApi; import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.product.api.storeproduct.StoreProductApi;
import cn.iocoder.yudao.module.product.api.storeproductattrvalue.StoreProductAttrValueApi;
import cn.iocoder.yudao.module.system.api.assetreceive.AssetReceiveApi; import cn.iocoder.yudao.module.system.api.assetreceive.AssetReceiveApi;
import cn.iocoder.yudao.module.system.api.assets.AssetsApi; import cn.iocoder.yudao.module.system.api.assets.AssetsApi;
import cn.iocoder.yudao.module.system.api.assets.AssetsTypeApi; import cn.iocoder.yudao.module.system.api.assets.AssetsTypeApi;
@ -34,7 +39,8 @@ import org.springframework.context.annotation.Configuration;
@EnableFeignClients(clients = {FileApi.class, RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, NotifyMessageSendApi.class, @EnableFeignClients(clients = {FileApi.class, RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, NotifyMessageSendApi.class,
SubscribeMessageSendApi.class, SocialClientApi.class, UsersExtApi.class, AttendanceApi.class, BankApi.class, ConfigApi.class, PositionApi.class, SupplierApi.class, AssetsApi.class, SubscribeMessageSendApi.class, SocialClientApi.class, UsersExtApi.class, AttendanceApi.class, BankApi.class, ConfigApi.class, PositionApi.class, SupplierApi.class, AssetsApi.class,
AssetsTypeApi.class, AssetReceiveApi.class, AttendanceApi.class, AttendanceGroupApi.class, WorkOvertimeApi.class, HolidayApi.class, AssetsTypeApi.class, AssetReceiveApi.class, AttendanceApi.class, AttendanceGroupApi.class, WorkOvertimeApi.class, HolidayApi.class,
RentalOrderApi.class, RentalDepositRecordApi.class, ProjectApi.class, RentalItemsRecordApi.class,AdminOauthUserOtherInfoApi.class RentalOrderApi.class, RentalDepositRecordApi.class, ProjectApi.class, RentalItemsRecordApi.class,AdminOauthUserOtherInfoApi.class, StoreProductAttrValueApi.class, StoreProductApi.class,
ContractApi.class, BusinessApi.class, CrmCustomerApi.class
}) })
public class RpcConfiguration { public class RpcConfiguration {
} }

View File

@ -1,15 +1,22 @@
package cn.iocoder.yudao.module.bpm.service.oa; package cn.iocoder.yudao.module.bpm.service.oa;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.contract.BpmOAContractVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.ContractStatisticsRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO;
import javax.validation.Valid; import javax.validation.Valid;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
public interface BpmOAContractService { public interface BpmOAContractService {
/** /**
* 创建出差申请 * 创建合同申请
* *
* @param userId 用户编号 * @param userId 用户编号
* @param createReqVO 创建信息 * @param createReqVO 创建信息
@ -18,15 +25,28 @@ public interface BpmOAContractService {
Long createContract(Long userId, @Valid BpmOAContractCreateReqVO createReqVO); Long createContract(Long userId, @Valid BpmOAContractCreateReqVO createReqVO);
/** /**
* 更新出差申请的状态 * 更新合同
* @param updateReqVO 更新信息
*/
void updateContract(BpmOAContractDO updateReqVO);
/**
* 修改开票金额
* @param contractId 合同编号
* @param amount 开票金额
*/
void updateInvoiceMoney(Long contractId, BigDecimal amount);
/**
* 更新合同申请的状态
* *
* @param id 编号 * @param id 编号
* @param result 结果 * @param result 结果
*/ */
void updateContractResult(Long id, Integer result); void updateContractResult(String processInstanceId, Long id, Integer result);
/** /**
* 获得出差申请 * 获得合同申请
* *
* @param id 编号 * @param id 编号
* @return 出差申请 * @return 出差申请
@ -34,7 +54,7 @@ public interface BpmOAContractService {
BpmOAContractDO getContract(Long id); BpmOAContractDO getContract(Long id);
/** /**
* 获得指定出差申请 * 获得指定合同申请
* @param processInstanceId 流程实例编号 * @param processInstanceId 流程实例编号
* @return 出差申请 * @return 出差申请
*/ */
@ -46,4 +66,42 @@ public interface BpmOAContractService {
* @return 合同列表 * @return 合同列表
*/ */
List<BpmOAContractDO> getListByDeptId(List<Long> userIds); List<BpmOAContractDO> getListByDeptId(List<Long> userIds);
/**
* 获得合同分页
* @param pageReqVO 分页参数
* @return 分页列表
*/
PageResult<BpmOAContractRespVO> getContractPage(BpmOAContractPageReqVO pageReqVO);
/**
* 获得指定流程实例的合同申请列表
* @param processInstanceIds 流程实例编号集合
* @return 合同列表
*/
List<BpmOAContractRespVO> getListByProcessInstanceIds(List<String> processInstanceIds);
/**
* 获得合同统计信息
* @param userId 用户编号
* @param relation 查询类型
* @return 统计信息
*/
ContractStatisticsRespVO getContractStatistics(Long userId, String relation);
/**
* 获得指定用户的合同统计信息列表
*
* @param userIds 用户编号集合
* @param createTime 创建时间
* @return 合同统计信息列表
*/
List<ContractStatisticsRespVO> getContractStatisticsListByUserIds(List<Long> userIds, LocalDateTime[] createTime);
/**
* 获得合同列表
* @param respVO 参数列表
* @return 合同列表
*/
List<BpmOAContractDO> getContractList(BpmOAContractVO respVO);
} }

View File

@ -1,26 +1,62 @@
package cn.iocoder.yudao.module.bpm.service.oa; package cn.iocoder.yudao.module.bpm.service.oa;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; import cn.iocoder.yudao.framework.common.enums.ShopCommonEnum;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractCreateReqVO; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile; import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bpm.api.oa.vo.contract.BpmOAContractVO;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.*;
import cn.iocoder.yudao.module.bpm.convert.oa.BpmOAContractConvert; import cn.iocoder.yudao.module.bpm.convert.oa.BpmOAContractConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAContractMapper; import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAContractMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmHistoryProcessInstanceService; import cn.iocoder.yudao.module.bpm.service.task.BpmHistoryProcessInstanceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.hrm.api.crmcontract.ContractApi;
import cn.iocoder.yudao.module.hrm.api.crmcontract.dto.CrmContractProductDTO;
import cn.iocoder.yudao.module.hrm.api.crmcustomer.CrmCustomerApi;
import cn.iocoder.yudao.module.hrm.api.crmcustomer.dto.CrmCustomerDTO;
import cn.iocoder.yudao.module.product.api.storeproduct.StoreProductApi;
import cn.iocoder.yudao.module.product.api.storeproductattrvalue.StoreProductAttrValueApi;
import cn.iocoder.yudao.module.product.api.storeproductattrvalue.dto.StoreProductAttrValueApiDTO;
import cn.iocoder.yudao.module.product.api.storeproductattrvalue.vo.StoreProductAttrValueApiVO;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.runtime.ProcessInstance;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.HashMap; import java.math.BigDecimal;
import java.util.List; import java.math.RoundingMode;
import java.util.Map; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 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.framework.common.util.date.DateUtils.SECOND_MILLIS;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_CONTRACT_NOT_EXISTS; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_CONTRACT_NOT_EXISTS;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PRODUCT_STOCK_LESS;
/** /**
* OA 合同审批 Service 实现类 * OA 合同审批 Service 实现类
@ -30,6 +66,7 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_CONTRACT_N
*/ */
@Service @Service
@Validated @Validated
@Slf4j
public class BpmOAContractServiceImpl extends BpmOABaseService implements BpmOAContractService{ public class BpmOAContractServiceImpl extends BpmOABaseService implements BpmOAContractService{
/** /**
@ -41,28 +78,79 @@ public class BpmOAContractServiceImpl extends BpmOABaseService implements BpmOAC
private BpmOAContractMapper contractMapper; private BpmOAContractMapper contractMapper;
@Resource @Resource
private BpmProcessInstanceApi processInstanceApi; private BpmProcessInstanceService processInstanceService;
@Resource @Resource
private BpmHistoryProcessInstanceService historyProcessInstanceService; private BpmHistoryProcessInstanceService historyProcessInstanceService;
@Resource
private RedissonClient redissonClient;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private AdminUserApi userApi;
@Resource
private DeptApi deptApi;
@Resource
private StoreProductAttrValueApi storeProductAttrValueApi;
@Resource
private StoreProductApi storeProductApi;
@Resource
private ContractApi contractApi;
@Resource
private CrmCustomerApi customerApi;
@Override @Override
public Long createContract(Long userId, BpmOAContractCreateReqVO createReqVO) { public Long createContract(Long userId, BpmOAContractCreateReqVO createReqVO) {
// 新增合同DO
BpmOAContractDO contract = BpmOAContractConvert.INSTANCE.convert(createReqVO)
.setUserId(userId)
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult())
.setStatus(1);
//插入OA 出差申请 // 自动生成合同编号
BpmOAContractDO contract = BpmOAContractConvert.INSTANCE.convert(createReqVO).setUserId(userId) // 获取当前日期
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); String now = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
contract.setMCompanyName(createReqVO.getmCompanyName()); String key = "bpm_contract_no_" + now;
contract.setMHeadName(createReqVO.getmHeadName()); // 获取分布式锁
contract.setOCompanyName(createReqVO.getoCompanyName()); String LOCK_KEY = "lock:rental:order:create";
contract.setOHeadName(createReqVO.getoHeadName()); RLock lock = redissonClient.getLock(LOCK_KEY);
try {
lock.lock();
// redis 获取当天订单号
String no = stringRedisTemplate.opsForValue().get(key);
if (no != null) {
no = "HT" + now + String.format("%03d", Integer.parseInt(no) + 1);
// redis 缓存订单号
stringRedisTemplate.opsForValue().increment(key, 1);
}else {
no = "HT" + now + String.format("%03d", 1);
// redis 缓存订单号
stringRedisTemplate.opsForValue().set(key, "1", 1, TimeUnit.DAYS);
}
// 设置合同编号
contract.setContractNo(no);
} catch (Exception ex) {
log.error("[messageResend][执行异常]", ex);
} finally {
lock.unlock();
}
//插入OA 合同审批
contractMapper.insert(contract) ; contractMapper.insert(contract) ;
// 发起 BPM 流程 // 发起 BPM 流程
Map<String, Object> processInstanceVariables = new HashMap<>(); Map<String, Object> processInstanceVariables = new HashMap<>();
String processInstanceId = processInstanceApi.createProcessInstance(userId, String processInstanceId = processInstanceService.createProcessInstance(userId,
new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY) new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY)
.setVariables(processInstanceVariables).setBusinessKey(String.valueOf(contract.getId()))).getCheckedData(); .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(contract.getId())));
// 将工作流的编号更新到 OA 请假单中 // 将工作流的编号更新到 OA 请假单中
contractMapper.updateById(new BpmOAContractDO().setId(contract.getId()).setProcessInstanceId(processInstanceId)); contractMapper.updateById(new BpmOAContractDO().setId(contract.getId()).setProcessInstanceId(processInstanceId));
@ -78,20 +166,74 @@ public class BpmOAContractServiceImpl extends BpmOABaseService implements BpmOAC
if (fileItems != null && !fileItems.isEmpty()) { if (fileItems != null && !fileItems.isEmpty()) {
uploadBpmFileProcessInstanceId(processInstanceId,fileItems) ; uploadBpmFileProcessInstanceId(processInstanceId,fileItems) ;
} }
// 同步插入关联产品记录
createContractProductList(contract.getId(), createReqVO.getContractProducts());
return contract.getId(); return contract.getId();
} }
@Override @Override
public void updateContractResult(Long id, Integer result) { public void updateContract(BpmOAContractDO updateReqVO) {
validateLeaveExists(updateReqVO.getId());
validateLeaveExists(id); //更新合同
contractMapper.updateById(new BpmOAContractDO().setId(id).setResult(result)); contractMapper.updateById(updateReqVO);
} }
private void validateLeaveExists(Long id) { @Override
if (contractMapper.selectById(id) == null) { public void updateInvoiceMoney(Long contractId, BigDecimal amount) {
contractMapper.update(null, new LambdaUpdateWrapper<BpmOAContractDO>()
.setSql("invoice_money = invoice_money - " + amount)
.eq(BpmOAContractDO::getId, contractId));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateContractResult(String processInstanceId, Long id, Integer result) {
BpmOAContractDO contract = validateLeaveExists(id);
contractMapper.updateById(new BpmOAContractDO().setId(id).setResult(result));
//审核通过 最后节点
if (BpmProcessInstanceResultEnum.APPROVE.getResult().equals(result)) {
ProcessInstance instance = processInstanceService.getProcessInstance(processInstanceId);
if (instance.isEnded()) {
// 审批通过后更新客户成交信息
customerApi.updateCustomerPurchaseTotal(new CrmCustomerDTO()
.setId(contract.getCustomerId())
.setDealStatus(ShopCommonEnum.IS_STATUS_1.getValue())
.setDealTime(LocalDateTime.now())
.setPurchaseTotal(contract.getContractMoney()));
}
}
// -- 自己取消
// -- 审核拒绝
if (BpmProcessInstanceResultEnum.REJECT.getResult().equals(result)
|| BpmProcessInstanceResultEnum.CANCEL.getResult().equals(result)
|| BpmProcessInstanceResultEnum.BACK.getResult().equals(result)) {
// 获取合同商品详情
List<CrmContractProductDTO> productDTOS = contractApi.getProduct(id).getCheckedData();
for (CrmContractProductDTO product : productDTOS) {
// 回复商品库存
this.inProductStock(product.getNums(), product.getProductId(), product.getProductAttrUnique());
}
}
}
private BpmOAContractDO validateLeaveExists(Long id) {
BpmOAContractDO contractDO = contractMapper.selectById(id);
if (contractDO == null) {
throw exception(OA_CONTRACT_NOT_EXISTS); throw exception(OA_CONTRACT_NOT_EXISTS);
} }
return contractDO;
} }
@Override @Override
@ -115,6 +257,169 @@ public class BpmOAContractServiceImpl extends BpmOABaseService implements BpmOAC
return contractMapper.selectList(new LambdaQueryWrapperX<BpmOAContractDO>() return contractMapper.selectList(new LambdaQueryWrapperX<BpmOAContractDO>()
.eq(BpmOAContractDO::getResult, BpmProcessInstanceResultEnum.APPROVE.getResult()) .eq(BpmOAContractDO::getResult, BpmProcessInstanceResultEnum.APPROVE.getResult())
.inIfPresent(BpmOAContractDO::getUserId, userIds)); .inIfPresent(BpmOAContractDO::getUserId, userIds)
.eq(BpmOAContractDO::getContractType, 1)
.apply("return_money > invoice_money"));
}
@Override
public PageResult<BpmOAContractRespVO> getContractPage(BpmOAContractPageReqVO pageReqVO) {
return contractMapper.selectPage(pageReqVO, getLoginUserId());
}
@Override
public List<BpmOAContractRespVO> getListByProcessInstanceIds(List<String> processInstanceIds) {
List<BpmOAContractDO> bpmOAContractDOS = contractMapper.selectList(new LambdaQueryWrapperX<BpmOAContractDO>()
.inIfPresent(BpmOAContractDO::getProcessInstanceId, processInstanceIds));
List<BpmOAContractRespVO> respVOS = BeanUtils.toBean(bpmOAContractDOS, BpmOAContractRespVO.class);
// 获取用户和签约人信息
List<Long> userIds = bpmOAContractDOS.stream()
.flatMap(item -> Stream.of(item.getUserId(), item.getSignatoryId()))
.collect(Collectors.toList());
Map<Long, AdminUserRespDTO> userMap = userApi.getUserMap(userIds);
// 获取公司信息
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(bpmOAContractDOS, BpmOAContractDO::getCompanyId));
respVOS.forEach(respVO -> {
// 设置创建人签约人名称
respVO.setSignatoryName(userMap.get(respVO.getSignatoryId()) != null ? userMap.get(respVO.getSignatoryId()).getNickname() : null);
respVO.setCreateName(userMap.get(respVO.getUserId()) != null ? userMap.get(respVO.getUserId()).getNickname() : null);
// 设置公司名称
respVO.setCompanyName(deptMap.get(respVO.getCompanyId()) != null ? deptMap.get(respVO.getCompanyId()).getName() : null);
});
return respVOS;
}
@Override
public ContractStatisticsRespVO getContractStatistics(Long userId, String relation) {
ContractStatisticsRespVO respVO = contractMapper.selectContractStatistics(userId, relation);
// 设置数量金额百分比
respVO.setCountPercentage(calculatePercentageChange(respVO.getTodayAdditions(), respVO.getYesterdayAdditions()));
respVO.setMoneyPercentage(calculatePercentageChange(respVO.getTodayAddMoney(), respVO.getYesterdayAddMoney()));
return respVO;
}
@Override
public List<ContractStatisticsRespVO> getContractStatisticsListByUserIds(List<Long> userIds, LocalDateTime[] createTime) {
return contractMapper.selectContractStatisticsListByUserIds(userIds, createTime);
}
@Override
public List<BpmOAContractDO> getContractList(BpmOAContractVO respVO) {
Date[] dateArray = new Date[] { respVO.getStarTime(), respVO.getEndTime() };
return contractMapper.selectList(new LambdaQueryWrapperX<BpmOAContractDO>()
.inIfPresent(BpmOAContractDO::getUserId, respVO.getUserId())
.eqIfPresent(BpmOAContractDO::getContractType, respVO.getContractType())
.betweenIfPresent(BpmOAContractDO::getSigningDate, dateArray)
.eq(BpmOAContractDO::getResult, BpmProcessInstanceResultEnum.APPROVE.getResult()));
}
public static BigDecimal calculatePercentageChange(Object today, Object yesterday) {
// 转换为 BigDecimal 进行高精度计算
BigDecimal bigToday = new BigDecimal(today.toString());
BigDecimal bigYesterday = new BigDecimal(yesterday.toString());
if (bigYesterday.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ZERO;
}
// 计算百分比变化
BigDecimal difference = bigToday.subtract(bigYesterday); // a - b
BigDecimal percentageChange = difference.divide(bigYesterday, 2, RoundingMode.HALF_UP); // (a - b) / b
// 转为百分比并返回
return percentageChange.multiply(new BigDecimal(100)); // (result) * 100
}
private void createContractProductList(Long contractId, List<CrmContractProductVO> list) {
List<StoreProductAttrValueApiVO> storeProductAttrValueList = new ArrayList<>();
if (CollUtil.isNotEmpty(list)) {
List<Long> productIds = list.stream().map(CrmContractProductVO::getProductId).collect(Collectors.toList());
List<String> productAttrUnique = list.stream().map(CrmContractProductVO::getProductAttrUnique).collect(Collectors.toList());
storeProductAttrValueList = storeProductAttrValueApi.getStoreProductAttrValueList(new StoreProductAttrValueApiDTO()
.setProductIds(productIds)
.setUniques(productAttrUnique)).getCheckedData();
}
Map<String, List<StoreProductAttrValueApiVO>> map = storeProductAttrValueList.stream().collect(Collectors.groupingBy(a -> a.getProductId() + "_" + a.getUnique()));
list.forEach(o -> {
o.setContractId(contractId);
//库存处理
List<StoreProductAttrValueApiVO> storeProductAttrValueApiVOS = map.get(o.getProductId() + "_" + o.getProductAttrUnique());
int count = 0;
if (CollUtil.isNotEmpty(storeProductAttrValueApiVOS)) {
count = storeProductAttrValueApiVOS.stream().mapToInt(StoreProductAttrValueApiVO::getStock).sum();
}
if (NumberUtil.compare(count, o.getNums()) < 0) {
throw exception(new ErrorCode(202408250, "该商品ID" + o.getProductId() + "库存不足"));
}
// TODO: 2024/11/21 这里的库存扣件完后 - 如果审批不通过 库存退回
this.decProductStock(o.getNums(), o.getProductId(), o.getProductAttrUnique());
});
contractApi.createProduct(BeanUtils.toBean(list, CrmContractProductDTO.class));
}
/**
* 减少库存
*
* @param num 数量
* @param productId 产品ID
* @param unique sku唯一值
*/
private void decProductStock(int num, Long productId, String unique) {
String lockKey = productId + "_" + unique;
// -- 分布式锁
RLock lock = redissonClient.getLock(lockKey);
try {
lock.lock(120 * SECOND_MILLIS, TimeUnit.MILLISECONDS);
// 执行逻辑
int res = 0;
res = storeProductAttrValueApi.decStockIncSales(num, productId, unique).getCheckedData();
if (res == 0) {
throw exception(PRODUCT_STOCK_LESS);
}
int product = storeProductApi.decStockIncSales(num, productId).getCheckedData();
if (product == 0) {
throw exception(PRODUCT_STOCK_LESS);
}
} finally {
lock.unlock();
}
}
/**
* 加库存
*
* @param num 数量
* @param productId 产品ID
* @param unique sku唯一值
*/
private void inProductStock(int num, Long productId, String unique) {
String lockKey = productId + "_" + unique;
// -- 分布式锁
RLock lock = redissonClient.getLock(lockKey);
try {
lock.lock(120 * SECOND_MILLIS, TimeUnit.MILLISECONDS);
// 执行逻辑
storeProductAttrValueApi.inStockIncSales(num, productId, unique).getCheckedData();
storeProductApi.inStockIncSales(num, productId).getCheckedData();
} finally {
lock.unlock();
}
} }
} }

View File

@ -1,9 +1,13 @@
package cn.iocoder.yudao.module.bpm.service.oa; package cn.iocoder.yudao.module.bpm.service.oa;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoicePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAInvoiceDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAInvoiceDO;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.List;
/** /**
* 开票申请 Service 接口 * 开票申请 Service 接口
@ -44,4 +48,18 @@ public interface BpmOAInvoiceService {
* @return 开票申请 * @return 开票申请
*/ */
BpmOAInvoiceDO getByProcessInstanceId(String processInstanceId); BpmOAInvoiceDO getByProcessInstanceId(String processInstanceId);
/**
* 获得开票申请分页列表
* @param pageReqVO 分页参数
* @return 开票分页
*/
PageResult<BpmOAInvoiceRespVO> getInvoicePage(BpmOAInvoicePageReqVO pageReqVO);
/**
* 获得开票申请列表
* @param processInstanceIds 流程实例编号
* @return 开票申请列表
*/
List<BpmOAInvoiceRespVO> getListByProcessInstanceIds(List<String> processInstanceIds);
} }

View File

@ -1,25 +1,36 @@
package cn.iocoder.yudao.module.bpm.service.oa; package cn.iocoder.yudao.module.bpm.service.oa;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile; import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoicePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.invoice.BpmOAInvoiceRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAInvoiceDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAInvoiceDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAInvoiceMapper; import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAInvoiceMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmHistoryProcessInstanceService; import cn.iocoder.yudao.module.bpm.service.task.BpmHistoryProcessInstanceService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_INVOICE_NOT_EXISTS; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.SECOND_MILLIS;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
/** /**
* OA 开票申请 Service 实现类 * OA 开票申请 Service 实现类
@ -45,12 +56,23 @@ public class BpmOAInvoiceServiceImpl extends BpmOABaseService implements BpmOAIn
@Resource @Resource
private BpmHistoryProcessInstanceService historyProcessInstanceService; private BpmHistoryProcessInstanceService historyProcessInstanceService;
@Resource
private BpmOAContractService contractService;
@Resource
private RedissonClient redissonClient;
@Override @Override
@Transactional(rollbackFor = Exception.class)
public Long createInvoice(Long userId, BpmOAInvoiceCreateReqVO createReqVO) { public Long createInvoice(Long userId, BpmOAInvoiceCreateReqVO createReqVO) {
// 校验关联合同 是否还有开票余额
validateContractBalance(createReqVO.getContractId(), createReqVO.getAmount());
//插入OA 开票申请 //插入OA 开票申请
BpmOAInvoiceDO invoice = BeanUtils.toBean(createReqVO, BpmOAInvoiceDO.class) BpmOAInvoiceDO invoice = BeanUtils.toBean(createReqVO, BpmOAInvoiceDO.class)
.setUserId(userId) .setUserId(userId)
.setStatus(0)
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
invoiceMapper.insert(invoice) ; invoiceMapper.insert(invoice) ;
@ -73,20 +95,75 @@ public class BpmOAInvoiceServiceImpl extends BpmOABaseService implements BpmOAIn
if (fileItems != null && !fileItems.isEmpty()) { if (fileItems != null && !fileItems.isEmpty()) {
uploadBpmFileProcessInstanceId(processInstanceId,fileItems) ; uploadBpmFileProcessInstanceId(processInstanceId,fileItems) ;
} }
return invoice.getId(); return invoice.getId();
} }
private void validateContractBalance(Long contractId, BigDecimal amount) {
String lockKey = "bpm:invoice:create_" + contractId;
// -- 分布式锁
RLock lock = redissonClient.getLock(lockKey);
try {
lock.lock(120 * SECOND_MILLIS, TimeUnit.MILLISECONDS);
// 获取合同信息
BpmOAContractDO contractDO = contractService.getContract(contractId);
if (contractDO == null) {
throw exception(OA_CONTRACT_NOT_EXISTS);
}
// 判断是否还有开票余额
BigDecimal balance = contractDO.getReturnMoney().subtract(contractDO.getInvoiceMoney());
if (balance.compareTo(amount) <= 0) {
throw exception(OA_CONTRACT_INVOICE_AMOUNT_LACK);
}
// 余额足够时 更新合同的开票金额
contractService.updateContract(new BpmOAContractDO()
.setId(contractDO.getId())
.setInvoiceMoney(contractDO.getInvoiceMoney().add(amount)));
} finally {
lock.unlock();
}
}
@Override @Override
public void updateInvoiceResult(Long id, Integer result) { public void updateInvoiceResult(Long id, Integer result) {
validateLeaveExists(id); BpmOAInvoiceDO invoiceDO = validateLeaveExists(id);
invoiceMapper.updateById(new BpmOAInvoiceDO().setId(id).setResult(result)); invoiceMapper.updateById(new BpmOAInvoiceDO().setId(id).setResult(result));
// -- 自己取消
// -- 审核拒绝
if (BpmProcessInstanceResultEnum.REJECT.getResult().equals(result)
|| BpmProcessInstanceResultEnum.CANCEL.getResult().equals(result)
|| BpmProcessInstanceResultEnum.BACK.getResult().equals(result)) {
String lockKey = "bpm:invoice:create_" + invoiceDO.getContractId();
// -- 分布式锁
RLock lock = redissonClient.getLock(lockKey);
try {
lock.lock(120 * SECOND_MILLIS, TimeUnit.MILLISECONDS);
// 取消时回复合同之前的开票金额
contractService.updateInvoiceMoney(invoiceDO.getContractId(), invoiceDO.getAmount());
} finally {
lock.unlock();
}
}
} }
private void validateLeaveExists(Long id) { private BpmOAInvoiceDO validateLeaveExists(Long id) {
if (invoiceMapper.selectById(id) == null) { BpmOAInvoiceDO invoice = invoiceMapper.selectById(id);
if (invoice == null) {
throw exception(OA_INVOICE_NOT_EXISTS); throw exception(OA_INVOICE_NOT_EXISTS);
} }
return invoice;
} }
@Override @Override
@ -104,4 +181,16 @@ public class BpmOAInvoiceServiceImpl extends BpmOABaseService implements BpmOAIn
} }
return bpmOAInvoiceDOS.get(0); return bpmOAInvoiceDOS.get(0);
} }
@Override
public PageResult<BpmOAInvoiceRespVO> getInvoicePage(BpmOAInvoicePageReqVO pageReqVO) {
return invoiceMapper.selectPage(pageReqVO, getLoginUserId());
}
@Override
public List<BpmOAInvoiceRespVO> getListByProcessInstanceIds(List<String> processInstanceIds) {
return invoiceMapper.selectListByProcessInstanceIds(processInstanceIds);
}
} }

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.bpm.service.oa;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.BpmOALeaveRpcVO; import cn.iocoder.yudao.module.bpm.api.oa.vo.leave.BpmOALeaveRpcVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.leave.BpmOALeaveCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.leave.BpmOALeaveCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.leave.BpmOALeavePageReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.leave.BpmOALeavePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.leave.CalculateAndVerifyLeaveDTO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.leave.CalculateAndVerifyLeaveDTO;

View File

@ -11,7 +11,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile; import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.bpm.api.oa.vo.BpmOALeaveRpcVO; import cn.iocoder.yudao.module.bpm.api.oa.vo.leave.BpmOALeaveRpcVO;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.leave.BpmOALeaveCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.leave.BpmOALeaveCreateReqVO;

View File

@ -28,6 +28,6 @@ public class BpmOAContractResultListener extends BpmProcessInstanceResultEventLi
@Override @Override
protected void onEvent(BpmProcessInstanceResultEvent event) { protected void onEvent(BpmProcessInstanceResultEvent event) {
contractService.updateContractResult(Long.parseLong(event.getBusinessKey()), event.getResult()); contractService.updateContractResult(event.getId(), Long.parseLong(event.getBusinessKey()), event.getResult());
} }
} }

View File

@ -264,4 +264,12 @@ public interface BpmTaskService {
* @return 公司列表 * @return 公司列表
*/ */
List<Long> getTodoTaskDept(BpmTaskTodoPageReqVO pageVO, Long userId); List<Long> getTodoTaskDept(BpmTaskTodoPageReqVO pageVO, Long userId);
/**
* 获取CRM审批任务
* @param userId 当前登录用户编号
* @param pageReqVO 分页参数
* @return CRM审批任务
*/
PageResult<BpmTaskExtDO> getCrmTask(Long userId, BpmCrmTaskPageReqVO pageReqVO);
} }

View File

@ -1513,5 +1513,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
return taskExtMapper.selectTodoTaskDept(pageVO, userId); return taskExtMapper.selectTodoTaskDept(pageVO, userId);
} }
@Override
public PageResult<BpmTaskExtDO> getCrmTask(Long userId, BpmCrmTaskPageReqVO pageReqVO) {
return taskExtMapper.selectCrmPage(userId, pageReqVO);
}
} }

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAContractMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
<select id="selectContractStatistics" resultType="cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.ContractStatisticsRespVO">
SELECT
COUNT( CASE WHEN DATE( a.create_time ) = CURDATE() THEN 1 END ) AS todayAdditions,
COALESCE ( SUM( CASE WHEN DATE( a.create_time ) = CURDATE() THEN a.contract_money END ), 0 ) AS todayAddMoney,
COUNT( CASE WHEN DATE( a.create_time ) = CURDATE() - INTERVAL 1 DAY THEN 1 END ) AS yesterdayAdditions,
COALESCE ( SUM( CASE WHEN DATE( a.create_time ) = CURDATE() - INTERVAL 1 DAY THEN a.contract_money END ), 0 ) AS yesterdayAddMoney
FROM
bpm_oa_contract a
<if test="relation == 'sub'">
INNER JOIN (
SELECT DISTINCT
u.id
FROM
system_users u
INNER JOIN system_dept d ON d.leader_user_id = #{userId}
INNER JOIN system_dept d1 ON d1.flag LIKE CONCAT( '%', d.id, '-' )
OR d1.flag LIKE CONCAT( '%-', d.id, '-%' )
OR d1.flag LIKE CONCAT( '-', d.id, '%' )
WHERE
u.dept_id = d1.id
AND u.STATUS = 0
) subordinate ON subordinate.id = a.user_id
</if>
WHERE
a.result = 2
<if test="relation == 'my'">
AND a.user_id = #{userId}
</if>
</select>
<select id="selectContractStatisticsListByUserIds" resultType="cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.ContractStatisticsRespVO">
SELECT
user_id AS userId,
COUNT(1) AS count,
SUM(contract_money) AS money
FROM
bpm_oa_contract
WHERE
deleted = 0
AND result = 2
AND user_id IN
<foreach item="userId" collection="userIds" separator="," open="(" close=")" index="">
#{userId}
</foreach>
<if test="createTime != null and createTime.length > 0">
<if test="createTime[0] != null">
and create_time &gt;= #{createTime[0]}
</if>
<if test="createTime[1] != null">
and create_time &lt;= #{createTime[1]}
</if>
</if>
GROUP BY user_id
</select>
</mapper>

View File

@ -9,7 +9,7 @@
文档可见https://www.iocoder.cn/MyBatis/x-plugins/ 文档可见https://www.iocoder.cn/MyBatis/x-plugins/
--> -->
<select id="getLeaveListByTime" resultType="cn.iocoder.yudao.module.bpm.api.oa.vo.BpmOALeaveRpcVO"> <select id="getLeaveListByTime" resultType="cn.iocoder.yudao.module.bpm.api.oa.vo.leave.BpmOALeaveRpcVO">
SELECT SELECT
* *
FROM FROM

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.hrm.api.crmbusiness;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.hrm.api.crmbusiness.dto.CrmBusinessDTO;
import cn.iocoder.yudao.module.hrm.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 流程实例")
public interface BusinessApi {
String PREFIX = ApiConstants.PREFIX + "/crm/business";
@GetMapping(PREFIX + "/get")
@Operation(summary = "获得商机信息")
@Parameter(name = "id", description = "ID", required = true)
CommonResult<CrmBusinessDTO> getBusiness(@RequestParam("id") Long id);
}

View File

@ -0,0 +1,62 @@
package cn.iocoder.yudao.module.hrm.api.crmbusiness.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 商机新增/修改 Request VO")
@Data
public class CrmBusinessDTO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "20239")
private Long id;
@Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "16784")
private Long customerId;
@Schema(description = "客户")
private String customerName;
@Schema(description = "销售阶段", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "阶段推进时间")
private LocalDateTime statusTime;
@Schema(description = "0洽淡中1成交2失败3无效", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer isEnd;
@Schema(description = "下次联系时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime nextTime;
@Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
private String name;
@Schema(description = "商机金额", requiredMode = Schema.RequiredMode.REQUIRED)
private BigDecimal money;
@Schema(description = "产品总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "30725")
private BigDecimal totalPrice;
@Schema(description = "预计成交日期")
private LocalDateTime dealTime;
@Schema(description = "整单折扣", requiredMode = Schema.RequiredMode.REQUIRED)
private BigDecimal discountRate;
@Schema(description = "备注", example = "随便")
private String remark;
@Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "24317")
private Long ownerUserId;
@Schema(description = "添加时间")
private LocalDateTime createTime;
@Schema(description = "负责人")
private String ownUserName;
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.hrm.api.crmcontract;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.hrm.api.crmcontract.dto.CrmContractProductDTO;
import cn.iocoder.yudao.module.hrm.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 流程实例")
public interface ContractApi {
String PREFIX = ApiConstants.PREFIX + "/crm/contract";
@PostMapping(PREFIX + "/create")
@Operation(summary = "创建合同商品信息")
CommonResult<Boolean> createProduct(@RequestBody List<CrmContractProductDTO> createReqVO);
@GetMapping(PREFIX + "/get-product")
@Operation(summary = "获得合同商品信息")
@Parameter(name = "contractId", description = "合同ID", required = true)
CommonResult<List<CrmContractProductDTO>> getProduct(@RequestParam("contractId") Long contractId);
}

View File

@ -0,0 +1,38 @@
package cn.iocoder.yudao.module.hrm.api.crmcontract.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 合同产品关联 DTO")
@Data
public class CrmContractProductDTO {
@Schema(description = "合同编号")
private Long contractId;
@Schema(description = "产品编号")
private Long productId;
@Schema(description = "产品名称")
private String name;
@Schema(description = "产品属性")
private String productAttrUnique;
@Schema(description = "产品单价")
private BigDecimal price;
@Schema(description = "数量")
private Integer nums;
@Schema(description = "折扣")
private BigDecimal discount;
@Schema(description = "小计")
private BigDecimal subtotal;
@Schema(description = "备注")
private String remarks;
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.hrm.api.crmcustomer;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.hrm.api.crmcustomer.dto.CrmCustomerDTO;
import cn.iocoder.yudao.module.hrm.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 流程实例")
public interface CrmCustomerApi {
String PREFIX = ApiConstants.PREFIX + "/crm/customer";
@PostMapping(PREFIX + "/update")
@Operation(summary = "更新客户信息")
CommonResult<Boolean> updateCustomerPurchaseTotal(@RequestBody CrmCustomerDTO updateReqVO);
}

View File

@ -0,0 +1,113 @@
package cn.iocoder.yudao.module.hrm.api.crmcustomer.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 客户新增/修改 Request VO")
@Data
public class CrmCustomerDTO {
@Schema(description = "是否转移记录 1-是 2-否")
private Integer isTransfer;
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "12015")
private Long id;
@Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@NotEmpty(message = "客户名称不能为空")
private String name;
@Schema(description = "手机", requiredMode = Schema.RequiredMode.REQUIRED)
// @NotEmpty(message = "手机不能为空")
private String mobile;
@Schema(description = "电话", requiredMode = Schema.RequiredMode.REQUIRED)
//@NotEmpty(message = "电话不能为空")
private String telephone;
@Schema(description = "成交状态0未成交1成交", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
// @NotNull(message = "成交状态0未成交1成交不能为空")
private Integer dealStatus;
@Schema(description = "成交时间")
private LocalDateTime dealTime;
@Schema(description = "1锁定")
private Boolean isLock;
@Schema(description = "客户级别ID", requiredMode = Schema.RequiredMode.REQUIRED)
//@NotNull(message = "客户级别ID不能为空")
private Integer level;
@Schema(description = "客户行业ID", requiredMode = Schema.RequiredMode.REQUIRED)
//@NotNull(message = "客户行业ID不能为空")
private Integer industry;
@Schema(description = " 客户标签")
private String tags;
@Schema(description = "客户来源ID", requiredMode = Schema.RequiredMode.REQUIRED)
//@NotNull(message = "客户来源ID不能为空")
private Integer source;
@Schema(description = "备注", example = "你说的对")
private String remark;
@Schema(description = "省份id")
private Integer province;
@Schema(description = "城市ID")
private Integer city;
@Schema(description = "区域ID")
private Integer area;
/**
* 地址组合字符串
*/
private String addressStr;
@Schema(description = "详细地址", requiredMode = Schema.RequiredMode.REQUIRED)
//@NotEmpty(message = "详细地址不能为空")
private String detailAddress;
@Schema(description = "地理位置经度")
private Double lng;
@Schema(description = "地理位置维度")
private Double lat;
@Schema(description = "下次联系时间", requiredMode = Schema.RequiredMode.REQUIRED)
//@NotNull(message = "下次联系时间不能为空")
private LocalDateTime nextTime;
@Schema(description = "最后跟进时间")
private LocalDateTime followTime;
@Schema(description = "领取时间")
private LocalDateTime collectTime;
@Schema(description = "微信")
private String weixin;
@Schema(description = "QQ")
private String qq;
@Schema(description = "消费总额")
private BigDecimal purchaseTotal;
@Schema(description = "消费次数")
private Integer purchaseTimes;
@Schema(description = "跟进状态", example = "1")
private Integer followStatus;
}

View File

@ -119,6 +119,12 @@
<groupId>cn.iocoder.cloud</groupId> <groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-file</artifactId> <artifactId>yudao-spring-boot-starter-file</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-bpm-api</artifactId>
<version>2.0.0-jdk8-snapshot</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<finalName>${project.artifactId}</finalName> <finalName>${project.artifactId}</finalName>

View File

@ -155,6 +155,12 @@
<groupId>cn.iocoder.cloud</groupId> <groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-file</artifactId> <artifactId>yudao-spring-boot-starter-file</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-bpm-api</artifactId>
<version>2.0.0-jdk8-snapshot</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.crm.api.crmbusiness;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.crmbusiness.vo.CrmBusinessRespVO;
import cn.iocoder.yudao.module.crm.service.crmbusiness.CrmBusinessService;
import cn.iocoder.yudao.module.hrm.api.crmbusiness.BusinessApi;
import cn.iocoder.yudao.module.hrm.api.crmbusiness.dto.CrmBusinessDTO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
* Flowable CRM Api 实现类
*/
@RestController
@Validated
public class BusinessApiImpl implements BusinessApi {
@Resource
private CrmBusinessService businessService;
@Override
public CommonResult<CrmBusinessDTO> getBusiness(Long id) {
CrmBusinessRespVO businessDO = businessService.getBusiness(id);
return success(BeanUtils.toBean(businessDO, CrmBusinessDTO.class));
}
}

View File

@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.crm.api.crmcontract;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractProductDO;
import cn.iocoder.yudao.module.crm.service.crmcontract.CrmContractService;
import cn.iocoder.yudao.module.hrm.api.crmcontract.ContractApi;
import cn.iocoder.yudao.module.hrm.api.crmcontract.dto.CrmContractProductDTO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
* Flowable CRM Api 实现类
*/
@RestController
@Validated
public class ContractApiImpl implements ContractApi {
@Resource
private CrmContractService crmContractService;
@Override
public CommonResult<Boolean> createProduct(List<CrmContractProductDTO> createReqVO) {
crmContractService.createProduct(BeanUtils.toBean(createReqVO, CrmContractProductDO.class));
return success(true);
}
@Override
public CommonResult<List<CrmContractProductDTO>> getProduct(Long contractId) {
List<CrmContractProductDO> contractProductDOS = crmContractService.getContractProductListByContractId(contractId);
return success(BeanUtils.toBean(contractProductDOS, CrmContractProductDTO.class));
}
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.crm.api.crmcustomer;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo.CrmCustomerSaveReqVO;
import cn.iocoder.yudao.module.crm.service.crmcustomer.CrmCustomerService;
import cn.iocoder.yudao.module.hrm.api.crmcustomer.CrmCustomerApi;
import cn.iocoder.yudao.module.hrm.api.crmcustomer.dto.CrmCustomerDTO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
* Flowable CRM Api 实现类
*/
@RestController
@Validated
public class CrmCustomerApiImpl implements CrmCustomerApi {
@Resource
private CrmCustomerService customerService;
@Override
public CommonResult<Boolean> updateCustomerPurchaseTotal(CrmCustomerDTO updateReqVO) {
customerService.updateCustomerPurchaseTotal(BeanUtils.toBean(updateReqVO, CrmCustomerSaveReqVO.class));
return success(true);
}
}

View File

@ -66,9 +66,9 @@ public class CrmAchievementController {
@GetMapping("/depts") @GetMapping("/depts")
@Operation(summary = "获得部门业绩目标") @Operation(summary = "获得部门业绩目标")
@PreAuthorize("@ss.hasPermission('crm:achievement:query')") @PreAuthorize("@ss.hasPermission('crm:achievement:query')")
public CommonResult<List<DeptAchieveRespVO>> getDeptsAchievement(@RequestParam(name = "type", required = false) Integer type, public CommonResult<List<DeptAchieveRespVO>> getDeptsAchievement(@RequestParam(name = "config", required = false) Integer config,
@RequestParam(name = "year", required = false) Integer year) { @RequestParam(name = "year", required = false) Integer year) {
List<DeptAchieveRespVO> deptAchieveRespVOS = achievementService.getDeptAchieve(type, year); List<DeptAchieveRespVO> deptAchieveRespVOS = achievementService.getDeptAchieve(config, year);
return success(deptAchieveRespVOS); return success(deptAchieveRespVOS);
} }
@ -86,6 +86,4 @@ public class CrmAchievementController {
@RequestParam(name = "year", required = false) Integer year) { @RequestParam(name = "year", required = false) Integer year) {
return success(achievementService.getCount(type, year, month)); return success(achievementService.getCount(type, year, month));
} }
} }

View File

@ -60,8 +60,4 @@ public class CustomerController {
public CommonResult<PageResult<UserRecordVO>> getRecord(@Valid AchievementPageReqVO pageReqVO) { public CommonResult<PageResult<UserRecordVO>> getRecord(@Valid AchievementPageReqVO pageReqVO) {
return success(customerService.getRecord(pageReqVO)); return success(customerService.getRecord(pageReqVO));
} }
} }

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.crm.controller.admin.crmclues.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 线索统计 RespVO")
@Data
public class CrmCluesStatisticsRespVO {
@Schema(description = "负责人用户编号")
private Long ownerUserId;
@Schema(description = "线索总数量")
private Integer count;
@Schema(description = "转客数量")
private Integer successCount;
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 客户统计 Response VO")
@Data
public class CustomerStatisticRespVO {
@Schema(description = "负责人编号")
private Long ownerUserId;
@Schema(description = "客户数量")
private Integer count;
@Schema(description = "成交数量")
private Integer DealsCount;
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 跟进统计 Response VO")
@Data
public class RecordStatisticsRespVO {
@Schema(description = "跟进人")
private String creator;
@Schema(description = "跟进总数量")
private Integer count;
@Schema(description = "客户跟进数量")
private Integer customerCount;
@Schema(description = "商机跟进数量")
private Integer businessCount;
@Schema(description = "线索跟进数量")
private Integer cluesCount;
}

View File

@ -3,10 +3,15 @@ package cn.iocoder.yudao.module.crm.dal.mysql.crmachievement;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.crmachievement.vo.DeptAchieveSaveVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmachievement.CrmAchievementDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmachievement.CrmAchievementDO;
import cn.iocoder.yudao.module.crm.controller.admin.crmachievement.vo.CrmAchievementPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.crmachievement.vo.CrmAchievementPageReqVO;
import cn.iocoder.yudao.module.hrm.enums.FlowStepEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/** /**
* 业绩目标 Mapper * 业绩目标 Mapper
* *
@ -39,4 +44,21 @@ public interface CrmAchievementMapper extends BaseMapperX<CrmAchievementDO> {
.orderByDesc(CrmAchievementDO::getId)); .orderByDesc(CrmAchievementDO::getId));
} }
default List<CrmAchievementDO> selectAchievementList(Integer type, Integer year, List<Long> typeIds) {
return selectList(new LambdaQueryWrapper<CrmAchievementDO>()
.eq(CrmAchievementDO::getType, FlowStepEnum.TYPE_3.getValue())
.eq(CrmAchievementDO::getConfig, type)
.in(CrmAchievementDO::getTypeId, typeIds)
.eq(CrmAchievementDO::getYear, year));
}
default List<CrmAchievementDO> selectAchievementList(DeptAchieveSaveVO createReqVO, List<Long> typeIds) {
return selectList(new LambdaQueryWrapperX<CrmAchievementDO>()
.eq(CrmAchievementDO::getType, createReqVO.getAchievementRespVO().getType())
.eq(CrmAchievementDO::getConfig, createReqVO.getAchievementRespVO().getConfig())
.eq(CrmAchievementDO::getYear, createReqVO.getAchievementRespVO().getYear())
.inIfPresent(CrmAchievementDO::getTypeId, typeIds));
}
} }

View File

@ -3,11 +3,13 @@ package cn.iocoder.yudao.module.crm.dal.mysql.crmclues;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.crm.controller.admin.crmclues.vo.CrmCluesPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.crmclues.vo.CrmCluesPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmclues.vo.CrmCluesRespVO; import cn.iocoder.yudao.module.crm.controller.admin.crmclues.vo.CrmCluesRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmclues.vo.CrmCluesStatisticsRespVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmclues.CrmCluesDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmclues.CrmCluesDO;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
@ -21,4 +23,7 @@ public interface CrmCluesMapper extends BaseMapperX<CrmCluesDO> {
IPage<CrmCluesRespVO> selectPageList(@Param("page") IPage mpPage, @Param("dto") CrmCluesPageReqVO pageReqVO, @Param("ids") List<Long> ids); IPage<CrmCluesRespVO> selectPageList(@Param("page") IPage mpPage, @Param("dto") CrmCluesPageReqVO pageReqVO, @Param("ids") List<Long> ids);
IPage<CrmCluesRespVO> selectPageList2(@Param("page") IPage mpPage, @Param("dto") CrmCluesPageReqVO pageReqVO); IPage<CrmCluesRespVO> selectPageList2(@Param("page") IPage mpPage, @Param("dto") CrmCluesPageReqVO pageReqVO);
List<CrmCluesStatisticsRespVO> selectStatisticsByUserIds(@Param("ownerUserIds") List<Long> ownerUserIds,
@Param("createTime") LocalDateTime[] createTime);
} }

View File

@ -4,17 +4,13 @@ package cn.iocoder.yudao.module.crm.dal.mysql.crmcontract;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.ContractVO; import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.ContractVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractRespVO; import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractRespVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomer.CrmCustomerDO;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List; import java.util.List;
@ -56,9 +52,6 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
.orderByDesc(CrmContractDO::getId)); .orderByDesc(CrmContractDO::getId));
} }
@Select("select any_value(t.id) as id,COUNT(t.id) AS count,SUM(t.money) AS money,t1.nickname AS nickname from crm_contract t " +
"LEFT JOIN system_users t1 ON (t1.id = t.order_admin_id) " +
"where t.deleted=0 GROUP BY t.order_admin_id ORDER BY any_value(money) DESC limit 10")
List<ContractVO> selectContractTop(); List<ContractVO> selectContractTop();
} }

View File

@ -5,12 +5,13 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo.CrmCustomerPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo.CrmCustomerPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo.CrmCustomerRespVO; import cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo.CrmCustomerRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo.CustomerStatisticRespVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomer.CrmCustomerDO;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
@ -41,4 +42,6 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
} }
IPage<CrmCustomerRespVO> selectPageList(@Param("page") IPage page, @Param("dto") CrmCustomerPageReqVO pageReqVO, @Param("ids") List<Long> ids); IPage<CrmCustomerRespVO> selectPageList(@Param("page") IPage page, @Param("dto") CrmCustomerPageReqVO pageReqVO, @Param("ids") List<Long> ids);
List<CustomerStatisticRespVO> selectStatistic(@Param("userIds") List<Long> userIds, @Param("createTime") LocalDateTime[] createTime);
} }

View File

@ -4,9 +4,12 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.CrmRecordPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.CrmRecordPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.RecordStatisticsRespVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmrecord.CrmRecordDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmrecord.CrmRecordDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
@ -28,4 +31,5 @@ public interface CrmRecordMapper extends BaseMapperX<CrmRecordDO> {
.orderByDesc(CrmRecordDO::getId)); .orderByDesc(CrmRecordDO::getId));
} }
List<RecordStatisticsRespVO> selectStatistics(@Param("userIds") List<Long> userIds, @Param("createTime") LocalDateTime[] createTime);
} }

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.framework.rpc.config; package cn.iocoder.yudao.module.crm.framework.rpc.config;
import cn.iocoder.yudao.module.bpm.api.oa.BpmOAContractApi;
import cn.iocoder.yudao.module.product.api.storeproduct.StoreProductApi; import cn.iocoder.yudao.module.product.api.storeproduct.StoreProductApi;
import cn.iocoder.yudao.module.product.api.storeproductattrvalue.StoreProductAttrValueApi; import cn.iocoder.yudao.module.product.api.storeproductattrvalue.StoreProductAttrValueApi;
import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.DeptApi;
@ -12,6 +13,7 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = {DeptApi.class, DictDataApi.class, AdminUserApi.class, StoreProductApi.class, StoreProductAttrValueApi.class, SmsSendApi.class, MailSendApi.class, NoticeApi.class}) @EnableFeignClients(clients = {DeptApi.class, DictDataApi.class, AdminUserApi.class, StoreProductApi.class, StoreProductAttrValueApi.class, SmsSendApi.class, MailSendApi.class, NoticeApi.class,
BpmOAContractApi.class})
public class RpcConfiguration { public class RpcConfiguration {
} }

View File

@ -56,11 +56,11 @@ public interface CrmAchievementService {
/** /**
* 获取部门业绩 * 获取部门业绩
* *
* @param type * @param config
* @param year * @param year
* @return * @return
*/ */
List<DeptAchieveRespVO> getDeptAchieve(Integer type, Integer year); List<DeptAchieveRespVO> getDeptAchieve(Integer config, Integer year);
/** /**
* 获取业绩完成度 * 获取业绩完成度

View File

@ -1,31 +1,32 @@
package cn.iocoder.yudao.module.crm.service.crmachievement; package cn.iocoder.yudao.module.crm.service.crmachievement;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.DeptTypeEnum;
import cn.iocoder.yudao.framework.common.enums.ShopCommonEnum; import cn.iocoder.yudao.framework.common.enums.ShopCommonEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.oa.BpmOAContractApi;
import cn.iocoder.yudao.module.bpm.api.oa.vo.contract.BpmOAContractDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.contract.BpmOAContractVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmachievement.vo.*; import cn.iocoder.yudao.module.crm.controller.admin.crmachievement.vo.*;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmachievement.CrmAchievementDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmachievement.CrmAchievementDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontractreceivables.CrmContractReceivablesDO;
import cn.iocoder.yudao.module.crm.dal.mysql.crmachievement.CrmAchievementMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crmachievement.CrmAchievementMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcontract.CrmContractMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crmcontract.CrmContractMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcontractreceivables.CrmContractReceivablesMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crmcontractreceivables.CrmContractReceivablesMapper;
import cn.iocoder.yudao.module.hrm.enums.ContractStatusEnum;
import cn.iocoder.yudao.module.hrm.enums.FlowStepEnum; import cn.iocoder.yudao.module.hrm.enums.FlowStepEnum;
import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptApiDTO; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptApiVO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserApiDTO; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserApiDTO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserApiVO; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserApiVO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserPageApiDTO; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserPageApiDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -35,6 +36,8 @@ import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.ACHIEVEMENT_NOT_EXISTS; import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.ACHIEVEMENT_NOT_EXISTS;
@ -50,7 +53,7 @@ public class CrmAchievementServiceImpl implements CrmAchievementService {
@Resource @Resource
private CrmAchievementMapper achievementMapper; private CrmAchievementMapper achievementMapper;
@Resource @Resource
private DeptApi deptService; private DeptApi deptApi;
@Resource @Resource
private CrmContractMapper contractMapper; private CrmContractMapper contractMapper;
@Resource @Resource
@ -58,28 +61,53 @@ public class CrmAchievementServiceImpl implements CrmAchievementService {
@Resource @Resource
private AdminUserApi adminUserApi; private AdminUserApi adminUserApi;
@Resource
private BpmOAContractApi contractApi;
@Override @Override
public void createAchievement(DeptAchieveSaveVO createReqVO) { public void createAchievement(DeptAchieveSaveVO createReqVO) {
//计算年目标
List<BigDecimal> bigDecimals = Arrays.asList(
createReqVO.getAchievementRespVO().getJanuary(), createReqVO.getAchievementRespVO().getFebruary(),
createReqVO.getAchievementRespVO().getMarch(), createReqVO.getAchievementRespVO().getApril(),
createReqVO.getAchievementRespVO().getMay(), createReqVO.getAchievementRespVO().getJune(),
createReqVO.getAchievementRespVO().getJuly(), createReqVO.getAchievementRespVO().getAugust(),
createReqVO.getAchievementRespVO().getSeptember(), createReqVO.getAchievementRespVO().getOctober(),
createReqVO.getAchievementRespVO().getNovember(), createReqVO.getAchievementRespVO().getDecember()
);
BigDecimal yearTarget = bigDecimals.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
createReqVO.getAchievementRespVO().setYeartarget(yearTarget);
if (createReqVO.getId() == 0) { if (createReqVO.getId() == 0) {
List<CrmAchievementDO> achievements;
//批量 //批量
if (FlowStepEnum.TYPE_3.getValue().equals(createReqVO.getAchievementRespVO().getType())) { if (FlowStepEnum.TYPE_3.getValue().equals(createReqVO.getAchievementRespVO().getType())) {
List<DeptApiVO> list = deptService.getDeptList( // 获取所有营销部门列表
new DeptApiDTO().setStatus(CommonStatusEnum.ENABLE.getStatus())).getCheckedData(); List<DeptRespDTO> list = deptApi.getDeptListByType(DeptTypeEnum.SALE_DEPT.getValue()).getCheckedData();
list.forEach(dept -> { // 获取 插入/更新信息列表
createReqVO.setId(dept.getId()); achievements = list.stream().map(item -> {
createReqVO.setName(dept.getName()); CrmAchievementDO achievement = BeanUtils.toBean(createReqVO.getAchievementRespVO(), CrmAchievementDO.class);
saveAchievement(createReqVO); achievement.setTypeId(item.getId());
}); achievement.setName(item.getName());
return achievement;
}).collect(Collectors.toList());
} else { } else {
List<AdminUserApiVO> list = adminUserApi.getUserList(new AdminUserApiDTO().setStatus(CommonStatusEnum.ENABLE.getStatus())).getCheckedData(); List<AdminUserRespDTO> list = adminUserApi.getUserListAll(1, null, CommonStatusEnum.ENABLE.getStatus(), "sale").getCheckedData();
list.forEach(user -> { // 获取 插入/更新信息列表
createReqVO.setId(user.getId()); achievements = list.stream().map(item -> {
createReqVO.setName(user.getNickname()); CrmAchievementDO achievement = BeanUtils.toBean(createReqVO.getAchievementRespVO(), CrmAchievementDO.class);
saveAchievement(createReqVO); achievement.setTypeId(item.getId());
}); achievement.setName(item.getNickname());
return achievement;
}).collect(Collectors.toList());
} }
// 批量插入操作
saveAchievementBatch(createReqVO, achievements);
} else { } else {
//单个操作 //单个操作
if (FlowStepEnum.TYPE_3.getValue().equals(createReqVO.getAchievementRespVO().getType())) { if (FlowStepEnum.TYPE_3.getValue().equals(createReqVO.getAchievementRespVO().getType())) {
@ -89,8 +117,6 @@ public class CrmAchievementServiceImpl implements CrmAchievementService {
} }
saveAchievement(createReqVO); saveAchievement(createReqVO);
} }
} }
/** /**
@ -103,29 +129,48 @@ public class CrmAchievementServiceImpl implements CrmAchievementService {
CrmAchievementDO achievement = BeanUtils.toBean(createReqVO.getAchievementRespVO(), CrmAchievementDO.class); CrmAchievementDO achievement = BeanUtils.toBean(createReqVO.getAchievementRespVO(), CrmAchievementDO.class);
achievement.setTypeId(createReqVO.getId()); achievement.setTypeId(createReqVO.getId());
achievement.setName(createReqVO.getName()); achievement.setName(createReqVO.getName());
LambdaQueryWrapper<CrmAchievementDO> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapperX<CrmAchievementDO> wrapper = new LambdaQueryWrapperX<>();
wrapper.eq(CrmAchievementDO::getType, achievement.getType()) wrapper.eq(CrmAchievementDO::getType, achievement.getType())
.eq(CrmAchievementDO::getTypeId, achievement.getTypeId()) .eq(CrmAchievementDO::getTypeId, achievement.getTypeId())
.eq(CrmAchievementDO::getConfig, achievement.getConfig()) .eq(CrmAchievementDO::getConfig, achievement.getConfig())
.eq(CrmAchievementDO::getYear, achievement.getYear()); .eq(CrmAchievementDO::getYear, achievement.getYear());
CrmAchievementDO achievementOld = achievementMapper.selectOne(wrapper); CrmAchievementDO achievementOld = achievementMapper.selectOne(wrapper);
if (achievementOld == null) { if (achievementOld == null) {
achievementMapper.insert(achievement); achievementMapper.insert(achievement);
} else { } else {
achievement.setId(achievementOld.getId()); achievement.setId(achievementOld.getId());
achievementMapper.updateById(achievement); achievementMapper.updateById(achievement);
} }
}
//更新年目标 /**
List<BigDecimal> bigDecimals = Arrays.asList( * 批量插入操作
achievement.getJanuary(), achievement.getFebruary(), achievement.getMarch(), achievement.getApril(), * @param createReqVO 更新信息
achievement.getMay(), achievement.getJune(), achievement.getJuly(), achievement.getAugust(), * @param achievements 更新信息
achievement.getSeptember(), achievement.getOctober(), achievement.getNovember(), achievement.getDecember() */
); private void saveAchievementBatch(DeptAchieveSaveVO createReqVO, List<CrmAchievementDO> achievements) {
BigDecimal yearTarget = bigDecimals.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add); if (CollectionUtil.isNotEmpty(achievements)) {
CrmAchievementDO achievementDO = CrmAchievementDO.builder().yeartarget(yearTarget).id(achievement.getId()).build();
achievementMapper.updateById(achievementDO); List<Long> typeIds = convertList(achievements, CrmAchievementDO::getId);
// 判断业绩数据是否已存在
List<CrmAchievementDO> achievementOld = achievementMapper.selectAchievementList(createReqVO, typeIds);
if (CollectionUtil.isEmpty(achievementOld)) {
achievementMapper.insertBatch(achievements);
}else {
// 获取之前业绩数据Map
Map<Long, CrmAchievementDO> oldMap = convertMap(achievementOld, CrmAchievementDO::getTypeId);
// 设置ID
achievements.forEach(item -> {
item.setId(oldMap.get(item.getTypeId()).getId());
});
achievementMapper.updateBatch(achievements);
}
}
} }
@Override @Override
@ -158,62 +203,63 @@ public class CrmAchievementServiceImpl implements CrmAchievementService {
@Override @Override
public PageResult<UserAchieveRespVO> getAchievementPage(CrmAchievementPageReqVO pageReqVO) { public PageResult<UserAchieveRespVO> getAchievementPage(CrmAchievementPageReqVO pageReqVO) {
// 设置用户查询条件
AdminUserPageApiDTO userPageReqVO = new AdminUserPageApiDTO(); AdminUserPageApiDTO userPageReqVO = new AdminUserPageApiDTO();
userPageReqVO.setPageNo(pageReqVO.getPageNo()); userPageReqVO.setPageNo(pageReqVO.getPageNo());
userPageReqVO.setPageSize(pageReqVO.getPageSize()); userPageReqVO.setPageSize(pageReqVO.getPageSize());
PageResult<AdminUserApiVO> pageResult = adminUserApi.getUserPage(userPageReqVO).getCheckedData(); userPageReqVO.setNickname(pageReqVO.getName());
PageResult<UserAchieveRespVO> pageResult1 = BeanUtils.toBean(pageResult, UserAchieveRespVO.class); userPageReqVO.setRoleCodes("sale"); // 设置查询用户的角色类型
List<Long> typeIds = new ArrayList<>();
for (UserAchieveRespVO vo : pageResult1.getList()) { // 查询用户分页列表
typeIds.add(vo.getId()); PageResult<AdminUserApiVO> pageResult = adminUserApi.getUserPageByRole(userPageReqVO).getCheckedData();
PageResult<UserAchieveRespVO> userPage = BeanUtils.toBean(pageResult, UserAchieveRespVO.class);
if (CollectionUtil.isNotEmpty(pageResult.getList())) {
// 查询用户的 业绩信息
List<Long> typeIds = convertList(userPage.getList(), UserAchieveRespVO::getId);
List<CrmAchievementDO> crmAchievementDOS = achievementMapper.selectList(new LambdaQueryWrapperX<CrmAchievementDO>()
.eq(CrmAchievementDO::getType, FlowStepEnum.TYPE_2.getValue())
.eq(CrmAchievementDO::getConfig, pageReqVO.getConfig())
.in(CrmAchievementDO::getTypeId, typeIds)
.eq(CrmAchievementDO::getYear, pageReqVO.getYear())
);
// 获得业绩信息Map
Map<Long, CrmAchievementDO> achievementMap = convertMap(crmAchievementDOS, CrmAchievementDO::getTypeId);
userPage.getList().forEach(item -> {
CrmAchievementDO crmAchievementDO = achievementMap.get(item.getId());
if (crmAchievementDO == null) {
crmAchievementDO = CrmAchievementDO.builder()
.january(BigDecimal.ZERO).february(BigDecimal.ZERO).march(BigDecimal.ZERO)
.april(BigDecimal.ZERO).may(BigDecimal.ZERO).june(BigDecimal.ZERO)
.july(BigDecimal.ZERO).august(BigDecimal.ZERO).september(BigDecimal.ZERO)
.october(BigDecimal.ZERO).november(BigDecimal.ZERO).december(BigDecimal.ZERO)
.yeartarget(BigDecimal.ZERO)
.build();
}
item.setAchievementRespVO(BeanUtils.toBean(crmAchievementDO, CrmAchievementRespVO.class));
});
} }
typeIds = typeIds.stream().distinct().collect(Collectors.toList());
List<CrmAchievementDO> crmAchievementDOS = achievementMapper.selectList(new LambdaQueryWrapper<CrmAchievementDO>()
.eq(CrmAchievementDO::getType, FlowStepEnum.TYPE_2.getValue())
.eq(CrmAchievementDO::getConfig, pageReqVO.getConfig())
.in(CrmAchievementDO::getTypeId, typeIds)
.eq(CrmAchievementDO::getYear, pageReqVO.getYear())
);
Map<Long, List<CrmAchievementDO>> map = crmAchievementDOS.stream().collect(Collectors.groupingBy(CrmAchievementDO::getTypeId)); return userPage;
pageResult1.getList().forEach(v -> {
List<CrmAchievementDO> doList = map.get(v.getId());
CrmAchievementDO crmAchievementDO = null;
if (CollUtil.isNotEmpty(doList)) {
crmAchievementDO = doList.get(0);
}
if (crmAchievementDO == null) {
crmAchievementDO = CrmAchievementDO.builder()
.january(BigDecimal.ZERO).february(BigDecimal.ZERO).march(BigDecimal.ZERO)
.april(BigDecimal.ZERO).may(BigDecimal.ZERO).june(BigDecimal.ZERO)
.july(BigDecimal.ZERO).august(BigDecimal.ZERO).september(BigDecimal.ZERO)
.october(BigDecimal.ZERO).november(BigDecimal.ZERO).december(BigDecimal.ZERO)
.yeartarget(BigDecimal.ZERO)
.build();
}
v.setAchievementRespVO(BeanUtils.toBean(crmAchievementDO, CrmAchievementRespVO.class));
});
return pageResult1;
} }
@Override @Override
public List<DeptAchieveRespVO> getDeptAchieve(Integer type, Integer year) { public List<DeptAchieveRespVO> getDeptAchieve(Integer config, Integer year) {
List<DeptApiVO> list = deptService.getDeptList(
new DeptApiDTO().setStatus(CommonStatusEnum.ENABLE.getStatus())).getCheckedData(); // 获取销售部门列表
List<DeptAchieveRespVO> achieveRespVOS = BeanUtils.toBean(list, DeptAchieveRespVO.class); List<DeptRespDTO> list = deptApi.getDeptListByType(DeptTypeEnum.SALE_DEPT.getValue()).getCheckedData();
List<Long> typeIds = new ArrayList<>(); // 获取部门编号列表
for (DeptAchieveRespVO vo : achieveRespVOS) { List<Long> typeIds = list.stream().map(DeptRespDTO::getId).distinct().collect(Collectors.toList());
typeIds.add(vo.getId());
} // 获取部门业绩列表
typeIds = typeIds.stream().distinct().collect(Collectors.toList()); List<CrmAchievementDO> crmAchievementDOS = achievementMapper.selectAchievementList(config, year, typeIds);
List<CrmAchievementDO> crmAchievementDOS = achievementMapper.selectList(new LambdaQueryWrapper<CrmAchievementDO>()
.eq(CrmAchievementDO::getType, FlowStepEnum.TYPE_3.getValue())
.eq(CrmAchievementDO::getConfig, type)
.in(CrmAchievementDO::getTypeId, typeIds)
.eq(CrmAchievementDO::getYear, year)
);
Map<Long, List<CrmAchievementDO>> map = crmAchievementDOS.stream().collect(Collectors.groupingBy(CrmAchievementDO::getTypeId)); Map<Long, List<CrmAchievementDO>> map = crmAchievementDOS.stream().collect(Collectors.groupingBy(CrmAchievementDO::getTypeId));
List<DeptAchieveRespVO> achieveRespVOS = BeanUtils.toBean(list, DeptAchieveRespVO.class);
achieveRespVOS.forEach(vo -> { achieveRespVOS.forEach(vo -> {
List<CrmAchievementDO> doList = map.get(vo.getId()); List<CrmAchievementDO> doList = map.get(vo.getId());
CrmAchievementDO crmAchievementDO = null; CrmAchievementDO crmAchievementDO = null;
@ -236,8 +282,11 @@ public class CrmAchievementServiceImpl implements CrmAchievementService {
@Override @Override
public AchieveCountRespVO getCount(Integer type, Integer year, Integer month) { public AchieveCountRespVO getCount(Integer type, Integer year, Integer month) {
// 获取当前登录用户编号
List<Long> userIds = new ArrayList<>(); List<Long> userIds = new ArrayList<>();
Long typeId = SecurityFrameworkUtils.getLoginUserId(); Long typeId = SecurityFrameworkUtils.getLoginUserId();
if (FlowStepEnum.TYPE_2.getValue().equals(type)) { if (FlowStepEnum.TYPE_2.getValue().equals(type)) {
userIds.add(typeId); userIds.add(typeId);
} else { } else {
@ -265,43 +314,47 @@ public class CrmAchievementServiceImpl implements CrmAchievementService {
endTime = DateUtil.endOfMonth(calendar.getTime()); endTime = DateUtil.endOfMonth(calendar.getTime());
} }
//合同目标 List<CrmAchievementDO> achievementDOS = achievementMapper.selectList(new LambdaQueryWrapperX<CrmAchievementDO>()
CrmAchievementDO crmAchievementDO = achievementMapper.selectOne(new LambdaQueryWrapperX<CrmAchievementDO>()
.eq(CrmAchievementDO::getType, type) .eq(CrmAchievementDO::getType, type)
.eq(CrmAchievementDO::getTypeId, typeId) .eq(CrmAchievementDO::getTypeId, typeId)
.eqIfPresent(CrmAchievementDO::getConfig, ShopCommonEnum.ACH_1.getValue())
.eqIfPresent(CrmAchievementDO::getYear, year));
//回款目标
CrmAchievementDO crmAchievementDO2 = achievementMapper.selectOne(new LambdaQueryWrapperX<CrmAchievementDO>()
.eq(CrmAchievementDO::getType, type)
.eq(CrmAchievementDO::getTypeId, typeId)
.eqIfPresent(CrmAchievementDO::getConfig, ShopCommonEnum.ACH_2.getValue())
.eqIfPresent(CrmAchievementDO::getYear, year)); .eqIfPresent(CrmAchievementDO::getYear, year));
Map<Integer, CrmAchievementDO> achievementMap = convertMap(achievementDOS, CrmAchievementDO::getConfig);
// 合同目标
CrmAchievementDO crmAchievementDO = achievementMap.get(ShopCommonEnum.ACH_1.getValue());
// 回款目标
CrmAchievementDO crmAchievementDO2 = achievementMap.get(ShopCommonEnum.ACH_2.getValue());
// 获取合同信息
List<BpmOAContractDTO> contractDTOS = contractApi.getContractList(new BpmOAContractVO()
.setContractType(BpmOAContractVO.SALE_CONTRACT)
.setUserId(userIds)
.setStarTime(starTime)
.setEndTime(endTime)).getCheckedData();
List<CrmContractDO> crmContractDOS = contractMapper.selectList(new LambdaQueryWrapper<CrmContractDO>() // List<CrmContractDO> crmContractDOS = contractMapper.selectList(new LambdaQueryWrapperX<CrmContractDO>()
.in(CrmContractDO::getOwnerUserId, userIds) // .in(CrmContractDO::getOwnerUserId, userIds)
.eq(CrmContractDO::getCheckStatus, ContractStatusEnum.STATUS_2.getValue()) // .eq(CrmContractDO::getCheckStatus, ContractStatusEnum.STATUS_2.getValue())
.between(CrmContractDO::getOrderTime, starTime, endTime)); // .between(CrmContractDO::getOrderTime, starTime, endTime));
BigDecimal contractSuccessMoney = BigDecimal.ZERO; BigDecimal contractSuccessMoney = BigDecimal.ZERO;
if (!crmContractDOS.isEmpty()) { if (CollectionUtil.isNotEmpty(contractDTOS)) {
contractSuccessMoney = crmContractDOS // 累加合同金额
.stream() contractSuccessMoney = contractDTOS.stream()
.map(CrmContractDO::getMoney) .map(BpmOAContractDTO::getContractMoney)
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
} }
List<CrmContractReceivablesDO> crmContractReceivablesDOS = contractReceivablesMapper
.selectList(new LambdaQueryWrapper<CrmContractReceivablesDO>() // List<CrmContractReceivablesDO> crmContractReceivablesDOS = contractReceivablesMapper
.in(CrmContractReceivablesDO::getOwnerUserId, userIds) // .selectList(new LambdaQueryWrapper<CrmContractReceivablesDO>()
.eq(CrmContractReceivablesDO::getCheckStatus, ContractStatusEnum.STATUS_2.getValue()) // .in(CrmContractReceivablesDO::getOwnerUserId, userIds)
.between(CrmContractReceivablesDO::getReturnTime, starTime, endTime)); // .eq(CrmContractReceivablesDO::getCheckStatus, ContractStatusEnum.STATUS_2.getValue())
// .between(CrmContractReceivablesDO::getReturnTime, starTime, endTime));
BigDecimal receivablesSuccessMoney = BigDecimal.ZERO; BigDecimal receivablesSuccessMoney = BigDecimal.ZERO;
if (!crmContractReceivablesDOS.isEmpty()) { // if (!crmContractReceivablesDOS.isEmpty()) {
receivablesSuccessMoney = crmContractReceivablesDOS // receivablesSuccessMoney = crmContractReceivablesDOS
.stream() // .stream()
.map(CrmContractReceivablesDO::getMoney) // .map(CrmContractReceivablesDO::getMoney)
.reduce(BigDecimal.ZERO, BigDecimal::add); // .reduce(BigDecimal.ZERO, BigDecimal::add);
} // }
BigDecimal contractMoney = BigDecimal.ZERO; BigDecimal contractMoney = BigDecimal.ZERO;
BigDecimal receivablesMoney = BigDecimal.ZERO; BigDecimal receivablesMoney = BigDecimal.ZERO;
@ -412,8 +465,6 @@ public class CrmAchievementServiceImpl implements CrmAchievementService {
receivablesMoney = crmAchievementDO2.getDecember(); receivablesMoney = crmAchievementDO2.getDecember();
} }
} }
} }
if (contractMoney.compareTo(BigDecimal.ZERO) > 0) { if (contractMoney.compareTo(BigDecimal.ZERO) > 0) {
contractPer = NumberUtil.round(NumberUtil.div(contractSuccessMoney, contractMoney), 2) contractPer = NumberUtil.round(NumberUtil.div(contractSuccessMoney, contractMoney), 2)

View File

@ -9,7 +9,10 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.oa.BpmOAContractApi;
import cn.iocoder.yudao.module.bpm.api.oa.vo.contract.ContractStatisticsDTO;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.*; import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.*;
import cn.iocoder.yudao.module.crm.controller.admin.crmclues.vo.CrmCluesStatisticsRespVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmachievement.CrmAchievementDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmachievement.CrmAchievementDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmbusiness.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmbusiness.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmclues.CrmCluesDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmclues.CrmCluesDO;
@ -22,7 +25,7 @@ import cn.iocoder.yudao.module.crm.dal.mysql.crmclues.CrmCluesMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcontract.CrmContractMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crmcontract.CrmContractMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcontractreceivables.CrmContractReceivablesMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crmcontractreceivables.CrmContractReceivablesMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcustomer.CrmCustomerMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crmcustomer.CrmCustomerMapper;
import cn.iocoder.yudao.module.hrm.enums.CluesStatusEnum; import cn.iocoder.yudao.module.crm.service.crmclues.CrmCluesService;
import cn.iocoder.yudao.module.hrm.enums.ContractStatusEnum; import cn.iocoder.yudao.module.hrm.enums.ContractStatusEnum;
import cn.iocoder.yudao.module.hrm.enums.FlowStepEnum; import cn.iocoder.yudao.module.hrm.enums.FlowStepEnum;
import cn.iocoder.yudao.module.hrm.enums.RelationEnum; import cn.iocoder.yudao.module.hrm.enums.RelationEnum;
@ -39,12 +42,12 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.*;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/** /**
* 业绩目标 Service 实现类 * 业绩目标 Service 实现类
* *
@ -71,55 +74,75 @@ public class AchievementServiceImpl implements AchievementService {
@Resource @Resource
private CrmCluesMapper crmCluesMapper; private CrmCluesMapper crmCluesMapper;
@Resource
private BpmOAContractApi contractApi;
@Resource
private CrmCluesService cluesService;
@Override @Override
public PageResult<UserAchieveVO> getAchievementPage(AchievementPageReqVO pageReqVO) { public PageResult<UserAchieveVO> getAchievementPage(AchievementPageReqVO pageReqVO) {
AdminUserPageApiDTO dto = new AdminUserPageApiDTO(); AdminUserPageApiDTO dto = new AdminUserPageApiDTO();
dto.setUsername(pageReqVO.getName()); dto.setNickname(pageReqVO.getName());
dto.setPageNo(pageReqVO.getPageNo()); dto.setPageNo(pageReqVO.getPageNo());
dto.setPageSize(pageReqVO.getPageSize()); dto.setPageSize(pageReqVO.getPageSize());
PageResult<AdminUserApiVO> pageResult = adminUserApi.getUserPage(dto).getCheckedData(); dto.setRoleCodes("sale"); // 设置查询用户的角色类型
// 获取销售角色 分页列表
PageResult<AdminUserApiVO> pageResult = adminUserApi.getUserPageByRole(dto).getCheckedData();
PageResult<UserAchieveVO> pageResult1 = BeanUtils.toBean(pageResult, UserAchieveVO.class); PageResult<UserAchieveVO> pageResult1 = BeanUtils.toBean(pageResult, UserAchieveVO.class);
pageResult1.getList().forEach(v -> {
LambdaQueryWrapperX<CrmContractDO> wrapperX = new LambdaQueryWrapperX<>();
wrapperX.eq(CrmContractDO::getOwnerUserId, v.getId())
.eq(CrmContractDO::getCheckStatus, ContractStatusEnum.STATUS_2.getValue())
.betweenIfPresent(CrmContractDO::getCreateTime, pageReqVO.getCreateTime());
v.setContractCount(contractMapper.selectCount(wrapperX));
List<CrmContractDO> crmContractDOS = contractMapper.selectList(wrapperX);
BigDecimal contractMoney = crmContractDOS
.stream()
.map(CrmContractDO::getMoney)
.reduce(BigDecimal.ZERO, BigDecimal::add);
v.setContractMoney(contractMoney);
List<CrmContractReceivablesDO> crmContractReceivablesDOS = contractReceivablesMapper
.selectList(new LambdaQueryWrapperX<CrmContractReceivablesDO>()
.eq(CrmContractReceivablesDO::getOwnerUserId, v.getId())
.eq(CrmContractReceivablesDO::getCheckStatus, ContractStatusEnum.STATUS_2.getValue())
.betweenIfPresent(CrmContractReceivablesDO::getCreateTime, pageReqVO.getCreateTime()));
BigDecimal receivablesMoney = crmContractReceivablesDOS
.stream()
.map(CrmContractReceivablesDO::getMoney)
.reduce(BigDecimal.ZERO, BigDecimal::add);
v.setReceivablesMoney(receivablesMoney);
LambdaQueryWrapperX<CrmCluesDO> wrapperX2 = new LambdaQueryWrapperX<>(); if (CollUtil.isNotEmpty(pageResult1.getList())) {
wrapperX2.eq(CrmCluesDO::getOwnerUserId, v.getId())
.betweenIfPresent(CrmCluesDO::getCreateTime, pageReqVO.getCreateTime());
Long cluesCount = crmCluesMapper.selectCount(wrapperX2);
v.setCluesCount(cluesCount);
wrapperX2.eq(CrmCluesDO::getStatus, CluesStatusEnum.STATUS_1.getValue()); // 获取所有用户编号列表
Long cluesToCustomerCount = crmCluesMapper.selectCount(wrapperX2); List<Long> userIds = convertList(pageResult1.getList(), UserAchieveVO::getId);
v.setCluesToCustomerCount(cluesToCustomerCount);
String per = "0";
if (cluesCount > 0) {
per = NumberUtil.round(NumberUtil.div(cluesToCustomerCount, cluesCount), 2)
.multiply(new BigDecimal("100")).toString();
}
v.setCluesToCustomerPer(per);
}); // 获取所有用户的合同统计列表
List<ContractStatisticsDTO> contractStatisticsDTOS = contractApi.getContractStatistics(userIds, pageReqVO.getCreateTime()).getCheckedData();
Map<Long, ContractStatisticsDTO> contractMap = convertMap(contractStatisticsDTOS, ContractStatisticsDTO::getUserId);
// 获取所有用户的线索统计列表
List<CrmCluesStatisticsRespVO> cluesStatisticsRespVOS = cluesService.getCluesStatisticsByUserIds(userIds, pageReqVO.getCreateTime());
Map<Long, CrmCluesStatisticsRespVO> clusesMap = convertMap(cluesStatisticsRespVOS, CrmCluesStatisticsRespVO::getOwnerUserId);
pageResult1.getList().forEach(v -> {
String per = "0";
if (contractMap.get(v.getId()) == null) {
// 设置合同数量
v.setContractCount(0L);
// 设置合同金额
v.setContractMoney(BigDecimal.ZERO);
// 设置回款金额
v.setReceivablesMoney(BigDecimal.ZERO);
// 设置线索数量
v.setCluesCount(0L);
// 设置转客数量
v.setCluesToCustomerCount(0L);
v.setCluesToCustomerPer(per);
}else {
// 设置合同数量
v.setContractCount(Long.valueOf(contractMap.get(v.getId()).getCount()));
// 设置合同金额
v.setContractMoney(contractMap.get(v.getId()).getMoney());
// 设置回款金额
v.setReceivablesMoney(BigDecimal.ZERO);
// 设置线索数量
v.setCluesCount(Long.valueOf(clusesMap.get(v.getId()).getCount()));
// 设置转客数量
v.setCluesToCustomerCount(Long.valueOf(clusesMap.get(v.getId()).getSuccessCount()));
// 设置转客比率
if (v.getCluesCount() > 0) {
per = NumberUtil.round(NumberUtil.div(v.getCluesToCustomerCount(), v.getCluesCount()), 2)
.multiply(new BigDecimal("100")).toString();
}
v.setCluesToCustomerPer(per);
}
});
}
return pageResult1; return pageResult1;
} }

View File

@ -6,6 +6,10 @@ import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.AchievementPa
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.CustomerLevelVO; import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.CustomerLevelVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.UserRecordVO; import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.UserRecordVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.UserVolumeVO; import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.UserVolumeVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo.CustomerStatisticRespVO;
import java.time.LocalDateTime;
import java.util.List;
/** /**
* 客户分析 Service 接口 * 客户分析 Service 接口
@ -45,5 +49,11 @@ public interface CustomerService {
*/ */
PageResult<UserRecordVO> getRecord(AchievementPageReqVO pageReqVO); PageResult<UserRecordVO> getRecord(AchievementPageReqVO pageReqVO);
/**
* 获取指定用户的客户统计信息列表
* @param userIds 用户id集合
* @param createTime 创建时间
* @return 客户统计信息
*/
List<CustomerStatisticRespVO> getCustomerStatistic(List<Long> userIds, LocalDateTime[] createTime);
} }

View File

@ -1,23 +1,24 @@
package cn.iocoder.yudao.module.crm.service.crmanalysis; package cn.iocoder.yudao.module.crm.service.crmanalysis;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.common.enums.ShopCommonEnum; import cn.iocoder.yudao.framework.common.enums.ShopCommonEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.AchievementPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.AchievementPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.CustomerLevelVO; import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.CustomerLevelVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.UserRecordVO; import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.UserRecordVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.UserVolumeVO; import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.UserVolumeVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo.CustomerStatisticRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.RecordStatisticsRespVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmrecord.CrmRecordDO;
import cn.iocoder.yudao.module.crm.dal.mysql.crmachievement.CrmAchievementMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crmachievement.CrmAchievementMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmclues.CrmCluesMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crmclues.CrmCluesMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcontract.CrmContractMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crmcontract.CrmContractMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcontractreceivables.CrmContractReceivablesMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crmcontractreceivables.CrmContractReceivablesMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcustomer.CrmCustomerMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crmcustomer.CrmCustomerMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmrecord.CrmRecordMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crmrecord.CrmRecordMapper;
import cn.iocoder.yudao.module.hrm.enums.TypesEnum; import cn.iocoder.yudao.module.crm.service.crmrecord.CrmRecordService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi; import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO; import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
@ -30,10 +31,15 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/** /**
* 客户分析 Service 实现类 * 客户分析 Service 实现类
* *
@ -43,25 +49,17 @@ import java.util.stream.Collectors;
@Validated @Validated
public class CustomerServiceImpl implements CustomerService { public class CustomerServiceImpl implements CustomerService {
@Resource
private CrmAchievementMapper achievementMapper;
@Resource
private DeptApi deptApi;
@Resource @Resource
private DictDataApi dictDataApi; private DictDataApi dictDataApi;
@Resource
private CrmContractMapper contractMapper;
@Resource
private CrmContractReceivablesMapper contractReceivablesMapper;
@Resource
private CrmCluesMapper crmCluesMapper;
@Resource @Resource
private AdminUserApi adminUserApi; private AdminUserApi adminUserApi;
@Resource @Resource
private CrmCustomerMapper customerMapper; private CrmCustomerMapper customerMapper;
@Resource @Resource
private CrmRecordMapper crmRecordMapper; private CrmRecordService recordService;
@Override @Override
public CustomerLevelVO getCustomerLevel() { public CustomerLevelVO getCustomerLevel() {
@ -127,31 +125,41 @@ public class CustomerServiceImpl implements CustomerService {
@Override @Override
public PageResult<UserVolumeVO> getCustomerVolume(AchievementPageReqVO pageReqVO) { public PageResult<UserVolumeVO> getCustomerVolume(AchievementPageReqVO pageReqVO) {
AdminUserPageApiDTO dto = new AdminUserPageApiDTO(); AdminUserPageApiDTO dto = new AdminUserPageApiDTO();
dto.setUsername(pageReqVO.getName()); dto.setNickname(pageReqVO.getName());
dto.setPageNo(pageReqVO.getPageNo()); dto.setPageNo(pageReqVO.getPageNo());
dto.setPageSize(pageReqVO.getPageSize()); dto.setPageSize(pageReqVO.getPageSize());
PageResult<AdminUserApiVO> pageResult = adminUserApi.getUserPage(dto).getCheckedData(); dto.setRoleCodes("sale"); // 设置查询用户的角色类型
PageResult<AdminUserApiVO> pageResult = adminUserApi.getUserPageByRole(dto).getCheckedData();
PageResult<UserVolumeVO> pageResult1 = BeanUtils.toBean(pageResult, UserVolumeVO.class); PageResult<UserVolumeVO> pageResult1 = BeanUtils.toBean(pageResult, UserVolumeVO.class);
pageResult1.getList().forEach(v -> { if (CollectionUtil.isNotEmpty(pageResult1.getList())) {
LambdaQueryWrapperX<CrmCustomerDO> wrapperX = new LambdaQueryWrapperX<>();
wrapperX.eq(CrmCustomerDO::getOwnerUserId, v.getId())
.betweenIfPresent(CrmCustomerDO::getCreateTime, pageReqVO.getCreateTime());
Long customerCount = customerMapper.selectCount(wrapperX);
wrapperX.eq(CrmCustomerDO::getDealStatus, ShopCommonEnum.IS_STATUS_1.getValue());
Long successCount = customerMapper.selectCount(wrapperX);
v.setCustomerCount(customerCount); List<Long> userIds = convertList(pageResult1.getList(), UserVolumeVO::getId);
v.setSuccessCount(successCount); // 获取所有用户的客户统计列表
List<CustomerStatisticRespVO> statisticRespVOS = this.getCustomerStatistic(userIds, pageReqVO.getCreateTime());
Map<Long, CustomerStatisticRespVO> customerMap = convertMap(statisticRespVOS, CustomerStatisticRespVO::getOwnerUserId);
String per = "0"; pageResult1.getList().forEach(v -> {
if (customerCount > 0) {
per = NumberUtil.round(NumberUtil.div(successCount, customerCount), 2)
.multiply(new BigDecimal("100")).toString();
}
v.setSuccessPer(per);
}); String per = "0";
if (customerMap.get(v.getId()) == null) {
v.setCustomerCount(0L);
v.setSuccessCount(0L);
v.setSuccessPer(per);
}else {
v.setCustomerCount(Long.valueOf(customerMap.get(v.getId()).getCount()));
v.setSuccessCount(Long.valueOf(customerMap.get(v.getId()).getDealsCount()));
if (v.getCustomerCount() > 0) {
per = NumberUtil.round(NumberUtil.div(v.getSuccessCount(), v.getCustomerCount()), 2)
.multiply(new BigDecimal("100")).toString();
}
v.setSuccessPer(per);
}
});
}
return pageResult1; return pageResult1;
} }
@ -162,30 +170,48 @@ public class CustomerServiceImpl implements CustomerService {
dto.setUsername(pageReqVO.getName()); dto.setUsername(pageReqVO.getName());
dto.setPageNo(pageReqVO.getPageNo()); dto.setPageNo(pageReqVO.getPageNo());
dto.setPageSize(pageReqVO.getPageSize()); dto.setPageSize(pageReqVO.getPageSize());
PageResult<AdminUserApiVO> pageResult = adminUserApi.getUserPage(dto).getCheckedData(); dto.setRoleCodes("sale"); // 设置查询用户的角色类型
PageResult<AdminUserApiVO> pageResult = adminUserApi.getUserPageByRole(dto).getCheckedData();
PageResult<UserRecordVO> pageResult1 = BeanUtils.toBean(pageResult, UserRecordVO.class); PageResult<UserRecordVO> pageResult1 = BeanUtils.toBean(pageResult, UserRecordVO.class);
pageResult1.getList().forEach(v -> { if (CollectionUtil.isNotEmpty(pageResult1.getList())) {
LambdaQueryWrapperX<CrmRecordDO> wrapperX = new LambdaQueryWrapperX<>();
wrapperX.eq(CrmRecordDO::getCreator, v.getId())
.betweenIfPresent(CrmRecordDO::getCreateTime, pageReqVO.getCreateTime());
Long totalCount = crmRecordMapper.selectCount(wrapperX);
wrapperX.eq(CrmRecordDO::getTypes, TypesEnum.CUSTOMER.getValue()); // 获取所有用户跟进统计列表
Long customerCount = crmRecordMapper.selectCount(wrapperX); List<Long> userIds = convertList(pageResult1.getList(), UserRecordVO::getId);
List<RecordStatisticsRespVO> respVOS = recordService.getRecordStatistics(userIds, pageReqVO.getCreateTime());
Map<String, RecordStatisticsRespVO> recordMap = convertMap(respVOS, RecordStatisticsRespVO::getCreator);
wrapperX.eq(CrmRecordDO::getTypes, TypesEnum.BUSINESS.getValue()); pageResult1.getList().forEach(v -> {
Long businessCount = crmRecordMapper.selectCount(wrapperX);
wrapperX.eq(CrmRecordDO::getTypes, TypesEnum.CLUES.getValue()); RecordStatisticsRespVO respVO = recordMap.get(v.getId().toString());
Long cluesCount = crmRecordMapper.selectCount(wrapperX); if (respVO == null) {
// 设置跟进总数量
v.setTotalCount(totalCount); v.setTotalCount(0L);
v.setCustomerCount(customerCount); // 设置跟进客户数量
v.setBusinessCount(businessCount); v.setCustomerCount(0L);
v.setCluesCount(cluesCount); // 设置跟进商机数量
v.setBusinessCount(0L);
}); // 设置跟进线索数量
v.setCluesCount(0L);
}else {
// 设置跟进总数量
v.setTotalCount(Long.valueOf(respVO.getCount()));
// 设置跟进客户数量
v.setCustomerCount(Long.valueOf(respVO.getCustomerCount()));
// 设置跟进商机数量
v.setBusinessCount(Long.valueOf(respVO.getBusinessCount()));
// 设置跟进线索数量
v.setCluesCount(Long.valueOf(respVO.getCluesCount()));
}
});
}
return pageResult1; return pageResult1;
} }
@Override
public List<CustomerStatisticRespVO> getCustomerStatistic(List<Long> userIds, LocalDateTime[] createTime) {
return customerMapper.selectStatistic(userIds, createTime);
}
} }

View File

@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo.CustomerImpor
import cn.iocoder.yudao.module.crm.dal.dataobject.crmclues.CrmCluesDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmclues.CrmCluesDO;
import javax.validation.Valid; import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
@ -88,5 +89,10 @@ public interface CrmCluesService {
*/ */
void transfer(CrmCluesTransferVO transferVO); void transfer(CrmCluesTransferVO transferVO);
/**
* 获取指定用户的线索统计信息
* @param ownerUserIds 用户ID
* @return 线索统计信息列表
*/
List<CrmCluesStatisticsRespVO> getCluesStatisticsByUserIds(List<Long> ownerUserIds, LocalDateTime[] createTime);
} }

View File

@ -268,4 +268,10 @@ public class CrmCluesServiceImpl implements CrmCluesService {
} }
} }
@Override
public List<CrmCluesStatisticsRespVO> getCluesStatisticsByUserIds(List<Long> ownerUserIds, LocalDateTime[] createTime) {
return cluesMapper.selectStatisticsByUserIds(ownerUserIds, createTime);
}
} }

View File

@ -1,10 +1,13 @@
package cn.iocoder.yudao.module.crm.service.crmcontract; package cn.iocoder.yudao.module.crm.service.crmcontract;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.*; import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CheckInfoVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontract.vo.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractProductDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractProductDO;
import javax.validation.Valid;
import javax.validation.Valid;
import java.util.List; import java.util.List;
/** /**
@ -74,4 +77,10 @@ public interface CrmContractService {
*/ */
void check(CheckInfoVO checkInfoVO); void check(CheckInfoVO checkInfoVO);
/**
* 创建合同产品
* @param createReqVO 创建信息
* @return 编号
*/
void createProduct(List<CrmContractProductDO> createReqVO);
} }

View File

@ -390,6 +390,12 @@ public class CrmContractServiceImpl implements CrmContractService {
} }
@Override
public void createProduct(List<CrmContractProductDO> createReqVO) {
contractProductMapper.insertBatch(createReqVO);
}
private void createContractProductList(Long contractId, List<CrmContractProductDO> list) { private void createContractProductList(Long contractId, List<CrmContractProductDO> list) {
List<StoreProductAttrValueApiVO> storeProductAttrValueList = new ArrayList<>(); List<StoreProductAttrValueApiVO> storeProductAttrValueList = new ArrayList<>();
if (CollUtil.isNotEmpty(list)) { if (CollUtil.isNotEmpty(list)) {

View File

@ -29,6 +29,13 @@ public interface CrmCustomerService {
*/ */
void updateCustomer(@Valid CrmCustomerSaveReqVO updateReqVO); void updateCustomer(@Valid CrmCustomerSaveReqVO updateReqVO);
/**
* 更新客户成交信息
*
* @param updateReqVO 更新信息
*/
void updateCustomerPurchaseTotal(@Valid CrmCustomerSaveReqVO updateReqVO);
/** /**
* 删除客户 * 删除客户
* *

View File

@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontractreceivables.vo.CrmContractReceivablesRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo.*; import cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo.*;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmbusiness.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmbusiness.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractDO;
@ -116,7 +115,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateCustomer(CrmCustomerSaveReqVO updateReqVO) { public void updateCustomer(CrmCustomerSaveReqVO updateReqVO) {
// 校验存在 // 校验存在
validateCustomerExists(updateReqVO.getId()); CrmCustomerDO customerDO = validateCustomerExists(updateReqVO.getId());
// 更新 // 更新
CrmCustomerDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerDO.class); CrmCustomerDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerDO.class);
customerMapper.updateById(updateObj); customerMapper.updateById(updateObj);
@ -125,6 +124,26 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
crmOperatelogService.createLog("修改客户", updateReqVO.getId(), 0L, 0L); crmOperatelogService.createLog("修改客户", updateReqVO.getId(), 0L, 0L);
} }
@Override
@Transactional(rollbackFor = Exception.class)
public void updateCustomerPurchaseTotal(CrmCustomerSaveReqVO updateReqVO) {
// 校验存在
CrmCustomerDO customerDO = validateCustomerExists(updateReqVO.getId());
// 更新
updateReqVO.setPurchaseTimes(customerDO.getPurchaseTimes() + 1);
updateReqVO.setPurchaseTotal(customerDO.getPurchaseTotal().add(updateReqVO.getPurchaseTotal()));
CrmCustomerDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerDO.class);
customerMapper.updateById(updateObj);
//插入日志
crmOperatelogService.createLog("修改客户", updateReqVO.getId(), 0L, 0L);
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void deleteCustomer(Long id) { public void deleteCustomer(Long id) {
@ -197,10 +216,12 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
crmOperatelogService.createLog("公海领取客户", id, 0L, 0L); crmOperatelogService.createLog("公海领取客户", id, 0L, 0L);
} }
private void validateCustomerExists(Long id) { private CrmCustomerDO validateCustomerExists(Long id) {
if (customerMapper.selectById(id) == null) { CrmCustomerDO customerDO = customerMapper.selectById(id);
if (customerDO == null) {
throw exception(CUSTOMER_NOT_EXISTS); throw exception(CUSTOMER_NOT_EXISTS);
} }
return customerDO;
} }
@Override @Override

View File

@ -1,7 +1,10 @@
package cn.iocoder.yudao.module.crm.service.crmcustomercontacts; package cn.iocoder.yudao.module.crm.service.crmcustomercontacts;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.crmcustomercontacts.vo.*; import cn.iocoder.yudao.module.crm.controller.admin.crmcustomercontacts.vo.CrmCustomerContactsPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcustomercontacts.vo.CrmCustomerContactsRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcustomercontacts.vo.CrmCustomerContactsSaveReqVO;
import javax.validation.Valid; import javax.validation.Valid;
/** /**
@ -48,5 +51,4 @@ public interface CrmCustomerContactsService {
* @return 联系人分页 * @return 联系人分页
*/ */
PageResult<CrmCustomerContactsRespVO> getCustomerContactsPage(CrmCustomerContactsPageReqVO pageReqVO); PageResult<CrmCustomerContactsRespVO> getCustomerContactsPage(CrmCustomerContactsPageReqVO pageReqVO);
} }

View File

@ -95,5 +95,4 @@ public class CrmCustomerContactsServiceImpl implements CrmCustomerContactsServic
IPage<CrmCustomerContactsRespVO> pageResult = customerContactsMapper.selectPageList(mpPage, pageReqVO, ids); IPage<CrmCustomerContactsRespVO> pageResult = customerContactsMapper.selectPageList(mpPage, pageReqVO, ids);
return new PageResult<>(pageResult.getRecords(), pageResult.getTotal()); return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
} }
} }

View File

@ -5,12 +5,12 @@ import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.ShopCommonEnum; import cn.iocoder.yudao.framework.common.enums.ShopCommonEnum;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.oa.BpmOAContractApi;
import cn.iocoder.yudao.module.bpm.api.oa.vo.contract.ContractStatisticsDTO;
import cn.iocoder.yudao.module.crm.controller.admin.crmindex.vo.BrieCountVO; import cn.iocoder.yudao.module.crm.controller.admin.crmindex.vo.BrieCountVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmindex.vo.CrmIndexRespVO; import cn.iocoder.yudao.module.crm.controller.admin.crmindex.vo.CrmIndexRespVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmbusiness.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmbusiness.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmclues.CrmCluesDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmclues.CrmCluesDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontractreceivables.CrmContractReceivablesDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomercontacts.CrmCustomerContactsDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomercontacts.CrmCustomerContactsDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmrecord.CrmRecordDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmrecord.CrmRecordDO;
@ -26,7 +26,6 @@ import cn.iocoder.yudao.module.system.api.notice.NoticeApi;
import cn.iocoder.yudao.module.system.api.notice.dto.NoticeDTO; import cn.iocoder.yudao.module.system.api.notice.dto.NoticeDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -64,6 +63,9 @@ public class CrmIndexServiceImpl implements CrmIndexService {
@Resource @Resource
private AdminUserApi adminUserApi; private AdminUserApi adminUserApi;
@Resource
private BpmOAContractApi bpmOAContractApi;
@Override @Override
public CrmIndexRespVO getIndexCount(String relation) { public CrmIndexRespVO getIndexCount(String relation) {
@ -180,65 +182,81 @@ public class CrmIndexServiceImpl implements CrmIndexService {
.multiply(new BigDecimal("100")).toString(); .multiply(new BigDecimal("100")).toString();
} }
Long count05 = contractMapper.selectCount(new LambdaQueryWrapper<CrmContractDO>() // 获取今日合同新增数量
.in(!ids.isEmpty(), CrmContractDO::getOwnerUserId, ids) ContractStatisticsDTO contractStatisticsDTO = bpmOAContractApi.getContractCount(relation).getCheckedData();
.between(CrmContractDO::getCreateTime, todayStart, todayEnd)); // 设置今日新增数量
Long count005 = contractMapper.selectCount(new LambdaQueryWrapper<CrmContractDO>() Long count05 = Long.valueOf(contractStatisticsDTO.getTodayAdditions());
.in(!ids.isEmpty(), CrmContractDO::getOwnerUserId, ids) // 设置昨日新增数量
.between(CrmContractDO::getCreateTime, yesterdayStart, yesterdayEnd)); Long count005 = Long.valueOf(contractStatisticsDTO.getYesterdayAdditions());
String per005 = "0"; // 设置较昨日比率
if (count005 > 0) { String per005 = contractStatisticsDTO.getCountPercentage().toString();
per005 = NumberUtil.div(NumberUtil.sub(count05, count005), count005, 2) // 设置今日新增金额
.multiply(new BigDecimal("100")).toString(); BigDecimal count06 = contractStatisticsDTO.getTodayAddMoney();
} // 设置昨日新增金额
BigDecimal count006 = contractStatisticsDTO.getYesterdayAddMoney();
// 设置较昨日比率
String per006 = contractStatisticsDTO.getMoneyPercentage().toString();
QueryWrapper<CrmContractDO> queryWrapper06 = new QueryWrapper<>(); // Long count05 = contractMapper.selectCount(new LambdaQueryWrapper<CrmContractDO>()
queryWrapper06.select("sum(money) as count06"); // .in(!ids.isEmpty(), CrmContractDO::getOwnerUserId, ids)
queryWrapper06.eq("to_days(create_time)", "to_days(now())"); // .between(CrmContractDO::getCreateTime, todayStart, todayEnd));
queryWrapper06.in(!ids.isEmpty(), "owner_user_id", ids); // Long count005 = contractMapper.selectCount(new LambdaQueryWrapper<CrmContractDO>()
CrmContractDO crmContractDO = contractMapper.selectOne(queryWrapper06); // .in(!ids.isEmpty(), CrmContractDO::getOwnerUserId, ids)
BigDecimal count06 = BigDecimal.ZERO; // .between(CrmContractDO::getCreateTime, yesterdayStart, yesterdayEnd));
if (crmContractDO != null) { // String per005 = "0";
count06 = crmContractDO.getCount06(); // if (count005 > 0) {
} // per005 = NumberUtil.div(NumberUtil.sub(count05, count005), count005, 2)
// .multiply(new BigDecimal("100")).toString();
// }
QueryWrapper<CrmContractDO> queryWrapper006 = new QueryWrapper<>(); // QueryWrapper<CrmContractDO> queryWrapper06 = new QueryWrapper<>();
queryWrapper006.select("sum(money) as count006"); // queryWrapper06.select("sum(money) as count06");
queryWrapper006.eq("to_days(now())-to_days(create_time)", 1); // queryWrapper06.eq("to_days(create_time)", "to_days(now())");
queryWrapper006.in(!ids.isEmpty(), "owner_user_id", ids); // queryWrapper06.in(!ids.isEmpty(), "owner_user_id", ids);
CrmContractDO crmContractDO006 = contractMapper.selectOne(queryWrapper006); // CrmContractDO crmContractDO = contractMapper.selectOne(queryWrapper06);
BigDecimal count006 = BigDecimal.ZERO; // BigDecimal count06 = BigDecimal.ZERO;
if (crmContractDO006 != null) { // if (crmContractDO != null) {
count006 = crmContractDO006.getCount006(); // count06 = crmContractDO.getCount06();
} // }
String per006 = "0"; //
if (count006.compareTo(BigDecimal.ZERO) > 0) { // QueryWrapper<CrmContractDO> queryWrapper006 = new QueryWrapper<>();
per006 = NumberUtil.div(NumberUtil.sub(count06, count006), count006, 2) // queryWrapper006.select("sum(money) as count006");
.multiply(new BigDecimal("100")).toString(); // queryWrapper006.eq("to_days(now())-to_days(create_time)", 1);
} // queryWrapper006.in(!ids.isEmpty(), "owner_user_id", ids);
// CrmContractDO crmContractDO006 = contractMapper.selectOne(queryWrapper006);
// BigDecimal count006 = BigDecimal.ZERO;
// if (crmContractDO006 != null) {
// count006 = crmContractDO006.getCount006();
// }
// String per006 = "0";
// if (count006.compareTo(BigDecimal.ZERO) > 0) {
// per006 = NumberUtil.div(NumberUtil.sub(count06, count006), count006, 2)
// .multiply(new BigDecimal("100")).toString();
// }
QueryWrapper<CrmContractReceivablesDO> queryWrapper07 = new QueryWrapper<>(); // QueryWrapper<CrmContractReceivablesDO> queryWrapper07 = new QueryWrapper<>();
queryWrapper07.select("sum(money) as count07"); // queryWrapper07.select("sum(money) as count07");
queryWrapper07.eq("to_days(create_time)", "to_days(now())"); // queryWrapper07.eq("to_days(create_time)", "to_days(now())");
CrmContractReceivablesDO crmContractReceivablesDO = contractReceivablesMapper.selectOne(queryWrapper07); // CrmContractReceivablesDO crmContractReceivablesDO = contractReceivablesMapper.selectOne(queryWrapper07);
BigDecimal count07 = BigDecimal.ZERO; BigDecimal count07 = BigDecimal.ZERO;
if (crmContractDO != null) { // if (crmContractDO != null) {
count07 = crmContractReceivablesDO.getCount07(); // count07 = crmContractReceivablesDO.getCount07();
} // }
QueryWrapper<CrmContractReceivablesDO> queryWrapper007 = new QueryWrapper<>(); // QueryWrapper<CrmContractReceivablesDO> queryWrapper007 = new QueryWrapper<>();
queryWrapper007.select("sum(money) as count007"); // queryWrapper007.select("sum(money) as count007");
queryWrapper007.eq("to_days(now())-to_days(create_time)", 1); // queryWrapper007.eq("to_days(now())-to_days(create_time)", 1);
CrmContractReceivablesDO crmContractReceivablesDO007 = contractReceivablesMapper.selectOne(queryWrapper007); // CrmContractReceivablesDO crmContractReceivablesDO007 = contractReceivablesMapper.selectOne(queryWrapper007);
BigDecimal count007 = BigDecimal.ZERO; BigDecimal count007 = BigDecimal.ZERO;
if (crmContractReceivablesDO007 != null) { // if (crmContractReceivablesDO007 != null) {
count007 = crmContractReceivablesDO007.getCount007(); // count007 = crmContractReceivablesDO007.getCount007();
} // }
String per007 = "0"; String per007 = "0";
if (count007.compareTo(BigDecimal.ZERO) > 0) { // if (count007.compareTo(BigDecimal.ZERO) > 0) {
per007 = NumberUtil.div(NumberUtil.sub(count07, count007), count007, 2) // per007 = NumberUtil.div(NumberUtil.sub(count07, count007), count007, 2)
.multiply(new BigDecimal("100")).toString(); // .multiply(new BigDecimal("100")).toString();
} // }
Long count08 = customerContactsMapper.selectCount(new LambdaQueryWrapper<CrmCustomerContactsDO>() Long count08 = customerContactsMapper.selectCount(new LambdaQueryWrapper<CrmCustomerContactsDO>()
.in(!ids.isEmpty(), CrmCustomerContactsDO::getOwnerUserId, ids) .in(!ids.isEmpty(), CrmCustomerContactsDO::getOwnerUserId, ids)

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.crm.service.crminvoice;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.ShopCommonEnum; import cn.iocoder.yudao.framework.common.enums.ShopCommonEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.exception.ErrorCode; import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@ -25,7 +24,6 @@ import cn.iocoder.yudao.module.crm.dal.mysql.crmflowlog.CrmFlowLogMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crminvoice.CrmInvoiceMapper; import cn.iocoder.yudao.module.crm.dal.mysql.crminvoice.CrmInvoiceMapper;
import cn.iocoder.yudao.module.hrm.enums.*; import cn.iocoder.yudao.module.hrm.enums.*;
import cn.iocoder.yudao.module.system.api.mail.MailSendApi; import cn.iocoder.yudao.module.system.api.mail.MailSendApi;
import cn.iocoder.yudao.module.system.api.mail.dto.MailSendSingleReqDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -38,9 +36,7 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*;
@ -71,8 +67,6 @@ public class CrmInvoiceServiceImpl implements CrmInvoiceService {
private RedissonClient redissonClient; private RedissonClient redissonClient;
@Resource @Resource
private CrmFlowLogMapper crmFlowLogMapper; private CrmFlowLogMapper crmFlowLogMapper;
@Resource
private MailSendApi mailSendApi;
private static final String LOCK_KEY = "invoice:check:lock"; private static final String LOCK_KEY = "invoice:check:lock";
@ -301,13 +295,13 @@ public class CrmInvoiceServiceImpl implements CrmInvoiceService {
crmContractDO.setInvoiceMoney(money); crmContractDO.setInvoiceMoney(money);
contractMapper.updateById(crmContractDO); contractMapper.updateById(crmContractDO);
//发送邮件 // //发送邮件
if(StrUtil.isNotEmpty(updateObj.getFiles()) && StrUtil.isNotEmpty(updateObj.getEmail())){ // if(StrUtil.isNotEmpty(updateObj.getFiles()) && StrUtil.isNotEmpty(updateObj.getEmail())){
Map<String, Object> templateParams = new HashMap<>(); // Map<String, Object> templateParams = new HashMap<>();
templateParams.put("files",updateObj.getFiles()); // templateParams.put("files",updateObj.getFiles());
mailSendApi.sendSingleMail(new MailSendSingleReqDTO() // mailSendApi.sendSingleMail(new MailSendSingleReqDTO()
.setMail(updateObj.getEmail()).setUserId(updateObj.getCustomerId()).setUserType(UserTypeEnum.CUSTOMER.getValue()) // .setMail(updateObj.getEmail()).setUserId(updateObj.getCustomerId()).setUserType(UserTypeEnum.CUSTOMER.getValue())
.setTemplateCode("invoice-send").setTemplateParams(templateParams)); // .setTemplateCode("invoice-send").setTemplateParams(templateParams));
} // }
} }
} }

View File

@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.*; import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.*;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmrecord.CrmRecordDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmrecord.CrmRecordDO;
import javax.validation.Valid; import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.List;
/** /**
* 跟进记录 Service 接口 * 跟进记录 Service 接口
@ -50,4 +52,12 @@ public interface CrmRecordService {
*/ */
PageResult<CrmRecordRespVO> getRecordPage(CrmRecordPageReqVO pageReqVO); PageResult<CrmRecordRespVO> getRecordPage(CrmRecordPageReqVO pageReqVO);
/**
* 获得跟进统计
* @param userIds 跟进人用户编号
* @param createTime 创建时间
* @return 跟进统计列表
*/
List<RecordStatisticsRespVO> getRecordStatistics(List<Long> userIds, LocalDateTime[] createTime);
} }

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.CrmRecordPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.CrmRecordPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.CrmRecordRespVO; import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.CrmRecordRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.CrmRecordSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.CrmRecordSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.RecordStatisticsRespVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmbusiness.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmbusiness.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmclues.CrmCluesDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmclues.CrmCluesDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomer.CrmCustomerDO;
@ -155,4 +156,10 @@ public class CrmRecordServiceImpl implements CrmRecordService {
return pageResult1; return pageResult1;
} }
@Override
public List<RecordStatisticsRespVO> getRecordStatistics(List<Long> userIds, LocalDateTime[] createTime) {
return recordMapper.selectStatistics(userIds, createTime);
}
} }

View File

@ -88,4 +88,28 @@
</where> </where>
order by a.id desc order by a.id desc
</select> </select>
<select id="selectStatisticsByUserIds" resultType="cn.iocoder.yudao.module.crm.controller.admin.crmclues.vo.CrmCluesStatisticsRespVO">
SELECT
owner_user_id AS ownerUserId,
COUNT(1) AS count,
COUNT( CASE WHEN status = 1 THEN 1 END ) AS successCount
FROM
crm_clues
WHERE
deleted = 0
AND owner_user_id IN
<foreach item="ownerUserId" collection="ownerUserIds" separator="," open="(" close=")" index="">
#{ownerUserId}
</foreach>
<if test="createTime != null and createTime.length > 0">
<if test="createTime[0] != null">
AND create_time &gt;= #{createTime[0]}
</if>
<if test="createTime[1] != null">
AND create_time &lt;= #{createTime[1]}
</if>
</if>
GROUP BY owner_user_id
</select>
</mapper> </mapper>

View File

@ -60,4 +60,25 @@
</if> </if>
</where> </where>
</select> </select>
<select id="selectContractTop" resultType="cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.ContractVO">
SELECT
t.user_id AS id,
COUNT( t.id ) AS count,
SUM( t.contract_money ) AS money,
u.nickname AS nickname
FROM
bpm_oa_contract t
LEFT JOIN system_users u ON ( u.id = t.user_id )
AND u.deleted = 0
WHERE
t.deleted = 0
AND t.result = 2
AND t.contract_type = 1
GROUP BY
t.user_id
ORDER BY
money DESC
LIMIT 10
</select>
</mapper> </mapper>

View File

@ -50,4 +50,28 @@
</where> </where>
order by a.id desc order by a.id desc
</select> </select>
<select id="selectStatistic" resultType="cn.iocoder.yudao.module.crm.controller.admin.crmcustomer.vo.CustomerStatisticRespVO">
SELECT
owner_user_id AS ownerUserId,
COUNT(1) AS count,
COUNT( CASE WHEN deal_status = 1 THEN 1 END ) AS DealsCount
FROM
crm_customer
WHERE
deleted = 0
AND owner_user_id IN
<foreach item="userId" collection="userIds" separator="," open="(" close=")" index="">
#{userId}
</foreach>
<if test="createTime != null and createTime.length > 0">
<if test="createTime[0] != null">
AND create_time &gt;= #{createTime[0]}
</if>
<if test="createTime[1] != null">
AND create_time &lt;= #{createTime[1]}
</if>
</if>
GROUP BY owner_user_id
</select>
</mapper> </mapper>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.crmrecord.CrmRecordMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.yixiang.co/MyBatis/x-plugins/
-->
<select id="selectStatistics" resultType="cn.iocoder.yudao.module.crm.controller.admin.crmrecord.vo.RecordStatisticsRespVO">
SELECT
creator AS creator,
COUNT(1) AS count,
SUM(CASE WHEN types = 'business' THEN 1 ELSE 0 END) AS businessCount,
SUM(CASE WHEN types = 'customer' THEN 1 ELSE 0 END) AS customerCount,
SUM(CASE WHEN types = 'clues' THEN 1 ELSE 0 END) AS cluesCount
FROM
crm_record
WHERE
deleted = 0
AND creator IN
<foreach item="userId" collection="userIds" separator="," open="(" close=")" index="">
#{userId}
</foreach>
<if test="createTime != null and createTime.length > 0">
<if test="createTime[0] != null">
AND create_time &gt;= #{createTime[0]}
</if>
<if test="createTime[1] != null">
AND create_time &lt;= #{createTime[1]}
</if>
</if>
GROUP BY creator
</select>
</mapper>

View File

@ -1,19 +1,13 @@
package cn.iocoder.yudao.module.product.api.storeproduct; package cn.iocoder.yudao.module.product.api.storeproduct;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.product.api.storeproductattrvalue.dto.StoreProductAttrValueApiDTO;
import cn.iocoder.yudao.module.product.api.storeproductattrvalue.vo.StoreProductAttrValueApiVO;
import cn.iocoder.yudao.module.product.enums.ApiConstants; import cn.iocoder.yudao.module.product.enums.ApiConstants;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory = @FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 商品属性值 API") @Tag(name = "RPC 服务 - 商品属性值 API")
public interface StoreProductApi { public interface StoreProductApi {
@ -21,10 +15,15 @@ public interface StoreProductApi {
String PREFIX = ApiConstants.PREFIX + "/storeProduct"; String PREFIX = ApiConstants.PREFIX + "/storeProduct";
@GetMapping(PREFIX + "/decStockIncSales") @GetMapping(PREFIX + "/decStockIncSales")
@Schema(description = "正常商品库存 减销量") @Schema(description = "正常商品库存 减库存 加销量")
CommonResult<Integer> decStockIncSales(@RequestParam(name = "num") Integer num, CommonResult<Integer> decStockIncSales(@RequestParam(name = "num") Integer num,
@RequestParam(name = "productId") Long productId); @RequestParam(name = "productId") Long productId);
@GetMapping(PREFIX + "/inStockIncSales")
@Schema(description = "正常商品库存 加库存 减销量")
CommonResult<Integer> inStockIncSales(@RequestParam(name = "num") Integer num,
@RequestParam(name = "productId") Long productId);
@GetMapping(PREFIX + "/selectCount") @GetMapping(PREFIX + "/selectCount")
@Schema(description = "获取数量") @Schema(description = "获取数量")
CommonResult<Long> selectCount(); CommonResult<Long> selectCount();

View File

@ -25,9 +25,15 @@ public interface StoreProductAttrValueApi {
CommonResult<List<StoreProductAttrValueApiVO>> getStoreProductAttrValueList(@RequestBody StoreProductAttrValueApiDTO dto); CommonResult<List<StoreProductAttrValueApiVO>> getStoreProductAttrValueList(@RequestBody StoreProductAttrValueApiDTO dto);
@GetMapping(PREFIX + "/decStockIncSales") @GetMapping(PREFIX + "/decStockIncSales")
@Schema(description = "正常商品 加库存 减销量") @Schema(description = "普通商品 减库存 加销量")
CommonResult<Integer> decStockIncSales(@RequestParam(name = "num") Integer num, CommonResult<Integer> decStockIncSales(@RequestParam(name = "num") Integer num,
@RequestParam(name = "productId") Long productId, @RequestParam(name = "productId") Long productId,
@RequestParam(name = "unique") String unique); @RequestParam(name = "unique") String unique);
@GetMapping(PREFIX + "/inStockIncSales")
@Schema(description = "普通商品 加库存 减销量")
CommonResult<Integer> inStockIncSales(@RequestParam(name = "num") Integer num,
@RequestParam(name = "productId") Long productId,
@RequestParam(name = "unique") String unique);
} }

View File

@ -7,6 +7,8 @@ import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource; import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/** /**
* 商品 SPU API 接口实现类 * 商品 SPU API 接口实现类
* *
@ -22,12 +24,17 @@ public class StoreProductApiImpl implements StoreProductApi {
@Override @Override
public CommonResult<Integer> decStockIncSales(Integer num, Long productId) { public CommonResult<Integer> decStockIncSales(Integer num, Long productId) {
return CommonResult.success(storeProductService.decStockIncSales(num, productId)); return success(storeProductService.decStockIncSales(num, productId));
}
@Override
public CommonResult<Integer> inStockIncSales(Integer num, Long productId) {
return success(storeProductService.inStockIncSales(num, productId));
} }
@Override @Override
public CommonResult<Long> selectCount() { public CommonResult<Long> selectCount() {
return CommonResult.success(storeProductService.count()); return success(storeProductService.count());
} }
} }

View File

@ -1,19 +1,20 @@
package cn.iocoder.yudao.module.product.api.storeproductattrvalue; package cn.iocoder.yudao.module.product.api.storeproductattrvalue;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.product.api.storeproductattrvalue.dto.StoreProductAttrValueApiDTO; import cn.iocoder.yudao.module.product.api.storeproductattrvalue.dto.StoreProductAttrValueApiDTO;
import cn.iocoder.yudao.module.product.api.storeproductattrvalue.vo.StoreProductAttrValueApiVO; import cn.iocoder.yudao.module.product.api.storeproductattrvalue.vo.StoreProductAttrValueApiVO;
import cn.iocoder.yudao.module.product.dal.dataobject.storeproductattrvalue.StoreProductAttrValueDO; import cn.iocoder.yudao.module.product.dal.dataobject.storeproductattrvalue.StoreProductAttrValueDO;
import cn.iocoder.yudao.module.product.service.storeproductattrvalue.StoreProductAttrValueService; import cn.iocoder.yudao.module.product.service.storeproductattrvalue.StoreProductAttrValueService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/** /**
* 商品 SPU API 接口实现类 * 商品 SPU API 接口实现类
* *
@ -29,17 +30,22 @@ public class StoreProductAttrValueApiImpl implements StoreProductAttrValueApi {
@Override @Override
public CommonResult<List<StoreProductAttrValueApiVO>> getStoreProductAttrValueList(StoreProductAttrValueApiDTO dto) { public CommonResult<List<StoreProductAttrValueApiVO>> getStoreProductAttrValueList(StoreProductAttrValueApiDTO dto) {
List<StoreProductAttrValueDO> list = storeProductAttrValueService.list(new LambdaQueryWrapper<StoreProductAttrValueDO>() List<StoreProductAttrValueDO> list = storeProductAttrValueService.list(new LambdaQueryWrapperX<StoreProductAttrValueDO>()
.in(CollUtil.isNotEmpty(dto.getProductIds()), StoreProductAttrValueDO::getProductId, dto.getProductIds()) .inIfPresent(StoreProductAttrValueDO::getProductId, dto.getProductIds())
.in(CollUtil.isNotEmpty(dto.getUniques()), StoreProductAttrValueDO::getUnique, dto.getUniques()) .inIfPresent(StoreProductAttrValueDO::getUnique, dto.getUniques())
); );
List<StoreProductAttrValueApiVO> result = BeanUtil.copyToList(list, StoreProductAttrValueApiVO.class); List<StoreProductAttrValueApiVO> result = BeanUtil.copyToList(list, StoreProductAttrValueApiVO.class);
return CommonResult.success(result); return success(result);
} }
@Override @Override
public CommonResult<Integer> decStockIncSales(Integer num, Long productId, String unique) { public CommonResult<Integer> decStockIncSales(Integer num, Long productId, String unique) {
return CommonResult.success(storeProductAttrValueService.decStockIncSales(num, productId, unique)); return success(storeProductAttrValueService.decStockIncSales(num, productId, unique));
}
@Override
public CommonResult<Integer> inStockIncSales(Integer num, Long productId, String unique) {
return success(storeProductAttrValueService.inStockIncSales(num, productId, unique));
} }
} }

View File

@ -28,7 +28,7 @@ public interface StoreProductAttrValueMapper extends BaseMapperX<StoreProductAtt
@Param("unique") String unique); @Param("unique") String unique);
/** /**
* 正常商品 加库存 减销量 * 普通商品 加库存 减销量
* *
* @param num * @param num
* @param productId * @param productId

View File

@ -109,4 +109,12 @@ public interface StoreProductService extends IService<StoreProductDO> {
* @return * @return
*/ */
int decStockIncSales(Integer num, Long productId); int decStockIncSales(Integer num, Long productId);
/**
* 正常商品库存 加库存 减销量
* @param num 数量
* @param productId 产品编号
* @return
*/
Integer inStockIncSales(Integer num, Long productId);
} }

Some files were not shown because too many files have changed in this diff Show More