【同步】BOOT 和 CLOUD 的功能

This commit is contained in:
YunaiV
2026-08-30 19:42:20 +08:00
parent bdcb27fc25
commit df1f45530e
32 changed files with 486 additions and 37 deletions
@@ -173,7 +173,7 @@ public class LambdaQueryWrapperX<T> extends LambdaQueryWrapper<T> {
@Override
public LambdaQueryWrapperX<T> and(boolean condition, Consumer<LambdaQueryWrapper<T>> consumer) {
super.and(condition,consumer);
super.and(condition, consumer);
return this;
}
@@ -17,17 +17,19 @@ import jakarta.validation.constraints.NotNull;
public class WebProperties {
@NotNull(message = "APP API 不能为空")
@Valid
private Api appApi = new Api("/app-api", "**.controller.app.**");
@NotNull(message = "Admin API 不能为空")
@Valid
private Api adminApi = new Api("/admin-api", "**.controller.admin.**");
@NotNull(message = "Admin UI 不能为空")
@Valid
private Ui adminUi;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Valid
public static class Api {
/**
@@ -53,7 +55,6 @@ public class WebProperties {
}
@Data
@Valid
public static class Ui {
/**
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.d
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -12,6 +13,7 @@ import jakarta.annotation.Resource;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
@@ -91,4 +93,19 @@ public abstract class AbstractBpmTaskCandidateDeptLeaderStrategy implements BpmT
return deptApi.getDept(startUser.getDeptId()).getCheckedData();
}
/**
* 获取发起人的部门(优先使用流程变量中的发起人编号)
*
* @param startUserId 发起人 Id(流程变量不存在时使用)
* @param processVariables 流程变量
* @return 发起人的部门
*/
protected DeptRespDTO getStartUserDept(Long startUserId, Map<String, Object> processVariables) {
Long processStartUserId = FlowableUtils.getProcessInstanceStartUserId(processVariables);
if (processStartUserId != null && processStartUserId > 0) {
startUserId = processStartUserId;
}
return getStartUserDept(startUserId);
}
}
@@ -49,7 +49,7 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBp
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
// 获取发起人的 multi 部门负责人
DeptRespDTO dept = super.getStartUserDept(startUserId);
DeptRespDTO dept = super.getStartUserDept(startUserId, processInstance.getProcessVariables());
if (dept == null) {
return new HashSet<>();
}
@@ -60,7 +60,7 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBp
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
int level = Integer.parseInt(param); // 参数是部门的层级
DeptRespDTO dept = super.getStartUserDept(startUserId);
DeptRespDTO dept = super.getStartUserDept(startUserId, processVariables);
if (dept == null) {
return new HashSet<>();
}
@@ -48,19 +48,20 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategy extends AbstractBpmTask
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
// 获取发起人的部门负责人
return getStartUserDeptLeader(startUserId, param);
return getStartUserDeptLeader(startUserId, param, processInstance.getProcessVariables());
}
@Override
public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
// 获取发起人的部门负责人
return getStartUserDeptLeader(startUserId, param);
return getStartUserDeptLeader(startUserId, param, processVariables);
}
private Set<Long> getStartUserDeptLeader(Long startUserId, String param) {
private Set<Long> getStartUserDeptLeader(Long startUserId, String param,
Map<String, Object> processVariables) {
int level = Integer.parseInt(param); // 参数是部门的层级
DeptRespDTO dept = super.getStartUserDept(startUserId);
DeptRespDTO dept = super.getStartUserDept(startUserId, processVariables);
if (dept == null) {
return new HashSet<>();
}
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
@@ -133,6 +134,17 @@ public class FlowableUtils {
return (Integer) processVariables.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
}
/**
* 获得流程实例的发起用户 ID
*
* @param processVariables 流程实例的 variables
* @return 发起用户 ID
*/
public static Long getProcessInstanceStartUserId(Map<String, Object> processVariables) {
return processVariables == null ? null : Convert.toLong(
processVariables.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID));
}
/**
* 获得流程实例的审批原因
*
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
@@ -13,6 +14,8 @@ import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -42,12 +45,16 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMo
String param = "2";
// mock 方法(获得流程发起人)
Long startUserId = 1L;
Long processVariableStartUserId = 2L;
ProcessInstance processInstance = mock(ProcessInstance.class);
DelegateExecution execution = mock(DelegateExecution.class);
when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
when(processInstance.getStartUserId()).thenReturn(startUserId.toString());
Map<String, Object> processVariables = new HashMap<>();
processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, processVariableStartUserId);
when(processInstance.getProcessVariables()).thenReturn(processVariables);
// mock 方法(获取发起人的 multi 部门负责人)
mockGetStartUserDept(startUserId);
mockGetStartUserDept(processVariableStartUserId);
// 调用
Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
@@ -61,12 +68,14 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMo
String param = "2";
// mock 方法
Long startUserId = 1L;
mockGetStartUserDept(startUserId);
Long processVariableStartUserId = 2L;
Map<String, Object> processVariables = new HashMap<>();
processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, processVariableStartUserId);
mockGetStartUserDept(processVariableStartUserId);
// 调用
Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param,
startUserId, null, null);
// 断言
startUserId, null, processVariables);
assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds);
}
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
@@ -13,6 +14,8 @@ import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -42,12 +45,16 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockito
String param = "2";
// mock 方法(获得流程发起人)
Long startUserId = 1L;
Long processVariableStartUserId = 2L;
ProcessInstance processInstance = mock(ProcessInstance.class);
DelegateExecution execution = mock(DelegateExecution.class);
when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
when(processInstance.getStartUserId()).thenReturn(startUserId.toString());
Map<String, Object> processVariables = new HashMap<>();
processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, processVariableStartUserId);
when(processInstance.getProcessVariables()).thenReturn(processVariables);
// mock 方法(获取发起人的部门负责人)
mockGetStartUserDeptLeader(startUserId);
mockGetStartUserDeptLeader(processVariableStartUserId);
// 调用
Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
@@ -61,12 +68,14 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockito
String param = "2";
// mock 方法
Long startUserId = 1L;
mockGetStartUserDeptLeader(startUserId);
Long processVariableStartUserId = 2L;
Map<String, Object> processVariables = new HashMap<>();
processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, processVariableStartUserId);
mockGetStartUserDeptLeader(processVariableStartUserId);
// 调用
Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param,
startUserId, null, null);
// 断言
startUserId, null, processVariables);
assertEquals(Sets.newLinkedHashSet(1001L), userIds);
}
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
@@ -22,6 +23,29 @@ import static org.junit.jupiter.api.Assertions.assertNull;
*/
class FlowableUtilsTest {
@Test
public void testGetProcessInstanceStartUserId() {
// 准备参数
Map<String, Object> processVariables = new HashMap<>();
processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, "1");
// 调用
Long startUserId = FlowableUtils.getProcessInstanceStartUserId(processVariables);
// 断言
assertEquals(1L, startUserId);
}
@Test
public void testGetProcessInstanceStartUserId_empty() {
// 准备参数
Map<String, Object> processVariables = new HashMap<>();
// 调用 & 断言
assertNull(FlowableUtils.getProcessInstanceStartUserId(processVariables));
assertNull(FlowableUtils.getProcessInstanceStartUserId(null));
}
@Test
public void testGetSummary_customSummary_parseDbFormFields() {
// 准备参数:模拟 DB 中 form_fields 字段,列表里每个元素都是一个 form-create 字段 JSON。
@@ -102,7 +102,7 @@ public class FmsAccountUserServiceImpl implements FmsAccountUserService {
FmsAccountUserUpdateReqVO.Member::getUserId, FmsAccountUserUpdateReqVO.Member::getLevel);
Set<Long> memberUserIds = new HashSet<>(memberLevelMap.keySet());
memberUserIds.removeAll(founderUserIds);
adminUserApi.validateUserList(memberUserIds);
adminUserApi.validateUserList(memberUserIds).checkError();
// 2.1 移出取消授权的成员
Set<Long> currentMemberUserIds = convertSet(accountUsers, FmsAccountUserDO::getUserId,
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.fms.dal.mysql.config.FmsAccountUserMapper;
import cn.iocoder.yudao.module.fms.enums.config.FmsAccountUserLevelEnum;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
@@ -22,6 +23,8 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -38,6 +41,11 @@ public class FmsAccountUserServiceImplTest extends BaseDbUnitTest {
@MockitoBean
private AdminUserApi adminUserApi;
@BeforeEach
public void setUp() {
when(adminUserApi.validateUserList(anyCollection())).thenReturn(success(true));
}
@Test
public void testCreateAccountOwner_firstAccountSet() {
// 准备参数
@@ -312,7 +312,7 @@ public class HrmAttendanceGroupServiceImpl implements HrmAttendanceGroupService
private void validateAttendanceGroup(HrmAttendanceGroupSaveReqVO reqVO) {
// 1. 校验适用部门存在
deptApi.validateDeptList(reqVO.getDeptIds());
deptApi.validateDeptList(reqVO.getDeptIds()).checkError();
// 2. 校验适用员工存在
employeeService.validateEmployeeListExists(reqVO.getEmployeeIds());
}
@@ -628,7 +628,7 @@ public class HrmEmployeeServiceImpl implements HrmEmployeeService {
throw exception(EMPLOYEE_CREATE_USER_LIST_DUPLICATE);
}
}
adminUserApi.validateUserList(userIds);
adminUserApi.validateUserList(userIds).checkError();
Map<Long, AdminUserRespDTO> userMap = convertMap(
adminUserApi.getUserList(userIds).getCheckedData(), AdminUserRespDTO::getId);
// 1.2 构建并校验全部员工档案,避免部分数据校验失败时已经执行写库
@@ -1179,7 +1179,7 @@ public class HrmEmployeeServiceImpl implements HrmEmployeeService {
private void validateDeptAndLeader(Long employeeId, Long deptId, Long leaderEmployeeId) {
// 1. 校验部门是否存在
if (deptId != null) {
deptApi.validateDeptList(Collections.singleton(deptId));
deptApi.validateDeptList(Collections.singleton(deptId)).checkError();
}
// 2. 校验直属上级链路不存在自引用或环路
if (leaderEmployeeId == null) {
@@ -1417,7 +1417,7 @@ public class HrmEmployeeServiceImpl implements HrmEmployeeService {
try {
notifyMessageSendApi.sendSingleMessageToAdmin(new NotifySendSingleToUserReqDTO()
.setUserId(employee.getUserId()).setTemplateCode(templateCode)
.setTemplateParams(Collections.singletonMap("employeeName", employee.getName())));
.setTemplateParams(Collections.singletonMap("employeeName", employee.getName()))).checkError();
return true;
} catch (RuntimeException ex) {
log.warn("[sendEmployeeMessage][employeeId({}) userId({}) templateCode({}) 发送失败]",
@@ -536,7 +536,7 @@ public class HrmInsuranceMonthEmployeeRecordServiceImpl implements HrmInsuranceM
notifyMessageSendApi.sendSingleMessageToAdmin(new NotifySendSingleToUserReqDTO()
.setUserId(employee.getUserId())
.setTemplateCode(MessageTemplateConstants.INSURANCE_MONTH_RECORD_CREATED)
.setTemplateParams(templateParams));
.setTemplateParams(templateParams)).checkError();
}
}
@@ -350,7 +350,7 @@ public class HrmPerformanceAssessmentProcessServiceImpl
notifyMessageSendApi.sendSingleMessageToAdmin(new NotifySendSingleToUserReqDTO()
.setUserId(handler.getUserId())
.setTemplateCode(MessageTemplateConstants.PERFORMANCE_TASK_PENDING)
.setTemplateParams(templateParams));
.setTemplateParams(templateParams)).checkError();
}
@Override
@@ -386,7 +386,7 @@ public class HrmPerformanceAssessmentProcessServiceImpl
notifyMessageSendApi.sendSingleMessageToAdmin(new NotifySendSingleToUserReqDTO()
.setUserId(employee.getUserId())
.setTemplateCode(MessageTemplateConstants.PERFORMANCE_PROCESS_RESULT)
.setTemplateParams(templateParams));
.setTemplateParams(templateParams)).checkError();
}
}
@@ -555,7 +555,7 @@ public class HrmPerformancePlanServiceImpl implements HrmPerformancePlanService
employeeService.validateEmployeeListExists(employeeIds);
}
if (CollUtil.isNotEmpty(deptIds)) {
deptApi.validateDeptList(deptIds);
deptApi.validateDeptList(deptIds).checkError();
}
}
@@ -351,7 +351,7 @@ public class HrmRecruitInterviewServiceImpl implements HrmRecruitInterviewServic
templateParams.put("interviewTime", INTERVIEW_TIME_FORMATTER.format(recruitInterview.getInterviewTime()));
userIds.forEach(userId -> notifyMessageSendApi.sendSingleMessageToAdmin(new NotifySendSingleToUserReqDTO()
.setUserId(userId).setTemplateCode(MessageTemplateConstants.RECRUIT_INTERVIEW_ARRANGED)
.setTemplateParams(templateParams)));
.setTemplateParams(templateParams)).checkError());
}
}
@@ -146,7 +146,7 @@ public class HrmRecruitPostServiceImpl implements HrmRecruitPostService {
// 1. 校验职位类型和用人部门存在
recruitPostTypeService.validateRecruitPostTypeExists(reqVO.getPostTypeId());
if (reqVO.getDeptId() != null) {
deptApi.validateDeptList(Collections.singleton(reqVO.getDeptId()));
deptApi.validateDeptList(Collections.singleton(reqVO.getDeptId())).checkError();
}
// 2. 校验招聘负责人和面试官存在
@@ -221,7 +221,7 @@ public class HrmSalaryGroupServiceImpl implements HrmSalaryGroupService {
// 1. 校验适用部门及其上下级部门未归属其他薪资组
Set<Long> selectedDeptIds = CollUtil.isEmpty(deptIds)
? Collections.emptySet() : new HashSet<>(deptIds);
deptApi.validateDeptList(selectedDeptIds);
deptApi.validateDeptList(selectedDeptIds).checkError();
List<HrmSalaryGroupDO> otherSalaryGroups = convertList(salaryGroupMapper.selectListByIdDesc(),
salaryGroup -> salaryGroup, salaryGroup -> ObjUtil.notEqual(salaryGroup.getId(), id));
if (CollUtil.isNotEmpty(selectedDeptIds)) {
@@ -256,7 +256,7 @@ public class HrmSalarySlipServiceImpl implements HrmSalarySlipService {
templateParams.put("realSalary", employeeRecord.getRealPaySalary().toPlainString());
notifyMessageSendApi.sendSingleMessageToAdmin(new NotifySendSingleToUserReqDTO()
.setUserId(employee.getUserId()).setTemplateCode(MessageTemplateConstants.SALARY_SLIP_SENT)
.setTemplateParams(templateParams));
.setTemplateParams(templateParams)).checkError();
}
}
@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.hrm.enums.attendance.config.HrmAttendanceMisscard
import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
@@ -22,13 +23,23 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* {@link HrmAttendanceGroupServiceImpl} 的单元测试类
@@ -49,6 +60,11 @@ public class HrmAttendanceGroupServiceImplTest extends BaseMockitoUnitTest {
@Mock
private DeptApi deptApi;
@BeforeEach
public void setUp() {
lenient().when(deptApi.validateDeptList(anyCollection())).thenReturn(success(true));
}
@Test
public void testGetAttendanceGroupMap_employeeAndDeptAndDefaultGroup() {
// mock 数据
@@ -68,6 +68,7 @@ import static java.util.Arrays.asList;
import static java.util.Collections.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.*;
@@ -128,6 +129,9 @@ public class HrmEmployeeServiceImplTest extends BaseDbUnitTest {
HrmEmployeeChangeRecordCreateReqVO reqVO = invocation.getArgument(0);
return BeanUtils.toBean(reqVO, HrmEmployeeChangeRecordDO.class).setId(randomLongId());
});
lenient().when(deptApi.validateDeptList(anyCollection())).thenReturn(success(true));
lenient().when(adminUserApi.validateUserList(anyCollection())).thenReturn(success(true));
lenient().when(notifyMessageSendApi.sendSingleMessageToAdmin(any())).thenReturn(success(randomLongId()));
}
@Test
@@ -25,6 +25,7 @@ import cn.iocoder.yudao.module.hrm.service.insurance.config.HrmInsuranceSchemeSe
import cn.iocoder.yudao.module.hrm.service.insurance.employee.HrmInsuranceEmployeeInfoService;
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
@@ -37,6 +38,8 @@ import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.INSURANCE_EMPLOYEE_SCHEME_NOT_CONFIGURED;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.INSURANCE_MONTH_EMPLOYEE_NOT_ELIGIBLE;
@@ -73,6 +76,11 @@ public class HrmInsuranceMonthEmployeeRecordServiceImplTest extends BaseDbUnitTe
@MockitoBean
private NotifyMessageSendApi notifyMessageSendApi;
@BeforeEach
public void setUp() {
when(notifyMessageSendApi.sendSingleMessageToAdmin(any())).thenReturn(success(randomLongId()));
}
@Test
public void testCreateMonthEmployeeRecordList_success() {
// mock 数据
@@ -43,6 +43,7 @@ import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.context.annotation.Import;
@@ -57,6 +58,7 @@ import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.PERFORMANCE_STAGE_NO_PERMISSION;
@@ -70,6 +72,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -111,6 +114,11 @@ public class HrmPerformanceAssessmentProcessServiceImplTest extends BaseDbUnitTe
@MockitoBean
private HrmPerformanceAssessmentActionRecordService assessmentActionRecordService;
@BeforeEach
public void setUp() {
lenient().when(notifyMessageSendApi.sendSingleMessageToAdmin(any())).thenReturn(success(randomLongId()));
}
@Test
public void testInitializeAssessmentStages_unboundEmployee_fail() {
// mock 数据
@@ -15,6 +15,7 @@ import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService;
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.context.annotation.Import;
@@ -27,6 +28,7 @@ import java.util.Map;
import java.util.function.Consumer;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
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.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
@@ -68,6 +70,11 @@ public class HrmRecruitInterviewServiceImplTest extends BaseDbUnitTest {
@MockitoBean
private NotifyMessageSendApi notifyMessageSendApi;
@BeforeEach
public void setUp() {
when(notifyMessageSendApi.sendSingleMessageToAdmin(any())).thenReturn(success(randomLongId()));
}
@Test
public void testCreateRecruitInterview_success() {
// mock 数据
@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.hrm.enums.recruit.post.HrmRecruitPostStatusEnum;
import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
@@ -25,6 +26,7 @@ import java.util.Map;
import java.util.function.Consumer;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
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.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.getDateTimeRange;
@@ -44,6 +46,7 @@ import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
@@ -68,6 +71,11 @@ public class HrmRecruitPostServiceImplTest extends BaseDbUnitTest {
@MockitoBean
private DeptApi deptApi;
@BeforeEach
public void setUp() {
when(deptApi.validateDeptList(anyCollection())).thenReturn(success(true));
}
@Test
public void testCreateRecruitPost_success() {
// 准备参数
@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.hrm.service.employee.info.HrmEmployeeService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
@@ -32,10 +33,10 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.argThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
/**
* {@link HrmSalaryGroupServiceImpl} 的单元测试类
@@ -58,6 +59,11 @@ public class HrmSalaryGroupServiceImplTest extends BaseDbUnitTest {
@MockitoBean
private DeptApi deptApi;
@BeforeEach
public void setUp() {
when(deptApi.validateDeptList(anyCollection())).thenReturn(success(true));
}
@Test
public void testCreateSalaryGroup_success() {
// 准备参数
@@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
import cn.iocoder.yudao.module.hrm.enums.salary.slip.HrmSalarySlipReadStatusEnum;
import cn.iocoder.yudao.module.hrm.enums.salary.slip.HrmSalarySlipTemplateOptionTypeEnum;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
@@ -27,6 +28,7 @@ import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.SALARY_SLIP_ALREADY_SENT;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.SALARY_SLIP_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -37,6 +39,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
/**
* {@link HrmSalarySlipServiceImpl} 的单元测试类
@@ -56,6 +59,12 @@ public class HrmSalarySlipServiceImplTest extends BaseDbUnitTest {
@MockitoBean
private NotifyMessageSendApi notifyMessageSendApi;
@BeforeEach
public void setUp() {
when(notifyMessageSendApi.sendSingleMessageToAdmin(org.mockito.ArgumentMatchers.any()))
.thenReturn(success(randomLongId()));
}
@Test
public void testCreateSalarySlipList_success() {
// mock 数据
@@ -54,6 +54,7 @@ public class PayClientFactoryImpl implements PayClientFactory {
clientClass.put(ALIPAY_APP, AlipayAppPayClient.class);
clientClass.put(ALIPAY_PC, AlipayPcPayClient.class);
clientClass.put(ALIPAY_BAR, AlipayBarPayClient.class);
clientClass.put(ALIPAY_LITE, AlipayLitePayClient.class);
// 钱包支付客户端
clientClass.put(WALLET, WalletPayClient.class);
// Mock 支付客户端
@@ -0,0 +1,86 @@
package cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.pay.enums.PayChannelEnum;
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.module.pay.framework.pay.core.enums.PayOrderDisplayModeEnum;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradeCreateModel;
import com.alipay.api.request.AlipayTradeCreateRequest;
import com.alipay.api.response.AlipayTradeCreateResponse;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
import static cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_CERTIFICATE;
/**
* 支付宝【小程序】的 PayClient 实现类
*
* 文档:<a href="https://opendocs.alipay.com/mini/6039ed0c_alipay.trade.create">统一收单交易创建</a>
*
* @author graypxl
*/
public class AlipayLitePayClient extends AbstractAlipayPayClient {
public static final String BUYER_ID_KEY = "buyer_id";
public static final String BUYER_OPEN_ID_KEY = "buyer_open_id";
public AlipayLitePayClient(Long channelId, AlipayPayClientConfig config) {
super(channelId, PayChannelEnum.ALIPAY_LITE.getCode(), config);
}
@Override
public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
AlipayTradeCreateModel model = new AlipayTradeCreateModel();
model.setOutTradeNo(reqDTO.getOutTradeNo());
model.setSubject(reqDTO.getSubject());
model.setBody(reqDTO.getBody());
model.setTotalAmount(formatAmount(reqDTO.getPrice()));
model.setTimeExpire(formatTime(reqDTO.getExpireTime()));
model.setProductCode("JSAPI_PAY");
model.setOpAppId(config.getAppId());
applyBuyerIdentity(model, reqDTO);
AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
request.setBizModel(model);
request.setNotifyUrl(reqDTO.getNotifyUrl());
AlipayTradeCreateResponse response;
if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) {
response = client.certificateExecute(request);
} else {
response = client.execute(request);
}
if (!response.isSuccess()) {
return buildClosedPayOrderRespDTO(reqDTO, response);
}
return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), response.getTradeNo(),
reqDTO.getOutTradeNo(), response);
}
static void applyBuyerIdentity(AlipayTradeCreateModel model, PayOrderUnifiedReqDTO reqDTO) {
String buyerId = MapUtil.getStr(reqDTO.getChannelExtras(), BUYER_ID_KEY);
String buyerOpenId = MapUtil.getStr(reqDTO.getChannelExtras(), BUYER_OPEN_ID_KEY);
if (StrUtil.isBlank(buyerOpenId)) {
buyerOpenId = MapUtil.getStr(reqDTO.getChannelExtras(), "openid");
}
if (isAlipayUserId(buyerId)) {
model.setBuyerId(buyerId);
} else if (StrUtil.isNotBlank(buyerOpenId)) {
model.setBuyerOpenId(buyerOpenId);
} else if (StrUtil.isNotBlank(buyerId)) {
model.setBuyerOpenId(buyerId);
} else {
throw invalidParamException("支付请求的 buyer_open_id 不能为空!");
}
}
static boolean isAlipayUserId(String value) {
return StrUtil.isNotBlank(value) && value.startsWith("2088") && value.length() <= 28
&& StrUtil.isNumeric(value);
}
}
@@ -0,0 +1,201 @@
package cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay;
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.module.pay.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.module.pay.framework.pay.core.enums.PayOrderDisplayModeEnum;
import cn.iocoder.yudao.module.pay.framework.pay.core.client.exception.PayClientException;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradeCreateModel;
import com.alipay.api.request.AlipayTradeCreateRequest;
import com.alipay.api.response.AlipayTradeCreateResponse;
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.HashMap;
import java.util.Map;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum.CLOSED;
import static cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum.WAITING;
import static cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_CERTIFICATE;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.when;
/**
* {@link AlipayLitePayClient} 单元测试
*
* @author graypxl
*/
public class AlipayLitePayClientTest extends AbstractAlipayClientTest {
@InjectMocks
private AlipayLitePayClient client = new AlipayLitePayClient(randomLongId(), config);
@Override
@BeforeEach
public void setUp() {
setClient(client);
}
@Test
@DisplayName("支付宝小程序支付:buyer_open_id 下单成功")
public void testUnifiedOrder_buyerOpenId() throws AlipayApiException {
String buyerOpenId = "060d4dhFytu7VF5haNiDacG7b-n5toPeevhdLa07Kz9ass8";
String tradeNo = randomString();
AlipayTradeCreateResponse response = randomPojo(AlipayTradeCreateResponse.class, o -> {
o.setSubCode("");
o.setTradeNo(tradeNo);
});
when(defaultAlipayClient.execute(argThat(assertRequest(config.getAppId(), null, buyerOpenId))))
.thenReturn(response);
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), randomString(), randomInteger());
reqDTO.setChannelExtras(Map.of(AlipayLitePayClient.BUYER_OPEN_ID_KEY, buyerOpenId));
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
assertEquals(WAITING.getStatus(), resp.getStatus());
assertEquals(reqDTO.getOutTradeNo(), resp.getOutTradeNo());
assertNull(resp.getChannelOrderNo());
assertNull(resp.getChannelUserId());
assertNull(resp.getSuccessTime());
assertEquals(PayOrderDisplayModeEnum.APP.getMode(), resp.getDisplayMode());
assertEquals(tradeNo, resp.getDisplayContent());
assertSame(response, resp.getRawData());
assertNull(resp.getChannelErrorCode());
assertNull(resp.getChannelErrorMsg());
}
@Test
@DisplayName("支付宝小程序支付:buyer_id 下单成功")
public void testUnifiedOrder_buyerId() throws AlipayApiException {
String buyerId = "2088123456789012";
AlipayTradeCreateResponse response = randomPojo(AlipayTradeCreateResponse.class, o -> o.setSubCode(""));
when(defaultAlipayClient.execute(argThat(assertRequest(config.getAppId(), buyerId, null))))
.thenReturn(response);
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), randomString(), randomInteger());
reqDTO.setChannelExtras(Map.of(AlipayLitePayClient.BUYER_ID_KEY, buyerId));
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
assertEquals(WAITING.getStatus(), resp.getStatus());
assertEquals(reqDTO.getOutTradeNo(), resp.getOutTradeNo());
assertEquals(PayOrderDisplayModeEnum.APP.getMode(), resp.getDisplayMode());
assertSame(response, resp.getRawData());
}
@Test
@DisplayName("支付宝小程序支付:兼容 openid 参数")
public void testUnifiedOrder_openidAlias() throws AlipayApiException {
String buyerOpenId = randomString();
AlipayTradeCreateResponse response = randomPojo(AlipayTradeCreateResponse.class, o -> o.setSubCode(""));
when(defaultAlipayClient.execute(argThat(assertRequest(config.getAppId(), null, buyerOpenId))))
.thenReturn(response);
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), randomString(), randomInteger());
reqDTO.setChannelExtras(Map.of("openid", buyerOpenId));
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
assertEquals(WAITING.getStatus(), resp.getStatus());
assertEquals(reqDTO.getOutTradeNo(), resp.getOutTradeNo());
assertEquals(PayOrderDisplayModeEnum.APP.getMode(), resp.getDisplayMode());
assertSame(response, resp.getRawData());
}
@Test
@DisplayName("支付宝小程序支付:证书模式下单成功")
public void testUnifiedOrder_certificateMode() throws AlipayApiException {
config.setMode(MODE_CERTIFICATE);
String buyerOpenId = randomString();
AlipayTradeCreateResponse response = randomPojo(AlipayTradeCreateResponse.class, o -> o.setSubCode(""));
when(defaultAlipayClient.certificateExecute(argThat(assertRequest(config.getAppId(), null, buyerOpenId))))
.thenReturn(response);
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), randomString(), randomInteger());
reqDTO.setChannelExtras(Map.of(AlipayLitePayClient.BUYER_OPEN_ID_KEY, buyerOpenId));
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
assertEquals(WAITING.getStatus(), resp.getStatus());
assertEquals(reqDTO.getOutTradeNo(), resp.getOutTradeNo());
assertEquals(PayOrderDisplayModeEnum.APP.getMode(), resp.getDisplayMode());
assertSame(response, resp.getRawData());
}
@Test
@DisplayName("支付宝小程序支付:渠道返回失败")
public void testUnifiedOrder_channelFailed() throws AlipayApiException {
String subCode = randomString();
String subMsg = randomString();
AlipayTradeCreateResponse response = randomPojo(AlipayTradeCreateResponse.class, o -> {
o.setSubCode(subCode);
o.setSubMsg(subMsg);
});
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeCreateRequest>) request -> true)))
.thenReturn(response);
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), randomString(), randomInteger());
reqDTO.setChannelExtras(Map.of(AlipayLitePayClient.BUYER_OPEN_ID_KEY, randomString()));
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
assertEquals(CLOSED.getStatus(), resp.getStatus());
assertEquals(reqDTO.getOutTradeNo(), 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());
}
@Test
@DisplayName("支付宝小程序支付:买家标识为空")
public void testUnifiedOrder_buyerEmpty() {
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), randomString(), randomInteger());
reqDTO.setChannelExtras(new HashMap<>());
assertThrows(ServiceException.class, () -> client.unifiedOrder(reqDTO));
}
@Test
@DisplayName("支付宝小程序支付:抛出业务异常")
public void testUnifiedOrder_throwServiceException() throws AlipayApiException {
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeCreateRequest>) request -> true)))
.thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), randomString(), randomInteger());
reqDTO.setChannelExtras(Map.of(AlipayLitePayClient.BUYER_OPEN_ID_KEY, randomString()));
assertThrows(ServiceException.class, () -> client.unifiedOrder(reqDTO));
}
@Test
@DisplayName("支付宝小程序支付:抛出系统异常")
public void testUnifiedOrder_throwPayException() throws AlipayApiException {
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeCreateRequest>) request -> true)))
.thenThrow(new RuntimeException("系统异常"));
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), randomString(), randomInteger());
reqDTO.setChannelExtras(Map.of(AlipayLitePayClient.BUYER_OPEN_ID_KEY, randomString()));
assertThrows(PayClientException.class, () -> client.unifiedOrder(reqDTO));
}
private static ArgumentMatcher<AlipayTradeCreateRequest> assertRequest(String appId, String buyerId,
String buyerOpenId) {
return request -> {
AlipayTradeCreateModel model = (AlipayTradeCreateModel) request.getBizModel();
assertEquals("JSAPI_PAY", model.getProductCode());
assertEquals(appId, model.getOpAppId());
assertEquals(buyerId, model.getBuyerId());
assertEquals(buyerOpenId, model.getBuyerOpenId());
return true;
};
}
}
@@ -398,4 +398,18 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
assertFalse(formattedContent.contains("{activationLink}"));
}
@Test
public void testFormatMailTemplateContent_dollarInCodeBlock() {
// 准备参数
Map<String, Object> params = new HashMap<>();
// 测试代码块中包含 $ 与 \\(历史上 appendReplacement 会抛 IllegalArgumentException: Illegal group reference)
String content = "<pre><code>echo $HOME && ls \\root</code></pre>";
// 调用,并断言内容原样保留
String result = mailTemplateService.formatMailTemplateContent(content, params);
assertTrue(result.contains("echo $HOME && ls \\root"));
assertTrue(result.contains("<div><code>"));
}
}