| | |
| | | import com.webmanage.common.BusinessException; |
| | | import com.webmanage.common.PageResult; |
| | | import com.webmanage.dto.AddPointsFlowDTO; |
| | | import com.webmanage.dto.DeductUserPointsDTO; |
| | | import com.webmanage.dto.PointsFlowQueryDTO; |
| | | import com.webmanage.emun.RuleTypeEnum; |
| | | import com.webmanage.entity.PointsFlow; |
| | |
| | | import com.webmanage.entity.UserPoints; |
| | | import com.webmanage.mapper.PointsFlowMapper; |
| | | import com.webmanage.mapper.UserPointsMapper; |
| | | import com.webmanage.mapper.PointsTransactionMapper; |
| | | import com.webmanage.entity.PointsTransaction; |
| | | import com.webmanage.service.OrderInfoService; |
| | | import com.webmanage.service.PointsFlowService; |
| | | import com.webmanage.service.PointsRuleService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | @Resource |
| | | private PointsRuleService pointsRuleService; |
| | | |
| | | @Resource |
| | | private PointsTransactionMapper pointsTransactionMapper; |
| | | |
| | | |
| | | @Override |
| | | public PageResult<PointsFlow> getPersonalPointsFlowPage(PointsFlowQueryDTO queryDTO) { |
| | | if (queryDTO.getUserId() == null) { |
| | | if (!StringUtils.hasText(queryDTO.getUserId())) { |
| | | throw new BusinessException("用户ID不能为空"); |
| | | } |
| | | |
| | |
| | | throw new BusinessException("参数不能为空"); |
| | | } |
| | | |
| | | Long userId = addPointsFlowDTO.getUserId(); |
| | | Long unitId = addPointsFlowDTO.getUnitId(); |
| | | String userId = addPointsFlowDTO.getUserId(); |
| | | String providerId = addPointsFlowDTO.getProviderId(); |
| | | String unitId = addPointsFlowDTO.getUnitId(); |
| | | Integer ruleType = addPointsFlowDTO.getRuleType(); |
| | | String category = addPointsFlowDTO.getCategory(); |
| | | String ruleNameCode = addPointsFlowDTO.getRuleNameCode(); |
| | | Integer count = addPointsFlowDTO.getCount() != null ? addPointsFlowDTO.getCount() : 1; |
| | | |
| | | // 根据ruleType、ruleNameCode、category查询生效时间最新的积分规则 |
| | | PointsRule pointsRule = getLatestEffectiveRule(ruleType, ruleNameCode, category); |
| | | if (pointsRule == null) { |
| | | throw new BusinessException("积分规则不存在或未启用: ruleType=" + ruleType + ", ruleNameCode=" + ruleNameCode + ", category=" + category); |
| | | boolean applyUser = StringUtils.hasText(userId) ; |
| | | boolean applyProvider = StringUtils.hasText(providerId) ;; |
| | | if (!applyUser && !applyProvider) { |
| | | throw new BusinessException("userId 与 providerId 不能同时为空"); |
| | | } |
| | | |
| | | // 验证规则类型是否匹配 |
| | | if (!ruleType.equals(pointsRule.getRuleType())) { |
| | | throw new BusinessException("规则类型不匹配,期望: " + pointsRule.getRuleType() + ",实际: " + ruleType); |
| | | // 分别获取用户侧/提供者侧最新生效规则(按 points_winner 过滤:1用户、0提供者) |
| | | PointsRule userRule = null; |
| | | PointsRule providerRule = null; |
| | | if (applyUser) { |
| | | userRule = getLatestEffectiveRule(ruleType, ruleNameCode, category, 1); |
| | | if (userRule == null) { |
| | | throw new BusinessException("未找到用户侧生效规则: ruleType=" + ruleType + ", ruleNameCode=" + ruleNameCode + ", category=" + category + ", points_winner=1"); |
| | | } |
| | | if (!ruleType.equals(userRule.getRuleType())) { |
| | | throw new BusinessException("用户侧规则类型不匹配,期望: " + userRule.getRuleType() + ",实际: " + ruleType); |
| | | } |
| | | } |
| | | if (applyProvider) { |
| | | providerRule = getLatestEffectiveRule(ruleType, ruleNameCode, category, 0); |
| | | if (providerRule == null) { |
| | | throw new BusinessException("未找到提供者侧生效规则: ruleType=" + ruleType + ", ruleNameCode=" + ruleNameCode + ", category=" + category + ", points_winner=0"); |
| | | } |
| | | if (!ruleType.equals(providerRule.getRuleType())) { |
| | | throw new BusinessException("提供者侧规则类型不匹配,期望: " + providerRule.getRuleType() + ",实际: " + ruleType); |
| | | } |
| | | } |
| | | |
| | | // 计算积分值 |
| | | Integer basePoints = pointsRule.getPointsValue() != null ? pointsRule.getPointsValue() : 0; |
| | | Integer totalPoints = basePoints * count; |
| | | |
| | | // 如果是消费类型,积分为负数 |
| | | if (ruleType == RuleTypeEnum.CONSUME.getCode()) { // 1表示消费类型 |
| | | totalPoints = -totalPoints; |
| | | // 计算两侧积分变化(各自按规则计算) |
| | | int userPointsChange = 0; |
| | | int providerPointsChange = 0; |
| | | if (applyUser) { |
| | | int base = userRule.getPointsValue() != null ? userRule.getPointsValue() : 0; |
| | | int amount = base * count; |
| | | userPointsChange = (ruleType == RuleTypeEnum.CONSUME.getCode()) ? -Math.abs(amount) : Math.abs(amount); |
| | | } |
| | | if (applyProvider) { |
| | | int base = providerRule.getPointsValue() != null ? providerRule.getPointsValue() : 0; |
| | | int amount = base * count; |
| | | providerPointsChange = (ruleType == RuleTypeEnum.CONSUME.getCode()) ? -Math.abs(amount) : Math.abs(amount); |
| | | } |
| | | |
| | | // 检查每日积分上限 |
| | | if (pointsRule.getIsLimit() != null && pointsRule.getIsLimit() == 0) { // 0表示有每日上限 |
| | | checkDailyLimitByRule(userId, unitId, pointsRule, totalPoints); |
| | | // 每日上限校验(分别校验) |
| | | if (applyUser && userRule.getIsLimit() != null && userRule.getIsLimit() == 0) { |
| | | checkDailyLimitByRule(userId, unitId, userRule, userPointsChange); |
| | | } |
| | | if (applyProvider && providerRule.getIsLimit() != null && providerRule.getIsLimit() == 0) { |
| | | checkDailyLimitByRule(providerId, providerId, providerRule, providerPointsChange); |
| | | } |
| | | |
| | | // 如果是扣积分操作,先检查余额是否足够 |
| | | if (totalPoints < 0) { |
| | | checkBalanceSufficient(userId, unitId, Math.abs(totalPoints)); |
| | | // 余额校验(仅在扣减时) |
| | | if (applyUser && userPointsChange < 0) { |
| | | checkBalanceSufficient(userId, unitId, Math.abs(userPointsChange)); |
| | | } |
| | | if (applyProvider && providerPointsChange < 0) { |
| | | checkBalanceSufficient(providerId, providerId, Math.abs(providerPointsChange)); |
| | | } |
| | | |
| | | // 创建积分流水记录 |
| | | PointsFlow pointsFlow = new PointsFlow(); |
| | | pointsFlow.setUserId(userId); |
| | | pointsFlow.setUnitId(unitId); |
| | | pointsFlow.setDataType(ruleType); |
| | | pointsFlow.setDataCategory(addPointsFlowDTO.getCategory()); |
| | | pointsFlow.setPoints(totalPoints); |
| | | pointsFlow.setName(addPointsFlowDTO.getDescription() != null ? addPointsFlowDTO.getDescription() : pointsRule.getRuleDescription()); |
| | | pointsFlow.setFlowTime(LocalDateTime.now()); |
| | | pointsFlow.setRlueId(pointsRule.getId()); |
| | | // 生成用户侧流水并更新账户 |
| | | if (applyUser && userPointsChange != 0) { |
| | | PointsFlow pointsFlow = new PointsFlow(); |
| | | pointsFlow.setUserId(userId); |
| | | pointsFlow.setUnitId(unitId); |
| | | pointsFlow.setDataType(ruleType); |
| | | pointsFlow.setDataCategory(addPointsFlowDTO.getCategory()); |
| | | pointsFlow.setPoints(userPointsChange); |
| | | pointsFlow.setName(addPointsFlowDTO.getDescription() != null ? addPointsFlowDTO.getDescription() : userRule.getRuleDescription()); |
| | | pointsFlow.setFlowTime(LocalDateTime.now()); |
| | | pointsFlow.setRlueId(userRule.getId()); |
| | | |
| | | boolean saved = save(pointsFlow); |
| | | if (!saved) { |
| | | throw new BusinessException("保存积分流水失败"); |
| | | boolean saved = save(pointsFlow); |
| | | if (!saved) { |
| | | throw new BusinessException("保存积分流水失败"); |
| | | } |
| | | |
| | | updateUserPointsByRule(userId, unitId, userPointsChange); |
| | | } |
| | | |
| | | // 更新用户积分账户 |
| | | updateUserPointsByRule(userId, unitId, totalPoints); |
| | | // 生成提供者侧流水并更新账户 |
| | | if (applyProvider && providerPointsChange != 0) { |
| | | PointsFlow providerFlow = new PointsFlow(); |
| | | providerFlow.setUserId(providerId); |
| | | providerFlow.setUnitId(providerId); |
| | | providerFlow.setDataType(ruleType); |
| | | providerFlow.setDataCategory(addPointsFlowDTO.getCategory()); |
| | | providerFlow.setPoints(providerPointsChange); |
| | | providerFlow.setName(addPointsFlowDTO.getDescription() != null ? addPointsFlowDTO.getDescription() : providerRule.getRuleDescription()); |
| | | providerFlow.setFlowTime(LocalDateTime.now()); |
| | | providerFlow.setRlueId(providerRule.getId()); |
| | | |
| | | boolean providerSaved = save(providerFlow); |
| | | if (!providerSaved) { |
| | | throw new BusinessException("保存提供者积分流水失败"); |
| | | } |
| | | |
| | | updateProviderUnitPoints(providerId, providerPointsChange); |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public UserPoints getUserPointsTotal(Long userId) { |
| | | if (userId == null) { |
| | | public UserPoints getUserPointsTotal(String userId) { |
| | | if (!StringUtils.hasText(userId)) { |
| | | throw new BusinessException("用户ID不能为null"); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * 根据ruleType、ruleNameCode、category、points_winner查询生效时间最新的积分规则 |
| | | */ |
| | | private PointsRule getLatestEffectiveRule(Integer ruleType, String ruleNameCode, String category, Integer pointsWinner) { |
| | | QueryWrapper<PointsRule> wrapper = new QueryWrapper<>(); |
| | | wrapper.eq("is_enabled", 0) |
| | | .eq("rule_type", ruleType) |
| | | .eq("rule_name_code", ruleNameCode) |
| | | .eq("category", category) |
| | | .eq(pointsWinner != null, "points_winner", pointsWinner) |
| | | .orderByDesc("created_at") |
| | | .last("LIMIT 1"); |
| | | return pointsRuleService.getOne(wrapper); |
| | | } |
| | | |
| | | /** |
| | | * 检查每日积分上限(基于规则) |
| | | */ |
| | | private void checkDailyLimitByRule(Long userId, Long unitId, PointsRule pointsRule, Integer currentPoints) { |
| | | private void checkDailyLimitByRule(String userId, String unitId, PointsRule pointsRule, Integer currentPoints) { |
| | | // 获取今日开始和结束时间 |
| | | LocalDate today = LocalDate.now(); |
| | | LocalDateTime startOfDay = today.atStartOfDay(); |
| | |
| | | /** |
| | | * 检查积分余额是否足够 |
| | | */ |
| | | private void checkBalanceSufficient(Long userId, Long unitId, Integer requiredPoints) { |
| | | // 检查个人积分余额 |
| | | private void checkBalanceSufficient(String userId, String unitId, Integer requiredPoints) { |
| | | // 仅检查个人积分余额(移除单位余额判定) |
| | | QueryWrapper<UserPoints> userWrapper = new QueryWrapper<>(); |
| | | userWrapper.eq("deleted", 0) |
| | | .eq("user_id", userId); |
| | |
| | | throw new BusinessException("个人积分余额不足,当前余额: " + (userPoints != null ? userPoints.getBalance() : 0) + ",需要扣除: " + requiredPoints); |
| | | } |
| | | |
| | | // 检查单位积分余额 |
| | | QueryWrapper<UserPoints> unitWrapper = new QueryWrapper<>(); |
| | | unitWrapper.eq("deleted", 0) |
| | | .eq("unit_id", unitId); |
| | | |
| | | UserPoints unitPoints = userPointsMapper.selectOne(unitWrapper); |
| | | if (unitPoints == null || unitPoints.getBalance() < requiredPoints) { |
| | | throw new BusinessException("单位积分余额不足,当前余额: " + (unitPoints != null ? unitPoints.getBalance() : 0) + ",需要扣除: " + requiredPoints); |
| | | } |
| | | // 原单位余额校验已移除 |
| | | } |
| | | |
| | | /** |
| | |
| | | /** |
| | | * 更新用户积分 |
| | | */ |
| | | private void updateUserPoints(Long userId, Long unitId, Integer pointsValue) { |
| | | private void updateUserPoints(String userId, String unitId, Integer pointsValue) { |
| | | // 更新个人积分 |
| | | QueryWrapper<UserPoints> userWrapper = new QueryWrapper<>(); |
| | | userWrapper.eq("deleted", 0) |
| | |
| | | /** |
| | | * 根据规则更新用户积分账户 |
| | | */ |
| | | private void updateUserPointsByRule(Long userId, Long unitId, Integer pointsValue) { |
| | | private void updateUserPointsByRule(String userId, String unitId, Integer pointsValue) { |
| | | // 更新个人积分账户 |
| | | QueryWrapper<UserPoints> userWrapper = new QueryWrapper<>(); |
| | | userWrapper.eq("deleted", 0) |
| | |
| | | userPointsMapper.updateById(unitPoints); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 扣减用户积分 |
| | | */ |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public boolean deductUserPoints(DeductUserPointsDTO deductDTO) { |
| | | try { |
| | | String userId = deductDTO.getUserId(); |
| | | String providerId = deductDTO.getProviderId(); |
| | | String unitId = deductDTO.getUnitId(); |
| | | Integer points = deductDTO.getPoints(); |
| | | String orderId = deductDTO.getOrderId(); |
| | | String remark = deductDTO.getRemark(); |
| | | String dataCategory = deductDTO.getDataCategory(); |
| | | Integer dataType = deductDTO.getDataType(); |
| | | |
| | | if (!StringUtils.hasText(userId)) { |
| | | throw new BusinessException("用户ID不能为空"); |
| | | } |
| | | if (points == null || points <= 0) { |
| | | throw new BusinessException("扣减积分数量必须大于0"); |
| | | } |
| | | |
| | | // 检查用户积分余额是否充足 |
| | | checkBalanceSufficient(userId, unitId, points); |
| | | |
| | | // 创建积分流水记录 |
| | | PointsFlow pointsFlow = new PointsFlow(); |
| | | pointsFlow.setUserId(userId); |
| | | pointsFlow.setUnitId(unitId); |
| | | pointsFlow.setDataCategory(dataCategory != null ? dataCategory : "积分交易"); |
| | | pointsFlow.setDataType(dataType != null ? dataType : 1); |
| | | pointsFlow.setPoints(-points); // 负数表示扣减 |
| | | pointsFlow.setName(remark != null ? remark : "积分扣减"); // name字段存储remark内容 |
| | | pointsFlow.setFlowTime(LocalDateTime.now()); |
| | | pointsFlow.setRlueId(null); // 直接扣减,不关联规则 |
| | | |
| | | boolean saved = save(pointsFlow); |
| | | if (!saved) { |
| | | throw new BusinessException("保存积分流水失败"); |
| | | } |
| | | |
| | | // 更新用户积分账户 |
| | | updateUserPointsOnly(userId, -points); |
| | | |
| | | // 新增积分交易记录(与流水同事务) |
| | | PointsTransaction trans = new PointsTransaction(); |
| | | // tb_points_transaction 的 chk_data_category 仅允许 '用户参与'、'其他' |
| | | // 这里将业务类目映射为数据库允许的值 |
| | | trans.setDataCategory("用户参与"); |
| | | trans.setTransactionName(pointsFlow.getName()); |
| | | trans.setTransactionTime(LocalDateTime.now()); |
| | | trans.setPointsChange(-points); |
| | | trans.setRuleType("消耗"); |
| | | try { |
| | | trans.setUserId(Long.valueOf(userId)); |
| | | } catch (Exception ignore) {} |
| | | try { |
| | | if (StringUtils.hasText(unitId)) trans.setUnitId(Long.valueOf(unitId)); |
| | | } catch (Exception ignore) {} |
| | | trans.setUserType("个人用户"); |
| | | trans.setRuleId(null); |
| | | trans.setDetailId(null); |
| | | trans.setCreatedAt(LocalDateTime.now()); |
| | | trans.setDeleted(0); |
| | | int inserted = pointsTransactionMapper.insert(trans); |
| | | log.info("Points transaction inserted rows={}, id={}", inserted, trans.getId()); |
| | | if (inserted <= 0 || trans.getId() == null) { |
| | | throw new BusinessException("保存积分交易记录失败"); |
| | | } |
| | | // 为提供者新增积分 |
| | | AddPointsFlowDTO addPointsFlowDTO = new AddPointsFlowDTO(); |
| | | addPointsFlowDTO.setCategory("资源交易"); |
| | | addPointsFlowDTO.setCount(1); |
| | | addPointsFlowDTO.setProviderId(providerId); |
| | | addPointsFlowDTO.setRuleType(0); |
| | | addPointsFlowDTO.setRuleNameCode("digital_product_transaction"); |
| | | addPointsFlowDTO.setDescription("产品交易"); |
| | | addPointsFlowByRule(addPointsFlowDTO); |
| | | return true; |
| | | } catch (Exception e) { |
| | | log.error("扣减用户积分失败", e); |
| | | throw new BusinessException("扣减用户积分失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 仅更新提供者(单位)积分账户 |
| | | */ |
| | | private void updateProviderUnitPoints(String providerUnitId, Integer pointsValue) { |
| | | QueryWrapper<UserPoints> unitWrapper = new QueryWrapper<>(); |
| | | unitWrapper.eq("deleted", 0) |
| | | .eq("unit_id", providerUnitId); |
| | | |
| | | UserPoints unitPoints = userPointsMapper.selectOne(unitWrapper); |
| | | if (unitPoints == null) { |
| | | if (pointsValue < 0) { |
| | | throw new BusinessException("提供者积分余额不足,无法扣除积分"); |
| | | } |
| | | |
| | | unitPoints = new UserPoints(); |
| | | unitPoints.setUserId(null); |
| | | unitPoints.setUnitId(providerUnitId); |
| | | unitPoints.setBalance(pointsValue); |
| | | unitPoints.setTotalEarned(pointsValue > 0 ? pointsValue : 0); |
| | | unitPoints.setTotalConsumed(pointsValue < 0 ? Math.abs(pointsValue) : 0); |
| | | userPointsMapper.insert(unitPoints); |
| | | } else { |
| | | if (pointsValue < 0 && unitPoints.getBalance() + pointsValue < 0) { |
| | | throw new BusinessException("提供者积分余额不足,当前余额: " + unitPoints.getBalance() + ",需要扣除: " + Math.abs(pointsValue)); |
| | | } |
| | | |
| | | unitPoints.setBalance(unitPoints.getBalance() + pointsValue); |
| | | |
| | | if (pointsValue > 0) { |
| | | unitPoints.setTotalEarned(unitPoints.getTotalEarned() != null ? |
| | | unitPoints.getTotalEarned() + pointsValue : pointsValue); |
| | | } |
| | | if (pointsValue < 0) { |
| | | unitPoints.setTotalConsumed(unitPoints.getTotalConsumed() != null ? |
| | | unitPoints.getTotalConsumed() + Math.abs(pointsValue) : Math.abs(pointsValue)); |
| | | } |
| | | |
| | | unitPoints.setUpdateTime(LocalDateTime.now()); |
| | | userPointsMapper.updateById(unitPoints); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 仅更新个人积分账户(不操作单位账户) |
| | | */ |
| | | private void updateUserPointsOnly(String userId, Integer pointsValue) { |
| | | // 更新个人积分账户 |
| | | QueryWrapper<UserPoints> userWrapper = new QueryWrapper<>(); |
| | | userWrapper.eq("deleted", 0) |
| | | .eq("user_id", userId); |
| | | UserPoints userPoints = userPointsMapper.selectOne(userWrapper); |
| | | if (userPoints == null) { |
| | | if (pointsValue < 0) { |
| | | throw new BusinessException("个人积分余额不足,无法扣除积分"); |
| | | } |
| | | userPoints = new UserPoints(); |
| | | userPoints.setUserId(userId); |
| | | userPoints.setUnitId(null); |
| | | userPoints.setBalance(pointsValue); |
| | | userPoints.setTotalEarned(pointsValue > 0 ? pointsValue : 0); |
| | | userPoints.setTotalConsumed(pointsValue < 0 ? Math.abs(pointsValue) : 0); |
| | | userPointsMapper.insert(userPoints); |
| | | } else { |
| | | if (pointsValue < 0 && userPoints.getBalance() + pointsValue < 0) { |
| | | throw new BusinessException("个人积分余额不足,当前余额: " + userPoints.getBalance() + ",需要扣除: " + Math.abs(pointsValue)); |
| | | } |
| | | userPoints.setBalance(userPoints.getBalance() + pointsValue); |
| | | if (pointsValue > 0) { |
| | | userPoints.setTotalEarned(userPoints.getTotalEarned() != null ? userPoints.getTotalEarned() + pointsValue : pointsValue); |
| | | } |
| | | if (pointsValue < 0) { |
| | | userPoints.setTotalConsumed(userPoints.getTotalConsumed() != null ? userPoints.getTotalConsumed() + Math.abs(pointsValue) : Math.abs(pointsValue)); |
| | | } |
| | | userPoints.setUpdateTime(LocalDateTime.now()); |
| | | userPointsMapper.updateById(userPoints); |
| | | } |
| | | } |
| | | } |