|
|
@@ -1,219 +1,146 @@
|
|
|
package com.etotem.cfc.service;
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import com.etotem.cfc.entity.PlatformBalanceLog;
|
|
|
-import com.etotem.cfc.entity.User;
|
|
|
import com.etotem.cfc.entity.UserPlatformBalance;
|
|
|
import com.etotem.cfc.mapper.PlatformBalanceLogMapper;
|
|
|
-import com.etotem.cfc.mapper.UserMapper;
|
|
|
import com.etotem.cfc.mapper.UserPlatformBalanceMapper;
|
|
|
-import org.springframework.dao.DuplicateKeyException;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
-import org.springframework.transaction.annotation.Transactional;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
-import com.etotem.cfc.service.NotificationDispatchService;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
-import java.util.Date;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.Map;
|
|
|
|
|
|
+/**
|
|
|
+ * 个人平台积分池服务 —— ⚠️ F-8(2026-10-25)写能力已下线,仅保留只读能力
|
|
|
+ *
|
|
|
+ * <p>本服务原本负责个人「CF 值」(平台积分) 的赚取/消费/冻结/提现全流程。
|
|
|
+ * 该「个人 CF 池」语义已被 F-1~F-7 系列(2026-10-17 起)否定:CF 值不存在「个人池」,
|
|
|
+ * 唯一事实源是统一账本 {@code cf_balance}(成员维度)。迁移 F-6 已将
|
|
|
+ * {@code user_platform_balance} 的可用余额清零、冻结金额释放为可用后,
|
|
|
+ * 该表仅作为只读历史账本保留,不再承接任何写入。
|
|
|
+ *
|
|
|
+ * <p><b>410 Gone</b>:以下写方法统一抛出 {@link IllegalStateException}
|
|
|
+ * (消息含 "410 Gone"),调用方必须迁移到
|
|
|
+ * {@link CfBalanceService}(获取:{@code earnByRule};消耗:{@code spend})。
|
|
|
+ * 参照同类下线的 {@link CommissionDistService}。
|
|
|
+ *
|
|
|
+ * <p><b>保留的只读方法</b>(不写 {@code user_platform_balance},仅用于历史查询/管理报表):
|
|
|
+ * {@link #getBalance}、{@link #getWithdrawable}、{@link #getLogs}。
|
|
|
+ */
|
|
|
@Slf4j
|
|
|
@Service
|
|
|
public class PlatformPointsService {
|
|
|
|
|
|
- private static final String TYPE_EARN = "earn";
|
|
|
- private static final String TYPE_SPEND = "spend";
|
|
|
- private static final String TYPE_FREEZE = "freeze";
|
|
|
- private static final String TYPE_UNFREEZE = "unfreeze";
|
|
|
- private static final String TYPE_WITHDRAW = "withdraw";
|
|
|
-
|
|
|
@Resource
|
|
|
private UserPlatformBalanceMapper balanceMapper;
|
|
|
|
|
|
@Resource
|
|
|
private PlatformBalanceLogMapper logMapper;
|
|
|
|
|
|
- @Resource
|
|
|
- private UserMapper userMapper;
|
|
|
-
|
|
|
- @Resource
|
|
|
- private NotificationDispatchService notificationDispatchService;
|
|
|
-
|
|
|
- /** 获取或创建用户的 CF 值余额记录 */
|
|
|
+ /**
|
|
|
+ * 获取用户 CF 值余额记录(不创建)。
|
|
|
+ *
|
|
|
+ * <p><b>410 Gone(F-8 / 2026-10-25)</b>:迁移前本方法会向 {@code user_platform_balance}
|
|
|
+ * 懒插入空行。迁移 F-6 之后该表仅作只读历史账本保留,禁止再新增行;
|
|
|
+ * 新成员的 CF 余额由 {@link CfBalanceService} 写入 {@code cf_balance}。
|
|
|
+ */
|
|
|
+ @Deprecated
|
|
|
private UserPlatformBalance getOrCreate(Long userId) {
|
|
|
- UserPlatformBalance balance = balanceMapper.selectOne(
|
|
|
- new LambdaQueryWrapper<UserPlatformBalance>()
|
|
|
- .eq(UserPlatformBalance::getUserId, userId));
|
|
|
- if (balance == null) {
|
|
|
- balance = new UserPlatformBalance();
|
|
|
- balance.setUserId(userId);
|
|
|
- balance.setTotalEarned(0);
|
|
|
- balance.setAvailable(0);
|
|
|
- balance.setFrozen(0);
|
|
|
- balance.setWithdrawn(0);
|
|
|
- balance.setExchanged(0);
|
|
|
- balance.setCreatedAt(new Date());
|
|
|
- balance.setUpdatedAt(new Date());
|
|
|
- balanceMapper.insert(balance);
|
|
|
- }
|
|
|
- // 回填家庭 ID(用户进家庭后首次操作时补齐,供流水按家庭维度统计)
|
|
|
- if (balance.getFamilyId() == null) {
|
|
|
- User u = userMapper.selectById(userId);
|
|
|
- if (u != null && u.getFamilyId() != null) {
|
|
|
- balance.setFamilyId(u.getFamilyId());
|
|
|
- balance.setUpdatedAt(new Date());
|
|
|
- balanceMapper.updateById(balance);
|
|
|
- }
|
|
|
- }
|
|
|
- return balance;
|
|
|
+ log.warn("410 Gone:PlatformPointsService.getOrCreate 已下线,user_platform_balance 不再建行,请使用 CfBalanceService");
|
|
|
+ throw new IllegalStateException("410 Gone:PlatformPointsService.getOrCreate 已下线(user_platform_balance 仅作只读历史账本),请使用 CfBalanceService");
|
|
|
}
|
|
|
|
|
|
private int safeInt(Integer v) {
|
|
|
return v == null ? 0 : v;
|
|
|
}
|
|
|
|
|
|
- private void writeLog(Long userId, Long familyId, String type, int amount,
|
|
|
- int balanceAfter, String refType, Long refId, String remark) {
|
|
|
- PlatformBalanceLog log = new PlatformBalanceLog();
|
|
|
- log.setUserId(userId);
|
|
|
- log.setFamilyId(familyId);
|
|
|
- log.setType(type);
|
|
|
- log.setAmount(amount);
|
|
|
- log.setBalanceAfter(balanceAfter);
|
|
|
- log.setRefType(refType);
|
|
|
- log.setRefId(refId);
|
|
|
- log.setRemark(remark);
|
|
|
- log.setCreatedAt(new Date());
|
|
|
- logMapper.insert(log);
|
|
|
- }
|
|
|
-
|
|
|
- /** 用户获得 CF 值 */
|
|
|
- @Transactional
|
|
|
+ /**
|
|
|
+ * 用户获得 CF 值。
|
|
|
+ *
|
|
|
+ * <p><b>410 Gone(F-8 / 2026-10-25)</b>:CF 值不存在「个人池」。获取统一走
|
|
|
+ * {@link CfBalanceService#earnByRule}(24 条获取规则种子见 cf_earn_rule,迁移 408)。
|
|
|
+ */
|
|
|
+ @Deprecated
|
|
|
public void earn(Long userId, int amount, String refType, Long refId, String remark) {
|
|
|
- if (amount <= 0) {
|
|
|
- throw new IllegalArgumentException("CF值数量必须为正数");
|
|
|
- }
|
|
|
- // 幂等:按 (userId, ref_type, ref_id) 去重
|
|
|
- if (refId != null) {
|
|
|
- Long existing = logMapper.selectCount(
|
|
|
- new LambdaQueryWrapper<PlatformBalanceLog>()
|
|
|
- .eq(PlatformBalanceLog::getUserId, userId)
|
|
|
- .eq(PlatformBalanceLog::getRefType, refType)
|
|
|
- .eq(PlatformBalanceLog::getRefId, refId));
|
|
|
- if (existing != null && existing > 0) {
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- UserPlatformBalance b = getOrCreate(userId);
|
|
|
- b.setTotalEarned(safeInt(b.getTotalEarned()) + amount);
|
|
|
- b.setAvailable(safeInt(b.getAvailable()) + amount);
|
|
|
- b.setUpdatedAt(new Date());
|
|
|
- balanceMapper.updateById(b);
|
|
|
- try {
|
|
|
- writeLog(userId, b.getFamilyId(), TYPE_EARN, amount, safeInt(b.getAvailable()),
|
|
|
- refType, refId, remark);
|
|
|
- sendCfNotification(userId, TYPE_EARN, amount, remark);
|
|
|
- } catch (DuplicateKeyException e) {
|
|
|
- throw new RuntimeException("重复发放拦截: userId=" + userId + ", refType=" + refType + ", refId=" + refId, e);
|
|
|
- }
|
|
|
+ log.warn("410 Gone:PlatformPointsService.earn 已下线 userId={}, amount={}, refType={}, refId={}",
|
|
|
+ userId, amount, refType, refId);
|
|
|
+ throw new IllegalStateException("410 Gone:PlatformPointsService.earn 已下线,请使用 CfBalanceService.earnByRule");
|
|
|
}
|
|
|
|
|
|
- /** 扣减可用 CF 值(转让给家庭成员等),余额不足抛异常。原子条件扣减防并发超扣,不计入 exchanged(转让非兑换)。 */
|
|
|
- @Transactional
|
|
|
+ /**
|
|
|
+ * 扣减可用 CF 值(转让给家庭成员等)。
|
|
|
+ *
|
|
|
+ * <p><b>410 Gone(F-8 / 2026-10-25)</b>:消耗统一走 {@link CfSpendDispatchService}
|
|
|
+ * → {@link CfBalanceService#spend},refType 见消费侧清单。
|
|
|
+ */
|
|
|
+ @Deprecated
|
|
|
public void spend(Long userId, int amount, String refType, Long refId, String remark) {
|
|
|
- if (amount <= 0) {
|
|
|
- throw new IllegalArgumentException("CF值数量必须为正数");
|
|
|
- }
|
|
|
- int updated = balanceMapper.update(null, new LambdaUpdateWrapper<UserPlatformBalance>()
|
|
|
- .eq(UserPlatformBalance::getUserId, userId)
|
|
|
- .ge(UserPlatformBalance::getAvailable, amount)
|
|
|
- .setSql("available = available - " + amount)
|
|
|
- .setSql("updated_at = NOW()"));
|
|
|
- if (updated == 0) {
|
|
|
- throw new RuntimeException("CF值余额不足");
|
|
|
- }
|
|
|
- UserPlatformBalance b = getOrCreate(userId);
|
|
|
- writeLog(userId, b.getFamilyId(), TYPE_SPEND, amount, safeInt(b.getAvailable()),
|
|
|
- refType, refId, remark);
|
|
|
- sendCfNotification(userId, TYPE_SPEND, amount, remark);
|
|
|
+ log.warn("410 Gone:PlatformPointsService.spend 已下线 userId={}, amount={}, refType={}, refId={}",
|
|
|
+ userId, amount, refType, refId);
|
|
|
+ throw new IllegalStateException("410 Gone:PlatformPointsService.spend 已下线,请使用 CfSpendDispatchService / CfBalanceService.spend");
|
|
|
}
|
|
|
|
|
|
- /** 提现申请:可用 → 冻结 */
|
|
|
- @Transactional
|
|
|
+ /**
|
|
|
+ * 提现申请:可用 → 冻结。
|
|
|
+ *
|
|
|
+ * <p><b>410 Gone(F-8 / 2026-10-25)</b>:走 {@link CfBalanceService#freeze}(refType=withdrawal)。
|
|
|
+ */
|
|
|
+ @Deprecated
|
|
|
public void freeze(Long userId, int amount, Long refId) {
|
|
|
- if (amount <= 0) {
|
|
|
- throw new IllegalArgumentException("CF值数量必须为正数");
|
|
|
- }
|
|
|
- UserPlatformBalance b = getOrCreate(userId);
|
|
|
- int available = safeInt(b.getAvailable());
|
|
|
- if (available < amount) {
|
|
|
- throw new RuntimeException("CF值余额不足");
|
|
|
- }
|
|
|
- b.setAvailable(available - amount);
|
|
|
- b.setFrozen(safeInt(b.getFrozen()) + amount);
|
|
|
- b.setUpdatedAt(new Date());
|
|
|
- balanceMapper.updateById(b);
|
|
|
- writeLog(userId, b.getFamilyId(), TYPE_FREEZE, amount, safeInt(b.getAvailable()),
|
|
|
- "withdrawal", refId, "提现冻结");
|
|
|
+ log.warn("410 Gone:PlatformPointsService.freeze 已下线 userId={}, amount={}, refId={}", userId, amount, refId);
|
|
|
+ throw new IllegalStateException("410 Gone:PlatformPointsService.freeze 已下线,请使用 CfBalanceService.freeze");
|
|
|
}
|
|
|
|
|
|
- /** 提现审核拒绝:冻结 → 可用 */
|
|
|
- @Transactional
|
|
|
+ /**
|
|
|
+ * 提现审核拒绝:冻结 → 可用。
|
|
|
+ *
|
|
|
+ * <p><b>410 Gone(F-8 / 2026-10-25)</b>:走 {@link CfBalanceService#unfreeze}(refType=withdrawal)。
|
|
|
+ */
|
|
|
+ @Deprecated
|
|
|
public void unfreeze(Long userId, int amount, Long refId) {
|
|
|
- if (amount <= 0) {
|
|
|
- throw new IllegalArgumentException("CF值数量必须为正数");
|
|
|
- }
|
|
|
- UserPlatformBalance b = getOrCreate(userId);
|
|
|
- int frozen = safeInt(b.getFrozen());
|
|
|
- if (frozen < amount) {
|
|
|
- throw new RuntimeException("冻结CF值不足");
|
|
|
- }
|
|
|
- b.setFrozen(frozen - amount);
|
|
|
- b.setAvailable(safeInt(b.getAvailable()) + amount);
|
|
|
- b.setUpdatedAt(new Date());
|
|
|
- balanceMapper.updateById(b);
|
|
|
- writeLog(userId, b.getFamilyId(), TYPE_UNFREEZE, amount, safeInt(b.getAvailable()),
|
|
|
- "withdrawal", refId, "提现审核拒绝解冻");
|
|
|
+ log.warn("410 Gone:PlatformPointsService.unfreeze 已下线 userId={}, amount={}, refId={}", userId, amount, refId);
|
|
|
+ throw new IllegalStateException("410 Gone:PlatformPointsService.unfreeze 已下线,请使用 CfBalanceService.unfreeze");
|
|
|
}
|
|
|
|
|
|
- /** 提现审核通过:冻结 → 已提现 */
|
|
|
- @Transactional
|
|
|
+ /**
|
|
|
+ * 提现审核通过:冻结 → 已提现。
|
|
|
+ *
|
|
|
+ * <p><b>410 Gone(F-8 / 2026-10-25)</b>:走 {@link CfBalanceService#withdraw}(refType=withdrawal)。
|
|
|
+ */
|
|
|
+ @Deprecated
|
|
|
public void confirmWithdraw(Long userId, int amount, Long refId) {
|
|
|
- if (amount <= 0) {
|
|
|
- throw new IllegalArgumentException("CF值数量必须为正数");
|
|
|
- }
|
|
|
- UserPlatformBalance b = getOrCreate(userId);
|
|
|
- int frozen = safeInt(b.getFrozen());
|
|
|
- if (frozen < amount) {
|
|
|
- throw new RuntimeException("冻结CF值不足");
|
|
|
- }
|
|
|
- b.setFrozen(frozen - amount);
|
|
|
- b.setWithdrawn(safeInt(b.getWithdrawn()) + amount);
|
|
|
- b.setUpdatedAt(new Date());
|
|
|
- balanceMapper.updateById(b);
|
|
|
- writeLog(userId, b.getFamilyId(), TYPE_WITHDRAW, amount, safeInt(b.getAvailable()),
|
|
|
- "withdrawal", refId, "提现成功");
|
|
|
+ log.warn("410 Gone:PlatformPointsService.confirmWithdraw 已下线 userId={}, amount={}, refId={}", userId, amount, refId);
|
|
|
+ throw new IllegalStateException("410 Gone:PlatformPointsService.confirmWithdraw 已下线,请使用 CfBalanceService.withdraw");
|
|
|
}
|
|
|
|
|
|
- /** 查询用户 CF 值余额 */
|
|
|
+ /**
|
|
|
+ * 查询用户 CF 值余额(只读历史)。
|
|
|
+ *
|
|
|
+ * <p>注:F-6 迁移已把 {@code user_platform_balance} 可用余额清零,
|
|
|
+ * 这里查到的多为 0 值历史快照;实时 CF 余额请用 {@link CfBalanceService#getBalance}。
|
|
|
+ */
|
|
|
public Map<String, Object> getBalance(Long userId) {
|
|
|
- UserPlatformBalance b = getOrCreate(userId);
|
|
|
+ UserPlatformBalance b = balanceMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<UserPlatformBalance>()
|
|
|
+ .eq(UserPlatformBalance::getUserId, userId));
|
|
|
Map<String, Object> result = new HashMap<>();
|
|
|
- result.put("available", safeInt(b.getAvailable()));
|
|
|
- result.put("frozen", safeInt(b.getFrozen()));
|
|
|
- result.put("withdrawn", safeInt(b.getWithdrawn()));
|
|
|
- result.put("exchanged", safeInt(b.getExchanged()));
|
|
|
- result.put("totalEarned", safeInt(b.getTotalEarned()));
|
|
|
+ result.put("available", b != null ? safeInt(b.getAvailable()) : 0);
|
|
|
+ result.put("frozen", b != null ? safeInt(b.getFrozen()) : 0);
|
|
|
+ result.put("withdrawn", b != null ? safeInt(b.getWithdrawn()) : 0);
|
|
|
+ result.put("exchanged", b != null ? safeInt(b.getExchanged()) : 0);
|
|
|
+ result.put("totalEarned", b != null ? safeInt(b.getTotalEarned()) : 0);
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- /** 查询可提现余额 */
|
|
|
+ /** 查询可提现余额(只读历史,F-6 迁移后恒为 0;实时值用 CfBalanceService) */
|
|
|
public Integer getWithdrawable(Long userId) {
|
|
|
- UserPlatformBalance b = getOrCreate(userId);
|
|
|
- return safeInt(b.getAvailable());
|
|
|
+ UserPlatformBalance b = balanceMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<UserPlatformBalance>()
|
|
|
+ .eq(UserPlatformBalance::getUserId, userId));
|
|
|
+ return b != null ? safeInt(b.getAvailable()) : 0;
|
|
|
}
|
|
|
|
|
|
/** CF 值流水分页 */
|
|
|
@@ -224,28 +151,4 @@ public class PlatformPointsService {
|
|
|
.eq(PlatformBalanceLog::getUserId, userId)
|
|
|
.orderByDesc(PlatformBalanceLog::getCreatedAt));
|
|
|
}
|
|
|
-
|
|
|
- /** 发送 CF 值变更站内通知 */
|
|
|
- private void sendCfNotification(Long userId, String type, int amount, String remark) {
|
|
|
- try {
|
|
|
- String title, content;
|
|
|
- if (TYPE_EARN.equals(type)) {
|
|
|
- title = "获得CF值";
|
|
|
- content = String.format("%s获得%d CF值", remark != null ? remark + " " : "", amount);
|
|
|
- } else if (TYPE_SPEND.equals(type)) {
|
|
|
- title = "CF值支出";
|
|
|
- content = String.format("%s支出%d CF值", remark != null ? remark : "消费", amount);
|
|
|
- } else {
|
|
|
- title = "CF值变动";
|
|
|
- content = String.format("CF值变动%d", amount);
|
|
|
- }
|
|
|
- Map<String, Object> vars = new HashMap<>();
|
|
|
- vars.put("amount", amount);
|
|
|
- vars.put("type", type);
|
|
|
- vars.put("remark", remark);
|
|
|
- notificationDispatchService.sendByTemplate("cf_change", userId, vars, "platformBalanceLog", null, title, content);
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("cf notification failed userId={}, error={}", userId, e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
}
|