1. 项目概述:经方药食两用服务平台的技术架构
这个基于Java SpringBoot+Vue3+MyBatis的经方药食两用服务平台,是一个典型的前后端分离架构的中医药健康类应用。系统采用MySQL作为主数据库,主要解决传统中医药材与食疗方案数字化管理的问题。我在实际开发中发现,这类系统既要处理复杂的药材配伍关系,又要考虑现代用户友好的交互体验,技术选型上需要兼顾传统业务逻辑和现代Web开发趋势。
平台核心功能包括:经方药材库管理、药食配伍智能推荐、用户个性化方案生成等模块。前端采用Vue3的组合式API开发,后端基于SpringBoot的模块化设计,通过RESTful API进行数据交互。这种架构既保证了后端业务逻辑的严谨性(特别是中医药特有的配伍禁忌校验),又能提供流畅的前端用户体验。
2. 技术栈深度解析与选型考量
2.1 后端技术组合:SpringBoot+MyBatis的黄金搭档
选择SpringBoot作为后端框架主要基于三个实际考量:
- 自动配置特性大幅减少了XML配置,特别适合需要快速迭代的健康类应用
- 内置Tomcat容器简化部署,这对需要频繁更新药材数据库的场景很关键
- Starter依赖机制能灵活集成Redis等缓存组件,应对高并发的方案查询请求
MyBatis的选用则是因为经方数据关系的复杂性:
xml复制<!-- 典型的中药材关联查询映射示例 -->
<select id="findHerbsWithCompatibilities" resultMap="herbDetailMap">
SELECT h.*, c.compatible_herb_id
FROM herbs h LEFT JOIN herb_compatibility c ON h.id = c.herb_id
WHERE h.category = #{category}
</select>
这种灵活的SQL编写方式特别适合处理中医药材的多层级关联关系。我们在项目中还使用了MyBatis-Plus的Lambda查询,简化了基础CRUD操作:
java复制// 基于症状的药材筛选示例
List<Herb> herbs = herbMapper.selectList(Wrappers.<Herb>lambdaQuery()
.like(Herb::getEffects, symptom)
.orderByDesc(Herb::getUsageFrequency));
2.2 前端演进:为什么选择Vue3而非React
Vue3的组合式API在开发复杂交互的健康管理界面时展现出独特优势:
- Composition API更好地组织与药食配伍相关的业务逻辑
- 更小的打包体积(经实测gzip后约20KB),这对移动端用户很重要
- 更好的TypeScript支持,配合Volar插件提升开发效率
典型方案推荐页面的组件结构:
javascript复制// 药食配伍推荐逻辑封装
const useRecommendation = () => {
const ingredients = ref([])
const incompatibleItems = computed(() => {
return checkIncompatibles(ingredients.value)
})
const addIngredient = (herb) => {
if(!ingredients.value.some(item => item.id === herb.id)) {
ingredients.value.push(herb)
}
}
return { ingredients, incompatibleItems, addIngredient }
}
3. 核心业务模块实现细节
3.1 药食配伍智能推荐引擎
中医药领域的特殊需求在于:
- 需要处理"十八反十九畏"等配伍禁忌规则
- 要考虑药材的性味归经(寒热温凉、升降浮沉)
- 需支持模糊匹配(如"气虚"可匹配黄芪、党参等)
我们设计的推荐算法包含三层过滤:
- 禁忌规则过滤(硬性排除)
- 性味协调评分(0-100分)
- 功效协同加权(根据主症加权)
对应的数据库设计关键表:
sql复制CREATE TABLE herb_compatibility (
id BIGINT PRIMARY KEY,
herb_id BIGINT NOT NULL,
compatible_herb_id BIGINT NOT NULL,
compatibility_type TINYINT COMMENT '1-相须 2-相使 3-相畏 4-相杀',
description VARCHAR(255),
FOREIGN KEY (herb_id) REFERENCES herbs(id),
FOREIGN KEY (compatible_herb_id) REFERENCES herbs(id)
);
3.2 个性化方案生成系统
基于用户体质辨识的算法流程:
- 用户完成中医问卷(约30道题)
- 后端计算体质倾向得分(如阳虚质、痰湿质)
- 结合当前季节气候因素调整
- 从方案库匹配基准方案
- 根据用户特殊需求微调
SpringBoot中的服务层实现:
java复制public Program generateProgram(UserTrait trait, Season season) {
// 1. 获取基准方案
Program template = programRepo.findByTraitType(trait.getPrimaryType());
// 2. 季节调整
adjustBySeason(template, season);
// 3. 个性化替换
if(trait.hasSpecialCondition()) {
replaceForSpecialConditions(template, trait);
}
// 4. 保存用户版本
return programRepo.save(template.toUserVersion());
}
4. 前后端分离架构的实践要点
4.1 接口设计规范
我们制定了严格的RESTful接口规范:
- 版本控制:/api/v1/herbs
- 状态码标准化:
- 200 成功
- 400 参数错误
- 403 权限不足
- 422 业务规则冲突(如配伍禁忌)
典型响应体结构:
json复制{
"code": 200,
"data": {
"id": 123,
"name": "黄芪",
"compatibles": [
{"id": 124, "name": "当归", "type": 1}
]
},
"message": "success"
}
4.2 跨域与安全方案
针对健康数据的敏感性,我们实施了:
- JWT认证(含refreshToken机制)
- 敏感字段加密(如用户体质信息)
- 请求参数签名防篡改
- 严格的CORS策略
SpringSecurity配置示例:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.addFilter(new JwtAuthFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
5. 性能优化实战记录
5.1 药材搜索的Elasticsearch整合
原生MySQL搜索的局限:
- LIKE查询无法满足"归经+功效"的复合搜索
- 通配符搜索导致全表扫描
我们的优化方案:
- 使用Logstash定期从MySQL同步数据到ES
- 构建包含以下字段的索引:
- 药材名称(分词+拼音)
- 功效(分词)
- 性味(不分词)
- 归经(不分词)
搜索API示例:
java复制@GetMapping("/herbs/search")
public List<Herb> searchHerbs(@RequestParam String query) {
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery(query, "name","effects"))
.build();
return elasticsearchTemplate.search(searchQuery, Herb.class)
.get().map(SearchHit::getContent).collect(Collectors.toList());
}
5.2 方案缓存策略
基于Spring Cache的二级缓存设计:
- 本地Caffeine缓存(高频访问数据)
- 最大500条
- 过期时间5分钟
- Redis分布式缓存(共享数据)
- 过期时间2小时
- 主动刷新机制
配置示例:
java复制@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager();
manager.setCaffeine(Caffeine.newBuilder()
.maximumSize(500)
.expireAfterWrite(5, TimeUnit.MINUTES));
return manager;
}
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(2));
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}
6. 开发环境与工具链
6.1 高效协作配置
我们采用的工具组合:
- 后端:
- JDK17 + Lombok(减少样板代码)
- SpringBoot DevTools(热部署)
- MapStruct(DTO转换)
- 前端:
- Vite(极速启动)
- Pinia(状态管理)
- Element Plus(UI组件)
- 数据库:
- Flyway(迁移管理)
- Druid(连接池监控)
关键pom.xml依赖:
xml复制<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.3.Final</version>
</dependency>
</dependencies>
6.2 代码质量保障
实施的质量门禁:
- SonarQube静态扫描(阻断严重问题)
- SpotBugs检查潜在缺陷
- 前端ESLint + Prettier统一风格
- 单元测试覆盖率要求:
- Service层 ≥80%
- 核心算法类 ≥95%
测试示例:
java复制@Test
void testHerbCompatibilityCheck() {
Herb herb1 = herbRepository.findById(1L).orElseThrow();
Herb herb2 = herbRepository.findById(2L).orElseThrow();
CompatibilityResult result = compatibilityService.check(herb1, herb2);
assertThat(result.isCompatible()).isFalse();
assertThat(result.getConflictType()).isEqualTo(ConflictType.CONTRARY);
}
7. 典型问题排查实录
7.1 MyBatis关联查询N+1问题
现象:获取包含配伍关系的药材列表时,响应时间随数据量线性增长
根本原因:循环中触发延迟加载
解决方案:
- 使用
一次性加载 - 添加@BatchSize注解
- 启用二级缓存
优化后的映射:
xml复制<resultMap id="herbWithCompatibilities" type="Herb">
<id property="id" column="id"/>
<collection property="compatibles" ofType="CompatibleHerb"
select="findCompatiblesByHerbId" column="id"
fetchType="eager"/>
</resultMap>
7.2 Vue3组件重复渲染问题
现象:方案编辑页面频繁刷新导致输入框失焦
排查过程:
- 使用Vue Devtools检查props变化
- 发现父组件不必要的响应式数据更新
- 确认是deep watch导致的
修复方案:
javascript复制// 错误写法
watch(ingredients, (newVal) => {
// 深度监听导致任何嵌套变化都触发
}, { deep: true })
// 正确写法
watch(() => [...ingredients.value], (newVal) => {
// 仅当数组引用变化时触发
})
8. 部署架构与监控方案
8.1 生产环境部署
我们采用的云原生方案:
- 容器化:
- 后端:JDK17 Alpine镜像(约150MB)
- 前端:Nginx镜像(约20MB)
- 编排:
- Kubernetes Deployment
- HPA自动扩缩容
- 数据库:
- AWS RDS MySQL
- 只读副本分担查询压力
关键Dockerfile配置:
dockerfile复制# 后端Dockerfile示例
FROM eclipse-temurin:17-jre-alpine
COPY target/app.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
8.2 监控体系搭建
实施的监控维度:
- 基础指标:
- Prometheus + Grafana(JVM/容器指标)
- 业务指标:
- 方案生成成功率
- 配伍检查响应时间
- 日志分析:
- ELK收集错误日志
- 关键操作审计日志
SpringBoot监控配置:
yaml复制management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
metrics:
tags:
application: ${spring.application.name}
9. 项目演进方向
9.1 技术债清理计划
已识别的主要改进点:
- 替换JPA的部分使用为MyBatis(复杂查询场景)
- 前端组件库按需加载
- 引入GraalVM原生镜像编译
9.2 业务功能扩展
规划中的增强功能:
- 药材图片AI识别
- 用户健康数据趋势分析
- 第三方中医师协作平台
技术预研重点:
java复制// 正在评估的AI集成方案
public interface HerbRecognizer {
@PostMapping("/recognize")
RecognizeResult recognize(@RequestParam MultipartFile image);
@PostMapping("/verify")
VerificationResult verify(@RequestBody VerificationRequest request);
}
10. 团队协作经验总结
10.1 接口文档管理
我们采用的方案:
- Swagger UI作为基础文档
- 补充Markdown格式的领域说明
- 使用Postman制作接口示例集
Swagger配置示例:
java复制@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info().title("经方平台API")
.version("1.0")
.description("包含药食配伍核心业务接口"))
.externalDocs(new ExternalDocumentation()
.description("药材数据字典")
.url("/docs/herb-dictionary.md"));
}
10.2 分支策略实践
优化的Git工作流:
- 功能分支:feature/功能名
- 发布分支:release/日期
- 热修复:hotfix/问题单号
配合的CI流程:
yaml复制# GitLab CI示例
stages:
- test
- build
- deploy
unit-test:
stage: test
script:
- mvn test
- npm run test:unit
docker-build:
stage: build
only:
- master
script:
- docker build -t registry.example.com/app:$CI_COMMIT_SHA .
