feat(pin): 重构置顶逻辑并抽离PkPinService
- 将UserController中置顶/取消置顶逻辑下沉到PkPinService,统一事务与异常处理 - UserDao新增原子增减积分方法,避免并发扣减问题 - VVTools抽取SECONDS_PER_HOUR常量并修复向上取整计算 - 新增EpochSecondProvider等接口与实现,为后续测试提供时钟桩 - 补充PkPinServiceImplTests单元测试,覆盖置顶成功、积分不足、重复取消等场景
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
package vvpkassistant.Tools;
|
||||
|
||||
public interface EpochSecondProvider {
|
||||
long nowEpochSecond();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package vvpkassistant.Tools;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SystemEpochSecondProvider implements EpochSecondProvider {
|
||||
@Override
|
||||
public long nowEpochSecond() {
|
||||
return VVTools.currentTimeStamp();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ import java.util.Map;
|
||||
************************/
|
||||
public class VVTools {
|
||||
|
||||
private static final long SECONDS_PER_HOUR = 3600L;
|
||||
|
||||
// 获取当前时间戳
|
||||
public static long currentTimeStamp() {
|
||||
long timeStamp = Calendar.getInstance().getTimeInMillis() / 1000;
|
||||
@@ -117,12 +119,12 @@ public class VVTools {
|
||||
public static long calculateHoursRound(long expireTime, long currentTime) {
|
||||
if (expireTime <= currentTime) return 0;
|
||||
long diffSeconds = expireTime - currentTime;
|
||||
return diffSeconds / 3600;
|
||||
return (diffSeconds + SECONDS_PER_HOUR - 1) / SECONDS_PER_HOUR;
|
||||
}
|
||||
|
||||
// 返还积分用(不足1小时忽略)
|
||||
public static long calculateHoursFloor(long expireTime, long currentTime) {
|
||||
if (expireTime <= currentTime) return 0;
|
||||
return (expireTime - currentTime) / 3600;
|
||||
return (expireTime - currentTime) / SECONDS_PER_HOUR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,21 @@ import vvpkassistant.User.model.UserModel;
|
||||
@Mapper
|
||||
public interface UserDao extends BaseMapper<UserModel> {
|
||||
|
||||
// 原子扣减积分:当 points >= cost 时扣减,返回受影响行数(1=成功,0=积分不足/用户不存在)
|
||||
default int decreasePointsIfEnough(Integer userId, int cost) {
|
||||
return update(null, Wrappers.<UserModel>lambdaUpdate()
|
||||
.eq(UserModel::getId, userId)
|
||||
.ge(UserModel::getPoints, cost)
|
||||
.setSql("points = points - " + cost));
|
||||
}
|
||||
|
||||
// 原子增加积分:返回受影响行数(1=成功,0=用户不存在)
|
||||
default int increasePoints(Integer userId, int amount) {
|
||||
return update(null, Wrappers.<UserModel>lambdaUpdate()
|
||||
.eq(UserModel::getId, userId)
|
||||
.setSql("points = points + " + amount));
|
||||
}
|
||||
|
||||
// 根据用户的手机号查询用户
|
||||
default UserModel queryWithPhoneNumber(String phoneNumber) {
|
||||
return selectOne(Wrappers.<UserModel>lambdaQuery()
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package vvpkassistant.config;
|
||||
|
||||
public interface FunctionConfigProvider {
|
||||
String getValue(String functionName);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package vvpkassistant.config;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class HolderBackedFunctionConfigProvider implements FunctionConfigProvider {
|
||||
@Override
|
||||
public String getValue(String functionName) {
|
||||
return FunctionConfigHolder.getValue(functionName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
import vvpkassistant.CoinRecords.CoinRecords;
|
||||
import vvpkassistant.CoinRecords.CoinRecordsDao;
|
||||
import vvpkassistant.Data.ResponseData;
|
||||
import vvpkassistant.Data.ResponseInfo;
|
||||
import vvpkassistant.Data.WxChatParam;
|
||||
import vvpkassistant.User.mapper.UserDao;
|
||||
import vvpkassistant.User.mapper.SignInRecordDao;
|
||||
@@ -35,6 +34,7 @@ import vvpkassistant.pk.mapper.PkRecordDao;
|
||||
import vvpkassistant.pk.model.PkInfoModel;
|
||||
import vvpkassistant.pk.model.PkRecordDetail;
|
||||
import vvpkassistant.pk.mapper.PkRecordDetailDao;
|
||||
import vvpkassistant.pk.service.PkPinService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
@@ -76,6 +76,9 @@ public class UserController {
|
||||
@Resource
|
||||
private MailService mailService;
|
||||
|
||||
@Resource
|
||||
private PkPinService pkPinService;
|
||||
|
||||
// 配置用户信息
|
||||
@PostMapping("inputUserInfo")
|
||||
public ResponseData<Object> inputUserInfo(@RequestBody UserInputUserInfoDTO param) {
|
||||
@@ -305,81 +308,23 @@ public class UserController {
|
||||
// 置顶文章
|
||||
@PostMapping("pinToTop")
|
||||
public ResponseData<Object> pinToTop(@RequestBody UserPinToTopDTO request) {
|
||||
// 文章id
|
||||
Integer articleId = request.getArticleId();
|
||||
PkInfoModel pkInfoModel = pkInfoDao.selectById(articleId);
|
||||
Integer userId = pkInfoModel.getSenderId();
|
||||
// 到期时间戳
|
||||
Integer pinExpireTime = request.getPinExpireTime();
|
||||
|
||||
long currentTimeStamp = VVTools.currentTimeStamp();
|
||||
long hour = VVTools.calculateHoursRound(pinExpireTime, currentTimeStamp);
|
||||
String coin = FunctionConfigHolder.getValue("置顶扣除积分");
|
||||
int totalCoin = (int) (Integer.parseInt(coin) * hour);
|
||||
|
||||
UserModel userModel = userDao.selectById(userId);
|
||||
if (userModel != null) {
|
||||
// 扣除积分 更新数据
|
||||
Integer points = userModel.getPoints();
|
||||
if (points - totalCoin > 0) {
|
||||
userModel.setPoints(userModel.getPoints() - totalCoin);
|
||||
userDao.updateById(userModel);
|
||||
// 设置置顶到期时间
|
||||
pkInfoModel.setPinExpireTime(pinExpireTime);
|
||||
// 设置创建置顶的时间
|
||||
pkInfoModel.setPinCreateTime((int) VVTools.currentTimeStamp());
|
||||
// 更新pk文章数据
|
||||
int i = pkInfoDao.updateById(pkInfoModel);
|
||||
if (i == 1) {
|
||||
String info = String.format("置顶成功,扣除%d积分",totalCoin);
|
||||
// 增加积分变动记录
|
||||
CoinRecords coinRecords = new CoinRecords("置顶扣除积分",userId,totalCoin, (int) VVTools.currentTimeStamp(),0);
|
||||
coinRecordsDao.insert(coinRecords);
|
||||
// 返回给前端数据
|
||||
if (request == null || request.getArticleId() == null || request.getPinExpireTime() == null) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不能为空");
|
||||
}
|
||||
int operatorUserId = Integer.parseInt(StpUtil.getLoginId().toString());
|
||||
String info = pkPinService.pinToTop(operatorUserId, request.getArticleId(), request.getPinExpireTime());
|
||||
return ResponseData.success(info);
|
||||
}else {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
|
||||
}
|
||||
}else {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR,String.format("积分不足,需要%d积分",totalCoin));
|
||||
}
|
||||
}else {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"用户不存在");
|
||||
}
|
||||
}
|
||||
|
||||
// 取消置顶
|
||||
@PostMapping("cancelPin")
|
||||
public ResponseData<Object> cancelPin(@RequestBody UserCancelPinDTO request) {
|
||||
Integer articleId = request.getArticleId();
|
||||
PkInfoModel pkInfoModel = pkInfoDao.selectById(articleId);
|
||||
Integer pinExpireTime = pkInfoModel.getPinExpireTime();
|
||||
long hour = VVTools.calculateHoursFloor(pinExpireTime, VVTools.currentTimeStamp());
|
||||
|
||||
String coin = FunctionConfigHolder.getValue("置顶扣除积分");
|
||||
// 计算总积分。用于返还给用户
|
||||
int totalCoin = (int) (Integer.parseInt(coin) * hour);
|
||||
|
||||
// 获取用户对象
|
||||
UserModel userModel = userDao.selectById(pkInfoModel.getSenderId());
|
||||
Integer points = userModel.getPoints();
|
||||
// 返还用户积分
|
||||
userModel.setPoints(points + totalCoin);
|
||||
// 更新数据库
|
||||
userDao.updateById(userModel);
|
||||
|
||||
// 重置置顶时间
|
||||
pkInfoModel.setPinExpireTime(0);
|
||||
pkInfoModel.setPinCreateTime(0);
|
||||
int i = pkInfoDao.updateById(pkInfoModel);
|
||||
if (i == 1) {
|
||||
// 添加积分更变相关记录
|
||||
CoinRecords coinRecords = new CoinRecords("取消置顶返还积分",pkInfoModel.getSenderId(),totalCoin, (int) VVTools.currentTimeStamp(),1);
|
||||
coinRecordsDao.insert(coinRecords);
|
||||
return ResponseData.success(String.format("操作成功,返还%d积分",totalCoin));
|
||||
}else {
|
||||
return ResponseData.error(ResponseInfo.ERROR.getCode(),null);
|
||||
if (request == null || request.getArticleId() == null) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不能为空");
|
||||
}
|
||||
int operatorUserId = Integer.parseInt(StpUtil.getLoginId().toString());
|
||||
String info = pkPinService.cancelPin(operatorUserId, request.getArticleId());
|
||||
return ResponseData.success(info);
|
||||
|
||||
}
|
||||
|
||||
|
||||
8
src/main/java/vvpkassistant/pk/service/PkPinService.java
Normal file
8
src/main/java/vvpkassistant/pk/service/PkPinService.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package vvpkassistant.pk.service;
|
||||
|
||||
public interface PkPinService {
|
||||
String pinToTop(int operatorUserId, int articleId, int pinExpireTime);
|
||||
|
||||
String cancelPin(int operatorUserId, int articleId);
|
||||
}
|
||||
|
||||
167
src/main/java/vvpkassistant/pk/service/PkPinServiceImpl.java
Normal file
167
src/main/java/vvpkassistant/pk/service/PkPinServiceImpl.java
Normal file
@@ -0,0 +1,167 @@
|
||||
package vvpkassistant.pk.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import vvpkassistant.CoinRecords.CoinRecords;
|
||||
import vvpkassistant.CoinRecords.CoinRecordsDao;
|
||||
import vvpkassistant.Tools.EpochSecondProvider;
|
||||
import vvpkassistant.Tools.VVTools;
|
||||
import vvpkassistant.User.mapper.UserDao;
|
||||
import vvpkassistant.User.model.UserModel;
|
||||
import vvpkassistant.common.ErrorCode;
|
||||
import vvpkassistant.config.FunctionConfigProvider;
|
||||
import vvpkassistant.exception.BusinessException;
|
||||
import vvpkassistant.pk.mapper.PkInfoDao;
|
||||
import vvpkassistant.pk.model.PkInfoModel;
|
||||
|
||||
@Service
|
||||
public class PkPinServiceImpl implements PkPinService {
|
||||
|
||||
private static final String PIN_COIN_CONFIG_NAME = "置顶扣除积分";
|
||||
private static final int COIN_RECORD_ADD = 1;
|
||||
private static final int COIN_RECORD_DEDUCT = 0;
|
||||
|
||||
private final PkInfoDao pkInfoDao;
|
||||
private final UserDao userDao;
|
||||
private final CoinRecordsDao coinRecordsDao;
|
||||
private final FunctionConfigProvider functionConfigProvider;
|
||||
private final EpochSecondProvider epochSecondProvider;
|
||||
|
||||
public PkPinServiceImpl(
|
||||
PkInfoDao pkInfoDao,
|
||||
UserDao userDao,
|
||||
CoinRecordsDao coinRecordsDao,
|
||||
FunctionConfigProvider functionConfigProvider,
|
||||
EpochSecondProvider epochSecondProvider
|
||||
) {
|
||||
this.pkInfoDao = pkInfoDao;
|
||||
this.userDao = userDao;
|
||||
this.coinRecordsDao = coinRecordsDao;
|
||||
this.functionConfigProvider = functionConfigProvider;
|
||||
this.epochSecondProvider = epochSecondProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String pinToTop(int operatorUserId, int articleId, int pinExpireTime) {
|
||||
long now = epochSecondProvider.nowEpochSecond();
|
||||
ensureExpireTimeValid(pinExpireTime, now);
|
||||
|
||||
PkInfoModel pkInfoModel = pkInfoDao.selectById(articleId);
|
||||
if (pkInfoModel == null) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "该信息不存在");
|
||||
}
|
||||
ensureOwner(operatorUserId, pkInfoModel.getSenderId());
|
||||
|
||||
int costPerHour = getPinCoinPerHour();
|
||||
long hours = VVTools.calculateHoursRound(pinExpireTime, now);
|
||||
int totalCoin = toIntCoin(multiplyCoin(costPerHour, hours, "置顶时长过长"), "置顶时长过长");
|
||||
|
||||
ensureDeductPoints(operatorUserId, totalCoin);
|
||||
updatePinTime(pkInfoModel, pinExpireTime, (int) now);
|
||||
insertCoinRecord("置顶扣除积分", operatorUserId, totalCoin, (int) now, COIN_RECORD_DEDUCT);
|
||||
|
||||
return String.format("置顶成功,扣除%d积分", totalCoin);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String cancelPin(int operatorUserId, int articleId) {
|
||||
long now = epochSecondProvider.nowEpochSecond();
|
||||
|
||||
PkInfoModel pkInfoModel = pkInfoDao.selectById(articleId);
|
||||
if (pkInfoModel == null) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "该信息不存在");
|
||||
}
|
||||
ensureOwner(operatorUserId, pkInfoModel.getSenderId());
|
||||
|
||||
int costPerHour = getPinCoinPerHour();
|
||||
int pinExpireTime = pkInfoModel.getPinExpireTime() == null ? 0 : pkInfoModel.getPinExpireTime();
|
||||
long hours = VVTools.calculateHoursFloor(pinExpireTime, now);
|
||||
int refundCoin = toIntCoin(multiplyCoin(costPerHour, hours, "返还积分计算溢出"), "返还积分计算溢出");
|
||||
|
||||
if (refundCoin > 0) {
|
||||
int updated = userDao.increasePoints(operatorUserId, refundCoin);
|
||||
if (updated != 1) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "用户不存在");
|
||||
}
|
||||
}
|
||||
|
||||
pkInfoModel.setPinExpireTime(0);
|
||||
pkInfoModel.setPinCreateTime(0);
|
||||
if (pkInfoDao.updateById(pkInfoModel) != 1) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
|
||||
}
|
||||
|
||||
insertCoinRecord("取消置顶返还积分", operatorUserId, refundCoin, (int) now, COIN_RECORD_ADD);
|
||||
return String.format("操作成功,返还%d积分", refundCoin);
|
||||
}
|
||||
|
||||
private static void ensureExpireTimeValid(int pinExpireTime, long now) {
|
||||
if (pinExpireTime <= now) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "置顶到期时间必须大于当前时间");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureOwner(int operatorUserId, Integer senderId) {
|
||||
if (senderId == null || senderId != operatorUserId) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "无权限操作");
|
||||
}
|
||||
}
|
||||
|
||||
private int getPinCoinPerHour() {
|
||||
String value = functionConfigProvider.getValue(PIN_COIN_CONFIG_NAME);
|
||||
if (value == null) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "未配置置顶扣除积分");
|
||||
}
|
||||
int coin;
|
||||
try {
|
||||
coin = Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "置顶扣除积分配置错误");
|
||||
}
|
||||
if (coin <= 0) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "置顶扣除积分配置错误");
|
||||
}
|
||||
return coin;
|
||||
}
|
||||
|
||||
private static long multiplyCoin(int costPerHour, long hours, String overflowMessage) {
|
||||
try {
|
||||
return Math.multiplyExact((long) costPerHour, hours);
|
||||
} catch (ArithmeticException e) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, overflowMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private static int toIntCoin(long coin, String overflowMessage) {
|
||||
if (coin > Integer.MAX_VALUE) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, overflowMessage);
|
||||
}
|
||||
return (int) coin;
|
||||
}
|
||||
|
||||
private void ensureDeductPoints(int operatorUserId, int totalCoin) {
|
||||
int updated = userDao.decreasePointsIfEnough(operatorUserId, totalCoin);
|
||||
if (updated == 1) {
|
||||
return;
|
||||
}
|
||||
UserModel userModel = userDao.selectById(operatorUserId);
|
||||
if (userModel == null) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "用户不存在");
|
||||
}
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, String.format("积分不足,需要%d积分", totalCoin));
|
||||
}
|
||||
|
||||
private void updatePinTime(PkInfoModel pkInfoModel, int pinExpireTime, int now) {
|
||||
pkInfoModel.setPinExpireTime(pinExpireTime);
|
||||
pkInfoModel.setPinCreateTime(now);
|
||||
if (pkInfoDao.updateById(pkInfoModel) != 1) {
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private void insertCoinRecord(String desc, int userId, int coin, int now, int type) {
|
||||
coinRecordsDao.insert(new CoinRecords(desc, userId, coin, now, type));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package vvpkassistant.pk.service;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import vvpkassistant.CoinRecords.CoinRecordsDao;
|
||||
import vvpkassistant.Tools.EpochSecondProvider;
|
||||
import vvpkassistant.User.mapper.UserDao;
|
||||
import vvpkassistant.User.model.UserModel;
|
||||
import vvpkassistant.common.ErrorCode;
|
||||
import vvpkassistant.config.FunctionConfigProvider;
|
||||
import vvpkassistant.exception.BusinessException;
|
||||
import vvpkassistant.pk.mapper.PkInfoDao;
|
||||
import vvpkassistant.pk.model.PkInfoModel;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class PkPinServiceImplTests {
|
||||
|
||||
@Test
|
||||
void shouldFailWhenExpireTimeNotInFuture() {
|
||||
PkPinServiceImpl service = newServiceWithNow(100L);
|
||||
BusinessException ex = Assertions.assertThrows(BusinessException.class,
|
||||
() -> service.pinToTop(1, 10, 100));
|
||||
Assertions.assertEquals(ErrorCode.PARAMS_ERROR.getCode(), ex.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailWhenPointsNotEnoughEvenIfLessThanOneHour() {
|
||||
long now = 1_000L;
|
||||
|
||||
PkInfoDao pkInfoDao = mock(PkInfoDao.class);
|
||||
UserDao userDao = mock(UserDao.class);
|
||||
CoinRecordsDao coinRecordsDao = mock(CoinRecordsDao.class);
|
||||
FunctionConfigProvider functionConfigProvider = mock(FunctionConfigProvider.class);
|
||||
EpochSecondProvider epochSecondProvider = () -> now;
|
||||
|
||||
when(functionConfigProvider.getValue("置顶扣除积分")).thenReturn("10");
|
||||
|
||||
PkInfoModel pkInfoModel = new PkInfoModel();
|
||||
pkInfoModel.setId(10);
|
||||
pkInfoModel.setSenderId(1);
|
||||
when(pkInfoDao.selectById(10)).thenReturn(pkInfoModel);
|
||||
|
||||
when(userDao.decreasePointsIfEnough(1, 10)).thenReturn(0);
|
||||
UserModel userModel = new UserModel();
|
||||
userModel.setId(1);
|
||||
userModel.setPoints(9);
|
||||
when(userDao.selectById(1)).thenReturn(userModel);
|
||||
|
||||
PkPinServiceImpl service = new PkPinServiceImpl(
|
||||
pkInfoDao, userDao, coinRecordsDao, functionConfigProvider, epochSecondProvider
|
||||
);
|
||||
|
||||
int pinExpireTime = (int) (now + 1);
|
||||
BusinessException ex = Assertions.assertThrows(BusinessException.class,
|
||||
() -> service.pinToTop(1, 10, pinExpireTime));
|
||||
Assertions.assertTrue(ex.getMessage().contains("积分不足"));
|
||||
|
||||
verify(pkInfoDao, never()).updateById(any());
|
||||
verify(coinRecordsDao, never()).insert(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSucceedAndChargeOneHourWhenJustOneSecond() {
|
||||
long now = 2_000L;
|
||||
|
||||
PkInfoDao pkInfoDao = mock(PkInfoDao.class);
|
||||
UserDao userDao = mock(UserDao.class);
|
||||
CoinRecordsDao coinRecordsDao = mock(CoinRecordsDao.class);
|
||||
FunctionConfigProvider functionConfigProvider = mock(FunctionConfigProvider.class);
|
||||
EpochSecondProvider epochSecondProvider = () -> now;
|
||||
|
||||
when(functionConfigProvider.getValue("置顶扣除积分")).thenReturn("10");
|
||||
|
||||
PkInfoModel pkInfoModel = new PkInfoModel();
|
||||
pkInfoModel.setId(10);
|
||||
pkInfoModel.setSenderId(1);
|
||||
when(pkInfoDao.selectById(10)).thenReturn(pkInfoModel);
|
||||
|
||||
when(userDao.decreasePointsIfEnough(1, 10)).thenReturn(1);
|
||||
when(pkInfoDao.updateById(any())).thenReturn(1);
|
||||
when(coinRecordsDao.insert(any())).thenReturn(1);
|
||||
|
||||
PkPinServiceImpl service = new PkPinServiceImpl(
|
||||
pkInfoDao, userDao, coinRecordsDao, functionConfigProvider, epochSecondProvider
|
||||
);
|
||||
|
||||
int pinExpireTime = (int) (now + 1);
|
||||
String msg = service.pinToTop(1, 10, pinExpireTime);
|
||||
Assertions.assertTrue(msg.contains("扣除10积分"));
|
||||
|
||||
ArgumentCaptor<PkInfoModel> captor = ArgumentCaptor.forClass(PkInfoModel.class);
|
||||
verify(pkInfoDao).updateById(captor.capture());
|
||||
Assertions.assertEquals(Integer.valueOf(pinExpireTime), captor.getValue().getPinExpireTime());
|
||||
Assertions.assertEquals(Integer.valueOf((int) now), captor.getValue().getPinCreateTime());
|
||||
}
|
||||
|
||||
private static PkPinServiceImpl newServiceWithNow(long now) {
|
||||
PkInfoDao pkInfoDao = mock(PkInfoDao.class);
|
||||
UserDao userDao = mock(UserDao.class);
|
||||
CoinRecordsDao coinRecordsDao = mock(CoinRecordsDao.class);
|
||||
FunctionConfigProvider functionConfigProvider = mock(FunctionConfigProvider.class);
|
||||
EpochSecondProvider epochSecondProvider = () -> now;
|
||||
return new PkPinServiceImpl(pkInfoDao, userDao, coinRecordsDao, functionConfigProvider, epochSecondProvider);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user