diff --git a/src/main/java/com/greenorange/promotion/controller/project/ProjectController.java b/src/main/java/com/greenorange/promotion/controller/project/ProjectController.java index 9c9a234..ac8ca70 100644 --- a/src/main/java/com/greenorange/promotion/controller/project/ProjectController.java +++ b/src/main/java/com/greenorange/promotion/controller/project/ProjectController.java @@ -91,27 +91,7 @@ public class ProjectController { @RequiresPermission(mustRole = UserConstant.DEFAULT_ROLE) // @SysLog(title = "项目管理", content = "小程序用户查看项目列表") public BaseResponse> queryProjectCardList(HttpServletRequest request) { - Long userId = (Long) request.getAttribute("userId"); - // 获取项目明细抽佣列表 - List projectCommissionList = commonService.findByFieldEqTargetField(ProjectCommission::getUserId, userId, projectCommissionService); - // 封装Map集合(键:项目id, 值:项目总价) - Map projectPriceMap = new HashMap<>(); - for (ProjectCommission projectCommission : projectCommissionList) { - Long projectId = projectCommission.getProjectId(); - BigDecimal projectPrice = projectPriceMap.get(projectId); - if (projectPrice == null) { - projectPrice = projectCommission.getMyUnitPrice(); - } else { - projectPrice = projectPrice.add(projectCommission.getMyUnitPrice()); - } - projectPriceMap.put(projectId, projectPrice); - } - List projectList = commonService.findByFieldEqTargetField(Project::getIsShelves, 1, projectService); - for (Project project : projectList) { - BigDecimal projectPrice = projectPriceMap.get(project.getId()); - project.setProjectPrice(projectPrice == null ? BigDecimal.ZERO : projectPrice); - } - List projectCardVOS = commonService.convertList(projectList, ProjectCardVO.class); + List projectCardVOS = projectService.queryProjectCardList(request); return ResultUtils.success(projectCardVOS); } @@ -281,10 +261,7 @@ public class ProjectController { @RequiresPermission(mustRole = UserConstant.ADMIN_ROLE) @SysLog(title = "项目管理", content = "web端管理员根据id查询项目") public BaseResponse queryProjectById(@Valid @RequestBody CommonRequest commonRequest) { - Long id = commonRequest.getId(); - Project project = projectService.getById(id); - ThrowUtils.throwIf(project == null, ErrorCode.OPERATION_ERROR, "当前项目不存在"); - ProjectVO projectVO = commonService.copyProperties(project, ProjectVO.class); + ProjectVO projectVO = projectService.queryProjectById(commonRequest); return ResultUtils.success(projectVO); } diff --git a/src/main/java/com/greenorange/promotion/controller/project/PromoCodeController.java b/src/main/java/com/greenorange/promotion/controller/project/PromoCodeController.java index fa2cdfd..fd31140 100644 --- a/src/main/java/com/greenorange/promotion/controller/project/PromoCodeController.java +++ b/src/main/java/com/greenorange/promotion/controller/project/PromoCodeController.java @@ -114,12 +114,7 @@ public class PromoCodeController { @RequiresPermission(mustRole = UserConstant.ADMIN_ROLE) @SysLog(title = "推广码管理", content = "web端管理员批量删除推广码") public BaseResponse delBatchPromoCode(@Valid @RequestBody CommonBatchRequest commonBatchRequest) { - List ids = commonBatchRequest.getIds(); - LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); - lambdaQueryWrapper.in(PromoCode::getId, ids).eq(PromoCode::getPromoCodeStatus, true); - List promoCodeList = promoCodeService.list(lambdaQueryWrapper); - ThrowUtils.throwIf(promoCodeList.size() > 0, ErrorCode.OPERATION_ERROR, "当前推广码正在使用中,无法删除"); - promoCodeService.removeByIds(ids); + promoCodeService.delBatchPromoCode(commonBatchRequest); return ResultUtils.success(true); } diff --git a/src/main/java/com/greenorange/promotion/controller/userInfo/UserAccountController.java b/src/main/java/com/greenorange/promotion/controller/userInfo/UserAccountController.java index 82c94b0..98c803e 100644 --- a/src/main/java/com/greenorange/promotion/controller/userInfo/UserAccountController.java +++ b/src/main/java/com/greenorange/promotion/controller/userInfo/UserAccountController.java @@ -58,10 +58,7 @@ public class UserAccountController { @RequiresPermission(mustRole = UserConstant.DEFAULT_ROLE) // @SysLog(title = "用户账户管理", content = "小程序端用户添加用户账户") public BaseResponse addUserAccount(@Valid @RequestBody UserAccountAddRequest userAccountAddRequest, HttpServletRequest request) { - Long userId = (Long) request.getAttribute("userId"); - UserAccount userAccount = commonService.copyProperties(userAccountAddRequest, UserAccount.class); - userAccount.setUserId(userId); - userAccountService.save(userAccount); + userAccountService.addUserAccount(userAccountAddRequest, request); return ResultUtils.success(true); } @@ -75,10 +72,7 @@ public class UserAccountController { @RequiresPermission(mustRole = UserConstant.DEFAULT_ROLE) // @SysLog(title = "用户账户管理", content = "小程序端用户根据id修改用户账户信息") public BaseResponse updateUserAccount(@Valid @RequestBody UserAccountUpdateRequest userAccountUpdateRequest, HttpServletRequest request) { - Long userId = (Long) request.getAttribute("userId"); - UserAccount userAccount = commonService.copyProperties(userAccountUpdateRequest, UserAccount.class); - userAccount.setUserId(userId); - userAccountService.updateById(userAccount); + userAccountService.updateUserAccount(userAccountUpdateRequest, request); return ResultUtils.success(true); } diff --git a/src/main/java/com/greenorange/promotion/model/vo/project/ProjectCardVO.java b/src/main/java/com/greenorange/promotion/model/vo/project/ProjectCardVO.java index 663cfd0..6154a0f 100644 --- a/src/main/java/com/greenorange/promotion/model/vo/project/ProjectCardVO.java +++ b/src/main/java/com/greenorange/promotion/model/vo/project/ProjectCardVO.java @@ -1,13 +1,17 @@ package com.greenorange.promotion.model.vo.project; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; @Data +@NoArgsConstructor +@AllArgsConstructor public class ProjectCardVO implements Serializable { /** diff --git a/src/main/java/com/greenorange/promotion/service/project/ProjectService.java b/src/main/java/com/greenorange/promotion/service/project/ProjectService.java index c59a491..0997d04 100644 --- a/src/main/java/com/greenorange/promotion/service/project/ProjectService.java +++ b/src/main/java/com/greenorange/promotion/service/project/ProjectService.java @@ -1,9 +1,15 @@ package com.greenorange.promotion.service.project; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.greenorange.promotion.model.dto.CommonRequest; import com.greenorange.promotion.model.dto.project.ProjectQueryRequest; import com.greenorange.promotion.model.entity.Project; import com.baomidou.mybatisplus.extension.service.IService; +import com.greenorange.promotion.model.vo.project.ProjectCardVO; +import com.greenorange.promotion.model.vo.project.ProjectVO; +import jakarta.servlet.http.HttpServletRequest; + +import java.util.List; /** * @author 35880 @@ -17,4 +23,17 @@ public interface ProjectService extends IService { * 获取查询条件 */ QueryWrapper getQueryWrapper(ProjectQueryRequest projectQueryRequest); + + + /** + * 小程序用户查看项目列表 + * @return 项目列表 + */ + List queryProjectCardList(HttpServletRequest request); + + + /** + * web端管理员根据id查询项目 + */ + ProjectVO queryProjectById(CommonRequest commonRequest); } diff --git a/src/main/java/com/greenorange/promotion/service/project/PromoCodeService.java b/src/main/java/com/greenorange/promotion/service/project/PromoCodeService.java index ac4cd35..4c65b28 100644 --- a/src/main/java/com/greenorange/promotion/service/project/PromoCodeService.java +++ b/src/main/java/com/greenorange/promotion/service/project/PromoCodeService.java @@ -1,5 +1,6 @@ package com.greenorange.promotion.service.project; +import com.greenorange.promotion.model.dto.CommonBatchRequest; import com.greenorange.promotion.model.entity.PromoCode; import com.baomidou.mybatisplus.extension.service.IService; @@ -10,4 +11,9 @@ import com.baomidou.mybatisplus.extension.service.IService; */ public interface PromoCodeService extends IService { + + /** + * web端管理员批量删除推广码 + */ + void delBatchPromoCode(CommonBatchRequest commonBatchRequest); } diff --git a/src/main/java/com/greenorange/promotion/service/project/impl/ProjectServiceImpl.java b/src/main/java/com/greenorange/promotion/service/project/impl/ProjectServiceImpl.java index 4689965..b6797d2 100644 --- a/src/main/java/com/greenorange/promotion/service/project/impl/ProjectServiceImpl.java +++ b/src/main/java/com/greenorange/promotion/service/project/impl/ProjectServiceImpl.java @@ -2,16 +2,32 @@ package com.greenorange.promotion.service.project.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.greenorange.promotion.common.ErrorCode; +import com.greenorange.promotion.common.ResultUtils; import com.greenorange.promotion.constant.CommonConstant; +import com.greenorange.promotion.exception.ThrowUtils; +import com.greenorange.promotion.model.dto.CommonRequest; import com.greenorange.promotion.model.dto.project.ProjectQueryRequest; import com.greenorange.promotion.model.entity.Project; +import com.greenorange.promotion.model.entity.ProjectCommission; import com.greenorange.promotion.model.entity.UserInfo; +import com.greenorange.promotion.model.vo.project.ProjectCardVO; +import com.greenorange.promotion.model.vo.project.ProjectVO; +import com.greenorange.promotion.service.common.CommonService; +import com.greenorange.promotion.service.project.ProjectCommissionService; import com.greenorange.promotion.service.project.ProjectService; import com.greenorange.promotion.mapper.ProjectMapper; import com.greenorange.promotion.utils.SqlUtils; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * @author 35880 * @description 针对表【project(项目表)】的数据库操作Service实现 @@ -21,6 +37,19 @@ import org.springframework.stereotype.Service; public class ProjectServiceImpl extends ServiceImpl implements ProjectService{ + + @Resource + private CommonService commonService; + + + @Resource + private ProjectService projectService; + + + @Resource + private ProjectCommissionService projectCommissionService; + + /** * 获取查询条件 */ @@ -36,6 +65,49 @@ public class ProjectServiceImpl extends ServiceImpl queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC), sortField); return queryWrapper; } + + + /** + * 小程序用户查看项目列表 + * @return 项目列表 + */ + @Override + public List queryProjectCardList(HttpServletRequest request) { + + Long userId = (Long) request.getAttribute("userId"); + // 获取项目明细抽佣列表 + List projectCommissionList = commonService.findByFieldEqTargetField(ProjectCommission::getUserId, userId, projectCommissionService); + // 封装Map集合(键:项目id, 值:项目总价) + Map projectPriceMap = new HashMap<>(); + for (ProjectCommission projectCommission : projectCommissionList) { + Long projectId = projectCommission.getProjectId(); + BigDecimal projectPrice = projectPriceMap.get(projectId); + if (projectPrice == null) { + projectPrice = projectCommission.getMyUnitPrice(); + } else { + projectPrice = projectPrice.add(projectCommission.getMyUnitPrice()); + } + projectPriceMap.put(projectId, projectPrice); + } + List projectList = commonService.findByFieldEqTargetField(Project::getIsShelves, 1, projectService); + for (Project project : projectList) { + BigDecimal projectPrice = projectPriceMap.get(project.getId()); + project.setProjectPrice(projectPrice == null ? BigDecimal.ZERO : projectPrice); + } + return commonService.convertList(projectList, ProjectCardVO.class); + } + + + /** + * web端管理员根据id查询项目 + */ + @Override + public ProjectVO queryProjectById(CommonRequest commonRequest) { + Long id = commonRequest.getId(); + Project project = projectService.getById(id); + ThrowUtils.throwIf(project == null, ErrorCode.OPERATION_ERROR, "当前项目不存在"); + return commonService.copyProperties(project, ProjectVO.class); + } } diff --git a/src/main/java/com/greenorange/promotion/service/project/impl/PromoCodeServiceImpl.java b/src/main/java/com/greenorange/promotion/service/project/impl/PromoCodeServiceImpl.java index 42b2f25..cca338b 100644 --- a/src/main/java/com/greenorange/promotion/service/project/impl/PromoCodeServiceImpl.java +++ b/src/main/java/com/greenorange/promotion/service/project/impl/PromoCodeServiceImpl.java @@ -1,11 +1,17 @@ package com.greenorange.promotion.service.project.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.greenorange.promotion.common.ErrorCode; +import com.greenorange.promotion.exception.ThrowUtils; +import com.greenorange.promotion.model.dto.CommonBatchRequest; import com.greenorange.promotion.model.entity.PromoCode; import com.greenorange.promotion.service.project.PromoCodeService; import com.greenorange.promotion.mapper.PromoCodeMapper; import org.springframework.stereotype.Service; +import java.util.List; + /** * @author 35880 * @description 针对表【promo_code(推广码信息表)】的数据库操作Service实现 @@ -15,6 +21,19 @@ import org.springframework.stereotype.Service; public class PromoCodeServiceImpl extends ServiceImpl implements PromoCodeService{ + + /** + * web端管理员批量删除推广码 + */ + @Override + public void delBatchPromoCode(CommonBatchRequest commonBatchRequest) { + List ids = commonBatchRequest.getIds(); + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.in(PromoCode::getId, ids).eq(PromoCode::getPromoCodeStatus, true); + List promoCodeList = this.list(lambdaQueryWrapper); + ThrowUtils.throwIf(promoCodeList.size() > 0, ErrorCode.OPERATION_ERROR, "当前推广码正在使用中,无法删除"); + this.removeByIds(ids); + } } diff --git a/src/main/java/com/greenorange/promotion/service/settle/UserAccountService.java b/src/main/java/com/greenorange/promotion/service/settle/UserAccountService.java index 0527893..ff69990 100644 --- a/src/main/java/com/greenorange/promotion/service/settle/UserAccountService.java +++ b/src/main/java/com/greenorange/promotion/service/settle/UserAccountService.java @@ -1,7 +1,10 @@ package com.greenorange.promotion.service.settle; +import com.greenorange.promotion.model.dto.userAccount.UserAccountAddRequest; +import com.greenorange.promotion.model.dto.userAccount.UserAccountUpdateRequest; import com.greenorange.promotion.model.entity.UserAccount; import com.baomidou.mybatisplus.extension.service.IService; +import jakarta.servlet.http.HttpServletRequest; /** * @author 35880 @@ -10,4 +13,14 @@ import com.baomidou.mybatisplus.extension.service.IService; */ public interface UserAccountService extends IService { + /** + * 小程序端用户添加用户账户 + */ + void addUserAccount(UserAccountAddRequest userAccountAddRequest, HttpServletRequest request); + + + /** + * 小程序端用户根据id修改用户账户信息 + */ + void updateUserAccount(UserAccountUpdateRequest userAccountUpdateRequest, HttpServletRequest request); } diff --git a/src/main/java/com/greenorange/promotion/service/settle/impl/UserAccountServiceImpl.java b/src/main/java/com/greenorange/promotion/service/settle/impl/UserAccountServiceImpl.java index 1ca6320..f761580 100644 --- a/src/main/java/com/greenorange/promotion/service/settle/impl/UserAccountServiceImpl.java +++ b/src/main/java/com/greenorange/promotion/service/settle/impl/UserAccountServiceImpl.java @@ -1,9 +1,14 @@ package com.greenorange.promotion.service.settle.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.greenorange.promotion.model.dto.userAccount.UserAccountAddRequest; +import com.greenorange.promotion.model.dto.userAccount.UserAccountUpdateRequest; import com.greenorange.promotion.model.entity.UserAccount; +import com.greenorange.promotion.service.common.CommonService; import com.greenorange.promotion.service.settle.UserAccountService; import com.greenorange.promotion.mapper.UserAccountMapper; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.stereotype.Service; /** @@ -15,6 +20,33 @@ import org.springframework.stereotype.Service; public class UserAccountServiceImpl extends ServiceImpl implements UserAccountService{ + + @Resource + private CommonService commonService; + + + /** + * 小程序端用户添加用户账户 + */ + @Override + public void addUserAccount(UserAccountAddRequest userAccountAddRequest, HttpServletRequest request) { + Long userId = (Long) request.getAttribute("userId"); + UserAccount userAccount = commonService.copyProperties(userAccountAddRequest, UserAccount.class); + userAccount.setUserId(userId); + this.save(userAccount); + } + + + /** + * 小程序端用户根据id修改用户账户信息 + */ + @Override + public void updateUserAccount(UserAccountUpdateRequest userAccountUpdateRequest, HttpServletRequest request) { + Long userId = (Long) request.getAttribute("userId"); + UserAccount userAccount = commonService.copyProperties(userAccountUpdateRequest, UserAccount.class); + userAccount.setUserId(userId); + this.updateById(userAccount); + } } diff --git a/src/test/java/com/greenorange/promotion/ApplicationStartupTest.java b/src/test/java/com/greenorange/promotion/ApplicationStartupTest.java new file mode 100644 index 0000000..eb28b6e --- /dev/null +++ b/src/test/java/com/greenorange/promotion/ApplicationStartupTest.java @@ -0,0 +1,23 @@ +package com.greenorange.promotion; + + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +// 使用 JUnit5 的 SpringExtension 来集成 Spring TestContext +@ExtendWith(SpringExtension.class) +// 指定要启动的 Spring Boot 应用主类;若主类在默认扫描路径下,可省略 classes 属性 +@SpringBootTest(classes = GreenOrangeApplication.class) +public class ApplicationStartupTest { + + /** + * 最简单的 context load 测试: + * 如果 Spring 容器无法启动,这个测试就会失败。 + */ + @Test + void contextLoads() { + // 只要能启动到这里,就证明 Spring Boot 应用上下文加载成功 + } +} diff --git a/src/test/java/com/greenorange/promotion/junit/ProjectServiceImplTest.java b/src/test/java/com/greenorange/promotion/junit/ProjectServiceImplTest.java new file mode 100644 index 0000000..3cf8ff5 --- /dev/null +++ b/src/test/java/com/greenorange/promotion/junit/ProjectServiceImplTest.java @@ -0,0 +1,75 @@ +package com.greenorange.promotion.junit; + +import com.greenorange.promotion.model.dto.CommonRequest; +import com.greenorange.promotion.model.entity.Project; +import com.greenorange.promotion.model.vo.project.ProjectVO; +import com.greenorange.promotion.service.common.CommonService; +import com.greenorange.promotion.service.project.ProjectService; +import com.greenorange.promotion.service.project.impl.ProjectServiceImpl; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class ProjectServiceImplTest { + + @InjectMocks + private ProjectServiceImpl service; + // 把真正的业务实现类注入进来 + + @Mock + private CommonService commonService; + // 用于 copyProperties + + @Mock + private ProjectService projectService; + // 用于 getById + + @Test + void queryProjectById_notFound_throwsException() { + // Arrange + CommonRequest req = new CommonRequest(); + req.setId(10L); + when(projectService.getById(10L)).thenReturn(null); + + // Act & Assert + RuntimeException ex = assertThrows(RuntimeException.class, () -> + service.queryProjectById(req) + ); + assertTrue(ex.getMessage().contains("当前项目不存在")); + + // commonService.copyProperties 不应被调用 + verify(commonService, never()).copyProperties(any(), any()); + } + + @Test + void queryProjectById_found_returnsVO() { + // Arrange + Long projectId = 20L; + CommonRequest req = new CommonRequest(); + req.setId(projectId); + + Project project = new Project(); + project.setId(projectId); + project.setProjectName("示例项目"); + when(projectService.getById(projectId)).thenReturn(project); + + ProjectVO vo = new ProjectVO(); + vo.setId(projectId); + vo.setProjectName("示例项目"); + when(commonService.copyProperties(project, ProjectVO.class)) + .thenReturn(vo); + + // Act + ProjectVO result = service.queryProjectById(req); + + // Assert + assertSame(vo, result, "应返回 commonService.copyProperties 的结果"); + verify(commonService, times(1)).copyProperties(project, ProjectVO.class); + } +} diff --git a/src/test/java/com/greenorange/promotion/junit/PromoCodeServiceImplTest.java b/src/test/java/com/greenorange/promotion/junit/PromoCodeServiceImplTest.java new file mode 100644 index 0000000..7f3d8dd --- /dev/null +++ b/src/test/java/com/greenorange/promotion/junit/PromoCodeServiceImplTest.java @@ -0,0 +1,79 @@ +package com.greenorange.promotion.junit; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.greenorange.promotion.model.dto.CommonBatchRequest; +import com.greenorange.promotion.model.entity.PromoCode; +import com.greenorange.promotion.service.project.impl.PromoCodeServiceImpl; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class PromoCodeServiceImplTest { + + @Spy + @InjectMocks + private PromoCodeServiceImpl service; + // Spy + InjectMocks:使用真实的 PromoCodeServiceImpl,但可以 stub 它的 list(...) 和 removeByIds(...) + + /** + * 当存在状态为“占用”的推广码时,应抛出异常并且不执行删除 + */ + @Test + void delBatchPromoCode_whenCodesInUse_shouldThrow() { + // Arrange + List ids = Arrays.asList(1L, 2L, 3L); + CommonBatchRequest req = new CommonBatchRequest(); + req.setIds(ids); + + // 模拟 list(...) 返回一个非空列表,表示有正在使用的推广码 + PromoCode inUse = new PromoCode(); + inUse.setId(2L); + inUse.setPromoCodeStatus(true); + doReturn(Collections.singletonList(inUse)) + .when(service).list(any(LambdaQueryWrapper.class)); + + // Act & Assert + RuntimeException ex = assertThrows(RuntimeException.class, + () -> service.delBatchPromoCode(req)); + assertTrue(ex.getMessage().contains("当前推广码正在使用中")); + + // 验证 removeByIds(...) 从未被调用 + verify(service, never()).removeByIds(anyList()); + } + + /** + * 当所有推广码都未被占用时,应正常调用 removeByIds 删除 + */ + @Test + void delBatchPromoCode_whenNoCodesInUse_shouldRemove() { + // Arrange + List ids = Arrays.asList(4L, 5L); + CommonBatchRequest req = new CommonBatchRequest(); + req.setIds(ids); + + // 模拟 list(...) 返回空列表,表示无任何占用的推广码 + doReturn(Collections.emptyList()) + .when(service).list(any(LambdaQueryWrapper.class)); + + // 模拟 removeByIds(...) 返回 true,表示删除成功 + doReturn(true).when(service).removeByIds(ids); + + // Act + service.delBatchPromoCode(req); + + // Assert + // 验证 removeByIds(...) 被调用一次,且参数正是传入的 ids + verify(service, times(1)).removeByIds(ids); + } +} diff --git a/src/test/java/com/greenorange/promotion/junit/UserAccountServiceImplTest.java b/src/test/java/com/greenorange/promotion/junit/UserAccountServiceImplTest.java new file mode 100644 index 0000000..dc65457 --- /dev/null +++ b/src/test/java/com/greenorange/promotion/junit/UserAccountServiceImplTest.java @@ -0,0 +1,96 @@ +package com.greenorange.promotion.junit; + +import com.greenorange.promotion.model.dto.userAccount.UserAccountUpdateRequest; +import com.greenorange.promotion.model.entity.UserAccount; +import com.greenorange.promotion.service.common.CommonService; +import com.greenorange.promotion.service.settle.impl.UserAccountServiceImpl; +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class UserAccountServiceImplTest { + + @Spy + @InjectMocks + private UserAccountServiceImpl userAccountService; + // Spy + InjectMocks:用真实的 serviceImpl,但可以对它的方法做部分 stub + + @Mock + private CommonService commonService; + // Mock CommonService,用于模拟 copyProperties + + @Mock + private HttpServletRequest request; + // Mock HttpServletRequest,用于模拟获取 userId + + @Test + void updateUserAccount_shouldCopyProperties_setUserId_andUpdateById() { + // --- Arrange 准备阶段 --- + Long userId = 456L; + // 模拟从 request 中拿到当前登录用户 ID + when(request.getAttribute("userId")).thenReturn(userId); + + // 构造更新请求 DTO,并设置要更新的账户 ID + UserAccountUpdateRequest req = new UserAccountUpdateRequest(); + req.setId(99L); + req.setCardHolder("李四"); + req.setIdCardNumber("110101199002022345"); + req.setPhoneNumber("15900001111"); + req.setBankCardNumber("6222020202020202"); + req.setOpenBank("中国农业银行"); + + // 准备一个空实体,模拟 commonService.copyProperties 拷贝结果 + UserAccount stubEntity = new UserAccount(); + // 假设 copyProperties 会拷贝所有字段,包括 id + stubEntity.setId(req.getId()); + stubEntity.setCardHolder(req.getCardHolder()); + stubEntity.setIdCardNumber(req.getIdCardNumber()); + stubEntity.setPhoneNumber(req.getPhoneNumber()); + stubEntity.setBankCardNumber(req.getBankCardNumber()); + stubEntity.setOpenBank(req.getOpenBank()); + // stub copyProperties 返回我们准备的 stubEntity + when(commonService.copyProperties(req, UserAccount.class)) + .thenReturn(stubEntity); + + // 对 Spy 的 updateById(...) 方法做 stub,避免走到 MyBatis-Plus 真逻辑 + doReturn(true).when(userAccountService).updateById(any(UserAccount.class)); + + // --- Act 执行阶段 --- + userAccountService.updateUserAccount(req, request); + // 方法内部执行顺序: + // 1. 取 request.getAttribute("userId") -> 456L + // 2. commonService.copyProperties(req, UserAccount.class) -> stubEntity + // 3. stubEntity.setUserId(456L) + // 4. 调用 updateById(stubEntity) + + // --- Assert 验证阶段 --- + // 捕获 updateById 调用时传入的参数 + ArgumentCaptor captor = ArgumentCaptor.forClass(UserAccount.class); + verify(userAccountService).updateById(captor.capture()); + UserAccount updated = captor.getValue(); + + // 验证传给 updateById 的正是 stubEntity 对象 + assertSame(stubEntity, updated, "应该传入同一个 stubEntity 实例"); + + // 验证 userId 已正确赋值 + assertEquals(userId, updated.getUserId(), "userId 应该从 request 中取出并赋值"); + + // 验证其他字段都被 copyProperties 拷贝过来,包括账户 ID + assertEquals(req.getId(), updated.getId(), "账户 ID 应保持一致"); + assertEquals("李四", updated.getCardHolder(), "持卡人应一致"); + assertEquals("110101199002022345", updated.getIdCardNumber(), "身份证号应一致"); + assertEquals("15900001111", updated.getPhoneNumber(), "手机号应一致"); + assertEquals("6222020202020202", updated.getBankCardNumber(),"银行卡号应一致"); + assertEquals("中国农业银行", updated.getOpenBank(), "开户银行应一致"); + } +} diff --git a/src/test/java/com/greenorange/promotion/junit/UserInfoServiceImplTest.java b/src/test/java/com/greenorange/promotion/junit/UserInfoServiceImplTest.java new file mode 100644 index 0000000..6c94b62 --- /dev/null +++ b/src/test/java/com/greenorange/promotion/junit/UserInfoServiceImplTest.java @@ -0,0 +1,103 @@ +package com.greenorange.promotion.junit; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.greenorange.promotion.model.dto.userInfo.UserInfoMiniPasswordLoginRequest; +import com.greenorange.promotion.model.entity.UserInfo; +import com.greenorange.promotion.service.userInfo.impl.UserInfoServiceImpl; +import com.greenorange.promotion.utils.JWTUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.*; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class UserInfoServiceImplTest { + + @Spy + @InjectMocks + private UserInfoServiceImpl userService; // 真实的 ServiceImpl,部分方法用 spy + + @Mock + private JWTUtils jwtUtils; // 用来生成 token 的工具 + + @BeforeEach + void setUp() { + // MockitoAnnotations.openMocks(this); // @ExtendWith 已自动初始化 + } + + private UserInfoMiniPasswordLoginRequest buildRequest(String phone, String pwd) { + UserInfoMiniPasswordLoginRequest req = new UserInfoMiniPasswordLoginRequest(); + req.setPhoneNumber(phone); + req.setUserPassword(pwd); + return req; + } + + /** 1. 手机号格式无效,应直接抛参数错误 */ + @Test + void givenInvalidPhone_whenLoginByPwd_thenThrow() { + UserInfoMiniPasswordLoginRequest req = buildRequest("not-a-phone", "whatever"); + assertThrows(RuntimeException.class, () -> userService.userInfoMiniLoginByPwd(req)); + } + + /** 2. 手机号未注册,应抛操作错误 */ + @Test + void givenUnregisteredPhone_whenLoginByPwd_thenThrow() { + // stub 第一次 getOne(...) 返回 null + doReturn(null) + .when(userService).getOne(any(LambdaQueryWrapper.class)); + + UserInfoMiniPasswordLoginRequest req = buildRequest("13812345678", "pwd"); + assertThrows(RuntimeException.class, () -> userService.userInfoMiniLoginByPwd(req)); + } + + /** 3. 密码不正确,应抛操作错误 */ + @Test + void givenWrongPassword_whenLoginByPwd_thenThrow() { + UserInfo dummy = new UserInfo(); + dummy.setPhoneNumber("13812345678"); + dummy.setUserPassword("rightPwd"); + + // stub 第一次 getOne(...) 返回非 null(手机号存在) + // stub 第二次 getOne(...) 返回 null(密码校验失败) + doReturn(dummy, null) + .when(userService).getOne(any(LambdaQueryWrapper.class)); + + UserInfoMiniPasswordLoginRequest req = buildRequest("13812345678", "wrongPwd"); + assertThrows(RuntimeException.class, () -> userService.userInfoMiniLoginByPwd(req)); + } + + /** 4. 成功场景:正确手机号 + 密码,返回 token,并验证 jwtUtils 调用 */ + @Test + void givenValidCredentials_whenLoginByPwd_thenReturnToken() { + UserInfo dummy = new UserInfo(); + dummy.setPhoneNumber("13812345678"); + dummy.setUserPassword("rightPwd"); + + // stub getOne(...) 两次都返回同一个 dummy(表示手机号存在且密码正确) + doReturn(dummy, dummy) + .when(userService).getOne(any(LambdaQueryWrapper.class)); + + // stub jwtUtils.generateToken(...) + String expectedToken = "jwt-token-xyz"; + when(jwtUtils.generateToken(anyMap())).thenReturn(expectedToken); + + UserInfoMiniPasswordLoginRequest req = buildRequest("13812345678", "rightPwd"); + String token = userService.userInfoMiniLoginByPwd(req); + + assertEquals(expectedToken, token); + + // 验证 payload 内容正确 + ArgumentCaptor> captor = ArgumentCaptor.forClass(Map.class); + verify(jwtUtils, times(1)).generateToken(captor.capture()); + Map payload = captor.getValue(); + assertEquals("13812345678", payload.get("userAccount")); + assertEquals("rightPwd", payload.get("userPassword")); + } +}