Commit 490418a1fc21a7806a6c081640d071ffda53eef0

Authored by puhui999
1 parent 9ea06073

CRM-线索:完善数据权限,新增权限关联、场景分页查询,新增权限关联批量查询

Showing 18 changed files with 262 additions and 218 deletions
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
... ... @@ -11,18 +11,20 @@ import cn.iocoder.yudao.module.crm.service.clue.CrmClueService;
11 11 import io.swagger.v3.oas.annotations.Operation;
12 12 import io.swagger.v3.oas.annotations.Parameter;
13 13 import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.servlet.http.HttpServletResponse;
  16 +import jakarta.validation.Valid;
14 17 import org.springframework.security.access.prepost.PreAuthorize;
15 18 import org.springframework.validation.annotation.Validated;
16 19 import org.springframework.web.bind.annotation.*;
17 20  
18   -import jakarta.annotation.Resource;
19   -import jakarta.servlet.http.HttpServletResponse;
20   -import jakarta.validation.Valid;
21 21 import java.io.IOException;
22 22 import java.util.List;
23 23  
24 24 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  25 +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
25 26 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
  27 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
26 28  
27 29 @Tag(name = "管理后台 - 线索")
28 30 @RestController
... ... @@ -70,7 +72,7 @@ public class CrmClueController {
70 72 @Operation(summary = "获得线索分页")
71 73 @PreAuthorize("@ss.hasPermission('crm:clue:query')")
72 74 public CommonResult<PageResult<CrmClueRespVO>> getCluePage(@Valid CrmCluePageReqVO pageVO) {
73   - PageResult<CrmClueDO> pageResult = clueService.getCluePage(pageVO);
  75 + PageResult<CrmClueDO> pageResult = clueService.getCluePage(pageVO, getLoginUserId());
74 76 return success(CrmClueConvert.INSTANCE.convertPage(pageResult));
75 77 }
76 78  
... ... @@ -78,9 +80,9 @@ public class CrmClueController {
78 80 @Operation(summary = "导出线索 Excel")
79 81 @PreAuthorize("@ss.hasPermission('crm:clue:export')")
80 82 @OperateLog(type = EXPORT)
81   - public void exportClueExcel(@Valid CrmClueExportReqVO exportReqVO,
82   - HttpServletResponse response) throws IOException {
83   - List<CrmClueDO> list = clueService.getClueList(exportReqVO);
  83 + public void exportClueExcel(@Valid CrmCluePageReqVO pageReqVO, HttpServletResponse response) throws IOException {
  84 + pageReqVO.setPageSize(PAGE_SIZE_NONE);
  85 + List<CrmClueDO> list = clueService.getCluePage(pageReqVO, getLoginUserId()).getList();
84 86 // 导出 Excel
85 87 List<CrmClueExcelVO> datas = CrmClueConvert.INSTANCE.convertList02(list);
86 88 ExcelUtils.write(response, "线索.xls", "数据", CrmClueExcelVO.class, datas);
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueExportReqVO.java deleted
1   -package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
2   -
3   -import lombok.*;
4   -import java.util.*;
5   -import io.swagger.v3.oas.annotations.media.Schema;
6   -import cn.iocoder.yudao.framework.common.pojo.PageParam;
7   -import java.time.LocalDateTime;
8   -import org.springframework.format.annotation.DateTimeFormat;
9   -
10   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
11   -
12   -@Schema(description = "管理后台 - 线索 Excel 导出 Request VO,参数和 CrmCluePageReqVO 是一致的")
13   -@Data
14   -public class CrmClueExportReqVO {
15   -
16   - @Schema(description = "转化状态", example = "true")
17   - private Boolean transformStatus;
18   -
19   - @Schema(description = "跟进状态", example = "true")
20   - private Boolean followUpStatus;
21   -
22   - @Schema(description = "线索名称", example = "线索xxx")
23   - private String name;
24   -
25   - @Schema(description = "客户id", example = "520")
26   - private Long customerId;
27   -
28   - @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
29   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
30   - private LocalDateTime[] contactNextTime;
31   -
32   - @Schema(description = "电话", example = "18000000000")
33   - private String telephone;
34   -
35   - @Schema(description = "手机号", example = "18000000000")
36   - private String mobile;
37   -
38   - @Schema(description = "地址", example = "北京市海淀区")
39   - private String address;
40   -
41   - @Schema(description = "负责人的用户编号", example = "27199")
42   - private Long ownerUserId;
43   -
44   - @Schema(description = "最后跟进时间")
45   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
46   - private LocalDateTime[] contactLastTime;
47   -
48   - @Schema(description = "创建时间")
49   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
50   - private LocalDateTime[] createTime;
51   -
52   -}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java
1 1 package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
4 6 import io.swagger.v3.oas.annotations.media.Schema;
5 7 import lombok.Data;
6 8 import lombok.EqualsAndHashCode;
... ... @@ -21,4 +23,14 @@ public class CrmCluePageReqVO extends PageParam {
21 23 @Schema(description = "手机号", example = "18000000000")
22 24 private String mobile;
23 25  
  26 + /**
  27 + * 场景类型,为 null 时则表示全部
  28 + */
  29 + @Schema(description = "场景类型", example = "1")
  30 + @InEnum(CrmSceneTypeEnum.class)
  31 + private Integer sceneType;
  32 +
  33 + @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  34 + private Boolean pool; // null 则表示为不是公海数据
  35 +
24 36 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactPageReqVO.java
1 1 package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
4 6 import io.swagger.v3.oas.annotations.media.Schema;
5 7 import lombok.Data;
6 8 import lombok.EqualsAndHashCode;
... ... @@ -33,4 +35,14 @@ public class CrmContactPageReqVO extends PageParam {
33 35 @Schema(description = "微信", example = "zzZ98373")
34 36 private String wechat;
35 37  
  38 + /**
  39 + * 场景类型,为 null 时则表示全部
  40 + */
  41 + @Schema(description = "场景类型", example = "1")
  42 + @InEnum(CrmSceneTypeEnum.class)
  43 + private Integer sceneType;
  44 +
  45 + @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  46 + private Boolean pool; // null 则表示为不是公海数据
  47 +
36 48 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java
1 1 package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
4 6 import io.swagger.v3.oas.annotations.media.Schema;
5 7 import lombok.Data;
6 8 import lombok.EqualsAndHashCode;
... ... @@ -24,4 +26,14 @@ public class CrmContractPageReqVO extends PageParam {
24 26 @Schema(description = "商机编号", example = "10864")
25 27 private Long businessId;
26 28  
  29 + /**
  30 + * 场景类型,为 null 时则表示全部
  31 + */
  32 + @Schema(description = "场景类型", example = "1")
  33 + @InEnum(CrmSceneTypeEnum.class)
  34 + private Integer sceneType;
  35 +
  36 + @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  37 + private Boolean pool; // null 则表示为不是公海数据
  38 +
27 39 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java
1 1 package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
4 6 import io.swagger.v3.oas.annotations.media.Schema;
5 7 import lombok.Data;
6 8 import lombok.EqualsAndHashCode;
... ... @@ -19,4 +21,14 @@ public class CrmReceivablePlanPageReqVO extends PageParam {
19 21 @Schema(description = "合同名称", example = "3473")
20 22 private Long contractId;
21 23  
  24 + /**
  25 + * 场景类型,为 null 时则表示全部
  26 + */
  27 + @Schema(description = "场景类型", example = "1")
  28 + @InEnum(CrmSceneTypeEnum.class)
  29 + private Integer sceneType;
  30 +
  31 + @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  32 + private Boolean pool; // null 则表示为不是公海数据
  33 +
22 34 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java
1 1 package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
4 6 import io.swagger.v3.oas.annotations.media.Schema;
5 7 import lombok.Data;
6 8 import lombok.EqualsAndHashCode;
... ... @@ -21,4 +23,14 @@ public class CrmReceivablePageReqVO extends PageParam {
21 23 @Schema(description = "客户编号", example = "4963")
22 24 private Long customerId;
23 25  
  26 + /**
  27 + * 场景类型,为 null 时则表示全部
  28 + */
  29 + @Schema(description = "场景类型", example = "1")
  30 + @InEnum(CrmSceneTypeEnum.class)
  31 + private Integer sceneType;
  32 +
  33 + @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  34 + private Boolean pool; // null 则表示为不是公海数据
  35 +
24 36 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java
... ... @@ -73,6 +73,13 @@ public class CrmClueDO extends BaseDO {
73 73 */
74 74 private String remark;
75 75  
  76 + /**
  77 + * 负责人的用户编号
  78 + *
  79 + * 关联 AdminUserDO 的 id 字段
  80 + */
  81 + private Long ownerUserId;
  82 +
76 83 // TODO 芋艿:客户级别;
77 84 // TODO 芋艿:线索来源;
78 85 // TODO 芋艿:客户行业;
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java
... ... @@ -2,12 +2,15 @@ package cn.iocoder.yudao.module.crm.dal.mysql.clue;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6   -import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueExportReqVO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
7 6 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
8 7 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
  8 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  9 +import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
  10 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
9 11 import org.apache.ibatis.annotations.Mapper;
10 12  
  13 +import java.util.Collection;
11 14 import java.util.List;
12 15  
13 16 /**
... ... @@ -18,27 +21,30 @@ import java.util.List;
18 21 @Mapper
19 22 public interface CrmClueMapper extends BaseMapperX<CrmClueDO> {
20 23  
21   - default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO reqVO) {
22   - return selectPage(reqVO, new LambdaQueryWrapperX<CrmClueDO>()
23   - .likeIfPresent(CrmClueDO::getName, reqVO.getName())
24   - .likeIfPresent(CrmClueDO::getTelephone, reqVO.getTelephone())
25   - .likeIfPresent(CrmClueDO::getMobile, reqVO.getMobile())
26   - .orderByDesc(CrmClueDO::getId));
  24 + default int updateOwnerUserIdById(Long id, Long ownerUserId) {
  25 + return update(new LambdaUpdateWrapper<CrmClueDO>()
  26 + .eq(CrmClueDO::getId, id)
  27 + .set(CrmClueDO::getOwnerUserId, ownerUserId));
27 28 }
28 29  
29   - default List<CrmClueDO> selectList(CrmClueExportReqVO reqVO) {
30   - return selectList(new LambdaQueryWrapperX<CrmClueDO>()
31   - .eqIfPresent(CrmClueDO::getTransformStatus, reqVO.getTransformStatus())
32   - .eqIfPresent(CrmClueDO::getFollowUpStatus, reqVO.getFollowUpStatus())
33   - .likeIfPresent(CrmClueDO::getName, reqVO.getName())
34   - .eqIfPresent(CrmClueDO::getCustomerId, reqVO.getCustomerId())
35   - .betweenIfPresent(CrmClueDO::getContactNextTime, reqVO.getContactNextTime())
36   - .likeIfPresent(CrmClueDO::getTelephone, reqVO.getTelephone())
37   - .likeIfPresent(CrmClueDO::getMobile, reqVO.getMobile())
38   - .likeIfPresent(CrmClueDO::getAddress, reqVO.getAddress())
39   - .betweenIfPresent(CrmClueDO::getContactLastTime, reqVO.getContactLastTime())
40   - .betweenIfPresent(CrmClueDO::getCreateTime, reqVO.getCreateTime())
41   - .orderByDesc(CrmClueDO::getId));
  30 + default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO pageReqVO, Long userId) {
  31 + MPJLambdaWrapperX<CrmClueDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>();
  32 + // 构建数据权限连表条件
  33 + CrmQueryWrapperUtils.builderPageQuery(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_LEADS.getType(), CrmClueDO::getId,
  34 + userId, pageReqVO.getSceneType(), pageReqVO.getPool());
  35 + mpjLambdaWrapperX.selectAll(CrmClueDO.class)
  36 + .likeIfPresent(CrmClueDO::getName, pageReqVO.getName())
  37 + .likeIfPresent(CrmClueDO::getTelephone, pageReqVO.getTelephone())
  38 + .likeIfPresent(CrmClueDO::getMobile, pageReqVO.getMobile())
  39 + .orderByDesc(CrmClueDO::getId);
  40 + return selectJoinPage(pageReqVO, CrmClueDO.class, mpjLambdaWrapperX);
  41 + }
  42 +
  43 + default List<CrmClueDO> selectBatchIds(Collection<Long> ids, Long userId) {
  44 + MPJLambdaWrapperX<CrmClueDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>();
  45 + // 构建数据权限连表条件
  46 + CrmQueryWrapperUtils.builderListQueryBatch(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_LEADS.getType(), ids, userId);
  47 + return selectJoinList(CrmClueDO.class, mpjLambdaWrapperX);
42 48 }
43 49  
44 50 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/core/annotations/CrmPermission.java
... ... @@ -35,9 +35,8 @@ public @interface CrmPermission {
35 35  
36 36 /**
37 37 * 数据编号,通过 Spring EL 表达式获取
38   - * TODO 数据权限完成后去除 default ""
39 38 */
40   - String bizId() default "";
  39 + String bizId();
41 40  
42 41 /**
43 42 * 操作所需权限级别
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
... ... @@ -71,12 +71,14 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
71 71  
72 72 @Override
73 73 @Transactional(rollbackFor = Exception.class)
74   - @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, level = CrmPermissionLevelEnum.OWNER)
  74 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
75 75 public void deleteBusiness(Long id) {
76 76 // 校验存在
77 77 validateBusinessExists(id);
78 78 // 删除
79 79 businessMapper.deleteById(id);
  80 + // 删除数据权限
  81 + crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_BUSINESS.getType(), id);
80 82 }
81 83  
82 84 private CrmBusinessDO validateBusinessExists(Long id) {
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java
1 1 package cn.iocoder.yudao.module.crm.service.clue;
2 2  
3   -import java.util.*;
4   -import jakarta.validation.*;
5   -import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.*;
6   -import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
7 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueCreateReqVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueUpdateReqVO;
  7 +import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
  8 +import jakarta.validation.Valid;
  9 +
  10 +import java.util.Collection;
  11 +import java.util.List;
8 12  
9 13 /**
10 14 * 线索 Service 接口
... ... @@ -49,22 +53,15 @@ public interface CrmClueService {
49 53 * @param ids 编号
50 54 * @return 线索列表
51 55 */
52   - List<CrmClueDO> getClueList(Collection<Long> ids);
  56 + List<CrmClueDO> getClueList(Collection<Long> ids, Long userId);
53 57  
54 58 /**
55 59 * 获得线索分页
56 60 *
57 61 * @param pageReqVO 分页查询
  62 + * @param userId 用户编号
58 63 * @return 线索分页
59 64 */
60   - PageResult<CrmClueDO> getCluePage(CrmCluePageReqVO pageReqVO);
61   -
62   - /**
63   - * 获得线索列表, 用于 Excel 导出
64   - *
65   - * @param exportReqVO 查询条件
66   - * @return 线索列表
67   - */
68   - List<CrmClueDO> getClueList(CrmClueExportReqVO exportReqVO);
  65 + PageResult<CrmClueDO> getCluePage(CrmCluePageReqVO pageReqVO, Long userId);
69 66  
70 67 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
... ... @@ -4,23 +4,25 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.ListUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueCreateReqVO;
7   -import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueExportReqVO;
8 7 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
9 8 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueUpdateReqVO;
10 9 import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert;
11 10 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
12 11 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper;
  12 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  13 +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
  14 +import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
13 15 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
  16 +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
  17 +import jakarta.annotation.Resource;
14 18 import org.springframework.stereotype.Service;
15 19 import org.springframework.validation.annotation.Validated;
16 20  
17   -import jakarta.annotation.Resource;
18 21 import java.util.Collection;
19 22 import java.util.List;
20 23  
21 24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
22 25 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CLUE_NOT_EXISTS;
23   -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS;
24 26  
25 27 /**
26 28 * 线索 Service 实现类
... ... @@ -35,6 +37,8 @@ public class CrmClueServiceImpl implements CrmClueService {
35 37 private CrmClueMapper clueMapper;
36 38 @Resource
37 39 private CrmCustomerService customerService;
  40 + @Resource
  41 + private CrmPermissionService crmPermissionService;
38 42  
39 43 @Override
40 44 public Long createClue(CrmClueCreateReqVO createReqVO) {
... ... @@ -48,6 +52,7 @@ public class CrmClueServiceImpl implements CrmClueService {
48 52 }
49 53  
50 54 @Override
  55 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
51 56 public void updateClue(CrmClueUpdateReqVO updateReqVO) {
52 57 // 校验存在
53 58 validateClueExists(updateReqVO.getId());
... ... @@ -60,11 +65,14 @@ public class CrmClueServiceImpl implements CrmClueService {
60 65 }
61 66  
62 67 @Override
  68 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
63 69 public void deleteClue(Long id) {
64 70 // 校验存在
65 71 validateClueExists(id);
66 72 // 删除
67 73 clueMapper.deleteById(id);
  74 + // 删除数据权限
  75 + crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_LEADS.getType(), id);
68 76 }
69 77  
70 78 private void validateClueExists(Long id) {
... ... @@ -74,26 +82,22 @@ public class CrmClueServiceImpl implements CrmClueService {
74 82 }
75 83  
76 84 @Override
  85 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.READ)
77 86 public CrmClueDO getClue(Long id) {
78 87 return clueMapper.selectById(id);
79 88 }
80 89  
81 90 @Override
82   - public List<CrmClueDO> getClueList(Collection<Long> ids) {
  91 + public List<CrmClueDO> getClueList(Collection<Long> ids, Long userId) {
83 92 if (CollUtil.isEmpty(ids)) {
84 93 return ListUtil.empty();
85 94 }
86   - return clueMapper.selectBatchIds(ids);
87   - }
88   -
89   - @Override
90   - public PageResult<CrmClueDO> getCluePage(CrmCluePageReqVO pageReqVO) {
91   - return clueMapper.selectPage(pageReqVO);
  95 + return clueMapper.selectBatchIds(ids, userId);
92 96 }
93 97  
94 98 @Override
95   - public List<CrmClueDO> getClueList(CrmClueExportReqVO exportReqVO) {
96   - return clueMapper.selectList(exportReqVO);
  99 + public PageResult<CrmClueDO> getCluePage(CrmCluePageReqVO pageReqVO, Long userId) {
  100 + return clueMapper.selectPage(pageReqVO, userId);
97 101 }
98 102  
99 103 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contactbusinesslink/CrmContactBusinessLinkServiceImpl.java
... ... @@ -12,14 +12,18 @@ import cn.iocoder.yudao.module.crm.convert.contactbusinessslink.CrmContactBusine
12 12 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
13 13 import cn.iocoder.yudao.module.crm.dal.dataobject.contactbusinesslink.CrmContactBusinessLinkDO;
14 14 import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessLinkMapper;
  15 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  16 +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
  17 +import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
15 18 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
  19 +import jakarta.annotation.Resource;
16 20 import org.springframework.stereotype.Service;
17 21 import org.springframework.validation.annotation.Validated;
18 22  
19   -import jakarta.annotation.Resource;
20 23 import java.util.List;
21 24  
22 25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  26 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
23 27 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTACT_BUSINESS_LINK_NOT_EXISTS;
24 28  
25 29 // TODO @puhui999:数据权限的校验;每个操作;
... ... @@ -54,6 +58,7 @@ public class CrmContactBusinessLinkServiceImpl implements CrmContactBusinessLink
54 58 }
55 59  
56 60 @Override
  61 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
57 62 public void updateContactBusinessLink(CrmContactBusinessLinkSaveReqVO updateReqVO) {
58 63 // 校验存在
59 64 validateContactBusinessLinkExists(updateReqVO.getId());
... ... @@ -80,6 +85,7 @@ public class CrmContactBusinessLinkServiceImpl implements CrmContactBusinessLink
80 85 }
81 86  
82 87 @Override
  88 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#id", level = CrmPermissionLevelEnum.READ)
83 89 public CrmContactBusinessLinkDO getContactBusinessLink(Long id) {
84 90 return contactBusinessLinkMapper.selectById(id);
85 91 }
... ... @@ -90,7 +96,7 @@ public class CrmContactBusinessLinkServiceImpl implements CrmContactBusinessLink
90 96 crmContactBusinessLinkPageReqVO.setContactId(pageReqVO.getContactId());
91 97 PageResult<CrmContactBusinessLinkDO> businessLinkDOS = contactBusinessLinkMapper.selectPageByContact(crmContactBusinessLinkPageReqVO);
92 98 List<CrmBusinessDO> businessDOS = crmBusinessService.getBusinessList(CollectionUtils.convertList(businessLinkDOS.getList(),
93   - CrmContactBusinessLinkDO::getBusinessId));
  99 + CrmContactBusinessLinkDO::getBusinessId), getLoginUserId());
94 100 PageResult<CrmBusinessRespVO> pageResult = new PageResult<CrmBusinessRespVO>();
95 101 pageResult.setList(CrmBusinessConvert.INSTANCE.convert(businessDOS));
96 102 pageResult.setTotal(businessLinkDOS.getTotal());
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
... ... @@ -78,6 +78,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
78 78  
79 79 // 删除
80 80 customerMapper.deleteById(id);
  81 + // 删除数据权限
  82 + crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
81 83 }
82 84  
83 85 private void validateCustomerExists(Long id) {
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java
... ... @@ -7,8 +7,8 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
7 7 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
8 8 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
9 9 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
10   -
11 10 import jakarta.validation.Valid;
  11 +
12 12 import java.util.Collection;
13 13 import java.util.List;
14 14  
... ... @@ -58,9 +58,17 @@ public interface CrmPermissionService {
58 58 void deletePermission(Integer bizType, Long bizId, Integer level);
59 59  
60 60 /**
  61 + * 删除数据权限
  62 + *
  63 + * @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
  64 + * @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
  65 + */
  66 + void deletePermission(Integer bizType, Long bizId);
  67 +
  68 + /**
61 69 * 批量删除数据权限
62 70 *
63   - * @param ids 权限编号
  71 + * @param ids 权限编号
64 72 * @param userId 用户编号
65 73 */
66 74 void deletePermissionBatch(Collection<Long> ids, Long userId);
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
... ... @@ -11,11 +11,11 @@ import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
11 11 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
12 12 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
13 13 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  14 +import jakarta.annotation.Resource;
14 15 import org.springframework.stereotype.Service;
15 16 import org.springframework.transaction.annotation.Transactional;
16 17 import org.springframework.validation.annotation.Validated;
17 18  
18   -import jakarta.annotation.Resource;
19 19 import java.util.Collection;
20 20 import java.util.Collections;
21 21 import java.util.List;
... ... @@ -138,6 +138,17 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
138 138 }
139 139  
140 140 @Override
  141 + public void deletePermission(Integer bizType, Long bizId) {
  142 + List<CrmPermissionDO> permissionList = crmPermissionMapper.selectByBizTypeAndBizId(bizType, bizId);
  143 + if (CollUtil.isEmpty(permissionList)) {
  144 + return;
  145 + }
  146 +
  147 + // 删除数据权限
  148 + crmPermissionMapper.deleteBatchIds(convertSet(permissionList, CrmPermissionDO::getId));
  149 + }
  150 +
  151 + @Override
141 152 public void deletePermissionBatch(Collection<Long> ids, Long userId) {
142 153 List<CrmPermissionDO> permissions = crmPermissionMapper.selectBatchIds(ids);
143 154 if (CollUtil.isEmpty(permissions)) {
... ...
yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImplTest.java
... ... @@ -3,19 +3,18 @@ package cn.iocoder.yudao.module.crm.service.clue;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
5 5 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueCreateReqVO;
6   -import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueExportReqVO;
7 6 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
8 7 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueUpdateReqVO;
9 8 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
10 9 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper;
  10 +import jakarta.annotation.Resource;
11 11 import org.junit.jupiter.api.Disabled;
12 12 import org.junit.jupiter.api.Test;
13 13 import org.springframework.context.annotation.Import;
14 14  
15   -import jakarta.annotation.Resource;
16 15 import java.util.List;
17 16  
18   -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
  17 +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
19 18 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
20 19 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
21 20 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
... ... @@ -25,6 +24,7 @@ import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CLUE_NOT_EXIS
25 24 import static org.junit.jupiter.api.Assertions.*;
26 25  
27 26 // TODO 芋艿:单测后续补;
  27 +
28 28 /**
29 29 * {@link CrmClueServiceImpl} 的单元测试类
30 30 *
... ... @@ -90,8 +90,8 @@ public class CrmClueServiceImplTest extends BaseDbUnitTest {
90 90  
91 91 // 调用
92 92 clueService.deleteClue(id);
93   - // 校验数据不存在了
94   - assertNull(clueMapper.selectById(id));
  93 + // 校验数据不存在了
  94 + assertNull(clueMapper.selectById(id));
95 95 }
96 96  
97 97 @Test
... ... @@ -106,110 +106,102 @@ public class CrmClueServiceImplTest extends BaseDbUnitTest {
106 106 @Test
107 107 @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
108 108 public void testGetCluePage() {
109   - // mock 数据
110   - CrmClueDO dbClue = randomPojo(CrmClueDO.class, o -> { // 等会查询到
111   - o.setTransformStatus(null);
112   - o.setFollowUpStatus(null);
113   - o.setName(null);
114   - o.setCustomerId(null);
115   - o.setContactNextTime(null);
116   - o.setTelephone(null);
117   - o.setMobile(null);
118   - o.setAddress(null);
119   - o.setContactLastTime(null);
120   - o.setCreateTime(null);
121   - });
122   - clueMapper.insert(dbClue);
123   - // 测试 transformStatus 不匹配
124   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setTransformStatus(null)));
125   - // 测试 followUpStatus 不匹配
126   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setFollowUpStatus(null)));
127   - // 测试 name 不匹配
128   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setName(null)));
129   - // 测试 customerId 不匹配
130   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setCustomerId(null)));
131   - // 测试 contactNextTime 不匹配
132   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setContactNextTime(null)));
133   - // 测试 telephone 不匹配
134   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setTelephone(null)));
135   - // 测试 mobile 不匹配
136   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setMobile(null)));
137   - // 测试 address 不匹配
138   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setAddress(null)));
139   - // 测试 contactLastTime 不匹配
140   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setContactLastTime(null)));
141   - // 测试 createTime 不匹配
142   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setCreateTime(null)));
143   - // 准备参数
144   - CrmCluePageReqVO reqVO = new CrmCluePageReqVO();
145   - reqVO.setName(null);
146   - reqVO.setTelephone(null);
147   - reqVO.setMobile(null);
148   -
149   - // 调用
150   - PageResult<CrmClueDO> pageResult = clueService.getCluePage(reqVO);
151   - // 断言
152   - assertEquals(1, pageResult.getTotal());
153   - assertEquals(1, pageResult.getList().size());
154   - assertPojoEquals(dbClue, pageResult.getList().get(0));
  109 + // mock 数据
  110 + CrmClueDO dbClue = randomPojo(CrmClueDO.class, o -> { // 等会查询到
  111 + o.setTransformStatus(null);
  112 + o.setFollowUpStatus(null);
  113 + o.setName(null);
  114 + o.setCustomerId(null);
  115 + o.setContactNextTime(null);
  116 + o.setTelephone(null);
  117 + o.setMobile(null);
  118 + o.setAddress(null);
  119 + o.setContactLastTime(null);
  120 + o.setCreateTime(null);
  121 + });
  122 + clueMapper.insert(dbClue);
  123 + // 测试 transformStatus 不匹配
  124 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setTransformStatus(null)));
  125 + // 测试 followUpStatus 不匹配
  126 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setFollowUpStatus(null)));
  127 + // 测试 name 不匹配
  128 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setName(null)));
  129 + // 测试 customerId 不匹配
  130 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setCustomerId(null)));
  131 + // 测试 contactNextTime 不匹配
  132 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setContactNextTime(null)));
  133 + // 测试 telephone 不匹配
  134 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setTelephone(null)));
  135 + // 测试 mobile 不匹配
  136 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setMobile(null)));
  137 + // 测试 address 不匹配
  138 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setAddress(null)));
  139 + // 测试 contactLastTime 不匹配
  140 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setContactLastTime(null)));
  141 + // 测试 createTime 不匹配
  142 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setCreateTime(null)));
  143 + // 准备参数
  144 + CrmCluePageReqVO reqVO = new CrmCluePageReqVO();
  145 + reqVO.setName(null);
  146 + reqVO.setTelephone(null);
  147 + reqVO.setMobile(null);
  148 +
  149 + // 调用
  150 + PageResult<CrmClueDO> pageResult = clueService.getCluePage(reqVO, getLoginUserId());
  151 + // 断言
  152 + assertEquals(1, pageResult.getTotal());
  153 + assertEquals(1, pageResult.getList().size());
  154 + assertPojoEquals(dbClue, pageResult.getList().get(0));
155 155 }
156 156  
157 157 @Test
158 158 @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
159 159 public void testGetClueList() {
160   - // mock 数据
161   - CrmClueDO dbClue = randomPojo(CrmClueDO.class, o -> { // 等会查询到
162   - o.setTransformStatus(null);
163   - o.setFollowUpStatus(null);
164   - o.setName(null);
165   - o.setCustomerId(null);
166   - o.setContactNextTime(null);
167   - o.setTelephone(null);
168   - o.setMobile(null);
169   - o.setAddress(null);
170   - o.setContactLastTime(null);
171   - o.setCreateTime(null);
172   - });
173   - clueMapper.insert(dbClue);
174   - // 测试 transformStatus 不匹配
175   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setTransformStatus(null)));
176   - // 测试 followUpStatus 不匹配
177   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setFollowUpStatus(null)));
178   - // 测试 name 不匹配
179   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setName(null)));
180   - // 测试 customerId 不匹配
181   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setCustomerId(null)));
182   - // 测试 contactNextTime 不匹配
183   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setContactNextTime(null)));
184   - // 测试 telephone 不匹配
185   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setTelephone(null)));
186   - // 测试 mobile 不匹配
187   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setMobile(null)));
188   - // 测试 address 不匹配
189   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setAddress(null)));
190   - // 测试 contactLastTime 不匹配
191   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setContactLastTime(null)));
192   - // 测试 createTime 不匹配
193   - clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setCreateTime(null)));
194   - // 准备参数
195   - CrmClueExportReqVO reqVO = new CrmClueExportReqVO();
196   - reqVO.setTransformStatus(null);
197   - reqVO.setFollowUpStatus(null);
198   - reqVO.setName(null);
199   - reqVO.setCustomerId(null);
200   - reqVO.setContactNextTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
201   - reqVO.setTelephone(null);
202   - reqVO.setMobile(null);
203   - reqVO.setAddress(null);
204   - reqVO.setOwnerUserId(null);
205   - reqVO.setContactLastTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
206   - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
207   -
208   - // 调用
209   - List<CrmClueDO> list = clueService.getClueList(reqVO);
210   - // 断言
211   - assertEquals(1, list.size());
212   - assertPojoEquals(dbClue, list.get(0));
  160 + // mock 数据
  161 + CrmClueDO dbClue = randomPojo(CrmClueDO.class, o -> { // 等会查询到
  162 + o.setTransformStatus(null);
  163 + o.setFollowUpStatus(null);
  164 + o.setName(null);
  165 + o.setCustomerId(null);
  166 + o.setContactNextTime(null);
  167 + o.setTelephone(null);
  168 + o.setMobile(null);
  169 + o.setAddress(null);
  170 + o.setContactLastTime(null);
  171 + o.setCreateTime(null);
  172 + });
  173 + clueMapper.insert(dbClue);
  174 + // 测试 transformStatus 不匹配
  175 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setTransformStatus(null)));
  176 + // 测试 followUpStatus 不匹配
  177 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setFollowUpStatus(null)));
  178 + // 测试 name 不匹配
  179 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setName(null)));
  180 + // 测试 customerId 不匹配
  181 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setCustomerId(null)));
  182 + // 测试 contactNextTime 不匹配
  183 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setContactNextTime(null)));
  184 + // 测试 telephone 不匹配
  185 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setTelephone(null)));
  186 + // 测试 mobile 不匹配
  187 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setMobile(null)));
  188 + // 测试 address 不匹配
  189 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setAddress(null)));
  190 + // 测试 contactLastTime 不匹配
  191 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setContactLastTime(null)));
  192 + // 测试 createTime 不匹配
  193 + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setCreateTime(null)));
  194 + // 准备参数
  195 + CrmCluePageReqVO reqVO = new CrmCluePageReqVO();
  196 + reqVO.setName(null);
  197 + reqVO.setTelephone(null);
  198 + reqVO.setMobile(null);
  199 + reqVO.setPageSize(PAGE_SIZE_NONE);
  200 + // 调用
  201 + List<CrmClueDO> list = clueService.getCluePage(reqVO, 1L).getList();
  202 + // 断言
  203 + assertEquals(1, list.size());
  204 + assertPojoEquals(dbClue, list.get(0));
213 205 }
214 206  
215 207 }
... ...