feat(credit): 添加被执行人企业关联匹配功能
- 在 CreditJudgmentDebtorController 中新增 refreshCompanyIdByCompanyName 接口 - 实现根据企业名称自动匹配并更新 companyId 的批量处理逻辑 - 支持按租户维度进行企业名称匹配,避免跨租户误匹配 - 添加企业名称标准化处理和模糊匹配机制 - 实现批量更新和事务管理,提升处理效率 - 优化关键词搜索条件,精确匹配企业名称而非模糊匹配 - 添加公司名称规范化方法 normalizeCompanyName - 修复竞品表字段别名从 mainCompanyName 改为 companyName feat(shop): 优化分销商推荐关系绑定机制 - 修改 ShopDealerRefereeController 的 save 方法为幂等绑定 - 新增 bindFirstLevel 方法实现一级推荐关系的幂等绑定 - 添加用户身份验证和安全校验机制 - 增加 source 和 scene 字段支持来源追踪 - 实现重复绑定防护和业务异常处理 - 添加经销商有效性校验机制
This commit is contained in:
@@ -6,9 +6,11 @@ import com.gxwebsoft.common.core.web.BaseController;
|
||||
import com.gxwebsoft.common.core.web.BatchParam;
|
||||
import com.gxwebsoft.common.core.web.PageResult;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
import com.gxwebsoft.credit.entity.CreditCompany;
|
||||
import com.gxwebsoft.credit.entity.CreditJudgmentDebtor;
|
||||
import com.gxwebsoft.credit.param.CreditJudgmentDebtorImportParam;
|
||||
import com.gxwebsoft.credit.param.CreditJudgmentDebtorParam;
|
||||
import com.gxwebsoft.credit.service.CreditCompanyService;
|
||||
import com.gxwebsoft.credit.service.CreditJudgmentDebtorService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -51,6 +53,9 @@ public class CreditJudgmentDebtorController extends BaseController {
|
||||
@Resource
|
||||
private BatchImportSupport batchImportSupport;
|
||||
|
||||
@Resource
|
||||
private CreditCompanyService creditCompanyService;
|
||||
|
||||
@Operation(summary = "分页查询被执行人")
|
||||
@GetMapping("/page")
|
||||
public ApiResult<PageResult<CreditJudgmentDebtor>> page(CreditJudgmentDebtorParam param) {
|
||||
@@ -143,6 +148,171 @@ public class CreditJudgmentDebtorController extends BaseController {
|
||||
return fail("删除失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据企业名称匹配企业并更新 companyId(匹配 CreditCompany.name / CreditCompany.matchName)
|
||||
*
|
||||
* <p>默认仅更新 companyId 为空的记录;如需覆盖更新,传 onlyNull=false。</p>
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('credit:creditJudgmentDebtor:update')")
|
||||
@OperationLog
|
||||
@Operation(summary = "根据企业名称匹配并更新companyId")
|
||||
@PostMapping("/company-id/refresh")
|
||||
public ApiResult<Map<String, Object>> refreshCompanyIdByCompanyName(
|
||||
@RequestParam(value = "onlyNull", required = false, defaultValue = "true") Boolean onlyNull,
|
||||
@RequestParam(value = "limit", required = false) Integer limit
|
||||
) {
|
||||
User loginUser = getLoginUser();
|
||||
Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;
|
||||
|
||||
// 1) 读取待处理数据(仅取必要字段,避免一次性拉全表字段)
|
||||
var debtorQuery = creditJudgmentDebtorService.lambdaQuery()
|
||||
.select(CreditJudgmentDebtor::getId, CreditJudgmentDebtor::getName, CreditJudgmentDebtor::getCompanyId, CreditJudgmentDebtor::getTenantId)
|
||||
.eq(currentTenantId != null, CreditJudgmentDebtor::getTenantId, currentTenantId)
|
||||
.isNotNull(CreditJudgmentDebtor::getName);
|
||||
if (Boolean.TRUE.equals(onlyNull)) {
|
||||
debtorQuery.isNull(CreditJudgmentDebtor::getCompanyId);
|
||||
}
|
||||
if (limit != null && limit > 0) {
|
||||
debtorQuery.last("limit " + Math.min(limit, 200000));
|
||||
}
|
||||
List<CreditJudgmentDebtor> debtors = debtorQuery.list();
|
||||
|
||||
if (CollectionUtils.isEmpty(debtors)) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("updated", 0);
|
||||
result.put("matched", 0);
|
||||
result.put("notFound", 0);
|
||||
result.put("ambiguous", 0);
|
||||
return success("无可更新数据", result);
|
||||
}
|
||||
|
||||
// 2) 按租户维度匹配(避免管理员/跨租户场景误匹配)
|
||||
Map<Integer, List<CreditJudgmentDebtor>> debtorsByTenant = new LinkedHashMap<>();
|
||||
for (CreditJudgmentDebtor d : debtors) {
|
||||
if (d == null) {
|
||||
continue;
|
||||
}
|
||||
Integer tenantId = currentTenantId != null ? currentTenantId : d.getTenantId();
|
||||
if (tenantId == null) {
|
||||
// 未知租户下不做跨租户匹配,避免误更新
|
||||
continue;
|
||||
}
|
||||
debtorsByTenant.computeIfAbsent(tenantId, k -> new ArrayList<>()).add(d);
|
||||
}
|
||||
|
||||
// 3) 批量更新 companyId
|
||||
int updated = 0;
|
||||
int matched = 0;
|
||||
int notFound = 0;
|
||||
int ambiguous = 0;
|
||||
final int batchSize = 500;
|
||||
List<CreditJudgmentDebtor> updates = new ArrayList<>(batchSize);
|
||||
|
||||
final int inChunkSize = 900;
|
||||
for (Map.Entry<Integer, List<CreditJudgmentDebtor>> entry : debtorsByTenant.entrySet()) {
|
||||
Integer tenantId = entry.getKey();
|
||||
List<CreditJudgmentDebtor> tenantDebtors = entry.getValue();
|
||||
if (tenantId == null || CollectionUtils.isEmpty(tenantDebtors)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3.1) 查询当前租户下的 companyId 映射
|
||||
LinkedHashMap<String, Integer> companyIdByName = new LinkedHashMap<>();
|
||||
LinkedHashMap<String, Integer> ambiguousByName = new LinkedHashMap<>();
|
||||
java.util.LinkedHashSet<String> nameSet = new java.util.LinkedHashSet<>();
|
||||
for (CreditJudgmentDebtor d : tenantDebtors) {
|
||||
String name = normalizeCompanyName(d != null ? d.getName() : null);
|
||||
if (name != null) {
|
||||
nameSet.add(name);
|
||||
}
|
||||
}
|
||||
List<String> allNames = new ArrayList<>(nameSet);
|
||||
for (int i = 0; i < allNames.size(); i += inChunkSize) {
|
||||
List<String> chunk = allNames.subList(i, Math.min(allNames.size(), i + inChunkSize));
|
||||
if (CollectionUtils.isEmpty(chunk)) {
|
||||
continue;
|
||||
}
|
||||
List<CreditCompany> companies = creditCompanyService.lambdaQuery()
|
||||
.select(CreditCompany::getId, CreditCompany::getName, CreditCompany::getMatchName, CreditCompany::getTenantId)
|
||||
.eq(CreditCompany::getTenantId, tenantId)
|
||||
.and(w -> w.in(CreditCompany::getName, chunk).or().in(CreditCompany::getMatchName, chunk))
|
||||
.list();
|
||||
|
||||
for (CreditCompany c : companies) {
|
||||
if (c == null || c.getId() == null) {
|
||||
continue;
|
||||
}
|
||||
addCompanyNameMapping(companyIdByName, ambiguousByName, normalizeCompanyName(c.getName()), c.getId());
|
||||
addCompanyNameMapping(companyIdByName, ambiguousByName, normalizeCompanyName(c.getMatchName()), c.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 3.2) 更新当前租户下的被执行人 companyId
|
||||
for (CreditJudgmentDebtor d : tenantDebtors) {
|
||||
String key = normalizeCompanyName(d != null ? d.getName() : null);
|
||||
if (key == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Integer amb = ambiguousByName.get(key);
|
||||
if (amb != null && amb > 0) {
|
||||
ambiguous++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Integer companyId = companyIdByName.get(key);
|
||||
if (companyId == null) {
|
||||
notFound++;
|
||||
continue;
|
||||
}
|
||||
matched++;
|
||||
|
||||
boolean needUpdate = d.getCompanyId() == null || !companyId.equals(d.getCompanyId());
|
||||
if (Boolean.TRUE.equals(onlyNull)) {
|
||||
needUpdate = d.getCompanyId() == null;
|
||||
}
|
||||
if (!needUpdate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CreditJudgmentDebtor patch = new CreditJudgmentDebtor();
|
||||
patch.setId(d.getId());
|
||||
patch.setCompanyId(companyId);
|
||||
updates.add(patch);
|
||||
if (updates.size() >= batchSize) {
|
||||
updated += batchImportSupport.runInNewTx(() -> {
|
||||
boolean ok = creditJudgmentDebtorService.updateBatchById(updates, batchSize);
|
||||
return ok ? updates.size() : 0;
|
||||
});
|
||||
updates.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// currentTenantId 为空时,租户缺失的数据不做匹配更新,避免误更新
|
||||
if (currentTenantId == null) {
|
||||
for (CreditJudgmentDebtor d : debtors) {
|
||||
if (d != null && d.getTenantId() == null) {
|
||||
notFound++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!updates.isEmpty()) {
|
||||
updated += batchImportSupport.runInNewTx(() -> {
|
||||
boolean ok = creditJudgmentDebtorService.updateBatchById(updates, batchSize);
|
||||
return ok ? updates.size() : 0;
|
||||
});
|
||||
}
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("updated", updated);
|
||||
result.put("matched", matched);
|
||||
result.put("notFound", notFound);
|
||||
result.put("ambiguous", ambiguous);
|
||||
return success("更新完成,更新" + updated + "条", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量导入被执行人
|
||||
*/
|
||||
@@ -244,10 +414,32 @@ public class CreditJudgmentDebtorController extends BaseController {
|
||||
if (isImportHeaderRow(param)) {
|
||||
return true;
|
||||
}
|
||||
return ImportHelper.isBlank(param.getCaseNumber())
|
||||
&& ImportHelper.isBlank(param.getName())
|
||||
&& ImportHelper.isBlank(param.getName1())
|
||||
&& ImportHelper.isBlank(param.getCode());
|
||||
return ImportHelper.isBlank(param.getCaseNumber());
|
||||
}
|
||||
|
||||
private static String normalizeCompanyName(String name) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
String v = name.replace(' ', ' ').trim();
|
||||
return v.isEmpty() ? null : v;
|
||||
}
|
||||
|
||||
private static void addCompanyNameMapping(Map<String, Integer> idByName,
|
||||
Map<String, Integer> ambiguousByName,
|
||||
String key,
|
||||
Integer companyId) {
|
||||
if (key == null || companyId == null) {
|
||||
return;
|
||||
}
|
||||
Integer existing = idByName.get(key);
|
||||
if (existing == null) {
|
||||
idByName.put(key, companyId);
|
||||
return;
|
||||
}
|
||||
if (!existing.equals(companyId)) {
|
||||
ambiguousByName.put(key, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isImportHeaderRow(CreditJudgmentDebtorImportParam param) {
|
||||
|
||||
@@ -146,8 +146,10 @@ public class CreditJudicialDocumentController extends BaseController {
|
||||
int successCount = 0;
|
||||
|
||||
try {
|
||||
// 支持按选项卡名称导入:默认读取“裁判文书”sheet(不存在则回退到第 0 个sheet)
|
||||
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "裁判文书", 0);
|
||||
ExcelImportSupport.ImportResult<CreditJudicialDocumentImportParam> importResult = ExcelImportSupport.read(
|
||||
file, CreditJudicialDocumentImportParam.class, this::isEmptyImportRow);
|
||||
file, CreditJudicialDocumentImportParam.class, this::isEmptyImportRow, sheetIndex);
|
||||
List<CreditJudicialDocumentImportParam> list = importResult.getData();
|
||||
int usedTitleRows = importResult.getTitleRows();
|
||||
int usedHeadRows = importResult.getHeadRows();
|
||||
@@ -544,8 +546,7 @@ public class CreditJudicialDocumentController extends BaseController {
|
||||
if (param == null) {
|
||||
return true;
|
||||
}
|
||||
return ImportHelper.isBlank(param.getCaseNumber())
|
||||
&& ImportHelper.isBlank(param.getCauseOfAction());
|
||||
return ImportHelper.isBlank(param.getCaseNumber());
|
||||
}
|
||||
|
||||
private CreditJudicialDocument convertImportParamToEntity(CreditJudicialDocumentImportParam param) {
|
||||
|
||||
@@ -228,10 +228,10 @@ public class CreditJudiciaryController extends BaseController {
|
||||
}
|
||||
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
|
||||
// 验证必填字段
|
||||
if (item.getName() == null || item.getName().trim().isEmpty()) {
|
||||
errorMessages.add("第" + excelRowNumber + "行:项目名称不能为空");
|
||||
continue;
|
||||
}
|
||||
// if (item.getName() == null || item.getName().trim().isEmpty()) {
|
||||
// errorMessages.add("第" + excelRowNumber + "行:项目名称不能为空");
|
||||
// continue;
|
||||
// }
|
||||
if (item.getCode() == null || item.getCode().trim().isEmpty()) {
|
||||
errorMessages.add("第" + excelRowNumber + "行:唯一标识不能为空");
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user