|
|
@@ -0,0 +1,222 @@
|
|
|
+package com.fs.company.service.tenant.impl;
|
|
|
+
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
+import com.fs.call.config.CallApiConfig;
|
|
|
+import com.fs.common.enums.DataSourceType;
|
|
|
+import com.fs.common.utils.StringUtils;
|
|
|
+import com.fs.company.domain.CompanyVoiceApi;
|
|
|
+import com.fs.company.domain.CompanyVoiceApiTenant;
|
|
|
+import com.fs.company.domain.tenant.TenantCompanyVoiceApi;
|
|
|
+import com.fs.company.mapper.CompanyVoiceApiMapper;
|
|
|
+import com.fs.company.mapper.CompanyVoiceApiTenantMapper;
|
|
|
+import com.fs.company.mapper.tenant.TenantCompanyVoiceApiMapper;
|
|
|
+import com.fs.company.service.tenant.ITenantCompanyVoiceApiSyncService;
|
|
|
+import com.fs.framework.datasource.DynamicDataSourceContextHolder;
|
|
|
+import com.fs.framework.datasource.TenantDataSourceManager;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 总后台主库外呼接口/绑定同步至租户库 company_voice_api。
|
|
|
+ * 内部通过 TenantDataSourceManager.ensureSwitchByTenantId 切换租户数据源。
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class TenantCompanyVoiceApiSyncServiceImpl implements ITenantCompanyVoiceApiSyncService {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(TenantCompanyVoiceApiSyncServiceImpl.class);
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private CompanyVoiceApiMapper companyVoiceApiMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private CompanyVoiceApiTenantMapper companyVoiceApiTenantMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private TenantCompanyVoiceApiMapper tenantCompanyVoiceApiMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private TenantDataSourceManager tenantDataSourceManager;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void syncBindingToTenantDb(Long tenantId, Long apiId) {
|
|
|
+ if (tenantId == null || apiId == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ensureMasterDataSource();
|
|
|
+ CompanyVoiceApi masterApi = companyVoiceApiMapper.selectCompanyVoiceApiById(apiId);
|
|
|
+ CompanyVoiceApiTenant binding = companyVoiceApiTenantMapper.selectByApiAndTenant(apiId, tenantId);
|
|
|
+ if (masterApi == null) {
|
|
|
+ markDeletedInTenantDb(tenantId, apiId);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ TenantCompanyVoiceApi tenantRow = buildTenantRow(masterApi, binding);
|
|
|
+ runInTenantDb(tenantId, () -> tenantCompanyVoiceApiMapper.upsertTenantVoiceApi(tenantRow));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void syncMasterApiToAllTenantDbs(Long apiId) {
|
|
|
+ if (apiId == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ensureMasterDataSource();
|
|
|
+ CompanyVoiceApi masterApi = companyVoiceApiMapper.selectCompanyVoiceApiById(apiId);
|
|
|
+ List<CompanyVoiceApiTenant> bindings = companyVoiceApiTenantMapper.selectTenantsByApiId(apiId);
|
|
|
+ if (bindings == null || bindings.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (masterApi == null) {
|
|
|
+ for (CompanyVoiceApiTenant binding : bindings) {
|
|
|
+ if (binding == null || binding.getTenantId() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ markDeletedInTenantDb(binding.getTenantId(), apiId);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[TenantVoiceApiSync] 主库接口已删除,同步禁用租户库失败 apiId={}, tenantId={}",
|
|
|
+ apiId, binding.getTenantId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for (CompanyVoiceApiTenant binding : bindings) {
|
|
|
+ if (binding == null || binding.getTenantId() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ TenantCompanyVoiceApi tenantRow = buildTenantRow(masterApi, binding);
|
|
|
+ runInTenantDb(binding.getTenantId(),
|
|
|
+ () -> tenantCompanyVoiceApiMapper.upsertTenantVoiceApi(tenantRow));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[TenantVoiceApiSync] 同步主库接口到租户库失败 apiId={}, tenantId={}",
|
|
|
+ apiId, binding.getTenantId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void disableInTenantDb(Long tenantId, Long apiId) {
|
|
|
+ if (tenantId == null || apiId == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ runInTenantDb(tenantId, () -> tenantCompanyVoiceApiMapper.updateStatusByApiId(apiId, 0));
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 主库接口已删除时,租户库标记为删除(is_del=1) */
|
|
|
+ private void markDeletedInTenantDb(Long tenantId, Long apiId) {
|
|
|
+ if (tenantId == null || apiId == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ runInTenantDb(tenantId, () -> tenantCompanyVoiceApiMapper.markDeletedByApiId(apiId));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void syncBindingsToTenantDb(List<Long> bindingIds) {
|
|
|
+ if (bindingIds == null || bindingIds.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ensureMasterDataSource();
|
|
|
+ Set<String> synced = new HashSet<>();
|
|
|
+ for (Long bindingId : bindingIds) {
|
|
|
+ if (bindingId == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ CompanyVoiceApiTenant binding = companyVoiceApiTenantMapper.selectCompanyVoiceApiTenantById(bindingId);
|
|
|
+ if (binding == null || binding.getTenantId() == null || binding.getApiId() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String key = binding.getTenantId() + ":" + binding.getApiId();
|
|
|
+ if (!synced.add(key)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ syncBindingToTenantDb(binding.getTenantId(), binding.getApiId());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[TenantVoiceApiSync] 批量同步绑定失败 bindingId={}", bindingId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private TenantCompanyVoiceApi buildTenantRow(CompanyVoiceApi masterApi, CompanyVoiceApiTenant binding) {
|
|
|
+ TenantCompanyVoiceApi row = new TenantCompanyVoiceApi();
|
|
|
+ row.setApiId(masterApi.getApiId());
|
|
|
+ row.setApiName(masterApi.getApiName());
|
|
|
+ row.setApiType(masterApi.getApiType() != null ? String.valueOf(masterApi.getApiType()) : null);
|
|
|
+ row.setApiJson(buildApiJson(masterApi));
|
|
|
+ row.setApiUrl(masterApi.getApiUrl());
|
|
|
+ row.setDialogUrl(masterApi.getDialogUrl());
|
|
|
+ row.setRemark(masterApi.getRemark());
|
|
|
+ row.setIsDel(resolveIsDel(masterApi));
|
|
|
+ row.setStatus(resolveEffectiveStatus(masterApi, binding));
|
|
|
+ if (binding != null) {
|
|
|
+ row.setSalePrice(binding.getSalePrice());
|
|
|
+ row.setPriority(binding.getPriority() != null ? binding.getPriority() : 1);
|
|
|
+ row.setIsPrimary(binding.getIsPrimary() != null ? binding.getIsPrimary() : 0);
|
|
|
+ row.setSelectable(parseSelectable(binding.getSelectable()));
|
|
|
+ } else {
|
|
|
+ row.setPriority(1);
|
|
|
+ row.setIsPrimary(0);
|
|
|
+ row.setSelectable(0);
|
|
|
+ }
|
|
|
+ return row;
|
|
|
+ }
|
|
|
+
|
|
|
+ private int resolveIsDel(CompanyVoiceApi masterApi) {
|
|
|
+ if (masterApi == null || masterApi.getIsDel() == null) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return masterApi.getIsDel() == 1 ? 1 : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private int resolveEffectiveStatus(CompanyVoiceApi masterApi, CompanyVoiceApiTenant binding) {
|
|
|
+ int masterStatus = masterApi.getStatus() != null && masterApi.getStatus() == 1 ? 1 : 0;
|
|
|
+ if (binding == null) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ int bindingStatus = binding.getStatus() != null && binding.getStatus() == 1 ? 1 : 0;
|
|
|
+ return masterStatus == 1 && bindingStatus == 1 ? 1 : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String buildApiJson(CompanyVoiceApi masterApi) {
|
|
|
+ if (StringUtils.isNotEmpty(masterApi.getApiJson())) {
|
|
|
+ return masterApi.getApiJson();
|
|
|
+ }
|
|
|
+ if (StringUtils.isEmpty(masterApi.getAccount())
|
|
|
+ && StringUtils.isEmpty(masterApi.getPassword())
|
|
|
+ && StringUtils.isEmpty(masterApi.getApiUrl())) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ CallApiConfig config = new CallApiConfig();
|
|
|
+ config.setAccount(masterApi.getAccount());
|
|
|
+ config.setPassword(masterApi.getPassword());
|
|
|
+ config.setUrl(masterApi.getApiUrl());
|
|
|
+ return JSONUtil.toJsonStr(config);
|
|
|
+ }
|
|
|
+
|
|
|
+ private int parseSelectable(String selectable) {
|
|
|
+ if (selectable == null) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return "1".equals(selectable) || "true".equalsIgnoreCase(selectable) ? 1 : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void runInTenantDb(Long tenantId, Runnable action) {
|
|
|
+ ensureMasterDataSource();
|
|
|
+ try {
|
|
|
+ tenantDataSourceManager.ensureSwitchByTenantId(tenantId);
|
|
|
+ action.run();
|
|
|
+ } finally {
|
|
|
+ tenantDataSourceManager.clear();
|
|
|
+ ensureMasterDataSource();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ensureMasterDataSource() {
|
|
|
+ DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
|
|
|
+ }
|
|
|
+}
|