feat(crm): 增加时间筛选功能并优化客户查询- 新增 TimeQueryVO 类用于时间筛选

- 在 RankController 和 RankService 中集成时间筛选功能- 优化 CrmCustomerMapper 中的查询方法,支持分页和多条件查询
- 在 CrmCustomerDO 中添加 ownUserName 字段
- 更新相关 XML 文件以支持新的查询条件
This commit is contained in:
aikai 2025-03-17 09:12:53 +08:00
parent 05ac097711
commit 6380c668e9
14 changed files with 130 additions and 39 deletions

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.crmanalysis;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.ContractVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.TimeQueryVO;
import cn.iocoder.yudao.module.crm.service.crmanalysis.RankService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -28,14 +29,14 @@ public class RankController {
@GetMapping("/contract")
@Operation(summary = "获得合同排名")
public CommonResult<List<ContractVO>> getContractRank() {
return success(rankService.getContractRank());
public CommonResult<List<ContractVO>> getContractRank(TimeQueryVO vo) {
return success(rankService.getContractRank(vo));
}
@GetMapping("/receivables")
@Operation(summary = "获得回款排名")
public CommonResult<List<ContractVO>> getRecevablesRank() {
return success(rankService.getReceivablesRank());
public CommonResult<List<ContractVO>> getRecevablesRank(TimeQueryVO vo) {
return success(rankService.getReceivablesRank(vo));
}

View File

@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Data
public class TimeQueryVO {
@Schema(description = "时间数组")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] times;
}

View File

@ -44,7 +44,6 @@ public class UserLiveTreeController {
@PostMapping("/create")
@Operation(summary = "创建crm用户结构树")
@PreAuthorize("@ss.hasPermission('crm:user-live-tree:create')")
public CommonResult<Long> createUserLiveTree(@Valid @RequestBody UserLiveTreeSaveReqVO createReqVO) {
return success(userLiveTreeService.createUserLiveTree(createReqVO));
}
@ -58,7 +57,6 @@ public class UserLiveTreeController {
@PutMapping("/update")
@Operation(summary = "更新crm用户结构树")
@PreAuthorize("@ss.hasPermission('crm:user-live-tree:update')")
public CommonResult<Boolean> updateUserLiveTree(@Valid @RequestBody UserLiveTreeSaveReqVO updateReqVO) {
userLiveTreeService.updateUserLiveTree(updateReqVO);
return success(true);
@ -67,7 +65,6 @@ public class UserLiveTreeController {
@DeleteMapping("/delete")
@Operation(summary = "删除crm用户结构树")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('crm:user-live-tree:delete')")
public CommonResult<Boolean> deleteUserLiveTree(@RequestParam("id") Long id) {
userLiveTreeService.deleteUserLiveTree(id);
return success(true);
@ -76,7 +73,6 @@ public class UserLiveTreeController {
@GetMapping("/get")
@Operation(summary = "获得crm用户结构树")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('crm:user-live-tree:query')")
public CommonResult<UserLiveTreeRespVO> getUserLiveTree(@RequestParam("id") Long id) {
UserLiveTreeDO userLiveTree = userLiveTreeService.getUserLiveTree(id);
return success(BeanUtils.toBean(userLiveTree, UserLiveTreeRespVO.class));
@ -93,7 +89,6 @@ public class UserLiveTreeController {
@GetMapping("/page")
@Operation(summary = "获得crm用户结构树分页")
@PreAuthorize("@ss.hasPermission('crm:user-live-tree:query')")
public CommonResult<PageResult<UserLiveTreeListVO>> getUserLiveTreePage(@Valid UserLiveTreePageReqVO pageReqVO) {
return success(userLiveTreeService.getUserLiveTreePage(pageReqVO));
@ -101,7 +96,6 @@ public class UserLiveTreeController {
@GetMapping("/export-excel")
@Operation(summary = "导出crm用户结构树 Excel")
@PreAuthorize("@ss.hasPermission('crm:user-live-tree:export')")
@OperateLog(type = EXPORT)
public void exportUserLiveTreeExcel(@Valid UserLiveTreePageReqVO pageReqVO,
HttpServletResponse response) throws IOException {

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomer;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@ -145,4 +146,7 @@ public class CrmCustomerDO extends BaseDO {
*/
private Integer followStatus;
@TableField(exist = false)
private String ownUserName;
}

View File

@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.ContractVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.TimeQueryVO;
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.dal.dataobject.crmcontract.CrmContractDO;
@ -55,7 +56,7 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
.orderByDesc(CrmContractDO::getId));
}
List<ContractVO> selectContractTop();
List<ContractVO> selectContractTop(@Param("vo") TimeQueryVO vo);
/**
* 获取用户合同产品数量

View File

@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.ContractVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.TimeQueryVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontractreceivables.vo.CrmContractReceivablesPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmcontractreceivables.vo.CrmContractReceivablesRespVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.crmcontractreceivables.CrmContractReceivablesDO;
@ -45,5 +46,5 @@ public interface CrmContractReceivablesMapper extends BaseMapperX<CrmContractRec
.orderByDesc(CrmContractReceivablesDO::getId));
}
List<ContractVO> selectReceivablesTop();
List<ContractVO> selectReceivablesTop(@Param("vo") TimeQueryVO vo);
}

View File

@ -22,24 +22,26 @@ import java.util.List;
@Mapper
public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO reqVO, List<Long> ids) {
// default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO reqVO, List<Long> ids) {
//
//
// return selectPage(reqVO, new LambdaQueryWrapperX<CrmCustomerDO>()
// //.eq(CustomerTypesEnum.OPEN.getValue().equals(reqVO.getType()),)
// .inIfPresent(CrmCustomerDO::getOwnerUserId, ids)
// .likeIfPresent(CrmCustomerDO::getName, reqVO.getName())
// .eqIfPresent(CrmCustomerDO::getMobile, reqVO.getMobile())
// .eqIfPresent(CrmCustomerDO::getTelephone, reqVO.getTelephone())
// .eqIfPresent(CrmCustomerDO::getDealStatus, reqVO.getDealStatus())
// .eqIfPresent(CrmCustomerDO::getLevel, reqVO.getLevel())
// .eqIfPresent(CrmCustomerDO::getIndustry, reqVO.getIndustry())
// .eqIfPresent(CrmCustomerDO::getSource, reqVO.getSource())
// .eqIfPresent(CrmCustomerDO::getWeixin, reqVO.getWeixin())
// .eqIfPresent(CrmCustomerDO::getQq, reqVO.getQq())
// .eqIfPresent(CrmCustomerDO::getFollowStatus, reqVO.getFollowStatus())
// .orderByDesc(CrmCustomerDO::getId));
// }
return selectPage(reqVO, new LambdaQueryWrapperX<CrmCustomerDO>()
//.eq(CustomerTypesEnum.OPEN.getValue().equals(reqVO.getType()),)
.inIfPresent(CrmCustomerDO::getOwnerUserId, ids)
.likeIfPresent(CrmCustomerDO::getName, reqVO.getName())
.eqIfPresent(CrmCustomerDO::getMobile, reqVO.getMobile())
.eqIfPresent(CrmCustomerDO::getTelephone, reqVO.getTelephone())
.eqIfPresent(CrmCustomerDO::getDealStatus, reqVO.getDealStatus())
.eqIfPresent(CrmCustomerDO::getLevel, reqVO.getLevel())
.eqIfPresent(CrmCustomerDO::getIndustry, reqVO.getIndustry())
.eqIfPresent(CrmCustomerDO::getSource, reqVO.getSource())
.eqIfPresent(CrmCustomerDO::getWeixin, reqVO.getWeixin())
.eqIfPresent(CrmCustomerDO::getQq, reqVO.getQq())
.eqIfPresent(CrmCustomerDO::getFollowStatus, reqVO.getFollowStatus())
.orderByDesc(CrmCustomerDO::getId));
}
IPage<CrmCustomerDO> selectPage(@Param("page") IPage page, @Param("dto") CrmCustomerPageReqVO pageReqVO, @Param("ids") List<Long> ids);
IPage<CrmCustomerRespVO> selectPageList2(@Param("page") IPage page, @Param("dto") CrmCustomerPageReqVO pageReqVO, @Param("ids") List<Long> ids);

View File

@ -13,7 +13,7 @@ import javax.annotation.Resource;
@Slf4j
public class AutoConfirmJob {
// TODO: 2024/11/04 - 每个月月初03号 未确认的 自动确认上个月的结算记录
// TODO: 2024/11/04 - 每个月月初04号 0点10分 未确认的 自动确认上个月的结算记录
@Resource
private SalesPerformanceSettlementService salesPerformanceSettlementService;

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.service.crmanalysis;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.ContractVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.TimeQueryVO;
import java.util.List;
@ -13,8 +14,8 @@ import java.util.List;
public interface RankService {
List<ContractVO> getContractRank();
List<ContractVO> getContractRank(TimeQueryVO vo);
List<ContractVO> getReceivablesRank();
List<ContractVO> getReceivablesRank(TimeQueryVO vo);
}

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.crm.service.crmanalysis;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.ContractVO;
import cn.iocoder.yudao.module.crm.controller.admin.crmanalysis.vo.TimeQueryVO;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcontract.CrmContractMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.crmcontractreceivables.CrmContractReceivablesMapper;
import org.springframework.stereotype.Service;
@ -25,12 +26,12 @@ public class RankServiceImpl implements RankService {
@Override
public List<ContractVO> getContractRank() {
return contractMapper.selectContractTop();
public List<ContractVO> getContractRank(TimeQueryVO vo) {
return contractMapper.selectContractTop(vo);
}
@Override
public List<ContractVO> getReceivablesRank() {
return contractReceivablesMapper.selectReceivablesTop();
public List<ContractVO> getReceivablesRank(TimeQueryVO vo) {
return contractReceivablesMapper.selectReceivablesTop(vo);
}
}

View File

@ -126,7 +126,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
&& CollUtil.isNotEmpty(repeatOwnerUserNameList)
&& updateReqVO.getRepeatConfirmationFlag() == 0) {
List<String> items = repeatOwnerUserNameList.stream().distinct().collect(Collectors.toList());
throw exception(new ErrorCode(200012, "该客户已被:" + String.join(",", items) + "添加过,是否继续添加?")); }
throw exception(new ErrorCode(200012, "该客户已被:" + String.join(",", items) + "添加过,是否继续添加?"));
}
// 更新
CrmCustomerDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerDO.class);
@ -260,10 +261,10 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
return PageResult.empty();
}
IPage<CrmCustomerDO> pageResult;
IPage mpPage = MyBatisUtils.buildPage(pageReqVO);
if (pageReqVO.getDistinctFlag() == 0) {
return customerMapper.selectPage(pageReqVO, ids);
pageResult = customerMapper.selectPage(mpPage, pageReqVO, ids);
} else {
IPage mpPage = MyBatisUtils.buildPage(pageReqVO);
pageResult = customerMapper.selectPageList(mpPage, pageReqVO, ids);
}
return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());

View File

@ -75,6 +75,14 @@
t.deleted = 0
AND t.result = 2
AND t.contract_type = 1
<if test="vo.times != null and vo.times.length > 0">
<if test="vo.times[0] != null">
and t.signing_date &gt;= #{vo.times[0]}
</if>
<if test="vo.times[1] != null">
and t.signing_date &lt;= #{vo.times[1]}
</if>
</if>
GROUP BY
t.user_id
ORDER BY

View File

@ -69,6 +69,14 @@
WHERE
t.deleted = 0
AND t.result = 2
<if test="vo.times != null and vo.times.length > 0">
<if test="vo.times[0] != null">
and t.return_time &gt;= #{vo.times[0]}
</if>
<if test="vo.times[1] != null">
and t.return_time &lt;= #{vo.times[1]}
</if>
</if>
GROUP BY
t.user_id
ORDER BY

View File

@ -135,4 +135,57 @@
and t1.deleted = 0
and a.name = #{name}
</select>
<select id="selectPage" resultType="cn.iocoder.yudao.module.crm.dal.dataobject.crmcustomer.CrmCustomerDO">
select
a.*,
t1.nickname as ownUserName
from crm_customer as a
left join system_users as t1 on a.owner_user_id = t1.id
<where>
a.deleted = 0
<if test="ids != null and ids.size() > 0">
and a.owner_user_id in
<foreach item="ownerUserId" collection="ids" separator="," open="(" close=")" index="">
#{ownerUserId}
</foreach>
</if>
<if test="dto.name != null and dto.name != ''">
and a.name like concat('%', #{dto.name}, '%')
</if>
<if test="dto.mobile != null and dto.mobile != ''">
and a.mobile = #{dto.mobile}
</if>
<if test="dto.telephone != null and dto.telephone != ''">
and a.telephone = #{dto.telephone}
</if>
<if test="dto.dealStatus != null">
and a.deal_status = #{dto.dealStatus}
</if>
<if test="dto.level != null">
and a.level = #{dto.level}
</if>
<if test="dto.industry != null">
and a.industry = #{dto.industry}
</if>
<if test="dto.source != null">
and a.source = #{dto.source}
</if>
<if test="dto.weixin != null and dto.weixin != ''">
and a.weixin = #{dto.weixin}
</if>
<if test="dto.qq != null and dto.qq != ''">
and a.qq = #{dto.qq}
</if>
<if test="dto.qq != null and dto.qq != ''">
and a.qq = #{dto.qq}
</if>
<if test="dto.followStatus != null">
and a.follow_status = #{dto.followStatus}
</if>
<if test="dto.ownUserName != null and dto.ownUserName != ''">
and t1.nickname like concat('%', #{dto.ownUserName}, '%')
</if>
</where>
order by a.id desc
</select>
</mapper>