puzhibing
4 小时以前 88580dae55106dcf4a8f80693b90638e07af02fa
修改bug
2个文件已添加
15个文件已修改
388 ■■■■■ 已修改文件
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/vo/SiteInfoVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TOrderEvaluate.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppCouponController.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/TAppUserWhitelistService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/TAppUserWhitelistServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/resources/mapper/account/TCompanyAllocateRecordMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/test/java/com/ruoyi/account/service/impl/TAppUserWhitelistServiceImplTest.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TOrderEvaluateController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/ChargingDetails.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TOrderEvaluateServiceImpl.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TCardChargingOrderMapper.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TVinChargingOrderMapper.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TChargingOrderServiceImplVinFallbackTest.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TOrderEvaluateServiceImplVinTypeTest.java 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/20261013_order_evaluate_vin_type.sql 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/charging_pile_order.sql 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/vo/SiteInfoVO.java
@@ -64,6 +64,9 @@
    @ApiModelProperty(value = "是否命中白名单")
    private Boolean whitelistMatched = false;
    @ApiModelProperty(value = "白名单账户可用余额(企业账户充电显示,共享模式=公司可用余额,分配模式=白名单用户余额)")
    private BigDecimal whitelistBalance;
    @ApiModelProperty(value = "白名单权益:1=全部免费,2=免电费,3=免服务费")
    private Integer whitelistBenefitType;
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TOrderEvaluate.java
@@ -31,7 +31,7 @@
    @TableId(value = "id", type = IdType.NONE)
    private Long id;
    @ApiModelProperty(value = "订单类型(1=充电订单,2=购物订单,3=兑换订单,4=会员订单)")
    @ApiModelProperty(value = "订单类型(1=充电订单,2=购物订单,3=兑换订单,4=会员订单,5=VIN码充电订单)")
    @TableField("order_type")
    private Integer orderType;
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppCouponController.java
@@ -179,6 +179,10 @@
            }
            AppUserWhitelistMatchVO whitelist = whitelistService.matchWhitelist(tokenService.getLoginUserApplet().getUserId(), chargingGun.getSiteId());
            data.setWhitelistMatched(Boolean.TRUE.equals(whitelist.getMatched()));
            if (Boolean.TRUE.equals(whitelist.getMatched())) {
                // 充电页余额显示:按白名单所属公司账户模式下发账户可用余额(共享=公司余额,分配=白名单余额)
                data.setWhitelistBalance(whitelistService.resolveWhitelistAccountBalance(whitelist));
            }
            // 白名单权益已下线(充电完全按企业优惠结算),不再下发权益类型与名称,仅保留命中标识
            data.setWhitelistBenefitType(null);
            data.setWhitelistBenefitName(null);
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/TAppUserWhitelistService.java
@@ -50,6 +50,13 @@
    AppUserWhitelistMatchVO matchWhitelist(Long appUserId, Integer siteId);
    /**
     * 解析白名单账户可用余额(小程序充电页余额显示):按所属公司账户模式,
     * 共享模式=公司可用余额,分配模式=白名单用户余额,账户模式未知按共享处理;
     * 公司信息查询失败时降级返回 null(由调用方保持原余额显示)
     */
    BigDecimal resolveWhitelistAccountBalance(AppUserWhitelistMatchVO match);
    /**
     * 按公司查询未删除的白名单用户列表(公司详情-充电用户信息聚合使用)
     */
    List<TAppUserWhitelist> listByCompany(Integer companyId);
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/TAppUserWhitelistServiceImpl.java
@@ -457,6 +457,26 @@
    }
    @Override
    public BigDecimal resolveWhitelistAccountBalance(AppUserWhitelistMatchVO match) {
        if (match == null || !Boolean.TRUE.equals(match.getMatched()) || match.getCompanyId() == null) {
            return null;
        }
        try {
            TGroupCompany company = loadCompany(match.getCompanyId());
            Integer accountMode = company == null ? null : company.getAccountMode();
            if (accountMode == null) {
                // 历史数据账户模式为空,按共享模式处理
                accountMode = ACCOUNT_MODE_SHARED;
            }
            return resolveAvailableBalance(accountMode, match.getBalance(),
                    company == null ? null : company.getAvailableBalance());
        } catch (Exception ex) {
            log.warn("解析白名单账户可用余额失败,companyId={}", match.getCompanyId(), ex);
            return null;
        }
    }
    @Override
    public List<TAppUserWhitelist> listByCompany(Integer companyId) {
        if (companyId == null) {
            return Collections.emptyList();
ruoyi-service/ruoyi-account/src/main/resources/mapper/account/TCompanyAllocateRecordMapper.xml
@@ -4,6 +4,9 @@
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.account.mapper.TCompanyAllocateRecordMapper">
    <!-- 分配记录弹窗只统计「给子账号(VIN码/刷卡/白名单)分配余额」的流水:
         change_type = 1 仅取分配类(1=分配;排除 2=充电结算消费流水,其 after_balance 可为负数);
         user_type != 4 排除账户模式切换时企业账户自身的回退留痕(不属于子账号分配) -->
    <select id="selectRecordPage" resultType="com.ruoyi.account.api.model.TCompanyAllocateRecord">
        SELECT id,
               company_id,
@@ -21,6 +24,8 @@
               create_time
        FROM t_company_allocate_record
        WHERE company_id = #{query.companyId}
          AND change_type = 1
          AND user_type != 4
        ORDER BY operate_time DESC, id DESC
    </select>
</mapper>
ruoyi-service/ruoyi-account/src/test/java/com/ruoyi/account/service/impl/TAppUserWhitelistServiceImplTest.java
@@ -50,6 +50,7 @@
import java.util.concurrent.atomic.AtomicLong;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -259,6 +260,83 @@
        assertEquals(null, result.getFleetId());
    }
    // ---------------- 白名单账户可用余额解析(充电页余额显示) ----------------
    @Test
    void resolveWhitelistAccountBalanceReturnsCompanyBalanceForSharedMode() {
        TGroupCompany company = new TGroupCompany();
        company.setAccountMode(1);
        company.setAvailableBalance(new BigDecimal("500.50"));
        when(groupCompanyClient.getCompanyById(12)).thenReturn(R.ok(company));
        AppUserWhitelistMatchVO match = new AppUserWhitelistMatchVO();
        match.setMatched(true);
        match.setCompanyId(12);
        match.setBalance(new BigDecimal("100.00"));
        BigDecimal result = service.resolveWhitelistAccountBalance(match);
        assertEquals(new BigDecimal("500.50"), result);
    }
    @Test
    void resolveWhitelistAccountBalanceReturnsWhitelistBalanceForAllocationMode() {
        TGroupCompany company = new TGroupCompany();
        company.setAccountMode(2);
        company.setAvailableBalance(new BigDecimal("500.50"));
        when(groupCompanyClient.getCompanyById(12)).thenReturn(R.ok(company));
        AppUserWhitelistMatchVO match = new AppUserWhitelistMatchVO();
        match.setMatched(true);
        match.setCompanyId(12);
        match.setBalance(new BigDecimal("100.00"));
        BigDecimal result = service.resolveWhitelistAccountBalance(match);
        assertEquals(new BigDecimal("100.00"), result);
    }
    @Test
    void resolveWhitelistAccountBalanceTreatsNullAccountModeAsShared() {
        TGroupCompany company = new TGroupCompany();
        company.setAccountMode(null);
        company.setAvailableBalance(new BigDecimal("500.50"));
        when(groupCompanyClient.getCompanyById(12)).thenReturn(R.ok(company));
        AppUserWhitelistMatchVO match = new AppUserWhitelistMatchVO();
        match.setMatched(true);
        match.setCompanyId(12);
        match.setBalance(new BigDecimal("100.00"));
        BigDecimal result = service.resolveWhitelistAccountBalance(match);
        assertEquals(new BigDecimal("500.50"), result);
    }
    @Test
    void resolveWhitelistAccountBalanceReturnsNullWhenCompanyQueryFails() {
        when(groupCompanyClient.getCompanyById(12)).thenReturn(R.fail("公司信息查询失败"));
        AppUserWhitelistMatchVO match = new AppUserWhitelistMatchVO();
        match.setMatched(true);
        match.setCompanyId(12);
        match.setBalance(new BigDecimal("100.00"));
        BigDecimal result = service.resolveWhitelistAccountBalance(match);
        assertNull(result);
    }
    @Test
    void resolveWhitelistAccountBalanceReturnsNullWhenNotMatched() {
        AppUserWhitelistMatchVO match = new AppUserWhitelistMatchVO();
        match.setMatched(false);
        BigDecimal result = service.resolveWhitelistAccountBalance(match);
        assertNull(result);
    }
    // ---------------- 额度分配 ----------------
    @Test
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TOrderEvaluateController.java
@@ -183,7 +183,7 @@
    @ApiOperation(value = "获取订单评价详情", tags = {"小程序-充电记录"})
    @ApiImplicitParams({
            @ApiImplicitParam(value = "订单id", name = "orderId", required = true),
            @ApiImplicitParam(value = "订单类型(1=充电订单,2=购物订单,3=兑换订单,4=会员订单)", name = "orderType", required = true),
            @ApiImplicitParam(value = "订单类型(1=充电订单,2=购物订单,3=兑换订单,4=会员订单,5=VIN码充电订单)", name = "orderType", required = true),
    })
    public AjaxResult<OrderEvaluateVo> getOrderEvaluate(String orderId, Integer orderType){
        OrderEvaluateVo orderEvaluate = orderEvaluateService.getOrderEvaluate(orderId, orderType);
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/ChargingDetails.java
@@ -36,7 +36,7 @@
    private BigDecimal chargedDegrees;
    @ApiModelProperty("已充电时间")
    private String chargedTime;
    @ApiModelProperty("充电费用")
    @ApiModelProperty("已使用费用(设备上报已充金额,缺失时回退订单已使用金额)")
    private BigDecimal chargingCost;
    @ApiModelProperty("充电状态(0=未知,1=等待中,2=启动中,3=充电中,4=停止中,5=已结束)")
    private Integer status;
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -2796,7 +2796,8 @@
        boolean reservationPending = ReservationOrderSupport.isPending(
                one.getStatus(), one.getReservationTime(), LocalDateTime.now());
        chargingDetails.setStatus(reservationPending ? 1 : one.getStatus());
        chargingDetails.setChargingCost(one.getResidualAmount());
        // 已使用费用:默认取订单已使用金额,存在实时监测数据时以设备上报已充金额(电费+服务费)为准
        chargingDetails.setChargingCost(money(one.getResidualAmount()));
        UploadRealTimeMonitoringData data = uploadRealTimeMonitoringDataService.getLastDataById(one.getCode());
        if (null != data) {
            chargingDetails.setChargeCurrent(data.getOutput_current());
@@ -2809,6 +2810,10 @@
            Integer h = Integer.valueOf(data.getCumulative_charging_time() / 60);
            Integer m = Integer.valueOf(data.getCumulative_charging_time() % 60);
            chargingDetails.setChargedTime(String.format("%02d", h) + ":" + String.format("%02d", m));
            // 设备上报已充金额非空时覆盖,上报缺失则沿用订单已使用金额
            if (null != data.getPaid_amount()) {
                chargingDetails.setChargingCost(money(data.getPaid_amount()));
            }
            if (null != one.getAppUserCarId()) {
                TAppUserCar appUserCar = appUserCarClient.getCarById(one.getAppUserCarId().toString()).getData();
                //   续航 = 续航里程 * soc
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TOrderEvaluateServiceImpl.java
@@ -24,6 +24,7 @@
import com.ruoyi.order.api.model.TEvaluationAutoReply;
import com.ruoyi.order.api.model.TOrderEvaluate;
import com.ruoyi.order.api.model.TOrderEvaluateTag;
import com.ruoyi.order.api.model.TVinChargingOrder;
import com.ruoyi.order.api.query.TOrderEvaluateQuery;
import com.ruoyi.order.api.vo.SiteDetailEvaluateVO;
import com.ruoyi.order.api.vo.TOrderEvaluateVO;
@@ -36,6 +37,7 @@
import com.ruoyi.order.service.TChargingOrderService;
import com.ruoyi.order.service.TEvaluationAutoReplyService;
import com.ruoyi.order.service.TOrderEvaluateService;
import com.ruoyi.order.service.TVinChargingOrderService;
import com.ruoyi.order.service.WxSubscribeMessageService;
import com.ruoyi.other.api.domain.TEvaluationTag;
import com.ruoyi.other.api.domain.TIntegralRule;
@@ -90,6 +92,9 @@
    
    @Resource
    private TChargingOrderService chargingOrderService;
    @Resource
    private TVinChargingOrderService vinChargingOrderService;
    
    @Resource
    private IntegralRuleClient integralRuleClient;
@@ -345,9 +350,25 @@
        BeanUtils.copyProperties(query, orderEvaluate);
        Long userid = tokenService.getLoginUserApplet().getUserId();
        orderEvaluate.setAppUserId(userid);
        if(query.getOrderType() == 1){
        Integer orderType = query.getOrderType();
        if(null == orderType){
            return AjaxResult.error("订单类型不能为空");
        }
        if(orderType == 1){
            TChargingOrder chargingOrder = chargingOrderService.getById(query.getOrderId());
            if(null == chargingOrder){
                return AjaxResult.error("订单不存在");
            }
            orderEvaluate.setAppUserCarId(chargingOrder.getAppUserCarId());
        }else if(orderType == 5){
            //VIN码充电订单,仅允许评价本人订单
            TVinChargingOrder vinChargingOrder = vinChargingOrderService.getById(query.getOrderId());
            if(null == vinChargingOrder || !userid.equals(vinChargingOrder.getAppUserId())){
                return AjaxResult.error("订单不存在");
            }
            orderEvaluate.setAppUserCarId(vinChargingOrder.getAppUserCarId());
        }else{
            return AjaxResult.error("订单类型不支持评价");
        }
        TAppUser appUser = appUserClient.getUserById(userid).getData();
    
@@ -392,7 +413,6 @@
                appUser.setPoints(appUser.getPoints() + num1);
                appUserClient.updateAppUser(appUser);
    
                TChargingOrder chargingOrder = chargingOrderService.getById(orderEvaluate.getOrderId());
                //积分变动明细
                TAppUserIntegralChange appUserIntegralChange = new TAppUserIntegralChange();
                appUserIntegralChange.setAppUserId(appUser.getId());
@@ -400,7 +420,7 @@
                appUserIntegralChange.setHistoricalIntegral(points);
                appUserIntegralChange.setCurrentIntegral(points + num1);
                appUserIntegralChange.setCreateTime(LocalDateTime.now());
                appUserIntegralChange.setOrderCode(chargingOrder.getCode());
                appUserIntegralChange.setOrderCode(getOrderCode(orderEvaluate.getOrderType(), orderEvaluate.getOrderId()));
                appUserIntegralChange.setExtension(orderEvaluate.getOrderId().toString());
                appUserIntegralChangeClient.addAppUserIntegralChange(appUserIntegralChange);
            }
@@ -408,6 +428,21 @@
        return AjaxResult.success();
    }
    
    /**
     * 根据评价的订单类型获取对应的订单编号
     * @param orderType 订单类型(1=充电订单,5=VIN码充电订单)
     * @param orderId 订单id
     * @return 订单编号
     */
    private String getOrderCode(Integer orderType, Long orderId) {
        if(Integer.valueOf(5).equals(orderType)){
            TVinChargingOrder vinChargingOrder = vinChargingOrderService.getById(orderId);
            return null == vinChargingOrder ? null : vinChargingOrder.getCode();
        }
        TChargingOrder chargingOrder = chargingOrderService.getById(orderId);
        return null == chargingOrder ? null : chargingOrder.getCode();
    }
    private void applyAutoReply(TOrderEvaluate orderEvaluate) {
        TEvaluationAutoReply rule = evaluationAutoReplyService.matchRule(orderEvaluate.getContent(), orderEvaluate.getMark());
        if (rule == null || StringUtils.isEmpty(rule.getReplyContent())) {
ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TCardChargingOrderMapper.xml
@@ -36,7 +36,7 @@
                AND o.charging_gun_id = #{query.gunId}
            </if>
            <if test="startTime1 != null and startTime1 != '' and startTime2 != null and startTime2 != ''">
                AND o.pay_time BETWEEN #{startTime1} AND #{startTime2}
                AND o.start_time BETWEEN #{startTime1} AND #{startTime2}
            </if>
            <if test="endTime1 != null and endTime1 != '' and endTime2 != null and endTime2 != ''">
                AND o.end_time BETWEEN #{endTime1} AND #{endTime2}
ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TVinChargingOrderMapper.xml
@@ -60,7 +60,7 @@
               o.status,
               CASE WHEN EXISTS (
                   SELECT 1 FROM t_order_evaluate e
                   WHERE e.order_id = o.id AND e.order_type = 1 AND e.del_flag = 0
                   WHERE e.order_id = o.id AND e.order_type = 5 AND e.del_flag = 0
               ) THEN 2 ELSE 1 END AS evaluate
        FROM t_vin_charging_order o
        WHERE o.del_flag = 0
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TChargingOrderServiceImplVinFallbackTest.java
@@ -134,6 +134,46 @@
    }
    @Test
    void getChargingDetailsUsesPaidAmountAsUsedCostWhenRealTimeDataPresent() {
        LoginUserApplet loginUser = new LoginUserApplet();
        loginUser.setUserId(88L);
        when(tokenService.getLoginUserApplet()).thenReturn(loginUser);
        when(mapper.selectOne(any())).thenReturn(null);
        TVinChargingOrder vinOrder = new TVinChargingOrder();
        vinOrder.setId(1001L);
        vinOrder.setAppUserId(88L);
        vinOrder.setChargingGunId(5);
        vinOrder.setStatus(3);
        vinOrder.setCode("VD20261013001");
        vinOrder.setResidualAmount(new BigDecimal("66.60"));
        when(vinChargingOrderMapper.selectOne(any())).thenReturn(vinOrder);
        TChargingGun gun = new TChargingGun();
        gun.setChargingPileId(7);
        when(chargingGunClient.getChargingGunById(5)).thenReturn(R.ok(gun));
        TChargingPile pile = new TChargingPile();
        pile.setName("1号桩");
        pile.setSiteId(9);
        when(chargingPileClient.getChargingPileById(7)).thenReturn(R.ok(pile));
        Site site = new Site();
        site.setName("城东站");
        when(siteClient.getSiteByIds(Arrays.asList(9))).thenReturn(R.ok(Collections.singletonList(site)));
        UploadRealTimeMonitoringData data = new UploadRealTimeMonitoringData();
        data.setOutput_current(new BigDecimal("10"));
        data.setOutput_voltage(new BigDecimal("220"));
        data.setSoc(50);
        data.setTime_remaining(30);
        data.setCharging_degree(new BigDecimal("5.5"));
        data.setCumulative_charging_time(75);
        // 设备上报已充金额为 4 位小数,展示口径统一保留 2 位
        data.setPaid_amount(new BigDecimal("12.3456"));
        when(uploadRealTimeMonitoringDataService.getLastDataById("VD20261013001")).thenReturn(data);
        ChargingDetails details = service.getChargingDetails(5);
        assertEquals(0, new BigDecimal("12.35").compareTo(details.getChargingCost()));
    }
    @Test
    void getChargingDetailsReturnsNullWhenNeitherNormalNorVinOrderExists() {
        LoginUserApplet loginUser = new LoginUserApplet();
        loginUser.setUserId(88L);
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TOrderEvaluateServiceImplVinTypeTest.java
New file
@@ -0,0 +1,165 @@
package com.ruoyi.order.service.impl;
import com.ruoyi.account.api.feignClient.AppUserClient;
import com.ruoyi.account.api.model.TAppUser;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.order.api.model.TOrderEvaluate;
import com.ruoyi.order.api.model.TVinChargingOrder;
import com.ruoyi.order.dto.OrderEvaluateVo;
import com.ruoyi.order.mapper.TOrderEvaluateMapper;
import com.ruoyi.order.service.TChargingOrderService;
import com.ruoyi.order.service.TEvaluationAutoReplyService;
import com.ruoyi.order.service.TVinChargingOrderService;
import com.ruoyi.system.api.model.LoginUserApplet;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
 * VIN 码充电订单评价(orderType=5)分支测试
 * 覆盖:本人 VIN 订单可评价、他人订单/不存在订单被拒、普通充电订单不存在的兜底校验
 */
@ExtendWith(MockitoExtension.class)
class TOrderEvaluateServiceImplVinTypeTest {
    @InjectMocks
    private TOrderEvaluateServiceImpl service;
    @Mock
    private TOrderEvaluateMapper orderEvaluateMapper;
    @Mock
    private TokenService tokenService;
    @Mock
    private TChargingOrderService chargingOrderService;
    @Mock
    private TVinChargingOrderService vinChargingOrderService;
    @Mock
    private AppUserClient appUserClient;
    @Mock
    private TEvaluationAutoReplyService evaluationAutoReplyService;
    @BeforeEach
    void setUp() {
        ReflectionTestUtils.setField(service, "baseMapper", orderEvaluateMapper);
        ReflectionTestUtils.setField(service, "orderEvaluateMapper", orderEvaluateMapper);
        LoginUserApplet loginUser = new LoginUserApplet();
        loginUser.setUserId(88L);
        when(tokenService.getLoginUserApplet()).thenReturn(loginUser);
    }
    private OrderEvaluateVo buildQuery(Long orderId, Integer orderType) {
        OrderEvaluateVo query = new OrderEvaluateVo();
        query.setOrderId(orderId);
        query.setOrderType(orderType);
        query.setMark(5);
        return query;
    }
    @Test
    void addOrderEvaluateForOwnVinOrderStoresTypeFiveAndOwnerCar() {
        TVinChargingOrder vinOrder = new TVinChargingOrder();
        vinOrder.setId(1001L);
        vinOrder.setAppUserId(88L);
        vinOrder.setAppUserCarId(66L);
        vinOrder.setCode("VD_1001");
        when(vinChargingOrderService.getById(1001L)).thenReturn(vinOrder);
        TAppUser appUser = new TAppUser();
        appUser.setId(88L);
        appUser.setWxOpenid("openid_88");
        when(appUserClient.getUserById(88L)).thenReturn(R.ok(appUser));
        when(orderEvaluateMapper.insert(any(TOrderEvaluate.class))).thenReturn(1);
        AjaxResult result = service.addOrderEvaluate(buildQuery(1001L, 5));
        assertTrue(result.isSuccess());
        ArgumentCaptor<TOrderEvaluate> captor = ArgumentCaptor.forClass(TOrderEvaluate.class);
        verify(orderEvaluateMapper).insert(captor.capture());
        TOrderEvaluate saved = captor.getValue();
        assertEquals(Integer.valueOf(5), saved.getOrderType());
        assertEquals(Long.valueOf(88L), saved.getAppUserId());
        assertEquals(Long.valueOf(66L), saved.getAppUserCarId());
        // VIN 订单评价不应再走普通充电订单表
        verify(chargingOrderService, never()).getById(anyLong());
    }
    @Test
    void addOrderEvaluateRejectsVinOrderBelongingToOtherUser() {
        TVinChargingOrder vinOrder = new TVinChargingOrder();
        vinOrder.setId(1001L);
        vinOrder.setAppUserId(99L);
        when(vinChargingOrderService.getById(1001L)).thenReturn(vinOrder);
        AjaxResult result = service.addOrderEvaluate(buildQuery(1001L, 5));
        assertFalse(result.isSuccess());
        assertEquals("订单不存在", result.get(AjaxResult.MSG_TAG));
        verify(orderEvaluateMapper, never()).insert(any(TOrderEvaluate.class));
    }
    @Test
    void addOrderEvaluateRejectsMissingVinOrder() {
        AjaxResult result = service.addOrderEvaluate(buildQuery(1001L, 5));
        assertFalse(result.isSuccess());
        assertEquals("订单不存在", result.get(AjaxResult.MSG_TAG));
        verify(orderEvaluateMapper, never()).insert(any(TOrderEvaluate.class));
    }
    @Test
    void addOrderEvaluateRejectsMissingChargingOrder() {
        AjaxResult result = service.addOrderEvaluate(buildQuery(2002L, 1));
        assertFalse(result.isSuccess());
        assertEquals("订单不存在", result.get(AjaxResult.MSG_TAG));
        verify(orderEvaluateMapper, never()).insert(any(TOrderEvaluate.class));
    }
    @Test
    void addOrderEvaluateRejectsUnsupportedOrderType() {
        AjaxResult result = service.addOrderEvaluate(buildQuery(3003L, 2));
        assertFalse(result.isSuccess());
        assertEquals("订单类型不支持评价", result.get(AjaxResult.MSG_TAG));
        verify(orderEvaluateMapper, never()).insert(any(TOrderEvaluate.class));
    }
    @Test
    void addOrderEvaluateStillWorksForNormalChargingOrder() {
        TChargingOrder chargingOrder = new TChargingOrder();
        chargingOrder.setId(2002L);
        chargingOrder.setAppUserCarId(55L);
        chargingOrder.setCode("CD_2002");
        when(chargingOrderService.getById(2002L)).thenReturn(chargingOrder);
        TAppUser appUser = new TAppUser();
        appUser.setId(88L);
        appUser.setWxOpenid("openid_88");
        when(appUserClient.getUserById(88L)).thenReturn(R.ok(appUser));
        when(orderEvaluateMapper.insert(any(TOrderEvaluate.class))).thenReturn(1);
        AjaxResult result = service.addOrderEvaluate(buildQuery(2002L, 1));
        assertTrue(result.isSuccess());
        ArgumentCaptor<TOrderEvaluate> captor = ArgumentCaptor.forClass(TOrderEvaluate.class);
        verify(orderEvaluateMapper).insert(captor.capture());
        TOrderEvaluate saved = captor.getValue();
        assertEquals(Integer.valueOf(1), saved.getOrderType());
        assertEquals(Long.valueOf(55L), saved.getAppUserCarId());
        verify(vinChargingOrderService, never()).getById(anyLong());
    }
}
sql/20261013_order_evaluate_vin_type.sql
New file
@@ -0,0 +1,6 @@
-- VIN码充电订单评价:t_order_evaluate.order_type 新增取值 5=VIN码充电订单
-- 背景:小程序 VIN 码充电订单列表"评价/查看评价"原先按 order_type=1 提交,后端按普通充电订单表查询导致空指针;
--       现改为独立类型 5,与普通充电订单(1)区分,本脚本仅更新字段注释,无表结构调整。
-- 执行环境:测试库已执行,其他环境发版前同步执行
ALTER TABLE `t_order_evaluate`
    MODIFY COLUMN `order_type` int NULL DEFAULT NULL COMMENT '订单类型(1=充电订单,2=购物订单,3=兑换订单,4=会员订单,5=VIN码充电订单)';
sql/charging_pile_order.sql
@@ -4287,7 +4287,7 @@
DROP TABLE IF EXISTS `t_order_evaluate`;
CREATE TABLE `t_order_evaluate`  (
  `id` bigint NOT NULL COMMENT '主键',
  `order_type` int NULL DEFAULT NULL COMMENT '订单类型(1=充电订单,2=购物订单,3=兑换订单,4=会员订单)',
  `order_type` int NULL DEFAULT NULL COMMENT '订单类型(1=充电订单,2=购物订单,3=兑换订单,4=会员订单,5=VIN码充电订单)',
  `order_id` bigint NULL DEFAULT NULL COMMENT '订单id',
  `app_user_id` bigint NULL DEFAULT NULL COMMENT '用户id',
  `mark` int NULL DEFAULT NULL COMMENT '评分',