ManagementYKDTravel/guns-admin/src/main/webapp/static/img/logo.jpgUserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/config/datasource/MultiDataSourceConfig.java
@@ -6,8 +6,10 @@ import com.stylefeng.guns.core.config.properties.DruidProperties; import com.stylefeng.guns.core.config.properties.MutiDataSourceProperties; import com.stylefeng.guns.core.datascope.DataScopeInterceptor; import com.stylefeng.guns.core.intercept.BalanceUpdateInterceptor; import com.stylefeng.guns.core.mutidatasource.DynamicDataSource; import com.stylefeng.guns.core.mutidatasource.aop.MultiSourceExAop; import org.apache.ibatis.plugin.Interceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -122,4 +124,9 @@ public DataSourceTransactionManager dataSourceTransactionManager(DynamicDataSource mutiDataSource) { return new DataSourceTransactionManager(mutiDataSource); } @Bean public Interceptor balanceUpdateInterceptor() { return new BalanceUpdateInterceptor(); } } UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/config/datasource/SingleDataSourceConfig.java
@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.plugins.PaginationInterceptor; import com.stylefeng.guns.core.config.properties.DruidProperties; import com.stylefeng.guns.core.datascope.DataScopeInterceptor; import com.stylefeng.guns.core.intercept.BalanceUpdateInterceptor; import org.apache.ibatis.plugin.Interceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; @@ -57,5 +59,9 @@ return new OptimisticLockerInterceptor(); } @Bean public Interceptor balanceUpdateInterceptor() { return new BalanceUpdateInterceptor(); } } UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/core/intercept/BalanceUpdateInterceptor.java
New file @@ -0,0 +1,104 @@ package com.stylefeng.guns.core.intercept; import com.stylefeng.guns.modular.system.dao.TravelCardMapper; import com.stylefeng.guns.modular.system.dao.UserInfoMapper; import com.stylefeng.guns.modular.system.model.UserInfo; import com.stylefeng.guns.modular.system.model.vo.BalanceModifyRecordVo; import org.apache.ibatis.javassist.bytecode.analysis.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Signature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.util.Map; import java.util.Objects; import java.util.Properties; /** * 用户余额变更拦截器 * @author sky * @version 1.0.0 * @since 2026/7/3 12:03 */ @Component @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) public class BalanceUpdateInterceptor implements Interceptor { @Autowired private UserInfoMapper userInfoMapper; @Autowired private TravelCardMapper travelCardMapper; @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement ms = (MappedStatement) invocation.getArgs()[0]; Object param = invocation.getArgs()[1]; // 获取SQL类型 SqlCommandType sqlCommandType = ms.getSqlCommandType(); if (sqlCommandType != SqlCommandType.UPDATE) { return invocation.proceed(); } BoundSql boundSql = ms.getBoundSql(param); String sql = boundSql.getSql().toLowerCase(); // 只处理 user 表 if (!sql.contains("t_user")) { return invocation.proceed(); } // 关键:先执行前查旧值(需要拿 userId) Long userId = extractUserId(param); // 查询旧的用户 UserInfo oldUser = userInfoMapper.selectById(userId); // 执行更新 Object result = invocation.proceed(); // 查新的用户 UserInfo newUser = userInfoMapper.selectById(userId); // 新增余额的变更记录(所有余额的变更记录都需要存) if(Objects.nonNull(oldUser) && Objects.nonNull(newUser)) { travelCardMapper.insertBalanceModifyRecord(BalanceModifyRecordVo.convert(oldUser, newUser)); } return result; } @Override public Object plugin(Object target) { return null; } @Override public void setProperties(Properties properties) { } /** * 获取用户id */ private Long extractUserId(Object param) { // 兼容 Map / entity / wrapper if (param instanceof Map) { Object id = ((Map<?, ?>) param).get("id"); return id == null ? null : Long.valueOf(id.toString()); } try { Field f = param.getClass().getDeclaredField("id"); f.setAccessible(true); return (Long) f.get(param); } catch (Exception e) { return null; } } } UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/model/vo/BalanceModifyRecordVo.java
@@ -1,5 +1,6 @@ package com.stylefeng.guns.modular.system.model.vo; import com.stylefeng.guns.modular.system.model.UserInfo; import lombok.Data; import java.math.BigDecimal; @@ -53,7 +54,7 @@ */ private BigDecimal money; public static BalanceModifyRecordVo convert(Integer id, String nickName, String phone, BigDecimal amount) { public static BalanceModifyRecordVo convert(UserInfo oldUser, UserInfo newUser) { Random random = new Random(); StringBuilder result = new StringBuilder(); for (int i = 0; i < 8; i++) { @@ -61,12 +62,21 @@ } BalanceModifyRecordVo recordVo = new BalanceModifyRecordVo(); recordVo.setRecordID(result.toString()); recordVo.setUserName(nickName); recordVo.setPhone(phone); recordVo.setUserId(id); recordVo.setType(1); // 出行卡兑换增加 recordVo.setMoney(amount); recordVo.setUserName(newUser.getNickName()); recordVo.setPhone(newUser.getPhone()); recordVo.setUserId(newUser.getId()); recordVo.setCreateTime(new Date()); BigDecimal newBalance = newUser.getBalance() != null ? BigDecimal.valueOf(newUser.getBalance()) : BigDecimal.ZERO; BigDecimal oldBalance = oldUser.getBalance() != null ? BigDecimal.valueOf(oldUser.getBalance()) : BigDecimal.ZERO; BigDecimal amount = newBalance.subtract(oldBalance); // 判断方向 if (amount.compareTo(BigDecimal.ZERO) > 0) { recordVo.setType(1); // 增加金额(充值) recordVo.setMoney(amount); } else { recordVo.setType(2); // 减少金额,转为正数(消费) recordVo.setMoney(amount.abs()); } return recordVo; } } UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/service/impl/TravelCardServiceImpl.java
@@ -62,7 +62,7 @@ travelCard.setRechargeTime(new Date()); this.baseMapper.updateById(travelCard); // 新增余额变更记录(出行卡兑换类型) this.baseMapper.insertBalanceModifyRecord(BalanceModifyRecordVo.convert(userInfo.getId(), userInfo.getNickName(), userInfo.getPhone(), travelCard.getAmount())); // this.baseMapper.insertBalanceModifyRecord(BalanceModifyRecordVo.convert(userInfo.getId(), userInfo.getNickName(), userInfo.getPhone(), travelCard.getAmount())); } return travelCard.getAmount(); } @@ -74,15 +74,21 @@ * @return 变更记录列表 */ public List<BalanceRecordVo> getBalanceModifyRecord(Integer userId, Integer type, String startDate, String endDate) { if(StringUtils.isNotEmpty(startDate)) { startDate = startDate + " 00:00:00"; } if(StringUtils.isNotEmpty(endDate)) { endDate = endDate + " 23:59:59"; } List<BalanceModifyRecordVo> recordVoList = this.baseMapper.getBalanceModifyRecord(userId, type, startDate, endDate); List<BalanceRecordVo> orderRecords = this.baseMapper.getOrderRecord(userId, startDate, endDate); // List<BalanceRecordVo> orderRecords = this.baseMapper.getOrderRecord(userId, startDate, endDate); List<BalanceRecordVo> balanceRecordVos = new ArrayList<>(); if(CollectionUtils.isNotEmpty(recordVoList)) { recordVoList.forEach(r -> balanceRecordVos.add(BalanceRecordVo.convert(r))); } if(CollectionUtils.isNotEmpty(orderRecords)) { /* if(CollectionUtils.isNotEmpty(orderRecords)) { orderRecords.forEach(order -> balanceRecordVos.add(BalanceRecordVo.convert(order, userId))); } }*/ return balanceRecordVos; }