| | |
| | | | 2026-09-28 17:33:00(估算,含授权前方案分析与两轮确认问答) | 2026-09-28 17:39:39 | 约 399(估算,授权后执行 188 秒 + 方案分析约 211 秒) | 推送代码,编写完整的修改描述 | 是:先只读核查两仓库分支/远端/改动清单与编译产物,发现工作区含两个功能块改动(充电用户信息 + 优惠设置/集团联系人),经两轮确认问答确定「全部推送、每仓库一个 commit」后获「开始执行」授权 | 提交并推送公司详情充电用户信息聚合表格、公司优惠设置/账户模式改造、集团联系人字段三批改动;服务端提交 `29e9ce47`,管理后台提交 `a84f8954` | 服务端 38 个文件(VIN/刷卡/白名单三套 list-by-company 内部接口与 Feign/FallbackFactory、TGroupCompany 聚合与 CompanyChargingUserVO、TCompanyPreferential 全套与 CompanyPreferentialSupport、TGroup 联系人字段、sql/20260928_company_preferential.sql、sql/20260928_group_contact.sql、三个测试类、doc/耗时统计.md、sql/20260926_统一执行清单.md);管理后台 9 个文件(company/index.jsx、service.js、RechargeModal.jsx、AccountModeModal.jsx、PreferentialModal.jsx、companyPayload/preferential 及各自单测) | 后端 `mvn -pl ruoyi-api/ruoyi-api-account,ruoyi-api/ruoyi-api-other,ruoyi-service/ruoyi-account,ruoyi-service/ruoyi-other -am compile` BUILD SUCCESS(17/17 模块);前端删 dist 后 `npm run build` 成功(Webpack Compiled successfully,dist 生成);推送后远端引用核验 aa8119b5..29e9ce47(2.3)、686b5246..a84f8954(dev_2.3);IDE lint 0 错误 | company_preferential 与 group_contact 两个 DDL 尚未执行(测试库待执行,见 sql/20260926_统一执行清单.md 第十节);本统计行在推送后追加,未随本次提交推送 | |
| | | | 2026-09-28 18:07:00(估算,授权后执行阶段;含授权前方案分析与两轮确认约 19:xx 起) | 2026-09-28 18:40:44 | 约 2024(估算,授权后执行约 34 分钟;含授权前方案分析与两轮确认问答总计约 55 分钟) | 分析代码,将VIN码、卡片、白名单全部都舍弃init_quota字段,统一使用balance字段表示余额,额度分配和初始额度也是修改balance字段 | 是:先只读盘点 init_quota 全部 40 处引用(模型/DTO/Mapper/Service/前端页面/历史 SQL),输出三表统一方案(VIN/白名单 CHANGE 列名保留数据、卡片 DROP 列丢弃、前端仅改字段不改文案),经两轮确认问答(卡片历史数据直接丢弃、文案保留原表述、VIN 与白名单一同修改)后获「开始执行」授权 | 三模块舍弃 init_quota 统一 balance:VIN/白名单模型 DTO 改名 balance,卡片模型/DTO 删除 initQuota 复用既有 balance;额度分配与初始额度写库改为 balance 列;卡片编辑仅传正数余额才更新 balance(未传保持原余额);新建统一 DDL 脚本并追加执行清单第十一节 | 服务端 20 个文件(3 模型+3 DTO、TVinManagementMapper.java、三个 Mapper XML、三个 ServiceImpl、TGroupCompanyServiceImpl、三个测试类、sql/20260928_unify_quota_to_balance.sql 新建、sql/20260926_统一执行清单.md、doc/耗时统计.md);管理后台 6 个文件(vin/index.jsx、vin/AllocateModal.jsx、card/index.jsx、person/whitelist/index.jsx、whitelist/AddAndEdit.jsx、fleet/index.jsx) | 后端 `mvn -pl ruoyi-api-account,ruoyi-account,ruoyi-other -am compile` BUILD SUCCESS;surefire 2.22.2 跑 TChargingCardServiceImplTest 50/50、TVinManagementServiceImplTest 48/48、TAppUserWhitelistServiceImplTest 1/1、TFleetServiceImplTest 6/6、TGroupCompanyServiceImplTest 11/11 全部通过;前端删 dist 后 build 成功、`npx tsc --noEmit` 退出码 0;IDE lint 0 错误 | 首轮单测失败 1 条:updateCardRequiresExistingCardAndDoesNotChangeBalance 因编辑时 dto.balance=999.99 触发「请选择所属公司后再填写初始分配额度」,按新语义修正测试(编辑未传 balance 余额保持)并加固主代码(balance 仅正数才写列,避免共享模式清空余额);统一 DDL(sql/20260928_unify_quota_to_balance.sql)尚未在任何库执行,发版前必须先执行;前端工作区另有 company/index.jsx、global.less 公司树选中样式改动(非本任务,未纳入) | |
| | | | 2026-09-29(开始时间未记录,估算) | 2026-09-29 10:52:50 | 未记录(估算约 5400 秒,跨会话续作) | 充电时间超过授权时间主动停止充电的判断最好提前1分钟校验,避免数据传输延迟导致的导出实现(续作:VIN 码充电 5 项需求落码) | 是:先只读分析并输出修改方案(含提前量常量、边界保护口径),用户确认「开始执行」后落码 | 完成 VIN 码充电 5 项需求:①余额按企业账户模式(共享锁企业/分配锁 VIN)实扣冻结;②授权站点与时段(含指定电价阶段)校验 + 授权到期提前 60 秒主动停充;③按企业优惠设置(服务费比例/计费模板单价)优惠计费并落优惠金额;④结算解锁扣费退回 + 分配记录消费流水(按订单幂等);⑤补偿/关单路径统一释放锁定;前端分配记录新增「类型」列;同步删除 IoT 下发硬编码余额 100 | 服务端 22 个:ruoyi-api-order/TVinChargingOrder(5 新列)、TVinChargingOrderAccountingStrategy(3 新列)、ruoyi-api-account/TCompanyAllocateRecord(change_type/charging_order_code/USER_TYPE_COMPANY)、TGroupCompany、TVinManagement、4 个新 Feign 客户端与 fallback、3 个 inner 接口控制器,ruoyi-service/ruoyi-order/TVinChargingOrderServiceImpl(授权校验/锁定/停充/优惠结算/流水)、ruoyi-service/ruoyi-integration/IotMessageListener、sql/20260929_vin_charge_lock_{other,account,order}.sql;测试类 TVinChargingOrderCompanyFleetTest;管理后台 2 个:allocateRecord.js、AllocateRecordModal.jsx 与其单测 | 后端 order 192/192、account 159/159、integration 2/2 通过(surefire 2.22.2);other 128 条中 4 条、chargingPile 40 条中 1 条为历史失败(Emind/EnergyReport/导出列顺序,工作区未改相关实现,非本次引入);前端 Jest 13/13;三方测试库执行 3 个 DDL 脚本并回查 12 项列全部存在、中文注释无乱码、分片表下推查询通过 | 开始时间因跨会话续作未记录,耗时按估算标注;测试库(内网 111:8066 / 外网 221.182.45.100:8066 同库)已执行 DDL,其他环境发版前须先执行三个 SQL 脚本 | |
| | | | 2026-09-29 19:22:00(估算) | 2026-09-29 19:47:00(估算) | 约 1500(估算) | VIN码管理页面字段与筛选改造(前一会话续作:补充「导入模板也更新,添加和编辑去掉备注字段」) | 是:先只读复核 DTO/Mapper/Service/前端与三个测试类,输出含字段矩阵、影响面、回归方案的完整方案,用户确认「开始执行」后落码 | ①列表新增账户模式/可用余额/超额/账户状态,移除初始分配额度与备注列,手机号改「联系人手机号」且取消筛选,启用状态改「启停状态」;②新增创建时间区间筛选(beginTime/endTime,闭区间);③行内新增解绑、工具栏顺序调整为添加车辆/批量解绑/额度分配/导入/导出/删除;④导出改 10 列并回填车队名与账户信息;⑤导入模板改 5 列(去掉备注);⑥添加/编辑弹窗去掉备注,后端不再接收/写入 remark(编辑不再清空历史备注),DB 列保留无 DDL | 服务端 7 个:ruoyi-api-account/VinManagementQueryDTO、VinManagementExcelDTO、VinManagementSaveDTO、TVinManagement;ruoyi-account/TVinManagementMapper.xml、TVinManagementServiceImpl;测试 3 个:TVinManagementServiceImplTest、VinManagementMapperStructureTest、TVinManagementControllerTest;管理后台 4 个:pages/vin/index.jsx、queryParams.js(新增)、queryParams.test.js(新增)、components/ImportModal.jsx | 后端 surefire 2.22.2:VIN 三个测试类 70/70(controller 15、mapper 2、service 53)、account 模块全量 172/172 通过;依赖模块 ruoyi-other、ruoyi-order test-compile BUILD SUCCESS;前端 Jest 7/7、`npm run tsc` 退出码 0、@babel/parser 语法校验 5 个改动文件全部通过;IDE 诊断 0 错误 | 开始时间未精确记录,按估算标注;未执行真实数据库与页面联调,功能验证为代码走查+编译;旧模板(含手机号/备注/启用状态列头)只能识别 VIN码/车牌号/所属公司,需用新模板;导入仍按历史逻辑只写公司名称文本、不关联公司ID,账户类列为空;接口 phone 查询条件保留兼容 |
| | |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import lombok.Data; |
| | | import java.io.Serializable; |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * VIN码管理 Excel 传输对象。 |
| | | * <p>导入模板与导入解析只使用「导出导入」列(VIN码/车牌号/联系人手机号/所属公司/启停状态); |
| | | * 导出额外使用 type=EXPORT 的派生列(所属车队、账户模式、可用余额、超额、账户状态), |
| | | * 派生的车队/公司/余额字段仅供导出内部回填,不参与Excel读写。</p> |
| | | */ |
| | | @Data |
| | | public class VinManagementExcelDTO implements Serializable { |
| | | private static final long serialVersionUID = 1L; |
| | | @Excel(name = "VIN码", width = 24, prompt = "必填,最多64个字符") private String vin; |
| | | @Excel(name = "车牌号", width = 16) private String plateNumber; |
| | | @Excel(name = "手机号", width = 16) private String phone; |
| | | @Excel(name = "所属公司", width = 24) private String companyName; |
| | | @Excel(name = "备注", width = 30) private String remark; |
| | | @Excel(name = "启用状态", width = 14, readConverterExp = "1=启用,2=禁用", combo = {"启用", "禁用"}) private String status; |
| | | @Excel(name = "VIN码", sort = 1, width = 24, prompt = "必填,最多64个字符") private String vin; |
| | | @Excel(name = "车牌号", sort = 2, width = 16) private String plateNumber; |
| | | @Excel(name = "联系人手机号", sort = 3, width = 16) private String phone; |
| | | @Excel(name = "所属公司", sort = 4, width = 24) private String companyName; |
| | | /** 所属车队(仅导出,按车队ID远程回填) */ |
| | | @Excel(name = "所属车队", sort = 5, width = 16, type = Excel.Type.EXPORT) private String fleetName; |
| | | /** 账户模式(仅导出,1=共享模式,2=分配模式) */ |
| | | @Excel(name = "账户模式", sort = 6, width = 12, type = Excel.Type.EXPORT, readConverterExp = "1=共享模式,2=分配模式") private Integer accountMode; |
| | | /** 可用余额(仅导出,共享模式=公司可用余额,分配模式=VIN码自身余额) */ |
| | | @Excel(name = "可用余额", sort = 7, width = 14, type = Excel.Type.EXPORT, scale = 2) private BigDecimal availableBalance; |
| | | /** 超额(仅导出,可用余额为负时取其绝对值) */ |
| | | @Excel(name = "超额", sort = 8, width = 14, type = Excel.Type.EXPORT, scale = 2) private BigDecimal overrun; |
| | | /** 账户状态(仅导出,1=正常,2=欠款) */ |
| | | @Excel(name = "账户状态", sort = 9, width = 12, type = Excel.Type.EXPORT, readConverterExp = "1=正常,2=欠款") private Integer accountStatus; |
| | | @Excel(name = "启停状态", sort = 10, width = 14, readConverterExp = "1=启用,2=禁用", combo = {"启用", "禁用"}) private String status; |
| | | /** 所属公司ID(仅导出内部使用,不落Excel) */ |
| | | private Integer companyId; |
| | | /** 所属车队ID(仅导出内部使用,不落Excel) */ |
| | | private Integer fleetId; |
| | | /** VIN码自身余额(仅导出内部使用,不落Excel) */ |
| | | private BigDecimal balance; |
| | | } |
| | |
| | | private Integer companyId; |
| | | /** 所属车队ID(车队管理-查看车队抽屉按车队筛选VIN码车辆使用) */ |
| | | private Integer fleetId; |
| | | /** 创建时间起(含),格式 yyyy-MM-dd HH:mm:ss */ |
| | | private String beginTime; |
| | | /** 创建时间止(含),格式 yyyy-MM-dd HH:mm:ss */ |
| | | private String endTime; |
| | | } |
| | |
| | | private Integer fleetId; |
| | | /** 余额(元),分配模式公司设置后从公司可用余额扣减 */ |
| | | private BigDecimal balance; |
| | | private String remark; |
| | | private Integer status; |
| | | } |
| | |
| | | @TableField(exist = false) private String fleetName; |
| | | /** 可用余额(展示值,非表字段:公司共享模式=公司当前可用余额,分配模式=VIN码自身余额) */ |
| | | @TableField(exist = false) private BigDecimal availableBalance; |
| | | /** 账户模式(展示值,非表字段:1=共享模式,2=分配模式,取所属公司账户模式) */ |
| | | @TableField(exist = false) private Integer accountMode; |
| | | /** 超额(展示值,非表字段:可用余额为负时取其绝对值) */ |
| | | @TableField(exist = false) private BigDecimal overrun; |
| | | /** 账户状态(展示值,非表字段:1=正常,2=欠款,可用余额未知时为空) */ |
| | | @TableField(exist = false) private Integer accountStatus; |
| | | @Excel(name = "备注", width = 30) private String remark; |
| | | @Excel(name = "启用状态", readConverterExp = "1=启用,2=禁用") private Integer status; |
| | | @TableLogic(value = "0", delval = "2") private Integer delFlag; |
| | |
| | | |
| | | /** 分配模式(公司账户模式 2=分配模式,可设置初始分配额度并扣减公司可用余额) */ |
| | | private static final int ACCOUNT_MODE_ALLOCATION = 2; |
| | | /** 共享模式(公司账户模式 1=共享模式,可用余额取公司可用余额) */ |
| | | private static final int ACCOUNT_MODE_SHARED = 1; |
| | | /** 账户状态:正常 */ |
| | | private static final int ACCOUNT_STATUS_NORMAL = 1; |
| | | /** 账户状态:欠款 */ |
| | | private static final int ACCOUNT_STATUS_OVERDUE = 2; |
| | | /** 金额展示精度 */ |
| | | private static final int AMOUNT_SCALE = 2; |
| | | /** 初始分配额度上限(对应 decimal(12,2)) */ |
| | | private static final BigDecimal MAX_INIT_QUOTA = new BigDecimal("99999999.99"); |
| | | /** 额度分配:单次最多分配的VIN数量,防止单次请求过大 */ |
| | |
| | | long page = safe.getPageCurr() == null || safe.getPageCurr() < 1 ? 1 : safe.getPageCurr(); |
| | | long size = safe.getPageSize() == null || safe.getPageSize() < 1 ? 10 : safe.getPageSize(); |
| | | IPage<TVinManagement> result = baseMapper.selectPageList(new Page<>(page, size), safe); |
| | | fillFleetNames(result == null ? null : result.getRecords()); |
| | | List<TVinManagement> records = result == null ? null : result.getRecords(); |
| | | fillFleetNames(records); |
| | | fillAccountInfo(records); |
| | | return result; |
| | | } |
| | | @Override public boolean saveVin(VinManagementSaveDTO dto) { |
| | |
| | | return boundIds.size(); |
| | | } |
| | | @Override public boolean updateStatus(Long id, Integer status) { if (id == null || !validStatus(status)) throw new ServiceException("VIN码状态参数错误"); if (getById(id) == null) throw new ServiceException("VIN码不存在或已删除"); TVinManagement item = new TVinManagement(); item.setId(id); item.setStatus(status); item.setUpdateTime(LocalDateTime.now()); item.setUpdateBy(SecurityUtils.getUserId()); return updateById(item); } |
| | | @Override public List<VinManagementExcelDTO> selectExportList(VinManagementQueryDTO query) { return baseMapper.selectExportList(query == null ? new VinManagementQueryDTO() : query); } |
| | | @Override |
| | | public List<VinManagementExcelDTO> selectExportList(VinManagementQueryDTO query) { |
| | | List<VinManagementExcelDTO> items = baseMapper.selectExportList(query == null ? new VinManagementQueryDTO() : query); |
| | | if (items == null || items.isEmpty()) { |
| | | return items; |
| | | } |
| | | fillExportFleetNames(items); |
| | | fillExportAccountInfo(items); |
| | | return items; |
| | | } |
| | | @Override public List<TVinManagement> selectAppletVehicleList(Long appUserId) { |
| | | List<TVinManagement> vehicles = baseMapper.selectAppletVehicleList(appUserId); |
| | | if (vehicles == null || vehicles.isEmpty()) return vehicles; |
| | |
| | | TVinManagement latest = getById(vinId); |
| | | return latest == null ? BigDecimal.ZERO : normalizeQuota(latest.getBalance()); |
| | | } |
| | | @Override @Transactional(rollbackFor = Exception.class) public String importVins(List<VinManagementExcelDTO> items) { if (items == null || items.isEmpty()) throw new ServiceException("导入数据不能为空"); Set<String> vins = new HashSet<>(); int row = 1; for (VinManagementExcelDTO item : items) { row++; if (item == null || StringUtils.isEmpty(item.getVin())) throw new ServiceException("第" + row + "行VIN码不能为空"); String vin = item.getVin().trim(); if (vin.length() > 64) throw new ServiceException("第" + row + "行VIN码不能超过64个字符"); if (!vins.add(vin) || baseMapper.countByVin(vin, null) > 0) throw new ServiceException("第" + row + "行VIN码已存在或重复"); parseStatus(item.getStatus(), row); } Long userId = SecurityUtils.getUserId(); LocalDateTime now = LocalDateTime.now(); for (VinManagementExcelDTO item : items) { TVinManagement v = new TVinManagement(); v.setVin(item.getVin().trim()); v.setPlateNumber(trim(item.getPlateNumber())); v.setPhone(trim(item.getPhone())); v.setCompanyName(trim(item.getCompanyName())); v.setRemark(trim(item.getRemark())); v.setStatus(parseStatus(item.getStatus(), 0)); v.setDelFlag(0); v.setCreateBy(userId); v.setCreateTime(now); save(v); } return "成功导入" + items.size() + "条VIN码数据"; } |
| | | @Override @Transactional(rollbackFor = Exception.class) public String importVins(List<VinManagementExcelDTO> items) { if (items == null || items.isEmpty()) throw new ServiceException("导入数据不能为空"); Set<String> vins = new HashSet<>(); int row = 1; for (VinManagementExcelDTO item : items) { row++; if (item == null || StringUtils.isEmpty(item.getVin())) throw new ServiceException("第" + row + "行VIN码不能为空"); String vin = item.getVin().trim(); if (vin.length() > 64) throw new ServiceException("第" + row + "行VIN码不能超过64个字符"); if (!vins.add(vin) || baseMapper.countByVin(vin, null) > 0) throw new ServiceException("第" + row + "行VIN码已存在或重复"); parseStatus(item.getStatus(), row); } Long userId = SecurityUtils.getUserId(); LocalDateTime now = LocalDateTime.now(); for (VinManagementExcelDTO item : items) { TVinManagement v = new TVinManagement(); v.setVin(item.getVin().trim()); v.setPlateNumber(trim(item.getPlateNumber())); v.setPhone(trim(item.getPhone())); v.setCompanyName(trim(item.getCompanyName())); v.setStatus(parseStatus(item.getStatus(), 0)); v.setDelFlag(0); v.setCreateBy(userId); v.setCreateTime(now); save(v); } return "成功导入" + items.size() + "条VIN码数据"; } |
| | | private void validate(VinManagementSaveDTO dto, boolean update) { if (dto == null || StringUtils.isEmpty(dto.getVin())) throw new ServiceException("VIN码不能为空"); if (update && dto.getId() == null) throw new ServiceException("VIN码ID不能为空"); String vin = dto.getVin().trim(); if (vin.length() > 64) throw new ServiceException("VIN码不能超过64个字符"); if (!validStatus(dto.getStatus() == null ? ENABLED : dto.getStatus())) throw new ServiceException("VIN码状态参数错误"); if (baseMapper.countByVin(vin, dto.getId()) > 0) throw new ServiceException("VIN码已存在"); } |
| | | private TVinManagement from(VinManagementSaveDTO d) { TVinManagement v = new TVinManagement(); v.setVin(d.getVin().trim()); v.setPlateNumber(trim(d.getPlateNumber())); v.setPhone(trim(d.getPhone())); v.setCompanyName(trim(d.getCompanyName())); v.setRemark(trim(d.getRemark())); v.setStatus(d.getStatus() == null ? ENABLED : d.getStatus()); return v; } |
| | | private TVinManagement from(VinManagementSaveDTO d) { TVinManagement v = new TVinManagement(); v.setVin(d.getVin().trim()); v.setPlateNumber(trim(d.getPlateNumber())); v.setPhone(trim(d.getPhone())); v.setCompanyName(trim(d.getCompanyName())); v.setStatus(d.getStatus() == null ? ENABLED : d.getStatus()); return v; } |
| | | private String trim(String value) { return value == null ? null : value.trim(); } |
| | | |
| | | /** 写入VIN码分配流水(分配为正数、回收为负数;公司为空或金额为0时自动忽略) */ |
| | |
| | | } |
| | | } |
| | | |
| | | /** 更新VIN记录(显式设置表单字段,保证公司/车队/额度可被清空) */ |
| | | /** 导出回填车队名称(远程失败不影响导出) */ |
| | | private void fillExportFleetNames(List<VinManagementExcelDTO> items) { |
| | | List<Integer> fleetIds = items.stream() |
| | | .map(VinManagementExcelDTO::getFleetId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | if (fleetIds.isEmpty()) { |
| | | return; |
| | | } |
| | | try { |
| | | R<List<TFleet>> response = groupCompanyClient.listFleetsByIds(fleetIds); |
| | | if (response == null || !R.isSuccess(response) || response.getData() == null) { |
| | | return; |
| | | } |
| | | Map<Integer, String> nameMap = new HashMap<>(); |
| | | for (TFleet fleet : response.getData()) { |
| | | if (fleet != null && fleet.getId() != null) { |
| | | nameMap.put(fleet.getId(), fleet.getFleetName()); |
| | | } |
| | | } |
| | | items.forEach(item -> item.setFleetName(nameMap.get(item.getFleetId()))); |
| | | } catch (Exception ex) { |
| | | log.warn("导出回填车队名称失败,fleetIds={}", fleetIds, ex); |
| | | } |
| | | } |
| | | |
| | | /** 列表回填账户信息:账户模式、可用余额、超额、账户状态(远程失败仅留空,不影响列表查询) */ |
| | | private void fillAccountInfo(List<TVinManagement> records) { |
| | | if (records == null || records.isEmpty()) { |
| | | return; |
| | | } |
| | | Map<Integer, TGroupCompany> companyMap = loadCompanyMap(records.stream() |
| | | .map(TVinManagement::getCompanyId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList())); |
| | | for (TVinManagement record : records) { |
| | | TGroupCompany company = companyMap.get(record.getCompanyId()); |
| | | Integer accountMode = company == null ? null : company.getAccountMode(); |
| | | BigDecimal balance = resolveAvailableBalance(accountMode, record.getBalance(), |
| | | company == null ? null : company.getAvailableBalance()); |
| | | record.setAccountMode(accountMode); |
| | | record.setAvailableBalance(balance); |
| | | record.setOverrun(resolveOverrun(balance)); |
| | | record.setAccountStatus(resolveAccountStatus(balance)); |
| | | } |
| | | } |
| | | |
| | | /** 导出回填账户信息:账户模式、可用余额、超额、账户状态(与列表同口径) */ |
| | | private void fillExportAccountInfo(List<VinManagementExcelDTO> items) { |
| | | Map<Integer, TGroupCompany> companyMap = loadCompanyMap(items.stream() |
| | | .map(VinManagementExcelDTO::getCompanyId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList())); |
| | | for (VinManagementExcelDTO item : items) { |
| | | TGroupCompany company = companyMap.get(item.getCompanyId()); |
| | | Integer accountMode = company == null ? null : company.getAccountMode(); |
| | | BigDecimal balance = resolveAvailableBalance(accountMode, item.getBalance(), |
| | | company == null ? null : company.getAvailableBalance()); |
| | | item.setAccountMode(accountMode); |
| | | item.setAvailableBalance(balance); |
| | | item.setOverrun(resolveOverrun(balance)); |
| | | item.setAccountStatus(resolveAccountStatus(balance)); |
| | | } |
| | | } |
| | | |
| | | /** 按ID批量加载公司(远程失败或服务降级时返回空Map,由调用方按未知处理) */ |
| | | private Map<Integer, TGroupCompany> loadCompanyMap(List<Integer> companyIds) { |
| | | List<Integer> distinctIds = companyIds.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList()); |
| | | if (distinctIds.isEmpty()) { |
| | | return new HashMap<>(); |
| | | } |
| | | try { |
| | | R<List<TGroupCompany>> response = groupCompanyClient.listCompaniesByIds(distinctIds); |
| | | if (response == null || !R.isSuccess(response) || response.getData() == null) { |
| | | return new HashMap<>(); |
| | | } |
| | | Map<Integer, TGroupCompany> companyMap = new HashMap<>(); |
| | | for (TGroupCompany company : response.getData()) { |
| | | if (company != null && company.getId() != null) { |
| | | companyMap.put(company.getId(), company); |
| | | } |
| | | } |
| | | return companyMap; |
| | | } catch (Exception ex) { |
| | | log.warn("回填公司账户信息失败,companyIds={}", distinctIds, ex); |
| | | return new HashMap<>(); |
| | | } |
| | | } |
| | | |
| | | /** 计算可用余额:分配模式=VIN码自身余额,共享模式=公司可用余额,账户模式未知时留空 */ |
| | | private BigDecimal resolveAvailableBalance(Integer accountMode, BigDecimal vinBalance, BigDecimal companyBalance) { |
| | | if (Integer.valueOf(ACCOUNT_MODE_ALLOCATION).equals(accountMode)) { |
| | | return vinBalance; |
| | | } |
| | | if (Integer.valueOf(ACCOUNT_MODE_SHARED).equals(accountMode)) { |
| | | return companyBalance; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** 超额:可用余额为负时取其绝对值,其余情况为空 */ |
| | | private BigDecimal resolveOverrun(BigDecimal balance) { |
| | | if (balance == null || balance.signum() >= 0) { |
| | | return null; |
| | | } |
| | | return balance.abs().setScale(AMOUNT_SCALE, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | /** 账户状态:可用余额为负=欠款,非负=正常,可用余额未知时为空 */ |
| | | private Integer resolveAccountStatus(BigDecimal balance) { |
| | | if (balance == null) { |
| | | return null; |
| | | } |
| | | return balance.signum() < 0 ? ACCOUNT_STATUS_OVERDUE : ACCOUNT_STATUS_NORMAL; |
| | | } |
| | | |
| | | /** 更新VIN记录(显式设置表单字段,保证公司/车队/额度可被清空;备注已下线,历史值保持不动) */ |
| | | private boolean updateVinRecord(TVinManagement item) { |
| | | return lambdaUpdate() |
| | | .eq(TVinManagement::getId, item.getId()) |
| | |
| | | .set(TVinManagement::getCompanyId, item.getCompanyId()) |
| | | .set(TVinManagement::getFleetId, item.getFleetId()) |
| | | .set(TVinManagement::getBalance, item.getBalance()) |
| | | .set(TVinManagement::getRemark, item.getRemark()) |
| | | .set(TVinManagement::getStatus, item.getStatus()) |
| | | .set(TVinManagement::getUpdateTime, item.getUpdateTime()) |
| | | .set(TVinManagement::getUpdateBy, item.getUpdateBy()) |
| | |
| | | <if test="query != null and query.status != null">AND status = #{query.status}</if> |
| | | <if test="query != null and query.companyId != null">AND company_id = #{query.companyId}</if> |
| | | <if test="query != null and query.fleetId != null">AND fleet_id = #{query.fleetId}</if> |
| | | <if test="query != null and query.beginTime != null and query.beginTime != ''">AND create_time >= #{query.beginTime}</if> |
| | | <if test="query != null and query.endTime != null and query.endTime != ''">AND create_time <= #{query.endTime}</if> |
| | | ORDER BY create_time DESC, id DESC |
| | | </select> |
| | | <select id="selectExportList" resultType="com.ruoyi.account.api.dto.VinManagementExcelDTO"> |
| | | SELECT vin, plate_number AS plateNumber, phone, company_name AS companyName, remark, status |
| | | SELECT vin, plate_number AS plateNumber, phone, company_name AS companyName, |
| | | company_id AS companyId, fleet_id AS fleetId, balance, status |
| | | FROM t_vin_management WHERE del_flag = 0 |
| | | <if test="query != null and query.vin != null and query.vin != ''">AND vin LIKE CONCAT('%', #{query.vin}, '%')</if> |
| | | <if test="query != null and query.plateNumber != null and query.plateNumber != ''">AND plate_number LIKE CONCAT('%', #{query.plateNumber}, '%')</if> |
| | |
| | | <if test="query != null and query.status != null">AND status = #{query.status}</if> |
| | | <if test="query != null and query.companyId != null">AND company_id = #{query.companyId}</if> |
| | | <if test="query != null and query.fleetId != null">AND fleet_id = #{query.fleetId}</if> |
| | | <if test="query != null and query.beginTime != null and query.beginTime != ''">AND create_time >= #{query.beginTime}</if> |
| | | <if test="query != null and query.endTime != null and query.endTime != ''">AND create_time <= #{query.endTime}</if> |
| | | ORDER BY create_time DESC, id DESC |
| | | </select> |
| | | <select id="countByVin" resultType="int">SELECT COUNT(1) FROM t_vin_management WHERE vin = #{vin}<if test="excludeId != null"> AND id != #{excludeId}</if></select> |
| | |
| | | import com.ruoyi.system.api.model.LoginUserApplet; |
| | | import org.apache.poi.ss.usermodel.Row; |
| | | import org.apache.poi.ss.usermodel.Sheet; |
| | | import org.apache.poi.ss.usermodel.Workbook; |
| | | import org.apache.poi.xssf.usermodel.XSSFWorkbook; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.mockito.ArgumentCaptor; |
| | |
| | | import org.springframework.mock.web.MockMultipartFile; |
| | | import org.springframework.test.util.ReflectionTestUtils; |
| | | |
| | | import java.io.ByteArrayInputStream; |
| | | import java.io.ByteArrayOutputStream; |
| | | import java.math.BigDecimal; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | |
| | | assertEquals("川A12345", parsed.getPlateNumber()); |
| | | assertEquals("13800138000", parsed.getPhone()); |
| | | assertEquals("测试公司", parsed.getCompanyName()); |
| | | assertEquals("备注", parsed.getRemark()); |
| | | assertEquals("1", parsed.getStatus()); |
| | | } |
| | | |
| | | @Test |
| | | void templateHeaderUsesRenamedColumnsWithoutRemark() throws Exception { |
| | | TVinManagementService service = mock(TVinManagementService.class); |
| | | TVinManagementController controller = buildController(service); |
| | | MockHttpServletResponse response = new MockHttpServletResponse(); |
| | | |
| | | controller.template(response); |
| | | |
| | | Row header = workbookHeader(response); |
| | | assertEquals(5, header.getLastCellNum()); |
| | | assertEquals("VIN码", header.getCell(0).getStringCellValue()); |
| | | assertEquals("车牌号", header.getCell(1).getStringCellValue()); |
| | | assertEquals("联系人手机号", header.getCell(2).getStringCellValue()); |
| | | assertEquals("所属公司", header.getCell(3).getStringCellValue()); |
| | | assertEquals("启停状态", header.getCell(4).getStringCellValue()); |
| | | } |
| | | |
| | | @Test |
| | | void exportHeaderAndAccountColumnsFollowListCaliber() throws Exception { |
| | | TVinManagementService service = mock(TVinManagementService.class); |
| | | VinManagementExcelDTO item = new VinManagementExcelDTO(); |
| | | item.setVin("LSVAM4187C2184842"); |
| | | item.setPlateNumber("川A12345"); |
| | | item.setPhone("13800138000"); |
| | | item.setCompanyName("测试公司"); |
| | | item.setFleetName("测试车队"); |
| | | item.setAccountMode(1); |
| | | item.setAvailableBalance(new BigDecimal("10.50")); |
| | | item.setOverrun(new BigDecimal("2.00")); |
| | | item.setAccountStatus(2); |
| | | item.setStatus("1"); |
| | | when(service.selectExportList(any())).thenReturn(Collections.singletonList(item)); |
| | | TVinManagementController controller = buildController(service); |
| | | |
| | | MockHttpServletResponse response = new MockHttpServletResponse(); |
| | | controller.export(response, new VinManagementQueryDTO()); |
| | | |
| | | Workbook workbook = new XSSFWorkbook(new ByteArrayInputStream(response.getContentAsByteArray())); |
| | | Row header = workbook.getSheetAt(0).getRow(0); |
| | | assertEquals(10, header.getLastCellNum()); |
| | | assertEquals("VIN码", header.getCell(0).getStringCellValue()); |
| | | assertEquals("车牌号", header.getCell(1).getStringCellValue()); |
| | | assertEquals("联系人手机号", header.getCell(2).getStringCellValue()); |
| | | assertEquals("所属公司", header.getCell(3).getStringCellValue()); |
| | | assertEquals("所属车队", header.getCell(4).getStringCellValue()); |
| | | assertEquals("账户模式", header.getCell(5).getStringCellValue()); |
| | | assertEquals("可用余额", header.getCell(6).getStringCellValue()); |
| | | assertEquals("超额", header.getCell(7).getStringCellValue()); |
| | | assertEquals("账户状态", header.getCell(8).getStringCellValue()); |
| | | assertEquals("启停状态", header.getCell(9).getStringCellValue()); |
| | | Row data = workbook.getSheetAt(0).getRow(1); |
| | | assertEquals("共享模式", data.getCell(5).getStringCellValue()); |
| | | assertEquals(10.5D, data.getCell(6).getNumericCellValue()); |
| | | assertEquals(2.0D, data.getCell(7).getNumericCellValue()); |
| | | assertEquals("欠款", data.getCell(8).getStringCellValue()); |
| | | assertEquals("启用", data.getCell(9).getStringCellValue()); |
| | | } |
| | | |
| | | private Row workbookHeader(MockHttpServletResponse response) throws Exception { |
| | | Workbook workbook = new XSSFWorkbook(new ByteArrayInputStream(response.getContentAsByteArray())); |
| | | return workbook.getSheetAt(0).getRow(0); |
| | | } |
| | | |
| | | @Test |
| | |
| | | Row header = sheet.createRow(0); |
| | | header.createCell(0).setCellValue("VIN码"); |
| | | header.createCell(1).setCellValue("车牌号"); |
| | | header.createCell(2).setCellValue("手机号"); |
| | | header.createCell(2).setCellValue("联系人手机号"); |
| | | header.createCell(3).setCellValue("所属公司"); |
| | | header.createCell(4).setCellValue("备注"); |
| | | header.createCell(5).setCellValue("启用状态"); |
| | | header.createCell(4).setCellValue("启停状态"); |
| | | Row row = sheet.createRow(1); |
| | | row.createCell(0).setCellValue("LSVAM4187C2184842"); |
| | | row.createCell(1).setCellValue("川A12345"); |
| | | row.createCell(2).setCellValue("13800138000"); |
| | | row.createCell(3).setCellValue("测试公司"); |
| | | row.createCell(4).setCellValue("备注"); |
| | | row.createCell(5).setCellValue("启用"); |
| | | row.createCell(4).setCellValue("启用"); |
| | | workbook.write(output); |
| | | return new MockMultipartFile("file", "vin.xlsx", |
| | | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", output.toByteArray()); |
| | |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | import static org.junit.jupiter.api.Assertions.assertFalse; |
| | | import static org.junit.jupiter.api.Assertions.assertNotNull; |
| | | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | | |
| | | /** |
| | | * VIN 管理 Mapper 结构断言:列表与导出必须共用同一套筛选条件(含所属公司/车队),避免导出结果与列表不一致。 |
| | | * VIN 管理 Mapper 结构断言:列表与导出必须共用同一套筛选条件(含所属公司/车队/创建时间区间),避免导出结果与列表不一致。 |
| | | */ |
| | | class VinManagementMapperStructureTest { |
| | | |
| | | @Test |
| | | void pageListAndExportShareCompanyAndFleetFilters() throws IOException { |
| | | InputStream input = getClass().getClassLoader().getResourceAsStream("mapper/account/TVinManagementMapper.xml"); |
| | | assertNotNull(input); |
| | | String mapper; |
| | | try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { |
| | | mapper = reader.lines().collect(Collectors.joining("\n")).toLowerCase(); |
| | | } |
| | | String mapper = loadMapper(); |
| | | assertTrue(mapper.contains("select id=\"selectpagelist\"")); |
| | | assertTrue(mapper.contains("select id=\"selectexportlist\"")); |
| | | assertEquals(2, occurrences(mapper, "and company_id = #{query.companyid}")); |
| | | assertEquals(2, occurrences(mapper, "and fleet_id = #{query.fleetid}")); |
| | | assertEquals(2, occurrences(mapper, "and create_time >= #{query.begintime}")); |
| | | assertEquals(2, occurrences(mapper, "and create_time <= #{query.endtime}")); |
| | | } |
| | | |
| | | @Test |
| | | void exportListSelectsAccountColumnsWithoutRemark() throws IOException { |
| | | String mapper = loadMapper(); |
| | | int start = mapper.indexOf("select id=\"selectexportlist\""); |
| | | int end = mapper.indexOf("</select>", start); |
| | | String exportSql = mapper.substring(start, end); |
| | | assertTrue(exportSql.contains("company_id as companyid")); |
| | | assertTrue(exportSql.contains("fleet_id as fleetid")); |
| | | assertTrue(exportSql.contains("balance")); |
| | | assertTrue(exportSql.contains("phone")); |
| | | assertTrue(exportSql.contains("status")); |
| | | assertFalse(exportSql.contains("remark")); |
| | | } |
| | | |
| | | private String loadMapper() throws IOException { |
| | | InputStream input = getClass().getClassLoader().getResourceAsStream("mapper/account/TVinManagementMapper.xml"); |
| | | assertNotNull(input); |
| | | try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { |
| | | return reader.lines().collect(Collectors.joining("\n")).toLowerCase(); |
| | | } |
| | | } |
| | | |
| | | private int occurrences(String content, String keyword) { |
| | |
| | | import com.ruoyi.common.core.exception.ServiceException; |
| | | import com.ruoyi.order.api.feignClient.ChargingOrderClient; |
| | | import com.ruoyi.order.api.vo.VinChargingStatusVO; |
| | | import com.ruoyi.other.api.domain.TFleet; |
| | | import com.ruoyi.other.api.domain.TGroupCompany; |
| | | import com.ruoyi.other.api.feignClient.GroupCompanyClient; |
| | | import org.junit.jupiter.api.AfterEach; |
| | |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | import static org.junit.jupiter.api.Assertions.assertFalse; |
| | | import static org.junit.jupiter.api.Assertions.assertNotNull; |
| | | import static org.junit.jupiter.api.Assertions.assertNull; |
| | | import static org.junit.jupiter.api.Assertions.assertSame; |
| | | import static org.junit.jupiter.api.Assertions.assertThrows; |
| | | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | |
| | | dto.setPlateNumber(" 川A12345 "); |
| | | dto.setPhone("13800138000"); |
| | | dto.setCompanyName(" 测试公司 "); |
| | | dto.setRemark(" 备注 "); |
| | | |
| | | boolean result = service.saveVin(dto); |
| | | |
| | |
| | | assertEquals("川A12345", saved.getPlateNumber()); |
| | | assertEquals("13800138000", saved.getPhone()); |
| | | assertEquals("测试公司", saved.getCompanyName()); |
| | | assertEquals("备注", saved.getRemark()); |
| | | assertNull(saved.getRemark()); |
| | | assertEquals(Integer.valueOf(1), saved.getStatus()); |
| | | assertEquals(Integer.valueOf(0), saved.getDelFlag()); |
| | | assertNotNull(saved.getCreateTime()); |
| | |
| | | assertTrue(sqlSet.contains("status=")); |
| | | assertTrue(sqlSet.contains("update_time=")); |
| | | assertTrue(sqlSet.contains("update_by=")); |
| | | // 备注已从表单下线,编辑不再写入 remark,历史备注不会被清空 |
| | | assertFalse(sqlSet.contains("remark=")); |
| | | } |
| | | |
| | | @Test |
| | |
| | | assertNotNull(queryCaptor.getValue()); |
| | | } |
| | | |
| | | // ---------------- 账户信息回填(列表/导出) ---------------- |
| | | |
| | | @Test |
| | | void selectPageListFillsSharedAccountInfoFromCompanyBalance() { |
| | | TVinManagement record = new TVinManagement(); |
| | | record.setId(1L); |
| | | record.setCompanyId(8); |
| | | record.setBalance(new BigDecimal("5.00")); |
| | | Page<TVinManagement> expected = new Page<>(1, 10); |
| | | expected.setRecords(Collections.singletonList(record)); |
| | | when(mapper.selectPageList(any(Page.class), any())).thenReturn(expected); |
| | | TGroupCompany company = new TGroupCompany(); |
| | | company.setId(8); |
| | | company.setAccountMode(1); |
| | | company.setAvailableBalance(new BigDecimal("88.60")); |
| | | when(groupCompanyClient.listCompaniesByIds(Collections.singletonList(8))) |
| | | .thenReturn(R.ok(Collections.singletonList(company))); |
| | | |
| | | IPage<TVinManagement> result = service.selectPageList(new VinManagementQueryDTO()); |
| | | |
| | | TVinManagement filled = result.getRecords().get(0); |
| | | assertEquals(Integer.valueOf(1), filled.getAccountMode()); |
| | | assertEquals(new BigDecimal("88.60"), filled.getAvailableBalance()); |
| | | assertNull(filled.getOverrun()); |
| | | assertEquals(Integer.valueOf(1), filled.getAccountStatus()); |
| | | } |
| | | |
| | | @Test |
| | | void selectPageListUsesOwnBalanceAndFlagsOverrunForAllocationMode() { |
| | | TVinManagement record = new TVinManagement(); |
| | | record.setId(2L); |
| | | record.setCompanyId(9); |
| | | record.setBalance(new BigDecimal("-12.345")); |
| | | Page<TVinManagement> expected = new Page<>(1, 10); |
| | | expected.setRecords(Collections.singletonList(record)); |
| | | when(mapper.selectPageList(any(Page.class), any())).thenReturn(expected); |
| | | TGroupCompany company = new TGroupCompany(); |
| | | company.setId(9); |
| | | company.setAccountMode(2); |
| | | company.setAvailableBalance(new BigDecimal("1000.00")); |
| | | when(groupCompanyClient.listCompaniesByIds(Collections.singletonList(9))) |
| | | .thenReturn(R.ok(Collections.singletonList(company))); |
| | | |
| | | IPage<TVinManagement> result = service.selectPageList(new VinManagementQueryDTO()); |
| | | |
| | | TVinManagement filled = result.getRecords().get(0); |
| | | assertEquals(Integer.valueOf(2), filled.getAccountMode()); |
| | | assertEquals(new BigDecimal("-12.345"), filled.getAvailableBalance()); |
| | | assertEquals(new BigDecimal("12.35"), filled.getOverrun()); |
| | | assertEquals(Integer.valueOf(2), filled.getAccountStatus()); |
| | | } |
| | | |
| | | @Test |
| | | void selectPageListLeavesAccountFieldsEmptyWhenCompanyLookupFails() { |
| | | TVinManagement record = new TVinManagement(); |
| | | record.setId(3L); |
| | | record.setCompanyId(10); |
| | | record.setBalance(new BigDecimal("1.00")); |
| | | Page<TVinManagement> expected = new Page<>(1, 10); |
| | | expected.setRecords(Collections.singletonList(record)); |
| | | when(mapper.selectPageList(any(Page.class), any())).thenReturn(expected); |
| | | when(groupCompanyClient.listCompaniesByIds(Collections.singletonList(10))) |
| | | .thenReturn(R.fail("other service unavailable")); |
| | | |
| | | IPage<TVinManagement> result = service.selectPageList(new VinManagementQueryDTO()); |
| | | |
| | | TVinManagement filled = result.getRecords().get(0); |
| | | assertNull(filled.getAccountMode()); |
| | | assertNull(filled.getAvailableBalance()); |
| | | assertNull(filled.getOverrun()); |
| | | assertNull(filled.getAccountStatus()); |
| | | } |
| | | |
| | | @Test |
| | | void selectExportListFillsFleetNameAndAccountInfo() { |
| | | VinManagementExcelDTO item = new VinManagementExcelDTO(); |
| | | item.setVin("LSVAM4187C2184842"); |
| | | item.setCompanyId(8); |
| | | item.setFleetId(3); |
| | | item.setBalance(new BigDecimal("7.00")); |
| | | when(mapper.selectExportList(any())).thenReturn(Collections.singletonList(item)); |
| | | TFleet fleet = new TFleet(); |
| | | fleet.setId(3); |
| | | fleet.setFleetName("测试车队"); |
| | | when(groupCompanyClient.listFleetsByIds(Collections.singletonList(3))) |
| | | .thenReturn(R.ok(Collections.singletonList(fleet))); |
| | | TGroupCompany company = new TGroupCompany(); |
| | | company.setId(8); |
| | | company.setAccountMode(1); |
| | | company.setAvailableBalance(new BigDecimal("66.00")); |
| | | when(groupCompanyClient.listCompaniesByIds(Collections.singletonList(8))) |
| | | .thenReturn(R.ok(Collections.singletonList(company))); |
| | | |
| | | List<VinManagementExcelDTO> result = service.selectExportList(new VinManagementQueryDTO()); |
| | | |
| | | assertEquals("测试车队", result.get(0).getFleetName()); |
| | | assertEquals(Integer.valueOf(1), result.get(0).getAccountMode()); |
| | | assertEquals(new BigDecimal("66.00"), result.get(0).getAvailableBalance()); |
| | | assertNull(result.get(0).getOverrun()); |
| | | assertEquals(Integer.valueOf(1), result.get(0).getAccountStatus()); |
| | | } |
| | | |
| | | @Test |
| | | void selectExportListKeepsRowsWhenRemoteLookupsFail() { |
| | | VinManagementExcelDTO item = new VinManagementExcelDTO(); |
| | | item.setVin("LSVAM4187C2184842"); |
| | | item.setCompanyId(8); |
| | | item.setFleetId(3); |
| | | when(mapper.selectExportList(any())).thenReturn(Collections.singletonList(item)); |
| | | when(groupCompanyClient.listFleetsByIds(Collections.singletonList(3))) |
| | | .thenThrow(new ServiceException("fleet service unavailable")); |
| | | when(groupCompanyClient.listCompaniesByIds(Collections.singletonList(8))) |
| | | .thenThrow(new ServiceException("company service unavailable")); |
| | | |
| | | List<VinManagementExcelDTO> result = service.selectExportList(new VinManagementQueryDTO()); |
| | | |
| | | assertEquals(1, result.size()); |
| | | assertNull(result.get(0).getFleetName()); |
| | | assertNull(result.get(0).getAccountMode()); |
| | | assertNull(result.get(0).getAvailableBalance()); |
| | | } |
| | | |
| | | // ---------------- importVins ---------------- |
| | | |
| | | @Test |
| | |
| | | first.setPlateNumber(" 川A12345 "); |
| | | first.setPhone("13800138000"); |
| | | first.setCompanyName(" 公司A "); |
| | | first.setRemark(" 备注A "); |
| | | first.setStatus("启用"); |
| | | |
| | | VinManagementExcelDTO second = new VinManagementExcelDTO(); |
| | |
| | | assertEquals("LSVAM4187C2184842", saved.get(0).getVin()); |
| | | assertEquals("川A12345", saved.get(0).getPlateNumber()); |
| | | assertEquals("公司A", saved.get(0).getCompanyName()); |
| | | assertEquals("备注A", saved.get(0).getRemark()); |
| | | assertNull(saved.get(0).getRemark()); |
| | | assertEquals(Integer.valueOf(1), saved.get(0).getStatus()); |
| | | assertEquals(Integer.valueOf(2), saved.get(1).getStatus()); |
| | | assertEquals(Integer.valueOf(1), saved.get(2).getStatus()); |