package com.ruoyi.other.service.impl;
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.ruoyi.account.api.feignClient.AppUserWhitelistClient;
|
import com.ruoyi.account.api.feignClient.ChargingCardClient;
|
import com.ruoyi.account.api.feignClient.VinManagementClient;
|
import com.ruoyi.account.api.model.TAppUserWhitelist;
|
import com.ruoyi.account.api.model.TChargingCard;
|
import com.ruoyi.account.api.model.TVinManagement;
|
import com.ruoyi.common.core.domain.R;
|
import com.ruoyi.common.core.utils.StringUtils;
|
import com.ruoyi.common.core.web.domain.AjaxResult;
|
import com.ruoyi.other.api.domain.TFleet;
|
import com.ruoyi.other.api.domain.TGroupCompany;
|
import com.ruoyi.other.api.model.TCompanyRechargeRecord;
|
import com.ruoyi.other.api.vo.CompanyChargingUserVO;
|
import com.ruoyi.other.mapper.TCompanyRechargeRecordMapper;
|
import com.ruoyi.other.mapper.TFleetMapper;
|
import com.ruoyi.other.mapper.TGroupCompanyMapper;
|
import com.ruoyi.other.service.TGroupCompanyService;
|
import com.ruoyi.system.api.domain.SysUser;
|
import com.ruoyi.system.api.feignClient.SysRoleClient;
|
import com.ruoyi.system.api.feignClient.SysUserClient;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import javax.annotation.Resource;
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.util.ArrayList;
|
import java.util.Date;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.stream.Collectors;
|
|
/**
|
* <p>
|
* 公司(企业)管理 服务实现类
|
* </p>
|
*
|
* @author mxcd
|
* @since 2026-09-22
|
*/
|
@Service
|
public class TGroupCompanyServiceImpl extends ServiceImpl<TGroupCompanyMapper, TGroupCompany> implements TGroupCompanyService {
|
|
/** 金额保留小数位(与 t_company_recharge_record.arrival_amount 一致) */
|
private static final int AMOUNT_SCALE = 2;
|
|
/** 公司账号角色标识(内置角色 role_key=company) */
|
private static final String COMPANY_ROLE_KEY = "company";
|
|
/** 公司账号角色类型(sys_user.roleType=5) */
|
private static final int COMPANY_ROLE_TYPE = 5;
|
|
/** 账户模式:共享模式(新增企业未指定时按此建档) */
|
private static final int ACCOUNT_MODE_SHARED = 1;
|
|
/** 账户模式:分配模式 */
|
private static final int ACCOUNT_MODE_ALLOCATION = 2;
|
|
/** 充电用户类型:VIN码 */
|
private static final int USER_TYPE_VIN = 1;
|
|
/** 充电用户类型:刷卡 */
|
private static final int USER_TYPE_CARD = 2;
|
|
/** 充电用户类型:白名单 */
|
private static final int USER_TYPE_WHITELIST = 3;
|
|
/** 账户状态:正常 */
|
private static final int ACCOUNT_STATUS_NORMAL = 1;
|
|
/** 账户状态:欠款 */
|
private static final int ACCOUNT_STATUS_OVERDUE = 2;
|
|
/**
|
* 车队 Mapper。
|
* 此处注入 Mapper 而非 TFleetService:TFleetServiceImpl 已依赖 TGroupCompanyService,
|
* 反向注入 Service 会形成循环引用(Spring Boot 2.7 默认禁止)。
|
*/
|
@Resource
|
private TFleetMapper fleetMapper;
|
|
/** 公司充值记录 Mapper */
|
@Resource
|
private TCompanyRechargeRecordMapper companyRechargeRecordMapper;
|
|
/** 系统用户远程客户端(公司登录账号同步) */
|
@Resource
|
private SysUserClient sysUserClient;
|
|
/** 系统角色远程客户端(公司默认角色查询) */
|
@Resource
|
private SysRoleClient sysRoleClient;
|
|
/** VIN管理远程客户端(公司详情-充电用户信息聚合使用) */
|
@Resource
|
private VinManagementClient vinManagementClient;
|
|
/** 充电卡远程客户端(公司详情-充电用户信息聚合使用) */
|
@Resource
|
private ChargingCardClient chargingCardClient;
|
|
/** 白名单远程客户端(公司详情-充电用户信息聚合使用) */
|
@Resource
|
private AppUserWhitelistClient appUserWhitelistClient;
|
|
@Override
|
public TGroupCompany getDetail(Integer id) {
|
if (id == null) {
|
return null;
|
}
|
TGroupCompany company = getById(id);
|
if (company == null) {
|
return null;
|
}
|
// 车队数量:t_fleet 逻辑删除由 @TableLogic 自动过滤,统计含停用车队
|
company.setFleetCount(countFleet(id));
|
// 月充值金额:本月到账金额合计,仅覆盖响应对象,不落库
|
company.setMonthRechargeAmount(sumCurrentMonthRechargeAmount(id));
|
// 累计充值到账金额:作为公司总账户余额展示(额度分配弹窗使用),仅覆盖响应对象,不落库
|
company.setTotalRechargeAmount(sumRechargeAmount(id));
|
return company;
|
}
|
|
@Override
|
public TGroupCompany getDetailWithChargingUsers(Integer id) {
|
TGroupCompany company = getDetail(id);
|
if (company == null) {
|
return null;
|
}
|
List<TVinManagement> vins = fetchVins(id);
|
// VIN码车辆数量:按公司统计未删除车辆(与 VIN 列表接口同口径),降级时返回 0
|
company.setVinCount(vins.size());
|
company.setChargingUsers(buildChargingUsers(company, vins));
|
return company;
|
}
|
|
/**
|
* 聚合公司充电用户信息:VIN码、刷卡、白名单三类数据按统一结构返回。
|
* 仅公司详情使用;remoteGet 远程调用保持 getDetail(不聚合),避免跨服务循环调用。
|
*/
|
private List<CompanyChargingUserVO> buildChargingUsers(TGroupCompany company, List<TVinManagement> vins) {
|
Integer companyId = company.getId();
|
Integer accountMode = company.getAccountMode();
|
List<CompanyChargingUserVO> result = new ArrayList<>();
|
|
List<TChargingCard> cards = fetchCards(companyId);
|
List<TAppUserWhitelist> whitelists = fetchWhitelists(companyId);
|
|
List<Integer> fleetIds = new ArrayList<>();
|
for (TVinManagement vin : vins) {
|
if (vin.getFleetId() != null) {
|
fleetIds.add(vin.getFleetId());
|
}
|
}
|
for (TChargingCard card : cards) {
|
if (card.getFleetId() != null) {
|
fleetIds.add(card.getFleetId());
|
}
|
}
|
for (TAppUserWhitelist whitelist : whitelists) {
|
if (whitelist.getFleetId() != null) {
|
fleetIds.add(whitelist.getFleetId());
|
}
|
}
|
Map<Integer, String> fleetNameMap = loadFleetNameMap(fleetIds);
|
|
String companyName = company.getName();
|
BigDecimal companyBalance = company.getAvailableBalance();
|
for (TVinManagement vin : vins) {
|
result.add(buildRow(USER_TYPE_VIN + "-" + vin.getId(), USER_TYPE_VIN, vin.getVin(),
|
companyName, fleetNameMap.get(vin.getFleetId()), accountMode,
|
vin.getBalance(), companyBalance, vin.getStatus()));
|
}
|
for (TChargingCard card : cards) {
|
result.add(buildRow(USER_TYPE_CARD + "-" + card.getId(), USER_TYPE_CARD, card.getCardNo(),
|
companyName, fleetNameMap.get(card.getFleetId()), accountMode,
|
card.getBalance(), companyBalance, card.getStatus()));
|
}
|
for (TAppUserWhitelist whitelist : whitelists) {
|
result.add(buildRow(USER_TYPE_WHITELIST + "-" + whitelist.getId(), USER_TYPE_WHITELIST, whitelist.getPhone(),
|
companyName, fleetNameMap.get(whitelist.getFleetId()), accountMode,
|
whitelist.getBalance(), companyBalance, whitelist.getStatus()));
|
}
|
return result;
|
}
|
|
/**
|
* 组装单行充电用户信息。可用余额:分配模式取子账户余额/初始分配额度,共享模式取公司可用余额。
|
* 超额/账户状态按余额正负推算:余额为负时超额=|余额|、账户状态=欠款;否则无超额、状态正常。
|
*/
|
private CompanyChargingUserVO buildRow(String rowKey, Integer userType, String userInfo,
|
String companyName, String fleetName, Integer accountMode,
|
BigDecimal accountBalance, BigDecimal companyBalance, Integer status) {
|
CompanyChargingUserVO vo = new CompanyChargingUserVO();
|
vo.setRowKey(rowKey);
|
vo.setUserType(userType);
|
vo.setUserInfo(userInfo);
|
vo.setCompanyName(companyName);
|
vo.setFleetName(fleetName);
|
vo.setAccountMode(accountMode);
|
vo.setBalance(resolveBalance(accountMode, accountBalance, companyBalance));
|
vo.setOverrun(null);
|
vo.setAccountStatus(ACCOUNT_STATUS_NORMAL);
|
BigDecimal balance = vo.getBalance();
|
if (balance != null && balance.compareTo(BigDecimal.ZERO) < 0) {
|
vo.setOverrun(balance.abs().setScale(AMOUNT_SCALE, RoundingMode.HALF_UP));
|
vo.setAccountStatus(ACCOUNT_STATUS_OVERDUE);
|
}
|
vo.setStatus(status);
|
return vo;
|
}
|
|
/**
|
* 计算可用余额:分配模式=子账户余额/初始分配额度,共享模式=公司可用余额
|
*/
|
private BigDecimal resolveBalance(Integer accountMode, BigDecimal accountBalance, BigDecimal companyBalance) {
|
if (Integer.valueOf(ACCOUNT_MODE_ALLOCATION).equals(accountMode)) {
|
return accountBalance;
|
}
|
if (Integer.valueOf(ACCOUNT_MODE_SHARED).equals(accountMode)) {
|
return companyBalance;
|
}
|
return null;
|
}
|
|
/**
|
* 远程查询公司VIN码列表(降级返回空列表,保证详情主流程不受影响)
|
*/
|
private List<TVinManagement> fetchVins(Integer companyId) {
|
try {
|
R<List<TVinManagement>> response = vinManagementClient.listByCompany(companyId);
|
return (response != null && R.isSuccess(response) && response.getData() != null)
|
? response.getData() : new ArrayList<>();
|
} catch (Exception e) {
|
return new ArrayList<>();
|
}
|
}
|
|
/**
|
* 远程查询公司卡片列表(降级返回空列表)
|
*/
|
private List<TChargingCard> fetchCards(Integer companyId) {
|
try {
|
R<List<TChargingCard>> response = chargingCardClient.listByCompany(companyId);
|
return (response != null && R.isSuccess(response) && response.getData() != null)
|
? response.getData() : new ArrayList<>();
|
} catch (Exception e) {
|
return new ArrayList<>();
|
}
|
}
|
|
/**
|
* 远程查询公司白名单列表(降级返回空列表)
|
*/
|
private List<TAppUserWhitelist> fetchWhitelists(Integer companyId) {
|
try {
|
R<List<TAppUserWhitelist>> response = appUserWhitelistClient.listByCompany(companyId);
|
return (response != null && R.isSuccess(response) && response.getData() != null)
|
? response.getData() : new ArrayList<>();
|
} catch (Exception e) {
|
return new ArrayList<>();
|
}
|
}
|
|
/**
|
* 批量查询车队名称(本地库 t_fleet,逻辑删除由 @TableLogic 自动过滤)
|
*/
|
private Map<Integer, String> loadFleetNameMap(List<Integer> fleetIds) {
|
Map<Integer, String> map = new HashMap<>();
|
if (fleetIds == null || fleetIds.isEmpty()) {
|
return map;
|
}
|
List<Integer> distinctIds = fleetIds.stream()
|
.filter(Objects::nonNull)
|
.distinct()
|
.collect(Collectors.toList());
|
if (distinctIds.isEmpty()) {
|
return map;
|
}
|
List<TFleet> fleets = fleetMapper.selectList(
|
Wrappers.<TFleet>lambdaQuery().in(TFleet::getId, distinctIds));
|
for (TFleet fleet : fleets) {
|
map.put(fleet.getId(), fleet.getFleetName());
|
}
|
return map;
|
}
|
|
/**
|
* 统计公司未删除的车队数量
|
*/
|
private Integer countFleet(Integer companyId) {
|
Long count = fleetMapper.selectCount(Wrappers.<TFleet>lambdaQuery().eq(TFleet::getCompanyId, companyId));
|
return count == null ? 0 : count.intValue();
|
}
|
|
/**
|
* 统计公司本月(自然月,按充值时间 [本月1日 00:00:00, 次月1日 00:00:00))到账金额合计
|
*/
|
private BigDecimal sumCurrentMonthRechargeAmount(Integer companyId) {
|
LocalDateTime start = monthStart(LocalDate.now());
|
LocalDateTime end = start.plusMonths(1);
|
List<TCompanyRechargeRecord> records = companyRechargeRecordMapper.selectList(
|
Wrappers.<TCompanyRechargeRecord>lambdaQuery()
|
.eq(TCompanyRechargeRecord::getCompanyId, companyId)
|
.ge(TCompanyRechargeRecord::getRechargeTime, start)
|
.lt(TCompanyRechargeRecord::getRechargeTime, end));
|
return sumArrivalAmount(records);
|
}
|
|
/**
|
* 统计公司累计充值到账金额(全部有效充值记录,逻辑删除由 @TableLogic 自动过滤)
|
*/
|
private BigDecimal sumRechargeAmount(Integer companyId) {
|
List<TCompanyRechargeRecord> records = companyRechargeRecordMapper.selectList(
|
Wrappers.<TCompanyRechargeRecord>lambdaQuery()
|
.eq(TCompanyRechargeRecord::getCompanyId, companyId));
|
return sumArrivalAmount(records);
|
}
|
|
/**
|
* 当月起始时刻(本月1日 00:00:00)
|
*/
|
static LocalDateTime monthStart(LocalDate today) {
|
return today.withDayOfMonth(1).atStartOfDay();
|
}
|
|
/**
|
* 汇总到账金额(空集合返回 0.00,忽略空金额)
|
*/
|
static BigDecimal sumArrivalAmount(List<TCompanyRechargeRecord> records) {
|
if (records == null || records.isEmpty()) {
|
return BigDecimal.ZERO.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);
|
}
|
return records.stream()
|
.map(TCompanyRechargeRecord::getArrivalAmount)
|
.filter(Objects::nonNull)
|
.reduce(BigDecimal.ZERO, BigDecimal::add)
|
.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);
|
}
|
|
/**
|
* 查询公司默认角色ID(内置角色 role_key=company)
|
*/
|
private Long resolveCompanyRoleId() {
|
R<Long> roleR = sysRoleClient.getRoleIdByRoleKey(COMPANY_ROLE_KEY);
|
if (roleR == null || 200 != roleR.getCode() || roleR.getData() == null) {
|
throw new RuntimeException("系统内置角色【" + COMPANY_ROLE_KEY + "】不存在或已停用,请先配置角色");
|
}
|
return roleR.getData();
|
}
|
|
/**
|
* 检查登录账号在 t_group_company 中是否已存在(编辑时排除自身)
|
*/
|
private boolean accountExists(String account, Integer excludeId) {
|
Long count = count(Wrappers.<TGroupCompany>lambdaQuery()
|
.eq(TGroupCompany::getAccount, account)
|
.ne(excludeId != null, TGroupCompany::getId, excludeId));
|
return count != null && count > 0;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public AjaxResult addCompany(TGroupCompany company) {
|
if (!StringUtils.hasText(company.getAccount())) {
|
return AjaxResult.error("登录账号不能为空");
|
}
|
// 添加企业弹窗不再配置账户模式:未指定时按共享模式建档,后续可在“账户模式”弹窗切换
|
if (company.getAccountMode() == null) {
|
company.setAccountMode(ACCOUNT_MODE_SHARED);
|
}
|
if (!StringUtils.hasText(company.getPassword())) {
|
return AjaxResult.error("登录密码不能为空");
|
}
|
if (accountExists(company.getAccount(), null)) {
|
return AjaxResult.error("登录账号不能重复");
|
}
|
R<SysUser> existR = sysUserClient.queryUserByUserName(company.getAccount());
|
SysUser existUser = existR == null ? null : existR.getData();
|
if (existUser != null) {
|
return AjaxResult.error("登录账号不能重复");
|
}
|
Long roleId = resolveCompanyRoleId();
|
SysUser user = new SysUser();
|
user.setUserName(company.getAccount());
|
user.setNickName(company.getName());
|
user.setPhonenumber(company.getContactPhone());
|
user.setPassword(company.getPassword());
|
user.setStatus("0");
|
user.setDelFlag("0");
|
user.setRoleId(roleId);
|
user.setRoleType(COMPANY_ROLE_TYPE);
|
user.setPassWordUpdate(new Date());
|
R<Long> r = sysUserClient.addSysUser(user);
|
if (r == null || 200 != r.getCode()) {
|
throw new RuntimeException(r == null ? "创建登录账号失败" : r.getMsg());
|
}
|
save(company);
|
// 回写 objectId=公司ID(账号创建时公司尚未保存)
|
R<SysUser> createdR = sysUserClient.getSysUser(r.getData());
|
if (createdR != null && 200 == createdR.getCode() && createdR.getData() != null) {
|
SysUser created = createdR.getData();
|
created.setObjectId(company.getId());
|
sysUserClient.updateUser(created);
|
}
|
return AjaxResult.success();
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public AjaxResult editCompany(TGroupCompany company) {
|
TGroupCompany old = getById(company.getId());
|
if (old == null) {
|
return AjaxResult.error("公司不存在");
|
}
|
// 编辑时账号只读(前端禁用),此处兼容外部调用方传入账号的场景:唯一性校验(排除自身)
|
if (StringUtils.hasText(company.getAccount())) {
|
if (accountExists(company.getAccount(), company.getId())) {
|
return AjaxResult.error("登录账号不能重复");
|
}
|
}
|
String account = StringUtils.hasText(company.getAccount()) ? company.getAccount() : old.getAccount();
|
if (StringUtils.hasText(account)) {
|
syncCompanyUserOnEdit(company, account);
|
}
|
updateById(company);
|
return AjaxResult.success();
|
}
|
|
/**
|
* 编辑公司时同步登录账号:存在则仅更新账号资料(不修改密码),不存在则补建
|
*/
|
private void syncCompanyUserOnEdit(TGroupCompany company, String account) {
|
R<SysUser> userR = sysUserClient.queryUserByUserName(account);
|
SysUser user = userR == null ? null : userR.getData();
|
if (user == null) {
|
if (!StringUtils.hasText(company.getPassword())) {
|
throw new RuntimeException("补建登录账号时登录密码不能为空");
|
}
|
Long roleId = resolveCompanyRoleId();
|
SysUser newUser = new SysUser();
|
newUser.setUserName(account);
|
newUser.setNickName(company.getName());
|
newUser.setPhonenumber(company.getContactPhone());
|
newUser.setPassword(company.getPassword());
|
newUser.setStatus("0");
|
newUser.setDelFlag("0");
|
newUser.setRoleId(roleId);
|
newUser.setRoleType(COMPANY_ROLE_TYPE);
|
newUser.setObjectId(company.getId());
|
newUser.setPassWordUpdate(new Date());
|
R<Long> r = sysUserClient.addSysUser(newUser);
|
if (r == null || 200 != r.getCode()) {
|
throw new RuntimeException(r == null ? "创建登录账号失败" : r.getMsg());
|
}
|
return;
|
}
|
// 远程查询返回的密码字段可能是哈希或脱敏值,显式清空避免再次写回
|
user.setPassword(null);
|
user.setPassWordUpdate(null);
|
user.setUserName(account);
|
user.setNickName(company.getName());
|
user.setPhonenumber(company.getContactPhone());
|
R<Boolean> r = sysUserClient.updateSysUser(user);
|
if (r == null || 200 != r.getCode()) {
|
throw new RuntimeException(r == null ? "更新登录账号失败" : r.getMsg());
|
}
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public AjaxResult delCompany(Integer id) {
|
TGroupCompany company = getById(id);
|
if (company == null) {
|
return AjaxResult.error("公司不存在");
|
}
|
removeById(id);
|
if (StringUtils.hasText(company.getAccount())) {
|
R<SysUser> userR = sysUserClient.queryUserByUserName(company.getAccount());
|
SysUser user = userR == null ? null : userR.getData();
|
if (user != null) {
|
user.setDelFlag("2");
|
sysUserClient.updateUser(user);
|
}
|
}
|
return AjaxResult.success();
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public AjaxResult updateAccountMode(Integer id, Integer accountMode) {
|
if (id == null) {
|
return AjaxResult.error("公司ID不能为空");
|
}
|
if (accountMode == null
|
|| (accountMode != ACCOUNT_MODE_SHARED && accountMode != ACCOUNT_MODE_ALLOCATION)) {
|
return AjaxResult.error("账户模式不合法");
|
}
|
TGroupCompany company = getById(id);
|
if (company == null) {
|
return AjaxResult.error("公司不存在");
|
}
|
TGroupCompany update = new TGroupCompany();
|
update.setId(id);
|
update.setAccountMode(accountMode);
|
updateById(update);
|
return AjaxResult.success();
|
}
|
|
}
|