- 1.8.0-snapshot
+ 1.8.1-snapshot
+ 1.5.0
2.7.14
@@ -23,10 +24,10 @@
2.5
1.2.18
- 3.5.3.1
- 3.5.3.1
+ 3.5.3.2
+ 3.5.3.2
3.6.1
- 1.4.5
+ 1.4.6
3.18.0
8.1.2.141
@@ -219,7 +220,7 @@
com.github.yulichang
mybatis-plus-join-boot-starter
- ${mybatis-plus-join-boot-starter.version}
+ ${mybatis-plus-join.version}
@@ -645,4 +646,35 @@
+
+
+
+
+ org.codehaus.mojo
+ flatten-maven-plugin
+ ${flatten-maven-plugin.version}
+
+ resolveCiFriendliesOnly
+ true
+
+
+
+
+ flatten
+
+ flatten
+ process-resources
+
+
+
+ clean
+
+ flatten.clean
+ clean
+
+
+
+
+
+
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java
index bfb291b6d..e29292dd8 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java
@@ -41,7 +41,7 @@ public class CommonResult implements Serializable {
* 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
*
* @param result 传入的 result 对象
- * @param 返回的泛型
+ * @param 返回的泛型
* @return 新的 CommonResult 对象
*/
public static CommonResult error(CommonResult> result) {
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/SortingField.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/SortingField.java
index 98411730e..07a68b064 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/SortingField.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/SortingField.java
@@ -1,5 +1,9 @@
package cn.iocoder.yudao.framework.common.pojo;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
import java.io.Serializable;
/**
@@ -7,6 +11,9 @@ import java.io.Serializable;
*
* 类名加了 ing 的原因是,避免和 ES SortField 重名。
*/
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
public class SortingField implements Serializable {
/**
@@ -27,30 +34,4 @@ public class SortingField implements Serializable {
*/
private String order;
- // 空构造方法,解决反序列化
- public SortingField() {
- }
-
- public SortingField(String field, String order) {
- this.field = field;
- this.order = order;
- }
-
- public String getField() {
- return field;
- }
-
- public SortingField setField(String field) {
- this.field = field;
- return this;
- }
-
- public String getOrder() {
- return order;
- }
-
- public SortingField setOrder(String order) {
- this.order = order;
- return this;
- }
}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
index c2d069a59..c7e50a487 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
@@ -23,6 +23,8 @@ public class DateUtils {
*/
public static final long SECOND_MILLIS = 1000;
+ public static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd";
+
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
public static final String FORMAT_HOUR_MINUTE_SECOND = "HH:mm:ss";
@@ -34,6 +36,9 @@ public class DateUtils {
* @return LocalDateTime
*/
public static Date of(LocalDateTime date) {
+ if (date == null) {
+ return null;
+ }
// 将此日期时间与时区相结合以创建 ZonedDateTime
ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault());
// 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳
@@ -49,6 +54,9 @@ public class DateUtils {
* @return LocalDateTime
*/
public static LocalDateTime of(Date date) {
+ if (date == null) {
+ return null;
+ }
// 转为时间戳
Instant instant = date.toInstant();
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java
new file mode 100644
index 000000000..e2fd3fa6e
--- /dev/null
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java
@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.framework.common.util.number;
+
+import cn.hutool.core.util.NumberUtil;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+/**
+ * 金额工具类
+ *
+ * @author 芋道源码
+ */
+public class MoneyUtils {
+
+ /**
+ * 计算百分比金额,四舍五入
+ *
+ * @param price 金额
+ * @param rate 百分比,例如说 56.77% 则传入 56.77
+ * @return 百分比金额
+ */
+ public static Integer calculateRatePrice(Integer price, Double rate) {
+ return calculateRatePrice(price, rate, 0, RoundingMode.HALF_UP).intValue();
+ }
+
+ /**
+ * 计算百分比金额,向下传入
+ *
+ * @param price 金额
+ * @param rate 百分比,例如说 56.77% 则传入 56.77
+ * @return 百分比金额
+ */
+ public static Integer calculateRatePriceFloor(Integer price, Double rate) {
+ return calculateRatePrice(price, rate, 0, RoundingMode.FLOOR).intValue();
+ }
+
+ /**
+ * 计算百分比金额
+ *
+ * @param price 金额
+ * @param rate 百分比,例如说 56.77% 则传入 56.77
+ * @param scale 保留小数位数
+ * @param roundingMode 舍入模式
+ */
+ public static BigDecimal calculateRatePrice(Number price, Number rate, int scale, RoundingMode roundingMode) {
+ return NumberUtil.toBigDecimal(price).multiply(NumberUtil.toBigDecimal(rate)) // 乘以
+ .divide(BigDecimal.valueOf(100), scale, roundingMode); // 除以 100
+ }
+
+}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnum.java
index c2d56b0c5..139b08965 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnum.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnum.java
@@ -17,7 +17,7 @@ import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
- validatedBy = InEnumValidator.class
+ validatedBy = {InEnumValidator.class, InEnumCollectionValidator.class}
)
public @interface InEnum {
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumCollectionValidator.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumCollectionValidator.java
new file mode 100644
index 000000000..d20a71703
--- /dev/null
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumCollectionValidator.java
@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.framework.common.validation;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class InEnumCollectionValidator implements ConstraintValidator> {
+
+ private List values;
+
+ @Override
+ public void initialize(InEnum annotation) {
+ IntArrayValuable[] values = annotation.value().getEnumConstants();
+ if (values.length == 0) {
+ this.values = Collections.emptyList();
+ } else {
+ this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
+ }
+ }
+
+ @Override
+ public boolean isValid(Collection list, ConstraintValidatorContext context) {
+ // 校验通过
+ if (CollUtil.containsAll(values, list)) {
+ return true;
+ }
+ // 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
+ context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
+ context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
+ .replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句
+ return false;
+ }
+
+}
+
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptor.java b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptor.java
index 6e5564865..98f64d11a 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptor.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptor.java
@@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactory;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
-import com.alibaba.ttl.TransmittableThreadLocal;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
@@ -508,6 +507,9 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme
// 单条规则的条件
String tableName = MyBatisUtils.getTableName(table);
Expression oneExpress = rule.getExpression(tableName, table.getAlias());
+ if (oneExpress == null){
+ continue;
+ }
// 拼接到 allExpression 中
allExpression = allExpression == null ? oneExpress
: new AndExpression(allExpression, oneExpress);
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java
index 41f03fbe6..bc54314b4 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java
@@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
-import cn.iocoder.yudao.framework.expression.OrExpressionX;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
@@ -17,10 +16,8 @@ import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespD
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import net.sf.jsqlparser.expression.Alias;
-import net.sf.jsqlparser.expression.Expression;
-import net.sf.jsqlparser.expression.LongValue;
-import net.sf.jsqlparser.expression.NullValue;
+import net.sf.jsqlparser.expression.*;
+import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
@@ -144,7 +141,7 @@ public class DeptDataPermissionRule implements DataPermissionRule {
return deptExpression;
}
// 目前,如果有指定部门 + 可查看自己,采用 OR 条件。即,WHERE (dept_id IN ? OR user_id = ?)
- return new OrExpressionX(deptExpression, userExpression);
+ return new Parenthesis(new OrExpression(deptExpression, userExpression));
}
private Expression buildDeptExpression(String tableName, Alias tableAlias, Set deptIds) {
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientFactory.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientFactory.java
index f6d0ca6b5..53f1a8c06 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientFactory.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientFactory.java
@@ -1,5 +1,7 @@
package cn.iocoder.yudao.framework.pay.core.client;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
+
/**
* 支付客户端的工厂接口
*
@@ -25,4 +27,12 @@ public interface PayClientFactory {
void createOrUpdatePayClient(Long channelId, String channelCode,
Config config);
+ /**
+ * 注册支付客户端 Class,用于模块中实现的 PayClient
+ *
+ * @param channel 支付渠道的编码的枚举
+ * @param payClientClass 支付客户端 class
+ */
+ void registerPayClientClass(PayChannelEnum channel, Class> payClientClass);
+
}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClientConfig.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/NonePayClientConfig.java
similarity index 57%
rename from yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClientConfig.java
rename to yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/NonePayClientConfig.java
index 3e35c52dc..48319036c 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClientConfig.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/NonePayClientConfig.java
@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.framework.pay.core.client.impl.mock;
+package cn.iocoder.yudao.framework.pay.core.client.impl;
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
import lombok.Data;
@@ -6,23 +6,26 @@ import lombok.Data;
import javax.validation.Validator;
/**
- * 模拟支付的 PayClientConfig 实现类
+ * 无需任何配置 PayClientConfig 实现类
*
* @author jason
*/
@Data
-public class MockPayClientConfig implements PayClientConfig {
+public class NonePayClientConfig implements PayClientConfig {
/**
* 配置名称
- *
+ *
* 如果不加任何属性,JsonUtils.parseObject2 解析会报错,所以暂时加个名称
*/
private String name;
- @Override
- public void validate(Validator validator) {
- // 模拟支付配置无需校验
+ public NonePayClientConfig(){
+ this.name = "none-config";
}
+ @Override
+ public void validate(Validator validator) {
+ // 无任何配置不需要校验
+ }
}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
index 121aeb087..0b39587ab 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
@@ -1,19 +1,22 @@
package cn.iocoder.yudao.framework.pay.core.client.impl;
import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.*;
import cn.iocoder.yudao.framework.pay.core.client.impl.mock.MockPayClient;
-import cn.iocoder.yudao.framework.pay.core.client.impl.mock.MockPayClientConfig;
import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.*;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import lombok.extern.slf4j.Slf4j;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import static cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum.*;
+
/**
* 支付客户端的工厂实现类
*
@@ -24,10 +27,38 @@ public class PayClientFactoryImpl implements PayClientFactory {
/**
* 支付客户端 Map
+ *
* key:渠道编号
*/
private final ConcurrentMap> clients = new ConcurrentHashMap<>();
+ /**
+ * 支付客户端 Class Map
+ */
+ private final Map> clientClass = new ConcurrentHashMap<>();
+
+ public PayClientFactoryImpl() {
+ // 微信支付客户端
+ clientClass.put(WX_PUB, WxPubPayClient.class);
+ clientClass.put(WX_LITE, WxLitePayClient.class);
+ clientClass.put(WX_APP, WxAppPayClient.class);
+ clientClass.put(WX_BAR, WxBarPayClient.class);
+ clientClass.put(WX_NATIVE, WxNativePayClient.class);
+ // 支付包支付客户端
+ clientClass.put(ALIPAY_WAP, AlipayWapPayClient.class);
+ clientClass.put(ALIPAY_QR, AlipayQrPayClient.class);
+ clientClass.put(ALIPAY_APP, AlipayAppPayClient.class);
+ clientClass.put(ALIPAY_PC, AlipayPcPayClient.class);
+ clientClass.put(ALIPAY_BAR, AlipayBarPayClient.class);
+ // Mock 支付客户端
+ clientClass.put(MOCK, MockPayClient.class);
+ }
+
+ @Override
+ public void registerPayClientClass(PayChannelEnum channel, Class> payClientClass) {
+ clientClass.put(channel, payClientClass);
+ }
+
@Override
public PayClient getPayClient(Long channelId) {
AbstractPayClient> client = clients.get(channelId);
@@ -52,30 +83,13 @@ public class PayClientFactoryImpl implements PayClientFactory {
}
@SuppressWarnings("unchecked")
- private AbstractPayClient createPayClient(
- Long channelId, String channelCode, Config config) {
+ private AbstractPayClient createPayClient(Long channelId, String channelCode,
+ Config config) {
PayChannelEnum channelEnum = PayChannelEnum.getByCode(channelCode);
- Assert.notNull(channelEnum, String.format("支付渠道(%s) 为空", channelEnum));
- // 创建客户端
- switch (channelEnum) {
- // 微信支付
- case WX_PUB: return (AbstractPayClient) new WxPubPayClient(channelId, (WxPayClientConfig) config);
- case WX_LITE: return (AbstractPayClient) new WxLitePayClient(channelId, (WxPayClientConfig) config);
- case WX_APP: return (AbstractPayClient) new WxAppPayClient(channelId, (WxPayClientConfig) config);
- case WX_BAR: return (AbstractPayClient) new WxBarPayClient(channelId, (WxPayClientConfig) config);
- case WX_NATIVE: return (AbstractPayClient) new WxNativePayClient(channelId, (WxPayClientConfig) config);
- // 支付宝支付
- case ALIPAY_WAP: return (AbstractPayClient) new AlipayWapPayClient(channelId, (AlipayPayClientConfig) config);
- case ALIPAY_QR: return (AbstractPayClient) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config);
- case ALIPAY_APP: return (AbstractPayClient) new AlipayAppPayClient(channelId, (AlipayPayClientConfig) config);
- case ALIPAY_PC: return (AbstractPayClient) new AlipayPcPayClient(channelId, (AlipayPayClientConfig) config);
- case ALIPAY_BAR: return (AbstractPayClient) new AlipayBarPayClient(channelId, (AlipayPayClientConfig) config);
- // 其它支付
- case MOCK: return (AbstractPayClient) new MockPayClient(channelId, (MockPayClientConfig) config);
- }
- // 创建失败,错误日志 + 抛出异常
- log.error("[createPayClient][配置({}) 找不到合适的客户端实现]", config);
- throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", config));
+ Assert.notNull(channelEnum, String.format("支付渠道(%s) 为空", channelCode));
+ Class> payClientClass = clientClass.get(channelEnum);
+ Assert.notNull(payClientClass, String.format("支付渠道(%s) Class 为空", channelCode));
+ return (AbstractPayClient) ReflectUtil.newInstance(payClientClass, channelId, config);
}
}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
index 5e52e8e8e..b1edf87ac 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
@@ -27,6 +27,7 @@ import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
+import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@@ -47,6 +48,7 @@ import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
@Slf4j
public abstract class AbstractAlipayPayClient extends AbstractPayClient {
+ @Getter // 仅用于单测场景
protected DefaultAlipayClient client;
public AbstractAlipayPayClient(Long channelId, String channelCode, AlipayPayClientConfig config) {
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
index 5254bc8c9..94644430f 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
@@ -13,6 +13,8 @@ import com.alipay.api.request.AlipayTradePayRequest;
import com.alipay.api.response.AlipayTradePayResponse;
import lombok.extern.slf4j.Slf4j;
+import java.time.LocalDateTime;
+
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
@@ -63,8 +65,10 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
return buildClosedPayOrderRespDTO(reqDTO, response);
}
if ("10000".equals(response.getCode())) { // 免密支付
- return PayOrderRespDTO.successOf(response.getTradeNo(), response.getBuyerUserId(), LocalDateTimeUtil.of(response.getGmtPayment()),
- response.getOutTradeNo(), response);
+ LocalDateTime successTime = LocalDateTimeUtil.of(response.getGmtPayment());
+ return PayOrderRespDTO.successOf(response.getTradeNo(), response.getBuyerUserId(), successTime,
+ response.getOutTradeNo(), response)
+ .setDisplayMode(displayMode).setDisplayContent("");
}
// 大额支付,需要用户输入密码,所以返回 waiting。此时,前端一般会进行轮询
return PayOrderRespDTO.waitingOf(displayMode, "",
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java
index 1d3f4d48b..f26e76c28 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDT
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
+import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import java.time.LocalDateTime;
@@ -17,11 +18,11 @@ import java.util.Map;
*
* @author jason
*/
-public class MockPayClient extends AbstractPayClient {
+public class MockPayClient extends AbstractPayClient {
private static final String MOCK_RESP_SUCCESS_DATA = "MOCK_SUCCESS";
- public MockPayClient(Long channelId, MockPayClientConfig config) {
+ public MockPayClient(Long channelId, NonePayClientConfig config) {
super(channelId, PayChannelEnum.MOCK.getCode(), config);
}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
index 76a162ebf..87b9c4bc2 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
@@ -56,7 +56,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient {
+ o.setServerUrl(randomURL());
+ o.setPrivateKey(randomString());
+ o.setMode(MODE_PUBLIC_KEY);
+ o.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
+ o.setAppCertContent("");
+ o.setAlipayPublicCertContent("");
+ o.setRootCertContent("");
+ });
+
+ @Mock
+ protected DefaultAlipayClient defaultAlipayClient;
+
+ @Setter
+ private AbstractAlipayPayClient client;
+
+ /**
+ * 子类需要实现该方法. 设置 client 的具体实现
+ */
+ @BeforeEach
+ public abstract void setUp();
+
+ @Test
+ @DisplayName("支付宝 Client 初始化")
+ public void testDoInit() {
+ // 调用
+ client.doInit();
+ // 断言
+ DefaultAlipayClient realClient = client.getClient();
+ assertNotSame(defaultAlipayClient, realClient);
+ assertInstanceOf(DefaultSigner.class, realClient.getSigner());
+ assertEquals(config.getPrivateKey(), ((DefaultSigner) realClient.getSigner()).getPrivateKey());
+ }
+
+ @Test
+ @DisplayName("支付宝 Client 统一退款:成功")
+ public void testUnifiedRefund_success() throws AlipayApiException {
+ // mock 方法
+ String notifyUrl = randomURL();
+ Date refundTime = randomDate();
+ String outRefundNo = randomString();
+ String outTradeNo = randomString();
+ Integer refundAmount = randomInteger();
+ AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {
+ o.setSubCode("");
+ o.setGmtRefundPay(refundTime);
+ });
+ when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> {
+ assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());
+ AlipayTradeRefundModel bizModel = (AlipayTradeRefundModel) request.getBizModel();
+ assertEquals(outRefundNo, bizModel.getOutRequestNo());
+ assertEquals(outTradeNo, bizModel.getOutTradeNo());
+ assertEquals(String.valueOf(refundAmount / 100.0), bizModel.getRefundAmount());
+ return true;
+ }))).thenReturn(response);
+ // 准备请求参数
+ PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
+ o.setOutRefundNo(outRefundNo);
+ o.setOutTradeNo(outTradeNo);
+ o.setNotifyUrl(notifyUrl);
+ o.setRefundPrice(refundAmount);
+ });
+
+ // 调用
+ PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);
+ // 断言
+ assertEquals(PayRefundStatusRespEnum.SUCCESS.getStatus(), resp.getStatus());
+ assertEquals(outRefundNo, resp.getOutRefundNo());
+ assertNull(resp.getChannelRefundNo());
+ assertEquals(LocalDateTimeUtil.of(refundTime), resp.getSuccessTime());
+ assertSame(response, resp.getRawData());
+ assertNull(resp.getChannelErrorCode());
+ assertNull(resp.getChannelErrorMsg());
+ }
+
+ @Test
+ @DisplayName("支付宝 Client 统一退款:渠道返回失败")
+ public void test_unified_refund_channel_failed() throws AlipayApiException {
+ // mock 方法
+ String notifyUrl = randomURL();
+ String subCode = randomString();
+ String subMsg = randomString();
+ AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {
+ o.setSubCode(subCode);
+ o.setSubMsg(subMsg);
+ });
+ when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> {
+ assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());
+ return true;
+ }))).thenReturn(response);
+ // 准备请求参数
+ String outRefundNo = randomString();
+ String outTradeNo = randomString();
+ PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
+ o.setOutRefundNo(outRefundNo);
+ o.setOutTradeNo(outTradeNo);
+ o.setNotifyUrl(notifyUrl);
+ });
+
+ // 调用
+ PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);
+ // 断言
+ assertEquals(PayRefundStatusRespEnum.FAILURE.getStatus(), resp.getStatus());
+ assertEquals(outRefundNo, resp.getOutRefundNo());
+ assertNull(resp.getChannelRefundNo());
+ assertNull(resp.getSuccessTime());
+ assertSame(response, resp.getRawData());
+ assertEquals(subCode, resp.getChannelErrorCode());
+ assertEquals(subMsg, resp.getChannelErrorMsg());
+ }
+
+ @Test
+ @DisplayName("支付宝 Client 统一退款:参数校验不通过")
+ public void testUnifiedRefund_paramInvalidate() {
+ // 准备请求参数
+ String notifyUrl = randomURL();
+ PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
+ o.setOutTradeNo("");
+ o.setNotifyUrl(notifyUrl);
+ });
+
+ // 调用,并断言
+ assertThrows(ConstraintViolationException.class, () -> client.unifiedRefund(refundReqDTO));
+ }
+
+ @Test
+ @DisplayName("支付宝 Client 统一退款:抛出业务异常")
+ public void testUnifiedRefund_throwServiceException() throws AlipayApiException {
+ // mock 方法
+ when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> true)))
+ .thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
+ // 准备请求参数
+ String notifyUrl = randomURL();
+ PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> o.setNotifyUrl(notifyUrl));
+
+ // 调用,并断言
+ assertThrows(ServiceException.class, () -> client.unifiedRefund(refundReqDTO));
+ }
+
+ @Test
+ @DisplayName("支付宝 Client 统一退款:抛出系统异常")
+ public void testUnifiedRefund_throwPayException() throws AlipayApiException {
+ // mock 方法
+ when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> true)))
+ .thenThrow(new RuntimeException("系统异常"));
+ // 准备请求参数
+ String notifyUrl = randomURL();
+ PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> o.setNotifyUrl(notifyUrl));
+
+ // 调用,并断言
+ assertThrows(PayException.class, () -> client.unifiedRefund(refundReqDTO));
+ }
+
+ @Test
+ @DisplayName("支付宝 Client 统一下单:参数校验不通过")
+ public void testUnifiedOrder_paramInvalidate() {
+ // 准备请求参数
+ String outTradeNo = randomString();
+ String notifyUrl = randomURL();
+ PayOrderUnifiedReqDTO reqDTO = randomPojo(PayOrderUnifiedReqDTO.class, o -> {
+ o.setOutTradeNo(outTradeNo);
+ o.setNotifyUrl(notifyUrl);
+ });
+
+ // 调用,并断言
+ assertThrows(ConstraintViolationException.class, () -> client.unifiedOrder(reqDTO));
+ }
+
+ protected PayOrderUnifiedReqDTO buildOrderUnifiedReqDTO(String notifyUrl, String outTradeNo, Integer price) {
+ return randomPojo(PayOrderUnifiedReqDTO.class, o -> {
+ o.setOutTradeNo(outTradeNo);
+ o.setNotifyUrl(notifyUrl);
+ o.setPrice(price);
+ o.setSubject(RandomUtil.randomString(32));
+ o.setBody(RandomUtil.randomString(32));
+ });
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClientTest.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClientTest.java
new file mode 100644
index 000000000..47f10081c
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClientTest.java
@@ -0,0 +1,170 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
+
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.domain.AlipayTradePayModel;
+import com.alipay.api.request.AlipayTradePayRequest;
+import com.alipay.api.response.AlipayTradePayResponse;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.InjectMocks;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.CLOSED;
+import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.WAITING;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.when;
+
+/**
+ * {@link AlipayBarPayClient} 单元测试
+ *
+ * @author jason
+ */
+public class AlipayBarPayClientTest extends AbstractAlipayClientTest {
+
+ @InjectMocks
+ private AlipayBarPayClient client = new AlipayBarPayClient(randomLongId(), config);
+
+ @Override
+ @BeforeEach
+ public void setUp() {
+ setClient(client);
+ }
+
+ @Test
+ @DisplayName("支付宝条码支付:非免密码支付下单成功")
+ public void testUnifiedOrder_success() throws AlipayApiException {
+ // mock 方法
+ String outTradeNo = randomString();
+ String notifyUrl = randomURL();
+ Integer price = randomInteger();
+ String authCode = randomString();
+ AlipayTradePayResponse response = randomPojo(AlipayTradePayResponse.class, o -> o.setSubCode(""));
+ when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> {
+ assertInstanceOf(AlipayTradePayModel.class, request.getBizModel());
+ assertEquals(notifyUrl, request.getNotifyUrl());
+ AlipayTradePayModel model = (AlipayTradePayModel) request.getBizModel();
+ assertEquals(outTradeNo, model.getOutTradeNo());
+ assertEquals(String.valueOf(price / 100.0), model.getTotalAmount());
+ assertEquals(authCode, model.getAuthCode());
+ return true;
+ }))).thenReturn(response);
+ // 准备请求参数
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
+ Map extraParam = new HashMap<>();
+ extraParam.put("auth_code", authCode);
+ reqDTO.setChannelExtras(extraParam);
+
+ // 调用方法
+ PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
+ // 断言
+ assertEquals(WAITING.getStatus(), resp.getStatus());
+ assertEquals(outTradeNo, resp.getOutTradeNo());
+ assertNull(resp.getChannelOrderNo());
+ assertNull(resp.getChannelUserId());
+ assertNull(resp.getSuccessTime());
+ assertEquals(PayOrderDisplayModeEnum.BAR_CODE.getMode(), resp.getDisplayMode());
+ assertEquals("", resp.getDisplayContent());
+ assertSame(response, resp.getRawData());
+ assertNull(resp.getChannelErrorCode());
+ assertNull(resp.getChannelErrorMsg());
+ }
+
+ @Test
+ @DisplayName("支付宝条码支付:免密码支付下单成功")
+ public void testUnifiedOrder_code10000Success() throws AlipayApiException {
+ // mock 方法
+ String outTradeNo = randomString();
+ String channelNo = randomString();
+ String channelUserId = randomString();
+ Date payTime = randomDate();
+ AlipayTradePayResponse response = randomPojo(AlipayTradePayResponse.class, o -> {
+ o.setSubCode("");
+ o.setCode("10000");
+ o.setOutTradeNo(outTradeNo);
+ o.setTradeNo(channelNo);
+ o.setBuyerUserId(channelUserId);
+ o.setGmtPayment(payTime);
+ });
+ when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> true)))
+ .thenReturn(response);
+ // 准备请求参数
+ String authCode = randomString();
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), outTradeNo, randomInteger());
+ Map extraParam = new HashMap<>();
+ extraParam.put("auth_code", authCode);
+ reqDTO.setChannelExtras(extraParam);
+
+ // 下单请求
+ PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
+ // 断言
+ assertEquals(PayOrderStatusRespEnum.SUCCESS.getStatus(), resp.getStatus());
+ assertEquals(outTradeNo, resp.getOutTradeNo());
+ assertEquals(channelNo, resp.getChannelOrderNo());
+ assertEquals(channelUserId, resp.getChannelUserId());
+ assertEquals(LocalDateTimeUtil.of(payTime), resp.getSuccessTime());
+ assertEquals(PayOrderDisplayModeEnum.BAR_CODE.getMode(), resp.getDisplayMode());
+ assertEquals("", resp.getDisplayContent());
+ assertSame(response, resp.getRawData());
+ assertNull(resp.getChannelErrorCode());
+ assertNull(resp.getChannelErrorMsg());
+ }
+
+ @Test
+ @DisplayName("支付宝条码支付:没有传条码")
+ public void testUnifiedOrder_emptyAuthCode() {
+ // 准备参数
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), randomString(), randomInteger());
+
+ // 调用,并断言
+ assertThrows(ServiceException.class, () -> client.unifiedOrder(reqDTO));
+ }
+
+ @Test
+ @DisplayName("支付宝条码支付:渠道返回失败")
+ public void test_unified_order_channel_failed() throws AlipayApiException {
+ // mock 方法
+ String subCode = randomString();
+ String subMsg = randomString();
+ AlipayTradePayResponse response = randomPojo(AlipayTradePayResponse.class, o -> {
+ o.setSubCode(subCode);
+ o.setSubMsg(subMsg);
+ });
+ when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> true)))
+ .thenReturn(response);
+ // 准备请求参数
+ String authCode = randomString();
+ String outTradeNo = randomString();
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), outTradeNo, randomInteger());
+ Map extraParam = new HashMap<>();
+ extraParam.put("auth_code", authCode);
+ reqDTO.setChannelExtras(extraParam);
+
+ // 调用方法
+ PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
+ // 断言
+ assertEquals(CLOSED.getStatus(), resp.getStatus());
+ assertEquals(outTradeNo, resp.getOutTradeNo());
+ assertNull(resp.getChannelOrderNo());
+ assertNull(resp.getChannelUserId());
+ assertNull(resp.getSuccessTime());
+ assertNull(resp.getDisplayMode());
+ assertNull(resp.getDisplayContent());
+ assertSame(response, resp.getRawData());
+ assertEquals(subCode, resp.getChannelErrorCode());
+ assertEquals(subMsg, resp.getChannelErrorMsg());
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClientTest.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClientTest.java
new file mode 100644
index 000000000..d78caf28c
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClientTest.java
@@ -0,0 +1,131 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
+
+import cn.hutool.http.Method;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.request.AlipayTradePagePayRequest;
+import com.alipay.api.response.AlipayTradePagePayResponse;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.InjectMocks;
+
+import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.CLOSED;
+import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.WAITING;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+/**
+ * {@link AlipayPcPayClient} 单元测试
+ *
+ * @author jason
+ */
+public class AlipayPcPayClientTest extends AbstractAlipayClientTest {
+
+ @InjectMocks
+ private AlipayPcPayClient client = new AlipayPcPayClient(randomLongId(), config);
+
+ @Override
+ @BeforeEach
+ public void setUp() {
+ setClient(client);
+ }
+
+ @Test
+ @DisplayName("支付宝 PC 网站支付:URL Display Mode 下单成功")
+ public void testUnifiedOrder_urlSuccess() throws AlipayApiException {
+ // mock 方法
+ String notifyUrl = randomURL();
+ AlipayTradePagePayResponse response = randomPojo(AlipayTradePagePayResponse.class, o -> o.setSubCode(""));
+ when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher) request -> true),
+ eq(Method.GET.name()))).thenReturn(response);
+ // 准备请求参数
+ String outTradeNo = randomString();
+ Integer price = randomInteger();
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
+ reqDTO.setDisplayMode(null);
+
+ // 调用
+ PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
+ // 断言
+ assertEquals(WAITING.getStatus(), resp.getStatus());
+ assertEquals(outTradeNo, resp.getOutTradeNo());
+ assertNull(resp.getChannelOrderNo());
+ assertNull(resp.getChannelUserId());
+ assertNull(resp.getSuccessTime());
+ assertEquals(PayOrderDisplayModeEnum.URL.getMode(), resp.getDisplayMode());
+ assertEquals(response.getBody(), resp.getDisplayContent());
+ assertSame(response, resp.getRawData());
+ assertNull(resp.getChannelErrorCode());
+ assertNull(resp.getChannelErrorMsg());
+ }
+
+ @Test
+ @DisplayName("支付宝 PC 网站支付:Form Display Mode 下单成功")
+ public void testUnifiedOrder_formSuccess() throws AlipayApiException {
+ // mock 方法
+ String notifyUrl = randomURL();
+ AlipayTradePagePayResponse response = randomPojo(AlipayTradePagePayResponse.class, o -> o.setSubCode(""));
+ when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher) request -> true),
+ eq(Method.POST.name()))).thenReturn(response);
+ // 准备请求参数
+ String outTradeNo = randomString();
+ Integer price = randomInteger();
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
+ reqDTO.setDisplayMode(PayOrderDisplayModeEnum.FORM.getMode());
+
+ // 调用
+ PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
+ // 断言
+ assertEquals(WAITING.getStatus(), resp.getStatus());
+ assertEquals(outTradeNo, resp.getOutTradeNo());
+ assertNull(resp.getChannelOrderNo());
+ assertNull(resp.getChannelUserId());
+ assertNull(resp.getSuccessTime());
+ assertEquals(PayOrderDisplayModeEnum.FORM.getMode(), resp.getDisplayMode());
+ assertEquals(response.getBody(), resp.getDisplayContent());
+ assertSame(response, resp.getRawData());
+ assertNull(resp.getChannelErrorCode());
+ assertNull(resp.getChannelErrorMsg());
+ }
+
+ @Test
+ @DisplayName("支付宝 PC 网站支付:渠道返回失败")
+ public void testUnifiedOrder_channelFailed() throws AlipayApiException {
+ // mock 方法
+ String subCode = randomString();
+ String subMsg = randomString();
+ AlipayTradePagePayResponse response = randomPojo(AlipayTradePagePayResponse.class, o -> {
+ o.setSubCode(subCode);
+ o.setSubMsg(subMsg);
+ });
+ when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher) request -> true),
+ eq(Method.GET.name()))).thenReturn(response);
+ // 准备请求参数
+ String outTradeNo = randomString();
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), outTradeNo, randomInteger());
+ reqDTO.setDisplayMode(PayOrderDisplayModeEnum.URL.getMode());
+
+ // 调用
+ PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
+ // 断言
+ assertEquals(CLOSED.getStatus(), resp.getStatus());
+ assertEquals(outTradeNo, resp.getOutTradeNo());
+ assertNull(resp.getChannelOrderNo());
+ assertNull(resp.getChannelUserId());
+ assertNull(resp.getSuccessTime());
+ assertNull(resp.getDisplayMode());
+ assertNull(resp.getDisplayContent());
+ assertSame(response, resp.getRawData());
+ assertEquals(subCode, resp.getChannelErrorCode());
+ assertEquals(subMsg, resp.getChannelErrorMsg());
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java
index 0b37db8b8..c7e1eb33f 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java
@@ -1,104 +1,85 @@
package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
-import cn.hutool.core.date.LocalDateTimeUtil;
-import cn.hutool.core.util.RandomUtil;
-import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
-import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
-import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import com.alipay.api.AlipayApiException;
-import com.alipay.api.DefaultAlipayClient;
-import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipayTradePrecreateRequest;
-import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
-import com.alipay.api.response.AlipayTradeRefundResponse;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import javax.validation.ConstraintViolationException;
-import java.util.Date;
-
-import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_PUBLIC_KEY;
import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.CLOSED;
import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.WAITING;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.when;
+
/**
* {@link AlipayQrPayClient} 单元测试
*
* @author jason
*/
-public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
-
- private final AlipayPayClientConfig config = randomPojo(AlipayPayClientConfig.class, t -> {
- t.setServerUrl(randomURL());
- t.setMode(MODE_PUBLIC_KEY);
- t.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
- t.setAppCertContent("");
- t.setAlipayPublicCertContent("");
- t.setRootCertContent("");
- });
+public class AlipayQrPayClientTest extends AbstractAlipayClientTest {
@InjectMocks
- AlipayQrPayClient client = new AlipayQrPayClient(randomLongId(), config);
+ private AlipayQrPayClient client = new AlipayQrPayClient(randomLongId(), config);
- @Mock
- private DefaultAlipayClient defaultAlipayClient;
-
- @Test
- public void test_do_init() {
- client.doInit();
- assertNotSame(defaultAlipayClient, ReflectUtil.getFieldValue(client, "defaultAlipayClient"));
+ @BeforeEach
+ public void setUp() {
+ setClient(client);
}
@Test
- @DisplayName("支付包扫描支付下单成功")
- public void test_unified_order_success() throws AlipayApiException {
- // 准备返回对象
+ @DisplayName("支付宝扫描支付:下单成功")
+ public void testUnifiedOrder_success() throws AlipayApiException {
+ // mock 方法
String notifyUrl = randomURL();
String qrCode = randomString();
+ Integer price = randomInteger();
AlipayTradePrecreateResponse response = randomPojo(AlipayTradePrecreateResponse.class, o -> {
o.setQrCode(qrCode);
o.setSubCode("");
});
- // mock
when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> {
assertEquals(notifyUrl, request.getNotifyUrl());
return true;
}))).thenReturn(response);
// 准备请求参数
String outTradeNo = randomString();
- PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo);
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
+ // 调用
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
// 断言
assertEquals(WAITING.getStatus(), resp.getStatus());
- assertEquals(PayOrderDisplayModeEnum.QR_CODE.getMode(), resp.getDisplayMode());
assertEquals(outTradeNo, resp.getOutTradeNo());
- assertEquals(qrCode, resp.getDisplayContent());
+ assertNull(resp.getChannelOrderNo());
+ assertNull(resp.getChannelUserId());
+ assertNull(resp.getSuccessTime());
+ assertEquals(PayOrderDisplayModeEnum.QR_CODE.getMode(), resp.getDisplayMode());
+ assertEquals(response.getQrCode(), resp.getDisplayContent());
assertSame(response, resp.getRawData());
+ assertNull(resp.getChannelErrorCode());
+ assertNull(resp.getChannelErrorMsg());
}
@Test
- @DisplayName("支付包扫描支付,渠道返回失败")
- public void test_unified_order_channel_failed() throws AlipayApiException {
+ @DisplayName("支付宝扫描支付:渠道返回失败")
+ public void testUnifiedOrder_channelFailed() throws AlipayApiException {
+ // mock 方法
String notifyUrl = randomURL();
String subCode = randomString();
String subMsg = randomString();
+ Integer price = randomInteger();
AlipayTradePrecreateResponse response = randomPojo(AlipayTradePrecreateResponse.class, o -> {
o.setSubCode(subCode);
o.setSubMsg(subMsg);
@@ -110,185 +91,57 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
}))).thenReturn(response);
// 准备请求参数
String outTradeNo = randomString();
- PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo);
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
+ // 调用
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
// 断言
assertEquals(CLOSED.getStatus(), resp.getStatus());
+ assertEquals(outTradeNo, resp.getOutTradeNo());
+ assertNull(resp.getChannelOrderNo());
+ assertNull(resp.getChannelUserId());
+ assertNull(resp.getSuccessTime());
+ assertNull(resp.getDisplayMode());
+ assertNull(resp.getDisplayContent());
+ assertSame(response, resp.getRawData());
assertEquals(subCode, resp.getChannelErrorCode());
assertEquals(subMsg, resp.getChannelErrorMsg());
- assertSame(response, resp.getRawData());
}
@Test
- @DisplayName("支付包扫描支付,抛出系统异常")
- public void test_unified_order_throw_pay_exception() throws AlipayApiException {
- // 准备请求参数
+ @DisplayName("支付宝扫描支付, 抛出系统异常")
+ public void testUnifiedOrder_throwPayException() throws AlipayApiException {
+ // mock 方法
String outTradeNo = randomString();
String notifyUrl = randomURL();
- // mock
+ Integer price = randomInteger();
when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> {
assertEquals(notifyUrl, request.getNotifyUrl());
return true;
}))).thenThrow(new RuntimeException("系统异常"));
// 准备请求参数
- PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo);
- // 断言
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo,price);
+
+ // 调用,并断言
assertThrows(PayException.class, () -> client.unifiedOrder(reqDTO));
}
@Test
- @DisplayName("支付包扫描支付,抛出业务异常")
- public void test_unified_order_throw_service_exception() throws AlipayApiException {
- // 准备请求参数
+ @DisplayName("支付宝 Client 统一下单:抛出业务异常")
+ public void testUnifiedOrder_throwServiceException() throws AlipayApiException {
+ // mock 方法
String outTradeNo = randomString();
String notifyUrl = randomURL();
- // mock
+ Integer price = randomInteger();
when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> {
assertEquals(notifyUrl, request.getNotifyUrl());
return true;
}))).thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
// 准备请求参数
- PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo);
- // 断言
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
+
+ // 调用,并断言
assertThrows(ServiceException.class, () -> client.unifiedOrder(reqDTO));
}
- @Test
- @DisplayName("支付包扫描支付,参数校验不通过")
- public void test_unified_order_param_validate() {
- // 准备请求参数
- String outTradeNo = randomString();
- String notifyUrl = randomURL();
- PayOrderUnifiedReqDTO reqDTO = randomPojo(PayOrderUnifiedReqDTO.class, o -> {
- o.setOutTradeNo(outTradeNo);
- o.setNotifyUrl(notifyUrl);
- });
- // 断言
- assertThrows(ConstraintViolationException.class, () -> client.unifiedOrder(reqDTO));
- }
-
- private PayOrderUnifiedReqDTO buildOrderUnifiedReqDTO(String notifyUrl, String outTradeNo) {
- return randomPojo(PayOrderUnifiedReqDTO.class, o -> {
- o.setOutTradeNo(outTradeNo);
- o.setNotifyUrl(notifyUrl);
- o.setSubject(RandomUtil.randomString(32));
- o.setBody(RandomUtil.randomString(32));
- });
- }
-
- @Test
- @DisplayName("支付包扫描退款成功")
- public void test_unified_refund_success() throws AlipayApiException {
- // 准备返回对象
- String notifyUrl = randomURL();
- Date refundTime = randomDate();
- String outRefundNo = randomString();
- String outTradeNo = randomString();
- Integer refundAmount = randomInteger();
- AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {
- o.setSubCode("");
- o.setGmtRefundPay(refundTime);
- });
- // mock
- when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> {
- assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());
- AlipayTradeRefundModel bizModel = (AlipayTradeRefundModel) request.getBizModel();
- assertEquals(outRefundNo, bizModel.getOutRequestNo());
- assertEquals(outTradeNo, bizModel.getOutTradeNo());
- assertEquals(String.valueOf(refundAmount / 100.0), bizModel.getRefundAmount());
- return true;
- }))).thenReturn(response);
- // 准备请求参数
- PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
- o.setOutRefundNo(outRefundNo);
- o.setOutTradeNo(outTradeNo);
- o.setNotifyUrl(notifyUrl);
- o.setRefundPrice(refundAmount);
- });
- PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);
- // 断言
- assertEquals(PayRefundStatusRespEnum.SUCCESS.getStatus(), resp.getStatus());
- assertNull(resp.getChannelRefundNo());
- assertEquals(LocalDateTimeUtil.of(refundTime), resp.getSuccessTime());
- assertEquals(outRefundNo, resp.getOutRefundNo());
- assertSame(response, resp.getRawData());
- }
-
- @Test
- @DisplayName("支付包扫描退款,渠道返回失败")
- public void test_unified_refund_channel_failed() throws AlipayApiException {
- // 准备返回对象
- String notifyUrl = randomURL();
- String subCode = randomString();
- String subMsg = randomString();
- AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {
- o.setSubCode(subCode);
- o.setSubMsg(subMsg);
- });
- // mock
- when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> {
- assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());
- return true;
- }))).thenReturn(response);
- // 准备请求参数
- String outRefundNo = randomString();
- String outTradeNo = randomString();
- PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
- o.setOutRefundNo(outRefundNo);
- o.setOutTradeNo(outTradeNo);
- o.setNotifyUrl(notifyUrl);
- });
- PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);
- // 断言
- assertEquals(PayRefundStatusRespEnum.FAILURE.getStatus(), resp.getStatus());
- assertNull(resp.getChannelRefundNo());
- assertEquals(subCode, resp.getChannelErrorCode());
- assertEquals(subMsg, resp.getChannelErrorMsg());
- assertNull(resp.getSuccessTime());
- assertEquals(outRefundNo, resp.getOutRefundNo());
- assertSame(response, resp.getRawData());
- }
-
- @Test
- @DisplayName("支付包扫描退款,参数校验不通过")
- public void test_unified_refund_param_validate() {
- // 准备请求参数
- String notifyUrl = randomURL();
- PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
- o.setOutTradeNo("");
- o.setNotifyUrl(notifyUrl);
- });
- // 断言
- assertThrows(ConstraintViolationException.class, () -> client.unifiedRefund(refundReqDTO));
- }
-
- @Test
- @DisplayName("支付包扫描退款,抛出业务异常")
- public void test_unified_refund_throw_service_exception() throws AlipayApiException {
- // mock
- when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> true)))
- .thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
- // 准备请求参数
- String notifyUrl = randomURL();
- PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
- o.setNotifyUrl(notifyUrl);
- });
- // 断言
- assertThrows(ServiceException.class, () -> client.unifiedRefund(refundReqDTO));
- }
- @Test
- @DisplayName("支付包扫描退款,抛出系统异常")
- public void test_unified_refund_throw_pay_exception() throws AlipayApiException {
- // mock
- when(defaultAlipayClient.execute(argThat((ArgumentMatcher) request -> true)))
- .thenThrow(new RuntimeException("系统异常"));
- // 准备请求参数
- String notifyUrl = randomURL();
- PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
- o.setNotifyUrl(notifyUrl);
- });
- // 断言
- assertThrows(PayException.class, () -> client.unifiedRefund(refundReqDTO));
- }
}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClientTest.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClientTest.java
new file mode 100644
index 000000000..af0c7170b
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClientTest.java
@@ -0,0 +1,111 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
+
+import cn.hutool.http.Method;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.domain.AlipayTradeWapPayModel;
+import com.alipay.api.request.AlipayTradeWapPayRequest;
+import com.alipay.api.response.AlipayTradeWapPayResponse;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.InjectMocks;
+
+import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.CLOSED;
+import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.WAITING;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+/**
+ * {@link AlipayWapPayClient} 单元测试
+ *
+ * @author jason
+ */
+public class AlipayWapPayClientTest extends AbstractAlipayClientTest {
+
+ /**
+ * 支付宝 H5 支付 Client
+ */
+ @InjectMocks
+ private AlipayWapPayClient client = new AlipayWapPayClient(randomLongId(), config);
+
+ @BeforeEach
+ public void setUp() {
+ setClient(client);
+ }
+
+ @Test
+ @DisplayName("支付宝 H5 支付:下单成功")
+ public void testUnifiedOrder_success() throws AlipayApiException {
+ // mock 方法
+ String h5Body = randomString();
+ Integer price = randomInteger();
+ AlipayTradeWapPayResponse response = randomPojo(AlipayTradeWapPayResponse.class, o -> {
+ o.setSubCode("");
+ o.setBody(h5Body);
+ });
+ String notifyUrl = randomURL();
+ when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher) request -> {
+ assertInstanceOf(AlipayTradeWapPayModel.class, request.getBizModel());
+ AlipayTradeWapPayModel bizModel = (AlipayTradeWapPayModel) request.getBizModel();
+ assertEquals(String.valueOf(price / 100.0), bizModel.getTotalAmount());
+ assertEquals(notifyUrl, request.getNotifyUrl());
+ return true;
+ }), eq(Method.GET.name()))).thenReturn(response);
+ // 准备请求参数
+ String outTradeNo = randomString();
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
+
+ // 调用
+ PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
+ // 断言
+ assertEquals(WAITING.getStatus(), resp.getStatus());
+ assertEquals(outTradeNo, resp.getOutTradeNo());
+ assertNull(resp.getChannelOrderNo());
+ assertNull(resp.getChannelUserId());
+ assertNull(resp.getSuccessTime());
+ assertEquals(PayOrderDisplayModeEnum.URL.getMode(), resp.getDisplayMode());
+ assertEquals(response.getBody(), resp.getDisplayContent());
+ assertSame(response, resp.getRawData());
+ assertNull(resp.getChannelErrorCode());
+ assertNull(resp.getChannelErrorMsg());
+ }
+
+ @Test
+ @DisplayName("支付宝 H5 支付:渠道返回失败")
+ public void test_unified_order_channel_failed() throws AlipayApiException {
+ // mock 方法
+ String subCode = randomString();
+ String subMsg = randomString();
+ AlipayTradeWapPayResponse response = randomPojo(AlipayTradeWapPayResponse.class, o -> {
+ o.setSubCode(subCode);
+ o.setSubMsg(subMsg);
+ });
+ when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher) request -> true),
+ eq(Method.GET.name()))).thenReturn(response);
+ String outTradeNo = randomString();
+ PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), outTradeNo, randomInteger());
+
+ // 调用
+ PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
+ // 断言
+ assertEquals(CLOSED.getStatus(), resp.getStatus());
+ assertEquals(outTradeNo, resp.getOutTradeNo());
+ assertNull(resp.getChannelOrderNo());
+ assertNull(resp.getChannelUserId());
+ assertNull(resp.getSuccessTime());
+ assertNull(resp.getDisplayMode());
+ assertNull(resp.getDisplayContent());
+ assertSame(response, resp.getRawData());
+ assertEquals(subCode, resp.getChannelErrorCode());
+ assertEquals(subMsg, resp.getChannelErrorMsg());
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/YudaoAuthRequestFactory.java b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/YudaoAuthRequestFactory.java
index 5d5475e84..6cabb0ccd 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/YudaoAuthRequestFactory.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/YudaoAuthRequestFactory.java
@@ -4,6 +4,7 @@ import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.social.core.enums.AuthExtendSource;
import cn.iocoder.yudao.framework.social.core.request.AuthWeChatMiniAppRequest;
+import cn.iocoder.yudao.framework.social.core.request.AuthWeChatMpRequest;
import com.xingyuv.jushauth.cache.AuthStateCache;
import com.xingyuv.jushauth.config.AuthConfig;
import com.xingyuv.jushauth.config.AuthSource;
@@ -13,6 +14,8 @@ import com.xingyuv.justauth.autoconfigure.JustAuthProperties;
import java.lang.reflect.Method;
+import static com.xingyuv.jushauth.config.AuthDefaultSource.WECHAT_MP;
+
/**
* 第三方授权拓展 request 工厂类
* 为使得拓展配置 {@link AuthConfig} 和默认配置齐平,所以自定义本工厂类
@@ -55,6 +58,12 @@ public class YudaoAuthRequestFactory extends AuthRequestFactory {
}
protected AuthRequest getExtendRequest(String source) {
+ // TODO 芋艿:临时兼容 justauth 迁移的类型不对问题;
+ if (WECHAT_MP.name().equalsIgnoreCase(source)) {
+ AuthConfig config = properties.getType().get(WECHAT_MP.name());
+ return new AuthWeChatMpRequest(config, authStateCache);
+ }
+
AuthExtendSource authExtendSource;
try {
authExtendSource = EnumUtil.fromString(AuthExtendSource.class, source.toUpperCase());
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/request/AuthWeChatMpRequest.java b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/request/AuthWeChatMpRequest.java
new file mode 100644
index 000000000..13b77cd35
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/request/AuthWeChatMpRequest.java
@@ -0,0 +1,178 @@
+package cn.iocoder.yudao.framework.social.core.request;
+
+import com.alibaba.fastjson.JSONObject;
+import com.xingyuv.jushauth.cache.AuthStateCache;
+import com.xingyuv.jushauth.config.AuthConfig;
+import com.xingyuv.jushauth.config.AuthDefaultSource;
+import com.xingyuv.jushauth.enums.AuthResponseStatus;
+import com.xingyuv.jushauth.enums.AuthUserGender;
+import com.xingyuv.jushauth.enums.scope.AuthWechatMpScope;
+import com.xingyuv.jushauth.exception.AuthException;
+import com.xingyuv.jushauth.model.AuthCallback;
+import com.xingyuv.jushauth.model.AuthResponse;
+import com.xingyuv.jushauth.model.AuthToken;
+import com.xingyuv.jushauth.model.AuthUser;
+import com.xingyuv.jushauth.request.AuthDefaultRequest;
+import com.xingyuv.jushauth.utils.AuthScopeUtils;
+import com.xingyuv.jushauth.utils.GlobalAuthUtils;
+import com.xingyuv.jushauth.utils.HttpUtils;
+import com.xingyuv.jushauth.utils.UrlBuilder;
+
+/**
+ * 微信公众平台登录
+ *
+ * @author yangkai.shen (https://xkcoding.com)
+ * @since 1.1.0
+ */
+public class AuthWeChatMpRequest extends AuthDefaultRequest {
+ public AuthWeChatMpRequest(AuthConfig config) {
+ super(config, AuthDefaultSource.WECHAT_MP);
+ }
+
+ public AuthWeChatMpRequest(AuthConfig config, AuthStateCache authStateCache) {
+ super(config, AuthDefaultSource.WECHAT_MP, authStateCache);
+ }
+
+ /**
+ * 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
+ *
+ * @param authCallback 回调返回的参数
+ * @return 所有信息
+ */
+ @Override
+ protected AuthToken getAccessToken(AuthCallback authCallback) {
+ return this.getToken(accessTokenUrl(authCallback.getCode()));
+ }
+
+ @Override
+ protected AuthUser getUserInfo(AuthToken authToken) {
+ String openId = authToken.getOpenId();
+
+ String response = doGetUserInfo(authToken);
+ JSONObject object = JSONObject.parseObject(response);
+
+ this.checkResponse(object);
+
+ String location = String.format("%s-%s-%s", object.getString("country"), object.getString("province"), object.getString("city"));
+
+ if (object.containsKey("unionid")) {
+ authToken.setUnionId(object.getString("unionid"));
+ }
+
+ return AuthUser.builder()
+ .rawUserInfo(object)
+ .username(object.getString("nickname"))
+ .nickname(object.getString("nickname"))
+ .avatar(object.getString("headimgurl"))
+ .location(location)
+ .uuid(openId)
+ .gender(AuthUserGender.getWechatRealGender(object.getString("sex")))
+ .token(authToken)
+ .source(source.toString())
+ .build();
+ }
+
+ @Override
+ public AuthResponse refresh(AuthToken oldToken) {
+ return AuthResponse.builder()
+ .code(AuthResponseStatus.SUCCESS.getCode())
+ .data(this.getToken(refreshTokenUrl(oldToken.getRefreshToken())))
+ .build();
+ }
+
+ /**
+ * 检查响应内容是否正确
+ *
+ * @param object 请求响应内容
+ */
+ private void checkResponse(JSONObject object) {
+ if (object.containsKey("errcode")) {
+ throw new AuthException(object.getIntValue("errcode"), object.getString("errmsg"));
+ }
+ }
+
+ /**
+ * 获取token,适用于获取access_token和刷新token
+ *
+ * @param accessTokenUrl 实际请求token的地址
+ * @return token对象
+ */
+ private AuthToken getToken(String accessTokenUrl) {
+ String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl).getBody();
+ JSONObject accessTokenObject = JSONObject.parseObject(response);
+
+ this.checkResponse(accessTokenObject);
+
+ return AuthToken.builder()
+ .accessToken(accessTokenObject.getString("access_token"))
+ .refreshToken(accessTokenObject.getString("refresh_token"))
+ .expireIn(accessTokenObject.getIntValue("expires_in"))
+ .openId(accessTokenObject.getString("openid"))
+ .scope(accessTokenObject.getString("scope"))
+ .build();
+ }
+
+ /**
+ * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
+ *
+ * @param state state 验证授权流程的参数,可以防止csrf
+ * @return 返回授权地址
+ * @since 1.9.3
+ */
+ @Override
+ public String authorize(String state) {
+ return UrlBuilder.fromBaseUrl(source.authorize())
+ .queryParam("appid", config.getClientId())
+ .queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri()))
+ .queryParam("response_type", "code")
+ .queryParam("scope", this.getScopes(",", false, AuthScopeUtils.getDefaultScopes(AuthWechatMpScope.values())))
+ .queryParam("state", getRealState(state).concat("#wechat_redirect"))
+ .build();
+ }
+
+ /**
+ * 返回获取accessToken的url
+ *
+ * @param code 授权码
+ * @return 返回获取accessToken的url
+ */
+ @Override
+ protected String accessTokenUrl(String code) {
+ return UrlBuilder.fromBaseUrl(source.accessToken())
+ .queryParam("appid", config.getClientId())
+ .queryParam("secret", config.getClientSecret())
+ .queryParam("code", code)
+ .queryParam("grant_type", "authorization_code")
+ .build();
+ }
+
+ /**
+ * 返回获取userInfo的url
+ *
+ * @param authToken 用户授权后的token
+ * @return 返回获取userInfo的url
+ */
+ @Override
+ protected String userInfoUrl(AuthToken authToken) {
+ return UrlBuilder.fromBaseUrl(source.userInfo())
+ .queryParam("access_token", authToken.getAccessToken())
+ .queryParam("openid", authToken.getOpenId())
+ .queryParam("lang", "zh_CN")
+ .build();
+ }
+
+ /**
+ * 返回获取userInfo的url
+ *
+ * @param refreshToken getAccessToken方法返回的refreshToken
+ * @return 返回获取userInfo的url
+ */
+ @Override
+ protected String refreshTokenUrl(String refreshToken) {
+ return UrlBuilder.fromBaseUrl(source.refresh())
+ .queryParam("appid", config.getClientId())
+ .queryParam("grant_type", "refresh_token")
+ .queryParam("refresh_token", refreshToken)
+ .build();
+ }
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/expression/AndExpressionX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/expression/AndExpressionX.java
deleted file mode 100644
index 54198e496..000000000
--- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/expression/AndExpressionX.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package cn.iocoder.yudao.framework.expression;
-
-import net.sf.jsqlparser.expression.Expression;
-import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
-
-/**
- * AndExpression 的扩展类(会在原有表达式两端加上括号)
- */
-public class AndExpressionX extends AndExpression {
-
- public AndExpressionX() {
- }
-
- public AndExpressionX(Expression leftExpression, Expression rightExpression) {
- this.setLeftExpression(leftExpression);
- this.setRightExpression(rightExpression);
- }
-
- @Override
- public String toString() {
- return "(" + super.toString() + ")";
- }
-
-}
diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/expression/OrExpressionX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/expression/OrExpressionX.java
deleted file mode 100644
index 19b4695cf..000000000
--- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/expression/OrExpressionX.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package cn.iocoder.yudao.framework.expression;
-
-import net.sf.jsqlparser.expression.Expression;
-import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
-
-/**
- * OrExpression 的扩展类(会在原有表达式两端加上括号)
- */
-public class OrExpressionX extends OrExpression {
-
- public OrExpressionX() {
- }
-
- public OrExpressionX(Expression leftExpression, Expression rightExpression) {
- this.setLeftExpression(leftExpression);
- this.setRightExpression(rightExpression);
- }
-
- @Override
- public String toString() {
- return "(" + super.toString() + ")";
- }
-
-}
diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
index 839b5abb8..3383ec879 100644
--- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
+++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.mybatis.core.mapper;
+import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
@@ -10,6 +11,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.toolkit.Db;
+import com.github.yulichang.base.MPJBaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
@@ -17,8 +19,11 @@ import java.util.List;
/**
* 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力
+ *
+ * 1. {@link BaseMapper} 为 MyBatis Plus 的基础接口,提供基础的 CRUD 能力
+ * 2. {@link MPJBaseMapper} 为 MyBatis Plus Join 的基础接口,提供连表 Join 能力
*/
-public interface BaseMapperX extends BaseMapper {
+public interface BaseMapperX extends MPJBaseMapper {
default PageResult selectPage(PageParam pageParam, @Param("ew") Wrapper queryWrapper) {
// MyBatis Plus 查询
@@ -75,10 +80,16 @@ public interface BaseMapperX extends BaseMapper {
}
default List selectList(String field, Collection> values) {
+ if (CollUtil.isEmpty(values)) {
+ return CollUtil.newArrayList();
+ }
return selectList(new QueryWrapper().in(field, values));
}
default List selectList(SFunction field, Collection> values) {
+ if (CollUtil.isEmpty(values)) {
+ return CollUtil.newArrayList();
+ }
return selectList(new LambdaQueryWrapper().in(field, values));
}
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
index 3a2bea582..832b72fd8 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
@@ -1,7 +1,6 @@
package cn.iocoder.yudao.framework.web.core.util;
import cn.hutool.core.util.NumberUtil;
-import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.web.config.WebProperties;
@@ -89,10 +88,10 @@ public class WebFrameworkUtils {
return userType;
}
// 2. 其次,基于 URL 前缀的约定
- if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) {
+ if (request.getServletPath().startsWith(properties.getAdminApi().getPrefix())) {
return UserTypeEnum.ADMIN.getValue();
}
- if (request.getRequestURI().startsWith(properties.getAppApi().getPrefix())) {
+ if (request.getServletPath().startsWith(properties.getAppApi().getPrefix())) {
return UserTypeEnum.MEMBER.getValue();
}
return null;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmActivityConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmActivityConvert.java
index 509408457..3cb674c1c 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmActivityConvert.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmActivityConvert.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.bpm.convert.task;
+import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
import org.flowable.engine.history.HistoricActivityInstance;
import org.mapstruct.Mapper;
@@ -14,7 +15,7 @@ import java.util.List;
*
* @author 芋道源码
*/
-@Mapper
+@Mapper(uses = DateUtils.class)
public interface BpmActivityConvert {
BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class);
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
index a1eea62e9..8296b8bb3 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.bpm.convert.task;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageItemRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
@@ -28,7 +29,7 @@ import java.util.Map;
*
* @author 芋道源码
*/
-@Mapper
+@Mapper(uses = DateUtils.class)
public interface BpmProcessInstanceConvert {
BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class);
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
index b85c4e533..82ad35d6a 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.convert.task;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskDonePageItemRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
@@ -26,7 +27,7 @@ import java.util.Map;
*
* @author 芋道源码
*/
-@Mapper
+@Mapper(uses = DateUtils.class)
public interface BpmTaskConvert {
BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class);
@@ -46,8 +47,6 @@ public interface BpmTaskConvert {
}
@Mapping(source = "suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState")
- @Mapping(target = "claimTime", expression = "java(bean.getClaimTime()==null?null: LocalDateTime.ofInstant(bean.getClaimTime().toInstant(),ZoneId.systemDefault()))")
- @Mapping(target = "createTime", expression = "java(bean.getCreateTime()==null?null:LocalDateTime.ofInstant(bean.getCreateTime().toInstant(),ZoneId.systemDefault()))")
BpmTaskTodoPageItemRespVO convert1(Task bean);
@Named("convertSuspendedToSuspensionState")
@@ -106,8 +105,6 @@ public interface BpmTaskConvert {
}
@Mapping(source = "taskDefinitionKey", target = "definitionKey")
- @Mapping(target = "createTime", expression = "java(bean.getCreateTime() == null ? null : LocalDateTime.ofInstant(bean.getCreateTime().toInstant(), ZoneId.systemDefault()))")
- @Mapping(target = "endTime", expression = "java(bean.getEndTime() == null ? null : LocalDateTime.ofInstant(bean.getEndTime().toInstant(), ZoneId.systemDefault()))")
BpmTaskRespVO convert3(HistoricTaskInstance bean);
BpmTaskRespVO.User convert3(AdminUserRespDTO bean);
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobLogController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobLogController.java
index 24f1f44b7..1441f9cf6 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobLogController.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobLogController.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.framework.operatelog.core.util.OperateLogUtils;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogExcelVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogPageReqVO;
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm
index a732039ce..148d1fa41 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm
@@ -15,6 +15,9 @@ import ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}
import static ${ServiceExceptionUtilClassName}.exception;
import static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
+
/**
* ${table.classComment} Service 实现类
*
@@ -61,6 +64,9 @@ public class ${table.className}ServiceImpl implements ${table.className}Service
@Override
public ${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id) {
+ if (CollUtil.isEmpty(ids)) {
+ return ListUtil.empty();
+ }
return ${classNameVar}Mapper.selectById(id);
}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
index 1e98abe13..67b51d412 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
@@ -120,7 +120,6 @@
-
#foreach($column in $columns)
#if ($column.listOperationResult)
#set ($dictType=$column.dictType)
@@ -142,7 +141,7 @@
#else
-
+
#end
#end
#end
@@ -180,7 +179,7 @@
<${simpleClassName}Form ref="formRef" @success="getList" />
-
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm
index e5faddd29..d9538a064 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm
@@ -1,47 +1,12 @@
-
-
-
-
-
- {{ t('action.create') }}
-
-
- {{ t('action.export') }}
-
-
-
-
-
-
-
-
- <${simpleClassName}Modal @register="registerModal" @success="reload()" />
-
-
+
+
+
+
+
+ {{ t('action.create') }}
+
+
+ {{ t('action.export') }}
+
+
+
+
+
+
+
+
+ <${simpleClassName}Modal @register="registerModal" @success="reload()" />
+
+
\ No newline at end of file
diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java
index 20f84f87c..03641012f 100644
--- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java
+++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java
@@ -59,5 +59,14 @@ public class ProductSkuRespDTO {
* 商品体积,单位:m^3 平米
*/
private Double volume;
+ // TODO @puhui999:firstBrokeragePrice ;尴尬,我当时打错了;secondBrokeragePrice
+ /**
+ * 一级分销的佣金,单位:分
+ */
+ private Integer firstBrokerageRecord;
+ /**
+ * 二级分销的佣金,单位:分
+ */
+ private Integer secondBrokerageRecord;
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/ProductCommentController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/ProductCommentController.java
index 91020a51c..70e95cd9a 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/ProductCommentController.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/ProductCommentController.java
@@ -51,7 +51,7 @@ public class ProductCommentController {
return success(true);
}
- @PutMapping("/create")
+ @PostMapping("/create")
@Operation(summary = "添加自评")
@PreAuthorize("@ss.hasPermission('product:comment:update')")
public CommonResult createComment(@Valid @RequestBody ProductCommentCreateReqVO createReqVO) {
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentBaseVO.java
index 24d6a5456..f88f5ed68 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentBaseVO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentBaseVO.java
@@ -11,11 +11,9 @@ import java.util.List;
public class ProductCommentBaseVO {
@Schema(description = "评价人", requiredMode = Schema.RequiredMode.REQUIRED, example = "16868")
- @NotNull(message = "评价人不能为空")
private Long userId;
@Schema(description = "评价订单项", requiredMode = Schema.RequiredMode.REQUIRED, example = "19292")
- @NotNull(message = "评价订单项不能为空")
private Long orderItemId;
@Schema(description = "评价人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小姑凉")
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentRespVO.java
index 9205e34cc..d03359f7f 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentRespVO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentRespVO.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.product.controller.admin.comment.vo;
+import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -7,6 +8,7 @@ import lombok.ToString;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
+import java.util.List;
@Schema(description = "管理后台 - 商品评价 Response VO")
@Data
@@ -23,10 +25,10 @@ public class ProductCommentRespVO extends ProductCommentBaseVO {
@Schema(description = "交易订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24428")
private Long orderId;
- @Schema(description = "是否可见:[true:显示 false:隐藏]", requiredMode = Schema.RequiredMode.REQUIRED)
+ @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean visible;
- @Schema(description = "商家是否回复:[1:回复 0:未回复]", requiredMode = Schema.RequiredMode.REQUIRED)
+ @Schema(description = "商家是否回复", requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean replyStatus;
@Schema(description = "回复管理员编号", example = "9527")
@@ -52,4 +54,10 @@ public class ProductCommentRespVO extends ProductCommentBaseVO {
@NotNull(message = "商品 SPU 名称不能为空")
private String spuName;
+ @Schema(description = "商品 SKU 图片地址", example = "https://www.iocoder.cn/yudao.jpg")
+ private String skuPicUrl;
+
+ @Schema(description = "商品 SKU 规格值数组")
+ private List skuProperties;
+
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java
index 038642db1..9acbacd66 100755
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java
@@ -1,57 +1,14 @@
package cn.iocoder.yudao.module.product.controller.admin.sku;
-import cn.hutool.core.collection.CollUtil;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
-import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuOptionRespVO;
-import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
-import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
-import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
-import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
-import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
-import io.swagger.v3.oas.annotations.Operation;
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.RestController;
-import javax.annotation.Resource;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-
-@Tag(name = "管理后台 - 商品 sku")
+@Tag(name = "管理后台 - 商品 SKU")
@RestController
@RequestMapping("/product/sku")
@Validated
public class ProductSkuController {
- @Resource
- private ProductSkuService productSkuService;
- @Resource
- private ProductSpuService productSpuService;
-
- @GetMapping("/get-option-list")
- @Operation(summary = "获得商品 SKU 选项的列表")
-// @PreAuthorize("@ss.hasPermission('product:sku:query')")
- public CommonResult> getSkuOptionList() {
- // 获得 SKU 列表
- List skus = productSkuService.getSkuList();
- if (CollUtil.isEmpty(skus)) {
- return success(Collections.emptyList());
- }
-
- // 获得对应的 SPU 映射
- Map spuMap = productSpuService.getSpuMap(convertSet(skus, ProductSkuDO::getSpuId));
- // 转换为返回结果
- List skuVOs = ProductSkuConvert.INSTANCE.convertList05(skus);
- skuVOs.forEach(sku -> MapUtils.findAndThen(spuMap, sku.getSpuId(),
- spu -> sku.setSpuId(spu.getId()).setSpuName(spu.getName())));
- return success(skuVOs);
- }
-
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java
index 13da82214..f3eee6123 100755
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java
@@ -51,10 +51,10 @@ public class ProductSkuBaseVO {
private Double volume;
@Schema(description = "一级分销的佣金,单位:分", example = "199")
- private Integer subCommissionFirstPrice;
+ private Integer firstBrokerageRecord;
@Schema(description = "二级分销的佣金,单位:分", example = "19")
- private Integer subCommissionSecondPrice;
+ private Integer secondBrokerageRecord;
@Schema(description = "属性数组")
private List properties;
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuOptionRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuOptionRespVO.java
deleted file mode 100644
index f06ecc358..000000000
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuOptionRespVO.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package cn.iocoder.yudao.module.product.controller.admin.sku.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-@Schema(description = "管理后台 - 商品 SKU 选项 Response VO") // 用于前端 SELECT 选项
-@Data
-public class ProductSkuOptionRespVO {
-
- @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
- private Long id;
-
- @Schema(description = "商品 SKU 名字", example = "红色")
- private String name;
-
- @Schema(description = "销售价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
- private String price;
-
- @Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
- private Integer stock;
-
- // ========== 商品 SPU 信息 ==========
-
- @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- private Long spuId;
-
- @Schema(description = "商品 SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "iPhone 11")
- private String spuName;
-
-}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/AppFavoriteController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/AppFavoriteController.java
index cb9f73c87..7e54c8ed9 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/AppFavoriteController.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/favorite/AppFavoriteController.java
@@ -20,7 +20,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
-import java.util.Objects;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -43,6 +42,14 @@ public class AppFavoriteController {
return success(productFavoriteService.createFavorite(getLoginUserId(), reqVO.getSpuId()));
}
+ @PostMapping(value = "/create-list")
+ @Operation(summary = "添加多个商品收藏")
+ @PreAuthenticated
+ public CommonResult createFavoriteList(@RequestBody @Valid AppFavoriteBatchReqVO reqVO) {
+ // todo @jason:待实现;如果有已经收藏的,不用报错,忽略即可;
+ return success(true);
+ }
+
@DeleteMapping(value = "/delete")
@Operation(summary = "取消单个商品收藏")
@PreAuthenticated
@@ -84,8 +91,15 @@ public class AppFavoriteController {
@Operation(summary = "检查是否收藏过商品")
@PreAuthenticated
public CommonResult isFavoriteExists(AppFavoriteReqVO reqVO) {
- ProductFavoriteDO favoriteDO = productFavoriteService.getFavorite(getLoginUserId(), reqVO.getSpuId());
- return success(Objects.nonNull(favoriteDO));
+ ProductFavoriteDO favorite = productFavoriteService.getFavorite(getLoginUserId(), reqVO.getSpuId());
+ return success(favorite != null);
+ }
+
+ @GetMapping(value = "/get-count")
+ @Operation(summary = "获得商品收藏数量")
+ @PreAuthenticated
+ public CommonResult getFavoriteCount() {
+ return success(productFavoriteService.getFavoriteCount(getLoginUserId()));
}
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/comment/ProductCommentConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/comment/ProductCommentConvert.java
index cabc79a7d..944eb2bc2 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/comment/ProductCommentConvert.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/comment/ProductCommentConvert.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.product.convert.comment;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
-import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentCreateReqVO;
@@ -24,6 +23,8 @@ import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
+import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
+
/**
* 商品评价 Convert
*
@@ -60,7 +61,7 @@ public interface ProductCommentConvert {
item.setUserNickname(ProductCommentDO.NICKNAME_ANONYMOUS);
}
// 设置 SKU 规格值
- MapUtils.findAndThen(skuMap, item.getSkuId(),
+ findAndThen(skuMap, item.getSkuId(),
sku -> item.setSkuProperties(convertList01(sku.getProperties())));
});
return page;
@@ -87,7 +88,7 @@ public interface ProductCommentConvert {
@Mapping(target = "scores",
expression = "java(convertScores(createReqDTO.getDescriptionScores(), createReqDTO.getBenefitScores()))")
- default ProductCommentDO convert(ProductCommentCreateReqDTO createReqDTO, ProductSpuDO spuDO, MemberUserRespDTO user) {
+ default ProductCommentDO convert(ProductCommentCreateReqDTO createReqDTO, ProductSpuDO spuDO, ProductSkuDO skuDO, MemberUserRespDTO user) {
ProductCommentDO commentDO = convert(createReqDTO);
if (user != null) {
commentDO.setUserId(user.getId());
@@ -98,9 +99,15 @@ public interface ProductCommentConvert {
commentDO.setSpuId(spuDO.getId());
commentDO.setSpuName(spuDO.getName());
}
+ if (skuDO != null) {
+ commentDO.setSkuPicUrl(skuDO.getPicUrl());
+ commentDO.setSkuProperties(skuDO.getProperties());
+ }
return commentDO;
}
+ @Mapping(target = "visible", constant = "true")
+ @Mapping(target = "replyStatus", constant = "false")
@Mapping(target = "userId", constant = "0L")
@Mapping(target = "orderId", constant = "0L")
@Mapping(target = "orderItemId", constant = "0L")
@@ -111,4 +118,15 @@ public interface ProductCommentConvert {
List convertList02(List list);
+ default ProductCommentDO convert(ProductCommentCreateReqVO createReq, ProductSpuDO spu, ProductSkuDO sku) {
+ ProductCommentDO commentDO = convert(createReq);
+ if (spu != null) {
+ commentDO.setSpuId(spu.getId()).setSpuName(spu.getName());
+ }
+ if (sku != null) {
+ commentDO.setSkuPicUrl(sku.getPicUrl()).setSkuProperties(sku.getProperties());
+ }
+ return commentDO;
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java
index 2b78a00ef..5065a9c40 100755
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java
@@ -5,7 +5,6 @@ import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
-import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuOptionRespVO;
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO;
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
import org.mapstruct.Mapper;
@@ -44,8 +43,6 @@ public interface ProductSkuConvert {
List convertList04(List list);
- List convertList05(List skus);
-
/**
* 获得 SPU 的库存变化 Map
*
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/comment/ProductCommentDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/comment/ProductCommentDO.java
index 366b237a2..40b04caf0 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/comment/ProductCommentDO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/comment/ProductCommentDO.java
@@ -73,11 +73,14 @@ public class ProductCommentDO extends BaseDO {
/**
* 商品 SPU 编号
+ *
* 关联 {@link ProductSpuDO#getId()}
*/
private Long spuId;
/**
* 商品 SPU 名称
+ *
+ * 关联 {@link ProductSpuDO#getName()}
*/
private String spuName;
/**
@@ -86,6 +89,19 @@ public class ProductCommentDO extends BaseDO {
* 关联 {@link ProductSkuDO#getId()}
*/
private Long skuId;
+ /**
+ * 商品 SKU 图片地址
+ *
+ * 关联 {@link ProductSkuDO#getPicUrl()}
+ */
+ private String skuPicUrl;
+ /**
+ * 属性数组,JSON 格式
+ *
+ * 关联 {@link ProductSkuDO#getProperties()}
+ */
+ @TableField(typeHandler = ProductSkuDO.PropertyTypeHandler.class)
+ private List skuProperties;
/**
* 是否可见
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java
index fa576683b..e164915dd 100755
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java
@@ -81,11 +81,11 @@ public class ProductSkuDO extends BaseDO {
/**
* 一级分销的佣金,单位:分
*/
- private Integer subCommissionFirstPrice;
+ private Integer firstBrokerageRecord;
/**
* 二级分销的佣金,单位:分
*/
- private Integer subCommissionSecondPrice;
+ private Integer secondBrokerageRecord;
// ========== 营销相关字段 =========
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/comment/ProductCommentMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/comment/ProductCommentMapper.java
index c933c4370..387a3736b 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/comment/ProductCommentMapper.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/comment/ProductCommentMapper.java
@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.product.dal.mysql.comment;
-
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -20,6 +19,7 @@ public interface ProductCommentMapper extends BaseMapperX {
.eqIfPresent(ProductCommentDO::getOrderId, reqVO.getOrderId())
.eqIfPresent(ProductCommentDO::getSpuId, reqVO.getSpuId())
.eqIfPresent(ProductCommentDO::getScores, reqVO.getScores())
+ .eqIfPresent(ProductCommentDO::getReplyStatus, reqVO.getReplyStatus())
.betweenIfPresent(ProductCommentDO::getCreateTime, reqVO.getCreateTime())
.likeIfPresent(ProductCommentDO::getSpuName, reqVO.getSpuName())
.orderByDesc(ProductCommentDO::getId));
@@ -53,11 +53,10 @@ public interface ProductCommentMapper extends BaseMapperX {
return selectPage(reqVO, queryWrapper);
}
- default ProductCommentDO selectByUserIdAndOrderItemIdAndSpuId(Long userId, Long orderItemId, Long skuId) {
+ default ProductCommentDO selectByUserIdAndOrderItemId(Long userId, Long orderItemId) {
return selectOne(new LambdaQueryWrapperX()
.eq(ProductCommentDO::getUserId, userId)
- .eq(ProductCommentDO::getOrderItemId, orderItemId)
- .eq(ProductCommentDO::getSpuId, skuId));
+ .eq(ProductCommentDO::getOrderItemId, orderItemId));
}
default Long selectCountBySpuId(Long spuId, Boolean visible, Integer type) {
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/favorite/ProductFavoriteMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/favorite/ProductFavoriteMapper.java
index 68f7db319..54d9d2dd6 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/favorite/ProductFavoriteMapper.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/favorite/ProductFavoriteMapper.java
@@ -21,4 +21,8 @@ public interface ProductFavoriteMapper extends BaseMapperX {
.orderByDesc(ProductFavoriteDO::getId));
}
+ default Long selectCountByUserId(Long userId) {
+ return selectCount(ProductFavoriteDO::getUserId, userId);
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java
index 2c2b35197..e531f5513 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java
@@ -24,38 +24,6 @@ import java.util.List;
@Validated
public interface ProductCommentService {
- /**
- * 获得商品评价分页
- *
- * @param pageReqVO 分页查询
- * @return 商品评价分页
- */
- PageResult getCommentPage(ProductCommentPageReqVO pageReqVO);
-
- /**
- * 修改评论是否可见
- *
- * @param updateReqVO 修改评论可见
- */
- void updateCommentVisible(ProductCommentUpdateVisibleReqVO updateReqVO);
-
- /**
- * 商家回复
- *
- * @param replyVO 商家回复
- * @param loginUserId 管理后台商家登陆人 ID
- */
- void replyComment(ProductCommentReplyReqVO replyVO, Long loginUserId);
-
- /**
- * 获得商品评价分页
- *
- * @param pageVO 分页查询
- * @param visible 是否可见
- * @return 商品评价分页
- */
- PageResult getCommentPage(AppCommentPageReqVO pageVO, Boolean visible);
-
/**
* 创建商品评论
* 后台管理员创建评论使用
@@ -73,6 +41,38 @@ public interface ProductCommentService {
*/
Long createComment(ProductCommentCreateReqDTO createReqDTO);
+ /**
+ * 修改评论是否可见
+ *
+ * @param updateReqVO 修改评论可见
+ */
+ void updateCommentVisible(ProductCommentUpdateVisibleReqVO updateReqVO);
+
+ /**
+ * 商家回复
+ *
+ * @param replyVO 商家回复
+ * @param userId 管理后台商家登陆人 ID
+ */
+ void replyComment(ProductCommentReplyReqVO replyVO, Long userId);
+
+ /**
+ * 【管理员】获得商品评价分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 商品评价分页
+ */
+ PageResult getCommentPage(ProductCommentPageReqVO pageReqVO);
+
+ /**
+ * 【会员】获得商品评价分页
+ *
+ * @param pageVO 分页查询
+ * @param visible 是否可见
+ * @return 商品评价分页
+ */
+ PageResult getCommentPage(AppCommentPageReqVO pageVO, Boolean visible);
+
/**
* 获得商品的评价统计
*
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.java
index 97a3d1562..934946109 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.java
@@ -20,7 +20,6 @@ import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
@@ -53,69 +52,55 @@ public class ProductCommentServiceImpl implements ProductCommentService {
private MemberUserApi memberUserApi;
@Override
- @Transactional(rollbackFor = Exception.class)
- public void updateCommentVisible(ProductCommentUpdateVisibleReqVO updateReqVO) {
- // 校验评论是否存在
- ProductCommentDO productCommentDO = validateCommentExists(updateReqVO.getId());
- productCommentDO.setVisible(updateReqVO.getVisible());
-
- // 更新可见状态
- productCommentMapper.updateById(productCommentDO);
- }
-
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void replyComment(ProductCommentReplyReqVO replyVO, Long loginUserId) {
- // 校验评论是否存在
- ProductCommentDO productCommentDO = validateCommentExists(replyVO.getId());
- productCommentDO.setReplyTime(LocalDateTime.now());
- productCommentDO.setReplyUserId(loginUserId);
- productCommentDO.setReplyStatus(Boolean.TRUE);
- productCommentDO.setReplyContent(replyVO.getReplyContent());
-
- // 回复评论
- productCommentMapper.updateById(productCommentDO);
- }
-
- @Override
- @Transactional(rollbackFor = Exception.class)
public void createComment(ProductCommentCreateReqVO createReqVO) {
- // 校验评论
- validateComment(createReqVO.getSkuId(), createReqVO.getUserId(), createReqVO.getOrderItemId());
+ // 校验 SKU
+ ProductSkuDO skuDO = validateSku(createReqVO.getSkuId());
+ // 校验 SPU
+ ProductSpuDO spuDO = validateSpu(skuDO.getSpuId());
- ProductCommentDO commentDO = ProductCommentConvert.INSTANCE.convert(createReqVO);
- productCommentMapper.insert(commentDO);
+ // 创建评论
+ ProductCommentDO comment = ProductCommentConvert.INSTANCE.convert(createReqVO, spuDO, skuDO);
+ productCommentMapper.insert(comment);
}
@Override
- @Transactional(rollbackFor = Exception.class)
public Long createComment(ProductCommentCreateReqDTO createReqDTO) {
- // 通过 sku ID 拿到 spu 相关信息
- ProductSkuDO sku = productSkuService.getSku(createReqDTO.getSkuId());
- if (sku == null) {
- throw exception(SKU_NOT_EXISTS);
- }
- // 校验 spu 如果存在返回详情
- ProductSpuDO spuDO = validateSpu(sku.getSpuId());
+ // 校验 SKU
+ ProductSkuDO skuDO = validateSku(createReqDTO.getSkuId());
+ // 校验 SPU
+ ProductSpuDO spuDO = validateSpu(skuDO.getSpuId());
// 校验评论
- validateComment(spuDO.getId(), createReqDTO.getUserId(), createReqDTO.getOrderId());
+ validateCommentExists(createReqDTO.getUserId(), createReqDTO.getOrderId());
// 获取用户详细信息
MemberUserRespDTO user = memberUserApi.getUser(createReqDTO.getUserId());
// 创建评论
- ProductCommentDO commentDO = ProductCommentConvert.INSTANCE.convert(createReqDTO, spuDO, user);
- productCommentMapper.insert(commentDO);
- return commentDO.getId();
+ ProductCommentDO comment = ProductCommentConvert.INSTANCE.convert(createReqDTO, spuDO, skuDO, user);
+ productCommentMapper.insert(comment);
+ return comment.getId();
}
- private void validateComment(Long skuId, Long userId, Long orderItemId) {
- // 判断当前订单的当前商品用户是否评价过
- ProductCommentDO exist = productCommentMapper.selectByUserIdAndOrderItemIdAndSpuId(userId, orderItemId, skuId);
- if (null != exist) {
+ /**
+ * 判断当前订单的当前商品用户是否评价过
+ *
+ * @param userId 用户编号
+ * @param orderItemId 订单项编号
+ */
+ private void validateCommentExists(Long userId, Long orderItemId) {
+ ProductCommentDO exist = productCommentMapper.selectByUserIdAndOrderItemId(userId, orderItemId);
+ if (exist != null) {
throw exception(COMMENT_ORDER_EXISTS);
}
}
+ private ProductSkuDO validateSku(Long skuId) {
+ ProductSkuDO sku = productSkuService.getSku(skuId);
+ if (sku == null) {
+ throw exception(SKU_NOT_EXISTS);
+ }
+ return sku;
+ }
+
private ProductSpuDO validateSpu(Long spuId) {
ProductSpuDO spu = productSpuService.getSpu(spuId);
if (null == spu) {
@@ -124,6 +109,26 @@ public class ProductCommentServiceImpl implements ProductCommentService {
return spu;
}
+ @Override
+ public void updateCommentVisible(ProductCommentUpdateVisibleReqVO updateReqVO) {
+ // 校验评论是否存在
+ validateCommentExists(updateReqVO.getId());
+
+ // 更新可见状态
+ productCommentMapper.updateById(new ProductCommentDO().setId(updateReqVO.getId())
+ .setVisible(true));
+ }
+
+ @Override
+ public void replyComment(ProductCommentReplyReqVO replyVO, Long userId) {
+ // 校验评论是否存在
+ validateCommentExists(replyVO.getId());
+ // 回复评论
+ productCommentMapper.updateById(new ProductCommentDO().setId(replyVO.getId())
+ .setReplyTime(LocalDateTime.now()).setReplyUserId(userId)
+ .setReplyStatus(Boolean.TRUE).setReplyContent(replyVO.getReplyContent()));
+ }
+
private ProductCommentDO validateCommentExists(Long id) {
ProductCommentDO productComment = productCommentMapper.selectById(id);
if (productComment == null) {
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteService.java
index 7ea7489a4..00aeddb8a 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteService.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteService.java
@@ -16,7 +16,7 @@ public interface ProductFavoriteService {
/**
* 创建商品收藏
*
- * @param userId 用户 id
+ * @param userId 用户编号
* @param spuId SPU 编号
*/
Long createFavorite(Long userId, Long spuId);
@@ -24,7 +24,7 @@ public interface ProductFavoriteService {
/**
* 取消商品收藏
*
- * @param userId 用户 id
+ * @param userId 用户编号
* @param spuId SPU 编号
*/
void deleteFavorite(Long userId, Long spuId);
@@ -32,7 +32,7 @@ public interface ProductFavoriteService {
/**
* 分页查询用户收藏列表
*
- * @param userId 用户 id
+ * @param userId 用户编号
* @param reqVO 请求 vo
*/
PageResult getFavoritePage(Long userId, @Valid AppFavoritePageReqVO reqVO);
@@ -40,9 +40,17 @@ public interface ProductFavoriteService {
/**
* 获取收藏过商品
*
- * @param userId 用户id
+ * @param userId 用户编号
* @param spuId SPU 编号
*/
ProductFavoriteDO getFavorite(Long userId, Long spuId);
+ /**
+ * 获取用户收藏数量
+ *
+ * @param userId 用户编号
+ * @return 数量
+ */
+ Long getFavoriteCount(Long userId);
+
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteServiceImpl.java
index 775aa0e69..983cbf83c 100644
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteServiceImpl.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteServiceImpl.java
@@ -10,7 +10,6 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.Valid;
-import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.FAVORITE_EXISTS;
@@ -31,7 +30,7 @@ public class ProductFavoriteServiceImpl implements ProductFavoriteService {
@Override
public Long createFavorite(Long userId, Long spuId) {
ProductFavoriteDO favorite = productFavoriteMapper.selectByUserIdAndSpuId(userId, spuId);
- if (Objects.nonNull(favorite)) {
+ if (favorite != null) {
throw exception(FAVORITE_EXISTS);
}
@@ -43,7 +42,7 @@ public class ProductFavoriteServiceImpl implements ProductFavoriteService {
@Override
public void deleteFavorite(Long userId, Long spuId) {
ProductFavoriteDO favorite = productFavoriteMapper.selectByUserIdAndSpuId(userId, spuId);
- if (Objects.isNull(favorite)) {
+ if (favorite == null) {
throw exception(FAVORITE_NOT_EXISTS);
}
@@ -60,4 +59,9 @@ public class ProductFavoriteServiceImpl implements ProductFavoriteService {
return productFavoriteMapper.selectByUserIdAndSpuId(userId, spuId);
}
+ @Override
+ public Long getFavoriteCount(Long userId) {
+ return productFavoriteMapper.selectCountByUserId(userId);
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
index 7c6c5030e..cd0ba6b46 100755
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.product.service.sku;
import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO;
@@ -74,6 +75,9 @@ public class ProductSkuServiceImpl implements ProductSkuService {
@Override
public List getSkuList(Collection ids) {
+ if (CollUtil.isEmpty(ids)) {
+ return ListUtil.empty();
+ }
return productSkuMapper.selectBatchIds(ids);
}
diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
index 3ada0be78..43421315f 100755
--- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
@@ -110,8 +110,9 @@ public class ProductSpuServiceImpl implements ProductSpuService {
spu.setMarketPrice(getMinValue(skus, ProductSkuCreateOrUpdateReqVO::getMarketPrice));
// sku 单价最低的商品的成本价格
spu.setCostPrice(getMinValue(skus, ProductSkuCreateOrUpdateReqVO::getCostPrice));
- // sku 单价最低的商品的条形码
- spu.setBarCode(getMinValue(skus, ProductSkuCreateOrUpdateReqVO::getBarCode));
+ // sku 单价最低的商品的条形码 TODO 芋艿:条形码字段,是不是可以删除
+ spu.setBarCode("");
+// spu.setBarCode(getMinValue(skus, ProductSkuCreateOrUpdateReqVO::getBarCode));
// skus 库存总数
spu.setStock(getSumValue(skus, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum));
// 若是 spu 已有状态则不处理
diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java
index e57540292..b505ad161 100755
--- a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java
+++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java
@@ -92,8 +92,8 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
o.setMarketPrice(generaInt());
o.setStock(generaInt());
o.setWarnStock(10);
- o.setSubCommissionFirstPrice(generaInt());
- o.setSubCommissionSecondPrice(generaInt());
+ o.setFirstBrokerageRecord(generaInt());
+ o.setSecondBrokerageRecord(generaInt());
// 限制分数为两位数
o.setWeight(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP));
o.setVolume(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP));
@@ -143,8 +143,8 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest {
o.setMarketPrice(generaInt());
o.setStock(generaInt());
o.setWarnStock(10);
- o.setSubCommissionFirstPrice(generaInt());
- o.setSubCommissionSecondPrice(generaInt());
+ o.setFirstBrokerageRecord(generaInt());
+ o.setSecondBrokerageRecord(generaInt());
// 限制分数为两位数
o.setWeight(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP));
o.setVolume(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP));
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainActivityApi.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainActivityApi.java
new file mode 100644
index 000000000..eb912e417
--- /dev/null
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainActivityApi.java
@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.promotion.api.bargain;
+
+/**
+ * 砍价活动 Api 接口
+ *
+ * @author HUIHUI
+ */
+public interface BargainActivityApi {
+
+ /**
+ * 更新砍价活动库存
+ *
+ * @param id 砍价活动编号
+ * @param count 购买数量
+ */
+ void updateBargainActivityStock(Long id, Integer count);
+
+}
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/dto/BargainRecordCreateReqDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/dto/BargainRecordCreateReqDTO.java
index 22ef80a1e..d7835b23c 100644
--- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/dto/BargainRecordCreateReqDTO.java
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/dto/BargainRecordCreateReqDTO.java
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.promotion.api.bargain.dto;
import lombok.Data;
-import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
// TODO @芋艿:这块要在看看
@@ -40,17 +39,7 @@ public class BargainRecordCreateReqDTO {
*/
@NotNull(message = "订单编号不能为空")
private Long orderId;
- // TODO @puhui999:spuName、picUrl、 之类字段不用传递;
- /**
- * 商品名字
- */
- @NotEmpty(message = "商品名字不能为空")
- private String spuName;
- /**
- * 商品图片
- */
- @NotEmpty(message = "商品图片不能为空")
- private String picUrl;
+
/**
* 砍价商品单价
*/
@@ -61,17 +50,7 @@ public class BargainRecordCreateReqDTO {
*/
@NotNull(message = "商品原价不能为空")
private Integer price;
- // TODO @puhui999:nickname、avatar 不用传递,去查询;
- /**
- * 用户昵称
- */
- @NotEmpty(message = "用户昵称不能为空")
- private String nickname;
- /**
- * 用户头像
- */
- @NotEmpty(message = "用户头像不能为空")
- private String avatar;
+
/**
* 开团状态:进行中 砍价成功 砍价失败
*/
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java
new file mode 100644
index 000000000..11ea1c4f3
--- /dev/null
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java
@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.promotion.api.combination;
+
+import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationActivityUpdateStockReqDTO;
+
+// TODO @puhui999:是不是改成 CombinationActivityApi
+/**
+ * 拼团活动 Api 接口
+ *
+ * @author HUIHUI
+ */
+public interface CombinationApi {
+
+ /**
+ * 更新活动库存
+ *
+ * @param reqDTO 请求
+ */
+ // TODO @puhui999:应该是更新哇?还是校验哈;
+ void validateCombination(CombinationActivityUpdateStockReqDTO reqDTO);
+
+}
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApi.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApi.java
index 631a70906..859eb5c7e 100644
--- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApi.java
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApi.java
@@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.promotion.api.combination;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
-import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUpdateStatusReqDTO;
import javax.validation.Valid;
+import java.time.LocalDateTime;
import java.util.List;
// TODO @芋艿:后面也再撸撸这几个接口
@@ -51,13 +51,29 @@ public interface CombinationRecordApi {
*/
void validateCombinationLimitCount(Long activityId, Integer count, Integer sumCount);
- // TODO @puhui999:是不是搞成具体的方法,拼团成功,拼团失败,这种方法;
+ /**
+ * 更新拼团状态为成功
+ *
+ * @param userId 用户编号
+ * @param orderId 订单编号
+ */
+ void updateRecordStatusToSuccess(Long userId, Long orderId);
/**
- * 更新开团记录状态
+ * 更新拼团状态为失败
*
- * @param reqDTO 请求 DTO
+ * @param userId 用户编号
+ * @param orderId 订单编号
*/
- void updateCombinationRecordStatus(CombinationRecordUpdateStatusReqDTO reqDTO);
+ void updateRecordStatusToFailed(Long userId, Long orderId);
+
+ /**
+ * 更新拼团状态为 进行中
+ *
+ * @param userId 用户编号
+ * @param orderId 订单编号
+ * @param startTime 开始时间
+ */
+ void updateRecordStatusToInProgress(Long userId, Long orderId, LocalDateTime startTime);
}
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationActivityUpdateStockReqDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationActivityUpdateStockReqDTO.java
new file mode 100644
index 000000000..078f22fb0
--- /dev/null
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationActivityUpdateStockReqDTO.java
@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.promotion.api.combination.dto;
+
+import lombok.Data;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 拼团活动更新活动库存 Request DTO
+ *
+ * @author HUIHUI
+ */
+@Data
+public class CombinationActivityUpdateStockReqDTO {
+
+ // TODO @puhui999:是不是一个 activityId、count、skuId 参数就完事啦。
+
+ @NotNull(message = "活动编号不能为空")
+ private Long activityId;
+
+ @NotNull(message = "购买数量不能为空")
+ private Integer count;
+
+ @NotNull(message = "活动商品不能为空")
+ private Item item;
+
+ @Data
+ @Valid
+ public static class Item {
+
+ @NotNull(message = "SPU 编号不能为空")
+ private Long spuId;
+
+ @NotNull(message = "SKU 编号活动商品不能为空")
+ private Long skuId;
+
+ @NotNull(message = "购买数量不能为空")
+ private Integer count;
+
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordCreateReqDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordCreateReqDTO.java
index 4d4c0dafd..21e6d4965 100644
--- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordCreateReqDTO.java
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordCreateReqDTO.java
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.promotion.api.combination.dto;
import lombok.Data;
-import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
// TODO @芋艿:这块要在看看
@@ -14,65 +13,27 @@ import javax.validation.constraints.NotNull;
@Data
public class CombinationRecordCreateReqDTO {
- /**
- * 拼团活动编号
- */
+ // TODO @puhui999:注释还是要的哈
+
@NotNull(message = "拼团活动编号不能为空")
private Long activityId;
- /**
- * spu 编号
- */
+
@NotNull(message = "spu 编号不能为空")
private Long spuId;
- /**
- * sku 编号
- */
+
@NotNull(message = "sku 编号不能为空")
private Long skuId;
- /**
- * 用户编号
- */
- @NotNull(message = "用户编号不能为空")
- private Long userId;
- /**
- * 订单编号
- */
+
@NotNull(message = "订单编号不能为空")
private Long orderId;
- /**
- * 团长编号
- */
+
+ @NotNull(message = "用户编号不能为空")
+ private Long userId;
+
@NotNull(message = "团长编号不能为空")
private Long headId;
- /**
- * 商品名字
- */
- @NotEmpty(message = "商品名字不能为空")
- private String spuName;
- /**
- * 商品图片
- */
- @NotEmpty(message = "商品图片不能为空")
- private String picUrl;
- /**
- * 拼团商品单价
- */
+
@NotNull(message = "拼团商品单价不能为空")
private Integer combinationPrice;
- /**
- * 用户昵称
- */
- @NotEmpty(message = "用户昵称不能为空")
- private String nickname;
- /**
- * 用户头像
- */
- @NotEmpty(message = "用户头像不能为空")
- private String avatar;
- /**
- * 开团状态:正在开团 拼团成功 拼团失败
- */
- @NotNull(message = "开团状态不能为空")
- private Integer status;
}
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordUpdateStatusReqDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordUpdateStatusReqDTO.java
deleted file mode 100644
index 3ba49ff36..000000000
--- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordUpdateStatusReqDTO.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package cn.iocoder.yudao.module.promotion.api.combination.dto;
-
-import lombok.Data;
-
-import javax.validation.constraints.NotNull;
-import java.time.LocalDateTime;
-
-/**
- * 拼团记录的更新状态 Request DTO
- *
- * @author HUIHUI
- */
-@Data
-public class CombinationRecordUpdateStatusReqDTO {
-
- /**
- * 用户编号
- */
- @NotNull(message = "用户编号不能为空")
- private Long userId;
-
- /**
- * 订单编号
- */
- @NotNull(message = "订单编号不能为空")
- private Long orderId;
-
- /**
- * 开团状态:正在开团 拼团成功 拼团失败
- */
- @NotNull(message = "开团状态不能为空")
- private Integer status;
-
- /**
- * 团开始时间
- */
- private LocalDateTime startTime;
-
-}
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApi.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApi.java
index ce7a712da..ab970c0a3 100644
--- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApi.java
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApi.java
@@ -20,6 +20,13 @@ public interface CouponApi {
*/
void useCoupon(@Valid CouponUseReqDTO useReqDTO);
+ /**
+ * 退还已使用的优惠券
+ *
+ * @param id 优惠券编号
+ */
+ void returnUsedCoupon(Long id);
+
/**
* 校验优惠劵
*
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponRespDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponRespDTO.java
index 34031e604..a404bf27d 100644
--- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponRespDTO.java
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponRespDTO.java
@@ -71,9 +71,9 @@ public class CouponRespDTO {
*/
private Integer productScope;
/**
- * 商品 SPU 编号的数组
+ * 商品范围编号的数组
*/
- private List productSpuIds;
+ private List productScopeValues;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApi.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApi.java
new file mode 100644
index 000000000..9c5cf1d35
--- /dev/null
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApi.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.promotion.api.seckill;
+
+import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityUpdateStockReqDTO;
+
+/**
+ * 秒杀活动 API 接口
+ *
+ * @author HUIHUI
+ */
+public interface SeckillActivityApi {
+
+ /**
+ * 更新秒杀库存
+ *
+ * @param updateStockReqDTO 请求
+ */
+ void updateSeckillStock(SeckillActivityUpdateStockReqDTO updateStockReqDTO);
+
+}
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/dto/SeckillActivityUpdateStockReqDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/dto/SeckillActivityUpdateStockReqDTO.java
new file mode 100644
index 000000000..fa274744d
--- /dev/null
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/dto/SeckillActivityUpdateStockReqDTO.java
@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.promotion.api.seckill.dto;
+
+import lombok.Data;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 更新秒杀库存 request DTO
+ *
+ * @author HUIHUI
+ */
+@Data
+public class SeckillActivityUpdateStockReqDTO {
+
+ // TODO @puhui999:可以不用 dto,直接 activityId、skuId、count 即可
+
+ @NotNull(message = "活动编号不能为空")
+ private Long activityId;
+
+ @NotNull(message = "购买数量不能为空")
+ private Integer count;
+
+ @NotNull(message = "活动商品不能为空")
+ private Item item;
+
+ @Data
+ @Valid
+ public static class Item {
+
+ @NotNull(message = "SPU 编号不能为空")
+ private Long spuId;
+
+ @NotNull(message = "SKU 编号活动商品不能为空")
+ private Long skuId;
+
+ @NotNull(message = "购买数量不能为空")
+ private Integer count;
+
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
index e17f0de44..4fc58004a 100644
--- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
@@ -27,12 +27,17 @@ public interface ErrorCodeConstants {
// ========== 优惠劵模板 1013004000 ==========
ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1013004000, "优惠劵模板不存在");
ErrorCode COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL = new ErrorCode(1013004001, "发放数量不能小于已领取数量({})");
+ ErrorCode COUPON_TEMPLATE_NOT_ENOUGH = new ErrorCode(1013004002, "当前剩余数量不够领取");
+ ErrorCode COUPON_TEMPLATE_USER_ALREADY_TAKE = new ErrorCode(1013004003, "用户已领取过此优惠券");
+ ErrorCode COUPON_TEMPLATE_EXPIRED = new ErrorCode(1013004004, "优惠券已过期");
+ ErrorCode COUPON_TEMPLATE_CANNOT_TAKE = new ErrorCode(1013004005, "领取方式不正确");
- // ========== 优惠劵模板 1013005000 ==========
+ // ========== 优惠劵 1013005000 ==========
ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1013005000, "优惠券不存在");
ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1013005001, "回收优惠劵失败,优惠劵已被使用");
ErrorCode COUPON_STATUS_NOT_UNUSED = new ErrorCode(1013005002, "优惠劵不处于待使用状态");
ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1013005003, "优惠券不在使用时间范围内");
+ ErrorCode COUPON_STATUS_NOT_USED = new ErrorCode(1013005004, "优惠劵不是已使用状态");
// ========== 满减送活动 1013006000 ==========
ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1013006000, "满减送活动不存在");
@@ -46,10 +51,11 @@ public interface ErrorCodeConstants {
// ========== 秒杀活动 1013008000 ==========
ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1013008000, "秒杀活动不存在");
- ErrorCode SECKILL_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013008002, "存在商品参加了其它秒杀活动");
+ ErrorCode SECKILL_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013008002, "存在商品参加了其它秒杀活动,秒杀时段冲突");
ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013008003, "秒杀活动已关闭,不能修改");
ErrorCode SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013008004, "秒杀活动未关闭或未结束,不能删除");
ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013008005, "秒杀活动已关闭,不能重复关闭");
+ ErrorCode SECKILL_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013008006, "秒杀失败,原因秒杀库存不足");
// ========== 秒杀时段 1013009000 ==========
ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1013009000, "秒杀时段不存在");
@@ -59,24 +65,26 @@ public interface ErrorCodeConstants {
// ========== 拼团活动 1013010000 ==========
ErrorCode COMBINATION_ACTIVITY_NOT_EXISTS = new ErrorCode(1013010000, "拼团活动不存在");
ErrorCode COMBINATION_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013010001, "存在商品参加了其它拼团活动");
- ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010002, "拼团活动已关闭不能修改");
+ ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE_NOT_UPDATE = new ErrorCode(1013010002, "拼团活动已关闭不能修改");
ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013010003, "拼团活动未关闭或未结束,不能删除");
- ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013010004, "拼团不存在");
+ ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010004, "拼团失败,原因:拼团活动已关闭");
// ========== 拼团记录 1013011000 ==========
- ErrorCode COMBINATION_RECORD_EXISTS = new ErrorCode(1013011000, "拼团失败,已参与过该拼团");
- ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013011001, "拼团失败,父拼团不存在");
- ErrorCode COMBINATION_RECORD_USER_FULL = new ErrorCode(1013011002, "拼团失败,拼团人数已满");
- ErrorCode COMBINATION_RECORD_FAILED_HAVE_JOINED = new ErrorCode(1013011003, "拼团失败,已参与其它拼团");
- ErrorCode COMBINATION_RECORD_FAILED_TIME_END = new ErrorCode(1013011004, "拼团失败,活动已经结束");
- ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1013011005, "拼团失败,单次限购超出");
- ErrorCode COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED = new ErrorCode(1013011006, "拼团失败,单次限购超出");
+ ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013011000, "拼团不存在");
+ ErrorCode COMBINATION_RECORD_EXISTS = new ErrorCode(1013011001, "拼团失败,已参与过该拼团");
+ ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013011002, "拼团失败,父拼团不存在");
+ ErrorCode COMBINATION_RECORD_USER_FULL = new ErrorCode(1013011003, "拼团失败,拼团人数已满");
+ ErrorCode COMBINATION_RECORD_FAILED_HAVE_JOINED = new ErrorCode(1013011004, "拼团失败,已参与其它拼团");
+ ErrorCode COMBINATION_RECORD_FAILED_TIME_END = new ErrorCode(1013011005, "拼团失败,活动已经结束");
+ ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1013011006, "拼团失败,原因:单次限购超出");
+ ErrorCode COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED = new ErrorCode(1013011007, "拼团失败,原因:超出总购买次数");
// ========== 砍价活动 1013012000 ==========
ErrorCode BARGAIN_ACTIVITY_NOT_EXISTS = new ErrorCode(1013012000, "砍价活动不存在");
ErrorCode BARGAIN_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013012001, "存在商品参加了其它砍价活动");
ErrorCode BARGAIN_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013012002, "砍价活动已关闭不能修改");
ErrorCode BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013012003, "砍价活动未关闭或未结束,不能删除");
+ ErrorCode BARGAIN_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013012004, "砍价失败,原因:该砍价活动库存不足");
// ========== 砍价记录 1013013000 ==========
ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1013013000, "砍价记录不存在");
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionProductScopeEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionProductScopeEnum.java
index 0a7a4994d..6ae9f9f46 100644
--- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionProductScopeEnum.java
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionProductScopeEnum.java
@@ -15,8 +15,9 @@ import java.util.Arrays;
@AllArgsConstructor
public enum PromotionProductScopeEnum implements IntArrayValuable {
- ALL(1, "全部商品参与"),
- SPU(2, "指定商品参与"),
+ ALL(1, "通用卷"), // 全部商品
+ SPU(2, "商品卷"), // 指定商品
+ CATEGORY(3, "品类卷"), // 指定商品
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionProductScopeEnum::getScope).toArray();
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTakeTypeEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTakeTypeEnum.java
index ce7974142..1513e62ea 100644
--- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTakeTypeEnum.java
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTakeTypeEnum.java
@@ -15,8 +15,9 @@ import java.util.Arrays;
@Getter
public enum CouponTakeTypeEnum implements IntArrayValuable {
- BY_USER(1, "直接领取"), // 用户可在首页、每日领劵直接领取
- BY_ADMIN(2, "指定发放"), // 后台指定会员赠送优惠劵
+ USER(1, "直接领取"), // 用户可在首页、每日领劵直接领取
+ ADMIN(2, "指定发放"), // 后台指定会员赠送优惠劵
+ REGISTER(3, "新人券"), // 注册时自动领取
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponTakeTypeEnum::getValue).toArray();
diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/decorate/DecoratePageEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/decorate/DecoratePageEnum.java
index a618fa7e3..3b662db7a 100644
--- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/decorate/DecoratePageEnum.java
+++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/decorate/DecoratePageEnum.java
@@ -15,7 +15,9 @@ import java.util.Arrays;
@Getter
public enum DecoratePageEnum implements IntArrayValuable {
- INDEX(1, "首页");
+ INDEX(1, "首页"),
+ MY(2, "个人中心"),
+ ;
private static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DecoratePageEnum::getPage).toArray();
diff --git a/yudao-module-mall/yudao-module-promotion-biz/pom.xml b/yudao-module-mall/yudao-module-promotion-biz/pom.xml
index 266cb1511..bad2c8a35 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/pom.xml
+++ b/yudao-module-mall/yudao-module-promotion-biz/pom.xml
@@ -29,6 +29,11 @@
yudao-module-product-api
${revision}
+
+ cn.iocoder.boot
+ yudao-module-trade-api
+ ${revision}
+
cn.iocoder.boot
yudao-module-member-api
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainActivityApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainActivityApiImpl.java
new file mode 100644
index 000000000..c439fcbc2
--- /dev/null
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainActivityApiImpl.java
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.promotion.api.bargain;
+
+import cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+/**
+ * 砍价活动 Api 接口实现类
+ *
+ * @author HUIHUI
+ */
+@Service
+public class BargainActivityApiImpl implements BargainActivityApi {
+
+ @Resource
+ private BargainActivityService bargainActivityService;
+
+ @Override
+ public void updateBargainActivityStock(Long id, Integer count) {
+ bargainActivityService.updateBargainActivityStock(id, count);
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java
new file mode 100644
index 000000000..30d4a5cb0
--- /dev/null
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.promotion.api.combination;
+
+import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationActivityUpdateStockReqDTO;
+import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+
+/**
+ * 拼团活动 Api 接口实现类
+ *
+ * @author HUIHUI
+ */
+@Service
+@Validated
+public class CombinationApiImpl implements CombinationApi {
+
+ @Resource
+ private CombinationActivityService activityService;
+
+ @Override
+ public void validateCombination(CombinationActivityUpdateStockReqDTO reqDTO) {
+ activityService.validateCombination(reqDTO);
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
index e9dd9d201..7f7ec6170 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
@@ -2,13 +2,13 @@ package cn.iocoder.yudao.module.promotion.api.combination;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
-import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUpdateStatusReqDTO;
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
+import java.time.LocalDateTime;
import java.util.List;
/**
@@ -43,12 +43,19 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
}
@Override
- public void updateCombinationRecordStatus(CombinationRecordUpdateStatusReqDTO reqDTO) {
- if (null == reqDTO.getStartTime()) {
- recordService.updateCombinationRecordStatusByUserIdAndOrderId(reqDTO);
- } else {
- recordService.updateCombinationRecordStatusAndStartTimeByUserIdAndOrderId(reqDTO);
- }
+ public void updateRecordStatusToSuccess(Long userId, Long orderId) {
+ recordService.updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordStatusEnum.SUCCESS.getStatus(), userId, orderId);
+ }
+
+ @Override
+ public void updateRecordStatusToFailed(Long userId, Long orderId) {
+ recordService.updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordStatusEnum.FAILED.getStatus(), userId, orderId);
+ }
+
+ @Override
+ public void updateRecordStatusToInProgress(Long userId, Long orderId, LocalDateTime startTime) {
+ recordService.updateRecordStatusAndStartTimeByUserIdAndOrderId(CombinationRecordStatusEnum.IN_PROGRESS.getStatus(),
+ userId, orderId, startTime);
}
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java
index a06ab57cd..94d00e35c 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java
@@ -28,6 +28,11 @@ public class CouponApiImpl implements CouponApi {
useReqDTO.getOrderId());
}
+ @Override
+ public void returnUsedCoupon(Long id) {
+ couponService.returnUsedCoupon(id);
+ }
+
@Override
public CouponRespDTO validateCoupon(CouponValidReqDTO validReqDTO) {
CouponDO coupon = couponService.validCoupon(validReqDTO.getId(), validReqDTO.getUserId());
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApiImpl.java
new file mode 100644
index 000000000..f8840415a
--- /dev/null
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApiImpl.java
@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.module.promotion.api.seckill;
+
+import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityUpdateStockReqDTO;
+import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+/**
+ * 秒杀活动接口 Api 接口实现类
+ *
+ * @author HUIHUI
+ */
+@Service
+public class SeckillActivityApiImpl implements SeckillActivityApi {
+
+ @Resource
+ private SeckillActivityService activityService;
+
+ @Override
+ public void updateSeckillStock(SeckillActivityUpdateStockReqDTO updateStockReqDTO) {
+ activityService.updateSeckillStock(updateStockReqDTO);
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/BargainActivityBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/BargainActivityBaseVO.java
index 7387ae4cf..a4865bc5c 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/BargainActivityBaseVO.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/BargainActivityBaseVO.java
@@ -18,7 +18,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
@Data
public class BargainActivityBaseVO {
- @Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "越拼越省钱")
+ @Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "砍得越多省得越多,是兄弟就来砍我")
@NotNull(message = "砍价名称不能为空")
private String name;
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.java
index e7780ca2a..c5460c348 100755
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.java
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponSendReqVO;
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
@@ -21,7 +22,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Map;
-import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@@ -64,12 +64,20 @@ public class CouponController {
if (CollUtil.isEmpty(pageResulVO.getList())) {
return success(pageResulVO);
}
+
// 读取用户信息,进行拼接
- Set userIds = convertSet(pageResult.getList(), CouponDO::getUserId);
- Map userMap = memberUserApi.getUserMap(userIds);
+ Map userMap = memberUserApi.getUserMap(convertSet(pageResult.getList(), CouponDO::getUserId));
pageResulVO.getList().forEach(itemRespVO -> MapUtils.findAndThen(userMap, itemRespVO.getUserId(),
userRespDTO -> itemRespVO.setNickname(userRespDTO.getNickname())));
return success(pageResulVO);
}
+ @PostMapping("/send")
+ @Operation(summary = "发送优惠劵")
+ @PreAuthorize("@ss.hasPermission('promotion:coupon:send')")
+ public CommonResult sendCoupon(@Valid @RequestBody CouponSendReqVO reqVO) {
+ couponService.takeCouponByAdmin(reqVO.getTemplateId(), reqVO.getUserIds());
+ return success(true);
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponTemplateController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponTemplateController.java
index 1b1ae505c..69e39d13c 100755
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponTemplateController.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponTemplateController.java
@@ -75,5 +75,4 @@ public class CouponTemplateController {
PageResult pageResult = couponTemplateService.getCouponTemplatePage(pageVO);
return success(CouponTemplateConvert.INSTANCE.convertPage(pageResult));
}
-
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponBaseVO.java
index 742c10cc7..0d7459867 100755
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponBaseVO.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponBaseVO.java
@@ -26,7 +26,7 @@ public class CouponBaseVO {
// ========== 基本信息 BEGIN ==========
@Schema(description = "优惠劵模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "优惠劵模板编号不能为空")
- private Integer templateId;
+ private Long templateId;
@Schema(description = "优惠劵名", requiredMode = Schema.RequiredMode.REQUIRED, example = "春节送送送")
@NotNull(message = "优惠劵名不能为空")
@@ -67,8 +67,8 @@ public class CouponBaseVO {
@InEnum(PromotionProductScopeEnum.class)
private Integer productScope;
- @Schema(description = "商品 SPU 编号的数组", example = "1,3")
- private List productSpuIds;
+ @Schema(description = "商品范围编号的数组", example = "1,3")
+ private List productScopeValues;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponSendReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponSendReqVO.java
new file mode 100644
index 000000000..bac879f9c
--- /dev/null
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponSendReqVO.java
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.ToString;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.Set;
+
+@Schema(description = "管理后台 - 优惠劵发放 Request VO")
+@Data
+@ToString(callSuper = true)
+public class CouponSendReqVO {
+
+ @Schema(description = "优惠劵模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @NotNull(message = "优惠劵模板编号不能为空")
+ private Long templateId;
+
+ @Schema(description = "用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
+ @NotEmpty(message = "用户编号列表不能为空")
+ private Set userIds;
+
+}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java
index 7c1855d38..2529f79ac 100755
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java
@@ -54,8 +54,8 @@ public class CouponTemplateBaseVO {
@InEnum(PromotionProductScopeEnum.class)
private Integer productScope;
- @Schema(description = "商品 SPU 编号的数组", example = "1,3")
- private List productSpuIds;
+ @Schema(description = "商品范围编号的数组", example = "[1, 3]")
+ private List productScopeValues;
@Schema(description = "生效日期类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "生效日期类型不能为空")
@@ -95,11 +95,11 @@ public class CouponTemplateBaseVO {
@Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用
private Integer discountLimitPrice;
- @AssertTrue(message = "商品 SPU 编号的数组不能为空")
+ @AssertTrue(message = "商品范围编号的数组不能为空")
@JsonIgnore
- public boolean isProductSpuIdsValid() {
+ public boolean isProductScopeValuesValid() {
return Objects.equals(productScope, PromotionProductScopeEnum.ALL.getScope()) // 全部范围时,可以为空
- || CollUtil.isNotEmpty(productSpuIds);
+ || CollUtil.isNotEmpty(productScopeValues);
}
@AssertTrue(message = "生效开始时间不能为空")
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplatePageReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplatePageReqVO.java
index e78d0140f..abc7134e1 100755
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplatePageReqVO.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplatePageReqVO.java
@@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -8,6 +10,7 @@ import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
+import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@@ -30,4 +33,8 @@ public class CouponTemplatePageReqVO extends PageParam {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
+ @Schema(description = "可以领取的类型", example = "[1,2, 3]")
+ @InEnum(value = CouponTakeTypeEnum.class, message = "可以领取的类型,必须是 {value}")
+ private List canTakeTypes;
+
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java
index a8a793c77..44ec72e8a 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java
@@ -81,12 +81,13 @@ public class SeckillConfigController {
@GetMapping("/list-all-simple")
@Operation(summary = "获得所有开启状态的秒杀时段精简列表", description = "主要用于前端的下拉选项")
public CommonResult> getListAllSimple() {
- List list = seckillConfigService.getSeckillConfigListByStatus(CommonStatusEnum.ENABLE.getStatus());
+ List list = seckillConfigService.getSeckillConfigListByStatus(
+ CommonStatusEnum.ENABLE.getStatus());
return success(SeckillConfigConvert.INSTANCE.convertList1(list));
}
@GetMapping("/page")
- @Operation(summary = "获得秒杀活动分页")
+ @Operation(summary = "获得秒杀时间段分页")
@PreAuthorize("@ss.hasPermission('promotion:seckill-config:query')")
public CommonResult> getSeckillActivityPage(@Valid SeckillConfigPageReqVO pageVO) {
PageResult pageResult = seckillConfigService.getSeckillConfigPage(pageVO);
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainActivityController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainActivityController.java
index 537350aed..2232ead7b 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainActivityController.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainActivityController.java
@@ -35,7 +35,7 @@ public class AppBargainActivityController {
activity1.setId(1L);
activity1.setName("618 大砍价");
activity1.setSpuId(2048L);
- activity1.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+ activity1.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
activity1.setMarketPrice(50);
activity1.setBargainPrice(100);
activity1.setStartTime(LocalDateTimeUtils.addTime(Duration.ofDays(-2)));
@@ -47,7 +47,7 @@ public class AppBargainActivityController {
activity2.setId(2L);
activity2.setName("双十一砍价");
activity2.setSpuId(4096L);
- activity2.setPicUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKXMYJOomfp7cebz3cIeb8sHk3GGSIJtWEgREe3j7J1WoAbTvIOicpcNdFkWAziatBSMod8b5RyS4CQ/132");
+ activity2.setPicUrl("https://static.iocoder.cn/mall/132.jpeg");
activity2.setMarketPrice(100);
activity2.setBargainPrice(200);
activity2.setStartTime(LocalDateTimeUtils.addTime(Duration.ofDays(-2)));
@@ -69,7 +69,7 @@ public class AppBargainActivityController {
activity1.setId(1L);
activity1.setName("618 大砍价");
activity1.setSpuId(2048L);
- activity1.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+ activity1.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
activity1.setMarketPrice(50);
activity1.setBargainPrice(100);
activityList.add(activity1);
@@ -78,7 +78,7 @@ public class AppBargainActivityController {
activity2.setId(2L);
activity2.setName("双十一砍价");
activity2.setSpuId(4096L);
- activity2.setPicUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKXMYJOomfp7cebz3cIeb8sHk3GGSIJtWEgREe3j7J1WoAbTvIOicpcNdFkWAziatBSMod8b5RyS4CQ/132");
+ activity2.setPicUrl("https://static.iocoder.cn/mall/132.jpeg");
activity2.setMarketPrice(100);
activity2.setBargainPrice(200);
activityList.add(activity2);
@@ -94,7 +94,7 @@ public class AppBargainActivityController {
activity.setId(2L);
activity.setName("618 大砍价");
activity.setSpuId(2048L);
- activity.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+ activity.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
activity.setMarketPrice(50);
activity.setBargainPrice(100);
activity.setStock(10);
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainRecordController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainRecordController.java
index 0df8db49a..bb2bf584f 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainRecordController.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainRecordController.java
@@ -81,7 +81,7 @@ public class AppBargainRecordController {
record1.setPrice(200);
record1.setPayPrice(180);
record1.setStatus(1);
- record1.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+ record1.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
record1.setExpireTime(LocalDateTimeUtils.addTime(Duration.ofDays(2)));
page.getList().add(record1);
@@ -96,7 +96,7 @@ public class AppBargainRecordController {
record2.setPrice(200);
record2.setPayPrice(280);
record2.setStatus(2);
- record2.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+ record2.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
record2.setExpireTime(LocalDateTimeUtils.addTime(Duration.ofDays(2)));
page.getList().add(record2);
@@ -111,7 +111,7 @@ public class AppBargainRecordController {
record3.setPrice(200);
record3.setPayPrice(380);
record3.setStatus(2);
- record3.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+ record3.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
record3.setExpireTime(LocalDateTimeUtils.addTime(Duration.ofDays(2)));
record3.setOrderId(100L);
page.getList().add(record3);
@@ -127,7 +127,7 @@ public class AppBargainRecordController {
record4.setPrice(200);
record4.setPayPrice(380);
record4.setStatus(3);
- record4.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+ record4.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
record4.setExpireTime(LocalDateTimeUtils.addTime(Duration.ofDays(2)));
record4.setOrderId(100L);
page.getList().add(record4);
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationActivityController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationActivityController.java
index 216ec5285..75693f90b 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationActivityController.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationActivityController.java
@@ -38,7 +38,7 @@ public class AppCombinationActivityController {
activity1.setName("618 大拼团");
activity1.setUserSize(3);
activity1.setSpuId(2048L);
- activity1.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+ activity1.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
activity1.setMarketPrice(50);
activity1.setCombinationPrice(100);
activityList.add(activity1);
@@ -48,7 +48,7 @@ public class AppCombinationActivityController {
activity2.setName("双十一拼团");
activity2.setUserSize(5);
activity2.setSpuId(4096L);
- activity2.setPicUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKXMYJOomfp7cebz3cIeb8sHk3GGSIJtWEgREe3j7J1WoAbTvIOicpcNdFkWAziatBSMod8b5RyS4CQ/132");
+ activity2.setPicUrl("https://static.iocoder.cn/mall/132.jpeg");
activity2.setMarketPrice(100);
activity2.setCombinationPrice(200);
activityList.add(activity2);
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationRecordController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationRecordController.java
index 74cd39c5a..ef0e7ff99 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationRecordController.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationRecordController.java
@@ -1,7 +1,7 @@
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.framework.common.util.date.LocalDateTimeUtils;
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;
@@ -16,8 +16,8 @@ import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.Max;
import java.time.Duration;
+import java.time.LocalDateTime;
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -41,7 +41,7 @@ public class AppCombinationRecordController {
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");
+ summary.getAvatars().add("https://static.iocoder.cn/mall/132.jpeg");
return success(summary);
}
@@ -58,10 +58,10 @@ public class AppCombinationRecordController {
record.setId((long) i);
record.setNickname("用户" + i);
record.setAvatar("头像" + i);
- record.setExpireTime(new Date());
+ record.setExpireTime(LocalDateTime.now());
record.setUserSize(10);
record.setUserCount(i);
- record.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+ record.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
record.setActivityId(1L);
record.setSpuName("活动:" + i);
list.add(record);
@@ -79,12 +79,12 @@ public class AppCombinationRecordController {
headRecord.setId(1L);
headRecord.setNickname("用户" + 1);
headRecord.setAvatar("头像" + 1);
- headRecord.setExpireTime(DateUtils.addTime(Duration.ofDays(1)));
+ headRecord.setExpireTime(LocalDateTimeUtils.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.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
headRecord.setCombinationPrice(100);
detail.setHeadRecord(headRecord);
// 团员
@@ -94,7 +94,7 @@ public class AppCombinationRecordController {
record.setId((long) i);
record.setNickname("用户" + i);
record.setAvatar("头像" + i);
- record.setExpireTime(new Date());
+ record.setExpireTime(LocalDateTime.now());
record.setUserSize(10);
record.setUserCount(i);
record.setStatus(1);
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordRespVO.java
index 1a3469769..09d6ff3be 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordRespVO.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordRespVO.java
@@ -3,7 +3,7 @@ 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;
+import java.time.LocalDateTime;
@Schema(description = "用户 App - 拼团记录 Response VO")
@Data
@@ -22,7 +22,7 @@ public class AppCombinationRecordRespVO {
private String avatar;
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
- private Date expireTime;
+ private LocalDateTime expireTime;
@Schema(description = "可参团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer userSize;
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java
index 5813b8454..8a9b82bbb 100755
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java
@@ -2,22 +2,26 @@ package cn.iocoder.yudao.module.promotion.controller.app.coupon;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;
+import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "用户 App - 优惠劵")
@RestController
@@ -25,6 +29,9 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Validated
public class AppCouponController {
+ @Resource
+ private CouponService couponService;
+
// TODO 芋艿:待实现
@PostMapping("/take")
@Operation(summary = "领取优惠劵")
@@ -93,4 +100,11 @@ public class AppCouponController {
return success(new PageResult<>(list, 20L));
}
+ @GetMapping(value = "/get-unused-count")
+ @Operation(summary = "获得未使用的优惠劵数量")
+ @PreAuthenticated
+ public CommonResult getUnusedCouponCount() {
+ return success(couponService.getUnusedCouponCount(getLoginUserId()));
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/template/AppCouponTemplateRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/template/AppCouponTemplateRespVO.java
index 83410fd89..7dd0042ed 100755
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/template/AppCouponTemplateRespVO.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/template/AppCouponTemplateRespVO.java
@@ -28,8 +28,8 @@ public class AppCouponTemplateRespVO {
// @InEnum(PromotionProductScopeEnum.class)
// private Integer productScope;
//
-// @Schema(description = "商品 SPU 编号的数组", example = "1,3")
-// private List productSpuIds;
+// @Schema(description = "商品范围编号的数组", example = "1,3")
+// private List productScopeValues;
@Schema(description = "生效日期类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer validityType;
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillActivityController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillActivityController.java
index 7c4fd9ee2..098dfca67 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillActivityController.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillActivityController.java
@@ -39,7 +39,7 @@ public class AppSeckillActivityController {
activity1.setId(1L);
activity1.setName("618 大秒杀");
activity1.setSpuId(2048L);
- activity1.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+ activity1.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
activity1.setMarketPrice(50);
activity1.setSeckillPrice(100);
activityList.add(activity1);
@@ -48,7 +48,7 @@ public class AppSeckillActivityController {
activity2.setId(2L);
activity2.setName("双十一大秒杀");
activity2.setSpuId(4096L);
- activity2.setPicUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKXMYJOomfp7cebz3cIeb8sHk3GGSIJtWEgREe3j7J1WoAbTvIOicpcNdFkWAziatBSMod8b5RyS4CQ/132");
+ activity2.setPicUrl("https://static.iocoder.cn/mall/132.jpeg");
activity2.setMarketPrice(100);
activity2.setSeckillPrice(200);
activityList.add(activity2);
@@ -65,7 +65,7 @@ public class AppSeckillActivityController {
activity1.setId(1L);
activity1.setName("618 大秒杀");
activity1.setSpuId(2048L);
- activity1.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+ activity1.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
activity1.setMarketPrice(50);
activity1.setSeckillPrice(100);
activity1.setUnitName("个");
@@ -77,7 +77,7 @@ public class AppSeckillActivityController {
activity2.setId(2L);
activity2.setName("双十一大秒杀");
activity2.setSpuId(4096L);
- activity2.setPicUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKXMYJOomfp7cebz3cIeb8sHk3GGSIJtWEgREe3j7J1WoAbTvIOicpcNdFkWAziatBSMod8b5RyS4CQ/132");
+ activity2.setPicUrl("https://static.iocoder.cn/mall/132.jpeg");
activity2.setMarketPrice(100);
activity2.setSeckillPrice(200);
activity2.setUnitName("套");
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillConfigController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillConfigController.java
index 23a5aa4bc..d59a365b9 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillConfigController.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillConfigController.java
@@ -25,8 +25,8 @@ public class AppSeckillConfigController {
public CommonResult> getSeckillConfigList() {
return success(Arrays.asList(
new AppSeckillConfigRespVO().setId(1L).setStartTime("00:00").setEndTime("09:59")
- .setSliderPicUrls(Arrays.asList("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg",
- "https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKXMYJOomfp7cebz3cIeb8sHk3GGSIJtWEgREe3j7J1WoAbTvIOicpcNdFkWAziatBSMod8b5RyS4CQ/132")),
+ .setSliderPicUrls(Arrays.asList("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg",
+ "https://static.iocoder.cn/mall/132.jpeg")),
new AppSeckillConfigRespVO().setId(2L).setStartTime("10:00").setEndTime("12:59"),
new AppSeckillConfigRespVO().setId(2L).setStartTime("13:00").setEndTime("22:59"),
new AppSeckillConfigRespVO().setId(2L).setStartTime("23:00").setEndTime("23:59")
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java
index 7bfdca706..364095a4d 100755
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java
@@ -4,9 +4,14 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
+import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
+import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
+import java.time.LocalDateTime;
+
/**
* 优惠劵 Convert
*
@@ -21,4 +26,27 @@ public interface CouponConvert {
CouponRespDTO convert(CouponDO bean);
+ default CouponDO convert(CouponTemplateDO template, Long userId) {
+ CouponDO couponDO = new CouponDO()
+ .setTemplateId(template.getId())
+ .setName(template.getName())
+ .setTakeType(template.getTakeType())
+ .setUsePrice(template.getUsePrice())
+ .setProductScope(template.getProductScope())
+ .setProductScopeValues(template.getProductScopeValues())
+ .setDiscountType(template.getDiscountType())
+ .setDiscountPercent(template.getDiscountPercent())
+ .setDiscountPrice(template.getDiscountPrice())
+ .setDiscountLimitPrice(template.getDiscountLimitPrice())
+ .setStatus(CouponStatusEnum.UNUSED.getStatus())
+ .setUserId(userId);
+ if (CouponTemplateValidityTypeEnum.DATE.getType().equals(template.getValidityType())) {
+ couponDO.setValidStartTime(template.getValidStartTime());
+ couponDO.setValidEndTime(template.getValidEndTime());
+ } else if (CouponTemplateValidityTypeEnum.TERM.getType().equals(template.getValidityType())) {
+ couponDO.setValidStartTime(LocalDateTime.now().plusDays(template.getFixedStartTerm()));
+ couponDO.setValidEndTime(LocalDateTime.now().plusDays(template.getFixedEndTerm()));
+ }
+ return couponDO;
+ }
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationRecordDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationRecordDO.java
index f89c8f7ee..ea851d67e 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationRecordDO.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationRecordDO.java
@@ -9,6 +9,7 @@ import lombok.*;
import java.time.LocalDateTime;
+// TODO 芋艿:把字段的顺序,和 do 顺序对齐下
/**
* 拼团记录 DO
*
@@ -27,34 +28,28 @@ import java.time.LocalDateTime;
@AllArgsConstructor
public class CombinationRecordDO extends BaseDO {
+ /**
+ * 编号,主键自增
+ */
@TableId
private Long id;
+
/**
* 拼团活动编号
+ *
+ * 关联 {@link CombinationActivityDO#getId()}
*/
private Long activityId;
+ /**
+ * 拼团商品单价
+ *
+ * 冗余 {@link CombinationProductDO#getCombinationPrice()}
+ */
+ private Integer combinationPrice;
/**
* SPU 编号
*/
private Long spuId;
- /**
- * SKU 编号
- */
- private Long skuId;
- /**
- * 用户编号
- */
- private Long userId;
- /**
- * 订单编号
- */
- private Long orderId;
- /**
- * 团长编号
- *
- * 关联 {@link CombinationRecordDO#getId()}
- */
- private Long headId;
/**
* 商品名字
*/
@@ -64,9 +59,14 @@ public class CombinationRecordDO extends BaseDO {
*/
private String picUrl;
/**
- * 拼团商品单价
+ * SKU 编号
*/
- private Integer combinationPrice;
+ private Long skuId;
+
+ /**
+ * 用户编号
+ */
+ private Long userId;
/**
* 用户昵称
*/
@@ -75,6 +75,13 @@ public class CombinationRecordDO extends BaseDO {
* 用户头像
*/
private String avatar;
+
+ /**
+ * 团长编号
+ *
+ * 关联 {@link CombinationRecordDO#getId()}
+ */
+ private Long headId;
/**
* 开团状态
*
@@ -82,23 +89,9 @@ public class CombinationRecordDO extends BaseDO {
*/
private Integer status;
/**
- * 是否虚拟成团
+ * 订单编号
*/
- private Boolean virtualGroup;
- /**
- * 过期时间,单位:小时
- *
- * 关联 {@link CombinationActivityDO#getLimitDuration()}
- */
- private Integer expireTime;
- /**
- * 开始时间 (订单付款后开始的时间)
- */
- private LocalDateTime startTime;
- /**
- * 结束时间(成团时间/失败时间)
- */
- private LocalDateTime endTime;
+ private Long orderId;
/**
* 开团需要人数
*
@@ -109,5 +102,24 @@ public class CombinationRecordDO extends BaseDO {
* 已加入拼团人数
*/
private Integer userCount;
+ /**
+ * 是否虚拟成团
+ */
+ private Boolean virtualGroup;
+
+ /**
+ * 过期时间
+ *
+ * 基于 {@link CombinationRecordDO#getStartTime()} + {@link CombinationActivityDO#getLimitDuration()} 计算
+ */
+ private LocalDateTime expireTime;
+ /**
+ * 开始时间 (订单付款后开始的时间)
+ */
+ private LocalDateTime startTime;
+ /**
+ * 结束时间(成团时间/失败时间)
+ */
+ private LocalDateTime endTime;
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java
index 7971392d4..b98615093 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java
@@ -36,7 +36,7 @@ public class CouponDO extends BaseDO {
*
* 关联 {@link CouponTemplateDO#getId()}
*/
- private Integer templateId;
+ private Long templateId;
/**
* 优惠劵名
*
@@ -89,12 +89,12 @@ public class CouponDO extends BaseDO {
*/
private Integer productScope;
/**
- * 商品 SPU 编号的数组
+ * 商品范围编号的数组
*
- * 冗余 {@link CouponTemplateDO#getProductSpuIds()}
+ * 冗余 {@link CouponTemplateDO#getProductScopeValues()}
*/
@TableField(typeHandler = LongListTypeHandler.class)
- private List productSpuIds;
+ private List productScopeValues;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java
index 93f9ace35..6cab9c58c 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java
@@ -85,10 +85,10 @@ public class CouponTemplateDO extends BaseDO {
*/
private Integer productScope;
/**
- * 商品 SPU 编号的数组
+ * 商品范围编号的数组
*/
@TableField(typeHandler = LongListTypeHandler.class)
- private List productSpuIds;
+ private List productScopeValues;
/**
* 生效日期类型
*
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java
index 836127ab9..94d46d1eb 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.BargainActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@@ -28,4 +29,18 @@ public interface BargainActivityMapper extends BaseMapperX {
return selectList(BargainActivityDO::getStatus, status);
}
+ /**
+ * 更新活动库存
+ *
+ * @param id 活动编号
+ * @param count 扣减的库存数量
+ * @return 影响的行数
+ */
+ default int updateActivityStock(Long id, int count) {
+ return update(null, new LambdaUpdateWrapper()
+ .eq(BargainActivityDO::getId, id)
+ .gt(BargainActivityDO::getStock, 0) // TODO @puhui999:不是 > 0,是要大于 count 哈
+ .setSql("stock = stock - " + count));
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java
index e5ae5f79d..ddf90691c 100755
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java
@@ -49,4 +49,17 @@ public interface CouponMapper extends BaseMapperX {
.eq(CouponDO::getId, id).eq(CouponDO::getStatus, status));
}
+ default Long selectCountByUserIdAndStatus(Long userId, Integer status) {
+ return selectCount(new LambdaQueryWrapperX()
+ .eq(CouponDO::getUserId, userId)
+ .eq(CouponDO::getStatus, status));
+ }
+
+ default List selectListByTemplateIdAndUserId(Long templateId, Collection userIds) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(CouponDO::getTemplateId, templateId)
+ .in(CouponDO::getUserId, userIds)
+ );
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java
index 7cea814af..c95513e18 100755
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java
@@ -1,13 +1,19 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.coupon;
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
+import java.time.LocalDateTime;
+import java.util.function.Consumer;
+
/**
* 优惠劵模板 Mapper
*
@@ -17,11 +23,23 @@ import org.apache.ibatis.annotations.Param;
public interface CouponTemplateMapper extends BaseMapperX {
default PageResult selectPage(CouponTemplatePageReqVO reqVO) {
+ // 构建可领取的查询条件, 好啰嗦 ( ╯-_-)╯┴—┴
+ Consumer> canTakeConsumer = null;
+ if (CollUtil.isNotEmpty(reqVO.getCanTakeTypes())) {
+ canTakeConsumer = w ->
+ w.eq(CouponTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) // 1. 状态为可用的
+ .in(CouponTemplateDO::getTakeType, reqVO.getCanTakeTypes()) // 2. 领取方式一致
+ .and(ww -> ww.isNull(CouponTemplateDO::getValidEndTime) // 3. 未过期
+ .or().gt(CouponTemplateDO::getValidEndTime, LocalDateTime.now()))
+ .apply(" take_count < total_count "); // 4. 剩余数量大于 0
+ }
+ // 执行分页查询
return selectPage(reqVO, new LambdaQueryWrapperX()
.likeIfPresent(CouponTemplateDO::getName, reqVO.getName())
.eqIfPresent(CouponTemplateDO::getStatus, reqVO.getStatus())
.eqIfPresent(CouponTemplateDO::getDiscountType, reqVO.getDiscountType())
.betweenIfPresent(CouponTemplateDO::getCreateTime, reqVO.getCreateTime())
+ .and(canTakeConsumer != null, canTakeConsumer)
.orderByDesc(CouponTemplateDO::getId));
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
index 70fe79d53..79eca4aa0 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@@ -32,4 +33,19 @@ public interface SeckillActivityMapper extends BaseMapperX {
.eqIfPresent(SeckillActivityDO::getStatus, status));
}
+ /**
+ * 更新活动库存
+ *
+ * @param id 活动编号
+ * @param count 扣减的库存数量
+ * @return 影响的行数
+ */
+ default int updateActivityStock(Long id, int count) {
+ return update(null, new LambdaUpdateWrapper()
+ .eq(SeckillActivityDO::getId, id)
+ .gt(SeckillActivityDO::getTotalStock, 0)
+ .setSql("stock = stock + " + count)
+ .setSql("totalStock = totalStock - " + count));
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java
index f11bd1aba..13e2c1e2f 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
@@ -23,4 +24,18 @@ public interface SeckillProductMapper extends BaseMapperX {
return selectList(SeckillProductDO::getActivityId, ids);
}
+ /**
+ * 更新活动库存
+ *
+ * @param id 活动编号
+ * @param count 扣减的库存数量
+ * @return 影响的行数
+ */
+ default int updateActivityStock(Long id, int count) {
+ return update(null, new LambdaUpdateWrapper()
+ .eq(SeckillProductDO::getId, id)
+ .gt(SeckillProductDO::getStock, 0) // TODO @puhui999:不是 > 0,是要大于 count 哈
+ .setSql("stock = stock - " + count));
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java
index 38bbe18da..6d07cfcdf 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java
@@ -16,7 +16,7 @@ public interface SeckillConfigMapper extends BaseMapperX {
return selectPage(reqVO, new LambdaQueryWrapperX()
.likeIfPresent(SeckillConfigDO::getName, reqVO.getName())
.eqIfPresent(SeckillConfigDO::getStatus, reqVO.getStatus())
- .orderByDesc(SeckillConfigDO::getId));
+ .orderByAsc(SeckillConfigDO::getStartTime));
}
default List selectListByStatus(Integer status) {
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityService.java
index 1c49155a8..71eedfc7f 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityService.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityService.java
@@ -30,6 +30,14 @@ public interface BargainActivityService {
*/
void updateBargainActivity(@Valid BargainActivityUpdateReqVO updateReqVO);
+ /**
+ * 更新砍价活动库存
+ *
+ * @param id 砍价活动编号
+ * @param count 购买数量
+ */
+ void updateBargainActivityStock(Long id, Integer count);
+
/**
* 删除砍价活动
*
@@ -53,5 +61,4 @@ public interface BargainActivityService {
*/
PageResult getBargainActivityPage(BargainActivityPageReqVO pageReqVO);
-
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java
index 4a889fbf7..9c79c4ad4 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java
@@ -39,6 +39,7 @@ public class BargainActivityServiceImpl implements BargainActivityService {
private ProductSkuApi productSkuApi;
@Override
+ @Transactional(rollbackFor = Exception.class)
public Long createBargainActivity(BargainActivityCreateReqVO createReqVO) {
// 校验商品 SPU 是否存在是否参加的别的活动
validateBargainConflict(createReqVO.getSpuId(), null);
@@ -53,6 +54,7 @@ public class BargainActivityServiceImpl implements BargainActivityService {
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public void updateBargainActivity(BargainActivityUpdateReqVO updateReqVO) {
// 校验存在
BargainActivityDO activityDO = validateBargainActivityExists(updateReqVO.getId());
@@ -70,6 +72,22 @@ public class BargainActivityServiceImpl implements BargainActivityService {
bargainActivityMapper.updateById(updateObj);
}
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateBargainActivityStock(Long id, Integer count) {
+ // 查询砍价活动
+ BargainActivityDO activity = getBargainActivity(id);
+ if (activity == null) {
+ throw exception(BARGAIN_ACTIVITY_NOT_EXISTS);
+ }
+
+ // 更新砍价库存
+ int updateCount = bargainActivityMapper.updateActivityStock(id, count);
+ if (updateCount == 0) {
+ throw exception(BARGAIN_ACTIVITY_UPDATE_STOCK_FAIL);
+ }
+ }
+
private void validateBargainConflict(Long spuId, Long activityId) {
// 查询所有开启的砍价活动
List activityList = bargainActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java
index 99b87df6c..c3ee01fcc 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.promotion.service.combination;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationActivityUpdateStockReqDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
@@ -72,4 +73,11 @@ public interface CombinationActivityService {
*/
List getCombinationProductsByActivityIds(Collection activityIds);
+ /**
+ * 更新拼图活动库存
+ *
+ * @param reqDTO 请求
+ */
+ void validateCombination(CombinationActivityUpdateStockReqDTO reqDTO);
+
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java
index c6a7c8c49..2becc6b01 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java
@@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
+import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationActivityUpdateStockReqDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
@@ -16,8 +17,12 @@ import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationActivityMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationProductMapper;
+import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
+import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -28,8 +33,8 @@ import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
@@ -49,10 +54,16 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
@Resource
private CombinationProductMapper combinationProductMapper;
+ @Resource
+ @Lazy // TODO @puhui999:我感觉 validateCombination 可以挪到 CombinationRecordServiceImpl 中,因为它更偏向能不能创建拼团记录;
+ private CombinationRecordService combinationRecordService;
+
@Resource
private ProductSpuApi productSpuApi;
@Resource
private ProductSkuApi productSkuApi;
+ @Resource
+ private TradeOrderApi tradeOrderApi;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -97,7 +108,7 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
* 校验拼团商品是否都存在
*
* @param spuId 商品 SPU 编号
- * @param products 秒杀商品
+ * @param products 拼团商品
*/
private void validateProductExists(Long spuId, List products) {
// 1. 校验商品 spu 是否存在
@@ -123,7 +134,7 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
CombinationActivityDO activityDO = validateCombinationActivityExists(updateReqVO.getId());
// 校验状态
if (ObjectUtil.equal(activityDO.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
- throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE);
+ throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE_NOT_UPDATE);
}
// 校验商品冲突
validateProductConflict(updateReqVO.getSpuId(), updateReqVO.getId());
@@ -205,4 +216,36 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
return combinationProductMapper.selectListByActivityIds(activityIds);
}
+ @Override
+ public void validateCombination(CombinationActivityUpdateStockReqDTO reqDTO) {
+ // 1.1 校验拼团活动是否存在
+ CombinationActivityDO activity = validateCombinationActivityExists(reqDTO.getActivityId());
+ // 1.2 校验活动是否开启
+ if (ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
+ throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE);
+ }
+ // 1.3 校验是否超出单次限购数量
+ if (activity.getSingleLimitCount() < reqDTO.getCount()) {
+ throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED);
+ }
+
+ // 2. 校验是否超出总限购数量
+ // TODO @puhui999:userId 应该接口传递哈;要保证 service 无状态
+ List recordList = combinationRecordService.getRecordListByUserIdAndActivityId(
+ getLoginUserId(), reqDTO.getActivityId());
+ // TODO @puhui999:最好 if true return;减少括号层数
+ if (CollUtil.isNotEmpty(recordList)) {
+ // 过滤出拼团成功的
+ // TODO @puhui999:count 要不存一个在 record 里?
+ List skuIds = convertList(recordList, CombinationRecordDO::getSkuId,
+ item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus()));
+ Integer countSum = tradeOrderApi.getOrderItemCountSumByOrderIdAndSkuId(convertList(recordList,
+ CombinationRecordDO::getOrderId,
+ item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus())), skuIds);
+ if (activity.getTotalLimitCount() < countSum) {
+ throw exception(COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED);
+ }
+ }
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordService.java
index 6c12cb551..fbc51c6b9 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordService.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordService.java
@@ -1,9 +1,9 @@
package cn.iocoder.yudao.module.promotion.service.combination;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
-import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUpdateStatusReqDTO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
+import java.time.LocalDateTime;
import java.util.List;
/**
@@ -16,9 +16,11 @@ public interface CombinationRecordService {
/**
* 更新拼团状态
*
- * @param reqDTO 请求 DTO
+ * @param status 状态
+ * @param userId 用户编号
+ * @param orderId 订单编号
*/
- void updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO);
+ void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId);
/**
* 创建拼团记录
@@ -30,9 +32,12 @@ public interface CombinationRecordService {
/**
* 更新拼团状态和开始时间
*
- * @param reqDTO 请求 DTO
+ * @param status 状态
+ * @param userId 用户编号
+ * @param orderId 订单编号
+ * @param startTime 开始时间
*/
- void updateCombinationRecordStatusAndStartTimeByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO);
+ void updateRecordStatusAndStartTimeByUserIdAndOrderId(Integer status, Long userId, Long orderId, LocalDateTime startTime);
/**
* 获得拼团状态
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
index 4e2f85e27..c82621805 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
@@ -2,13 +2,19 @@ package cn.iocoder.yudao.module.promotion.service.combination;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
+import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
+import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
+import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
+import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
+import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
+import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
-import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUpdateStatusReqDTO;
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -21,6 +27,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
// TODO 芋艿:等拼团记录做完,完整 review 下
+
/**
* 拼团记录 Service 实现类
*
@@ -31,34 +38,43 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
public class CombinationRecordServiceImpl implements CombinationRecordService {
@Resource
+ @Lazy
private CombinationActivityService combinationActivityService;
-
@Resource
private CombinationRecordMapper recordMapper;
+ @Resource
+ private MemberUserApi memberUserApi;
+ @Resource
+ @Lazy
+ private ProductSpuApi productSpuApi;
+ @Resource
+ @Lazy
+ private ProductSkuApi productSkuApi;
+
@Override
@Transactional(rollbackFor = Exception.class)
- public void updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO) {
+ public void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId) {
// 校验拼团是否存在
- CombinationRecordDO recordDO = validateCombinationRecord(reqDTO.getUserId(), reqDTO.getOrderId());
+ CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId);
// 更新状态
- recordDO.setStatus(reqDTO.getStatus());
+ recordDO.setStatus(status);
recordMapper.updateById(recordDO);
}
@Override
@Transactional(rollbackFor = Exception.class)
- public void updateCombinationRecordStatusAndStartTimeByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO) {
- CombinationRecordDO recordDO = validateCombinationRecord(reqDTO.getUserId(), reqDTO.getOrderId());
+ public void updateRecordStatusAndStartTimeByUserIdAndOrderId(Integer status, Long userId, Long orderId, LocalDateTime startTime) {
+ CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId);
// 更新状态
- recordDO.setStatus(reqDTO.getStatus());
+ recordDO.setStatus(status);
// 更新开始时间
- recordDO.setStartTime(reqDTO.getStartTime());
+ recordDO.setStartTime(startTime);
recordMapper.updateById(recordDO);
// 更新拼团参入人数
- List recordDOs = recordMapper.selectListByHeadIdAndStatus(recordDO.getHeadId(), reqDTO.getStatus());
+ List recordDOs = recordMapper.selectListByHeadIdAndStatus(recordDO.getHeadId(), status);
if (CollUtil.isNotEmpty(recordDOs)) {
recordDOs.forEach(item -> {
item.setUserCount(recordDOs.size());
@@ -102,22 +118,29 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
// 1.5 父拼团是否存在,是否已经满了
if (reqDTO.getHeadId() != null) {
// 查询进行中的父拼团
- CombinationRecordDO recordDO1 = recordMapper.selectOneByHeadId(reqDTO.getHeadId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
- if (recordDO1 == null) {
+ CombinationRecordDO record = recordMapper.selectOneByHeadId(reqDTO.getHeadId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
+ if (record == null) {
throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);
}
// 校验拼团是否满足要求
- if (ObjectUtil.equal(recordDO1.getUserCount(), recordDO1.getUserSize())) {
+ if (ObjectUtil.equal(record.getUserCount(), record.getUserSize())) {
throw exception(COMBINATION_RECORD_USER_FULL);
}
}
// 2. 创建拼团记录
+ // TODO @puhui999:可以把 user、spu、sku 一起放 convert 里哈;
CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO);
record.setVirtualGroup(false);
- // TODO @puhui999:过期时间,应该是 Date 哈;
- record.setExpireTime(activity.getLimitDuration());
+ record.setExpireTime(record.getStartTime().plusHours(activity.getLimitDuration()));
record.setUserSize(activity.getUserSize());
+ MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId());
+ record.setNickname(user.getNickname());
+ record.setAvatar(user.getAvatar());
+ ProductSpuRespDTO spu = productSpuApi.getSpu(record.getSpuId());
+ record.setSpuName(spu.getName());
+ ProductSkuRespDTO sku = productSkuApi.getSku(record.getSkuId());
+ record.setPicUrl(sku.getPicUrl());
recordMapper.insert(record);
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
index 96b5b5d63..cf22fe2b3 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
@@ -1,10 +1,13 @@
package cn.iocoder.yudao.module.promotion.service.coupon;
+import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
+import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
import java.util.List;
+import java.util.Set;
/**
* 优惠劵 Service 接口
@@ -51,6 +54,13 @@ public interface CouponService {
*/
void useCoupon(Long id, Long userId, Long orderId);
+ /**
+ * 退还已使用的优惠券
+ *
+ * @param id 优惠券编号
+ */
+ void returnUsedCoupon(Long id);
+
/**
* 回收优惠劵
*
@@ -67,4 +77,51 @@ public interface CouponService {
*/
List getCouponList(Long userId, Integer status);
+ /**
+ * 获得未使用的优惠劵数量
+ *
+ * @param userId 用户编号
+ * @return 未使用的优惠劵数量
+ */
+ Long getUnusedCouponCount(Long userId);
+
+ /**
+ * 领取优惠券
+ *
+ * @param templateId 优惠券模板编号
+ * @param userIds 用户编号列表
+ * @param takeType 领取方式
+ */
+ void takeCoupon(Long templateId, Set userIds, CouponTakeTypeEnum takeType);
+
+ /**
+ * 【管理员】给用户发送优惠券
+ *
+ * @param templateId 优惠券模板编号
+ * @param userIds 用户编号列表
+ */
+ default void takeCouponByAdmin(Long templateId, Set userIds) {
+ takeCoupon(templateId, userIds, CouponTakeTypeEnum.ADMIN);
+ }
+
+ /**
+ * 【会员】领取优惠券
+ *
+ * @param templateId 优惠券模板编号
+ * @param userId 用户编号
+ */
+ default void takeCouponByUser(Long templateId, Long userId) {
+ takeCoupon(templateId, CollUtil.newHashSet(userId), CouponTakeTypeEnum.USER);
+ }
+
+ /**
+ * 【系统】给用户发送新人券
+ *
+ * @param templateId 优惠券模板编号
+ * @param userId 用户编号列表
+ */
+ default void takeCouponByRegister(Long templateId, Long userId) {
+ takeCoupon(templateId, CollUtil.newHashSet(userId), CouponTakeTypeEnum.REGISTER);
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
index a573e0d9c..f4b56260c 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
@@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.promotion.service.coupon;
+import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -9,9 +11,13 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
+import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
+import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
+import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -19,9 +25,12 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
import static java.util.Arrays.asList;
@@ -84,6 +93,7 @@ public class CouponServiceImpl implements CouponService {
public void useCoupon(Long id, Long userId, Long orderId) {
// 校验优惠劵
validCoupon(id, userId);
+
// 更新状态
int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(),
new CouponDO().setStatus(CouponStatusEnum.USED.getStatus())
@@ -93,6 +103,31 @@ public class CouponServiceImpl implements CouponService {
}
}
+ @Override
+ public void returnUsedCoupon(Long id) {
+ // 校验存在
+ CouponDO coupon = couponMapper.selectById(id);
+ if (coupon == null) {
+ throw exception(COUPON_NOT_EXISTS);
+ }
+ // 校验状态
+ if (ObjectUtil.notEqual(coupon.getTemplateId(), CouponStatusEnum.USED.getStatus())) {
+ throw exception(COUPON_STATUS_NOT_USED);
+ }
+
+ // 退还
+ Integer status = LocalDateTimeUtils.beforeNow(coupon.getValidEndTime())
+ ? CouponStatusEnum.EXPIRE.getStatus() // 退还时可能已经过期了
+ : CouponStatusEnum.UNUSED.getStatus();
+ int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(),
+ new CouponDO().setStatus(status));
+ if (updateCount == 0) {
+ throw exception(COUPON_STATUS_NOT_USED);
+ }
+
+ // TODO 增加优惠券变动记录?
+ }
+
@Override
@Transactional
public void deleteCoupon(Long id) {
@@ -120,4 +155,77 @@ public class CouponServiceImpl implements CouponService {
}
}
+ @Override
+ public Long getUnusedCouponCount(Long userId) {
+ return couponMapper.selectCountByUserIdAndStatus(userId, CouponStatusEnum.UNUSED.getStatus());
+ }
+
+ @Override
+ public void takeCoupon(Long templateId, Set userIds, CouponTakeTypeEnum takeType) {
+ CouponTemplateDO template = couponTemplateService.getCouponTemplate(templateId);
+ // 1. 过滤掉达到领取限制的用户
+ removeTakeLimitUser(userIds, template);
+ // 2. 校验优惠劵是否可以领取
+ validateCouponTemplateCanTake(template, userIds, takeType);
+
+ // 3. 批量保存优惠劵
+ couponMapper.insertBatch(convertList(userIds, userId -> CouponConvert.INSTANCE.convert(template, userId)));
+
+ // 3. 增加优惠劵模板的领取数量
+ couponTemplateService.updateCouponTemplateTakeCount(templateId, userIds.size());
+ }
+
+ /**
+ * 校验优惠券是否可以领取
+ *
+ * @param couponTemplate 优惠券模板
+ * @param userIds 领取人列表
+ * @param takeType 领取方式
+ */
+ private void validateCouponTemplateCanTake(CouponTemplateDO couponTemplate, Set userIds, CouponTakeTypeEnum takeType) {
+ // 如果所有用户都领取过,则抛出异常
+ if (CollUtil.isEmpty(userIds)) {
+ throw exception(COUPON_TEMPLATE_USER_ALREADY_TAKE);
+ }
+
+ // 校验模板
+ if (couponTemplate == null) {
+ throw exception(COUPON_TEMPLATE_NOT_EXISTS);
+ }
+ // 校验剩余数量
+ if (couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
+ throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
+ }
+ // 校验"固定日期"的有效期类型是否过期
+ if (CouponTemplateValidityTypeEnum.DATE.getType().equals(couponTemplate.getValidityType())) {
+ if (LocalDateTimeUtils.beforeNow(couponTemplate.getValidEndTime())) {
+ throw exception(COUPON_TEMPLATE_EXPIRED);
+ }
+ }
+ // 校验领取方式
+ if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getValue())) {
+ throw exception(COUPON_TEMPLATE_CANNOT_TAKE);
+ }
+ }
+
+ /**
+ * 过滤掉达到领取上线的用户
+ *
+ * @param userIds 用户编号数组
+ * @param couponTemplate 优惠劵模版
+ */
+ private void removeTakeLimitUser(Set userIds, CouponTemplateDO couponTemplate) {
+ if (couponTemplate.getTakeLimitCount() <= 0) {
+ return;
+ }
+ // 查询已领过券的用户
+ List alreadyTakeCoupons = couponMapper.selectListByTemplateIdAndUserId(couponTemplate.getId(), userIds);
+ if (CollUtil.isEmpty(alreadyTakeCoupons)) {
+ return;
+ }
+ // 移除达到领取限制的用户
+ Map userTakeCountMap = CollStreamUtil.groupBy(alreadyTakeCoupons, CouponDO::getUserId, Collectors.summingInt(c -> 1));
+ userIds.removeIf(userId -> MapUtil.getInt(userTakeCountMap, userId, 0) >= couponTemplate.getTakeLimitCount());
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateService.java
index fdf018974..9f2d925a2 100755
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateService.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateService.java
@@ -33,7 +33,7 @@ public interface CouponTemplateService {
/**
* 更新优惠劵模板的状态
*
- * @param id 编号
+ * @param id 编号
* @param status 状态
*/
void updateCouponTemplateStatus(Long id, Integer status);
@@ -64,7 +64,7 @@ public interface CouponTemplateService {
/**
* 更新优惠劵模板的领取数量
*
- * @param id 优惠劵模板编号
+ * @param id 优惠劵模板编号
* @param incrCount 增加数量
*/
void updateCouponTemplateTakeCount(Long id, int incrCount);
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java
index 1a9cc8bfb..a5be46746 100755
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java
@@ -14,7 +14,8 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_TEMPLATE_NOT_EXISTS;
+import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL;
/**
* 优惠劵模板 Service 实现类
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
index 2e33a944d..89fa6fb84 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.promotion.service.seckill;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityUpdateStockReqDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO;
@@ -33,6 +34,13 @@ public interface SeckillActivityService {
*/
void updateSeckillActivity(@Valid SeckillActivityUpdateReqVO updateReqVO);
+ /**
+ * 更新秒杀库存
+ *
+ * @param updateStockReqDTO 更新信息
+ */
+ void updateSeckillStock(SeckillActivityUpdateStockReqDTO updateStockReqDTO);
+
/**
* 关闭秒杀活动
*
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
index a3577a50b..da098914c 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
+import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityUpdateStockReqDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO;
@@ -79,8 +80,8 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
* 1. 校验秒杀时段是否存在
* 2. 秒杀商品是否参加其它活动
*
- * @param configIds 秒杀时段数组
- * @param spuId 商品 SPU 编号
+ * @param configIds 秒杀时段数组
+ * @param spuId 商品 SPU 编号
* @param activityId 秒杀活动编号
*/
private void validateProductConflict(List configIds, Long spuId, Long activityId) {
@@ -92,15 +93,9 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
if (activityId != null) { // 排除自己
activityList.removeIf(item -> ObjectUtil.equal(item.getId(), activityId));
}
- // TODO @puhui999:一个 spu,参与两个活动应该没关系,关键是活动时间不充能重叠;
- // 2.2 过滤出所有 spuId 有交集的活动,判断是否存在重叠
- List activityDOs1 = filterList(activityList, s -> ObjectUtil.equal(s.getSpuId(), spuId));
- if (isNotEmpty(activityDOs1)) {
- throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS);
- }
- // 2.3 过滤出所有 configIds 有交集的活动,判断是否存在重叠
- List activityDOs2 = filterList(activityList, s -> containsAny(s.getConfigIds(), configIds));
- if (isNotEmpty(activityDOs2)) {
+ // 2.2 过滤出所有 configIds 有交集的活动,判断是否存在重叠
+ List conflictActivityList = filterList(activityList, s -> containsAny(s.getConfigIds(), configIds));
+ if (isNotEmpty(conflictActivityList)) {
throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS);
}
}
@@ -108,7 +103,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
/**
* 校验秒杀商品是否都存在
*
- * @param spuId 商品 SPU 编号
+ * @param spuId 商品 SPU 编号
* @param products 秒杀商品
*/
private void validateProductExists(Long spuId, List products) {
@@ -150,11 +145,44 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
updateSeckillProduct(updateObj, updateReqVO.getProducts());
}
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateSeckillStock(SeckillActivityUpdateStockReqDTO updateStockReqDTO) {
+ // 1、校验秒杀活动是否存在
+ SeckillActivityDO seckillActivity = getSeckillActivity(updateStockReqDTO.getActivityId());
+ // 1.1、校验库存是否充足
+ if (seckillActivity.getTotalStock() < updateStockReqDTO.getCount()) {
+ throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
+ }
+
+ // 2、获取活动商品
+ List products = getSeckillProductListByActivityId(updateStockReqDTO.getActivityId());
+ // 2.1、过滤出购买的商品
+ SeckillProductDO product = findFirst(products, item -> ObjectUtil.equal(updateStockReqDTO.getItem().getSkuId(), item.getSkuId()));
+ // 2.2、检查活动商品库存是否充足
+ boolean isSufficient = product == null || (product.getStock() == 0 || (product.getStock() < updateStockReqDTO.getItem().getCount()) || (product.getStock() - updateStockReqDTO.getItem().getCount()) < 0);
+ if (isSufficient) {
+ throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
+ }
+
+ // 3、更新活动商品库存
+ int updateCount = seckillProductMapper.updateActivityStock(product.getId(), updateStockReqDTO.getItem().getCount());
+ if (updateCount == 0) {
+ throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
+ }
+
+ // 4、更新活动库存
+ updateCount = seckillActivityMapper.updateActivityStock(seckillActivity.getId(), updateStockReqDTO.getCount());
+ if (updateCount == 0) {
+ throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
+ }
+ }
+
/**
* 更新秒杀商品
*
* @param activity 秒杀活动
- * @param products 该活动的最新商品配置
+ * @param products 该活动的最新商品配置
*/
private void updateSeckillProduct(SeckillActivityDO activity, List products) {
// 第一步,对比新老数据,获得添加、修改、删除的列表
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java
index dd5551fe2..2ad362fe2 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java
@@ -1,18 +1,9 @@
package cn.iocoder.yudao.module.promotion.util;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
-import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Function;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
-import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
/**
* 活动工具类
@@ -31,21 +22,4 @@ public class PromotionUtils {
return LocalDateTimeUtils.beforeNow(endTime) ? CommonStatusEnum.DISABLE.getStatus() : CommonStatusEnum.ENABLE.getStatus();
}
- /**
- * 校验商品 sku 是否都存在
- *
- * @param skus 数据库中的商品 skus
- * @param products 需要校验的商品
- * @param func 获取需要校验的商品的 skuId
- */
- public static void validateProductSkuAllExists(List skus, List products, Function func) {
- // 校验 sku 个数是否一致
- Set skuIdsSet = CollectionUtils.convertSet(products, func);
- Set skuIdsSet1 = CollectionUtils.convertSet(skus, ProductSkuRespDTO::getId);
- // 校验 skuId 是否存在
- if (anyMatch(skuIdsSet, s -> !skuIdsSet1.contains(s))) {
- throw exception(SKU_NOT_EXISTS);
- }
- }
-
}
diff --git a/yudao-module-mall/yudao-module-trade-api/pom.xml b/yudao-module-mall/yudao-module-trade-api/pom.xml
index 1299ad11d..6dd926b7b 100644
--- a/yudao-module-mall/yudao-module-trade-api/pom.xml
+++ b/yudao-module-mall/yudao-module-trade-api/pom.xml
@@ -21,6 +21,13 @@
cn.iocoder.boot
yudao-common
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ true
+
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/BrokerageApi.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/BrokerageApi.java
new file mode 100644
index 000000000..a08ce7f46
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/BrokerageApi.java
@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.trade.api.brokerage;
+
+import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
+import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageUserDTO;
+
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+// TODO @疯狂:是不是不需要这个啦?
+/**
+ * 分销 API 接口
+ *
+ * @author owen
+ */
+public interface BrokerageApi {
+
+ /**
+ * 获得分销用户
+ *
+ * @param userId 用户编号
+ * @return 分销用户信息
+ */
+ BrokerageUserDTO getBrokerageUser(Long userId);
+
+ /**
+ * 【会员】绑定推广员
+ *
+ * @param userId 用户编号
+ * @param bindUserId 推广员编号
+ * @param registerTime 用户注册时间
+ * @return 是否绑定
+ */
+ default boolean bindUser(@NotNull Long userId, @NotNull Long bindUserId, @NotNull LocalDateTime registerTime) {
+ // 注册时间在30秒内的,都算新用户
+ // TODO @疯狂:这个要不抽到 service 里哈?
+ boolean isNewUser = LocalDateTimeUtils.afterNow(registerTime.minusSeconds(30));
+ return bindUser(userId, bindUserId, isNewUser);
+ }
+
+ /**
+ * 绑定推广员
+ *
+ * @param userId 用户编号
+ * @param bindUserId 推广员编号
+ * @param isNewUser 是否为新用户
+ * @return 是否绑定
+ */
+ boolean bindUser(@NotNull(message = "用户编号不能为空") Long userId,
+ @NotNull(message = "推广员编号不能为空") Long bindUserId,
+ @NotNull Boolean isNewUser);
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/dto/BrokerageUserDTO.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/dto/BrokerageUserDTO.java
new file mode 100644
index 000000000..864abe148
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/dto/BrokerageUserDTO.java
@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.module.trade.api.brokerage.dto;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 分销用户 DTO
+ *
+ * @author owen
+ */
+@Data
+public class BrokerageUserDTO {
+
+ /**
+ * 用户编号
+ *
+ * 对应 MemberUserDO 的 id 字段
+ */
+ private Long id;
+
+ /**
+ * 推广员编号
+ *
+ * 关联 MemberUserDO 的 id 字段
+ */
+ private Long bindUserId;
+ /**
+ * 推广员绑定时间
+ */
+ private LocalDateTime bindUserTime;
+
+ /**
+ * 推广资格
+ */
+ private Boolean brokerageEnabled;
+ /**
+ * 成为分销员时间
+ */
+ private LocalDateTime brokerageTime;
+
+ /**
+ * 可用佣金
+ */
+ private Integer price;
+ /**
+ * 冻结佣金
+ */
+ private Integer frozenPrice;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApi.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApi.java
index 5f98073d1..8f33fff28 100644
--- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApi.java
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApi.java
@@ -1,5 +1,7 @@
package cn.iocoder.yudao.module.trade.api.order;
+import java.util.Collection;
+
/**
* 订单 API 接口
*
@@ -16,4 +18,13 @@ public interface TradeOrderApi {
*/
Long validateOrder(Long userId, Long orderItemId);
+ /**
+ * 获取订单项商品购买数量总和
+ *
+ * @param orderIds 订单编号
+ * @param skuIds sku 编号
+ * @return 订单项商品购买数量总和
+ */
+ Integer getOrderItemCountSumByOrderIdAndSkuId(Collection orderIds, Collection skuIds);
+
}
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
index dc757b05e..b49571c9c 100644
--- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
@@ -11,7 +11,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
- // ========== Order 模块 1011000000 ==========
+ // ========== Order 模块 1011000000 ==========
ErrorCode ORDER_CREATE_SKU_NOT_FOUND = new ErrorCode(1011000001, "商品 SKU 不存在");
ErrorCode ORDER_CREATE_SPU_NOT_SALE = new ErrorCode(1011000002, "商品 SPU 不可售卖");
ErrorCode ORDER_CREATE_SKU_STOCK_NOT_ENOUGH = new ErrorCode(1011000004, "商品 SKU 库存不足");
@@ -32,8 +32,13 @@ public interface ErrorCodeConstants {
ErrorCode ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE = new ErrorCode(1011000021, "交易订单发货失败,订单已退款或部分退款");
ErrorCode ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1011000022, "交易订单发货失败,拼团未成功");
ErrorCode ORDER_DELIVERY_FAIL_BARGAIN_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1011000023, "交易订单发货失败,砍价未成功");
+ ErrorCode ORDER_DELIVERY_FAIL_DELIVERY_TYPE_NOT_EXPRESS = new ErrorCode(1011000024, "交易订单发货失败,发货类型不是快递");
+ ErrorCode ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID = new ErrorCode(1011000025, "交易订单取消失败,订单不是【待支付】状态");
+ ErrorCode ORDER_UPDATE_PRICE_FAIL_PAID = new ErrorCode(1011000026, "支付订单调价失败,原因:支付订单已付款,不能调价");
+ ErrorCode ORDER_UPDATE_PRICE_FAIL_EQUAL = new ErrorCode(1011000027, "支付订单调价失败,原因:价格没有变化");
+ ErrorCode ORDER_UPDATE_PRICE_FAIL_NOT_ITEM = new ErrorCode(1011000028, "支付订单调价失败,原因:订单项不存在");
- // ========== After Sale 模块 1011000100 ==========
+ // ========== After Sale 模块 1011000100 ==========
ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在");
ErrorCode AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR = new ErrorCode(1011000101, "申请退款金额错误");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED = new ErrorCode(1011000102, "订单已关闭,无法申请售后");
@@ -48,7 +53,7 @@ public interface ErrorCodeConstants {
ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY =
new ErrorCode(1011000111, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】或【商家待收货】");
- // ========== Cart 模块 1011002000 ==========
+ // ========== Cart 模块 1011002000 ==========
ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1011002000, "购物车项不存在");
// ========== Price 相关 1011003000 ============
@@ -56,7 +61,7 @@ public interface ErrorCodeConstants {
ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY = new ErrorCode(1011003001, "计算快递运费异常,收件人地址编号为空");
ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1011003002, "计算快递运费异常,找不到对应的运费模板");
- // ========== 物流 Express 模块 1011004000 ==========
+ // ========== 物流 Express 模块 1011004000 ==========
ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1011004000, "快递公司不存在");
ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1011004001, "已经存在该编码的快递公司");
ErrorCode EXPRESS_CLIENT_NOT_PROVIDE = new ErrorCode(1011004002, "需要接入快递服务商,比如【快递100】");
@@ -65,11 +70,22 @@ public interface ErrorCodeConstants {
ErrorCode EXPRESS_API_QUERY_ERROR = new ErrorCode(1011004101, "快递查询接口异常");
ErrorCode EXPRESS_API_QUERY_FAILED = new ErrorCode(1011004102, "快递查询返回失败,原因:{}");
- // ========== 物流 Template 模块 1011005000 ==========
+ // ========== 物流 Template 模块 1011005000 ==========
ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1011005000, "已经存在该运费模板名");
ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1011005001, "运费模板不存在");
- // ========== 物流 PICK_UP 模块 1011006000 ==========
+ // ========== 物流 PICK_UP 模块 1011006000 ==========
ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011006000, "自提门店不存在");
+
+ // ========== 分销用户 模块 1011007000 ==========
+ ErrorCode BROKERAGE_USER_NOT_EXISTS = new ErrorCode(1011007000, "分销用户不存在");
+ ErrorCode BROKERAGE_USER_FROZEN_PRICE_NOT_ENOUGH = new ErrorCode(1011007001, "用户冻结佣金({})数量不足");
+ ErrorCode BROKERAGE_BIND_SELF = new ErrorCode(1011007002, "不能绑定自己");
+ ErrorCode BROKERAGE_BIND_USER_NOT_ENABLED = new ErrorCode(1011007003, "绑定用户没有推广资格");
+ ErrorCode BROKERAGE_BIND_CONDITION_ADMIN = new ErrorCode(1011007004, "仅可在后台绑定推广员");
+ ErrorCode BROKERAGE_BIND_MODE_REGISTER = new ErrorCode(1011007005, "只有在注册时可以绑定");
+ ErrorCode BROKERAGE_BIND_OVERRIDE = new ErrorCode(1011007006, "已绑定了推广人");
+ ErrorCode BROKERAGE_BIND_LOOP = new ErrorCode(1011007007, "下级不能绑定自己的上级");
+
}
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/TradeAfterSaleStatusEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/TradeAfterSaleStatusEnum.java
index 74513bd3d..3a8f3d46a 100644
--- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/TradeAfterSaleStatusEnum.java
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/TradeAfterSaleStatusEnum.java
@@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
+import java.util.Collection;
import static cn.hutool.core.util.ArrayUtil.firstMatch;
@@ -55,6 +56,18 @@ public enum TradeAfterSaleStatusEnum implements IntArrayValuable {
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeAfterSaleStatusEnum::getStatus).toArray();
+ /**
+ * 进行中的售后状态
+ *
+ * 不包括已经结束的状态
+ */
+ public static final Collection APPLYING_STATUSES = Arrays.asList(
+ APPLY.getStatus(),
+ SELLER_AGREE.getStatus(),
+ BUYER_DELIVERY.getStatus(),
+ WAIT_REFUND.getStatus()
+ );
+
/**
* 状态
*/
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageBindModeEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageBindModeEnum.java
new file mode 100644
index 000000000..3b6610ac9
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageBindModeEnum.java
@@ -0,0 +1,48 @@
+package cn.iocoder.yudao.module.trade.enums.brokerage;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 分销关系绑定模式枚举
+ *
+ * @author owen
+ */
+@AllArgsConstructor
+@Getter
+public enum BrokerageBindModeEnum implements IntArrayValuable {
+
+ /**
+ * 只要用户没有推广人,随时都可以绑定分销关系
+ */
+ ANYTIME(1, "没有推广人"),
+ /**
+ * 仅新用户注册时才能绑定推广关系
+ */
+ REGISTER(2, "新用户"),
+ /**
+ * 每次扫码都覆盖
+ */
+ OVERRIDE(3, "扫码覆盖"),
+ ;
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageBindModeEnum::getMode).toArray();
+
+ /**
+ * 模式
+ */
+ private final Integer mode;
+ /**
+ * 名字
+ */
+ private final String name;
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageEnabledConditionEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageEnabledConditionEnum.java
new file mode 100644
index 000000000..990d10e16
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageEnabledConditionEnum.java
@@ -0,0 +1,44 @@
+package cn.iocoder.yudao.module.trade.enums.brokerage;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 分佣模式枚举
+ *
+ * @author owen
+ */
+@AllArgsConstructor
+@Getter
+public enum BrokerageEnabledConditionEnum implements IntArrayValuable {
+
+ /**
+ * 所有用户都可以分销
+ */
+ ALL(1, "人人分销"),
+ /**
+ * 仅可后台手动设置推广员
+ */
+ ADMIN(2, "指定分销"),
+ ;
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageEnabledConditionEnum::getCondition).toArray();
+
+ /**
+ * 模式
+ */
+ private final Integer condition;
+ /**
+ * 名字
+ */
+ private final String name;
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageRecordBizTypeEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageRecordBizTypeEnum.java
new file mode 100644
index 000000000..ae798a6ac
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageRecordBizTypeEnum.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.trade.enums.brokerage;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 佣金记录业务类型枚举
+ *
+ * @author owen
+ */
+@AllArgsConstructor
+@Getter
+public enum BrokerageRecordBizTypeEnum implements IntArrayValuable {
+
+ ORDER(1, "获得推广佣金", "获得推广佣金 {}", true),
+ WITHDRAW(2, "提现申请", "提现申请扣除佣金 {}", false),
+ ;
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageRecordBizTypeEnum::getType).toArray();
+
+ /**
+ * 类型
+ */
+ private final Integer type;
+ /**
+ * 标题
+ */
+ private final String title;
+ /**
+ * 描述
+ */
+ private final String description;
+ /**
+ * 是否为增加佣金
+ */
+ private final boolean add;
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageRecordStatusEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageRecordStatusEnum.java
new file mode 100644
index 000000000..827390998
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageRecordStatusEnum.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.trade.enums.brokerage;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 佣金记录状态枚举
+ *
+ * @author owen
+ */
+@AllArgsConstructor
+@Getter
+public enum BrokerageRecordStatusEnum implements IntArrayValuable {
+
+ WAIT_SETTLEMENT(0, "待结算"),
+ SETTLEMENT(1, "已结算"),
+ CANCEL(2, "已取消"),
+ ;
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageRecordStatusEnum::getStatus).toArray();
+
+ /**
+ * 状态
+ */
+ private final Integer status;
+ /**
+ * 名字
+ */
+ private final String name;
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageUserTypeEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageUserTypeEnum.java
new file mode 100644
index 000000000..a75ad4414
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageUserTypeEnum.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.trade.enums.brokerage;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+// TODO @疯狂:是不是搞成层级,类似 level 这样?因为本质上,它是 1 级、2 级、3 级这样的关系哈
+/**
+ * 分销用户类型枚举
+ *
+ * @author owen
+ */
+@AllArgsConstructor
+@Getter
+public enum BrokerageUserTypeEnum implements IntArrayValuable {
+
+ ALL(0, "全部"),
+ FIRST(1, "一级推广人"),
+ SECOND(2, "二级推广人"),
+ ;
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageUserTypeEnum::getType).toArray();
+
+ /**
+ * 类型
+ */
+ private final Integer type;
+ /**
+ * 名字
+ */
+ private final String name;
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageWithdrawStatusEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageWithdrawStatusEnum.java
new file mode 100644
index 000000000..a80aad02a
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageWithdrawStatusEnum.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.trade.enums.brokerage;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 佣金提现状态枚举
+ *
+ * @author owen
+ */
+@AllArgsConstructor
+@Getter
+public enum BrokerageWithdrawStatusEnum implements IntArrayValuable {
+
+ AUDITING(0, "审核中"),
+ AUDIT_SUCCESS(10, "审核通过"),
+ WITHDRAW_SUCCESS(11, "提现成功"),
+ AUDIT_FAIL(20, "审核不通过"),
+ WITHDRAW_FAIL(21, "提现失败"),
+ ;
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageWithdrawStatusEnum::getStatus).toArray();
+
+ /**
+ * 状态
+ */
+ private final Integer status;
+ /**
+ * 名字
+ */
+ private final String name;
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageWithdrawTypeEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageWithdrawTypeEnum.java
new file mode 100644
index 000000000..46edf010e
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageWithdrawTypeEnum.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.trade.enums.brokerage;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 佣金提现类型枚举
+ *
+ * @author owen
+ */
+@AllArgsConstructor
+@Getter
+public enum BrokerageWithdrawTypeEnum implements IntArrayValuable {
+
+ WALLET(1, "钱包"),
+ BANK(2, "银行卡"),
+ WECHAT(3, "微信"),
+ ALIPAY(4, "支付宝"),
+ ;
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageWithdrawTypeEnum::getType).toArray();
+
+ /**
+ * 类型
+ */
+ private final Integer type;
+ /**
+ * 名字
+ */
+ private final String name;
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderOperateTypeEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderOperateTypeEnum.java
new file mode 100644
index 000000000..f16e35601
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderOperateTypeEnum.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.trade.enums.order;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * 订单操作类型的枚举
+ *
+ * @author 陈賝
+ * @since 2023/7/6 15:31
+ */
+@RequiredArgsConstructor
+@Getter
+public enum TradeOrderOperateTypeEnum {
+
+ MEMBER_CREATE(1, "用户下单"),
+ TEST(2, "用户({nickname})做了({thing})"),
+ ;
+
+ /**
+ * 类型
+ */
+ private final Integer type;
+ /**
+ * 类型
+ */
+ private final String content;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/pom.xml b/yudao-module-mall/yudao-module-trade-biz/pom.xml
index 810446fa9..912135eea 100644
--- a/yudao-module-mall/yudao-module-trade-biz/pom.xml
+++ b/yudao-module-mall/yudao-module-trade-biz/pom.xml
@@ -54,6 +54,10 @@
cn.iocoder.boot
yudao-spring-boot-starter-biz-operatelog
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-biz-tenant
+
cn.iocoder.boot
yudao-spring-boot-starter-biz-ip
@@ -82,6 +86,11 @@
yudao-spring-boot-starter-mybatis
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-redis
+
+
cn.iocoder.boot
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/BrokerageApiImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/BrokerageApiImpl.java
new file mode 100644
index 000000000..4910f07a5
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/BrokerageApiImpl.java
@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.trade.api.brokerage;
+
+import cn.iocoder.yudao.module.trade.api.brokerage.dto.BrokerageUserDTO;
+import cn.iocoder.yudao.module.trade.convert.brokerage.user.BrokerageUserConvert;
+import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+
+/**
+ * 分销 API 接口实现类
+ *
+ * @author owen
+ */
+@Service
+@Validated
+public class BrokerageApiImpl implements BrokerageApi {
+
+ @Resource
+ private BrokerageUserService brokerageUserService;
+
+ @Override
+ public BrokerageUserDTO getBrokerageUser(Long userId) {
+ return BrokerageUserConvert.INSTANCE.convertDTO(brokerageUserService.getBrokerageUser(userId));
+ }
+
+ @Override
+ public boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser) {
+ return brokerageUserService.bindBrokerageUser(userId, bindUserId, isNewUser);
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApiImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApiImpl.java
index 7895386cf..3ee315ccb 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApiImpl.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApiImpl.java
@@ -6,6 +6,7 @@ import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
+import java.util.Collection;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_ITEM_NOT_FOUND;
@@ -32,4 +33,9 @@ public class TradeOrderApiImpl implements TradeOrderApi {
return item.getOrderId();
}
+ @Override
+ public Integer getOrderItemCountSumByOrderIdAndSkuId(Collection orderIds, Collection skuIds) {
+ return tradeOrderQueryService.getOrderItemCountSumByOrderIdAndSkuId(orderIds, skuIds);
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.java
index c12d2b5c0..0bf5a9cd9 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.java
@@ -7,13 +7,15 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
-import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleDisagreeReqVO;
-import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSalePageReqVO;
-import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRefuseReqVO;
-import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRespPageItemVO;
+import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.*;
import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
+import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
+import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
+import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogRespDTO;
+import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.service.AfterSaleLogService;
import cn.iocoder.yudao.module.trade.service.aftersale.TradeAfterSaleService;
+import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -25,6 +27,9 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.validation.Valid;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -41,7 +46,10 @@ public class TradeAfterSaleController {
@Resource
private TradeAfterSaleService afterSaleService;
-
+ @Resource
+ private TradeOrderQueryService tradeOrderQueryService;
+ @Resource
+ private AfterSaleLogService afterSaleLogService;
@Resource
private MemberUserApi memberUserApi;
@@ -61,6 +69,50 @@ public class TradeAfterSaleController {
return success(TradeAfterSaleConvert.INSTANCE.convertPage(pageResult, memberUsers));
}
+ @GetMapping("/get-detail")
+ @Operation(summary = "获得售后订单详情")
+ @Parameter(name = "id", description = "售后编号", required = true, example = "1")
+ @PreAuthorize("@ss.hasPermission('trade:after-sale:query')")
+ public CommonResult getOrderDetail(@RequestParam("id") Long id) {
+ // 查询订单
+ TradeAfterSaleDO afterSale = afterSaleService.getAfterSale(id);
+ if (afterSale == null) {
+ return success(null);
+ }
+
+ // 查询订单
+ TradeOrderDO order = tradeOrderQueryService.getOrder(afterSale.getOrderId());
+ // 查询订单项
+ List orderItems = tradeOrderQueryService.getOrderItemListByOrderId(id);
+ // 拼接数据
+ MemberUserRespDTO user = memberUserApi.getUser(afterSale.getUserId());
+ // 获取售后日志
+ List logs = afterSaleLogService.getLog(afterSale.getId());
+ // TODO 方便测试看效果,review 后移除
+ if (logs == null) {
+ logs = new ArrayList<>();
+ }
+ for (int i = 1; i <= 6; i++) {
+ TradeAfterSaleLogRespDTO respVO = new TradeAfterSaleLogRespDTO();
+ respVO.setId((long) i);
+ respVO.setUserId((long) i);
+ respVO.setUserType(i % 2 == 0 ? 2 : 1);
+ // 模拟系统操作
+ if (i == 2) {
+ respVO.setUserType(3);
+ }
+ respVO.setAfterSaleId(id);
+ respVO.setOrderId((long) i);
+ respVO.setOrderItemId((long) i);
+ respVO.setBeforeStatus((i - 1) * 10);
+ respVO.setAfterStatus(i * 10);
+ respVO.setContent("66+6");
+ respVO.setCreateTime(LocalDateTime.now());
+ logs.add(respVO);
+ }
+ return success(TradeAfterSaleConvert.INSTANCE.convert(afterSale, order, orderItems, user, logs));
+ }
+
@PutMapping("/agree")
@Operation(summary = "同意售后")
@Parameter(name = "id", description = "售后编号", required = true, example = "1")
@@ -88,7 +140,7 @@ public class TradeAfterSaleController {
}
@PutMapping("/refuse")
- @Operation(summary = "确认收货")
+ @Operation(summary = "拒绝收货")
@Parameter(name = "id", description = "售后编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('trade:after-sale:receive')")
public CommonResult refuseAfterSale(TradeAfterSaleRefuseReqVO refuseReqVO) {
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/TradeAfterSaleDetailRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/TradeAfterSaleDetailRespVO.java
new file mode 100644
index 000000000..af6db85c7
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/vo/TradeAfterSaleDetailRespVO.java
@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;
+
+import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.log.TradeAfterSaleLogRespVO;
+import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
+import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
+import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderBaseVO;
+import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderItemBaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Schema(description = "管理后台 - 售后订单的详情 Response VO")
+@Data
+public class TradeAfterSaleDetailRespVO extends TradeAfterSaleBaseVO {
+
+ @Schema(description = "售后编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+ /**
+ * 订单项列表
+ */
+ private List- items;
+
+ /**
+ * 订单基本信息
+ */
+ private TradeOrderBaseVO order;
+
+ /**
+ * 用户信息
+ */
+ private MemberUserRespVO user;
+
+ /**
+ * 售后日志
+ */
+ private List logs;
+
+ @Schema(description = "管理后台 - 交易订单的详情的订单项目")
+ @Data
+ public static class Item extends TradeOrderItemBaseVO {
+
+ /**
+ * 属性数组
+ */
+ private List properties;
+
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/record/BrokerageRecordController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/record/BrokerageRecordController.java
new file mode 100644
index 000000000..9bb135670
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/record/BrokerageRecordController.java
@@ -0,0 +1,66 @@
+package cn.iocoder.yudao.module.trade.controller.admin.brokerage.record;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
+import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
+import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordPageReqVO;
+import cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo.BrokerageRecordRespVO;
+import cn.iocoder.yudao.module.trade.convert.brokerage.record.BrokerageRecordConvert;
+import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.record.BrokerageRecordDO;
+import cn.iocoder.yudao.module.trade.service.brokerage.record.BrokerageRecordService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.Map;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+
+@Tag(name = "管理后台 - 佣金记录")
+@RestController
+@RequestMapping("/trade/brokerage-record")
+@Validated
+public class BrokerageRecordController {
+
+ @Resource
+ private BrokerageRecordService brokerageRecordService;
+
+ @Resource
+ private MemberUserApi memberUserApi;
+
+ @GetMapping("/get")
+ @Operation(summary = "获得佣金记录")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('trade:brokerage-record:query')")
+ public CommonResult getBrokerageRecord(@RequestParam("id") Integer id) {
+ BrokerageRecordDO brokerageRecord = brokerageRecordService.getBrokerageRecord(id);
+ return success(BrokerageRecordConvert.INSTANCE.convert(brokerageRecord));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得佣金记录分页")
+ @PreAuthorize("@ss.hasPermission('trade:brokerage-record:query')")
+ public CommonResult> getBrokerageRecordPage(@Valid BrokerageRecordPageReqVO pageVO) {
+ PageResult pageResult = brokerageRecordService.getBrokerageRecordPage(pageVO);
+
+ // 查询用户信息
+ Set userIds = convertSet(pageResult.getList(), BrokerageRecordDO::getUserId);
+ userIds.addAll(convertList(pageResult.getList(), BrokerageRecordDO::getSourceUserId));
+ Map userMap = memberUserApi.getUserMap(userIds);
+ // 拼接数据
+ return success(BrokerageRecordConvert.INSTANCE.convertPage(pageResult, userMap));
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/record/vo/BrokerageRecordBaseVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/record/vo/BrokerageRecordBaseVO.java
new file mode 100644
index 000000000..477f89978
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/record/vo/BrokerageRecordBaseVO.java
@@ -0,0 +1,65 @@
+package cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * 佣金记录 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class BrokerageRecordBaseVO {
+
+ @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25973")
+ @NotNull(message = "用户编号不能为空")
+ private Long userId;
+
+ @Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23353")
+ @NotEmpty(message = "业务编号不能为空")
+ private String bizId;
+
+ @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "业务类型不能为空")
+ private Integer bizType;
+
+ @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotEmpty(message = "标题不能为空")
+ private String title;
+
+ @Schema(description = "金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "28731")
+ @NotNull(message = "金额不能为空")
+ private Integer price;
+
+ @Schema(description = "当前总佣金", requiredMode = Schema.RequiredMode.REQUIRED, example = "13226")
+ @NotNull(message = "当前总佣金不能为空")
+ private Integer totalPrice;
+
+ @Schema(description = "说明", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
+ @NotNull(message = "说明不能为空")
+ private String description;
+
+ @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "状态不能为空")
+ private Integer status;
+
+ @Schema(description = "冻结时间(天)", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "冻结时间(天)不能为空")
+ private Integer frozenDays;
+
+ @Schema(description = "解冻时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime unfreezeTime;
+
+ @Schema(description = "来源用户类型")
+ private Integer sourceUserType;
+
+ @Schema(description = "来源用户编号")
+ private Long sourceUserId;
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/record/vo/BrokerageRecordPageReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/record/vo/BrokerageRecordPageReqVO.java
new file mode 100644
index 000000000..4ef472290
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/record/vo/BrokerageRecordPageReqVO.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageUserTypeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 佣金记录分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class BrokerageRecordPageReqVO extends PageParam {
+
+ @Schema(description = "用户编号", example = "25973")
+ private Long userId;
+
+ @Schema(description = "业务类型", example = "1")
+ private Integer bizType;
+
+ @Schema(description = "状态", example = "1")
+ private Integer status;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+ @Schema(description = "用户类型")
+ @InEnum(value = BrokerageUserTypeEnum.class, message = "用户类型必须是 {value}")
+ private Integer sourceUserType;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/record/vo/BrokerageRecordRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/record/vo/BrokerageRecordRespVO.java
new file mode 100644
index 000000000..1f58b53ee
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/record/vo/BrokerageRecordRespVO.java
@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.trade.controller.admin.brokerage.record.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 佣金记录 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class BrokerageRecordRespVO extends BrokerageRecordBaseVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28896")
+ private Integer id;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+
+ // ========== 用户信息 ==========
+
+ @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
+ private String userAvatar;
+ @Schema(description = "用户昵称", example = "李四")
+ private String userNickname;
+
+
+ // ========== 来源用户信息 ==========
+
+ @Schema(description = "来源用户头像", example = "https://www.iocoder.cn/xxx.png")
+ private String sourceUserAvatar;
+ @Schema(description = "来源用户昵称", example = "李四")
+ private String sourceUserNickname;
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/BrokerageUserController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/BrokerageUserController.java
new file mode 100644
index 000000000..f47f5d9ed
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/BrokerageUserController.java
@@ -0,0 +1,106 @@
+package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
+import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
+import cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo.*;
+import cn.iocoder.yudao.module.trade.convert.brokerage.user.BrokerageUserConvert;
+import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.user.BrokerageUserDO;
+import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
+import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
+import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageUserTypeEnum;
+import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryBO;
+import cn.iocoder.yudao.module.trade.service.brokerage.record.BrokerageRecordService;
+import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.Map;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+
+@Tag(name = "管理后台 - 分销用户")
+@RestController
+@RequestMapping("/trade/brokerage-user")
+@Validated
+public class BrokerageUserController {
+
+ @Resource
+ private BrokerageUserService brokerageUserService;
+ @Resource
+ private BrokerageRecordService brokerageRecordService;
+
+ @Resource
+ private MemberUserApi memberUserApi;
+
+ @PutMapping("/update-bind-user")
+ @Operation(summary = "修改推广员")
+ @PreAuthorize("@ss.hasPermission('trade:brokerage-user:update-bind-user')")
+ public CommonResult updateBindUser(@Valid @RequestBody BrokerageUserUpdateBrokerageUserReqVO updateReqVO) {
+ brokerageUserService.updateBrokerageUserId(updateReqVO.getId(), updateReqVO.getBindUserId());
+ return success(true);
+ }
+
+ @PutMapping("/clear-bind-user")
+ @Operation(summary = "清除推广员")
+ @PreAuthorize("@ss.hasPermission('trade:brokerage-user:clear-bind-user')")
+ public CommonResult clearBindUser(@Valid @RequestBody BrokerageUserClearBrokerageUserReqVO updateReqVO) {
+ brokerageUserService.updateBrokerageUserId(updateReqVO.getId(), null);
+ return success(true);
+ }
+
+ @PutMapping("/update-brokerage-enable")
+ @Operation(summary = "修改推广资格")
+ @PreAuthorize("@ss.hasPermission('trade:brokerage-user:update-brokerage-enable')")
+ public CommonResult updateBrokerageEnabled(@Valid @RequestBody BrokerageUserUpdateBrokerageEnabledReqVO updateReqVO) {
+ brokerageUserService.updateBrokerageUserEnabled(updateReqVO.getId(), updateReqVO.getEnabled());
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得分销用户")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('trade:brokerage-user:query')")
+ public CommonResult getBrokerageUser(@RequestParam("id") Long id) {
+ BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(id);
+ BrokerageUserRespVO respVO = BrokerageUserConvert.INSTANCE.convert(brokerageUser);
+ return success(BrokerageUserConvert.INSTANCE.copyTo(memberUserApi.getUser(id), respVO));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得分销用户分页")
+ @PreAuthorize("@ss.hasPermission('trade:brokerage-user:query')")
+ public CommonResult> getBrokerageUserPage(@Valid BrokerageUserPageReqVO pageVO) {
+ // 分页查询
+ PageResult pageResult = brokerageUserService.getBrokerageUserPage(pageVO);
+
+ // 涉及到的用户
+ Set userIds = convertSet(pageResult.getList(), BrokerageUserDO::getId);
+ // 查询用户信息
+ Map userMap = memberUserApi.getUserMap(userIds);
+ // 合计分佣订单
+ Map userOrderSummaryMap = convertMap(userIds,
+ userId -> userId,
+ userId -> brokerageRecordService.getUserBrokerageSummaryByUserId(userId,
+ BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus()));
+ // 合计推广用户数量
+ Map brokerageUserCountMap = convertMap(userIds,
+ userId -> userId,
+ userId -> brokerageUserService.getBrokerageUserCountByBindUserId(userId, BrokerageUserTypeEnum.ALL));
+
+ // todo 合计提现
+
+ return success(BrokerageUserConvert.INSTANCE.convertPage(pageResult, userMap, brokerageUserCountMap, userOrderSummaryMap));
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserBaseVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserBaseVO.java
new file mode 100644
index 000000000..34f792b72
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserBaseVO.java
@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * 分销用户 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class BrokerageUserBaseVO {
+
+ @Schema(description = "推广员编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4587")
+ @NotNull(message = "推广员编号不能为空")
+ private Long bindUserId;
+
+ @Schema(description = "推广员绑定时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime bindUserTime;
+
+ @Schema(description = "推广资格", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "推广资格不能为空")
+ private Boolean brokerageEnabled;
+
+ @Schema(description = "成为分销员时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime brokerageTime;
+
+ @Schema(description = "可用佣金", requiredMode = Schema.RequiredMode.REQUIRED, example = "11089")
+ @NotNull(message = "可用佣金不能为空")
+ private Integer price;
+
+ @Schema(description = "冻结佣金", requiredMode = Schema.RequiredMode.REQUIRED, example = "30916")
+ @NotNull(message = "冻结佣金不能为空")
+ private Integer frozenPrice;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserClearBrokerageUserReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserClearBrokerageUserReqVO.java
new file mode 100644
index 000000000..edb0102a0
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserClearBrokerageUserReqVO.java
@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 分销用户 - 清除推广员 Request VO")
+@Data
+@ToString(callSuper = true)
+public class BrokerageUserClearBrokerageUserReqVO {
+
+ @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
+ @NotNull(message = "用户编号不能为空")
+ private Long id;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserPageReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserPageReqVO.java
new file mode 100644
index 000000000..c40affac0
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserPageReqVO.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageUserTypeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 分销用户分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class BrokerageUserPageReqVO extends PageParam {
+
+ @Schema(description = "推广员编号", example = "4587")
+ private Long bindUserId;
+
+ @Schema(description = "推广资格")
+ private Boolean brokerageEnabled;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+ @Schema(description = "用户类型")
+ @InEnum(value = BrokerageUserTypeEnum.class, message = "用户类型必须是 {value}")
+ private Integer userType;
+
+ @Schema(description = "绑定时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] bindUserTime;
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserRespVO.java
new file mode 100644
index 000000000..4aa3e5732
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserRespVO.java
@@ -0,0 +1,45 @@
+package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 分销用户 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class BrokerageUserRespVO extends BrokerageUserBaseVO {
+
+ @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
+ private Long id;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+ // ========== 用户信息 ==========
+
+ @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
+ private String avatar;
+ @Schema(description = "用户昵称", example = "李四")
+ private String nickname;
+
+ // ========== 推广信息 ==========
+
+ @Schema(description = "推广用户数量", example = "20019")
+ private Integer brokerageUserCount;
+ @Schema(description = "推广订单数量", example = "20019")
+ private Integer brokerageOrderCount;
+ @Schema(description = "推广订单金额", example = "20019")
+ private Integer brokerageOrderPrice;
+
+ // ========== 提现信息 ==========
+
+ @Schema(description = "已提现金额", example = "20019")
+ private Integer withdrawPrice;
+ @Schema(description = "已提现次数", example = "20019")
+ private Integer withdrawCount;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserUpdateBrokerageEnabledReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserUpdateBrokerageEnabledReqVO.java
new file mode 100644
index 000000000..92001e698
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserUpdateBrokerageEnabledReqVO.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 分销用户 - 修改推广员 Request VO")
+@Data
+@ToString(callSuper = true)
+public class BrokerageUserUpdateBrokerageEnabledReqVO {
+
+ @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
+ @NotNull(message = "用户编号不能为空")
+ private Long id;
+
+ @Schema(description = "推广资格", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "推广资格不能为空")
+ private Boolean enabled;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserUpdateBrokerageUserReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserUpdateBrokerageUserReqVO.java
new file mode 100644
index 000000000..d62c7fd1e
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserUpdateBrokerageUserReqVO.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.trade.controller.admin.brokerage.user.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 分销用户 - 修改推广员 Request VO")
+@Data
+@ToString(callSuper = true)
+public class BrokerageUserUpdateBrokerageUserReqVO {
+
+ @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019")
+ @NotNull(message = "用户编号不能为空")
+ private Long id;
+
+ @Schema(description = "推广员编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4587")
+ @NotNull(message = "推广员编号不能为空")
+ private Long bindUserId;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/TradeConfigController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/TradeConfigController.java
new file mode 100644
index 000000000..e41f98081
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/TradeConfigController.java
@@ -0,0 +1,45 @@
+package cn.iocoder.yudao.module.trade.controller.admin.config;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.trade.controller.admin.config.vo.TradeConfigRespVO;
+import cn.iocoder.yudao.module.trade.controller.admin.config.vo.TradeConfigSaveReqVO;
+import cn.iocoder.yudao.module.trade.convert.config.TradeConfigConvert;
+import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
+import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
+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("/trade/config")
+@Validated
+public class TradeConfigController {
+
+ @Resource
+ private TradeConfigService tradeConfigService;
+
+ @PutMapping("/save")
+ @Operation(summary = "更新交易中心配置")
+ @PreAuthorize("@ss.hasPermission('trade:config:save')")
+ public CommonResult updateConfig(@Valid @RequestBody TradeConfigSaveReqVO updateReqVO) {
+ tradeConfigService.saveTradeConfig(updateReqVO);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得交易中心配置")
+ @PreAuthorize("@ss.hasPermission('trade:config:query')")
+ public CommonResult getConfig() {
+ TradeConfigDO config = tradeConfigService.getTradeConfig();
+ return success(TradeConfigConvert.INSTANCE.convert(config));
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/vo/TradeConfigBaseVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/vo/TradeConfigBaseVO.java
new file mode 100644
index 000000000..ee20156f1
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/vo/TradeConfigBaseVO.java
@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.trade.controller.admin.config.vo;
+
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageBindModeEnum;
+import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageEnabledConditionEnum;
+import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Range;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.PositiveOrZero;
+import java.util.List;
+
+/**
+ * 交易中心配置 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class TradeConfigBaseVO {
+
+ // ========== 分销相关 ==========
+
+ @Schema(description = "是否启用分佣", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @NotNull(message = "是否启用分佣不能为空")
+ private Boolean brokerageEnabled;
+
+ @Schema(description = "分佣模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+ @NotNull(message = "分佣模式不能为空")
+ @InEnum(value = BrokerageEnabledConditionEnum.class, message = "分佣模式必须是 {value}")
+ private Integer brokerageEnabledCondition;
+
+ @Schema(description = "分销关系绑定模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+ @NotNull(message = "分销关系绑定模式不能为空")
+ @InEnum(value = BrokerageBindModeEnum.class, message = "分销关系绑定模式必须是 {value}")
+ private Integer brokerageBindMode;
+
+ @Schema(description = "分销海报图地址数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[https://www.iocoder.cn/yudao.jpg]")
+ private List brokeragePostUrls;
+
+ @Schema(description = "一级返佣比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+ @NotNull(message = "一级返佣比例不能为空")
+ @Range(min = 0, max = 100, message = "一级返佣比例必须在 0 - 100 之间")
+ private Integer brokerageFirstPercent;
+
+ @Schema(description = "二级返佣比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+ @NotNull(message = "二级返佣比例不能为空")
+ @Range(min = 0, max = 100, message = "二级返佣比例必须在 0 - 100 之间")
+ private Integer brokerageSecondPercent;
+
+ @Schema(description = "用户提现最低金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
+ @NotNull(message = "用户提现最低金额不能为空")
+ @PositiveOrZero(message = "用户提现最低金额不能是负数")
+ private Integer brokerageWithdrawMinPrice;
+
+ @Schema(description = "提现银行", requiredMode = Schema.RequiredMode.REQUIRED, example = "[0, 1]")
+ @NotEmpty(message = "提现银行不能为空")
+ private List brokerageBankNames;
+
+ @Schema(description = "佣金冻结时间(天)", requiredMode = Schema.RequiredMode.REQUIRED, example = "7")
+ @NotNull(message = "佣金冻结时间(天)不能为空")
+ @PositiveOrZero(message = "佣金冻结时间不能是负数")
+ private Integer brokerageFrozenDays;
+
+ @Schema(description = "提现方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "[0, 1]")
+ @NotNull(message = "提现方式不能为空")
+ @InEnum(value = BrokerageWithdrawTypeEnum.class, message = "提现方式必须是 {value}")
+ private List brokerageWithdrawType;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/vo/TradeConfigRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/vo/TradeConfigRespVO.java
new file mode 100644
index 000000000..52aff751f
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/vo/TradeConfigRespVO.java
@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.module.trade.controller.admin.config.vo;
+
+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 TradeConfigRespVO extends TradeConfigBaseVO {
+
+ @Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/vo/TradeConfigSaveReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/vo/TradeConfigSaveReqVO.java
new file mode 100644
index 000000000..03a0c41df
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/config/vo/TradeConfigSaveReqVO.java
@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.trade.controller.admin.config.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - 交易中心配置更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class TradeConfigSaveReqVO extends TradeConfigBaseVO {
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressController.java
index cff98a558..12236abba 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressController.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressController.java
@@ -1,11 +1,11 @@
package cn.iocoder.yudao.module.trade.controller.admin.delivery;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.*;
-import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressUpdateReqVO;
import cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
@@ -67,6 +67,13 @@ public class DeliveryExpressController {
return success(DeliveryExpressConvert.INSTANCE.convert(deliveryExpress));
}
+ @GetMapping("/list-all-simple")
+ @Operation(summary = "获取快递公司精简信息列表", description = "主要用于前端的下拉选项")
+ public CommonResult
> getSimpleDeliveryExpressList() {
+ List list = deliveryExpressService.getDeliveryExpressListByStatus(CommonStatusEnum.ENABLE.getStatus());
+ return success(DeliveryExpressConvert.INSTANCE.convertList1(list));
+ }
+
@GetMapping("/page")
@Operation(summary = "获得快递公司分页")
@PreAuthorize("@ss.hasPermission('trade:delivery:express:query')")
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryPickUpStoreController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryPickUpStoreController.java
index 3b7421aa4..4235d6ef5 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryPickUpStoreController.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryPickUpStoreController.java
@@ -1,9 +1,8 @@
package cn.iocoder.yudao.module.trade.controller.admin.delivery;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
-import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.*;
import cn.iocoder.yudao.module.trade.convert.delivery.DeliveryPickUpStoreConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO;
@@ -16,14 +15,11 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
-import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
-import java.io.IOException;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 自提门店")
@RestController
@@ -67,6 +63,14 @@ public class DeliveryPickUpStoreController {
return success(DeliveryPickUpStoreConvert.INSTANCE.convert(deliveryPickUpStore));
}
+ @GetMapping("/list-all-simple")
+ @Operation(summary = "获得自提门店精简信息列表")
+ public CommonResult> getSimpleDeliveryPickUpStoreList() {
+ List list = deliveryPickUpStoreService.getDeliveryPickUpStoreListByStatus(
+ CommonStatusEnum.ENABLE.getStatus());
+ return success(DeliveryPickUpStoreConvert.INSTANCE.convertList1(list));
+ }
+
@GetMapping("/list")
@Operation(summary = "获得自提门店列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@@ -84,16 +88,4 @@ public class DeliveryPickUpStoreController {
return success(DeliveryPickUpStoreConvert.INSTANCE.convertPage(pageResult));
}
- // TODO @jason:导出去掉好列;简化下,一般用不到哈。
- @GetMapping("/export-excel")
- @Operation(summary = "导出自提门店 Excel")
- @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:export')")
- @OperateLog(type = EXPORT)
- public void exportDeliveryPickUpStoreExcel(@Valid DeliveryPickUpStoreExportReqVO exportReqVO,
- HttpServletResponse response) throws IOException {
- List list = deliveryPickUpStoreService.getDeliveryPickUpStoreList(exportReqVO);
- // 导出 Excel
- List datas = DeliveryPickUpStoreConvert.INSTANCE.convertList02(list);
- ExcelUtils.write(response, "自提门店.xls", "数据", DeliveryPickUpStoreExcelVO.class, datas);
- }
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressSimpleRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressSimpleRespVO.java
new file mode 100644
index 000000000..b97cc2317
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/express/DeliveryExpressSimpleRespVO.java
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 快递公司精简信息 Response VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class DeliveryExpressSimpleRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6592")
+ @NotNull(message = "编号不能为空")
+ private Long id;
+
+ @Schema(description = "快递公司名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "顺丰速运")
+ @NotNull(message = "快递公司名称不能为空")
+ private String name;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreExcelVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreExcelVO.java
deleted file mode 100644
index a50b0ce75..000000000
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreExcelVO.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup;
-
-import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
-import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
-import cn.iocoder.yudao.module.system.enums.DictTypeConstants;
-import com.alibaba.excel.annotation.ExcelProperty;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-@Data
-public class DeliveryPickUpStoreExcelVO {
-
- @ExcelProperty("编号")
- private Long id;
-
- @ExcelProperty("门店名称")
- private String name;
-
- @ExcelProperty("门店简介")
- private String introduction;
-
- @ExcelProperty("门店手机")
- private String phone;
-
- @ExcelProperty("门店所在区域")
- private String areaName;
-
- @ExcelProperty("门店详细地址")
- private String detailAddress;
-
- @ExcelProperty("门店logo")
- private String logo;
-
- // TODO @jason:是不是可以加个 convert?
- /**
- * easy-excel 好像暂时不支持 LocalTime. 转成string
- */
- @ExcelProperty("营业开始时间")
- private String openingTime;
-
- @ExcelProperty("营业结束时间")
- private String closingTime;
-
- @ExcelProperty("纬度")
- private String latitude;
-
- @ExcelProperty("经度")
- private String longitude;
-
- @ExcelProperty(value = "状态", converter = DictConvert.class)
- @DictFormat(DictTypeConstants.COMMON_STATUS)
- private Integer status;
-
- @ExcelProperty("创建时间")
- private LocalDateTime createTime;
-
-}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreExportReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreExportReqVO.java
deleted file mode 100644
index 05546de77..000000000
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreExportReqVO.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup;
-
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-@Schema(description = "管理后台 - 自提门店 Excel 导出 Request VO,参数和 DeliveryPickUpStorePageReqVO 是一致的")
-@Data
-public class DeliveryPickUpStoreExportReqVO {
-
- @Schema(description = "门店名称", example = "李四")
- private String name;
-
- @Schema(description = "门店手机")
- private String phone;
-
- @Schema(description = "区域id", example = "18733")
- private Integer areaId;
-
- @Schema(description = "门店状态", example = "1")
- @InEnum(CommonStatusEnum.class)
- private Integer status;
-
- @Schema(description = "创建时间")
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
- private LocalDateTime[] createTime;
-
-}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreSimpleRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreSimpleRespVO.java
new file mode 100644
index 000000000..c12fc9fc3
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreSimpleRespVO.java
@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Schema(description = "管理后台 - 自提门店精简信息 Response VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class DeliveryPickUpStoreSimpleRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23128")
+ private Long id;
+
+ @Schema(description = "门店名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
+ private String name;
+
+ @Schema(description = "门店手机", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601892312")
+ private String phone;
+
+ @Schema(description = "区域编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18733")
+ private Integer areaId;
+
+ @Schema(description = "区域名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xx市")
+ private String areaName;
+
+ @Schema(description = "门店详细地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "复旦大学路 188 号")
+ private String detailAddress;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.java
index 62ef72597..ddc7c1dd5 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.java
@@ -5,10 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
-import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
-import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO;
-import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO;
-import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
+import cn.iocoder.yudao.module.trade.controller.admin.order.vo.*;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
@@ -28,7 +25,6 @@ import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 交易订单")
@RestController
@@ -71,19 +67,56 @@ public class TradeOrderController {
public CommonResult getOrderDetail(@RequestParam("id") Long id) {
// 查询订单
TradeOrderDO order = tradeOrderQueryService.getOrder(id);
+ if (order == null) {
+ return success(null);
+ }
+
// 查询订单项
List orderItems = tradeOrderQueryService.getOrderItemListByOrderId(id);
-
+ // orderLog
// 拼接数据
MemberUserRespDTO user = memberUserApi.getUser(order.getUserId());
return success(TradeOrderConvert.INSTANCE.convert(order, orderItems, user));
}
- @PostMapping("/delivery")
- @Operation(summary = "发货订单")
- @PreAuthorize("@ss.hasPermission('trade:order:delivery')")
+ @GetMapping("/get-express-track-list")
+ @Operation(summary = "获得交易订单的物流轨迹")
+ @Parameter(name = "id", description = "交易订单编号")
+ @PreAuthorize("@ss.hasPermission('trade:order:query')")
+ public CommonResult> getOrderExpressTrackList(@RequestParam("id") Long id) {
+ return success(TradeOrderConvert.INSTANCE.convertList02(
+ tradeOrderQueryService.getExpressTrackList(id)));
+ }
+
+ @PutMapping("/delivery")
+ @Operation(summary = "订单发货")
+ @PreAuthorize("@ss.hasPermission('trade:order:update')")
public CommonResult deliveryOrder(@RequestBody TradeOrderDeliveryReqVO deliveryReqVO) {
- tradeOrderUpdateService.deliveryOrder(getLoginUserId(), deliveryReqVO);
+ tradeOrderUpdateService.deliveryOrder(deliveryReqVO);
+ return success(true);
+ }
+
+ @PutMapping("/update-remark")
+ @Operation(summary = "订单备注")
+ @PreAuthorize("@ss.hasPermission('trade:order:update')")
+ public CommonResult updateOrderRemark(@RequestBody TradeOrderRemarkReqVO reqVO) {
+ tradeOrderUpdateService.updateOrderRemark(reqVO);
+ return success(true);
+ }
+
+ @PutMapping("/update-price")
+ @Operation(summary = "订单调价")
+ @PreAuthorize("@ss.hasPermission('trade:order:update')")
+ public CommonResult updateOrderPrice(@RequestBody TradeOrderUpdatePriceReqVO reqVO) {
+ tradeOrderUpdateService.updateOrderPrice(reqVO);
+ return success(true);
+ }
+
+ @PutMapping("/update-address")
+ @Operation(summary = "修改订单收货地址")
+ @PreAuthorize("@ss.hasPermission('trade:order:update')")
+ public CommonResult updateOrderAddress(@RequestBody TradeOrderUpdateAddressReqVO reqVO) {
+ tradeOrderUpdateService.updateOrderAddress(reqVO);
return success(true);
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderBaseVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderBaseVO.java
index 3fa6ca8ab..7d05a0cfe 100755
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderBaseVO.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderBaseVO.java
@@ -6,9 +6,9 @@ import lombok.Data;
import java.time.LocalDateTime;
/**
-* 交易订单 Base VO,提供给添加、修改、详细的子 VO 使用
-* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
-*/
+ * 交易订单 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
@Data
public class TradeOrderBaseVO {
@@ -87,6 +87,12 @@ public class TradeOrderBaseVO {
// ========== 收件 + 物流基本信息 ==========
+ @Schema(description = "配送方式", example = "10")
+ private Integer deliveryType;
+
+ @Schema(description = "自提门店", example = "10")
+ private Long pickUpStoreId;
+
@Schema(description = "配送模板编号", example = "1024")
private Long deliveryTemplateId;
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java
index a14c91390..791c4088e 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java
@@ -1,11 +1,8 @@
package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
-import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 订单发货 Request VO")
@@ -16,18 +13,11 @@ public class TradeOrderDeliveryReqVO {
@NotNull(message = "订单编号不能为空")
private Long id;
- // TODO @puhui999:可以去掉 type;如果无需发货,则 logisticsId 传递 0;logisticsNo 传递空串
- @Schema(description = "发货类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
- @InEnum(DeliveryTypeEnum.class)
- @NotNull(message = "发货类型不能为空")
- private Integer type;
-
@Schema(description = "发货物流公司编号", example = "1")
@NotNull(message = "发货物流公司不能为空")
private Long logisticsId;
@Schema(description = "发货物流单号", example = "SF123456789")
- @NotEmpty(message = "发货物流单号不能为空")
private String logisticsNo;
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDetailRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDetailRespVO.java
index ae135c95e..4ba8ed4f8 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDetailRespVO.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDetailRespVO.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.Prod
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 交易订单的详情 Response VO")
@@ -24,6 +25,26 @@ public class TradeOrderDetailRespVO extends TradeOrderBaseVO {
*/
private MemberUserRespVO user;
+ /**
+ * TODO 订单操作日志, 先模拟一波
+ */
+ private List logs;
+
+ @Schema(description = "管理后台 - 交易订单的操作日志")
+ @Data
+ public static class OrderLog {
+
+ @Schema(description = "操作详情", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单发货")
+ private String content;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2023-06-01 10:50:20")
+ private LocalDateTime createTime;
+
+ @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Integer userType;
+
+ }
+
@Schema(description = "管理后台 - 交易订单的详情的订单项目")
@Data
public static class Item extends TradeOrderItemBaseVO {
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderPageReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderPageReqVO.java
index 9b0b577cf..dca688588 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderPageReqVO.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderPageReqVO.java
@@ -10,6 +10,7 @@ import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
+import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@@ -30,12 +31,14 @@ public class TradeOrderPageReqVO extends PageParam {
@Mobile
private String userMobile;
- @Schema(description = "收件人名称", example = "小红")
- private String receiverName;
+ @Schema(description = "配送方式", example = "1")
+ private Integer deliveryType;
- @Schema(description = "收件人手机", example = "1560")
- @Mobile
- private String receiverMobile;
+ @Schema(description = "发货物流公司编号", example = "1")
+ private Long logisticsId;
+
+ @Schema(description = "自提门店编号", example = "[1,2]")
+ private List pickUpStoreIds;
@Schema(description = "订单类型", example = "1")
private Integer type;
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderRemarkReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderRemarkReqVO.java
new file mode 100644
index 000000000..4ef8da40e
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderRemarkReqVO.java
@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 订单备注 Request VO")
+@Data
+public class TradeOrderRemarkReqVO {
+
+ @Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @NotNull(message = "订单编号不能为空")
+ private Long id;
+
+ @Schema(description = "商家备注", example = "你猜一下")
+ @NotEmpty(message = "订单备注不能为空")
+ private String remark;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderUpdateAddressReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderUpdateAddressReqVO.java
new file mode 100644
index 000000000..b66216b46
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderUpdateAddressReqVO.java
@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 订单修改地址 Request VO")
+@Data
+public class TradeOrderUpdateAddressReqVO {
+
+ @Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "订单编号不能为空")
+ private Long id;
+
+ @Schema(description = "收件人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "z张三")
+ @NotEmpty(message = "收件人名称不能为空")
+ private String receiverName;
+
+ @Schema(description = "收件人手机", requiredMode = Schema.RequiredMode.REQUIRED, example = "19988188888")
+ @NotEmpty(message = "收件人手机不能为空")
+ private String receiverMobile;
+
+ @Schema(description = "收件人地区编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7310")
+ @NotNull(message = "收件人地区编号不能为空")
+ private Integer receiverAreaId;
+
+ @Schema(description = "收件人详细地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "昆明市五华区xxx小区xxx")
+ @NotEmpty(message = "收件人详细地址不能为空")
+ private String receiverDetailAddress;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderUpdatePriceReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderUpdatePriceReqVO.java
new file mode 100644
index 000000000..d3e0afb7c
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderUpdatePriceReqVO.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 订单改价 Request VO")
+@Data
+public class TradeOrderUpdatePriceReqVO {
+
+ @Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @NotNull(message = "订单编号不能为空")
+ private Long id;
+
+ @Schema(description = "订单调价,单位:分。正数,加价;负数,减价", requiredMode = Schema.RequiredMode.REQUIRED, example = "-100")
+ @NotNull(message = "订单调价价格不能为空")
+ private Integer adjustPrice;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppTradeAfterSaleController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppTradeAfterSaleController.java
index 6ff27f332..ef3e75c7d 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppTradeAfterSaleController.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppTradeAfterSaleController.java
@@ -8,8 +8,10 @@ import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSa
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleRespVO;
import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert;
import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleOperateTypeEnum;
+import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.annotations.AfterSaleLog;
+import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.util.AfterSaleLogUtils;
import cn.iocoder.yudao.module.trade.service.aftersale.TradeAfterSaleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -50,6 +52,12 @@ public class AppTradeAfterSaleController {
return success(TradeAfterSaleConvert.INSTANCE.convert(afterSaleService.getAfterSale(getLoginUserId(), id)));
}
+ @GetMapping(value = "/get-applying-count")
+ @Operation(summary = "获得进行中的售后订单数量")
+ public CommonResult getApplyingAfterSaleCount() {
+ return success(afterSaleService.getApplyingAfterSaleCount(getLoginUserId()));
+ }
+
// TODO 芋艿:待实现
@GetMapping(value = "/get-reason-list")
@Operation(summary = "获得售后原因")
@@ -65,6 +73,8 @@ public class AppTradeAfterSaleController {
@Operation(summary = "申请售后")
@AfterSaleLog(id = "#info.data", content = "'申请售后:售后编号['+#info.data+'],订单编号['+#createReqVO.orderItemId+'], '", operateType = AfterSaleOperateTypeEnum.APPLY)
public CommonResult createAfterSale(@RequestBody AppTradeAfterSaleCreateReqVO createReqVO) {
+ AfterSaleLogUtils.setBeforeStatus(0);
+ AfterSaleLogUtils.setAfterStatus(TradeAfterSaleStatusEnum.APPLY.getStatus());
return success(afterSaleService.createAfterSale(getLoginUserId(), createReqVO));
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageRecordController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageRecordController.java
new file mode 100644
index 000000000..66fb2994a
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageRecordController.java
@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
+import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO;
+import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO;
+import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordRespVO;
+import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+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.annotation.Resource;
+import javax.validation.Valid;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
+import static java.util.Arrays.asList;
+
+@Tag(name = "用户 APP - 分销用户")
+@RestController
+@RequestMapping("/trade/brokerage-record")
+@Validated
+@Slf4j
+public class AppBrokerageRecordController {
+ @Resource
+ private BrokerageUserService brokerageUserService;
+
+ // TODO 芋艿:临时 mock =>
+ @GetMapping("/page")
+ @Operation(summary = "获得分销记录分页")
+ @PreAuthenticated
+ public CommonResult> getBrokerageRecordPage(@Valid AppBrokerageRecordPageReqVO pageReqVO) {
+ AppBrokerageRecordRespVO vo1 = new AppBrokerageRecordRespVO()
+ .setId(1L).setPrice(10).setTitle("收到钱").setCreateTime(LocalDateTime.now())
+ .setFinishTime(LocalDateTime.now());
+ AppBrokerageRecordRespVO vo2 = new AppBrokerageRecordRespVO()
+ .setId(2L).setPrice(-20).setTitle("提现钱").setCreateTime(LocalDateTime.now())
+ .setFinishTime(LocalDateTime.now());
+ return success(new PageResult<>(asList(vo1, vo2), 10L));
+ }
+
+ @GetMapping("/get-product-brokerage-price")
+ @Operation(summary = "获得商品的分销金额")
+ public CommonResult getProductBrokeragePrice(@RequestParam("spuId") Long spuId) {
+ AppBrokerageProductPriceRespVO respVO = new AppBrokerageProductPriceRespVO();
+ respVO.setEnabled(brokerageUserService.getUserBrokerageEnabled(getLoginUserId()));
+ respVO.setBrokerageMinPrice(1);
+ respVO.setBrokerageMaxPrice(2);
+ return success(respVO);
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageUserController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageUserController.java
new file mode 100644
index 000000000..208ecee2e
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageUserController.java
@@ -0,0 +1,134 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
+import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.*;
+import cn.iocoder.yudao.module.trade.service.brokerage.user.BrokerageUserService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+import static java.util.Arrays.asList;
+
+@Tag(name = "用户 APP - 分销用户")
+@RestController
+@RequestMapping("/trade/brokerage-user")
+@Validated
+@Slf4j
+public class AppBrokerageUserController {
+ @Resource
+ private BrokerageUserService brokerageUserService;
+
+ // TODO 芋艿:临时 mock =>
+ @GetMapping("/get")
+ @Operation(summary = "获得个人分销信息")
+ @PreAuthenticated
+ public CommonResult getBrokerageUser() {
+ AppBrokerageUserRespVO respVO = new AppBrokerageUserRespVO()
+ .setBrokerageEnabled(true)
+ .setPrice(2000)
+ .setFrozenPrice(3000);
+ return success(respVO);
+ }
+
+ @PutMapping("/bind")
+ @Operation(summary = "绑定推广员")
+ @PreAuthenticated
+ public CommonResult bindBrokerageUser(@Valid @RequestBody AppBrokerageUserBindReqVO reqVO) {
+ return success(brokerageUserService.bindBrokerageUser(getLoginUserId(), reqVO.getBindUserId(), false));
+ }
+
+ // TODO 芋艿:临时 mock =>
+ @GetMapping("/get-summary")
+ @Operation(summary = "获得个人分销统计")
+ @PreAuthenticated
+ public CommonResult getBrokerageUserSummary() {
+ AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO()
+ .setYesterdayPrice(1)
+ .setBrokeragePrice(2)
+ .setFrozenPrice(3)
+ .setWithdrawPrice(4)
+ .setFirstBrokerageUserCount(166)
+ .setSecondBrokerageUserCount(233);
+ return success(respVO);
+ }
+
+ // TODO 芋艿:临时 mock =>
+ @GetMapping("/rank-page-by-user-count")
+ @Operation(summary = "获得分销用户排行分页(基于用户量)")
+ @PreAuthenticated
+ public CommonResult> getBrokerageUserRankPageByUserCount(AppBrokerageUserRankPageReqVO pageReqVO) {
+ AppBrokerageUserRankByUserCountRespVO vo1 = new AppBrokerageUserRankByUserCountRespVO()
+ .setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
+ .setBrokerageUserCount(10);
+ AppBrokerageUserRankByUserCountRespVO vo2 = new AppBrokerageUserRankByUserCountRespVO()
+ .setId(2L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
+ .setBrokerageUserCount(6);
+ AppBrokerageUserRankByUserCountRespVO vo3 = new AppBrokerageUserRankByUserCountRespVO()
+ .setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
+ .setBrokerageUserCount(4);
+ AppBrokerageUserRankByUserCountRespVO vo4 = new AppBrokerageUserRankByUserCountRespVO()
+ .setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
+ .setBrokerageUserCount(4);
+ return success(new PageResult<>(asList(vo1, vo2, vo3, vo4), 10L));
+ }
+
+ // TODO 芋艿:临时 mock =>
+ @GetMapping("/rank-page-by-price")
+ @Operation(summary = "获得分销用户排行分页(基于佣金)")
+ @PreAuthenticated
+ public CommonResult> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) {
+ AppBrokerageUserRankByPriceRespVO vo1 = new AppBrokerageUserRankByPriceRespVO()
+ .setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
+ .setBrokeragePrice(10);
+ AppBrokerageUserRankByPriceRespVO vo2 = new AppBrokerageUserRankByPriceRespVO()
+ .setId(2L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
+ .setBrokeragePrice(6);
+ AppBrokerageUserRankByPriceRespVO vo3 = new AppBrokerageUserRankByPriceRespVO()
+ .setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
+ .setBrokeragePrice(4);
+ AppBrokerageUserRankByPriceRespVO vo4 = new AppBrokerageUserRankByPriceRespVO()
+ .setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
+ .setBrokeragePrice(4);
+ return success(new PageResult<>(asList(vo1, vo2, vo3, vo4), 10L));
+ }
+
+ // TODO 芋艿:临时 mock =>
+ @GetMapping("/child-summary-page")
+ @Operation(summary = "获得下级分销统计分页")
+ @PreAuthenticated
+ public CommonResult> getBrokerageUserChildSummaryPage(
+ AppBrokerageUserChildSummaryPageReqVO pageReqVO) {
+ AppBrokerageUserChildSummaryRespVO vo1 = new AppBrokerageUserChildSummaryRespVO()
+ .setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
+ .setBrokeragePrice(10).setBrokeragePrice(20).setBrokerageOrderCount(30)
+ .setBrokerageTime(LocalDateTime.now());
+ AppBrokerageUserChildSummaryRespVO vo2 = new AppBrokerageUserChildSummaryRespVO()
+ .setId(1L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
+ .setBrokeragePrice(20).setBrokeragePrice(30).setBrokerageOrderCount(40)
+ .setBrokerageTime(LocalDateTime.now());
+ return success(new PageResult<>(asList(vo1, vo2), 10L));
+ }
+
+ // TODO 芋艿:临时 mock =>
+ @GetMapping("/get-rank-by-price")
+ @Operation(summary = "获得分销用户排行(基于佣金)")
+ @Parameter(name = "times", description = "时间段", required = true)
+ public CommonResult bindBrokerageUser(
+ @RequestParam("times") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) LocalDateTime[] times) {
+ return success(1);
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageWithdrawController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageWithdrawController.java
new file mode 100644
index 000000000..1f6d383ea
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageWithdrawController.java
@@ -0,0 +1,47 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
+import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO;
+import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawRespVO;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static java.util.Arrays.asList;
+
+@Tag(name = "用户 APP - 分销提现")
+@RestController
+@RequestMapping("/trade/brokerage-withdraw")
+@Validated
+@Slf4j
+public class AppBrokerageWithdrawController {
+
+ // TODO 芋艿:临时 mock =>
+ @GetMapping("/page")
+ @Operation(summary = "获得分销提现分页")
+ @PreAuthenticated
+ public CommonResult> getBrokerageWithdrawPage() {
+ AppBrokerageWithdrawRespVO vo1 = new AppBrokerageWithdrawRespVO()
+ .setId(1L).setStatus(10).setPrice(10).setStatusName("审批通过").setCreateTime(LocalDateTime.now());
+ AppBrokerageWithdrawRespVO vo2 = new AppBrokerageWithdrawRespVO()
+ .setId(2L).setStatus(0).setPrice(20).setStatusName("审批中").setCreateTime(LocalDateTime.now());
+ return success(new PageResult<>(asList(vo1, vo2), 10L));
+ }
+
+ // TODO 芋艿:临时 mock =>
+ @PostMapping("/create")
+ @Operation(summary = "创建分销提现")
+ @PreAuthenticated
+ public CommonResult createBrokerageWithdraw(@RequestBody @Valid AppBrokerageWithdrawCreateReqVO createReqVO) {
+ return success(1L);
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageProductPriceRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageProductPriceRespVO.java
new file mode 100644
index 000000000..6b2191d5f
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageProductPriceRespVO.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "用户 App - 商品的分销金额 Response VO")
+@Data
+public class AppBrokerageProductPriceRespVO {
+
+ @Schema(description = "是否开启", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Boolean enabled;
+
+ @Schema(description = "分销最小金额,单位:分", example = "100")
+ private Integer brokerageMinPrice;
+
+ @Schema(description = "分销最大金额,单位:分", example = "100")
+ private Integer brokerageMaxPrice;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageRecordPageReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageRecordPageReqVO.java
new file mode 100644
index 000000000..e2df6dae6
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageRecordPageReqVO.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
+import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "应用 App - 分销记录分页 Request VO")
+@Data
+public class AppBrokerageRecordPageReqVO extends PageParam {
+
+ @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @InEnum(value = BrokerageRecordBizTypeEnum.class, message = "业务类型必须是 {value}")
+ private Integer bizType;
+
+ @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @InEnum(value = BrokerageRecordStatusEnum.class, message = "状态必须是 {value}")
+ private Integer status;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageRecordRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageRecordRespVO.java
new file mode 100644
index 000000000..221d19758
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageRecordRespVO.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "用户 App - 分销记录 Response VO")
+@Data
+public class AppBrokerageRecordRespVO {
+
+ @Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Long id;
+
+ @Schema(description = "分销标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "用户下单")
+ private String title;
+
+ @Schema(description = "分销金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
+ private Integer price;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+ @Schema(description = "完成时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime finishTime;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserBindReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserBindReqVO.java
new file mode 100644
index 000000000..f2a14996a
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserBindReqVO.java
@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "应用 App - 绑定推广员 Request VO")
+@Data
+public class AppBrokerageUserBindReqVO extends PageParam {
+
+ @Schema(description = "推广员编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @NotNull(message = "推广员编号不能为空")
+ private Long bindUserId;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryPageReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryPageReqVO.java
new file mode 100644
index 000000000..066fc0912
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryPageReqVO.java
@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.SortingField;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "用户 App - 下级分销统计分页 Request VO")
+@Data
+public class AppBrokerageUserChildSummaryPageReqVO extends PageParam {
+
+ public static final String SORT_FIELD_USER_COUNT = "userCount";
+ public static final String SORT_FIELD_ORDER_COUNT = "orderCount";
+ public static final String SORT_FIELD_PRICE = "price";
+
+ @Schema(description = "用户昵称", example = "李") // 模糊匹配
+ private String nickname;
+
+ @Schema(description = "排序字段", example = "userCount")
+ private SortingField sortingField;
+
+ @Schema(description = "下级的级别", example = "1") // 1 - 直接下级;2 - 间接下级
+ private Integer level;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryRespVO.java
new file mode 100644
index 000000000..1beb1b5e2
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryRespVO.java
@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "用户 App - 下级分销统计 Response VO")
+@Data
+public class AppBrokerageUserChildSummaryRespVO {
+
+ @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Long id;
+
+ @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小王")
+ private String nickname;
+
+ @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
+ private String avatar;
+
+ @Schema(description = "佣金金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Integer brokeragePrice;
+
+ @Schema(description = "分销订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
+ private Integer brokerageOrderCount;
+
+ @Schema(description = "分销用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "30")
+ private Integer brokerageUserCount;
+
+ @Schema(description = "成为分销员时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime brokerageTime;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserMySummaryRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserMySummaryRespVO.java
new file mode 100644
index 000000000..cc9a03ebc
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserMySummaryRespVO.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "用户 App - 个人分销统计 Response VO")
+@Data
+public class AppBrokerageUserMySummaryRespVO {
+
+ @Schema(description = "昨天的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Integer yesterdayPrice;
+
+ @Schema(description = "提现的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Integer withdrawPrice;
+
+ @Schema(description = "可用的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408")
+ private Integer brokeragePrice;
+
+ @Schema(description = "冻结的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "234")
+ private Integer frozenPrice;
+
+ @Schema(description = "分销用户数量(一级)", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Integer firstBrokerageUserCount;
+
+ @Schema(description = "分销用户数量(二级)", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Integer secondBrokerageUserCount;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByPriceRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByPriceRespVO.java
new file mode 100644
index 000000000..91345ea78
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByPriceRespVO.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "用户 App - 分销排行用户(基于用户量) Response VO")
+@Data
+public class AppBrokerageUserRankByPriceRespVO {
+
+ @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Long id;
+
+ @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小王")
+ private String nickname;
+
+ @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
+ private String avatar;
+
+ @Schema(description = "佣金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Integer brokeragePrice;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByUserCountRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByUserCountRespVO.java
new file mode 100644
index 000000000..1a6de8138
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByUserCountRespVO.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "用户 App - 分销排行用户(基于用户量) Response VO")
+@Data
+public class AppBrokerageUserRankByUserCountRespVO {
+
+ @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Long id;
+
+ @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小王")
+ private String nickname;
+
+ @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
+ private String avatar;
+
+ @Schema(description = "邀请用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Integer brokerageUserCount;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankPageReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankPageReqVO.java
new file mode 100644
index 000000000..de1d61a7b
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankPageReqVO.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotEmpty;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "应用 App - 分销用户排行 Request VO")
+@Data
+public class AppBrokerageUserRankPageReqVO extends PageParam {
+
+ @Schema(description = "开始 + 结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ @NotEmpty(message = "时间不能为空")
+ private LocalDateTime[] times;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRespVO.java
new file mode 100644
index 000000000..40b70bed2
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRespVO.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "用户 App - 分销用户信息 Response VO")
+@Data
+public class AppBrokerageUserRespVO {
+
+ @Schema(description = "是否有分销资格", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ private Boolean brokerageEnabled;
+
+ @Schema(description = "可用的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408")
+ private Integer price;
+
+ @Schema(description = "冻结的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "234")
+ private Integer frozenPrice;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java
new file mode 100644
index 000000000..a8b1523b2
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java
@@ -0,0 +1,73 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw;
+
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.URL;
+
+import javax.validation.Validator;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+
+@Schema(description = "用户 App - 分销提现创建 Request VO")
+@Data
+public class AppBrokerageWithdrawCreateReqVO {
+
+ @Schema(description = "提现方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @InEnum(value = BrokerageWithdrawTypeEnum.class, message = "提现方式必须是 {value}")
+ private Integer type;
+
+ @Schema(description = "提现金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
+ @Min(value = 1, message = "提现金额不能小于 1")
+ private Integer price;
+
+ // ========== 银行卡、微信、支付宝 提现相关字段 ==========
+
+ @Schema(description = "提现账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456789")
+ @NotBlank(message = "提现账号不能为空", groups = {Bank.class, Wechat.class, Alipay.class})
+ private String accountNo;
+
+ // ========== 微信、支付宝 提现相关字段 ==========
+
+ @Schema(description = "收款码的图片", example = "https://www.iocoder.cn/1.png")
+ @URL(message = "收款码的图片,必须是一个 URL")
+ private String accountQrCodeUrl;
+
+ // ========== 银行卡 提现相关字段 ==========
+
+ @Schema(description = "持卡人姓名", example = "张三")
+ @NotBlank(message = "持卡人姓名不能为空", groups = {Bank.class})
+ private String name;
+ @Schema(description = "提现银行", example = "1")
+ @NotBlank(message = "提现银行不能为空", groups = {Bank.class})
+ private Integer bankName;
+ @Schema(description = "开户地址", example = "海淀支行")
+ private String bankAddress;
+
+ public interface Wallet {
+ }
+
+ public interface Bank {
+ }
+
+ public interface Wechat {
+ }
+
+ public interface Alipay {
+ }
+
+ public void validate(Validator validator) {
+ if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(type)) {
+ ValidationUtils.validate(validator, this, Wallet.class);
+ } else if (BrokerageWithdrawTypeEnum.BANK.getType().equals(type)) {
+ ValidationUtils.validate(validator, this, Bank.class);
+ } else if (BrokerageWithdrawTypeEnum.WECHAT.getType().equals(type)) {
+ ValidationUtils.validate(validator, this, Wechat.class);
+ } else if (BrokerageWithdrawTypeEnum.ALIPAY.getType().equals(type)) {
+ ValidationUtils.validate(validator, this, Alipay.class);
+ }
+ }
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawRespVO.java
new file mode 100644
index 000000000..4cfe930c8
--- /dev/null
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawRespVO.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "用户 App - 分销提现 Response VO")
+@Data
+public class AppBrokerageWithdrawRespVO {
+
+ @Schema(description = "提现编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Long id;
+
+ @Schema(description = "提现状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ private Integer status;
+
+ @Schema(description = "提现状态名", requiredMode = Schema.RequiredMode.REQUIRED, example = "审核中")
+ private String statusName;
+
+ @Schema(description = "提现金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
+ private Integer price;
+
+ @Schema(description = "提现时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.http b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/AppCartController.http
similarity index 100%
rename from yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.http
rename to yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/AppCartController.http
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/AppCartController.java
similarity index 65%
rename from yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.java
rename to yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/AppCartController.java
index 9216773ad..2c9d77b52 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/AppCartController.java
@@ -2,11 +2,8 @@ package cn.iocoder.yudao.module.trade.controller.app.cart;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
-import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO;
-import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
-import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartResetReqVO;
-import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartUpdateReqVO;
-import cn.iocoder.yudao.module.trade.service.cart.TradeCartService;
+import cn.iocoder.yudao.module.trade.controller.app.cart.vo.*;
+import cn.iocoder.yudao.module.trade.service.cart.CartService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -18,7 +15,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
-import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@@ -29,30 +25,38 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
@RequiredArgsConstructor
@Validated
@Slf4j
-public class TradeCartController {
+public class AppCartController {
@Resource
- private TradeCartService cartService;
+ private CartService cartService;
@PostMapping("/add")
@Operation(summary = "添加购物车商品")
@PreAuthenticated
- public CommonResult addCart(@Valid @RequestBody AppTradeCartAddReqVO addCountReqVO) {
+ public CommonResult addCart(@Valid @RequestBody AppCartAddReqVO addCountReqVO) {
return success(cartService.addCart(getLoginUserId(), addCountReqVO));
}
- @PutMapping("/update")
- @Operation(summary = "更新购物车商品")
+ @PutMapping("/update-count")
+ @Operation(summary = "更新购物车商品数量")
@PreAuthenticated
- public CommonResult updateCart(@Valid @RequestBody AppTradeCartUpdateReqVO updateReqVO) {
- cartService.updateCart(getLoginUserId(), updateReqVO);
+ public CommonResult updateCartCount(@Valid @RequestBody AppCartUpdateCountReqVO updateReqVO) {
+ cartService.updateCartCount(getLoginUserId(), updateReqVO);
+ return success(true);
+ }
+
+ @PutMapping("/update-selected")
+ @Operation(summary = "更新购物车商品选中")
+ @PreAuthenticated
+ public CommonResult updateCartSelected(@Valid @RequestBody AppCartUpdateSelectedReqVO updateReqVO) {
+ cartService.updateCartSelected(getLoginUserId(), updateReqVO);
return success(true);
}
@PutMapping("/reset")
@Operation(summary = "重置购物车商品")
@PreAuthenticated
- public CommonResult resetCart(@Valid @RequestBody AppTradeCartResetReqVO updateReqVO) {
+ public CommonResult resetCart(@Valid @RequestBody AppCartResetReqVO updateReqVO) {
cartService.resetCart(getLoginUserId(), updateReqVO);
return success(true);
}
@@ -73,17 +77,10 @@ public class TradeCartController {
return success(cartService.getCartCount(getLoginUserId()));
}
- @GetMapping("get-count-map")
- @Operation(summary = "查询用户在购物车中的商品 SPU 数量 Map")
- @PreAuthenticated
- public CommonResult