Commit edea17e781fbca173c650f647e9b121052e2bc40
Merge remote-tracking branch 'upstream/master'
Showing
320 changed files
with
14699 additions
and
140 deletions
Too many changes.
To preserve performance only 100 of 320 files are displayed.
pom.xml
| ... | ... | @@ -21,6 +21,7 @@ |
| 21 | 21 | <!-- <module>yudao-module-mp</module>--> |
| 22 | 22 | <!-- <module>yudao-module-pay</module>--> |
| 23 | 23 | <!-- <module>yudao-module-mall</module>--> |
| 24 | +<!-- <module>yudao-module-crm</module>--> | |
| 24 | 25 | <!-- 示例项目 --> |
| 25 | 26 | <!-- <module>yudao-example</module>--> |
| 26 | 27 | </modules> | ... | ... |
sql/mysql/crm.sql
0 → 100644
| 1 | +SET NAMES utf8mb4; | ... | ... |
sql/mysql/crm_data.sql
0 → 100644
| 1 | + | |
| 2 | +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'); | |
| 3 | + | |
| 4 | +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'); | |
| 5 | + | |
| 6 | + | |
| 7 | +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'); | |
| 8 | +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'); | |
| 9 | +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'); | |
| 10 | +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'); | |
| 11 | +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'); | |
| 12 | + | |
| 13 | +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'); | |
| 14 | +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'); | |
| 15 | +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'); | |
| 16 | +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'); | |
| 17 | +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'); | |
| 18 | +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'); | |
| 19 | +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'); | |
| 20 | +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'); | ... | ... |
sql/mysql/crm_menu.sql
0 → 100644
| 1 | +-- ---------------------------- | |
| 2 | +-- 客户公海配置 | |
| 3 | +-- ---------------------------- | |
| 4 | +-- 菜单 SQL | |
| 5 | +INSERT INTO system_menu( | |
| 6 | + name, permission, type, sort, parent_id, | |
| 7 | + path, icon, component, status, component_name | |
| 8 | +) | |
| 9 | +VALUES ( | |
| 10 | + '客户公海配置', '', 2, 0, 2397, | |
| 11 | + 'customer-pool-config', 'ep:data-analysis', 'crm/customerPoolConf/index', 0, 'CustomerPoolConf' | |
| 12 | +); | |
| 13 | + | |
| 14 | +-- 按钮父菜单ID | |
| 15 | +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 | |
| 16 | +SELECT @parentId := LAST_INSERT_ID(); | |
| 17 | + | |
| 18 | +-- 按钮 SQL | |
| 19 | +INSERT INTO system_menu( | |
| 20 | + name, permission, type, sort, parent_id, | |
| 21 | + path, icon, component, status | |
| 22 | +) | |
| 23 | +VALUES ( | |
| 24 | + '客户公海配置保存', 'crm:customer-pool-config:update', 3, 1, @parentId, | |
| 25 | + '', '', '', 0 | |
| 26 | +); | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | +-- ---------------------------- | |
| 32 | +-- 客户限制配置管理 | |
| 33 | +-- ---------------------------- | |
| 34 | +-- 菜单 SQL | |
| 35 | +INSERT INTO system_menu( | |
| 36 | + name, permission, type, sort, parent_id, | |
| 37 | + path, icon, component, status, component_name | |
| 38 | +) | |
| 39 | +VALUES ( | |
| 40 | + '客户限制配置管理', '', 2, 0, 2397, | |
| 41 | + 'customer-limit-config', '', 'crm/customerLimitConfig/index', 0, 'CrmCustomerLimitConfig' | |
| 42 | +); | |
| 43 | + | |
| 44 | +-- 按钮父菜单ID | |
| 45 | +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 | |
| 46 | +SELECT @parentId := LAST_INSERT_ID(); | |
| 47 | + | |
| 48 | +-- 按钮 SQL | |
| 49 | +INSERT INTO system_menu( | |
| 50 | + name, permission, type, sort, parent_id, | |
| 51 | + path, icon, component, status | |
| 52 | +) | |
| 53 | +VALUES ( | |
| 54 | + '客户限制配置查询', 'crm:customer-limit-config:query', 3, 1, @parentId, | |
| 55 | + '', '', '', 0 | |
| 56 | +); | |
| 57 | +INSERT INTO system_menu( | |
| 58 | + name, permission, type, sort, parent_id, | |
| 59 | + path, icon, component, status | |
| 60 | +) | |
| 61 | +VALUES ( | |
| 62 | + '客户限制配置创建', 'crm:customer-limit-config:create', 3, 2, @parentId, | |
| 63 | + '', '', '', 0 | |
| 64 | +); | |
| 65 | +INSERT INTO system_menu( | |
| 66 | + name, permission, type, sort, parent_id, | |
| 67 | + path, icon, component, status | |
| 68 | +) | |
| 69 | +VALUES ( | |
| 70 | + '客户限制配置更新', 'crm:customer-limit-config:update', 3, 3, @parentId, | |
| 71 | + '', '', '', 0 | |
| 72 | +); | |
| 73 | +INSERT INTO system_menu( | |
| 74 | + name, permission, type, sort, parent_id, | |
| 75 | + path, icon, component, status | |
| 76 | +) | |
| 77 | +VALUES ( | |
| 78 | + '客户限制配置删除', 'crm:customer-limit-config:delete', 3, 4, @parentId, | |
| 79 | + '', '', '', 0 | |
| 80 | +); | |
| 81 | +INSERT INTO system_menu( | |
| 82 | + name, permission, type, sort, parent_id, | |
| 83 | + path, icon, component, status | |
| 84 | +) | |
| 85 | +VALUES ( | |
| 86 | + '客户限制配置导出', 'crm:customer-limit-config:export', 3, 5, @parentId, | |
| 87 | + '', '', '', 0 | |
| 88 | +); | ... | ... |
sql/mysql/pay_wallet.sql
| ... | ... | @@ -246,3 +246,11 @@ VALUES ( |
| 246 | 246 | '转账订单', '', 2, 3, 1117, |
| 247 | 247 | 'transfer', 'ep:credit-card', 'pay/transfer/index', 0, 'PayTransfer' |
| 248 | 248 | ); |
| 249 | + | |
| 250 | +-- 转账通知脚本 | |
| 251 | + | |
| 252 | +ALTER TABLE `pay_app` | |
| 253 | + ADD COLUMN `transfer_notify_url` varchar(1024) NOT NULL COMMENT '转账结果的回调地址' AFTER `refund_notify_url`; | |
| 254 | +ALTER TABLE `pay_notify_task` | |
| 255 | + MODIFY COLUMN `merchant_order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '商户订单编号' AFTER `status`, | |
| 256 | + ADD COLUMN `merchant_transfer_id` varchar(64) COMMENT '商户转账单编号' AFTER `merchant_order_id`; | |
| 249 | 257 | \ No newline at end of file | ... | ... |
sql/mysql/ruoyi-vue-pro.sql
| ... | ... | @@ -535,8 +535,8 @@ CREATE TABLE `infra_demo01_contact` ( |
| 535 | 535 | `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字', |
| 536 | 536 | `sex` tinyint(1) NOT NULL COMMENT '性别', |
| 537 | 537 | `birthday` datetime NOT NULL COMMENT '出生年', |
| 538 | - `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '简介', | |
| 539 | - `avatar` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '头像', | |
| 538 | + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '简介', | |
| 539 | + `avatar` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '头像', | |
| 540 | 540 | `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', |
| 541 | 541 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', |
| 542 | 542 | `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', |
| ... | ... | @@ -651,7 +651,7 @@ CREATE TABLE `infra_demo03_student` ( |
| 651 | 651 | `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字', |
| 652 | 652 | `sex` tinyint NOT NULL COMMENT '性别', |
| 653 | 653 | `birthday` datetime NOT NULL COMMENT '出生日期', |
| 654 | - `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '简介', | |
| 654 | + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '简介', | |
| 655 | 655 | `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', |
| 656 | 656 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', |
| 657 | 657 | `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/ServiceErrorCodeRange.java
| ... | ... | @@ -35,9 +35,12 @@ public class ServiceErrorCodeRange { |
| 35 | 35 | // 模块 member 错误码区间 [1-004-000-000 ~ 1-005-000-000) |
| 36 | 36 | // 模块 mp 错误码区间 [1-006-000-000 ~ 1-007-000-000) |
| 37 | 37 | // 模块 pay 错误码区间 [1-007-000-000 ~ 1-008-000-000) |
| 38 | - // 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000) | |
| 39 | 38 | // 模块 bpm 错误码区间 [1-009-000-000 ~ 1-010-000-000) |
| 39 | + | |
| 40 | + // 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000) | |
| 40 | 41 | // 模块 trade 错误码区间 [1-011-000-000 ~ 1-012-000-000) |
| 41 | 42 | // 模块 promotion 错误码区间 [1-013-000-000 ~ 1-014-000-000) |
| 42 | 43 | |
| 44 | + // 模块 crm 错误码区间 [1-020-000-000 ~ 1-021-000-000) | |
| 45 | + | |
| 43 | 46 | } | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
| ... | ... | @@ -280,6 +280,15 @@ public class CollectionUtils { |
| 280 | 280 | return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); |
| 281 | 281 | } |
| 282 | 282 | |
| 283 | + public static <T, U, R> List<R> convertListByFlatMap(Collection<T> from, | |
| 284 | + Function<? super T, ? extends U> mapper, | |
| 285 | + Function<U, ? extends Stream<? extends R>> func) { | |
| 286 | + if (CollUtil.isEmpty(from)) { | |
| 287 | + return new ArrayList<>(); | |
| 288 | + } | |
| 289 | + return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); | |
| 290 | + } | |
| 291 | + | |
| 283 | 292 | public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from, |
| 284 | 293 | Function<T, ? extends Stream<? extends U>> func) { |
| 285 | 294 | if (CollUtil.isEmpty(from)) { |
| ... | ... | @@ -288,4 +297,13 @@ public class CollectionUtils { |
| 288 | 297 | return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); |
| 289 | 298 | } |
| 290 | 299 | |
| 300 | + public static <T, U, R> Set<R> convertSetByFlatMap(Collection<T> from, | |
| 301 | + Function<? super T, ? extends U> mapper, | |
| 302 | + Function<U, ? extends Stream<? extends R>> func) { | |
| 303 | + if (CollUtil.isEmpty(from)) { | |
| 304 | + return new HashSet<>(); | |
| 305 | + } | |
| 306 | + return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); | |
| 307 | + } | |
| 308 | + | |
| 291 | 309 | } | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/Telephone.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.common.validation; | |
| 2 | + | |
| 3 | +import javax.validation.Constraint; | |
| 4 | +import javax.validation.Payload; | |
| 5 | +import java.lang.annotation.*; | |
| 6 | + | |
| 7 | +@Target({ | |
| 8 | + ElementType.METHOD, | |
| 9 | + ElementType.FIELD, | |
| 10 | + ElementType.ANNOTATION_TYPE, | |
| 11 | + ElementType.CONSTRUCTOR, | |
| 12 | + ElementType.PARAMETER, | |
| 13 | + ElementType.TYPE_USE | |
| 14 | +}) | |
| 15 | +@Retention(RetentionPolicy.RUNTIME) | |
| 16 | +@Documented | |
| 17 | +@Constraint( | |
| 18 | + validatedBy = TelephoneValidator.class | |
| 19 | +) | |
| 20 | +public @interface Telephone { | |
| 21 | + | |
| 22 | + String message() default "电话格式不正确"; | |
| 23 | + | |
| 24 | + Class<?>[] groups() default {}; | |
| 25 | + | |
| 26 | + Class<? extends Payload>[] payload() default {}; | |
| 27 | + | |
| 28 | +} | ... | ... |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/TelephoneValidator.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.common.validation; | |
| 2 | + | |
| 3 | +import cn.hutool.core.text.CharSequenceUtil; | |
| 4 | +import cn.hutool.core.util.PhoneUtil; | |
| 5 | + | |
| 6 | +import javax.validation.ConstraintValidator; | |
| 7 | +import javax.validation.ConstraintValidatorContext; | |
| 8 | + | |
| 9 | +public class TelephoneValidator implements ConstraintValidator<Telephone, String> { | |
| 10 | + | |
| 11 | + @Override | |
| 12 | + public void initialize(Telephone annotation) { | |
| 13 | + } | |
| 14 | + | |
| 15 | + @Override | |
| 16 | + public boolean isValid(String value, ConstraintValidatorContext context) { | |
| 17 | + // 如果手机号为空,默认不校验,即校验通过 | |
| 18 | + if (CharSequenceUtil.isEmpty(value)) { | |
| 19 | + return true; | |
| 20 | + } | |
| 21 | + // 校验手机 | |
| 22 | + return PhoneUtil.isTel(value) || PhoneUtil.isPhone(value); | |
| 23 | + } | |
| 24 | + | |
| 25 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
| ... | ... | @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; |
| 6 | 6 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; |
| 7 | 7 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; |
| 8 | 8 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; |
| 9 | +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; | |
| 9 | 10 | |
| 10 | 11 | import java.util.Map; |
| 11 | 12 | |
| ... | ... | @@ -86,4 +87,12 @@ public interface PayClient { |
| 86 | 87 | */ |
| 87 | 88 | PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO); |
| 88 | 89 | |
| 90 | + /** | |
| 91 | + * 获得转账订单信息 | |
| 92 | + * | |
| 93 | + * @param outTradeNo 外部订单号 | |
| 94 | + * @param type 转账类型 | |
| 95 | + * @return 转账信息 | |
| 96 | + */ | |
| 97 | + PayTransferRespDTO getTransfer(String outTradeNo, PayTransferTypeEnum type); | |
| 89 | 98 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferRespDTO.java
| ... | ... | @@ -53,11 +53,24 @@ public class PayTransferRespDTO { |
| 53 | 53 | /** |
| 54 | 54 | * 创建【WAITING】状态的转账返回 |
| 55 | 55 | */ |
| 56 | - public static PayTransferRespDTO waitingOf(String channelOrderNo, | |
| 56 | + public static PayTransferRespDTO waitingOf(String channelTransferNo, | |
| 57 | 57 | String outTransferNo, Object rawData) { |
| 58 | 58 | PayTransferRespDTO respDTO = new PayTransferRespDTO(); |
| 59 | 59 | respDTO.status = PayTransferStatusRespEnum.WAITING.getStatus(); |
| 60 | - respDTO.channelTransferNo = channelOrderNo; | |
| 60 | + respDTO.channelTransferNo = channelTransferNo; | |
| 61 | + respDTO.outTransferNo = outTransferNo; | |
| 62 | + respDTO.rawData = rawData; | |
| 63 | + return respDTO; | |
| 64 | + } | |
| 65 | + | |
| 66 | + /** | |
| 67 | + * 创建【IN_PROGRESS】状态的转账返回 | |
| 68 | + */ | |
| 69 | + public static PayTransferRespDTO dealingOf(String channelTransferNo, | |
| 70 | + String outTransferNo, Object rawData) { | |
| 71 | + PayTransferRespDTO respDTO = new PayTransferRespDTO(); | |
| 72 | + respDTO.status = PayTransferStatusRespEnum.IN_PROGRESS.getStatus(); | |
| 73 | + respDTO.channelTransferNo = channelTransferNo; | |
| 61 | 74 | respDTO.outTransferNo = outTransferNo; |
| 62 | 75 | respDTO.rawData = rawData; |
| 63 | 76 | return respDTO; | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
| ... | ... | @@ -188,11 +188,11 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen |
| 188 | 188 | |
| 189 | 189 | @Override |
| 190 | 190 | public final PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO) { |
| 191 | + validatePayTransferReqDTO(reqDTO); | |
| 191 | 192 | PayTransferRespDTO resp; |
| 192 | - try{ | |
| 193 | - validatePayTransferReqDTO(reqDTO); | |
| 193 | + try { | |
| 194 | 194 | resp = doUnifiedTransfer(reqDTO); |
| 195 | - }catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可 | |
| 195 | + } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可 | |
| 196 | 196 | throw ex; |
| 197 | 197 | } catch (Throwable ex) { |
| 198 | 198 | // 系统异常,则包装成 PayException 异常抛出 |
| ... | ... | @@ -219,9 +219,25 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen |
| 219 | 219 | } |
| 220 | 220 | } |
| 221 | 221 | |
| 222 | + @Override | |
| 223 | + public final PayTransferRespDTO getTransfer(String outTradeNo, PayTransferTypeEnum type) { | |
| 224 | + try { | |
| 225 | + return doGetTransfer(outTradeNo, type); | |
| 226 | + } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可 | |
| 227 | + throw ex; | |
| 228 | + } catch (Throwable ex) { | |
| 229 | + log.error("[getTransfer][客户端({}) outTradeNo({}) type({}) 查询转账单异常]", | |
| 230 | + getId(), outTradeNo, type, ex); | |
| 231 | + throw buildPayException(ex); | |
| 232 | + } | |
| 233 | + } | |
| 234 | + | |
| 222 | 235 | protected abstract PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) |
| 223 | 236 | throws Throwable; |
| 224 | 237 | |
| 238 | + protected abstract PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) | |
| 239 | + throws Throwable; | |
| 240 | + | |
| 225 | 241 | // ========== 各种工具方法 ========== |
| 226 | 242 | |
| 227 | 243 | private PayException buildPayException(Throwable ex) { | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
| ... | ... | @@ -23,14 +23,8 @@ import com.alipay.api.AlipayResponse; |
| 23 | 23 | import com.alipay.api.DefaultAlipayClient; |
| 24 | 24 | import com.alipay.api.domain.*; |
| 25 | 25 | import com.alipay.api.internal.util.AlipaySignature; |
| 26 | -import com.alipay.api.request.AlipayFundTransUniTransferRequest; | |
| 27 | -import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest; | |
| 28 | -import com.alipay.api.request.AlipayTradeQueryRequest; | |
| 29 | -import com.alipay.api.request.AlipayTradeRefundRequest; | |
| 30 | -import com.alipay.api.response.AlipayFundTransUniTransferResponse; | |
| 31 | -import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse; | |
| 32 | -import com.alipay.api.response.AlipayTradeQueryResponse; | |
| 33 | -import com.alipay.api.response.AlipayTradeRefundResponse; | |
| 26 | +import com.alipay.api.request.*; | |
| 27 | +import com.alipay.api.response.*; | |
| 34 | 28 | import lombok.Getter; |
| 35 | 29 | import lombok.SneakyThrows; |
| 36 | 30 | import lombok.extern.slf4j.Slf4j; |
| ... | ... | @@ -126,7 +120,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa |
| 126 | 120 | } |
| 127 | 121 | // 2.2 解析订单的状态 |
| 128 | 122 | Integer status = parseStatus(response.getTradeStatus()); |
| 129 | - Assert.notNull(status, () -> { | |
| 123 | + Assert.notNull(status, () -> { | |
| 130 | 124 | throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", response.getBody())); |
| 131 | 125 | }); |
| 132 | 126 | return PayOrderRespDTO.of(status, response.getTradeNo(), response.getBuyerUserId(), LocalDateTimeUtil.of(response.getSendPayDate()), |
| ... | ... | @@ -228,7 +222,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa |
| 228 | 222 | protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws AlipayApiException { |
| 229 | 223 | // 1.1 校验公钥类型 必须使用公钥证书模式 |
| 230 | 224 | if (!Objects.equals(config.getMode(), MODE_CERTIFICATE)) { |
| 231 | - throw exception0(ERROR_CONFIGURATION.getCode(),"支付宝单笔转账必须使用公钥证书模式"); | |
| 225 | + throw exception0(ERROR_CONFIGURATION.getCode(), "支付宝单笔转账必须使用公钥证书模式"); | |
| 232 | 226 | } |
| 233 | 227 | // 1.2 构建 AlipayFundTransUniTransferModel |
| 234 | 228 | AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel(); |
| ... | ... | @@ -238,44 +232,96 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa |
| 238 | 232 | model.setOutBizNo(reqDTO.getOutTransferNo()); |
| 239 | 233 | model.setProductCode("TRANS_ACCOUNT_NO_PWD"); // 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD |
| 240 | 234 | model.setBizScene("DIRECT_TRANSFER"); // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER |
| 241 | - model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras())); | |
| 235 | + if (reqDTO.getChannelExtras() != null) { | |
| 236 | + model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras())); | |
| 237 | + } | |
| 238 | + // ② 个性化的参数 | |
| 239 | + Participant payeeInfo = new Participant(); | |
| 242 | 240 | PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(reqDTO.getType()); |
| 243 | 241 | switch (transferType) { |
| 244 | 242 | // TODO @jason:是不是不用传递 transferType 参数哈?因为应该已经明确是支付宝啦? |
| 245 | 243 | // @芋艿。 是不是还要考虑转账到银行卡。所以传 transferType 但是转账到银行卡不知道要如何测试?? |
| 246 | 244 | case ALIPAY_BALANCE: { |
| 247 | - // ② 个性化的参数 | |
| 248 | - Participant payeeInfo = new Participant(); | |
| 249 | 245 | payeeInfo.setIdentityType("ALIPAY_LOGON_ID"); |
| 250 | 246 | payeeInfo.setIdentity(reqDTO.getAlipayLogonId()); // 支付宝登录号 |
| 251 | 247 | payeeInfo.setName(reqDTO.getUserName()); // 支付宝账号姓名 |
| 252 | 248 | model.setPayeeInfo(payeeInfo); |
| 253 | - // 1.3 构建 AlipayFundTransUniTransferRequest | |
| 254 | - AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest(); | |
| 255 | - request.setBizModel(model); | |
| 256 | - // 执行请求 | |
| 257 | - AlipayFundTransUniTransferResponse response = client.certificateExecute(request); | |
| 258 | - // 处理结果 | |
| 259 | - if (!response.isSuccess()) { | |
| 260 | - // 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询 | |
| 261 | - if (ObjectUtils.equalsAny(response.getSubCode(), "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) { | |
| 262 | - return PayTransferRespDTO.waitingOf(null, reqDTO.getOutTransferNo(), response); | |
| 263 | - } | |
| 264 | - return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(), | |
| 265 | - reqDTO.getOutTransferNo(), response); | |
| 266 | - } | |
| 267 | - return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()), | |
| 268 | - response.getOutBizNo(), response); | |
| 249 | + break; | |
| 269 | 250 | } |
| 270 | 251 | case BANK_CARD: { |
| 271 | - Participant payeeInfo = new Participant(); | |
| 272 | 252 | payeeInfo.setIdentityType("BANKCARD_ACCOUNT"); |
| 273 | 253 | // TODO 待实现 |
| 274 | 254 | throw exception(NOT_IMPLEMENTED); |
| 275 | 255 | } |
| 276 | 256 | default: { |
| 277 | - throw exception0(BAD_REQUEST.getCode(),"不正确的转账类型: {}",transferType); | |
| 257 | + throw exception0(BAD_REQUEST.getCode(), "不正确的转账类型: {}", transferType); | |
| 258 | + } | |
| 259 | + } | |
| 260 | + // 1.3 构建 AlipayFundTransUniTransferRequest | |
| 261 | + AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest(); | |
| 262 | + request.setBizModel(model); | |
| 263 | + // 执行请求 | |
| 264 | + AlipayFundTransUniTransferResponse response = client.certificateExecute(request); | |
| 265 | + // 处理结果 | |
| 266 | + if (!response.isSuccess()) { | |
| 267 | + // 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询,或相同 outBizNo 重新发起转账 | |
| 268 | + // 发现 outBizNo 相同 两次请求参数相同. 会返回 "PAYMENT_INFO_INCONSISTENCY", 不知道哪里的问题. 暂时返回 WAIT. 后续job 会轮询 | |
| 269 | + if (ObjectUtils.equalsAny(response.getSubCode(),"PAYMENT_INFO_INCONSISTENCY", "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) { | |
| 270 | + return PayTransferRespDTO.waitingOf(null, reqDTO.getOutTransferNo(), response); | |
| 271 | + } | |
| 272 | + return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(), | |
| 273 | + reqDTO.getOutTransferNo(), response); | |
| 274 | + } else { | |
| 275 | + if (ObjectUtils.equalsAny(response.getStatus(), "REFUND", "FAIL")) { // 转账到银行卡会出现 "REFUND" "FAIL" | |
| 276 | + return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(), | |
| 277 | + reqDTO.getOutTransferNo(), response); | |
| 278 | + } | |
| 279 | + if (Objects.equals(response.getStatus(), "DEALING")) { // 转账到银行卡会出现 "DEALING" 处理中 | |
| 280 | + return PayTransferRespDTO.dealingOf(response.getOrderId(), reqDTO.getOutTransferNo(), response); | |
| 281 | + } | |
| 282 | + return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()), | |
| 283 | + response.getOutBizNo(), response); | |
| 284 | + } | |
| 285 | + | |
| 286 | + } | |
| 287 | + | |
| 288 | + @Override | |
| 289 | + protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) throws Throwable { | |
| 290 | + // 1.1 构建 AlipayFundTransCommonQueryModel | |
| 291 | + AlipayFundTransCommonQueryModel model = new AlipayFundTransCommonQueryModel(); | |
| 292 | + model.setProductCode(type == PayTransferTypeEnum.BANK_CARD ? "TRANS_BANKCARD_NO_PWD" : "TRANS_ACCOUNT_NO_PWD"); | |
| 293 | + model.setBizScene("DIRECT_TRANSFER"); //业务场景 | |
| 294 | + model.setOutBizNo(outTradeNo); | |
| 295 | + // 1.2 构建 AlipayFundTransCommonQueryRequest | |
| 296 | + AlipayFundTransCommonQueryRequest request = new AlipayFundTransCommonQueryRequest(); | |
| 297 | + request.setBizModel(model); | |
| 298 | + | |
| 299 | + // 2.1 执行请求 | |
| 300 | + AlipayFundTransCommonQueryResponse response; | |
| 301 | + if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) { // 证书模式 | |
| 302 | + response = client.certificateExecute(request); | |
| 303 | + } else { | |
| 304 | + response = client.execute(request); | |
| 305 | + } | |
| 306 | + // 2.2 处理返回结果 | |
| 307 | + if (response.isSuccess()) { | |
| 308 | + if (ObjectUtils.equalsAny(response.getStatus(), "REFUND", "FAIL")) { // 转账到银行卡会出现 "REFUND" "FAIL" | |
| 309 | + return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(), | |
| 310 | + outTradeNo, response); | |
| 311 | + } | |
| 312 | + if (Objects.equals(response.getStatus(), "DEALING")) { // 转账到银行卡会出现 "DEALING" 处理中 | |
| 313 | + return PayTransferRespDTO.dealingOf(response.getOrderId(), outTradeNo, response); | |
| 278 | 314 | } |
| 315 | + return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getPayDate()), | |
| 316 | + response.getOutBizNo(), response); | |
| 317 | + } else { | |
| 318 | + // 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询, 或相同 outBizNo 重新发起转账 | |
| 319 | + // 当出现 ORDER_NOT_EXIST 可能是转账还在处理中,也可能是转账处理失败. 返回 WAIT 状态. 后续 job 会轮询, 或相同 outBizNo 重新发起转账 | |
| 320 | + if (ObjectUtils.equalsAny(response.getSubCode(), "ORDER_NOT_EXIST", "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) { | |
| 321 | + return PayTransferRespDTO.waitingOf(null, outTradeNo, response); | |
| 322 | + } | |
| 323 | + return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(), | |
| 324 | + outTradeNo, response); | |
| 279 | 325 | } |
| 280 | 326 | } |
| 281 | 327 | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java
| ... | ... | @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifie |
| 9 | 9 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; |
| 10 | 10 | import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig; |
| 11 | 11 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; |
| 12 | +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; | |
| 12 | 13 | |
| 13 | 14 | import java.time.LocalDateTime; |
| 14 | 15 | import java.util.Map; |
| ... | ... | @@ -71,4 +72,9 @@ public class MockPayClient extends AbstractPayClient<NonePayClientConfig> { |
| 71 | 72 | throw new UnsupportedOperationException("待实现"); |
| 72 | 73 | } |
| 73 | 74 | |
| 75 | + @Override | |
| 76 | + protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) { | |
| 77 | + throw new UnsupportedOperationException("待实现"); | |
| 78 | + } | |
| 79 | + | |
| 74 | 80 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
| ... | ... | @@ -16,6 +16,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDT |
| 16 | 16 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; |
| 17 | 17 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; |
| 18 | 18 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; |
| 19 | +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; | |
| 19 | 20 | import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; |
| 20 | 21 | import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; |
| 21 | 22 | import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; |
| ... | ... | @@ -431,6 +432,12 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC |
| 431 | 432 | protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) { |
| 432 | 433 | throw new UnsupportedOperationException("待实现"); |
| 433 | 434 | } |
| 435 | + | |
| 436 | + @Override | |
| 437 | + protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) { | |
| 438 | + throw new UnsupportedOperationException("待实现"); | |
| 439 | + } | |
| 440 | + | |
| 434 | 441 | // ========== 各种工具方法 ========== |
| 435 | 442 | |
| 436 | 443 | static String formatDateV2(LocalDateTime time) { | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferStatusRespEnum.java
| ... | ... | @@ -38,4 +38,8 @@ public enum PayTransferStatusRespEnum { |
| 38 | 38 | public static boolean isClosed(Integer status) { |
| 39 | 39 | return Objects.equals(status, CLOSED.getStatus()); |
| 40 | 40 | } |
| 41 | + | |
| 42 | + public static boolean isInProgress(Integer status) { | |
| 43 | + return Objects.equals(status, IN_PROGRESS.getStatus()); | |
| 44 | + } | |
| 41 | 45 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/context/FlowableContextHolder.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.flowable.core.context; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import com.alibaba.ttl.TransmittableThreadLocal; | |
| 5 | + | |
| 6 | +import java.util.Collections; | |
| 7 | +import java.util.List; | |
| 8 | +import java.util.Map; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 工作流--用户用到的上下文相关信息 | |
| 12 | + */ | |
| 13 | +public class FlowableContextHolder { | |
| 14 | + | |
| 15 | + private static final ThreadLocal<Map<String, List<Long>>> ASSIGNEE = new TransmittableThreadLocal<>(); | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 通过流程任务的定义 key ,拿到提前选好的审批人 | |
| 19 | + * 此方法目的:首次创建流程实例时,数据库中还查询不到 assignee 字段,所以存入上下文中获取 | |
| 20 | + * | |
| 21 | + * @param taskDefinitionKey 流程任务 key | |
| 22 | + * @return 审批人 ID 集合 | |
| 23 | + */ | |
| 24 | + public static List<Long> getAssigneeByTaskDefinitionKey(String taskDefinitionKey) { | |
| 25 | + if (CollUtil.isNotEmpty(ASSIGNEE.get())) { | |
| 26 | + return ASSIGNEE.get().get(taskDefinitionKey); | |
| 27 | + } | |
| 28 | + return Collections.emptyList(); | |
| 29 | + } | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * 存入提前选好的审批人到上下文线程变量中 | |
| 33 | + * | |
| 34 | + * @param assignee 流程任务 key -> 审批人 ID 炅和 | |
| 35 | + */ | |
| 36 | + public static void setAssignee(Map<String, List<Long>> assignee) { | |
| 37 | + ASSIGNEE.set(assignee); | |
| 38 | + } | |
| 39 | + | |
| 40 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
| ... | ... | @@ -12,6 +12,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage; |
| 12 | 12 | import com.baomidou.mybatisplus.core.toolkit.support.SFunction; |
| 13 | 13 | import com.baomidou.mybatisplus.extension.toolkit.Db; |
| 14 | 14 | import com.github.yulichang.base.MPJBaseMapper; |
| 15 | +import com.github.yulichang.interfaces.MPJBaseJoin; | |
| 15 | 16 | import org.apache.ibatis.annotations.Param; |
| 16 | 17 | |
| 17 | 18 | import java.util.Collection; |
| ... | ... | @@ -39,6 +40,13 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> { |
| 39 | 40 | return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); |
| 40 | 41 | } |
| 41 | 42 | |
| 43 | + default <DTO> PageResult<DTO> selectJoinPage(PageParam pageParam, Class<DTO> resultTypeClass, MPJBaseJoin<T> joinQueryWrapper) { | |
| 44 | + IPage<DTO> mpPage = MyBatisUtils.buildPage(pageParam); | |
| 45 | + selectJoinPage(mpPage, resultTypeClass, joinQueryWrapper); | |
| 46 | + // 转换返回 | |
| 47 | + return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); | |
| 48 | + } | |
| 49 | + | |
| 42 | 50 | default T selectOne(String field, Object value) { |
| 43 | 51 | return selectOne(new QueryWrapper<T>().eq(field, value)); |
| 44 | 52 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.api.task.dto; |
| 3 | 3 | import lombok.Data; |
| 4 | 4 | |
| 5 | 5 | import javax.validation.constraints.NotEmpty; |
| 6 | +import java.util.List; | |
| 6 | 7 | import java.util.Map; |
| 7 | 8 | |
| 8 | 9 | /** |
| ... | ... | @@ -30,4 +31,15 @@ public class BpmProcessInstanceCreateReqDTO { |
| 30 | 31 | */ |
| 31 | 32 | @NotEmpty(message = "业务的唯一标识") |
| 32 | 33 | private String businessKey; |
| 34 | + | |
| 35 | + // TODO @hai:assignees 复数 | |
| 36 | + /** | |
| 37 | + * 提前指派的审批人 | |
| 38 | + * | |
| 39 | + * key:taskKey 任务编码 | |
| 40 | + * value:审批人的数组 | |
| 41 | + * 例如: { taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批 | |
| 42 | + */ | |
| 43 | + private Map<String, List<Long>> assignee; | |
| 44 | + | |
| 33 | 45 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java
| ... | ... | @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 4 | import lombok.Data; |
| 5 | 5 | |
| 6 | 6 | import javax.validation.constraints.NotEmpty; |
| 7 | +import java.util.List; | |
| 7 | 8 | import java.util.Map; |
| 8 | 9 | |
| 9 | 10 | @Schema(description = "管理后台 - 流程实例的创建 Request VO") |
| ... | ... | @@ -17,4 +18,8 @@ public class BpmProcessInstanceCreateReqVO { |
| 17 | 18 | @Schema(description = "变量实例") |
| 18 | 19 | private Map<String, Object> variables; |
| 19 | 20 | |
| 21 | + // TODO @hai:assignees 复数 | |
| 22 | + @Schema(description = "提前指派的审批人", requiredMode = Schema.RequiredMode.REQUIRED, example = "{taskKey1: [1, 2]}") | |
| 23 | + private Map<String, List<Long>> assignee; | |
| 24 | + | |
| 20 | 25 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmProcessInstanceExtDO.java
| ... | ... | @@ -12,6 +12,7 @@ import lombok.EqualsAndHashCode; |
| 12 | 12 | import lombok.ToString; |
| 13 | 13 | |
| 14 | 14 | import java.time.LocalDateTime; |
| 15 | +import java.util.List; | |
| 15 | 16 | import java.util.Map; |
| 16 | 17 | |
| 17 | 18 | /** |
| ... | ... | @@ -87,4 +88,11 @@ public class BpmProcessInstanceExtDO extends BaseDO { |
| 87 | 88 | @TableField(typeHandler = JacksonTypeHandler.class) |
| 88 | 89 | private Map<String, Object> formVariables; |
| 89 | 90 | |
| 91 | + // TODO @hai:assignees 复数 | |
| 92 | + /** | |
| 93 | + * 提前设定好的审批人 | |
| 94 | + */ | |
| 95 | + @TableField(typeHandler = JacksonTypeHandler.class, exist = false) // TODO 芋艿:临时 exist = false,避免 db 报错; | |
| 96 | + private Map<String, List<Long>> assignee; | |
| 97 | + | |
| 90 | 98 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java
| ... | ... | @@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper; |
| 18 | 18 | import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants; |
| 19 | 19 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; |
| 20 | 20 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; |
| 21 | +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | |
| 21 | 22 | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| 22 | 23 | import cn.iocoder.yudao.module.system.api.dept.PostApi; |
| 23 | 24 | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| ... | ... | @@ -39,6 +40,7 @@ import org.springframework.validation.annotation.Validated; |
| 39 | 40 | import javax.annotation.Resource; |
| 40 | 41 | import javax.validation.Valid; |
| 41 | 42 | import java.util.*; |
| 43 | +import java.util.function.Function; | |
| 42 | 44 | |
| 43 | 45 | import static cn.hutool.core.text.CharSequenceUtil.format; |
| 44 | 46 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| ... | ... | @@ -77,6 +79,9 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { |
| 77 | 79 | private DictDataApi dictDataApi; |
| 78 | 80 | @Resource |
| 79 | 81 | private PermissionApi permissionApi; |
| 82 | + @Resource | |
| 83 | + @Lazy // 解决循环依赖 | |
| 84 | + private BpmProcessInstanceService processInstanceService; | |
| 80 | 85 | /** |
| 81 | 86 | * 任务分配脚本 |
| 82 | 87 | */ |
| ... | ... | @@ -234,6 +239,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { |
| 234 | 239 | @Override |
| 235 | 240 | @DataPermission(enable = false) // 忽略数据权限,不然分配会存在问题 |
| 236 | 241 | public Set<Long> calculateTaskCandidateUsers(DelegateExecution execution) { |
| 242 | + // 1. 先从提前选好的审批人中获取 | |
| 243 | + List<Long> assignee = processInstanceService.getAssigneeByProcessInstanceIdAndTaskDefinitionKey( | |
| 244 | + execution.getProcessInstanceId(), execution.getCurrentActivityId()); | |
| 245 | + if (CollUtil.isNotEmpty(assignee)) { | |
| 246 | + // TODO @hai:new HashSet 即可 | |
| 247 | + return convertSet(assignee, Function.identity()); | |
| 248 | + } | |
| 249 | + // 2. 通过分配规则,计算审批人 | |
| 237 | 250 | BpmTaskAssignRuleDO rule = getTaskRule(execution); |
| 238 | 251 | return calculateTaskCandidateUsers(execution, rule); |
| 239 | 252 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
| ... | ... | @@ -49,16 +49,17 @@ public interface BpmProcessInstanceService { |
| 49 | 49 | /** |
| 50 | 50 | * 获得流程实例的分页 |
| 51 | 51 | * |
| 52 | - * @param userId 用户编号 | |
| 52 | + * @param userId 用户编号 | |
| 53 | 53 | * @param pageReqVO 分页请求 |
| 54 | 54 | * @return 流程实例的分页 |
| 55 | 55 | */ |
| 56 | 56 | PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId, |
| 57 | 57 | @Valid BpmProcessInstanceMyPageReqVO pageReqVO); |
| 58 | + | |
| 58 | 59 | /** |
| 59 | 60 | * 创建流程实例(提供给前端) |
| 60 | 61 | * |
| 61 | - * @param userId 用户编号 | |
| 62 | + * @param userId 用户编号 | |
| 62 | 63 | * @param createReqVO 创建信息 |
| 63 | 64 | * @return 实例的编号 |
| 64 | 65 | */ |
| ... | ... | @@ -67,7 +68,7 @@ public interface BpmProcessInstanceService { |
| 67 | 68 | /** |
| 68 | 69 | * 创建流程实例(提供给内部) |
| 69 | 70 | * |
| 70 | - * @param userId 用户编号 | |
| 71 | + * @param userId 用户编号 | |
| 71 | 72 | * @param createReqDTO 创建信息 |
| 72 | 73 | * @return 实例的编号 |
| 73 | 74 | */ |
| ... | ... | @@ -84,7 +85,7 @@ public interface BpmProcessInstanceService { |
| 84 | 85 | /** |
| 85 | 86 | * 取消流程实例 |
| 86 | 87 | * |
| 87 | - * @param userId 用户编号 | |
| 88 | + * @param userId 用户编号 | |
| 88 | 89 | * @param cancelReqVO 取消信息 |
| 89 | 90 | */ |
| 90 | 91 | void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO); |
| ... | ... | @@ -139,9 +140,19 @@ public interface BpmProcessInstanceService { |
| 139 | 140 | /** |
| 140 | 141 | * 更新 ProcessInstance 拓展记录为不通过 |
| 141 | 142 | * |
| 142 | - * @param id 流程编号 | |
| 143 | + * @param id 流程编号 | |
| 143 | 144 | * @param reason 理由。例如说,审批不通过时,需要传递该值 |
| 144 | 145 | */ |
| 145 | 146 | void updateProcessInstanceExtReject(String id, String reason); |
| 146 | 147 | |
| 148 | + // TODO @hai:改成 getProcessInstanceAssigneesByTaskDefinitionKey(String id, String taskDefinitionKey) | |
| 149 | + /** | |
| 150 | + * 获取流程实例中,取出指定流程任务提前指定的审批人 | |
| 151 | + * | |
| 152 | + * @param processInstanceId 流程实例的编号 | |
| 153 | + * @param taskDefinitionKey 流程任务定义的 key | |
| 154 | + * @return 审批人集合 | |
| 155 | + */ | |
| 156 | + List<Long> getAssigneeByProcessInstanceIdAndTaskDefinitionKey(String processInstanceId, String taskDefinitionKey); | |
| 157 | + | |
| 147 | 158 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
| 1 | -package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; 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 lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Valid; import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 创建流程实例 ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()) .setFormVariables(variables)); return instance.getId(); } } | |
| 2 | 1 | \ No newline at end of file |
| 2 | +package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.flowable.core.context.FlowableContextHolder; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; 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 lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Valid; import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * <p> * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * <p> * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * <p> * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getAssignee()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getAssignee()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String) event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> assignee) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 设置上下文信息 // TODO @hai:要不往 variables 存到一个全局固定 key 里,减少对上下文的依赖 FlowableContextHolder.setAssignee(assignee); // 创建流程实例 ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()) .setFormVariables(variables).setAssignee(assignee)); return instance.getId(); } @Override public List<Long> getAssigneeByProcessInstanceIdAndTaskDefinitionKey(String processInstanceId, String taskDefinitionKey) { // 1. 先从上下文获取,首次提交数据库中查询不到 List<Long> result = FlowableContextHolder.getAssigneeByTaskDefinitionKey(taskDefinitionKey); if (CollUtil.isNotEmpty(result)) { return result; } // 2. 从数据库中获取 BpmProcessInstanceExtDO instance = processInstanceExtMapper.selectByProcessInstanceId(processInstanceId); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } if (CollUtil.isNotEmpty(instance.getAssignee())) { return instance.getAssignee().get(taskDefinitionKey); } return Collections.emptyList(); } } | |
| 3 | 3 | \ No newline at end of file | ... | ... |
yudao-module-crm/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | |
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| 5 | + <parent> | |
| 6 | + <groupId>cn.iocoder.boot</groupId> | |
| 7 | + <artifactId>yudao</artifactId> | |
| 8 | + <version>${revision}</version> | |
| 9 | + </parent> | |
| 10 | + <modules> | |
| 11 | + <module>yudao-module-crm-api</module> | |
| 12 | + <module>yudao-module-crm-biz</module> | |
| 13 | + </modules> | |
| 14 | + <modelVersion>4.0.0</modelVersion> | |
| 15 | + <artifactId>yudao-module-crm</artifactId> | |
| 16 | + <packaging>pom</packaging> | |
| 17 | + | |
| 18 | + <name>${project.artifactId}</name> | |
| 19 | + | |
| 20 | + <description> | |
| 21 | + crm 包下,客户关系管理(Customer Relationship Management)。 | |
| 22 | + 例如说:客户、联系人、商机、合同、回款等等 | |
| 23 | + </description> | |
| 24 | + | |
| 25 | +</project> | ... | ... |
yudao-module-crm/yudao-module-crm-api/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | |
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| 5 | + <parent> | |
| 6 | + <groupId>cn.iocoder.boot</groupId> | |
| 7 | + <artifactId>yudao-module-crm</artifactId> | |
| 8 | + <version>${revision}</version> | |
| 9 | + </parent> | |
| 10 | + <modelVersion>4.0.0</modelVersion> | |
| 11 | + <artifactId>yudao-module-crm-api</artifactId> | |
| 12 | + <packaging>jar</packaging> | |
| 13 | + | |
| 14 | + <name>${project.artifactId}</name> | |
| 15 | + <description> | |
| 16 | + crm 模块 API,暴露给其它模块调用 | |
| 17 | + </description> | |
| 18 | + | |
| 19 | + <dependencies> | |
| 20 | + <dependency> | |
| 21 | + <groupId>cn.iocoder.boot</groupId> | |
| 22 | + <artifactId>yudao-common</artifactId> | |
| 23 | + </dependency> | |
| 24 | + | |
| 25 | + <!-- 参数校验 --> | |
| 26 | + <dependency> | |
| 27 | + <groupId>org.springframework.boot</groupId> | |
| 28 | + <artifactId>spring-boot-starter-validation</artifactId> | |
| 29 | + <optional>true</optional> | |
| 30 | + </dependency> | |
| 31 | + </dependencies> | |
| 32 | + | |
| 33 | +</project> | ... | ... |
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java
0 → 100644
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/AuditStatusEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.enums; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; | |
| 4 | + | |
| 5 | +import java.util.Arrays; | |
| 6 | + | |
| 7 | +// TODO @liuhongfeng:这个状态,还是搞成专属 CrmReceivableDO 专属的 status; | |
| 8 | +/** | |
| 9 | + * 流程审批状态枚举类 | |
| 10 | + * 0 未审核 1 审核通过 2 审核拒绝 3 审核中 4 已撤回 TODO @liuhongfeng:这一行可以删除,因为已经有枚举属性了哈; | |
| 11 | + * @author 赤焰 | |
| 12 | + */ | |
| 13 | +// TODO @liuhongfeng:可以使用 @Getter、@AllArgsConstructor 简化 get、构造方法 | |
| 14 | +public enum AuditStatusEnum implements IntArrayValuable { | |
| 15 | + | |
| 16 | + // TODO @liuhongfeng:草稿 0;10 审核中;20 审核通过;30 审核拒绝;40 已撤回;主要是留好间隙,万一每个地方要做点拓展; 然后,枚举字段的顺序调整下,审批中,一定要放两个审批通过、拒绝前面哈; | |
| 17 | + /** | |
| 18 | + * 未审批 | |
| 19 | + */ | |
| 20 | + AUDIT_NEW(0, "未审批"), | |
| 21 | + /** | |
| 22 | + * 审核通过 | |
| 23 | + */ | |
| 24 | + AUDIT_FINISH(1, "审核通过"), | |
| 25 | + /** | |
| 26 | + * 审核拒绝 | |
| 27 | + */ | |
| 28 | + AUDIT_REJECT(2, "审核拒绝"), | |
| 29 | + /** | |
| 30 | + * 审核中 | |
| 31 | + */ | |
| 32 | + AUDIT_DOING(3, "审核中"), | |
| 33 | + /** | |
| 34 | + * 已撤回 | |
| 35 | + */ | |
| 36 | + AUDIT_RETURN(4, "已撤回"); | |
| 37 | + | |
| 38 | + // TODO liuhongfeng:value 改成 status;desc 改成 name; | |
| 39 | + private final Integer value; | |
| 40 | + private final String desc; | |
| 41 | + | |
| 42 | + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AuditStatusEnum::getValue).toArray(); | |
| 43 | + | |
| 44 | + AuditStatusEnum(Integer value, String desc) { | |
| 45 | + this.value = value; | |
| 46 | + this.desc = desc; | |
| 47 | + } | |
| 48 | + | |
| 49 | + public Integer getValue() { | |
| 50 | + return value; | |
| 51 | + } | |
| 52 | + | |
| 53 | + public String getDesc() { | |
| 54 | + return desc; | |
| 55 | + } | |
| 56 | + | |
| 57 | + @Override | |
| 58 | + public int[] array() { | |
| 59 | + return ARRAYS; | |
| 60 | + } | |
| 61 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.enums; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * CRM 字典类型的枚举类 | |
| 5 | + * | |
| 6 | + * @author 芋道源码 | |
| 7 | + */ | |
| 8 | +public interface DictTypeConstants { | |
| 9 | + | |
| 10 | + // ========== CRM 模块 ========== | |
| 11 | + String CRM_CUSTOMER_INDUSTRY = "crm_customer_industry"; // CRM 客户所属行业 | |
| 12 | + String CRM_CUSTOMER_LEVEL = "crm_customer_level"; // CRM 客户等级 | |
| 13 | + String CRM_CUSTOMER_SOURCE = "crm_customer_source"; // CRM 客户来源 | |
| 14 | + String CRM_RECEIVABLE_CHECK_STATUS = "crm_receivable_check_status"; // CRM 审批状态 | |
| 15 | + | |
| 16 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.enums; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.exception.ErrorCode; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * CRM 错误码枚举类 | |
| 7 | + * <p> | |
| 8 | + * crm 系统,使用 1-020-000-000 段 | |
| 9 | + */ | |
| 10 | +public interface ErrorCodeConstants { | |
| 11 | + | |
| 12 | + // ========== 合同管理 1-020-000-000 ========== | |
| 13 | + ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在"); | |
| 14 | + | |
| 15 | + // ========== 线索管理 1-020-001-000 ========== | |
| 16 | + ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在"); | |
| 17 | + | |
| 18 | + // ========== 商机管理 1-020-002-000 ========== | |
| 19 | + ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_002_000, "商机不存在"); | |
| 20 | + | |
| 21 | + // TODO @lilleo:商机状态、商机类型,都单独错误码段 | |
| 22 | + | |
| 23 | + ErrorCode BUSINESS_STATUS_TYPE_NOT_EXISTS = new ErrorCode(1_020_002_001, "商机状态类型不存在"); | |
| 24 | + ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_002_002, "商机状态不存在"); | |
| 25 | + | |
| 26 | + // ========== 联系人管理 1-020-003-000 ========== | |
| 27 | + ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在"); | |
| 28 | + | |
| 29 | + // ========== 回款管理 1-020-004-000 ========== | |
| 30 | + ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款管理不存在"); | |
| 31 | + | |
| 32 | + // ========== 合同管理 1-020-005-000 ========== | |
| 33 | + ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在"); | |
| 34 | + | |
| 35 | + // ========== 客户管理 1_020_006_000 ========== | |
| 36 | + ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_006_000, "客户不存在"); | |
| 37 | + ErrorCode CUSTOMER_OWNER_EXISTS = new ErrorCode(1_020_006_001, "客户已存在所属负责人"); | |
| 38 | + ErrorCode CUSTOMER_LOCKED = new ErrorCode(1_020_006_002, "客户状态已锁定"); | |
| 39 | + ErrorCode CUSTOMER_ALREADY_DEAL = new ErrorCode(1_020_006_003, "客户已交易"); | |
| 40 | + // TODO @wanwan:这 2 个单独配置段噢 | |
| 41 | + ErrorCode CUSTOMER_POOL_CONFIG_ERROR = new ErrorCode(1_020_006_001, "客户公海规则设置不正确"); | |
| 42 | + ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_006_002, "客户限制配置不存在"); | |
| 43 | + | |
| 44 | + // ========== 权限管理 1_020_007_000 ========== | |
| 45 | + ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在"); | |
| 46 | + ErrorCode CRM_PERMISSION_DENIED = new ErrorCode(1_020_007_001, "{}操作失败,原因:没有权限"); | |
| 47 | + ErrorCode CRM_PERMISSION_MODEL_NOT_EXISTS = new ErrorCode(1_020_007_002, "{}不存在"); | |
| 48 | + ErrorCode CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS = new ErrorCode(1_020_007_003, "{}操作失败,原因:转移对象已经是该负责人"); | |
| 49 | + | |
| 50 | + // ========== 产品 1_020_008_000 ========== | |
| 51 | + ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产品不存在"); | |
| 52 | + ErrorCode PRODUCT_NO_EXISTS = new ErrorCode(1_020_008_001, "产品编号已存在"); | |
| 53 | + | |
| 54 | + // ========== 产品分类 1_020_009_000 ========== | |
| 55 | + ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_020_009_000, "产品分类不存在"); | |
| 56 | + | |
| 57 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ReturnTypeEnum.java
0 → 100644
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.enums.customer; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; | |
| 4 | +import lombok.AllArgsConstructor; | |
| 5 | +import lombok.Getter; | |
| 6 | + | |
| 7 | +import java.util.Arrays; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * CRM 客户等级 | |
| 11 | + * | |
| 12 | + * @author Wanwan | |
| 13 | + */ | |
| 14 | +@Getter | |
| 15 | +@AllArgsConstructor | |
| 16 | +public enum CrmCustomerLevelEnum implements IntArrayValuable { | |
| 17 | + | |
| 18 | + IMPORTANT(1, "A(重点客户)"), | |
| 19 | + GENERAL(2, "B(普通客户)"), | |
| 20 | + LOW_PRIORITY(3, "C(非优先客户)"); | |
| 21 | + | |
| 22 | + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerLevelEnum::getLevel).toArray(); | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * 状态 | |
| 26 | + */ | |
| 27 | + private final Integer level; | |
| 28 | + /** | |
| 29 | + * 状态名 | |
| 30 | + */ | |
| 31 | + private final String name; | |
| 32 | + | |
| 33 | + @Override | |
| 34 | + public int[] array() { | |
| 35 | + return ARRAYS; | |
| 36 | + } | |
| 37 | + | |
| 38 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerSceneEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.enums.customer; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ObjUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; | |
| 5 | +import lombok.AllArgsConstructor; | |
| 6 | +import lombok.Getter; | |
| 7 | + | |
| 8 | +import java.util.Arrays; | |
| 9 | + | |
| 10 | +// TODO @puhui999:这个应该是 crm 全局的,不仅仅属于 customer 客户哈; | |
| 11 | +/** | |
| 12 | + * CRM 客户等级 | |
| 13 | + * | |
| 14 | + * @author Wanwan | |
| 15 | + */ | |
| 16 | +@Getter | |
| 17 | +@AllArgsConstructor | |
| 18 | +public enum CrmCustomerSceneEnum implements IntArrayValuable { | |
| 19 | + | |
| 20 | + OWNER(1, "我负责的客户"), | |
| 21 | + FOLLOW(2, "我关注的客户"); | |
| 22 | + | |
| 23 | + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerSceneEnum::getType).toArray(); | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 场景类型 | |
| 27 | + */ | |
| 28 | + private final Integer type; | |
| 29 | + /** | |
| 30 | + * 场景名称 | |
| 31 | + */ | |
| 32 | + private final String name; | |
| 33 | + | |
| 34 | + public static boolean isOwner(Integer type) { | |
| 35 | + return ObjUtil.equal(OWNER.getType(), type); | |
| 36 | + } | |
| 37 | + | |
| 38 | + public static boolean isFollow(Integer type) { | |
| 39 | + return ObjUtil.equal(FOLLOW.getType(), type); | |
| 40 | + } | |
| 41 | + | |
| 42 | + @Override | |
| 43 | + public int[] array() { | |
| 44 | + return ARRAYS; | |
| 45 | + } | |
| 46 | + | |
| 47 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| 3 | + xmlns="http://maven.apache.org/POM/4.0.0" | |
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| 5 | + <parent> | |
| 6 | + <groupId>cn.iocoder.boot</groupId> | |
| 7 | + <artifactId>yudao-module-crm</artifactId> | |
| 8 | + <version>${revision}</version> | |
| 9 | + </parent> | |
| 10 | + <modelVersion>4.0.0</modelVersion> | |
| 11 | + <artifactId>yudao-module-crm-biz</artifactId> | |
| 12 | + | |
| 13 | + <name>${project.artifactId}</name> | |
| 14 | + <description> | |
| 15 | + crm 包下,客户关系管理(Customer Relationship Management)。 | |
| 16 | + 例如说:客户、联系人、商机、合同、回款等等 | |
| 17 | + </description> | |
| 18 | + | |
| 19 | + <dependencies> | |
| 20 | + <dependency> | |
| 21 | + <groupId>cn.iocoder.boot</groupId> | |
| 22 | + <artifactId>yudao-module-system-api</artifactId> | |
| 23 | + <version>${revision}</version> | |
| 24 | + </dependency> | |
| 25 | + <dependency> | |
| 26 | + <groupId>cn.iocoder.boot</groupId> | |
| 27 | + <artifactId>yudao-module-crm-api</artifactId> | |
| 28 | + <version>${revision}</version> | |
| 29 | + </dependency> | |
| 30 | + | |
| 31 | + <!-- 业务组件 --> | |
| 32 | + <dependency> | |
| 33 | + <groupId>cn.iocoder.boot</groupId> | |
| 34 | + <artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId> | |
| 35 | + </dependency> | |
| 36 | + <dependency> | |
| 37 | + <groupId>cn.iocoder.boot</groupId> | |
| 38 | + <artifactId>yudao-spring-boot-starter-biz-ip</artifactId> | |
| 39 | + </dependency> | |
| 40 | + | |
| 41 | + <!-- Web 相关 --> | |
| 42 | + <dependency> | |
| 43 | + <groupId>cn.iocoder.boot</groupId> | |
| 44 | + <artifactId>yudao-spring-boot-starter-web</artifactId> | |
| 45 | + </dependency> | |
| 46 | + | |
| 47 | + <dependency> | |
| 48 | + <groupId>cn.iocoder.boot</groupId> | |
| 49 | + <artifactId>yudao-spring-boot-starter-security</artifactId> | |
| 50 | + </dependency> | |
| 51 | + | |
| 52 | + <!-- DB 相关 --> | |
| 53 | + <dependency> | |
| 54 | + <groupId>cn.iocoder.boot</groupId> | |
| 55 | + <artifactId>yudao-spring-boot-starter-mybatis</artifactId> | |
| 56 | + </dependency> | |
| 57 | + | |
| 58 | + <!-- 工具类相关 --> | |
| 59 | + <dependency> | |
| 60 | + <groupId>cn.iocoder.boot</groupId> | |
| 61 | + <artifactId>yudao-spring-boot-starter-excel</artifactId> | |
| 62 | + </dependency> | |
| 63 | + | |
| 64 | + <!-- Test 测试相关 --> | |
| 65 | + <dependency> | |
| 66 | + <groupId>cn.iocoder.boot</groupId> | |
| 67 | + <artifactId>yudao-spring-boot-starter-test</artifactId> | |
| 68 | + </dependency> | |
| 69 | + </dependencies> | |
| 70 | +</project> | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java
0 → 100644
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.http
0 → 100644
| 1 | +### 请求 /transfer | |
| 2 | +PUT {{baseUrl}}/crm/business/transfer | |
| 3 | +Content-Type: application/json | |
| 4 | +Authorization: Bearer {{token}} | |
| 5 | +tenant-id: {{adminTenentId}} | |
| 6 | + | |
| 7 | +{ | |
| 8 | + "id": 1, | |
| 9 | + "ownerUserId": 2, | |
| 10 | + "transferType": 2, | |
| 11 | + "permissionType": 2 | |
| 12 | +} | |
| 13 | + | |
| 14 | +### 请求 /update | |
| 15 | +PUT {{baseUrl}}/crm/business/update | |
| 16 | +Content-Type: application/json | |
| 17 | +Authorization: Bearer {{token}} | |
| 18 | +tenant-id: {{adminTenentId}} | |
| 19 | + | |
| 20 | +{ | |
| 21 | + "id": 1, | |
| 22 | + "name": "2", | |
| 23 | + "statusTypeId": 2, | |
| 24 | + "statusId": 2, | |
| 25 | + "customerId": 1 | |
| 26 | +} | |
| 27 | + | |
| 28 | +### 请求 /get | |
| 29 | +GET {{baseUrl}}/crm/business/get?id=1024 | |
| 30 | +Content-Type: application/json | |
| 31 | +Authorization: Bearer {{token}} | |
| 32 | +tenant-id: {{adminTenentId}} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.business; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 6 | +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | |
| 7 | +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.*; | |
| 8 | +import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert; | |
| 9 | +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; | |
| 10 | +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; | |
| 11 | +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; | |
| 12 | +import io.swagger.v3.oas.annotations.Operation; | |
| 13 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 14 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 15 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 16 | +import org.springframework.validation.annotation.Validated; | |
| 17 | +import org.springframework.web.bind.annotation.*; | |
| 18 | + | |
| 19 | +import javax.annotation.Resource; | |
| 20 | +import javax.servlet.http.HttpServletResponse; | |
| 21 | +import javax.validation.Valid; | |
| 22 | +import java.io.IOException; | |
| 23 | +import java.util.List; | |
| 24 | + | |
| 25 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 26 | +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; | |
| 27 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 28 | + | |
| 29 | +@Tag(name = "管理后台 - 商机") | |
| 30 | +@RestController | |
| 31 | +@RequestMapping("/crm/business") | |
| 32 | +@Validated | |
| 33 | +public class CrmBusinessController { | |
| 34 | + | |
| 35 | + @Resource | |
| 36 | + private CrmBusinessService businessService; | |
| 37 | + | |
| 38 | + @PostMapping("/create") | |
| 39 | + @Operation(summary = "创建商机") | |
| 40 | + @PreAuthorize("@ss.hasPermission('crm:business:create')") | |
| 41 | + public CommonResult<Long> createBusiness(@Valid @RequestBody CrmBusinessCreateReqVO createReqVO) { | |
| 42 | + return success(businessService.createBusiness(createReqVO, getLoginUserId())); | |
| 43 | + } | |
| 44 | + | |
| 45 | + @PutMapping("/update") | |
| 46 | + @Operation(summary = "更新商机") | |
| 47 | + @PreAuthorize("@ss.hasPermission('crm:business:update')") | |
| 48 | + public CommonResult<Boolean> updateBusiness(@Valid @RequestBody CrmBusinessUpdateReqVO updateReqVO) { | |
| 49 | + businessService.updateBusiness(updateReqVO); | |
| 50 | + return success(true); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @DeleteMapping("/delete") | |
| 54 | + @Operation(summary = "删除商机") | |
| 55 | + @Parameter(name = "id", description = "编号", required = true) | |
| 56 | + @PreAuthorize("@ss.hasPermission('crm:business:delete')") | |
| 57 | + public CommonResult<Boolean> deleteBusiness(@RequestParam("id") Long id) { | |
| 58 | + businessService.deleteBusiness(id); | |
| 59 | + return success(true); | |
| 60 | + } | |
| 61 | + | |
| 62 | + @GetMapping("/get") | |
| 63 | + @Operation(summary = "获得商机") | |
| 64 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 65 | + @PreAuthorize("@ss.hasPermission('crm:business:query')") | |
| 66 | + public CommonResult<CrmBusinessRespVO> getBusiness(@RequestParam("id") Long id) { | |
| 67 | + CrmBusinessDO business = businessService.getBusiness(id); | |
| 68 | + return success(CrmBusinessConvert.INSTANCE.convert(business)); | |
| 69 | + } | |
| 70 | + | |
| 71 | + @GetMapping("/page") | |
| 72 | + @Operation(summary = "获得商机分页") | |
| 73 | + @PreAuthorize("@ss.hasPermission('crm:business:query')") | |
| 74 | + public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPage(@Valid CrmBusinessPageReqVO pageVO) { | |
| 75 | + PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO, getLoginUserId()); | |
| 76 | + return success(CrmBusinessConvert.INSTANCE.convertPage(pageResult)); | |
| 77 | + } | |
| 78 | + | |
| 79 | + @GetMapping("/pool-page") | |
| 80 | + @Operation(summary = "获得商机公海分页") | |
| 81 | + @PreAuthorize("@ss.hasPermission('crm:business:query')") | |
| 82 | + public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPoolPage(@Valid CrmBusinessPageReqVO pageVO) { | |
| 83 | + PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO, CrmPermissionDO.POOL_USER_ID); | |
| 84 | + return success(CrmBusinessConvert.INSTANCE.convertPage(pageResult)); | |
| 85 | + } | |
| 86 | + | |
| 87 | + @GetMapping("/export-excel") | |
| 88 | + @Operation(summary = "导出商机 Excel") | |
| 89 | + @PreAuthorize("@ss.hasPermission('crm:business:export')") | |
| 90 | + @OperateLog(type = EXPORT) | |
| 91 | + public void exportBusinessExcel(@Valid CrmBusinessExportReqVO exportReqVO, | |
| 92 | + HttpServletResponse response) throws IOException { | |
| 93 | + List<CrmBusinessDO> list = businessService.getBusinessList(exportReqVO); | |
| 94 | + // 导出 Excel | |
| 95 | + List<CrmBusinessExcelVO> datas = CrmBusinessConvert.INSTANCE.convertList02(list); | |
| 96 | + ExcelUtils.write(response, "商机.xls", "数据", CrmBusinessExcelVO.class, datas); | |
| 97 | + } | |
| 98 | + | |
| 99 | + @PutMapping("/transfer") | |
| 100 | + @Operation(summary = "商机转移") | |
| 101 | + @PreAuthorize("@ss.hasPermission('crm:business:update')") | |
| 102 | + public CommonResult<Boolean> transfer(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) { | |
| 103 | + businessService.transferBusiness(reqVO, getLoginUserId()); | |
| 104 | + return success(true); | |
| 105 | + } | |
| 106 | + | |
| 107 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/package-info.java
0 → 100644
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessBaseVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.business.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 6 | + | |
| 7 | +import javax.validation.constraints.NotNull; | |
| 8 | +import java.math.BigDecimal; | |
| 9 | +import java.time.LocalDateTime; | |
| 10 | + | |
| 11 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * 商机 Base VO,提供给添加、修改、详细的子 VO 使用 | |
| 15 | + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | |
| 16 | + */ | |
| 17 | +@Data | |
| 18 | +public class CrmBusinessBaseVO { | |
| 19 | + | |
| 20 | + @Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") | |
| 21 | + @NotNull(message = "商机名称不能为空") | |
| 22 | + private String name; | |
| 23 | + | |
| 24 | + @Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714") | |
| 25 | + @NotNull(message = "商机状态类型不能为空") | |
| 26 | + private Long statusTypeId; | |
| 27 | + | |
| 28 | + @Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") | |
| 29 | + @NotNull(message = "商机状态不能为空") | |
| 30 | + private Long statusId; | |
| 31 | + | |
| 32 | + @Schema(description = "下次联系时间") | |
| 33 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 34 | + private LocalDateTime contactNextTime; | |
| 35 | + | |
| 36 | + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299") | |
| 37 | + @NotNull(message = "客户不能为空") | |
| 38 | + private Long customerId; | |
| 39 | + | |
| 40 | + @Schema(description = "预计成交日期") | |
| 41 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 42 | + private LocalDateTime dealTime; | |
| 43 | + | |
| 44 | + @Schema(description = "商机金额", example = "12371") | |
| 45 | + private Integer price; | |
| 46 | + | |
| 47 | + // TODO @ljileo:折扣使用 Integer 类型,存储时,默认 * 100;展示的时候,前端需要 / 100;避免精度丢失问题 | |
| 48 | + @Schema(description = "整单折扣") | |
| 49 | + private Integer discountPercent; | |
| 50 | + | |
| 51 | + @Schema(description = "产品总金额", example = "12025") | |
| 52 | + private BigDecimal productPrice; | |
| 53 | + | |
| 54 | + @Schema(description = "备注", example = "随便") | |
| 55 | + private String remark; | |
| 56 | + | |
| 57 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.business.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - 商机创建 Request VO") | |
| 9 | +@Data | |
| 10 | +@EqualsAndHashCode(callSuper = true) | |
| 11 | +@ToString(callSuper = true) | |
| 12 | +public class CrmBusinessCreateReqVO extends CrmBusinessBaseVO { | |
| 13 | + | |
| 14 | + // TODO @ljileo:新建的时候,应该可以传递添加的产品; | |
| 15 | + | |
| 16 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.business.vo; | |
| 2 | + | |
| 3 | +import com.alibaba.excel.annotation.ExcelProperty; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.math.BigDecimal; | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | +import java.util.Set; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 商机 Excel VO | |
| 12 | + * | |
| 13 | + * @author ljlleo | |
| 14 | + */ | |
| 15 | +@Data | |
| 16 | +public class CrmBusinessExcelVO { | |
| 17 | + | |
| 18 | + @ExcelProperty("主键") | |
| 19 | + private Long id; | |
| 20 | + | |
| 21 | + @ExcelProperty("商机名称") | |
| 22 | + private String name; | |
| 23 | + | |
| 24 | + @ExcelProperty("商机状态类型编号") | |
| 25 | + private Long statusTypeId; | |
| 26 | + | |
| 27 | + @ExcelProperty("商机状态编号") | |
| 28 | + private Long statusId; | |
| 29 | + | |
| 30 | + @ExcelProperty("下次联系时间") | |
| 31 | + private LocalDateTime contactNextTime; | |
| 32 | + | |
| 33 | + @ExcelProperty("客户编号") | |
| 34 | + private Long customerId; | |
| 35 | + | |
| 36 | + @ExcelProperty("预计成交日期") | |
| 37 | + private LocalDateTime dealTime; | |
| 38 | + | |
| 39 | + @ExcelProperty("商机金额") | |
| 40 | + private BigDecimal price; | |
| 41 | + | |
| 42 | + @ExcelProperty("整单折扣") | |
| 43 | + private BigDecimal discountPercent; | |
| 44 | + | |
| 45 | + @ExcelProperty("产品总金额") | |
| 46 | + private BigDecimal productPrice; | |
| 47 | + | |
| 48 | + @ExcelProperty("备注") | |
| 49 | + private String remark; | |
| 50 | + | |
| 51 | + @ExcelProperty("负责人的用户编号") | |
| 52 | + private Long ownerUserId; | |
| 53 | + | |
| 54 | + @ExcelProperty("创建时间") | |
| 55 | + private LocalDateTime createTime; | |
| 56 | + | |
| 57 | + @ExcelProperty("只读权限的用户编号数组") | |
| 58 | + private Set<Long> roUserIds; | |
| 59 | + | |
| 60 | + @ExcelProperty("读写权限的用户编号数组") | |
| 61 | + private Set<Long> rwUserIds; | |
| 62 | + | |
| 63 | + @ExcelProperty("1赢单2输单3无效") | |
| 64 | + private Integer endStatus; | |
| 65 | + | |
| 66 | + @ExcelProperty("结束时的备注") | |
| 67 | + private String endRemark; | |
| 68 | + | |
| 69 | + @ExcelProperty("最后跟进时间") | |
| 70 | + private LocalDateTime contactLastTime; | |
| 71 | + | |
| 72 | + @ExcelProperty("跟进状态") | |
| 73 | + private Integer followUpStatus; | |
| 74 | + | |
| 75 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessExportReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.business.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 6 | + | |
| 7 | +import java.math.BigDecimal; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | + | |
| 10 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - 商机 Excel 导出 Request VO,参数和 CrmBusinessPageReqVO 是一致的") | |
| 13 | +@Data | |
| 14 | +public class CrmBusinessExportReqVO { | |
| 15 | + | |
| 16 | + @Schema(description = "商机名称", example = "李四") | |
| 17 | + private String name; | |
| 18 | + | |
| 19 | + @Schema(description = "商机状态类型编号", example = "25714") | |
| 20 | + private Long statusTypeId; | |
| 21 | + | |
| 22 | + @Schema(description = "商机状态编号", example = "30320") | |
| 23 | + private Long statusId; | |
| 24 | + | |
| 25 | + @Schema(description = "下次联系时间") | |
| 26 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 27 | + private LocalDateTime[] contactNextTime; | |
| 28 | + | |
| 29 | + @Schema(description = "客户编号", example = "10299") | |
| 30 | + private Long customerId; | |
| 31 | + | |
| 32 | + @Schema(description = "预计成交日期") | |
| 33 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 34 | + private LocalDateTime[] dealTime; | |
| 35 | + | |
| 36 | + @Schema(description = "商机金额", example = "12371") | |
| 37 | + private BigDecimal price; | |
| 38 | + | |
| 39 | + @Schema(description = "整单折扣") | |
| 40 | + private BigDecimal discountPercent; | |
| 41 | + | |
| 42 | + @Schema(description = "产品总金额", example = "12025") | |
| 43 | + private BigDecimal productPrice; | |
| 44 | + | |
| 45 | + @Schema(description = "备注", example = "随便") | |
| 46 | + private String remark; | |
| 47 | + | |
| 48 | + @Schema(description = "负责人的用户编号", example = "25562") | |
| 49 | + private Long ownerUserId; | |
| 50 | + | |
| 51 | + @Schema(description = "创建时间") | |
| 52 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 53 | + private LocalDateTime[] createTime; | |
| 54 | + | |
| 55 | + @Schema(description = "只读权限的用户编号数组") | |
| 56 | + private String roUserIds; | |
| 57 | + | |
| 58 | + @Schema(description = "读写权限的用户编号数组") | |
| 59 | + private String rwUserIds; | |
| 60 | + | |
| 61 | + @Schema(description = "1赢单2输单3无效", example = "1") | |
| 62 | + private Integer endStatus; | |
| 63 | + | |
| 64 | + @Schema(description = "结束时的备注", example = "你说的对") | |
| 65 | + private String endRemark; | |
| 66 | + | |
| 67 | + @Schema(description = "最后跟进时间") | |
| 68 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 69 | + private LocalDateTime[] contactLastTime; | |
| 70 | + | |
| 71 | + @Schema(description = "跟进状态", example = "1") | |
| 72 | + private Integer followUpStatus; | |
| 73 | + | |
| 74 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.business.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | +import lombok.EqualsAndHashCode; | |
| 7 | +import lombok.ToString; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - 商机分页 Request VO") | |
| 10 | +@Data | |
| 11 | +@EqualsAndHashCode(callSuper = true) | |
| 12 | +@ToString(callSuper = true) | |
| 13 | +public class CrmBusinessPageReqVO extends PageParam { | |
| 14 | + | |
| 15 | + @Schema(description = "商机名称", example = "李四") | |
| 16 | + private String name; | |
| 17 | + | |
| 18 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.business.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.*; | |
| 5 | +import java.time.LocalDateTime; | |
| 6 | + | |
| 7 | +@Schema(description = "管理后台 - 商机 Response VO") | |
| 8 | +@Data | |
| 9 | +@EqualsAndHashCode(callSuper = true) | |
| 10 | +@ToString(callSuper = true) | |
| 11 | +public class CrmBusinessRespVO extends CrmBusinessBaseVO { | |
| 12 | + | |
| 13 | + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129") | |
| 14 | + private Long id; | |
| 15 | + | |
| 16 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 17 | + private LocalDateTime createTime; | |
| 18 | + | |
| 19 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessTransferReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.business.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.crm.framework.enums.CrmPermissionLevelEnum; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import javax.validation.constraints.NotNull; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - 商机转移 Request VO") | |
| 10 | +@Data | |
| 11 | +public class CrmBusinessTransferReqVO { | |
| 12 | + | |
| 13 | + @Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | |
| 14 | + @NotNull(message = "联系人编号不能为空") | |
| 15 | + private Long id; | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 新负责人的用户编号 | |
| 19 | + */ | |
| 20 | + @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | |
| 21 | + @NotNull(message = "新负责人的用户编号不能为空") | |
| 22 | + private Long newOwnerUserId; | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * 老负责人加入团队后的权限级别。如果 null 说明移除 | |
| 26 | + * | |
| 27 | + * 关联 {@link CrmPermissionLevelEnum} | |
| 28 | + */ | |
| 29 | + @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 30 | + private Integer oldOwnerPermissionLevel; | |
| 31 | + | |
| 32 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessUpdateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.business.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +import javax.validation.constraints.NotNull; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - 商机更新 Request VO") | |
| 11 | +@Data | |
| 12 | +@EqualsAndHashCode(callSuper = true) | |
| 13 | +@ToString(callSuper = true) | |
| 14 | +public class CrmBusinessUpdateReqVO extends CrmBusinessBaseVO { | |
| 15 | + | |
| 16 | + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129") | |
| 17 | + @NotNull(message = "主键不能为空") | |
| 18 | + private Long id; | |
| 19 | + | |
| 20 | + // TODO @ljileo:修改的时候,应该可以传递添加的产品; | |
| 21 | + | |
| 22 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/CrmBusinessStatusController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatus; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 6 | +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | |
| 7 | +import cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo.*; | |
| 8 | +import cn.iocoder.yudao.module.crm.convert.businessstatus.CrmBusinessStatusConvert; | |
| 9 | +import cn.iocoder.yudao.module.crm.dal.dataobject.businessstatus.CrmBusinessStatusDO; | |
| 10 | +import cn.iocoder.yudao.module.crm.service.businessstatus.CrmBusinessStatusService; | |
| 11 | +import io.swagger.v3.oas.annotations.Operation; | |
| 12 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 13 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 14 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 15 | +import org.springframework.validation.annotation.Validated; | |
| 16 | +import org.springframework.web.bind.annotation.*; | |
| 17 | + | |
| 18 | +import javax.annotation.Resource; | |
| 19 | +import javax.servlet.http.HttpServletResponse; | |
| 20 | +import javax.validation.Valid; | |
| 21 | +import java.io.IOException; | |
| 22 | +import java.util.Collection; | |
| 23 | +import java.util.List; | |
| 24 | + | |
| 25 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 26 | +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; | |
| 27 | + | |
| 28 | +// TODO @lilleo:这个模块,可以挪到 business 下;这样我打开 business 包下,就知道,噢~原来里面有 business 商机、有 type 状态类型、status 具体状态; | |
| 29 | +@Tag(name = "管理后台 - 商机状态") | |
| 30 | +@RestController | |
| 31 | +@RequestMapping("/crm/business-status") | |
| 32 | +@Validated | |
| 33 | +public class CrmBusinessStatusController { | |
| 34 | + | |
| 35 | + @Resource | |
| 36 | + private CrmBusinessStatusService businessStatusService; | |
| 37 | + | |
| 38 | + @PostMapping("/create") | |
| 39 | + @Operation(summary = "创建商机状态") | |
| 40 | + @PreAuthorize("@ss.hasPermission('crm:business-status:create')") | |
| 41 | + public CommonResult<Long> createBusinessStatus(@Valid @RequestBody CrmBusinessStatusCreateReqVO createReqVO) { | |
| 42 | + return success(businessStatusService.createBusinessStatus(createReqVO)); | |
| 43 | + } | |
| 44 | + | |
| 45 | + @PutMapping("/update") | |
| 46 | + @Operation(summary = "更新商机状态") | |
| 47 | + @PreAuthorize("@ss.hasPermission('crm:business-status:update')") | |
| 48 | + public CommonResult<Boolean> updateBusinessStatus(@Valid @RequestBody CrmBusinessStatusUpdateReqVO updateReqVO) { | |
| 49 | + businessStatusService.updateBusinessStatus(updateReqVO); | |
| 50 | + return success(true); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @DeleteMapping("/delete") | |
| 54 | + @Operation(summary = "删除商机状态") | |
| 55 | + @Parameter(name = "id", description = "编号", required = true) | |
| 56 | + @PreAuthorize("@ss.hasPermission('crm:business-status:delete')") | |
| 57 | + public CommonResult<Boolean> deleteBusinessStatus(@RequestParam("id") Long id) { | |
| 58 | + businessStatusService.deleteBusinessStatus(id); | |
| 59 | + return success(true); | |
| 60 | + } | |
| 61 | + | |
| 62 | + @GetMapping("/get") | |
| 63 | + @Operation(summary = "获得商机状态") | |
| 64 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 65 | + @PreAuthorize("@ss.hasPermission('crm:business-status:query')") | |
| 66 | + public CommonResult<CrmBusinessStatusRespVO> getBusinessStatus(@RequestParam("id") Long id) { | |
| 67 | + CrmBusinessStatusDO businessStatus = businessStatusService.getBusinessStatus(id); | |
| 68 | + return success(CrmBusinessStatusConvert.INSTANCE.convert(businessStatus)); | |
| 69 | + } | |
| 70 | + | |
| 71 | + // TODO @lilleo:这个接口,暂时用不到,可以考虑先删除掉 | |
| 72 | + @GetMapping("/list") | |
| 73 | + @Operation(summary = "获得商机状态列表") | |
| 74 | + @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048") | |
| 75 | + @PreAuthorize("@ss.hasPermission('crm:business-status:query')") | |
| 76 | + public CommonResult<List<CrmBusinessStatusRespVO>> getBusinessStatusList(@RequestParam("ids") Collection<Long> ids) { | |
| 77 | + List<CrmBusinessStatusDO> list = businessStatusService.getBusinessStatusList(ids); | |
| 78 | + return success(CrmBusinessStatusConvert.INSTANCE.convertList(list)); | |
| 79 | + } | |
| 80 | + | |
| 81 | + @GetMapping("/page") | |
| 82 | + @Operation(summary = "获得商机状态分页") | |
| 83 | + @PreAuthorize("@ss.hasPermission('crm:business-status:query')") | |
| 84 | + public CommonResult<PageResult<CrmBusinessStatusRespVO>> getBusinessStatusPage(@Valid CrmBusinessStatusPageReqVO pageVO) { | |
| 85 | + PageResult<CrmBusinessStatusDO> pageResult = businessStatusService.getBusinessStatusPage(pageVO); | |
| 86 | + return success(CrmBusinessStatusConvert.INSTANCE.convertPage(pageResult)); | |
| 87 | + } | |
| 88 | + | |
| 89 | + @GetMapping("/export-excel") | |
| 90 | + @Operation(summary = "导出商机状态 Excel") | |
| 91 | + @PreAuthorize("@ss.hasPermission('crm:business-status:export')") | |
| 92 | + @OperateLog(type = EXPORT) | |
| 93 | + public void exportBusinessStatusExcel(@Valid CrmBusinessStatusExportReqVO exportReqVO, | |
| 94 | + HttpServletResponse response) throws IOException { | |
| 95 | + List<CrmBusinessStatusDO> list = businessStatusService.getBusinessStatusList(exportReqVO); | |
| 96 | + // 导出 Excel | |
| 97 | + List<CrmBusinessStatusExcelVO> datas = CrmBusinessStatusConvert.INSTANCE.convertList02(list); | |
| 98 | + ExcelUtils.write(response, "商机状态.xls", "数据", CrmBusinessStatusExcelVO.class, datas); | |
| 99 | + } | |
| 100 | + | |
| 101 | + // TODO 芋艿:后续再看看 | |
| 102 | + @GetMapping("/get-simple-list") | |
| 103 | + @Operation(summary = "获得商机状态列表") | |
| 104 | + @PreAuthorize("@ss.hasPermission('crm:business-status:query')") | |
| 105 | + public CommonResult<List<CrmBusinessStatusRespVO>> getBusinessStatusListByTypeId(@RequestParam("typeId") Integer typeId) { | |
| 106 | + List<CrmBusinessStatusDO> list = businessStatusService.getBusinessStatusListByTypeId(typeId); | |
| 107 | + return success(CrmBusinessStatusConvert.INSTANCE.convertList(list)); | |
| 108 | + } | |
| 109 | + | |
| 110 | + // TODO 芋艿:后续再看看 | |
| 111 | + @GetMapping("/get-all-list") | |
| 112 | + @Operation(summary = "获得商机状态列表") | |
| 113 | + @PreAuthorize("@ss.hasPermission('crm:business-status:query')") | |
| 114 | + public CommonResult<List<CrmBusinessStatusRespVO>> getBusinessStatusList() { | |
| 115 | + List<CrmBusinessStatusDO> list = businessStatusService.getBusinessStatusList(); | |
| 116 | + return success(CrmBusinessStatusConvert.INSTANCE.convertList(list)); | |
| 117 | + } | |
| 118 | + | |
| 119 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusBaseVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import javax.validation.constraints.NotNull; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * 商机状态 Base VO,提供给添加、修改、详细的子 VO 使用 | |
| 10 | + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | |
| 11 | + */ | |
| 12 | +@Data | |
| 13 | +public class CrmBusinessStatusBaseVO { | |
| 14 | + | |
| 15 | + // TODO @lilleo:example 要写下 | |
| 16 | + | |
| 17 | + @Schema(description = "状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22882") | |
| 18 | + @NotNull(message = "状态类型编号不能为空") | |
| 19 | + private Long typeId; | |
| 20 | + | |
| 21 | + @Schema(description = "状态名", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") | |
| 22 | + @NotNull(message = "状态名不能为空") | |
| 23 | + private String name; | |
| 24 | + | |
| 25 | + // TODO @lilleo:percent 应该是 Integer; | |
| 26 | + @Schema(description = "赢单率") | |
| 27 | + private String percent; | |
| 28 | + | |
| 29 | + // TODO @lilleo:这个是不是不用前端新增和修改的时候传递,交给顺序计算出来,存储起来就好了; | |
| 30 | + @Schema(description = "排序") | |
| 31 | + private Integer sort; | |
| 32 | + | |
| 33 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo; | |
| 2 | + | |
| 3 | +import lombok.*; | |
| 4 | +import java.util.*; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import javax.validation.constraints.*; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - 商机状态创建 Request VO") | |
| 9 | +@Data | |
| 10 | +@EqualsAndHashCode(callSuper = true) | |
| 11 | +@ToString(callSuper = true) | |
| 12 | +public class CrmBusinessStatusCreateReqVO extends CrmBusinessStatusBaseVO { | |
| 13 | + | |
| 14 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo; | |
| 2 | + | |
| 3 | +import com.alibaba.excel.annotation.ExcelProperty; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +// TODO @lilleo:这个暂时不需要;嘿嘿~不是每个模块都需要导出哈 | |
| 7 | +/** | |
| 8 | + * 商机状态 Excel VO | |
| 9 | + * | |
| 10 | + * @author ljlleo | |
| 11 | + */ | |
| 12 | +@Data | |
| 13 | +public class CrmBusinessStatusExcelVO { | |
| 14 | + | |
| 15 | + @ExcelProperty("主键") | |
| 16 | + private Long id; | |
| 17 | + | |
| 18 | + @ExcelProperty("状态类型编号") | |
| 19 | + private Long typeId; | |
| 20 | + | |
| 21 | + @ExcelProperty("状态名") | |
| 22 | + private String name; | |
| 23 | + | |
| 24 | + @ExcelProperty("赢单率") | |
| 25 | + private String percent; | |
| 26 | + | |
| 27 | + @ExcelProperty("排序") | |
| 28 | + private Integer sort; | |
| 29 | + | |
| 30 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusExportReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +// TODO @lilleo:这个暂时不需要;嘿嘿~不是每个模块都需要导出哈 | |
| 7 | +@Schema(description = "管理后台 - 商机状态 Excel 导出 Request VO,参数和 CrmBusinessStatusPageReqVO 是一致的") | |
| 8 | +@Data | |
| 9 | +public class CrmBusinessStatusExportReqVO { | |
| 10 | + | |
| 11 | + @Schema(description = "状态类型编号", example = "22882") | |
| 12 | + private Long typeId; | |
| 13 | + | |
| 14 | + @Schema(description = "状态名", example = "李四") | |
| 15 | + private String name; | |
| 16 | + | |
| 17 | + @Schema(description = "赢单率") | |
| 18 | + private String percent; | |
| 19 | + | |
| 20 | + @Schema(description = "排序") | |
| 21 | + private Integer sort; | |
| 22 | + | |
| 23 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | +import lombok.EqualsAndHashCode; | |
| 7 | +import lombok.ToString; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - 商机状态分页 Request VO") | |
| 10 | +@Data | |
| 11 | +@EqualsAndHashCode(callSuper = true) | |
| 12 | +@ToString(callSuper = true) | |
| 13 | +public class CrmBusinessStatusPageReqVO extends PageParam { | |
| 14 | + | |
| 15 | + @Schema(description = "状态类型编号", example = "22882") | |
| 16 | + private Long typeId; | |
| 17 | + | |
| 18 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.*; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - 商机状态 Response VO") | |
| 7 | +@Data | |
| 8 | +@EqualsAndHashCode(callSuper = true) | |
| 9 | +@ToString(callSuper = true) | |
| 10 | +public class CrmBusinessStatusRespVO extends CrmBusinessStatusBaseVO { | |
| 11 | + | |
| 12 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6802") | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusUpdateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +import javax.validation.constraints.NotNull; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - 商机状态更新 Request VO") | |
| 11 | +@Data | |
| 12 | +@EqualsAndHashCode(callSuper = true) | |
| 13 | +@ToString(callSuper = true) | |
| 14 | +public class CrmBusinessStatusUpdateReqVO extends CrmBusinessStatusBaseVO { | |
| 15 | + | |
| 16 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6802") | |
| 17 | + @NotNull(message = "编号不能为空") | |
| 18 | + private Long id; | |
| 19 | + | |
| 20 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/CrmBusinessStatusTypeController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 6 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 7 | +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | |
| 8 | +import cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo.*; | |
| 9 | +import cn.iocoder.yudao.module.crm.convert.businessstatustype.CrmBusinessStatusTypeConvert; | |
| 10 | +import cn.iocoder.yudao.module.crm.dal.dataobject.businessstatustype.CrmBusinessStatusTypeDO; | |
| 11 | +import cn.iocoder.yudao.module.crm.service.businessstatustype.CrmBusinessStatusTypeService; | |
| 12 | +import io.swagger.v3.oas.annotations.Operation; | |
| 13 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 14 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 15 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 16 | +import org.springframework.validation.annotation.Validated; | |
| 17 | +import org.springframework.web.bind.annotation.*; | |
| 18 | + | |
| 19 | +import javax.annotation.Resource; | |
| 20 | +import javax.servlet.http.HttpServletResponse; | |
| 21 | +import javax.validation.Valid; | |
| 22 | +import java.io.IOException; | |
| 23 | +import java.util.Collection; | |
| 24 | +import java.util.List; | |
| 25 | + | |
| 26 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 27 | +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; | |
| 28 | + | |
| 29 | +// TODO @lilleo:这个模块,可以挪到 business 下;这样我打开 business 包下,就知道,噢~原来里面有 business 商机、有 type 状态类型、status 具体状态; | |
| 30 | +@Tag(name = "管理后台 - 商机状态类型") | |
| 31 | +@RestController | |
| 32 | +@RequestMapping("/crm/business-status-type") | |
| 33 | +@Validated | |
| 34 | +public class CrmBusinessStatusTypeController { | |
| 35 | + | |
| 36 | + @Resource | |
| 37 | + private CrmBusinessStatusTypeService businessStatusTypeService; | |
| 38 | + | |
| 39 | + @PostMapping("/create") | |
| 40 | + @Operation(summary = "创建商机状态类型") | |
| 41 | + @PreAuthorize("@ss.hasPermission('crm:business-status-type:create')") | |
| 42 | + public CommonResult<Long> createBusinessStatusType(@Valid @RequestBody CrmBusinessStatusTypeCreateReqVO createReqVO) { | |
| 43 | + return success(businessStatusTypeService.createBusinessStatusType(createReqVO)); | |
| 44 | + } | |
| 45 | + | |
| 46 | + @PutMapping("/update") | |
| 47 | + @Operation(summary = "更新商机状态类型") | |
| 48 | + @PreAuthorize("@ss.hasPermission('crm:business-status-type:update')") | |
| 49 | + public CommonResult<Boolean> updateBusinessStatusType(@Valid @RequestBody CrmBusinessStatusTypeUpdateReqVO updateReqVO) { | |
| 50 | + businessStatusTypeService.updateBusinessStatusType(updateReqVO); | |
| 51 | + return success(true); | |
| 52 | + } | |
| 53 | + | |
| 54 | + @DeleteMapping("/delete") | |
| 55 | + @Operation(summary = "删除商机状态类型") | |
| 56 | + @Parameter(name = "id", description = "编号", required = true) | |
| 57 | + @PreAuthorize("@ss.hasPermission('crm:business-status-type:delete')") | |
| 58 | + public CommonResult<Boolean> deleteBusinessStatusType(@RequestParam("id") Long id) { | |
| 59 | + businessStatusTypeService.deleteBusinessStatusType(id); | |
| 60 | + return success(true); | |
| 61 | + } | |
| 62 | + | |
| 63 | + @GetMapping("/get") | |
| 64 | + @Operation(summary = "获得商机状态类型") | |
| 65 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 66 | + @PreAuthorize("@ss.hasPermission('crm:business-status-type:query')") | |
| 67 | + public CommonResult<CrmBusinessStatusTypeRespVO> getBusinessStatusType(@RequestParam("id") Long id) { | |
| 68 | + CrmBusinessStatusTypeDO businessStatusType = businessStatusTypeService.getBusinessStatusType(id); | |
| 69 | + return success(CrmBusinessStatusTypeConvert.INSTANCE.convert(businessStatusType)); | |
| 70 | + } | |
| 71 | + | |
| 72 | + // TODO @lilleo:这个接口,暂时用不到,可以考虑先删除掉 | |
| 73 | + @GetMapping("/list") | |
| 74 | + @Operation(summary = "获得商机状态类型列表") | |
| 75 | + @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048") | |
| 76 | + @PreAuthorize("@ss.hasPermission('crm:business-status-type:query')") | |
| 77 | + public CommonResult<List<CrmBusinessStatusTypeRespVO>> getBusinessStatusTypeList(@RequestParam("ids") Collection<Long> ids) { | |
| 78 | + List<CrmBusinessStatusTypeDO> list = businessStatusTypeService.getBusinessStatusTypeList(ids); | |
| 79 | + return success(CrmBusinessStatusTypeConvert.INSTANCE.convertList(list)); | |
| 80 | + } | |
| 81 | + | |
| 82 | + @GetMapping("/page") | |
| 83 | + @Operation(summary = "获得商机状态类型分页") | |
| 84 | + @PreAuthorize("@ss.hasPermission('crm:business-status-type:query')") | |
| 85 | + public CommonResult<PageResult<CrmBusinessStatusTypeRespVO>> getBusinessStatusTypePage(@Valid CrmBusinessStatusTypePageReqVO pageVO) { | |
| 86 | + PageResult<CrmBusinessStatusTypeDO> pageResult = businessStatusTypeService.getBusinessStatusTypePage(pageVO); | |
| 87 | + return success(CrmBusinessStatusTypeConvert.INSTANCE.convertPage(pageResult)); | |
| 88 | + } | |
| 89 | + | |
| 90 | + @GetMapping("/export-excel") | |
| 91 | + @Operation(summary = "导出商机状态类型 Excel") | |
| 92 | + @PreAuthorize("@ss.hasPermission('crm:business-status-type:export')") | |
| 93 | + @OperateLog(type = EXPORT) | |
| 94 | + public void exportBusinessStatusTypeExcel(@Valid CrmBusinessStatusTypeExportReqVO exportReqVO, | |
| 95 | + HttpServletResponse response) throws IOException { | |
| 96 | + List<CrmBusinessStatusTypeDO> list = businessStatusTypeService.getBusinessStatusTypeList(exportReqVO); | |
| 97 | + // 导出 Excel | |
| 98 | + List<CrmBusinessStatusTypeExcelVO> datas = CrmBusinessStatusTypeConvert.INSTANCE.convertList02(list); | |
| 99 | + ExcelUtils.write(response, "商机状态类型.xls", "数据", CrmBusinessStatusTypeExcelVO.class, datas); | |
| 100 | + } | |
| 101 | + | |
| 102 | + @GetMapping("/get-simple-list") | |
| 103 | + @Operation(summary = "获得商机状态类型列表") | |
| 104 | + @PreAuthorize("@ss.hasPermission('crm:business-status-type:query')") | |
| 105 | + public CommonResult<List<CrmBusinessStatusTypeRespVO>> getBusinessStatusTypeList() { | |
| 106 | + List<CrmBusinessStatusTypeDO> list = businessStatusTypeService.getBusinessStatusTypeListByStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 107 | + return success(CrmBusinessStatusTypeConvert.INSTANCE.convertList(list)); | |
| 108 | + } | |
| 109 | + | |
| 110 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeBaseVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import javax.validation.constraints.NotNull; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * 商机状态类型 Base VO,提供给添加、修改、详细的子 VO 使用 | |
| 10 | + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | |
| 11 | + */ | |
| 12 | +@Data | |
| 13 | +public class CrmBusinessStatusTypeBaseVO { | |
| 14 | + | |
| 15 | + @Schema(description = "状态类型名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") | |
| 16 | + @NotNull(message = "状态类型名不能为空") | |
| 17 | + private String name; | |
| 18 | + | |
| 19 | + @Schema(description = "使用的部门编号", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 20 | + @NotNull(message = "使用的部门编号不能为空") | |
| 21 | + private String deptIds; | |
| 22 | + | |
| 23 | + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 24 | + @NotNull(message = "开启状态不能为空") | |
| 25 | + private Boolean status; | |
| 26 | + | |
| 27 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +// TODO 状态类型和状态添加,是在一个请求里,所以需要把 CrmBusinessStatusCreateReqVO 融合进来; | |
| 9 | +@Schema(description = "管理后台 - 商机状态类型创建 Request VO") | |
| 10 | +@Data | |
| 11 | +@EqualsAndHashCode(callSuper = true) | |
| 12 | +@ToString(callSuper = true) | |
| 13 | +public class CrmBusinessStatusTypeCreateReqVO extends CrmBusinessStatusTypeBaseVO { | |
| 14 | + | |
| 15 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo; | |
| 2 | + | |
| 3 | +import com.alibaba.excel.annotation.ExcelProperty; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +// TODO @lilleo:这个暂时不需要;嘿嘿~不是每个模块都需要导出哈 | |
| 9 | +/** | |
| 10 | + * 商机状态类型 Excel VO | |
| 11 | + * | |
| 12 | + * @author ljlleo | |
| 13 | + */ | |
| 14 | +@Data | |
| 15 | +public class CrmBusinessStatusTypeExcelVO { | |
| 16 | + | |
| 17 | + @ExcelProperty("主键") | |
| 18 | + private Long id; | |
| 19 | + | |
| 20 | + @ExcelProperty("状态类型名") | |
| 21 | + private String name; | |
| 22 | + | |
| 23 | + @ExcelProperty("使用的部门编号") | |
| 24 | + private String deptIds; | |
| 25 | + | |
| 26 | + @ExcelProperty("开启状态") | |
| 27 | + private Boolean status; | |
| 28 | + | |
| 29 | + @ExcelProperty("创建时间") | |
| 30 | + private LocalDateTime createTime; | |
| 31 | + | |
| 32 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeExportReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 6 | + | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | + | |
| 9 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 10 | + | |
| 11 | +// TODO @lilleo:这个暂时不需要;嘿嘿~不是每个模块都需要导出哈 | |
| 12 | +@Schema(description = "管理后台 - 商机状态类型 Excel 导出 Request VO,参数和 CrmBusinessStatusTypePageReqVO 是一致的") | |
| 13 | +@Data | |
| 14 | +public class CrmBusinessStatusTypeExportReqVO { | |
| 15 | + | |
| 16 | + @Schema(description = "状态类型名", example = "芋艿") | |
| 17 | + private String name; | |
| 18 | + | |
| 19 | + @Schema(description = "使用的部门编号") | |
| 20 | + private String deptIds; | |
| 21 | + | |
| 22 | + @Schema(description = "开启状态", example = "1") | |
| 23 | + private Boolean status; | |
| 24 | + | |
| 25 | + @Schema(description = "创建时间") | |
| 26 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 27 | + private LocalDateTime[] createTime; | |
| 28 | + | |
| 29 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypePageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | +import lombok.EqualsAndHashCode; | |
| 7 | +import lombok.ToString; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - 商机状态类型分页 Request VO") | |
| 10 | +@Data | |
| 11 | +@EqualsAndHashCode(callSuper = true) | |
| 12 | +@ToString(callSuper = true) | |
| 13 | +public class CrmBusinessStatusTypePageReqVO extends PageParam { | |
| 14 | + | |
| 15 | + @Schema(description = "状态类型名", example = "芋艿") | |
| 16 | + private String name; | |
| 17 | + | |
| 18 | + @Schema(description = "开启状态", example = "1") | |
| 19 | + private Boolean status; | |
| 20 | + | |
| 21 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.*; | |
| 5 | +import java.time.LocalDateTime; | |
| 6 | + | |
| 7 | +@Schema(description = "管理后台 - 商机状态类型 Response VO") | |
| 8 | +@Data | |
| 9 | +@EqualsAndHashCode(callSuper = true) | |
| 10 | +@ToString(callSuper = true) | |
| 11 | +public class CrmBusinessStatusTypeRespVO extends CrmBusinessStatusTypeBaseVO { | |
| 12 | + | |
| 13 | + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "24019") | |
| 14 | + private Long id; | |
| 15 | + | |
| 16 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 17 | + private LocalDateTime createTime; | |
| 18 | + | |
| 19 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeUpdateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +import javax.validation.constraints.NotNull; | |
| 9 | + | |
| 10 | +// TODO 状态类型和状态添加,是在一个请求里,所以需要把 CrmBusinessStatusUpdateReqVO 融合进来; | |
| 11 | +@Schema(description = "管理后台 - 商机状态类型更新 Request VO") | |
| 12 | +@Data | |
| 13 | +@EqualsAndHashCode(callSuper = true) | |
| 14 | +@ToString(callSuper = true) | |
| 15 | +public class CrmBusinessStatusTypeUpdateReqVO extends CrmBusinessStatusTypeBaseVO { | |
| 16 | + | |
| 17 | + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "24019") | |
| 18 | + @NotNull(message = "主键不能为空") | |
| 19 | + private Long id; | |
| 20 | + | |
| 21 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.clue; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 6 | +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | |
| 7 | +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.*; | |
| 8 | +import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert; | |
| 9 | +import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; | |
| 10 | +import cn.iocoder.yudao.module.crm.service.clue.CrmClueService; | |
| 11 | +import io.swagger.v3.oas.annotations.Operation; | |
| 12 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 13 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 14 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 15 | +import org.springframework.validation.annotation.Validated; | |
| 16 | +import org.springframework.web.bind.annotation.*; | |
| 17 | + | |
| 18 | +import javax.annotation.Resource; | |
| 19 | +import javax.servlet.http.HttpServletResponse; | |
| 20 | +import javax.validation.Valid; | |
| 21 | +import java.io.IOException; | |
| 22 | +import java.util.List; | |
| 23 | + | |
| 24 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 25 | +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; | |
| 26 | + | |
| 27 | +@Tag(name = "管理后台 - 线索") | |
| 28 | +@RestController | |
| 29 | +@RequestMapping("/crm/clue") | |
| 30 | +@Validated | |
| 31 | +public class CrmClueController { | |
| 32 | + | |
| 33 | + @Resource | |
| 34 | + private CrmClueService clueService; | |
| 35 | + | |
| 36 | + @PostMapping("/create") | |
| 37 | + @Operation(summary = "创建线索") | |
| 38 | + @PreAuthorize("@ss.hasPermission('crm:clue:create')") | |
| 39 | + public CommonResult<Long> createClue(@Valid @RequestBody CrmClueCreateReqVO createReqVO) { | |
| 40 | + return success(clueService.createClue(createReqVO)); | |
| 41 | + } | |
| 42 | + | |
| 43 | + @PutMapping("/update") | |
| 44 | + @Operation(summary = "更新线索") | |
| 45 | + @PreAuthorize("@ss.hasPermission('crm:clue:update')") | |
| 46 | + public CommonResult<Boolean> updateClue(@Valid @RequestBody CrmClueUpdateReqVO updateReqVO) { | |
| 47 | + clueService.updateClue(updateReqVO); | |
| 48 | + return success(true); | |
| 49 | + } | |
| 50 | + | |
| 51 | + @DeleteMapping("/delete") | |
| 52 | + @Operation(summary = "删除线索") | |
| 53 | + @Parameter(name = "id", description = "编号", required = true) | |
| 54 | + @PreAuthorize("@ss.hasPermission('crm:clue:delete')") | |
| 55 | + public CommonResult<Boolean> deleteClue(@RequestParam("id") Long id) { | |
| 56 | + clueService.deleteClue(id); | |
| 57 | + return success(true); | |
| 58 | + } | |
| 59 | + | |
| 60 | + @GetMapping("/get") | |
| 61 | + @Operation(summary = "获得线索") | |
| 62 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 63 | + @PreAuthorize("@ss.hasPermission('crm:clue:query')") | |
| 64 | + public CommonResult<CrmClueRespVO> getClue(@RequestParam("id") Long id) { | |
| 65 | + CrmClueDO clue = clueService.getClue(id); | |
| 66 | + return success(CrmClueConvert.INSTANCE.convert(clue)); | |
| 67 | + } | |
| 68 | + | |
| 69 | + @GetMapping("/page") | |
| 70 | + @Operation(summary = "获得线索分页") | |
| 71 | + @PreAuthorize("@ss.hasPermission('crm:clue:query')") | |
| 72 | + public CommonResult<PageResult<CrmClueRespVO>> getCluePage(@Valid CrmCluePageReqVO pageVO) { | |
| 73 | + PageResult<CrmClueDO> pageResult = clueService.getCluePage(pageVO); | |
| 74 | + return success(CrmClueConvert.INSTANCE.convertPage(pageResult)); | |
| 75 | + } | |
| 76 | + | |
| 77 | + @GetMapping("/export-excel") | |
| 78 | + @Operation(summary = "导出线索 Excel") | |
| 79 | + @PreAuthorize("@ss.hasPermission('crm:clue:export')") | |
| 80 | + @OperateLog(type = EXPORT) | |
| 81 | + public void exportClueExcel(@Valid CrmClueExportReqVO exportReqVO, | |
| 82 | + HttpServletResponse response) throws IOException { | |
| 83 | + List<CrmClueDO> list = clueService.getClueList(exportReqVO); | |
| 84 | + // 导出 Excel | |
| 85 | + List<CrmClueExcelVO> datas = CrmClueConvert.INSTANCE.convertList02(list); | |
| 86 | + ExcelUtils.write(response, "线索.xls", "数据", CrmClueExcelVO.class, datas); | |
| 87 | + } | |
| 88 | + | |
| 89 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/package-info.java
0 → 100644
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueBaseVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.Mobile; | |
| 4 | +import cn.iocoder.yudao.framework.common.validation.Telephone; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import lombok.Data; | |
| 7 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 8 | + | |
| 9 | +import javax.validation.constraints.NotEmpty; | |
| 10 | +import javax.validation.constraints.NotNull; | |
| 11 | +import java.time.LocalDateTime; | |
| 12 | + | |
| 13 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * 线索 Base VO,提供给添加、修改、详细的子 VO 使用 | |
| 17 | + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | |
| 18 | + */ | |
| 19 | +@Data | |
| 20 | +public class CrmClueBaseVO { | |
| 21 | + | |
| 22 | + @Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx") | |
| 23 | + @NotEmpty(message = "线索名称不能为空") | |
| 24 | + private String name; | |
| 25 | + | |
| 26 | + @Schema(description = "客户 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "520") | |
| 27 | + @NotNull(message = "客户不能为空") | |
| 28 | + private Long customerId; | |
| 29 | + | |
| 30 | + @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00") | |
| 31 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 32 | + private LocalDateTime contactNextTime; | |
| 33 | + | |
| 34 | + @Schema(description = "电话", example = "18000000000") | |
| 35 | + @Telephone | |
| 36 | + private String telephone; | |
| 37 | + | |
| 38 | + @Schema(description = "手机号", example = "18000000000") | |
| 39 | + @Mobile | |
| 40 | + private String mobile; | |
| 41 | + | |
| 42 | + @Schema(description = "地址", example = "北京市海淀区") | |
| 43 | + private String address; | |
| 44 | + | |
| 45 | + @Schema(description = "最后跟进时间") | |
| 46 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 47 | + private LocalDateTime contactLastTime; | |
| 48 | + | |
| 49 | + @Schema(description = "备注", example = "随便") | |
| 50 | + private String remark; | |
| 51 | + | |
| 52 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; | |
| 2 | + | |
| 3 | +import lombok.*; | |
| 4 | +import java.util.*; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import javax.validation.constraints.*; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - 线索创建 Request VO") | |
| 9 | +@Data | |
| 10 | +@EqualsAndHashCode(callSuper = true) | |
| 11 | +@ToString(callSuper = true) | |
| 12 | +public class CrmClueCreateReqVO extends CrmClueBaseVO { | |
| 13 | + | |
| 14 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.*; | |
| 6 | +import java.util.*; | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | +import java.time.LocalDateTime; | |
| 10 | +import java.time.LocalDateTime; | |
| 11 | + | |
| 12 | +import com.alibaba.excel.annotation.ExcelProperty; | |
| 13 | +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | |
| 14 | +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | |
| 15 | + | |
| 16 | +/** | |
| 17 | + * 线索 Excel VO | |
| 18 | + * | |
| 19 | + * @author Wanwan | |
| 20 | + */ | |
| 21 | +@Data | |
| 22 | +public class CrmClueExcelVO { | |
| 23 | + | |
| 24 | + @ExcelProperty("编号") | |
| 25 | + private Long id; | |
| 26 | + | |
| 27 | + @ExcelProperty(value = "转化状态", converter = DictConvert.class) | |
| 28 | + @DictFormat(DictTypeConstants.BOOLEAN_STRING) | |
| 29 | + private Boolean transformStatus; | |
| 30 | + | |
| 31 | + @ExcelProperty(value = "跟进状态", converter = DictConvert.class) | |
| 32 | + @DictFormat(DictTypeConstants.BOOLEAN_STRING) | |
| 33 | + private Boolean followUpStatus; | |
| 34 | + | |
| 35 | + @ExcelProperty("线索名称") | |
| 36 | + private String name; | |
| 37 | + | |
| 38 | + // TODO 这里需要导出成客户名称 | |
| 39 | + @ExcelProperty("客户id") | |
| 40 | + private Long customerId; | |
| 41 | + | |
| 42 | + @ExcelProperty("下次联系时间") | |
| 43 | + private LocalDateTime contactNextTime; | |
| 44 | + | |
| 45 | + @ExcelProperty("电话") | |
| 46 | + private String telephone; | |
| 47 | + | |
| 48 | + @ExcelProperty("手机号") | |
| 49 | + private String mobile; | |
| 50 | + | |
| 51 | + @ExcelProperty("地址") | |
| 52 | + private String address; | |
| 53 | + | |
| 54 | + @ExcelProperty("负责人的用户编号") | |
| 55 | + private Long ownerUserId; | |
| 56 | + | |
| 57 | + @ExcelProperty("最后跟进时间") | |
| 58 | + private LocalDateTime contactLastTime; | |
| 59 | + | |
| 60 | + @ExcelProperty("备注") | |
| 61 | + private String remark; | |
| 62 | + | |
| 63 | + @ExcelProperty("创建时间") | |
| 64 | + private LocalDateTime createTime; | |
| 65 | + | |
| 66 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueExportReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; | |
| 2 | + | |
| 3 | +import lombok.*; | |
| 4 | +import java.util.*; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 9 | + | |
| 10 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - 线索 Excel 导出 Request VO,参数和 CrmCluePageReqVO 是一致的") | |
| 13 | +@Data | |
| 14 | +public class CrmClueExportReqVO { | |
| 15 | + | |
| 16 | + @Schema(description = "转化状态", example = "true") | |
| 17 | + private Boolean transformStatus; | |
| 18 | + | |
| 19 | + @Schema(description = "跟进状态", example = "true") | |
| 20 | + private Boolean followUpStatus; | |
| 21 | + | |
| 22 | + @Schema(description = "线索名称", example = "线索xxx") | |
| 23 | + private String name; | |
| 24 | + | |
| 25 | + @Schema(description = "客户id", example = "520") | |
| 26 | + private Long customerId; | |
| 27 | + | |
| 28 | + @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00") | |
| 29 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 30 | + private LocalDateTime[] contactNextTime; | |
| 31 | + | |
| 32 | + @Schema(description = "电话", example = "18000000000") | |
| 33 | + private String telephone; | |
| 34 | + | |
| 35 | + @Schema(description = "手机号", example = "18000000000") | |
| 36 | + private String mobile; | |
| 37 | + | |
| 38 | + @Schema(description = "地址", example = "北京市海淀区") | |
| 39 | + private String address; | |
| 40 | + | |
| 41 | + @Schema(description = "负责人的用户编号", example = "27199") | |
| 42 | + private Long ownerUserId; | |
| 43 | + | |
| 44 | + @Schema(description = "最后跟进时间") | |
| 45 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 46 | + private LocalDateTime[] contactLastTime; | |
| 47 | + | |
| 48 | + @Schema(description = "创建时间") | |
| 49 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 50 | + private LocalDateTime[] createTime; | |
| 51 | + | |
| 52 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | +import lombok.EqualsAndHashCode; | |
| 7 | +import lombok.ToString; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - 线索分页 Request VO") | |
| 10 | +@Data | |
| 11 | +@EqualsAndHashCode(callSuper = true) | |
| 12 | +@ToString(callSuper = true) | |
| 13 | +public class CrmCluePageReqVO extends PageParam { | |
| 14 | + | |
| 15 | + @Schema(description = "线索名称", example = "线索xxx") | |
| 16 | + private String name; | |
| 17 | + | |
| 18 | + @Schema(description = "电话", example = "18000000000") | |
| 19 | + private String telephone; | |
| 20 | + | |
| 21 | + @Schema(description = "手机号", example = "18000000000") | |
| 22 | + private String mobile; | |
| 23 | + | |
| 24 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.*; | |
| 5 | + | |
| 6 | +import javax.validation.constraints.NotNull; | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - 线索 Response VO") | |
| 10 | +@Data | |
| 11 | +@EqualsAndHashCode(callSuper = true) | |
| 12 | +@ToString(callSuper = true) | |
| 13 | +public class CrmClueRespVO extends CrmClueBaseVO { | |
| 14 | + | |
| 15 | + @Schema(description = "编号,主键自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "10969") | |
| 16 | + private Long id; | |
| 17 | + | |
| 18 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 19 | + private LocalDateTime createTime; | |
| 20 | + | |
| 21 | + @Schema(description = "转化状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 22 | + private Boolean transformStatus; | |
| 23 | + | |
| 24 | + @Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 25 | + private Boolean followUpStatus; | |
| 26 | + | |
| 27 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueUpdateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +import javax.validation.constraints.NotNull; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - 线索更新 Request VO") | |
| 11 | +@Data | |
| 12 | +@EqualsAndHashCode(callSuper = true) | |
| 13 | +@ToString(callSuper = true) | |
| 14 | +public class CrmClueUpdateReqVO extends CrmClueBaseVO { | |
| 15 | + | |
| 16 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10969") | |
| 17 | + @NotNull(message = "编号不能为空") | |
| 18 | + private Long id; | |
| 19 | + | |
| 20 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/ContactController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contact; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.NumberUtil; | |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 7 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 8 | +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | |
| 9 | +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*; | |
| 10 | +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerExportReqVO; | |
| 11 | +import cn.iocoder.yudao.module.crm.convert.contact.ContactConvert; | |
| 12 | +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.ContactDO; | |
| 13 | +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; | |
| 14 | +import cn.iocoder.yudao.module.crm.service.contact.ContactService; | |
| 15 | +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; | |
| 16 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 17 | +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | |
| 18 | +import com.google.common.collect.Lists; | |
| 19 | +import io.swagger.v3.oas.annotations.Operation; | |
| 20 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 21 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 22 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 23 | +import org.springframework.validation.annotation.Validated; | |
| 24 | +import org.springframework.web.bind.annotation.*; | |
| 25 | + | |
| 26 | +import javax.annotation.Resource; | |
| 27 | +import javax.servlet.http.HttpServletResponse; | |
| 28 | +import javax.validation.Valid; | |
| 29 | +import java.io.IOException; | |
| 30 | +import java.util.List; | |
| 31 | +import java.util.Map; | |
| 32 | +import java.util.Optional; | |
| 33 | +import java.util.stream.Collectors; | |
| 34 | + | |
| 35 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 36 | +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; | |
| 37 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 38 | + | |
| 39 | +// TODO @zya:crm 所有的类,dou带 Crm 前缀,因为它的名字太通用了,可能和后续的 erp 之类的冲突 | |
| 40 | +@Tag(name = "管理后台 - CRM 联系人") | |
| 41 | +@RestController | |
| 42 | +@RequestMapping("/crm/contact") | |
| 43 | +@Validated | |
| 44 | +public class ContactController { | |
| 45 | + | |
| 46 | + @Resource | |
| 47 | + private ContactService contactService; | |
| 48 | + // TODO @zyna:模块内,注入的变量,不用带 crm 前缀哈 | |
| 49 | + @Resource | |
| 50 | + private CrmCustomerService crmCustomerService; | |
| 51 | + | |
| 52 | + @Resource | |
| 53 | + private AdminUserApi adminUserApi; | |
| 54 | + | |
| 55 | + @PostMapping("/create") | |
| 56 | + @Operation(summary = "创建联系人") | |
| 57 | + @PreAuthorize("@ss.hasPermission('crm:contact:create')") | |
| 58 | + public CommonResult<Long> createContact(@Valid @RequestBody ContactCreateReqVO createReqVO) { | |
| 59 | + return success(contactService.createContact(createReqVO, getLoginUserId())); | |
| 60 | + } | |
| 61 | + | |
| 62 | + @PutMapping("/update") | |
| 63 | + @Operation(summary = "更新联系人") | |
| 64 | + @PreAuthorize("@ss.hasPermission('crm:contact:update')") | |
| 65 | + public CommonResult<Boolean> updateContact(@Valid @RequestBody ContactUpdateReqVO updateReqVO) { | |
| 66 | + contactService.updateContact(updateReqVO); | |
| 67 | + return success(true); | |
| 68 | + } | |
| 69 | + | |
| 70 | + @DeleteMapping("/delete") | |
| 71 | + @Operation(summary = "删除联系人") | |
| 72 | + @Parameter(name = "id", description = "编号", required = true) | |
| 73 | + @PreAuthorize("@ss.hasPermission('crm:contact:delete')") | |
| 74 | + public CommonResult<Boolean> deleteContact(@RequestParam("id") Long id) { | |
| 75 | + contactService.deleteContact(id); | |
| 76 | + return success(true); | |
| 77 | + } | |
| 78 | + | |
| 79 | + @GetMapping("/get") | |
| 80 | + @Operation(summary = "获得联系人") | |
| 81 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 82 | + @PreAuthorize("@ss.hasPermission('crm:contact:query')") | |
| 83 | + public CommonResult<ContactRespVO> getContact(@RequestParam("id") Long id) { | |
| 84 | + ContactDO contact = contactService.getContact(id); | |
| 85 | + // TODO @zyna:需要考虑 null 的情况; | |
| 86 | + ContactRespVO contactRespVO = ContactConvert.INSTANCE.convert(contact); | |
| 87 | + // TODO @zyna:可以把数据读完后,convert 统一交给 ContactConvert,让 controller 更简洁;而 convert 专门去做一些转换逻辑 | |
| 88 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(CollUtil.removeNull(Lists.newArrayList( | |
| 89 | + NumberUtil.parseLong(contact.getCreator())))); | |
| 90 | + contactRespVO.setCreatorName(Optional.ofNullable(userMap.get(NumberUtil.parseLong(contact.getCreator()))).map(AdminUserRespDTO::getNickname).orElse(null)); | |
| 91 | + contactRespVO.setCustomerName(Optional.ofNullable(crmCustomerService.getCustomer(contact.getCustomerId())).map(CrmCustomerDO::getName).orElse(null)); | |
| 92 | + return success(contactRespVO); | |
| 93 | + } | |
| 94 | + | |
| 95 | + // TODO @zyna:url 使用中划线噢;然后,单词的拼写也要注意呀,AllList 是不是更好呀; | |
| 96 | + @GetMapping("/simpleAlllist") | |
| 97 | + @Operation(summary = "获得联系人列表") | |
| 98 | + @PreAuthorize("@ss.hasPermission('crm:contact:query')") | |
| 99 | + public CommonResult<List<ContactSimpleRespVO>> simpleAlllist() { | |
| 100 | + // TODO @zyna:方法名改成,getContactList;方法命名,要动名词,get 动词;all 可以去掉,因为没条件,自然是全部 | |
| 101 | + List<ContactDO> list = contactService.allContactList(); | |
| 102 | + return success(ContactConvert.INSTANCE.convertAllList(list)); | |
| 103 | + } | |
| 104 | + | |
| 105 | + @GetMapping("/page") | |
| 106 | + @Operation(summary = "获得联系人分页") | |
| 107 | + @PreAuthorize("@ss.hasPermission('crm:contact:query')") | |
| 108 | + public CommonResult<PageResult<ContactRespVO>> getContactPage(@Valid ContactPageReqVO pageVO) { | |
| 109 | + PageResult<ContactDO> pageData = contactService.getContactPage(pageVO); | |
| 110 | + PageResult<ContactRespVO> pageResult =ContactConvert.INSTANCE.convertPage(pageData); | |
| 111 | + // TODO @zyna:需要考虑 null 的情况; | |
| 112 | + // TODO @zyna:可以把数据读完后,convert 统一交给 ContactConvert,让 controller 更简洁;而 convert 专门去做一些转换逻辑 | |
| 113 | + //待接口实现后修改 | |
| 114 | + List<CrmCustomerDO> crmCustomerDOList = crmCustomerService.getCustomerList(new CrmCustomerExportReqVO()); | |
| 115 | + Map<Long,CrmCustomerDO> crmCustomerDOMap = crmCustomerDOList.stream().collect(Collectors.toMap(CrmCustomerDO::getId,v->v)); | |
| 116 | + pageResult.getList().forEach(item -> { | |
| 117 | + item.setCustomerName(Optional.ofNullable(crmCustomerDOMap.get(item.getCustomerId())).map(CrmCustomerDO::getName).orElse(null)); | |
| 118 | + }); | |
| 119 | + return success(pageResult); | |
| 120 | + } | |
| 121 | + | |
| 122 | + // TODO @zyna:可以看下新的导出写法,这里调整下 | |
| 123 | + @GetMapping("/export-excel") | |
| 124 | + @Operation(summary = "导出联系人 Excel") | |
| 125 | + @PreAuthorize("@ss.hasPermission('crm:contact:export')") | |
| 126 | + @OperateLog(type = EXPORT) | |
| 127 | + public void exportContactExcel(@Valid ContactExportReqVO exportReqVO, | |
| 128 | + HttpServletResponse response) throws IOException { | |
| 129 | + List<ContactDO> list = contactService.getContactList(exportReqVO); | |
| 130 | + // 导出 Excel | |
| 131 | + List<ContactExcelVO> datas = ContactConvert.INSTANCE.convertList02(list); | |
| 132 | + ExcelUtils.write(response, "crm联系人.xls", "数据", ContactExcelVO.class, datas); | |
| 133 | + } | |
| 134 | + | |
| 135 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactBaseVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 6 | + | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | + | |
| 9 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY; | |
| 10 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 11 | + | |
| 12 | +// TODO zyna:参考新的 vo,重新拆分下 VO | |
| 13 | +/** | |
| 14 | + * CRM 联系人 Base VO,提供给添加、修改、详细的子 VO 使用 | |
| 15 | + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | |
| 16 | + */ | |
| 17 | +@Data | |
| 18 | +public class ContactBaseVO { | |
| 19 | + | |
| 20 | + // TODO @zyna:example 最好都写下 | |
| 21 | + // TODO @zyna:必要的字段校验,例如说 @Mobile,@Emal 等等 | |
| 22 | + | |
| 23 | + @Schema(description = "下次联系时间") | |
| 24 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY) | |
| 25 | + private LocalDateTime nextTime; | |
| 26 | + | |
| 27 | + @Schema(description = "手机号") | |
| 28 | + private String mobile; | |
| 29 | + | |
| 30 | + @Schema(description = "电话") | |
| 31 | + private String telephone; | |
| 32 | + | |
| 33 | + @Schema(description = "电子邮箱") | |
| 34 | + private String email; | |
| 35 | + | |
| 36 | + @Schema(description = "客户编号", example = "10795") | |
| 37 | + private Long customerId; | |
| 38 | + | |
| 39 | + @Schema(description = "地址") | |
| 40 | + private String address; | |
| 41 | + | |
| 42 | + @Schema(description = "备注", example = "你说的对") | |
| 43 | + private String remark; | |
| 44 | + | |
| 45 | + @Schema(description = "最后跟进时间") | |
| 46 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 47 | + private LocalDateTime lastTime; | |
| 48 | + | |
| 49 | + @Schema(description = "直属上级", example = "23457") | |
| 50 | + private Long parentId; | |
| 51 | + | |
| 52 | + @Schema(description = "姓名", example = "芋艿") | |
| 53 | + private String name; | |
| 54 | + | |
| 55 | + @Schema(description = "职位") | |
| 56 | + private String post; | |
| 57 | + | |
| 58 | + @Schema(description = "QQ") | |
| 59 | + private Long qq; | |
| 60 | + | |
| 61 | + @Schema(description = "微信") | |
| 62 | + private String webchat; | |
| 63 | + | |
| 64 | + @Schema(description = "性别") | |
| 65 | + private Integer sex; | |
| 66 | + | |
| 67 | + @Schema(description = "是否关键决策人") | |
| 68 | + private Boolean policyMakers; | |
| 69 | + | |
| 70 | + @Schema(description = "负责人用户编号", example = "14334") | |
| 71 | + private String ownerUserId; | |
| 72 | + | |
| 73 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - CRM 联系人创建 Request VO") | |
| 9 | +@Data | |
| 10 | +@EqualsAndHashCode(callSuper = true) | |
| 11 | +@ToString(callSuper = true) | |
| 12 | +public class ContactCreateReqVO extends ContactBaseVO { | |
| 13 | + | |
| 14 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; | |
| 2 | + | |
| 3 | +import com.alibaba.excel.annotation.ExcelProperty; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +// TODO @zyna:参考新的 VO 结构,把 ContactExcelVO 融合到 ContactRespVO 中 | |
| 9 | +/** | |
| 10 | + * crm联系人 Excel VO | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Data | |
| 15 | +@Deprecated | |
| 16 | +public class ContactExcelVO { | |
| 17 | + | |
| 18 | + @ExcelProperty("下次联系时间") | |
| 19 | + private LocalDateTime nextTime; | |
| 20 | + | |
| 21 | + @ExcelProperty("手机号") | |
| 22 | + private String mobile; | |
| 23 | + | |
| 24 | + @ExcelProperty("电话") | |
| 25 | + private String telephone; | |
| 26 | + | |
| 27 | + @ExcelProperty("电子邮箱") | |
| 28 | + private String email; | |
| 29 | + | |
| 30 | + @ExcelProperty("客户编号") | |
| 31 | + private Long customerId; | |
| 32 | + | |
| 33 | + @ExcelProperty("地址") | |
| 34 | + private String address; | |
| 35 | + | |
| 36 | + @ExcelProperty("备注") | |
| 37 | + private String remark; | |
| 38 | + | |
| 39 | + @ExcelProperty("创建时间") | |
| 40 | + private LocalDateTime createTime; | |
| 41 | + | |
| 42 | + @ExcelProperty("最后跟进时间") | |
| 43 | + private LocalDateTime lastTime; | |
| 44 | + | |
| 45 | + @ExcelProperty("主键") | |
| 46 | + private Long id; | |
| 47 | + | |
| 48 | + @ExcelProperty("直属上级") | |
| 49 | + private Long parentId; | |
| 50 | + | |
| 51 | + @ExcelProperty("姓名") | |
| 52 | + private String name; | |
| 53 | + | |
| 54 | + @ExcelProperty("职位") | |
| 55 | + private String post; | |
| 56 | + | |
| 57 | + @ExcelProperty("QQ") | |
| 58 | + private Long qq; | |
| 59 | + | |
| 60 | + @ExcelProperty("微信") | |
| 61 | + private String webchat; | |
| 62 | + | |
| 63 | + @ExcelProperty("性别") | |
| 64 | + private Integer sex; | |
| 65 | + | |
| 66 | + @ExcelProperty("是否关键决策人") | |
| 67 | + private Boolean policyMakers; | |
| 68 | + | |
| 69 | + @ExcelProperty("负责人用户编号") | |
| 70 | + private String ownerUserId; | |
| 71 | + | |
| 72 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactExportReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 6 | + | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | + | |
| 9 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 10 | + | |
| 11 | +// TODO @zyna:参考新的 VO 结构,使用 ContactPageReqVO 查询导出的数据 | |
| 12 | +@Schema(description = "管理后台 - crm联系人 Excel 导出 Request VO,参数和 ContactPageReqVO 是一致的") | |
| 13 | +@Data | |
| 14 | +@Deprecated | |
| 15 | +public class ContactExportReqVO { | |
| 16 | + | |
| 17 | + @Schema(description = "下次联系时间") | |
| 18 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 19 | + private LocalDateTime[] nextTime; | |
| 20 | + | |
| 21 | + @Schema(description = "手机号") | |
| 22 | + private String mobile; | |
| 23 | + | |
| 24 | + @Schema(description = "电话") | |
| 25 | + private String telephone; | |
| 26 | + | |
| 27 | + @Schema(description = "电子邮箱") | |
| 28 | + private String email; | |
| 29 | + | |
| 30 | + @Schema(description = "客户编号", example = "10795") | |
| 31 | + private Long customerId; | |
| 32 | + | |
| 33 | + @Schema(description = "地址") | |
| 34 | + private String address; | |
| 35 | + | |
| 36 | + @Schema(description = "备注", example = "你说的对") | |
| 37 | + private String remark; | |
| 38 | + | |
| 39 | + @Schema(description = "创建时间") | |
| 40 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 41 | + private LocalDateTime[] createTime; | |
| 42 | + | |
| 43 | + @Schema(description = "最后跟进时间") | |
| 44 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 45 | + private LocalDateTime[] lastTime; | |
| 46 | + | |
| 47 | + @Schema(description = "直属上级", example = "23457") | |
| 48 | + private Long parentId; | |
| 49 | + | |
| 50 | + @Schema(description = "姓名", example = "芋艿") | |
| 51 | + private String name; | |
| 52 | + | |
| 53 | + @Schema(description = "职位") | |
| 54 | + private String post; | |
| 55 | + | |
| 56 | + @Schema(description = "QQ") | |
| 57 | + private Long qq; | |
| 58 | + | |
| 59 | + @Schema(description = "微信") | |
| 60 | + private String webchat; | |
| 61 | + | |
| 62 | + @Schema(description = "性别") | |
| 63 | + private Integer sex; | |
| 64 | + | |
| 65 | + @Schema(description = "是否关键决策人") | |
| 66 | + private Boolean policyMakers; | |
| 67 | + | |
| 68 | + @Schema(description = "负责人用户编号", example = "14334") | |
| 69 | + private String ownerUserId; | |
| 70 | + | |
| 71 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | +import lombok.EqualsAndHashCode; | |
| 7 | +import lombok.ToString; | |
| 8 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 9 | + | |
| 10 | +import java.time.LocalDateTime; | |
| 11 | + | |
| 12 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 13 | + | |
| 14 | +@Schema(description = "管理后台 - crm联系人分页 Request VO") | |
| 15 | +@Data | |
| 16 | +@EqualsAndHashCode(callSuper = true) | |
| 17 | +@ToString(callSuper = true) | |
| 18 | +public class ContactPageReqVO extends PageParam { | |
| 19 | + | |
| 20 | + // TODO @zyna:筛选条件 | |
| 21 | + // ●客户: | |
| 22 | + // ●姓名: | |
| 23 | + // ●手机、电话、座机、QQ、微信、邮箱 | |
| 24 | + | |
| 25 | + @Schema(description = "下次联系时间") | |
| 26 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 27 | + private LocalDateTime[] nextTime; | |
| 28 | + | |
| 29 | + @Schema(description = "手机号") | |
| 30 | + private String mobile; | |
| 31 | + | |
| 32 | + @Schema(description = "电话") | |
| 33 | + private String telephone; | |
| 34 | + | |
| 35 | + @Schema(description = "电子邮箱") | |
| 36 | + private String email; | |
| 37 | + | |
| 38 | + @Schema(description = "客户编号", example = "10795") | |
| 39 | + private Long customerId; | |
| 40 | + | |
| 41 | + @Schema(description = "地址") | |
| 42 | + private String address; | |
| 43 | + | |
| 44 | + @Schema(description = "备注", example = "你说的对") | |
| 45 | + private String remark; | |
| 46 | + | |
| 47 | + @Schema(description = "创建时间") | |
| 48 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 49 | + private LocalDateTime[] createTime; | |
| 50 | + | |
| 51 | + @Schema(description = "最后跟进时间") | |
| 52 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 53 | + private LocalDateTime[] lastTime; | |
| 54 | + | |
| 55 | + @Schema(description = "直属上级", example = "23457") | |
| 56 | + private Long parentId; | |
| 57 | + | |
| 58 | + @Schema(description = "姓名", example = "芋艿") | |
| 59 | + private String name; | |
| 60 | + | |
| 61 | + @Schema(description = "职位") | |
| 62 | + private String post; | |
| 63 | + | |
| 64 | + @Schema(description = "QQ") | |
| 65 | + private Long qq; | |
| 66 | + | |
| 67 | + @Schema(description = "微信") | |
| 68 | + private String webchat; | |
| 69 | + | |
| 70 | + @Schema(description = "性别") | |
| 71 | + private Integer sex; | |
| 72 | + | |
| 73 | + @Schema(description = "是否关键决策人") | |
| 74 | + private Boolean policyMakers; | |
| 75 | + | |
| 76 | + @Schema(description = "负责人用户编号", example = "14334") | |
| 77 | + private String ownerUserId; | |
| 78 | + | |
| 79 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.*; | |
| 5 | +import java.time.LocalDateTime; | |
| 6 | + | |
| 7 | +@Schema(description = "管理后台 - CRM 联系人 Response VO") | |
| 8 | +@Data | |
| 9 | +@EqualsAndHashCode(callSuper = true) | |
| 10 | +@ToString(callSuper = true) | |
| 11 | +public class ContactRespVO extends ContactBaseVO { | |
| 12 | + | |
| 13 | + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") | |
| 14 | + private Long id; | |
| 15 | + | |
| 16 | + @Schema(description = "创建时间") | |
| 17 | + private LocalDateTime createTime; | |
| 18 | + | |
| 19 | + // TODO @zyna:example 最好写下; | |
| 20 | + | |
| 21 | + @Schema(description = "创建人") | |
| 22 | + private String creatorName; | |
| 23 | + | |
| 24 | + @Schema(description = "客户名字") | |
| 25 | + private String customerName; | |
| 26 | + | |
| 27 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactSimpleRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.ToString; | |
| 6 | + | |
| 7 | +@Schema(description = "管理后台 - CRM 联系人 Response VO") | |
| 8 | +@Data | |
| 9 | +@ToString(callSuper = true) | |
| 10 | +public class ContactSimpleRespVO { | |
| 11 | + @Schema(description = "姓名", example = "芋艿") // TODO @zyna:requiredMode = Schema.RequiredMode.REQUIRED;需要空一行;字段的顺序改下,id 在 name 前面,会更干净 | |
| 12 | + private String name; | |
| 13 | + | |
| 14 | + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") | |
| 15 | + private Long id; | |
| 16 | + | |
| 17 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactUpdateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +import javax.validation.constraints.NotNull; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - CRM 联系人更新 Request VO") | |
| 11 | +@Data | |
| 12 | +@EqualsAndHashCode(callSuper = true) | |
| 13 | +@ToString(callSuper = true) | |
| 14 | +public class ContactUpdateReqVO extends ContactBaseVO { | |
| 15 | + | |
| 16 | + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") | |
| 17 | + @NotNull(message = "主键不能为空") | |
| 18 | + private Long id; | |
| 19 | + | |
| 20 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.crm.framework.enums.CrmPermissionLevelEnum; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import javax.validation.constraints.NotNull; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - CRM 联系人转移 Request VO") | |
| 10 | +@Data | |
| 11 | +public class CrmContactTransferReqVO { | |
| 12 | + | |
| 13 | + @Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | |
| 14 | + @NotNull(message = "联系人编号不能为空") | |
| 15 | + private Long id; | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 新负责人的用户编号 | |
| 19 | + */ | |
| 20 | + @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | |
| 21 | + @NotNull(message = "新负责人的用户编号不能为空") | |
| 22 | + private Long newOwnerUserId; | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * 老负责人加入团队后的权限级别。如果 null 说明移除 | |
| 26 | + * | |
| 27 | + * 关联 {@link CrmPermissionLevelEnum} | |
| 28 | + */ | |
| 29 | + @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 30 | + private Integer oldOwnerPermissionLevel; | |
| 31 | + | |
| 32 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/ContractController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contract; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 6 | +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | |
| 7 | +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*; | |
| 8 | +import cn.iocoder.yudao.module.crm.convert.contract.ContractConvert; | |
| 9 | +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.ContractDO; | |
| 10 | +import cn.iocoder.yudao.module.crm.service.contract.ContractService; | |
| 11 | +import io.swagger.v3.oas.annotations.Operation; | |
| 12 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 13 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 14 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 15 | +import org.springframework.validation.annotation.Validated; | |
| 16 | +import org.springframework.web.bind.annotation.*; | |
| 17 | + | |
| 18 | +import javax.annotation.Resource; | |
| 19 | +import javax.servlet.http.HttpServletResponse; | |
| 20 | +import javax.validation.Valid; | |
| 21 | +import java.io.IOException; | |
| 22 | +import java.util.List; | |
| 23 | + | |
| 24 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 25 | +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; | |
| 26 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 27 | + | |
| 28 | +@Tag(name = "管理后台 - CRM 合同") | |
| 29 | +@RestController | |
| 30 | +@RequestMapping("/crm/contract") | |
| 31 | +@Validated | |
| 32 | +public class ContractController { | |
| 33 | + | |
| 34 | + @Resource | |
| 35 | + private ContractService contractService; | |
| 36 | + | |
| 37 | + @PostMapping("/create") | |
| 38 | + @Operation(summary = "创建合同") | |
| 39 | + @PreAuthorize("@ss.hasPermission('crm:contract:create')") | |
| 40 | + public CommonResult<Long> createContract(@Valid @RequestBody ContractCreateReqVO createReqVO) { | |
| 41 | + return success(contractService.createContract(createReqVO, getLoginUserId())); | |
| 42 | + } | |
| 43 | + | |
| 44 | + @PutMapping("/update") | |
| 45 | + @Operation(summary = "更新合同") | |
| 46 | + @PreAuthorize("@ss.hasPermission('crm:contract:update')") | |
| 47 | + public CommonResult<Boolean> updateContract(@Valid @RequestBody ContractUpdateReqVO updateReqVO) { | |
| 48 | + contractService.updateContract(updateReqVO); | |
| 49 | + return success(true); | |
| 50 | + } | |
| 51 | + | |
| 52 | + @DeleteMapping("/delete") | |
| 53 | + @Operation(summary = "删除合同") | |
| 54 | + @Parameter(name = "id", description = "编号", required = true) | |
| 55 | + @PreAuthorize("@ss.hasPermission('crm:contract:delete')") | |
| 56 | + public CommonResult<Boolean> deleteContract(@RequestParam("id") Long id) { | |
| 57 | + contractService.deleteContract(id); | |
| 58 | + return success(true); | |
| 59 | + } | |
| 60 | + | |
| 61 | + @GetMapping("/get") | |
| 62 | + @Operation(summary = "获得合同") | |
| 63 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 64 | + @PreAuthorize("@ss.hasPermission('crm:contract:query')") | |
| 65 | + public CommonResult<ContractRespVO> getContract(@RequestParam("id") Long id) { | |
| 66 | + ContractDO contract = contractService.getContract(id); | |
| 67 | + return success(ContractConvert.INSTANCE.convert(contract)); | |
| 68 | + } | |
| 69 | + | |
| 70 | + @GetMapping("/page") | |
| 71 | + @Operation(summary = "获得合同分页") | |
| 72 | + @PreAuthorize("@ss.hasPermission('crm:contract:query')") | |
| 73 | + public CommonResult<PageResult<ContractRespVO>> getContractPage(@Valid ContractPageReqVO pageVO) { | |
| 74 | + PageResult<ContractDO> pageResult = contractService.getContractPage(pageVO); | |
| 75 | + return success(ContractConvert.INSTANCE.convertPage(pageResult)); | |
| 76 | + } | |
| 77 | + | |
| 78 | + @GetMapping("/export-excel") | |
| 79 | + @Operation(summary = "导出合同 Excel") | |
| 80 | + @PreAuthorize("@ss.hasPermission('crm:contract:export')") | |
| 81 | + @OperateLog(type = EXPORT) | |
| 82 | + public void exportContractExcel(@Valid ContractExportReqVO exportReqVO, | |
| 83 | + HttpServletResponse response) throws IOException { | |
| 84 | + List<ContractDO> list = contractService.getContractList(exportReqVO); | |
| 85 | + // 导出 Excel | |
| 86 | + List<ContractExcelVO> datas = ContractConvert.INSTANCE.convertList02(list); | |
| 87 | + ExcelUtils.write(response, "合同.xls", "数据", ContractExcelVO.class, datas); | |
| 88 | + } | |
| 89 | + | |
| 90 | + @PutMapping("/transfer") | |
| 91 | + @Operation(summary = "合同转移") | |
| 92 | + @PreAuthorize("@ss.hasPermission('crm:contract:update')") | |
| 93 | + public CommonResult<Boolean> transfer(@Valid @RequestBody CrmContractTransferReqVO reqVO) { | |
| 94 | + contractService.transferContract(reqVO, getLoginUserId()); | |
| 95 | + return success(true); | |
| 96 | + } | |
| 97 | + | |
| 98 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractBaseVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 6 | + | |
| 7 | +import javax.validation.constraints.NotNull; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | + | |
| 10 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 11 | + | |
| 12 | +// TODO @dhb52:所有类,带下 Crm 前缀,避免和别的模块重复 | |
| 13 | +/** | |
| 14 | + * 合同 Base VO,提供给添加、修改、详细的子 VO 使用 | |
| 15 | + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | |
| 16 | + */ | |
| 17 | +@Data | |
| 18 | +public class ContractBaseVO { | |
| 19 | + | |
| 20 | + // TODO @dhb52:类似 no 字段的 example 要写xia 哈; | |
| 21 | + | |
| 22 | + @Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") | |
| 23 | + @NotNull(message = "合同名称不能为空") | |
| 24 | + private String name; | |
| 25 | + | |
| 26 | + // TODO @dhb52:这个必须传递 | |
| 27 | + @Schema(description = "客户编号", example = "18336") | |
| 28 | + private Long customerId; | |
| 29 | + | |
| 30 | + @Schema(description = "商机编号", example = "10864") | |
| 31 | + private Long businessId; | |
| 32 | + | |
| 33 | + @Schema(description = "工作流编号", example = "1043") | |
| 34 | + private Long processInstanceId; | |
| 35 | + | |
| 36 | + // TODO @dhb52:这个必须传递 | |
| 37 | + @Schema(description = "下单日期") | |
| 38 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 39 | + private LocalDateTime orderDate; | |
| 40 | + | |
| 41 | + // TODO @dhb52:这个必须传递 | |
| 42 | + @Schema(description = "负责人的用户编号", example = "17144") | |
| 43 | + private Long ownerUserId; | |
| 44 | + | |
| 45 | + // TODO @芋艿:未来应该支持自动生成; | |
| 46 | + // TODO @dhb52:这个必须传递; | |
| 47 | + @Schema(description = "合同编号") | |
| 48 | + private String no; | |
| 49 | + | |
| 50 | + @Schema(description = "开始时间") | |
| 51 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 52 | + private LocalDateTime startTime; | |
| 53 | + | |
| 54 | + @Schema(description = "结束时间") | |
| 55 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 56 | + private LocalDateTime endTime; | |
| 57 | + | |
| 58 | + @Schema(description = "合同金额", example = "5617") | |
| 59 | + private Integer price; | |
| 60 | + | |
| 61 | + @Schema(description = "整单折扣") | |
| 62 | + private Integer discountPercent; | |
| 63 | + | |
| 64 | + @Schema(description = "产品总金额", example = "19510") | |
| 65 | + private Integer productPrice; | |
| 66 | + | |
| 67 | + @Schema(description = "联系人编号", example = "18546") | |
| 68 | + private Long contactId; | |
| 69 | + | |
| 70 | + @Schema(description = "公司签约人", example = "14036") | |
| 71 | + private Long signUserId; | |
| 72 | + | |
| 73 | + @Schema(description = "最后跟进时间") | |
| 74 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 75 | + private LocalDateTime contactLastTime; | |
| 76 | + | |
| 77 | + @Schema(description = "备注", example = "你猜") | |
| 78 | + private String remark; | |
| 79 | + | |
| 80 | + // TODO @dhb52:增加一个 status 字段:具体有哪些值,你来枚举下;主要页面上有个【草稿】【提交审核】的流程,可以看看。然后要对接工作流,这块也可以看看,不确定的地方问我。 | |
| 81 | + | |
| 82 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - CRM 合同创建 Request VO") | |
| 9 | +@Data | |
| 10 | +@EqualsAndHashCode(callSuper = true) | |
| 11 | +@ToString(callSuper = true) | |
| 12 | +public class ContractCreateReqVO extends ContractBaseVO { | |
| 13 | + | |
| 14 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; | |
| 2 | + | |
| 3 | +import com.alibaba.excel.annotation.ExcelProperty; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * CRM 合同 Excel VO | |
| 10 | + * | |
| 11 | + * @author dhb52 | |
| 12 | + */ | |
| 13 | +@Data | |
| 14 | +public class ContractExcelVO { | |
| 15 | + | |
| 16 | + @ExcelProperty("合同编号") | |
| 17 | + private Long id; | |
| 18 | + | |
| 19 | + @ExcelProperty("合同名称") | |
| 20 | + private String name; | |
| 21 | + | |
| 22 | + @ExcelProperty("客户编号") | |
| 23 | + private Long customerId; | |
| 24 | + | |
| 25 | + @ExcelProperty("商机编号") | |
| 26 | + private Long businessId; | |
| 27 | + | |
| 28 | + @ExcelProperty("工作流编号") | |
| 29 | + private Long processInstanceId; | |
| 30 | + | |
| 31 | + @ExcelProperty("下单日期") | |
| 32 | + private LocalDateTime orderDate; | |
| 33 | + | |
| 34 | + @ExcelProperty("负责人的用户编号") | |
| 35 | + private Long ownerUserId; | |
| 36 | + | |
| 37 | + @ExcelProperty("合同编号") | |
| 38 | + private String no; | |
| 39 | + | |
| 40 | + @ExcelProperty("开始时间") | |
| 41 | + private LocalDateTime startTime; | |
| 42 | + | |
| 43 | + @ExcelProperty("结束时间") | |
| 44 | + private LocalDateTime endTime; | |
| 45 | + | |
| 46 | + @ExcelProperty("合同金额") | |
| 47 | + private Integer price; | |
| 48 | + | |
| 49 | + @ExcelProperty("整单折扣") | |
| 50 | + private Integer discountPercent; | |
| 51 | + | |
| 52 | + @ExcelProperty("产品总金额") | |
| 53 | + private Integer productPrice; | |
| 54 | + | |
| 55 | + @ExcelProperty("联系人编号") | |
| 56 | + private Long contactId; | |
| 57 | + | |
| 58 | + @ExcelProperty("公司签约人") | |
| 59 | + private Long signUserId; | |
| 60 | + | |
| 61 | + @ExcelProperty("最后跟进时间") | |
| 62 | + private LocalDateTime contactLastTime; | |
| 63 | + | |
| 64 | + @ExcelProperty("备注") | |
| 65 | + private String remark; | |
| 66 | + | |
| 67 | + @ExcelProperty("创建时间") | |
| 68 | + private LocalDateTime createTime; | |
| 69 | + | |
| 70 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractExportReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 6 | + | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | + | |
| 9 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 10 | + | |
| 11 | +@Schema(description = "管理后台 - CRM 合同 Excel 导出 Request VO,参数和 ContractPageReqVO 是一致的") | |
| 12 | +@Data | |
| 13 | +public class ContractExportReqVO { | |
| 14 | + | |
| 15 | + @Schema(description = "合同名称", example = "王五") | |
| 16 | + private String name; | |
| 17 | + | |
| 18 | + @Schema(description = "客户编号", example = "18336") | |
| 19 | + private Long customerId; | |
| 20 | + | |
| 21 | + @Schema(description = "商机编号", example = "10864") | |
| 22 | + private Long businessId; | |
| 23 | + | |
| 24 | + @Schema(description = "下单日期") | |
| 25 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 26 | + private LocalDateTime[] orderDate; | |
| 27 | + | |
| 28 | + @Schema(description = "合同编号") | |
| 29 | + private String no; | |
| 30 | + | |
| 31 | + @Schema(description = "整单折扣") | |
| 32 | + private Integer discountPercent; | |
| 33 | + | |
| 34 | + @Schema(description = "产品总金额", example = "19510") | |
| 35 | + private Integer productPrice; | |
| 36 | + | |
| 37 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | +import lombok.EqualsAndHashCode; | |
| 7 | +import lombok.ToString; | |
| 8 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 9 | + | |
| 10 | +import java.time.LocalDateTime; | |
| 11 | + | |
| 12 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 13 | + | |
| 14 | +@Schema(description = "管理后台 - CRM 合同分页 Request VO") | |
| 15 | +@Data | |
| 16 | +@EqualsAndHashCode(callSuper = true) | |
| 17 | +@ToString(callSuper = true) | |
| 18 | +public class ContractPageReqVO extends PageParam { | |
| 19 | + | |
| 20 | + @Schema(description = "合同名称", example = "王五") | |
| 21 | + private String name; | |
| 22 | + | |
| 23 | + @Schema(description = "客户编号", example = "18336") | |
| 24 | + private Long customerId; | |
| 25 | + | |
| 26 | + @Schema(description = "商机编号", example = "10864") | |
| 27 | + private Long businessId; | |
| 28 | + | |
| 29 | + @Schema(description = "下单日期") | |
| 30 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 31 | + private LocalDateTime[] orderDate; | |
| 32 | + | |
| 33 | + @Schema(description = "合同编号") | |
| 34 | + private String no; | |
| 35 | + | |
| 36 | + @Schema(description = "整单折扣") | |
| 37 | + private Integer discountPercent; | |
| 38 | + | |
| 39 | + @Schema(description = "产品总金额", example = "19510") | |
| 40 | + private Integer productPrice; | |
| 41 | + | |
| 42 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - CRM 合同 Response VO") | |
| 11 | +@Data | |
| 12 | +@EqualsAndHashCode(callSuper = true) | |
| 13 | +@ToString(callSuper = true) | |
| 14 | +public class ContractRespVO extends ContractBaseVO { | |
| 15 | + | |
| 16 | + @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | |
| 17 | + private Long id; | |
| 18 | + | |
| 19 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 20 | + private LocalDateTime createTime; | |
| 21 | + | |
| 22 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractUpdateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +import javax.validation.constraints.NotNull; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - CRM 合同更新 Request VO") | |
| 11 | +@Data | |
| 12 | +@EqualsAndHashCode(callSuper = true) | |
| 13 | +@ToString(callSuper = true) | |
| 14 | +public class ContractUpdateReqVO extends ContractBaseVO { | |
| 15 | + | |
| 16 | + @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | |
| 17 | + @NotNull(message = "合同编号不能为空") | |
| 18 | + private Long id; | |
| 19 | + | |
| 20 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractTransferReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.crm.framework.enums.CrmPermissionLevelEnum; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import javax.validation.constraints.NotNull; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - CRM 合同转移 Request VO") | |
| 10 | +@Data | |
| 11 | +public class CrmContractTransferReqVO { | |
| 12 | + | |
| 13 | + @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | |
| 14 | + @NotNull(message = "联系人编号不能为空") | |
| 15 | + private Long id; | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 新负责人的用户编号 | |
| 19 | + */ | |
| 20 | + @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") | |
| 21 | + @NotNull(message = "新负责人的用户编号不能为空") | |
| 22 | + private Long newOwnerUserId; | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * 老负责人加入团队后的权限级别。如果 null 说明移除 | |
| 26 | + * | |
| 27 | + * 关联 {@link CrmPermissionLevelEnum} | |
| 28 | + */ | |
| 29 | + @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 30 | + private Integer oldOwnerPermissionLevel; | |
| 31 | + | |
| 32 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.customer; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; | |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 7 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 8 | +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | |
| 9 | +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*; | |
| 10 | +import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; | |
| 11 | +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; | |
| 12 | +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; | |
| 13 | +import cn.iocoder.yudao.module.crm.framework.enums.CrmBizTypeEnum; | |
| 14 | +import cn.iocoder.yudao.module.crm.framework.enums.CrmPermissionLevelEnum; | |
| 15 | +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; | |
| 16 | +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; | |
| 17 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 18 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 19 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 20 | +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | |
| 21 | +import io.swagger.v3.oas.annotations.Operation; | |
| 22 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 23 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 24 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 25 | +import org.springframework.util.CollectionUtils; | |
| 26 | +import org.springframework.validation.annotation.Validated; | |
| 27 | +import org.springframework.web.bind.annotation.*; | |
| 28 | + | |
| 29 | +import javax.annotation.Resource; | |
| 30 | +import javax.servlet.http.HttpServletResponse; | |
| 31 | +import javax.validation.Valid; | |
| 32 | +import java.io.IOException; | |
| 33 | +import java.util.Collections; | |
| 34 | +import java.util.List; | |
| 35 | +import java.util.Map; | |
| 36 | +import java.util.Set; | |
| 37 | + | |
| 38 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; | |
| 39 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 40 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | |
| 41 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 42 | +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; | |
| 43 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 44 | + | |
| 45 | +@Tag(name = "管理后台 - CRM 客户") | |
| 46 | +@RestController | |
| 47 | +@RequestMapping("/crm/customer") | |
| 48 | +@Validated | |
| 49 | +public class CrmCustomerController { | |
| 50 | + | |
| 51 | + @Resource | |
| 52 | + private CrmCustomerService customerService; | |
| 53 | + | |
| 54 | + @Resource | |
| 55 | + private DeptApi deptApi; | |
| 56 | + @Resource | |
| 57 | + private AdminUserApi adminUserApi; | |
| 58 | + @Resource | |
| 59 | + private CrmPermissionService permissionService; | |
| 60 | + | |
| 61 | + @PostMapping("/create") | |
| 62 | + @Operation(summary = "创建客户") | |
| 63 | + @PreAuthorize("@ss.hasPermission('crm:customer:create')") | |
| 64 | + public CommonResult<Long> createCustomer(@Valid @RequestBody CrmCustomerCreateReqVO createReqVO) { | |
| 65 | + return success(customerService.createCustomer(createReqVO, getLoginUserId())); | |
| 66 | + } | |
| 67 | + | |
| 68 | + @PutMapping("/update") | |
| 69 | + @Operation(summary = "更新客户") | |
| 70 | + @PreAuthorize("@ss.hasPermission('crm:customer:update')") | |
| 71 | + public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) { | |
| 72 | + customerService.updateCustomer(updateReqVO); | |
| 73 | + return success(true); | |
| 74 | + } | |
| 75 | + | |
| 76 | + @DeleteMapping("/delete") | |
| 77 | + @Operation(summary = "删除客户") | |
| 78 | + @Parameter(name = "id", description = "编号", required = true) | |
| 79 | + @PreAuthorize("@ss.hasPermission('crm:customer:delete')") | |
| 80 | + public CommonResult<Boolean> deleteCustomer(@RequestParam("id") Long id) { | |
| 81 | + customerService.deleteCustomer(id); | |
| 82 | + return success(true); | |
| 83 | + } | |
| 84 | + | |
| 85 | + @GetMapping("/get") | |
| 86 | + @Operation(summary = "获得客户") | |
| 87 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 88 | + @PreAuthorize("@ss.hasPermission('crm:customer:query')") | |
| 89 | + public CommonResult<CrmCustomerRespVO> getCustomer(@RequestParam("id") Long id) { | |
| 90 | + // 1. 获取客户 | |
| 91 | + CrmCustomerDO customer = customerService.getCustomer(id); | |
| 92 | + if (customer == null) { | |
| 93 | + return success(null); | |
| 94 | + } | |
| 95 | + | |
| 96 | + // 2. 拼接数据 | |
| 97 | + // 2.1 获取负责人 | |
| 98 | + List<CrmPermissionDO> ownerList = permissionService.getPermissionByBizTypeAndBizIdsAndLevel( | |
| 99 | + CrmBizTypeEnum.CRM_CUSTOMER.getType(), Collections.singletonList(customer.getId()), | |
| 100 | + CrmPermissionLevelEnum.OWNER.getLevel()); | |
| 101 | + Map<Long, CrmPermissionDO> ownerMap = convertMap(ownerList, CrmPermissionDO::getBizId); | |
| 102 | + // 2.2 获取负责人详情 | |
| 103 | + Set<Long> userIds = convertSet(ownerList, CrmPermissionDO::getUserId); | |
| 104 | + userIds.add(Long.parseLong(customer.getCreator())); // 加入创建者 | |
| 105 | + List<AdminUserRespDTO> userList = adminUserApi.getUserList(userIds); | |
| 106 | + Map<Long, AdminUserRespDTO> userMap = convertMap(userList, AdminUserRespDTO::getId); | |
| 107 | + // 2.3 获取部门详情 | |
| 108 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userList, AdminUserRespDTO::getDeptId)); | |
| 109 | + return success(CrmCustomerConvert.INSTANCE.convert(customer, ownerMap, userMap, deptMap)); | |
| 110 | + } | |
| 111 | + | |
| 112 | + // TODO @puhui999:可以在 CrmCustomerPageReqVO 里面加个 pool 参数,为 true 时,代表来自公海客户的分页 | |
| 113 | + @GetMapping("/page") | |
| 114 | + @Operation(summary = "获得客户分页") | |
| 115 | + @PreAuthorize("@ss.hasPermission('crm:customer:query')") | |
| 116 | + public CommonResult<PageResult<CrmCustomerRespVO>> getCustomerPage(@Valid CrmCustomerPageReqVO pageVO) { | |
| 117 | + PageResult<CrmCustomerDO> pageResult = customerService.getCustomerPage(pageVO, getLoginUserId()); | |
| 118 | + if (CollUtil.isEmpty(pageResult.getList())) { | |
| 119 | + return success(PageResult.empty(pageResult.getTotal())); | |
| 120 | + } | |
| 121 | + // 拼接数据 | |
| 122 | + // TODO @puhui999:这块的拼接逻辑,可以和 convertPage 合并下; | |
| 123 | +// Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( | |
| 124 | +// convertSetByFlatMap(pageResult.getList(), user -> Stream.of(NumberUtil.parseLong(user.getCreator()), user.getOwnerUserId()))); | |
| 125 | +// Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap( | |
| 126 | +// convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); | |
| 127 | + return convertPage(customerService.getCustomerPage(pageVO, getLoginUserId())); | |
| 128 | + } | |
| 129 | + | |
| 130 | + // TODO @puhui999: | |
| 131 | + @GetMapping("/pool-page") | |
| 132 | + @Operation(summary = "获得公海客户分页") | |
| 133 | + @PreAuthorize("@ss.hasPermission('crm:customer:query')") | |
| 134 | + public CommonResult<PageResult<CrmCustomerRespVO>> getPoolCustomerPage(@Valid CrmCustomerPageReqVO pageVO) { | |
| 135 | + return convertPage(customerService.getCustomerPage(pageVO, CrmPermissionDO.POOL_USER_ID)); | |
| 136 | + } | |
| 137 | + | |
| 138 | + private CommonResult<PageResult<CrmCustomerRespVO>> convertPage(PageResult<CrmCustomerDO> pageResult) { | |
| 139 | + // 2. 拼接数据 | |
| 140 | + Set<Long> ids = convertSet(pageResult.getList(), CrmCustomerDO::getId); | |
| 141 | + // 2.1 获取负责人 | |
| 142 | + List<CrmPermissionDO> ownerList = permissionService.getPermissionByBizTypeAndBizIdsAndLevel( | |
| 143 | + CrmBizTypeEnum.CRM_CUSTOMER.getType(), ids, CrmPermissionLevelEnum.OWNER.getLevel()); | |
| 144 | + Map<Long, CrmPermissionDO> ownerMap = convertMap(ownerList, CrmPermissionDO::getBizId); | |
| 145 | + // 2.2 获取负责人详情 | |
| 146 | + Set<Long> userIds = convertSet(ownerList, CrmPermissionDO::getUserId); | |
| 147 | + userIds.addAll(convertSet(pageResult.getList(), item -> Long.parseLong(item.getCreator()))); // 加入创建者 | |
| 148 | + List<AdminUserRespDTO> userList = adminUserApi.getUserList(userIds); | |
| 149 | + Map<Long, AdminUserRespDTO> userMap = convertMap(userList, AdminUserRespDTO::getId); | |
| 150 | + // 2.3 获取部门详情 | |
| 151 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userList, AdminUserRespDTO::getDeptId)); | |
| 152 | + return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, ownerMap, userMap, deptMap)); | |
| 153 | + } | |
| 154 | + | |
| 155 | + @GetMapping("/export-excel") | |
| 156 | + @Operation(summary = "导出客户 Excel") | |
| 157 | + @PreAuthorize("@ss.hasPermission('crm:customer:export')") | |
| 158 | + @OperateLog(type = EXPORT) | |
| 159 | + public void exportCustomerExcel(@Valid CrmCustomerExportReqVO exportReqVO, | |
| 160 | + HttpServletResponse response) throws IOException { | |
| 161 | + List<CrmCustomerDO> list = customerService.getCustomerList(exportReqVO); | |
| 162 | + // 导出 Excel | |
| 163 | + List<CrmCustomerExcelVO> datas = CrmCustomerConvert.INSTANCE.convertList02(list); | |
| 164 | + ExcelUtils.write(response, "客户.xls", "数据", CrmCustomerExcelVO.class, datas); | |
| 165 | + } | |
| 166 | + | |
| 167 | + @PutMapping("/transfer") | |
| 168 | + @Operation(summary = "客户转移") | |
| 169 | + @PreAuthorize("@ss.hasPermission('crm:customer:update')") | |
| 170 | + public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) { | |
| 171 | + customerService.transferCustomer(reqVO, getLoginUserId()); | |
| 172 | + return success(true); | |
| 173 | + } | |
| 174 | + | |
| 175 | + // TODO @Joey:单独建一个属于自己业务的 ReqVO;因为前端如果模拟请求,是不是可以更新其它字段了; | |
| 176 | + @PutMapping("/lock") | |
| 177 | + @Operation(summary = "锁定/解锁客户") | |
| 178 | + @PreAuthorize("@ss.hasPermission('crm:customer:update')") | |
| 179 | + public CommonResult<Boolean> lockCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) { | |
| 180 | + customerService.lockCustomer(updateReqVO); | |
| 181 | + return success(true); | |
| 182 | + } | |
| 183 | + | |
| 184 | + @PutMapping("/receive") | |
| 185 | + @Operation(summary = "领取公海客户") | |
| 186 | + // TODO @xiaqing:1)receiveCustomer 方法名字;2)cIds 改成 ids,要加下 @RequestParam,还有 swagger 注解;3)参数非空,使用 validator 校验;4)返回 true 即可; | |
| 187 | + @PreAuthorize("@ss.hasPermission('crm:customer:receive')") | |
| 188 | + public CommonResult<String> receiveByIds(List<Long> cIds){ | |
| 189 | + // 判断是否为空 | |
| 190 | + if(CollectionUtils.isEmpty(cIds)) | |
| 191 | + return error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),GlobalErrorCodeConstants.BAD_REQUEST.getMsg()); | |
| 192 | + // 领取公海任务 | |
| 193 | + // TODO @xiaqing:userid,通过 controller 传递给 service,不要在 service 里面获取,无状态 | |
| 194 | + customerService.receive(cIds); | |
| 195 | + return success("领取成功"); | |
| 196 | + } | |
| 197 | + | |
| 198 | + // TODO @xiaqing:1)distributeCustomer 方法名;2)cIds 同上;3)参数校验,同上;4)ownerId 改成 ownerUserId,和别的模块统一;5)返回 true 即可; | |
| 199 | + @PutMapping("/distributeByIds") | |
| 200 | + @Operation(summary = "分配公海给对应负责人") | |
| 201 | + @PreAuthorize("@ss.hasPermission('crm:customer:distributeByIds')") | |
| 202 | + public CommonResult<String> distributeByIds(Long ownerId,List<Long>cIds){ | |
| 203 | + //判断参数不能为空 | |
| 204 | + if(ownerId==null || CollectionUtils.isEmpty(cIds)) | |
| 205 | + return error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),GlobalErrorCodeConstants.BAD_REQUEST.getMsg()); | |
| 206 | + customerService.distributeByIds(cIds,ownerId); | |
| 207 | + return success("分配成功"); | |
| 208 | + } | |
| 209 | + | |
| 210 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerLimitConfigController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.customer; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 6 | +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLimitConfigCreateReqVO; | |
| 7 | +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLimitConfigPageReqVO; | |
| 8 | +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLimitConfigRespVO; | |
| 9 | +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLimitConfigUpdateReqVO; | |
| 10 | +import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerLimitConfigConvert; | |
| 11 | +import cn.iocoder.yudao.module.crm.dal.dataobject.customerlimitconfig.CrmCustomerLimitConfigDO; | |
| 12 | +import cn.iocoder.yudao.module.crm.service.customerlimitconfig.CrmCustomerLimitConfigService; | |
| 13 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 14 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 15 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 16 | +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | |
| 17 | +import io.swagger.v3.oas.annotations.Operation; | |
| 18 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 19 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 20 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 21 | +import org.springframework.validation.annotation.Validated; | |
| 22 | +import org.springframework.web.bind.annotation.*; | |
| 23 | + | |
| 24 | +import javax.annotation.Resource; | |
| 25 | +import javax.validation.Valid; | |
| 26 | +import java.util.Collection; | |
| 27 | +import java.util.Map; | |
| 28 | + | |
| 29 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 30 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; | |
| 31 | + | |
| 32 | +@Tag(name = "管理后台 - 客户限制配置") | |
| 33 | +@RestController | |
| 34 | +@RequestMapping("/crm/customer-limit-config") | |
| 35 | +@Validated | |
| 36 | +public class CrmCustomerLimitConfigController { | |
| 37 | + | |
| 38 | + @Resource | |
| 39 | + private CrmCustomerLimitConfigService customerLimitConfigService; | |
| 40 | + | |
| 41 | + @Resource | |
| 42 | + private DeptApi deptApi; | |
| 43 | + @Resource | |
| 44 | + private AdminUserApi adminUserApi; | |
| 45 | + | |
| 46 | + @PostMapping("/create") | |
| 47 | + @Operation(summary = "创建客户限制配置") | |
| 48 | + @PreAuthorize("@ss.hasPermission('crm:customer-limit-config:create')") | |
| 49 | + public CommonResult<Long> createCustomerLimitConfig(@Valid @RequestBody CrmCustomerLimitConfigCreateReqVO createReqVO) { | |
| 50 | + return success(customerLimitConfigService.createCustomerLimitConfig(createReqVO)); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @PutMapping("/update") | |
| 54 | + @Operation(summary = "更新客户限制配置") | |
| 55 | + @PreAuthorize("@ss.hasPermission('crm:customer-limit-config:update')") | |
| 56 | + public CommonResult<Boolean> updateCustomerLimitConfig(@Valid @RequestBody CrmCustomerLimitConfigUpdateReqVO updateReqVO) { | |
| 57 | + customerLimitConfigService.updateCustomerLimitConfig(updateReqVO); | |
| 58 | + return success(true); | |
| 59 | + } | |
| 60 | + | |
| 61 | + @DeleteMapping("/delete") | |
| 62 | + @Operation(summary = "删除客户限制配置") | |
| 63 | + @Parameter(name = "id", description = "编号", required = true) | |
| 64 | + @PreAuthorize("@ss.hasPermission('crm:customer-limit-config:delete')") | |
| 65 | + public CommonResult<Boolean> deleteCustomerLimitConfig(@RequestParam("id") Long id) { | |
| 66 | + customerLimitConfigService.deleteCustomerLimitConfig(id); | |
| 67 | + return success(true); | |
| 68 | + } | |
| 69 | + | |
| 70 | + @GetMapping("/get") | |
| 71 | + @Operation(summary = "获得客户限制配置") | |
| 72 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 73 | + @PreAuthorize("@ss.hasPermission('crm:customer-limit-config:query')") | |
| 74 | + public CommonResult<CrmCustomerLimitConfigRespVO> getCustomerLimitConfig(@RequestParam("id") Long id) { | |
| 75 | + CrmCustomerLimitConfigDO customerLimitConfig = customerLimitConfigService.getCustomerLimitConfig(id); | |
| 76 | + // 拼接数据 | |
| 77 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(customerLimitConfig.getUserIds()); | |
| 78 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(customerLimitConfig.getDeptIds()); | |
| 79 | + return success(CrmCustomerLimitConfigConvert.INSTANCE.convert(customerLimitConfig, userMap, deptMap)); | |
| 80 | + } | |
| 81 | + | |
| 82 | + @GetMapping("/page") | |
| 83 | + @Operation(summary = "获得客户限制配置分页") | |
| 84 | + @PreAuthorize("@ss.hasPermission('crm:customer-limit-config:query')") | |
| 85 | + public CommonResult<PageResult<CrmCustomerLimitConfigRespVO>> getCustomerLimitConfigPage(@Valid CrmCustomerLimitConfigPageReqVO pageVO) { | |
| 86 | + PageResult<CrmCustomerLimitConfigDO> pageResult = customerLimitConfigService.getCustomerLimitConfigPage(pageVO); | |
| 87 | + if (CollUtil.isEmpty(pageResult.getList())) { | |
| 88 | + return success(PageResult.empty(pageResult.getTotal())); | |
| 89 | + } | |
| 90 | + // 拼接数据 | |
| 91 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( | |
| 92 | + convertSetByFlatMap(pageResult.getList(), CrmCustomerLimitConfigDO::getUserIds, Collection::stream)); | |
| 93 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap( | |
| 94 | + convertSetByFlatMap(pageResult.getList(), CrmCustomerLimitConfigDO::getDeptIds, Collection::stream)); | |
| 95 | + return success(CrmCustomerLimitConfigConvert.INSTANCE.convertPage(pageResult, userMap, deptMap)); | |
| 96 | + } | |
| 97 | + | |
| 98 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerPoolConfigController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.customer; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 4 | +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPoolConfigRespVO; | |
| 5 | +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPoolConfigUpdateReqVO; | |
| 6 | +import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; | |
| 7 | +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; | |
| 8 | +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService; | |
| 9 | +import io.swagger.v3.oas.annotations.Operation; | |
| 10 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 11 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 12 | +import org.springframework.validation.annotation.Validated; | |
| 13 | +import org.springframework.web.bind.annotation.*; | |
| 14 | + | |
| 15 | +import javax.annotation.Resource; | |
| 16 | +import javax.validation.Valid; | |
| 17 | + | |
| 18 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 19 | + | |
| 20 | +@Tag(name = "管理后台 - CRM 客户公海配置") | |
| 21 | +@RestController | |
| 22 | +@RequestMapping("/crm/customer-pool-config") | |
| 23 | +@Validated | |
| 24 | +public class CrmCustomerPoolConfigController { | |
| 25 | + | |
| 26 | + @Resource | |
| 27 | + private CrmCustomerPoolConfigService customerPoolConfigService; | |
| 28 | + | |
| 29 | + @GetMapping("/get") | |
| 30 | + @Operation(summary = "获取客户公海规则设置") | |
| 31 | + @PreAuthorize("@ss.hasPermission('crm:customer-pool-config:query')") | |
| 32 | + public CommonResult<CrmCustomerPoolConfigRespVO> getCustomerPoolConfig() { | |
| 33 | + CrmCustomerPoolConfigDO customerPoolConfig = customerPoolConfigService.getCustomerPoolConfig(); | |
| 34 | + return success(CrmCustomerConvert.INSTANCE.convert(customerPoolConfig)); | |
| 35 | + } | |
| 36 | + | |
| 37 | + // TODO @wanwan:这个请求,搞成 save 哈; | |
| 38 | + @PutMapping("/update") | |
| 39 | + @Operation(summary = "更新客户公海规则设置") | |
| 40 | + @PreAuthorize("@ss.hasPermission('crm:customer-pool-config:update')") | |
| 41 | + public CommonResult<Boolean> updateCustomerPoolConfig(@Valid @RequestBody CrmCustomerPoolConfigUpdateReqVO updateReqVO) { | |
| 42 | + customerPoolConfigService.updateCustomerPoolConfig(updateReqVO); | |
| 43 | + return success(true); | |
| 44 | + } | |
| 45 | + | |
| 46 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerBaseVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 4 | +import cn.iocoder.yudao.framework.common.validation.Mobile; | |
| 5 | +import cn.iocoder.yudao.framework.common.validation.Telephone; | |
| 6 | +import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum; | |
| 7 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 8 | +import lombok.Data; | |
| 9 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 10 | + | |
| 11 | +import javax.validation.constraints.Email; | |
| 12 | +import javax.validation.constraints.NotEmpty; | |
| 13 | +import javax.validation.constraints.Size; | |
| 14 | +import java.time.LocalDateTime; | |
| 15 | + | |
| 16 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 17 | + | |
| 18 | +/** | |
| 19 | + * 客户 Base VO,提供给添加、修改、详细的子 VO 使用 | |
| 20 | + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | |
| 21 | + */ | |
| 22 | +@Data | |
| 23 | +public class CrmCustomerBaseVO { | |
| 24 | + | |
| 25 | + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") | |
| 26 | + @NotEmpty(message = "客户名称不能为空") | |
| 27 | + private String name; | |
| 28 | + | |
| 29 | + @Schema(description = "所属行业", example = "1") | |
| 30 | + private Integer industryId; | |
| 31 | + | |
| 32 | + @Schema(description = "客户等级", example = "2") | |
| 33 | + @InEnum(CrmCustomerLevelEnum.class) | |
| 34 | + private Integer level; | |
| 35 | + | |
| 36 | + @Schema(description = "客户来源", example = "3") | |
| 37 | + private Integer source; | |
| 38 | + | |
| 39 | + @Schema(description = "手机", example = "18000000000") | |
| 40 | + @Mobile | |
| 41 | + private String mobile; | |
| 42 | + | |
| 43 | + @Schema(description = "电话", example = "18000000000") | |
| 44 | + @Telephone | |
| 45 | + private String telephone; | |
| 46 | + | |
| 47 | + @Schema(description = "网址", example = "https://www.baidu.com") | |
| 48 | + private String website; | |
| 49 | + | |
| 50 | + @Schema(description = "QQ", example = "123456789") | |
| 51 | + @Size(max = 20, message = "QQ长度不能超过 20 个字符") | |
| 52 | + private String qq; | |
| 53 | + | |
| 54 | + @Schema(description = "wechat", example = "123456789") | |
| 55 | + @Size(max = 255, message = "微信长度不能超过 255 个字符") | |
| 56 | + private String wechat; | |
| 57 | + | |
| 58 | + @Schema(description = "email", example = "123456789@qq.com") | |
| 59 | + @Email(message = "邮箱格式不正确") | |
| 60 | + @Size(max = 255, message = "邮箱长度不能超过 255 个字符") | |
| 61 | + private String email; | |
| 62 | + | |
| 63 | + @Schema(description = "客户描述", example = "任意文字") | |
| 64 | + @Size(max = 4096, message = "客户描述长度不能超过 4096 个字符") | |
| 65 | + private String description; | |
| 66 | + | |
| 67 | + @Schema(description = "备注", example = "随便") | |
| 68 | + private String remark; | |
| 69 | + | |
| 70 | + @Schema(description = "地区编号", example = "20158") | |
| 71 | + private Integer areaId; | |
| 72 | + | |
| 73 | + @Schema(description = "详细地址", example = "北京市海淀区") | |
| 74 | + private String detailAddress; | |
| 75 | + | |
| 76 | + @Schema(description = "下次联系时间") | |
| 77 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 78 | + private LocalDateTime contactNextTime; | |
| 79 | + | |
| 80 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +import javax.validation.constraints.NotNull; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - CRM 客户创建 Request VO") | |
| 11 | +@Data | |
| 12 | +@EqualsAndHashCode(callSuper = true) | |
| 13 | +@ToString(callSuper = true) | |
| 14 | +public class CrmCustomerCreateReqVO extends CrmCustomerBaseVO { | |
| 15 | + | |
| 16 | + @Schema(description = "负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") | |
| 17 | + @NotNull(message = "负责人不能为空") | |
| 18 | + private Long ownerUserId; | |
| 19 | + | |
| 20 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExcelVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | |
| 4 | +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | |
| 5 | +import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; | |
| 6 | +import com.alibaba.excel.annotation.ExcelProperty; | |
| 7 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 8 | +import lombok.Data; | |
| 9 | + | |
| 10 | +import java.time.LocalDateTime; | |
| 11 | + | |
| 12 | +// TODO 芋艿:导出最后做,等基本确认的差不多之后; | |
| 13 | +/** | |
| 14 | + * CRM 客户 Excel VO | |
| 15 | + * | |
| 16 | + * @author Wanwan | |
| 17 | + */ | |
| 18 | +@Data | |
| 19 | +public class CrmCustomerExcelVO { | |
| 20 | + | |
| 21 | + @ExcelProperty("编号") | |
| 22 | + private Long id; | |
| 23 | + | |
| 24 | + @ExcelProperty("客户名称") | |
| 25 | + private String name; | |
| 26 | + | |
| 27 | + @ExcelProperty(value = "跟进状态", converter = DictConvert.class) | |
| 28 | + @DictFormat(DictTypeConstants.BOOLEAN_STRING) | |
| 29 | + private Boolean followUpStatus; | |
| 30 | + | |
| 31 | + @ExcelProperty(value = "锁定状态", converter = DictConvert.class) | |
| 32 | + @DictFormat(DictTypeConstants.BOOLEAN_STRING) | |
| 33 | + private Boolean lockStatus; | |
| 34 | + | |
| 35 | + @ExcelProperty(value = "成交状态", converter = DictConvert.class) | |
| 36 | + @DictFormat(DictTypeConstants.BOOLEAN_STRING) | |
| 37 | + private Boolean dealStatus; | |
| 38 | + | |
| 39 | + @ExcelProperty(value = "所属行业", converter = DictConvert.class) | |
| 40 | + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY) | |
| 41 | + private Integer industryId; | |
| 42 | + | |
| 43 | + @ExcelProperty(value = "客户等级", converter = DictConvert.class) | |
| 44 | + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL) | |
| 45 | + private Integer level; | |
| 46 | + | |
| 47 | + @ExcelProperty(value = "客户来源", converter = DictConvert.class) | |
| 48 | + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE) | |
| 49 | + private Integer source; | |
| 50 | + | |
| 51 | + @ExcelProperty("手机") | |
| 52 | + private String mobile; | |
| 53 | + | |
| 54 | + @ExcelProperty("电话") | |
| 55 | + private String telephone; | |
| 56 | + | |
| 57 | + @ExcelProperty("网址") | |
| 58 | + private String website; | |
| 59 | + | |
| 60 | + @ExcelProperty("QQ") | |
| 61 | + private String qq; | |
| 62 | + | |
| 63 | + @ExcelProperty("wechat") | |
| 64 | + private String wechat; | |
| 65 | + | |
| 66 | + @ExcelProperty("email") | |
| 67 | + private String email; | |
| 68 | + | |
| 69 | + @ExcelProperty("客户描述") | |
| 70 | + private String description; | |
| 71 | + | |
| 72 | + @ExcelProperty("备注") | |
| 73 | + private String remark; | |
| 74 | + | |
| 75 | + @ExcelProperty("负责人的用户编号") | |
| 76 | + private Long ownerUserId; | |
| 77 | + | |
| 78 | + @ExcelProperty("地区编号") | |
| 79 | + private Integer areaId; | |
| 80 | + | |
| 81 | + @ExcelProperty("详细地址") | |
| 82 | + private String detailAddress; | |
| 83 | + | |
| 84 | + @ExcelProperty("最后跟进时间") | |
| 85 | + private LocalDateTime contactLastTime; | |
| 86 | + | |
| 87 | + @ExcelProperty("下次联系时间") | |
| 88 | + private LocalDateTime contactNextTime; | |
| 89 | + | |
| 90 | + @ExcelProperty("创建时间") | |
| 91 | + private LocalDateTime createTime; | |
| 92 | + | |
| 93 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExportReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +// TODO 芋艿:导出最后做,等基本确认的差不多之后; | |
| 7 | +@Schema(description = "管理后台 - CRM 客户 Excel 导出 Request VO,参数和 CrmCustomerPageReqVO 是一致的") | |
| 8 | +@Data | |
| 9 | +public class CrmCustomerExportReqVO { | |
| 10 | + | |
| 11 | + @Schema(description = "客户名称", example = "赵六") | |
| 12 | + private String name; | |
| 13 | + | |
| 14 | + @Schema(description = "手机", example = "18000000000") | |
| 15 | + private String mobile; | |
| 16 | + | |
| 17 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerLimitConfigBaseVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import javax.validation.constraints.NotNull; | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 9 | +// TODO @wanwan:vo 下,可以新建一个 limitconfig,放它的 vo; | |
| 10 | +/** | |
| 11 | + * 客户限制配置 Base VO,提供给添加、修改、详细的子 VO 使用 | |
| 12 | + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | |
| 13 | + */ | |
| 14 | +@Data | |
| 15 | +public class CrmCustomerLimitConfigBaseVO { | |
| 16 | + | |
| 17 | + @Schema(description = "规则类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 18 | + @NotNull(message = "规则类型不能为空") | |
| 19 | + private Integer type; | |
| 20 | + | |
| 21 | + @Schema(description = "规则适用人群") | |
| 22 | + private List<Long> userIds; | |
| 23 | + | |
| 24 | + @Schema(description = "规则适用部门") | |
| 25 | + private List<Long> deptIds; | |
| 26 | + | |
| 27 | + @Schema(description = "数量上限", requiredMode = Schema.RequiredMode.REQUIRED, example = "28384") | |
| 28 | + @NotNull(message = "数量上限不能为空") | |
| 29 | + private Integer maxCount; | |
| 30 | + | |
| 31 | + @Schema(description = "成交客户是否占有拥有客户数(当 type = 1 时)") | |
| 32 | + private Boolean dealCountEnabled; | |
| 33 | + | |
| 34 | +} | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerLimitConfigCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import lombok.EqualsAndHashCode; | |
| 6 | +import lombok.ToString; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - 客户限制配置创建 Request VO") | |
| 9 | +@Data | |
| 10 | +@EqualsAndHashCode(callSuper = true) | |
| 11 | +@ToString(callSuper = true) | |
| 12 | +public class CrmCustomerLimitConfigCreateReqVO extends CrmCustomerLimitConfigBaseVO { | |
| 13 | + | |
| 14 | +} | ... | ... |