# 系统配置与审计通知中心实施计划

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** 完成系统配置（字典管理、系统参数）、审计中心（登录日志、操作日志、异常追踪）、通知中心（系统公告、消息推送WebSocket）、文件管理（上传/下载/预览）的数据库持久化与功能完善

**Architecture:** 基于Spring R2DBC响应式架构，遵循现有分层模式（Handler->Service->Repository->Entity），使用H2/PostgreSQL + Caffeine缓存，消息推送采用WebSocket

**Tech Stack:** Spring WebFlux, Spring Data R2DBC, H2/PostgreSQL, Caffeine, WebSocket, Lombok

---

## 第一阶段：数据层基础设施建设

### Task 1: 创建数据库表SQL脚本

**Files:**
- Modify: `docs/sql/init.sql` (创建以下表)

**Step 1: 编写SQL脚本**

```sql
-- 字典类型表
CREATE TABLE IF NOT EXISTS sys_dict_type (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    dict_name VARCHAR(100) NOT NULL COMMENT '字典名称',
    dict_type VARCHAR(100) NOT NULL UNIQUE COMMENT '字典类型',
    status VARCHAR(1) DEFAULT '0' COMMENT '状态（0正常 1停用）',
    remark VARCHAR(500) COMMENT '备注',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP
);

-- 字典数据表
CREATE TABLE IF NOT EXISTS sys_dict_data (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    dict_sort INT DEFAULT 0 COMMENT '字典排序',
    dict_label VARCHAR(100) NOT NULL COMMENT '字典标签',
    dict_value VARCHAR(100) NOT NULL COMMENT '字典键值',
    dict_type VARCHAR(100) NOT NULL COMMENT '字典类型',
    css_class VARCHAR(100) COMMENT '样式属性',
    list_class VARCHAR(100) COMMENT '表格回显样式',
    is_default VARCHAR(1) DEFAULT 'N' COMMENT '是否默认（Y是 N否）',
    status VARCHAR(1) DEFAULT '0' COMMENT '状态（0正常 1停用）',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP
);

-- 系统配置表
CREATE TABLE IF NOT EXISTS sys_config (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    config_name VARCHAR(100) NOT NULL COMMENT '配置名称',
    config_key VARCHAR(100) NOT NULL UNIQUE COMMENT '配置键名',
    config_value VARCHAR(500) NOT NULL COMMENT '配置值',
    config_type VARCHAR(1) DEFAULT 'N' COMMENT '系统内置（Y是 N否）',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP
);

-- 登录日志表
CREATE TABLE IF NOT EXISTS sys_login_log (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) COMMENT '用户名',
    ip VARCHAR(50) COMMENT 'IP地址',
    location VARCHAR(255) COMMENT '登录位置',
    browser VARCHAR(50) COMMENT '浏览器类型',
    os VARCHAR(50) COMMENT '操作系统',
    status VARCHAR(1) COMMENT '登录状态（0成功 1失败）',
    message VARCHAR(255) COMMENT '提示消息',
    login_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 异常日志表
CREATE TABLE IF NOT EXISTS sys_exception_log (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) COMMENT '用户名',
    title VARCHAR(100) COMMENT '异常标题',
    exception_name VARCHAR(100) COMMENT '异常名称',
    method_name VARCHAR(100) COMMENT '方法名称',
    method_params TEXT COMMENT '方法参数',
    exception_msg TEXT COMMENT '异常信息',
    exception_stack TEXT COMMENT '堆栈信息',
    ip VARCHAR(50) COMMENT 'IP地址',
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 系统公告表
CREATE TABLE IF NOT EXISTS sys_notice (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    notice_title VARCHAR(100) NOT NULL COMMENT '公告标题',
    notice_type VARCHAR(1) DEFAULT '1' COMMENT '公告类型（1通知 2公告）',
    notice_content TEXT COMMENT '公告内容',
    status VARCHAR(1) DEFAULT '0' COMMENT '公告状态（0正常 1关闭）',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP
);

-- 文件管理表
CREATE TABLE IF NOT EXISTS sys_file (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    file_name VARCHAR(255) NOT NULL COMMENT '文件名',
    file_path VARCHAR(500) NOT NULL COMMENT '文件路径',
    file_size VARCHAR(50) COMMENT '文件大小',
    file_type VARCHAR(50) COMMENT '文件类型',
    storage_type VARCHAR(20) DEFAULT 'local' COMMENT '存储类型（local/oss/s3）',
    create_by VARCHAR(50) COMMENT '创建者',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP
);

-- 用户消息表（消息推送用）
CREATE TABLE IF NOT EXISTS sys_user_message (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL COMMENT '接收用户ID',
    title VARCHAR(100) COMMENT '消息标题',
    content TEXT COMMENT '消息内容',
    message_type VARCHAR(1) DEFAULT '1' COMMENT '消息类型（1系统 2通知）',
    is_read VARCHAR(1) DEFAULT '0' COMMENT '是否已读（0未读 1已读）',
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

**Step 2: 提交SQL脚本**

```bash
git add docs/sql/init.sql
git commit -m "feat: 添加系统配置和审计模块数据库表脚本"
```

---

### Task 2: 创建字典类型Entity

**Files:**
- Create: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/infrastructure/db/entity/SysDictTypeEntity.java`
- Modify: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/infrastructure/db/entity/BaseEntity.java`

**Step 1: 在BaseEntity添加公共字段**

查看现有BaseEntity确保包含:
- id, createdAt, updatedAt, deletedAt

**Step 2: 创建SysDictTypeEntity**

```java
package cn.novalon.manage.sys.infrastructure.db.entity;

import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;

@Table("sys_dict_type")
public class SysDictTypeEntity {

    @Id
    private Long id;

    @Column("dict_name")
    private String dictName;

    @Column("dict_type")
    private String dictType;

    @Column("status")
    private String status;

    @Column("remark")
    private String remark;

    @Column("created_at")
    private LocalDateTime createdAt;

    @Column("updated_at")
    private LocalDateTime updatedAt;

    @Column("deleted_at")
    private LocalDateTime deletedAt;

    // Getters and Setters
}
```

**Step 3: 提交**

```bash
git add entity/SysDictTypeEntity.java
git commit -m "feat: 添加字典类型Entity"
```

---

### Task 3: 创建其他Entity（字典数据、系统配置、登录日志、异常日志、公告、文件、消息）

**Files:**
- Create: `infrastructure/db/entity/SysDictDataEntity.java`
- Create: `infrastructure/db/entity/SysConfigEntity.java`
- Create: `infrastructure/db/entity/SysLoginLogEntity.java`
- Create: `infrastructure/db/entity/SysExceptionLogEntity.java`
- Create: `infrastructure/db/entity/SysNoticeEntity.java`
- Create: `infrastructure/db/entity/SysFileEntity.java`
- Create: `infrastructure/db/entity/SysUserMessageEntity.java`

**每Entity结构:** 继承BaseEntity，添加对应字段的@Column注解和getter/setter

**示例 - SysConfigEntity:**
```java
@Table("sys_config")
public class SysConfigEntity {
    @Id
    private Long id;
    
    @Column("config_name")
    private String configName;
    
    @Column("config_key")
    private String configKey;
    
    @Column("config_value")
    private String configValue;
    
    @Column("config_type")
    private String configType;
    
    @Column("created_at")
    private LocalDateTime createdAt;
    
    @Column("updated_at")
    private LocalDateTime updatedAt;
    
    @Column("deleted_at")
    private LocalDateTime deletedAt;
    
    // Getters and Setters
}
```

---

### Task 4: 创建Repository接口

**Files:**
- Create: `infrastructure/db/repository/SysDictTypeRepository.java`
- Create: `infrastructure/db/repository/SysDictDataRepository.java`
- Create: `infrastructure/db/repository/SysConfigRepository.java`
- Create: `infrastructure/db/repository/SysLoginLogRepository.java`
- Create: `infrastructure/db/repository/SysExceptionLogRepository.java`
- Create: `infrastructure/db/repository/SysNoticeRepository.java`
- Create: `infrastructure/db/repository/SysFileRepository.java`
- Create: `infrastructure/db/repository/SysUserMessageRepository.java`

**每Repository:** 继承 ReactiveCrudRepository<Entity, Long>

**示例:**
```java
package cn.novalon.manage.sys.infrastructure.db.repository;

import cn.novalon.manage.sys.infrastructure.db.entity.SysDictTypeEntity;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface SysDictTypeRepository extends ReactiveCrudRepository<SysDictTypeEntity, Long> {
}
```

---

## 第二阶段：领域层（Domain + Converter）

### Task 5: 更新Domain模型

**Files:**
- Modify: `core/domain/SysDictType.java` - 添加dictDataList字段
- Modify: `core/domain/SysDictData.java` - 确保字段完整
- Modify: `core/domain/SysConfig.java` - 确保字段完整
- Modify: `core/domain/SysLoginLog.java` - 确保字段完整
- Create: `core/domain/SysExceptionLog.java`
- Create: `core/domain/SysUserMessage.java`

---

### Task 6: 创建Converter转换器

**Files:**
- Create: `infrastructure/db/converter/SysDictTypeConverter.java`
- Create: `infrastructure/db/converter/SysDictDataConverter.java`
- Create: `infrastructure/db/converter/SysConfigConverter.java`
- Create: `infrastructure/db/converter/SysLoginLogConverter.java`
- Create: `infrastructure/db/converter/SysExceptionLogConverter.java`
- Create: `infrastructure/db/converter/SysNoticeConverter.java`
- Create: `infrastructure/db/converter/SysFileConverter.java`

---

## 第三阶段：服务层（Service）

### Task 7: 创建Service接口和实现

**Files:**
- Create: `core/service/ISysDictTypeService.java` + `impl/SysDictTypeServiceImpl.java`
- Create: `core/service/ISysDictDataService.java` + `impl/SysDictDataServiceImpl.java`
- Create: `core/service/ISysConfigService.java` + `impl/SysConfigServiceImpl.java`
- Create: `core/service/ISysLoginLogService.java` + `impl/SysLoginLogServiceImpl.java`
- Create: `core/service/ISysExceptionLogService.java` + `impl/SysExceptionLogServiceImpl.java`
- Create: `core/service/ISysNoticeService.java` + `impl/SysNoticeServiceImpl.java`
- Create: `core/service/ISysFileService.java` + `impl/SysFileServiceImpl.java`
- Create: `core/service/ISysUserMessageService.java` + `impl/SysUserMessageServiceImpl.java`

**核心方法:**
- CRUD操作
- 字典: 根据类型查询数据列表
- 配置: 根据key查询value
- 日志: 分页查询、按条件筛选
- 消息: 查询用户未读消息

---

## 第四阶段：Handler层（API接口）

### Task 8: 重构Handler接入数据库

**Files:**
- Modify: `handler/sys/SysDictHandler.java` - 注入Service替换内存Map
- Modify: `handler/sys/SysConfigHandler.java` - 注入Service替换内存Map
- Modify: `handler/sys/SysLogHandler.java` - 添加登录日志和异常日志查询
- Modify: `handler/sys/SysNoticeHandler.java` - 注入Service替换内存Map
- Modify: `handler/sys/SysFileHandler.java` - 完善文件预览功能

---

## 第五阶段：消息推送（WebSocket）

### Task 9: WebSocket配置

**Files:**
- Create: `config/WebSocketConfig.java`
- Create: `handler/websocket/WebSocketHandler.java`
- Create: `service/impl/WebSocketService.java`

```java
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketHandler(), "/ws")
                .setAllowedOrigins("*");
    }
    
    @Bean
    public WebSocketHandler webSocketHandler() {
        return new WebSocketHandler();
    }
}
```

---

### Task 10: 消息推送Service

**Files:**
- Create: `core/service/IWebSocketService.java`
- Create: `core/service/impl/WebSocketServiceImpl.java`

**功能:**
- 发送消息给指定用户
- 发送消息给所有在线用户
- 用户上线/下线跟踪

---

## 第六阶段：完善文件预览

### Task 11: 文件预览支持

**Files:**
- Modify: `handler/sys/SysFileHandler.java` - 添加预览接口
- 创建DTO: `dto/response/FilePreviewResponse.java`

**预览支持:**
- 图片: 返回Base64或直接流
- PDF: 返回PDF流
- 文本: 返回文本内容

---

## 第七阶段：异常日志自动记录

### Task 12: 全局异常处理器

**Files:**
- Create: `handler/GlobalExceptionHandler.java`
- Create: `config/GlobalExceptionHandlerConfig.java`

**功能:**
- 捕获所有Controller异常
- 自动记录到sys_exception_log表
- 返回统一格式的错误响应

---

## 第八阶段：测试

### Task 13: 单元测试

**Files:**
- Create: `test/service/SysDictTypeServiceTest.java`
- Create: `test/service/SysConfigServiceTest.java`
- Create: `test/service/SysLoginLogServiceTest.java`
- Create: `test/handler/SysDictHandlerTest.java`
- Create: `test/handler/SysNoticeHandlerTest.java`

**测试覆盖:**
- Service层CRUD操作
- Handler层API响应
- 异常场景处理

---

## 实施顺序

1. Task 1: SQL脚本
2. Task 2-4: Entity + Repository
3. Task 5-6: Domain + Converter
4. Task 7: Service层
5. Task 8: Handler层重构
6. Task 9-10: WebSocket消息推送
7. Task 11: 文件预览
8. Task 12: 异常日志
9. Task 13: 测试
