mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 07:11:52 +08:00
Merge remote-tracking branch 'yudao/feature/mall_product' into feature/mall_product
This commit is contained in:
commit
f462ac7f96
2
sql/mysql/mall.sql
Normal file
2
sql/mysql/mall.sql
Normal file
@ -0,0 +1,2 @@
|
||||
INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name)
|
||||
VALUES ('核销订单', '', 2, 2, 2166, 'pick-up-order', 'ep:list', 'mall/trade/delivery/pickUpOrder/index', 'PickUpOrder');
|
@ -77,7 +77,7 @@ ALTER TABLE `pay_channel`
|
||||
MODIFY COLUMN `config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '支付渠道配置' AFTER `app_id`;
|
||||
|
||||
-- ----------------------------
|
||||
-- 套餐充值表
|
||||
-- 充值套餐表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `pay_wallet_recharge_package`;
|
||||
CREATE TABLE `pay_wallet_recharge_package`
|
||||
@ -94,7 +94,7 @@ CREATE TABLE `pay_wallet_recharge_package`
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB COMMENT='套餐充值表';
|
||||
) ENGINE=InnoDB COMMENT='充值套餐表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for pay_wallet_recharge
|
||||
|
@ -11,7 +11,7 @@
|
||||
Target Server Version : 80034
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 17/10/2023 23:22:59
|
||||
Date: 18/10/2023 23:33:15
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
@ -384,7 +384,7 @@ CREATE TABLE `infra_api_error_log` (
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1735 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1739 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of infra_api_error_log
|
||||
@ -422,7 +422,7 @@ CREATE TABLE `infra_codegen_column` (
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1804 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1805 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of infra_codegen_column
|
||||
@ -455,7 +455,7 @@ CREATE TABLE `infra_codegen_table` (
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 136 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 137 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of infra_codegen_table
|
||||
@ -615,7 +615,7 @@ CREATE TABLE `infra_job` (
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 27 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 28 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of infra_job
|
||||
@ -656,7 +656,7 @@ CREATE TABLE `infra_job_log` (
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 232 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务日志表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 233 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of infra_job_log
|
||||
@ -709,7 +709,7 @@ CREATE TABLE `member_address` (
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_userId`(`user_id` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 24 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '用户收件地址';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '用户收件地址';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of member_address
|
||||
@ -768,7 +768,7 @@ CREATE TABLE `member_experience_record` (
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_user_id`(`user_id` ASC) USING BTREE COMMENT '会员经验记录-用户编号',
|
||||
INDEX `idx_user_biz_type`(`user_id` ASC, `biz_type` ASC) USING BTREE COMMENT '会员经验记录-用户业务类型'
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 40 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员经验记录';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 41 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员经验记录';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of member_experience_record
|
||||
@ -883,7 +883,7 @@ CREATE TABLE `member_level_record` (
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_user_id`(`user_id` ASC) USING BTREE COMMENT '会员等级记录-用户编号'
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 19 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员等级记录';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员等级记录';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of member_level_record
|
||||
@ -913,7 +913,7 @@ CREATE TABLE `member_point_record` (
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `index_userId`(`user_id` ASC) USING BTREE,
|
||||
INDEX `index_title`(`title` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 59 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户积分记录';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 60 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户积分记录';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of member_point_record
|
||||
@ -1540,7 +1540,7 @@ CREATE TABLE `system_error_code` (
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 5932 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '错误码表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 5933 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '错误码表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_error_code
|
||||
@ -1569,7 +1569,7 @@ CREATE TABLE `system_login_log` (
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 2582 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 2599 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_login_log
|
||||
@ -1634,7 +1634,7 @@ CREATE TABLE `system_mail_log` (
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 354 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮件日志表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 355 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮件日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_mail_log
|
||||
@ -2283,7 +2283,7 @@ CREATE TABLE `system_notify_template` (
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '站内信模板表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '站内信模板表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_notify_template
|
||||
@ -2311,7 +2311,7 @@ CREATE TABLE `system_oauth2_access_token` (
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 3056 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 3063 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_oauth2_access_token
|
||||
@ -2433,7 +2433,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1063 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1070 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_oauth2_refresh_token
|
||||
@ -3481,7 +3481,7 @@ CREATE TABLE `system_sms_code` (
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 530 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 531 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_sms_code
|
||||
@ -3574,6 +3574,33 @@ INSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `cont
|
||||
INSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (16, 1, 0, 'user-reset-password', '会员用户 - 重置密码', '您的验证码{code},该验证码 5 分钟内有效,请勿泄漏于他人!', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:18', b'0');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for system_social_client
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `system_social_client`;
|
||||
CREATE TABLE `system_social_client` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
|
||||
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名',
|
||||
`social_type` tinyint NOT NULL COMMENT '社交平台的类型',
|
||||
`user_type` tinyint NOT NULL COMMENT '用户类型',
|
||||
`client_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端编号',
|
||||
`client_secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户端密钥',
|
||||
`status` tinyint NOT NULL COMMENT '状态',
|
||||
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 43 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交客户端表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_social_client
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for system_social_user
|
||||
-- ----------------------------
|
||||
@ -3596,7 +3623,7 @@ CREATE TABLE `system_social_user` (
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交用户表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交用户表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_social_user
|
||||
@ -3621,7 +3648,7 @@ CREATE TABLE `system_social_user_bind` (
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 76 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交绑定表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 79 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交绑定表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_social_user_bind
|
||||
@ -3797,7 +3824,7 @@ CREATE TABLE `system_users` (
|
||||
-- Records of system_users
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://test.yudao.iocoder.cn/e1fdd7271685ec143a0900681606406621717a666ad0b2798b096df41422b32f.png', 0, '0:0:0:0:0:0:0:1', '2023-10-17 21:03:03', 'admin', '2021-01-05 17:03:47', NULL, '2023-10-17 21:03:03', b'0', 1);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://test.yudao.iocoder.cn/e1fdd7271685ec143a0900681606406621717a666ad0b2798b096df41422b32f.png', 0, '0:0:0:0:0:0:0:1', '2023-10-18 22:31:35', 'admin', '2021-01-05 17:03:47', NULL, '2023-10-18 22:31:35', b'0', 1);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '127.0.0.1', '2022-07-09 23:03:33', '', '2021-01-07 09:07:17', NULL, '2022-07-09 23:03:33', b'0', 1);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$10$YMpimV4T6BtDhIaA8jSW.u8UTGBeGhc/qwXP4oxoMr4mOw9.qttt6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '127.0.0.1', '2022-07-08 01:26:27', '', '2021-01-13 23:50:35', NULL, '2022-07-08 01:26:27', b'0', 1);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$10$GP8zvqHB//TekuzYZSBYAuBQJiNq1.fxQVDYJ.uBCOnWCtDVKE4H6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2023-09-24 18:21:19', '', '2021-01-21 02:13:53', NULL, '2023-09-24 18:21:19', b'0', 1);
|
||||
@ -3805,7 +3832,7 @@ INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`,
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (108, 'admin108', '$2a$10$y6mfvKoNYL1GXWak8nYwVOH.kCWqjactkzdoIDgiKl93WN3Ejg.Lu', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:00:50', '1', '2022-02-27 08:26:53', b'0', 119);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (109, 'admin109', '$2a$10$JAqvH0tEc0I7dfDVBI7zyuB4E3j.uH6daIjV53.vUS6PknFkDJkuK', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:11:50', '1', '2022-02-27 08:26:56', b'0', 120);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (110, 'admin110', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '小王', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '127.0.0.1', '2022-09-25 22:47:33', '1', '2022-02-22 00:56:14', NULL, '2022-09-25 22:47:33', b'0', 121);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (111, 'test', '$2a$10$mExveopHUx9Q4QiLtAzhDeH3n4/QlNLzEsM4AqgxKrU.ciUZDXZCy', '测试用户', NULL, NULL, '[]', '', '', 0, '', 0, '', NULL, '110', '2022-02-23 13:14:33', '110', '2022-02-23 13:14:33', b'0', 121);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (111, 'test', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '测试用户', NULL, NULL, '[]', '', '', 0, '', 0, '0:0:0:0:0:0:0:1', '2023-10-18 23:31:51', '110', '2022-02-23 13:14:33', NULL, '2023-10-18 23:31:51', b'0', 121);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (112, 'newobject', '$2a$10$3alwklxqfq8/hKoW6oUV0OJp0IdQpBDauLy4633SpUjrRsStl6kMa', '新对象', NULL, 100, '[]', '', '', 1, '', 0, '0:0:0:0:0:0:0:1', '2023-02-10 13:48:13', '1', '2022-02-23 19:08:03', NULL, '2023-02-10 13:48:13', b'0', 1);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (113, 'aoteman', '$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', '芋道', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '127.0.0.1', '2022-03-19 18:38:51', '1', '2022-03-07 21:37:58', NULL, '2022-03-19 18:38:51', b'0', 122);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (114, 'hrmgr', '$2a$10$TR4eybBioGRhBmDBWkqWLO6NIh3mzYa8KBKDDB5woiGYFVlRAi.fu', 'hr 小姐姐', NULL, NULL, '[3]', '', '', 0, '', 0, '127.0.0.1', '2022-03-19 22:15:43', '1', '2022-03-19 21:50:58', NULL, '2022-03-19 22:15:43', b'0', 1);
|
||||
|
@ -51,7 +51,9 @@ public class PayTransferUnifiedReqDTO {
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 收款方信息,转账类型不同,收款方信息不同
|
||||
* 收款方信息。
|
||||
*
|
||||
* 转账类型 {@link #type} 不同,收款方信息不同
|
||||
*/
|
||||
@NotEmpty(message = "收款方信息 不能为空")
|
||||
private Map<String, String> payeeInfo;
|
||||
|
@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
||||
|
||||
// TODO @jason:看看能不能融合到 AbstractAlipayPayClient 中。
|
||||
/**
|
||||
* 支付宝转账的 PayClient 实现类
|
||||
*
|
||||
@ -29,6 +30,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
|
||||
*/
|
||||
@Slf4j
|
||||
public class AlipayTransferClient extends AbstractAlipayPayClient {
|
||||
// TODO @jason:方法之间,要有空格噢
|
||||
public AlipayTransferClient(Long channelId, AlipayPayClientConfig config) {
|
||||
super(channelId, PayChannelEnum.ALIPAY_TRANSFER.getCode(), config);
|
||||
}
|
||||
@ -49,16 +51,17 @@ public class AlipayTransferClient extends AbstractAlipayPayClient {
|
||||
model.setOrderTitle(reqDTO.getTitle()); // 转账业务的标题,用于在支付宝用户的账单里显示。
|
||||
model.setOutBizNo(reqDTO.getOutTransferNo());
|
||||
model.setProductCode("TRANS_ACCOUNT_NO_PWD"); // 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD
|
||||
model.setBizScene("DIRECT_TRANSFER"); // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER。
|
||||
model.setBizScene("DIRECT_TRANSFER"); // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER
|
||||
model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras()));
|
||||
PayTransferTypeEnum transferType = PayTransferTypeEnum.ofType(reqDTO.getType());
|
||||
switch(transferType){
|
||||
case WX_BALANCE :
|
||||
case WALLET_BALANCE : {
|
||||
switch(transferType) {
|
||||
case WX_BALANCE:
|
||||
case WALLET_BALANCE: {
|
||||
log.error("[doUnifiedTransfer],支付宝转账不支持的转账类型{}", transferType);
|
||||
throw new UnsupportedOperationException(String.format("支付宝转账不支持转账类型: %s",transferType.getName()));
|
||||
}
|
||||
case ALIPAY_BALANCE : {
|
||||
// TODO @jason:是不是不用传递 transferType 参数哈?因为应该已经明确是支付宝啦?
|
||||
case ALIPAY_BALANCE: {
|
||||
// ② 个性化的参数
|
||||
Participant payeeInfo = new Participant();
|
||||
payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
|
||||
@ -90,7 +93,7 @@ public class AlipayTransferClient extends AbstractAlipayPayClient {
|
||||
return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()),
|
||||
response.getOutBizNo(), response);
|
||||
}
|
||||
case BANK_CARD : {
|
||||
case BANK_CARD: {
|
||||
Participant payeeInfo = new Participant();
|
||||
payeeInfo.setIdentityType("BANKCARD_ACCOUNT");
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
@ -100,4 +103,5 @@ public class AlipayTransferClient extends AbstractAlipayPayClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -70,4 +70,5 @@ public class MockPayClient extends AbstractPayClient<NonePayClientConfig> {
|
||||
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ public enum PayChannelEnum {
|
||||
ALIPAY_APP("alipay_app", "支付宝App 支付", AlipayPayClientConfig.class),
|
||||
ALIPAY_QR("alipay_qr", "支付宝扫码支付", AlipayPayClientConfig.class),
|
||||
ALIPAY_BAR("alipay_bar", "支付宝条码支付", AlipayPayClientConfig.class),
|
||||
// TODO @jason:是不是按照微信聊的,合并回 ALIPAY_PC;因为支付宝、微信都是多种支付方式,选择其中的一种即可;
|
||||
ALIPAY_TRANSFER("alipay_transfer", "支付宝转账", AlipayPayClientConfig.class),
|
||||
|
||||
MOCK("mock", "模拟支付", NonePayClientConfig.class),
|
||||
|
@ -18,10 +18,10 @@ public enum PayTransferStatusRespEnum {
|
||||
|
||||
/**
|
||||
* TODO 转账到银行卡. 会有T+0 T+1 到账的请情况。 还未实现
|
||||
* TODO @jason:可以看看其它开源项目,针对这个场景,处理策略是怎么样的?例如说,每天主动轮询?这个状态的单子?
|
||||
*/
|
||||
IN_PROGRESS(10, "转账进行中"),
|
||||
|
||||
|
||||
SUCCESS(20, "转账成功"),
|
||||
/**
|
||||
* 转账关闭 (失败,或者其它情况)
|
||||
|
@ -15,6 +15,7 @@ import java.util.Arrays;
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum PayTransferTypeEnum implements IntArrayValuable {
|
||||
|
||||
ALIPAY_BALANCE(1, "支付宝余额"),
|
||||
WX_BALANCE(2, "微信余额"),
|
||||
BANK_CARD(3, "银行卡"),
|
||||
@ -33,7 +34,9 @@ public enum PayTransferTypeEnum implements IntArrayValuable {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
// TODO @jason:是不是 typeOf 更符合预期哈?
|
||||
public static PayTransferTypeEnum ofType(Integer type) {
|
||||
return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,313 @@
|
||||
package cn.iocoder.yudao.framework.mybatis.core.query;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import com.github.yulichang.toolkit.MPJWrappers;
|
||||
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 拓展 MyBatis Plus Join QueryWrapper 类,主要增加如下功能:
|
||||
* <p>
|
||||
* 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
|
||||
*
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> {
|
||||
|
||||
public MPJLambdaWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) {
|
||||
MPJWrappers.lambdaJoin().like(column, val);
|
||||
if (StringUtils.hasText(val)) {
|
||||
return (MPJLambdaWrapperX<T>) super.like(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MPJLambdaWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {
|
||||
if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
|
||||
return (MPJLambdaWrapperX<T>) super.in(column, values);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MPJLambdaWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) {
|
||||
if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
|
||||
return (MPJLambdaWrapperX<T>) super.in(column, values);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MPJLambdaWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {
|
||||
if (ObjectUtil.isNotEmpty(val)) {
|
||||
return (MPJLambdaWrapperX<T>) super.eq(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MPJLambdaWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) {
|
||||
if (ObjectUtil.isNotEmpty(val)) {
|
||||
return (MPJLambdaWrapperX<T>) super.ne(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MPJLambdaWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) {
|
||||
if (val != null) {
|
||||
return (MPJLambdaWrapperX<T>) super.gt(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MPJLambdaWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) {
|
||||
if (val != null) {
|
||||
return (MPJLambdaWrapperX<T>) super.ge(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MPJLambdaWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) {
|
||||
if (val != null) {
|
||||
return (MPJLambdaWrapperX<T>) super.lt(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MPJLambdaWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) {
|
||||
if (val != null) {
|
||||
return (MPJLambdaWrapperX<T>) super.le(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MPJLambdaWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) {
|
||||
if (val1 != null && val2 != null) {
|
||||
return (MPJLambdaWrapperX<T>) super.between(column, val1, val2);
|
||||
}
|
||||
if (val1 != null) {
|
||||
return (MPJLambdaWrapperX<T>) ge(column, val1);
|
||||
}
|
||||
if (val2 != null) {
|
||||
return (MPJLambdaWrapperX<T>) le(column, val2);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MPJLambdaWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) {
|
||||
Object val1 = ArrayUtils.get(values, 0);
|
||||
Object val2 = ArrayUtils.get(values, 1);
|
||||
return betweenIfPresent(column, val1, val2);
|
||||
}
|
||||
|
||||
// ========== 重写父类方法,方便链式调用 ==========
|
||||
|
||||
@Override
|
||||
public <X> MPJLambdaWrapperX<T> eq(boolean condition, SFunction<X, ?> column, Object val) {
|
||||
super.eq(condition, column, val);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> MPJLambdaWrapperX<T> eq(SFunction<X, ?> column, Object val) {
|
||||
super.eq(column, val);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> MPJLambdaWrapperX<T> orderByDesc(SFunction<X, ?> column) {
|
||||
//noinspection unchecked
|
||||
super.orderByDesc(true, column);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MPJLambdaWrapperX<T> last(String lastSql) {
|
||||
super.last(lastSql);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> MPJLambdaWrapperX<T> in(SFunction<X, ?> column, Collection<?> coll) {
|
||||
super.in(column, coll);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MPJLambdaWrapperX<T> selectAll(Class<?> clazz) {
|
||||
super.selectAll(clazz);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MPJLambdaWrapperX<T> selectAll(Class<?> clazz, String prefix) {
|
||||
super.selectAll(clazz, prefix);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> MPJLambdaWrapperX<T> selectAs(SFunction<S, ?> column, String alias) {
|
||||
super.selectAs(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> MPJLambdaWrapperX<T> selectAs(String column, SFunction<E, ?> alias) {
|
||||
super.selectAs(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectAs(SFunction<S, ?> column, SFunction<X, ?> alias) {
|
||||
super.selectAs(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E, X> MPJLambdaWrapperX<T> selectAs(String index, SFunction<E, ?> column, SFunction<X, ?> alias) {
|
||||
super.selectAs(index, column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> MPJLambdaWrapperX<T> selectAsClass(Class<E> source, Class<?> tag) {
|
||||
super.selectAsClass(source, tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E, F> MPJLambdaWrapperX<T> selectSub(Class<E> clazz, Consumer<MPJLambdaWrapper<E>> consumer, SFunction<F, ?> alias) {
|
||||
super.selectSub(clazz, consumer, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E, F> MPJLambdaWrapperX<T> selectSub(Class<E> clazz, String st, Consumer<MPJLambdaWrapper<E>> consumer, SFunction<F, ?> alias) {
|
||||
super.selectSub(clazz, st, consumer, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column) {
|
||||
super.selectCount(column);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MPJLambdaWrapperX<T> selectCount(Object column, String alias) {
|
||||
super.selectCount(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> MPJLambdaWrapperX<T> selectCount(Object column, SFunction<X, ?> alias) {
|
||||
super.selectCount(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column, String alias) {
|
||||
super.selectCount(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column, SFunction<X, ?> alias) {
|
||||
super.selectCount(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column) {
|
||||
super.selectSum(column);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column, String alias) {
|
||||
super.selectSum(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column, SFunction<X, ?> alias) {
|
||||
super.selectSum(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column) {
|
||||
super.selectMax(column);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column, String alias) {
|
||||
super.selectMax(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column, SFunction<X, ?> alias) {
|
||||
super.selectMax(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column) {
|
||||
super.selectMin(column);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column, String alias) {
|
||||
super.selectMin(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column, SFunction<X, ?> alias) {
|
||||
super.selectMin(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column) {
|
||||
super.selectAvg(column);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column, String alias) {
|
||||
super.selectAvg(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column, SFunction<X, ?> alias) {
|
||||
super.selectAvg(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column) {
|
||||
super.selectLen(column);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column, String alias) {
|
||||
super.selectLen(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, X> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column, SFunction<X, ?> alias) {
|
||||
super.selectLen(column, alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -46,7 +46,7 @@ public class AppActivityController {
|
||||
@Parameter(name = "spuId", description = "商品编号", required = true)
|
||||
public CommonResult<List<AppActivityRespVO>> getActivityListBySpuId(@RequestParam("spuId") Long spuId) {
|
||||
// 每种活动,只返回一个
|
||||
return success(getAppActivityRespVOList(Collections.singletonList(spuId)));
|
||||
return success(getAppActivityList(Collections.singletonList(spuId)));
|
||||
}
|
||||
|
||||
@GetMapping("/list-by-spu-ids")
|
||||
@ -57,16 +57,17 @@ public class AppActivityController {
|
||||
return success(MapUtil.empty());
|
||||
}
|
||||
// 每种活动,只返回一个;key 为 SPU 编号
|
||||
return success(convertMultiMap(getAppActivityRespVOList(spuIds), AppActivityRespVO::getSpuId));
|
||||
return success(convertMultiMap(getAppActivityList(spuIds), AppActivityRespVO::getSpuId));
|
||||
}
|
||||
|
||||
private List<AppActivityRespVO> getAppActivityRespVOList(Collection<Long> spuIds) {
|
||||
private List<AppActivityRespVO> getAppActivityList(Collection<Long> spuIds) {
|
||||
if (CollUtil.isEmpty(spuIds)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
List<AppActivityRespVO> activityList = new ArrayList<>();
|
||||
// 拼团活动-获取开启的且开始的且没有结束的活动
|
||||
|
||||
// 1. 拼团活动 - 获取开启的且开始的且没有结束的活动
|
||||
List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityBySpuIdsAndStatusAndDateTimeLt(
|
||||
spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
|
||||
if (CollUtil.isNotEmpty(combinationActivities)) {
|
||||
@ -76,7 +77,8 @@ public class AppActivityController {
|
||||
.setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
|
||||
});
|
||||
}
|
||||
// 秒杀活动-获取开启的且开始的且没有结束的活动
|
||||
|
||||
// 2. 秒杀活动 - 获取开启的且开始的且没有结束的活动
|
||||
List<SeckillActivityDO> seckillActivities = seckillActivityService.getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(
|
||||
spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
|
||||
if (CollUtil.isNotEmpty(seckillActivities)) {
|
||||
@ -86,7 +88,8 @@ public class AppActivityController {
|
||||
.setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
|
||||
});
|
||||
}
|
||||
// 砍价活动-获取开启的且开始的且没有结束的活动
|
||||
|
||||
// 3. 砍价活动 - 获取开启的且开始的且没有结束的活动
|
||||
List<BargainActivityDO> bargainActivities = bargainActivityService.getBargainActivityBySpuIdsAndStatusAndDateTimeLt(
|
||||
spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
|
||||
if (CollUtil.isNotEmpty(bargainActivities)) {
|
||||
|
@ -32,7 +32,7 @@ public class AppArticleCategoryController {
|
||||
public CommonResult<List<AppArticleCategoryRespVO>> getArticleCategoryList() {
|
||||
List<ArticleCategoryDO> categoryList = articleCategoryService.getArticleCategoryListByStatus(
|
||||
CommonStatusEnum.ENABLE.getStatus());
|
||||
categoryList.sort(Comparator.comparing(ArticleCategoryDO::getSort).reversed()); // 按 sort 降序排列
|
||||
categoryList.sort(Comparator.comparing(ArticleCategoryDO::getSort)); // 按 sort 降序排列
|
||||
return success(ArticleCategoryConvert.INSTANCE.convertList04(categoryList));
|
||||
}
|
||||
|
||||
|
@ -56,10 +56,12 @@ public class AppArticleController {
|
||||
return success(ArticleConvert.INSTANCE.convert01(articleService.getArticle(id)));
|
||||
}
|
||||
|
||||
// TODO @puhui999:add-browse-count 噢;前端 uniapp 也要接下;就是打开文章的时候,调用下这个接口;
|
||||
@PutMapping("/add-browseCount")
|
||||
@Operation(summary = "增加文章浏览量")
|
||||
@Parameter(name = "id", description = "文章编号", example = "1024")
|
||||
public CommonResult<Boolean> addBrowseCount(@RequestParam("id") Long id) {
|
||||
// TODO @puhui999:addArticleBrowseCount
|
||||
articleService.addBrowseCount(id);
|
||||
return success(true);
|
||||
}
|
||||
|
@ -204,9 +204,6 @@ public interface CombinationActivityConvert {
|
||||
return respVO;
|
||||
}
|
||||
|
||||
@Mapping(target = "id", ignore = true)
|
||||
CombinationRecordDO convert5(CombinationRecordDO headRecord);
|
||||
|
||||
/**
|
||||
* 转换生成虚拟成团虚拟记录
|
||||
*
|
||||
@ -214,21 +211,19 @@ public interface CombinationActivityConvert {
|
||||
* @return 虚拟记录列表
|
||||
*/
|
||||
default List<CombinationRecordDO> convertVirtualRecordList(CombinationRecordDO headRecord) {
|
||||
List<CombinationRecordDO> createRecords = new ArrayList<>();
|
||||
// 计算需要创建的虚拟成团记录数量
|
||||
int count = headRecord.getUserSize() - headRecord.getUserCount();
|
||||
List<CombinationRecordDO> createRecords = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
// 基础信息和团长保持一致
|
||||
CombinationRecordDO newRecord = convert5(headRecord);
|
||||
// 虚拟信息
|
||||
newRecord.setCount(0);
|
||||
newRecord.setUserId(0L);
|
||||
newRecord.setNickname("");
|
||||
newRecord.setAvatar("");
|
||||
newRecord.setOrderId(0L);
|
||||
newRecord.setCount(0) // 会单独更新下,在后续的 Service 逻辑里
|
||||
.setUserId(0L).setNickname("").setAvatar("").setOrderId(0L);
|
||||
createRecords.add(newRecord);
|
||||
}
|
||||
return createRecords;
|
||||
}
|
||||
@Mapping(target = "id", ignore = true)
|
||||
CombinationRecordDO convert5(CombinationRecordDO headRecord);
|
||||
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> {
|
||||
.groupBy("spu_id"));
|
||||
}
|
||||
|
||||
// TODO @puhui999:是不是只要 endTime 小于就可以啦;
|
||||
/**
|
||||
* 获取指定活动编号的活动列表且
|
||||
* 开始时间和结束时间小于给定时间 dateTime 的活动列表
|
||||
|
@ -10,7 +10,6 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleCategoryD
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.mysql.article.ArticleMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@ -112,12 +111,10 @@ public class ArticleServiceImpl implements ArticleService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void addBrowseCount(Long id) {
|
||||
// 校验文章是否存在
|
||||
validateArticleExists(id);
|
||||
|
||||
// 增加浏览次数 TODO 先简单做,用户规模不大,只 +1
|
||||
// 增加浏览次数
|
||||
articleMapper.updateBrowseCount(id);
|
||||
}
|
||||
|
||||
|
@ -369,6 +369,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
||||
keyValue.setValue(keyValue.getValue() + 1);
|
||||
}
|
||||
} catch (Exception ignored) { // 处理异常继续循环
|
||||
// TODO @puhui999:拼团过期 or 虚拟成团 可以改成 expireCombinationRecord;因为找方法更容易一些;
|
||||
log.error("[拼团过期 or 虚拟成团][record({}) 处理异常,请进行处理!record 数据是:{}]",
|
||||
record.getId(), JsonUtils.toJsonString(record));
|
||||
}
|
||||
@ -383,10 +384,10 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void handleExpireRecord(CombinationRecordDO headRecord) {
|
||||
// 1.更新拼团记录
|
||||
// 1. 更新拼团记录
|
||||
List<CombinationRecordDO> headAndRecords = updateBatchCombinationRecords(headRecord,
|
||||
CombinationRecordStatusEnum.FAILED);
|
||||
// 2.订单取消
|
||||
// 2. 订单取消
|
||||
headAndRecords.forEach(item -> tradeOrderApi.cancelPaidOrder(item.getUserId(), item.getOrderId()));
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,11 @@ import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
|
||||
@Tag(name = "管理后台 - 交易订单")
|
||||
@ -56,7 +59,9 @@ public class TradeOrderController {
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(convertSet(pageResult.getList(), TradeOrderDO::getUserId));
|
||||
Set<Long> userIds = CollUtil.unionDistinct(convertList(pageResult.getList(), TradeOrderDO::getUserId),
|
||||
convertList(pageResult.getList(), TradeOrderDO::getBrokerageUserId, Objects::nonNull));
|
||||
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);
|
||||
// 查询订单项
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(
|
||||
convertSet(pageResult.getList(), TradeOrderDO::getId));
|
||||
@ -64,6 +69,13 @@ public class TradeOrderController {
|
||||
return success(TradeOrderConvert.INSTANCE.convertPage(pageResult, orderItems, userMap));
|
||||
}
|
||||
|
||||
@GetMapping("/summary")
|
||||
@Operation(summary = "获得交易订单统计")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:query')")
|
||||
public CommonResult<TradeOrderSummaryRespVO> getOrderSummary(TradeOrderPageReqVO reqVO) {
|
||||
return success(tradeOrderQueryService.getOrderSummary(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-detail")
|
||||
@Operation(summary = "获得交易订单详情")
|
||||
@Parameter(name = "id", description = "订单编号", required = true, example = "1")
|
||||
|
@ -145,4 +145,7 @@ public class TradeOrderBaseVO {
|
||||
@Schema(description = "VIP 减免金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "888")
|
||||
private Integer vipPrice;
|
||||
|
||||
@Schema(description = "推广人编号", example = "1")
|
||||
private Long brokerageUserId;
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@ -14,24 +14,20 @@ public class TradeOrderPageItemRespVO extends TradeOrderBaseVO {
|
||||
@Schema(description = "收件人地区名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海 上海市 普陀区")
|
||||
private String receiverAreaName;
|
||||
|
||||
/**
|
||||
* 订单项列表
|
||||
*/
|
||||
@Schema(description = "订单项列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<Item> items;
|
||||
|
||||
// TODO @xiaobai:使用 MemberUserRespVO 返回哈;DTO 不直接给前端
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
private MemberUserRespDTO user;
|
||||
@Schema(description = "用户信息", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private MemberUserRespVO user;
|
||||
|
||||
@Schema(description = "推广人信息")
|
||||
private MemberUserRespVO brokerageUser;
|
||||
|
||||
@Schema(description = "管理后台 - 交易订单的分页项的订单项目")
|
||||
@Data
|
||||
public static class Item extends TradeOrderItemBaseVO {
|
||||
|
||||
/**
|
||||
* 属性数组
|
||||
*/
|
||||
@Schema(description = "属性列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<ProductPropertyValueDetailRespVO> properties;
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 交易订单统计 Response VO")
|
||||
@Data
|
||||
public class TradeOrderSummaryRespVO {
|
||||
|
||||
@Schema(description = "订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long orderCount;
|
||||
|
||||
@Schema(description = "订单金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long orderPayPrice;
|
||||
|
||||
@Schema(description = "退款单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long afterSaleCount;
|
||||
|
||||
@Schema(description = "退款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long afterSalePrice;
|
||||
|
||||
}
|
@ -124,13 +124,17 @@ public interface TradeOrderConvert {
|
||||
TradeOrderPageItemRespVO orderVO = convert(order, xOrderItems);
|
||||
// 处理收货地址
|
||||
orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));
|
||||
// 增加用户昵称
|
||||
orderVO.setUser(memberUserMap.get(orderVO.getUserId()));
|
||||
// 增加用户信息
|
||||
orderVO.setUser(convertUser(memberUserMap.get(orderVO.getUserId())));
|
||||
// 增加推广人信息
|
||||
orderVO.setBrokerageUser(convertUser(memberUserMap.get(orderVO.getBrokerageUserId())));
|
||||
return orderVO;
|
||||
});
|
||||
return new PageResult<>(orderVOs, pageResult.getTotal());
|
||||
}
|
||||
|
||||
MemberUserRespVO convertUser(MemberUserRespDTO memberUserRespDTO);
|
||||
|
||||
TradeOrderPageItemRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> items);
|
||||
|
||||
ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);
|
||||
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.dal.mysql.order;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
@ -11,6 +12,7 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Mapper
|
||||
@ -42,6 +44,27 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
|
||||
.orderByDesc(TradeOrderDO::getId));
|
||||
}
|
||||
|
||||
// TODO @疯狂:如果用 map 返回,要不这里直接用 TradeOrderSummaryRespVO 返回?也算合理,就当 sql 查询出这么个玩意~~
|
||||
default List<Map<String, Object>> selectOrderSummaryGroupByRefundStatus(TradeOrderPageReqVO reqVO, Set<Long> userIds) {
|
||||
return selectMaps(new MPJLambdaWrapperX<TradeOrderDO>()
|
||||
.selectAs(TradeOrderDO::getRefundStatus, TradeOrderDO::getRefundStatus) // 售后状态
|
||||
.selectCount(TradeOrderDO::getId, "count") // 售后状态对应的数量
|
||||
.selectSum(TradeOrderDO::getPayPrice, "price") // 售后状态对应的支付金额
|
||||
.likeIfPresent(TradeOrderDO::getNo, reqVO.getNo())
|
||||
.eqIfPresent(TradeOrderDO::getUserId, reqVO.getUserId())
|
||||
.eqIfPresent(TradeOrderDO::getDeliveryType, reqVO.getDeliveryType())
|
||||
.inIfPresent(TradeOrderDO::getUserId, userIds)
|
||||
.eqIfPresent(TradeOrderDO::getType, reqVO.getType())
|
||||
.eqIfPresent(TradeOrderDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(TradeOrderDO::getPayChannelCode, reqVO.getPayChannelCode())
|
||||
.eqIfPresent(TradeOrderDO::getTerminal, reqVO.getTerminal())
|
||||
.eqIfPresent(TradeOrderDO::getLogisticsId, reqVO.getLogisticsId())
|
||||
.inIfPresent(TradeOrderDO::getPickUpStoreId, reqVO.getPickUpStoreIds())
|
||||
.likeIfPresent(TradeOrderDO::getPickUpVerifyCode, reqVO.getPickUpVerifyCode())
|
||||
.betweenIfPresent(TradeOrderDO::getCreateTime, reqVO.getCreateTime())
|
||||
.groupBy(TradeOrderDO::getRefundStatus)); // 按售后状态分组
|
||||
}
|
||||
|
||||
default PageResult<TradeOrderDO> selectPage(AppTradeOrderPageReqVO reqVO, Long userId) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<TradeOrderDO>()
|
||||
.eq(TradeOrderDO::getUserId, userId)
|
||||
@ -92,7 +115,7 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
|
||||
default TradeOrderDO selectOneByPickUpVerifyCode(String pickUpVerifyCode) {
|
||||
return selectOne(TradeOrderDO::getPickUpVerifyCode, pickUpVerifyCode);
|
||||
}
|
||||
|
||||
|
||||
default TradeOrderDO selectByUserIdAndCombinationActivityIdAndStatus(Long userId, Long combinationActivityId, Integer status) {
|
||||
return selectOne(new LambdaQueryWrapperX<TradeOrderDO>()
|
||||
.eq(TradeOrderDO::getUserId, userId)
|
||||
|
@ -58,10 +58,6 @@ public class TradeOrderLogAspect {
|
||||
*/
|
||||
private static final ThreadLocal<Map<String, Object>> EXTS = new ThreadLocal<>();
|
||||
|
||||
public TradeOrderLogAspect() {
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
@Resource
|
||||
private TradeOrderLogService orderLogService;
|
||||
|
||||
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.service.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderSummaryRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
@ -64,6 +65,14 @@ public interface TradeOrderQueryService {
|
||||
*/
|
||||
PageResult<TradeOrderDO> getOrderPage(TradeOrderPageReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 获得订单统计
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 订单统计
|
||||
*/
|
||||
TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 【会员】获得交易订单分页
|
||||
*
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.trade.service.order;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
@ -8,6 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderSummaryRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
@ -15,6 +17,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.redis.RedisKeyConstants;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory;
|
||||
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
|
||||
@ -85,24 +88,57 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
|
||||
|
||||
@Override
|
||||
public PageResult<TradeOrderDO> getOrderPage(TradeOrderPageReqVO reqVO) {
|
||||
// 根据用户查询条件构建用户编号列表
|
||||
Set<Long> userIds = buildQueryConditionUserIds(reqVO);
|
||||
if (userIds == null) { // 没查询到用户,说明肯定也没他的订单
|
||||
return PageResult.empty();
|
||||
}
|
||||
// 分页查询
|
||||
return tradeOrderMapper.selectPage(reqVO, userIds);
|
||||
}
|
||||
|
||||
private Set<Long> buildQueryConditionUserIds(TradeOrderPageReqVO reqVO) {
|
||||
// 获得 userId 相关的查询
|
||||
Set<Long> userIds = new HashSet<>();
|
||||
if (StrUtil.isNotEmpty(reqVO.getUserMobile())) {
|
||||
MemberUserRespDTO user = memberUserApi.getUserByMobile(reqVO.getUserMobile());
|
||||
if (user == null) { // 没查询到用户,说明肯定也没他的订单
|
||||
return new PageResult<>();
|
||||
return null;
|
||||
}
|
||||
userIds.add(user.getId());
|
||||
}
|
||||
if (StrUtil.isNotEmpty(reqVO.getUserNickname())) {
|
||||
List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(reqVO.getUserNickname());
|
||||
if (CollUtil.isEmpty(users)) { // 没查询到用户,说明肯定也没他的订单
|
||||
return new PageResult<>();
|
||||
return null;
|
||||
}
|
||||
userIds.addAll(convertSet(users, MemberUserRespDTO::getId));
|
||||
}
|
||||
// 分页查询
|
||||
return tradeOrderMapper.selectPage(reqVO, userIds);
|
||||
return userIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO) {
|
||||
// 根据用户查询条件构建用户编号列表
|
||||
Set<Long> userIds = buildQueryConditionUserIds(reqVO);
|
||||
if (userIds == null) { // 没查询到用户,说明肯定也没他的订单
|
||||
return new TradeOrderSummaryRespVO();
|
||||
}
|
||||
// 查询每个售后状态对应的数量、金额
|
||||
List<Map<String, Object>> list = tradeOrderMapper.selectOrderSummaryGroupByRefundStatus(reqVO, null);
|
||||
|
||||
TradeOrderSummaryRespVO vo = new TradeOrderSummaryRespVO().setAfterSaleCount(0L).setAfterSalePrice(0L);
|
||||
for (Map<String, Object> map : list) {
|
||||
Long count = MapUtil.getLong(map, "count", 0L);
|
||||
Long price = MapUtil.getLong(map, "price", 0L);
|
||||
// 未退款的计入订单,部分退款、全部退款计入售后
|
||||
if (TradeOrderRefundStatusEnum.NONE.getStatus().equals(MapUtil.getInt(map, "refundStatus"))) {
|
||||
vo.setOrderCount(count).setOrderPayPrice(price);
|
||||
} else {
|
||||
vo.setAfterSaleCount(vo.getAfterSaleCount() + count).setAfterSalePrice(vo.getAfterSalePrice() + price);
|
||||
}
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,7 +19,6 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_004_003_000, "登录失败,账号密码不正确");
|
||||
ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_004_003_001, "登录失败,账号被禁用");
|
||||
ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_004_003_005, "未绑定账号,需要进行绑定");
|
||||
ErrorCode AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1_004_003_006, "获得手机号失败");
|
||||
ErrorCode AUTH_MOBILE_USED = new ErrorCode(1_004_003_007, "手机号已经被使用");
|
||||
|
||||
// ========== 用户收件地址 1-004-004-000 ==========
|
||||
|
@ -43,10 +43,6 @@
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-weixin</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
|
@ -59,3 +59,8 @@ tenant-id: {{appTenentId}}
|
||||
POST {{appApi}}/member/auth/refresh-token?refreshToken=bc43d929094849a28b3a69f6e6940d70
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
### 请求 /auth/create-weixin-jsapi-signature 接口 => 成功
|
||||
POST {{appApi}}/member/auth/create-weixin-jsapi-signature?url=http://www.iocoder.cn
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
@ -1,12 +1,16 @@
|
||||
package cn.iocoder.yudao.module.member.controller.app.auth;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
|
||||
import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
|
||||
import cn.iocoder.yudao.module.member.service.auth.MemberAuthService;
|
||||
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
@ -33,6 +37,9 @@ public class AppAuthController {
|
||||
@Resource
|
||||
private MemberAuthService authService;
|
||||
|
||||
@Resource
|
||||
private SocialClientApi socialClientApi;
|
||||
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@ -109,4 +116,13 @@ public class AppAuthController {
|
||||
return success(authService.weixinMiniAppLogin(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/create-weixin-jsapi-signature")
|
||||
@Operation(summary = "创建微信 JS SDK 初始化所需的签名",
|
||||
description = "参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档")
|
||||
public CommonResult<SocialWxJsapiSignatureRespDTO> createWeixinMpJsapiSignature(@RequestParam("url") String url) {
|
||||
SocialWxJsapiSignatureRespDTO signature = socialClientApi.createWxMpJsapiSignature(
|
||||
UserTypeEnum.MEMBER.getValue(), url);
|
||||
return success(AuthConvert.INSTANCE.convert(signature));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
package cn.iocoder.yudao.module.member.controller.app.auth.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Schema(description = "用户 APP - 微信公众号 JSAPI 签名 Response VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AuthWeixinJsapiSignatureRespVO {
|
||||
|
||||
@Schema(description = "微信公众号的 appId", requiredMode = Schema.RequiredMode.REQUIRED, example = "hello")
|
||||
private String appId;
|
||||
|
||||
@Schema(description = "匿名串", requiredMode = Schema.RequiredMode.REQUIRED, example = "world")
|
||||
private String nonceStr;
|
||||
|
||||
@Schema(description = "时间戳", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long timestamp;
|
||||
|
||||
@Schema(description = "URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "阿巴阿巴")
|
||||
private String signature;
|
||||
|
||||
}
|
@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
|
||||
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
@ -29,4 +30,6 @@ public interface AuthConvert {
|
||||
|
||||
SmsCodeValidateReqDTO convert(AppAuthSmsValidateReqVO bean);
|
||||
|
||||
SocialWxJsapiSignatureRespDTO convert(SocialWxJsapiSignatureRespDTO bean);
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package cn.iocoder.yudao.module.member.service.auth;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
@ -19,9 +17,11 @@ import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
|
||||
import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
|
||||
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
|
||||
import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
||||
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants;
|
||||
@ -56,10 +56,9 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||
@Resource
|
||||
private SocialUserApi socialUserApi;
|
||||
@Resource
|
||||
private OAuth2TokenApi oauth2TokenApi;
|
||||
|
||||
private SocialClientApi socialClientApi;
|
||||
@Resource
|
||||
private WxMaService wxMaService;
|
||||
private OAuth2TokenApi oauth2TokenApi;
|
||||
|
||||
@Override
|
||||
public AppAuthLoginRespVO login(AppAuthLoginReqVO reqVO) {
|
||||
@ -121,15 +120,13 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||
@Override
|
||||
public AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO) {
|
||||
// 获得对应的手机号信息
|
||||
// TODO @芋艿:需要弱化微信小程序的依赖,通过 system 获取手机号
|
||||
WxMaPhoneNumberInfo phoneNumberInfo;
|
||||
try {
|
||||
phoneNumberInfo = wxMaService.getUserService().getPhoneNoInfo(reqVO.getPhoneCode());
|
||||
} catch (Exception exception) {
|
||||
throw exception(AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR);
|
||||
}
|
||||
SocialWxPhoneNumberInfoRespDTO phoneNumberInfo = socialClientApi.getWxMaPhoneNumberInfo(
|
||||
UserTypeEnum.MEMBER.getValue(), reqVO.getPhoneCode());
|
||||
Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空");
|
||||
|
||||
// 获得获得注册用户
|
||||
MemberUserDO user = userService.createUserIfAbsent(phoneNumberInfo.getPurePhoneNumber(), getClientIP(), TerminalEnum.WECHAT_MINI_PROGRAM.getTerminal());
|
||||
MemberUserDO user = userService.createUserIfAbsent(phoneNumberInfo.getPurePhoneNumber(),
|
||||
getClientIP(), TerminalEnum.WECHAT_MINI_PROGRAM.getTerminal());
|
||||
Assert.notNull(user, "获取用户失败,结果为空");
|
||||
|
||||
// 绑定社交用户
|
||||
@ -154,7 +151,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||
|
||||
@Override
|
||||
public String getSocialAuthorizeUrl(Integer type, String redirectUri) {
|
||||
return socialUserApi.getAuthorizeUrl(type, redirectUri);
|
||||
return socialClientApi.getAuthorizeUrl(type, UserTypeEnum.MEMBER.getValue(), redirectUri);
|
||||
}
|
||||
|
||||
private MemberUserDO login0(String mobile, String password) {
|
||||
|
@ -18,4 +18,5 @@ public interface PayTransferApi {
|
||||
* @return 转账单编号
|
||||
*/
|
||||
Long createTransfer(@Valid PayTransferCreateReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ public class PayTransferCreateReqDTO {
|
||||
* 类型
|
||||
*/
|
||||
@NotNull(message = "转账类型不能为空")
|
||||
// TODO @jason:枚举的校验
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
@ -34,9 +35,11 @@ public class PayTransferCreateReqDTO {
|
||||
/**
|
||||
* 转账金额,单位:分
|
||||
*/
|
||||
// TODO @jason:这个金额是不是非空哈
|
||||
@Min(value = 1, message = "转账金额必须大于零")
|
||||
private Integer price;
|
||||
|
||||
// TODO @jason:这个标题,是不是不允许空呀
|
||||
/**
|
||||
* 转账标题
|
||||
*/
|
||||
@ -44,4 +47,5 @@ public class PayTransferCreateReqDTO {
|
||||
|
||||
@NotEmpty(message = "收款方信息不能为空")
|
||||
private Map<String, String> payeeInfo;
|
||||
|
||||
}
|
||||
|
@ -27,8 +27,6 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PAY_ORDER_IS_EXPIRED = new ErrorCode(1_007_002_003, "支付订单已经过期");
|
||||
ErrorCode PAY_ORDER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1_007_002_004, "发起支付报错,错误码:{},错误提示:{}");
|
||||
ErrorCode PAY_ORDER_REFUND_FAIL_STATUS_ERROR = new ErrorCode(1_007_002_005, "支付订单退款失败,原因:状态不是已支付或已退款");
|
||||
ErrorCode ORDER_UPDATE_PRICE_FAIL_PAID = new ErrorCode(1_007_002_006, "支付订单调价失败,原因:支付订单已付款,不能调价");
|
||||
ErrorCode ORDER_UPDATE_PRICE_FAIL_EQUAL = new ErrorCode(1_007_002_007, "支付订单调价失败,原因:价格没有变化");
|
||||
|
||||
// ========== ORDER 模块(拓展单) 1-007-003-000 ==========
|
||||
ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1_007_003_000, "支付交易拓展单不存在");
|
||||
@ -64,6 +62,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode WALLET_RECHARGE_PACKAGE_AND_PRICE_IS_EMPTY = new ErrorCode(1_007_008_011, "充值金额和充钱套餐不能同时为空");
|
||||
ErrorCode WALLET_RECHARGE_PACKAGE_NOT_FOUND = new ErrorCode(1_007_008_012, "钱包充值套餐不存在");
|
||||
ErrorCode WALLET_RECHARGE_PACKAGE_IS_DISABLE = new ErrorCode(1_007_008_013, "钱包充值套餐已禁用");
|
||||
|
||||
// ========== 转账模块 1-007-009-000 ==========
|
||||
ErrorCode PAY_TRANSFER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1_007_009_000, "发起转账报错,错误码:{},错误提示:{}");
|
||||
ErrorCode PAY_TRANSFER_ALIPAY_LOGIN_ID_IS_EMPTY = new ErrorCode(1_007_009_001, "支付宝登录 ID 不能为空");
|
||||
|
@ -24,7 +24,13 @@ public enum PayTransferStatusEnum {
|
||||
*/
|
||||
CLOSED(30, "转账关闭");
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private final Integer status;
|
||||
/**
|
||||
* 状态名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
public static boolean isSuccess(Integer status) {
|
||||
@ -45,4 +51,5 @@ public enum PayTransferStatusEnum {
|
||||
public static boolean isPendingStatus(Integer status) {
|
||||
return Objects.equals(status, WAITING.getStatus()) || Objects.equals(status, IN_PROGRESS.status);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,14 +5,18 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* 转账单 API 实现类
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class PayTransferApiImpl implements PayTransferApi {
|
||||
|
||||
@Override
|
||||
public Long createTransfer(PayTransferCreateReqDTO reqDTO) {
|
||||
|
||||
// TODO @jason:貌似没实现噢
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -17,6 +17,7 @@ import java.util.Map;
|
||||
@Data
|
||||
public class PayDemoTransferCreateReqVO {
|
||||
|
||||
// TODO @jason:这个字段,是不是叫 type 就好了。
|
||||
@Schema(description = "转账类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "转账类型不能为空")
|
||||
@InEnum(PayTransferTypeEnum.class)
|
||||
@ -26,7 +27,9 @@ public class PayDemoTransferCreateReqVO {
|
||||
@Min(value = 1, message = "转账金额必须大于零")
|
||||
private Integer price;
|
||||
|
||||
// TODO @jason:感觉这个动态字段,晚点改;可能要讨论下怎么搞好;
|
||||
@Schema(description = "收款方信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "{'ALIPAY_LOGON_ID':'xxxx'}")
|
||||
@NotEmpty(message = "收款方信息不能为空")
|
||||
private Map<String, String> payeeInfo;
|
||||
|
||||
}
|
||||
|
@ -29,8 +29,10 @@ public class PayTransferController {
|
||||
|
||||
@PostMapping("/submit")
|
||||
@Operation(summary = "提交转账订单")
|
||||
// TODO @jason:权限的设置
|
||||
public CommonResult<PayTransferSubmitRespVO> submitPayTransfer(@Valid @RequestBody PayTransferSubmitReqVO reqVO) {
|
||||
PayTransferSubmitRespVO respVO = payTransferService.submitTransfer(reqVO, getClientIP());
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,4 +21,5 @@ public class PayTransferSubmitReqVO {
|
||||
|
||||
@Schema(description = "转账渠道的额外参数")
|
||||
private Map<String, String> channelExtras;
|
||||
|
||||
}
|
||||
|
@ -9,4 +9,5 @@ public class PayTransferSubmitRespVO {
|
||||
|
||||
@Schema(description = "转账状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 参见 PayTransferStatusEnum 枚举
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
|
@ -57,4 +57,5 @@ public class PayWalletRechargeController {
|
||||
Long.valueOf(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayRefundId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,9 +32,11 @@ public class PayWalletTransactionController {
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得钱包流水分页")
|
||||
// TODO @jason:权限校验,缺一个
|
||||
public CommonResult<PageResult<PayWalletTransactionRespVO>> getWalletTransactionPage(
|
||||
@Valid PayWalletTransactionPageReqVO pageReqVO) {
|
||||
PageResult<PayWalletTransactionDO> result = payWalletTransactionService.getWalletTransactionPage(pageReqVO);
|
||||
return success(PayWalletTransactionConvert.INSTANCE.convertPage2(result));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import lombok.Data;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 套餐充值 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 充值套餐 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
|
@ -5,7 +5,7 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 套餐充值创建 Request VO")
|
||||
@Schema(description = "管理后台 - 充值套餐创建 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
|
@ -11,7 +11,7 @@ import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 套餐充值分页 Request VO")
|
||||
@Schema(description = "管理后台 - 充值套餐分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@ -20,6 +20,8 @@ public class WalletRechargePackagePageReqVO extends PageParam {
|
||||
@Schema(description = "套餐名", example = "李四")
|
||||
private String name;
|
||||
|
||||
// TODO @jason:payPrice 和 bonusPrice 可以去掉。。。一般太少检索啦;
|
||||
|
||||
@Schema(description = "支付金额", example = "16454")
|
||||
private Integer payPrice;
|
||||
|
||||
|
@ -4,7 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 套餐充值 Response VO")
|
||||
@Schema(description = "管理后台 - 充值套餐 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
|
@ -7,7 +7,7 @@ import lombok.ToString;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 套餐充值更新 Request VO")
|
||||
@Schema(description = "管理后台 - 充值套餐更新 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
|
@ -10,4 +10,5 @@ public class PayWalletTransactionPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "钱包编号", example = "1")
|
||||
private Long walletId;
|
||||
|
||||
}
|
||||
|
@ -29,4 +29,7 @@ public class PayWalletTransactionRespVO {
|
||||
|
||||
@Schema(description = "交易时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
// TODO @jason:merchantOrderId 字段,需要在 PayWalletTransaction 存储下;然后,前端也返回下这个字段,界面也展示下商户名
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.wallet;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import lombok.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
@ -18,6 +20,7 @@ public class PayWalletPageReqVO extends PageParam {
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户类型", example = "1")
|
||||
@InEnum(UserTypeEnum.class)
|
||||
private Integer userType;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
|
@ -19,4 +19,6 @@ public class PayWalletRespVO extends PayWalletBaseVO {
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
// TODO @jason:要不把用户昵称 + avatar 也读取下?
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,9 @@ public class AppPayWalletRechargeCreateReqVO {
|
||||
@Min(value = 1, message = "支付金额必须大于零")
|
||||
private Integer payPrice;
|
||||
|
||||
|
||||
@Schema(description = "充值套餐编号", example = "1024")
|
||||
private Long packageId;
|
||||
|
||||
// TODO @jaosn:写个 AssertTrue 的校验方法,payPrice 和 packageId 必须二选一
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ public interface PayWalletConvert {
|
||||
|
||||
AppPayWalletRespVO convert(PayWalletDO bean);
|
||||
|
||||
PayWalletRespVO convert02(PayWalletDO wallet);
|
||||
PayWalletRespVO convert02(PayWalletDO bean);
|
||||
|
||||
PageResult<PayWalletRespVO> convertPage(PageResult<PayWalletDO> page);
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import java.util.Map;
|
||||
@KeySequence("pay_demo_transfer_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
public class PayDemoTransferDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
@ -41,6 +42,7 @@ public class PayDemoTransferDO extends BaseDO {
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
// TODO @jason:要不字段还是弄成正确的平铺开?
|
||||
/**
|
||||
* 收款人信息,不同类型和渠道不同
|
||||
*/
|
||||
@ -66,4 +68,5 @@ public class PayDemoTransferDO extends BaseDO {
|
||||
* 转账支付时间
|
||||
*/
|
||||
private LocalDateTime transferTime;
|
||||
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
package cn.iocoder.yudao.module.pay.dal.dataobject.transfer;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@ -26,70 +31,76 @@ public class PayTransferDO extends BaseDO {
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 应用编号
|
||||
*
|
||||
* 关联 {@link PayAppDO#getId()}
|
||||
*/
|
||||
private Long appId;
|
||||
/**
|
||||
* 转账渠道编号
|
||||
*
|
||||
* 关联 {@link PayChannelDO#getId()}
|
||||
*/
|
||||
private Long channelId;
|
||||
/**
|
||||
* 转账渠道编码
|
||||
*
|
||||
* 枚举 {@link PayChannelEnum}
|
||||
*/
|
||||
private String channelCode;
|
||||
/**
|
||||
* 类型
|
||||
*
|
||||
* 枚举 {@link PayTransferTypeEnum}
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 应用编号
|
||||
*/
|
||||
private Long appId;
|
||||
// ========== 商户相关字段 ==========
|
||||
|
||||
/**
|
||||
* 商户订单编号
|
||||
*
|
||||
* 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一
|
||||
*/
|
||||
private String merchantOrderId;
|
||||
|
||||
/**
|
||||
* 转账金额,单位:分
|
||||
*/
|
||||
private Integer price;
|
||||
|
||||
// TODO @jason:这个字段,要不要改成 subject。。。和 payorderdo 保持一致;哈哈哈,我也忘记为啥当时选了这个名字了。。。
|
||||
/**
|
||||
* 转账标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
// ========== 转账相关字段 ==========
|
||||
/**
|
||||
* 收款人信息,不同类型和渠道不同
|
||||
* 转账金额,单位:分
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private Map<String, String> payeeInfo;
|
||||
|
||||
private Integer price;
|
||||
/**
|
||||
* 转账状态
|
||||
*
|
||||
* 枚举 {@link PayTransferStatusRespEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 订单转账成功时间
|
||||
*
|
||||
*/
|
||||
private LocalDateTime successTime;
|
||||
|
||||
/**
|
||||
* 转账成功的转账拓展单编号
|
||||
*
|
||||
* 关联 {@link PayTransferExtensionDO#getId()}
|
||||
*/
|
||||
private Long extensionId;
|
||||
|
||||
/**
|
||||
* 转账成功的转账拓展单号
|
||||
*
|
||||
* 关联 {@link PayTransferExtensionDO#getNo()}
|
||||
*/
|
||||
private String no;
|
||||
|
||||
/**
|
||||
* 转账渠道编号
|
||||
* 收款人信息,不同类型和渠道不同
|
||||
*/
|
||||
private Long channelId;
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private Map<String, String> payeeInfo;
|
||||
|
||||
/**
|
||||
* 转账渠道编码
|
||||
*/
|
||||
private String channelCode;
|
||||
}
|
@ -10,6 +10,7 @@ import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
// TODO @jason:转账是不是类似 refund,不用拓展单呀?支付做拓展单的原因,是因为它存在不确定性,可以切换多种;转账和退款,都是明确方式的;
|
||||
/**
|
||||
* 转账拓展单 DO
|
||||
*
|
||||
@ -61,4 +62,5 @@ public class PayTransferExtensionDO extends BaseDO {
|
||||
* 支付渠道异步通知的内容
|
||||
*/
|
||||
private String channelNotifyData;
|
||||
|
||||
}
|
@ -42,7 +42,6 @@ public class PayWalletRechargeDO extends BaseDO {
|
||||
* 实际支付金额
|
||||
*/
|
||||
private Integer payPrice;
|
||||
|
||||
/**
|
||||
* 钱包赠送金额
|
||||
*/
|
||||
@ -50,8 +49,10 @@ public class PayWalletRechargeDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 充值套餐编号
|
||||
*
|
||||
* 关联 {@link PayWalletRechargeDO#getPackageId()} 字段
|
||||
*/
|
||||
private Long packageId;
|
||||
private Long packageId;
|
||||
|
||||
/**
|
||||
* 是否已支付
|
||||
|
@ -9,7 +9,7 @@ import lombok.Data;
|
||||
/**
|
||||
* 会员钱包充值套餐 DO
|
||||
*
|
||||
* 通过套餐充值时,可以赠送一定金额;
|
||||
* 通过充值套餐时,可以赠送一定金额;
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
|
@ -7,8 +7,4 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper
|
||||
public interface PayDemoTransferMapper extends BaseMapperX<PayDemoTransferDO> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -14,10 +14,11 @@ public interface PayTransferExtensionMapper extends BaseMapperX<PayTransferExten
|
||||
return selectOne(PayTransferExtensionDO::getNo, no);
|
||||
}
|
||||
|
||||
default int updateByIdAndStatus(Long id, List<Integer> status, PayTransferExtensionDO updateObj){
|
||||
default int updateByIdAndStatus(Long id, List<Integer> whereStatuses, PayTransferExtensionDO updateObj) {
|
||||
return update(updateObj, new LambdaQueryWrapper<PayTransferExtensionDO>()
|
||||
.eq(PayTransferExtensionDO::getId, id).in(PayTransferExtensionDO::getStatus, status));
|
||||
.eq(PayTransferExtensionDO::getId, id).in(PayTransferExtensionDO::getStatus, whereStatuses));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -14,6 +14,7 @@ public interface PayTransferMapper extends BaseMapperX<PayTransferDO> {
|
||||
return update(updateObj, new LambdaQueryWrapper<PayTransferDO>()
|
||||
.eq(PayTransferDO::getId, id).in(PayTransferDO::getStatus, status));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -70,6 +70,7 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
|
||||
|
||||
/**
|
||||
* 冻结钱包部分余额
|
||||
*
|
||||
* @param id 钱包 id
|
||||
* @param price 冻结金额
|
||||
*/
|
||||
@ -84,6 +85,7 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
|
||||
|
||||
/**
|
||||
* 解冻钱包余额
|
||||
*
|
||||
* @param id 钱包 id
|
||||
* @param price 解冻金额
|
||||
*/
|
||||
@ -98,6 +100,7 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
|
||||
|
||||
/**
|
||||
* 当充值退款时, 更新钱包
|
||||
*
|
||||
* @param id 钱包 id
|
||||
* @param price 退款金额
|
||||
*/
|
||||
|
@ -18,8 +18,4 @@ public interface PayWalletRechargeMapper extends BaseMapperX<PayWalletRechargeDO
|
||||
.eq(PayWalletRechargeDO::getId, id).eq(PayWalletRechargeDO::getRefundStatus, whereRefundStatus));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -12,6 +12,7 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
public interface PayWalletRechargePackageMapper extends BaseMapperX<PayWalletRechargePackageDO> {
|
||||
|
||||
default PageResult<PayWalletRechargePackageDO> selectPage(WalletRechargePackagePageReqVO reqVO) {
|
||||
// TODO @jason:排序按照充值金额
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<PayWalletRechargePackageDO>()
|
||||
.likeIfPresent(PayWalletRechargePackageDO::getName, reqVO.getName())
|
||||
.eqIfPresent(PayWalletRechargePackageDO::getPayPrice, reqVO.getPayPrice())
|
||||
@ -20,8 +21,5 @@ public interface PayWalletRechargePackageMapper extends BaseMapperX<PayWalletRec
|
||||
.betweenIfPresent(PayWalletRechargePackageDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(PayWalletRechargePackageDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -180,4 +180,5 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
|
||||
public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService {
|
||||
return demoTransfer.getId();
|
||||
}
|
||||
|
||||
// TODO @jason:可以参考 AppBrokerageWithdrawCreateReqVO 搞下字段哈,进行校验
|
||||
private void validatePayeeInfo(Integer transferType, Map<String, String> payeeInfo) {
|
||||
PayTransferTypeEnum transferTypeEnum = ofType(transferType);
|
||||
switch (transferTypeEnum) {
|
||||
@ -83,4 +84,5 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferSubmi
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 转账 Service 接口
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
public interface PayTransferService {
|
||||
@ -29,4 +31,5 @@ public interface PayTransferService {
|
||||
* @return 转账单编号
|
||||
*/
|
||||
Long createTransfer(@Valid PayTransferCreateReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
@ -34,7 +34,10 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
|
||||
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum.*;
|
||||
|
||||
// TODO @jason:等彻底实现完,单测写写;
|
||||
/**
|
||||
* 转账 Service 实现类
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Service
|
||||
@ -47,10 +50,12 @@ public class PayTransferServiceImpl implements PayTransferService {
|
||||
private PayTransferMapper transferMapper;
|
||||
@Resource
|
||||
private PayTransferExtensionMapper transferExtensionMapper;
|
||||
|
||||
@Resource
|
||||
private PayAppService appService;
|
||||
@Resource
|
||||
private PayChannelService channelService;
|
||||
|
||||
@Resource
|
||||
private PayNoRedisDAO noRedisDAO;
|
||||
|
||||
@ -64,7 +69,7 @@ public class PayTransferServiceImpl implements PayTransferService {
|
||||
PayChannelDO channel = validateChannelCanSubmit(transfer.getAppId(), reqVO.getChannelCode());
|
||||
PayClient client = channelService.getPayClient(channel.getId());
|
||||
|
||||
// 2 新增转账拓展单
|
||||
// 2. 新增转账拓展单
|
||||
String no = noRedisDAO.generate(TRANSFER_NO_PREFIX);
|
||||
PayTransferExtensionDO transferExtension = new PayTransferExtensionDO().setNo(no)
|
||||
.setTransferId(transfer.getId()).setChannelId(channel.getId())
|
||||
@ -112,8 +117,9 @@ public class PayTransferServiceImpl implements PayTransferService {
|
||||
notifyTransferClosed(channel, notify);
|
||||
}
|
||||
// WAITING 状态无需处理
|
||||
// TODO IN_PROGRESS 待处理
|
||||
// TODO IN_PROGRESS 待处理
|
||||
}
|
||||
|
||||
private void notifyTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) {
|
||||
// 1. 更新 PayTransferExtensionDO 转账成功
|
||||
PayTransferExtensionDO transferExtension = updateTransferExtensionSuccess(notify);
|
||||
@ -121,7 +127,7 @@ public class PayTransferServiceImpl implements PayTransferService {
|
||||
// 2. 更新 PayTransferDO 转账成功
|
||||
Boolean transferred = updateTransferSuccess(channel,transferExtension, notify);
|
||||
if (transferred) {
|
||||
return ;
|
||||
return;
|
||||
}
|
||||
// 3. TODO 插入转账通知记录
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ public interface PayWalletRechargePackageService {
|
||||
PayWalletRechargePackageDO validWalletRechargePackage(Long packageId);
|
||||
|
||||
/**
|
||||
* 创建套餐充值
|
||||
* 创建充值套餐
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
@ -39,33 +39,25 @@ public interface PayWalletRechargePackageService {
|
||||
Long createWalletRechargePackage(@Valid WalletRechargePackageCreateReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新套餐充值
|
||||
* 更新充值套餐
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateWalletRechargePackage(@Valid WalletRechargePackageUpdateReqVO updateReqVO);
|
||||
|
||||
|
||||
/**
|
||||
* 删除套餐充值
|
||||
* 删除充值套餐
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteWalletRechargePackage(Long id);
|
||||
|
||||
/**
|
||||
* 获得套餐充值列表
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 套餐充值列表
|
||||
*/
|
||||
List<PayWalletRechargePackageDO> getWalletRechargePackageList(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得套餐充值分页
|
||||
* 获得充值套餐分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 套餐充值分页
|
||||
* @return 充值套餐分页
|
||||
*/
|
||||
PageResult<PayWalletRechargePackageDO> getWalletRechargePackagePage(WalletRechargePackagePageReqVO pageReqVO);
|
||||
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ public class PayWalletRechargePackageServiceImpl implements PayWalletRechargePac
|
||||
return walletRechargePackage.getId();
|
||||
}
|
||||
|
||||
// TODO @jason:校验下,套餐名唯一
|
||||
@Override
|
||||
public void updateWalletRechargePackage(WalletRechargePackageUpdateReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
@ -80,14 +81,6 @@ public class PayWalletRechargePackageServiceImpl implements PayWalletRechargePac
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PayWalletRechargePackageDO> getWalletRechargePackageList(Collection<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return ListUtil.empty();
|
||||
}
|
||||
return walletRechargePackageMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<PayWalletRechargePackageDO> getWalletRechargePackagePage(WalletRechargePackagePageReqVO pageReqVO) {
|
||||
return walletRechargePackageMapper.selectPage(pageReqVO);
|
||||
|
@ -44,7 +44,7 @@ import static cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum.*;
|
||||
public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
|
||||
/**
|
||||
* TODO 放到 配置文件中
|
||||
* TODO 芋艿:放到 payconfig
|
||||
*/
|
||||
private static final Long WALLET_PAY_APP_ID = 8L;
|
||||
|
||||
@ -65,13 +65,14 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public PayWalletRechargeDO createWalletRecharge(Long userId, Integer userType,
|
||||
AppPayWalletRechargeCreateReqVO reqVO) {
|
||||
// 1.1 校验参数
|
||||
// 1.1 校验参数 TODO @jason:AppPayWalletRechargeCreateReqVO 看下校验;
|
||||
if (Objects.isNull(reqVO.getPayPrice()) && Objects.isNull(reqVO.getPackageId())) {
|
||||
throw exception(WALLET_RECHARGE_PACKAGE_AND_PRICE_IS_EMPTY);
|
||||
}
|
||||
// 1.2 新增钱包充值记录
|
||||
int payPrice ;
|
||||
int bonusPrice = 0 ;
|
||||
|
||||
// 1.1 计算充值金额
|
||||
int payPrice;
|
||||
int bonusPrice = 0;
|
||||
if (Objects.nonNull(reqVO.getPackageId())) {
|
||||
PayWalletRechargePackageDO rechargePackage = payWalletRechargePackageService.validWalletRechargePackage(reqVO.getPackageId());
|
||||
payPrice = rechargePackage.getPayPrice();
|
||||
@ -79,23 +80,22 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
} else {
|
||||
payPrice = reqVO.getPayPrice();
|
||||
}
|
||||
|
||||
// 1.2 插入充值记录
|
||||
PayWalletDO wallet = payWalletService.getOrCreateWallet(userId, userType);
|
||||
PayWalletRechargeDO walletRecharge = INSTANCE.convert(wallet.getId(), payPrice, bonusPrice, reqVO.getPackageId());
|
||||
walletRechargeMapper.insert(walletRecharge);
|
||||
PayWalletRechargeDO recharge = INSTANCE.convert(wallet.getId(), payPrice, bonusPrice, reqVO.getPackageId());
|
||||
walletRechargeMapper.insert(recharge);
|
||||
|
||||
// 2.1 创建支付单
|
||||
Long payOrderId = payOrderService.createOrder(new PayOrderCreateReqDTO()
|
||||
.setAppId(WALLET_PAY_APP_ID).setUserIp(getClientIP())
|
||||
.setMerchantOrderId(walletRecharge.getId().toString()) // 业务的订单编号
|
||||
.setSubject(WALLET_RECHARGE_ORDER_SUBJECT).setBody("").setPrice(walletRecharge.getPayPrice())
|
||||
.setExpireTime(addTime(Duration.ofHours(2L))));
|
||||
.setAppId(WALLET_PAY_APP_ID).setUserIp(getClientIP()) // TODO @jason:clientIp 从 controller 传递进来噢
|
||||
.setMerchantOrderId(recharge.getId().toString()) // 业务的订单编号
|
||||
.setSubject(WALLET_RECHARGE_ORDER_SUBJECT).setBody("")
|
||||
.setPrice(recharge.getPayPrice())
|
||||
.setExpireTime(addTime(Duration.ofHours(2L)))); // TODO @芋艿:支付超时时间
|
||||
// 2.2 更新钱包充值记录中支付订单
|
||||
walletRechargeMapper.updateById(new PayWalletRechargeDO().setPayOrderId(payOrderId)
|
||||
.setId(walletRecharge.getId()));
|
||||
|
||||
walletRecharge.setPayOrderId(payOrderId);
|
||||
return walletRecharge;
|
||||
walletRechargeMapper.updateById(new PayWalletRechargeDO().setId(recharge.getId()).setPayOrderId(payOrderId));
|
||||
recharge.setPayOrderId(payOrderId);
|
||||
return recharge;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -136,9 +136,11 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
}
|
||||
// 1.2 校验钱包充值是否可以发起退款
|
||||
PayWalletDO wallet = validateWalletRechargeCanRefund(walletRecharge);
|
||||
// 2 冻结退款的余额, 暂时只处理赠送的余额也全部退回
|
||||
|
||||
// 2. 冻结退款的余额,暂时只处理赠送的余额也全部退回
|
||||
payWalletService.freezePrice(wallet.getId(), walletRecharge.getTotalPrice());
|
||||
// 3 创建退款单
|
||||
|
||||
// 3. 创建退款单
|
||||
String walletRechargeId = String.valueOf(id);
|
||||
String refundId = walletRechargeId + "-refund";
|
||||
Long payRefundId = payRefundService.createPayRefund(new PayRefundCreateReqDTO()
|
||||
@ -146,7 +148,9 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
.setMerchantOrderId(walletRechargeId)
|
||||
.setMerchantRefundId(refundId)
|
||||
.setReason("想退钱").setPrice(walletRecharge.getPayPrice()));
|
||||
// 4 更新充值记录退款单号
|
||||
|
||||
// 4. 更新充值记录退款单号
|
||||
// TODO @jaosn:一般新建这种 update 对象,建议是,第一个 set id 属性,容易知道以它为更新
|
||||
walletRechargeMapper.updateById(new PayWalletRechargeDO().setPayRefundId(payRefundId)
|
||||
.setRefundStatus(WAITING.getStatus()).setId(walletRecharge.getId()));
|
||||
}
|
||||
@ -229,6 +233,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
if (wallet.getBalance() < walletRecharge.getTotalPrice()) {
|
||||
throw exception(WALLET_RECHARGE_REFUND_BALANCE_NOT_ENOUGH);
|
||||
}
|
||||
// TODO @芋艿:需要考虑下,赠送的金额,会不会导致提现超过;
|
||||
return wallet;
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,7 @@ public interface PayWalletService {
|
||||
*/
|
||||
void freezePrice(Long id, Integer price);
|
||||
|
||||
// TODO @jason:unfreeze 是单词哈,f 不用大写
|
||||
/**
|
||||
* 解冻钱包余额
|
||||
*
|
||||
|
@ -109,6 +109,7 @@ public class PayWalletServiceImpl implements PayWalletService {
|
||||
if (walletTransaction == null) {
|
||||
throw exception(WALLET_TRANSACTION_NOT_FOUND);
|
||||
}
|
||||
// 2. 校验退款是否存在
|
||||
PayWalletTransactionDO refundTransaction = walletTransactionService.getWalletTransaction(
|
||||
String.valueOf(refundId), PAYMENT_REFUND);
|
||||
if (refundTransaction != null) {
|
||||
@ -128,7 +129,7 @@ public class PayWalletServiceImpl implements PayWalletService {
|
||||
}
|
||||
|
||||
// 2.1 扣除余额
|
||||
int updateCounts = 0 ;
|
||||
int updateCounts;
|
||||
switch (bizType) {
|
||||
case PAYMENT: {
|
||||
updateCounts = walletMapper.updateWhenConsumption(payWallet.getId(), price);
|
||||
|
@ -0,0 +1,42 @@
|
||||
package cn.iocoder.yudao.module.system.api.social;
|
||||
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
|
||||
/**
|
||||
* 社交应用的 API 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface SocialClientApi {
|
||||
|
||||
/**
|
||||
* 获得社交平台的授权 URL
|
||||
*
|
||||
* @param type 社交平台的类型 {@link SocialTypeEnum}
|
||||
* @param userType 用户类型
|
||||
* @param redirectUri 重定向 URL
|
||||
* @return 社交平台的授权 URL
|
||||
*/
|
||||
String getAuthorizeUrl(Integer type, Integer userType, String redirectUri);
|
||||
|
||||
/**
|
||||
* 创建微信公众号 JS SDK 初始化所需的签名
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @param url 访问的 URL 地址
|
||||
* @return 签名
|
||||
*/
|
||||
SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url);
|
||||
|
||||
/**
|
||||
* 获得微信小程序的手机信息
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @param phoneCode 手机授权码
|
||||
* @return 手机信息
|
||||
*/
|
||||
SocialWxPhoneNumberInfoRespDTO getWxMaPhoneNumberInfo(Integer userType, String phoneCode);
|
||||
|
||||
}
|
@ -4,7 +4,6 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
@ -15,15 +14,6 @@ import javax.validation.Valid;
|
||||
*/
|
||||
public interface SocialUserApi {
|
||||
|
||||
/**
|
||||
* 获得社交平台的授权 URL
|
||||
*
|
||||
* @param type 社交平台的类型 {@link SocialTypeEnum}
|
||||
* @param redirectUri 重定向 URL
|
||||
* @return 社交平台的授权 URL
|
||||
*/
|
||||
String getAuthorizeUrl(Integer type, String redirectUri);
|
||||
|
||||
/**
|
||||
* 绑定社交用户
|
||||
*
|
||||
|
@ -37,7 +37,7 @@ public class SocialUserBindReqDTO {
|
||||
*/
|
||||
@InEnum(SocialTypeEnum.class)
|
||||
@NotNull(message = "社交平台的类型不能为空")
|
||||
private Integer type;
|
||||
private Integer socialType;
|
||||
/**
|
||||
* 授权码
|
||||
*/
|
||||
|
@ -33,12 +33,12 @@ public class SocialUserUnbindReqDTO {
|
||||
*/
|
||||
@InEnum(SocialTypeEnum.class)
|
||||
@NotNull(message = "社交平台的类型不能为空")
|
||||
private Integer type;
|
||||
private Integer socialType;
|
||||
|
||||
/**
|
||||
* 社交平台的 unionId
|
||||
* 社交平台的 openid
|
||||
*/
|
||||
@NotEmpty(message = "社交平台的 unionId 不能为空")
|
||||
private String unionId;
|
||||
@NotEmpty(message = "社交平台的 openid 不能为空")
|
||||
private String openid;
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package cn.iocoder.yudao.module.system.api.social.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 微信公众号 JSAPI 签名 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class SocialWxJsapiSignatureRespDTO {
|
||||
|
||||
/**
|
||||
* 微信公众号的 appId
|
||||
*/
|
||||
private String appId;
|
||||
/**
|
||||
* 匿名串
|
||||
*/
|
||||
private String nonceStr;
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
/**
|
||||
* URL
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 签名
|
||||
*/
|
||||
private String signature;
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.system.api.social.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 微信小程序的手机信息 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class SocialWxPhoneNumberInfoRespDTO {
|
||||
|
||||
/**
|
||||
* 用户绑定的手机号(国外手机号会有区号)
|
||||
*/
|
||||
private String phoneNumber;
|
||||
|
||||
/**
|
||||
* 没有区号的手机号
|
||||
*/
|
||||
private String purePhoneNumber;
|
||||
/**
|
||||
* 区号
|
||||
*/
|
||||
private String countryCode;
|
||||
|
||||
}
|
@ -119,6 +119,8 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode SOCIAL_USER_UNBIND_NOT_SELF = new ErrorCode(1_002_018_001, "社交解绑失败,非当前用户绑定");
|
||||
ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1_002_018_002, "社交授权失败,找不到对应的用户");
|
||||
|
||||
ErrorCode SOCIAL_APP_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1_002_018_103, "获得手机号失败");
|
||||
|
||||
// ========== 系统敏感词 1-002-019-000 =========
|
||||
ErrorCode SENSITIVE_WORD_NOT_EXISTS = new ErrorCode(1_002_019_000, "系统敏感词在所有标签中都不存在");
|
||||
ErrorCode SENSITIVE_WORD_EXISTS = new ErrorCode(1_002_019_001, "系统敏感词已在标签中存在");
|
||||
|
@ -18,33 +18,39 @@ public enum SocialTypeEnum implements IntArrayValuable {
|
||||
|
||||
/**
|
||||
* Gitee
|
||||
* 文档链接:https://gitee.com/api/v5/oauth_doc#/
|
||||
*
|
||||
* @see <a href="https://gitee.com/api/v5/oauth_doc#/">接入文档</a>
|
||||
*/
|
||||
GITEE(10, "GITEE"),
|
||||
/**
|
||||
* 钉钉
|
||||
* 文档链接:https://developers.dingtalk.com/document/app/obtain-identity-credentials
|
||||
*
|
||||
* @see <a href="https://developers.dingtalk.com/document/app/obtain-identity-credentials">接入文档</a>
|
||||
*/
|
||||
DINGTALK(20, "DINGTALK"),
|
||||
|
||||
/**
|
||||
* 企业微信
|
||||
* 文档链接:https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html
|
||||
*
|
||||
* @see <a href="https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html">接入文档</a>
|
||||
*/
|
||||
WECHAT_ENTERPRISE(30, "WECHAT_ENTERPRISE"),
|
||||
/**
|
||||
* 微信公众平台 - 移动端 H5
|
||||
* 文档链接:https://www.cnblogs.com/juewuzhe/p/11905461.html
|
||||
*
|
||||
* @see <a href="https://www.cnblogs.com/juewuzhe/p/11905461.html">接入文档</a>
|
||||
*/
|
||||
WECHAT_MP(31, "WECHAT_MP"),
|
||||
/**
|
||||
* 微信开放平台 - 网站应用 PC 端扫码授权登录
|
||||
* 文档链接:https://justauth.wiki/guide/oauth/wechat_open/#_2-申请开发者资质认证
|
||||
*
|
||||
* @see <a href="https://justauth.wiki/guide/oauth/wechat_open/#_2-申请开发者资质认证">接入文档</a>
|
||||
*/
|
||||
WECHAT_OPEN(32, "WECHAT_OPEN"),
|
||||
/**
|
||||
* 微信小程序
|
||||
* 文档链接:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
|
||||
*
|
||||
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html">接入文档</a>
|
||||
*/
|
||||
WECHAT_MINI_APP(34, "WECHAT_MINI_APP"),
|
||||
;
|
||||
|
@ -0,0 +1,43 @@
|
||||
package cn.iocoder.yudao.module.system.api.social;
|
||||
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
||||
import cn.iocoder.yudao.module.system.convert.social.SocialClientConvert;
|
||||
import cn.iocoder.yudao.module.system.service.social.SocialClientService;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 社交应用的 API 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class SocialClientApiImpl implements SocialClientApi {
|
||||
|
||||
@Resource
|
||||
private SocialClientService socialClientService;
|
||||
|
||||
@Override
|
||||
public String getAuthorizeUrl(Integer type, Integer userType, String redirectUri) {
|
||||
return socialClientService.getAuthorizeUrl(type, userType, redirectUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url) {
|
||||
WxJsapiSignature signature = socialClientService.createWxMpJsapiSignature(userType, url);
|
||||
return SocialClientConvert.INSTANCE.convert(signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocialWxPhoneNumberInfoRespDTO getWxMaPhoneNumberInfo(Integer userType, String phoneCode) {
|
||||
WxMaPhoneNumberInfo info = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode);
|
||||
return SocialClientConvert.INSTANCE.convert(info);
|
||||
}
|
||||
|
||||
}
|
@ -21,11 +21,6 @@ public class SocialUserApiImpl implements SocialUserApi {
|
||||
@Resource
|
||||
private SocialUserService socialUserService;
|
||||
|
||||
@Override
|
||||
public String getAuthorizeUrl(Integer type, String redirectUri) {
|
||||
return socialUserService.getAuthorizeUrl(type, redirectUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
|
||||
return socialUserService.bindSocialUser(reqDTO);
|
||||
@ -34,7 +29,7 @@ public class SocialUserApiImpl implements SocialUserApi {
|
||||
@Override
|
||||
public void unbindSocialUser(SocialUserUnbindReqDTO reqDTO) {
|
||||
socialUserService.unbindSocialUser(reqDTO.getUserId(), reqDTO.getUserType(),
|
||||
reqDTO.getType(), reqDTO.getUnionId());
|
||||
reqDTO.getSocialType(), reqDTO.getOpenid());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.controller.admin.auth;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||
@ -16,7 +17,7 @@ import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.MenuService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.RoleService;
|
||||
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
|
||||
import cn.iocoder.yudao.module.system.service.social.SocialClientService;
|
||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@ -57,7 +58,7 @@ public class AuthController {
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
@Resource
|
||||
private SocialUserService socialUserService;
|
||||
private SocialClientService socialClientService;
|
||||
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
@ -147,7 +148,7 @@ public class AuthController {
|
||||
})
|
||||
public CommonResult<String> socialLogin(@RequestParam("type") Integer type,
|
||||
@RequestParam("redirectUri") String redirectUri) {
|
||||
return CommonResult.success(socialUserService.getAuthorizeUrl(type, redirectUri));
|
||||
return success(socialClientService.getAuthorizeUrl(type, UserTypeEnum.ADMIN.getValue(), redirectUri));
|
||||
}
|
||||
|
||||
@PostMapping("/social-login")
|
||||
|
@ -1,4 +0,0 @@
|
||||
/**
|
||||
* 占位,避免 package 无法提交到 Git 仓库
|
||||
*/
|
||||
package cn.iocoder.yudao.module.system.controller.app;
|
@ -1,4 +0,0 @@
|
||||
### 请求 /login 接口 => 成功
|
||||
POST {{appApi}}/system/wx-mp/create-jsapi-signature?url=http://www.iocoder.cn
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
@ -1,38 +0,0 @@
|
||||
package cn.iocoder.yudao.module.system.controller.app.weixin;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.mp.api.WxMpService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "微信公众号")
|
||||
@RestController
|
||||
@RequestMapping("/system/wx-mp")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class AppWxMpController {
|
||||
|
||||
@Resource
|
||||
private WxMpService mpService;
|
||||
|
||||
// TODO @芋艿:需要额外考虑个问题;多租户下,如果每个小程序一个微信公众号,则会存在多个 appid;
|
||||
@PostMapping("/create-jsapi-signature")
|
||||
@Operation(summary = "创建微信 JS SDK 初始化所需的签名",
|
||||
description = "参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档")
|
||||
public CommonResult<WxJsapiSignature> createJsapiSignature(@RequestParam("url") String url) throws WxErrorException {
|
||||
return success(mpService.createJsapiSignature(url));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.system.convert.social;
|
||||
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface SocialClientConvert {
|
||||
|
||||
SocialClientConvert INSTANCE = Mappers.getMapper(SocialClientConvert.class);
|
||||
|
||||
SocialWxJsapiSignatureRespDTO convert(WxJsapiSignature bean);
|
||||
|
||||
SocialWxPhoneNumberInfoRespDTO convert(WxMaPhoneNumberInfo bean);
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package cn.iocoder.yudao.module.system.dal.dataobject.social;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.xingyuv.jushauth.config.AuthConfig;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 社交客户端 DO
|
||||
*
|
||||
* 对应 {@link AuthConfig} 配置,满足不同租户,有自己的客户端配置,实现社交(三方)登录
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName(value = "system_social_client", autoResultMap = true)
|
||||
@KeySequence("system_social_client_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SocialClientDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号,自增
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 应用名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 社交类型
|
||||
*
|
||||
* 枚举 {@link SocialTypeEnum}
|
||||
*/
|
||||
private Integer socialType;
|
||||
/**
|
||||
* 用户类型
|
||||
*
|
||||
* 目的:不同用户类型,对应不同的小程序,需要自己的配置
|
||||
*
|
||||
* 枚举 {@link UserTypeEnum}
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 状态
|
||||
*
|
||||
* 枚举 {@link CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 客户端 id
|
||||
*/
|
||||
private String clientId;
|
||||
/**
|
||||
* 客户端 Secret
|
||||
*/
|
||||
private String clientSecret;
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package cn.iocoder.yudao.module.system.dal.mysql.social;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface SocialClientMapper extends BaseMapperX<SocialClientDO> {
|
||||
|
||||
default SocialClientDO selectBySocialTypeAndUserType(Integer socialType, Integer userType) {
|
||||
return selectOne(SocialClientDO::getSocialType, socialType,
|
||||
SocialClientDO::getUserType, userType);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package cn.iocoder.yudao.module.system.service.social;
|
||||
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
import com.xingyuv.jushauth.model.AuthUser;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
|
||||
/**
|
||||
* 社交应用 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface SocialClientService {
|
||||
|
||||
/**
|
||||
* 获得社交平台的授权 URL
|
||||
*
|
||||
* @param socialType 社交平台的类型 {@link SocialTypeEnum}
|
||||
* @param userType 用户类型
|
||||
* @param redirectUri 重定向 URL
|
||||
* @return 社交平台的授权 URL
|
||||
*/
|
||||
String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri);
|
||||
|
||||
/**
|
||||
* 请求社交平台,获得授权的用户
|
||||
*
|
||||
* @param socialType 社交平台的类型
|
||||
* @param userType 用户类型
|
||||
* @param code 授权码
|
||||
* @param state 授权 state
|
||||
* @return 授权的用户
|
||||
*/
|
||||
AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state);
|
||||
|
||||
// =================== 微信公众号独有 ===================
|
||||
|
||||
/**
|
||||
* 创建微信公众号的 JS SDK 初始化所需的签名
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @param url 访问的 URL 地址
|
||||
* @return 签名
|
||||
*/
|
||||
WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url);
|
||||
|
||||
// =================== 微信小程序独有 ===================
|
||||
|
||||
/**
|
||||
* 获得微信小程序的手机信息
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @param phoneCode 手机授权码
|
||||
* @return 手机信息
|
||||
*/
|
||||
WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode);
|
||||
|
||||
}
|
@ -0,0 +1,258 @@
|
||||
package cn.iocoder.yudao.module.system.service.social;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
|
||||
import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
|
||||
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.xingyuv.jushauth.config.AuthConfig;
|
||||
import com.xingyuv.jushauth.model.AuthCallback;
|
||||
import com.xingyuv.jushauth.model.AuthResponse;
|
||||
import com.xingyuv.jushauth.model.AuthUser;
|
||||
import com.xingyuv.jushauth.request.AuthRequest;
|
||||
import com.xingyuv.jushauth.utils.AuthStateUtils;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
|
||||
import me.chanjar.weixin.mp.api.WxMpService;
|
||||
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
|
||||
import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_APP_WEIXIN_MINI_APP_PHONE_CODE_ERROR;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_AUTH_FAILURE;
|
||||
|
||||
/**
|
||||
* 社交应用 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class SocialClientServiceImpl implements SocialClientService {
|
||||
|
||||
@Resource // 由于自定义了 YudaoAuthRequestFactory 无法覆盖默认的 AuthRequestFactory,所以只能注入它
|
||||
private YudaoAuthRequestFactory yudaoAuthRequestFactory;
|
||||
|
||||
@Resource
|
||||
private WxMpService wxMpService;
|
||||
@Resource
|
||||
private WxMpProperties wxMpProperties;
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate; // WxMpService 需要使用到,所以在 Service 注入了它
|
||||
/**
|
||||
* 缓存 WxMpService 对象
|
||||
*
|
||||
* key:使用微信公众号的 appId + secret 拼接,即 {@link SocialClientDO} 的 clientId 和 clientSecret 属性。
|
||||
* 为什么 key 使用这种格式?因为 {@link SocialClientDO} 在管理后台可以变更,通过这个 key 存储它的单例。
|
||||
*
|
||||
* 为什么要做 WxMpService 缓存?因为 WxMpService 构建成本比较大,所以尽量保证它是单例。
|
||||
*/
|
||||
private final LoadingCache<String, WxMpService> wxMpServiceCache = CacheUtils.buildAsyncReloadingCache(
|
||||
Duration.ofSeconds(10L),
|
||||
new CacheLoader<String, WxMpService>() {
|
||||
|
||||
@Override
|
||||
public WxMpService load(String key) {
|
||||
String[] keys = key.split(":");
|
||||
return buildWxMpService(keys[0], keys[1]);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@Resource
|
||||
private WxMaService wxMaService;
|
||||
@Resource
|
||||
private WxMaProperties wxMaProperties;
|
||||
/**
|
||||
* 缓存 WxMaService 对象
|
||||
*
|
||||
* 说明同 {@link #wxMpServiceCache} 变量
|
||||
*/
|
||||
private final LoadingCache<String, WxMaService> wxMaServiceCache = CacheUtils.buildAsyncReloadingCache(
|
||||
Duration.ofSeconds(10L),
|
||||
new CacheLoader<String, WxMaService>() {
|
||||
|
||||
@Override
|
||||
public WxMaService load(String key) {
|
||||
String[] keys = key.split(":");
|
||||
return buildWxMaService(keys[0], keys[1]);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@Resource
|
||||
private SocialClientMapper socialClientMapper;
|
||||
|
||||
@Override
|
||||
public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) {
|
||||
// 获得对应的 AuthRequest 实现
|
||||
AuthRequest authRequest = buildAuthRequest(socialType, userType);
|
||||
// 生成跳转地址
|
||||
String authorizeUri = authRequest.authorize(AuthStateUtils.createState());
|
||||
return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state) {
|
||||
// 构建请求
|
||||
AuthRequest authRequest = buildAuthRequest(socialType, userType);
|
||||
AuthCallback authCallback = AuthCallback.builder().code(code).state(state).build();
|
||||
// 执行请求
|
||||
AuthResponse<?> authResponse = authRequest.login(authCallback);
|
||||
log.info("[getAuthUser][请求社交平台 type({}) request({}) response({})]", socialType,
|
||||
toJsonString(authCallback), toJsonString(authResponse));
|
||||
if (!authResponse.ok()) {
|
||||
throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg());
|
||||
}
|
||||
return (AuthUser) authResponse.getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 AuthRequest 对象,支持多租户配置
|
||||
*
|
||||
* @param socialType 社交类型
|
||||
* @param userType 用户类型
|
||||
* @return AuthRequest 对象
|
||||
*/
|
||||
private AuthRequest buildAuthRequest(Integer socialType, Integer userType) {
|
||||
// 1. 先查找默认的配置项,从 application-*.yaml 中读取
|
||||
AuthRequest request = yudaoAuthRequestFactory.get(SocialTypeEnum.valueOfType(socialType).getSource());
|
||||
Assert.notNull(request, String.format("社交平台(%d) 不存在", socialType));
|
||||
// 2. 查询 DB 的配置项,如果存在则进行覆盖
|
||||
SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(socialType, userType);
|
||||
if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
|
||||
// 2.1 构造新的 AuthConfig 对象
|
||||
AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(request, "config");
|
||||
AuthConfig newAuthConfig = ReflectUtil.newInstance(authConfig.getClass());
|
||||
BeanUtil.copyProperties(authConfig, newAuthConfig);
|
||||
// 2.2 修改对应的 clientId + clientSecret 密钥
|
||||
newAuthConfig.setClientId(client.getClientId());
|
||||
newAuthConfig.setClientSecret(client.getClientSecret());
|
||||
// 2.3 设置会 request 里,进行后续使用
|
||||
ReflectUtil.setFieldValue(request, "config", newAuthConfig);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
// =================== 微信公众号独有 ===================
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url) {
|
||||
WxMpService service = getWxMpService(userType);
|
||||
return service.createJsapiSignature(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 clientId + clientSecret 对应的 WxMpService 对象
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @return WxMpService 对象
|
||||
*/
|
||||
private WxMpService getWxMpService(Integer userType) {
|
||||
// 第一步,查询 DB 的配置项,获得对应的 WxMpService 对象
|
||||
SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
|
||||
SocialTypeEnum.WECHAT_MP.getType(), userType);
|
||||
if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
|
||||
return wxMpServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret());
|
||||
}
|
||||
// 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMpService 对象
|
||||
return wxMpService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 clientId + clientSecret 对应的 WxMpService 对象
|
||||
*
|
||||
* @param clientId 微信公众号 appId
|
||||
* @param clientSecret 微信公众号 secret
|
||||
* @return WxMpService 对象
|
||||
*/
|
||||
private WxMpService buildWxMpService(String clientId, String clientSecret) {
|
||||
// 第一步,创建 WxMpRedisConfigImpl 对象
|
||||
WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl(
|
||||
new RedisTemplateWxRedisOps(stringRedisTemplate),
|
||||
wxMpProperties.getConfigStorage().getKeyPrefix());
|
||||
configStorage.setAppId(clientId);
|
||||
configStorage.setSecret(clientSecret);
|
||||
|
||||
// 第二步,创建 WxMpService 对象
|
||||
WxMpService service = new WxMpServiceImpl();
|
||||
service.setWxMpConfigStorage(configStorage);
|
||||
return service;
|
||||
}
|
||||
|
||||
// =================== 微信小程序独有 ===================
|
||||
|
||||
@Override
|
||||
public WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode) {
|
||||
WxMaService service = getWxMaService(userType);
|
||||
try {
|
||||
return service.getUserService().getPhoneNoInfo(phoneCode);
|
||||
} catch (WxErrorException e) {
|
||||
log.error("[getPhoneNoInfo][userType({}) phoneCode({}) 获得手机号失败]", userType, phoneCode, e);
|
||||
throw exception(SOCIAL_APP_WEIXIN_MINI_APP_PHONE_CODE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 clientId + clientSecret 对应的 WxMpService 对象
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @return WxMpService 对象
|
||||
*/
|
||||
private WxMaService getWxMaService(Integer userType) {
|
||||
// 第一步,查询 DB 的配置项,获得对应的 WxMaService 对象
|
||||
SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
|
||||
SocialTypeEnum.WECHAT_MINI_APP.getType(), userType);
|
||||
if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
|
||||
return wxMaServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret());
|
||||
}
|
||||
// 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMaService 对象
|
||||
return wxMaService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 clientId + clientSecret 对应的 WxMaService 对象
|
||||
*
|
||||
* @param clientId 微信小程序 appId
|
||||
* @param clientSecret 微信小程序 secret
|
||||
* @return WxMaService 对象
|
||||
*/
|
||||
private WxMaService buildWxMaService(String clientId, String clientSecret) {
|
||||
// 第一步,创建 WxMaRedisBetterConfigImpl 对象
|
||||
WxMaRedisBetterConfigImpl configStorage = new WxMaRedisBetterConfigImpl(
|
||||
new RedisTemplateWxRedisOps(stringRedisTemplate),
|
||||
wxMaProperties.getConfigStorage().getKeyPrefix());
|
||||
configStorage.setAppid(clientId);
|
||||
configStorage.setSecret(clientSecret);
|
||||
|
||||
// 第二步,创建 WxMpService 对象
|
||||
WxMaService service = new WxMaServiceImpl();
|
||||
service.setWxMaConfig(configStorage);
|
||||
return service;
|
||||
}
|
||||
|
||||
}
|
@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -17,27 +16,6 @@ import java.util.List;
|
||||
*/
|
||||
public interface SocialUserService {
|
||||
|
||||
/**
|
||||
* 获得社交平台的授权 URL
|
||||
*
|
||||
* @param type 社交平台的类型 {@link SocialTypeEnum}
|
||||
* @param redirectUri 重定向 URL
|
||||
* @return 社交平台的授权 URL
|
||||
*/
|
||||
String getAuthorizeUrl(Integer type, String redirectUri);
|
||||
|
||||
/**
|
||||
* 授权获得对应的社交用户
|
||||
* 如果授权失败,则会抛出 {@link ServiceException} 异常
|
||||
*
|
||||
* @param type 社交平台的类型 {@link SocialTypeEnum}
|
||||
* @param code 授权码
|
||||
* @param state state
|
||||
* @return 授权用户
|
||||
*/
|
||||
@NotNull
|
||||
SocialUserDO authSocialUser(Integer type, String code, String state);
|
||||
|
||||
/**
|
||||
* 获得指定用户的社交用户列表
|
||||
*
|
||||
|
@ -2,8 +2,7 @@ package cn.iocoder.yudao.module.system.service.social;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
|
||||
import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
|
||||
@ -11,24 +10,22 @@ import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
import com.xingyuv.jushauth.model.AuthCallback;
|
||||
import com.xingyuv.jushauth.model.AuthResponse;
|
||||
import com.xingyuv.jushauth.model.AuthUser;
|
||||
import com.xingyuv.jushauth.request.AuthRequest;
|
||||
import com.xingyuv.jushauth.utils.AuthStateUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.AUTH_THIRD_LOGIN_NOT_BIND;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND;
|
||||
|
||||
/**
|
||||
* 社交用户 Service 实现类
|
||||
@ -40,51 +37,13 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||
@Slf4j
|
||||
public class SocialUserServiceImpl implements SocialUserService {
|
||||
|
||||
@Resource// 由于自定义了 YudaoAuthRequestFactory 无法覆盖默认的 AuthRequestFactory,所以只能注入它
|
||||
private YudaoAuthRequestFactory yudaoAuthRequestFactory;
|
||||
|
||||
@Resource
|
||||
private SocialUserBindMapper socialUserBindMapper;
|
||||
@Resource
|
||||
private SocialUserMapper socialUserMapper;
|
||||
|
||||
@Override
|
||||
public String getAuthorizeUrl(Integer type, String redirectUri) {
|
||||
// 获得对应的 AuthRequest 实现
|
||||
AuthRequest authRequest = yudaoAuthRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource());
|
||||
// 生成跳转地址
|
||||
String authorizeUri = authRequest.authorize(AuthStateUtils.createState());
|
||||
return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocialUserDO authSocialUser(Integer type, String code, String state) {
|
||||
// 优先从 DB 中获取,因为 code 有且可以使用一次。
|
||||
// 在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次
|
||||
SocialUserDO socialUser = socialUserMapper.selectByTypeAndCodeAnState(type, code, state);
|
||||
if (socialUser != null) {
|
||||
return socialUser;
|
||||
}
|
||||
|
||||
// 请求获取
|
||||
AuthUser authUser = getAuthUser(type, code, state);
|
||||
Assert.notNull(authUser, "三方用户不能为空");
|
||||
|
||||
// 保存到 DB 中
|
||||
socialUser = socialUserMapper.selectByTypeAndOpenid(type, authUser.getUuid());
|
||||
if (socialUser == null) {
|
||||
socialUser = new SocialUserDO();
|
||||
}
|
||||
socialUser.setType(type).setCode(code).setState(state) // 需要保存 code + state 字段,保证后续可查询
|
||||
.setOpenid(authUser.getUuid()).setToken(authUser.getToken().getAccessToken()).setRawTokenInfo((toJsonString(authUser.getToken())))
|
||||
.setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar()).setRawUserInfo(toJsonString(authUser.getRawUserInfo()));
|
||||
if (socialUser.getId() == null) {
|
||||
socialUserMapper.insert(socialUser);
|
||||
} else {
|
||||
socialUserMapper.updateById(socialUser);
|
||||
}
|
||||
return socialUser;
|
||||
}
|
||||
@Resource
|
||||
private SocialClientService socialClientService;
|
||||
|
||||
@Override
|
||||
public List<SocialUserDO> getSocialUserList(Long userId, Integer userType) {
|
||||
@ -101,7 +60,8 @@ public class SocialUserServiceImpl implements SocialUserService {
|
||||
@Transactional
|
||||
public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
|
||||
// 获得社交用户
|
||||
SocialUserDO socialUser = authSocialUser(reqDTO.getType(), reqDTO.getCode(), reqDTO.getState());
|
||||
SocialUserDO socialUser = authSocialUser(reqDTO.getSocialType(), reqDTO.getUserType(),
|
||||
reqDTO.getCode(), reqDTO.getState());
|
||||
Assert.notNull(socialUser, "社交用户不能为空");
|
||||
|
||||
// 社交用户可能之前绑定过别的用户,需要进行解绑
|
||||
@ -134,7 +94,7 @@ public class SocialUserServiceImpl implements SocialUserService {
|
||||
@Override
|
||||
public SocialUserRespDTO getSocialUser(Integer userType, Integer type, String code, String state) {
|
||||
// 获得社交用户
|
||||
SocialUserDO socialUser = authSocialUser(type, code, state);
|
||||
SocialUserDO socialUser = authSocialUser(type, userType, code, state);
|
||||
Assert.notNull(socialUser, "社交用户不能为空");
|
||||
|
||||
// 如果未绑定的社交用户,则无法自动登录,进行报错
|
||||
@ -146,24 +106,44 @@ public class SocialUserServiceImpl implements SocialUserService {
|
||||
return new SocialUserRespDTO(socialUser.getOpenid(), socialUserBind.getUserId());
|
||||
}
|
||||
|
||||
// TODO 芋艿:调整下单测
|
||||
/**
|
||||
* 请求社交平台,获得授权的用户
|
||||
* 授权获得对应的社交用户
|
||||
* 如果授权失败,则会抛出 {@link ServiceException} 异常
|
||||
*
|
||||
* @param type 社交平台的类型
|
||||
* @param type 社交平台的类型 {@link SocialTypeEnum}
|
||||
* @param userType 用户类型
|
||||
* @param code 授权码
|
||||
* @param state 授权 state
|
||||
* @return 授权的用户
|
||||
* @param state state
|
||||
* @return 授权用户
|
||||
*/
|
||||
private AuthUser getAuthUser(Integer type, String code, String state) {
|
||||
AuthRequest authRequest = yudaoAuthRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource());
|
||||
AuthCallback authCallback = AuthCallback.builder().code(code).state(state).build();
|
||||
AuthResponse<?> authResponse = authRequest.login(authCallback);
|
||||
log.info("[getAuthUser][请求社交平台 type({}) request({}) response({})]", type,
|
||||
toJsonString(authCallback), toJsonString(authResponse));
|
||||
if (!authResponse.ok()) {
|
||||
throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg());
|
||||
@NotNull
|
||||
public SocialUserDO authSocialUser(Integer type, Integer userType, String code, String state) {
|
||||
// 优先从 DB 中获取,因为 code 有且可以使用一次。
|
||||
// 在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次
|
||||
SocialUserDO socialUser = socialUserMapper.selectByTypeAndCodeAnState(type, code, state);
|
||||
if (socialUser != null) {
|
||||
return socialUser;
|
||||
}
|
||||
return (AuthUser) authResponse.getData();
|
||||
|
||||
// 请求获取
|
||||
AuthUser authUser = socialClientService.getAuthUser(type, userType, code, state);
|
||||
Assert.notNull(authUser, "三方用户不能为空");
|
||||
|
||||
// 保存到 DB 中
|
||||
socialUser = socialUserMapper.selectByTypeAndOpenid(type, authUser.getUuid());
|
||||
if (socialUser == null) {
|
||||
socialUser = new SocialUserDO();
|
||||
}
|
||||
socialUser.setType(type).setCode(code).setState(state) // 需要保存 code + state 字段,保证后续可查询
|
||||
.setOpenid(authUser.getUuid()).setToken(authUser.getToken().getAccessToken()).setRawTokenInfo((toJsonString(authUser.getToken())))
|
||||
.setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar()).setRawUserInfo(toJsonString(authUser.getRawUserInfo()));
|
||||
if (socialUser.getId() == null) {
|
||||
socialUserMapper.insert(socialUser);
|
||||
} else {
|
||||
socialUserMapper.updateById(socialUser);
|
||||
}
|
||||
return socialUser;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user