diff --git a/ibs/src/main/java/com/ruoyi/ibs/customerselect/controller/MyCustomerController.java b/ibs/src/main/java/com/ruoyi/ibs/customerselect/controller/MyCustomerController.java index fc39743..b1cee71 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/customerselect/controller/MyCustomerController.java +++ b/ibs/src/main/java/com/ruoyi/ibs/customerselect/controller/MyCustomerController.java @@ -18,8 +18,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.rmi.ServerException; @@ -220,4 +222,18 @@ public class MyCustomerController extends BaseController { } return AjaxResult.success(iSysCampaignGroupCustomerService.appointCustCamp( custId, custName, custIdc, custPhone, custIsn,socialCreditCode,lpName, campaignId, custType)); } + + @Log(title = "我的客户-异步导入企业客户价值分层", businessType = com.ruoyi.common.enums.BusinessType.IMPORT) + @PostMapping(value = "/importBusinessCustLevelAsync", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @ApiOperation("异步导入企业客户价值分层") + public AjaxResult importBusinessCustLevelAsync(@RequestPart("file") MultipartFile file) { + return AjaxResult.success("导入任务已提交,后台正在处理", myCustomerService.importBusinessCustLevelAsync(file)); + } + + @Log(title = "我的客户-查询企业客户价值分层导入状态") + @GetMapping("/importBusinessCustLevelStatus/{taskId}") + @ApiOperation("查询企业客户价值分层导入状态") + public AjaxResult importBusinessCustLevelStatus(@PathVariable String taskId) { + return AjaxResult.success(myCustomerService.getBusinessCustLevelImportStatus(taskId)); + } } diff --git a/ibs/src/main/java/com/ruoyi/ibs/customerselect/domain/BusinessCustLevelImportExcelDTO.java b/ibs/src/main/java/com/ruoyi/ibs/customerselect/domain/BusinessCustLevelImportExcelDTO.java new file mode 100644 index 0000000..dda76cb --- /dev/null +++ b/ibs/src/main/java/com/ruoyi/ibs/customerselect/domain/BusinessCustLevelImportExcelDTO.java @@ -0,0 +1,14 @@ +package com.ruoyi.ibs.customerselect.domain; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +@Data +public class BusinessCustLevelImportExcelDTO { + + @ExcelProperty("统信码") + private String socialCreditCode; + + @ExcelProperty("价值分层") + private String custLevel; +} diff --git a/ibs/src/main/java/com/ruoyi/ibs/customerselect/domain/BusinessCustLevelImportTaskVO.java b/ibs/src/main/java/com/ruoyi/ibs/customerselect/domain/BusinessCustLevelImportTaskVO.java new file mode 100644 index 0000000..2c09774 --- /dev/null +++ b/ibs/src/main/java/com/ruoyi/ibs/customerselect/domain/BusinessCustLevelImportTaskVO.java @@ -0,0 +1,30 @@ +package com.ruoyi.ibs.customerselect.domain; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class BusinessCustLevelImportTaskVO implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 0-处理中 1-成功 2-失败 + */ + private String status; + + private String message; + + private Integer totalCount; + + private Integer successCount; + + private Integer ignoredCount; + + private String userName; + + private String createTime; + + private String finishTime; +} diff --git a/ibs/src/main/java/com/ruoyi/ibs/customerselect/mapper/CustInfoBusinessMapper.java b/ibs/src/main/java/com/ruoyi/ibs/customerselect/mapper/CustInfoBusinessMapper.java index d66cdd4..5a00f15 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/customerselect/mapper/CustInfoBusinessMapper.java +++ b/ibs/src/main/java/com/ruoyi/ibs/customerselect/mapper/CustInfoBusinessMapper.java @@ -2,6 +2,7 @@ package com.ruoyi.ibs.customerselect.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.common.core.domain.entity.CustInfoBusiness; +import com.ruoyi.ibs.customerselect.domain.BusinessCustLevelImportExcelDTO; import com.ruoyi.ibs.customerselect.domain.CustBaseInfo; import com.ruoyi.ibs.customerselect.domain.CustInfoDeleteFromAnchor; import com.ruoyi.ibs.customerselect.domain.CustInfoUpdateFromAnchor; @@ -242,4 +243,10 @@ public interface CustInfoBusinessMapper extends BaseMapper public int insertCustomersToBusinessByScCode(List sysGroupCustomers); List selectRecord(String socialCreditCode); + + List selectExistingSocialCreditCodes(@Param("socialCreditCodes") List socialCreditCodes, + @Param("deptCode") String deptCode); + + int batchUpdateCustLevelBySocialCreditCode(@Param("list") List list, + @Param("deptCode") String deptCode); } diff --git a/ibs/src/main/java/com/ruoyi/ibs/customerselect/service/IMyCustomerService.java b/ibs/src/main/java/com/ruoyi/ibs/customerselect/service/IMyCustomerService.java index 8c6fb09..949c804 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/customerselect/service/IMyCustomerService.java +++ b/ibs/src/main/java/com/ruoyi/ibs/customerselect/service/IMyCustomerService.java @@ -1,6 +1,7 @@ package com.ruoyi.ibs.customerselect.service; import com.ruoyi.ibs.customerselect.domain.*; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -41,4 +42,8 @@ public interface IMyCustomerService { public CustListSearchVo selectCustomListSearchVo(CustBaseInfo sysCustomerBasedata); MerchantMcspInfo selectmerchantMessage(String custId); + + String importBusinessCustLevelAsync(MultipartFile file); + + BusinessCustLevelImportTaskVO getBusinessCustLevelImportStatus(String taskId); } diff --git a/ibs/src/main/java/com/ruoyi/ibs/customerselect/service/Impl/MyCustomerServiceImpl.java b/ibs/src/main/java/com/ruoyi/ibs/customerselect/service/Impl/MyCustomerServiceImpl.java index df539f4..2d565cd 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/customerselect/service/Impl/MyCustomerServiceImpl.java +++ b/ibs/src/main/java/com/ruoyi/ibs/customerselect/service/Impl/MyCustomerServiceImpl.java @@ -1,13 +1,19 @@ package com.ruoyi.ibs.customerselect.service.Impl; +import com.alibaba.excel.EasyExcel; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.annotation.DataScope; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.entity.CustInfoBusiness; import com.ruoyi.common.core.domain.entity.SysDictData; import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.ibs.dashboard.service.NotificationService; +import com.ruoyi.ibs.customerselect.domain.BusinessCustLevelImportExcelDTO; +import com.ruoyi.ibs.customerselect.domain.BusinessCustLevelImportTaskVO; import com.ruoyi.ibs.customerselect.domain.*; import com.ruoyi.ibs.customerselect.domain.vo.GridRelateVO; import com.ruoyi.ibs.customerselect.mapper.CustInfoBusinessMapper; @@ -32,9 +38,16 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** @@ -45,6 +58,10 @@ import java.util.stream.Collectors; @Service public class MyCustomerServiceImpl implements IMyCustomerService { + private static final String BUSINESS_CUST_LEVEL_IMPORT_TASK_KEY = "BUSINESS_CUST_LEVEL_IMPORT_TASK_"; + private static final int BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE = 1000; + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + @Autowired private CustInfoBusinessMapper custInfoBusinessMapper; @@ -80,6 +97,12 @@ public class MyCustomerServiceImpl implements IMyCustomerService { @Resource private RedisCache redisCache; + @Resource + private NotificationService notificationService; + + @Resource(name = "excelImportExecutor") + private ExecutorService excelImportExecutor; + private static Logger logger = LoggerFactory.getLogger(MyCustomerServiceImpl.class); /** @@ -467,5 +490,154 @@ public class MyCustomerServiceImpl implements IMyCustomerService { return merchantMcspInfoMapper.selectOne(new LambdaQueryWrapper().eq(MerchantMcspInfo::getCustId,custId)); } + @Override + public String importBusinessCustLevelAsync(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new ServiceException("导入文件不能为空"); + } + byte[] fileBytes; + try { + fileBytes = file.getBytes(); + } catch (IOException e) { + throw new ServiceException("读取导入文件失败"); + } + String taskId = IdUtils.fastSimpleUUID(); + String userName = SecurityUtils.getUsername(); + String deptCode = SecurityUtils.getHeadId(); + BusinessCustLevelImportTaskVO taskVO = new BusinessCustLevelImportTaskVO(); + taskVO.setStatus("0"); + taskVO.setMessage("导入任务已提交,后台正在处理"); + taskVO.setTotalCount(0); + taskVO.setSuccessCount(0); + taskVO.setIgnoredCount(0); + taskVO.setUserName(userName); + taskVO.setCreateTime(formatNow()); + cacheBusinessCustLevelImportTask(taskId, taskVO); + excelImportExecutor.submit(() -> doImportBusinessCustLevel(taskId, userName, deptCode, fileBytes)); + return taskId; + } + + @Override + public BusinessCustLevelImportTaskVO getBusinessCustLevelImportStatus(String taskId) { + BusinessCustLevelImportTaskVO taskVO = redisCache.getCacheObject(getBusinessCustLevelImportTaskKey(taskId)); + if (taskVO == null) { + throw new ServiceException("导入任务不存在或已过期"); + } + return taskVO; + } + + private void doImportBusinessCustLevel(String taskId, String userName, String deptCode, byte[] fileBytes) { + BusinessCustLevelImportTaskVO taskVO = redisCache.getCacheObject(getBusinessCustLevelImportTaskKey(taskId)); + if (taskVO == null) { + taskVO = new BusinessCustLevelImportTaskVO(); + taskVO.setUserName(userName); + taskVO.setCreateTime(formatNow()); + } + try { + List importRows = EasyExcel.read(new ByteArrayInputStream(fileBytes)) + .head(BusinessCustLevelImportExcelDTO.class) + .sheet() + .doReadSync(); + + Map levelMap = new LinkedHashMap<>(); + if (importRows != null) { + for (BusinessCustLevelImportExcelDTO row : importRows) { + String socialCreditCode = normalizeImportCell(row.getSocialCreditCode()); + String custLevel = normalizeImportCell(row.getCustLevel()); + if (StringUtils.isEmpty(socialCreditCode)) { + continue; + } + levelMap.put(socialCreditCode, custLevel); + } + } + + if (levelMap.isEmpty()) { + throw new ServiceException("Excel中未识别到有效的统信码和价值分层数据"); + } + + List existingSocialCreditCodes = getExistingBusinessSocialCreditCodes(new ArrayList<>(levelMap.keySet()), deptCode); + Set existingCodeSet = new HashSet<>(existingSocialCreditCodes); + List updateList = new ArrayList<>(); + for (Map.Entry entry : levelMap.entrySet()) { + if (!existingCodeSet.contains(entry.getKey())) { + continue; + } + BusinessCustLevelImportExcelDTO dto = new BusinessCustLevelImportExcelDTO(); + dto.setSocialCreditCode(entry.getKey()); + dto.setCustLevel(entry.getValue()); + updateList.add(dto); + } + + batchUpdateBusinessCustLevel(updateList, deptCode); + + int totalCount = levelMap.size(); + int successCount = updateList.size(); + int ignoredCount = totalCount - successCount; + String message = String.format("公司客户视图分层分类数据导入完成,成功更新%d条,忽略%d条", successCount, ignoredCount); + + taskVO.setStatus("1"); + taskVO.setMessage(message); + taskVO.setTotalCount(totalCount); + taskVO.setSuccessCount(successCount); + taskVO.setIgnoredCount(ignoredCount); + taskVO.setFinishTime(formatNow()); + cacheBusinessCustLevelImportTask(taskId, taskVO); + notificationService.sendNotification(userName, message); + } catch (Exception e) { + String errorMsg = StringUtils.isNotEmpty(e.getMessage()) ? e.getMessage() : "导入失败,请检查文件内容"; + taskVO.setStatus("2"); + taskVO.setMessage(errorMsg); + taskVO.setFinishTime(formatNow()); + cacheBusinessCustLevelImportTask(taskId, taskVO); + notificationService.sendNotification(userName, "公司客户视图分层分类数据导入失败:" + errorMsg); + logger.error("公司客户视图分层分类数据导入失败,taskId={}", taskId, e); + } + } + + private void batchUpdateBusinessCustLevel(List updateList, String deptCode) { + if (updateList == null || updateList.isEmpty()) { + return; + } + for (int i = 0; i < updateList.size(); i += BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE) { + int endIndex = Math.min(i + BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE, updateList.size()); + custInfoBusinessMapper.batchUpdateCustLevelBySocialCreditCode(updateList.subList(i, endIndex), deptCode); + } + } + + private List getExistingBusinessSocialCreditCodes(List socialCreditCodes, String deptCode) { + if (socialCreditCodes == null || socialCreditCodes.isEmpty()) { + return Collections.emptyList(); + } + List existingSocialCreditCodes = new ArrayList<>(); + for (int i = 0; i < socialCreditCodes.size(); i += BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE) { + int endIndex = Math.min(i + BUSINESS_CUST_LEVEL_IMPORT_BATCH_SIZE, socialCreditCodes.size()); + List batchCodes = socialCreditCodes.subList(i, endIndex); + List batchResult = custInfoBusinessMapper.selectExistingSocialCreditCodes(batchCodes, deptCode); + if (batchResult != null && !batchResult.isEmpty()) { + existingSocialCreditCodes.addAll(batchResult); + } + } + return existingSocialCreditCodes; + } + + private void cacheBusinessCustLevelImportTask(String taskId, BusinessCustLevelImportTaskVO taskVO) { + redisCache.setCacheObject(getBusinessCustLevelImportTaskKey(taskId), taskVO, 24, TimeUnit.HOURS); + } + + private String getBusinessCustLevelImportTaskKey(String taskId) { + return BUSINESS_CUST_LEVEL_IMPORT_TASK_KEY + taskId; + } + + private String normalizeImportCell(String value) { + if (value == null) { + return null; + } + String trimmedValue = value.trim(); + return trimmedValue.isEmpty() ? null : trimmedValue; + } + + private String formatNow() { + return LocalDateTime.now().format(DATE_TIME_FORMATTER); + } } diff --git a/ibs/src/main/java/com/ruoyi/ibs/dashboard/controller/DashboardController.java b/ibs/src/main/java/com/ruoyi/ibs/dashboard/controller/DashboardController.java index 7d27fc8..9d5f059 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/dashboard/controller/DashboardController.java +++ b/ibs/src/main/java/com/ruoyi/ibs/dashboard/controller/DashboardController.java @@ -173,6 +173,7 @@ public class DashboardController extends BaseController { public TableDataInfo list(SysNotice notice) { startPage(); + notice.setCurrentHeadDeptId(SecurityUtils.getHeadId() + "000"); List list = noticeService.selectNoticeList(notice); return getDataTable(list); } diff --git a/ibs/src/main/java/com/ruoyi/ibs/list/domain/Ent9vPortraitOrc.java b/ibs/src/main/java/com/ruoyi/ibs/list/domain/Ent9vPortraitOrc.java index dd27e5a..0da15b6 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/list/domain/Ent9vPortraitOrc.java +++ b/ibs/src/main/java/com/ruoyi/ibs/list/domain/Ent9vPortraitOrc.java @@ -23,44 +23,58 @@ public class Ent9vPortraitOrc implements Serializable { private String uniscid; /** 客户内码 */ + @TableField("cst_id") private String cstId; /** 机构号 */ + @TableField("org_no") private String orgNo; /** score_1合规经营 */ + @TableField("score_1") private BigDecimal score1; /** score_2风险准入 */ + @TableField("score_2") private BigDecimal score2; /** score_3高管信用评价 */ + @TableField("score_3") private String score3; /** score_4股东信用评价 */ + @TableField("score_4") private BigDecimal score4; /** score_5社会贡献度 */ + @TableField("score_5") private BigDecimal score5; /** score_6稳定经营 */ + @TableField("score_6") private BigDecimal score6; /** score_7经营能力 */ + @TableField("score_7") private BigDecimal score7; /** score_8偿债能力 */ + @TableField("score_8") private BigDecimal score8; /** score_9潜在代偿资源 */ + @TableField("score_9") private BigDecimal score9; /** 九维总分 */ + @TableField("score_all") private BigDecimal scoreAll; /** 九维总分排名 */ + @TableField("score_all_rank") private BigDecimal scoreAllRank; /** 会计日期 */ + @TableField("dat_dt") private String datDt; } diff --git a/ibs/src/main/java/com/ruoyi/ibs/list/domain/NineVFinalInfoOrc.java b/ibs/src/main/java/com/ruoyi/ibs/list/domain/NineVFinalInfoOrc.java index 8cb21e6..d31400b 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/list/domain/NineVFinalInfoOrc.java +++ b/ibs/src/main/java/com/ruoyi/ibs/list/domain/NineVFinalInfoOrc.java @@ -1,5 +1,6 @@ package com.ruoyi.ibs.list.domain; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @@ -21,206 +22,294 @@ public class NineVFinalInfoOrc implements Serializable { private String uniscid; /** 机构号 */ + @TableField("org_no") private String orgNo; + /** 一、企业合规经营模块 */ + /** 是否存在经营异常名录信息 */ + @TableField("score_1_1") private String score11; /** 是否存在严重违法失信企业名单信息 */ + @TableField("score_1_2") private String score12; /** 企业环境行为信仰登记是否为"E"或D */ + @TableField("score_1_3") private String score13; /** 是否存在税务重大税收违法黑名单信息 */ + @TableField("score_1_4") private String score14; /** 是否存在拖欠工资黑名单 */ + @TableField("score_1_5") private String score15; /** 是否存在工商吊销企业信息 */ + @TableField("score_1_6") private String score16; + /** 二、企业风险准入模块 */ + /** 是否存在注销企业信息 */ + @TableField("score_2_1") private String score21; /** 是否存在执行案件信息 */ + @TableField("score_2_2") private String score22; /** 是否存在查封信息 */ + @TableField("score_2_3") private String score23; /** 是否存在单位未履行生效裁判信息 */ + @TableField("score_2_4") private String score24; /** 是否存在企业破产清算信息 */ + @TableField("score_2_5") private String score25; /** 是否失信被执行人 */ + @TableField("score_2_6") private String score26; /** 是否为诉讼案件被告 */ + @TableField("score_2_7") private String score27; + /** 三、高管信用评价模块 */ + /** 是否存在查封信息(score_3) */ + @TableField("score_3_1") private String score31; /** 是否存在执行案件信息(score_3) */ + @TableField("score_3_2") private String score32; /** 是否存在个人未履行生效裁判信息 */ + @TableField("score_3_3") private String score33; /** 是否存在拖欠工资黑名单(score_3) */ + @TableField("score_3_4") private String score34; /** 是否失信被执行人(score_3) */ + @TableField("score_3_5") private String score35; /** 是否存在刑事案件被告人生效判决信息 */ + @TableField("score_3_6") private String score36; /** 是否为诉讼案件被告(score_3) */ + @TableField("score_3_7") private String score37; + /** 四、股东信用评价模块 */ + /** 是否存在查封信息(score_4) */ + @TableField("score_4_1") private String score41; /** 是否存在执行案件信息(score_4) */ + @TableField("score_4_2") private String score42; /** 是否存在个人未履行生效裁判信息(score_4) */ + @TableField("score_4_3") private String score43; /** 是否存在拖欠工资黑名单(score_4) */ + @TableField("score_4_4") private String score44; /** 是否失信被执行人(score_4) */ + @TableField("score_4_5") private String score45; /** 是否存在刑事案件被告人生效判决信息(score_4) */ + @TableField("score_4_6") private String score46; /** 是否为诉讼案件被告(score_4) */ + @TableField("score_4_7") private String score47; /** 是否存在企业未履行生效裁判信息 */ + @TableField("score_4_8") private String score48; + /** 五、企业社会贡献度模块 */ + /** 前12个月纳税金额 */ + @TableField("score_5_1") private String score51; /** 纳税等级 */ + @TableField("score_5_2") private String score52; /** 缴纳社保人数 */ + @TableField("score_5_3") private String score53; /** 公积金缴纳人数 */ + @TableField("score_5_4") private String score54; /** 是否为出口退税生产清单企业 */ + @TableField("score_5_5") private String score55; + /** 六、企业稳定经营模块 */ + /** 市场主体经营年限 */ + @TableField("score_6_1") private String score61; /** 股东(或发起人)或投资人信息认缴出资人数 */ + @TableField("score_6_2") private String score62; /** 最大股东持股占比 */ + @TableField("score_6_3") private String score63; /** 近三年法定代表人变更次数 */ + @TableField("score_6_4") private String score64; /** 近三年股东变更次数 */ + @TableField("score_6_5") private String score65; /** 近三年经营范围变更次数 */ + @TableField("score_6_6") private String score66; /** 近三年经营地址变更次数 */ + @TableField("score_6_7") private String score67; /** 近三年缴税年数 */ + @TableField("score_6_8") private String score68; /** 法人户籍 */ + @TableField("score_6_9") private String score69; /** 法人婚姻状况 */ + @TableField("score_6_10") private String score610; + /** 七、企业经营能力模块 */ + /** 上年增值税金额 */ + @TableField("score_7_1") private String score71; /** 今年增值税同比变动 */ + @TableField("score_7_2") private String score72; /** 上年企业所得税金额 */ + @TableField("score_7_3") private String score73; /** 今年所得税同比变动 */ + @TableField("score_7_4") private String score74; /** 缴纳社保人数同比变动 */ + @TableField("score_7_5") private String score75; /** 公积金缴纳人数同比变动 */ + @TableField("score_7_6") private String score76; /** 当年纳税金额同比变动 */ + @TableField("score_7_7") private String score77; /** 上年出口退税金额 */ + @TableField("score_7_8") private String score78; + /** 八、企业偿债能力模块 */ + /** 房产套数 */ + @TableField("score_8_1") private String score81; /** 房产面积 */ + @TableField("score_8_2") private String score82; /** 未抵押房产套数 */ + @TableField("score_8_3") private String score83; /** 未抵押房产面积 */ + @TableField("score_8_4") private String score84; /** 已抵押房产被担保债权数额 */ + @TableField("score_8_5") private String score85; /** 企业车产金额 */ + @TableField("score_8_6") private String score86; + /** 九、潜在代偿资源模块 */ + /** 法人及股东房产面积合计 */ + @TableField("score_9_1") private String score91; /** 法人及股东房产套数合计 */ + @TableField("score_9_2") private String score92; /** 法人及股东未抵押房产套数合计 */ + @TableField("score_9_3") private String score93; /** 法人及股东未抵押房产面积 */ + @TableField("score_9_4") private String score94; /** 法人及股东已抵押房产被担保债权数额合计 */ + @TableField("score_9_5") private String score95; /** 法人代表车产金额 */ + @TableField("score_9_6") private String score96; + /** 十、企业环境信用模块 */ + /** 生态信用扣分 */ + @TableField("score_b015") private String scoreB015; /** 列入环境失信黑名单时间 */ + @TableField("score_b016_time") private String scoreB016Time; /** 列入环境失信黑名单原因 */ + @TableField("score_b016_reason") private String scoreB016Reason; /** 环境行为信用评价等级 */ + @TableField("score_a020") private String scoreA020; } diff --git a/ibs/src/main/java/com/ruoyi/ibs/list/service/impl/CustInfoBusinessServiceImpl.java b/ibs/src/main/java/com/ruoyi/ibs/list/service/impl/CustInfoBusinessServiceImpl.java index c612e22..9c58c67 100644 --- a/ibs/src/main/java/com/ruoyi/ibs/list/service/impl/CustInfoBusinessServiceImpl.java +++ b/ibs/src/main/java/com/ruoyi/ibs/list/service/impl/CustInfoBusinessServiceImpl.java @@ -148,15 +148,15 @@ public class CustInfoBusinessServiceImpl implements ICustInfoBusinessService custInfoBusinessVo.setTagManual(TreeNode.convertToTreeByParentId(tagCreateVos)); // 仅当登录机构为825时,查询九维画像数据 -// if ("825".equals(getHeadId())) { -// LambdaQueryWrapper portraitWrapper = new LambdaQueryWrapper<>(); -// portraitWrapper.eq(Ent9vPortraitOrc::getUniscid, custInfoBusiness.getSocialCreditCode()); -// custInfoBusinessVo.setEnt9vPortrait(ent9vPortraitOrcMapper.selectOne(portraitWrapper)); -// -// LambdaQueryWrapper finalInfoWrapper = new LambdaQueryWrapper<>(); -// finalInfoWrapper.eq(NineVFinalInfoOrc::getUniscid, custInfoBusiness.getSocialCreditCode()); -// custInfoBusinessVo.setNineVFinalInfo(nineVFinalInfoOrcMapper.selectOne(finalInfoWrapper)); -// } + if ("965".equals(getHeadId())) { + LambdaQueryWrapper portraitWrapper = new LambdaQueryWrapper<>(); + portraitWrapper.eq(Ent9vPortraitOrc::getUniscid, custInfoBusiness.getSocialCreditCode()); + custInfoBusinessVo.setEnt9vPortrait(ent9vPortraitOrcMapper.selectOne(portraitWrapper)); + + LambdaQueryWrapper finalInfoWrapper = new LambdaQueryWrapper<>(); + finalInfoWrapper.eq(NineVFinalInfoOrc::getUniscid, custInfoBusiness.getSocialCreditCode()); + custInfoBusinessVo.setNineVFinalInfo(nineVFinalInfoOrcMapper.selectOne(finalInfoWrapper)); + } } return custInfoBusinessVo; } diff --git a/ibs/src/main/resources/mapper/CustInfoBusinessMapper.xml b/ibs/src/main/resources/mapper/CustInfoBusinessMapper.xml index 0b94978..13ee130 100644 --- a/ibs/src/main/resources/mapper/CustInfoBusinessMapper.xml +++ b/ibs/src/main/resources/mapper/CustInfoBusinessMapper.xml @@ -857,6 +857,28 @@ select id,cust_name from cust_info_business where social_credit_code = #{socialCreditCode} + + + + update cust_info_business_${deptCode} + set cust_level = case social_credit_code + + when #{item.socialCreditCode} then #{item.custLevel} + + end + where social_credit_code in + + #{item.socialCreditCode} + + + INSERT INTO cust_info_business (cust_id,cust_name, social_credit_code, update_by, update_time, cust_phone, industry, asset, credit) @@ -866,4 +888,4 @@ - \ No newline at end of file + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java index 8c07a54..4c976f3 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java @@ -31,6 +31,12 @@ public class SysNotice extends BaseEntity /** 公告状态(0正常 1关闭) */ private String status; + /** 可见总行部门ID,多选逗号分隔 */ + private String deptIds; + + /** 当前登录用户所属总行部门ID,仅用于查询过滤 */ + private String currentHeadDeptId; + public Long getNoticeId() { return noticeId; @@ -84,6 +90,26 @@ public class SysNotice extends BaseEntity return status; } + public String getDeptIds() + { + return deptIds; + } + + public void setDeptIds(String deptIds) + { + this.deptIds = deptIds; + } + + public String getCurrentHeadDeptId() + { + return currentHeadDeptId; + } + + public void setCurrentHeadDeptId(String currentHeadDeptId) + { + this.currentHeadDeptId = currentHeadDeptId; + } + @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) @@ -92,6 +118,7 @@ public class SysNotice extends BaseEntity .append("noticeType", getNoticeType()) .append("noticeContent", getNoticeContent()) .append("status", getStatus()) + .append("deptIds", getDeptIds()) .append("createBy", getCreateBy()) .append("createTime", getCreateTime()) .append("updateBy", getUpdateBy()) diff --git a/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml index 65d3079..948aa20 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -10,6 +10,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -18,7 +19,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark + select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, dept_ids, create_by, create_time, update_by, update_time, remark from sys_notice @@ -39,6 +40,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" AND create_by like concat('%', #{createBy}, '%') + + AND (dept_ids is null or dept_ids = '' or find_in_set(#{currentHeadDeptId}, dept_ids)) + @@ -48,6 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" notice_type, notice_content, status, + dept_ids, remark, create_by, create_time @@ -56,6 +61,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{noticeType}, #{noticeContent}, #{status}, + #{deptIds}, #{remark}, #{createBy}, sysdate() @@ -69,6 +75,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" notice_type = #{noticeType}, notice_content = #{noticeContent}, status = #{status}, + dept_ids = #{deptIds}, update_by = #{updateBy}, update_time = sysdate() @@ -86,4 +93,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - \ No newline at end of file + diff --git a/ruoyi-ui/src/api/grid/mycustomer.js b/ruoyi-ui/src/api/grid/mycustomer.js index 117fdfc..1364181 100644 --- a/ruoyi-ui/src/api/grid/mycustomer.js +++ b/ruoyi-ui/src/api/grid/mycustomer.js @@ -91,4 +91,20 @@ export function uploadTag(data) { data: data, isUpload: true }) -} \ No newline at end of file +} + +export function importBusinessCustLevelAsync(data) { + return request({ + url: '/system/custBaseInfo/importBusinessCustLevelAsync', + method: 'post', + data, + isUpload: true + }) +} + +export function getBusinessCustLevelImportStatus(taskId) { + return request({ + url: `/system/custBaseInfo/importBusinessCustLevelStatus/${taskId}`, + method: 'get' + }) +} diff --git a/ruoyi-ui/src/api/system/notice.js b/ruoyi-ui/src/api/system/notice.js index c274ea5..a67db5f 100644 --- a/ruoyi-ui/src/api/system/notice.js +++ b/ruoyi-ui/src/api/system/notice.js @@ -41,4 +41,12 @@ export function delNotice(noticeId) { url: '/system/notice/' + noticeId, method: 'delete' }) -} \ No newline at end of file +} + +// 查询总行列表 +export function getHeadList() { + return request({ + url: '/system/dept/headList', + method: 'get' + }) +} diff --git a/ruoyi-ui/src/layout/components/Navbar.vue b/ruoyi-ui/src/layout/components/Navbar.vue index 43db879..a950c34 100644 --- a/ruoyi-ui/src/layout/components/Navbar.vue +++ b/ruoyi-ui/src/layout/components/Navbar.vue @@ -168,7 +168,8 @@ export default { notReadCount: 0, noticeCenterList: [], open2: false, - downCenterList: [] + downCenterList: [], + noticeCenterTimer: null }; }, computed: { @@ -192,6 +193,12 @@ export default { }, created() { this.getCenterList(); + this.startNoticePolling(); + window.addEventListener('notice-center-refresh', this.getCenterList); + }, + beforeDestroy() { + this.clearNoticePolling(); + window.removeEventListener('notice-center-refresh', this.getCenterList); }, methods: { openModal() { @@ -223,6 +230,18 @@ export default { } }); }, + startNoticePolling() { + this.clearNoticePolling(); + this.noticeCenterTimer = setInterval(() => { + this.getCenterList(); + }, 10000); + }, + clearNoticePolling() { + if (this.noticeCenterTimer) { + clearInterval(this.noticeCenterTimer); + this.noticeCenterTimer = null; + } + }, // 下载中心列表 getDownCenterList() { diff --git a/ruoyi-ui/src/views/customer/charts/360charts/firm/index.vue b/ruoyi-ui/src/views/customer/charts/360charts/firm/index.vue index 1e1e8dd..9ff7cbf 100644 --- a/ruoyi-ui/src/views/customer/charts/360charts/firm/index.vue +++ b/ruoyi-ui/src/views/customer/charts/360charts/firm/index.vue @@ -1024,6 +1024,77 @@ + + + +
+ +
+

1.企业九维画像

+
+
+
+
+
+
+ 综合评分 +
+ {{ formatPortraitDisplay(ent9vPortrait.scoreAll) }} + +
+
+
+ 总分排名 +
+ {{ formatPortraitDisplay(ent9vPortrait.scoreAllRank) }} + +
+
+
+
+
+ + +
+

2.九维细项指标

+
+ + + + +
+
+
+
+ +
{ @@ -1347,6 +1531,12 @@ export default { showTagsList:[], addTagName:"", newCustTag:[], + // 企业九维数据 + ent9vPortrait: null, + nineVFinalInfo: null, + nineDimensionRadarChart: null, // 九维画像雷达图实例 + nineDimensionRadarResizeObserver: null, + nineDimensionRadarRenderTimer: null, regAddress: { lazy: true, lazyLoad(node, resolve) { @@ -1403,13 +1593,40 @@ export default { this.managerOptions = [] this.authUser = '' } + }, + ent9vPortrait: { + handler(newVal) { + if (!newVal) { + this.destroyNineDimensionRadar() + return + } + this.scheduleNineDimensionRadarRender() + }, + deep: true } }, components: { Custom }, computed: { - ...mapGetters(['roles', 'userName']), + ...mapGetters(['roles', 'userName', 'deptId']), + nineDimensionDetailGroups() { + return NINE_DIMENSION_DETAIL_CONFIG.map(group => ({ + title: group.title, + rows: this.buildNineDimensionRows(group.metrics) + })) + }, + nineDimensionScores() { + if (!this.ent9vPortrait) { + return [] + } + return [1, 2, 3, 4, 5, 6, 7, 8, 9].map(index => + this.normalizePortraitScore(this.ent9vPortrait[`score${index}`]) + ) + }, + nineDimensionRadarScale() { + return this.getAdaptiveRadarScale(this.nineDimensionScores) + }, isHeadAdmin() { return this.roles.includes('headAdmin') }, @@ -1443,6 +1660,9 @@ export default { // 海宁 is875() { return this.userName.slice(0, 3) === '875' + }, + is825() { + return String(this.deptId || '').substring(0, 3) === '825' } }, created() { @@ -1464,6 +1684,16 @@ export default { // systemUserAllTreeUser().then(res => { // this.secoureOption = res // }) + window.addEventListener('resize', this.handleRadarResize); + }, + activated() { + this.scheduleNineDimensionRadarRender(120) + }, + beforeDestroy() { + this.clearNineDimensionRadarRenderTimer() + this.destroyNineDimensionRadarObserver() + this.destroyNineDimensionRadar() + window.removeEventListener('resize', this.handleRadarResize); }, methods: { handleAddTag(){ @@ -1591,6 +1821,9 @@ export default { this.showTagsList=this.tagsList[0].children[0].children||[]; } this.tagManualList = cloneDeep(res.data.tagManual) || [] + // 企业九维数据 + this.ent9vPortrait = res.data.ent9vPortrait || null + this.nineVFinalInfo = res.data.nineVFinalInfo || null } }) }, @@ -2014,6 +2247,381 @@ export default { this.managerOptions = response.data; }); }, + normalizePortraitScore(value) { + const numericValue = Number(value) + return Number.isFinite(numericValue) ? numericValue : 0 + }, + getAdaptiveRadarScale(scores) { + const maxScore = Math.max(...scores, 0) + if (maxScore <= 0) { + return { + max: 10, + splitNumber: 5, + step: 2 + } + } + + const splitNumber = 5 + const roughStep = maxScore / splitNumber + const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep))) + const normalized = roughStep / magnitude + + let niceFactor = 10 + if (normalized <= 1) { + niceFactor = 1 + } else if (normalized <= 2) { + niceFactor = 2 + } else if (normalized <= 2.5) { + niceFactor = 2.5 + } else if (normalized <= 5) { + niceFactor = 5 + } + + const step = niceFactor * magnitude + let max = Math.ceil(maxScore / step) * step + if (max <= maxScore) { + max += step + } + + return { + max, + splitNumber, + step + } + }, + formatPortraitDisplay(value) { + if (value === null || value === undefined || value === '') { + return '-' + } + + const numericValue = Number(value) + if (!Number.isFinite(numericValue)) { + return value + } + + return Number.isInteger(numericValue) ? `${numericValue}` : numericValue.toFixed(2) + }, + clearNineDimensionRadarRenderTimer() { + if (this.nineDimensionRadarRenderTimer) { + clearTimeout(this.nineDimensionRadarRenderTimer) + this.nineDimensionRadarRenderTimer = null + } + }, + scheduleNineDimensionRadarRender(delay = 80) { + this.clearNineDimensionRadarRenderTimer() + this.nineDimensionRadarRenderTimer = setTimeout(() => { + this.initNineDimensionRadar() + }, delay) + }, + destroyNineDimensionRadar() { + if (this.nineDimensionRadarChart) { + this.nineDimensionRadarChart.dispose(); + this.nineDimensionRadarChart = null; + } + }, + destroyNineDimensionRadarObserver() { + if (this.nineDimensionRadarResizeObserver) { + this.nineDimensionRadarResizeObserver.disconnect() + this.nineDimensionRadarResizeObserver = null + } + }, + initNineDimensionRadarObserver(chartDom) { + if (!chartDom || typeof ResizeObserver === 'undefined' || this.nineDimensionRadarResizeObserver) { + return + } + + this.nineDimensionRadarResizeObserver = new ResizeObserver((entries) => { + const targetRect = entries && entries[0] ? entries[0].contentRect : null + if (this.nineDimensionRadarChart) { + this.handleRadarResize() + return + } + if (targetRect && targetRect.width >= 360 && targetRect.height >= 260) { + this.scheduleNineDimensionRadarRender(0) + } + }) + this.nineDimensionRadarResizeObserver.observe(chartDom) + if (chartDom.parentElement) { + this.nineDimensionRadarResizeObserver.observe(chartDom.parentElement) + } + }, + formatRadarLabel(label, chunkSize = 7) { + if (!label || label.length <= chunkSize) { + return label + } + + const segments = [] + for (let index = 0; index < label.length; index += chunkSize) { + segments.push(label.slice(index, index + chunkSize)) + } + + return segments.join('\n') + }, + // 九维画像分数字段标签映射 + getPortraitLabel(key) { + const labelMap = { + uniscid: '统一社会信用代码', + cstId: '客户内码', + orgNo: '机构号', + score1: '企业合规经营模块', + score2: '企业风险准入模块', + score3: '高管信用评价模块', + score4: '股东信用评价模块', + score5: '企业社会贡献模块', + score6: '企业稳定经营模块', + score7: '企业经营能力模块', + score8: '企业偿债能力模块', + score9: '潜在代偿资源模块', + scoreAll: '九维总分', + scoreAllRank: '九维总分排名', + datDt: '会计日期' + }; + return labelMap[key] || key; + }, + // 获取九维画像维度名称列表 + getNineDimensionLabels() { + return [ + '企业合规经营模块', + '企业风险准入模块', + '高管信用评价模块', + '股东信用评价模块', + '企业社会贡献模块', + '企业稳定经营模块', + '企业经营能力模块', + '企业偿债能力模块', + '潜在代偿资源模块' + ]; + }, + // 初始化九维画像雷达图 + initNineDimensionRadar() { + if (!this.ent9vPortrait) { + this.destroyNineDimensionRadar() + return; + } + + this.$nextTick(() => { + const chartDom = this.$refs.nineDimensionRadar; + if (!chartDom) return; + this.initNineDimensionRadarObserver(chartDom) + const chartWidth = chartDom.clientWidth || 0 + const chartHeight = chartDom.clientHeight || 0 + if (chartWidth < 360 || chartHeight < 260) { + this.scheduleNineDimensionRadarRender(140) + return + } + + const scores = this.nineDimensionScores + const radarScale = this.nineDimensionRadarScale + const dimensionLabels = this.getNineDimensionLabels() + const isCompactChart = chartWidth > 0 && chartWidth < 760 + + const currentInstance = echarts.getInstanceByDom(chartDom) + if (currentInstance) { + this.nineDimensionRadarChart = currentInstance + } else { + this.destroyNineDimensionRadar() + this.nineDimensionRadarChart = echarts.init(chartDom); + } + + const option = { + tooltip: { + trigger: 'item', + backgroundColor: 'rgba(17, 24, 39, 0.92)', + borderColor: 'rgba(99, 102, 241, 0.35)', + borderWidth: 1, + padding: [10, 14], + textStyle: { + color: '#f8fafc', + fontSize: 13 + }, + formatter: (params) => { + const lines = dimensionLabels.map((name, index) => `${name}: ${this.formatPortraitDisplay(params.value[index])}`) + return lines.join('
') + } + }, + radar: { + indicator: dimensionLabels.map(name => ({ name, max: radarScale.max })), + shape: 'polygon', + splitNumber: radarScale.splitNumber, + radius: isCompactChart ? '76%' : '82%', + center: ['50%', '54%'], + name: { + formatter: name => (isCompactChart ? this.formatRadarLabel(name, 6) : name), + textStyle: { + color: '#364152', + fontSize: isCompactChart ? 13 : 14, + fontWeight: 500 + } + }, + splitLine: { + lineStyle: { + color: 'rgba(122, 108, 255, 0.34)', + width: 1 + } + }, + splitArea: { + show: true, + areaStyle: { + color: [ + 'rgba(107, 114, 128, 0.12)', + 'rgba(148, 163, 184, 0.1)', + 'rgba(203, 213, 225, 0.08)', + 'rgba(226, 232, 240, 0.06)', + 'rgba(241, 245, 249, 0.04)' + ] + } + }, + axisLine: { + lineStyle: { + color: 'rgba(148, 163, 184, 0.4)', + width: 1 + } + }, + axisNameGap: isCompactChart ? 4 : 8 + }, + series: [ + { + name: '企业九维画像', + type: 'radar', + data: [ + { + value: scores, + name: '当前企业', + itemStyle: { + color: '#ffbf2f' + }, + areaStyle: { + color: 'rgba(255, 191, 47, 0.68)' + }, + lineStyle: { + color: '#f5b700', + width: 2.5 + }, + symbol: 'circle', + symbolSize: 6 + } + ] + } + ], + animationDuration: 650 + }; + + this.nineDimensionRadarChart.setOption(option); + this.handleRadarResize() + setTimeout(() => { + this.handleRadarResize() + }, 120) + }); + }, + // 处理雷达图窗口大小变化 + handleRadarResize() { + if (this.nineDimensionRadarChart) { + this.$nextTick(() => { + this.nineDimensionRadarChart.resize(); + }) + } + }, + buildNineDimensionRows(metrics) { + const rows = [] + for (let index = 0; index < metrics.length; index += 2) { + const firstMetric = metrics[index] + const secondMetric = metrics[index + 1] + rows.push([ + { + ...firstMetric, + value: this.nineVFinalInfo ? this.nineVFinalInfo[firstMetric.key] : null + }, + ...(secondMetric + ? [{ + ...secondMetric, + value: this.nineVFinalInfo ? this.nineVFinalInfo[secondMetric.key] : null + }] + : []) + ]) + } + return rows + }, + formatNineDimensionValue(metric) { + const value = metric ? metric.value : null + if (value === null || value === undefined || value === '') { + return '-' + } + + const rawValue = `${value}`.trim() + const normalizedValue = /^-?\d+(\.0+)?$/.test(rawValue) ? `${Number(rawValue)}` : rawValue + + if (metric && metric.valueMap) { + if (Object.prototype.hasOwnProperty.call(metric.valueMap, normalizedValue)) { + return metric.valueMap[normalizedValue] + } + if (Object.prototype.hasOwnProperty.call(metric.valueMap, rawValue)) { + return metric.valueMap[rawValue] + } + } + + if (metric && metric.label && metric.label.includes('是否')) { + if (['1', 1, true, 'true', 'TRUE', '是'].includes(value)) { + return '是' + } + if (['2', 2, false, 'false', 'FALSE', '否'].includes(value)) { + return '否' + } + } + + return value + }, + // 九维详细信息字段标签映射(部分常用字段) + getFinalInfoLabel(key) { + const labelMap = { + uniscid: '统一社会信用代码', + orgNo: '机构号', + // 维度1 - 合规经营 + score11: '是否存在经营异常名录信息', + score12: '是否存在严重违法失信企业名单信息', + score13: '企业环境行为信用等级是否为E或D', + score14: '是否存在税务重大税收违法黑名单信息', + score15: '是否存在拖欠工资黑名单', + score16: '是否存在工商吊销企业信息', + // 维度2 - 风险准入 + score21: '是否存在注销企业信息', + score22: '是否存在执行案件信息', + score23: '是否存在查封信息', + score24: '是否存在单位未履行生效裁判信息', + score25: '是否存在企业破产清算信息', + score26: '是否失信被执行人', + score27: '是否为诉讼案件被告', + // 维度5 - 社会贡献度 + score51: '前12个月纳税金额', + score52: '纳税等级', + score53: '缴纳社保人数', + score54: '公积金缴纳人数', + score55: '是否为出口退税生产清单企业', + // 维度6 - 稳定经营 + score61: '市场主体经营年限', + score62: '认缴出资人数', + score63: '最大股东持股占比', + score64: '近三年法定代表人变更次数', + score65: '近三年股东变更次数', + score69: '法人户籍', + score610: '法人婚姻状况', + // 维度7 - 经营能力 + score71: '上年增值税金额', + score72: '今年增值税同比变动', + score73: '上年企业所得税金额', + score74: '今年所得税同比变动', + score75: '缴纳社保人数同比变动', + score77: '当年纳税金额同比变动', + score78: '上年出口退税金额', + // 维度8 - 偿债能力 + score81: '房产套数', + score82: '房产面积', + score83: '未抵押房产套数', + score84: '未抵押房产面积', + score85: '已抵押房产被担保债权数额', + score86: '企业车产金额' + }; + return labelMap[key] || key; + }, } } @@ -2405,4 +3013,210 @@ export default { width: 320px !important; } } + +// 企业九维数据样式 +.nine-dimension-data { + padding: 20px 0; + background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%); + border-radius: 14px; + + .nine-dimension-section { + margin-bottom: 30px; + + &:last-child { + margin-bottom: 0; + } + + .section-title { + font-size: 15px; + font-weight: 600; + color: #202938; + margin-bottom: 18px; + padding-bottom: 12px; + border-bottom: 1px solid #edf1f7; + } + + .nine-dimension-chart-container { + display: flex; + align-items: stretch; + gap: 30px; + padding: 26px 30px; + justify-content: space-between; + background: linear-gradient(135deg, #ffffff 0%, #fbfcff 100%); + border: 1px solid #e7edf7; + border-radius: 14px; + box-shadow: 0 8px 26px rgba(31, 55, 88, 0.06); + + .radar-chart-wrapper { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: stretch; + padding: 4px 18px 4px 0; + border-right: 1px solid #edf1f7; + + .nine-dimension-radar { + width: 100%; + max-width: none; + height: 430px; + margin: 0 auto; + } + } + + .nine-dimension-summary { + width: 236px; + flex-shrink: 0; + padding: 18px 20px; + background: #fff; + border: 1px solid #edf1f7; + border-radius: 12px; + box-shadow: 0 12px 28px rgba(34, 65, 120, 0.08); + align-self: center; + + .summary-item { + display: flex; + flex-direction: column; + gap: 10px; + padding: 16px 0; + border-bottom: 1px solid #edf1f7; + + &:last-child { + padding-bottom: 6px; + border-bottom: 0; + } + + .summary-label { + display: block; + font-size: 15px; + color: #202938; + font-weight: 600; + } + + .summary-content { + display: flex; + align-items: baseline; + justify-content: flex-end; + gap: 8px; + } + + .summary-value { + display: block; + font-size: 40px; + line-height: 1; + font-weight: 700; + color: #1f6feb; + letter-spacing: -1px; + + &.is-rank { + color: #22c55e; + } + } + + .summary-unit { + font-size: 18px; + font-weight: 600; + color: #202938; + } + } + } + } + + .nine-dimension-detail-table { + overflow-x: auto; + + .detail-table { + width: 100%; + min-width: 980px; + border-collapse: collapse; + table-layout: fixed; + border: 1px solid #ebeef5; + + td { + padding: 18px 16px; + border: 1px solid #ebeef5; + font-size: 13px; + line-height: 1.6; + color: #303133; + vertical-align: middle; + word-break: break-word; + } + + .category-cell { + width: 184px; + background: #eef3f9; + color: #1f6feb; + font-size: 15px; + font-weight: 700; + text-align: center; + } + + .metric-label-cell { + background: #f6f8fb; + font-weight: 600; + } + + .metric-value-cell { + width: 120px; + background: #fff; + font-weight: 500; + text-align: center; + } + + .is-empty { + background: #fff; + } + } + } + + .data-item { + margin-bottom: 12px; + padding: 10px; + background-color: #f5f7fa; + border-radius: 4px; + + .data-label { + display: inline-block; + width: 120px; + font-size: 14px; + color: #606266; + font-weight: 500; + } + + .data-value { + font-size: 14px; + color: #303133; + } + } + } +} + +@media (max-width: 1400px) { + .nine-dimension-data { + .nine-dimension-section { + .nine-dimension-chart-container { + flex-direction: column; + align-items: stretch; + + .radar-chart-wrapper { + padding-right: 0; + border-right: 0; + border-bottom: 1px solid #edf1f7; + padding-bottom: 18px; + margin-bottom: 4px; + + .nine-dimension-radar { + max-width: 100%; + height: 390px; + } + } + + .nine-dimension-summary { + width: 100%; + } + } + } + } +} diff --git a/ruoyi-ui/src/views/grid/charts/customer/index.vue b/ruoyi-ui/src/views/grid/charts/customer/index.vue index 50a0570..52f8a80 100644 --- a/ruoyi-ui/src/views/grid/charts/customer/index.vue +++ b/ruoyi-ui/src/views/grid/charts/customer/index.vue @@ -142,6 +142,29 @@ 导出前1000条 + @@ -1062,6 +1159,26 @@ export default { } } +.import-action { + display: inline-block; + position: relative; +} + +.business-import-upload { + display: inline-block; +} + +.import-question { + position: absolute; + top: -2px; + right: -14px; + color: #b9b9b9; + font-size: 13px; + cursor: pointer; + line-height: 1; + z-index: 1; +} + .iframe-wrap { width: 100%; height: 500px; diff --git a/ruoyi-ui/src/views/system/notice/index.vue b/ruoyi-ui/src/views/system/notice/index.vue index 7982b54..f584c35 100644 --- a/ruoyi-ui/src/views/system/notice/index.vue +++ b/ruoyi-ui/src/views/system/notice/index.vue @@ -83,6 +83,11 @@ + + +