diff --git a/zhyc-admin/src/main/java/com/zhyc/Event/FarmLoginEvent.java b/zhyc-admin/src/main/java/com/zhyc/Event/FarmLoginEvent.java new file mode 100644 index 0000000..b345a01 --- /dev/null +++ b/zhyc-admin/src/main/java/com/zhyc/Event/FarmLoginEvent.java @@ -0,0 +1,24 @@ +package com.zhyc.Event; + +import lombok.Data; + +@Data +public class FarmLoginEvent { + + private final String farmCode; + private final Long userId; + + public FarmLoginEvent(String farmCode, Long userId) { + this.farmCode = farmCode; + this.userId = userId; + } + + public String getFarmCode() { + return farmCode; + } + + public Long getUserId() { + return userId; + } +} + diff --git a/zhyc-admin/src/main/java/com/zhyc/Routing/Filter/DataSourceRoutingFilter.java b/zhyc-admin/src/main/java/com/zhyc/Routing/Filter/DataSourceRoutingFilter.java new file mode 100644 index 0000000..1534502 --- /dev/null +++ b/zhyc-admin/src/main/java/com/zhyc/Routing/Filter/DataSourceRoutingFilter.java @@ -0,0 +1,43 @@ +package com.zhyc.Routing.Filter; + +import com.zhyc.framework.config.routing.DataSourceKeys; +import com.zhyc.framework.datasource.DynamicDataSourceContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +@Slf4j +@Order(value = -100) +public class DataSourceRoutingFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + + String uri = request.getRequestURI(); + + try { + if (uri.startsWith("/app") || uri.startsWith("/base") || uri.startsWith("/biosafety") || uri.startsWith("/common") + || uri.startsWith("/dairyProducts") || uri.startsWith("/enums") || uri.startsWith("/feed") || uri.startsWith("/frozen") || uri.startsWith("/produce") + || uri.startsWith("sale") || uri.startsWith("/stock") || uri.startsWith("/work") || uri.startsWith("/sheepfold_management")) { + log.debug("业务请求 : BUSINESS : {}", uri); + DynamicDataSourceContextHolder.setDataSourceType(DataSourceKeys.FARM_1); + } else { + log.debug("系统请求 : SYSTEM : {}", uri); + DynamicDataSourceContextHolder.setDataSourceType(DataSourceKeys.SYSTEM); + } + + filterChain.doFilter(request, response); + } finally { + // 业务结束后清楚数据源-防止线程复用导致错误 + DynamicDataSourceContextHolder.clearDataSourceType(); + } + } +} diff --git a/zhyc-admin/src/main/java/com/zhyc/web/mvc/MvcConfig.java b/zhyc-admin/src/main/java/com/zhyc/web/mvc/MvcConfig.java new file mode 100644 index 0000000..1adea86 --- /dev/null +++ b/zhyc-admin/src/main/java/com/zhyc/web/mvc/MvcConfig.java @@ -0,0 +1,17 @@ +package com.zhyc.web.mvc; + +import com.zhyc.Routing.Filter.DataSourceRoutingFilter; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MvcConfig { + @Bean + public FilterRegistrationBean myFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new DataSourceRoutingFilter()); + registrationBean.addUrlPatterns("/*"); + return registrationBean; + } +} diff --git a/zhyc-admin/src/main/resources/application-druid.yml b/zhyc-admin/src/main/resources/application-druid.yml index 3c517de..5b9cfd9 100644 --- a/zhyc-admin/src/main/resources/application-druid.yml +++ b/zhyc-admin/src/main/resources/application-druid.yml @@ -9,16 +9,15 @@ spring: # url: jdbc:mysql://localhost:3306/zhyc?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 # username: root # password: 123456 - url: jdbc:mysql://118.182.97.76:3306/zhyc?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + url: jdbc:mysql://118.182.97.76:3306/zhyc_sys?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: zhyc password: yszh123 # 从库数据源 - slave: + farm01: # 从数据源开关/默认关闭 - enabled: false - url: - username: - password: + url: jdbc:mysql://118.182.97.76:3306/zhyc_sheep01?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: zhyc + password: yszh123 # 初始连接数 initialSize: 5 # 最小连接池数量 diff --git a/zhyc-framework/src/main/java/com/zhyc/framework/config/DruidConfig.java b/zhyc-framework/src/main/java/com/zhyc/framework/config/DruidConfig.java index 606a686..fc1785c 100644 --- a/zhyc-framework/src/main/java/com/zhyc/framework/config/DruidConfig.java +++ b/zhyc-framework/src/main/java/com/zhyc/framework/config/DruidConfig.java @@ -9,6 +9,8 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; @@ -19,7 +21,6 @@ import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; import com.alibaba.druid.util.Utils; -import com.zhyc.common.enums.DataSourceType; import com.zhyc.common.utils.spring.SpringUtils; import com.zhyc.framework.config.properties.DruidProperties; import com.zhyc.framework.datasource.DynamicDataSource; @@ -39,6 +40,14 @@ public class DruidConfig DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); return druidProperties.dataSource(dataSource); } + @Bean + @ConfigurationProperties("spring.datasource.druid.farm01") + public DataSource farm1DataSource(DruidProperties druidProperties) { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean @ConfigurationProperties("spring.datasource.druid.slave") @@ -51,12 +60,16 @@ public class DruidConfig @Bean(name = "dynamicDataSource") @Primary - public DynamicDataSource dataSource(DataSource masterDataSource) - { + public DataSource dynamicDataSource( + @Qualifier("masterDataSource") DataSource systemDataSource, + @Qualifier("farm1DataSource") DataSource farm1DataSource) { + + // 存储数据源路由 Map targetDataSources = new HashMap<>(); - targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); - setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); - return new DynamicDataSource(masterDataSource, targetDataSources); + targetDataSources.put("system", systemDataSource); + targetDataSources.put("farm01", farm1DataSource); + + return new DynamicDataSource(systemDataSource, targetDataSources); } /** diff --git a/zhyc-framework/src/main/java/com/zhyc/framework/config/routing/DataSourceKeys.java b/zhyc-framework/src/main/java/com/zhyc/framework/config/routing/DataSourceKeys.java new file mode 100644 index 0000000..7f3a0c7 --- /dev/null +++ b/zhyc-framework/src/main/java/com/zhyc/framework/config/routing/DataSourceKeys.java @@ -0,0 +1,8 @@ +package com.zhyc.framework.config.routing; + +public interface DataSourceKeys { + String SYSTEM = "system"; + String FARM_1 = "farm01"; + String FARM_2 = "farm02"; +} + diff --git a/zhyc-framework/src/main/java/com/zhyc/framework/web/service/SysLoginService.java b/zhyc-framework/src/main/java/com/zhyc/framework/web/service/SysLoginService.java index 581b332..8be40ef 100644 --- a/zhyc-framework/src/main/java/com/zhyc/framework/web/service/SysLoginService.java +++ b/zhyc-framework/src/main/java/com/zhyc/framework/web/service/SysLoginService.java @@ -1,6 +1,8 @@ package com.zhyc.framework.web.service; import javax.annotation.Resource; + +import com.zhyc.framework.datasource.DynamicDataSourceContextHolder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; diff --git a/zhyc-module/src/main/java/com/zhyc/module/feed/controller/SgFeedListController.java b/zhyc-module/src/main/java/com/zhyc/module/feed/controller/SgFeedListController.java index db39029..f02277f 100644 --- a/zhyc-module/src/main/java/com/zhyc/module/feed/controller/SgFeedListController.java +++ b/zhyc-module/src/main/java/com/zhyc/module/feed/controller/SgFeedListController.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import javax.servlet.http.HttpServletResponse; import com.zhyc.module.feed.service.impl.SgFeedListServiceImpl; @@ -39,7 +40,7 @@ public class SgFeedListController extends BaseController { private final ISgFeedListService sgFeedListService; - public static boolean refresh = true; + public static final AtomicBoolean refresh = new AtomicBoolean(true); @Autowired public SgFeedListController(ISgFeedListService sgFeedListService) { @@ -57,9 +58,8 @@ public class SgFeedListController extends BaseController { 刷新缓存 当配方管理表出现更新 或 饲喂计划表出现增删改时会将refresh置为true 通知此处进行刷新 */ - if (refresh) { - sgFeedListService.SyncFeedList(); - refresh = false; + if (refresh.compareAndSet(true, false)) { + sgFeedListService.syncFeedListSafely(); } startPage(); List list = sgFeedListService.selectSgFeedListList(sgFeedList); diff --git a/zhyc-module/src/main/java/com/zhyc/module/feed/controller/SgFeedPlanController.java b/zhyc-module/src/main/java/com/zhyc/module/feed/controller/SgFeedPlanController.java index 39bde43..8bbb284 100644 --- a/zhyc-module/src/main/java/com/zhyc/module/feed/controller/SgFeedPlanController.java +++ b/zhyc-module/src/main/java/com/zhyc/module/feed/controller/SgFeedPlanController.java @@ -83,7 +83,7 @@ public class SgFeedPlanController extends BaseController { // 计算其他字段值 setPlan(sgFeedPlan); // 通知配料清单刷新数据 - SgFeedListController.refresh = true; + SgFeedListController.refresh.set(true); return toAjax(sgFeedPlanService.insertSgFeedPlan(sgFeedPlan)); } @@ -97,7 +97,7 @@ public class SgFeedPlanController extends BaseController { // 根据修改后的值重新计算 setPlan(sgFeedPlan); // 通知配料清单刷新数据 - SgFeedListController.refresh = true; + SgFeedListController.refresh.set(true); return toAjax(sgFeedPlanService.updateSgFeedPlan(sgFeedPlan)); } @@ -109,7 +109,7 @@ public class SgFeedPlanController extends BaseController { @DeleteMapping("/{createDates}") public AjaxResult remove(@PathVariable Date[] createDates) { // 通知配料清单刷新数据 - SgFeedListController.refresh = true; + SgFeedListController.refresh.set(true); return toAjax(sgFeedPlanService.deleteSgFeedPlanByCreateDates(createDates)); } diff --git a/zhyc-module/src/main/java/com/zhyc/module/feed/controller/SgFormulaManagementController.java b/zhyc-module/src/main/java/com/zhyc/module/feed/controller/SgFormulaManagementController.java index 9284c7a..e2cdb44 100644 --- a/zhyc-module/src/main/java/com/zhyc/module/feed/controller/SgFormulaManagementController.java +++ b/zhyc-module/src/main/java/com/zhyc/module/feed/controller/SgFormulaManagementController.java @@ -93,8 +93,7 @@ public class SgFormulaManagementController extends BaseController { @PostMapping @Transactional(rollbackFor = Exception.class) public AjaxResult add(@RequestBody SgFormulaManagement sgFormulaManagement) { - if (null == sgFormulaManagement) - throw new RuntimeException("ERROR: 数据为空"); + if (null == sgFormulaManagement) throw new RuntimeException("ERROR: 数据为空"); if (Objects.equals(sgFormulaManagement.getBatchId(), "0")) { SgFormulaManagement exist = sgFormulaManagementService.selectSgFormulaManagementByFormulaId(sgFormulaManagement.getFormulaId()); if (exist != null) { @@ -145,7 +144,7 @@ public class SgFormulaManagementController extends BaseController { } // 通知配料清单刷新数据 - SgFeedListController.refresh = true; + SgFeedListController.refresh.set(true); return toAjax(sgFormulaManagementService.updateSgFormulaManagement(sgFormulaManagement)); } @@ -172,7 +171,7 @@ public class SgFormulaManagementController extends BaseController { sgFormulaManagement.setBatchId(batchId); // 通知配料清单刷新数据 - SgFeedListController.refresh = true; + SgFeedListController.refresh.set(true); return toAjax(sgFormulaManagementService.deleteSgFormulaManagement(sgFormulaManagement)); } } diff --git a/zhyc-module/src/main/java/com/zhyc/module/feed/service/ISgFeedListService.java b/zhyc-module/src/main/java/com/zhyc/module/feed/service/ISgFeedListService.java index b3c5a5f..35100d2 100644 --- a/zhyc-module/src/main/java/com/zhyc/module/feed/service/ISgFeedListService.java +++ b/zhyc-module/src/main/java/com/zhyc/module/feed/service/ISgFeedListService.java @@ -59,5 +59,7 @@ public interface ISgFeedListService { */ int deleteSgFeedListById(Long id); + void syncFeedListSafely(); + void SyncFeedList(); } diff --git a/zhyc-module/src/main/java/com/zhyc/module/feed/service/impl/SgFeedListServiceImpl.java b/zhyc-module/src/main/java/com/zhyc/module/feed/service/impl/SgFeedListServiceImpl.java index 894ff47..68bea4d 100644 --- a/zhyc-module/src/main/java/com/zhyc/module/feed/service/impl/SgFeedListServiceImpl.java +++ b/zhyc-module/src/main/java/com/zhyc/module/feed/service/impl/SgFeedListServiceImpl.java @@ -11,6 +11,10 @@ import org.springframework.stereotype.Service; import com.zhyc.module.feed.mapper.SgFeedListMapper; import com.zhyc.module.feed.domain.SgFeedList; import com.zhyc.module.feed.service.ISgFeedListService; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import static com.zhyc.module.feed.controller.SgFeedListController.refresh; /** * 配料清单Service业务层处理 @@ -30,7 +34,15 @@ public class SgFeedListServiceImpl implements ISgFeedListService { this.sgFeedListMapper = sgFeedListMapper; this.sgFormulaManagementService = sgFormulaManagementService; this.sgFeedPlanService = sgFeedPlanService; - // 构造时将数据库初始数据写入缓存 + // 第一次请求时更新缓存 + refresh.set(true); + } + + /** + * 在数据源已切换后调用 + */ + public synchronized void initCacheIfNecessary() { + // 初次访问时将数据库初始数据写入缓存 List feedListsFromDataBase = this.selectSgFeedListList(new SgFeedList()); for (SgFeedList sgFeedListItem : feedListsFromDataBase) { String key = sgFeedListItem.getFormulaId() + "_" + sgFeedListItem.getFormulaBatchId() + "_" + sgFeedListItem.getDeployDate(); @@ -157,6 +169,16 @@ public class SgFeedListServiceImpl implements ISgFeedListService { } } + public void syncFeedListSafely() { + try { + SyncFeedList(); + } catch (Exception e) { + refresh.set(true); // 初始化失败,下次还能重试 + throw e; + } + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) public void SyncFeedList() { HashMap cacheTemp = new HashMap<>(sgFeedListMap); // 清空旧缓存 diff --git a/zhyc-module/src/main/resources/mapper/feed/SgMaterialMapper.xml b/zhyc-module/src/main/resources/mapper/feed/SgMaterialMapper.xml index c593ff4..c382acd 100644 --- a/zhyc-module/src/main/resources/mapper/feed/SgMaterialMapper.xml +++ b/zhyc-module/src/main/resources/mapper/feed/SgMaterialMapper.xml @@ -22,7 +22,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and is_granular = #{isGranular} - +