yanderong
2026-06-23 d4754bb5f0c0d86f71d9268af12245efb0c1ff58
feat: 新增余额记录、出行卡兑换
7个文件已添加
1个文件已修改
603 ■■■■■ 已修改文件
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/api/UserInfoController.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/dao/TravelCardMapper.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/dao/mapping/TravelCard.xml 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/model/TravelCard.java 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/model/vo/BalanceModifyRecordVo.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/model/vo/BalanceRecordVo.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/service/ITravelCardService.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/service/impl/TravelCardServiceImpl.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/api/UserInfoController.java
@@ -8,6 +8,7 @@
import com.stylefeng.guns.modular.account.util.OssUploadUtil;
import com.stylefeng.guns.modular.shunfeng.service.IDriverRideService;
import com.stylefeng.guns.modular.system.model.*;
import com.stylefeng.guns.modular.system.model.vo.BalanceRecordVo;
import com.stylefeng.guns.modular.system.service.*;
import com.stylefeng.guns.modular.system.util.ResultUtil;
import com.stylefeng.guns.modular.system.util.WeChatUtil;
@@ -18,6 +19,7 @@
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
@@ -28,11 +30,13 @@
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * 用户控制器
 */
@Slf4j
@Api
@RestController
@RequestMapping("")
@@ -69,6 +73,9 @@
    @Autowired
    private TDriverPromotionActivityService driverPromotionActivityService;
    @Autowired
    private ITravelCardService travelCardService;
    
    
    /**
@@ -852,9 +859,9 @@
            }
            return userInfoService.phoneLoginBindingWeChat(userId, jscode);
        }catch (Exception e){
            e.printStackTrace();
            return ResultUtil.runErr();
        }
        e.printStackTrace();
        return ResultUtil.runErr();
    }
    }
@@ -960,4 +967,53 @@
        }
        return null;
    }
    /**
     * 我的余额(获取余额记录列表)
     */
    @ResponseBody
    @PostMapping("/api/user/balanceRecord")
    @ApiOperation(value = "用户余额记录", tags = {"用户端-余额记录"}, notes = "")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "类型(1充值,2消费)", name = "type", required = false, dataType = "Integer"),
            @ApiImplicitParam(value = "开始时间", name = "startDate", required = false, dataType = "String"),
            @ApiImplicitParam(value = "j结束时间", name = "endDate", required = false, dataType = "String"),
    })
    public ResultUtil balanceRecord(HttpServletRequest request, Integer type, String startDate, String endDate){
        try {
            Integer userId = userInfoService.getUserIdFormRedis(request);
            if(null == userId){
                return ResultUtil.tokenErr();
            }
            List<BalanceRecordVo> recordVoList = travelCardService.getBalanceModifyRecord(userId, type, startDate, endDate);
            return ResultUtil.success(recordVoList);
        }catch (Exception e){
            log.error("用户余额记录-异常:", e);
            return ResultUtil.runErr();
        }
    }
    /**
     * 用户出行卡兑换
     */
    @ResponseBody
    @PostMapping("/api/user/travelCardExchange")
    @ApiOperation(value = "用户出行卡兑换", tags = {"用户端-出行卡兑换"}, notes = "")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "出行卡号", name = "cardNo", required = true, dataType = "String"),
            @ApiImplicitParam(value = "出行卡密码", name = "cardPassword", required = true, dataType = "String"),
    })
    public ResultUtil travelCardExchange(HttpServletRequest request, String cardNo, String cardPassword){
        try {
            Integer userId = userInfoService.getUserIdFormRedis(request);
            if(null == userId){
                return ResultUtil.tokenErr();
            }
            travelCardService.travelCardExchange(userId, cardNo, cardPassword);
            return ResultUtil.success();
        }catch (Exception e){
            log.error("用户出行卡兑换-异常:", e);
            return ResultUtil.runErr();
        }
    }
}
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/dao/TravelCardMapper.java
New file
@@ -0,0 +1,27 @@
package com.stylefeng.guns.modular.system.dao;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.stylefeng.guns.modular.system.model.TravelCard;
import com.stylefeng.guns.modular.system.model.vo.BalanceModifyRecordVo;
import com.stylefeng.guns.modular.system.model.vo.BalanceRecordVo;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface TravelCardMapper extends BaseMapper<TravelCard> {
    TravelCard getTravelCardByCardNo(@Param("cardNo") String cardNo);
    void insertBalanceModifyRecord(@Param("recordVo") BalanceModifyRecordVo recordVo);
    List<BalanceModifyRecordVo> getBalanceModifyRecord(@Param("userId") Integer userId,
                                                       @Param("type") Integer type,
                                                       @Param("startDate") String startDate,
                                                       @Param("endDate") String endDate);
    List<BalanceRecordVo> getOrderRecord(@Param("userId") Integer userId,
                                         @Param("startDate") String startDate,
                                         @Param("endDate") String endDate);
}
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/dao/mapping/TravelCard.xml
New file
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.stylefeng.guns.modular.system.dao.TravelCardMapper">
    <select id="getTravelCardByCardNo" resultType="com.stylefeng.guns.modular.system.model.TravelCard">
        select * from t_travel_card where cardNo = #{cardNo}
    </select>
    <select id="insertBalanceModifyRecord">
        insert into t_balance_modify_record (recordID, userId, userName, phone, money, type, createTime)
        values (#{recordVo.recordID}, #{recordVo.userId}, #{recordVo.userName}, #{recordVo.phone}, #{recordVo.money}, #{recordVo.type}, #{recordVo.createTime})
    </select>
    <select id="getBalanceModifyRecord" resultType="com.stylefeng.guns.modular.system.model.vo.BalanceModifyRecordVo">
        select * from t_balance_modify_record
        where userId = #{userId}
        <if test="null != type">
            AND type = #{type}
        </if>
        <if test="null != startDate and startDate != ''">
            AND createTime &gt;=  #{startDate}
        </if>
        <if test="null != endDate and endDate != ''">
            AND createTime &lt;=  #{endDate}
        </if>
    </select>
    <select id="getOrderRecord" resultType="com.stylefeng.guns.modular.system.model.vo.BalanceRecordVo">
        select tmp.payTime as payTime, tmp.payMoney as money from (
                 select t.payTime, t.payMoney from t_order_taxi t where t.payTime is not null and t.payType = 3 and userId = #{userId}
                    <if test="null != startDate and startDate != ''">
                        AND payTime &gt;=  #{startDate}
                    </if>
                    <if test="null != endDate and endDate != ''">
                        AND payTime &lt;=  #{endDate}
                    </if>
                 union all
                 select t.payTime, t.payMoney from t_order_private_car t where t.payTime is not null and t.payType = 3 and userId = #{userId}
                    <if test="null != startDate and startDate != ''">
                        AND payTime &gt;=  #{startDate}
                    </if>
                    <if test="null != endDate and endDate != ''">
                        AND payTime &lt;=  #{endDate}
                    </if>
                 union all
                 select t.payTime, t.payMoney from t_order_cross_city t where t.payTime is not null and  t.payType = 3 and userId = #{userId}
                    <if test="null != startDate and startDate != ''">
                        AND payTime &gt;=  #{startDate}
                    </if>
                    <if test="null != endDate and endDate != ''">
                        AND payTime &lt;=  #{endDate}
                    </if>
                 union all
                 select t.payTime, t.money as payMoney from app_order_ride t where t.payTime is not null and t.payType = 1 and userId = #{userId}
                    <if test="null != startDate and startDate != ''">
                        AND payTime &gt;=  #{startDate}
                    </if>
                    <if test="null != endDate and endDate != ''">
                        AND payTime &lt;=  #{endDate}
                    </if>
        ) tmp order by tmp.payTime desc
    </select>
</mapper>
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/model/TravelCard.java
New file
@@ -0,0 +1,187 @@
package com.stylefeng.guns.modular.system.model;
import com.baomidou.mybatisplus.activerecord.Model;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
 * <p>
 *  出行卡包
 * </p>
 */
@TableName("t_travel_card")
public class TravelCard extends Model<TravelCard> {
    private static final long serialVersionUID = 1L;
    /**
     * 主键id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * 卡包ID
     */
    private Integer packageId;
    /**
     * 卡号
     */
    private String cardNo;
    /**
     * 卡密码
     */
    private String cardPassword;
    /**
     * 卡金额
     */
    private BigDecimal amount;
    /**
     * 状态:1未充值,2已充值
     */
    private Integer status;
    /**
     * 充值用户ID
     */
    private Integer rechargeUserId;
    /**
     * 充值用户手机号
     */
    private String rechargeUserPhone;
    /**
     * 充值金额
     */
    private BigDecimal rechargeAmount;
    /**
     * 充值时间
     */
    private Date rechargeTime;
    /**
     * 创建时间
     */
    private Date insertTime;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public Integer getPackageId() {
        return packageId;
    }
    public void setPackageId(Integer packageId) {
        this.packageId = packageId;
    }
    public String getCardNo() {
        return cardNo;
    }
    public void setCardNo(String cardNo) {
        this.cardNo = cardNo;
    }
    public String getCardPassword() {
        return cardPassword;
    }
    public void setCardPassword(String cardPassword) {
        this.cardPassword = cardPassword;
    }
    public BigDecimal getAmount() {
        return amount;
    }
    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }
    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }
    public Integer getRechargeUserId() {
        return rechargeUserId;
    }
    public void setRechargeUserId(Integer rechargeUserId) {
        this.rechargeUserId = rechargeUserId;
    }
    public String getRechargeUserPhone() {
        return rechargeUserPhone;
    }
    public void setRechargeUserPhone(String rechargeUserPhone) {
        this.rechargeUserPhone = rechargeUserPhone;
    }
    public BigDecimal getRechargeAmount() {
        return rechargeAmount;
    }
    public void setRechargeAmount(BigDecimal rechargeAmount) {
        this.rechargeAmount = rechargeAmount;
    }
    public Date getRechargeTime() {
        return rechargeTime;
    }
    public void setRechargeTime(Date rechargeTime) {
        this.rechargeTime = rechargeTime;
    }
    public Date getInsertTime() {
        return insertTime;
    }
    public void setInsertTime(Date insertTime) {
        this.insertTime = insertTime;
    }
    @Override
    protected Serializable pkVal() {
        return this.id;
    }
    @Override
    public String toString() {
        return "TravelCard{" +
                "id=" + id +
                ", packageId=" + packageId +
                ", cardNo='" + cardNo + '\'' +
                ", cardPassword='" + cardPassword + '\'' +
                ", amount=" + amount +
                ", status=" + status +
                ", rechargeUserId=" + rechargeUserId +
                ", rechargeUserPhone='" + rechargeUserPhone + '\'' +
                ", rechargeAmount=" + rechargeAmount +
                ", rechargeTime=" + rechargeTime +
                ", insertTime=" + insertTime +
                '}';
    }
}
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/model/vo/BalanceModifyRecordVo.java
New file
@@ -0,0 +1,72 @@
package com.stylefeng.guns.modular.system.model.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Random;
/**
 * 余额修改记录
 *
 * @author Lux
 * @version 1.0.0
 * @since 2026/6/23 15:06
 */
@Data
public class BalanceModifyRecordVo {
    private Integer id;
    /**
     * 流水ID
     */
    private String recordID;
    /**
     * 用户名称
     */
    private String userName;
    /**
     * 用户手机号
     */
    private String phone;
    /**
     * 用户id
     */
    private Integer userId;
    /**
     * 1=增加,2=减少
     */
    private Integer type;
    /**
     * '添加时间'
     */
    private Date createTime;
    /**
     * '金额'
     */
    private BigDecimal money;
    public static BalanceModifyRecordVo convert(Integer id, String nickName, String phone, BigDecimal amount) {
        Random random = new Random();
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < 8; i++) {
            result.append(random.nextInt(10));
        }
        BalanceModifyRecordVo recordVo = new  BalanceModifyRecordVo();
        recordVo.setRecordID(result.toString());
        recordVo.setUserName(nickName);
        recordVo.setPhone(phone);
        recordVo.setUserId(id);
        recordVo.setType(1); // 出行卡兑换增加
        recordVo.setMoney(amount);
        recordVo.setCreateTime(new Date());
        return recordVo;
    }
}
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/model/vo/BalanceRecordVo.java
New file
@@ -0,0 +1,70 @@
package com.stylefeng.guns.modular.system.model.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;
/**
 * 余额记录
 *
 * @author Lux
 * @version 1.0.0
 * @since 2026/6/23 15:06
 */
@Data
public class BalanceRecordVo {
    /**
     * 用户id
     */
    private Integer userId;
    /**
     * 1=增加,2=减少
     */
    private Integer type;
    /**
     * 类型描述
     */
    private String typeDesc;
    /**
     * 支付时间
     */
    private Date payTime;
    /**
     * 金额
     */
    private BigDecimal money;
    public static BalanceRecordVo convert(BalanceModifyRecordVo modifyRecordVo) {
        BalanceRecordVo recordVo = new BalanceRecordVo();
        recordVo.setUserId(modifyRecordVo.getUserId());
        recordVo.setType(modifyRecordVo.getType());
        recordVo.setPayTime(modifyRecordVo.getCreateTime());
        recordVo.setMoney(modifyRecordVo.getMoney());
        if(Objects.nonNull(modifyRecordVo.getType())) {
            if(modifyRecordVo.getType() == 1) {
                recordVo.setTypeDesc("充值");
            } else if(modifyRecordVo.getType() == 2) {
                recordVo.setTypeDesc("消费");
            }
        }
        return recordVo;
    }
    public static BalanceRecordVo convert(BalanceRecordVo orderVo, Integer userId) {
        BalanceRecordVo recordVo = new BalanceRecordVo();
        recordVo.setUserId(userId);
        recordVo.setType(2);
        recordVo.setPayTime(orderVo.getPayTime());
        recordVo.setMoney(orderVo.getMoney());
        recordVo.setTypeDesc("消费");
        return recordVo;
    }
}
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/service/ITravelCardService.java
New file
@@ -0,0 +1,33 @@
package com.stylefeng.guns.modular.system.service;
import com.baomidou.mybatisplus.service.IService;
import com.stylefeng.guns.modular.system.model.TravelCard;
import com.stylefeng.guns.modular.system.model.vo.BalanceRecordVo;
import java.util.List;
/**
 * <p>
 *     出行卡服务
 * </p>
 */
public interface ITravelCardService extends IService<TravelCard> {
    /**
     * 出行卡兑换
     *
     * @param userId 用户id
     * @param cardNo 卡号
     * @param cardPassword 卡密码
     */
    void travelCardExchange(Integer userId, String cardNo, String cardPassword);
    /**
     * 查询用户余额变更记录
     *
     * @param userId 用户id
     * @return 变更记录列表
     */
    List<BalanceRecordVo> getBalanceModifyRecord(Integer userId, Integer type, String startDate, String endDate);
}
UserYKDTravel/guns-admin/src/main/java/com/stylefeng/guns/modular/system/service/impl/TravelCardServiceImpl.java
New file
@@ -0,0 +1,88 @@
package com.stylefeng.guns.modular.system.service.impl;
import com.aliyun.oss.ServiceException;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.stylefeng.guns.modular.system.dao.TravelCardMapper;
import com.stylefeng.guns.modular.system.model.TravelCard;
import com.stylefeng.guns.modular.system.model.UserInfo;
import com.stylefeng.guns.modular.system.model.vo.BalanceModifyRecordVo;
import com.stylefeng.guns.modular.system.model.vo.BalanceRecordVo;
import com.stylefeng.guns.modular.system.service.ITravelCardService;
import com.stylefeng.guns.modular.system.service.IUserInfoService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
/**
 * <p>
 *    出行卡,服务实现类
 * </p>
 * @author Lux
 * @version 1.0.0
 * @since 2026/6/17 18:21
 */
@Slf4j
@Service
@AllArgsConstructor
public class TravelCardServiceImpl extends ServiceImpl<TravelCardMapper, TravelCard> implements ITravelCardService {
    private final IUserInfoService userInfoService;
    /**
     * 出行卡兑换
     *
     * @param userId 用户id
     * @param cardNo 卡号
     * @param cardPassword 卡密码
     */
    @Transactional
    public void travelCardExchange(Integer userId, String cardNo, String cardPassword) {
        Optional.ofNullable(cardNo).filter(StringUtils::isNotEmpty).orElseThrow(() -> new ServiceException("出行卡兑换-卡号不能为空"));
        Optional.ofNullable(cardPassword).filter(StringUtils::isNotEmpty).orElseThrow(() -> new ServiceException("出行卡兑换-卡密码不能为空"));
        UserInfo userInfo = Optional.ofNullable(userInfoService.selectById(userId)).orElseThrow(() -> new ServiceException("出行卡兑换当前用户不存在"));
        TravelCard travelCard = Optional.ofNullable(this.baseMapper.getTravelCardByCardNo(cardNo)).orElseThrow(() -> new ServiceException("出行卡号不存在"));
        if(!travelCard.getCardPassword().equals(cardPassword)) {
            throw new ServiceException("出行卡密码错误");
        }
        if(Objects.nonNull(travelCard.getAmount())) {
            double newBalance = new BigDecimal(String.valueOf(userInfo.getBalance())).add(travelCard.getAmount()).setScale(2, RoundingMode.HALF_EVEN).doubleValue();
            userInfo.setBalance(newBalance);
            userInfoService.updateById(userInfo);
            travelCard.setStatus(2); // 已充值
            travelCard.setRechargeUserId(userInfo.getId());
            travelCard.setRechargeUserPhone(userInfo.getPhone());
            travelCard.setRechargeAmount(travelCard.getAmount());
            travelCard.setRechargeTime(new Date());
            this.baseMapper.updateById(travelCard);
            // 新增余额变更记录(出行卡兑换类型)
            this.baseMapper.insertBalanceModifyRecord(BalanceModifyRecordVo.convert(userInfo.getId(), userInfo.getNickName(), userInfo.getPhone(), travelCard.getAmount()));
        }
    }
    /**
     * 查询用户余额变更记录
     *
     * @param userId 用户id
     * @return 变更记录列表
     */
    public List<BalanceRecordVo> getBalanceModifyRecord(Integer userId, Integer type, String startDate, String endDate) {
        List<BalanceModifyRecordVo> recordVoList = this.baseMapper.getBalanceModifyRecord(userId, type, 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)) {
            orderRecords.forEach(order -> balanceRecordVos.add(BalanceRecordVo.convert(order, userId)));
        }
        return balanceRecordVos;
    }
}