mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-25 16:51:52 +08:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
edea17e781
1
pom.xml
1
pom.xml
@ -21,6 +21,7 @@
|
|||||||
<!-- <module>yudao-module-mp</module>-->
|
<!-- <module>yudao-module-mp</module>-->
|
||||||
<!-- <module>yudao-module-pay</module>-->
|
<!-- <module>yudao-module-pay</module>-->
|
||||||
<!-- <module>yudao-module-mall</module>-->
|
<!-- <module>yudao-module-mall</module>-->
|
||||||
|
<!-- <module>yudao-module-crm</module>-->
|
||||||
<!-- 示例项目 -->
|
<!-- 示例项目 -->
|
||||||
<!-- <module>yudao-example</module>-->
|
<!-- <module>yudao-example</module>-->
|
||||||
</modules>
|
</modules>
|
||||||
|
1
sql/mysql/crm.sql
Normal file
1
sql/mysql/crm.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
SET NAMES utf8mb4;
|
20
sql/mysql/crm_data.sql
Normal file
20
sql/mysql/crm_data.sql
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (184, '回款管理审批状态', 'crm_receivable_check_status', 0, '回款管理审批状态(0 未审核 1 审核通过 2 审核拒绝 3 审核中 4 已撤回)', '1', '2023-10-18 21:44:24', '1', '2023-10-18 21:44:24', b'0', '1970-01-01 00:00:00');
|
||||||
|
|
||||||
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (185, '回款管理-回款方式', 'crm_return_type', 0, '回款管理-回款方式', '1', '2023-10-18 21:54:10', '1', '2023-10-18 21:54:10', b'0', '1970-01-01 00:00:00');
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1389, 0, '未审核', '0', 'crm_receivable_check_status', 0, 'default', '', '0 未审核 ', '1', '2023-10-18 21:46:00', '1', '2023-10-18 21:47:16', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1390, 1, '审核通过', '1', 'crm_receivable_check_status', 0, 'default', '', '1 审核通过', '1', '2023-10-18 21:46:18', '1', '2023-10-18 21:47:08', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1391, 2, '审核拒绝', '2', 'crm_receivable_check_status', 0, 'default', '', ' 2 审核拒绝', '1', '2023-10-18 21:46:58', '1', '2023-10-18 21:47:21', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1392, 3, '审核中', '3', 'crm_receivable_check_status', 0, 'default', '', ' 3 审核中', '1', '2023-10-18 21:47:35', '1', '2023-10-18 21:47:35', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1393, 4, '已撤回', '4', 'crm_receivable_check_status', 0, 'default', '', ' 4 已撤回', '1', '2023-10-18 21:47:46', '1', '2023-10-18 21:47:46', b'0');
|
||||||
|
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1394, 1, '支票', '1', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:29', '1', '2023-10-18 21:54:29', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1395, 2, '现金', '2', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:41', '1', '2023-10-18 21:54:41', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1396, 3, '邮政汇款', '3', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:53', '1', '2023-10-18 21:54:53', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1397, 4, '电汇', '4', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:07', '1', '2023-10-18 21:55:07', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1398, 5, '网上转账', '5', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:24', '1', '2023-10-18 21:55:24', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1399, 6, '支付宝', '6', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:38', '1', '2023-10-18 21:55:38', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1400, 7, '微信支付', '7', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:53', '1', '2023-10-18 21:55:53', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1401, 8, '其他', '8', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:56:06', '1', '2023-10-18 21:56:06', b'0');
|
88
sql/mysql/crm_menu.sql
Normal file
88
sql/mysql/crm_menu.sql
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
-- ----------------------------
|
||||||
|
-- 客户公海配置
|
||||||
|
-- ----------------------------
|
||||||
|
-- 菜单 SQL
|
||||||
|
INSERT INTO system_menu(
|
||||||
|
name, permission, type, sort, parent_id,
|
||||||
|
path, icon, component, status, component_name
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'客户公海配置', '', 2, 0, 2397,
|
||||||
|
'customer-pool-config', 'ep:data-analysis', 'crm/customerPoolConf/index', 0, 'CustomerPoolConf'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 按钮父菜单ID
|
||||||
|
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
|
||||||
|
SELECT @parentId := LAST_INSERT_ID();
|
||||||
|
|
||||||
|
-- 按钮 SQL
|
||||||
|
INSERT INTO system_menu(
|
||||||
|
name, permission, type, sort, parent_id,
|
||||||
|
path, icon, component, status
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'客户公海配置保存', 'crm:customer-pool-config:update', 3, 1, @parentId,
|
||||||
|
'', '', '', 0
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 客户限制配置管理
|
||||||
|
-- ----------------------------
|
||||||
|
-- 菜单 SQL
|
||||||
|
INSERT INTO system_menu(
|
||||||
|
name, permission, type, sort, parent_id,
|
||||||
|
path, icon, component, status, component_name
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'客户限制配置管理', '', 2, 0, 2397,
|
||||||
|
'customer-limit-config', '', 'crm/customerLimitConfig/index', 0, 'CrmCustomerLimitConfig'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 按钮父菜单ID
|
||||||
|
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
|
||||||
|
SELECT @parentId := LAST_INSERT_ID();
|
||||||
|
|
||||||
|
-- 按钮 SQL
|
||||||
|
INSERT INTO system_menu(
|
||||||
|
name, permission, type, sort, parent_id,
|
||||||
|
path, icon, component, status
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'客户限制配置查询', 'crm:customer-limit-config:query', 3, 1, @parentId,
|
||||||
|
'', '', '', 0
|
||||||
|
);
|
||||||
|
INSERT INTO system_menu(
|
||||||
|
name, permission, type, sort, parent_id,
|
||||||
|
path, icon, component, status
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'客户限制配置创建', 'crm:customer-limit-config:create', 3, 2, @parentId,
|
||||||
|
'', '', '', 0
|
||||||
|
);
|
||||||
|
INSERT INTO system_menu(
|
||||||
|
name, permission, type, sort, parent_id,
|
||||||
|
path, icon, component, status
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'客户限制配置更新', 'crm:customer-limit-config:update', 3, 3, @parentId,
|
||||||
|
'', '', '', 0
|
||||||
|
);
|
||||||
|
INSERT INTO system_menu(
|
||||||
|
name, permission, type, sort, parent_id,
|
||||||
|
path, icon, component, status
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'客户限制配置删除', 'crm:customer-limit-config:delete', 3, 4, @parentId,
|
||||||
|
'', '', '', 0
|
||||||
|
);
|
||||||
|
INSERT INTO system_menu(
|
||||||
|
name, permission, type, sort, parent_id,
|
||||||
|
path, icon, component, status
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'客户限制配置导出', 'crm:customer-limit-config:export', 3, 5, @parentId,
|
||||||
|
'', '', '', 0
|
||||||
|
);
|
@ -246,3 +246,11 @@ VALUES (
|
|||||||
'转账订单', '', 2, 3, 1117,
|
'转账订单', '', 2, 3, 1117,
|
||||||
'transfer', 'ep:credit-card', 'pay/transfer/index', 0, 'PayTransfer'
|
'transfer', 'ep:credit-card', 'pay/transfer/index', 0, 'PayTransfer'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- 转账通知脚本
|
||||||
|
|
||||||
|
ALTER TABLE `pay_app`
|
||||||
|
ADD COLUMN `transfer_notify_url` varchar(1024) NOT NULL COMMENT '转账结果的回调地址' AFTER `refund_notify_url`;
|
||||||
|
ALTER TABLE `pay_notify_task`
|
||||||
|
MODIFY COLUMN `merchant_order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '商户订单编号' AFTER `status`,
|
||||||
|
ADD COLUMN `merchant_transfer_id` varchar(64) COMMENT '商户转账单编号' AFTER `merchant_order_id`;
|
@ -535,8 +535,8 @@ CREATE TABLE `infra_demo01_contact` (
|
|||||||
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',
|
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',
|
||||||
`sex` tinyint(1) NOT NULL COMMENT '性别',
|
`sex` tinyint(1) NOT NULL COMMENT '性别',
|
||||||
`birthday` datetime NOT NULL COMMENT '出生年',
|
`birthday` datetime NOT NULL COMMENT '出生年',
|
||||||
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '简介',
|
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '简介',
|
||||||
`avatar` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '头像',
|
`avatar` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '头像',
|
||||||
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
|
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
|
||||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
|
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
|
||||||
@ -651,7 +651,7 @@ CREATE TABLE `infra_demo03_student` (
|
|||||||
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',
|
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',
|
||||||
`sex` tinyint NOT NULL COMMENT '性别',
|
`sex` tinyint NOT NULL COMMENT '性别',
|
||||||
`birthday` datetime NOT NULL COMMENT '出生日期',
|
`birthday` datetime NOT NULL COMMENT '出生日期',
|
||||||
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '简介',
|
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '简介',
|
||||||
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
|
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
|
||||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
|
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
|
||||||
|
@ -35,9 +35,12 @@ public class ServiceErrorCodeRange {
|
|||||||
// 模块 member 错误码区间 [1-004-000-000 ~ 1-005-000-000)
|
// 模块 member 错误码区间 [1-004-000-000 ~ 1-005-000-000)
|
||||||
// 模块 mp 错误码区间 [1-006-000-000 ~ 1-007-000-000)
|
// 模块 mp 错误码区间 [1-006-000-000 ~ 1-007-000-000)
|
||||||
// 模块 pay 错误码区间 [1-007-000-000 ~ 1-008-000-000)
|
// 模块 pay 错误码区间 [1-007-000-000 ~ 1-008-000-000)
|
||||||
// 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000)
|
|
||||||
// 模块 bpm 错误码区间 [1-009-000-000 ~ 1-010-000-000)
|
// 模块 bpm 错误码区间 [1-009-000-000 ~ 1-010-000-000)
|
||||||
|
|
||||||
|
// 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000)
|
||||||
// 模块 trade 错误码区间 [1-011-000-000 ~ 1-012-000-000)
|
// 模块 trade 错误码区间 [1-011-000-000 ~ 1-012-000-000)
|
||||||
// 模块 promotion 错误码区间 [1-013-000-000 ~ 1-014-000-000)
|
// 模块 promotion 错误码区间 [1-013-000-000 ~ 1-014-000-000)
|
||||||
|
|
||||||
|
// 模块 crm 错误码区间 [1-020-000-000 ~ 1-021-000-000)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -280,6 +280,15 @@ public class CollectionUtils {
|
|||||||
return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
|
return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T, U, R> List<R> convertListByFlatMap(Collection<T> from,
|
||||||
|
Function<? super T, ? extends U> mapper,
|
||||||
|
Function<U, ? extends Stream<? extends R>> func) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from,
|
public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from,
|
||||||
Function<T, ? extends Stream<? extends U>> func) {
|
Function<T, ? extends Stream<? extends U>> func) {
|
||||||
if (CollUtil.isEmpty(from)) {
|
if (CollUtil.isEmpty(from)) {
|
||||||
@ -288,4 +297,13 @@ public class CollectionUtils {
|
|||||||
return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T, U, R> Set<R> convertSetByFlatMap(Collection<T> from,
|
||||||
|
Function<? super T, ? extends U> mapper,
|
||||||
|
Function<U, ? extends Stream<? extends R>> func) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashSet<>();
|
||||||
|
}
|
||||||
|
return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package cn.iocoder.yudao.framework.common.validation;
|
||||||
|
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
@Target({
|
||||||
|
ElementType.METHOD,
|
||||||
|
ElementType.FIELD,
|
||||||
|
ElementType.ANNOTATION_TYPE,
|
||||||
|
ElementType.CONSTRUCTOR,
|
||||||
|
ElementType.PARAMETER,
|
||||||
|
ElementType.TYPE_USE
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Constraint(
|
||||||
|
validatedBy = TelephoneValidator.class
|
||||||
|
)
|
||||||
|
public @interface Telephone {
|
||||||
|
|
||||||
|
String message() default "电话格式不正确";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package cn.iocoder.yudao.framework.common.validation;
|
||||||
|
|
||||||
|
import cn.hutool.core.text.CharSequenceUtil;
|
||||||
|
import cn.hutool.core.util.PhoneUtil;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
|
||||||
|
public class TelephoneValidator implements ConstraintValidator<Telephone, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(Telephone annotation) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||||
|
// 如果手机号为空,默认不校验,即校验通过
|
||||||
|
if (CharSequenceUtil.isEmpty(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 校验手机
|
||||||
|
return PhoneUtil.isTel(value) || PhoneUtil.isPhone(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
|||||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -86,4 +87,12 @@ public interface PayClient {
|
|||||||
*/
|
*/
|
||||||
PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO);
|
PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得转账订单信息
|
||||||
|
*
|
||||||
|
* @param outTradeNo 外部订单号
|
||||||
|
* @param type 转账类型
|
||||||
|
* @return 转账信息
|
||||||
|
*/
|
||||||
|
PayTransferRespDTO getTransfer(String outTradeNo, PayTransferTypeEnum type);
|
||||||
}
|
}
|
||||||
|
@ -53,11 +53,24 @@ public class PayTransferRespDTO {
|
|||||||
/**
|
/**
|
||||||
* 创建【WAITING】状态的转账返回
|
* 创建【WAITING】状态的转账返回
|
||||||
*/
|
*/
|
||||||
public static PayTransferRespDTO waitingOf(String channelOrderNo,
|
public static PayTransferRespDTO waitingOf(String channelTransferNo,
|
||||||
String outTransferNo, Object rawData) {
|
String outTransferNo, Object rawData) {
|
||||||
PayTransferRespDTO respDTO = new PayTransferRespDTO();
|
PayTransferRespDTO respDTO = new PayTransferRespDTO();
|
||||||
respDTO.status = PayTransferStatusRespEnum.WAITING.getStatus();
|
respDTO.status = PayTransferStatusRespEnum.WAITING.getStatus();
|
||||||
respDTO.channelTransferNo = channelOrderNo;
|
respDTO.channelTransferNo = channelTransferNo;
|
||||||
|
respDTO.outTransferNo = outTransferNo;
|
||||||
|
respDTO.rawData = rawData;
|
||||||
|
return respDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建【IN_PROGRESS】状态的转账返回
|
||||||
|
*/
|
||||||
|
public static PayTransferRespDTO dealingOf(String channelTransferNo,
|
||||||
|
String outTransferNo, Object rawData) {
|
||||||
|
PayTransferRespDTO respDTO = new PayTransferRespDTO();
|
||||||
|
respDTO.status = PayTransferStatusRespEnum.IN_PROGRESS.getStatus();
|
||||||
|
respDTO.channelTransferNo = channelTransferNo;
|
||||||
respDTO.outTransferNo = outTransferNo;
|
respDTO.outTransferNo = outTransferNo;
|
||||||
respDTO.rawData = rawData;
|
respDTO.rawData = rawData;
|
||||||
return respDTO;
|
return respDTO;
|
||||||
|
@ -188,9 +188,9 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
public final PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||||
|
validatePayTransferReqDTO(reqDTO);
|
||||||
PayTransferRespDTO resp;
|
PayTransferRespDTO resp;
|
||||||
try {
|
try {
|
||||||
validatePayTransferReqDTO(reqDTO);
|
|
||||||
resp = doUnifiedTransfer(reqDTO);
|
resp = doUnifiedTransfer(reqDTO);
|
||||||
} catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
|
} catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
|
||||||
throw ex;
|
throw ex;
|
||||||
@ -219,9 +219,25 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final PayTransferRespDTO getTransfer(String outTradeNo, PayTransferTypeEnum type) {
|
||||||
|
try {
|
||||||
|
return doGetTransfer(outTradeNo, type);
|
||||||
|
} catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
|
||||||
|
throw ex;
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
log.error("[getTransfer][客户端({}) outTradeNo({}) type({}) 查询转账单异常]",
|
||||||
|
getId(), outTradeNo, type, ex);
|
||||||
|
throw buildPayException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO)
|
protected abstract PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO)
|
||||||
throws Throwable;
|
throws Throwable;
|
||||||
|
|
||||||
|
protected abstract PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type)
|
||||||
|
throws Throwable;
|
||||||
|
|
||||||
// ========== 各种工具方法 ==========
|
// ========== 各种工具方法 ==========
|
||||||
|
|
||||||
private PayException buildPayException(Throwable ex) {
|
private PayException buildPayException(Throwable ex) {
|
||||||
|
@ -23,14 +23,8 @@ import com.alipay.api.AlipayResponse;
|
|||||||
import com.alipay.api.DefaultAlipayClient;
|
import com.alipay.api.DefaultAlipayClient;
|
||||||
import com.alipay.api.domain.*;
|
import com.alipay.api.domain.*;
|
||||||
import com.alipay.api.internal.util.AlipaySignature;
|
import com.alipay.api.internal.util.AlipaySignature;
|
||||||
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
|
import com.alipay.api.request.*;
|
||||||
import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
|
import com.alipay.api.response.*;
|
||||||
import com.alipay.api.request.AlipayTradeQueryRequest;
|
|
||||||
import com.alipay.api.request.AlipayTradeRefundRequest;
|
|
||||||
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
|
|
||||||
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
|
|
||||||
import com.alipay.api.response.AlipayTradeQueryResponse;
|
|
||||||
import com.alipay.api.response.AlipayTradeRefundResponse;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -238,37 +232,23 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
|||||||
model.setOutBizNo(reqDTO.getOutTransferNo());
|
model.setOutBizNo(reqDTO.getOutTransferNo());
|
||||||
model.setProductCode("TRANS_ACCOUNT_NO_PWD"); // 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD
|
model.setProductCode("TRANS_ACCOUNT_NO_PWD"); // 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD
|
||||||
model.setBizScene("DIRECT_TRANSFER"); // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER
|
model.setBizScene("DIRECT_TRANSFER"); // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER
|
||||||
|
if (reqDTO.getChannelExtras() != null) {
|
||||||
model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras()));
|
model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras()));
|
||||||
|
}
|
||||||
|
// ② 个性化的参数
|
||||||
|
Participant payeeInfo = new Participant();
|
||||||
PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(reqDTO.getType());
|
PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(reqDTO.getType());
|
||||||
switch (transferType) {
|
switch (transferType) {
|
||||||
// TODO @jason:是不是不用传递 transferType 参数哈?因为应该已经明确是支付宝啦?
|
// TODO @jason:是不是不用传递 transferType 参数哈?因为应该已经明确是支付宝啦?
|
||||||
// @芋艿。 是不是还要考虑转账到银行卡。所以传 transferType 但是转账到银行卡不知道要如何测试??
|
// @芋艿。 是不是还要考虑转账到银行卡。所以传 transferType 但是转账到银行卡不知道要如何测试??
|
||||||
case ALIPAY_BALANCE: {
|
case ALIPAY_BALANCE: {
|
||||||
// ② 个性化的参数
|
|
||||||
Participant payeeInfo = new Participant();
|
|
||||||
payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
|
payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
|
||||||
payeeInfo.setIdentity(reqDTO.getAlipayLogonId()); // 支付宝登录号
|
payeeInfo.setIdentity(reqDTO.getAlipayLogonId()); // 支付宝登录号
|
||||||
payeeInfo.setName(reqDTO.getUserName()); // 支付宝账号姓名
|
payeeInfo.setName(reqDTO.getUserName()); // 支付宝账号姓名
|
||||||
model.setPayeeInfo(payeeInfo);
|
model.setPayeeInfo(payeeInfo);
|
||||||
// 1.3 构建 AlipayFundTransUniTransferRequest
|
break;
|
||||||
AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
|
|
||||||
request.setBizModel(model);
|
|
||||||
// 执行请求
|
|
||||||
AlipayFundTransUniTransferResponse response = client.certificateExecute(request);
|
|
||||||
// 处理结果
|
|
||||||
if (!response.isSuccess()) {
|
|
||||||
// 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询
|
|
||||||
if (ObjectUtils.equalsAny(response.getSubCode(), "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) {
|
|
||||||
return PayTransferRespDTO.waitingOf(null, reqDTO.getOutTransferNo(), response);
|
|
||||||
}
|
|
||||||
return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
|
|
||||||
reqDTO.getOutTransferNo(), response);
|
|
||||||
}
|
|
||||||
return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()),
|
|
||||||
response.getOutBizNo(), response);
|
|
||||||
}
|
}
|
||||||
case BANK_CARD: {
|
case BANK_CARD: {
|
||||||
Participant payeeInfo = new Participant();
|
|
||||||
payeeInfo.setIdentityType("BANKCARD_ACCOUNT");
|
payeeInfo.setIdentityType("BANKCARD_ACCOUNT");
|
||||||
// TODO 待实现
|
// TODO 待实现
|
||||||
throw exception(NOT_IMPLEMENTED);
|
throw exception(NOT_IMPLEMENTED);
|
||||||
@ -277,6 +257,72 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
|||||||
throw exception0(BAD_REQUEST.getCode(), "不正确的转账类型: {}", transferType);
|
throw exception0(BAD_REQUEST.getCode(), "不正确的转账类型: {}", transferType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 1.3 构建 AlipayFundTransUniTransferRequest
|
||||||
|
AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
|
||||||
|
request.setBizModel(model);
|
||||||
|
// 执行请求
|
||||||
|
AlipayFundTransUniTransferResponse response = client.certificateExecute(request);
|
||||||
|
// 处理结果
|
||||||
|
if (!response.isSuccess()) {
|
||||||
|
// 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询,或相同 outBizNo 重新发起转账
|
||||||
|
// 发现 outBizNo 相同 两次请求参数相同. 会返回 "PAYMENT_INFO_INCONSISTENCY", 不知道哪里的问题. 暂时返回 WAIT. 后续job 会轮询
|
||||||
|
if (ObjectUtils.equalsAny(response.getSubCode(),"PAYMENT_INFO_INCONSISTENCY", "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) {
|
||||||
|
return PayTransferRespDTO.waitingOf(null, reqDTO.getOutTransferNo(), response);
|
||||||
|
}
|
||||||
|
return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
|
||||||
|
reqDTO.getOutTransferNo(), response);
|
||||||
|
} else {
|
||||||
|
if (ObjectUtils.equalsAny(response.getStatus(), "REFUND", "FAIL")) { // 转账到银行卡会出现 "REFUND" "FAIL"
|
||||||
|
return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
|
||||||
|
reqDTO.getOutTransferNo(), response);
|
||||||
|
}
|
||||||
|
if (Objects.equals(response.getStatus(), "DEALING")) { // 转账到银行卡会出现 "DEALING" 处理中
|
||||||
|
return PayTransferRespDTO.dealingOf(response.getOrderId(), reqDTO.getOutTransferNo(), response);
|
||||||
|
}
|
||||||
|
return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()),
|
||||||
|
response.getOutBizNo(), response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) throws Throwable {
|
||||||
|
// 1.1 构建 AlipayFundTransCommonQueryModel
|
||||||
|
AlipayFundTransCommonQueryModel model = new AlipayFundTransCommonQueryModel();
|
||||||
|
model.setProductCode(type == PayTransferTypeEnum.BANK_CARD ? "TRANS_BANKCARD_NO_PWD" : "TRANS_ACCOUNT_NO_PWD");
|
||||||
|
model.setBizScene("DIRECT_TRANSFER"); //业务场景
|
||||||
|
model.setOutBizNo(outTradeNo);
|
||||||
|
// 1.2 构建 AlipayFundTransCommonQueryRequest
|
||||||
|
AlipayFundTransCommonQueryRequest request = new AlipayFundTransCommonQueryRequest();
|
||||||
|
request.setBizModel(model);
|
||||||
|
|
||||||
|
// 2.1 执行请求
|
||||||
|
AlipayFundTransCommonQueryResponse response;
|
||||||
|
if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) { // 证书模式
|
||||||
|
response = client.certificateExecute(request);
|
||||||
|
} else {
|
||||||
|
response = client.execute(request);
|
||||||
|
}
|
||||||
|
// 2.2 处理返回结果
|
||||||
|
if (response.isSuccess()) {
|
||||||
|
if (ObjectUtils.equalsAny(response.getStatus(), "REFUND", "FAIL")) { // 转账到银行卡会出现 "REFUND" "FAIL"
|
||||||
|
return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
|
||||||
|
outTradeNo, response);
|
||||||
|
}
|
||||||
|
if (Objects.equals(response.getStatus(), "DEALING")) { // 转账到银行卡会出现 "DEALING" 处理中
|
||||||
|
return PayTransferRespDTO.dealingOf(response.getOrderId(), outTradeNo, response);
|
||||||
|
}
|
||||||
|
return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getPayDate()),
|
||||||
|
response.getOutBizNo(), response);
|
||||||
|
} else {
|
||||||
|
// 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询, 或相同 outBizNo 重新发起转账
|
||||||
|
// 当出现 ORDER_NOT_EXIST 可能是转账还在处理中,也可能是转账处理失败. 返回 WAIT 状态. 后续 job 会轮询, 或相同 outBizNo 重新发起转账
|
||||||
|
if (ObjectUtils.equalsAny(response.getSubCode(), "ORDER_NOT_EXIST", "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) {
|
||||||
|
return PayTransferRespDTO.waitingOf(null, outTradeNo, response);
|
||||||
|
}
|
||||||
|
return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
|
||||||
|
outTradeNo, response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 各种工具方法 ==========
|
// ========== 各种工具方法 ==========
|
||||||
|
@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifie
|
|||||||
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
|
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig;
|
import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig;
|
||||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -71,4 +72,9 @@ public class MockPayClient extends AbstractPayClient<NonePayClientConfig> {
|
|||||||
throw new UnsupportedOperationException("待实现");
|
throw new UnsupportedOperationException("待实现");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) {
|
||||||
|
throw new UnsupportedOperationException("待实现");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDT
|
|||||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
|
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
|
||||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
|
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
|
||||||
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
|
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
|
||||||
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
|
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
|
||||||
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
|
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
|
||||||
@ -431,6 +432,12 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
|||||||
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||||
throw new UnsupportedOperationException("待实现");
|
throw new UnsupportedOperationException("待实现");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) {
|
||||||
|
throw new UnsupportedOperationException("待实现");
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 各种工具方法 ==========
|
// ========== 各种工具方法 ==========
|
||||||
|
|
||||||
static String formatDateV2(LocalDateTime time) {
|
static String formatDateV2(LocalDateTime time) {
|
||||||
|
@ -38,4 +38,8 @@ public enum PayTransferStatusRespEnum {
|
|||||||
public static boolean isClosed(Integer status) {
|
public static boolean isClosed(Integer status) {
|
||||||
return Objects.equals(status, CLOSED.getStatus());
|
return Objects.equals(status, CLOSED.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isInProgress(Integer status) {
|
||||||
|
return Objects.equals(status, IN_PROGRESS.getStatus());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
package cn.iocoder.yudao.framework.flowable.core.context;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作流--用户用到的上下文相关信息
|
||||||
|
*/
|
||||||
|
public class FlowableContextHolder {
|
||||||
|
|
||||||
|
private static final ThreadLocal<Map<String, List<Long>>> ASSIGNEE = new TransmittableThreadLocal<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过流程任务的定义 key ,拿到提前选好的审批人
|
||||||
|
* 此方法目的:首次创建流程实例时,数据库中还查询不到 assignee 字段,所以存入上下文中获取
|
||||||
|
*
|
||||||
|
* @param taskDefinitionKey 流程任务 key
|
||||||
|
* @return 审批人 ID 集合
|
||||||
|
*/
|
||||||
|
public static List<Long> getAssigneeByTaskDefinitionKey(String taskDefinitionKey) {
|
||||||
|
if (CollUtil.isNotEmpty(ASSIGNEE.get())) {
|
||||||
|
return ASSIGNEE.get().get(taskDefinitionKey);
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存入提前选好的审批人到上下文线程变量中
|
||||||
|
*
|
||||||
|
* @param assignee 流程任务 key -> 审批人 ID 炅和
|
||||||
|
*/
|
||||||
|
public static void setAssignee(Map<String, List<Long>> assignee) {
|
||||||
|
ASSIGNEE.set(assignee);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -12,6 +12,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||||
import com.baomidou.mybatisplus.extension.toolkit.Db;
|
import com.baomidou.mybatisplus.extension.toolkit.Db;
|
||||||
import com.github.yulichang.base.MPJBaseMapper;
|
import com.github.yulichang.base.MPJBaseMapper;
|
||||||
|
import com.github.yulichang.interfaces.MPJBaseJoin;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -39,6 +40,13 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
|||||||
return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
|
return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default <DTO> PageResult<DTO> selectJoinPage(PageParam pageParam, Class<DTO> resultTypeClass, MPJBaseJoin<T> joinQueryWrapper) {
|
||||||
|
IPage<DTO> mpPage = MyBatisUtils.buildPage(pageParam);
|
||||||
|
selectJoinPage(mpPage, resultTypeClass, joinQueryWrapper);
|
||||||
|
// 转换返回
|
||||||
|
return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
|
||||||
|
}
|
||||||
|
|
||||||
default T selectOne(String field, Object value) {
|
default T selectOne(String field, Object value) {
|
||||||
return selectOne(new QueryWrapper<T>().eq(field, value));
|
return selectOne(new QueryWrapper<T>().eq(field, value));
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.api.task.dto;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,4 +31,15 @@ public class BpmProcessInstanceCreateReqDTO {
|
|||||||
*/
|
*/
|
||||||
@NotEmpty(message = "业务的唯一标识")
|
@NotEmpty(message = "业务的唯一标识")
|
||||||
private String businessKey;
|
private String businessKey;
|
||||||
|
|
||||||
|
// TODO @hai:assignees 复数
|
||||||
|
/**
|
||||||
|
* 提前指派的审批人
|
||||||
|
*
|
||||||
|
* key:taskKey 任务编码
|
||||||
|
* value:审批人的数组
|
||||||
|
* 例如: { taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批
|
||||||
|
*/
|
||||||
|
private Map<String, List<Long>> assignee;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 流程实例的创建 Request VO")
|
@Schema(description = "管理后台 - 流程实例的创建 Request VO")
|
||||||
@ -17,4 +18,8 @@ public class BpmProcessInstanceCreateReqVO {
|
|||||||
@Schema(description = "变量实例")
|
@Schema(description = "变量实例")
|
||||||
private Map<String, Object> variables;
|
private Map<String, Object> variables;
|
||||||
|
|
||||||
|
// TODO @hai:assignees 复数
|
||||||
|
@Schema(description = "提前指派的审批人", requiredMode = Schema.RequiredMode.REQUIRED, example = "{taskKey1: [1, 2]}")
|
||||||
|
private Map<String, List<Long>> assignee;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,4 +88,11 @@ public class BpmProcessInstanceExtDO extends BaseDO {
|
|||||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
private Map<String, Object> formVariables;
|
private Map<String, Object> formVariables;
|
||||||
|
|
||||||
|
// TODO @hai:assignees 复数
|
||||||
|
/**
|
||||||
|
* 提前设定好的审批人
|
||||||
|
*/
|
||||||
|
@TableField(typeHandler = JacksonTypeHandler.class, exist = false) // TODO 芋艿:临时 exist = false,避免 db 报错;
|
||||||
|
private Map<String, List<Long>> assignee;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
|
|||||||
import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants;
|
import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants;
|
||||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
|
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
|
||||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
|
import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
|
||||||
|
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||||
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.PostApi;
|
import cn.iocoder.yudao.module.system.api.dept.PostApi;
|
||||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||||
@ -39,6 +40,7 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static cn.hutool.core.text.CharSequenceUtil.format;
|
import static cn.hutool.core.text.CharSequenceUtil.format;
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
@ -77,6 +79,9 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
|
|||||||
private DictDataApi dictDataApi;
|
private DictDataApi dictDataApi;
|
||||||
@Resource
|
@Resource
|
||||||
private PermissionApi permissionApi;
|
private PermissionApi permissionApi;
|
||||||
|
@Resource
|
||||||
|
@Lazy // 解决循环依赖
|
||||||
|
private BpmProcessInstanceService processInstanceService;
|
||||||
/**
|
/**
|
||||||
* 任务分配脚本
|
* 任务分配脚本
|
||||||
*/
|
*/
|
||||||
@ -234,6 +239,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
|
|||||||
@Override
|
@Override
|
||||||
@DataPermission(enable = false) // 忽略数据权限,不然分配会存在问题
|
@DataPermission(enable = false) // 忽略数据权限,不然分配会存在问题
|
||||||
public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
|
public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) {
|
||||||
|
// 1. 先从提前选好的审批人中获取
|
||||||
|
List<Long> assignee = processInstanceService.getAssigneeByProcessInstanceIdAndTaskDefinitionKey(
|
||||||
|
execution.getProcessInstanceId(), execution.getCurrentActivityId());
|
||||||
|
if (CollUtil.isNotEmpty(assignee)) {
|
||||||
|
// TODO @hai:new HashSet 即可
|
||||||
|
return convertSet(assignee, Function.identity());
|
||||||
|
}
|
||||||
|
// 2. 通过分配规则,计算审批人
|
||||||
BpmTaskAssignRuleDO rule = getTaskRule(execution);
|
BpmTaskAssignRuleDO rule = getTaskRule(execution);
|
||||||
return calculateTaskCandidateUsers(execution, rule);
|
return calculateTaskCandidateUsers(execution, rule);
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,7 @@ public interface BpmProcessInstanceService {
|
|||||||
*/
|
*/
|
||||||
PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId,
|
PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId,
|
||||||
@Valid BpmProcessInstanceMyPageReqVO pageReqVO);
|
@Valid BpmProcessInstanceMyPageReqVO pageReqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建流程实例(提供给前端)
|
* 创建流程实例(提供给前端)
|
||||||
*
|
*
|
||||||
@ -144,4 +145,14 @@ public interface BpmProcessInstanceService {
|
|||||||
*/
|
*/
|
||||||
void updateProcessInstanceExtReject(String id, String reason);
|
void updateProcessInstanceExtReject(String id, String reason);
|
||||||
|
|
||||||
|
// TODO @hai:改成 getProcessInstanceAssigneesByTaskDefinitionKey(String id, String taskDefinitionKey)
|
||||||
|
/**
|
||||||
|
* 获取流程实例中,取出指定流程任务提前指定的审批人
|
||||||
|
*
|
||||||
|
* @param processInstanceId 流程实例的编号
|
||||||
|
* @param taskDefinitionKey 流程任务定义的 key
|
||||||
|
* @return 审批人集合
|
||||||
|
*/
|
||||||
|
List<Long> getAssigneeByProcessInstanceIdAndTaskDefinitionKey(String processInstanceId, String taskDefinitionKey);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
25
yudao-module-crm/pom.xml
Normal file
25
yudao-module-crm/pom.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modules>
|
||||||
|
<module>yudao-module-crm-api</module>
|
||||||
|
<module>yudao-module-crm-biz</module>
|
||||||
|
</modules>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>yudao-module-crm</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
crm 包下,客户关系管理(Customer Relationship Management)。
|
||||||
|
例如说:客户、联系人、商机、合同、回款等等
|
||||||
|
</description>
|
||||||
|
|
||||||
|
</project>
|
33
yudao-module-crm/yudao-module-crm-api/pom.xml
Normal file
33
yudao-module-crm/yudao-module-crm-api/pom.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-module-crm</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>yudao-module-crm-api</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>
|
||||||
|
crm 模块 API,暴露给其它模块调用
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-common</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 参数校验 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* crm API 包,定义暴露给其它模块的 API
|
||||||
|
*/
|
||||||
|
package cn.iocoder.yudao.module.crm.api;
|
@ -0,0 +1,61 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.enums;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
// TODO @liuhongfeng:这个状态,还是搞成专属 CrmReceivableDO 专属的 status;
|
||||||
|
/**
|
||||||
|
* 流程审批状态枚举类
|
||||||
|
* 0 未审核 1 审核通过 2 审核拒绝 3 审核中 4 已撤回 TODO @liuhongfeng:这一行可以删除,因为已经有枚举属性了哈;
|
||||||
|
* @author 赤焰
|
||||||
|
*/
|
||||||
|
// TODO @liuhongfeng:可以使用 @Getter、@AllArgsConstructor 简化 get、构造方法
|
||||||
|
public enum AuditStatusEnum implements IntArrayValuable {
|
||||||
|
|
||||||
|
// TODO @liuhongfeng:草稿 0;10 审核中;20 审核通过;30 审核拒绝;40 已撤回;主要是留好间隙,万一每个地方要做点拓展; 然后,枚举字段的顺序调整下,审批中,一定要放两个审批通过、拒绝前面哈;
|
||||||
|
/**
|
||||||
|
* 未审批
|
||||||
|
*/
|
||||||
|
AUDIT_NEW(0, "未审批"),
|
||||||
|
/**
|
||||||
|
* 审核通过
|
||||||
|
*/
|
||||||
|
AUDIT_FINISH(1, "审核通过"),
|
||||||
|
/**
|
||||||
|
* 审核拒绝
|
||||||
|
*/
|
||||||
|
AUDIT_REJECT(2, "审核拒绝"),
|
||||||
|
/**
|
||||||
|
* 审核中
|
||||||
|
*/
|
||||||
|
AUDIT_DOING(3, "审核中"),
|
||||||
|
/**
|
||||||
|
* 已撤回
|
||||||
|
*/
|
||||||
|
AUDIT_RETURN(4, "已撤回");
|
||||||
|
|
||||||
|
// TODO liuhongfeng:value 改成 status;desc 改成 name;
|
||||||
|
private final Integer value;
|
||||||
|
private final String desc;
|
||||||
|
|
||||||
|
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AuditStatusEnum::getValue).toArray();
|
||||||
|
|
||||||
|
AuditStatusEnum(Integer value, String desc) {
|
||||||
|
this.value = value;
|
||||||
|
this.desc = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDesc() {
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] array() {
|
||||||
|
return ARRAYS;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRM 字典类型的枚举类
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public interface DictTypeConstants {
|
||||||
|
|
||||||
|
// ========== CRM 模块 ==========
|
||||||
|
String CRM_CUSTOMER_INDUSTRY = "crm_customer_industry"; // CRM 客户所属行业
|
||||||
|
String CRM_CUSTOMER_LEVEL = "crm_customer_level"; // CRM 客户等级
|
||||||
|
String CRM_CUSTOMER_SOURCE = "crm_customer_source"; // CRM 客户来源
|
||||||
|
String CRM_RECEIVABLE_CHECK_STATUS = "crm_receivable_check_status"; // CRM 审批状态
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.enums;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRM 错误码枚举类
|
||||||
|
* <p>
|
||||||
|
* crm 系统,使用 1-020-000-000 段
|
||||||
|
*/
|
||||||
|
public interface ErrorCodeConstants {
|
||||||
|
|
||||||
|
// ========== 合同管理 1-020-000-000 ==========
|
||||||
|
ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在");
|
||||||
|
|
||||||
|
// ========== 线索管理 1-020-001-000 ==========
|
||||||
|
ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
|
||||||
|
|
||||||
|
// ========== 商机管理 1-020-002-000 ==========
|
||||||
|
ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_002_000, "商机不存在");
|
||||||
|
|
||||||
|
// TODO @lilleo:商机状态、商机类型,都单独错误码段
|
||||||
|
|
||||||
|
ErrorCode BUSINESS_STATUS_TYPE_NOT_EXISTS = new ErrorCode(1_020_002_001, "商机状态类型不存在");
|
||||||
|
ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_002_002, "商机状态不存在");
|
||||||
|
|
||||||
|
// ========== 联系人管理 1-020-003-000 ==========
|
||||||
|
ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
|
||||||
|
|
||||||
|
// ========== 回款管理 1-020-004-000 ==========
|
||||||
|
ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款管理不存在");
|
||||||
|
|
||||||
|
// ========== 合同管理 1-020-005-000 ==========
|
||||||
|
ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在");
|
||||||
|
|
||||||
|
// ========== 客户管理 1_020_006_000 ==========
|
||||||
|
ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_006_000, "客户不存在");
|
||||||
|
ErrorCode CUSTOMER_OWNER_EXISTS = new ErrorCode(1_020_006_001, "客户已存在所属负责人");
|
||||||
|
ErrorCode CUSTOMER_LOCKED = new ErrorCode(1_020_006_002, "客户状态已锁定");
|
||||||
|
ErrorCode CUSTOMER_ALREADY_DEAL = new ErrorCode(1_020_006_003, "客户已交易");
|
||||||
|
// TODO @wanwan:这 2 个单独配置段噢
|
||||||
|
ErrorCode CUSTOMER_POOL_CONFIG_ERROR = new ErrorCode(1_020_006_001, "客户公海规则设置不正确");
|
||||||
|
ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_006_002, "客户限制配置不存在");
|
||||||
|
|
||||||
|
// ========== 权限管理 1_020_007_000 ==========
|
||||||
|
ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在");
|
||||||
|
ErrorCode CRM_PERMISSION_DENIED = new ErrorCode(1_020_007_001, "{}操作失败,原因:没有权限");
|
||||||
|
ErrorCode CRM_PERMISSION_MODEL_NOT_EXISTS = new ErrorCode(1_020_007_002, "{}不存在");
|
||||||
|
ErrorCode CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS = new ErrorCode(1_020_007_003, "{}操作失败,原因:转移对象已经是该负责人");
|
||||||
|
|
||||||
|
// ========== 产品 1_020_008_000 ==========
|
||||||
|
ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产品不存在");
|
||||||
|
ErrorCode PRODUCT_NO_EXISTS = new ErrorCode(1_020_008_001, "产品编号已存在");
|
||||||
|
|
||||||
|
// ========== 产品分类 1_020_009_000 ==========
|
||||||
|
ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_020_009_000, "产品分类不存在");
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.enums;
|
||||||
|
|
||||||
|
// TODO @liuhongfeng:这个的作用是?
|
||||||
|
/**
|
||||||
|
* @author 赤焰
|
||||||
|
*/
|
||||||
|
public enum ReturnTypeEnum {
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.enums.customer;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRM 客户等级
|
||||||
|
*
|
||||||
|
* @author Wanwan
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum CrmCustomerLevelEnum implements IntArrayValuable {
|
||||||
|
|
||||||
|
IMPORTANT(1, "A(重点客户)"),
|
||||||
|
GENERAL(2, "B(普通客户)"),
|
||||||
|
LOW_PRIORITY(3, "C(非优先客户)");
|
||||||
|
|
||||||
|
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerLevelEnum::getLevel).toArray();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
*/
|
||||||
|
private final Integer level;
|
||||||
|
/**
|
||||||
|
* 状态名
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] array() {
|
||||||
|
return ARRAYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.enums.customer;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
// TODO @puhui999:这个应该是 crm 全局的,不仅仅属于 customer 客户哈;
|
||||||
|
/**
|
||||||
|
* CRM 客户等级
|
||||||
|
*
|
||||||
|
* @author Wanwan
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum CrmCustomerSceneEnum implements IntArrayValuable {
|
||||||
|
|
||||||
|
OWNER(1, "我负责的客户"),
|
||||||
|
FOLLOW(2, "我关注的客户");
|
||||||
|
|
||||||
|
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerSceneEnum::getType).toArray();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景类型
|
||||||
|
*/
|
||||||
|
private final Integer type;
|
||||||
|
/**
|
||||||
|
* 场景名称
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public static boolean isOwner(Integer type) {
|
||||||
|
return ObjUtil.equal(OWNER.getType(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isFollow(Integer type) {
|
||||||
|
return ObjUtil.equal(FOLLOW.getType(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] array() {
|
||||||
|
return ARRAYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
70
yudao-module-crm/yudao-module-crm-biz/pom.xml
Normal file
70
yudao-module-crm/yudao-module-crm-biz/pom.xml
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-module-crm</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>yudao-module-crm-biz</artifactId>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>
|
||||||
|
crm 包下,客户关系管理(Customer Relationship Management)。
|
||||||
|
例如说:客户、联系人、商机、合同、回款等等
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-module-system-api</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-module-crm-api</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 业务组件 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Web 相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- DB 相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 工具类相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-excel</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Test 测试相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* crm API 实现类,定义暴露给其它模块的 API
|
||||||
|
*/
|
||||||
|
package cn.iocoder.yudao.module.crm.api;
|
@ -0,0 +1,32 @@
|
|||||||
|
### 请求 /transfer
|
||||||
|
PUT {{baseUrl}}/crm/business/transfer
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
tenant-id: {{adminTenentId}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"ownerUserId": 2,
|
||||||
|
"transferType": 2,
|
||||||
|
"permissionType": 2
|
||||||
|
}
|
||||||
|
|
||||||
|
### 请求 /update
|
||||||
|
PUT {{baseUrl}}/crm/business/update
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
tenant-id: {{adminTenentId}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "2",
|
||||||
|
"statusTypeId": 2,
|
||||||
|
"statusId": 2,
|
||||||
|
"customerId": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
### 请求 /get
|
||||||
|
GET {{baseUrl}}/crm/business/get?id=1024
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
tenant-id: {{adminTenentId}}
|
@ -0,0 +1,107 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.business;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||||
|
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.*;
|
||||||
|
import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
|
||||||
|
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.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
|
||||||
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
|
|
||||||
|
@Tag(name = "管理后台 - 商机")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/crm/business")
|
||||||
|
@Validated
|
||||||
|
public class CrmBusinessController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CrmBusinessService businessService;
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@Operation(summary = "创建商机")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business:create')")
|
||||||
|
public CommonResult<Long> createBusiness(@Valid @RequestBody CrmBusinessCreateReqVO createReqVO) {
|
||||||
|
return success(businessService.createBusiness(createReqVO, getLoginUserId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update")
|
||||||
|
@Operation(summary = "更新商机")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business:update')")
|
||||||
|
public CommonResult<Boolean> updateBusiness(@Valid @RequestBody CrmBusinessUpdateReqVO updateReqVO) {
|
||||||
|
businessService.updateBusiness(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除商机")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business:delete')")
|
||||||
|
public CommonResult<Boolean> deleteBusiness(@RequestParam("id") Long id) {
|
||||||
|
businessService.deleteBusiness(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得商机")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
||||||
|
public CommonResult<CrmBusinessRespVO> getBusiness(@RequestParam("id") Long id) {
|
||||||
|
CrmBusinessDO business = businessService.getBusiness(id);
|
||||||
|
return success(CrmBusinessConvert.INSTANCE.convert(business));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/page")
|
||||||
|
@Operation(summary = "获得商机分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
||||||
|
public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPage(@Valid CrmBusinessPageReqVO pageVO) {
|
||||||
|
PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO, getLoginUserId());
|
||||||
|
return success(CrmBusinessConvert.INSTANCE.convertPage(pageResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/pool-page")
|
||||||
|
@Operation(summary = "获得商机公海分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business:query')")
|
||||||
|
public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPoolPage(@Valid CrmBusinessPageReqVO pageVO) {
|
||||||
|
PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO, CrmPermissionDO.POOL_USER_ID);
|
||||||
|
return success(CrmBusinessConvert.INSTANCE.convertPage(pageResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/export-excel")
|
||||||
|
@Operation(summary = "导出商机 Excel")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business:export')")
|
||||||
|
@OperateLog(type = EXPORT)
|
||||||
|
public void exportBusinessExcel(@Valid CrmBusinessExportReqVO exportReqVO,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
List<CrmBusinessDO> list = businessService.getBusinessList(exportReqVO);
|
||||||
|
// 导出 Excel
|
||||||
|
List<CrmBusinessExcelVO> datas = CrmBusinessConvert.INSTANCE.convertList02(list);
|
||||||
|
ExcelUtils.write(response, "商机.xls", "数据", CrmBusinessExcelVO.class, datas);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/transfer")
|
||||||
|
@Operation(summary = "商机转移")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business:update')")
|
||||||
|
public CommonResult<Boolean> transfer(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) {
|
||||||
|
businessService.transferBusiness(reqVO, getLoginUserId());
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* 商机(销售机会)
|
||||||
|
*/
|
||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.business;
|
@ -0,0 +1,57 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
|
||||||
|
|
||||||
|
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.LocalDateTime;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商机 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||||
|
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CrmBusinessBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
||||||
|
@NotNull(message = "商机名称不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
|
||||||
|
@NotNull(message = "商机状态类型不能为空")
|
||||||
|
private Long statusTypeId;
|
||||||
|
|
||||||
|
@Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
|
||||||
|
@NotNull(message = "商机状态不能为空")
|
||||||
|
private Long statusId;
|
||||||
|
|
||||||
|
@Schema(description = "下次联系时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime contactNextTime;
|
||||||
|
|
||||||
|
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
|
||||||
|
@NotNull(message = "客户不能为空")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@Schema(description = "预计成交日期")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime dealTime;
|
||||||
|
|
||||||
|
@Schema(description = "商机金额", example = "12371")
|
||||||
|
private Integer price;
|
||||||
|
|
||||||
|
// TODO @ljileo:折扣使用 Integer 类型,存储时,默认 * 100;展示的时候,前端需要 / 100;避免精度丢失问题
|
||||||
|
@Schema(description = "整单折扣")
|
||||||
|
private Integer discountPercent;
|
||||||
|
|
||||||
|
@Schema(description = "产品总金额", example = "12025")
|
||||||
|
private BigDecimal productPrice;
|
||||||
|
|
||||||
|
@Schema(description = "备注", example = "随便")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 商机创建 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmBusinessCreateReqVO extends CrmBusinessBaseVO {
|
||||||
|
|
||||||
|
// TODO @ljileo:新建的时候,应该可以传递添加的产品;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商机 Excel VO
|
||||||
|
*
|
||||||
|
* @author ljlleo
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CrmBusinessExcelVO {
|
||||||
|
|
||||||
|
@ExcelProperty("主键")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ExcelProperty("商机名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ExcelProperty("商机状态类型编号")
|
||||||
|
private Long statusTypeId;
|
||||||
|
|
||||||
|
@ExcelProperty("商机状态编号")
|
||||||
|
private Long statusId;
|
||||||
|
|
||||||
|
@ExcelProperty("下次联系时间")
|
||||||
|
private LocalDateTime contactNextTime;
|
||||||
|
|
||||||
|
@ExcelProperty("客户编号")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@ExcelProperty("预计成交日期")
|
||||||
|
private LocalDateTime dealTime;
|
||||||
|
|
||||||
|
@ExcelProperty("商机金额")
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
@ExcelProperty("整单折扣")
|
||||||
|
private BigDecimal discountPercent;
|
||||||
|
|
||||||
|
@ExcelProperty("产品总金额")
|
||||||
|
private BigDecimal productPrice;
|
||||||
|
|
||||||
|
@ExcelProperty("备注")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@ExcelProperty("负责人的用户编号")
|
||||||
|
private Long ownerUserId;
|
||||||
|
|
||||||
|
@ExcelProperty("创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@ExcelProperty("只读权限的用户编号数组")
|
||||||
|
private Set<Long> roUserIds;
|
||||||
|
|
||||||
|
@ExcelProperty("读写权限的用户编号数组")
|
||||||
|
private Set<Long> rwUserIds;
|
||||||
|
|
||||||
|
@ExcelProperty("1赢单2输单3无效")
|
||||||
|
private Integer endStatus;
|
||||||
|
|
||||||
|
@ExcelProperty("结束时的备注")
|
||||||
|
private String endRemark;
|
||||||
|
|
||||||
|
@ExcelProperty("最后跟进时间")
|
||||||
|
private LocalDateTime contactLastTime;
|
||||||
|
|
||||||
|
@ExcelProperty("跟进状态")
|
||||||
|
private Integer followUpStatus;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 商机 Excel 导出 Request VO,参数和 CrmBusinessPageReqVO 是一致的")
|
||||||
|
@Data
|
||||||
|
public class CrmBusinessExportReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "商机名称", example = "李四")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "商机状态类型编号", example = "25714")
|
||||||
|
private Long statusTypeId;
|
||||||
|
|
||||||
|
@Schema(description = "商机状态编号", example = "30320")
|
||||||
|
private Long statusId;
|
||||||
|
|
||||||
|
@Schema(description = "下次联系时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] contactNextTime;
|
||||||
|
|
||||||
|
@Schema(description = "客户编号", example = "10299")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@Schema(description = "预计成交日期")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] dealTime;
|
||||||
|
|
||||||
|
@Schema(description = "商机金额", example = "12371")
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
@Schema(description = "整单折扣")
|
||||||
|
private BigDecimal discountPercent;
|
||||||
|
|
||||||
|
@Schema(description = "产品总金额", example = "12025")
|
||||||
|
private BigDecimal productPrice;
|
||||||
|
|
||||||
|
@Schema(description = "备注", example = "随便")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@Schema(description = "负责人的用户编号", example = "25562")
|
||||||
|
private Long ownerUserId;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] createTime;
|
||||||
|
|
||||||
|
@Schema(description = "只读权限的用户编号数组")
|
||||||
|
private String roUserIds;
|
||||||
|
|
||||||
|
@Schema(description = "读写权限的用户编号数组")
|
||||||
|
private String rwUserIds;
|
||||||
|
|
||||||
|
@Schema(description = "1赢单2输单3无效", example = "1")
|
||||||
|
private Integer endStatus;
|
||||||
|
|
||||||
|
@Schema(description = "结束时的备注", example = "你说的对")
|
||||||
|
private String endRemark;
|
||||||
|
|
||||||
|
@Schema(description = "最后跟进时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] contactLastTime;
|
||||||
|
|
||||||
|
@Schema(description = "跟进状态", example = "1")
|
||||||
|
private Integer followUpStatus;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
|
||||||
|
|
||||||
|
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 = "管理后台 - 商机分页 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmBusinessPageReqVO extends PageParam {
|
||||||
|
|
||||||
|
@Schema(description = "商机名称", example = "李四")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.*;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 商机 Response VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmBusinessRespVO extends CrmBusinessBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.crm.framework.enums.CrmPermissionLevelEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 商机转移 Request VO")
|
||||||
|
@Data
|
||||||
|
public class CrmBusinessTransferReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||||
|
@NotNull(message = "联系人编号不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新负责人的用户编号
|
||||||
|
*/
|
||||||
|
@Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||||
|
@NotNull(message = "新负责人的用户编号不能为空")
|
||||||
|
private Long newOwnerUserId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 老负责人加入团队后的权限级别。如果 null 说明移除
|
||||||
|
*
|
||||||
|
* 关联 {@link CrmPermissionLevelEnum}
|
||||||
|
*/
|
||||||
|
@Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||||
|
private Integer oldOwnerPermissionLevel;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 商机更新 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmBusinessUpdateReqVO extends CrmBusinessBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
|
||||||
|
@NotNull(message = "主键不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
// TODO @ljileo:修改的时候,应该可以传递添加的产品;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatus;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||||
|
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo.*;
|
||||||
|
import cn.iocoder.yudao.module.crm.convert.businessstatus.CrmBusinessStatusConvert;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.businessstatus.CrmBusinessStatusDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.businessstatus.CrmBusinessStatusService;
|
||||||
|
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.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
|
||||||
|
|
||||||
|
// TODO @lilleo:这个模块,可以挪到 business 下;这样我打开 business 包下,就知道,噢~原来里面有 business 商机、有 type 状态类型、status 具体状态;
|
||||||
|
@Tag(name = "管理后台 - 商机状态")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/crm/business-status")
|
||||||
|
@Validated
|
||||||
|
public class CrmBusinessStatusController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CrmBusinessStatusService businessStatusService;
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@Operation(summary = "创建商机状态")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status:create')")
|
||||||
|
public CommonResult<Long> createBusinessStatus(@Valid @RequestBody CrmBusinessStatusCreateReqVO createReqVO) {
|
||||||
|
return success(businessStatusService.createBusinessStatus(createReqVO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update")
|
||||||
|
@Operation(summary = "更新商机状态")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status:update')")
|
||||||
|
public CommonResult<Boolean> updateBusinessStatus(@Valid @RequestBody CrmBusinessStatusUpdateReqVO updateReqVO) {
|
||||||
|
businessStatusService.updateBusinessStatus(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除商机状态")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status:delete')")
|
||||||
|
public CommonResult<Boolean> deleteBusinessStatus(@RequestParam("id") Long id) {
|
||||||
|
businessStatusService.deleteBusinessStatus(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得商机状态")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status:query')")
|
||||||
|
public CommonResult<CrmBusinessStatusRespVO> getBusinessStatus(@RequestParam("id") Long id) {
|
||||||
|
CrmBusinessStatusDO businessStatus = businessStatusService.getBusinessStatus(id);
|
||||||
|
return success(CrmBusinessStatusConvert.INSTANCE.convert(businessStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO @lilleo:这个接口,暂时用不到,可以考虑先删除掉
|
||||||
|
@GetMapping("/list")
|
||||||
|
@Operation(summary = "获得商机状态列表")
|
||||||
|
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status:query')")
|
||||||
|
public CommonResult<List<CrmBusinessStatusRespVO>> getBusinessStatusList(@RequestParam("ids") Collection<Long> ids) {
|
||||||
|
List<CrmBusinessStatusDO> list = businessStatusService.getBusinessStatusList(ids);
|
||||||
|
return success(CrmBusinessStatusConvert.INSTANCE.convertList(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/page")
|
||||||
|
@Operation(summary = "获得商机状态分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status:query')")
|
||||||
|
public CommonResult<PageResult<CrmBusinessStatusRespVO>> getBusinessStatusPage(@Valid CrmBusinessStatusPageReqVO pageVO) {
|
||||||
|
PageResult<CrmBusinessStatusDO> pageResult = businessStatusService.getBusinessStatusPage(pageVO);
|
||||||
|
return success(CrmBusinessStatusConvert.INSTANCE.convertPage(pageResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/export-excel")
|
||||||
|
@Operation(summary = "导出商机状态 Excel")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status:export')")
|
||||||
|
@OperateLog(type = EXPORT)
|
||||||
|
public void exportBusinessStatusExcel(@Valid CrmBusinessStatusExportReqVO exportReqVO,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
List<CrmBusinessStatusDO> list = businessStatusService.getBusinessStatusList(exportReqVO);
|
||||||
|
// 导出 Excel
|
||||||
|
List<CrmBusinessStatusExcelVO> datas = CrmBusinessStatusConvert.INSTANCE.convertList02(list);
|
||||||
|
ExcelUtils.write(response, "商机状态.xls", "数据", CrmBusinessStatusExcelVO.class, datas);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 芋艿:后续再看看
|
||||||
|
@GetMapping("/get-simple-list")
|
||||||
|
@Operation(summary = "获得商机状态列表")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status:query')")
|
||||||
|
public CommonResult<List<CrmBusinessStatusRespVO>> getBusinessStatusListByTypeId(@RequestParam("typeId") Integer typeId) {
|
||||||
|
List<CrmBusinessStatusDO> list = businessStatusService.getBusinessStatusListByTypeId(typeId);
|
||||||
|
return success(CrmBusinessStatusConvert.INSTANCE.convertList(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 芋艿:后续再看看
|
||||||
|
@GetMapping("/get-all-list")
|
||||||
|
@Operation(summary = "获得商机状态列表")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status:query')")
|
||||||
|
public CommonResult<List<CrmBusinessStatusRespVO>> getBusinessStatusList() {
|
||||||
|
List<CrmBusinessStatusDO> list = businessStatusService.getBusinessStatusList();
|
||||||
|
return success(CrmBusinessStatusConvert.INSTANCE.convertList(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商机状态 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||||
|
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CrmBusinessStatusBaseVO {
|
||||||
|
|
||||||
|
// TODO @lilleo:example 要写下
|
||||||
|
|
||||||
|
@Schema(description = "状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22882")
|
||||||
|
@NotNull(message = "状态类型编号不能为空")
|
||||||
|
private Long typeId;
|
||||||
|
|
||||||
|
@Schema(description = "状态名", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
||||||
|
@NotNull(message = "状态名不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
// TODO @lilleo:percent 应该是 Integer;
|
||||||
|
@Schema(description = "赢单率")
|
||||||
|
private String percent;
|
||||||
|
|
||||||
|
// TODO @lilleo:这个是不是不用前端新增和修改的时候传递,交给顺序计算出来,存储起来就好了;
|
||||||
|
@Schema(description = "排序")
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
import java.util.*;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import javax.validation.constraints.*;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 商机状态创建 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmBusinessStatusCreateReqVO extends CrmBusinessStatusBaseVO {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
// TODO @lilleo:这个暂时不需要;嘿嘿~不是每个模块都需要导出哈
|
||||||
|
/**
|
||||||
|
* 商机状态 Excel VO
|
||||||
|
*
|
||||||
|
* @author ljlleo
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CrmBusinessStatusExcelVO {
|
||||||
|
|
||||||
|
@ExcelProperty("主键")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ExcelProperty("状态类型编号")
|
||||||
|
private Long typeId;
|
||||||
|
|
||||||
|
@ExcelProperty("状态名")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ExcelProperty("赢单率")
|
||||||
|
private String percent;
|
||||||
|
|
||||||
|
@ExcelProperty("排序")
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
// TODO @lilleo:这个暂时不需要;嘿嘿~不是每个模块都需要导出哈
|
||||||
|
@Schema(description = "管理后台 - 商机状态 Excel 导出 Request VO,参数和 CrmBusinessStatusPageReqVO 是一致的")
|
||||||
|
@Data
|
||||||
|
public class CrmBusinessStatusExportReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "状态类型编号", example = "22882")
|
||||||
|
private Long typeId;
|
||||||
|
|
||||||
|
@Schema(description = "状态名", example = "李四")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "赢单率")
|
||||||
|
private String percent;
|
||||||
|
|
||||||
|
@Schema(description = "排序")
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
|
||||||
|
|
||||||
|
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 = "管理后台 - 商机状态分页 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmBusinessStatusPageReqVO extends PageParam {
|
||||||
|
|
||||||
|
@Schema(description = "状态类型编号", example = "22882")
|
||||||
|
private Long typeId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 商机状态 Response VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmBusinessStatusRespVO extends CrmBusinessStatusBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6802")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 商机状态更新 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmBusinessStatusUpdateReqVO extends CrmBusinessStatusBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6802")
|
||||||
|
@NotNull(message = "编号不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||||
|
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo.*;
|
||||||
|
import cn.iocoder.yudao.module.crm.convert.businessstatustype.CrmBusinessStatusTypeConvert;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.businessstatustype.CrmBusinessStatusTypeDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.businessstatustype.CrmBusinessStatusTypeService;
|
||||||
|
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.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
|
||||||
|
|
||||||
|
// TODO @lilleo:这个模块,可以挪到 business 下;这样我打开 business 包下,就知道,噢~原来里面有 business 商机、有 type 状态类型、status 具体状态;
|
||||||
|
@Tag(name = "管理后台 - 商机状态类型")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/crm/business-status-type")
|
||||||
|
@Validated
|
||||||
|
public class CrmBusinessStatusTypeController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CrmBusinessStatusTypeService businessStatusTypeService;
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@Operation(summary = "创建商机状态类型")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status-type:create')")
|
||||||
|
public CommonResult<Long> createBusinessStatusType(@Valid @RequestBody CrmBusinessStatusTypeCreateReqVO createReqVO) {
|
||||||
|
return success(businessStatusTypeService.createBusinessStatusType(createReqVO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update")
|
||||||
|
@Operation(summary = "更新商机状态类型")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status-type:update')")
|
||||||
|
public CommonResult<Boolean> updateBusinessStatusType(@Valid @RequestBody CrmBusinessStatusTypeUpdateReqVO updateReqVO) {
|
||||||
|
businessStatusTypeService.updateBusinessStatusType(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除商机状态类型")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status-type:delete')")
|
||||||
|
public CommonResult<Boolean> deleteBusinessStatusType(@RequestParam("id") Long id) {
|
||||||
|
businessStatusTypeService.deleteBusinessStatusType(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得商机状态类型")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status-type:query')")
|
||||||
|
public CommonResult<CrmBusinessStatusTypeRespVO> getBusinessStatusType(@RequestParam("id") Long id) {
|
||||||
|
CrmBusinessStatusTypeDO businessStatusType = businessStatusTypeService.getBusinessStatusType(id);
|
||||||
|
return success(CrmBusinessStatusTypeConvert.INSTANCE.convert(businessStatusType));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO @lilleo:这个接口,暂时用不到,可以考虑先删除掉
|
||||||
|
@GetMapping("/list")
|
||||||
|
@Operation(summary = "获得商机状态类型列表")
|
||||||
|
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status-type:query')")
|
||||||
|
public CommonResult<List<CrmBusinessStatusTypeRespVO>> getBusinessStatusTypeList(@RequestParam("ids") Collection<Long> ids) {
|
||||||
|
List<CrmBusinessStatusTypeDO> list = businessStatusTypeService.getBusinessStatusTypeList(ids);
|
||||||
|
return success(CrmBusinessStatusTypeConvert.INSTANCE.convertList(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/page")
|
||||||
|
@Operation(summary = "获得商机状态类型分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status-type:query')")
|
||||||
|
public CommonResult<PageResult<CrmBusinessStatusTypeRespVO>> getBusinessStatusTypePage(@Valid CrmBusinessStatusTypePageReqVO pageVO) {
|
||||||
|
PageResult<CrmBusinessStatusTypeDO> pageResult = businessStatusTypeService.getBusinessStatusTypePage(pageVO);
|
||||||
|
return success(CrmBusinessStatusTypeConvert.INSTANCE.convertPage(pageResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/export-excel")
|
||||||
|
@Operation(summary = "导出商机状态类型 Excel")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status-type:export')")
|
||||||
|
@OperateLog(type = EXPORT)
|
||||||
|
public void exportBusinessStatusTypeExcel(@Valid CrmBusinessStatusTypeExportReqVO exportReqVO,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
List<CrmBusinessStatusTypeDO> list = businessStatusTypeService.getBusinessStatusTypeList(exportReqVO);
|
||||||
|
// 导出 Excel
|
||||||
|
List<CrmBusinessStatusTypeExcelVO> datas = CrmBusinessStatusTypeConvert.INSTANCE.convertList02(list);
|
||||||
|
ExcelUtils.write(response, "商机状态类型.xls", "数据", CrmBusinessStatusTypeExcelVO.class, datas);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get-simple-list")
|
||||||
|
@Operation(summary = "获得商机状态类型列表")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:business-status-type:query')")
|
||||||
|
public CommonResult<List<CrmBusinessStatusTypeRespVO>> getBusinessStatusTypeList() {
|
||||||
|
List<CrmBusinessStatusTypeDO> list = businessStatusTypeService.getBusinessStatusTypeListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||||
|
return success(CrmBusinessStatusTypeConvert.INSTANCE.convertList(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商机状态类型 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||||
|
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CrmBusinessStatusTypeBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "状态类型名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
|
||||||
|
@NotNull(message = "状态类型名不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "使用的部门编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@NotNull(message = "使用的部门编号不能为空")
|
||||||
|
private String deptIds;
|
||||||
|
|
||||||
|
@Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
@NotNull(message = "开启状态不能为空")
|
||||||
|
private Boolean status;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
// TODO 状态类型和状态添加,是在一个请求里,所以需要把 CrmBusinessStatusCreateReqVO 融合进来;
|
||||||
|
@Schema(description = "管理后台 - 商机状态类型创建 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmBusinessStatusTypeCreateReqVO extends CrmBusinessStatusTypeBaseVO {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
// TODO @lilleo:这个暂时不需要;嘿嘿~不是每个模块都需要导出哈
|
||||||
|
/**
|
||||||
|
* 商机状态类型 Excel VO
|
||||||
|
*
|
||||||
|
* @author ljlleo
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CrmBusinessStatusTypeExcelVO {
|
||||||
|
|
||||||
|
@ExcelProperty("主键")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ExcelProperty("状态类型名")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ExcelProperty("使用的部门编号")
|
||||||
|
private String deptIds;
|
||||||
|
|
||||||
|
@ExcelProperty("开启状态")
|
||||||
|
private Boolean status;
|
||||||
|
|
||||||
|
@ExcelProperty("创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.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;
|
||||||
|
|
||||||
|
// TODO @lilleo:这个暂时不需要;嘿嘿~不是每个模块都需要导出哈
|
||||||
|
@Schema(description = "管理后台 - 商机状态类型 Excel 导出 Request VO,参数和 CrmBusinessStatusTypePageReqVO 是一致的")
|
||||||
|
@Data
|
||||||
|
public class CrmBusinessStatusTypeExportReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "状态类型名", example = "芋艿")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "使用的部门编号")
|
||||||
|
private String deptIds;
|
||||||
|
|
||||||
|
@Schema(description = "开启状态", example = "1")
|
||||||
|
private Boolean status;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] createTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
|
||||||
|
|
||||||
|
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 = "管理后台 - 商机状态类型分页 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmBusinessStatusTypePageReqVO extends PageParam {
|
||||||
|
|
||||||
|
@Schema(description = "状态类型名", example = "芋艿")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "开启状态", example = "1")
|
||||||
|
private Boolean status;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.*;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 商机状态类型 Response VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmBusinessStatusTypeRespVO extends CrmBusinessStatusTypeBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "24019")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
// TODO 状态类型和状态添加,是在一个请求里,所以需要把 CrmBusinessStatusUpdateReqVO 融合进来;
|
||||||
|
@Schema(description = "管理后台 - 商机状态类型更新 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmBusinessStatusTypeUpdateReqVO extends CrmBusinessStatusTypeBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "24019")
|
||||||
|
@NotNull(message = "主键不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.clue;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||||
|
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.*;
|
||||||
|
import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.clue.CrmClueService;
|
||||||
|
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.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
|
||||||
|
|
||||||
|
@Tag(name = "管理后台 - 线索")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/crm/clue")
|
||||||
|
@Validated
|
||||||
|
public class CrmClueController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CrmClueService clueService;
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@Operation(summary = "创建线索")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:clue:create')")
|
||||||
|
public CommonResult<Long> createClue(@Valid @RequestBody CrmClueCreateReqVO createReqVO) {
|
||||||
|
return success(clueService.createClue(createReqVO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update")
|
||||||
|
@Operation(summary = "更新线索")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:clue:update')")
|
||||||
|
public CommonResult<Boolean> updateClue(@Valid @RequestBody CrmClueUpdateReqVO updateReqVO) {
|
||||||
|
clueService.updateClue(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除线索")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:clue:delete')")
|
||||||
|
public CommonResult<Boolean> deleteClue(@RequestParam("id") Long id) {
|
||||||
|
clueService.deleteClue(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得线索")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:clue:query')")
|
||||||
|
public CommonResult<CrmClueRespVO> getClue(@RequestParam("id") Long id) {
|
||||||
|
CrmClueDO clue = clueService.getClue(id);
|
||||||
|
return success(CrmClueConvert.INSTANCE.convert(clue));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/page")
|
||||||
|
@Operation(summary = "获得线索分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:clue:query')")
|
||||||
|
public CommonResult<PageResult<CrmClueRespVO>> getCluePage(@Valid CrmCluePageReqVO pageVO) {
|
||||||
|
PageResult<CrmClueDO> pageResult = clueService.getCluePage(pageVO);
|
||||||
|
return success(CrmClueConvert.INSTANCE.convertPage(pageResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/export-excel")
|
||||||
|
@Operation(summary = "导出线索 Excel")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:clue:export')")
|
||||||
|
@OperateLog(type = EXPORT)
|
||||||
|
public void exportClueExcel(@Valid CrmClueExportReqVO exportReqVO,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
List<CrmClueDO> list = clueService.getClueList(exportReqVO);
|
||||||
|
// 导出 Excel
|
||||||
|
List<CrmClueExcelVO> datas = CrmClueConvert.INSTANCE.convertList02(list);
|
||||||
|
ExcelUtils.write(response, "线索.xls", "数据", CrmClueExcelVO.class, datas);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* 线索
|
||||||
|
*/
|
||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.clue;
|
@ -0,0 +1,52 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.validation.Mobile;
|
||||||
|
import cn.iocoder.yudao.framework.common.validation.Telephone;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线索 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||||
|
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CrmClueBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx")
|
||||||
|
@NotEmpty(message = "线索名称不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "客户 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "520")
|
||||||
|
@NotNull(message = "客户不能为空")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime contactNextTime;
|
||||||
|
|
||||||
|
@Schema(description = "电话", example = "18000000000")
|
||||||
|
@Telephone
|
||||||
|
private String telephone;
|
||||||
|
|
||||||
|
@Schema(description = "手机号", example = "18000000000")
|
||||||
|
@Mobile
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
@Schema(description = "地址", example = "北京市海淀区")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Schema(description = "最后跟进时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime contactLastTime;
|
||||||
|
|
||||||
|
@Schema(description = "备注", example = "随便")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
import java.util.*;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import javax.validation.constraints.*;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 线索创建 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmClueCreateReqVO extends CrmClueBaseVO {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线索 Excel VO
|
||||||
|
*
|
||||||
|
* @author Wanwan
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CrmClueExcelVO {
|
||||||
|
|
||||||
|
@ExcelProperty("编号")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ExcelProperty(value = "转化状态", converter = DictConvert.class)
|
||||||
|
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
|
||||||
|
private Boolean transformStatus;
|
||||||
|
|
||||||
|
@ExcelProperty(value = "跟进状态", converter = DictConvert.class)
|
||||||
|
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
|
||||||
|
private Boolean followUpStatus;
|
||||||
|
|
||||||
|
@ExcelProperty("线索名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
// TODO 这里需要导出成客户名称
|
||||||
|
@ExcelProperty("客户id")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@ExcelProperty("下次联系时间")
|
||||||
|
private LocalDateTime contactNextTime;
|
||||||
|
|
||||||
|
@ExcelProperty("电话")
|
||||||
|
private String telephone;
|
||||||
|
|
||||||
|
@ExcelProperty("手机号")
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
@ExcelProperty("地址")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@ExcelProperty("负责人的用户编号")
|
||||||
|
private Long ownerUserId;
|
||||||
|
|
||||||
|
@ExcelProperty("最后跟进时间")
|
||||||
|
private LocalDateTime contactLastTime;
|
||||||
|
|
||||||
|
@ExcelProperty("备注")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@ExcelProperty("创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
import java.util.*;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 线索 Excel 导出 Request VO,参数和 CrmCluePageReqVO 是一致的")
|
||||||
|
@Data
|
||||||
|
public class CrmClueExportReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "转化状态", example = "true")
|
||||||
|
private Boolean transformStatus;
|
||||||
|
|
||||||
|
@Schema(description = "跟进状态", example = "true")
|
||||||
|
private Boolean followUpStatus;
|
||||||
|
|
||||||
|
@Schema(description = "线索名称", example = "线索xxx")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "客户id", example = "520")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] contactNextTime;
|
||||||
|
|
||||||
|
@Schema(description = "电话", example = "18000000000")
|
||||||
|
private String telephone;
|
||||||
|
|
||||||
|
@Schema(description = "手机号", example = "18000000000")
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
@Schema(description = "地址", example = "北京市海淀区")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Schema(description = "负责人的用户编号", example = "27199")
|
||||||
|
private Long ownerUserId;
|
||||||
|
|
||||||
|
@Schema(description = "最后跟进时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] contactLastTime;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] createTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
|
||||||
|
|
||||||
|
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 = "管理后台 - 线索分页 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmCluePageReqVO extends PageParam {
|
||||||
|
|
||||||
|
@Schema(description = "线索名称", example = "线索xxx")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "电话", example = "18000000000")
|
||||||
|
private String telephone;
|
||||||
|
|
||||||
|
@Schema(description = "手机号", example = "18000000000")
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 线索 Response VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmClueRespVO extends CrmClueBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "编号,主键自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "10969")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@Schema(description = "转化状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||||
|
private Boolean transformStatus;
|
||||||
|
|
||||||
|
@Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||||
|
private Boolean followUpStatus;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 线索更新 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmClueUpdateReqVO extends CrmClueBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10969")
|
||||||
|
@NotNull(message = "编号不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contact;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.NumberUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||||
|
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerExportReqVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.convert.contact.ContactConvert;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.ContactDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.contact.ContactService;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||||
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||||
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
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.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
|
||||||
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
|
|
||||||
|
// TODO @zya:crm 所有的类,dou带 Crm 前缀,因为它的名字太通用了,可能和后续的 erp 之类的冲突
|
||||||
|
@Tag(name = "管理后台 - CRM 联系人")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/crm/contact")
|
||||||
|
@Validated
|
||||||
|
public class ContactController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ContactService contactService;
|
||||||
|
// TODO @zyna:模块内,注入的变量,不用带 crm 前缀哈
|
||||||
|
@Resource
|
||||||
|
private CrmCustomerService crmCustomerService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AdminUserApi adminUserApi;
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@Operation(summary = "创建联系人")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contact:create')")
|
||||||
|
public CommonResult<Long> createContact(@Valid @RequestBody ContactCreateReqVO createReqVO) {
|
||||||
|
return success(contactService.createContact(createReqVO, getLoginUserId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update")
|
||||||
|
@Operation(summary = "更新联系人")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contact:update')")
|
||||||
|
public CommonResult<Boolean> updateContact(@Valid @RequestBody ContactUpdateReqVO updateReqVO) {
|
||||||
|
contactService.updateContact(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除联系人")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contact:delete')")
|
||||||
|
public CommonResult<Boolean> deleteContact(@RequestParam("id") Long id) {
|
||||||
|
contactService.deleteContact(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得联系人")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
|
||||||
|
public CommonResult<ContactRespVO> getContact(@RequestParam("id") Long id) {
|
||||||
|
ContactDO contact = contactService.getContact(id);
|
||||||
|
// TODO @zyna:需要考虑 null 的情况;
|
||||||
|
ContactRespVO contactRespVO = ContactConvert.INSTANCE.convert(contact);
|
||||||
|
// TODO @zyna:可以把数据读完后,convert 统一交给 ContactConvert,让 controller 更简洁;而 convert 专门去做一些转换逻辑
|
||||||
|
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(CollUtil.removeNull(Lists.newArrayList(
|
||||||
|
NumberUtil.parseLong(contact.getCreator()))));
|
||||||
|
contactRespVO.setCreatorName(Optional.ofNullable(userMap.get(NumberUtil.parseLong(contact.getCreator()))).map(AdminUserRespDTO::getNickname).orElse(null));
|
||||||
|
contactRespVO.setCustomerName(Optional.ofNullable(crmCustomerService.getCustomer(contact.getCustomerId())).map(CrmCustomerDO::getName).orElse(null));
|
||||||
|
return success(contactRespVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO @zyna:url 使用中划线噢;然后,单词的拼写也要注意呀,AllList 是不是更好呀;
|
||||||
|
@GetMapping("/simpleAlllist")
|
||||||
|
@Operation(summary = "获得联系人列表")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
|
||||||
|
public CommonResult<List<ContactSimpleRespVO>> simpleAlllist() {
|
||||||
|
// TODO @zyna:方法名改成,getContactList;方法命名,要动名词,get 动词;all 可以去掉,因为没条件,自然是全部
|
||||||
|
List<ContactDO> list = contactService.allContactList();
|
||||||
|
return success(ContactConvert.INSTANCE.convertAllList(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/page")
|
||||||
|
@Operation(summary = "获得联系人分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contact:query')")
|
||||||
|
public CommonResult<PageResult<ContactRespVO>> getContactPage(@Valid ContactPageReqVO pageVO) {
|
||||||
|
PageResult<ContactDO> pageData = contactService.getContactPage(pageVO);
|
||||||
|
PageResult<ContactRespVO> pageResult =ContactConvert.INSTANCE.convertPage(pageData);
|
||||||
|
// TODO @zyna:需要考虑 null 的情况;
|
||||||
|
// TODO @zyna:可以把数据读完后,convert 统一交给 ContactConvert,让 controller 更简洁;而 convert 专门去做一些转换逻辑
|
||||||
|
//待接口实现后修改
|
||||||
|
List<CrmCustomerDO> crmCustomerDOList = crmCustomerService.getCustomerList(new CrmCustomerExportReqVO());
|
||||||
|
Map<Long,CrmCustomerDO> crmCustomerDOMap = crmCustomerDOList.stream().collect(Collectors.toMap(CrmCustomerDO::getId,v->v));
|
||||||
|
pageResult.getList().forEach(item -> {
|
||||||
|
item.setCustomerName(Optional.ofNullable(crmCustomerDOMap.get(item.getCustomerId())).map(CrmCustomerDO::getName).orElse(null));
|
||||||
|
});
|
||||||
|
return success(pageResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO @zyna:可以看下新的导出写法,这里调整下
|
||||||
|
@GetMapping("/export-excel")
|
||||||
|
@Operation(summary = "导出联系人 Excel")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contact:export')")
|
||||||
|
@OperateLog(type = EXPORT)
|
||||||
|
public void exportContactExcel(@Valid ContactExportReqVO exportReqVO,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
List<ContactDO> list = contactService.getContactList(exportReqVO);
|
||||||
|
// 导出 Excel
|
||||||
|
List<ContactExcelVO> datas = ContactConvert.INSTANCE.convertList02(list);
|
||||||
|
ExcelUtils.write(response, "crm联系人.xls", "数据", ContactExcelVO.class, datas);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contact.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;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||||
|
|
||||||
|
// TODO zyna:参考新的 vo,重新拆分下 VO
|
||||||
|
/**
|
||||||
|
* CRM 联系人 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||||
|
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ContactBaseVO {
|
||||||
|
|
||||||
|
// TODO @zyna:example 最好都写下
|
||||||
|
// TODO @zyna:必要的字段校验,例如说 @Mobile,@Emal 等等
|
||||||
|
|
||||||
|
@Schema(description = "下次联系时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||||
|
private LocalDateTime nextTime;
|
||||||
|
|
||||||
|
@Schema(description = "手机号")
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
@Schema(description = "电话")
|
||||||
|
private String telephone;
|
||||||
|
|
||||||
|
@Schema(description = "电子邮箱")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Schema(description = "客户编号", example = "10795")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@Schema(description = "地址")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Schema(description = "备注", example = "你说的对")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@Schema(description = "最后跟进时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime lastTime;
|
||||||
|
|
||||||
|
@Schema(description = "直属上级", example = "23457")
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
@Schema(description = "姓名", example = "芋艿")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "职位")
|
||||||
|
private String post;
|
||||||
|
|
||||||
|
@Schema(description = "QQ")
|
||||||
|
private Long qq;
|
||||||
|
|
||||||
|
@Schema(description = "微信")
|
||||||
|
private String webchat;
|
||||||
|
|
||||||
|
@Schema(description = "性别")
|
||||||
|
private Integer sex;
|
||||||
|
|
||||||
|
@Schema(description = "是否关键决策人")
|
||||||
|
private Boolean policyMakers;
|
||||||
|
|
||||||
|
@Schema(description = "负责人用户编号", example = "14334")
|
||||||
|
private String ownerUserId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
|
||||||
|
|
||||||
|
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 ContactCreateReqVO extends ContactBaseVO {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
// TODO @zyna:参考新的 VO 结构,把 ContactExcelVO 融合到 ContactRespVO 中
|
||||||
|
/**
|
||||||
|
* crm联系人 Excel VO
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Deprecated
|
||||||
|
public class ContactExcelVO {
|
||||||
|
|
||||||
|
@ExcelProperty("下次联系时间")
|
||||||
|
private LocalDateTime nextTime;
|
||||||
|
|
||||||
|
@ExcelProperty("手机号")
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
@ExcelProperty("电话")
|
||||||
|
private String telephone;
|
||||||
|
|
||||||
|
@ExcelProperty("电子邮箱")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@ExcelProperty("客户编号")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@ExcelProperty("地址")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@ExcelProperty("备注")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@ExcelProperty("创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@ExcelProperty("最后跟进时间")
|
||||||
|
private LocalDateTime lastTime;
|
||||||
|
|
||||||
|
@ExcelProperty("主键")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ExcelProperty("直属上级")
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
@ExcelProperty("姓名")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ExcelProperty("职位")
|
||||||
|
private String post;
|
||||||
|
|
||||||
|
@ExcelProperty("QQ")
|
||||||
|
private Long qq;
|
||||||
|
|
||||||
|
@ExcelProperty("微信")
|
||||||
|
private String webchat;
|
||||||
|
|
||||||
|
@ExcelProperty("性别")
|
||||||
|
private Integer sex;
|
||||||
|
|
||||||
|
@ExcelProperty("是否关键决策人")
|
||||||
|
private Boolean policyMakers;
|
||||||
|
|
||||||
|
@ExcelProperty("负责人用户编号")
|
||||||
|
private String ownerUserId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contact.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;
|
||||||
|
|
||||||
|
// TODO @zyna:参考新的 VO 结构,使用 ContactPageReqVO 查询导出的数据
|
||||||
|
@Schema(description = "管理后台 - crm联系人 Excel 导出 Request VO,参数和 ContactPageReqVO 是一致的")
|
||||||
|
@Data
|
||||||
|
@Deprecated
|
||||||
|
public class ContactExportReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "下次联系时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] nextTime;
|
||||||
|
|
||||||
|
@Schema(description = "手机号")
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
@Schema(description = "电话")
|
||||||
|
private String telephone;
|
||||||
|
|
||||||
|
@Schema(description = "电子邮箱")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Schema(description = "客户编号", example = "10795")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@Schema(description = "地址")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Schema(description = "备注", example = "你说的对")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] createTime;
|
||||||
|
|
||||||
|
@Schema(description = "最后跟进时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] lastTime;
|
||||||
|
|
||||||
|
@Schema(description = "直属上级", example = "23457")
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
@Schema(description = "姓名", example = "芋艿")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "职位")
|
||||||
|
private String post;
|
||||||
|
|
||||||
|
@Schema(description = "QQ")
|
||||||
|
private Long qq;
|
||||||
|
|
||||||
|
@Schema(description = "微信")
|
||||||
|
private String webchat;
|
||||||
|
|
||||||
|
@Schema(description = "性别")
|
||||||
|
private Integer sex;
|
||||||
|
|
||||||
|
@Schema(description = "是否关键决策人")
|
||||||
|
private Boolean policyMakers;
|
||||||
|
|
||||||
|
@Schema(description = "负责人用户编号", example = "14334")
|
||||||
|
private String ownerUserId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - crm联系人分页 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class ContactPageReqVO extends PageParam {
|
||||||
|
|
||||||
|
// TODO @zyna:筛选条件
|
||||||
|
// ●客户:
|
||||||
|
// ●姓名:
|
||||||
|
// ●手机、电话、座机、QQ、微信、邮箱
|
||||||
|
|
||||||
|
@Schema(description = "下次联系时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] nextTime;
|
||||||
|
|
||||||
|
@Schema(description = "手机号")
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
@Schema(description = "电话")
|
||||||
|
private String telephone;
|
||||||
|
|
||||||
|
@Schema(description = "电子邮箱")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Schema(description = "客户编号", example = "10795")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@Schema(description = "地址")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Schema(description = "备注", example = "你说的对")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] createTime;
|
||||||
|
|
||||||
|
@Schema(description = "最后跟进时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] lastTime;
|
||||||
|
|
||||||
|
@Schema(description = "直属上级", example = "23457")
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
@Schema(description = "姓名", example = "芋艿")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "职位")
|
||||||
|
private String post;
|
||||||
|
|
||||||
|
@Schema(description = "QQ")
|
||||||
|
private Long qq;
|
||||||
|
|
||||||
|
@Schema(description = "微信")
|
||||||
|
private String webchat;
|
||||||
|
|
||||||
|
@Schema(description = "性别")
|
||||||
|
private Integer sex;
|
||||||
|
|
||||||
|
@Schema(description = "是否关键决策人")
|
||||||
|
private Boolean policyMakers;
|
||||||
|
|
||||||
|
@Schema(description = "负责人用户编号", example = "14334")
|
||||||
|
private String ownerUserId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.*;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 联系人 Response VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class ContactRespVO extends ContactBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
// TODO @zyna:example 最好写下;
|
||||||
|
|
||||||
|
@Schema(description = "创建人")
|
||||||
|
private String creatorName;
|
||||||
|
|
||||||
|
@Schema(description = "客户名字")
|
||||||
|
private String customerName;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 联系人 Response VO")
|
||||||
|
@Data
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class ContactSimpleRespVO {
|
||||||
|
@Schema(description = "姓名", example = "芋艿") // TODO @zyna:requiredMode = Schema.RequiredMode.REQUIRED;需要空一行;字段的顺序改下,id 在 name 前面,会更干净
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 联系人更新 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class ContactUpdateReqVO extends ContactBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167")
|
||||||
|
@NotNull(message = "主键不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.crm.framework.enums.CrmPermissionLevelEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 联系人转移 Request VO")
|
||||||
|
@Data
|
||||||
|
public class CrmContactTransferReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||||
|
@NotNull(message = "联系人编号不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新负责人的用户编号
|
||||||
|
*/
|
||||||
|
@Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||||
|
@NotNull(message = "新负责人的用户编号不能为空")
|
||||||
|
private Long newOwnerUserId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 老负责人加入团队后的权限级别。如果 null 说明移除
|
||||||
|
*
|
||||||
|
* 关联 {@link CrmPermissionLevelEnum}
|
||||||
|
*/
|
||||||
|
@Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||||
|
private Integer oldOwnerPermissionLevel;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contract;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||||
|
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*;
|
||||||
|
import cn.iocoder.yudao.module.crm.convert.contract.ContractConvert;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.ContractDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.contract.ContractService;
|
||||||
|
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.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
|
||||||
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
|
|
||||||
|
@Tag(name = "管理后台 - CRM 合同")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/crm/contract")
|
||||||
|
@Validated
|
||||||
|
public class ContractController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ContractService contractService;
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@Operation(summary = "创建合同")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contract:create')")
|
||||||
|
public CommonResult<Long> createContract(@Valid @RequestBody ContractCreateReqVO createReqVO) {
|
||||||
|
return success(contractService.createContract(createReqVO, getLoginUserId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update")
|
||||||
|
@Operation(summary = "更新合同")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
|
||||||
|
public CommonResult<Boolean> updateContract(@Valid @RequestBody ContractUpdateReqVO updateReqVO) {
|
||||||
|
contractService.updateContract(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除合同")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contract:delete')")
|
||||||
|
public CommonResult<Boolean> deleteContract(@RequestParam("id") Long id) {
|
||||||
|
contractService.deleteContract(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得合同")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contract:query')")
|
||||||
|
public CommonResult<ContractRespVO> getContract(@RequestParam("id") Long id) {
|
||||||
|
ContractDO contract = contractService.getContract(id);
|
||||||
|
return success(ContractConvert.INSTANCE.convert(contract));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/page")
|
||||||
|
@Operation(summary = "获得合同分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contract:query')")
|
||||||
|
public CommonResult<PageResult<ContractRespVO>> getContractPage(@Valid ContractPageReqVO pageVO) {
|
||||||
|
PageResult<ContractDO> pageResult = contractService.getContractPage(pageVO);
|
||||||
|
return success(ContractConvert.INSTANCE.convertPage(pageResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/export-excel")
|
||||||
|
@Operation(summary = "导出合同 Excel")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contract:export')")
|
||||||
|
@OperateLog(type = EXPORT)
|
||||||
|
public void exportContractExcel(@Valid ContractExportReqVO exportReqVO,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
List<ContractDO> list = contractService.getContractList(exportReqVO);
|
||||||
|
// 导出 Excel
|
||||||
|
List<ContractExcelVO> datas = ContractConvert.INSTANCE.convertList02(list);
|
||||||
|
ExcelUtils.write(response, "合同.xls", "数据", ContractExcelVO.class, datas);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/transfer")
|
||||||
|
@Operation(summary = "合同转移")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
|
||||||
|
public CommonResult<Boolean> transfer(@Valid @RequestBody CrmContractTransferReqVO reqVO) {
|
||||||
|
contractService.transferContract(reqVO, getLoginUserId());
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||||
|
|
||||||
|
// TODO @dhb52:所有类,带下 Crm 前缀,避免和别的模块重复
|
||||||
|
/**
|
||||||
|
* 合同 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||||
|
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ContractBaseVO {
|
||||||
|
|
||||||
|
// TODO @dhb52:类似 no 字段的 example 要写xia 哈;
|
||||||
|
|
||||||
|
@Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
|
||||||
|
@NotNull(message = "合同名称不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
// TODO @dhb52:这个必须传递
|
||||||
|
@Schema(description = "客户编号", example = "18336")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@Schema(description = "商机编号", example = "10864")
|
||||||
|
private Long businessId;
|
||||||
|
|
||||||
|
@Schema(description = "工作流编号", example = "1043")
|
||||||
|
private Long processInstanceId;
|
||||||
|
|
||||||
|
// TODO @dhb52:这个必须传递
|
||||||
|
@Schema(description = "下单日期")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime orderDate;
|
||||||
|
|
||||||
|
// TODO @dhb52:这个必须传递
|
||||||
|
@Schema(description = "负责人的用户编号", example = "17144")
|
||||||
|
private Long ownerUserId;
|
||||||
|
|
||||||
|
// TODO @芋艿:未来应该支持自动生成;
|
||||||
|
// TODO @dhb52:这个必须传递;
|
||||||
|
@Schema(description = "合同编号")
|
||||||
|
private String no;
|
||||||
|
|
||||||
|
@Schema(description = "开始时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
|
@Schema(description = "结束时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime endTime;
|
||||||
|
|
||||||
|
@Schema(description = "合同金额", example = "5617")
|
||||||
|
private Integer price;
|
||||||
|
|
||||||
|
@Schema(description = "整单折扣")
|
||||||
|
private Integer discountPercent;
|
||||||
|
|
||||||
|
@Schema(description = "产品总金额", example = "19510")
|
||||||
|
private Integer productPrice;
|
||||||
|
|
||||||
|
@Schema(description = "联系人编号", example = "18546")
|
||||||
|
private Long contactId;
|
||||||
|
|
||||||
|
@Schema(description = "公司签约人", example = "14036")
|
||||||
|
private Long signUserId;
|
||||||
|
|
||||||
|
@Schema(description = "最后跟进时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime contactLastTime;
|
||||||
|
|
||||||
|
@Schema(description = "备注", example = "你猜")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
// TODO @dhb52:增加一个 status 字段:具体有哪些值,你来枚举下;主要页面上有个【草稿】【提交审核】的流程,可以看看。然后要对接工作流,这块也可以看看,不确定的地方问我。
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
|
||||||
|
|
||||||
|
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 ContractCreateReqVO extends ContractBaseVO {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRM 合同 Excel VO
|
||||||
|
*
|
||||||
|
* @author dhb52
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ContractExcelVO {
|
||||||
|
|
||||||
|
@ExcelProperty("合同编号")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ExcelProperty("合同名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ExcelProperty("客户编号")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@ExcelProperty("商机编号")
|
||||||
|
private Long businessId;
|
||||||
|
|
||||||
|
@ExcelProperty("工作流编号")
|
||||||
|
private Long processInstanceId;
|
||||||
|
|
||||||
|
@ExcelProperty("下单日期")
|
||||||
|
private LocalDateTime orderDate;
|
||||||
|
|
||||||
|
@ExcelProperty("负责人的用户编号")
|
||||||
|
private Long ownerUserId;
|
||||||
|
|
||||||
|
@ExcelProperty("合同编号")
|
||||||
|
private String no;
|
||||||
|
|
||||||
|
@ExcelProperty("开始时间")
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
|
@ExcelProperty("结束时间")
|
||||||
|
private LocalDateTime endTime;
|
||||||
|
|
||||||
|
@ExcelProperty("合同金额")
|
||||||
|
private Integer price;
|
||||||
|
|
||||||
|
@ExcelProperty("整单折扣")
|
||||||
|
private Integer discountPercent;
|
||||||
|
|
||||||
|
@ExcelProperty("产品总金额")
|
||||||
|
private Integer productPrice;
|
||||||
|
|
||||||
|
@ExcelProperty("联系人编号")
|
||||||
|
private Long contactId;
|
||||||
|
|
||||||
|
@ExcelProperty("公司签约人")
|
||||||
|
private Long signUserId;
|
||||||
|
|
||||||
|
@ExcelProperty("最后跟进时间")
|
||||||
|
private LocalDateTime contactLastTime;
|
||||||
|
|
||||||
|
@ExcelProperty("备注")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@ExcelProperty("创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contract.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;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 合同 Excel 导出 Request VO,参数和 ContractPageReqVO 是一致的")
|
||||||
|
@Data
|
||||||
|
public class ContractExportReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "合同名称", example = "王五")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "客户编号", example = "18336")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@Schema(description = "商机编号", example = "10864")
|
||||||
|
private Long businessId;
|
||||||
|
|
||||||
|
@Schema(description = "下单日期")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] orderDate;
|
||||||
|
|
||||||
|
@Schema(description = "合同编号")
|
||||||
|
private String no;
|
||||||
|
|
||||||
|
@Schema(description = "整单折扣")
|
||||||
|
private Integer discountPercent;
|
||||||
|
|
||||||
|
@Schema(description = "产品总金额", example = "19510")
|
||||||
|
private Integer productPrice;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 合同分页 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class ContractPageReqVO extends PageParam {
|
||||||
|
|
||||||
|
@Schema(description = "合同名称", example = "王五")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "客户编号", example = "18336")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
@Schema(description = "商机编号", example = "10864")
|
||||||
|
private Long businessId;
|
||||||
|
|
||||||
|
@Schema(description = "下单日期")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] orderDate;
|
||||||
|
|
||||||
|
@Schema(description = "合同编号")
|
||||||
|
private String no;
|
||||||
|
|
||||||
|
@Schema(description = "整单折扣")
|
||||||
|
private Integer discountPercent;
|
||||||
|
|
||||||
|
@Schema(description = "产品总金额", example = "19510")
|
||||||
|
private Integer productPrice;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 合同 Response VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class ContractRespVO extends ContractBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 合同更新 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class ContractUpdateReqVO extends ContractBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||||
|
@NotNull(message = "合同编号不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.crm.framework.enums.CrmPermissionLevelEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 合同转移 Request VO")
|
||||||
|
@Data
|
||||||
|
public class CrmContractTransferReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||||
|
@NotNull(message = "联系人编号不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新负责人的用户编号
|
||||||
|
*/
|
||||||
|
@Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||||
|
@NotNull(message = "新负责人的用户编号不能为空")
|
||||||
|
private Long newOwnerUserId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 老负责人加入团队后的权限级别。如果 null 说明移除
|
||||||
|
*
|
||||||
|
* 关联 {@link CrmPermissionLevelEnum}
|
||||||
|
*/
|
||||||
|
@Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||||
|
private Integer oldOwnerPermissionLevel;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,210 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.customer;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||||
|
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;
|
||||||
|
import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.framework.enums.CrmBizTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.crm.framework.enums.CrmPermissionLevelEnum;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
|
||||||
|
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 io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
|
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
|
||||||
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
|
|
||||||
|
@Tag(name = "管理后台 - CRM 客户")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/crm/customer")
|
||||||
|
@Validated
|
||||||
|
public class CrmCustomerController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CrmCustomerService customerService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DeptApi deptApi;
|
||||||
|
@Resource
|
||||||
|
private AdminUserApi adminUserApi;
|
||||||
|
@Resource
|
||||||
|
private CrmPermissionService permissionService;
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@Operation(summary = "创建客户")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer:create')")
|
||||||
|
public CommonResult<Long> createCustomer(@Valid @RequestBody CrmCustomerCreateReqVO createReqVO) {
|
||||||
|
return success(customerService.createCustomer(createReqVO, getLoginUserId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update")
|
||||||
|
@Operation(summary = "更新客户")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer:update')")
|
||||||
|
public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) {
|
||||||
|
customerService.updateCustomer(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除客户")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer:delete')")
|
||||||
|
public CommonResult<Boolean> deleteCustomer(@RequestParam("id") Long id) {
|
||||||
|
customerService.deleteCustomer(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得客户")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||||
|
public CommonResult<CrmCustomerRespVO> getCustomer(@RequestParam("id") Long id) {
|
||||||
|
// 1. 获取客户
|
||||||
|
CrmCustomerDO customer = customerService.getCustomer(id);
|
||||||
|
if (customer == null) {
|
||||||
|
return success(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 拼接数据
|
||||||
|
// 2.1 获取负责人
|
||||||
|
List<CrmPermissionDO> ownerList = permissionService.getPermissionByBizTypeAndBizIdsAndLevel(
|
||||||
|
CrmBizTypeEnum.CRM_CUSTOMER.getType(), Collections.singletonList(customer.getId()),
|
||||||
|
CrmPermissionLevelEnum.OWNER.getLevel());
|
||||||
|
Map<Long, CrmPermissionDO> ownerMap = convertMap(ownerList, CrmPermissionDO::getBizId);
|
||||||
|
// 2.2 获取负责人详情
|
||||||
|
Set<Long> userIds = convertSet(ownerList, CrmPermissionDO::getUserId);
|
||||||
|
userIds.add(Long.parseLong(customer.getCreator())); // 加入创建者
|
||||||
|
List<AdminUserRespDTO> userList = adminUserApi.getUserList(userIds);
|
||||||
|
Map<Long, AdminUserRespDTO> userMap = convertMap(userList, AdminUserRespDTO::getId);
|
||||||
|
// 2.3 获取部门详情
|
||||||
|
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userList, AdminUserRespDTO::getDeptId));
|
||||||
|
return success(CrmCustomerConvert.INSTANCE.convert(customer, ownerMap, userMap, deptMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:可以在 CrmCustomerPageReqVO 里面加个 pool 参数,为 true 时,代表来自公海客户的分页
|
||||||
|
@GetMapping("/page")
|
||||||
|
@Operation(summary = "获得客户分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||||
|
public CommonResult<PageResult<CrmCustomerRespVO>> getCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {
|
||||||
|
PageResult<CrmCustomerDO> pageResult = customerService.getCustomerPage(pageVO, getLoginUserId());
|
||||||
|
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||||
|
return success(PageResult.empty(pageResult.getTotal()));
|
||||||
|
}
|
||||||
|
// 拼接数据
|
||||||
|
// TODO @puhui999:这块的拼接逻辑,可以和 convertPage 合并下;
|
||||||
|
// Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||||
|
// convertSetByFlatMap(pageResult.getList(), user -> Stream.of(NumberUtil.parseLong(user.getCreator()), user.getOwnerUserId())));
|
||||||
|
// Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
|
||||||
|
// convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||||
|
return convertPage(customerService.getCustomerPage(pageVO, getLoginUserId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:
|
||||||
|
@GetMapping("/pool-page")
|
||||||
|
@Operation(summary = "获得公海客户分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||||
|
public CommonResult<PageResult<CrmCustomerRespVO>> getPoolCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {
|
||||||
|
return convertPage(customerService.getCustomerPage(pageVO, CrmPermissionDO.POOL_USER_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommonResult<PageResult<CrmCustomerRespVO>> convertPage(PageResult<CrmCustomerDO> pageResult) {
|
||||||
|
// 2. 拼接数据
|
||||||
|
Set<Long> ids = convertSet(pageResult.getList(), CrmCustomerDO::getId);
|
||||||
|
// 2.1 获取负责人
|
||||||
|
List<CrmPermissionDO> ownerList = permissionService.getPermissionByBizTypeAndBizIdsAndLevel(
|
||||||
|
CrmBizTypeEnum.CRM_CUSTOMER.getType(), ids, CrmPermissionLevelEnum.OWNER.getLevel());
|
||||||
|
Map<Long, CrmPermissionDO> ownerMap = convertMap(ownerList, CrmPermissionDO::getBizId);
|
||||||
|
// 2.2 获取负责人详情
|
||||||
|
Set<Long> userIds = convertSet(ownerList, CrmPermissionDO::getUserId);
|
||||||
|
userIds.addAll(convertSet(pageResult.getList(), item -> Long.parseLong(item.getCreator()))); // 加入创建者
|
||||||
|
List<AdminUserRespDTO> userList = adminUserApi.getUserList(userIds);
|
||||||
|
Map<Long, AdminUserRespDTO> userMap = convertMap(userList, AdminUserRespDTO::getId);
|
||||||
|
// 2.3 获取部门详情
|
||||||
|
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userList, AdminUserRespDTO::getDeptId));
|
||||||
|
return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, ownerMap, userMap, deptMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/export-excel")
|
||||||
|
@Operation(summary = "导出客户 Excel")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer:export')")
|
||||||
|
@OperateLog(type = EXPORT)
|
||||||
|
public void exportCustomerExcel(@Valid CrmCustomerExportReqVO exportReqVO,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
List<CrmCustomerDO> list = customerService.getCustomerList(exportReqVO);
|
||||||
|
// 导出 Excel
|
||||||
|
List<CrmCustomerExcelVO> datas = CrmCustomerConvert.INSTANCE.convertList02(list);
|
||||||
|
ExcelUtils.write(response, "客户.xls", "数据", CrmCustomerExcelVO.class, datas);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/transfer")
|
||||||
|
@Operation(summary = "客户转移")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer:update')")
|
||||||
|
public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) {
|
||||||
|
customerService.transferCustomer(reqVO, getLoginUserId());
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO @Joey:单独建一个属于自己业务的 ReqVO;因为前端如果模拟请求,是不是可以更新其它字段了;
|
||||||
|
@PutMapping("/lock")
|
||||||
|
@Operation(summary = "锁定/解锁客户")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer:update')")
|
||||||
|
public CommonResult<Boolean> lockCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) {
|
||||||
|
customerService.lockCustomer(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/receive")
|
||||||
|
@Operation(summary = "领取公海客户")
|
||||||
|
// TODO @xiaqing:1)receiveCustomer 方法名字;2)cIds 改成 ids,要加下 @RequestParam,还有 swagger 注解;3)参数非空,使用 validator 校验;4)返回 true 即可;
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer:receive')")
|
||||||
|
public CommonResult<String> receiveByIds(List<Long> cIds){
|
||||||
|
// 判断是否为空
|
||||||
|
if(CollectionUtils.isEmpty(cIds))
|
||||||
|
return error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),GlobalErrorCodeConstants.BAD_REQUEST.getMsg());
|
||||||
|
// 领取公海任务
|
||||||
|
// TODO @xiaqing:userid,通过 controller 传递给 service,不要在 service 里面获取,无状态
|
||||||
|
customerService.receive(cIds);
|
||||||
|
return success("领取成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO @xiaqing:1)distributeCustomer 方法名;2)cIds 同上;3)参数校验,同上;4)ownerId 改成 ownerUserId,和别的模块统一;5)返回 true 即可;
|
||||||
|
@PutMapping("/distributeByIds")
|
||||||
|
@Operation(summary = "分配公海给对应负责人")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer:distributeByIds')")
|
||||||
|
public CommonResult<String> distributeByIds(Long ownerId,List<Long>cIds){
|
||||||
|
//判断参数不能为空
|
||||||
|
if(ownerId==null || CollectionUtils.isEmpty(cIds))
|
||||||
|
return error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),GlobalErrorCodeConstants.BAD_REQUEST.getMsg());
|
||||||
|
customerService.distributeByIds(cIds,ownerId);
|
||||||
|
return success("分配成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.customer;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLimitConfigCreateReqVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLimitConfigPageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLimitConfigRespVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLimitConfigUpdateReqVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerLimitConfigConvert;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.customerlimitconfig.CrmCustomerLimitConfigDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.customerlimitconfig.CrmCustomerLimitConfigService;
|
||||||
|
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 io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
|
||||||
|
|
||||||
|
@Tag(name = "管理后台 - 客户限制配置")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/crm/customer-limit-config")
|
||||||
|
@Validated
|
||||||
|
public class CrmCustomerLimitConfigController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CrmCustomerLimitConfigService customerLimitConfigService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DeptApi deptApi;
|
||||||
|
@Resource
|
||||||
|
private AdminUserApi adminUserApi;
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@Operation(summary = "创建客户限制配置")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer-limit-config:create')")
|
||||||
|
public CommonResult<Long> createCustomerLimitConfig(@Valid @RequestBody CrmCustomerLimitConfigCreateReqVO createReqVO) {
|
||||||
|
return success(customerLimitConfigService.createCustomerLimitConfig(createReqVO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update")
|
||||||
|
@Operation(summary = "更新客户限制配置")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer-limit-config:update')")
|
||||||
|
public CommonResult<Boolean> updateCustomerLimitConfig(@Valid @RequestBody CrmCustomerLimitConfigUpdateReqVO updateReqVO) {
|
||||||
|
customerLimitConfigService.updateCustomerLimitConfig(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除客户限制配置")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer-limit-config:delete')")
|
||||||
|
public CommonResult<Boolean> deleteCustomerLimitConfig(@RequestParam("id") Long id) {
|
||||||
|
customerLimitConfigService.deleteCustomerLimitConfig(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得客户限制配置")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer-limit-config:query')")
|
||||||
|
public CommonResult<CrmCustomerLimitConfigRespVO> getCustomerLimitConfig(@RequestParam("id") Long id) {
|
||||||
|
CrmCustomerLimitConfigDO customerLimitConfig = customerLimitConfigService.getCustomerLimitConfig(id);
|
||||||
|
// 拼接数据
|
||||||
|
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(customerLimitConfig.getUserIds());
|
||||||
|
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(customerLimitConfig.getDeptIds());
|
||||||
|
return success(CrmCustomerLimitConfigConvert.INSTANCE.convert(customerLimitConfig, userMap, deptMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/page")
|
||||||
|
@Operation(summary = "获得客户限制配置分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer-limit-config:query')")
|
||||||
|
public CommonResult<PageResult<CrmCustomerLimitConfigRespVO>> getCustomerLimitConfigPage(@Valid CrmCustomerLimitConfigPageReqVO pageVO) {
|
||||||
|
PageResult<CrmCustomerLimitConfigDO> pageResult = customerLimitConfigService.getCustomerLimitConfigPage(pageVO);
|
||||||
|
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||||
|
return success(PageResult.empty(pageResult.getTotal()));
|
||||||
|
}
|
||||||
|
// 拼接数据
|
||||||
|
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||||
|
convertSetByFlatMap(pageResult.getList(), CrmCustomerLimitConfigDO::getUserIds, Collection::stream));
|
||||||
|
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
|
||||||
|
convertSetByFlatMap(pageResult.getList(), CrmCustomerLimitConfigDO::getDeptIds, Collection::stream));
|
||||||
|
return success(CrmCustomerLimitConfigConvert.INSTANCE.convertPage(pageResult, userMap, deptMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.customer;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPoolConfigRespVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPoolConfigUpdateReqVO;
|
||||||
|
import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
|
||||||
|
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
|
||||||
|
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
|
||||||
|
@Tag(name = "管理后台 - CRM 客户公海配置")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/crm/customer-pool-config")
|
||||||
|
@Validated
|
||||||
|
public class CrmCustomerPoolConfigController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CrmCustomerPoolConfigService customerPoolConfigService;
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获取客户公海规则设置")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer-pool-config:query')")
|
||||||
|
public CommonResult<CrmCustomerPoolConfigRespVO> getCustomerPoolConfig() {
|
||||||
|
CrmCustomerPoolConfigDO customerPoolConfig = customerPoolConfigService.getCustomerPoolConfig();
|
||||||
|
return success(CrmCustomerConvert.INSTANCE.convert(customerPoolConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO @wanwan:这个请求,搞成 save 哈;
|
||||||
|
@PutMapping("/update")
|
||||||
|
@Operation(summary = "更新客户公海规则设置")
|
||||||
|
@PreAuthorize("@ss.hasPermission('crm:customer-pool-config:update')")
|
||||||
|
public CommonResult<Boolean> updateCustomerPoolConfig(@Valid @RequestBody CrmCustomerPoolConfigUpdateReqVO updateReqVO) {
|
||||||
|
customerPoolConfigService.updateCustomerPoolConfig(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
|
import cn.iocoder.yudao.framework.common.validation.Mobile;
|
||||||
|
import cn.iocoder.yudao.framework.common.validation.Telephone;
|
||||||
|
import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Email;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||||
|
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CrmCustomerBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
|
||||||
|
@NotEmpty(message = "客户名称不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "所属行业", example = "1")
|
||||||
|
private Integer industryId;
|
||||||
|
|
||||||
|
@Schema(description = "客户等级", example = "2")
|
||||||
|
@InEnum(CrmCustomerLevelEnum.class)
|
||||||
|
private Integer level;
|
||||||
|
|
||||||
|
@Schema(description = "客户来源", example = "3")
|
||||||
|
private Integer source;
|
||||||
|
|
||||||
|
@Schema(description = "手机", example = "18000000000")
|
||||||
|
@Mobile
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
@Schema(description = "电话", example = "18000000000")
|
||||||
|
@Telephone
|
||||||
|
private String telephone;
|
||||||
|
|
||||||
|
@Schema(description = "网址", example = "https://www.baidu.com")
|
||||||
|
private String website;
|
||||||
|
|
||||||
|
@Schema(description = "QQ", example = "123456789")
|
||||||
|
@Size(max = 20, message = "QQ长度不能超过 20 个字符")
|
||||||
|
private String qq;
|
||||||
|
|
||||||
|
@Schema(description = "wechat", example = "123456789")
|
||||||
|
@Size(max = 255, message = "微信长度不能超过 255 个字符")
|
||||||
|
private String wechat;
|
||||||
|
|
||||||
|
@Schema(description = "email", example = "123456789@qq.com")
|
||||||
|
@Email(message = "邮箱格式不正确")
|
||||||
|
@Size(max = 255, message = "邮箱长度不能超过 255 个字符")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Schema(description = "客户描述", example = "任意文字")
|
||||||
|
@Size(max = 4096, message = "客户描述长度不能超过 4096 个字符")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "备注", example = "随便")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@Schema(description = "地区编号", example = "20158")
|
||||||
|
private Integer areaId;
|
||||||
|
|
||||||
|
@Schema(description = "详细地址", example = "北京市海淀区")
|
||||||
|
private String detailAddress;
|
||||||
|
|
||||||
|
@Schema(description = "下次联系时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime contactNextTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - CRM 客户创建 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmCustomerCreateReqVO extends CrmCustomerBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
|
||||||
|
@NotNull(message = "负责人不能为空")
|
||||||
|
private Long ownerUserId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||||
|
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||||
|
import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
// TODO 芋艿:导出最后做,等基本确认的差不多之后;
|
||||||
|
/**
|
||||||
|
* CRM 客户 Excel VO
|
||||||
|
*
|
||||||
|
* @author Wanwan
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CrmCustomerExcelVO {
|
||||||
|
|
||||||
|
@ExcelProperty("编号")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ExcelProperty("客户名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ExcelProperty(value = "跟进状态", converter = DictConvert.class)
|
||||||
|
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
|
||||||
|
private Boolean followUpStatus;
|
||||||
|
|
||||||
|
@ExcelProperty(value = "锁定状态", converter = DictConvert.class)
|
||||||
|
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
|
||||||
|
private Boolean lockStatus;
|
||||||
|
|
||||||
|
@ExcelProperty(value = "成交状态", converter = DictConvert.class)
|
||||||
|
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
|
||||||
|
private Boolean dealStatus;
|
||||||
|
|
||||||
|
@ExcelProperty(value = "所属行业", converter = DictConvert.class)
|
||||||
|
@DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY)
|
||||||
|
private Integer industryId;
|
||||||
|
|
||||||
|
@ExcelProperty(value = "客户等级", converter = DictConvert.class)
|
||||||
|
@DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL)
|
||||||
|
private Integer level;
|
||||||
|
|
||||||
|
@ExcelProperty(value = "客户来源", converter = DictConvert.class)
|
||||||
|
@DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE)
|
||||||
|
private Integer source;
|
||||||
|
|
||||||
|
@ExcelProperty("手机")
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
@ExcelProperty("电话")
|
||||||
|
private String telephone;
|
||||||
|
|
||||||
|
@ExcelProperty("网址")
|
||||||
|
private String website;
|
||||||
|
|
||||||
|
@ExcelProperty("QQ")
|
||||||
|
private String qq;
|
||||||
|
|
||||||
|
@ExcelProperty("wechat")
|
||||||
|
private String wechat;
|
||||||
|
|
||||||
|
@ExcelProperty("email")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@ExcelProperty("客户描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@ExcelProperty("备注")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@ExcelProperty("负责人的用户编号")
|
||||||
|
private Long ownerUserId;
|
||||||
|
|
||||||
|
@ExcelProperty("地区编号")
|
||||||
|
private Integer areaId;
|
||||||
|
|
||||||
|
@ExcelProperty("详细地址")
|
||||||
|
private String detailAddress;
|
||||||
|
|
||||||
|
@ExcelProperty("最后跟进时间")
|
||||||
|
private LocalDateTime contactLastTime;
|
||||||
|
|
||||||
|
@ExcelProperty("下次联系时间")
|
||||||
|
private LocalDateTime contactNextTime;
|
||||||
|
|
||||||
|
@ExcelProperty("创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
// TODO 芋艿:导出最后做,等基本确认的差不多之后;
|
||||||
|
@Schema(description = "管理后台 - CRM 客户 Excel 导出 Request VO,参数和 CrmCustomerPageReqVO 是一致的")
|
||||||
|
@Data
|
||||||
|
public class CrmCustomerExportReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "客户名称", example = "赵六")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "手机", example = "18000000000")
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// TODO @wanwan:vo 下,可以新建一个 limitconfig,放它的 vo;
|
||||||
|
/**
|
||||||
|
* 客户限制配置 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||||
|
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CrmCustomerLimitConfigBaseVO {
|
||||||
|
|
||||||
|
@Schema(description = "规则类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||||
|
@NotNull(message = "规则类型不能为空")
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
@Schema(description = "规则适用人群")
|
||||||
|
private List<Long> userIds;
|
||||||
|
|
||||||
|
@Schema(description = "规则适用部门")
|
||||||
|
private List<Long> deptIds;
|
||||||
|
|
||||||
|
@Schema(description = "数量上限", requiredMode = Schema.RequiredMode.REQUIRED, example = "28384")
|
||||||
|
@NotNull(message = "数量上限不能为空")
|
||||||
|
private Integer maxCount;
|
||||||
|
|
||||||
|
@Schema(description = "成交客户是否占有拥有客户数(当 type = 1 时)")
|
||||||
|
private Boolean dealCountEnabled;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - 客户限制配置创建 Request VO")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class CrmCustomerLimitConfigCreateReqVO extends CrmCustomerLimitConfigBaseVO {
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user