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;
/**
*
* 公司(企业)管理 服务实现类
*
*
* @author mxcd
* @since 2026-09-22
*/
@Service
public class TGroupCompanyServiceImpl extends ServiceImpl 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;
}
company.setChargingUsers(buildChargingUsers(company));
return company;
}
/**
* 聚合公司充电用户信息:VIN码、刷卡、白名单三类数据按统一结构返回。
* 仅公司详情使用;remoteGet 远程调用保持 getDetail(不聚合),避免跨服务循环调用。
*/
private List buildChargingUsers(TGroupCompany company) {
Integer companyId = company.getId();
Integer accountMode = company.getAccountMode();
List result = new ArrayList<>();
List vins = fetchVins(companyId);
List cards = fetchCards(companyId);
List whitelists = fetchWhitelists(companyId);
List 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 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 fetchVins(Integer companyId) {
try {
R> 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 fetchCards(Integer companyId) {
try {
R> 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 fetchWhitelists(Integer companyId) {
try {
R> 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 loadFleetNameMap(List fleetIds) {
Map map = new HashMap<>();
if (fleetIds == null || fleetIds.isEmpty()) {
return map;
}
List distinctIds = fleetIds.stream()
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (distinctIds.isEmpty()) {
return map;
}
List fleets = fleetMapper.selectList(
Wrappers.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.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 records = companyRechargeRecordMapper.selectList(
Wrappers.lambdaQuery()
.eq(TCompanyRechargeRecord::getCompanyId, companyId)
.ge(TCompanyRechargeRecord::getRechargeTime, start)
.lt(TCompanyRechargeRecord::getRechargeTime, end));
return sumArrivalAmount(records);
}
/**
* 统计公司累计充值到账金额(全部有效充值记录,逻辑删除由 @TableLogic 自动过滤)
*/
private BigDecimal sumRechargeAmount(Integer companyId) {
List records = companyRechargeRecordMapper.selectList(
Wrappers.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 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 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.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 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 r = sysUserClient.addSysUser(user);
if (r == null || 200 != r.getCode()) {
throw new RuntimeException(r == null ? "创建登录账号失败" : r.getMsg());
}
save(company);
// 回写 objectId=公司ID(账号创建时公司尚未保存)
R 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 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 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 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 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();
}
}