package com.ruoyi.basic.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.ruoyi.basic.dto.StorageBlobVO; import com.ruoyi.basic.mapper.CustomerFinancialReportMapper; import com.ruoyi.basic.mapper.CustomerMapper; import com.ruoyi.basic.mapper.CustomerUserMapper; import com.ruoyi.basic.mapper.StorageBlobMapper; import com.ruoyi.basic.pojo.Customer; import com.ruoyi.basic.pojo.CustomerFinancialReport; import com.ruoyi.basic.pojo.CustomerUser; import com.ruoyi.basic.pojo.StorageBlob; import com.ruoyi.basic.utils.FileUtil; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.framework.security.LoginUser; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.time.Year; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; /** * 客户近3年财务报表Service */ @Service @RequiredArgsConstructor public class CustomerFinancialReportService extends ServiceImpl { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private final CustomerMapper customerMapper; private final CustomerUserMapper customerUserMapper; private final StorageBlobMapper storageBlobMapper; private final FileUtil fileUtil; /** * 后台按客户查询近3年财务报表 */ public List listByCustomer(Long customerId) { if (customerId == null) { throw new ServiceException("请选择客户"); } List reports = list(new LambdaQueryWrapper() .eq(CustomerFinancialReport::getCustomerId, customerId) .orderByDesc(CustomerFinancialReport::getReportYear)); refreshFileUrls(reports); fillCustomerName(reports); return reports; } /** * 客户账号查询自己的近3年财务报表 */ public List listMine() { Long customerId = currentCustomerId(); List reports = list(new LambdaQueryWrapper() .eq(CustomerFinancialReport::getCustomerId, customerId) .orderByDesc(CustomerFinancialReport::getReportYear)); refreshFileUrls(reports); fillCustomerName(reports); return reports; } /** * 保存财务报表:同一客户同一年度仅保留一条,可反复覆盖修改 */ @Transactional(rollbackFor = Exception.class) public CustomerFinancialReport saveReport(CustomerFinancialReport input) { if (input.getReportYear() == null) { throw new ServiceException("请选择报表年度"); } int currentYear = Year.now().getValue(); if (input.getReportYear() < currentYear - 2 || input.getReportYear() > currentYear) { throw new ServiceException("只能上传最近三个年度的财务报表"); } if (StringUtils.isBlank(input.getReportFiles())) { throw new ServiceException("请上传财务报表附件"); } Long customerId; if (isCustomerAccount()) { customerId = currentCustomerId(); } else { customerId = input.getCustomerId(); if (customerId == null || customerMapper.selectById(customerId) == null) { throw new ServiceException("客户不存在"); } } CustomerFinancialReport entity = getOne(new LambdaQueryWrapper() .eq(CustomerFinancialReport::getCustomerId, customerId) .eq(CustomerFinancialReport::getReportYear, input.getReportYear()), false); LoginUser loginUser = SecurityUtils.getLoginUser(); if (entity == null) { entity = new CustomerFinancialReport(); entity.setCustomerId(customerId); entity.setReportYear(input.getReportYear()); } entity.setReportName(input.getReportName()); entity.setReportFiles(input.getReportFiles()); // 重新提交后回到待审批状态,等待内部人员重新审批 entity.setAuditStatus(0); entity.setAuditUserId(null); entity.setAuditUserName(null); entity.setAuditTime(null); entity.setAuditRemark(null); entity.setUploadUserId(loginUser.getUserId()); entity.setUploadUserName(loginUser.getNickName()); saveOrUpdate(entity); return entity; } /** * 申报审批列表:内部人员查看所有客户的提交情况 */ public List auditList(String customerName, Integer reportYear, Integer auditStatus) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(reportYear != null, CustomerFinancialReport::getReportYear, reportYear) .eq(auditStatus != null, CustomerFinancialReport::getAuditStatus, auditStatus) .orderByDesc(CustomerFinancialReport::getUpdateTime); if (StringUtils.isNotBlank(customerName)) { List customerIds = customerMapper.selectList(new LambdaQueryWrapper() .like(Customer::getCustomerName, customerName)) .stream().map(Customer::getId).filter(Objects::nonNull).collect(Collectors.toList()); if (customerIds.isEmpty()) { return new java.util.ArrayList<>(); } wrapper.in(CustomerFinancialReport::getCustomerId, customerIds); } List reports = list(wrapper); refreshFileUrls(reports); fillCustomerName(reports); return reports; } /** * 审批财务报表:1通过 2退回 */ @Transactional(rollbackFor = Exception.class) public CustomerFinancialReport auditReport(Long id, Integer auditStatus, String auditRemark) { if (isCustomerAccount()) { throw new ServiceException("客户账号无权审批财务报表"); } if (id == null) { throw new ServiceException("请选择申报记录"); } if (auditStatus == null || auditStatus != 1 && auditStatus != 2) { throw new ServiceException("审批结果不正确"); } CustomerFinancialReport entity = getById(id); if (entity == null) { throw new ServiceException("申报记录不存在"); } LoginUser loginUser = SecurityUtils.getLoginUser(); entity.setAuditStatus(auditStatus); entity.setAuditUserId(loginUser.getUserId()); entity.setAuditUserName(loginUser.getNickName()); entity.setAuditTime(LocalDateTime.now()); entity.setAuditRemark(auditRemark); updateById(entity); return entity; } /** * 删除财务报表:仅后台人员可删除,客户账号不允许删除 */ @Transactional(rollbackFor = Exception.class) public void deleteReport(Long id) { if (isCustomerAccount()) { throw new ServiceException("客户账号无权删除财务报表"); } CustomerFinancialReport entity = getById(id); if (entity == null) { throw new ServiceException("财务报表不存在"); } removeById(id); } /** * 重新生成附件预览/下载地址:数据库中保存的是上传时的带签名地址,会过期,读取时按文件id刷新 */ private void refreshFileUrls(List reports) { if (reports == null || reports.isEmpty()) { return; } for (CustomerFinancialReport report : reports) { String files = report.getReportFiles(); if (StringUtils.isBlank(files)) { continue; } try { JsonNode root = OBJECT_MAPPER.readTree(files); if (root == null || !root.isArray()) { continue; } for (JsonNode node : root) { Long blobId = node.hasNonNull("id") ? node.get("id").asLong() : null; if (blobId == null) { continue; } StorageBlob blob = storageBlobMapper.selectById(blobId); if (blob == null) { continue; } StorageBlobVO vo = new StorageBlobVO(); vo.setUidFilename(blob.getUidFilename()); vo.setPath(blob.getPath()); vo.setResourceKey(blob.getResourceKey()); ObjectNode objectNode = (ObjectNode) node; objectNode.put("url", fileUtil.buildSignedPreviewUrl(vo)); objectNode.put("downloadURL", fileUtil.buildSignedDownloadUrl(vo)); if (!node.hasNonNull("name")) { objectNode.put("name", blob.getOriginalFilename()); } } report.setReportFiles(OBJECT_MAPPER.writeValueAsString(root)); } catch (Exception e) { // 旧数据或非 JSON 格式,保持原值 } } } private boolean isCustomerAccount() { Long userId = SecurityUtils.getUserId(); return !SecurityUtils.isAdmin(userId) && SecurityUtils.hasRole("customer"); } /** * 获取当前登录账号绑定的客户id */ private Long currentCustomerId() { CustomerUser binding = customerUserMapper.selectOne(new LambdaQueryWrapper() .eq(CustomerUser::getUserId, SecurityUtils.getUserId()).last("limit 1")); if (binding == null || binding.getCustomerId() == null) { throw new ServiceException("当前账号尚未绑定客户,请联系管理员"); } return binding.getCustomerId(); } private void fillCustomerName(List reports) { if (reports == null || reports.isEmpty()) { return; } Set ids = reports.stream().map(CustomerFinancialReport::getCustomerId) .filter(Objects::nonNull).collect(Collectors.toSet()); if (ids.isEmpty()) { return; } List customers = customerMapper.selectBatchIds(ids); java.util.Map nameMap = customers.stream() .collect(Collectors.toMap(Customer::getId, Customer::getCustomerName, (a, b) -> a)); reports.forEach(item -> item.setCustomerName(nameMap.getOrDefault(item.getCustomerId(), ""))); } }