Sfoglia il codice sorgente

1、字典切换数据源调整

yys 1 settimana fa
parent
commit
fd360ed065

+ 210 - 34
fs-admin/src/main/java/com/fs/admin/controller/TenantDictController.java

@@ -7,7 +7,10 @@ import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.domain.entity.SysDictType;
 import com.fs.common.core.domain.entity.SysDictType;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.exception.CustomException;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.SecurityUtils;
+import com.fs.framework.datasource.TenantDataSourceContextHelper;
+import com.fs.tenant.dict.constant.TenantDictConstants;
 import com.fs.tenant.dict.domain.TenantDictTemplateData;
 import com.fs.tenant.dict.domain.TenantDictTemplateData;
 import com.fs.tenant.dict.domain.TenantDictTemplateType;
 import com.fs.tenant.dict.domain.TenantDictTemplateType;
 import com.fs.tenant.dict.dto.TenantDictSyncRunReq;
 import com.fs.tenant.dict.dto.TenantDictSyncRunReq;
@@ -15,17 +18,26 @@ import com.fs.tenant.dict.dto.TenantDictTemplateImportReq;
 import com.fs.tenant.dict.service.TenantDictManageService;
 import com.fs.tenant.dict.service.TenantDictManageService;
 import com.fs.tenant.dict.service.TenantDictSyncService;
 import com.fs.tenant.dict.service.TenantDictSyncService;
 import com.fs.tenant.dict.service.TenantDictTemplateService;
 import com.fs.tenant.dict.service.TenantDictTemplateService;
+import com.fs.tenant.dict.vo.TenantDictSyncInitVo;
+import com.fs.tenant.dict.vo.TenantDictSyncResultVo;
 import com.fs.tenant.dict.vo.TenantDictSyncTaskVo;
 import com.fs.tenant.dict.vo.TenantDictSyncTaskVo;
+import com.fs.tenant.dict.vo.TenantDictTemplateImportResultVo;
+import com.fs.tenant.domain.TenantInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.util.CollectionUtils;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 
 /**
 /**
  * 总后台 - 租户字典管理(方案1+2)
  * 总后台 - 租户字典管理(方案1+2)
+ * <p>租户库/主库切换通过 {@link TenantDataSourceContextHelper} 完成,Service 仅处理当前数据源下的业务逻辑。
  */
  */
 @RestController
 @RestController
 @RequestMapping("/tenant/dict")
 @RequestMapping("/tenant/dict")
@@ -40,17 +52,22 @@ public class TenantDictController extends BaseController {
     @Autowired
     @Autowired
     private TenantDictSyncService syncService;
     private TenantDictSyncService syncService;
 
 
+    @Autowired
+    private TenantDataSourceContextHelper tenantContextHelper;
+
+    // ---------- 租户字典(租户库) ----------
+
     @PreAuthorize("@ss.hasPermi('tenant:dict:list')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:list')")
     @GetMapping("/type/list")
     @GetMapping("/type/list")
     public TableDataInfo tenantTypeList(@RequestParam Long tenantId, SysDictType query) {
     public TableDataInfo tenantTypeList(@RequestParam Long tenantId, SysDictType query) {
-        List<SysDictType> list = tenantDictManageService.selectDictTypeList(tenantId, query);
+        List<SysDictType> list = tenantContextHelper.executeInTenant(tenantId, () -> tenantDictManageService.selectDictTypeList(query));
         return getDataTable(list);
         return getDataTable(list);
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:query')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:query')")
     @GetMapping("/type/{dictId}")
     @GetMapping("/type/{dictId}")
     public AjaxResult tenantTypeInfo(@PathVariable Long dictId, @RequestParam Long tenantId) {
     public AjaxResult tenantTypeInfo(@PathVariable Long dictId, @RequestParam Long tenantId) {
-        return AjaxResult.success(tenantDictManageService.selectDictTypeById(tenantId, dictId));
+        return AjaxResult.success(tenantContextHelper.executeInTenant(tenantId, () -> tenantDictManageService.selectDictTypeById(dictId)));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:add')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:add')")
@@ -58,7 +75,7 @@ public class TenantDictController extends BaseController {
     @PostMapping("/type")
     @PostMapping("/type")
     public AjaxResult tenantTypeAdd(@RequestParam Long tenantId, @Validated @RequestBody SysDictType dict) {
     public AjaxResult tenantTypeAdd(@RequestParam Long tenantId, @Validated @RequestBody SysDictType dict) {
         dict.setCreateBy(SecurityUtils.getUsername());
         dict.setCreateBy(SecurityUtils.getUsername());
-        return toAjax(tenantDictManageService.insertDictType(tenantId, dict));
+        return toAjax(tenantContextHelper.executeInTenant(tenantId, () -> tenantDictManageService.insertDictType(dict)));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:edit')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:edit')")
@@ -66,28 +83,31 @@ public class TenantDictController extends BaseController {
     @PutMapping("/type")
     @PutMapping("/type")
     public AjaxResult tenantTypeEdit(@RequestParam Long tenantId, @Validated @RequestBody SysDictType dict) {
     public AjaxResult tenantTypeEdit(@RequestParam Long tenantId, @Validated @RequestBody SysDictType dict) {
         dict.setUpdateBy(SecurityUtils.getUsername());
         dict.setUpdateBy(SecurityUtils.getUsername());
-        return toAjax(tenantDictManageService.updateDictType(tenantId, dict));
+        return toAjax(tenantContextHelper.executeInTenant(tenantId, () -> tenantDictManageService.updateDictType(dict)));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:remove')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:remove')")
     @Log(title = "租户字典类型", businessType = BusinessType.DELETE)
     @Log(title = "租户字典类型", businessType = BusinessType.DELETE)
     @DeleteMapping("/type/{dictIds}")
     @DeleteMapping("/type/{dictIds}")
     public AjaxResult tenantTypeRemove(@PathVariable Long[] dictIds, @RequestParam Long tenantId) {
     public AjaxResult tenantTypeRemove(@PathVariable Long[] dictIds, @RequestParam Long tenantId) {
-        tenantDictManageService.deleteDictTypeByIds(tenantId, dictIds);
+        tenantContextHelper.executeInTenant(tenantId, () -> {
+            tenantDictManageService.deleteDictTypeByIds(dictIds);
+            return null;
+        });
         return AjaxResult.success();
         return AjaxResult.success();
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:list')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:list')")
     @GetMapping("/data/list")
     @GetMapping("/data/list")
     public TableDataInfo tenantDataList(@RequestParam Long tenantId, SysDictData query) {
     public TableDataInfo tenantDataList(@RequestParam Long tenantId, SysDictData query) {
-        List<SysDictData> list = tenantDictManageService.selectDictDataList(tenantId, query);
+        List<SysDictData> list = tenantContextHelper.executeInTenant(tenantId, () -> tenantDictManageService.selectDictDataList(query));
         return getDataTable(list);
         return getDataTable(list);
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:query')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:query')")
     @GetMapping("/data/{dictCode}")
     @GetMapping("/data/{dictCode}")
     public AjaxResult tenantDataInfo(@PathVariable Long dictCode, @RequestParam Long tenantId) {
     public AjaxResult tenantDataInfo(@PathVariable Long dictCode, @RequestParam Long tenantId) {
-        return AjaxResult.success(tenantDictManageService.selectDictDataById(tenantId, dictCode));
+        return AjaxResult.success(tenantContextHelper.executeInTenant(tenantId, () -> tenantDictManageService.selectDictDataById(dictCode)));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:add')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:add')")
@@ -95,7 +115,7 @@ public class TenantDictController extends BaseController {
     @PostMapping("/data")
     @PostMapping("/data")
     public AjaxResult tenantDataAdd(@RequestParam Long tenantId, @Validated @RequestBody SysDictData dict) {
     public AjaxResult tenantDataAdd(@RequestParam Long tenantId, @Validated @RequestBody SysDictData dict) {
         dict.setCreateBy(SecurityUtils.getUsername());
         dict.setCreateBy(SecurityUtils.getUsername());
-        return toAjax(tenantDictManageService.insertDictData(tenantId, dict));
+        return toAjax(tenantContextHelper.executeInTenant(tenantId, () -> tenantDictManageService.insertDictData(dict)));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:edit')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:edit')")
@@ -103,36 +123,46 @@ public class TenantDictController extends BaseController {
     @PutMapping("/data")
     @PutMapping("/data")
     public AjaxResult tenantDataEdit(@RequestParam Long tenantId, @Validated @RequestBody SysDictData dict) {
     public AjaxResult tenantDataEdit(@RequestParam Long tenantId, @Validated @RequestBody SysDictData dict) {
         dict.setUpdateBy(SecurityUtils.getUsername());
         dict.setUpdateBy(SecurityUtils.getUsername());
-        return toAjax(tenantDictManageService.updateDictData(tenantId, dict));
+        return toAjax(tenantContextHelper.executeInTenant(tenantId, () -> tenantDictManageService.updateDictData(dict)));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:remove')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:remove')")
     @Log(title = "租户字典数据", businessType = BusinessType.DELETE)
     @Log(title = "租户字典数据", businessType = BusinessType.DELETE)
     @DeleteMapping("/data/{dictCodes}")
     @DeleteMapping("/data/{dictCodes}")
     public AjaxResult tenantDataRemove(@PathVariable Long[] dictCodes, @RequestParam Long tenantId) {
     public AjaxResult tenantDataRemove(@PathVariable Long[] dictCodes, @RequestParam Long tenantId) {
-        tenantDictManageService.deleteDictDataByIds(tenantId, dictCodes);
+        tenantContextHelper.executeInTenant(tenantId, () -> {
+            tenantDictManageService.deleteDictDataByIds(dictCodes);
+            return null;
+        });
         return AjaxResult.success();
         return AjaxResult.success();
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:edit')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:edit')")
     @DeleteMapping("/cache/refresh")
     @DeleteMapping("/cache/refresh")
     public AjaxResult refreshTenantCache(@RequestParam Long tenantId) {
     public AjaxResult refreshTenantCache(@RequestParam Long tenantId) {
-        tenantDictManageService.refreshDictCache(tenantId);
+        tenantContextHelper.executeInTenant(tenantId, () -> {
+            tenantDictManageService.refreshDictCache();
+            return null;
+        });
         return AjaxResult.success("租户字典缓存已刷新");
         return AjaxResult.success("租户字典缓存已刷新");
     }
     }
 
 
+    // ---------- 平台模板(主库) ----------
+
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:list')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:list')")
     @GetMapping("/template/type/list")
     @GetMapping("/template/type/list")
     public TableDataInfo templateTypeList(TenantDictTemplateType query) {
     public TableDataInfo templateTypeList(TenantDictTemplateType query) {
-        startPage();
-        List<TenantDictTemplateType> list = templateService.selectTemplateTypeList(query);
-        return getDataTable(list);
+        return tenantContextHelper.executeInMaster(() -> {
+            startPage();
+            List<TenantDictTemplateType> list = templateService.selectTemplateTypeList(query);
+            return getDataTable(list);
+        });
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:query')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:query')")
     @GetMapping("/template/type/{dictId}")
     @GetMapping("/template/type/{dictId}")
     public AjaxResult templateTypeInfo(@PathVariable Long dictId) {
     public AjaxResult templateTypeInfo(@PathVariable Long dictId) {
-        return AjaxResult.success(templateService.selectTemplateTypeById(dictId));
+        return AjaxResult.success(tenantContextHelper.executeInMaster(() -> templateService.selectTemplateTypeById(dictId)));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:add')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:add')")
@@ -140,7 +170,7 @@ public class TenantDictController extends BaseController {
     @PostMapping("/template/type")
     @PostMapping("/template/type")
     public AjaxResult templateTypeAdd(@RequestBody TenantDictTemplateType row) {
     public AjaxResult templateTypeAdd(@RequestBody TenantDictTemplateType row) {
         row.setCreateBy(SecurityUtils.getUsername());
         row.setCreateBy(SecurityUtils.getUsername());
-        return toAjax(templateService.insertTemplateType(row));
+        return toAjax(tenantContextHelper.executeInMaster(() -> templateService.insertTemplateType(row)));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:edit')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:edit')")
@@ -148,29 +178,34 @@ public class TenantDictController extends BaseController {
     @PutMapping("/template/type")
     @PutMapping("/template/type")
     public AjaxResult templateTypeEdit(@RequestBody TenantDictTemplateType row) {
     public AjaxResult templateTypeEdit(@RequestBody TenantDictTemplateType row) {
         row.setUpdateBy(SecurityUtils.getUsername());
         row.setUpdateBy(SecurityUtils.getUsername());
-        return toAjax(templateService.updateTemplateType(row));
+        return toAjax(tenantContextHelper.executeInMaster(() -> templateService.updateTemplateType(row)));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:remove')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:remove')")
     @Log(title = "平台字典模板类型", businessType = BusinessType.DELETE)
     @Log(title = "平台字典模板类型", businessType = BusinessType.DELETE)
     @DeleteMapping("/template/type/{dictIds}")
     @DeleteMapping("/template/type/{dictIds}")
     public AjaxResult templateTypeRemove(@PathVariable Long[] dictIds) {
     public AjaxResult templateTypeRemove(@PathVariable Long[] dictIds) {
-        templateService.deleteTemplateTypeByIds(dictIds);
+        tenantContextHelper.executeInMaster(() -> {
+            templateService.deleteTemplateTypeByIds(dictIds);
+            return null;
+        });
         return AjaxResult.success();
         return AjaxResult.success();
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:list')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:list')")
     @GetMapping("/template/data/list")
     @GetMapping("/template/data/list")
     public TableDataInfo templateDataList(TenantDictTemplateData query) {
     public TableDataInfo templateDataList(TenantDictTemplateData query) {
-        startPage();
-        List<TenantDictTemplateData> list = templateService.selectTemplateDataList(query);
-        return getDataTable(list);
+        return tenantContextHelper.executeInMaster(() -> {
+            startPage();
+            List<TenantDictTemplateData> list = templateService.selectTemplateDataList(query);
+            return getDataTable(list);
+        });
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:query')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:query')")
     @GetMapping("/template/data/{dictCode}")
     @GetMapping("/template/data/{dictCode}")
     public AjaxResult templateDataInfo(@PathVariable Long dictCode) {
     public AjaxResult templateDataInfo(@PathVariable Long dictCode) {
-        return AjaxResult.success(templateService.selectTemplateDataById(dictCode));
+        return AjaxResult.success(tenantContextHelper.executeInMaster(() -> templateService.selectTemplateDataById(dictCode)));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:add')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:add')")
@@ -178,7 +213,7 @@ public class TenantDictController extends BaseController {
     @PostMapping("/template/data")
     @PostMapping("/template/data")
     public AjaxResult templateDataAdd(@RequestBody TenantDictTemplateData row) {
     public AjaxResult templateDataAdd(@RequestBody TenantDictTemplateData row) {
         row.setCreateBy(SecurityUtils.getUsername());
         row.setCreateBy(SecurityUtils.getUsername());
-        return toAjax(templateService.insertTemplateData(row));
+        return toAjax(tenantContextHelper.executeInMaster(() -> templateService.insertTemplateData(row)));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:edit')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:edit')")
@@ -186,49 +221,103 @@ public class TenantDictController extends BaseController {
     @PutMapping("/template/data")
     @PutMapping("/template/data")
     public AjaxResult templateDataEdit(@RequestBody TenantDictTemplateData row) {
     public AjaxResult templateDataEdit(@RequestBody TenantDictTemplateData row) {
         row.setUpdateBy(SecurityUtils.getUsername());
         row.setUpdateBy(SecurityUtils.getUsername());
-        return toAjax(templateService.updateTemplateData(row));
+        return toAjax(tenantContextHelper.executeInMaster(() -> templateService.updateTemplateData(row)));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:remove')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:remove')")
     @Log(title = "平台字典模板数据", businessType = BusinessType.DELETE)
     @Log(title = "平台字典模板数据", businessType = BusinessType.DELETE)
     @DeleteMapping("/template/data/{dictCodes}")
     @DeleteMapping("/template/data/{dictCodes}")
     public AjaxResult templateDataRemove(@PathVariable Long[] dictCodes) {
     public AjaxResult templateDataRemove(@PathVariable Long[] dictCodes) {
-        templateService.deleteTemplateDataByIds(dictCodes);
+        tenantContextHelper.executeInMaster(() -> {
+            templateService.deleteTemplateDataByIds(dictCodes);
+            return null;
+        });
         return AjaxResult.success();
         return AjaxResult.success();
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:list')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:list')")
     @GetMapping("/template/type/optionselect")
     @GetMapping("/template/type/optionselect")
     public AjaxResult templateTypeOptions() {
     public AjaxResult templateTypeOptions() {
-        return AjaxResult.success(templateService.selectAllTemplateTypes());
+        return AjaxResult.success(tenantContextHelper.executeInMaster(() -> templateService.selectAllTemplateTypes()));
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:add')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:add')")
     @Log(title = "平台字典模板导入", businessType = BusinessType.INSERT)
     @Log(title = "平台字典模板导入", businessType = BusinessType.INSERT)
     @PostMapping("/template/import/platform")
     @PostMapping("/template/import/platform")
     public AjaxResult importFromPlatform(@RequestBody TenantDictTemplateImportReq req) {
     public AjaxResult importFromPlatform(@RequestBody TenantDictTemplateImportReq req) {
-        return AjaxResult.success(templateService.importFromPlatform(req, SecurityUtils.getUsername()));
+        TenantDictTemplateImportResultVo result = tenantContextHelper.executeInMaster(() ->
+                templateService.importFromPlatform(req, SecurityUtils.getUsername()));
+        return AjaxResult.success(result);
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:add')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:template:add')")
     @Log(title = "平台字典模板导入", businessType = BusinessType.INSERT)
     @Log(title = "平台字典模板导入", businessType = BusinessType.INSERT)
     @PostMapping("/template/import/tenant")
     @PostMapping("/template/import/tenant")
     public AjaxResult importFromTenant(@RequestBody TenantDictTemplateImportReq req) {
     public AjaxResult importFromTenant(@RequestBody TenantDictTemplateImportReq req) {
-        return AjaxResult.success(templateService.importFromTenant(req, SecurityUtils.getUsername()));
+        if (req.getTenantId() == null) {
+            throw new CustomException("请选择租户");
+        }
+        Map<String, SysDictType> typesByDictType = new LinkedHashMap<>();
+        Map<String, List<SysDictData>> dataByDictType = new LinkedHashMap<>();
+        tenantContextHelper.executeInTenant(req.getTenantId(), () -> {
+            if (CollectionUtils.isEmpty(req.getDictTypes())) {
+                throw new CustomException("请选择要导入的字典类型");
+            }
+            for (String dictType : req.getDictTypes()) {
+                SysDictType query = new SysDictType();
+                query.setDictType(dictType);
+                List<SysDictType> types = tenantDictManageService.selectDictTypeList(query);
+                if (!types.isEmpty()) {
+                    typesByDictType.put(dictType, types.get(0));
+                }
+                SysDictData dataQuery = new SysDictData();
+                dataQuery.setDictType(dictType);
+                dataByDictType.put(dictType, tenantDictManageService.selectDictDataList(dataQuery));
+            }
+            return null;
+        });
+        TenantDictTemplateImportResultVo result = tenantContextHelper.executeInMaster(() ->
+                templateService.importFromTenantSources(req, SecurityUtils.getUsername(), typesByDictType, dataByDictType));
+        return AjaxResult.success(result);
     }
     }
 
 
+    // ---------- 同步(主库任务 + 租户库写入) ----------
+
     @PreAuthorize("@ss.hasPermi('tenant:dict:sync')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:sync')")
     @Log(title = "租户字典同步", businessType = BusinessType.OTHER)
     @Log(title = "租户字典同步", businessType = BusinessType.OTHER)
     @PostMapping("/sync/run")
     @PostMapping("/sync/run")
     public AjaxResult syncRun(@RequestBody TenantDictSyncRunReq req) {
     public AjaxResult syncRun(@RequestBody TenantDictSyncRunReq req) {
-        Map<String, Object> data = syncService.runSync(req, SecurityUtils.getUsername());
-        return AjaxResult.success(data);
+        String operator = SecurityUtils.getUsername();
+        TenantDictSyncInitVo init = tenantContextHelper.executeInMaster(() -> syncService.initSync(req, operator));
+
+        if (init.isDryRun()) {
+            List<TenantDictSyncResultVo> preview = new ArrayList<>();
+            for (TenantInfo tenant : init.getTenants()) {
+                preview.add(tenantContextHelper.executeInTenant(tenant.getId(), () -> syncService.previewOneTenant(
+                        tenant, init.getTemplateTypes(), init.getDataCache(), init.getSyncMode())));
+            }
+            Map<String, Object> resp = new HashMap<>();
+            resp.put("dryRun", true);
+            resp.put("preview", preview);
+            return AjaxResult.success(resp);
+        }
+
+        final String taskNo = init.getTaskNo();
+        final List<TenantInfo> tenants = init.getTenants();
+        CompletableFuture.runAsync(() -> executeSyncTask(taskNo, req, tenants, init));
+
+        Map<String, Object> resp = new HashMap<>();
+        resp.put("taskNo", taskNo);
+        resp.put("status", TenantDictConstants.TASK_RUNNING);
+        resp.put("totalTenants", tenants.size());
+        resp.put("syncMode", init.getSyncMode());
+        return AjaxResult.success(resp);
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('tenant:dict:sync')")
     @PreAuthorize("@ss.hasPermi('tenant:dict:sync')")
     @GetMapping("/sync/task/{taskNo}")
     @GetMapping("/sync/task/{taskNo}")
     public AjaxResult syncTask(@PathVariable String taskNo) {
     public AjaxResult syncTask(@PathVariable String taskNo) {
-        TenantDictSyncTaskVo vo = syncService.getTask(taskNo);
+        TenantDictSyncTaskVo vo = tenantContextHelper.executeInMaster(() -> syncService.getTask(taskNo));
         return AjaxResult.success(vo);
         return AjaxResult.success(vo);
     }
     }
 
 
@@ -236,6 +325,93 @@ public class TenantDictController extends BaseController {
     @GetMapping("/sync/preview")
     @GetMapping("/sync/preview")
     public AjaxResult syncPreview(@RequestParam Long tenantId,
     public AjaxResult syncPreview(@RequestParam Long tenantId,
                                   @RequestParam(required = false) List<String> dictTypes) {
                                   @RequestParam(required = false) List<String> dictTypes) {
-        return AjaxResult.success(syncService.previewDiff(tenantId, dictTypes));
+        TenantDictSyncInitVo init = tenantContextHelper.executeInMaster(() -> {
+            TenantDictSyncRunReq req = new TenantDictSyncRunReq();
+            req.setDictTypes(dictTypes);
+            req.setDryRun(true);
+            req.setSyncMode(TenantDictConstants.SYNC_MERGE);
+            req.setScopeType(TenantDictConstants.SCOPE_INCLUDE);
+            req.setTenantIds(Collections.singletonList(tenantId));
+            return syncService.initSync(req, SecurityUtils.getUsername());
+        });
+        if (init.getTenants().isEmpty()) {
+            throw new CustomException("租户不存在或未启用");
+        }
+        TenantInfo tenant = init.getTenants().get(0);
+        TenantDictSyncResultVo result = tenantContextHelper.executeInTenant(tenantId, () -> syncService.previewOneTenant(
+                tenant, init.getTemplateTypes(), init.getDataCache(), init.getSyncMode()));
+        return AjaxResult.success(Collections.singletonList(result));
+    }
+
+    private void executeSyncTask(String taskNo, TenantDictSyncRunReq req,
+                                 List<TenantInfo> tenants, TenantDictSyncInitVo init) {
+        int success = 0;
+        int failed = 0;
+        if (Boolean.TRUE.equals(req.getParallel())) {
+            int threads = req.getThreads() == null || req.getThreads() <= 0 ? 4 : req.getThreads();
+            ExecutorService pool = Executors.newFixedThreadPool(threads);
+            try {
+                List<Future<Boolean>> futures = new ArrayList<>();
+                for (TenantInfo tenant : tenants) {
+                    futures.add(pool.submit(() -> syncOneTenantWithRecord(taskNo, tenant, init)));
+                }
+                for (Future<Boolean> f : futures) {
+                    try {
+                        if (Boolean.TRUE.equals(f.get())) {
+                            success++;
+                        } else {
+                            failed++;
+                        }
+                    } catch (Exception e) {
+                        failed++;
+                        logger.error("[TenantDictSync] 并行任务异常", e);
+                    }
+                }
+            } finally {
+                pool.shutdown();
+            }
+        } else {
+            for (TenantInfo tenant : tenants) {
+                if (syncOneTenantWithRecord(taskNo, tenant, init)) {
+                    success++;
+                } else {
+                    failed++;
+                }
+            }
+        }
+        final int successCount = success;
+        final int failedCount = failed;
+        tenantContextHelper.executeInMaster(() -> {
+            syncService.finalizeTask(taskNo, successCount, failedCount);
+            return null;
+        });
+    }
+
+    private boolean syncOneTenantWithRecord(String taskNo, TenantInfo tenant, TenantDictSyncInitVo init) {
+        Date start = new Date();
+        String status = TenantDictConstants.DETAIL_SUCCESS;
+        String errorMsg = null;
+        TenantDictSyncResultVo stats;
+        try {
+            stats = tenantContextHelper.executeInTenant(tenant.getId(), () -> syncService.syncOneTenant(
+                    tenant, init.getTemplateTypes(), init.getDataCache(), init.getSyncMode()));
+        } catch (Exception e) {
+            status = TenantDictConstants.DETAIL_FAILED;
+            errorMsg = e.getMessage();
+            stats = new TenantDictSyncResultVo();
+            stats.setTenantId(tenant.getId());
+            stats.setTenantCode(tenant.getTenantCode());
+            stats.setTenantName(tenant.getTenantName());
+            logger.error("[TenantDictSync] tenantId={} 同步失败", tenant.getId(), e);
+        }
+        final TenantDictSyncResultVo finalStats = stats;
+        final String finalStatus = status;
+        final String finalErrorMsg = errorMsg;
+        final Date end = new Date();
+        tenantContextHelper.executeInMaster(() -> {
+            syncService.recordTaskDetail(taskNo, tenant, finalStats, finalStatus, finalErrorMsg, start, end);
+            return null;
+        });
+        return TenantDictConstants.DETAIL_SUCCESS.equals(status);
     }
     }
 }
 }

+ 102 - 0
fs-framework/src/main/java/com/fs/framework/datasource/TenantDataSourceContextHelper.java

@@ -0,0 +1,102 @@
+package com.fs.framework.datasource;
+
+import com.fs.common.config.RedisTenantContext;
+import com.fs.common.enums.DataSourceType;
+import com.fs.common.exception.CustomException;
+import com.fs.tenant.domain.TenantInfo;
+import com.fs.tenant.service.TenantInfoService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.function.Supplier;
+
+/**
+ * 多租户数据源上下文工具:统一切库、Redis 租户前缀、资源释放。
+ * <p>
+ * 总后台代管租户数据时,在 Controller / 任务编排层调用本类,Service 仅处理当前数据源下的业务。
+ */
+@Component
+public class TenantDataSourceContextHelper {
+
+    @Autowired
+    private TenantDataSourceManager tenantDataSourceManager;
+
+    @Autowired
+    private TenantInfoService tenantInfoService;
+
+    /**
+     * 在租户库上下文中执行(无返回值)
+     */
+    public void runInTenant(Long tenantId, Runnable action) {
+        executeInTenant(tenantId, () -> {
+            action.run();
+            return null;
+        });
+    }
+
+    /**
+     * 在租户库上下文中执行(有返回值)
+     */
+    public <T> T executeInTenant(Long tenantId, Supplier<T> action) {
+        return executeInTenant(loadActiveTenant(tenantId), action);
+    }
+
+    /**
+     * 在租户库上下文中执行(已加载租户信息)
+     */
+    public <T> T executeInTenant(TenantInfo tenant, Supplier<T> action) {
+        DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+        try {
+            tenantDataSourceManager.switchTenant(tenant);
+            RedisTenantContext.setTenantId(tenant.getId());
+            return action.get();
+        } finally {
+            RedisTenantContext.clear();
+            tenantDataSourceManager.clear();
+            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+        }
+    }
+
+    /**
+     * 在主库上下文中执行(无返回值)
+     */
+    public void runInMaster(Runnable action) {
+        executeInMaster(() -> {
+            action.run();
+            return null;
+        });
+    }
+
+    /**
+     * 在主库上下文中执行(有返回值)
+     */
+    public <T> T executeInMaster(Supplier<T> action) {
+        DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+        try {
+            return action.get();
+        } finally {
+            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+        }
+    }
+
+    /**
+     * 校验并加载启用中的租户(查询走主库)
+     */
+    public TenantInfo loadActiveTenant(Long tenantId) {
+        if (tenantId == null) {
+            throw new CustomException("租户ID不能为空");
+        }
+        DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
+        TenantInfo tenant = tenantInfoService.getById(tenantId);
+        if (tenant == null) {
+            throw new CustomException("租户不存在: " + tenantId);
+        }
+        if (tenant.getStatus() != null && tenant.getStatus() == 2) {
+            throw new CustomException("租户初始化中,暂不可操作");
+        }
+        if (tenant.getStatus() == null || tenant.getStatus() != 1) {
+            throw new CustomException("租户未启用,暂不可操作");
+        }
+        return tenant;
+    }
+}

+ 0 - 93
fs-service/src/main/java/com/fs/tenant/dict/helper/TenantDictContextHelper.java

@@ -1,93 +0,0 @@
-//package com.fs.tenant.dict.helper;
-//
-//import com.fs.common.config.RedisTenantContext;
-//import com.fs.common.enums.DataSourceType;
-//import com.fs.common.exception.CustomException;
-//import com.fs.tenant.domain.TenantInfo;
-//import com.fs.tenant.mapper.TenantInfoMapper;
-//import org.springframework.beans.factory.annotation.Autowired;
-//import org.springframework.stereotype.Component;
-//
-//import java.util.function.Supplier;
-//
-///**
-// * 租户字典上下文工具:统一切库、Redis 租户前缀、资源释放。
-// * <p>
-// * 所有租户字典读写必须通过本类,避免数据源泄漏和 Redis 缓存串租户。
-// */
-//@Component
-//public class TenantDictContextHelper {
-//
-//    @Autowired
-//    private TenantDataSourceManager tenantDataSourceManager;
-//
-//    @Autowired
-//    private TenantInfoMapper tenantInfoMapper;
-//
-//    /**
-//     * 在租户库上下文中执行(无返回值)
-//     */
-//    public void runInTenant(Long tenantId, Runnable action) {
-//        executeInTenant(tenantId, () -> {
-//            action.run();
-//            return null;
-//        });
-//    }
-//
-//    /**
-//     * 在租户库上下文中执行(有返回值)
-//     */
-//    public <T> T executeInTenant(Long tenantId, Supplier<T> action) {
-//        TenantInfo tenant = loadActiveTenant(tenantId);
-//        DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
-//        try {
-//            tenantDataSourceManager.switchTenant(tenant);
-//            RedisTenantContext.setTenantId(tenantId);
-//            return action.get();
-//        } finally {
-//            RedisTenantContext.clear();
-//            tenantDataSourceManager.clear();
-//            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
-//        }
-//    }
-//
-//    /**
-//     * 在主库上下文中执行
-//     */
-//    public <T> T executeInMaster(Supplier<T> action) {
-//        DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
-//        try {
-//            return action.get();
-//        } finally {
-//            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
-//        }
-//    }
-//
-//    public void runInMaster(Runnable action) {
-//        executeInMaster(() -> {
-//            action.run();
-//            return null;
-//        });
-//    }
-//
-//    /**
-//     * 校验并加载启用中的租户
-//     */
-//    public TenantInfo loadActiveTenant(Long tenantId) {
-//        if (tenantId == null) {
-//            throw new CustomException("租户ID不能为空");
-//        }
-//        DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
-//        TenantInfo tenant = tenantInfoMapper.selectTenantInfoById(String.valueOf(tenantId));
-//        if (tenant == null) {
-//            throw new CustomException("租户不存在: " + tenantId);
-//        }
-//        if (tenant.getStatus() != null && tenant.getStatus() == 2) {
-//            throw new CustomException("租户初始化中,暂不可操作字典");
-//        }
-//        if (tenant.getStatus() == null || tenant.getStatus() != 1) {
-//            throw new CustomException("租户未启用,暂不可操作字典");
-//        }
-//        return tenant;
-//    }
-//}

+ 13 - 12
fs-service/src/main/java/com/fs/tenant/dict/service/TenantDictManageService.java

@@ -7,30 +7,31 @@ import java.util.List;
 
 
 /**
 /**
  * 租户字典管理服务(方案1:总后台代管单个租户字典)
  * 租户字典管理服务(方案1:总后台代管单个租户字典)
+ * <p>调用方需在接口层完成租户数据源切换后再调用本服务。
  */
  */
 public interface TenantDictManageService {
 public interface TenantDictManageService {
 
 
-    List<SysDictType> selectDictTypeList(Long tenantId, SysDictType query);
+    List<SysDictType> selectDictTypeList(SysDictType query);
 
 
-    SysDictType selectDictTypeById(Long tenantId, Long dictId);
+    SysDictType selectDictTypeById(Long dictId);
 
 
-    int insertDictType(Long tenantId, SysDictType dict);
+    int insertDictType(SysDictType dict);
 
 
-    int updateDictType(Long tenantId, SysDictType dict);
+    int updateDictType(SysDictType dict);
 
 
-    void deleteDictTypeByIds(Long tenantId, Long[] dictIds);
+    void deleteDictTypeByIds(Long[] dictIds);
 
 
-    List<SysDictData> selectDictDataList(Long tenantId, SysDictData query);
+    List<SysDictData> selectDictDataList(SysDictData query);
 
 
-    SysDictData selectDictDataById(Long tenantId, Long dictCode);
+    SysDictData selectDictDataById(Long dictCode);
 
 
-    int insertDictData(Long tenantId, SysDictData dict);
+    int insertDictData(SysDictData dict);
 
 
-    int updateDictData(Long tenantId, SysDictData dict);
+    int updateDictData(SysDictData dict);
 
 
-    void deleteDictDataByIds(Long tenantId, Long[] dictCodes);
+    void deleteDictDataByIds(Long[] dictCodes);
 
 
-    void refreshDictCache(Long tenantId);
+    void refreshDictCache();
 
 
-    String checkDictTypeUnique(Long tenantId, SysDictType dict);
+    String checkDictTypeUnique(SysDictType dict);
 }
 }

+ 36 - 3
fs-service/src/main/java/com/fs/tenant/dict/service/TenantDictSyncService.java

@@ -1,20 +1,53 @@
 package com.fs.tenant.dict.service;
 package com.fs.tenant.dict.service;
 
 
+import com.fs.tenant.dict.domain.TenantDictTemplateData;
+import com.fs.tenant.dict.domain.TenantDictTemplateType;
 import com.fs.tenant.dict.dto.TenantDictSyncRunReq;
 import com.fs.tenant.dict.dto.TenantDictSyncRunReq;
+import com.fs.tenant.dict.vo.TenantDictSyncInitVo;
 import com.fs.tenant.dict.vo.TenantDictSyncResultVo;
 import com.fs.tenant.dict.vo.TenantDictSyncResultVo;
 import com.fs.tenant.dict.vo.TenantDictSyncTaskVo;
 import com.fs.tenant.dict.vo.TenantDictSyncTaskVo;
+import com.fs.tenant.domain.TenantInfo;
 
 
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
 /**
 /**
  * 租户字典同步服务(方案2:模板下发到租户库)
  * 租户字典同步服务(方案2:模板下发到租户库)
+ * <p>租户库读写由接口层切库;主库任务表读写由接口层切主库后调用。
  */
  */
 public interface TenantDictSyncService {
 public interface TenantDictSyncService {
 
 
-    Map<String, Object> runSync(TenantDictSyncRunReq req, String triggerBy);
+    /**
+     * 在主库上下文校验参数、解析租户与模板,必要时创建同步任务。
+     */
+    TenantDictSyncInitVo initSync(TenantDictSyncRunReq req, String triggerBy);
 
 
-    TenantDictSyncTaskVo getTask(String taskNo);
+    /**
+     * 在租户库上下文预览单租户差异。
+     */
+    TenantDictSyncResultVo previewOneTenant(TenantInfo tenant,
+                                            List<TenantDictTemplateType> templateTypes,
+                                            Map<String, List<TenantDictTemplateData>> dataCache,
+                                            String syncMode);
+
+    /**
+     * 在租户库上下文同步单租户(不写主库任务明细)。
+     */
+    TenantDictSyncResultVo syncOneTenant(TenantInfo tenant,
+                                         List<TenantDictTemplateType> templateTypes,
+                                         Map<String, List<TenantDictTemplateData>> dataCache,
+                                         String syncMode);
+
+    /**
+     * 在主库上下文写入单租户同步明细。
+     */
+    void recordTaskDetail(String taskNo, TenantInfo tenant, TenantDictSyncResultVo stats,
+                          String status, String errorMsg, java.util.Date start, java.util.Date end);
 
 
-    List<TenantDictSyncResultVo> previewDiff(Long tenantId, List<String> dictTypes);
+    /**
+     * 在主库上下文汇总任务结果。
+     */
+    void finalizeTask(String taskNo, int success, int failed);
+
+    TenantDictSyncTaskVo getTask(String taskNo);
 }
 }

+ 11 - 2
fs-service/src/main/java/com/fs/tenant/dict/service/TenantDictTemplateService.java

@@ -1,15 +1,18 @@
 package com.fs.tenant.dict.service;
 package com.fs.tenant.dict.service;
 
 
+import com.fs.common.core.domain.entity.SysDictData;
+import com.fs.common.core.domain.entity.SysDictType;
 import com.fs.tenant.dict.domain.TenantDictTemplateData;
 import com.fs.tenant.dict.domain.TenantDictTemplateData;
 import com.fs.tenant.dict.domain.TenantDictTemplateType;
 import com.fs.tenant.dict.domain.TenantDictTemplateType;
-
 import com.fs.tenant.dict.dto.TenantDictTemplateImportReq;
 import com.fs.tenant.dict.dto.TenantDictTemplateImportReq;
 import com.fs.tenant.dict.vo.TenantDictTemplateImportResultVo;
 import com.fs.tenant.dict.vo.TenantDictTemplateImportResultVo;
 
 
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 
 
 /**
 /**
  * 平台字典模板服务(主库)
  * 平台字典模板服务(主库)
+ * <p>调用方需在接口层切主库后调用;从租户库导入时由接口层先切租户库读取数据再切主库写入。
  */
  */
 public interface TenantDictTemplateService {
 public interface TenantDictTemplateService {
 
 
@@ -39,5 +42,11 @@ public interface TenantDictTemplateService {
 
 
     TenantDictTemplateImportResultVo importFromPlatform(TenantDictTemplateImportReq req, String operator);
     TenantDictTemplateImportResultVo importFromPlatform(TenantDictTemplateImportReq req, String operator);
 
 
-    TenantDictTemplateImportResultVo importFromTenant(TenantDictTemplateImportReq req, String operator);
+    /**
+     * 将已从租户库读取的字典写入主库模板表。
+     */
+    TenantDictTemplateImportResultVo importFromTenantSources(TenantDictTemplateImportReq req,
+                                                             String operator,
+                                                             Map<String, SysDictType> typesByDictType,
+                                                             Map<String, List<SysDictData>> dataByDictType);
 }
 }

+ 79 - 106
fs-service/src/main/java/com/fs/tenant/dict/service/impl/TenantDictManageServiceImpl.java

@@ -1,30 +1,29 @@
 package com.fs.tenant.dict.service.impl;
 package com.fs.tenant.dict.service.impl;
 
 
-import com.fs.common.core.page.PageDomain;
-import com.fs.common.core.page.TableSupport;
-import com.fs.common.utils.StringUtils;
-import com.fs.common.utils.sql.SqlUtil;
-import com.github.pagehelper.PageHelper;
 import com.fs.common.constant.UserConstants;
 import com.fs.common.constant.UserConstants;
 import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.domain.entity.SysDictType;
 import com.fs.common.core.domain.entity.SysDictType;
+import com.fs.common.core.page.PageDomain;
+import com.fs.common.core.page.TableSupport;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.CustomException;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.sql.SqlUtil;
 import com.fs.system.service.ISysDictDataService;
 import com.fs.system.service.ISysDictDataService;
 import com.fs.system.service.ISysDictTypeService;
 import com.fs.system.service.ISysDictTypeService;
 import com.fs.tenant.dict.constant.TenantDictConstants;
 import com.fs.tenant.dict.constant.TenantDictConstants;
 import com.fs.tenant.dict.service.TenantDictManageService;
 import com.fs.tenant.dict.service.TenantDictManageService;
+import com.github.pagehelper.PageHelper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import java.util.List;
 import java.util.List;
 
 
 /**
 /**
- * 租户字典代管:在指定租户库内复用标准字典 Service。
+ * 租户字典代管:在已切换的租户库内复用标准字典 Service。
  */
  */
 @Service
 @Service
 public class TenantDictManageServiceImpl implements TenantDictManageService {
 public class TenantDictManageServiceImpl implements TenantDictManageService {
 
 
-
     @Autowired
     @Autowired
     private ISysDictTypeService dictTypeService;
     private ISysDictTypeService dictTypeService;
 
 
@@ -32,137 +31,111 @@ public class TenantDictManageServiceImpl implements TenantDictManageService {
     private ISysDictDataService dictDataService;
     private ISysDictDataService dictDataService;
 
 
     @Override
     @Override
-    public List<SysDictType> selectDictTypeList(Long tenantId, SysDictType query) {
-//        return contextHelper.executeInTenant(tenantId, () -> {
-//            startPage();
-//            return dictTypeService.selectDictTypeList(query);
-//        });
-        throw new CustomException("Not implemented");
+    public List<SysDictType> selectDictTypeList(SysDictType query) {
+        startPage();
+        return dictTypeService.selectDictTypeList(query);
     }
     }
 
 
     @Override
     @Override
-    public SysDictType selectDictTypeById(Long tenantId, Long dictId) {
-//        return contextHelper.executeInTenant(tenantId, () -> dictTypeService.selectDictTypeById(dictId));
-        throw new CustomException("Not implemented");
+    public SysDictType selectDictTypeById(Long dictId) {
+        return dictTypeService.selectDictTypeById(dictId);
     }
     }
 
 
     @Override
     @Override
-    public int insertDictType(Long tenantId, SysDictType dict) {
-//        markTenantManual(dict);
-//        return contextHelper.executeInTenant(tenantId, () -> {
-//            if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) {
-//                throw new CustomException("字典类型已存在: " + dict.getDictType());
-//            }
-//            return dictTypeService.insertDictType(dict);
-//        });
-        throw new CustomException("Not implemented");
+    public int insertDictType(SysDictType dict) {
+        markTenantManual(dict);
+        if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) {
+            throw new CustomException("字典类型已存在: " + dict.getDictType());
+        }
+        return dictTypeService.insertDictType(dict);
     }
     }
 
 
     @Override
     @Override
-    public int updateDictType(Long tenantId, SysDictType dict) {
-//        return contextHelper.executeInTenant(tenantId, () -> {
-//            SysDictType existing = dictTypeService.selectDictTypeById(dict.getDictId());
-//            if (existing == null) {
-//                throw new CustomException("字典类型不存在");
-//            }
-//            if (isPlatformLocked(existing)) {
-//                throw new CustomException("平台管控字典类型不可直接修改,请通过模板同步更新");
-//            }
-//            if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) {
-//                throw new CustomException("字典类型已存在: " + dict.getDictType());
-//            }
-//            if (dict.getDictSource() == null) {
-//                dict.setDictSource(existing.getDictSource());
-//            }
-//            if (dict.getIsPlatformManaged() == null) {
-//                dict.setIsPlatformManaged(existing.getIsPlatformManaged());
-//            }
-//            return dictTypeService.updateDictType(dict);
-//        });
-        throw new CustomException("Not implemented");
+    public int updateDictType(SysDictType dict) {
+        SysDictType existing = dictTypeService.selectDictTypeById(dict.getDictId());
+        if (existing == null) {
+            throw new CustomException("字典类型不存在");
+        }
+        if (isPlatformLocked(existing)) {
+            throw new CustomException("平台管控字典类型不可直接修改,请通过模板同步更新");
+        }
+        if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) {
+            throw new CustomException("字典类型已存在: " + dict.getDictType());
+        }
+        if (dict.getDictSource() == null) {
+            dict.setDictSource(existing.getDictSource());
+        }
+        if (dict.getIsPlatformManaged() == null) {
+            dict.setIsPlatformManaged(existing.getIsPlatformManaged());
+        }
+        return dictTypeService.updateDictType(dict);
     }
     }
 
 
     @Override
     @Override
-    public void deleteDictTypeByIds(Long tenantId, Long[] dictIds) {
-//        contextHelper.runInTenant(tenantId, () -> {
-//            for (Long dictId : dictIds) {
-//                SysDictType existing = dictTypeService.selectDictTypeById(dictId);
-//                if (existing != null && isPlatformLocked(existing)) {
-//                    throw new CustomException("平台管控字典类型不可删除: " + existing.getDictType());
-//                }
-//            }
-//            dictTypeService.deleteDictTypeByIds(dictIds);
-//        });
-        throw new CustomException("Not implemented");
+    public void deleteDictTypeByIds(Long[] dictIds) {
+        for (Long dictId : dictIds) {
+            SysDictType existing = dictTypeService.selectDictTypeById(dictId);
+            if (existing != null && isPlatformLocked(existing)) {
+                throw new CustomException("平台管控字典类型不可删除: " + existing.getDictType());
+            }
+        }
+        dictTypeService.deleteDictTypeByIds(dictIds);
     }
     }
 
 
     @Override
     @Override
-    public List<SysDictData> selectDictDataList(Long tenantId, SysDictData query) {
-//        return contextHelper.executeInTenant(tenantId, () -> {
-//            startPage();
-//            return dictDataService.selectDictDataList(query);
-//        });
-        throw new CustomException("Not implemented");
+    public List<SysDictData> selectDictDataList(SysDictData query) {
+        startPage();
+        return dictDataService.selectDictDataList(query);
     }
     }
 
 
     @Override
     @Override
-    public SysDictData selectDictDataById(Long tenantId, Long dictCode) {
-//        return contextHelper.executeInTenant(tenantId, () -> dictDataService.selectDictDataById(dictCode));
-        throw new CustomException("Not implemented");
+    public SysDictData selectDictDataById(Long dictCode) {
+        return dictDataService.selectDictDataById(dictCode);
     }
     }
 
 
     @Override
     @Override
-    public int insertDictData(Long tenantId, SysDictData dict) {
-//        markTenantManual(dict);
-//        return contextHelper.executeInTenant(tenantId, () -> dictDataService.insertDictData(dict));
-        throw new CustomException("Not implemented");
+    public int insertDictData(SysDictData dict) {
+        markTenantManual(dict);
+        return dictDataService.insertDictData(dict);
     }
     }
 
 
     @Override
     @Override
-    public int updateDictData(Long tenantId, SysDictData dict) {
-//        return contextHelper.executeInTenant(tenantId, () -> {
-//            SysDictData existing = dictDataService.selectDictDataById(dict.getDictCode());
-//            if (existing == null) {
-//                throw new CustomException("字典数据不存在");
-//            }
-//            if (isPlatformLocked(existing)) {
-//                throw new CustomException("平台管控字典数据不可直接修改: " + existing.getDictLabel());
-//            }
-//            if (dict.getDictSource() == null) {
-//                dict.setDictSource(existing.getDictSource());
-//            }
-//            if (dict.getIsPlatformManaged() == null) {
-//                dict.setIsPlatformManaged(existing.getIsPlatformManaged());
-//            }
-//            return dictDataService.updateDictData(dict);
-//        });
-        throw new CustomException("Not implemented");
+    public int updateDictData(SysDictData dict) {
+        SysDictData existing = dictDataService.selectDictDataById(dict.getDictCode());
+        if (existing == null) {
+            throw new CustomException("字典数据不存在");
+        }
+        if (isPlatformLocked(existing)) {
+            throw new CustomException("平台管控字典数据不可直接修改: " + existing.getDictLabel());
+        }
+        if (dict.getDictSource() == null) {
+            dict.setDictSource(existing.getDictSource());
+        }
+        if (dict.getIsPlatformManaged() == null) {
+            dict.setIsPlatformManaged(existing.getIsPlatformManaged());
+        }
+        return dictDataService.updateDictData(dict);
     }
     }
 
 
     @Override
     @Override
-    public void deleteDictDataByIds(Long tenantId, Long[] dictCodes) {
-//        contextHelper.runInTenant(tenantId, () -> {
-//            for (Long dictCode : dictCodes) {
-//                SysDictData existing = dictDataService.selectDictDataById(dictCode);
-//                if (existing != null && isPlatformLocked(existing)) {
-//                    throw new CustomException("平台管控字典数据不可删除: " + existing.getDictLabel());
-//                }
-//            }
-//            dictDataService.deleteDictDataByIds(dictCodes);
-//        });
-        throw new CustomException("Not implemented");
+    public void deleteDictDataByIds(Long[] dictCodes) {
+        for (Long dictCode : dictCodes) {
+            SysDictData existing = dictDataService.selectDictDataById(dictCode);
+            if (existing != null && isPlatformLocked(existing)) {
+                throw new CustomException("平台管控字典数据不可删除: " + existing.getDictLabel());
+            }
+        }
+        dictDataService.deleteDictDataByIds(dictCodes);
     }
     }
 
 
     @Override
     @Override
-    public void refreshDictCache(Long tenantId) {
-//        contextHelper.runInTenant(tenantId, () -> dictTypeService.resetDictCache());
-        throw new CustomException("Not implemented");
+    public void refreshDictCache() {
+        dictTypeService.resetDictCache();
     }
     }
 
 
     @Override
     @Override
-    public String checkDictTypeUnique(Long tenantId, SysDictType dict) {
-//        return contextHelper.executeInTenant(tenantId, () -> dictTypeService.checkDictTypeUnique(dict));
-        throw new CustomException("Not implemented");
+    public String checkDictTypeUnique(SysDictType dict) {
+        return dictTypeService.checkDictTypeUnique(dict);
     }
     }
 
 
     private void markTenantManual(SysDictType dict) {
     private void markTenantManual(SysDictType dict) {
@@ -209,7 +182,7 @@ public class TenantDictManageServiceImpl implements TenantDictManageService {
         return TenantDictConstants.SOURCE_PLATFORM.equals(data.getDictSource());
         return TenantDictConstants.SOURCE_PLATFORM.equals(data.getDictSource());
     }
     }
 
 
-    /** 切库后再开启分页,避免 loadActiveTenant 查询消耗 PageHelper */
+    /** 切库后再开启分页,避免租户校验查询消耗 PageHelper */
     private void startPage() {
     private void startPage() {
         PageDomain pageDomain = TableSupport.buildPageRequest();
         PageDomain pageDomain = TableSupport.buildPageRequest();
         Integer pageNum = pageDomain.getPageNum();
         Integer pageNum = pageDomain.getPageNum();

+ 157 - 236
fs-service/src/main/java/com/fs/tenant/dict/service/impl/TenantDictSyncServiceImpl.java

@@ -3,7 +3,6 @@ package com.fs.tenant.dict.service.impl;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.domain.entity.SysDictType;
 import com.fs.common.core.domain.entity.SysDictType;
-import com.fs.common.enums.DataSourceType;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.system.mapper.SysDictDataMapper;
 import com.fs.system.mapper.SysDictDataMapper;
@@ -15,6 +14,7 @@ import com.fs.tenant.dict.domain.TenantDictTemplateType;
 import com.fs.tenant.dict.dto.TenantDictSyncRunReq;
 import com.fs.tenant.dict.dto.TenantDictSyncRunReq;
 import com.fs.tenant.dict.service.TenantDictSyncService;
 import com.fs.tenant.dict.service.TenantDictSyncService;
 import com.fs.tenant.dict.service.TenantDictTemplateService;
 import com.fs.tenant.dict.service.TenantDictTemplateService;
+import com.fs.tenant.dict.vo.TenantDictSyncInitVo;
 import com.fs.tenant.dict.vo.TenantDictSyncResultVo;
 import com.fs.tenant.dict.vo.TenantDictSyncResultVo;
 import com.fs.tenant.dict.vo.TenantDictSyncTaskDetailVo;
 import com.fs.tenant.dict.vo.TenantDictSyncTaskDetailVo;
 import com.fs.tenant.dict.vo.TenantDictSyncTaskVo;
 import com.fs.tenant.dict.vo.TenantDictSyncTaskVo;
@@ -28,11 +28,11 @@ import org.springframework.stereotype.Service;
 
 
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.*;
-import java.util.concurrent.*;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 /**
 /**
  * 租户字典同步引擎:模板 → 租户库,支持 APPEND / MERGE / OVERWRITE。
  * 租户字典同步引擎:模板 → 租户库,支持 APPEND / MERGE / OVERWRITE。
+ * 数据源切换由接口层负责。
  */
  */
 @Service
 @Service
 public class TenantDictSyncServiceImpl implements TenantDictSyncService {
 public class TenantDictSyncServiceImpl implements TenantDictSyncService {
@@ -53,181 +53,126 @@ public class TenantDictSyncServiceImpl implements TenantDictSyncService {
     private JdbcTemplate jdbcTemplate;
     private JdbcTemplate jdbcTemplate;
 
 
     @Override
     @Override
-    public Map<String, Object> runSync(TenantDictSyncRunReq req, String triggerBy) {
-//        validateSyncReq(req);
-//        List<TenantDictTemplateType> templateTypes = resolveTemplateTypes(req.getDictTypes());
-//        if (templateTypes.isEmpty()) {
-//            throw new CustomException("没有可同步的模板字典类型,请先在平台模板中维护");
-//        }
-//        List<TenantInfo> tenants = resolveTenants(req);
-//        if (tenants.isEmpty()) {
-//            throw new CustomException("未匹配到可同步租户");
-//        }
-//
-//        if (Boolean.TRUE.equals(req.getDryRun())) {
-//            List<TenantDictSyncResultVo> preview = new ArrayList<>();
-//            for (TenantInfo tenant : tenants) {
-//                preview.add(previewOneTenant(tenant, templateTypes, req.getSyncMode()));
-//            }
-//            Map<String, Object> resp = new HashMap<>();
-//            resp.put("dryRun", true);
-//            resp.put("preview", preview);
-//            return resp;
-//        }
-//
-//        String taskNo = buildTaskNo();
-//        contextHelper.runInMaster(() -> {
-//            initMasterTaskTables();
-//            insertTask(taskNo, req, tenants.size(), triggerBy);
-//        });
-//
-//        CompletableFuture.runAsync(() -> executeSyncTask(taskNo, req, tenants, templateTypes));
-//
-//        Map<String, Object> resp = new HashMap<>();
-//        resp.put("taskNo", taskNo);
-//        resp.put("status", TenantDictConstants.TASK_RUNNING);
-//        resp.put("totalTenants", tenants.size());
-//        resp.put("syncMode", req.getSyncMode());
-//        return resp;
-        throw new CustomException("Not implemented");
-    }
+    public TenantDictSyncInitVo initSync(TenantDictSyncRunReq req, String triggerBy) {
+        validateSyncReq(req);
+        List<TenantDictTemplateType> templateTypes = resolveTemplateTypes(req.getDictTypes());
+        if (templateTypes.isEmpty()) {
+            throw new CustomException("没有可同步的模板字典类型,请先在平台模板中维护");
+        }
+        List<TenantInfo> tenants = resolveTenants(req);
+        if (tenants.isEmpty()) {
+            throw new CustomException("未匹配到可同步租户");
+        }
 
 
-    @Override
-    public TenantDictSyncTaskVo getTask(String taskNo) {
-//        return contextHelper.executeInMaster(() -> {
-//            initMasterTaskTables();
-//            List<Map<String, Object>> rows = jdbcTemplate.queryForList(
-//                    "SELECT task_no,sync_mode,scope_type,dict_types,total_tenants,success_tenants,failed_tenants,status,trigger_by,started_at,finished_at " +
-//                            "FROM tenant_dict_sync_task WHERE task_no=?", taskNo);
-//            if (rows.isEmpty()) {
-//                return null;
-//            }
-//            Map<String, Object> row = rows.get(0);
-//            TenantDictSyncTaskVo vo = new TenantDictSyncTaskVo();
-//            vo.setTaskNo(str(row.get("task_no")));
-//            vo.setSyncMode(str(row.get("sync_mode")));
-//            vo.setScopeType(str(row.get("scope_type")));
-//            vo.setDictTypes(str(row.get("dict_types")));
-//            vo.setTotalTenants(intVal(row.get("total_tenants")));
-//            vo.setSuccessTenants(intVal(row.get("success_tenants")));
-//            vo.setFailedTenants(intVal(row.get("failed_tenants")));
-//            vo.setStatus(str(row.get("status")));
-//            vo.setTriggerBy(str(row.get("trigger_by")));
-//            vo.setStartedAt(str(row.get("started_at")));
-//            vo.setFinishedAt(str(row.get("finished_at")));
-//            vo.setDetails(queryTaskDetails(taskNo));
-//            return vo;
-//        });
-        throw new CustomException("Not implemented");
+        Map<String, List<TenantDictTemplateData>> dataCache = loadTemplateDataCache(templateTypes);
+
+        TenantDictSyncInitVo init = new TenantDictSyncInitVo();
+        init.setDryRun(Boolean.TRUE.equals(req.getDryRun()));
+        init.setSyncMode(req.getSyncMode());
+        init.setTenants(tenants);
+        init.setTemplateTypes(templateTypes);
+        init.setDataCache(dataCache);
+
+        if (!init.isDryRun()) {
+            initMasterTaskTables();
+            String taskNo = buildTaskNo();
+            insertTask(taskNo, req, tenants.size(), triggerBy);
+            init.setTaskNo(taskNo);
+        }
+        return init;
     }
     }
 
 
     @Override
     @Override
-    public List<TenantDictSyncResultVo> previewDiff(Long tenantId, List<String> dictTypes) {
-//        TenantInfo tenant = contextHelper.loadActiveTenant(tenantId);
-//        List<TenantDictTemplateType> templateTypes = resolveTemplateTypes(dictTypes);
-//        TenantDictSyncResultVo result = previewOneTenant(tenant, templateTypes, TenantDictConstants.SYNC_MERGE);
-//        return Collections.singletonList(result);
-        throw new CustomException("Not implemented");
-    }
-
-    private void executeSyncTask(String taskNo, TenantDictSyncRunReq req, List<TenantInfo> tenants,
-                                 List<TenantDictTemplateType> templateTypes) {
-        int success = 0;
-        int failed = 0;
-        Map<String, List<TenantDictTemplateData>> dataCache = loadTemplateDataCache(templateTypes);
-
-        if (Boolean.TRUE.equals(req.getParallel())) {
-            int threads = req.getThreads() == null || req.getThreads() <= 0 ? 4 : req.getThreads();
-            ExecutorService pool = Executors.newFixedThreadPool(threads);
-            try {
-                List<Future<Boolean>> futures = new ArrayList<>();
-                for (TenantInfo tenant : tenants) {
-                    futures.add(pool.submit(() -> syncOneTenant(taskNo, tenant, templateTypes, dataCache, req.getSyncMode())));
-                }
-                for (Future<Boolean> f : futures) {
-                    try {
-                        if (Boolean.TRUE.equals(f.get())) success++;
-                        else failed++;
-                    } catch (Exception e) {
-                        failed++;
-                        log.error("[TenantDictSync] 并行任务异常", e);
-                    }
+    public TenantDictSyncResultVo previewOneTenant(TenantInfo tenant,
+                                                   List<TenantDictTemplateType> templateTypes,
+                                                   Map<String, List<TenantDictTemplateData>> dataCache,
+                                                   String syncMode) {
+        TenantDictSyncResultVo stats = new TenantDictSyncResultVo();
+        stats.setTenantId(tenant.getId());
+        stats.setTenantCode(tenant.getTenantCode());
+        stats.setTenantName(tenant.getTenantName());
+        for (TenantDictTemplateType templateType : templateTypes) {
+            String mode = resolveMode(syncMode, templateType);
+            previewType(templateType, mode, stats);
+            List<TenantDictTemplateData> templateDataList =
+                    dataCache.getOrDefault(templateType.getDictType(), Collections.emptyList());
+            if (TenantDictConstants.SYNC_OVERWRITE.equals(mode)) {
+                previewOverwriteData(templateType.getDictType(), templateDataList, stats);
+            } else {
+                for (TenantDictTemplateData templateData : templateDataList) {
+                    previewData(templateData, mode, stats);
                 }
                 }
-            } finally {
-                pool.shutdown();
             }
             }
-        } else {
-            for (TenantInfo tenant : tenants) {
-                if (syncOneTenant(taskNo, tenant, templateTypes, dataCache, req.getSyncMode())) {
-                    success++;
-                } else {
-                    failed++;
+        }
+        stats.setMessage("预览完成(未写入)");
+        return stats;
+    }
+
+    @Override
+    public TenantDictSyncResultVo syncOneTenant(TenantInfo tenant,
+                                                List<TenantDictTemplateType> templateTypes,
+                                                Map<String, List<TenantDictTemplateData>> dataCache,
+                                                String syncMode) {
+        TenantDictSyncResultVo stats = new TenantDictSyncResultVo();
+        stats.setTenantId(tenant.getId());
+        stats.setTenantCode(tenant.getTenantCode());
+        stats.setTenantName(tenant.getTenantName());
+        for (TenantDictTemplateType templateType : templateTypes) {
+            String mode = resolveMode(syncMode, templateType);
+            syncType(templateType, mode, stats);
+            List<TenantDictTemplateData> templateDataList =
+                    dataCache.getOrDefault(templateType.getDictType(), Collections.emptyList());
+            if (TenantDictConstants.SYNC_OVERWRITE.equals(mode)) {
+                overwriteData(templateType.getDictType(), templateDataList, stats);
+            } else {
+                for (TenantDictTemplateData templateData : templateDataList) {
+                    syncData(templateData, mode, stats);
                 }
                 }
             }
             }
         }
         }
-        finalizeTask(taskNo, success, failed);
-    }
-
-    private boolean syncOneTenant(String taskNo, TenantInfo tenant, List<TenantDictTemplateType> templateTypes,
-                                  Map<String, List<TenantDictTemplateData>> dataCache, String syncMode) {
-//        Date start = new Date();
-//        TenantDictSyncResultVo stats = new TenantDictSyncResultVo();
-//        stats.setTenantId(tenant.getId());
-//        stats.setTenantCode(tenant.getTenantCode());
-//        stats.setTenantName(tenant.getTenantName());
-//        String status = TenantDictConstants.DETAIL_SUCCESS;
-//        String errorMsg = null;
-//        try {
-//            contextHelper.executeInTenant(tenant.getId(), () -> {
-//                for (TenantDictTemplateType templateType : templateTypes) {
-//                    String mode = resolveMode(syncMode, templateType);
-//                    syncType(templateType, mode, stats);
-//                    List<TenantDictTemplateData> templateDataList = dataCache.getOrDefault(templateType.getDictType(), Collections.emptyList());
-//                    if (TenantDictConstants.SYNC_OVERWRITE.equals(mode)) {
-//                        overwriteData(templateType.getDictType(), templateDataList, stats);
-//                    } else {
-//                        for (TenantDictTemplateData templateData : templateDataList) {
-//                            syncData(templateData, mode, stats);
-//                        }
-//                    }
-//                }
-//                dictTypeService.resetDictCache();
-//                return null;
-//            });
-//        } catch (Exception e) {
-//            status = TenantDictConstants.DETAIL_FAILED;
-//            errorMsg = e.getMessage();
-//            log.error("[TenantDictSync] tenantId={} 同步失败", tenant.getId(), e);
-//        }
-//        insertTaskDetail(taskNo, tenant, stats, status, errorMsg, start, new Date());
-//        return TenantDictConstants.DETAIL_SUCCESS.equals(status);
-        throw new CustomException("Not implemented");
-    }
-
-    private TenantDictSyncResultVo previewOneTenant(TenantInfo tenant, List<TenantDictTemplateType> templateTypes, String syncMode) {
-//        Map<String, List<TenantDictTemplateData>> dataCache = loadTemplateDataCache(templateTypes);
-//        TenantDictSyncResultVo stats = new TenantDictSyncResultVo();
-//        stats.setTenantId(tenant.getId());
-//        stats.setTenantCode(tenant.getTenantCode());
-//        stats.setTenantName(tenant.getTenantName());
-//        contextHelper.executeInTenant(tenant.getId(), () -> {
-//            for (TenantDictTemplateType templateType : templateTypes) {
-//                String mode = resolveMode(syncMode, templateType);
-//                previewType(templateType, mode, stats);
-//                List<TenantDictTemplateData> templateDataList = dataCache.getOrDefault(templateType.getDictType(), Collections.emptyList());
-//                if (TenantDictConstants.SYNC_OVERWRITE.equals(mode)) {
-//                    previewOverwriteData(templateType.getDictType(), templateDataList, stats);
-//                } else {
-//                    for (TenantDictTemplateData templateData : templateDataList) {
-//                        previewData(templateData, mode, stats);
-//                    }
-//                }
-//            }
-//            return null;
-//        });
-//        stats.setMessage("预览完成(未写入)");
-//        return stats;
-        throw new CustomException("Not implemented");
+        dictTypeService.resetDictCache();
+        return stats;
+    }
+
+    @Override
+    public void recordTaskDetail(String taskNo, TenantInfo tenant, TenantDictSyncResultVo stats,
+                                 String status, String errorMsg, Date start, Date end) {
+        insertTaskDetail(taskNo, tenant, stats, status, errorMsg, start, end);
+    }
+
+    @Override
+    public void finalizeTask(String taskNo, int success, int failed) {
+        String status = failed == 0 ? TenantDictConstants.TASK_SUCCESS
+                : (success == 0 ? TenantDictConstants.TASK_FAILED : TenantDictConstants.TASK_PARTIAL);
+        jdbcTemplate.update(
+                "UPDATE tenant_dict_sync_task SET success_tenants=?, failed_tenants=?, status=?, finished_at=NOW() WHERE task_no=?",
+                success, failed, status, taskNo);
+    }
+
+    @Override
+    public TenantDictSyncTaskVo getTask(String taskNo) {
+        initMasterTaskTables();
+        List<Map<String, Object>> rows = jdbcTemplate.queryForList(
+                "SELECT task_no,sync_mode,scope_type,dict_types,total_tenants,success_tenants,failed_tenants,status,trigger_by,started_at,finished_at " +
+                        "FROM tenant_dict_sync_task WHERE task_no=?", taskNo);
+        if (rows.isEmpty()) {
+            return null;
+        }
+        Map<String, Object> row = rows.get(0);
+        TenantDictSyncTaskVo vo = new TenantDictSyncTaskVo();
+        vo.setTaskNo(str(row.get("task_no")));
+        vo.setSyncMode(str(row.get("sync_mode")));
+        vo.setScopeType(str(row.get("scope_type")));
+        vo.setDictTypes(str(row.get("dict_types")));
+        vo.setTotalTenants(intVal(row.get("total_tenants")));
+        vo.setSuccessTenants(intVal(row.get("success_tenants")));
+        vo.setFailedTenants(intVal(row.get("failed_tenants")));
+        vo.setStatus(str(row.get("status")));
+        vo.setTriggerBy(str(row.get("trigger_by")));
+        vo.setStartedAt(str(row.get("started_at")));
+        vo.setFinishedAt(str(row.get("finished_at")));
+        vo.setDetails(queryTaskDetails(taskNo));
+        return vo;
     }
     }
 
 
     private void syncType(TenantDictTemplateType template, String mode, TenantDictSyncResultVo stats) {
     private void syncType(TenantDictTemplateType template, String mode, TenantDictSyncResultVo stats) {
@@ -344,11 +289,7 @@ public class TenantDictSyncServiceImpl implements TenantDictSyncService {
     }
     }
 
 
     private boolean canUpdateType(SysDictType existing, String mode) {
     private boolean canUpdateType(SysDictType existing, String mode) {
-        if (TenantDictConstants.SYNC_APPEND.equals(mode)) {
-            return false;
-        }
-        // MERGE / OVERWRITE:模板中的类型均写入平台溯源字段,便于租户侧锁定
-        return true;
+        return !TenantDictConstants.SYNC_APPEND.equals(mode);
     }
     }
 
 
     private boolean canUpdateData(SysDictData existing, TenantDictTemplateData template, String mode) {
     private boolean canUpdateData(SysDictData existing, TenantDictTemplateData template, String mode) {
@@ -435,23 +376,17 @@ public class TenantDictSyncServiceImpl implements TenantDictSyncService {
     }
     }
 
 
     private List<TenantInfo> resolveTenants(TenantDictSyncRunReq req) {
     private List<TenantInfo> resolveTenants(TenantDictSyncRunReq req) {
-//        DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
-//        try {
-//            List<TenantInfo> all = tenantInfoService.selectTenantInfoList(new TenantInfo());
-//            all.removeIf(t -> t.getStatus() == null || t.getStatus() != 1);
-//            String scope = req.getScopeType() == null ? TenantDictConstants.SCOPE_ALL : req.getScopeType().toUpperCase();
-//            List<Long> ids = req.getTenantIds() == null ? Collections.emptyList() : req.getTenantIds();
-//            if (TenantDictConstants.SCOPE_INCLUDE.equals(scope)) {
-//                return all.stream().filter(t -> ids.contains(t.getId())).collect(Collectors.toList());
-//            }
-//            if (TenantDictConstants.SCOPE_EXCLUDE.equals(scope)) {
-//                return all.stream().filter(t -> !ids.contains(t.getId())).collect(Collectors.toList());
-//            }
-//            return all;
-//        } finally {
-//            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
-//        }
-        throw new CustomException("Not implemented");
+        List<TenantInfo> all = tenantInfoService.selectTenantInfoList(new TenantInfo());
+        all.removeIf(t -> t.getStatus() == null || t.getStatus() != 1);
+        String scope = req.getScopeType() == null ? TenantDictConstants.SCOPE_ALL : req.getScopeType().toUpperCase();
+        List<Long> ids = req.getTenantIds() == null ? Collections.emptyList() : req.getTenantIds();
+        if (TenantDictConstants.SCOPE_INCLUDE.equals(scope)) {
+            return all.stream().filter(t -> ids.contains(t.getId())).collect(Collectors.toList());
+        }
+        if (TenantDictConstants.SCOPE_EXCLUDE.equals(scope)) {
+            return all.stream().filter(t -> !ids.contains(t.getId())).collect(Collectors.toList());
+        }
+        return all;
     }
     }
 
 
     private void validateSyncReq(TenantDictSyncRunReq req) {
     private void validateSyncReq(TenantDictSyncRunReq req) {
@@ -490,58 +425,44 @@ public class TenantDictSyncServiceImpl implements TenantDictSyncService {
     }
     }
 
 
     private void insertTask(String taskNo, TenantDictSyncRunReq req, int total, String triggerBy) {
     private void insertTask(String taskNo, TenantDictSyncRunReq req, int total, String triggerBy) {
-//        contextHelper.runInMaster(() -> jdbcTemplate.update(
-//                "INSERT INTO tenant_dict_sync_task(task_no,sync_mode,scope_type,dict_types,total_tenants,status,trigger_by,started_at) VALUES(?,?,?,?,?,?,?,NOW())",
-//                taskNo, req.getSyncMode(), req.getScopeType(), JSON.toJSONString(req.getDictTypes()), total,
-//                TenantDictConstants.TASK_RUNNING, triggerBy));
-        throw new CustomException("Not implemented");
+        jdbcTemplate.update(
+                "INSERT INTO tenant_dict_sync_task(task_no,sync_mode,scope_type,dict_types,total_tenants,status,trigger_by,started_at) VALUES(?,?,?,?,?,?,?,NOW())",
+                taskNo, req.getSyncMode(), req.getScopeType(), JSON.toJSONString(req.getDictTypes()), total,
+                TenantDictConstants.TASK_RUNNING, triggerBy);
     }
     }
 
 
     private void insertTaskDetail(String taskNo, TenantInfo tenant, TenantDictSyncResultVo stats,
     private void insertTaskDetail(String taskNo, TenantInfo tenant, TenantDictSyncResultVo stats,
-                                    String status, String errorMsg, Date start, Date end) {
-//        contextHelper.runInMaster(() -> jdbcTemplate.update(
-//                "INSERT INTO tenant_dict_sync_task_detail(task_no,tenant_id,tenant_code,tenant_name,status,type_added,type_updated,data_added,data_updated,data_skipped,error_msg,started_at,finished_at) " +
-//                        "VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)",
-//                taskNo, tenant.getId(), tenant.getTenantCode(), tenant.getTenantName(), status,
-//                stats.getTypeAdded(), stats.getTypeUpdated(), stats.getDataAdded(), stats.getDataUpdated(),
-//                stats.getDataSkipped(), errorMsg, start, end));
-        throw new CustomException("Not implemented");
-    }
-
-    private void finalizeTask(String taskNo, int success, int failed) {
-//        contextHelper.runInMaster(() -> {
-//            String status = failed == 0 ? TenantDictConstants.TASK_SUCCESS
-//                    : (success == 0 ? TenantDictConstants.TASK_FAILED : TenantDictConstants.TASK_PARTIAL);
-//            jdbcTemplate.update(
-//                    "UPDATE tenant_dict_sync_task SET success_tenants=?, failed_tenants=?, status=?, finished_at=NOW() WHERE task_no=?",
-//                    success, failed, status, taskNo);
-//        });
-        throw new CustomException("Not implemented");
+                                  String status, String errorMsg, Date start, Date end) {
+        jdbcTemplate.update(
+                "INSERT INTO tenant_dict_sync_task_detail(task_no,tenant_id,tenant_code,tenant_name,status,type_added,type_updated,data_added,data_updated,data_skipped,error_msg,started_at,finished_at) " +
+                        "VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)",
+                taskNo, tenant.getId(), tenant.getTenantCode(), tenant.getTenantName(), status,
+                stats.getTypeAdded(), stats.getTypeUpdated(), stats.getDataAdded(), stats.getDataUpdated(),
+                stats.getDataSkipped(), errorMsg, start, end);
     }
     }
 
 
     private List<TenantDictSyncTaskDetailVo> queryTaskDetails(String taskNo) {
     private List<TenantDictSyncTaskDetailVo> queryTaskDetails(String taskNo) {
-//        List<Map<String, Object>> rows = jdbcTemplate.queryForList(
-//                "SELECT tenant_id,tenant_code,tenant_name,status,type_added,type_updated,data_added,data_updated,data_skipped,error_msg,started_at,finished_at " +
-//                        "FROM tenant_dict_sync_task_detail WHERE task_no=? ORDER BY id ASC", taskNo);
-//        List<TenantDictSyncTaskDetailVo> list = new ArrayList<>();
-//        for (Map<String, Object> row : rows) {
-//            TenantDictSyncTaskDetailVo vo = new TenantDictSyncTaskDetailVo();
-//            vo.setTenantId(longVal(row.get("tenant_id")));
-//            vo.setTenantCode(str(row.get("tenant_code")));
-//            vo.setTenantName(str(row.get("tenant_name")));
-//            vo.setStatus(str(row.get("status")));
-//            vo.setTypeAdded(intVal(row.get("type_added")));
-//            vo.setTypeUpdated(intVal(row.get("type_updated")));
-//            vo.setDataAdded(intVal(row.get("data_added")));
-//            vo.setDataUpdated(intVal(row.get("data_updated")));
-//            vo.setDataSkipped(intVal(row.get("data_skipped")));
-//            vo.setErrorMsg(str(row.get("error_msg")));
-//            vo.setStartedAt(str(row.get("started_at")));
-//            vo.setFinishedAt(str(row.get("finished_at")));
-//            list.add(vo);
-//        }
-//        return list;
-        throw new CustomException("Not implemented");
+        List<Map<String, Object>> rows = jdbcTemplate.queryForList(
+                "SELECT tenant_id,tenant_code,tenant_name,status,type_added,type_updated,data_added,data_updated,data_skipped,error_msg,started_at,finished_at " +
+                        "FROM tenant_dict_sync_task_detail WHERE task_no=? ORDER BY id ASC", taskNo);
+        List<TenantDictSyncTaskDetailVo> list = new ArrayList<>();
+        for (Map<String, Object> row : rows) {
+            TenantDictSyncTaskDetailVo vo = new TenantDictSyncTaskDetailVo();
+            vo.setTenantId(longVal(row.get("tenant_id")));
+            vo.setTenantCode(str(row.get("tenant_code")));
+            vo.setTenantName(str(row.get("tenant_name")));
+            vo.setStatus(str(row.get("status")));
+            vo.setTypeAdded(intVal(row.get("type_added")));
+            vo.setTypeUpdated(intVal(row.get("type_updated")));
+            vo.setDataAdded(intVal(row.get("data_added")));
+            vo.setDataUpdated(intVal(row.get("data_updated")));
+            vo.setDataSkipped(intVal(row.get("data_skipped")));
+            vo.setErrorMsg(str(row.get("error_msg")));
+            vo.setStartedAt(str(row.get("started_at")));
+            vo.setFinishedAt(str(row.get("finished_at")));
+            list.add(vo);
+        }
+        return list;
     }
     }
 
 
     private String str(Object o) {
     private String str(Object o) {

+ 105 - 139
fs-service/src/main/java/com/fs/tenant/dict/service/impl/TenantDictTemplateServiceImpl.java

@@ -1,24 +1,25 @@
 package com.fs.tenant.dict.service.impl;
 package com.fs.tenant.dict.service.impl;
 
 
 import com.fs.common.constant.UserConstants;
 import com.fs.common.constant.UserConstants;
-import com.fs.common.exception.CustomException;
 import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.domain.entity.SysDictType;
 import com.fs.common.core.domain.entity.SysDictType;
-import com.fs.tenant.dict.dto.TenantDictTemplateImportReq;
-import com.fs.tenant.dict.service.TenantDictManageService;
-import com.fs.tenant.dict.vo.TenantDictTemplateImportResultVo;
+import com.fs.common.exception.CustomException;
 import com.fs.system.service.ISysDictDataService;
 import com.fs.system.service.ISysDictDataService;
 import com.fs.system.service.ISysDictTypeService;
 import com.fs.system.service.ISysDictTypeService;
-import org.springframework.util.CollectionUtils;
 import com.fs.tenant.dict.domain.TenantDictTemplateData;
 import com.fs.tenant.dict.domain.TenantDictTemplateData;
 import com.fs.tenant.dict.domain.TenantDictTemplateType;
 import com.fs.tenant.dict.domain.TenantDictTemplateType;
+import com.fs.tenant.dict.dto.TenantDictTemplateImportReq;
 import com.fs.tenant.dict.mapper.TenantDictTemplateMapper;
 import com.fs.tenant.dict.mapper.TenantDictTemplateMapper;
 import com.fs.tenant.dict.service.TenantDictTemplateService;
 import com.fs.tenant.dict.service.TenantDictTemplateService;
+import com.fs.tenant.dict.vo.TenantDictTemplateImportResultVo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
 
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 
 
 @Service
 @Service
 public class TenantDictTemplateServiceImpl implements TenantDictTemplateService {
 public class TenantDictTemplateServiceImpl implements TenantDictTemplateService {
@@ -35,196 +36,155 @@ public class TenantDictTemplateServiceImpl implements TenantDictTemplateService
     @Autowired
     @Autowired
     private ISysDictDataService sysDictDataService;
     private ISysDictDataService sysDictDataService;
 
 
-    @Autowired
-    private TenantDictManageService tenantDictManageService;
-
     @Override
     @Override
     public List<TenantDictTemplateType> selectTemplateTypeList(TenantDictTemplateType query) {
     public List<TenantDictTemplateType> selectTemplateTypeList(TenantDictTemplateType query) {
-//        return contextHelper.executeInMaster(() -> {
-//            ensureTemplateTables();
-//            return templateMapper.selectTemplateTypeList(query);
-//        });
-        throw new CustomException("Not implemented");
+        ensureTemplateTables();
+        return templateMapper.selectTemplateTypeList(query);
     }
     }
 
 
     @Override
     @Override
     public TenantDictTemplateType selectTemplateTypeById(Long dictId) {
     public TenantDictTemplateType selectTemplateTypeById(Long dictId) {
-//        return contextHelper.executeInMaster(() -> templateMapper.selectTemplateTypeById(dictId));
-        throw new CustomException("Not implemented");
+        return templateMapper.selectTemplateTypeById(dictId);
     }
     }
 
 
     @Override
     @Override
     public int insertTemplateType(TenantDictTemplateType row) {
     public int insertTemplateType(TenantDictTemplateType row) {
-//        normalizeType(row, true);
-//        return contextHelper.executeInMaster(() -> {
-//            ensureTemplateTables();
-//            if (templateMapper.selectTemplateTypeByType(row.getDictType()) != null) {
-//                throw new CustomException("模板字典类型已存在: " + row.getDictType());
-//            }
-//            return templateMapper.insertTemplateType(row);
-//        });
-        throw new CustomException("Not implemented");
+        normalizeType(row, true);
+        ensureTemplateTables();
+        if (templateMapper.selectTemplateTypeByType(row.getDictType()) != null) {
+            throw new CustomException("模板字典类型已存在: " + row.getDictType());
+        }
+        return templateMapper.insertTemplateType(row);
     }
     }
 
 
     @Override
     @Override
     public int updateTemplateType(TenantDictTemplateType row) {
     public int updateTemplateType(TenantDictTemplateType row) {
-//        normalizeType(row, false);
-//        return contextHelper.executeInMaster(() -> templateMapper.updateTemplateType(row));
-        throw new CustomException("Not implemented");
+        normalizeType(row, false);
+        return templateMapper.updateTemplateType(row);
     }
     }
 
 
     @Override
     @Override
     public void deleteTemplateTypeByIds(Long[] dictIds) {
     public void deleteTemplateTypeByIds(Long[] dictIds) {
-//        contextHelper.runInMaster(() -> {
-//            for (Long dictId : dictIds) {
-//                TenantDictTemplateType type = templateMapper.selectTemplateTypeById(dictId);
-//                if (type != null && templateMapper.countTemplateDataByType(type.getDictType()) > 0) {
-//                    throw new CustomException(type.getDictName() + " 下存在模板数据,不能删除");
-//                }
-//            }
-//            templateMapper.deleteTemplateTypeByIds(dictIds);
-//        });
-        throw new CustomException("Not implemented");
+        for (Long dictId : dictIds) {
+            TenantDictTemplateType type = templateMapper.selectTemplateTypeById(dictId);
+            if (type != null && templateMapper.countTemplateDataByType(type.getDictType()) > 0) {
+                throw new CustomException(type.getDictName() + " 下存在模板数据,不能删除");
+            }
+        }
+        templateMapper.deleteTemplateTypeByIds(dictIds);
     }
     }
 
 
     @Override
     @Override
     public List<TenantDictTemplateData> selectTemplateDataList(TenantDictTemplateData query) {
     public List<TenantDictTemplateData> selectTemplateDataList(TenantDictTemplateData query) {
-//        return contextHelper.executeInMaster(() -> templateMapper.selectTemplateDataList(query));
-        throw new CustomException("Not implemented");
+        return templateMapper.selectTemplateDataList(query);
     }
     }
 
 
     @Override
     @Override
     public TenantDictTemplateData selectTemplateDataById(Long dictCode) {
     public TenantDictTemplateData selectTemplateDataById(Long dictCode) {
-//        return contextHelper.executeInMaster(() -> templateMapper.selectTemplateDataById(dictCode));
-        throw new CustomException("Not implemented");
+        return templateMapper.selectTemplateDataById(dictCode);
     }
     }
 
 
     @Override
     @Override
     public int insertTemplateData(TenantDictTemplateData row) {
     public int insertTemplateData(TenantDictTemplateData row) {
-//        normalizeData(row);
-//        return contextHelper.executeInMaster(() -> {
-//            if (templateMapper.selectTemplateTypeByType(row.getDictType()) == null) {
-//                throw new CustomException("模板字典类型不存在,请先创建类型: " + row.getDictType());
-//            }
-//            if (templateMapper.selectTemplateDataByTypeAndValue(row.getDictType(), row.getDictValue()) != null) {
-//                throw new CustomException("同类型下键值已存在: " + row.getDictValue());
-//            }
-//            return templateMapper.insertTemplateData(row);
-//        });
-        throw new CustomException("Not implemented");
+        normalizeData(row);
+        if (templateMapper.selectTemplateTypeByType(row.getDictType()) == null) {
+            throw new CustomException("模板字典类型不存在,请先创建类型: " + row.getDictType());
+        }
+        if (templateMapper.selectTemplateDataByTypeAndValue(row.getDictType(), row.getDictValue()) != null) {
+            throw new CustomException("同类型下键值已存在: " + row.getDictValue());
+        }
+        return templateMapper.insertTemplateData(row);
     }
     }
 
 
     @Override
     @Override
     public int updateTemplateData(TenantDictTemplateData row) {
     public int updateTemplateData(TenantDictTemplateData row) {
-//        normalizeData(row);
-//        return contextHelper.executeInMaster(() -> templateMapper.updateTemplateData(row));
-        throw new CustomException("Not implemented");
+        normalizeData(row);
+        return templateMapper.updateTemplateData(row);
     }
     }
 
 
     @Override
     @Override
     public void deleteTemplateDataByIds(Long[] dictCodes) {
     public void deleteTemplateDataByIds(Long[] dictCodes) {
-//        contextHelper.runInMaster(() -> templateMapper.deleteTemplateDataByIds(dictCodes));
-        throw new CustomException("Not implemented");
+        templateMapper.deleteTemplateDataByIds(dictCodes);
     }
     }
 
 
     @Override
     @Override
     public List<TenantDictTemplateType> selectAllTemplateTypes() {
     public List<TenantDictTemplateType> selectAllTemplateTypes() {
-//        return contextHelper.executeInMaster(() -> templateMapper.selectTemplateTypeList(new TenantDictTemplateType()));
-        throw new CustomException("Not implemented");
+        return templateMapper.selectTemplateTypeList(new TenantDictTemplateType());
     }
     }
 
 
     @Override
     @Override
     public List<TenantDictTemplateData> selectTemplateDataByType(String dictType) {
     public List<TenantDictTemplateData> selectTemplateDataByType(String dictType) {
-//        return contextHelper.executeInMaster(() -> templateMapper.selectTemplateDataByType(dictType));
-        throw new CustomException("Not implemented");
+        return templateMapper.selectTemplateDataByType(dictType);
     }
     }
 
 
     @Override
     @Override
     public TenantDictTemplateImportResultVo importFromPlatform(TenantDictTemplateImportReq req, String operator) {
     public TenantDictTemplateImportResultVo importFromPlatform(TenantDictTemplateImportReq req, String operator) {
-//        boolean overwrite = Boolean.TRUE.equals(req.getOverwriteExisting());
-//        List<SysDictType> sourceTypes = resolvePlatformSourceTypes(req);
-//        if (sourceTypes.isEmpty()) {
-//            throw new CustomException("未找到可导入的平台字典类型");
-//        }
-//        TenantDictTemplateImportResultVo result = new TenantDictTemplateImportResultVo();
-//        contextHelper.runInMaster(() -> {
-//            ensureTemplateTables();
-//            for (SysDictType sourceType : sourceTypes) {
-//                importOneTypeAndData(
-//                        toTemplateType(sourceType, operator),
-//                        loadPlatformData(sourceType.getDictType()),
-//                        overwrite,
-//                        result,
-//                        operator
-//                );
-//            }
-//        });
-//        result.setMessage(String.format("导入完成:类型+%d 更新%d 跳过%d;数据+%d 更新%d 跳过%d",
-//                result.getTypeAdded(), result.getTypeUpdated(), result.getTypeSkipped(),
-//                result.getDataAdded(), result.getDataUpdated(), result.getDataSkipped()));
-//        return result;
-        throw new CustomException("Not implemented");
+        boolean overwrite = Boolean.TRUE.equals(req.getOverwriteExisting());
+        List<SysDictType> sourceTypes = resolvePlatformSourceTypes(req);
+        if (sourceTypes.isEmpty()) {
+            throw new CustomException("未找到可导入的平台字典类型");
+        }
+        TenantDictTemplateImportResultVo result = new TenantDictTemplateImportResultVo();
+        ensureTemplateTables();
+        for (SysDictType sourceType : sourceTypes) {
+            importOneTypeAndData(
+                    toTemplateType(sourceType, operator),
+                    loadPlatformData(sourceType.getDictType()),
+                    overwrite,
+                    result,
+                    operator
+            );
+        }
+        fillImportMessage(result);
+        return result;
     }
     }
 
 
     @Override
     @Override
-    public TenantDictTemplateImportResultVo importFromTenant(TenantDictTemplateImportReq req, String operator) {
-//        if (req.getTenantId() == null) {
-//            throw new CustomException("请选择租户");
-//        }
-//        if (CollectionUtils.isEmpty(req.getDictTypes())) {
-//            throw new CustomException("请选择要导入的字典类型");
-//        }
-//        boolean overwrite = Boolean.TRUE.equals(req.getOverwriteExisting());
-//        TenantDictTemplateImportResultVo result = new TenantDictTemplateImportResultVo();
-//        for (String dictType : req.getDictTypes()) {
-//            SysDictType query = new SysDictType();
-//            query.setDictType(dictType);
-//            List<SysDictType> types = tenantDictManageService.selectDictTypeList(req.getTenantId(), query);
-//            if (types.isEmpty()) {
-//                result.setTypeSkipped(result.getTypeSkipped() + 1);
-//                continue;
-//            }
-//            SysDictType sourceType = types.get(0);
-//            SysDictData dataQuery = new SysDictData();
-//            dataQuery.setDictType(dictType);
-//            List<SysDictData> dataList = tenantDictManageService.selectDictDataList(req.getTenantId(), dataQuery);
-//            contextHelper.runInMaster(() -> {
-//                ensureTemplateTables();
-//                importOneTypeAndData(toTemplateType(sourceType, operator), dataList, overwrite, result, operator);
-//            });
-//        }
-//        result.setMessage(String.format("导入完成:类型+%d 更新%d 跳过%d;数据+%d 更新%d 跳过%d",
-//                result.getTypeAdded(), result.getTypeUpdated(), result.getTypeSkipped(),
-//                result.getDataAdded(), result.getDataUpdated(), result.getDataSkipped()));
-//        return result;
-        throw new CustomException("Not implemented");
+    public TenantDictTemplateImportResultVo importFromTenantSources(TenantDictTemplateImportReq req,
+                                                                  String operator,
+                                                                  Map<String, SysDictType> typesByDictType,
+                                                                  Map<String, List<SysDictData>> dataByDictType) {
+        if (CollectionUtils.isEmpty(req.getDictTypes())) {
+            throw new CustomException("请选择要导入的字典类型");
+        }
+        boolean overwrite = Boolean.TRUE.equals(req.getOverwriteExisting());
+        TenantDictTemplateImportResultVo result = new TenantDictTemplateImportResultVo();
+        ensureTemplateTables();
+        for (String dictType : req.getDictTypes()) {
+            SysDictType sourceType = typesByDictType.get(dictType);
+            if (sourceType == null) {
+                result.setTypeSkipped(result.getTypeSkipped() + 1);
+                continue;
+            }
+            List<SysDictData> dataList = dataByDictType.getOrDefault(dictType, new ArrayList<>());
+            importOneTypeAndData(toTemplateType(sourceType, operator), dataList, overwrite, result, operator);
+        }
+        fillImportMessage(result);
+        return result;
     }
     }
 
 
     private List<SysDictType> resolvePlatformSourceTypes(TenantDictTemplateImportReq req) {
     private List<SysDictType> resolvePlatformSourceTypes(TenantDictTemplateImportReq req) {
-//        return contextHelper.executeInMaster(() -> {
-//            if (!CollectionUtils.isEmpty(req.getDictIds())) {
-//                List<SysDictType> list = new java.util.ArrayList<>();
-//                for (Long dictId : req.getDictIds()) {
-//                    SysDictType row = sysDictTypeService.selectDictTypeById(dictId);
-//                    if (row != null) {
-//                        list.add(row);
-//                    }
-//                }
-//                return list;
-//            }
-//            if (!CollectionUtils.isEmpty(req.getDictTypes())) {
-//                List<SysDictType> list = new java.util.ArrayList<>();
-//                for (String dictType : req.getDictTypes()) {
-//                    SysDictType row = sysDictTypeService.selectDictTypeByType(dictType);
-//                    if (row != null) {
-//                        list.add(row);
-//                    }
-//                }
-//                return list;
-//            }
-//            return sysDictTypeService.selectDictTypeList(new SysDictType());
-//        });
-        throw new CustomException("Not implemented");
+        if (!CollectionUtils.isEmpty(req.getDictIds())) {
+            List<SysDictType> list = new ArrayList<>();
+            for (Long dictId : req.getDictIds()) {
+                SysDictType row = sysDictTypeService.selectDictTypeById(dictId);
+                if (row != null) {
+                    list.add(row);
+                }
+            }
+            return list;
+        }
+        if (!CollectionUtils.isEmpty(req.getDictTypes())) {
+            List<SysDictType> list = new ArrayList<>();
+            for (String dictType : req.getDictTypes()) {
+                SysDictType row = sysDictTypeService.selectDictTypeByType(dictType);
+                if (row != null) {
+                    list.add(row);
+                }
+            }
+            return list;
+        }
+        return sysDictTypeService.selectDictTypeList(new SysDictType());
     }
     }
 
 
     private List<SysDictData> loadPlatformData(String dictType) {
     private List<SysDictData> loadPlatformData(String dictType) {
@@ -293,6 +253,12 @@ public class TenantDictTemplateServiceImpl implements TenantDictTemplateService
         }
         }
     }
     }
 
 
+    private void fillImportMessage(TenantDictTemplateImportResultVo result) {
+        result.setMessage(String.format("导入完成:类型+%d 更新%d 跳过%d;数据+%d 更新%d 跳过%d",
+                result.getTypeAdded(), result.getTypeUpdated(), result.getTypeSkipped(),
+                result.getDataAdded(), result.getDataUpdated(), result.getDataSkipped()));
+    }
+
     private void normalizeType(TenantDictTemplateType row, boolean isCreate) {
     private void normalizeType(TenantDictTemplateType row, boolean isCreate) {
         if (row.getStatus() == null) {
         if (row.getStatus() == null) {
             row.setStatus(UserConstants.NORMAL);
             row.setStatus(UserConstants.NORMAL);

+ 28 - 0
fs-service/src/main/java/com/fs/tenant/dict/vo/TenantDictSyncInitVo.java

@@ -0,0 +1,28 @@
+package com.fs.tenant.dict.vo;
+
+import com.fs.tenant.dict.domain.TenantDictTemplateData;
+import com.fs.tenant.dict.domain.TenantDictTemplateType;
+import com.fs.tenant.domain.TenantInfo;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 同步任务初始化结果(主库上下文生成,按租户执行时由接口层切库)
+ */
+@Data
+public class TenantDictSyncInitVo {
+
+    private boolean dryRun;
+
+    private String taskNo;
+
+    private String syncMode;
+
+    private List<TenantInfo> tenants;
+
+    private List<TenantDictTemplateType> templateTypes;
+
+    private Map<String, List<TenantDictTemplateData>> dataCache;
+}