mirror of
https://gitee.com/zhijiantianya/ruoyi-vue-pro.git
synced 2026-03-22 05:07:17 +08:00
Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro
# Conflicts: # yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendSingleToUserReqDTO.java # yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java # yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java # yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java # yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java
This commit is contained in:
@@ -19,7 +19,6 @@ 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.operators.conditional.OrExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
|
||||
@@ -60,8 +59,6 @@ public class DeptDataPermissionRule implements DataPermissionRule {
|
||||
private static final String DEPT_COLUMN_NAME = "dept_id";
|
||||
private static final String USER_COLUMN_NAME = "user_id";
|
||||
|
||||
static final Expression EXPRESSION_NULL = new NullValue();
|
||||
|
||||
private final PermissionCommonApi permissionApi;
|
||||
|
||||
/**
|
||||
@@ -136,7 +133,7 @@ public class DeptDataPermissionRule implements DataPermissionRule {
|
||||
JsonUtils.toJsonString(loginUser), tableName, tableAlias, JsonUtils.toJsonString(deptDataPermission));
|
||||
// throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 构建的条件为空",
|
||||
// loginUser.getId(), tableName, tableAlias.getName()));
|
||||
return EXPRESSION_NULL;
|
||||
return new EqualsTo(null, null); // WHERE null = null,可以保证返回的数据为空
|
||||
}
|
||||
if (deptExpression == null) {
|
||||
return userExpression;
|
||||
|
||||
@@ -19,7 +19,6 @@ import org.mockito.MockedStatic;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataPermissionRule.EXPRESSION_NULL;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
@@ -150,7 +149,7 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
|
||||
// 调用
|
||||
Expression expression = rule.getExpression(tableName, tableAlias);
|
||||
// 断言
|
||||
assertSame(EXPRESSION_NULL, expression);
|
||||
assertEquals("null = null", expression.toString());
|
||||
assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +204,12 @@
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations-jakarta</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- 客户端 -->
|
||||
|
||||
@@ -27,7 +27,7 @@ import java.util.List;
|
||||
* @since 2024/4/14 17:35
|
||||
*/
|
||||
@TableName(value = "ai_chat_message", autoResultMap = true)
|
||||
@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@KeySequence("ai_chat_message_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
|
||||
@@ -14,7 +14,7 @@ import lombok.*;
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("ai_api_key")
|
||||
@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@KeySequence("ai_api_key_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
|
||||
@@ -51,8 +51,7 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegment
|
||||
MPJLambdaWrapper<AiKnowledgeSegmentDO> wrapper = new MPJLambdaWrapperX<AiKnowledgeSegmentDO>()
|
||||
.selectAs(AiKnowledgeSegmentDO::getDocumentId, AiKnowledgeSegmentProcessRespVO::getDocumentId)
|
||||
.selectCount(AiKnowledgeSegmentDO::getId, "count")
|
||||
.select("COUNT(CASE WHEN vector_id > '" + AiKnowledgeSegmentDO.VECTOR_ID_EMPTY
|
||||
+ "' THEN 1 ELSE NULL END) AS embeddingCount")
|
||||
.select("COUNT(CASE WHEN vector_id IS NOT NULL AND vector_id <> '" + AiKnowledgeSegmentDO.VECTOR_ID_EMPTY + "' THEN 1 ELSE NULL END) AS embeddingCount")
|
||||
.in(AiKnowledgeSegmentDO::getDocumentId, documentIds)
|
||||
.groupBy(AiKnowledgeSegmentDO::getDocumentId);
|
||||
return selectJoinList(AiKnowledgeSegmentProcessRespVO.class, wrapper);
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
|
||||
import org.flowable.common.engine.api.delegate.Expression;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -56,14 +57,7 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
|
||||
protected int resolveNrOfInstances(DelegateExecution execution) {
|
||||
// 情况一:UserTask 节点
|
||||
if (execution.getCurrentFlowElement() instanceof UserTask) {
|
||||
// 第一步,设置 collectionVariable 和 CollectionVariable
|
||||
// 从 execution.getVariable() 读取所有任务处理人的 key
|
||||
super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的
|
||||
super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
|
||||
// 从 execution.getVariable() 读取当前所有任务处理的人的 key
|
||||
super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
|
||||
|
||||
// 第二步,获取任务的所有处理人
|
||||
// 获取任务的所有处理人
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);
|
||||
if (assigneeUserIds == null) {
|
||||
@@ -94,4 +88,21 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
|
||||
return super.resolveNrOfInstances(execution);
|
||||
}
|
||||
|
||||
// ========== 屏蔽解析器覆写 ==========
|
||||
|
||||
@Override
|
||||
public void setCollectionExpression(Expression collectionExpression) {
|
||||
// 保持自定义变量名,忽略解析器写入的 collection 表达式
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCollectionVariable(String collectionVariable) {
|
||||
// 保持自定义变量名,忽略解析器写入的 collection 变量名
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCollectionElementVariable(String collectionElementVariable) {
|
||||
// 保持自定义变量名,忽略解析器写入的单元素变量名
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,14 +47,7 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB
|
||||
protected int resolveNrOfInstances(DelegateExecution execution) {
|
||||
// 情况一:UserTask 节点
|
||||
if (execution.getCurrentFlowElement() instanceof UserTask) {
|
||||
// 第一步,设置 collectionVariable 和 CollectionVariable
|
||||
// 从 execution.getVariable() 读取所有任务处理人的 key
|
||||
super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的
|
||||
super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
|
||||
// 从 execution.getVariable() 读取当前所有任务处理的人的 key
|
||||
super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
|
||||
|
||||
// 第二步,获取任务的所有处理人
|
||||
// 获取任务的所有处理人
|
||||
// 不使用 execution.getVariable 原因:目前依次审批任务回退后 collectionVariable 变量没有清理, 如果重新进入该任务不会重新分配审批人
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<Long> assigneeUserIds = (Set<Long>) execution.getVariableLocal(super.collectionVariable, Set.class);
|
||||
|
||||
@@ -31,14 +31,14 @@ public interface ErpFinancePaymentItemMapper extends BaseMapperX<ErpFinancePayme
|
||||
default BigDecimal selectPaymentPriceSumByBizIdAndBizType(Long bizId, Integer bizType) {
|
||||
// SQL sum 查询
|
||||
List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpFinancePaymentItemDO>()
|
||||
.select("SUM(payment_price) AS paymentPriceSum")
|
||||
.select("SUM(payment_price) AS payment_price_sum")
|
||||
.eq("biz_id", bizId)
|
||||
.eq("biz_type", bizType));
|
||||
// 获得数量
|
||||
if (CollUtil.isEmpty(result)) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), "paymentPriceSum", 0D));
|
||||
return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), "payment_price_sum", 0D));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,14 +31,14 @@ public interface ErpFinanceReceiptItemMapper extends BaseMapperX<ErpFinanceRecei
|
||||
default BigDecimal selectReceiptPriceSumByBizIdAndBizType(Long bizId, Integer bizType) {
|
||||
// SQL sum 查询
|
||||
List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpFinanceReceiptItemDO>()
|
||||
.select("SUM(receipt_price) AS receiptPriceSum")
|
||||
.select("SUM(receipt_price) AS receipt_price_sum")
|
||||
.eq("biz_id", bizId)
|
||||
.eq("biz_type", bizType));
|
||||
// 获得数量
|
||||
if (CollUtil.isEmpty(result)) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), "receiptPriceSum", 0D));
|
||||
return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), "receipt_price_sum", 0D));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,11 +46,11 @@ public interface ErpPurchaseInItemMapper extends BaseMapperX<ErpPurchaseInItemDO
|
||||
}
|
||||
// SQL sum 查询
|
||||
List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpPurchaseInItemDO>()
|
||||
.select("order_item_id, SUM(count) AS sumCount")
|
||||
.select("order_item_id, SUM(count) AS sum_count")
|
||||
.groupBy("order_item_id")
|
||||
.in("in_id", inIds));
|
||||
// 获得数量
|
||||
return convertMap(result, obj -> (Long) obj.get("order_item_id"), obj -> (BigDecimal) obj.get("sumCount"));
|
||||
return convertMap(result, obj -> (Long) obj.get("order_item_id"), obj -> (BigDecimal) obj.get("sum_count"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,11 +46,11 @@ public interface ErpPurchaseReturnItemMapper extends BaseMapperX<ErpPurchaseRetu
|
||||
}
|
||||
// SQL sum 查询
|
||||
List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpPurchaseReturnItemDO>()
|
||||
.select("order_item_id, SUM(count) AS sumCount")
|
||||
.select("order_item_id, SUM(count) AS sum_count")
|
||||
.groupBy("order_item_id")
|
||||
.in("return_id", returnIds));
|
||||
// 获得数量
|
||||
return convertMap(result, obj -> (Long) obj.get("order_item_id"), obj -> (BigDecimal) obj.get("sumCount"));
|
||||
return convertMap(result, obj -> (Long) obj.get("order_item_id"), obj -> (BigDecimal) obj.get("sum_count"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,11 +46,11 @@ public interface ErpSaleOutItemMapper extends BaseMapperX<ErpSaleOutItemDO> {
|
||||
}
|
||||
// SQL sum 查询
|
||||
List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpSaleOutItemDO>()
|
||||
.select("order_item_id, SUM(count) AS sumCount")
|
||||
.select("order_item_id, SUM(count) AS sum_count")
|
||||
.groupBy("order_item_id")
|
||||
.in("out_id", outIds));
|
||||
// 获得数量
|
||||
return convertMap(result, obj -> (Long) obj.get("order_item_id"), obj -> (BigDecimal) obj.get("sumCount"));
|
||||
return convertMap(result, obj -> (Long) obj.get("order_item_id"), obj -> (BigDecimal) obj.get("sum_count"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,11 +46,11 @@ public interface ErpSaleReturnItemMapper extends BaseMapperX<ErpSaleReturnItemDO
|
||||
}
|
||||
// SQL sum 查询
|
||||
List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpSaleReturnItemDO>()
|
||||
.select("order_item_id, SUM(count) AS sumCount")
|
||||
.select("order_item_id, SUM(count) AS sum_count")
|
||||
.groupBy("order_item_id")
|
||||
.in("return_id", returnIds));
|
||||
// 获得数量
|
||||
return convertMap(result, obj -> (Long) obj.get("order_item_id"), obj -> (BigDecimal) obj.get("sumCount"));
|
||||
return convertMap(result, obj -> (Long) obj.get("order_item_id"), obj -> (BigDecimal) obj.get("sum_count"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,13 +52,13 @@ public interface ErpStockMapper extends BaseMapperX<ErpStockDO> {
|
||||
default BigDecimal selectSumByProductId(Long productId) {
|
||||
// SQL sum 查询
|
||||
List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpStockDO>()
|
||||
.select("SUM(count) AS sumCount")
|
||||
.select("SUM(count) AS sum_count")
|
||||
.eq("product_id", productId));
|
||||
// 获得数量
|
||||
if (CollUtil.isEmpty(result)) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), "sumCount", 0D));
|
||||
return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), "sum_count", 0D));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,6 +29,6 @@ public class FileCreateReqVO {
|
||||
private String type;
|
||||
|
||||
@Schema(description = "文件大小", example = "2048", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer size;
|
||||
private Long size;
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class FileRespVO {
|
||||
private String type;
|
||||
|
||||
@Schema(description = "文件大小", example = "2048", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer size;
|
||||
private Long size;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@@ -52,6 +52,6 @@ public class FileDO extends BaseDO {
|
||||
/**
|
||||
* 文件大小
|
||||
*/
|
||||
private Integer size;
|
||||
private Long size;
|
||||
|
||||
}
|
||||
|
||||
@@ -59,9 +59,9 @@ public class DatabaseTableServiceImpl implements DatabaseTableService {
|
||||
strategyConfig.addInclude(name);
|
||||
} else {
|
||||
// 移除工作流和定时任务前缀的表名
|
||||
strategyConfig.addExclude("ACT_[\\S\\s]+|QRTZ_[\\S\\s]+|FLW_[\\S\\s]+");
|
||||
strategyConfig.addExclude("ACT_[\\S\\s]+|QRTZ_[\\S\\s]+|FLW_[\\S\\s]+|act_[\\S\\s]+|qrtz_[\\S\\s]+|flw_[\\S\\s]+");
|
||||
// 移除 ORACLE 相关的系统表
|
||||
strategyConfig.addExclude("IMPDP_[\\S\\s]+|ALL_[\\S\\s]+|HS_[\\S\\\\s]+");
|
||||
strategyConfig.addExclude("IMPDP_[\\S\\s]+|ALL_[\\S\\s]+|HS_[\\S\\s]+|impdp_[\\S\\s]+|all_[\\S\\s]+|hs_[\\S\\s]+");
|
||||
strategyConfig.addExclude("[\\S\\s]+\\$[\\S\\s]+|[\\S\\s]+\\$"); // 表里不能有 $,一般有都是系统的表
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ public class FileServiceImpl implements FileService {
|
||||
// 3. 保存到数据库
|
||||
fileMapper.insert(new FileDO().setConfigId(client.getId())
|
||||
.setName(name).setPath(path).setUrl(url)
|
||||
.setType(type).setSize(content.length));
|
||||
.setType(type).setSize((long) content.length));
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import { http } from '@/http/http'
|
||||
#else
|
||||
#set ($primaryTsType = "string")
|
||||
#end
|
||||
|
||||
/** ${table.classComment}信息 */
|
||||
export interface ${simpleClassName} {
|
||||
#foreach ($column in $columns)
|
||||
@@ -28,7 +27,6 @@ export interface ${simpleClassName} {
|
||||
#end
|
||||
}
|
||||
|
||||
// TODO @AI:`/system/operate-log/get?id=${id}` 类似这种
|
||||
/** 获取${table.classComment}分页列表 */
|
||||
export function get${simpleClassName}Page(params: PageParam) {
|
||||
return http.get<PageResult<${simpleClassName}>>('/${table.moduleName}/${simpleClassName_strikeCase}/page', params)
|
||||
@@ -36,7 +34,7 @@ export function get${simpleClassName}Page(params: PageParam) {
|
||||
|
||||
/** 获取${table.classComment}详情 */
|
||||
export function get${simpleClassName}(id: ${primaryTsType}) {
|
||||
return http.get<${simpleClassName}>('/${table.moduleName}/${simpleClassName_strikeCase}/get?id=' + id)
|
||||
return http.get<${simpleClassName}>(`/${table.moduleName}/${simpleClassName_strikeCase}/get?id=${id}`)
|
||||
}
|
||||
|
||||
/** 创建${table.classComment} */
|
||||
@@ -49,8 +47,7 @@ export function update${simpleClassName}(data: ${simpleClassName}) {
|
||||
return http.put<boolean>('/${table.moduleName}/${simpleClassName_strikeCase}/update', data)
|
||||
}
|
||||
|
||||
// TODO @AI:`/system/operate-log/get?id=${id}` 类似这种
|
||||
/** 删除${table.classComment} */
|
||||
export function delete${simpleClassName}(id: ${primaryTsType}) {
|
||||
return http.delete<boolean>('/${table.moduleName}/${simpleClassName_strikeCase}/delete?id=' + id)
|
||||
return http.delete<boolean>(`/${table.moduleName}/${simpleClassName_strikeCase}/delete?id=${id}`)
|
||||
}
|
||||
|
||||
@@ -294,4 +294,3 @@ function handleReset() {
|
||||
emit('reset')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'wot-design-uni/components/wd-form/types'
|
||||
#set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())
|
||||
#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
|
||||
#set ($primaryTsType = "number")
|
||||
@@ -204,7 +205,7 @@ const formRules = {
|
||||
#end
|
||||
#end
|
||||
}
|
||||
const formRef = ref()
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
|
||||
@@ -144,7 +144,7 @@ public class ProductSkuServiceImpl implements ProductSkuService {
|
||||
|
||||
@Override
|
||||
public void createSkuList(Long spuId, List<ProductSkuSaveReqVO> skuCreateReqList) {
|
||||
List<ProductSkuDO> skus = BeanUtils.toBean(skuCreateReqList, ProductSkuDO.class, sku -> sku.setSpuId(spuId));
|
||||
List<ProductSkuDO> skus = BeanUtils.toBean(skuCreateReqList, ProductSkuDO.class, sku -> sku.setSpuId(spuId).setSalesCount(0));
|
||||
productSkuMapper.insertBatch(skus);
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ public class ProductSpuServiceImpl implements ProductSpuService {
|
||||
// sku 单价最低的商品的成本价格
|
||||
spu.setCostPrice(getMinValue(skus, ProductSkuSaveReqVO::getCostPrice));
|
||||
// skus 库存总数
|
||||
spu.setStock(getSumValue(skus, ProductSkuSaveReqVO::getStock, Integer::sum));
|
||||
spu.setStock(getSumValue(skus, ProductSkuSaveReqVO::getStock, Math::addExact));
|
||||
// 若是 spu 已有状态则不处理
|
||||
if (spu.getStatus() == null) {
|
||||
spu.setStatus(ProductSpuStatusEnum.ENABLE.getStatus()); // 默认状态为上架
|
||||
|
||||
@@ -14,6 +14,7 @@ public class BrokerageUserCreateReqVO {
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "推广员编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4587")
|
||||
@NotNull(message = "推广员编号不能为空")
|
||||
private Long bindUserId;
|
||||
|
||||
}
|
||||
|
||||
@@ -265,8 +265,7 @@ public interface TradeOrderConvert {
|
||||
ProductSpuRespDTO spu, ProductSkuRespDTO sku) {
|
||||
BrokerageAddReqBO bo = new BrokerageAddReqBO().setBizId(String.valueOf(item.getId())).setSourceUserId(item.getUserId())
|
||||
.setBasePrice(item.getPayPrice())
|
||||
.setTitle(StrUtil.format("{}成功购买{}", user.getNickname(), item.getSpuName()))
|
||||
.setFirstFixedPrice(0).setSecondFixedPrice(0);
|
||||
.setTitle(StrUtil.format("{}成功购买{}", user.getNickname(), item.getSpuName()));
|
||||
if (BooleanUtil.isTrue(spu.getSubCommissionType())) {
|
||||
// 特殊:单独设置的佣金需要乘以购买数量。关联 https://gitee.com/yudaocode/yudao-mall-uniapp/issues/ICY7SJ
|
||||
bo.setFirstFixedPrice(sku.getFirstBrokeragePrice() * item.getCount())
|
||||
|
||||
@@ -65,11 +65,17 @@ public class TradeOrderLogAspect {
|
||||
public void doAfterReturning(JoinPoint joinPoint, TradeOrderLog orderLog) {
|
||||
try {
|
||||
// 1.1 操作用户
|
||||
Integer userType = getUserType();
|
||||
Long userId = getUserId();
|
||||
Integer userType = USER_TYPE.get();
|
||||
if (ObjectUtil.isNull(userType)) {
|
||||
userType = getUserType();
|
||||
}
|
||||
Long userId = USER_ID.get();
|
||||
if (ObjectUtil.isNull(userId)) {
|
||||
userId = getUserId();
|
||||
}
|
||||
// 1.2 订单信息
|
||||
Long orderId = ORDER_ID.get();
|
||||
if (orderId == null) { // 如果未设置,只有注解,说明不需要记录日志
|
||||
if (ObjectUtil.isNull(orderId)) { // 如果未设置,只有注解,说明不需要记录日志
|
||||
return;
|
||||
}
|
||||
Integer beforeStatus = BEFORE_STATUS.get();
|
||||
|
||||
@@ -142,7 +142,7 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
|
||||
*/
|
||||
int calculatePrice(Integer basePrice, Integer percent, Integer fixedPrice) {
|
||||
// 1. 优先使用固定佣金
|
||||
if (fixedPrice != null && fixedPrice > 0) {
|
||||
if (fixedPrice != null && fixedPrice >= 0) {
|
||||
return ObjectUtil.defaultIfNull(fixedPrice, 0);
|
||||
}
|
||||
// 2. 根据比例计算佣金
|
||||
|
||||
@@ -174,7 +174,11 @@ public class PayWalletServiceImpl implements PayWalletService {
|
||||
}
|
||||
|
||||
// 3. 生成钱包流水
|
||||
Integer afterBalance = payWallet.getBalance() - price;
|
||||
// 情况一:充值退款:balance 在冻结时已扣,updateWhenRechargeRefund 只扣 freeze_price,所以 afterBalance 不变。https://t.zsxq.com/OJk9m
|
||||
// 情况二:消费支付:updateWhenConsumption 从 balance 扣,所以 afterBalance = balance - price
|
||||
Integer afterBalance = bizType == PayWalletBizTypeEnum.RECHARGE_REFUND
|
||||
? payWallet.getBalance()
|
||||
: payWallet.getBalance() - price;
|
||||
WalletTransactionCreateReqBO bo = new WalletTransactionCreateReqBO().setWalletId(payWallet.getId())
|
||||
.setPrice(-price).setBalance(afterBalance).setBizId(String.valueOf(bizId))
|
||||
.setBizType(bizType.getType()).setTitle(bizType.getDescription());
|
||||
|
||||
@@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.report.framework.jmreport.core.service;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
|
||||
import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
||||
import cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
@@ -10,9 +12,6 @@ import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
|
||||
import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jeecg.modules.jmreport.api.JmReportTokenServiceI;
|
||||
@@ -158,4 +157,33 @@ public class JmReportTokenServiceImpl implements JmReportTokenServiceI {
|
||||
return StrUtil.toStringOrNull(loginUser.getTenantId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPermissions(String token) {
|
||||
// 设置租户上下文
|
||||
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
return null;
|
||||
}
|
||||
TenantContextHolder.setTenantId(loginUser.getTenantId());
|
||||
|
||||
// 参见文档 https://help.jimureport.com/prodSafe/ 文档
|
||||
// 适配:如果是本系统的管理员,则返回积木报表(仪表盘/大屏设计器)的所有权限指令
|
||||
// 如果不处理,会碰到 https://t.zsxq.com/yzlkA 反馈的问题
|
||||
Long userId = SecurityFrameworkUtils.getLoginUserId();
|
||||
if (permissionApi.hasAnyRoles(userId, RoleCodeEnum.SUPER_ADMIN.getCode())) {
|
||||
return new String[]{
|
||||
"drag:datasource:testConnection", // 数据库连接测试
|
||||
"drag:datasource:saveOrUpate", // 数据源保存
|
||||
"drag:datasource:delete", // 数据源删除
|
||||
"drag:analysis:sql", // SQL解析
|
||||
"drag:design:getTotalData", // 展示Online表单数据
|
||||
"drag:dataset:save", // 数据集保存
|
||||
"drag:dataset:delete", // 数据集删除
|
||||
"onl:drag:clear:recovery", // 清空回收站
|
||||
"onl:drag:page:delete" // 数据删除
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,14 +23,14 @@ public class MailSendApiImpl implements MailSendApi {
|
||||
public Long sendSingleMailToAdmin(MailSendSingleToUserReqDTO reqDTO) {
|
||||
return mailSendService.sendSingleMailToAdmin(reqDTO.getUserId(),
|
||||
reqDTO.getToMails(), reqDTO.getCcMails(), reqDTO.getBccMails(),
|
||||
reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
|
||||
reqDTO.getTemplateCode(), reqDTO.getTemplateParams(), reqDTO.getAttachments());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long sendSingleMailToMember(MailSendSingleToUserReqDTO reqDTO) {
|
||||
return mailSendService.sendSingleMailToMember(reqDTO.getUserId(),
|
||||
reqDTO.getToMails(), reqDTO.getCcMails(), reqDTO.getBccMails(),
|
||||
reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
|
||||
reqDTO.getTemplateCode(), reqDTO.getTemplateParams(), reqDTO.getAttachments());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package cn.iocoder.yudao.module.system.api.mail.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -46,4 +47,9 @@ public class MailSendSingleToUserReqDTO {
|
||||
*/
|
||||
private Map<String, Object> templateParams;
|
||||
|
||||
/**
|
||||
* 附件
|
||||
*/
|
||||
private File[] attachments;
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.api.user;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
|
||||
@@ -33,6 +34,7 @@ public class AdminUserApiImpl implements AdminUserApi {
|
||||
private DeptService deptService;
|
||||
|
||||
@Override
|
||||
@DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致无法查询用户。类似:https://github.com/YunaiV/ruoyi-vue-pro/issues/1051
|
||||
public AdminUserRespDTO getUser(Long id) {
|
||||
AdminUserDO user = userService.getUser(id);
|
||||
return BeanUtils.toBean(user, AdminUserRespDTO.class);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.system.convert.auth;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
@@ -39,8 +40,6 @@ public interface AuthConvert {
|
||||
.build();
|
||||
}
|
||||
|
||||
AuthPermissionInfoRespVO.MenuVO convertTreeNode(MenuDO menu);
|
||||
|
||||
/**
|
||||
* 将菜单列表,构建成菜单树
|
||||
*
|
||||
@@ -59,9 +58,10 @@ public interface AuthConvert {
|
||||
// 构建菜单树
|
||||
// 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
|
||||
Map<Long, AuthPermissionInfoRespVO.MenuVO> treeNodeMap = new LinkedHashMap<>();
|
||||
menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu)));
|
||||
menuList.forEach(menu -> treeNodeMap.put(menu.getId(),
|
||||
BeanUtils.toBean(menu, AuthPermissionInfoRespVO.MenuVO.class)));
|
||||
// 处理父子关系
|
||||
treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(ID_ROOT)).forEach(childNode -> {
|
||||
treeNodeMap.values().stream().filter(node -> ObjUtil.notEqual(node.getParentId(), ID_ROOT)).forEach(childNode -> {
|
||||
// 获得父节点
|
||||
AuthPermissionInfoRespVO.MenuVO parentNode = treeNodeMap.get(childNode.getParentId());
|
||||
if (parentNode == null) {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package cn.iocoder.yudao.module.system.mq.message.mail;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 邮箱发送消息
|
||||
@@ -55,4 +54,9 @@ public class MailSendMessage {
|
||||
@NotEmpty(message = "邮件内容不能为空")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 附件
|
||||
*/
|
||||
private File[] attachments;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package cn.iocoder.yudao.module.system.mq.producer.mail;
|
||||
|
||||
import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
* Mail 邮件相关消息的 Producer
|
||||
@@ -36,15 +33,17 @@ public class MailProducer {
|
||||
* @param nickname 邮件发件人
|
||||
* @param title 邮件标题
|
||||
* @param content 邮件内容
|
||||
* @param attachments 附件
|
||||
*/
|
||||
public void sendMailSendMessage(Long sendLogId,
|
||||
Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
|
||||
Long accountId, String nickname, String title, String content) {
|
||||
Long accountId, String nickname, String title, String content,
|
||||
File[] attachments) {
|
||||
MailSendMessage message = new MailSendMessage()
|
||||
.setLogId(sendLogId)
|
||||
.setToMails(toMails).setCcMails(ccMails).setBccMails(bccMails)
|
||||
.setAccountId(accountId).setNickname(nickname)
|
||||
.setTitle(title).setContent(content);
|
||||
.setTitle(title).setContent(content).setAttachments(attachments);
|
||||
applicationContext.publishEvent(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.service.mail;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -23,13 +24,15 @@ public interface MailSendService {
|
||||
* @param bccMails 密送邮箱
|
||||
* @param templateCode 邮件模版编码
|
||||
* @param templateParams 邮件模版参数
|
||||
* @param attachments 附件
|
||||
* @return 发送日志编号
|
||||
*/
|
||||
default Long sendSingleMailToAdmin(Long userId,
|
||||
Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
|
||||
String templateCode, Map<String, Object> templateParams) {
|
||||
String templateCode, Map<String, Object> templateParams,
|
||||
File... attachments) {
|
||||
return sendSingleMail(toMails, ccMails, bccMails, userId, UserTypeEnum.ADMIN.getValue(),
|
||||
templateCode, templateParams);
|
||||
templateCode, templateParams, attachments);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,13 +44,15 @@ public interface MailSendService {
|
||||
* @param bccMails 密送邮箱
|
||||
* @param templateCode 邮件模版编码
|
||||
* @param templateParams 邮件模版参数
|
||||
* @param attachments 附件
|
||||
* @return 发送日志编号
|
||||
*/
|
||||
default Long sendSingleMailToMember(Long userId,
|
||||
Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
|
||||
String templateCode, Map<String, Object> templateParams) {
|
||||
String templateCode, Map<String, Object> templateParams,
|
||||
File... attachments) {
|
||||
return sendSingleMail(toMails, ccMails, bccMails, userId, UserTypeEnum.MEMBER.getValue(),
|
||||
templateCode, templateParams);
|
||||
templateCode, templateParams, attachments);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,11 +65,13 @@ public interface MailSendService {
|
||||
* @param userType 用户类型
|
||||
* @param templateCode 邮件模版编码
|
||||
* @param templateParams 邮件模版参数
|
||||
* @param attachments 附件
|
||||
* @return 发送日志编号
|
||||
*/
|
||||
Long sendSingleMail(Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
|
||||
Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams);
|
||||
String templateCode, Map<String, Object> templateParams,
|
||||
File... attachments);
|
||||
|
||||
/**
|
||||
* 执行真正的邮件发送
|
||||
|
||||
@@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.system.service.mail;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.mail.MailAccount;
|
||||
import cn.hutool.extra.mail.MailUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
|
||||
@@ -15,11 +13,14 @@ import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
|
||||
import cn.iocoder.yudao.module.system.service.member.MemberService;
|
||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.hutool.extra.mail.MailAccount;
|
||||
import org.dromara.hutool.extra.mail.MailUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
@@ -56,7 +57,8 @@ public class MailSendServiceImpl implements MailSendService {
|
||||
@Override
|
||||
public Long sendSingleMail(Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
|
||||
Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams) {
|
||||
String templateCode, Map<String, Object> templateParams,
|
||||
File... attachments) {
|
||||
// 1.1 校验邮箱模版是否合法
|
||||
MailTemplateDO template = validateMailTemplate(templateCode);
|
||||
// 1.2 校验邮箱账号是否合法
|
||||
@@ -94,7 +96,7 @@ public class MailSendServiceImpl implements MailSendService {
|
||||
// 发送 MQ 消息,异步执行发送短信
|
||||
if (isSend) {
|
||||
mailProducer.sendMailSendMessage(sendLogId, toMailSet, ccMailSet, bccMailSet,
|
||||
account.getId(), template.getNickname(), title, content);
|
||||
account.getId(), template.getNickname(), title, content, attachments);
|
||||
}
|
||||
return sendLogId;
|
||||
}
|
||||
@@ -123,7 +125,7 @@ public class MailSendServiceImpl implements MailSendService {
|
||||
// 2. 发送邮件
|
||||
try {
|
||||
String messageId = MailUtil.send(mailAccount, message.getToMails(), message.getCcMails(), message.getBccMails(),
|
||||
message.getTitle(), message.getContent(), true);
|
||||
message.getTitle(), message.getContent(), true, message.getAttachments());
|
||||
// 3. 更新结果(成功)
|
||||
mailLogService.updateMailSendResult(message.getLogId(), messageId, null);
|
||||
} catch (Exception e) {
|
||||
@@ -135,7 +137,7 @@ public class MailSendServiceImpl implements MailSendService {
|
||||
private MailAccount buildMailAccount(MailAccountDO account, String nickname) {
|
||||
String from = StrUtil.isNotEmpty(nickname) ? nickname + " <" + account.getMail() + ">" : account.getMail();
|
||||
return new MailAccount().setFrom(from).setAuth(true)
|
||||
.setUser(account.getUsername()).setPass(account.getPassword())
|
||||
.setUser(account.getUsername()).setPass(account.getPassword().toCharArray())
|
||||
.setHost(account.getHost()).setPort(account.getPort())
|
||||
.setSslEnable(account.getSslEnable()).setStarttlsEnable(account.getStarttlsEnable());
|
||||
}
|
||||
|
||||
@@ -102,6 +102,19 @@ public class SocialClientServiceImpl implements SocialClientService {
|
||||
@Value("${yudao.wxa-subscribe-message.miniprogram-state:formal}")
|
||||
public String miniprogramState;
|
||||
|
||||
/**
|
||||
* 上传发货信息重试次数
|
||||
*/
|
||||
private static final int UPLOAD_SHIPPING_INFO_MAX_RETRIES = 5;
|
||||
/**
|
||||
* 上传发货信息重试间隔
|
||||
*/
|
||||
private static final Duration UPLOAD_SHIPPING_INFO_RETRY_INTERVAL = Duration.ofMillis(500L);
|
||||
/**
|
||||
* 微信错误码:支付单不存在
|
||||
*/
|
||||
private static final int WX_ERR_CODE_PAY_ORDER_NOT_EXIST = 10060001;
|
||||
|
||||
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
|
||||
@Autowired(required = false) // 由于 justauth.enable 配置项,可以关闭 AuthRequestFactory 的功能,所以这里只能不强制注入
|
||||
private AuthRequestFactory authRequestFactory;
|
||||
@@ -372,16 +385,34 @@ public class SocialClientServiceImpl implements SocialClientService {
|
||||
.payer(PayerBean.builder().openid(reqDTO.getOpenid()).build())
|
||||
.uploadTime(ZonedDateTime.now().format(UTC_MS_WITH_XXX_OFFSET_FORMATTER))
|
||||
.build();
|
||||
// 重试机制:解决支付回调与订单信息上传之间的时间差导致的 10060001 错误
|
||||
// 对应 ISSUE:https://gitee.com/zhijiantianya/yudao-cloud/pulls/230
|
||||
for (int attempt = 1; attempt <= UPLOAD_SHIPPING_INFO_MAX_RETRIES; attempt++) {
|
||||
try {
|
||||
WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().upload(request);
|
||||
if (response.getErrCode() != 0) {
|
||||
// 成功,直接返回
|
||||
if (response.getErrCode() == 0) {
|
||||
log.info("[uploadWxaOrderShippingInfo][上传微信小程序发货信息成功:request({}) response({})]", request, response);
|
||||
return;
|
||||
}
|
||||
// 如果是 10060001 错误(支付单不存在)且还有重试次数,则等待后重试
|
||||
if (response.getErrCode() == WX_ERR_CODE_PAY_ORDER_NOT_EXIST && attempt < UPLOAD_SHIPPING_INFO_MAX_RETRIES) {
|
||||
log.warn("[uploadWxaOrderShippingInfo][第 {} 次尝试失败,支付单不存在,{} 后重试:request({}) response({})]",
|
||||
attempt, UPLOAD_SHIPPING_INFO_RETRY_INTERVAL, request, response);
|
||||
Thread.sleep(UPLOAD_SHIPPING_INFO_RETRY_INTERVAL.toMillis());
|
||||
continue;
|
||||
}
|
||||
// 其他错误或重试次数用尽,抛出异常
|
||||
log.error("[uploadWxaOrderShippingInfo][上传微信小程序发货信息失败:request({}) response({})]", request, response);
|
||||
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, response.getErrMsg());
|
||||
}
|
||||
log.info("[uploadWxaOrderShippingInfo][上传微信小程序发货信息成功:request({}) response({})]", request, response);
|
||||
} catch (WxErrorException ex) {
|
||||
log.error("[uploadWxaOrderShippingInfo][上传微信小程序发货信息失败:request({})]", request, ex);
|
||||
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, ex.getError().getErrorMsg());
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("[uploadWxaOrderShippingInfo][重试等待被中断:request({})]", request, ex);
|
||||
throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, "重试等待被中断");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ import javax.annotation.Resource;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
@@ -284,11 +285,14 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
@Override
|
||||
public PageResult<AdminUserDO> getUserPage(UserPageReqVO reqVO) {
|
||||
// 如果有角色编号,查询角色对应的用户编号
|
||||
Set<Long> userIds = reqVO.getRoleId() != null ?
|
||||
permissionService.getUserRoleIdListByRoleId(singleton(reqVO.getRoleId())) : null;
|
||||
if (userIds != null && userIds.isEmpty()) {
|
||||
Set<Long> userIds = null;
|
||||
if (reqVO.getRoleId() != null) {
|
||||
userIds = permissionService.getUserRoleIdListByRoleId(singleton(reqVO.getRoleId()));
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
return userMapper.selectPage(reqVO, getDeptCondition(reqVO.getDeptId()), userIds);
|
||||
}
|
||||
@@ -484,12 +488,15 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
// 2. 遍历,逐个创建 or 更新
|
||||
UserImportRespVO respVO = UserImportRespVO.builder().createUsernames(new ArrayList<>())
|
||||
.updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build();
|
||||
AtomicInteger index = new AtomicInteger(1);
|
||||
importUsers.forEach(importUser -> {
|
||||
int currentIndex = index.getAndIncrement();
|
||||
// 2.1.1 校验字段是否符合要求
|
||||
try {
|
||||
ValidationUtils.validate(BeanUtils.toBean(importUser, UserSaveReqVO.class).setPassword(initPassword));
|
||||
} catch (ConstraintViolationException ex) {
|
||||
respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
|
||||
String key = StrUtil.blankToDefault(importUser.getUsername(), "第 " + currentIndex + " 行");
|
||||
respVO.getFailureUsernames().put(key, ex.getMessage());
|
||||
return;
|
||||
}
|
||||
// 2.1.2 校验,判断是否有不符合的原因
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package cn.iocoder.yudao.module.system.service.mail;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.extra.mail.*;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
@@ -14,12 +13,15 @@ import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
|
||||
import cn.iocoder.yudao.module.system.service.member.MemberService;
|
||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||
import org.assertj.core.util.Lists;
|
||||
import org.dromara.hutool.extra.mail.MailAccount;
|
||||
import org.dromara.hutool.extra.mail.MailUtil;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -60,7 +62,7 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
// .setFrom("奥特曼 <ydym_test@163.com>")
|
||||
.setFrom("ydym_test@163.com") // 邮箱地址
|
||||
.setHost("smtp.163.com").setPort(465).setSslEnable(true) // SMTP 服务器
|
||||
.setAuth(true).setUser("ydym_test@163.com").setPass("WBZTEINMIFVRYSOE"); // 登录账号密码
|
||||
.setAuth(true).setUser("ydym_test@163.com").setPass("WBZTEINMIFVRYSOE".toCharArray()); // 登录账号密码
|
||||
String messageId = MailUtil.send(mailAccount, "7685413@qq.com", "主题", "内容", false);
|
||||
System.out.println("发送结果:" + messageId);
|
||||
}
|
||||
@@ -106,7 +108,7 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
|
||||
// 调用
|
||||
Long resultMailLogId = mailSendService.sendSingleMail(toMails, ccMails, bccMails, userId,
|
||||
UserTypeEnum.ADMIN.getValue(), templateCode, templateParams);
|
||||
UserTypeEnum.ADMIN.getValue(), templateCode, templateParams, (File[]) null);
|
||||
// 断言
|
||||
assertEquals(mailLogId, resultMailLogId);
|
||||
// 断言调用
|
||||
@@ -114,7 +116,7 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
argThat(toMailSet -> toMailSet.contains(user.getEmail()) && toMailSet.contains("admin@test.com")),
|
||||
argThat(ccMailSet -> ccMailSet.contains("cc@test.com")),
|
||||
argThat(bccMailSet -> bccMailSet.contains("bcc@test.com")),
|
||||
eq(account.getId()), eq(template.getNickname()), eq(title), eq(content));
|
||||
eq(account.getId()), eq(template.getNickname()), eq(title), eq(content), isNull());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,7 +158,7 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId);
|
||||
|
||||
// 调用
|
||||
Long resultMailLogId = mailSendService.sendSingleMail(toMails, null, null, userId, userType, templateCode, templateParams);
|
||||
Long resultMailLogId = mailSendService.sendSingleMail(toMails, null, null, userId, userType, templateCode, templateParams, (java.io.File[]) null);
|
||||
// 断言
|
||||
assertEquals(mailLogId, resultMailLogId);
|
||||
// 断言调用
|
||||
@@ -164,7 +166,7 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
argThat(toMailSet -> toMailSet.contains(mail)),
|
||||
argThat(Collection::isEmpty),
|
||||
argThat(Collection::isEmpty),
|
||||
eq(account.getId()), eq(template.getNickname()), eq(title), eq(content));
|
||||
eq(account.getId()), eq(template.getNickname()), eq(title), eq(content), isNull());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,12 +208,12 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
eq(account), eq(template), eq(content), eq(templateParams), eq(false))).thenReturn(mailLogId);
|
||||
|
||||
// 调用
|
||||
Long resultMailLogId = mailSendService.sendSingleMail(toMails, null, null, userId, userType, templateCode, templateParams);
|
||||
Long resultMailLogId = mailSendService.sendSingleMail(toMails, null, null, userId, userType, templateCode, templateParams, (java.io.File[]) null);
|
||||
// 断言
|
||||
assertEquals(mailLogId, resultMailLogId);
|
||||
// 断言调用
|
||||
verify(mailProducer, times(0)).sendMailSendMessage(anyLong(), any(), any(), any(),
|
||||
anyLong(), anyString(), anyString(), anyString());
|
||||
anyLong(), anyString(), anyString(), anyString(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -261,7 +263,7 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> mailSendService.sendSingleMail(toMails, null, null, userId,
|
||||
UserTypeEnum.ADMIN.getValue(), templateCode, templateParams),
|
||||
UserTypeEnum.ADMIN.getValue(), templateCode, templateParams, (java.io.File[]) null),
|
||||
MAIL_SEND_MAIL_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@@ -282,13 +284,13 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom());
|
||||
assertTrue(mailAccount.isAuth());
|
||||
assertEquals(account.getUsername(), mailAccount.getUser());
|
||||
assertEquals(account.getPassword(), mailAccount.getPass());
|
||||
assertArrayEquals(account.getPassword().toCharArray(), mailAccount.getPass());
|
||||
assertEquals(account.getHost(), mailAccount.getHost());
|
||||
assertEquals(account.getPort(), mailAccount.getPort());
|
||||
assertEquals(account.getSslEnable(), mailAccount.isSslEnable());
|
||||
return true;
|
||||
}), eq(message.getToMails()), eq(message.getCcMails()), eq(message.getBccMails()),
|
||||
eq(message.getTitle()), eq(message.getContent()), eq(true)))
|
||||
eq(message.getTitle()), eq(message.getContent()), eq(true), eq(message.getAttachments())))
|
||||
.thenReturn(messageId);
|
||||
|
||||
// 调用
|
||||
@@ -314,13 +316,13 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom());
|
||||
assertTrue(mailAccount.isAuth());
|
||||
assertEquals(account.getUsername(), mailAccount.getUser());
|
||||
assertEquals(account.getPassword(), mailAccount.getPass());
|
||||
assertArrayEquals(account.getPassword().toCharArray(), mailAccount.getPass());
|
||||
assertEquals(account.getHost(), mailAccount.getHost());
|
||||
assertEquals(account.getPort(), mailAccount.getPort());
|
||||
assertEquals(account.getSslEnable(), mailAccount.isSslEnable());
|
||||
return true;
|
||||
}), eq(message.getToMails()), eq(message.getCcMails()), eq(message.getBccMails()),
|
||||
eq(message.getTitle()), eq(message.getContent()), eq(true))).thenThrow(e);
|
||||
eq(message.getTitle()), eq(message.getContent()), eq(true), same(message.getAttachments()))).thenThrow(e);
|
||||
|
||||
// 调用
|
||||
mailSendService.doSendMail(message);
|
||||
|
||||
Reference in New Issue
Block a user