Merge remote-tracking branch 'yudao/feature/mall_product' into feature/mall_product

This commit is contained in:
puhui999 2023-06-17 21:19:02 +08:00
commit 16f2a5b91a
117 changed files with 4113 additions and 697 deletions

230
sql/mysql/menu.sql Normal file
View File

@ -0,0 +1,230 @@
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'积分设置管理', '', 2, 0, 2162,
'config', '', 'point/config/index', 0, 'PointConfig'
);
-- 按钮父菜单ID
-- 暂时只支持 MySQL如果你是 OraclePostgreSQLSQLServer 的话需要手动修改 @parentId 的部分的代码
SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'积分设置查询', 'point:config:query', 3, 1, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'积分设置创建', 'point:config:create', 3, 2, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'积分设置更新', 'point:config:update', 3, 3, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'积分设置删除', 'point:config:delete', 3, 4, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'积分设置导出', 'point:config:export', 3, 5, @parentId,
'', '', '', 0
);
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'积分签到规则管理', '', 2, 0, 2162,
'sign-in-config', '', 'point/signInConfig/index', 0, 'SignInConfig'
);
-- 按钮父菜单ID
-- 暂时只支持 MySQL如果你是 OraclePostgreSQLSQLServer 的话需要手动修改 @parentId 的部分的代码
SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'积分签到规则查询', 'point:sign-in-config:query', 3, 1, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'积分签到规则创建', 'point:sign-in-config:create', 3, 2, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'积分签到规则更新', 'point:sign-in-config:update', 3, 3, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'积分签到规则删除', 'point:sign-in-config:delete', 3, 4, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'积分签到规则导出', 'point:sign-in-config:export', 3, 5, @parentId,
'', '', '', 0
);
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'用户积分记录管理', '', 2, 0, 2162,
'record', '', 'point/record/index', 0, 'PointRecord'
);
-- 按钮父菜单ID
-- 暂时只支持 MySQL如果你是 OraclePostgreSQLSQLServer 的话需要手动修改 @parentId 的部分的代码
SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'用户积分记录查询', 'point:record:query', 3, 1, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'用户积分记录创建', 'point:record:create', 3, 2, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'用户积分记录更新', 'point:record:update', 3, 3, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'用户积分记录删除', 'point:record:delete', 3, 4, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'用户积分记录导出', 'point:record:export', 3, 5, @parentId,
'', '', '', 0
);
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'用户签到积分管理', '', 2, 0, 2162,
'sign-in-record', '', 'point/signInRecord/index', 0, 'SignInRecord'
);
-- 按钮父菜单ID
-- 暂时只支持 MySQL如果你是 OraclePostgreSQLSQLServer 的话需要手动修改 @parentId 的部分的代码
SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'用户签到积分查询', 'point:sign-in-record:query', 3, 1, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'用户签到积分创建', 'point:sign-in-record:create', 3, 2, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'用户签到积分更新', 'point:sign-in-record:update', 3, 3, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'用户签到积分删除', 'point:sign-in-record:delete', 3, 4, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'用户签到积分导出', 'point:sign-in-record:export', 3, 5, @parentId,
'', '', '', 0
);

152
sql/mysql/point.sql Normal file
View File

@ -0,0 +1,152 @@
/*
Navicat Premium Data Transfer
Source Server : docer-master-root(3308)
Source Server Type : MySQL
Source Server Version : 80030
Source Host : 10.211.55.5:3308
Source Schema : mall
Target Server Type : MySQL
Target Server Version : 80030
File Encoding : 65001
Date: 10/06/2023 20:13:57
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for member_point_config
-- ----------------------------
DROP TABLE IF EXISTS `member_point_config`;
CREATE TABLE `member_point_config` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`trade_deduct_enable` int DEFAULT NULL COMMENT '1 开启积分抵扣\n0 关闭积分抵扣',
`trade_deduct_unit_price` decimal(10,2) DEFAULT NULL COMMENT '积分抵扣抵扣最低为分 以0.01表示 1积分抵扣0.01元(单位)',
`trade_deduct_max_price` bigint DEFAULT NULL COMMENT '积分抵扣最大值',
`trade_give_point` bigint DEFAULT NULL COMMENT '1元赠送多少分',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '变更时间',
`tenant_id` varchar(255) DEFAULT NULL COMMENT '租户id',
`deleted` int DEFAULT '0' COMMENT '是否被删除 0 未删除 1已删除',
`creator` varchar(255) DEFAULT NULL COMMENT '创建人',
`updater` varchar(255) DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='积分设置';
-- ----------------------------
-- Records of member_point_config
-- ----------------------------
BEGIN;
INSERT INTO `member_point_config` (`id`, `trade_deduct_enable`, `trade_deduct_unit_price`, `trade_deduct_max_price`, `trade_give_point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (1, 1, 0.01, 10000, 1, '2023-06-10 10:57:22', '2023-06-10 03:06:58', '1', 1, '1', '1');
INSERT INTO `member_point_config` (`id`, `trade_deduct_enable`, `trade_deduct_unit_price`, `trade_deduct_max_price`, `trade_give_point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (2, 1, 0.01, 10000, 10, '2023-06-10 11:07:12', '2023-06-10 11:07:12', '1', 0, '1', '1');
INSERT INTO `member_point_config` (`id`, `trade_deduct_enable`, `trade_deduct_unit_price`, `trade_deduct_max_price`, `trade_give_point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (3, 1, 12.00, 12, 12, '2023-06-10 16:09:26', '2023-06-10 08:10:53', '1', 1, '1', '1');
COMMIT;
-- ----------------------------
-- Table structure for member_point_record
-- ----------------------------
DROP TABLE IF EXISTS `member_point_record`;
CREATE TABLE `member_point_record` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`biz_id` varchar(255) DEFAULT NULL COMMENT '业务编码',
`biz_type` varchar(255) DEFAULT NULL COMMENT '业务类型',
`type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '1增加 0扣减',
`title` varchar(255) DEFAULT NULL COMMENT '积分标题',
`description` varchar(5000) DEFAULT NULL COMMENT '积分描述',
`point` int DEFAULT NULL COMMENT '积分',
`total_point` int NOT NULL COMMENT '变动后的积分',
`status` int DEFAULT NULL COMMENT '状态1-订单创建2-冻结期3-完成4-失效订单退款\n',
`user_id` int DEFAULT NULL COMMENT '用户id',
`freezing_time` datetime DEFAULT NULL COMMENT '冻结时间',
`thawing_time` datetime DEFAULT NULL COMMENT '解冻时间',
`create_time` datetime DEFAULT NULL COMMENT '发生时间',
`tenant_id` varchar(255) DEFAULT NULL COMMENT '租户',
`deleted` int DEFAULT '0' COMMENT '是否删除',
`creator` varchar(255) DEFAULT NULL COMMENT '创建用户',
`updater` varchar(255) DEFAULT NULL COMMENT '更新用户',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `index_userId` (`user_id`),
KEY `index_title` (`title`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户积分记录';
-- ----------------------------
-- Records of member_point_record
-- ----------------------------
BEGIN;
INSERT INTO `member_point_record` (`id`, `biz_id`, `biz_type`, `type`, `title`, `description`, `point`, `total_point`, `status`, `user_id`, `freezing_time`, `thawing_time`, `create_time`, `tenant_id`, `deleted`, `creator`, `updater`, `update_time`) VALUES (1, '1', '1', '1', '12', NULL, 212, 12, 1, 12, '2023-06-13 00:00:00', '2023-06-20 00:00:00', '2023-06-10 12:38:48', '1', 1, '1', '1', '2023-06-10 04:42:24');
INSERT INTO `member_point_record` (`id`, `biz_id`, `biz_type`, `type`, `title`, `description`, `point`, `total_point`, `status`, `user_id`, `freezing_time`, `thawing_time`, `create_time`, `tenant_id`, `deleted`, `creator`, `updater`, `update_time`) VALUES (2, '12', '1', '0', NULL, NULL, 1212, 12, 2, 12, '2023-06-28 00:00:00', NULL, '2023-06-10 12:42:48', '1', 0, '1', '1', '2023-06-10 12:43:04');
INSERT INTO `member_point_record` (`id`, `biz_id`, `biz_type`, `type`, `title`, `description`, `point`, `total_point`, `status`, `user_id`, `freezing_time`, `thawing_time`, `create_time`, `tenant_id`, `deleted`, `creator`, `updater`, `update_time`) VALUES (3, '12', '1', '1', '12', NULL, 12, 12, 1, 12, '2023-06-27 00:00:00', '2023-06-23 00:00:00', '2023-06-10 20:06:48', '1', 0, '1', '1', '2023-06-10 20:06:48');
COMMIT;
-- ----------------------------
-- Table structure for member_sign_in_config
-- ----------------------------
DROP TABLE IF EXISTS `member_sign_in_config`;
CREATE TABLE `member_sign_in_config` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '规则自增主键',
`day` int DEFAULT NULL COMMENT '签到第x天',
`point` int DEFAULT NULL COMMENT '签到天数对应分数',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '变更时间',
`tenant_id` varchar(255) DEFAULT NULL COMMENT '租户id',
`deleted` int DEFAULT '0' COMMENT '是否删除',
`creator` varchar(255) DEFAULT NULL COMMENT '创建人',
`updater` varchar(255) DEFAULT NULL COMMENT '变更人',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='积分签到规则';
-- ----------------------------
-- Records of member_sign_in_config
-- ----------------------------
BEGIN;
INSERT INTO `member_sign_in_config` (`id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (1, 1, 10, '2023-06-10 11:34:43', '2023-06-10 11:34:43', '1', 0, '1', '1');
INSERT INTO `member_sign_in_config` (`id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (2, 2, 20, '2023-06-10 11:34:59', '2023-06-10 03:55:35', '1', 1, '1', '1');
INSERT INTO `member_sign_in_config` (`id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (3, 7, 1001, '2023-06-10 17:47:45', '2023-06-10 19:54:37', '1', 0, '1', '1');
INSERT INTO `member_sign_in_config` (`id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (4, 6, 12121, '2023-06-10 17:47:55', '2023-06-10 19:48:37', '1', 0, '1', '1');
INSERT INTO `member_sign_in_config` (`id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (5, 2, 12, '2023-06-10 19:54:52', '2023-06-10 19:54:52', '1', 0, '1', '1');
COMMIT;
-- ----------------------------
-- Table structure for member_sign_in_record
-- ----------------------------
DROP TABLE IF EXISTS `member_sign_in_record`;
CREATE TABLE `member_sign_in_record` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '签到自增id',
`user_id` int DEFAULT NULL COMMENT '签到用户',
`day` int DEFAULT NULL COMMENT '第几天签到',
`point` int DEFAULT NULL COMMENT '签到的分数',
`create_time` datetime DEFAULT NULL COMMENT '签到时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '变更时间',
`tenant_id` varchar(255) DEFAULT NULL COMMENT '租户id',
`deleted` int DEFAULT '0' COMMENT '是否删除',
`creator` varchar(255) DEFAULT NULL COMMENT '创建人',
`updater` varchar(255) DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户签到积分';
-- ----------------------------
-- Records of member_sign_in_record
-- ----------------------------
BEGIN;
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (1, 121, 1, 123, '2023-06-10 12:58:18', '2023-06-10 04:59:00', '1', 1, '1', '1');
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (2, 12, 12, 12, '2023-06-10 19:56:39', '2023-06-10 11:56:45', '1', 1, '1', '1');
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (3, 12, 12, 1212, '2023-06-10 20:01:17', '2023-06-10 12:01:23', '1', 1, '1', '1');
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (4, 12, 12, 1212, '2023-06-10 20:01:27', '2023-06-10 20:01:27', '1', 0, '1', '1');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
INSERT INTO `mall`.`system_dict_type` ( `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES ( '积分业务类型', 'point_biz_type', 0, '', '1', '2023-06-10 12:15:00', '1', '2023-06-10 04:25:07', b'0', '1970-01-01 00:00:00');
INSERT INTO `mall`.`system_dict_type` ( `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES ( '积分订单状态', 'point_status', 0, '', '1', '2023-06-10 12:16:27', '1', '2023-06-10 12:16:27', b'0', '1970-01-01 00:00:00');
INSERT INTO `mall`.`system_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 1, '购物', '1', 'point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:27', '1', '2023-06-10 04:25:15', b'0');
INSERT INTO `mall`.`system_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 2, '签到', '2', 'point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:48', '1', '2023-06-10 04:25:18', b'0');
INSERT INTO `mall`.`system_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 1, '订单创建', '1', 'point_status', 0, '', '', '', '1', '2023-06-10 12:16:42', '1', '2023-06-10 12:16:42', b'0');
INSERT INTO `mall`.`system_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 2, '冻结期', '2', 'point_status', 0, '', '', '', '1', '2023-06-10 12:16:58', '1', '2023-06-10 12:16:58', b'0');
INSERT INTO `mall`.`system_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 3, '完成', '3', 'point_status', 0, '', '', '', '1', '2023-06-10 12:17:07', '1', '2023-06-10 12:17:07', b'0');
INSERT INTO `mall`.`system_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 4, '失效(订单退款)', '4', 'point_status', 0, '', '', '', '1', '2023-06-10 12:17:21', '1', '2023-06-10 12:17:21', b'0');

View File

@ -55,7 +55,6 @@ public class DateUtils {
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
} }
@Deprecated
public static Date addTime(Duration duration) { public static Date addTime(Duration duration) {
return new Date(System.currentTimeMillis() + duration.toMillis()); return new Date(System.currentTimeMillis() + duration.toMillis());
} }

View File

@ -4,22 +4,12 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.ToString; import lombok.ToString;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
/**
* @author xia
*/
@Schema(description = "管理后台 - Banner Response VO") @Schema(description = "管理后台 - Banner Response VO")
@Data @Data
@ToString(callSuper = true) @ToString(callSuper = true)
public class BannerRespVO extends BannerBaseVO { public class BannerRespVO extends BannerBaseVO {
@Schema(description = "banner编号", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "banner编号不能为空")
private Long id; private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
} }

View File

@ -1,42 +1,42 @@
package cn.iocoder.yudao.module.promotion.controller.app.banner; package cn.iocoder.yudao.module.promotion.controller.app.banner;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerRespVO; import cn.iocoder.yudao.module.promotion.controller.app.banner.vo.AppBannerRespVO;
import cn.iocoder.yudao.module.promotion.convert.banner.BannerConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO;
import cn.iocoder.yudao.module.promotion.service.banner.BannerService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
* @author: XIA
*/
@RestController @RestController
@RequestMapping("/market/banner") @RequestMapping("/promotion/banner")
@Tag(name = "用户APP- 首页Banner") @Tag(name = "用户 APP - 首页 Banner")
@Validated @Validated
public class AppBannerController { public class AppBannerController {
@Resource
private BannerService bannerService;
// TODO @xia新建一个 AppBannerRespVO只返回必要的字段status 要过滤下然后 sort 下结果
@GetMapping("/list") @GetMapping("/list")
@Operation(summary = "获得banner列表") @Operation(summary = "获得 banner 列表")
@PreAuthorize("@ss.hasPermission('market:banner:query')") // todo @芋艿swagger 注解待补全
public CommonResult<List<BannerRespVO>> getBannerList() { // TODO @芋艿可以增加缓存提升性能
List<BannerDO> list = bannerService.getBannerList(); // TODO @芋艿position = 1 首页position = 10 拼团活动页
return success(BannerConvert.INSTANCE.convertList(list)); public CommonResult<List<AppBannerRespVO>> getBannerList(@RequestParam("position") Integer position) {
List<AppBannerRespVO> bannerList = new ArrayList<>();
AppBannerRespVO banner1 = new AppBannerRespVO();
banner1.setUrl("https://www.example.com/link1");
banner1.setPicUrl("https://api.java.crmeb.net/crmebimage/public/content/2022/08/04/0f78716213f64bfa83f191d51a832cbf73f6axavoy.jpg");
bannerList.add(banner1);
AppBannerRespVO banner2 = new AppBannerRespVO();
banner2.setUrl("https://www.example.com/link2");
banner2.setPicUrl("https://api.java.crmeb.net/crmebimage/public/content/2023/01/11/be09e755268b43ee90b0db3a3e1b7132r7a6t2wvsm.jpg");
bannerList.add(banner2);
return success(bannerList);
} }
} }

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.promotion.controller.app.banner.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Schema(description = "用户 App - Banner Response VO")
@Data
public class AppBannerRespVO {
@Schema(description = "跳转链接", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "跳转链接不能为空")
private String url;
@Schema(description = "图片地址", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "图片地址不能为空")
private String picUrl;
}

View File

@ -1,7 +1,10 @@
package cn.iocoder.yudao.module.promotion.controller.app.combination; package cn.iocoder.yudao.module.promotion.controller.app.combination;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityDetailRespVO; import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityDetailRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityRespVO;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@ -21,20 +24,79 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@RestController @RestController
@RequestMapping("/promotion/combination-activity") @RequestMapping("/promotion/combination-activity")
@Validated @Validated
public class AppCombinationController { public class AppCombinationActivityController {
@GetMapping("/list")
@Operation(summary = "获得拼团活动列表", description = "用于小程序首页")
// TODO 芋艿增加 Spring Cache
// TODO 芋艿缺少 swagger 注解
public CommonResult<List<AppCombinationActivityRespVO>> getCombinationActivityList(
@RequestParam(name = "count", defaultValue = "6") Integer count) {
List<AppCombinationActivityRespVO> activityList = new ArrayList<>();
AppCombinationActivityRespVO activity1 = new AppCombinationActivityRespVO();
activity1.setId(1L);
activity1.setName("618 大拼团");
activity1.setUserSize(3);
activity1.setSpuId(2048L);
activity1.setPicUrl("商品图片地址");
activity1.setMarketPrice(50);
activity1.setCombinationPrice(100);
activityList.add(activity1);
AppCombinationActivityRespVO activity2 = new AppCombinationActivityRespVO();
activity2.setId(2L);
activity2.setName("双十一拼团");
activity2.setUserSize(5);
activity2.setSpuId(4096L);
activity2.setPicUrl("商品图片地址");
activity2.setMarketPrice(100);
activity2.setCombinationPrice(200);
activityList.add(activity2);
return success(activityList);
}
@GetMapping("/page")
@Operation(summary = "获得拼团活动分页")
public CommonResult<PageResult<AppCombinationActivityRespVO>> getCombinationActivityPage(PageParam pageParam) {
List<AppCombinationActivityRespVO> activityList = new ArrayList<>();
AppCombinationActivityRespVO activity1 = new AppCombinationActivityRespVO();
activity1.setId(1L);
activity1.setName("618 大拼团");
activity1.setUserSize(3);
activity1.setSpuId(2048L);
activity1.setPicUrl("商品图片地址");
activity1.setMarketPrice(50);
activity1.setCombinationPrice(100);
activityList.add(activity1);
AppCombinationActivityRespVO activity2 = new AppCombinationActivityRespVO();
activity2.setId(2L);
activity2.setName("双十一拼团");
activity2.setUserSize(5);
activity2.setSpuId(4096L);
activity2.setPicUrl("商品图片地址");
activity2.setMarketPrice(100);
activity2.setCombinationPrice(200);
activityList.add(activity2);
return success(new PageResult<>(activityList, 2L));
}
@GetMapping("/get-detail") @GetMapping("/get-detail")
@Operation(summary = "获得拼团活动明细") @Operation(summary = "获得拼团活动明细")
@Parameter(name = "id", description = "活动编号", required = true, example = "1024") @Parameter(name = "id", description = "活动编号", required = true, example = "1024")
public CommonResult<AppCombinationActivityDetailRespVO> getCombinationActivity(@RequestParam("id") Long id) { public CommonResult<AppCombinationActivityDetailRespVO> getCombinationActivityDetail(@RequestParam("id") Long id) {
// TODO 芋艿如果禁用的时候需要抛出异常 // TODO 芋艿如果禁用的时候需要抛出异常
AppCombinationActivityDetailRespVO obj = new AppCombinationActivityDetailRespVO(); AppCombinationActivityDetailRespVO obj = new AppCombinationActivityDetailRespVO();
// 设置其属性的值 // 设置其属性的值
obj.setId(id); obj.setId(id);
obj.setName("晚九点限时秒杀"); obj.setName("晚九点限时秒杀");
obj.setStatus(1); obj.setStatus(1);
obj.setStartTime(LocalDateTime.of(2023, 6, 11, 0, 0, 0)); obj.setStartTime(LocalDateTime.of(2023, 6, 15, 0, 0, 0));
obj.setEndTime(LocalDateTime.of(2023, 6, 11, 23, 59, 0)); obj.setEndTime(LocalDateTime.of(2023, 6, 15, 23, 59, 0));
obj.setUserSize(2);
obj.setSuccessCount(100);
obj.setSpuId(633L); obj.setSpuId(633L);
// 创建一个Product对象的列表 // 创建一个Product对象的列表
List<AppCombinationActivityDetailRespVO.Product> productList = new ArrayList<>(); List<AppCombinationActivityDetailRespVO.Product> productList = new ArrayList<>();

View File

@ -0,0 +1,109 @@
package cn.iocoder.yudao.module.promotion.controller.app.combination;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordDetailRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordSummaryRespVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.Max;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "用户 APP - 拼团活动")
@RestController
@RequestMapping("/promotion/combination-record")
@Validated
public class AppCombinationRecordController {
@GetMapping("/get-summary")
@Operation(summary = "获得拼团活动的概要信息", description = "用于小程序首页")
// TODO 芋艿增加 @Cache 缓存1 分钟过期
public CommonResult<AppCombinationRecordSummaryRespVO> getCombinationSummary() {
AppCombinationRecordSummaryRespVO summary = new AppCombinationRecordSummaryRespVO();
summary.setUserCount(1024);
summary.setAvatars(new ArrayList<>());
summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLjFK35Wvia9lJKHoXfQuHhk0qZbvpPNxrAiaEKF7aL2k4I8kuqrdTWwliamdPHeyAA7DjAg725X2GIQ/132");
summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTK1pXgdj5DvBMwrbe8v3tFibSWeQATEsAibt3fllD8XwJ460P2r6KS3WCQvDefuv1bVpDhNCle6CTCA/132");
summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTL7KRGHBE62N0awFyBesmmxiaCicf1fJ7E7UCh6zA8GWlT1QC1zT01gG4OxI7BWDESkdPZ5o7tno4hA/132");
summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/ouwtwJycbic2JrCoZjETict0klxd1uRuicRneKk00ewMcCClxVcVHQT91Sh9MJGtwibf1fOicD1WpwSP4icJM6eQq1AA/132");
summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/RpUrhwens58qc99OcGs993xL4M5QPOe05ekqF9Eia440kRicAlicicIdQWicHBmy2bzLgHzHguWEzHHxnIgeictL7bLA/132");
summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/S4tfqmxc8GZGsKc1K4mnhpvtG16gtMrLnTQfDibhr7jJich9LRI5RQKZDoqEjZM3azMib5nic7F4ZXKMEgYyLO08KA/132");
summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKXMYJOomfp7cebz3cIeb8sHk3GGSIJtWEgREe3j7J1WoAbTvIOicpcNdFkWAziatBSMod8b5RyS4CQ/132");
return success(summary);
}
@GetMapping("/get-head-list")
@Operation(summary = "获得最近 n 条拼团记录(团长发起的)")
// TODO @芋艿注解要补全
public CommonResult<List<AppCombinationRecordRespVO>> getHeadCombinationRecordList(
@RequestParam(value = "activityId", required = false) Long activityId,
@RequestParam("status") Integer status,
@RequestParam(value = "count", defaultValue = "20") @Max(20) Integer count) {
List<AppCombinationRecordRespVO> list = new ArrayList<>();
for (int i = 1; i <= count; i++) {
AppCombinationRecordRespVO record = new AppCombinationRecordRespVO();
record.setId((long) i);
record.setNickname("用户" + i);
record.setAvatar("头像" + i);
record.setExpireTime(new Date());
record.setUserSize(10);
record.setUserCount(i);
record.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
record.setActivityId(1L);
record.setSpuName("活动:" + i);
list.add(record);
}
return success(list);
}
@GetMapping("/get-detail")
@Operation(summary = "获得拼团记录明细")
@Parameter(name = "id", description = "拼团记录编号", required = true, example = "1024")
public CommonResult<AppCombinationRecordDetailRespVO> getCombinationRecordDetail(@RequestParam("id") Long id) {
AppCombinationRecordDetailRespVO detail = new AppCombinationRecordDetailRespVO();
// 团长
AppCombinationRecordRespVO headRecord = new AppCombinationRecordRespVO();
headRecord.setId(1L);
headRecord.setNickname("用户" + 1);
headRecord.setAvatar("头像" + 1);
headRecord.setExpireTime(DateUtils.addTime(Duration.ofDays(1)));
headRecord.setUserSize(10);
headRecord.setUserCount(3);
headRecord.setStatus(1);
headRecord.setActivityId(10L);
headRecord.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
headRecord.setCombinationPrice(100);
detail.setHeadRecord(headRecord);
// 团员
List<AppCombinationRecordRespVO> list = new ArrayList<>();
for (int i = 1; i <= 2; i++) {
AppCombinationRecordRespVO record = new AppCombinationRecordRespVO();
record.setId((long) i);
record.setNickname("用户" + i);
record.setAvatar("头像" + i);
record.setExpireTime(new Date());
record.setUserSize(10);
record.setUserCount(i);
record.setStatus(1);
list.add(record);
}
detail.setMemberRecords(list);
// 订单编号
detail.setOrderId(100L);
return success(detail);
}
}

View File

@ -25,18 +25,18 @@ public class AppCombinationActivityDetailRespVO {
@Schema(description = "活动结束时间", required = true) @Schema(description = "活动结束时间", required = true)
private LocalDateTime endTime; private LocalDateTime endTime;
@Schema(description = "拼团人数", required = true, example = "3")
private Integer userSize;
@Schema(description = "成功的拼团数量", required = true, example = "100")
private Integer successCount;
@Schema(description = "商品 SPU 编号", required = true, example = "2048") @Schema(description = "商品 SPU 编号", required = true, example = "2048")
private Long spuId; private Long spuId;
@Schema(description = "商品信息数组", required = true) @Schema(description = "商品信息数组", required = true)
private List<Product> products; private List<Product> products;
@Schema(description = "成功的拼团记录", required = true)
private List<Record> successRecords;
@Schema(description = "进行中的拼团记录", required = true)
private List<Record> runningRecords;
@Schema(description = "商品信息") @Schema(description = "商品信息")
@Data @Data
public static class Product { public static class Product {
@ -55,13 +55,4 @@ public class AppCombinationActivityDetailRespVO {
} }
@Schema(description = "拼团记录")
@Data
public static class Record {
@Schema(description = "拼团记录编号", required = true, example = "1024")
private Long id;
}
} }

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 App - 拼团活动 Response VO")
@Data
public class AppCombinationActivityRespVO {
@Schema(description = "拼团活动编号", required = true, example = "1024")
private Long id;
@Schema(description = "拼团活动名称", required = true, example = "618 大拼团")
private String name;
@Schema(description = "拼团人数", required = true, example = "3")
private Integer userSize;
@Schema(description = "商品 SPU 编号", required = true, example = "2048")
private Long spuId;
@Schema(description = "商品图片", required = true, example = "4096") // SPU picUrl 读取
private String picUrl;
@Schema(description = "商品市场价,单位:分", required = true, example = "50") // SPU marketPrice 读取
private Integer marketPrice;
@Schema(description = "拼团金额,单位:分", required = true, example = "100") // 从拼团商品里取最低价
private Integer combinationPrice;
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "用户 App - 拼团记录详细 Response VO")
@Data
public class AppCombinationRecordDetailRespVO {
@Schema(description = "团长的拼团记录", required = true)
private AppCombinationRecordRespVO headRecord;
@Schema(description = "成员的拼团记录", required = true)
private List<AppCombinationRecordRespVO> memberRecords;
@Schema(description = "当前用户参团记录对应的订单编号", required = true, example = "1024") // 如果没参团返回 null
private Long orderId;
}

View File

@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
@Schema(description = "用户 App - 拼团记录 Response VO")
@Data
public class AppCombinationRecordRespVO {
@Schema(description = "拼团记录编号", required = true, example = "1024")
private Long id;
@Schema(description = "拼团活动编号", required = true, example = "1024")
private Long activityId;
@Schema(description = "用户昵称", required = true, example = "1024")
private String nickname;
@Schema(description = "用户头像", required = true, example = "1024")
private String avatar;
@Schema(description = "过期时间", required = true)
private Date expireTime;
@Schema(description = "可参团人数", required = true, example = "10")
private Integer userSize;
@Schema(description = "已参团人数", required = true, example = "5")
private Integer userCount;
@Schema(description = "拼团状态", required = true, example = "1")
private Integer status;
@Schema(description = "商品名字", required = true, example = "我是大黄豆")
private String spuName;
@Schema(description = "商品图片", required = true, example = "https://www.iocoder.cn/1.png")
private String picUrl;
@Schema(description = "拼团金额,单位:分", required = true, example = "100")
private Integer combinationPrice;
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "用户 App - 拼团记录的简要概括 Response VO")
@Data
public class AppCombinationRecordSummaryRespVO {
@Schema(description = "拼团用户数量", required = true, example = "1024")
private Integer userCount;
@Schema(description = "拼团用户头像列表", required = true) // 只返回最近的 7
private List<String> avatars;
}

View File

@ -27,9 +27,6 @@ public class AppSeckillActivitiDetailRespVO {
@Schema(description = "活动结束时间", required = true) @Schema(description = "活动结束时间", required = true)
private LocalDateTime endTime; private LocalDateTime endTime;
private Long successGruopCount;
@Schema(description = "商品 SPU 编号", required = true, example = "2048") @Schema(description = "商品 SPU 编号", required = true, example = "2048")
private Long spuId; private Long spuId;

View File

@ -45,19 +45,26 @@ public interface ErrorCodeConstants {
// ========== Cart 模块 1011002000 ========== // ========== Cart 模块 1011002000 ==========
ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1011002000, "购物车项不存在"); ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1011002000, "购物车项不存在");
// ========== 物流配送模块 1011003000 ========== // ========== Price 相关 1011003000 ============
ErrorCode DELIVERY_EXPRESS_NOT_EXISTS = new ErrorCode(1011003000, "快递公司不存在"); ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1011003000, "支付价格计算异常,原因:价格小于等于 0");
// TODO @jason最好每个模块一段哈express 一个exmpresstemplate 一个pickup 一个 ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY = new ErrorCode(1011003001, "计算快递运费异常,收件人地址编号为空");
ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1011003001, "已经存在该编码的快递公司"); ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1011003002, "计算快递运费异常,找不到对应的运费模板");
ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1011003002, "运费模板不存在");
ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1011003003, "已经存在该运费模板名"); // ========== 物流 Express 模块 1011004000 ==========
ErrorCode DELIVERY_EXPRESS_USER_ADDRESS_IS_EMPTY = new ErrorCode(1011003004, "计算快递运费时,收件人地址编号为空"); // TODO @jaosn这个错误码放到 Price 这块 ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1011004000, "快递公司不存在");
ErrorCode PRODUCT_EXPRESS_TEMPLATE_NOT_FOUND = new ErrorCode(1011003005, "找不到到商品对应的运费模板"); // TODO @jaosn这个错误码放到 Price 这块 ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1011004001, "已经存在该编码的快递公司");
ErrorCode EXPRESS_API_QUERY_ERROR = new ErrorCode(1011003006, "快递查询接口异常"); ErrorCode EXPRESS_CLIENT_NOT_PROVIDE = new ErrorCode(1011004002, "需要接入快递服务商比如【快递100】");
ErrorCode EXPRESS_API_QUERY_FAILED = new ErrorCode(1011003007, "快递查询返回失败, 原因:{}");
ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011003008, "自提门店不存在"); ErrorCode EXPRESS_API_QUERY_ERROR = new ErrorCode(1011004101, "快递查询接口异常");
ErrorCode EXPRESS_API_QUERY_FAILED = new ErrorCode(1011004102, "快递查询返回失败,原因:{}");
// ========== 物流 Template 模块 1011005000 ==========
ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1011005000, "已经存在该运费模板名");
ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1011005001, "运费模板不存在");
// ========== 物流 PICK_UP 模块 1011006000 ==========
ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011006000, "自提门店不存在");
// ========== Price 相关 1011004000 ============
ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1011004000, "支付价格计算异常,原因:价格小于等于 0");
} }

View File

@ -18,6 +18,7 @@ public class AppTradeOrderSettlementReqVO {
@NotNull(message = "交易类型不能为空") @NotNull(message = "交易类型不能为空")
@InEnum(value = TradeOrderTypeEnum.class, message = "交易类型必须是 {value}") @InEnum(value = TradeOrderTypeEnum.class, message = "交易类型必须是 {value}")
@Deprecated // TODO 芋艿后续干掉这个字段对于前端不需要关注这个
private Integer type; private Integer type;
@Schema(description = "商品项数组", required = true) @Schema(description = "商品项数组", required = true)
@ -30,6 +31,17 @@ public class AppTradeOrderSettlementReqVO {
@Schema(description = "优惠劵编号", example = "1024") @Schema(description = "优惠劵编号", example = "1024")
private Long couponId; private Long couponId;
// ========== 秒杀活动相关字段 ==========
@Schema(description = "秒杀活动编号", example = "1024")
private Long seckillActivityId;
// ========== 拼团活动相关字段 ==========
@Schema(description = "拼团活动编号", example = "1024")
private Long combinationActivityId;
@Schema(description = "拼团团长编号", example = "2048")
private Long combinationHeadId;
@Data @Data
@Schema(description = "用户 App - 商品项") @Schema(description = "用户 App - 商品项")
@Valid @Valid

View File

@ -6,12 +6,16 @@ import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplat
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO; import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO; import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;
import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateChargeBO; import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;
import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateFreeBO; import com.google.common.collect.Maps;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
import java.util.List; import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst;
@Mapper @Mapper
public interface DeliveryExpressTemplateConvert { public interface DeliveryExpressTemplateConvert {
@ -49,7 +53,7 @@ public interface DeliveryExpressTemplateConvert {
DeliveryExpressTemplateChargeDO convertTemplateCharge(DeliveryExpressTemplateUpdateReqVO.ExpressTemplateChargeUpdateVO vo); DeliveryExpressTemplateChargeDO convertTemplateCharge(DeliveryExpressTemplateUpdateReqVO.ExpressTemplateChargeUpdateVO vo);
DeliveryExpressTemplateChargeBO convertTemplateCharge(DeliveryExpressTemplateChargeDO bean); DeliveryExpressTemplateRespBO.Charge convertTemplateCharge(DeliveryExpressTemplateChargeDO bean);
default List<DeliveryExpressTemplateChargeDO> convertTemplateChargeList(Long templateId, Integer chargeMode, List<ExpressTemplateChargeBaseVO> list) { default List<DeliveryExpressTemplateChargeDO> convertTemplateChargeList(Long templateId, Integer chargeMode, List<ExpressTemplateChargeBaseVO> list) {
return CollectionUtils.convertList(list, vo -> convertTemplateCharge(templateId, chargeMode, vo)); return CollectionUtils.convertList(list, vo -> convertTemplateCharge(templateId, chargeMode, vo));
@ -61,7 +65,7 @@ public interface DeliveryExpressTemplateConvert {
DeliveryExpressTemplateFreeDO convertTemplateFree(DeliveryExpressTemplateUpdateReqVO.ExpressTemplateFreeUpdateVO vo); DeliveryExpressTemplateFreeDO convertTemplateFree(DeliveryExpressTemplateUpdateReqVO.ExpressTemplateFreeUpdateVO vo);
DeliveryExpressTemplateFreeBO convertTemplateFree(DeliveryExpressTemplateFreeDO bean); DeliveryExpressTemplateRespBO.Free convertTemplateFree(DeliveryExpressTemplateFreeDO bean);
List<ExpressTemplateChargeBaseVO> convertTemplateChargeList(List<DeliveryExpressTemplateChargeDO> list); List<ExpressTemplateChargeBaseVO> convertTemplateChargeList(List<DeliveryExpressTemplateChargeDO> list);
@ -71,4 +75,22 @@ public interface DeliveryExpressTemplateConvert {
return CollectionUtils.convertList(list, vo -> convertTemplateFree(templateId, vo)); return CollectionUtils.convertList(list, vo -> convertTemplateFree(templateId, vo));
} }
default Map<Long, DeliveryExpressTemplateRespBO> convertMap(Integer areaId, List<DeliveryExpressTemplateDO> templateList,
List<DeliveryExpressTemplateChargeDO> chargeList,
List<DeliveryExpressTemplateFreeDO> freeList) {
Map<Long, List<DeliveryExpressTemplateChargeDO>> templateIdChargeMap = convertMultiMap(chargeList,
DeliveryExpressTemplateChargeDO::getTemplateId);
Map<Long, List<DeliveryExpressTemplateFreeDO>> templateIdFreeMap = convertMultiMap(freeList,
DeliveryExpressTemplateFreeDO::getTemplateId);
// 组合运费模板配置 RespBO
Map<Long, DeliveryExpressTemplateRespBO> result = Maps.newHashMapWithExpectedSize(templateList.size());
templateList.forEach(template -> {
DeliveryExpressTemplateRespBO bo = new DeliveryExpressTemplateRespBO()
.setChargeMode(template.getChargeMode())
.setCharge(convertTemplateCharge(findFirst(templateIdChargeMap.get(template.getId()), charge -> charge.getAreaIds().contains(areaId))))
.setFree(convertTemplateFree(findFirst(templateIdFreeMap.get(template.getId()), free -> free.getAreaIds().contains(areaId))));
result.put(template.getId(), bo);
});
return result;
}
} }

View File

@ -2,13 +2,11 @@ package cn.iocoder.yudao.module.trade.dal.mysql.delivery;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO; import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List; import java.util.List;
@Mapper @Mapper
@ -23,6 +21,11 @@ public interface DeliveryExpressTemplateChargeMapper extends BaseMapperX<Deliver
return delete(new LambdaQueryWrapper<DeliveryExpressTemplateChargeDO>() return delete(new LambdaQueryWrapper<DeliveryExpressTemplateChargeDO>()
.eq(DeliveryExpressTemplateChargeDO::getTemplateId, templateId)); .eq(DeliveryExpressTemplateChargeDO::getTemplateId, templateId));
} }
default List<DeliveryExpressTemplateChargeDO> selectByTemplateIds(Collection<Long> templateIds) {
return selectList(DeliveryExpressTemplateChargeDO::getTemplateId, templateIds);
}
} }

View File

@ -1,14 +1,11 @@
package cn.iocoder.yudao.module.trade.dal.mysql.delivery; package cn.iocoder.yudao.module.trade.dal.mysql.delivery;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO; import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List; import java.util.List;
@Mapper @Mapper
@ -23,6 +20,10 @@ public interface DeliveryExpressTemplateFreeMapper extends BaseMapperX<DeliveryE
return delete(new LambdaQueryWrapper<DeliveryExpressTemplateFreeDO>() return delete(new LambdaQueryWrapper<DeliveryExpressTemplateFreeDO>()
.eq(DeliveryExpressTemplateFreeDO::getTemplateId, templateId)); .eq(DeliveryExpressTemplateFreeDO::getTemplateId, templateId));
} }
default List<DeliveryExpressTemplateFreeDO> selectListByTemplateIds(Collection<Long> templateIds) {
return selectList(DeliveryExpressTemplateFreeDO::getTemplateId, templateIds);
}
} }

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.trade.framework.delivery.config;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.ExpressClientFactoryImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* 快递客户端端配置类
*
* 1. 快递客户端工厂 {@link ExpressClientFactory}
* 2. 默认的快递客户端实现 {@link ExpressClient}
*
* @author jason
*/
@Configuration(proxyBeanMethods = false)
public class ExpressClientConfig {
@Bean
public ExpressClientFactory expressClientFactory(TradeExpressProperties tradeExpressProperties,
RestTemplate restTemplate) {
return new ExpressClientFactoryImpl(tradeExpressProperties, restTemplate);
}
@Bean
public ExpressClient defaultExpressClient(ExpressClientFactory expressClientFactory) {
return expressClientFactory.getDefaultExpressClient();
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.trade.framework.delivery.config; package cn.iocoder.yudao.module.trade.framework.delivery.config;
import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProviderEnum; import cn.iocoder.yudao.module.trade.framework.delivery.core.enums.ExpressClientEnum;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -9,27 +9,25 @@ import org.springframework.validation.annotation.Validated;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
// TODO @jasonTradeExpressProperties更通用哈
// TODO @芋艿未来要不要放数据库中考虑 saas 多租户时不同租户使用不同的配置 // TODO @芋艿未来要不要放数据库中考虑 saas 多租户时不同租户使用不同的配置
/** /**
* 交易快递查询的配置项 * 交易运费快递的配置项
* *
* @author jason * @author jason
*/ */
@Component @Component
@ConfigurationProperties(prefix = "yudao.trade.express.query") @ConfigurationProperties(prefix = "yudao.trade.express")
@Data @Data
@Validated @Validated
public class TradeExpressQueryProperties { public class TradeExpressProperties {
/** /**
* 快递查询服务商 * 快递客户端
* *
* 如果未配置默认使用快递鸟 * 默认不提供需要提醒用户配置一个快递服务商
*/ */
// TODO @jason可以把 expressQueryProvider 改成 client 变量更简洁一点 private ExpressClientEnum client = ExpressClientEnum.NOT_PROVIDE;
private ExpressQueryProviderEnum expressQueryProvider; // TODO @jaosn默认值可以通过属性直接赋值哈
// TODO @jason需要考虑下用户只配置了其中一个
/** /**
* 快递鸟配置 * 快递鸟配置
*/ */

View File

@ -1,24 +0,0 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryRespDTO;
import java.util.List;
// TODO @jason可以改成 ExpressClient未来可能还对接别的接口噢
/**
* 快递查询客户端
*
* @author jason
*/
public interface ExpressQueryClient {
/**
* 快递实时查询
*
* @param reqDTO 查询请求参数
*/
// TODO @jason可以改成 getExpressTrackList返回字段可以参考 https://doc.youzanyun.com/detail/API/0/5 响应的 data
List<ExpressQueryRespDTO> realTimeQuery(ExpressQueryReqDTO reqDTO);
}

View File

@ -1,22 +0,0 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryRespDTO;
import java.util.List;
/**
* 快递查询服务商
*
* @author jason
*/
public interface ExpressQueryProvider {
/**
* 快递实时查询
*
* @param reqDTO 查询请求参数
*/
List<ExpressQueryRespDTO> realTimeQueryExpress(ExpressQueryReqDTO reqDTO);
}

View File

@ -1,33 +0,0 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core;
import lombok.Getter;
/**
* 快递查询服务商枚举
*
* @author jason
*/
@Getter
public enum ExpressQueryProviderEnum {
KD_NIAO("kd-niao", "快递鸟"),
KD_100("kd-100", "快递100");
/**
* 快递服务商唯一编码
*/
private final String code;
/**
* 快递服务商名称
*/
private final String name;
// TODO @jaosn@AllArgsConstructor 可以替代哈
ExpressQueryProviderEnum(String code, String name) {
this.code = code;
this.name = name;
}
}

View File

@ -1,19 +0,0 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core;
/**
* 快递服务商工厂用于创建和缓存快递服务商服务
*
* @author jason
*/
public interface ExpressQueryProviderFactory {
/**
* 通过枚举获取快递查询服务商
*
* 如果不存在就创建一个对应的快递查询服务商
*
* @param queryProviderEnum 快递服务商枚举
*/
ExpressQueryProvider getOrCreateExpressQueryProvider(ExpressQueryProviderEnum queryProviderEnum);
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.client;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
import java.util.List;
/**
* 快递客户端接口
*
* @author jason
*/
public interface ExpressClient {
/**
* 快递实时查询
*
* @param reqDTO 查询请求参数
*/
// TODO @jason返回字段可以参考 https://doc.youzanyun.com/detail/API/0/5 响应的 data
List<ExpressTrackRespDTO> getExpressTrackList(ExpressTrackQueryReqDTO reqDTO);
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.client;
import cn.iocoder.yudao.module.trade.framework.delivery.core.enums.ExpressClientEnum;
/**
* 快递客户端工厂接口用于创建和缓存快递客户端
*
* @author jason
*/
public interface ExpressClientFactory {
/**
* 获取默认的快递客户端
*/
ExpressClient getDefaultExpressClient();
/**
* 通过枚举获取快递客户端如果不存在就创建一个对应快递客户端
*
* @param clientEnum 快递客户端枚举
*/
ExpressClient getOrCreateExpressClient(ExpressClientEnum clientEnum);
}

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.client.convert;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100.Kd100ExpressQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100.Kd100ExpressQueryRespDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryRespDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface ExpressQueryConvert {
ExpressQueryConvert INSTANCE = Mappers.getMapper(ExpressQueryConvert.class);
List<ExpressTrackRespDTO> convertList(List<KdNiaoExpressQueryRespDTO.ExpressTrack> expressTrackList);
List<ExpressTrackRespDTO> convertList2(List<Kd100ExpressQueryRespDTO.ExpressTrack> expressTrackList);
KdNiaoExpressQueryReqDTO convert(ExpressTrackQueryReqDTO dto);
Kd100ExpressQueryReqDTO convert2(ExpressTrackQueryReqDTO dto);
}

View File

@ -1,23 +1,22 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.dto; package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import lombok.Data; import lombok.Data;
/** /**
* 快递查询 Req DTO * 快递轨迹的查询 Req DTO
* *
* @author jason * @author jason
*/ */
@Data @Data
public class ExpressQueryReqDTO { public class ExpressTrackQueryReqDTO {
/** /**
* 快递公司编码 * 快递公司编码
* *
* 对应 {@link DeliveryExpressDO#getCode()} * 对应 {@link DeliveryExpressDO#getCode()}
*/ */
// TODO @jaosn要不改成 expressCode项目里使用这个哈 private String expressCode;
private String expressCompanyCode;
/** /**
* 发货快递单号 * 发货快递单号

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.dto; package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto;
import lombok.Data; import lombok.Data;
@ -8,7 +8,7 @@ import lombok.Data;
* @author jason * @author jason
*/ */
@Data @Data
public class ExpressQueryRespDTO { public class ExpressTrackRespDTO {
// TODO @jasonLocalDateTime // TODO @jasonLocalDateTime
/** /**

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kd100; package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
@ -13,12 +13,11 @@ import lombok.Data;
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
public class Kd100ExpressQueryReqDTO { public class Kd100ExpressQueryReqDTO {
// TODO @jaosn要不改成 expressCode项目里使用这个哈
/** /**
* 快递公司编码 * 快递公司编码
*/ */
@JsonProperty("com") @JsonProperty("com")
private String expressCompanyCode; private String expressCode;
/** /**
* 快递单号 * 快递单号

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kd100; package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kdniao; package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
@ -13,12 +13,11 @@ import lombok.Data;
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
public class KdNiaoExpressQueryReqDTO { public class KdNiaoExpressQueryReqDTO {
// TODO @jaosn要不改成 expressCode项目里使用这个哈
/** /**
* 快递公司编码 * 快递公司编码
*/ */
@JsonProperty("ShipperCode") @JsonProperty("ShipperCode")
private String expressCompanyCode; private String expressCode;
/** /**
* 快递单号 * 快递单号
*/ */

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kdniao; package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data; import lombok.Data;

View File

@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kd100.Kd100ExpressClient;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kdniao.KdNiaoExpressClient;
import cn.iocoder.yudao.module.trade.framework.delivery.core.enums.ExpressClientEnum;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory;
import lombok.AllArgsConstructor;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 快递客户端工厂实现类
*
* @author jason
*/
@AllArgsConstructor
public class ExpressClientFactoryImpl implements ExpressClientFactory {
private final Map<ExpressClientEnum, ExpressClient> clientMap = new ConcurrentHashMap<>(8);
private final TradeExpressProperties tradeExpressProperties;
private final RestTemplate restTemplate;
@Override
public ExpressClient getDefaultExpressClient() {
ExpressClient defaultClient = getOrCreateExpressClient(tradeExpressProperties.getClient());
Assert.notNull("默认的快递客户端不能为空");
return defaultClient;
}
@Override
public ExpressClient getOrCreateExpressClient(ExpressClientEnum clientEnum) {
return clientMap.computeIfAbsent(clientEnum,
client -> createExpressClient(client, tradeExpressProperties));
}
private ExpressClient createExpressClient(ExpressClientEnum queryProviderEnum,
TradeExpressProperties tradeExpressProperties) {
switch (queryProviderEnum) {
case NOT_PROVIDE:
return new NoProvideExpressClient();
case KD_NIAO:
return new KdNiaoExpressClient(restTemplate, tradeExpressProperties.getKdNiao());
case KD_100:
return new Kd100ExpressClient(restTemplate, tradeExpressProperties.getKd100());
}
return null;
}
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_CLIENT_NOT_PROVIDE;
/**
* 未实现的快递客户端用来提醒用户需要接入快递服务商
*
* @author jason
*/
public class NoProvideExpressClient implements ExpressClient {
@Override
public List<ExpressTrackRespDTO> getExpressTrackList(ExpressTrackQueryReqDTO reqDTO) {
throw exception(EXPRESS_CLIENT_NOT_PROVIDE);
}
}

View File

@ -1,15 +1,16 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.impl; package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kd100;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.HexUtil; import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.crypto.digest.DigestUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressQueryProperties; import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProvider; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryRespDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kd100.Kd100ExpressQueryReqDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100.Kd100ExpressQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kd100.Kd100ExpressQueryRespDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100.Kd100ExpressQueryRespDTO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*; import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
@ -23,48 +24,40 @@ import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_API_QUERY_ERROR; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_API_QUERY_ERROR;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_API_QUERY_FAILED; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_API_QUERY_FAILED;
import static cn.iocoder.yudao.module.trade.framework.delivery.core.convert.ExpressQueryConvert.INSTANCE; import static cn.iocoder.yudao.module.trade.framework.delivery.core.client.convert.ExpressQueryConvert.INSTANCE;
// TODO @jason可以参考 KdNiaoExpressQueryProvider 建议改改哈
/** /**
* 快递 100 服务商 * 快递 100 客户端
* *
* @author jason * @author jason
*/ */
@Slf4j @Slf4j
public class Kd100ExpressQueryProvider implements ExpressQueryProvider { @AllArgsConstructor
public class Kd100ExpressClient implements ExpressClient {
private static final String REAL_TIME_QUERY_URL = "https://poll.kuaidi100.com/poll/query.do"; private static final String REAL_TIME_QUERY_URL = "https://poll.kuaidi100.com/poll/query.do";
private final RestTemplate restTemplate; private final RestTemplate restTemplate;
private final TradeExpressQueryProperties.Kd100Config config; private final TradeExpressProperties.Kd100Config config;
public Kd100ExpressQueryProvider(RestTemplate restTemplate, TradeExpressQueryProperties.Kd100Config config) {
this.restTemplate = restTemplate;
this.config = config;
}
@Override @Override
public List<ExpressQueryRespDTO> realTimeQueryExpress(ExpressQueryReqDTO reqDTO) { public List<ExpressTrackRespDTO> getExpressTrackList(ExpressTrackQueryReqDTO reqDTO) {
// 发起查询 // 发起查询
Kd100ExpressQueryReqDTO kd100ReqParam = INSTANCE.convert2(reqDTO); Kd100ExpressQueryReqDTO kd100ReqParam = INSTANCE.convert2(reqDTO);
kd100ReqParam.setExpressCompanyCode(kd100ReqParam.getExpressCompanyCode().toLowerCase()); // 快递公司编码需要转成小写 kd100ReqParam.setExpressCode(kd100ReqParam.getExpressCode().toLowerCase()); // 快递公司编码需要转成小写
Kd100ExpressQueryRespDTO respDTO = sendExpressQueryReq(REAL_TIME_QUERY_URL, kd100ReqParam, Kd100ExpressQueryRespDTO respDTO = requestExpressQuery(REAL_TIME_QUERY_URL, kd100ReqParam,
Kd100ExpressQueryRespDTO.class); Kd100ExpressQueryRespDTO.class);
log.debug("[realTimeQueryExpress][快递 100 接口 查询接口返回 {}]", respDTO); log.debug("[getExpressTrackList][快递 100 接口 查询接口返回 {}]", respDTO);
// 处理结果 // 处理结果
if (Objects.equals("false", respDTO.getResult())) { if (Objects.equals("false", respDTO.getResult())) {
log.error("[realTimeQueryExpress][快递 100 接口 返回失败 {}]", respDTO.getMessage()); log.error("[getExpressTrackList][快递 100 接口 返回失败 {}]", respDTO.getMessage());
throw exception(EXPRESS_API_QUERY_FAILED, respDTO.getMessage()); throw exception(EXPRESS_API_QUERY_FAILED, respDTO.getMessage());
// TODO @jsonelse 可以不用写哈
} else {
// TODO @jasonconvertList2 如果空应该返回 list
if (CollUtil.isNotEmpty(respDTO.getTracks())) {
return INSTANCE.convertList2(respDTO.getTracks());
} else {
return Collections.emptyList();
}
} }
if (CollUtil.isEmpty(respDTO.getTracks())) {
return Collections.emptyList();
}
return INSTANCE.convertList2(respDTO.getTracks());
} }
/** /**
@ -76,8 +69,7 @@ public class Kd100ExpressQueryProvider implements ExpressQueryProvider {
* @param <Req> 每个请求的请求结构 Req DTO * @param <Req> 每个请求的请求结构 Req DTO
* @param <Resp> 每个请求的响应结构 Resp DTO * @param <Resp> 每个请求的响应结构 Resp DTO
*/ */
// TODO @jason可以改成 request发起请求哈 private <Req, Resp> Resp requestExpressQuery(String url, Req req, Class<Resp> respClass) {
private <Req, Resp> Resp sendExpressQueryReq(String url, Req req, Class<Resp> respClass) {
// 请求头 // 请求头
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
@ -92,23 +84,18 @@ public class Kd100ExpressQueryProvider implements ExpressQueryProvider {
log.debug("[sendExpressQueryReq][快递 100 接口的请求参数: {}]", requestBody); log.debug("[sendExpressQueryReq][快递 100 接口的请求参数: {}]", requestBody);
// 发送请求 // 发送请求
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers); HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
// TODO @jason可以使用 restTemplate post 方法哇
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
log.debug("[sendExpressQueryReq][快递 100 接口响应结果 {}]", responseEntity); log.debug("[sendExpressQueryReq][快递 100 接口响应结果 {}]", responseEntity);
// 处理响应 // 处理响应
// TODO @jasonif return 原则if (!responseEntity.getStatusCode().is2xxSuccessful()) 抛出异常接着处理成功的 if (!responseEntity.getStatusCode().is2xxSuccessful()) {
if (responseEntity.getStatusCode().is2xxSuccessful()) {
String response = responseEntity.getBody();
return JsonUtils.parseObject(response, respClass);
} else {
throw exception(EXPRESS_API_QUERY_ERROR); throw exception(EXPRESS_API_QUERY_ERROR);
} }
return JsonUtils.parseObject(responseEntity.getBody(), respClass);
} }
private String generateReqSign(String param, String key, String customer) { private String generateReqSign(String param, String key, String customer) {
String plainText = String.format("%s%s%s", param, key, customer); String plainText = String.format("%s%s%s", param, key, customer);
// TODO @jasonDigestUtil.md5Hex(plainText);
return HexUtil.encodeHexStr(DigestUtil.md5(plainText), false); return HexUtil.encodeHexStr(DigestUtil.md5(plainText), false);
} }

View File

@ -1,16 +1,17 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.impl; package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kdniao;
import cn.hutool.core.codec.Base64; import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.net.URLEncodeUtil; import cn.hutool.core.net.URLEncodeUtil;
import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.crypto.digest.DigestUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressQueryProperties; import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProvider; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryRespDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kdniao.KdNiaoExpressQueryReqDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kdniao.KdNiaoExpressQueryRespDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryRespDTO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*; import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
@ -23,15 +24,16 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_API_QUERY_FAILED; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_API_QUERY_FAILED;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_API_QUERY_ERROR; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_API_QUERY_ERROR;
import static cn.iocoder.yudao.module.trade.framework.delivery.core.convert.ExpressQueryConvert.INSTANCE; import static cn.iocoder.yudao.module.trade.framework.delivery.core.client.convert.ExpressQueryConvert.INSTANCE;
/** /**
* 快递鸟服务商 * 快递鸟客户端
* *
* @author jason * @author jason
*/ */
@Slf4j @Slf4j
public class KdNiaoExpressQueryProvider implements ExpressQueryProvider { @AllArgsConstructor
public class KdNiaoExpressClient implements ExpressClient {
private static final String REAL_TIME_QUERY_URL = "https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx"; private static final String REAL_TIME_QUERY_URL = "https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx";
@ -39,15 +41,8 @@ public class KdNiaoExpressQueryProvider implements ExpressQueryProvider {
* 快递鸟即时查询免费版 RequestType * 快递鸟即时查询免费版 RequestType
*/ */
private static final String REAL_TIME_FREE_REQ_TYPE = "1002"; private static final String REAL_TIME_FREE_REQ_TYPE = "1002";
private final RestTemplate restTemplate; private final RestTemplate restTemplate;
private final TradeExpressQueryProperties.KdNiaoConfig config; private final TradeExpressProperties.KdNiaoConfig config;
// TODO @jason可以改成 lombok
public KdNiaoExpressQueryProvider(RestTemplate restTemplate, TradeExpressQueryProperties.KdNiaoConfig config) {
this.restTemplate = restTemplate;
this.config = config;
}
/** /**
* 快递鸟即时查询免费版本 * 快递鸟即时查询免费版本
@ -56,26 +51,27 @@ public class KdNiaoExpressQueryProvider implements ExpressQueryProvider {
* @param reqDTO 查询请求参数 * @param reqDTO 查询请求参数
*/ */
@Override @Override
public List<ExpressQueryRespDTO> realTimeQueryExpress(ExpressQueryReqDTO reqDTO) { public List<ExpressTrackRespDTO> getExpressTrackList(ExpressTrackQueryReqDTO reqDTO) {
KdNiaoExpressQueryReqDTO kdNiaoReqData = INSTANCE.convert(reqDTO); KdNiaoExpressQueryReqDTO kdNiaoReqData = INSTANCE.convert(reqDTO);
// 快递公司编码需要转成大写 // 快递公司编码需要转成大写
kdNiaoReqData.setExpressCompanyCode(reqDTO.getExpressCompanyCode().toUpperCase()); kdNiaoReqData.setExpressCode(reqDTO.getExpressCode().toUpperCase());
KdNiaoExpressQueryRespDTO respDTO = sendKdNiaoApiRequest(REAL_TIME_QUERY_URL, REAL_TIME_FREE_REQ_TYPE, KdNiaoExpressQueryRespDTO respDTO = requestKdNiaoApi(REAL_TIME_QUERY_URL, REAL_TIME_FREE_REQ_TYPE,
kdNiaoReqData, KdNiaoExpressQueryRespDTO.class); kdNiaoReqData, KdNiaoExpressQueryRespDTO.class);
log.debug("[realTimeQueryExpress][快递鸟即时查询接口返回 {}]", respDTO); log.debug("[getExpressTrackList][快递鸟即时查询接口返回 {}]", respDTO);
if(!respDTO.getSuccess()){
throw exception(EXPRESS_API_QUERY_FAILED, respDTO.getReason()); // 处理结果
}else{ if (respDTO == null || !respDTO.getSuccess()) {
if (CollUtil.isNotEmpty(respDTO.getTracks())) { throw exception(EXPRESS_API_QUERY_FAILED, respDTO == null ? "" : respDTO.getReason());
return INSTANCE.convertList(respDTO.getTracks());
}else{
return Collections.emptyList();
}
} }
if (CollUtil.isNotEmpty(respDTO.getTracks())) {
return Collections.emptyList();
}
return INSTANCE.convertList(respDTO.getTracks());
} }
/** /**
* 快递鸟 通用的 API 请求, 暂时没有其他应用场景 暂时放这里 * 快递鸟 通用的 API 请求暂时没有其他应用场景 暂时放这里
*
* @param url 请求 url * @param url 请求 url
* @param requestType 对应的请求指令 (快递鸟的RequestType) * @param requestType 对应的请求指令 (快递鸟的RequestType)
* @param req 对应请求的请求参数 * @param req 对应请求的请求参数
@ -83,8 +79,8 @@ public class KdNiaoExpressQueryProvider implements ExpressQueryProvider {
* @param <Req> 每个请求的请求结构 Req DTO * @param <Req> 每个请求的请求结构 Req DTO
* @param <Resp> 每个请求的响应结构 Resp DTO * @param <Resp> 每个请求的响应结构 Resp DTO
*/ */
private <Req, Resp> Resp sendKdNiaoApiRequest(String url, String requestType, Req req, private <Req, Resp> Resp requestKdNiaoApi(String url, String requestType, Req req,
Class<Resp> respClass){ Class<Resp> respClass){
// 请求头 // 请求头
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
@ -97,19 +93,16 @@ public class KdNiaoExpressQueryProvider implements ExpressQueryProvider {
requestBody.add("EBusinessID", config.getBusinessId()); requestBody.add("EBusinessID", config.getBusinessId());
requestBody.add("DataSign", dataSign); requestBody.add("DataSign", dataSign);
requestBody.add("RequestType", requestType); requestBody.add("RequestType", requestType);
log.debug("[sendKdNiaoApiRequest][快递鸟接口 RequestType : {}, 的请求参数 {}]", requestType, requestBody); log.debug("[requestKdNiaoApi][快递鸟接口 RequestType : {}, 的请求参数 {}]", requestType, requestBody);
// 发送请求 // 发送请求
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers); HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
log.debug("快递鸟接口 RequestType : {}, 的响应结果 {}", requestType, responseEntity); log.debug("快递鸟接口 RequestType : {}, 的响应结果 {}", requestType, responseEntity);
// 处理响应 // 处理响应
if (responseEntity.getStatusCode().is2xxSuccessful()) { if (!responseEntity.getStatusCode().is2xxSuccessful()) {
String response = responseEntity.getBody();
return JsonUtils.parseObject(response, respClass);
} else {
throw exception(EXPRESS_API_QUERY_ERROR); throw exception(EXPRESS_API_QUERY_ERROR);
} }
return JsonUtils.parseObject(responseEntity.getBody(), respClass);
} }
/** /**

View File

@ -1,27 +0,0 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.convert;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryRespDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kd100.Kd100ExpressQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kd100.Kd100ExpressQueryRespDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kdniao.KdNiaoExpressQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kdniao.KdNiaoExpressQueryRespDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface ExpressQueryConvert {
ExpressQueryConvert INSTANCE = Mappers.getMapper(ExpressQueryConvert.class);
List<ExpressQueryRespDTO> convertList(List<KdNiaoExpressQueryRespDTO.ExpressTrack> expressTrackList);
List<ExpressQueryRespDTO> convertList2(List<Kd100ExpressQueryRespDTO.ExpressTrack> expressTrackList);
KdNiaoExpressQueryReqDTO convert(ExpressQueryReqDTO dto);
Kd100ExpressQueryReqDTO convert2(ExpressQueryReqDTO dto);
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 快递客户端枚举
*
* @author jason
*/
@Getter
@AllArgsConstructor
public enum ExpressClientEnum {
NOT_PROVIDE("not-provide","未提供"),
KD_NIAO("kd-niao", "快递鸟"),
KD_100("kd-100", "快递100");
/**
* 快递服务商唯一编码
*/
private final String code;
/**
* 快递服务商名称
*/
private final String name;
}

View File

@ -1,65 +0,0 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.impl;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressQueryProperties;
import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryClient;
import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProvider;
import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProviderEnum;
import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProviderFactory;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProviderEnum.KD_NIAO;
// TODO @jason可以把整体包结构调整下参考 sms client 的方式
// + config
// + core
// client
// + dto
// + impl里面可以放 kdniaoclientkd100client
// ExpressClient
// ExpressClientFactory: 通过它直接获取默认和创建默认的 Client
// enums
/**
* 快递查询客户端实现
*
* @author jason
*/
@Component
@Slf4j
public class ExpressQueryClientImpl implements ExpressQueryClient {
@Resource
private ExpressQueryProviderFactory expressQueryProviderFactory;
@Resource
private TradeExpressQueryProperties tradeExpressQueryProperties;
private ExpressQueryProvider expressQueryProvider;
@PostConstruct
private void init() {
// 如果未设置默认使用快递鸟
ExpressQueryProviderEnum queryProvider = tradeExpressQueryProperties.getExpressQueryProvider();
if (queryProvider == null) {
queryProvider = KD_NIAO;
}
// 创建客户端
expressQueryProvider = expressQueryProviderFactory.getOrCreateExpressQueryProvider(queryProvider);
if (expressQueryProvider == null) {
log.error("获取创建快递查询服务商{}失败,请检查相关配置", queryProvider);
}
Assert.notNull(expressQueryProvider, "快递查询服务商不能为空");
}
@Override
public List<ExpressQueryRespDTO> realTimeQuery(ExpressQueryReqDTO reqDTO) {
return expressQueryProvider.realTimeQueryExpress(reqDTO);
}
}

View File

@ -1,48 +0,0 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.impl;
import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressQueryProperties;
import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProvider;
import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProviderEnum;
import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProviderFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* // TODO @jason注释不全
* @author jason
*/
@Component
public class ExpressQueryProviderFactoryImpl implements ExpressQueryProviderFactory {
private final Map<ExpressQueryProviderEnum, ExpressQueryProvider> providerMap = new ConcurrentHashMap<>(8);
@Resource
private TradeExpressQueryProperties tradeExpressQueryProperties;
@Resource
private RestTemplate restTemplate;
@Override
public ExpressQueryProvider getOrCreateExpressQueryProvider(ExpressQueryProviderEnum queryProviderEnum) {
return providerMap.computeIfAbsent(queryProviderEnum,
provider -> createExpressQueryProvider(provider, tradeExpressQueryProperties));
}
private ExpressQueryProvider createExpressQueryProvider(ExpressQueryProviderEnum queryProviderEnum,
TradeExpressQueryProperties tradeExpressQueryProperties) {
// TODO @jason是不是直接 return 就好啦更简洁一点
ExpressQueryProvider result = null;
switch (queryProviderEnum) {
case KD_NIAO:
result = new KdNiaoExpressQueryProvider(restTemplate, tradeExpressQueryProperties.getKdNiao());
break;
case KD_100:
result = new Kd100ExpressQueryProvider(restTemplate, tradeExpressQueryProperties.getKd100());
break;
}
return result;
}
}

View File

@ -75,7 +75,7 @@ public class DeliveryExpressServiceImpl implements DeliveryExpressService {
} }
private void validateDeliveryExpressExists(Long id) { private void validateDeliveryExpressExists(Long id) {
if (deliveryExpressMapper.selectById(id) == null) { if (deliveryExpressMapper.selectById(id) == null) {
throw exception(DELIVERY_EXPRESS_NOT_EXISTS); throw exception(EXPRESS_NOT_EXISTS);
} }
} }

View File

@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplat
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO; import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateUpdateReqVO; import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateUpdateReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO; import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Collection; import java.util.Collection;
@ -83,14 +83,13 @@ public interface DeliveryExpressTemplateService {
*/ */
DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId); DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId);
// TODO @jason可以把 spuIds 改成传递 ids 价格计算那 TradePriceCalculateRespBO 冗余好 templateId 字段目的是减少重复的查询
/** /**
* 基于指定的 SPU 编号数组和收件人地址区域编号. 获取匹配运费模板 * 基于运费模板编号数组和收件人地址区域编号获取匹配运费模板
* *
* @param spuIds SPU 编号列表 * @param ids 编号列表
* @param areaId 区域编号 * @param areaId 区域编号
* @return Map (spuId -> 运费模板设置) * @return Map (templateId -> 运费模板设置)
*/ */
Map<Long, SpuDeliveryExpressTemplateRespBO> getExpressTemplateMapBySpuIdsAndArea(Collection<Long> spuIds, Integer areaId); Map<Long, DeliveryExpressTemplateRespBO> getExpressTemplateMapByIdsAndArea(Collection<Long> ids, Integer areaId);
} }

View File

@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.trade.service.delivery;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateCreateReqVO; import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateDetailRespVO; import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO; import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO;
@ -15,9 +13,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemp
import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateChargeMapper; import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateChargeMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateFreeMapper; import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateFreeMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateMapper; import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateMapper;
import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateChargeBO; import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;
import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateFreeBO;
import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -46,8 +42,6 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla
private DeliveryExpressTemplateChargeMapper expressTemplateChargeMapper; private DeliveryExpressTemplateChargeMapper expressTemplateChargeMapper;
@Resource @Resource
private DeliveryExpressTemplateFreeMapper expressTemplateFreeMapper; private DeliveryExpressTemplateFreeMapper expressTemplateFreeMapper;
@Resource
private ProductSpuApi productSpuApi;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -228,41 +222,30 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla
} }
@Override @Override
public Map<Long, SpuDeliveryExpressTemplateRespBO> getExpressTemplateMapBySpuIdsAndArea(Collection<Long> spuIds, Integer areaId) { public Map<Long, DeliveryExpressTemplateRespBO> getExpressTemplateMapByIdsAndArea(Collection<Long> ids, Integer areaId) {
Assert.notNull(areaId, "区域编号 {} 不能为空", areaId); Assert.notNull(areaId, "区域编号 {} 不能为空", areaId);
List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds); // 查询 template 数组
if (CollUtil.isEmpty(spuList)) { if (CollUtil.isEmpty(ids)) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getDeliveryTemplateId); List<DeliveryExpressTemplateDO> templateList = expressTemplateMapper.selectBatchIds(ids);
List<DeliveryExpressTemplateDO> templateList = expressTemplateMapper.selectBatchIds(spuMap.keySet()); // 查询 templateCharge 数组
Map<Long, SpuDeliveryExpressTemplateRespBO> result = new HashMap<>(templateList.size()); List<DeliveryExpressTemplateChargeDO> chargeList = expressTemplateChargeMapper.selectByTemplateIds(ids);
templateList.forEach(item -> { // 查询 templateFree 数组
ProductSpuRespDTO spu = spuMap.get(item.getId()); List<DeliveryExpressTemplateFreeDO> freeList = expressTemplateFreeMapper.selectListByTemplateIds(ids);
if (spu == null) {
return; // 组合运费模板配置 RespBO
} return INSTANCE.convertMap(areaId, templateList, chargeList, freeList);
// TODO @jason避免循环查询最好类似 expressTemplateMapper.selectBatchIds(spuMap.keySet()); 批量查询内存组合
SpuDeliveryExpressTemplateRespBO bo = new SpuDeliveryExpressTemplateRespBO()
.setChargeMode(item.getChargeMode())
.setTemplateCharge(findMatchExpressTemplateCharge(item.getId(), areaId))
.setTemplateFree(findMatchExpressTemplateFree(item.getId(), areaId));
result.put(spu.getId(), bo);
});
return result;
} }
private DeliveryExpressTemplateChargeBO findMatchExpressTemplateCharge(Long templateId, Integer areaId) { private DeliveryExpressTemplateRespBO.Charge findMatchExpressTemplateCharge(
return INSTANCE.convertTemplateCharge(findFirst( List<DeliveryExpressTemplateChargeDO> templateChargeList, Integer areaId) {
expressTemplateChargeMapper.selectListByTemplateId(templateId), item -> item.getAreaIds().contains(areaId) return INSTANCE.convertTemplateCharge(findFirst(templateChargeList, item -> item.getAreaIds().contains(areaId)));
)
);
} }
private DeliveryExpressTemplateFreeBO findMatchExpressTemplateFree(Long templateId, Integer areaId) { private DeliveryExpressTemplateRespBO.Free findMatchExpressTemplateFree(
return INSTANCE.convertTemplateFree(findFirst( List<DeliveryExpressTemplateFreeDO> templateFreeList, Integer areaId) {
expressTemplateFreeMapper.selectListByTemplateId(templateId), item -> item.getAreaIds().contains(areaId) return INSTANCE.convertTemplateFree(findFirst(templateFreeList, item -> item.getAreaIds().contains(areaId)));
));
} }
} }

View File

@ -1,29 +0,0 @@
package cn.iocoder.yudao.module.trade.service.delivery.bo;
import lombok.Data;
/**
* 快递运费模板费用配置 BO
*
* @author jason
*/
@Data
public class DeliveryExpressTemplateChargeBO {
/**
* 首件数量(件数,重量或体积)
*/
private Double startCount;
/**
* 起步价单位
*/
private Integer startPrice;
/**
* 续件数量(, 重量或体积)
*/
private Double extraCount;
/**
* 额外价单位
*/
private Integer extraPrice;
}

View File

@ -1,26 +0,0 @@
package cn.iocoder.yudao.module.trade.service.delivery.bo;
import lombok.Data;
/**
* 快递运费模板包邮配置 BO
*
* @author jason
*/
@Data
public class DeliveryExpressTemplateFreeBO {
/**
* 包邮金额单位
*
* 订单总金额 > 包邮金额时才免运费
*/
private Integer freePrice;
/**
* 包邮件数
*
* 订单总件数 > 包邮件数时才免运费
*/
private Integer freeCount;
}

View File

@ -0,0 +1,80 @@
package cn.iocoder.yudao.module.trade.service.delivery.bo;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
import lombok.Data;
/**
* 运费模板配置 Resp BO
*
* @author jason
*/
@Data
public class DeliveryExpressTemplateRespBO {
/**
* 配送计费方式
*
* 枚举 {@link DeliveryExpressChargeModeEnum}
*/
private Integer chargeMode;
/**
* 运费模板快递运费设置
*/
private Charge charge;
/**
* 运费模板包邮设置
*/
private Free free;
/**
* 快递运费模板费用配置 BO
*
* @author jason
*/
@Data
public static class Charge {
/**
* 首件数量(件数,重量或体积)
*/
private Double startCount;
/**
* 起步价单位
*/
private Integer startPrice;
/**
* 续件数量(, 重量或体积)
*/
private Double extraCount;
/**
* 额外价单位
*/
private Integer extraPrice;
}
/**
* 快递运费模板包邮配置 BO
*
* @author jason
*/
@Data
public static class Free {
/**
* 包邮金额单位
*
* 订单总金额 > 包邮金额时才免运费
*/
private Integer freePrice;
/**
* 包邮件数
*
* 订单总件数 > 包邮件数时才免运费
*/
private Integer freeCount;
}
}

View File

@ -1,33 +0,0 @@
package cn.iocoder.yudao.module.trade.service.delivery.bo;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
import lombok.Data;
/**
* SPU 运费模板配置 Resp BO
*
* @author jason
*/
@Data
public class SpuDeliveryExpressTemplateRespBO {
/**
* 配送计费方式
*
* 枚举 {@link DeliveryExpressChargeModeEnum}
*/
private Integer chargeMode;
// TODO @jaosn可以把 DeliveryExpressTemplateChargeBO DeliveryExpressTemplateFreeBO 搞成内嵌的类这样简洁一点
/**
* 运费模板快递运费设置
*/
private DeliveryExpressTemplateChargeBO templateCharge;
/**
* 运费模板包邮设置
*/
private DeliveryExpressTemplateFreeBO templateFree;
}

View File

@ -342,7 +342,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
// TODO 芋艿logisticsId 校验存在 发货物流公司 fix // TODO 芋艿logisticsId 校验存在 发货物流公司 fix
DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId()); DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
if (deliveryExpress == null) { if (deliveryExpress == null) {
throw exception(DELIVERY_EXPRESS_NOT_EXISTS); throw exception(EXPRESS_NOT_EXISTS);
} }
// 更新 TradeOrderDO 状态为已发货等待收货 // 更新 TradeOrderDO 状态为已发货等待收货

View File

@ -180,6 +180,11 @@ public class TradePriceCalculateRespBO {
*/ */
private Long categoryId; private Long categoryId;
/**
* 运费模板 Id
*/
private Long deliveryTemplateId;
// ========== 商品 SKU 信息 ========== // ========== 商品 SKU 信息 ==========
/** /**
* 商品重量单位kg 千克 * 商品重量单位kg 千克

View File

@ -7,9 +7,7 @@ import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService; import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService;
import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateChargeBO; import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;
import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateFreeBO;
import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO.OrderItem; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO.OrderItem;
@ -24,8 +22,8 @@ import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.DELIVERY_EXPRESS_USER_ADDRESS_IS_EMPTY; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRODUCT_EXPRESS_TEMPLATE_NOT_FOUND; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND;
/** /**
* 运费的 {@link TradePriceCalculator} 实现类 * 运费的 {@link TradePriceCalculator} 实现类
@ -49,7 +47,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
return; return;
} }
if (param.getAddressId() == null) { if (param.getAddressId() == null) {
throw exception(DELIVERY_EXPRESS_USER_ADDRESS_IS_EMPTY); throw exception(PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY);
} }
// 1.2 得到收件地址区域 // 1.2 得到收件地址区域
AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId());
@ -57,29 +55,29 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
// 2. 过滤出已选中的商品SKU // 2. 过滤出已选中的商品SKU
List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected); List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected);
Set<Long> spuIds = convertSet(selectedItem, OrderItem::getSpuId); Set<Long> deliveryTemplateIds = convertSet(selectedItem, OrderItem::getDeliveryTemplateId);
Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap = Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap =
deliveryExpressTemplateService.getExpressTemplateMapBySpuIdsAndArea(spuIds, address.getAreaId()); deliveryExpressTemplateService.getExpressTemplateMapByIdsAndArea(deliveryTemplateIds, address.getAreaId());
// 3. 计算配送费用 // 3. 计算配送费用
if (CollUtil.isEmpty(spuExpressTemplateMap)) { if (CollUtil.isEmpty(expressTemplateMap)) {
log.error("[calculate][找不到商品 spuId{} areaId{} 对应的运费模板]", spuIds, address.getAreaId()); log.error("[calculate][找不到商品 templateIds {} areaId{} 对应的运费模板]", deliveryTemplateIds, address.getAreaId());
throw exception(PRODUCT_EXPRESS_TEMPLATE_NOT_FOUND); throw exception(PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND);
} }
calculateDeliveryPrice(selectedItem, spuExpressTemplateMap, result); calculateDeliveryPrice(selectedItem, expressTemplateMap, result);
} }
private void calculateDeliveryPrice(List<OrderItem> selectedSkus, private void calculateDeliveryPrice(List<OrderItem> selectedSkus,
Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap, Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap,
TradePriceCalculateRespBO result) { TradePriceCalculateRespBO result) {
// SPU 来计算商品的运费一个 spuId 可能对应多条订单商品 SKU // 商品运费模板来计算商品的运费相同的运费模板可能对应多条订单商品 SKU
Map<Long, List<OrderItem>> spuIdItemMap = convertMultiMap(selectedSkus, OrderItem::getSpuId); Map<Long, List<OrderItem>> tplIdItemMap = convertMultiMap(selectedSkus, OrderItem::getDeliveryTemplateId);
// 依次计算每个 SPU 快递运费 // 依次计算快递运费
for (Map.Entry<Long, List<OrderItem>> entry : spuIdItemMap.entrySet()) { for (Map.Entry<Long, List<OrderItem>> entry : tplIdItemMap.entrySet()) {
Long spuId = entry.getKey(); Long templateId = entry.getKey();
List<OrderItem> orderItems = entry.getValue(); List<OrderItem> orderItems = entry.getValue();
SpuDeliveryExpressTemplateRespBO templateBO = spuExpressTemplateMap.get(spuId); DeliveryExpressTemplateRespBO templateBO = expressTemplateMap.get(templateId);
if (templateBO == null) { if (templateBO == null) {
log.error("不能计算快递运费。不能找到 spuId : {}. 对应的运费模板配置 Resp BO", spuId); log.error("[calculateDeliveryPrice][不能计算快递运费,找不到 templateId({}) 对应的运费模板配置]", templateId);
continue; continue;
} }
// 总件数, 总金额, 总重量 总体积 // 总件数, 总金额, 总重量 总体积
@ -95,12 +93,12 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
} }
// 优先判断是否包邮. 如果包邮不计算快递运费 // 优先判断是否包邮. 如果包邮不计算快递运费
if (isExpressFree(templateBO.getChargeMode(), totalCount, totalWeight, if (isExpressFree(templateBO.getChargeMode(), totalCount, totalWeight,
totalVolume, totalPrice, templateBO.getTemplateFree())) { totalVolume, totalPrice, templateBO.getFree())) {
continue; continue;
} }
// 计算快递运费 // 计算快递运费
calculateExpressFeeByChargeMode(totalCount, totalWeight, totalVolume, calculateExpressFeeByChargeMode(totalCount, totalWeight, totalVolume,
templateBO.getChargeMode(), templateBO.getTemplateCharge(), orderItems); templateBO.getChargeMode(), templateBO.getCharge(), orderItems);
} }
TradePriceCalculatorHelper.recountAllPrice(result); TradePriceCalculatorHelper.recountAllPrice(result);
@ -117,10 +115,10 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
* @param orderItems SKU 商品项目 * @param orderItems SKU 商品项目
*/ */
private void calculateExpressFeeByChargeMode(double totalCount, double totalWeight, double totalVolume, private void calculateExpressFeeByChargeMode(double totalCount, double totalWeight, double totalVolume,
int chargeMode, DeliveryExpressTemplateChargeBO templateCharge, int chargeMode, DeliveryExpressTemplateRespBO.Charge templateCharge,
List<OrderItem> orderItems) { List<OrderItem> orderItems) {
if (templateCharge == null) { if (templateCharge == null) {
log.error("计算快递运费时,不能找到对应的快递运费模板费用配置。无法计算以下商品 SKU 项目运费: {}", orderItems); log.error("[calculateExpressFeeByChargeMode][计算快递运费时,找不到 SKU({}) 对应的运费模版]", orderItems);
return; return;
} }
DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode); DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);
@ -147,7 +145,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
* @param templateCharge 快递运费配置 * @param templateCharge 快递运费配置
* @param orderItems SKU 商品项目 * @param orderItems SKU 商品项目
*/ */
private void calculateExpressFee(double total, DeliveryExpressTemplateChargeBO templateCharge, List<OrderItem> orderItems) { private void calculateExpressFee(double total, DeliveryExpressTemplateRespBO.Charge templateCharge, List<OrderItem> orderItems) {
int deliveryPrice; int deliveryPrice;
if (total <= templateCharge.getStartCount()) { if (total <= templateCharge.getStartCount()) {
deliveryPrice = templateCharge.getStartPrice(); deliveryPrice = templateCharge.getStartPrice();
@ -176,7 +174,6 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
for (OrderItem item : orderItems) { for (OrderItem item : orderItems) {
// 更新快递运费 // 更新快递运费
item.setDeliveryPrice(dividePrice); item.setDeliveryPrice(dividePrice);
TradePriceCalculatorHelper.recountPayPrice(item); TradePriceCalculatorHelper.recountPayPrice(item);
} }
} }
@ -192,7 +189,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
* @param templateFree 包邮配置 * @param templateFree 包邮配置
*/ */
private boolean isExpressFree(Integer chargeMode, int totalCount, double totalWeight, private boolean isExpressFree(Integer chargeMode, int totalCount, double totalWeight,
double totalVolume, int totalPrice, DeliveryExpressTemplateFreeBO templateFree) { double totalVolume, int totalPrice, DeliveryExpressTemplateRespBO.Free templateFree) {
if (templateFree == null) { if (templateFree == null) {
return false; return false;
} }

View File

@ -56,7 +56,8 @@ public class TradePriceCalculatorHelper {
orderItem.setPicUrl(sku.getPicUrl()).setProperties(sku.getProperties()) orderItem.setPicUrl(sku.getPicUrl()).setProperties(sku.getProperties())
.setWeight(sku.getWeight()).setVolume(sku.getVolume()); .setWeight(sku.getWeight()).setVolume(sku.getVolume());
// spu 信息 // spu 信息
orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId()); orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId())
.setDeliveryTemplateId(spu.getDeliveryTemplateId());
if (orderItem.getPicUrl() == null) { if (orderItem.getPicUrl() == null) {
orderItem.setPicUrl(spu.getPicUrl()); orderItem.setPicUrl(spu.getPicUrl());
} }

View File

@ -1,8 +1,9 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.impl; package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressQueryProperties; import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kd100.Kd100ExpressClient;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -18,39 +19,41 @@ import javax.annotation.Resource;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
// TODO @jason可以参考 AliyunSmsClientTest mockito无需启动 spring 容器
/** /**
* @author jason * @author jason
*/ */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = Kd100ExpressQueryProviderTest.Application.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = Kd100ExpressClientTest.Application.class)
@ActiveProfiles("trade-delivery-query") // 设置使用 trade-delivery-query 配置文件 @ActiveProfiles("unit-test") // 设置使用 trade-delivery-query 配置文件
public class Kd100ExpressQueryProviderTest { public class Kd100ExpressClientTest {
@Resource @Resource
private RestTemplateBuilder builder; private RestTemplateBuilder builder;
@Resource @Resource
private TradeExpressQueryProperties expressQueryProperties; private TradeExpressProperties expressQueryProperties;
private Kd100ExpressQueryProvider kd100ExpressQueryProvider; private Kd100ExpressClient kd100ExpressClient;
@BeforeEach @BeforeEach
public void init(){ public void init(){
kd100ExpressQueryProvider = new Kd100ExpressQueryProvider(builder.build(),expressQueryProperties.getKd100()); kd100ExpressClient = new Kd100ExpressClient(builder.build(),expressQueryProperties.getKd100());
} }
@Test @Test
@Disabled("需要 授权 key. 暂时忽略") @Disabled("需要 授权 key. 暂时忽略")
void testRealTimeQueryExpressFailed() { void testRealTimeQueryExpressFailed() {
ServiceException t = assertThrows(ServiceException.class, () -> { ServiceException t = assertThrows(ServiceException.class, () -> {
ExpressQueryReqDTO reqDTO = new ExpressQueryReqDTO(); ExpressTrackQueryReqDTO reqDTO = new ExpressTrackQueryReqDTO();
reqDTO.setExpressCompanyCode("yto"); reqDTO.setExpressCode("yto");
reqDTO.setLogisticsNo("YT9383342193097"); reqDTO.setLogisticsNo("YT9383342193097");
kd100ExpressQueryProvider.realTimeQueryExpress(reqDTO); kd100ExpressClient.getExpressTrackList(reqDTO);
}); });
assertEquals(1011003007, t.getCode()); assertEquals(1011003005, t.getCode());
} }
@Import({ @Import({
RestTemplateAutoConfiguration.class RestTemplateAutoConfiguration.class
}) })
@EnableConfigurationProperties(TradeExpressQueryProperties.class) @EnableConfigurationProperties(TradeExpressProperties.class)
public static class Application { public static class Application {
} }
} }

View File

@ -1,8 +1,9 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.impl; package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressQueryProperties; import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kdniao.KdNiaoExpressClient;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -17,39 +18,42 @@ import javax.annotation.Resource;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
// TODO @芋艿单测最后 review // TODO @jason可以参考 AliyunSmsClientTest mockito无需启动 spring 容器
/** /**
* {@link KdNiaoExpressClient} 的单元测试
*
* @author jason * @author jason
*/ */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = KdNiaoExpressQueryProviderTest.Application.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = KdNiaoExpressClientTest.Application.class)
@ActiveProfiles("trade-delivery-query") // 设置使用 trade-delivery-query 配置文件 TODO @jason可以直接写到 application-unit-test.yaml 配置文件里 @ActiveProfiles("unit-test")
public class KdNiaoExpressQueryProviderTest { public class KdNiaoExpressClientTest {
@Resource @Resource
private RestTemplateBuilder builder; private RestTemplateBuilder builder;
@Resource @Resource
private TradeExpressQueryProperties expressQueryProperties; private TradeExpressProperties expressQueryProperties;
private KdNiaoExpressQueryProvider kdNiaoExpressQueryProvider; private KdNiaoExpressClient kdNiaoExpressClient;
@BeforeEach @BeforeEach
public void init(){ public void init(){
kdNiaoExpressQueryProvider = new KdNiaoExpressQueryProvider(builder.build(),expressQueryProperties.getKdNiao()); kdNiaoExpressClient = new KdNiaoExpressClient(builder.build(),expressQueryProperties.getKdNiao());
} }
@Test @Test
@Disabled("需要 授权 key. 暂时忽略") @Disabled("需要 授权 key. 暂时忽略")
void testRealTimeQueryExpressFailed() { void testRealTimeQueryExpressFailed() {
assertThrows(ServiceException.class,() ->{ assertThrows(ServiceException.class,() ->{
ExpressQueryReqDTO reqDTO = new ExpressQueryReqDTO(); ExpressTrackQueryReqDTO reqDTO = new ExpressTrackQueryReqDTO();
reqDTO.setExpressCompanyCode("yy"); reqDTO.setExpressCode("yy");
reqDTO.setLogisticsNo("YT9383342193097"); reqDTO.setLogisticsNo("YT9383342193097");
kdNiaoExpressQueryProvider.realTimeQueryExpress(reqDTO); kdNiaoExpressClient.getExpressTrackList(reqDTO);
}); });
} }
@Import({ @Import({
RestTemplateAutoConfiguration.class RestTemplateAutoConfiguration.class
}) })
@EnableConfigurationProperties(TradeExpressQueryProperties.class) @EnableConfigurationProperties(TradeExpressProperties.class)
public static class Application { public static class Application {
} }
} }

View File

@ -0,0 +1,53 @@
package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.module.trade.framework.delivery.config.ExpressClientConfig;
import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
// TODO @jason可以参考 AliyunSmsClientTest mockito无需启动 spring 容器
/**
* @author jason
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = NoProvideExpressClientTest.Application.class)
@ActiveProfiles("unit-test") // 设置使用 trade-delivery-query 配置文件
@Import({ExpressClientConfig.class})
public class NoProvideExpressClientTest {
@Resource
private ExpressClient expressClient;
@Test
void getExpressTrackList() {
ServiceException t = assertThrows(ServiceException.class, () -> {
expressClient.getExpressTrackList(null);
});
assertEquals(1011003006, t.getCode());
}
@Import({
RestTemplateAutoConfiguration.class,
})
@EnableConfigurationProperties(TradeExpressProperties.class)
public static class Application {
@Bean
private RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
}

View File

@ -1,12 +1,13 @@
package cn.iocoder.yudao.module.trade.service.price.calculator; package cn.iocoder.yudao.module.trade.service.price.calculator;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.member.api.address.AddressApi; import cn.iocoder.yudao.module.member.api.address.AddressApi;
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService; import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService;
import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateChargeBO; import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;
import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateFreeBO;
import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -16,20 +17,17 @@ import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum.PIECE;
import static cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum.EXPRESS;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.assertj.core.api.Assertions.assertThat;
/** /**
* {@link TradeDeliveryPriceCalculator} 的单元测试
*
* @author jason * @author jason
*/ */
public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest { public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest {
@ -43,49 +41,52 @@ public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest {
private TradePriceCalculateReqBO reqBO; private TradePriceCalculateReqBO reqBO;
private TradePriceCalculateRespBO resultBO; private TradePriceCalculateRespBO resultBO;
private AddressRespDTO addressResp;
private DeliveryExpressTemplateChargeBO chargeBO; private DeliveryExpressTemplateRespBO templateRespBO;
private DeliveryExpressTemplateFreeBO freeBO; private DeliveryExpressTemplateRespBO.Charge chargeBO;
private SpuDeliveryExpressTemplateRespBO spuTemplateRespBO; private DeliveryExpressTemplateRespBO.Free freeBO;
@BeforeEach @BeforeEach
public void init(){ public void init(){
// 准备参数 // 准备参数
reqBO = new TradePriceCalculateReqBO() reqBO = new TradePriceCalculateReqBO()
.setDeliveryType(EXPRESS.getMode()) .setDeliveryType(DeliveryTypeEnum.EXPRESS.getMode())
.setAddressId(10L) .setAddressId(10L)
.setUserId(1L) .setUserId(1L)
.setItems(asList( .setItems(asList(
new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true), new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true),
new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(10).setSelected(true), new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(10).setSelected(true),
new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(false) new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(false) // 未选中
)); ));
resultBO = new TradePriceCalculateRespBO() resultBO = new TradePriceCalculateRespBO()
.setPrice(new TradePriceCalculateRespBO.Price()) .setPrice(new TradePriceCalculateRespBO.Price())
.setPromotions(new ArrayList<>()) .setPromotions(new ArrayList<>())
.setItems(asList( .setItems(asList(
new TradePriceCalculateRespBO.OrderItem().setSpuId(1L).setSkuId(10L).setCount(2).setSelected(true) new TradePriceCalculateRespBO.OrderItem().setDeliveryTemplateId(1L).setSkuId(10L).setCount(2).setSelected(true)
.setWeight(10d).setVolume(10d).setPrice(100), .setWeight(10d).setVolume(10d).setPrice(100),
new TradePriceCalculateRespBO.OrderItem().setSpuId(1L).setSkuId(20L).setCount(10).setSelected(true) new TradePriceCalculateRespBO.OrderItem().setDeliveryTemplateId(1L).setSkuId(20L).setCount(10).setSelected(true)
.setWeight(10d).setVolume(10d).setPrice(200), .setWeight(10d).setVolume(10d).setPrice(200),
new TradePriceCalculateRespBO.OrderItem().setSpuId(1L).setSkuId(30L).setCount(1).setSelected(false) new TradePriceCalculateRespBO.OrderItem().setDeliveryTemplateId(1L).setSkuId(30L).setCount(1).setSelected(false)
.setWeight(10d).setVolume(10d).setPrice(300) .setWeight(10d).setVolume(10d).setPrice(300)
)); ));
// 保证价格被初始化上 // 保证价格被初始化上
TradePriceCalculatorHelper.recountPayPrice(resultBO.getItems()); TradePriceCalculatorHelper.recountPayPrice(resultBO.getItems());
TradePriceCalculatorHelper.recountAllPrice(resultBO); TradePriceCalculatorHelper.recountAllPrice(resultBO);
// 准备收件地址数据 // 准备收件地址数据
addressResp = randomPojo(AddressRespDTO.class, item -> item.setAreaId(10)); AddressRespDTO addressResp = randomPojo(AddressRespDTO.class, item -> item.setAreaId(10));
when(addressApi.getAddress(eq(10L), eq(1L))).thenReturn(addressResp);
// 准备运费模板费用配置数据 // 准备运费模板费用配置数据
chargeBO = randomPojo(DeliveryExpressTemplateChargeBO.class, chargeBO = randomPojo(DeliveryExpressTemplateRespBO.Charge.class,
item -> item.setStartCount(10D).setStartPrice(1000).setExtraCount(10D).setExtraPrice(2000)); item -> item.setStartCount(10D).setStartPrice(1000).setExtraCount(10D).setExtraPrice(2000));
// 准备运费模板包邮配置数据 订单总件数 < 包邮件数时 12 < 20 // 准备运费模板包邮配置数据订单总件数 < 包邮件数时 12 < 20
freeBO = randomPojo(DeliveryExpressTemplateFreeBO.class, freeBO = randomPojo(DeliveryExpressTemplateRespBO.Free.class,
item -> item.setFreeCount(20).setFreePrice(100)); item -> item.setFreeCount(20).setFreePrice(100));
// 准备 SP 运费模板 数据 // 准备 SP 运费模板数据
spuTemplateRespBO = randomPojo(SpuDeliveryExpressTemplateRespBO.class, templateRespBO = randomPojo(DeliveryExpressTemplateRespBO.class,
item -> item.setChargeMode(PIECE.getType()) item -> item.setChargeMode(DeliveryExpressChargeModeEnum.PIECE.getType())
.setTemplateCharge(chargeBO).setTemplateFree(freeBO)); .setCharge(chargeBO).setFree(freeBO));
} }
@Test @Test
@ -94,32 +95,27 @@ public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest {
// SKU 1 : 100 * 2 = 200 // SKU 1 : 100 * 2 = 200
// SKU 2 200 * 10 = 2000 // SKU 2 200 * 10 = 2000
// 运费 首件 1000 + 续件 2000 = 3000 // 运费 首件 1000 + 续件 2000 = 3000
Map<Long, SpuDeliveryExpressTemplateRespBO> respMap = new HashMap<>();
respMap.put(1L, spuTemplateRespBO);
// mock 方法 // mock 方法
when(addressApi.getAddress(eq(10L), eq(1L))).thenReturn(addressResp); when(deliveryExpressTemplateService.getExpressTemplateMapByIdsAndArea(eq(asSet(1L)), eq(10)))
when(deliveryExpressTemplateService.getExpressTemplateMapBySpuIdsAndArea(eq(asSet(1L)), eq(10))) .thenReturn(MapUtil.of(1L, templateRespBO));
.thenReturn(respMap);
// 调用
calculator.calculate(reqBO, resultBO); calculator.calculate(reqBO, resultBO);
// 断言
TradePriceCalculateRespBO.Price price = resultBO.getPrice(); TradePriceCalculateRespBO.Price price = resultBO.getPrice();
assertThat(price) assertThat(price)
.extracting("totalPrice","discountPrice","couponPrice","pointPrice","deliveryPrice","payPrice") .extracting("totalPrice","discountPrice","couponPrice","pointPrice","deliveryPrice","payPrice")
.containsExactly(2200, 0, 0, 0, 3000, 5200); .containsExactly(2200, 0, 0, 0, 3000, 5200);
// 断言SKU
assertThat(resultBO.getItems()).hasSize(3); assertThat(resultBO.getItems()).hasSize(3);
// SKU1 // 断言SKU1
assertThat(resultBO.getItems().get(0)) assertThat(resultBO.getItems().get(0))
.extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice") .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
.containsExactly(100, 2, 0, 0, 0, 1500, 1700); .containsExactly(100, 2, 0, 0, 0, 1500, 1700);
// SKU2 // 断言SKU2
assertThat(resultBO.getItems().get(1)) assertThat(resultBO.getItems().get(1))
.extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice") .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
.containsExactly(200, 10, 0, 0, 0, 1500, 3500); .containsExactly(200, 10, 0, 0, 0, 1500, 3500);
// SKU3 未选中 // 断言SKU3 未选中
assertThat(resultBO.getItems().get(2)) assertThat(resultBO.getItems().get(2))
.extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice") .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
.containsExactly(300, 1, 0, 0, 0, 0, 300); .containsExactly(300, 1, 0, 0, 0, 0, 300);
@ -131,38 +127,33 @@ public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest {
// SKU 1 : 100 * 2 = 200 // SKU 1 : 100 * 2 = 200
// SKU 2 200 * 10 = 2000 // SKU 2 200 * 10 = 2000
// 运费 0 // 运费 0
Map<Long, SpuDeliveryExpressTemplateRespBO> respMap = new HashMap<>();
respMap.put(1L, spuTemplateRespBO);
// 准备运费模板包邮配置数据 包邮 订单总件数 > 包邮件数时 12 > 10
freeBO = randomPojo(DeliveryExpressTemplateFreeBO.class,
item -> item.setFreeCount(10).setFreePrice(1000));
spuTemplateRespBO.setTemplateFree(freeBO);
// mock 方法 // mock 方法
when(addressApi.getAddress(eq(10L), eq(1L))).thenReturn(addressResp); // 准备运费模板包邮配置数据 包邮 订单总件数 > 包邮件数时 12 > 10
when(deliveryExpressTemplateService.getExpressTemplateMapBySpuIdsAndArea(eq(asSet(1L)), eq(10))) templateRespBO.setFree(randomPojo(DeliveryExpressTemplateRespBO.Free.class,
.thenReturn(respMap); item -> item.setFreeCount(10).setFreePrice(1000)));
when(deliveryExpressTemplateService.getExpressTemplateMapByIdsAndArea(eq(asSet(1L)), eq(10)))
.thenReturn(MapUtil.of(1L, templateRespBO));
// 调用
calculator.calculate(reqBO, resultBO); calculator.calculate(reqBO, resultBO);
// 断言
TradePriceCalculateRespBO.Price price = resultBO.getPrice(); TradePriceCalculateRespBO.Price price = resultBO.getPrice();
// 断言price
assertThat(price) assertThat(price)
.extracting("totalPrice","discountPrice","couponPrice","pointPrice","deliveryPrice","payPrice") .extracting("totalPrice","discountPrice","couponPrice","pointPrice","deliveryPrice","payPrice")
.containsExactly(2200, 0, 0, 0, 0, 2200); .containsExactly(2200, 0, 0, 0, 0, 2200);
// 断言SKU
assertThat(resultBO.getItems()).hasSize(3); assertThat(resultBO.getItems()).hasSize(3);
// SKU1 // 断言SKU1
assertThat(resultBO.getItems().get(0)) assertThat(resultBO.getItems().get(0))
.extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice") .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
.containsExactly(100, 2, 0, 0, 0, 0, 200); .containsExactly(100, 2, 0, 0, 0, 0, 200);
// SKU2 // 断言SKU2
assertThat(resultBO.getItems().get(1)) assertThat(resultBO.getItems().get(1))
.extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice") .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
.containsExactly(200, 10, 0, 0, 0, 0, 2000); .containsExactly(200, 10, 0, 0, 0, 0, 2000);
// SKU3 未选中 // 断言SKU3 未选中
assertThat(resultBO.getItems().get(2)) assertThat(resultBO.getItems().get(2))
.extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice") .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
.containsExactly(300, 1, 0, 0, 0, 0, 300); .containsExactly(300, 1, 0, 0, 0, 0, 300);
} }
}
}

View File

@ -1,18 +0,0 @@
spring:
main:
lazy-initialization: true # 开启懒加载,加快速度
banner-mode: off # 单元测试,禁用 Banner
--- #################### 交易快递查询相关配置 ####################
yudao:
trade:
express:
query:
express-query-provider: kd_niao
kd-niao:
api-key: xxx
business-id: xxxxxxxx
kd100:
customer: xxxx
key: xxxxx

View File

@ -51,3 +51,11 @@ yudao:
order: order:
app-id: 1 app-id: 1
merchant-order-id: 1 merchant-order-id: 1
express:
kd-niao:
api-key: xxxx
business-id: xxxxx
kd100:
customer: xxxxx
key: xxxxx
client: not_provide

View File

@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.member.controller.admin.point;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigRespVO;
import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigSaveReqVO;
import cn.iocoder.yudao.module.member.convert.point.MemberPointConfigConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO;
import cn.iocoder.yudao.module.member.service.point.MemberPointConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 会员积分设置")
@RestController
@RequestMapping("/point/config")
@Validated
public class MemberPointConfigController {
@Resource
private MemberPointConfigService memberPointConfigService;
@PutMapping("/update")
@Operation(summary = "保存会员积分配置")
@PreAuthorize("@ss.hasPermission('member:point-config:save')")
public CommonResult<Boolean> updateConfig(@Valid @RequestBody MemberPointConfigSaveReqVO saveReqVO) {
memberPointConfigService.saveConfig(saveReqVO);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得会员积分配置")
@PreAuthorize("@ss.hasPermission('member:point-config:query')")
public CommonResult<MemberPointConfigRespVO> getConfig() {
MemberPointConfigDO config = memberPointConfigService.getConfig();
return success(MemberPointConfigConvert.INSTANCE.convert(config));
}
}

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.member.controller.admin.point.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
/**
* 会员积分配置 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class MemberPointConfigBaseVO {
@Schema(description = "积分抵扣开关", required = true, example = "true")
private Boolean tradeDeductEnable;
@Schema(description = "积分抵扣,单位:分", example = "13506")
private BigDecimal tradeDeductUnitPrice;
@Schema(description = "积分抵扣最大值", example = "32428")
private Long tradeDeductMaxPrice;
@Schema(description = "1 元赠送多少分")
private Long tradeGivePoint;
}

View File

@ -0,0 +1,13 @@
package cn.iocoder.yudao.module.member.controller.admin.point.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 会员积分配置 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberPointConfigRespVO extends MemberPointConfigBaseVO {
}

View File

@ -0,0 +1,13 @@
package cn.iocoder.yudao.module.member.controller.admin.point.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 会员积分配置保存 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberPointConfigSaveReqVO extends MemberPointConfigBaseVO {
}

View File

@ -0,0 +1 @@
package cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod;

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.member.convert.point;
import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigRespVO;
import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 会员积分配置 Convert
*
* @author QingX
*/
@Mapper
public interface MemberPointConfigConvert {
MemberPointConfigConvert INSTANCE = Mappers.getMapper(MemberPointConfigConvert.class);
MemberPointConfigRespVO convert(MemberPointConfigDO bean);
}

View File

@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.member.dal.dataobject.point;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
/**
* 会员积分配置 DO
*
* @author QingX
*/
@TableName("member_point_config")
@KeySequence("member_point_config_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberPointConfigDO extends BaseDO {
/**
* 自增主键
*/
@TableId
private Integer id;
/**
* 积分抵扣开关
*/
private Boolean tradeDeductEnable;
/**
* 积分抵扣单位
*
* 1 积分抵扣多少分
*/
private BigDecimal tradeDeductUnitPrice;
/**
* 积分抵扣最大值
*/
private Integer tradeDeductMaxPrice;
/**
* 1 元赠送多少分
*/
private Integer tradeGivePoint;
}

View File

@ -0,0 +1,14 @@
package cn.iocoder.yudao.module.member.dal.mysql.point;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 积分设置 Mapper
*
* @author QingX
*/
@Mapper
public interface MemberPointConfigMapper extends BaseMapperX<MemberPointConfigDO> {
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.member.service.point;
import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigSaveReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO;
import javax.validation.Valid;
/**
* 会员积分配置 Service 接口
*
* @author QingX
*/
public interface MemberPointConfigService {
/**
* 保存会员积分配置
*
* @param saveReqVO 更新信息
*/
void saveConfig(@Valid MemberPointConfigSaveReqVO saveReqVO);
/**
* 获得会员积分配置
*
* @return 积分配置
*/
MemberPointConfigDO getConfig();
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.member.service.point;
import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigSaveReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO;
import cn.iocoder.yudao.module.member.dal.mysql.point.MemberPointConfigMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
/**
* 会员积分配置 Service 实现类
*
* @author QingX
*/
@Service
@Validated
public class MemberPointConfigServiceImpl implements MemberPointConfigService {
@Resource
private MemberPointConfigMapper memberPointConfigMapper;
@Override
public void saveConfig(MemberPointConfigSaveReqVO saveReqVO) {
// TODO qingx配置存在 update不存在则 insert
}
@Override
public MemberPointConfigDO getConfig() {
// TODO qingx直接查询到一条
return null;
}
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao</artifactId>
<version>${revision}</version>
</parent>
<artifactId>yudao-module-point</artifactId>
<packaging>pom</packaging>
<modules>
<module>yudao-module-point-api</module>
</modules>
</project>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-point</artifactId>
<version>${revision}</version>
</parent>
<artifactId>yudao-module-point-api</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-common</artifactId>
</dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.point.enums;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
/**
* Pay 错误码 Core 枚举类
*
* pay 系统使用 1-007-000-000
*/
public interface ErrorCodeConstants {
ErrorCode CONFIG_NOT_EXISTS = new ErrorCode(499, "积分设置不存在");
ErrorCode CONFIG_EXISTS = new ErrorCode(499, "积分设置已存在,只允配置一条记录");
ErrorCode SIGN_IN_CONFIG_NOT_EXISTS = new ErrorCode(499, "签到天数规则不存在");
ErrorCode SIGN_IN_CONFIG_EXISTS = new ErrorCode(499, "签到天数规则已存在");
ErrorCode RECORD_NOT_EXISTS = new ErrorCode( 499, "用户积分记录不存在");
ErrorCode SIGN_IN_RECORD_NOT_EXISTS = new ErrorCode(499, "用户签到积分不存在");
}

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-point</artifactId>
<version>${revision}</version>
</parent>
<artifactId>yudao-module-point-biz</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-point-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-security</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
</dependency>
<!-- Test 测试相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-test</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-excel</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,102 @@
package cn.iocoder.yudao.module.point.controller.admin.pointrecord;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo.*;
import cn.iocoder.yudao.module.point.dal.dataobject.pointrecord.PointRecordDO;
import cn.iocoder.yudao.module.point.convert.pointrecord.PointRecordConvert;
import cn.iocoder.yudao.module.point.service.pointrecord.PointRecordService;
@Tag(name = "管理后台 - 用户积分记录")
@RestController
@RequestMapping("/point/record")
@Validated
public class PointRecordController {
@Resource
private PointRecordService recordService;
@PostMapping("/create")
@Operation(summary = "创建用户积分记录")
@PreAuthorize("@ss.hasPermission('point:record:create')")
public CommonResult<Long> createRecord(@Valid @RequestBody PointRecordCreateReqVO createReqVO) {
return success(recordService.createRecord(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新用户积分记录")
@PreAuthorize("@ss.hasPermission('point:record:update')")
public CommonResult<Boolean> updateRecord(@Valid @RequestBody PointRecordUpdateReqVO updateReqVO) {
recordService.updateRecord(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除用户积分记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('point:record:delete')")
public CommonResult<Boolean> deleteRecord(@RequestParam("id") Long id) {
recordService.deleteRecord(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得用户积分记录")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('point:record:query')")
public CommonResult<PointRecordRespVO> getRecord(@RequestParam("id") Long id) {
PointRecordDO record = recordService.getRecord(id);
return success(PointRecordConvert.INSTANCE.convert(record));
}
@GetMapping("/list")
@Operation(summary = "获得用户积分记录列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@PreAuthorize("@ss.hasPermission('point:record:query')")
public CommonResult<List<PointRecordRespVO>> getRecordList(@RequestParam("ids") Collection<Long> ids) {
List<PointRecordDO> list = recordService.getRecordList(ids);
return success(PointRecordConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@Operation(summary = "获得用户积分记录分页")
@PreAuthorize("@ss.hasPermission('point:record:query')")
public CommonResult<PageResult<PointRecordRespVO>> getRecordPage(@Valid PointRecordPageReqVO pageVO) {
PageResult<PointRecordDO> pageResult = recordService.getRecordPage(pageVO);
return success(PointRecordConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/export-excel")
@Operation(summary = "导出用户积分记录 Excel")
@PreAuthorize("@ss.hasPermission('point:record:export')")
@OperateLog(type = EXPORT)
public void exportRecordExcel(@Valid PointRecordExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<PointRecordDO> list = recordService.getRecordList(exportReqVO);
// 导出 Excel
List<PointRecordExcelVO> datas = PointRecordConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "用户积分记录.xls", "数据", PointRecordExcelVO.class, datas);
}
}

View File

@ -0,0 +1,67 @@
package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import javax.validation.constraints.*;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 用户积分记录 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class PointRecordBaseVO {
@Schema(description = "业务编码", example = "22706")
@NotNull(message = "业务编码不能为空")
private String bizId;
@Schema(description = "业务类型", example = "1")
@NotNull(message = "业务类型不能为空")
private String bizType;
@Schema(description = "1增加 0扣减", example = "1")
@NotNull(message = "操作类型不能为空")
private String type;
@Schema(description = "积分标题")
@NotNull(message = "积分标题不能为空")
private String title;
@Schema(description = "积分描述", example = "你猜")
private String description;
@Schema(description = "积分")
@NotNull(message = "操作积分不能为空")
private Integer point;
@Schema(description = "变动后的积分", requiredMode = Schema.RequiredMode.REQUIRED)
// @NotNull(message = "变动后的积分不能为空")
private Integer totalPoint;
@Schema(description = "状态1-订单创建2-冻结期3-完成4-失效(订单退款) ", example = "1")
@NotNull(message = "积分状态不能为空")
private Integer status;
@Schema(description = "用户id", example = "31169")
@NotNull(message = "用户ID不能为空")
private Integer userId;
@Schema(description = "冻结时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@NotNull(message = "冻结时间不能为空")
private LocalDateTime freezingTime;
@Schema(description = "解冻时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@NotNull(message = "解冻时间不能为空")
private LocalDateTime thawingTime;
}

View File

@ -0,0 +1,14 @@
package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 用户积分记录创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class PointRecordCreateReqVO extends PointRecordBaseVO {
}

View File

@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.ExcelProperty;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
/**
* 用户积分记录 Excel VO
*
* @author QingX
*/
@Data
public class PointRecordExcelVO {
@ExcelProperty("自增主键")
private Long id;
@ExcelProperty("业务编码")
private String bizId;
@ExcelProperty(value = "业务类型", converter = DictConvert.class)
@DictFormat("biz_type") // TODO 代码优化建议设置到对应的 XXXDictTypeConstants 枚举类中
private String bizType;
@ExcelProperty("1增加 0扣减")
private String type;
@ExcelProperty("积分标题")
private String title;
@ExcelProperty("积分描述")
private String description;
@ExcelProperty("积分")
private Integer point;
@ExcelProperty("变动后的积分")
private Integer totalPoint;
@ExcelProperty(value = "状态1-订单创建2-冻结期3-完成4-失效(订单退款) ", converter = DictConvert.class)
@DictFormat("point_status") // TODO 代码优化建议设置到对应的 XXXDictTypeConstants 枚举类中
private Integer status;
@ExcelProperty("用户id")
private Integer userId;
@ExcelProperty("冻结时间")
private LocalDateTime freezingTime;
@ExcelProperty("解冻时间")
private LocalDateTime thawingTime;
@ExcelProperty("发生时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
@Schema(description = "管理后台 - 用户积分记录 Excel 导出 Request VO参数和 PointRecordPageReqVO 是一致的")
@Data
public class PointRecordExportReqVO {
@Schema(description = "业务编码", example = "22706")
private String bizId;
@Schema(description = "业务类型", example = "1")
private String bizType;
@Schema(description = "1增加 0扣减", example = "1")
private String type;
@Schema(description = "积分标题")
private String title;
@Schema(description = "状态1-订单创建2-冻结期3-完成4-失效(订单退款) ", example = "1")
private Integer status;
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
@Schema(description = "管理后台 - 用户积分记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class PointRecordPageReqVO extends PageParam {
@Schema(description = "业务编码", example = "22706")
private String bizId;
@Schema(description = "业务类型", example = "1")
private String bizType;
@Schema(description = "1增加 0扣减", example = "1")
private String type;
@Schema(description = "积分标题")
private String title;
@Schema(description = "状态1-订单创建2-冻结期3-完成4-失效(订单退款) ", example = "1")
private Integer status;
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 用户积分记录 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class PointRecordRespVO extends PointRecordBaseVO {
@Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "31457")
private Long id;
@Schema(description = "发生时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 用户积分记录更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class PointRecordUpdateReqVO extends PointRecordBaseVO {
@Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "31457")
@NotNull(message = "自增主键不能为空")
private Long id;
}

View File

@ -0,0 +1,102 @@
package cn.iocoder.yudao.module.point.controller.admin.signinconfig;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo.*;
import cn.iocoder.yudao.module.point.dal.dataobject.signinconfig.SignInConfigDO;
import cn.iocoder.yudao.module.point.convert.signinconfig.SignInConfigConvert;
import cn.iocoder.yudao.module.point.service.signinconfig.SignInConfigService;
@Tag(name = "管理后台 - 积分签到规则")
@RestController
@RequestMapping("/point/sign-in-config")
@Validated
public class SignInConfigController {
@Resource
private SignInConfigService signInConfigService;
@PostMapping("/create")
@Operation(summary = "创建积分签到规则")
@PreAuthorize("@ss.hasPermission('point:sign-in-config:create')")
public CommonResult<Integer> createSignInConfig(@Valid @RequestBody SignInConfigCreateReqVO createReqVO) {
return success(signInConfigService.createSignInConfig(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新积分签到规则")
@PreAuthorize("@ss.hasPermission('point:sign-in-config:update')")
public CommonResult<Boolean> updateSignInConfig(@Valid @RequestBody SignInConfigUpdateReqVO updateReqVO) {
signInConfigService.updateSignInConfig(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除积分签到规则")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('point:sign-in-config:delete')")
public CommonResult<Boolean> deleteSignInConfig(@RequestParam("id") Integer id) {
signInConfigService.deleteSignInConfig(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得积分签到规则")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('point:sign-in-config:query')")
public CommonResult<SignInConfigRespVO> getSignInConfig(@RequestParam("id") Integer id) {
SignInConfigDO signInConfig = signInConfigService.getSignInConfig(id);
return success(SignInConfigConvert.INSTANCE.convert(signInConfig));
}
@GetMapping("/list")
@Operation(summary = "获得积分签到规则列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@PreAuthorize("@ss.hasPermission('point:sign-in-config:query')")
public CommonResult<List<SignInConfigRespVO>> getSignInConfigList(@RequestParam("ids") Collection<Integer> ids) {
List<SignInConfigDO> list = signInConfigService.getSignInConfigList(ids);
return success(SignInConfigConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@Operation(summary = "获得积分签到规则分页")
@PreAuthorize("@ss.hasPermission('point:sign-in-config:query')")
public CommonResult<PageResult<SignInConfigRespVO>> getSignInConfigPage(@Valid SignInConfigPageReqVO pageVO) {
PageResult<SignInConfigDO> pageResult = signInConfigService.getSignInConfigPage(pageVO);
return success(SignInConfigConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/export-excel")
@Operation(summary = "导出积分签到规则 Excel")
@PreAuthorize("@ss.hasPermission('point:sign-in-config:export')")
@OperateLog(type = EXPORT)
public void exportSignInConfigExcel(@Valid SignInConfigExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<SignInConfigDO> list = signInConfigService.getSignInConfigList(exportReqVO);
// 导出 Excel
List<SignInConfigExcelVO> datas = SignInConfigConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "积分签到规则.xls", "数据", SignInConfigExcelVO.class, datas);
}
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import javax.validation.constraints.*;
/**
* 积分签到规则 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class SignInConfigBaseVO {
@Schema(description = "签到第x天", example = "7")
private Integer day;
@Schema(description = "签到天数对应分数", example = "10")
private Integer point;
}

View File

@ -0,0 +1,14 @@
package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 积分签到规则创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SignInConfigCreateReqVO extends SignInConfigBaseVO {
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.ExcelProperty;
/**
* 积分签到规则 Excel VO
*
* @author QingX
*/
@Data
public class SignInConfigExcelVO {
@ExcelProperty("签到第x天")
private Integer day;
@ExcelProperty("签到天数对应分数")
private Integer point;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,15 @@
package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
@Schema(description = "管理后台 - 积分签到规则 Excel 导出 Request VO参数和 SignInConfigPageReqVO 是一致的")
@Data
public class SignInConfigExportReqVO {
@Schema(description = "签到第x天", example = "7")
private Integer day;
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
@Schema(description = "管理后台 - 积分签到规则分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SignInConfigPageReqVO extends PageParam {
@Schema(description = "签到第x天", example = "7")
private Integer day;
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 积分签到规则 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SignInConfigRespVO extends SignInConfigBaseVO {
@Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "20937")
private Integer id;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 积分签到规则更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SignInConfigUpdateReqVO extends SignInConfigBaseVO {
@Schema(description = "规则自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "13653")
@NotNull(message = "规则自增主键不能为空")
private Integer id;
}

View File

@ -0,0 +1,102 @@
package cn.iocoder.yudao.module.point.controller.admin.signinrecord;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo.*;
import cn.iocoder.yudao.module.point.dal.dataobject.signinrecord.SignInRecordDO;
import cn.iocoder.yudao.module.point.convert.signinrecord.SignInRecordConvert;
import cn.iocoder.yudao.module.point.service.signinrecord.SignInRecordService;
@Tag(name = "管理后台 - 用户签到积分")
@RestController
@RequestMapping("/point/sign-in-record")
@Validated
public class SignInRecordController {
@Resource
private SignInRecordService signInRecordService;
@PostMapping("/create")
@Operation(summary = "创建用户签到积分")
@PreAuthorize("@ss.hasPermission('point:sign-in-record:create')")
public CommonResult<Long> createSignInRecord(@Valid @RequestBody SignInRecordCreateReqVO createReqVO) {
return success(signInRecordService.createSignInRecord(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新用户签到积分")
@PreAuthorize("@ss.hasPermission('point:sign-in-record:update')")
public CommonResult<Boolean> updateSignInRecord(@Valid @RequestBody SignInRecordUpdateReqVO updateReqVO) {
signInRecordService.updateSignInRecord(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除用户签到积分")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('point:sign-in-record:delete')")
public CommonResult<Boolean> deleteSignInRecord(@RequestParam("id") Long id) {
signInRecordService.deleteSignInRecord(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得用户签到积分")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('point:sign-in-record:query')")
public CommonResult<SignInRecordRespVO> getSignInRecord(@RequestParam("id") Long id) {
SignInRecordDO signInRecord = signInRecordService.getSignInRecord(id);
return success(SignInRecordConvert.INSTANCE.convert(signInRecord));
}
@GetMapping("/list")
@Operation(summary = "获得用户签到积分列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@PreAuthorize("@ss.hasPermission('point:sign-in-record:query')")
public CommonResult<List<SignInRecordRespVO>> getSignInRecordList(@RequestParam("ids") Collection<Long> ids) {
List<SignInRecordDO> list = signInRecordService.getSignInRecordList(ids);
return success(SignInRecordConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@Operation(summary = "获得用户签到积分分页")
@PreAuthorize("@ss.hasPermission('point:sign-in-record:query')")
public CommonResult<PageResult<SignInRecordRespVO>> getSignInRecordPage(@Valid SignInRecordPageReqVO pageVO) {
PageResult<SignInRecordDO> pageResult = signInRecordService.getSignInRecordPage(pageVO);
return success(SignInRecordConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/export-excel")
@Operation(summary = "导出用户签到积分 Excel")
@PreAuthorize("@ss.hasPermission('point:sign-in-record:export')")
@OperateLog(type = EXPORT)
public void exportSignInRecordExcel(@Valid SignInRecordExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<SignInRecordDO> list = signInRecordService.getSignInRecordList(exportReqVO);
// 导出 Excel
List<SignInRecordExcelVO> datas = SignInRecordConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "用户签到积分.xls", "数据", SignInRecordExcelVO.class, datas);
}
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import javax.validation.constraints.*;
/**
* 用户签到积分 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class SignInRecordBaseVO {
@Schema(description = "签到用户", example = "6507")
private Integer userId;
@Schema(description = "第几天签到")
private Integer day;
@Schema(description = "签到的分数")
private Integer point;
}

View File

@ -0,0 +1,14 @@
package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 用户签到积分创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SignInRecordCreateReqVO extends SignInRecordBaseVO {
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.ExcelProperty;
/**
* 用户签到积分 Excel VO
*
* @author 芋道源码
*/
@Data
public class SignInRecordExcelVO {
@ExcelProperty("签到自增id")
private Long id;
@ExcelProperty("签到用户")
private Integer userId;
@ExcelProperty("第几天签到")
private Integer day;
@ExcelProperty("签到的分数")
private Integer point;
@ExcelProperty("签到时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import java.time.LocalDateTime;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 用户签到积分 Excel 导出 Request VO参数和 SignInRecordPageReqVO 是一致的")
@Data
public class SignInRecordExportReqVO {
@Schema(description = "签到用户", example = "6507")
private Integer userId;
@Schema(description = "第几天签到")
private Integer day;
@Schema(description = "签到时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 用户签到积分分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SignInRecordPageReqVO extends PageParam {
@Schema(description = "签到用户", example = "6507")
private Integer userId;
@Schema(description = "第几天签到")
private Integer day;
@Schema(description = "签到时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 用户签到积分 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SignInRecordRespVO extends SignInRecordBaseVO {
@Schema(description = "签到自增id", requiredMode = Schema.RequiredMode.REQUIRED, example = "11903")
private Long id;
@Schema(description = "签到时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 用户签到积分更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SignInRecordUpdateReqVO extends SignInRecordBaseVO {
@Schema(description = "签到自增id", requiredMode = Schema.RequiredMode.REQUIRED, example = "11903")
@NotNull(message = "签到自增id不能为空")
private Long id;
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.point.convert.pointrecord;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo.*;
import cn.iocoder.yudao.module.point.dal.dataobject.pointrecord.PointRecordDO;
/**
* 用户积分记录 Convert
*
* @author QingX
*/
@Mapper
public interface PointRecordConvert {
PointRecordConvert INSTANCE = Mappers.getMapper(PointRecordConvert.class);
PointRecordDO convert(PointRecordCreateReqVO bean);
PointRecordDO convert(PointRecordUpdateReqVO bean);
PointRecordRespVO convert(PointRecordDO bean);
List<PointRecordRespVO> convertList(List<PointRecordDO> list);
PageResult<PointRecordRespVO> convertPage(PageResult<PointRecordDO> page);
List<PointRecordExcelVO> convertList02(List<PointRecordDO> list);
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.point.convert.signinconfig;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo.*;
import cn.iocoder.yudao.module.point.dal.dataobject.signinconfig.SignInConfigDO;
/**
* 积分签到规则 Convert
*
* @author QingX
*/
@Mapper
public interface SignInConfigConvert {
SignInConfigConvert INSTANCE = Mappers.getMapper(SignInConfigConvert.class);
SignInConfigDO convert(SignInConfigCreateReqVO bean);
SignInConfigDO convert(SignInConfigUpdateReqVO bean);
SignInConfigRespVO convert(SignInConfigDO bean);
List<SignInConfigRespVO> convertList(List<SignInConfigDO> list);
PageResult<SignInConfigRespVO> convertPage(PageResult<SignInConfigDO> page);
List<SignInConfigExcelVO> convertList02(List<SignInConfigDO> list);
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.point.convert.signinrecord;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo.*;
import cn.iocoder.yudao.module.point.dal.dataobject.signinrecord.SignInRecordDO;
/**
* 用户签到积分 Convert
*
* @author 芋道源码
*/
@Mapper
public interface SignInRecordConvert {
SignInRecordConvert INSTANCE = Mappers.getMapper(SignInRecordConvert.class);
SignInRecordDO convert(SignInRecordCreateReqVO bean);
SignInRecordDO convert(SignInRecordUpdateReqVO bean);
SignInRecordRespVO convert(SignInRecordDO bean);
List<SignInRecordRespVO> convertList(List<SignInRecordDO> list);
PageResult<SignInRecordRespVO> convertPage(PageResult<SignInRecordDO> page);
List<SignInRecordExcelVO> convertList02(List<SignInRecordDO> list);
}

Some files were not shown because too many files have changed in this diff Show More