1. 智慧社区系统架构设计
1.1 技术选型解析
在智慧社区系统的技术架构设计中,我们采用了当前主流的全栈技术组合。后端选用SpringBoot 2.7.x作为基础框架,主要基于以下考量:
- 自动配置特性大幅减少XML配置,内置Tomcat服务器简化部署
- Starter依赖机制能快速集成MyBatis、Redis等常用组件
- Actuator端点提供完善的系统监控能力
- 与Spring Security天然集成,便于实现权限控制
前端采用Vue3+TypeScript的组合方案,相比传统方案优势明显:
- Composition API使逻辑关注点更集中
- Vite构建工具实现秒级热更新
- Pinia状态管理替代Vuex,TypeScript增强类型安全
- Element Plus组件库提供丰富的UI控件
数据库层选择MySQL 8.0,主要考虑因素包括:
- 社区版完全免费且性能足够
- JSON类型支持便于存储半结构化数据
- 窗口函数等高级特性满足分析需求
- 与MyBatis的兼容性经过充分验证
1.2 前后端分离实践
我们采用严格的前后端分离架构,通过RESTful API进行通信。具体实现要点:
接口规范设计
java复制@RestController
@RequestMapping("/api/notice")
public class NoticeController {
@GetMapping
public Result<List<NoticeVO>> listNotices(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
// 分页查询逻辑
}
@PostMapping
@PreAuthorize("hasRole('ADMIN')")
public Result<Long> createNotice(@Valid @RequestBody NoticeDTO dto) {
// 创建公告逻辑
}
}
跨域解决方案
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.maxAge(3600);
}
}
接口文档生成
xml复制<!-- Swagger依赖 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
2. 核心功能模块实现
2.1 居民信息管理
居民模块采用RBAC权限模型,关键实现包括:
数据层设计
java复制public interface ResidentMapper {
@Select("SELECT * FROM resident_info WHERE resident_id = #{id}")
@Results({
@Result(property = "community", column = "community_id",
one = @One(select = "com.example.mapper.CommunityMapper.selectById"))
})
Resident selectWithDetail(Long id);
}
业务逻辑处理
java复制@Service
@RequiredArgsConstructor
public class ResidentService {
private final ResidentMapper residentMapper;
private final PasswordEncoder passwordEncoder;
public void register(ResidentRegisterDTO dto) {
Resident resident = new Resident();
BeanUtils.copyProperties(dto, resident);
resident.setPassword(passwordEncoder.encode(dto.getPassword()));
resident.setRegisterTime(LocalDateTime.now());
residentMapper.insert(resident);
}
}
2.2 智能门禁集成
门禁系统对接方案:
硬件通信协议
java复制public class DoorAccessClient {
private final WebClient webClient;
public Mono<String> openDoor(Long deviceId, String userId) {
return webClient.post()
.uri("/api/device/{id}/open", deviceId)
.bodyValue(new OpenRequest(userId))
.retrieve()
.bodyToMono(String.class);
}
}
进出记录存储
sql复制CREATE TABLE access_record (
record_id BIGINT PRIMARY KEY AUTO_INCREMENT,
resident_id BIGINT NOT NULL,
device_id VARCHAR(50) NOT NULL,
access_time DATETIME NOT NULL,
access_type TINYINT COMMENT '0:进入 1:离开',
face_image VARCHAR(255),
INDEX idx_resident (resident_id),
INDEX idx_time (access_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 系统安全设计
3.1 认证授权方案
采用JWT+Spring Security的安全方案:
安全配置类
java复制@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
JWT工具类
java复制public class JwtUtils {
private static final String SECRET = "your-256-bit-secret";
private static final long EXPIRATION = 86400000; // 24小时
public static String generateToken(UserDetails user) {
return Jwts.builder()
.setSubject(user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(SignatureAlgorithm.HS256, SECRET)
.compact();
}
}
3.2 数据安全措施
敏感数据加密
java复制@Converter
public class PhoneEncryptConverter implements AttributeConverter<String, String> {
private final String KEY = "your-encryption-key";
@Override
public String convertToDatabaseColumn(String attribute) {
// AES加密实现
}
@Override
public String convertToEntityAttribute(String dbData) {
// AES解密实现
}
}
SQL注入防护
java复制@Repository
public class NoticeRepository {
private final JdbcTemplate jdbcTemplate;
public List<Notice> search(String keyword) {
String sql = "SELECT * FROM notice WHERE title LIKE CONCAT('%',?,'%')";
return jdbcTemplate.query(sql,
new BeanPropertyRowMapper<>(Notice.class),
keyword);
}
}
4. 性能优化实践
4.1 缓存策略设计
多级缓存方案
java复制@Service
@CacheConfig(cacheNames = "notice")
@RequiredArgsConstructor
public class NoticeService {
private final NoticeMapper noticeMapper;
private final RedisTemplate<String, Object> redisTemplate;
@Cacheable(key = "#id")
public Notice getById(Long id) {
return noticeMapper.selectById(id);
}
@CacheEvict(key = "#notice.noticeId")
public void updateNotice(Notice notice) {
noticeMapper.updateById(notice);
}
}
缓存一致性方案
java复制@EventListener
public void handleNoticeUpdate(NoticeUpdateEvent event) {
String key = "notice::" + event.getNoticeId();
redisTemplate.delete(key);
redisTemplate.opsForValue().set(key, event.getNotice());
}
4.2 数据库优化
索引设计原则
sql复制ALTER TABLE service_request
ADD INDEX idx_status_resident (service_status, resident_id);
ALTER TABLE access_record
ADD INDEX idx_device_time (device_id, access_time);
查询优化示例
java复制@Select("<script>" +
"SELECT * FROM resident_info " +
"<where>" +
" <if test='name != null'>AND resident_name LIKE CONCAT('%',#{name},'%')</if>" +
" <if test='communityId != null'>AND community_id = #{communityId}</if>" +
"</where>" +
"ORDER BY register_time DESC " +
"LIMIT #{offset}, #{size}" +
"</script>")
List<Resident> selectByCondition(@Param("name") String name,
@Param("communityId") Long communityId,
@Param("offset") int offset,
@Param("size") int size);
5. 部署与监控
5.1 容器化部署
Dockerfile配置
dockerfile复制FROM openjdk:17-jdk-slim
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
docker-compose编排
yaml复制version: '3'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- redis
- mysql
redis:
image: redis:6
ports:
- "6379:6379"
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: community
ports:
- "3306:3306"
5.2 监控方案
Prometheus配置
yaml复制spring:
application:
name: community-service
metrics:
export:
prometheus:
enabled: true
tags:
service: ${spring.application.name}
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
distribution:
percentiles-histogram:
http.server.requests: true
日志收集方案
xml复制<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.2</version>
</dependency>
xml复制<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>logstash:5044</destination>
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
6. 开发经验与避坑指南
6.1 前后端协作规范
接口约定原则
- 使用Swagger UI维护实时接口文档
- 响应统一采用Result包装器:
typescript复制interface Result<T> {
code: number;
message: string;
data: T;
timestamp: number;
}
- 错误码标准化:
java复制public enum ErrorCode {
SUCCESS(0, "成功"),
PARAM_ERROR(1001, "参数错误"),
AUTH_FAILED(2001, "认证失败");
private final int code;
private final String message;
// ...
}
6.2 典型问题解决方案
MyBatis批量插入优化
java复制@Insert("<script>" +
"INSERT INTO resident_info (resident_name, resident_phone) VALUES " +
"<foreach collection='list' item='item' separator=','>" +
" (#{item.name}, #{item.phone})" +
"</foreach>" +
"</script>")
void batchInsert(@Param("list") List<Resident> residents);
Vue3性能优化
typescript复制// 使用v-memo优化列表渲染
<template>
<div v-for="item in list" v-memo="[item.id]">
{{ item.name }}
</div>
</template>
// 使用shallowRef减少响应式开销
const largeList = shallowRef([])
事务处理注意事项
java复制@Service
@RequiredArgsConstructor
public class PaymentService {
private final ResidentMapper residentMapper;
private final PaymentMapper paymentMapper;
@Transactional(rollbackFor = Exception.class)
public void processPayment(PaymentDTO dto) {
// 扣减余额
residentMapper.deductBalance(dto.getResidentId(), dto.getAmount());
// 记录支付
Payment payment = new Payment();
BeanUtils.copyProperties(dto, payment);
paymentMapper.insert(payment);
// 其他业务逻辑
}
}
在实际开发中,我们发现分页查询的性能对用户体验影响很大。经过测试,采用以下优化方案后,万级数据量的分页响应时间从1200ms降低到200ms以内:
- 避免使用
SELECT *,只查询必要字段 - 对排序字段建立复合索引
- 使用游标分页替代传统LIMIT分页:
sql复制-- 传统分页
SELECT * FROM table ORDER BY id LIMIT 10000, 20;
-- 优化方案
SELECT * FROM table WHERE id > 10000 ORDER BY id LIMIT 20;
另一个值得注意的问题是Vue3的响应式系统虽然强大,但不合理的使用会导致性能下降。我们总结出以下最佳实践:
- 对于大型列表(>1000项),使用shallowRef替代ref
- 避免在v-for中使用复杂表达式
- 使用computed属性缓存计算结果
- 合理使用v-once和v-memo指令
在权限控制方面,我们最初采用前端路由权限方案,但发现存在安全隐患。最终改进为:
- 前端根据角色动态生成菜单
- 后端接口进行细粒度权限校验
- 敏感操作增加二次认证
- 所有API请求携带权限上下文
系统上线后,通过APM工具发现MySQL连接池成为瓶颈。调整以下参数后,TPS从150提升到420:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
最后分享一个调试技巧:当遇到难以复现的并发问题时,可以使用Arthas进行线上诊断:
bash复制# 监控方法调用
watch com.example.service.*Service * '{params,returnObj,throwExp}' -x 3
# 追踪调用链路
trace com.example.controller.*Controller * -j