feat: 初始化工作日志服务平台项目
- 项目文档:PRD、SRS、架构设计文档、前后端详细设计文档、架构设计规范 - 数据库脚本:用户创建和数据库初始化脚本 - 后端框架:Spring Boot 3.2 + MyBatis-Plus 3.5 基础架构 - 公共组件:统一返回结果、分页封装、全局异常处理 - 基础功能:链路追踪、API日志、健康检查接口 - 配置文件:application.yml.example 配置模板 - 开发规范:遵循架构设计规范,data目录存放MyBatis-Plus文件
This commit is contained in:
commit
ae33bd4d6a
89
.gitignore
vendored
Normal file
89
.gitignore
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
# ====================================================
|
||||
# 工作日志服务平台 - Git 忽略配置
|
||||
# ====================================================
|
||||
|
||||
# ==================== IDE ====================
|
||||
# IntelliJ IDEA
|
||||
.idea/
|
||||
*.iml
|
||||
*.iws
|
||||
*.ipr
|
||||
out/
|
||||
|
||||
# Eclipse
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
bin/
|
||||
|
||||
# VSCode
|
||||
.vscode/
|
||||
|
||||
# ==================== 构建产物 ====================
|
||||
target/
|
||||
build/
|
||||
dist/
|
||||
*.class
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# ==================== 日志文件 ====================
|
||||
logs/
|
||||
*.log
|
||||
log/
|
||||
|
||||
# ==================== 临时文件 ====================
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
*.cache
|
||||
|
||||
# ==================== 配置文件(敏感信息)====================
|
||||
# 配置文件包含敏感信息,不提交到仓库
|
||||
application.yml
|
||||
application-dev.yml
|
||||
application-test.yml
|
||||
application-prod.yml
|
||||
bootstrap.yml
|
||||
|
||||
# 数据库备份
|
||||
*.sql.backup
|
||||
db_backup/
|
||||
|
||||
# ==================== 上传文件 ====================
|
||||
uploads/
|
||||
files/
|
||||
|
||||
# ==================== 系统文件 ====================
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
# ==================== Node.js(前端)====================
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# ==================== Maven ====================
|
||||
.mvn/
|
||||
mvnw
|
||||
mvnw.cmd
|
||||
|
||||
# ==================== Gradle ====================
|
||||
.gradle/
|
||||
gradle/
|
||||
gradlew
|
||||
gradlew.bat
|
||||
|
||||
# ==================== 其他 ====================
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
125
application-dev.yml.example
Normal file
125
application-dev.yml.example
Normal file
@ -0,0 +1,125 @@
|
||||
# ====================================================
|
||||
# 工作日志服务平台 - 开发环境配置文件
|
||||
# ====================================================
|
||||
# 说明:
|
||||
# 1. 复制本文件为 application-dev.yml
|
||||
# 2. 根据本地实际环境修改配置
|
||||
# 3. 不要将 application-dev.yml 提交到代码仓库
|
||||
# ====================================================
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
servlet:
|
||||
context-path: /
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: worklog-api
|
||||
|
||||
# 环境配置
|
||||
profiles:
|
||||
active: dev
|
||||
|
||||
# 数据源配置
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/worklog?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
|
||||
username: worklog
|
||||
password: Wlog@123
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
hikari:
|
||||
minimum-idle: 5
|
||||
maximum-pool-size: 20
|
||||
auto-commit: true
|
||||
idle-timeout: 600000
|
||||
max-lifetime: 1800000
|
||||
connection-timeout: 30000
|
||||
connection-test-query: SELECT 1
|
||||
|
||||
# Redis 配置
|
||||
data:
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password: zjf@123456
|
||||
database: 0
|
||||
timeout: 5000ms
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 8
|
||||
max-wait: -1ms
|
||||
max-idle: 8
|
||||
min-idle: 0
|
||||
|
||||
# Nacos 配置(在 bootstrap.yml 中配置)
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: localhost:8848
|
||||
username: nacos
|
||||
password: nacos
|
||||
namespace: worklog-dev
|
||||
group: DEFAULT_GROUP
|
||||
config:
|
||||
server-addr: localhost:8848
|
||||
username: nacos
|
||||
password: nacos
|
||||
namespace: worklog-dev
|
||||
file-extension: yml
|
||||
group: DEFAULT_GROUP
|
||||
|
||||
# MyBatis-Plus 配置
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: ASSIGN_ID
|
||||
logic-delete-field: deleted
|
||||
logic-delete-value: 1
|
||||
logic-not-delete-value: 0
|
||||
mapper-locations: classpath*:/mapper/**/*.xml
|
||||
type-aliases-package: com.wjbl.worklog.data.entity
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
root: INFO
|
||||
com.wjbl.worklog: DEBUG
|
||||
com.baomidou.mybatisplus: DEBUG
|
||||
com.wjbl.worklog.data.mapper: DEBUG
|
||||
file:
|
||||
path: ./logs
|
||||
pattern:
|
||||
console: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId:-}][%X{spanId:-}] %-5level %logger{50} - %msg%n'
|
||||
file: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId:-}][%X{spanId:-}] %-5level %logger{50} - %msg%n'
|
||||
|
||||
# 腾讯云 COS 配置(可选,用于文件上传功能)
|
||||
tencent:
|
||||
cos:
|
||||
enabled: false # 开发环境可以不启用
|
||||
app-id: 1308258046
|
||||
secret-id: AKIDukKfkY5LK2SbU6QTM7csugCSSDjzyiDS
|
||||
secret-key: 0lHXYIn20jDRP7ZlhNnyub3GEwObZHjw
|
||||
bucket-name: test-1308258046
|
||||
bucket-host: https://test-1308258046.cos.ap-beijing.myqcloud.com
|
||||
region: ap-beijing
|
||||
|
||||
# 应用配置
|
||||
worklog:
|
||||
# Token 配置
|
||||
token:
|
||||
expire-time: 86400 # Token 有效期(秒),默认 24 小时
|
||||
prefix: "auth:token:"
|
||||
|
||||
# 日志内容限制
|
||||
log:
|
||||
max-content-length: 2000 # 最大 2000 汉字
|
||||
|
||||
# 文件上传配置(如果不使用 COS)
|
||||
upload:
|
||||
enabled: true
|
||||
base-path: ./uploads
|
||||
max-file-size: 10MB
|
||||
allowed-extensions: jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx
|
||||
35
bootstrap.yml.example
Normal file
35
bootstrap.yml.example
Normal file
@ -0,0 +1,35 @@
|
||||
# ====================================================
|
||||
# 工作日志服务平台 - Bootstrap 配置文件
|
||||
# ====================================================
|
||||
# 说明:
|
||||
# 1. 复制本文件为 bootstrap.yml
|
||||
# 2. bootstrap.yml 在 application.yml 之前加载
|
||||
# 3. 主要用于配置 Nacos 配置中心和服务发现
|
||||
# ====================================================
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: worklog-api
|
||||
|
||||
# Nacos 配置中心
|
||||
cloud:
|
||||
nacos:
|
||||
# 服务发现配置
|
||||
discovery:
|
||||
server-addr: localhost:8848
|
||||
username: nacos
|
||||
password: nacos
|
||||
namespace: worklog-dev
|
||||
group: DEFAULT_GROUP
|
||||
enabled: true
|
||||
|
||||
# 配置中心
|
||||
config:
|
||||
server-addr: localhost:8848
|
||||
username: nacos
|
||||
password: nacos
|
||||
namespace: worklog-dev
|
||||
group: DEFAULT_GROUP
|
||||
file-extension: yml
|
||||
enabled: true
|
||||
refresh-enabled: true
|
||||
157
doc/产品需求文档PRD.md
Normal file
157
doc/产品需求文档PRD.md
Normal file
@ -0,0 +1,157 @@
|
||||
# 工作日志服务平台产品规格说明书 (PRD)
|
||||
**版本号:** V1.0
|
||||
**拟定日期:** 2024 年 5 月
|
||||
**适用年度:** 2026 年人员管理规划
|
||||
**文档状态:** 草稿
|
||||
|
||||
---
|
||||
|
||||
## 1. 项目概述
|
||||
|
||||
### 1.1 项目背景
|
||||
为配合 2026 年公司人员管理数字化升级需求,计划构建一套轻量级、多终端的工作日志服务平台。旨在规范员工日常工作记录,沉淀工作成果,并通过模板化管理提高日志填写的效率与质量。
|
||||
|
||||
### 1.2 项目目标
|
||||
1. **规范化:** 统一人员信息管理与日志格式。
|
||||
2. **便捷化:** 支持 PC 管理后台与移动端随时记录。
|
||||
3. **轻量化:** 聚焦核心功能,确保系统响应速度快,维护成本低。
|
||||
4. **安全性:** 保障人员信息与工作数据的安全,符合 2026 年数据安全合规要求。
|
||||
|
||||
### 1.3 适用范围
|
||||
* **管理后台(Web 端):** 供 HR、系统管理员及部门管理者使用。
|
||||
* **移动端(H5/小程序/App):** 供全体员工日常填写与查看日志使用。
|
||||
|
||||
---
|
||||
|
||||
## 2. 用户角色与权限
|
||||
|
||||
| 角色 | 描述 | 核心权限 |
|
||||
| :--- | :--- | :--- |
|
||||
| **系统管理员** | 负责系统基础配置与人员管理 | 人员管理(全权)、模板管理(全权)、查看所有日志、启用/禁用功能 |
|
||||
| **普通员工** | 系统的主要使用者 | 填写/编辑/删除**本人**日志、查看可用模板、修改本人基本信息 |
|
||||
|
||||
*(注:本期 V1.0 主要区分“管理员”与“普通用户”,管理者权限可后续通过数据范围控制实现)*
|
||||
|
||||
---
|
||||
|
||||
## 3. 功能需求详情
|
||||
|
||||
### 3.1 人员信息管理模块
|
||||
**目标:** 维护系统使用者的基础档案与账号状态。
|
||||
|
||||
| 功能点 | 详细描述 | 字段/规则 |
|
||||
| :--- | :--- | :--- |
|
||||
| **人员列表** | 展示所有人员信息,支持搜索与筛选。 | 支持按姓名、职位、状态(启用/禁用)筛选。 |
|
||||
| **新增人员** | 管理员创建新账号。 | **必填:** 姓名、登陆账号、登陆密码、职位<br>**选填:** 联系方式、电子邮箱、描述<br>**默认:** 状态为“启用” |
|
||||
| **编辑人员** | 修改现有人员信息。 | 管理员可修改所有字段;用户本人仅可修改联系方式、邮箱、描述。**账号与密码由管理员重置。** |
|
||||
| **删除人员** | 物理或逻辑删除人员。 | 建议采用**逻辑删除**(标记为离职),保留历史日志关联。 |
|
||||
| **启用/禁用** | 控制账号登陆权限。 | 禁用后,该账号无法登陆,历史数据保留。 |
|
||||
| **安全要求** | 密码存储与传输。 | 密码需加密存储(如 BCrypt),传输全程 HTTPS。 |
|
||||
|
||||
### 3.2 工作日志信息管理模块
|
||||
**目标:** 核心业务模块,记录每日工作内容。
|
||||
|
||||
| 功能点 | 详细描述 | 字段/规则 |
|
||||
| :--- | :--- | :--- |
|
||||
| **日志列表** | 展示日志记录,支持多端查看。 | 按日期倒序排列。支持按日期范围、记录人筛选(管理员)。 |
|
||||
| **新建日志** | 填写当日工作内容。 | **日志日期:** 默认当天,可补填过去日期。<br>**记录时间:** 系统自动获取提交时间。<br>**记录人:** 自动获取当前登陆用户。<br>**使用模板:** 可选,选择后自动填充内容框架。<br>**记录内容:** 支持 Markdown 编辑器,**上限 2000 汉字**(含标点)。 |
|
||||
| **编辑日志** | 修改已提交的日志。 | 仅允许修改**当日**或**未锁定**的日志(具体规则可配)。<br>内容修改需保留版本记录(可选)。 |
|
||||
| **删除日志** | 移除错误记录。 | 仅本人可删除本人日志,管理员可删除任意日志。需二次确认。 |
|
||||
| **查看详情** | 阅读日志具体内容。 | 渲染 Markdown 格式,显示模板名称(如有)。 |
|
||||
| **数据校验** | 内容长度限制。 | 前端与后端双重校验,超过 2K 汉字禁止提交并提示。 |
|
||||
|
||||
### 3.3 日志模板管理模块
|
||||
**目标:** 标准化日志格式,提高填写效率。
|
||||
|
||||
| 功能点 | 详细描述 | 字段/规则 |
|
||||
| :--- | :--- | :--- |
|
||||
| **模板列表** | 展示所有可用模板。 | 显示模板名称、状态、更新时间。 |
|
||||
| **新增模板** | 创建新的日志格式。 | **模板名称:** 唯一标识(如“研发日报”、“销售周报”)。<br>**模板内容:** 支持 Markdown 语法的预设文本。<br>**使用说明:** 指导员工如何填写该模板。 |
|
||||
| **编辑模板** | 更新模板内容。 | 修改后,新建日志时生效,不影响历史已提交的日志内容。 |
|
||||
| **删除模板** | 移除废弃模板。 | 若模板已被日志引用,建议仅“禁用”而非物理删除。 |
|
||||
| **启用/禁用** | 控制模板在前端的可见性。 | 禁用后,员工新建日志时无法选择该模板。 |
|
||||
|
||||
### 3.4 多终端服务
|
||||
**目标:** 满足不同场景下的使用需求。
|
||||
|
||||
| 终端 | 适用场景 | 功能侧重 | 技术要求 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **管理后台 (Web)** | 办公室、管理场景 | 人员管理、模板配置、数据导出、全量日志查询。 | 响应式布局,适配 Chrome/Edge/Safari。 |
|
||||
| **移动端 (Mobile)** | 外勤、居家、碎片时间 | 快速写日志、查看本人历史、接收提醒。 | 适配 iOS/Android,建议采用 H5 或微信小程序架构,免安装。 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 非功能性需求 (NFR)
|
||||
|
||||
### 4.1 性能要求
|
||||
* **响应时间:** 页面加载时间 < 1.5 秒(4G/5G 网络下)。
|
||||
* **并发支持:** 支持至少 500 人同时在线操作(满足 2026 年规模预估)。
|
||||
* **存储限制:** 单条日志内容严格限制在 2K 汉字以内,防止数据库膨胀。
|
||||
|
||||
### 4.2 安全与合规
|
||||
* **数据加密:** 敏感信息(密码、联系方式)数据库加密存储。
|
||||
* **访问控制:** 接口需具备 Token 鉴权机制,防止未授权访问。
|
||||
* **操作审计:** 管理员的敏感操作(如删除人员、禁用账号)需记录操作日志。
|
||||
* **隐私合规:** 符合《个人信息保护法》要求,提供账号注销或数据导出功能。
|
||||
|
||||
### 4.3 可靠性
|
||||
* **数据备份:** 每日自动增量备份,每周全量备份。
|
||||
* **异常处理:** 移动端在网络不佳时,支持草稿箱本地暂存功能。
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据模型设计 (ER 简述)
|
||||
|
||||
为满足需求,核心数据表结构设计如下:
|
||||
|
||||
1. **用户表 (sys_user)**
|
||||
* `id` (PK), `username`, `password_hash`, `name`, `phone`, `email`, `position`, `description`, `status` (0-禁用,1-启用), `created_at`
|
||||
2. **日志模板表 (log_template)**
|
||||
* `id` (PK), `title`, `content`, `instruction`, `status` (0-禁用,1-启用), `created_by`, `updated_at`
|
||||
3. **工作日志表 (work_log)**
|
||||
* `id` (PK), `user_id` (FK), `log_date`, `record_time`, `content`, `template_id` (FK, Nullable), `created_at`, `updated_at`
|
||||
|
||||
---
|
||||
|
||||
## 6. UI/UX 交互建议
|
||||
|
||||
1. **风格:** 简洁商务风,支持深色模式(Dark Mode),适应 2026 年主流操作系统特性。
|
||||
2. **编辑器:** 移动端 Markdown 编辑器需简化 toolbar,提供常用快捷键(如加粗、列表、引用)。
|
||||
3. **模板选择:** 新建日志时,模板选择采用弹窗或下拉式,支持“最近使用”推荐。
|
||||
4. **字数统计:** 编辑器右下角实时显示当前字数/2000 字数,接近上限时变红预警。
|
||||
|
||||
---
|
||||
|
||||
## 7. 实施路线图 (Roadmap)
|
||||
|
||||
* **阶段一:基础架构与人员管理 (T+1 月)**
|
||||
* 完成数据库设计、API 开发。
|
||||
* 实现 Web 端人员增删改查及登陆认证。
|
||||
* **阶段二:日志与模板核心功能 (T+2 月)**
|
||||
* 完成日志 CRUD、Markdown 解析。
|
||||
* 完成模板管理功能。
|
||||
* Web 端管理后台上线。
|
||||
* **阶段三:移动端适配与测试 (T+3 月)**
|
||||
* 完成移动端 H5/小程序开发。
|
||||
* 进行安全测试与压力测试。
|
||||
* 小范围试点运行。
|
||||
* **阶段四:全面推广 (2026 年初)**
|
||||
* 全员培训,正式切换使用。
|
||||
|
||||
---
|
||||
|
||||
## 8. 风险与应对
|
||||
|
||||
| 风险点 | 描述 | 应对策略 |
|
||||
| :--- | :--- | :--- |
|
||||
| **内容限制过严** | 2K 汉字可能无法满足部分复杂工作记录。 | 在 V1.0 严格执行,V2.0 评估是否支持附件上传或扩容。 |
|
||||
| **移动端体验** | Markdown 在手机上编辑体验较差。 | 优化移动端编辑器 UI,提供“纯文本模式”切换。 |
|
||||
| **数据迁移** | 若从旧系统迁移,数据格式可能不兼容。 | 提供 Excel 导入/导出功能,便于初期数据初始化。 |
|
||||
|
||||
---
|
||||
|
||||
**审批签字:**
|
||||
|
||||
产品经理:________________ 日期:________________
|
||||
技术负责人:______________ 日期:________________
|
||||
业务方代表:______________ 日期:________________
|
||||
667
doc/前端详细设计文档.md
Normal file
667
doc/前端详细设计文档.md
Normal file
@ -0,0 +1,667 @@
|
||||
# 工作日志服务平台 - 前端详细设计文档
|
||||
|
||||
**版本号**:V1.0
|
||||
**编写日期**:2026-02-24
|
||||
**文档状态**:初稿
|
||||
**适用范围**:管理后台(Web)与移动端 H5 前端实现
|
||||
|
||||
---
|
||||
|
||||
## 1. 前端总体设计
|
||||
|
||||
### 1.1 技术栈
|
||||
|
||||
- **管理后台(Web)**:
|
||||
- 框架:Vue 3 + TypeScript
|
||||
- 构建工具:Vite 5
|
||||
- UI 库:Element Plus
|
||||
- 状态管理:Pinia
|
||||
- 路由:Vue Router 4
|
||||
|
||||
- **移动端 H5**:
|
||||
- 框架:Vue 3 + TypeScript
|
||||
- 构建工具:Vite 5
|
||||
- UI 库:Vant 4
|
||||
- 状态管理:Pinia
|
||||
- 路由:Vue Router 4
|
||||
|
||||
- **公共约定**:
|
||||
- 接口协议:基于 `Result<T>` 的统一返回结构
|
||||
- 鉴权方式:Token(Bearer Token),存储于内存 + LocalStorage
|
||||
- 接口前缀:`/api/v1`(通过 Vite 代理转发到后端)
|
||||
|
||||
### 1.3 UI 主题与配色
|
||||
|
||||
- **整体色调**:以浅色主题为主(Light Theme),背景以白色或近白色为主色,辅以浅灰分隔线。
|
||||
- **主色 Primary**:建议使用偏蓝或偏青的浅色系(例如 `#409EFF` 或同级亮度色),用于主按钮、选中态、链接等高亮元素。
|
||||
- **辅色 Secondary**:用于标签、次级按钮和提示信息,可选用与主色相近的浅色系或中性灰。
|
||||
- **文字颜色**:
|
||||
- 标题文字:深灰或近黑色(例如 `#303133`),保证可读性。
|
||||
- 正文文字:中等深度灰(例如 `#606266`)。
|
||||
- 辅助文字(说明、占位):浅灰(例如 `#909399`)。
|
||||
- **组件风格**:
|
||||
- 管理后台沿用 Element Plus 默认浅色主题,根据主色配置 `primary-color`。
|
||||
- 移动端 H5 沿用 Vant 默认浅色主题,根据主色配置按钮、导航栏等组件颜色。
|
||||
- **暗色模式**:当前版本不强制支持暗色模式,如后续有需要,将新增独立的小节进行说明。
|
||||
|
||||
### 1.2 前端整体架构
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph Admin[管理后台]
|
||||
A1[路由 Router]
|
||||
A2[状态 Store]
|
||||
A3[视图 Views]
|
||||
A4[组件 Components]
|
||||
A5[API 封装]
|
||||
end
|
||||
|
||||
subgraph Mobile[移动端 H5]
|
||||
M1[路由 Router]
|
||||
M2[状态 Store]
|
||||
M3[视图 Views]
|
||||
M4[组件 Components]
|
||||
M5[API 封装]
|
||||
end
|
||||
|
||||
A3 --> A5
|
||||
A4 --> A5
|
||||
M3 --> M5
|
||||
M4 --> M5
|
||||
```
|
||||
|
||||
- 前后端通过统一的 API 封装层交互,前端不直接拼接 URL 字符串。
|
||||
- 管理后台与移动端的页面路由、状态管理相互独立,但可复用类型定义和部分工具函数。
|
||||
|
||||
---
|
||||
|
||||
## 2. 管理后台详细设计(Admin Web)
|
||||
|
||||
### 2.1 目录结构设计
|
||||
|
||||
```text
|
||||
worklog-admin/
|
||||
├── src/
|
||||
│ ├── main.ts
|
||||
│ ├── App.vue
|
||||
│ ├── router/
|
||||
│ │ └── index.ts # 路由定义
|
||||
│ ├── store/
|
||||
│ │ ├── index.ts # Pinia 创建
|
||||
│ │ ├── user.ts # 用户与权限状态
|
||||
│ │ └── layout.ts # 布局相关状态
|
||||
│ ├── api/
|
||||
│ │ ├── index.ts # 统一导出
|
||||
│ │ ├── request.ts # Axios 实例与拦截器
|
||||
│ │ ├── auth.ts # 认证相关 API
|
||||
│ │ ├── user.ts # 人员管理 API
|
||||
│ │ ├── log.ts # 日志管理 API
|
||||
│ │ └── template.ts # 模板管理 API
|
||||
│ ├── views/
|
||||
│ │ ├── login/
|
||||
│ │ │ └── Login.vue
|
||||
│ │ ├── layout/
|
||||
│ │ │ └── Layout.vue
|
||||
│ │ ├── user/
|
||||
│ │ │ ├── UserList.vue
|
||||
│ │ │ └── UserForm.vue
|
||||
│ │ ├── log/
|
||||
│ │ │ ├── LogList.vue
|
||||
│ │ │ ├── LogDetail.vue
|
||||
│ │ │ └── LogEdit.vue
|
||||
│ │ └── template/
|
||||
│ │ ├── TemplateList.vue
|
||||
│ │ └── TemplateForm.vue
|
||||
│ ├── components/
|
||||
│ │ ├── SearchBar.vue
|
||||
│ │ ├── Pagination.vue
|
||||
│ │ └── MarkdownEditor.vue
|
||||
│ ├── types/
|
||||
│ │ ├── api.d.ts # 接口类型定义
|
||||
│ │ └── models.d.ts # 领域模型类型定义
|
||||
│ └── utils/
|
||||
│ ├── auth.ts # Token 工具
|
||||
│ ├── request-helpers.ts # 请求工具
|
||||
│ └── validators.ts # 校验工具
|
||||
└── vite.config.ts
|
||||
```
|
||||
|
||||
**目录结构说明**:
|
||||
|
||||
按照架构设计规范,前端项目应采用独立目录或文件集中管理与后端交互的 API,避免在页面组件中硬编码 URL:
|
||||
|
||||
1. **api 目录职责**:
|
||||
- 统一管理所有后端接口调用
|
||||
- 按业务模块拆分(auth.ts、user.ts、log.ts、template.ts)
|
||||
- 所有 API 函数从 `@/api` 统一导出
|
||||
|
||||
2. **强制规范要求**:
|
||||
- ❌ **禁止硬编码**:组件中禁止直接调用 `request.get('/xxx/xxx')`
|
||||
- ✅ **统一入口**:所有 API 函数从 `@/api` 统一导出
|
||||
- ✅ **路径简化**:API 内部使用简化路径,由 `baseURL` 或拦截器统一添加前缀
|
||||
- ✅ **便于维护**:后端接口变更时,只需修改 API 定义文件
|
||||
|
||||
3. **API 定义示例**:
|
||||
|
||||
```typescript
|
||||
// api/user.ts
|
||||
import request from './request'
|
||||
|
||||
/**
|
||||
* 用户管理相关 API
|
||||
*/
|
||||
export const userApi = {
|
||||
// 分页查询用户列表
|
||||
page: (params: UserPageParams) =>
|
||||
request.get<PageResult<UserVO>>('/user/page', { params }),
|
||||
|
||||
// 创建用户
|
||||
create: (data: UserCreateDTO) =>
|
||||
request.post<string>('/user', data),
|
||||
|
||||
// 更新用户
|
||||
update: (id: string, data: UserUpdateDTO) =>
|
||||
request.put<void>(`/user/${id}`, data),
|
||||
|
||||
// 删除用户
|
||||
delete: (id: string) =>
|
||||
request.delete<void>(`/user/${id}`)
|
||||
}
|
||||
```
|
||||
|
||||
4. **组件使用示例**:
|
||||
|
||||
```typescript
|
||||
// views/user/UserList.vue
|
||||
<script setup lang="ts">
|
||||
import { userApi } from '@/api'
|
||||
|
||||
// ✅ 正确:使用 API 封装
|
||||
const loadData = async () => {
|
||||
const res = await userApi.page({ pageNum: 1, pageSize: 10 })
|
||||
// ...
|
||||
}
|
||||
|
||||
// ❌ 错误:硬编码 URL
|
||||
// const loadData = async () => {
|
||||
// const res = await request.get('/api/v1/user/page')
|
||||
// }
|
||||
</script>
|
||||
```
|
||||
|
||||
### 2.2 路由设计
|
||||
|
||||
#### 2.2.1 路由结构
|
||||
|
||||
```ts
|
||||
// router/index.ts(简化示意)
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
component: () => import('@/views/login/Login.vue'),
|
||||
meta: { public: true }
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: () => import('@/views/layout/Layout.vue'),
|
||||
children: [
|
||||
{ path: '', redirect: '/dashboard' },
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'Dashboard',
|
||||
component: () => import('@/views/dashboard/Dashboard.vue'),
|
||||
meta: { title: '首页' }
|
||||
},
|
||||
{
|
||||
path: '/user',
|
||||
name: 'UserList',
|
||||
component: () => import('@/views/user/UserList.vue'),
|
||||
meta: { title: '人员管理', requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: '/template',
|
||||
name: 'TemplateList',
|
||||
component: () => import('@/views/template/TemplateList.vue'),
|
||||
meta: { title: '模板管理', requiresAdmin: true }
|
||||
},
|
||||
{
|
||||
path: '/log',
|
||||
name: 'LogList',
|
||||
component: () => import('@/views/log/LogList.vue'),
|
||||
meta: { title: '日志管理' }
|
||||
},
|
||||
{
|
||||
path: '/log/:id',
|
||||
name: 'LogDetail',
|
||||
component: () => import('@/views/log/LogDetail.vue'),
|
||||
meta: { title: '日志详情' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### 2.2.2 路由守卫
|
||||
|
||||
- 检查 `meta.public`:公共路由(如登录页)不需 Token
|
||||
- 对其他路由:
|
||||
- 若无 Token,则重定向到 `/login`
|
||||
- 若 `requiresAdmin: true`,则检查用户角色是否为 `ADMIN`
|
||||
|
||||
### 2.3 状态管理设计(Pinia)
|
||||
|
||||
#### 2.3.1 用户状态 store/user.ts
|
||||
|
||||
- 状态字段:
|
||||
- `token`:当前用户 Token
|
||||
- `userInfo`:`{ id, username, name, role }`
|
||||
- 动作:
|
||||
- `login(payload)`:调用 `authApi.login`,保存 token 与 userInfo
|
||||
- `logout()`:清空本地状态和 LocalStorage
|
||||
- `fetchUserInfo()`:根据 token 获取最新用户信息(可选)
|
||||
|
||||
#### 2.3.2 布局状态 store/layout.ts
|
||||
|
||||
- 记录侧边栏折叠状态、当前菜单 key 等 UI 状态
|
||||
|
||||
### 2.4 API 封装设计
|
||||
|
||||
#### 2.4.1 Axios 实例(api/request.ts)
|
||||
|
||||
- 统一设置 `baseURL`(通过 Vite 代理到 `/api`)
|
||||
- 请求拦截器:
|
||||
- 自动在 Header 注入 `Authorization: Bearer {token}`
|
||||
- 响应拦截器:
|
||||
- 统一处理后端 `Result<T>` 结构
|
||||
- 当 `code == 401` 时,触发登出并跳转登录页
|
||||
|
||||
#### 2.4.2 API 模块
|
||||
|
||||
- `auth.ts`:登录、登出
|
||||
- `user.ts`:
|
||||
- `pageUsers(params)`
|
||||
- `createUser(data)`
|
||||
- `updateUser(id, data)`
|
||||
- `updateUserStatus(id, status)`
|
||||
- `deleteUser(id)`
|
||||
- `template.ts`:
|
||||
- `listTemplates(params)`
|
||||
- `createTemplate(data)`
|
||||
- `updateTemplate(id, data)`
|
||||
- `deleteTemplate(id)`
|
||||
- `log.ts`:
|
||||
- `pageLogs(params)`
|
||||
- `getLogDetail(id)`
|
||||
- `createLog(data)`
|
||||
- `updateLog(id, data)`
|
||||
- `deleteLog(id)`
|
||||
|
||||
### 2.5 页面详细设计
|
||||
|
||||
#### 2.5.1 登录页(Login.vue)
|
||||
|
||||
- **功能**:用户登录系统,获取 Token
|
||||
- **主要元素**:
|
||||
- 账号输入框(username)
|
||||
- 密码输入框(password)
|
||||
- 登录按钮
|
||||
- **交互逻辑**:
|
||||
1. 用户输入账号和密码
|
||||
2. 点击登录时进行前端必填校验
|
||||
3. 调用 `authApi.login` 接口
|
||||
4. 成功后保存 token 与用户信息,跳转到首页
|
||||
|
||||
#### 2.5.2 人员管理列表页(UserList.vue)
|
||||
|
||||
- **功能**:分页展示人员列表,支持搜索、启用/禁用、编辑、删除
|
||||
- **筛选条件**:
|
||||
- 姓名(模糊)
|
||||
- 职位(下拉/文本)
|
||||
- 状态(启用/禁用)
|
||||
- **表格字段**:姓名、账号、职位、状态、创建时间、操作
|
||||
- **操作按钮**:
|
||||
- 新增人员(打开 `UserForm` 弹窗)
|
||||
- 编辑(弹出编辑表单)
|
||||
- 启用/禁用(确认弹窗)
|
||||
- 删除(确认弹窗,逻辑删除)
|
||||
|
||||
##### 2.5.2.1 新增人员表单设计(UserForm.vue)
|
||||
|
||||
- **表单字段定义**:
|
||||
|
||||
| 字段 | 显示名称 | 类型 | 是否必填 | 说明 |
|
||||
|------|----------|------|----------|------|
|
||||
| username | 登录账号 | 输入框 | 是 | 全局唯一,只能包含字母、数字、部分符号(具体正则可配置) |
|
||||
| password | 初始密码 | 密码框 | 是 | 前端不做复杂度校验,后端做统一校验;仅在新增时显示 |
|
||||
| name | 姓名 | 输入框 | 是 | 方便在列表和日志中展示 |
|
||||
| role | 角色 | 下拉框 | 是 | 可选值:`USER`(普通用户)、`ADMIN`(管理员) |
|
||||
| phone | 联系方式 | 输入框 | 否 | 选填,手机号或座机号,用于联系 |
|
||||
| email | 电子邮箱 | 输入框 | 否 | 需通过基本邮箱格式校验 |
|
||||
| position | 职位 | 输入框/下拉 | 否 | 如“研发工程师”、“产品经理”等 |
|
||||
| description | 描述 | 多行文本 | 否 | 该用户的备注信息 |
|
||||
|
||||
- **提交规则**:
|
||||
1. 点击“保存”时触发表单校验,所有**必填字段**必须通过校验。
|
||||
2. 若是新增模式:调用 `createUser` API;编辑模式:调用 `updateUser` API。
|
||||
3. 保存成功后关闭弹窗并刷新人员列表。
|
||||
|
||||
#### 2.5.3 日志管理列表页(LogList.vue)
|
||||
|
||||
- **功能**:
|
||||
- 管理员:查看全员日志
|
||||
- 普通用户:仅查看本人日志
|
||||
- **筛选条件**:
|
||||
- 日期范围(起止)
|
||||
- 记录人(管理员可选)
|
||||
- 关键字(按内容模糊搜索)
|
||||
- **表格字段**:日期、记录人、内容摘要(前 50 字)、使用模板、记录时间、操作
|
||||
- **操作**:查看详情、删除
|
||||
|
||||
#### 2.5.4 日志详情页(LogDetail.vue)
|
||||
|
||||
- 展示完整日志内容(Markdown 渲染为 HTML)
|
||||
- 展示模板名称、记录人、记录时间
|
||||
|
||||
#### 2.5.5 模板列表与编辑页
|
||||
|
||||
- 模板列表:展示模板名称、状态、更新时间
|
||||
- 新建/编辑模板:
|
||||
- 使用 `MarkdownEditor` 组件
|
||||
- 填写模板名称、内容、使用说明
|
||||
|
||||
##### 2.5.5.1 新增/编辑模板表单设计(TemplateForm.vue)
|
||||
|
||||
- **表单字段定义**:
|
||||
|
||||
| 字段 | 显示名称 | 类型 | 是否必填 | 说明 |
|
||||
|------|----------|------|----------|------|
|
||||
| templateName | 模板名称 | 输入框 | 是 | 在系统内唯一,用于列表展示和选择 |
|
||||
| templateContent | 模板内容 | Markdown 编辑器 | 是 | 预设日志内容结构,支持 Markdown 语法 |
|
||||
| instruction | 使用说明 | 多行文本 | 否 | 说明该模板适用场景及填写注意事项 |
|
||||
| status | 状态 | 开关/单选 | 否(编辑时可选) | 0-禁用,1-启用,新增时默认启用 |
|
||||
|
||||
- **提交规则**:
|
||||
1. 模板名称与模板内容为**必填字段**,前端和后端均需校验非空。
|
||||
2. 模板名称建议在前端做简单重复校验(可选),后端做最终唯一性校验。
|
||||
3. 新增成功后返回模板列表;编辑成功后保持在当前页或返回列表(可配置)。
|
||||
|
||||
### 2.6 组件设计
|
||||
|
||||
#### 2.6.1 MarkdownEditor.vue
|
||||
|
||||
- 基于开源 Markdown 编辑器封装(如 `@kangc/v-md-editor`)
|
||||
- 提供:编辑区 + 预览区
|
||||
- Props:`modelValue`、`maxLength` 等
|
||||
- Emits:`update:modelValue`
|
||||
- 在日志编辑、模板编辑中复用
|
||||
|
||||
#### 2.6.2 Pagination.vue
|
||||
|
||||
- 封装 Element Plus 的分页组件
|
||||
- 接收:`total`、`pageNum`、`pageSize`
|
||||
- Emits:`update:pageNum`、`update:pageSize`
|
||||
|
||||
---
|
||||
|
||||
## 3. 移动端 H5 详细设计(Mobile H5)
|
||||
|
||||
### 3.1 目录结构设计
|
||||
|
||||
```text
|
||||
worklog-mobile/
|
||||
├── src/
|
||||
│ ├── main.ts
|
||||
│ ├── App.vue
|
||||
│ ├── router/
|
||||
│ │ └── index.ts
|
||||
│ ├── store/
|
||||
│ │ ├── index.ts
|
||||
│ │ └── user.ts
|
||||
│ ├── api/
|
||||
│ │ ├── index.ts
|
||||
│ │ ├── request.ts
|
||||
│ │ ├── auth.ts
|
||||
│ │ ├── log.ts
|
||||
│ │ └── template.ts
|
||||
│ ├── views/
|
||||
│ │ ├── login/Login.vue
|
||||
│ │ ├── home/Home.vue
|
||||
│ │ ├── log/LogList.vue
|
||||
│ │ ├── log/LogEdit.vue
|
||||
│ │ └── log/LogDetail.vue
|
||||
│ ├── components/
|
||||
│ │ ├── MobileNavBar.vue
|
||||
│ │ └── MobileMarkdownEditor.vue
|
||||
│ ├── types/
|
||||
│ └── utils/
|
||||
└── vite.config.ts
|
||||
```
|
||||
|
||||
**目录结构说明**:
|
||||
|
||||
移动端 H5 项目同样遵循架构设计规范的 API 管理规范:
|
||||
|
||||
1. **api 目录职责**:
|
||||
- 统一管理所有后端接口调用
|
||||
- 按业务模块拆分(auth.ts、log.ts、template.ts)
|
||||
- 所有 API 函数从 `@/api` 统一导出
|
||||
|
||||
2. **强制规范要求**(与管理后台一致):
|
||||
- ❌ **禁止硬编码**:组件中禁止直接调用 `request.get('/xxx/xxx')`
|
||||
- ✅ **统一入口**:所有 API 函数从 `@/api` 统一导出
|
||||
- ✅ **路径简化**:API 内部使用简化路径,由 `baseURL` 统一添加前缀
|
||||
- ✅ **便于维护**:后端接口变更时,只需修改 API 定义文件
|
||||
|
||||
3. **API 定义示例**:
|
||||
|
||||
```typescript
|
||||
// api/log.ts
|
||||
import request from './request'
|
||||
|
||||
/**
|
||||
* 日志管理相关 API
|
||||
*/
|
||||
export const logApi = {
|
||||
// 分页查询日志列表
|
||||
page: (params: LogPageParams) =>
|
||||
request.get<PageResult<LogVO>>('/log/page', { params }),
|
||||
|
||||
// 创建日志
|
||||
create: (data: LogCreateDTO) =>
|
||||
request.post<string>('/log', data),
|
||||
|
||||
// 查看日志详情
|
||||
detail: (id: string) =>
|
||||
request.get<LogDetailVO>(`/log/${id}`),
|
||||
|
||||
// 更新日志
|
||||
update: (id: string, data: LogUpdateDTO) =>
|
||||
request.put<void>(`/log/${id}`, data),
|
||||
|
||||
// 删除日志
|
||||
delete: (id: string) =>
|
||||
request.delete<void>(`/log/${id}`)
|
||||
}
|
||||
```
|
||||
|
||||
4. **组件使用示例**:
|
||||
|
||||
```typescript
|
||||
// views/log/LogList.vue
|
||||
<script setup lang="ts">
|
||||
import { logApi } from '@/api'
|
||||
|
||||
// ✅ 正确:使用 API 封装
|
||||
const loadData = async () => {
|
||||
const res = await logApi.page({
|
||||
pageNum: pageNum.value,
|
||||
pageSize: 10
|
||||
})
|
||||
// ...
|
||||
}
|
||||
|
||||
// ❌ 错误:硬编码 URL
|
||||
// const loadData = async () => {
|
||||
// const res = await request.get('/api/v1/log/page')
|
||||
// }
|
||||
</script>
|
||||
```
|
||||
|
||||
### 3.2 路由设计
|
||||
|
||||
- `/login`:登录页
|
||||
- `/home`:首页(入口导航)
|
||||
- `/log/list`:我的日志列表
|
||||
- `/log/edit`:新建日志
|
||||
- `/log/edit/:id`:编辑已有日志
|
||||
- `/log/detail/:id`:日志详情
|
||||
|
||||
路由守卫逻辑与管理后台类似:无 Token 时跳转 `/login`。
|
||||
|
||||
### 3.3 页面详细设计
|
||||
|
||||
#### 3.3.1 移动端登录页
|
||||
|
||||
- UI 使用 Vant 的 `Form`、`Field`、`Button`
|
||||
- 登录成功后跳转 `/home`
|
||||
|
||||
#### 3.3.2 首页(Home.vue)
|
||||
|
||||
- 展示常用入口:
|
||||
- 写日志
|
||||
- 我的日志
|
||||
- 模板说明(可选)
|
||||
- 可展示近期日志摘要或提醒(例如“今日未写日志”提示)。
|
||||
|
||||
#### 3.3.3 我的日志列表(LogList.vue)
|
||||
|
||||
- 显示当前登录用户的日志列表
|
||||
- 支持按日期筛选、按关键字搜索
|
||||
- 列表项展示:日期、内容摘要、记录时间
|
||||
- 向下滚动分页加载(下拉刷新 + 上拉加载更多)
|
||||
|
||||
#### 3.3.4 日志编辑页(LogEdit.vue)
|
||||
|
||||
- 字段:日志日期、模板选择、内容编辑
|
||||
- **日志日期**:默认当天,可修改为过去日期
|
||||
- **模板选择**:使用 Vant 的 `Picker` 或 `ActionSheet` 展示模板列表
|
||||
- **内容编辑**:采用简化版 Markdown 编辑器或富文本输入框
|
||||
- 底部悬浮提交按钮
|
||||
|
||||
##### 3.3.4.1 新增/编辑日志表单设计(LogEdit.vue)
|
||||
|
||||
- **表单字段定义**:
|
||||
|
||||
| 字段 | 显示名称 | 类型 | 是否必填 | 说明 |
|
||||
|------|----------|------|----------|------|
|
||||
| logDate | 日志日期 | 日期选择器 | 是 | 默认当前日期,允许选择历史日期 |
|
||||
| templateId | 使用模板 | 下拉/弹窗选择 | 否 | 选填,选择后自动填充内容(用户仍可修改) |
|
||||
| content | 日志内容 | 多行文本/Markdown 编辑器 | 是 | 最多 2000 汉字,前后端双重校验 |
|
||||
|
||||
- **提交规则**:
|
||||
1. `logDate` 与 `content` 为**必填字段**,提交前需通过校验。
|
||||
2. 内容长度校验:前端统计字符数,超过 2000 字给出提示并阻止提交;后端再做最终校验。
|
||||
3. 新增:调用 `createLog` 接口;编辑:调用 `updateLog` 接口。
|
||||
4. 保存成功后返回日志列表或详情页,并展示“保存成功”提示。
|
||||
|
||||
#### 3.3.5 日志详情页(LogDetail.vue)
|
||||
|
||||
- 展示完整日志内容(Markdown 渲染)
|
||||
- 显示日期、记录时间、模板名称
|
||||
- 提供“编辑”按钮(仅本人日志)
|
||||
|
||||
### 3.4 交互与体验设计
|
||||
|
||||
- 登录成功后缓存 Token 于 LocalStorage,并同步到 Pinia
|
||||
- 在网络不佳时,日志编辑页可支持“本地草稿”能力(可后续迭代):
|
||||
- 提交失败时将内容缓存到 LocalStorage
|
||||
- 下次进入编辑页时提示恢复草稿
|
||||
- 列表场景下,优先展示最近 30 天日志,并提供更早数据的加载入口
|
||||
|
||||
### 3.5 API 与数据复用策略
|
||||
|
||||
- 移动端与管理后台共享同一套后端 API 定义
|
||||
- 移动端的 `api` 目录在类型定义上可与管理后台共享(通过独立的 `@worklog/api-types` 内部包或复制类型文件)
|
||||
|
||||
---
|
||||
|
||||
## 4. 前端校验与错误处理
|
||||
|
||||
### 4.1 表单校验
|
||||
|
||||
- 使用 Element Plus / Vant 的表单校验能力结合自定义规则:
|
||||
- 登录:账号、密码必填
|
||||
- 人员新增:账号、姓名、初始角色必填
|
||||
- 日志:日期必填,内容必填且不超过 2000 字
|
||||
- 模板:模板名称和内容必填
|
||||
|
||||
### 4.2 错误提示与反馈
|
||||
|
||||
- 业务错误(如参数错误、权限不足)
|
||||
- 统一使用后端返回的 `message` 字段,在前端展示 Message/Toast
|
||||
- 系统异常(网络错误、服务器错误)
|
||||
- 显示通用错误提示,例如“系统异常,请稍后重试”
|
||||
- 表单提交成功
|
||||
- Message/Toast 提示“保存成功”,并按场景返回列表或停留当前页
|
||||
|
||||
### 4.3 新增/修改重复数据检测逻辑
|
||||
|
||||
> 本小节说明“新增/修改”场景下,前端识别和处理**重复数据**的依据,所有最终校验以后端为准。
|
||||
|
||||
- **人员新增/修改(User)**
|
||||
- **唯一性依据**:`username`(登录账号)字段在系统内必须唯一。
|
||||
- **后端实现约定**:
|
||||
- 数据库层对 `username` 建立唯一索引;
|
||||
- Service 层在创建用户前先查询是否已存在相同 `username`;
|
||||
- 在修改用户时,如修改了 `username`,也需进行相同的唯一性校验;
|
||||
- 若重复,返回 `code = 400`,`message` 类似“账号已存在,请更换后再试”。
|
||||
- **前端处理策略**:
|
||||
- 新增用户或在编辑状态下修改 `username` 时,不做全量拉取检查,仅在提交后根据后台返回的 `code`/`message` 判断是否为重复账号;
|
||||
- 若检测到“账号已存在”类错误,将错误提示绑定到 `username` 字段下方,并保持表单数据不丢失。
|
||||
|
||||
- **模板新增/修改(Template)**
|
||||
- **唯一性依据**:`templateName`(模板名称)在系统内必须唯一。
|
||||
- **后端实现约定**:
|
||||
- 数据库层对 `template_name` 建立唯一索引;
|
||||
- Service 层在创建模板前校验名称是否已存在;
|
||||
- 在修改模板时,如变更了 `templateName`,也需进行相同的唯一性校验;
|
||||
- 若重复,返回 `code = 400`,`message` 类似“模板名称已存在”。
|
||||
- **前端处理策略**:
|
||||
- 在 TemplateForm 中,模板名称为必填项,新增或编辑(名称变更)提交后根据后端返回的错误信息判断是否名称重复;
|
||||
- 若为重复名称错误,则在 `templateName` 字段下显示明确提示,不清空用户已输入的其他字段;
|
||||
- 可选增强:在失去焦点时调用后端“名称可用性检查”接口做即时反馈(如后续迭代增加此类接口)。
|
||||
|
||||
- **其他新增场景(如日志新增)**
|
||||
- 当前版本不对日志内容或日期做“重复日志”判定;
|
||||
- 允许同一用户在同一天写多条日志;
|
||||
- 如后续需要增加“同一天仅一条日志”或“内容完全相同视为重复”的规则,将在 PRD/SRS 中补充后,再同步更新本设计。
|
||||
|
||||
---
|
||||
|
||||
## 5. 安全与日志
|
||||
|
||||
### 5.1 安全考虑
|
||||
|
||||
- 不在前端长时间持久化敏感信息(只保存 Token 和必要的用户基础信息)
|
||||
- 对日志内容进行 XSS 过滤后再渲染(后端或前端统一处理)
|
||||
- 对管理后台的关键操作(删除用户、删除日志等)增加二次确认
|
||||
|
||||
### 5.2 前端日志
|
||||
|
||||
- 重要前端错误(如接口异常、未捕获异常)可上报到后端监控接口(可选)
|
||||
- 在开发环境启用详细日志,在生产环境关闭 console 输出
|
||||
|
||||
---
|
||||
|
||||
## 6. 与后端接口的协作约定
|
||||
|
||||
- 所有接口均返回 `Result<T>` 结构:`{ code, message, data, success }`
|
||||
- 分页接口返回 `PageResult<T>` 结构:`{ pageNum, pageSize, total, list }`
|
||||
- 认证接口约定:登录成功后,后端返回 Token 与用户信息,由前端存储并在后续请求中附加 `Authorization` 头
|
||||
- 错误码约定:
|
||||
- `200`:成功
|
||||
- `400`:参数错误(前端需校验并提示)
|
||||
- `401`:未授权/Token 无效(前端应跳转登录)
|
||||
- `403`:权限不足(前端提示“无权操作”)
|
||||
- `500`:服务器内部错误
|
||||
413
doc/后端模块详细设计文档.md
Normal file
413
doc/后端模块详细设计文档.md
Normal file
@ -0,0 +1,413 @@
|
||||
# 工作日志服务平台 - 后端模块详细设计文档
|
||||
|
||||
**版本号**:V1.0
|
||||
**编写日期**:2026-02-24
|
||||
**文档状态**:初稿
|
||||
**适用范围**:后端核心业务模块详细设计
|
||||
|
||||
---
|
||||
|
||||
## 1. 文档说明
|
||||
|
||||
本文档在总体架构的基础上,对核心业务模块进行更细粒度的设计,包括职责划分、主要流程、类结构与接口协作方式。各模块均遵循《架构设计规范》中关于目录结构、命名规范和分层规则。
|
||||
|
||||
### 1.1 基础模块说明
|
||||
|
||||
**当前项目架构**:
|
||||
|
||||
本项目采用单体应用架构,所有模块(Controller、Service、DataService、Mapper等)均在同一个应用 `worklog-api` 中。
|
||||
|
||||
**基础模块使用规范**:
|
||||
|
||||
按照架构设计规范,如果项目抽取公共能力到独立的 `common` 模块或其他基础模块,必须遵循以下规范:
|
||||
|
||||
1. **强制要求**:基础模块必须采用 Maven/Gradle 项目依赖方式引入
|
||||
|
||||
```xml
|
||||
<!-- 在业务服务的 pom.xml 中引入基础模块 -->
|
||||
<dependency>
|
||||
<groupId>com.wjbl.worklog</groupId>
|
||||
<artifactId>worklog-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
2. **禁止方式**:
|
||||
- ❌ 禁止拷贝代码到项目中
|
||||
- ❌ 禁止直接引入 JAR 包
|
||||
|
||||
3. **优势说明**:
|
||||
- 统一版本管理,避免基础代码不一致
|
||||
- 便于基础能力的统一升级和维护
|
||||
- 支持 Maven 的传递依赖管理
|
||||
|
||||
---
|
||||
|
||||
## 2. 人员管理模块(User Module)
|
||||
|
||||
### 2.1 模块职责
|
||||
|
||||
- 维护系统使用者的基础档案(账号、姓名、联系方式、职位等)。
|
||||
- 提供账号启用/禁用能力,支撑权限控制与登录拦截。
|
||||
- 为日志模块提供用户基础信息(用于展示、统计等)。
|
||||
|
||||
### 2.2 主要用例
|
||||
|
||||
| 用例编号 | 用例名称 | 发起方 | 说明 |
|
||||
|----------|----------|--------|------|
|
||||
| U01 | 查看人员列表 | 管理员 | 按姓名、职位、状态筛选人员列表 |
|
||||
| U02 | 新增人员 | 管理员 | 创建新账号,设置初始密码及角色 |
|
||||
| U03 | 编辑人员 | 管理员/本人 | 管理员可修改全字段,本人可修改联系方式、邮箱、描述 |
|
||||
| U04 | 启用/禁用人员 | 管理员 | 控制账号登录权限 |
|
||||
| U05 | 删除人员 | 管理员 | 建议逻辑删除,保留日志关联 |
|
||||
|
||||
### 2.3 分层设计
|
||||
|
||||
- **Controller 层**:`UserController`
|
||||
- `/api/v1/user/page`:分页查询人员列表
|
||||
- `/api/v1/user`:创建人员
|
||||
- `/api/v1/user/{id}`:修改人员信息
|
||||
- `/api/v1/user/{id}/status`:启用/禁用人员
|
||||
- `/api/v1/user/{id}`:删除人员
|
||||
|
||||
- **Service 层**:`UserService` / `UserServiceImpl`
|
||||
- 负责业务规则,例如:
|
||||
- 校验账号唯一性
|
||||
- 控制本人 vs 管理员的可编辑字段
|
||||
- 调用密码加密组件生成密码摘要
|
||||
|
||||
- **DataService 层**:`UserDataService` / `UserDataServiceImpl`
|
||||
- 继承 `IService<User>` 和 `ServiceImpl<UserMapper, User>`
|
||||
- 封装用户的 CRUD 与常用查询(如按状态统计)。
|
||||
|
||||
- **Mapper 层**:`UserMapper`
|
||||
- 继承 `BaseMapper<User>`,可定义复杂查询 SQL。
|
||||
|
||||
### 2.4 类与DTO/VO设计
|
||||
|
||||
- **实体类**:`User`
|
||||
- 对应表:`sys_user`
|
||||
- 主键:`id`,`String` 类型,存储雪花算法生成的 ID
|
||||
- 字段:`username`、`password`、`name`、`phone`、`email`、`position`、`description`、`status`、`role` 等
|
||||
|
||||
- **DTO**:
|
||||
- `UserCreateDTO`:用于新增用户
|
||||
- `UserUpdateDTO`:用于更新用户可编辑字段
|
||||
|
||||
- **VO**:
|
||||
- `UserVO`:用于列表/详情返回,隐藏敏感字段(如密码摘要)。
|
||||
|
||||
### 2.5 关键业务规则
|
||||
|
||||
1. **账号唯一性**:`username` 必须全局唯一。
|
||||
2. **密码存储**:使用 BCrypt 加密,业务层不返回密码明文。
|
||||
3. **状态控制**:当 `status=0` 时,登录拦截器拒绝所有业务请求。
|
||||
4. **软删除**:`deleted=1` 时用户不再出现在列表中,但保留日志关联数据。
|
||||
|
||||
### 2.6 重复数据检测
|
||||
|
||||
- **唯一性依据**:`username`(登录账号)字段在系统内必须唯一。
|
||||
- **后端实现约定**:
|
||||
- 数据库层对 `username` 建立唯一索引;
|
||||
- Service 层在创建用户前先查询是否已存在相同 `username`;
|
||||
- 在修改用户时,如修改了 `username`,也需进行相同的唯一性校验;
|
||||
- 若重复,返回 `code = 400`,`message` 类似"账号已存在,请更换后再试"。
|
||||
|
||||
---
|
||||
|
||||
## 3. 日志模板管理模块(Template Module)
|
||||
|
||||
### 3.1 模块职责
|
||||
|
||||
- 管理日志模板的创建、修改、启用/禁用和删除。
|
||||
- 为日志编写界面提供可选模板列表,提高日志填写效率和一致性。
|
||||
|
||||
### 3.2 主要用例
|
||||
|
||||
| 用例编号 | 用例名称 | 发起方 | 说明 |
|
||||
|----------|----------|--------|------|
|
||||
| T01 | 查看模板列表 | 管理员/普通用户 | 管理员查看全部,普通用户仅查看启用模板 |
|
||||
| T02 | 新增模板 | 管理员 | 创建新的日志模板(名称+内容+说明) |
|
||||
| T03 | 编辑模板 | 管理员 | 修改模板内容和说明,新建日志使用最新版本 |
|
||||
| T04 | 启用/禁用模板 | 管理员 | 控制模板在前端是否可选 |
|
||||
| T05 | 删除模板 | 管理员 | 若已被使用,建议仅禁用,不物理删除 |
|
||||
|
||||
### 3.3 分层设计
|
||||
|
||||
- **Controller 层**:`TemplateController`
|
||||
- `/api/v1/template/list`:按状态查询模板列表
|
||||
- `/api/v1/template`:创建模板
|
||||
- `/api/v1/template/{id}`:更新/删除模板(REST 风格拆分接口)
|
||||
|
||||
- **Service 层**:`TemplateService` / `TemplateServiceImpl`
|
||||
- 业务规则:
|
||||
- 模板名称唯一性校验
|
||||
- 禁用模板时不影响历史日志展示,仅影响新建时是否可选
|
||||
|
||||
- **DataService 层**:`LogTemplateDataService` / `LogTemplateDataServiceImpl`
|
||||
- 继承 `IService<LogTemplate>`,封装模板数据访问逻辑。
|
||||
|
||||
- **Mapper 层**:`LogTemplateMapper`
|
||||
|
||||
### 3.4 实体与字段约束
|
||||
|
||||
- **实体类**:`LogTemplate`
|
||||
- 对应表:`log_template`
|
||||
- `templateName`:唯一,非空
|
||||
- `templateContent`:文本型,存储 Markdown 内容
|
||||
- `instruction`:选填,文本
|
||||
- `status`:0-禁用,1-启用
|
||||
|
||||
### 3.5 缓存策略
|
||||
|
||||
- 启用中的模板列表可缓存到 Redis:`template:enabled:list`,TTL 30分钟。
|
||||
- 在模板增删改后,主动失效缓存,保持数据一致。
|
||||
|
||||
### 3.6 重复数据检测
|
||||
|
||||
- **唯一性依据**:`templateName`(模板名称)在系统内必须唯一。
|
||||
- **后端实现约定**:
|
||||
- 数据库层对 `template_name` 建立唯一索引;
|
||||
- Service 层在创建模板前校验名称是否已存在;
|
||||
- 在修改模板时,如变更了 `templateName`,也需进行相同的唯一性校验;
|
||||
- 若重复,返回 `code = 400`,`message` 类似"模板名称已存在"。
|
||||
|
||||
---
|
||||
|
||||
## 4. 工作日志管理模块(WorkLog Module)
|
||||
|
||||
### 4.1 模块职责
|
||||
|
||||
- 提供员工日常工作日志的创建、编辑、删除、查看能力。
|
||||
- 支持按日期范围、关键字等条件查询日志列表。
|
||||
- 支持与模板的关联使用,保留历史日志内容快照。
|
||||
|
||||
### 4.2 主要用例
|
||||
|
||||
| 用例编号 | 用例名称 | 发起方 | 说明 |
|
||||
|----------|----------|--------|------|
|
||||
| L01 | 日志列表查询 | 管理员/普通用户 | 普通用户仅查看本人日志,管理员可查看全部 |
|
||||
| L02 | 新建日志 | 普通用户 | 支持选择模板并编辑内容,限制 2000 汉字 |
|
||||
| L03 | 编辑日志 | 普通用户 | 仅允许修改自己的日志,规则可配置(如只允许当日) |
|
||||
| L04 | 删除日志 | 普通用户/管理员 | 普通用户删除自己的日志,管理员可删除任意日志 |
|
||||
| L05 | 查看日志详情 | 管理员/普通用户 | 渲染 Markdown 内容,展示模板名称 |
|
||||
|
||||
### 4.3 分层设计
|
||||
|
||||
- **Controller 层**:`LogController`
|
||||
- `/api/v1/log/page`:分页查询日志
|
||||
- `/api/v1/log`:创建日志
|
||||
- `/api/v1/log/{id}`:查看、更新、删除日志
|
||||
|
||||
- **Service 层**:`LogService` / `LogServiceImpl`
|
||||
- 负责业务规则:
|
||||
- 校验操作人是否为日志所有者(或管理员)
|
||||
- 校验字数限制(<= 2000 汉字)
|
||||
- 控制可编辑时间范围(如仅当日或未锁定日志)
|
||||
|
||||
- **DataService 层**:`WorkLogDataService` / `WorkLogDataServiceImpl`
|
||||
- 提供按用户、日期范围、关键字等条件的封装查询方法。
|
||||
|
||||
- **Mapper 层**:`WorkLogMapper`
|
||||
|
||||
### 4.4 字段与约束
|
||||
|
||||
- `logDate`:日期字段,允许补填过去日期
|
||||
- `recordTime`:日志记录时间,必须为服务器当前时间
|
||||
- `content`:文本字段,业务上限制为 2000 汉字(前后端双重校验)
|
||||
- `templateId`:可空,引用 `log_template.id`
|
||||
|
||||
### 4.5 日志编写流程概述
|
||||
|
||||
1. 用户发起"新建日志"请求,前端可选模板(可选)。
|
||||
2. 前端加载模板内容填入编辑器,用户编辑并提交。
|
||||
3. 后端校验:登录态、字数限制、日期格式等。
|
||||
4. 持久化日志数据,记录 `recordTime` 为当前服务器时间。
|
||||
5. 返回成功结果,前端跳转至列表或详情页。
|
||||
|
||||
### 4.6 重复数据检测
|
||||
|
||||
- **当前版本说明**:不对日志内容或日期做"重复日志"判定;
|
||||
- 允许同一用户在同一天写多条日志;
|
||||
- 如后续需要增加"同一天仅一条日志"或"内容完全相同视为重复"的规则,将在 PRD/SRS 中补充后,再同步更新本设计。
|
||||
|
||||
---
|
||||
|
||||
## 5. 认证与授权模块(Auth Module)
|
||||
|
||||
### 5.1 模块职责
|
||||
|
||||
- 提供用户登录、登出能力,并下发/校验 Token。
|
||||
- 对业务接口进行权限拦截,确保普通用户与管理员权限边界清晰。
|
||||
|
||||
### 5.2 组件设计
|
||||
|
||||
- `AuthController`:登录/登出接口
|
||||
- `TokenService`:
|
||||
- 生成 Token(UUID)
|
||||
- 存储 Token 到 Redis
|
||||
- 解析 Token 并还原用户上下文
|
||||
- `AuthFilter` / `AuthInterceptor`:
|
||||
- 拦截所有受保护的 `/api/v1/**` 请求
|
||||
- 从 `Authorization: Bearer {token}` 中读取 Token
|
||||
- 校验 Token 是否存在、是否过期
|
||||
- 将用户信息放入上下文(如 `UserContextHolder`)
|
||||
|
||||
### 5.3 登录流程
|
||||
|
||||
1. 用户调用 `/api/v1/auth/login`,提交账号与密码。
|
||||
2. 系统根据用户名查询用户记录,验证状态是否启用。
|
||||
3. 使用 BCrypt 对比密码。
|
||||
4. 生成 Token,写入 Redis:`auth:token:{token}`。
|
||||
5. 返回 Token 和用户基础信息。
|
||||
|
||||
### 5.4 授权规则
|
||||
|
||||
- 普通用户只能操作自己的日志和个人信息。
|
||||
- 管理员可操作所有用户和日志、模板。
|
||||
- 授权逻辑在 Service 层统一校验(如检查当前用户角色和操作对象归属)。
|
||||
|
||||
### 5.5 Token 存储结构
|
||||
|
||||
```java
|
||||
// Token 存储结构
|
||||
Key: auth:token:{token}
|
||||
Value: {
|
||||
"userId": "string",
|
||||
"username": "string",
|
||||
"role": "USER|ADMIN",
|
||||
"expireTime": timestamp
|
||||
}
|
||||
TTL: 24小时
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 公共组件模块(Common Module)
|
||||
|
||||
### 6.1 统一返回结构
|
||||
|
||||
- `Result<T>`:统一接口返回格式,包含:`code`、`message`、`data`、`success`。
|
||||
- `PageResult<T>`:分页返回结构,包含:`pageNum`、`pageSize`、`total`、`list`。
|
||||
|
||||
**Result 示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": { ... },
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
**PageResult 示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"pageNum": 1,
|
||||
"pageSize": 10,
|
||||
"total": 100,
|
||||
"list": [ ... ]
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 异常处理
|
||||
|
||||
- `BusinessException`:用于表达可预期的业务错误。
|
||||
- `GlobalExceptionHandler`:
|
||||
- 捕获 `BusinessException`,返回明确的业务错误码与提示文案。
|
||||
- 捕获全局 `Exception`,记录错误日志并返回通用错误信息。
|
||||
|
||||
**异常处理流程**:
|
||||
```java
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public Result<?> handleBusinessException(BusinessException e) {
|
||||
return Result.error(e.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public Result<?> handleException(Exception e) {
|
||||
log.error("系统异常", e);
|
||||
return Result.error(500, "系统异常,请稍后重试");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 AOP 日志
|
||||
|
||||
- 通过 AOP 切面统一记录 API 请求日志(方法、参数、耗时、结果)。
|
||||
- 日志输出到 `aop.log` 或通过 MDC 注入到统一日志格式中。
|
||||
- **所有日志必须包含 traceId 和 spanId**,便于进行日志跟踪。
|
||||
|
||||
**日志格式**:
|
||||
```text
|
||||
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId:-}][%X{spanId:-}] %-5level %logger{50} - %msg%n
|
||||
```
|
||||
|
||||
**日志示例**:
|
||||
```text
|
||||
2026-02-24 10:15:30.123 [http-nio-8080-exec-1] [a1b2c3d4...][1234abcd] INFO c.c.w.controller.LogController - 创建日志成功
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 附录
|
||||
|
||||
### 7.1 相关文档
|
||||
|
||||
- [架构设计文档](./架构设计文档.md)
|
||||
- [前端详细设计文档](./前端详细设计文档.md)
|
||||
- [架构设计规范](./规范/架构设计规范.md)
|
||||
- [产品需求文档 PRD](./产品需求文档PRD.md)
|
||||
- [需求规格说明书 SRS](./需求规格说明书SRS.md)
|
||||
|
||||
### 7.2 目录结构参考
|
||||
|
||||
```text
|
||||
worklog-api/
|
||||
├── src/main/java/com/company/worklog/
|
||||
│ ├── controller/ # 控制器层
|
||||
│ │ ├── UserController.java
|
||||
│ │ ├── LogController.java
|
||||
│ │ └── TemplateController.java
|
||||
│ ├── service/ # 业务服务层
|
||||
│ │ ├── UserService.java
|
||||
│ │ ├── LogService.java
|
||||
│ │ ├── TemplateService.java
|
||||
│ │ └── impl/
|
||||
│ ├── data/ # 数据访问层
|
||||
│ │ ├── entity/
|
||||
│ │ ├── mapper/
|
||||
│ │ └── service/ # DataService层
|
||||
│ │ ├── UserDataService.java
|
||||
│ │ ├── WorkLogDataService.java
|
||||
│ │ └── LogTemplateDataService.java
|
||||
│ ├── dto/ # 数据传输对象
|
||||
│ ├── vo/ # 视图对象
|
||||
│ └── common/ # 公共类
|
||||
│ ├── Result.java
|
||||
│ ├── PageResult.java
|
||||
│ └── exception/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**文档审批**
|
||||
|
||||
| 角色 | 姓名 | 签字 | 日期 |
|
||||
|------|------|------|------|
|
||||
| 架构师 | ______ | ______ | ______ |
|
||||
| 技术负责人 | ______ | ______ | ______ |
|
||||
|
||||
**变更记录**
|
||||
|
||||
| 版本 | 日期 | 变更内容 | 变更人 |
|
||||
|------|------|----------|--------|
|
||||
| V1.0 | 2026-02-24 | 从架构设计文档中独立模块详细设计 | ______ |
|
||||
1131
doc/架构设计文档.md
Normal file
1131
doc/架构设计文档.md
Normal file
File diff suppressed because it is too large
Load Diff
1276
doc/规范/架构设计规范.md
Normal file
1276
doc/规范/架构设计规范.md
Normal file
File diff suppressed because it is too large
Load Diff
113
doc/需求规格说明书SRS.md
Normal file
113
doc/需求规格说明书SRS.md
Normal file
@ -0,0 +1,113 @@
|
||||
# 工作日志服务平台 - 需求规格说明书 (SRS)
|
||||
|
||||
**版本号**:V1.0
|
||||
**日期**:2023-10-27
|
||||
**状态**:待开发
|
||||
|
||||
---
|
||||
|
||||
## 1. 引言
|
||||
### 1.1 项目背景
|
||||
配合 2026 年人员管理需求,构建一套简易的工作日志服务平台,用于记录员工日常工作内容,支持模板化管理及人员信息维护。
|
||||
### 1.2 建设目标
|
||||
实现人员信息数字化管理、工作日志在线填写与查看、日志模板标准化配置。
|
||||
### 1.3 范围
|
||||
包含人员管理、日志模板管理、工作日志管理三大模块。**不包含项目管理模块**。
|
||||
|
||||
---
|
||||
|
||||
## 2. 用户角色
|
||||
系统分为两种角色,权限控制如下:
|
||||
|
||||
| 角色 | 说明 | 权限范围 |
|
||||
| :--- | :--- | :--- |
|
||||
| **普通员工** | 系统的使用者 | 1. 修改个人密码/信息<br>2. 撰写、查看、修改、删除**自己的**日志<br>3. 查看启用的日志模板 |
|
||||
| **管理员** | 系统的维护者 | 1. 拥有普通员工所有权限<br>2. **人员管理**:增删改查所有人员、启用/禁用账号<br>3. **模板管理**:增删改查所有模板、启用/禁用模板<br>4. **日志管理**:可查看全公司日志(可选) |
|
||||
|
||||
---
|
||||
|
||||
## 3. 功能需求详解
|
||||
|
||||
### 3.1 人员管理模块
|
||||
| 功能点 | 描述 | 输入项 | 处理逻辑 | 输出/结果 |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **账号登录** | 用户进入系统 | 账号、密码 | 验证账号存在且状态为“启用”,密码匹配 | 登录成功跳转首页,失败提示错误 |
|
||||
| **人员列表** | 查看人员信息 | 无/搜索条件 | 展示人员基本信息列表 | 表格展示姓名、职位、状态等 |
|
||||
| **新增人员** | 创建新账号 | 姓名、账号、密码、职位等 | 账号唯一性校验,密码加密存储 | 创建成功,状态默认为“启用” |
|
||||
| **编辑人员** | 修改信息 | 除账号外的所有字段 | 验证权限(仅管理员或本人) | 信息更新 |
|
||||
| **启用/禁用** | 控制账号状态 | 无 | 切换状态标志位 | 禁用后该账号无法登录 |
|
||||
| **删除人员** | 移除人员 | 无 | 逻辑删除(建议)或物理删除 | 人员从列表消失 |
|
||||
|
||||
### 3.2 日志模板管理模块
|
||||
| 功能点 | 描述 | 输入项 | 处理逻辑 | 输出/结果 |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **模板列表** | 查看可用模板 | 无 | 仅展示状态为“启用”的模板给普通用户 | 模板名称、使用说明列表 |
|
||||
| **新增模板** | 创建新模板 | 名称、内容、说明 | 内容支持 Markdown 格式 | 模板创建成功 |
|
||||
| **编辑模板** | 修改模板 | 名称、内容、说明 | 验证权限(仅管理员) | 模板信息更新 |
|
||||
| **启用/禁用** | 控制模板可用性 | 无 | 切换状态标志位 | 禁用后用户写日志时不可选 |
|
||||
| **删除模板** | 移除模板 | 无 | 验证权限(仅管理员) | 模板从列表消失 |
|
||||
|
||||
### 3.3 工作日志管理模块
|
||||
| 功能点 | 描述 | 输入项 | 处理逻辑 | 输出/结果 |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **写日志** | 提交日常工作 | 日期、内容、选用模板 | 1. 记录人自动获取当前登录者<br>2. 内容字数校验<br>3. 记录时间自动生成 | 日志保存成功 |
|
||||
| **日志列表** | 查看历史日志 | 日期范围、关键词 | 普通用户仅见自己,管理员可见全部 | 日志列表(含日期、摘要) |
|
||||
| **编辑日志** | 修改已写日志 | 内容、模板 | 仅允许修改**自己的**日志 | 日志内容更新 |
|
||||
| **删除日志** | 移除日志 | 无 | 仅允许删除**自己的**日志 | 日志从列表消失 |
|
||||
| **日志详情** | 查看完整内容 | 无 | 渲染 Markdown 内容为 HTML | 展示格式化后的日志内容 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据字段与约束
|
||||
|
||||
### 4.1 人员信息 (User)
|
||||
* **姓名**:必填,文本。
|
||||
* **联系方式**:选填,文本。
|
||||
* **电子邮箱**:选填,需符合邮箱格式。
|
||||
* **登录账号**:必填,唯一,文本。
|
||||
* **登录密码**:必填,**加密存储**(不可明文)。
|
||||
* **职位**:选填,文本。
|
||||
* **描述**:选填,文本。
|
||||
* **状态**:启用/禁用。
|
||||
|
||||
### 4.2 日志模板 (Template)
|
||||
* **模板名称**:必填,文本。
|
||||
* **模板内容**:必填,支持 Markdown 语法。
|
||||
* **使用说明**:选填,文本。
|
||||
* **状态**:启用/禁用。
|
||||
|
||||
### 4.3 工作日志 (Log)
|
||||
* **日志日期**:必填,日期格式(YYYY-MM-DD)。
|
||||
* **记录人**:系统自动获取,不可手动修改。
|
||||
* **记录时间**:系统自动生成(YYYY-MM-DD HH:MM:SS)。
|
||||
* **记录内容**:必填,**最大 2000 汉字**,支持 Markdown 语法。
|
||||
* **使用模板**:选填,关联模板 ID。
|
||||
|
||||
---
|
||||
|
||||
## 5. 非功能需求 (NFR)
|
||||
|
||||
### 5.1 安全性
|
||||
* **密码安全**:所有用户密码必须经过哈希加密(如 BCrypt)后存入数据库。
|
||||
* **权限控制**:接口层需验证登录态(Token),普通用户不可越权访问管理员接口或他人数据。
|
||||
* **输入过滤**:防止 XSS 攻击,尤其是 Markdown 内容渲染时需做安全过滤。
|
||||
|
||||
### 5.2 性能与约束
|
||||
* **字数限制**:日志内容前端需实时统计字数,后端需二次校验,超过 2000 汉字禁止提交。
|
||||
* **并发支持**:支持至少 50 人同时在线操作无明显卡顿。
|
||||
* **数据保留**:禁用/删除操作建议采用“软删除”(标记状态位),以便后续审计追溯。
|
||||
|
||||
### 5.3 可用性
|
||||
* **Markdown 支持**:前端需集成 Markdown 编辑器,支持预览功能。
|
||||
* **响应式**:界面需适配主流浏览器及移动端基础查看。
|
||||
|
||||
---
|
||||
|
||||
## 6. 全局逻辑规则
|
||||
1. **登录拦截**:用户状态为“禁用”时,任何接口请求均返回“账号已禁用”。
|
||||
2. **模板关联**:删除模板时,若已有日志引用该模板,日志中的模板内容**不回溯修改**,仅断开关联或保留快照。
|
||||
3. **时间逻辑**:日志日期允许补填过去日期,但“记录时间”必须为当前服务器时间。
|
||||
4. **默认值**:新增人员默认状态为“启用”;新增模板默认状态为“启用”。
|
||||
|
||||
---
|
||||
**备注**:本文档为开发基准文档,如有变更需同步更新版本号。
|
||||
86
sql/create_user.sql
Normal file
86
sql/create_user.sql
Normal file
@ -0,0 +1,86 @@
|
||||
-- ====================================================
|
||||
-- 工作日志服务平台 - 数据库用户创建脚本
|
||||
-- ====================================================
|
||||
-- 版本:V1.0
|
||||
-- 创建日期:2026-02-24
|
||||
-- 说明:本脚本用于创建工作日志平台专用的数据库用户
|
||||
-- ====================================================
|
||||
|
||||
-- 使用说明:
|
||||
-- 1. 需要使用 root 用户或具有用户管理权限的用户执行
|
||||
-- 2. 执行命令:mysql -u root -p < create_user.sql
|
||||
-- 3. 创建成功后,使用新用户执行 init_database.sql
|
||||
|
||||
-- ====================================================
|
||||
-- 1. 创建数据库用户
|
||||
-- ====================================================
|
||||
|
||||
-- 删除已存在的用户(如果需要重新创建)
|
||||
-- DROP USER IF EXISTS 'worklog'@'localhost';
|
||||
-- DROP USER IF EXISTS 'worklog'@'%';
|
||||
|
||||
-- 创建本地连接用户
|
||||
CREATE USER IF NOT EXISTS 'worklog'@'localhost'
|
||||
IDENTIFIED WITH caching_sha2_password BY 'Wlog@123';
|
||||
|
||||
-- 创建远程连接用户(允许任意主机连接)
|
||||
CREATE USER IF NOT EXISTS 'worklog'@'%'
|
||||
IDENTIFIED WITH caching_sha2_password BY 'Wlog@123';
|
||||
|
||||
-- ====================================================
|
||||
-- 2. 授予权限
|
||||
-- ====================================================
|
||||
|
||||
-- 授予本地用户对 worklog 数据库的所有权限
|
||||
GRANT ALL PRIVILEGES ON worklog.* TO 'worklog'@'localhost';
|
||||
|
||||
-- 授予远程用户对 worklog 数据库的所有权限
|
||||
GRANT ALL PRIVILEGES ON worklog.* TO 'worklog'@'%';
|
||||
|
||||
-- ====================================================
|
||||
-- 3. 刷新权限
|
||||
-- ====================================================
|
||||
|
||||
FLUSH PRIVILEGES;
|
||||
|
||||
-- ====================================================
|
||||
-- 4. 验证用户创建
|
||||
-- ====================================================
|
||||
|
||||
-- 查看创建的用户
|
||||
SELECT user, host, plugin FROM mysql.user WHERE user = 'worklog';
|
||||
|
||||
-- 查看用户权限
|
||||
SHOW GRANTS FOR 'worklog'@'localhost';
|
||||
SHOW GRANTS FOR 'worklog'@'%';
|
||||
|
||||
-- ====================================================
|
||||
-- 用户信息说明
|
||||
-- ====================================================
|
||||
--
|
||||
-- 数据库用户:worklog
|
||||
-- 数据库密码:Wlog@123
|
||||
-- 认证插件:caching_sha2_password (MySQL 8.0 默认)
|
||||
--
|
||||
-- 权限范围:
|
||||
-- - worklog 数据库的所有权限(SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX 等)
|
||||
-- - 不包含其他数据库的访问权限
|
||||
-- - 不包含用户管理、系统管理等特权
|
||||
--
|
||||
-- 连接范围:
|
||||
-- - worklog@localhost:仅允许本地连接
|
||||
-- - worklog@%:允许任意主机连接(开发环境)
|
||||
--
|
||||
-- 安全建议:
|
||||
-- 1. 生产环境应限制远程连接的主机范围,例如:
|
||||
-- CREATE USER 'worklog'@'192.168.1.%' IDENTIFIED WITH caching_sha2_password BY 'Wlog@123';
|
||||
-- 2. 定期更换数据库密码
|
||||
-- 3. 根据实际需要调整权限范围
|
||||
--
|
||||
-- 测试连接:
|
||||
-- mysql -u worklog -p -h localhost worklog
|
||||
-- 输入密码:Wlog@123
|
||||
--
|
||||
-- ====================================================
|
||||
-- 脚本执行完成
|
||||
-- ====================================================
|
||||
206
sql/init_database.sql
Normal file
206
sql/init_database.sql
Normal file
@ -0,0 +1,206 @@
|
||||
-- ====================================================
|
||||
-- 工作日志服务平台 - 数据库初始化脚本
|
||||
-- ====================================================
|
||||
-- 版本:V1.0
|
||||
-- 创建日期:2026-02-24
|
||||
-- 说明:本脚本用于创建工作日志平台所需的数据库表结构
|
||||
-- ====================================================
|
||||
|
||||
-- 设置字符集
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ====================================================
|
||||
-- 1. 创建数据库(如果不存在)
|
||||
-- ====================================================
|
||||
CREATE DATABASE IF NOT EXISTS `worklog`
|
||||
DEFAULT CHARACTER SET utf8mb4
|
||||
DEFAULT COLLATE utf8mb4_general_ci;
|
||||
|
||||
USE `worklog`;
|
||||
|
||||
-- ====================================================
|
||||
-- 2. 创建数据库用户(如果需要)
|
||||
-- ====================================================
|
||||
-- 注意:需要以 root 用户或具有用户管理权限的用户执行以下语句
|
||||
|
||||
-- 创建数据库用户
|
||||
-- CREATE USER IF NOT EXISTS 'worklog'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'Wlog@123';
|
||||
-- CREATE USER IF NOT EXISTS 'worklog'@'%' IDENTIFIED WITH caching_sha2_password BY 'Wlog@123';
|
||||
|
||||
-- 授予权限
|
||||
-- GRANT ALL PRIVILEGES ON worklog.* TO 'worklog'@'localhost';
|
||||
-- GRANT ALL PRIVILEGES ON worklog.* TO 'worklog'@'%';
|
||||
|
||||
-- 刷新权限
|
||||
-- FLUSH PRIVILEGES;
|
||||
|
||||
-- ====================================================
|
||||
-- 3. 系统用户表 (sys_user)
|
||||
-- ====================================================
|
||||
-- 功能:存储系统用户基础信息,包括登录凭证、个人信息、角色权限等
|
||||
-- 主键策略:VARCHAR(20) 存储雪花算法生成的19位数字ID
|
||||
DROP TABLE IF EXISTS `sys_user`;
|
||||
CREATE TABLE `sys_user` (
|
||||
`id` VARCHAR(20) NOT NULL COMMENT '用户ID(雪花算法生成)',
|
||||
`username` VARCHAR(50) NOT NULL COMMENT '登录账号',
|
||||
`password` VARCHAR(100) NOT NULL COMMENT '登录密码(BCrypt加密)',
|
||||
`name` VARCHAR(50) NOT NULL COMMENT '姓名',
|
||||
`phone` VARCHAR(20) DEFAULT NULL COMMENT '联系方式',
|
||||
`email` VARCHAR(100) DEFAULT NULL COMMENT '电子邮箱',
|
||||
`position` VARCHAR(50) DEFAULT NULL COMMENT '职位',
|
||||
`description` VARCHAR(500) DEFAULT NULL COMMENT '描述',
|
||||
`status` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '状态(0-禁用,1-启用)',
|
||||
`role` VARCHAR(20) NOT NULL DEFAULT 'USER' COMMENT '角色(USER-普通用户,ADMIN-管理员)',
|
||||
`created_by` VARCHAR(20) NOT NULL COMMENT '创建人ID',
|
||||
`created_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_by` VARCHAR(20) NOT NULL COMMENT '更新人ID',
|
||||
`updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除(0-未删除,1-已删除)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_username` (`username`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_deleted` (`deleted`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统用户表';
|
||||
|
||||
-- ====================================================
|
||||
-- 4. 日志模板表 (log_template)
|
||||
-- ====================================================
|
||||
-- 功能:存储工作日志模板,提供标准化的日志格式
|
||||
DROP TABLE IF EXISTS `log_template`;
|
||||
CREATE TABLE `log_template` (
|
||||
`id` VARCHAR(20) NOT NULL COMMENT '模板ID(雪花算法生成)',
|
||||
`template_name` VARCHAR(100) NOT NULL COMMENT '模板名称',
|
||||
`template_content` TEXT NOT NULL COMMENT '模板内容(Markdown格式)',
|
||||
`instruction` VARCHAR(500) DEFAULT NULL COMMENT '使用说明',
|
||||
`status` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '状态(0-禁用,1-启用)',
|
||||
`created_by` VARCHAR(20) NOT NULL COMMENT '创建人ID',
|
||||
`created_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_by` VARCHAR(20) NOT NULL COMMENT '更新人ID',
|
||||
`updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除(0-未删除,1-已删除)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_template_name` (`template_name`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_deleted` (`deleted`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='日志模板表';
|
||||
|
||||
-- ====================================================
|
||||
-- 5. 工作日志表 (work_log)
|
||||
-- ====================================================
|
||||
-- 功能:存储员工每日工作日志记录
|
||||
DROP TABLE IF EXISTS `work_log`;
|
||||
CREATE TABLE `work_log` (
|
||||
`id` VARCHAR(20) NOT NULL COMMENT '日志ID(雪花算法生成)',
|
||||
`user_id` VARCHAR(20) NOT NULL COMMENT '记录人ID',
|
||||
`log_date` DATE NOT NULL COMMENT '日志日期',
|
||||
`record_time` DATETIME NOT NULL COMMENT '记录时间',
|
||||
`content` TEXT NOT NULL COMMENT '日志内容(Markdown格式,最大2000汉字)',
|
||||
`template_id` VARCHAR(20) DEFAULT NULL COMMENT '使用模板ID',
|
||||
`created_by` VARCHAR(20) NOT NULL COMMENT '创建人ID',
|
||||
`created_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_by` VARCHAR(20) NOT NULL COMMENT '更新人ID',
|
||||
`updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除(0-未删除,1-已删除)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_user_id` (`user_id`),
|
||||
KEY `idx_log_date` (`log_date`),
|
||||
KEY `idx_deleted` (`deleted`),
|
||||
KEY `idx_user_date` (`user_id`, `log_date`),
|
||||
CONSTRAINT `fk_work_log_user` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`),
|
||||
CONSTRAINT `fk_work_log_template` FOREIGN KEY (`template_id`) REFERENCES `log_template` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='工作日志表';
|
||||
|
||||
-- ====================================================
|
||||
-- 6. 初始化数据
|
||||
-- ====================================================
|
||||
|
||||
-- 6.1 创建默认管理员账号
|
||||
-- 密码:admin123(BCrypt加密后的值,实际使用时需要通过程序生成)
|
||||
-- 注意:以下密码哈希值仅为示例,实际部署时应通过程序生成
|
||||
INSERT INTO `sys_user` (
|
||||
`id`, `username`, `password`, `name`, `phone`, `email`,
|
||||
`position`, `description`, `status`, `role`,
|
||||
`created_by`, `updated_by`
|
||||
) VALUES (
|
||||
'1000000000000000001',
|
||||
'admin',
|
||||
'$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH',
|
||||
'系统管理员',
|
||||
NULL,
|
||||
'admin@example.com',
|
||||
'系统管理员',
|
||||
'系统默认管理员账号',
|
||||
1,
|
||||
'ADMIN',
|
||||
'1000000000000000001',
|
||||
'1000000000000000001'
|
||||
);
|
||||
|
||||
-- 6.2 创建默认日志模板
|
||||
INSERT INTO `log_template` (
|
||||
`id`, `template_name`, `template_content`, `instruction`,
|
||||
`status`, `created_by`, `updated_by`
|
||||
) VALUES
|
||||
(
|
||||
'2000000000000000001',
|
||||
'每日工作日志',
|
||||
'## 今日工作内容\n\n### 1. 已完成工作\n- \n\n### 2. 进行中工作\n- \n\n### 3. 遇到的问题\n- \n\n### 4. 明日计划\n- ',
|
||||
'适用于记录每日工作内容、进度和计划',
|
||||
1,
|
||||
'1000000000000000001',
|
||||
'1000000000000000001'
|
||||
),
|
||||
(
|
||||
'2000000000000000002',
|
||||
'研发日报',
|
||||
'## 研发日报\n\n### 开发任务\n- **任务名称**:\n- **完成进度**:\n- **技术方案**:\n\n### 代码提交\n- **提交次数**:\n- **主要功能**:\n\n### Bug修复\n- **问题描述**:\n- **解决方案**:\n\n### 技术难点\n- \n\n### 明日计划\n- ',
|
||||
'适用于研发人员记录每日开发工作',
|
||||
1,
|
||||
'1000000000000000001',
|
||||
'1000000000000000001'
|
||||
),
|
||||
(
|
||||
'2000000000000000003',
|
||||
'周报模板',
|
||||
'## 本周工作总结\n\n### 本周完成工作\n1. \n\n### 工作亮点\n- \n\n### 存在问题\n- \n\n### 下周工作计划\n1. \n\n### 需要协调的资源\n- ',
|
||||
'适用于周度工作总结和计划',
|
||||
1,
|
||||
'1000000000000000001',
|
||||
'1000000000000000001'
|
||||
);
|
||||
|
||||
-- ====================================================
|
||||
-- 7. 数据库说明
|
||||
-- ====================================================
|
||||
--
|
||||
-- 数据库配置:
|
||||
-- - 数据库名:worklog
|
||||
-- - 数据库用户:worklog
|
||||
-- - 数据库密码:Wlog@123
|
||||
-- - 字符集:utf8mb4
|
||||
-- - 排序规则:utf8mb4_general_ci
|
||||
--
|
||||
-- 设计原则:
|
||||
-- 1. 主键策略:所有表主键使用 VARCHAR(20) 存储雪花算法生成的19位数字ID
|
||||
-- 2. 审计字段:统一包含 created_by、created_time、updated_by、updated_time、deleted
|
||||
-- 3. 逻辑删除:使用 deleted 字段实现软删除,保留历史数据
|
||||
-- 4. 索引设计:为常用查询字段建立索引,提升查询性能
|
||||
-- 5. 字符集:统一使用 utf8mb4,支持 emoji 等特殊字符
|
||||
-- 6. 唯一性约束:username 和 template_name 建立唯一索引
|
||||
--
|
||||
-- 外键关系:
|
||||
-- - work_log.user_id -> sys_user.id
|
||||
-- - work_log.template_id -> log_template.id
|
||||
--
|
||||
-- 注意事项:
|
||||
-- 1. 默认管理员密码需要在实际部署时通过程序使用 BCrypt 算法重新生成
|
||||
-- 2. 雪花算法ID需要在应用层实现,数据库仅存储
|
||||
-- 3. 日志内容限制为2000汉字,需在应用层进行校验
|
||||
--
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
-- ====================================================
|
||||
-- 脚本执行完成
|
||||
-- ====================================================
|
||||
162
worklog-api/README.md
Normal file
162
worklog-api/README.md
Normal file
@ -0,0 +1,162 @@
|
||||
# 工作日志服务平台 - 后端 API
|
||||
|
||||
## 项目说明
|
||||
|
||||
工作日志服务平台后端 API,基于 Spring Boot 3.2 + MyBatis-Plus 构建。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **框架**: Spring Boot 3.2.2
|
||||
- **数据库**: MySQL 8.0
|
||||
- **缓存**: Redis 7.x
|
||||
- **ORM**: MyBatis-Plus 3.5.5
|
||||
- **工具库**: Hutool 5.8.25
|
||||
- **API 文档**: SpringDoc OpenAPI 2.3.0
|
||||
|
||||
## 环境要求
|
||||
|
||||
- JDK 21
|
||||
- Maven 3.6+
|
||||
- MySQL 8.0
|
||||
- Redis 7.x
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 数据库初始化
|
||||
|
||||
#### 步骤1: 创建数据库用户 (需要 root 权限)
|
||||
|
||||
```bash
|
||||
cd /home/along/MyCode/wanjiabuluo/worklog
|
||||
mysql -u root -p < sql/create_user.sql
|
||||
```
|
||||
|
||||
执行后会创建:
|
||||
- 用户: `worklog`
|
||||
- 密码: `Wlog@123`
|
||||
- 权限: worklog 数据库的所有权限
|
||||
|
||||
#### 步骤2: 初始化数据库结构和数据
|
||||
|
||||
```bash
|
||||
mysql -u worklog -p < sql/init_database.sql
|
||||
```
|
||||
|
||||
输入密码: `Wlog@123`
|
||||
|
||||
这将创建:
|
||||
- 数据库: `worklog`
|
||||
- 表: `sys_user`, `log_template`, `work_log`
|
||||
- 初始数据: 默认管理员账号 (admin/admin123)
|
||||
|
||||
### 2. 配置文件
|
||||
|
||||
项目已包含开发环境配置文件:
|
||||
- `src/main/resources/application.yml` - 主配置
|
||||
- `src/main/resources/application-dev.yml` - 开发环境配置
|
||||
- `src/main/resources/logback-spring.xml` - 日志配置
|
||||
|
||||
数据库连接配置 (application-dev.yml):
|
||||
```yaml
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://localhost:3306/worklog?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai
|
||||
username: worklog
|
||||
password: Wlog@123
|
||||
```
|
||||
|
||||
Redis 配置:
|
||||
```yaml
|
||||
spring:
|
||||
data:
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password: zjf@123456
|
||||
```
|
||||
|
||||
### 3. 编译项目
|
||||
|
||||
```bash
|
||||
cd worklog-api
|
||||
mvn clean compile
|
||||
```
|
||||
|
||||
### 4. 运行项目
|
||||
|
||||
```bash
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
或者打包后运行:
|
||||
|
||||
```bash
|
||||
mvn clean package
|
||||
java -jar target/worklog-api-1.0.0.jar
|
||||
```
|
||||
|
||||
### 5. 访问服务
|
||||
|
||||
- **健康检查**: http://localhost:8080/api/v1/health
|
||||
- **API 文档**: http://localhost:8080/swagger-ui.html
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
src/main/java/com/wjbl/worklog/
|
||||
├── WorklogApplication.java # 启动类
|
||||
├── config/ # 配置类
|
||||
│ ├── MybatisPlusConfig.java # MyBatis-Plus 配置
|
||||
│ ├── TraceInterceptor.java # 链路追踪拦截器
|
||||
│ ├── ApiLogAspect.java # API 日志切面
|
||||
│ └── WebMvcConfig.java # Web MVC 配置
|
||||
├── controller/ # 控制器
|
||||
│ └── HealthController.java # 健康检查接口
|
||||
├── service/ # 业务服务层
|
||||
│ └── impl/
|
||||
├── data/ # 数据访问层 (MyBatis-Plus)
|
||||
│ ├── entity/ # 实体类
|
||||
│ ├── mapper/ # Mapper 接口
|
||||
│ └── service/ # DataService (数据服务)
|
||||
│ └── impl/
|
||||
├── dto/ # 数据传输对象
|
||||
├── vo/ # 视图对象
|
||||
├── enums/ # 枚举类
|
||||
└── common/ # 公共组件
|
||||
├── Result.java # 统一返回结果
|
||||
├── PageResult.java # 分页结果
|
||||
└── exception/ # 异常处理
|
||||
├── BusinessException.java
|
||||
└── GlobalExceptionHandler.java
|
||||
```
|
||||
|
||||
## 日志
|
||||
|
||||
日志文件位于 `logs/` 目录:
|
||||
- `app.log` - 应用日志
|
||||
- `sql.log` - SQL 执行日志
|
||||
|
||||
所有日志都包含链路追踪信息 (traceId, spanId)。
|
||||
|
||||
## 开发规范
|
||||
|
||||
1. **目录结构**: 严格遵循架构设计规范,MyBatis-Plus 相关文件放在 `data` 目录
|
||||
2. **DataService 命名**: XxxDataService / XxxDataServiceImpl
|
||||
3. **分层职责**: Controller → Service → DataService → Mapper
|
||||
4. **主键策略**: VARCHAR(20) 存储雪花算法 ID
|
||||
5. **统一返回**: 使用 Result<T> 封装响应
|
||||
|
||||
## 测试账号
|
||||
|
||||
- **管理员账号**: admin
|
||||
- **管理员密码**: admin123
|
||||
|
||||
## 下一步
|
||||
|
||||
阶段一基础框架已完成,接下来将开发:
|
||||
1. 认证授权模块 (登录/登出)
|
||||
2. 人员管理模块
|
||||
3. 模板管理模块
|
||||
4. 工作日志模块
|
||||
|
||||
详见开发计划文档。
|
||||
129
worklog-api/pom.xml
Normal file
129
worklog-api/pom.xml
Normal file
@ -0,0 +1,129 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<groupId>com.wjbl</groupId>
|
||||
<artifactId>worklog-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<name>WorkLog Platform API</name>
|
||||
<description>工作日志服务平台后端API</description>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<mybatis-plus.version>3.5.5</mybatis-plus.version>
|
||||
<hutool.version>5.8.25</hutool.version>
|
||||
<springdoc.version>2.3.0</springdoc.version>
|
||||
<mysql.version>8.0.33</mysql.version>
|
||||
<commonmark.version>0.21.0</commonmark.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Boot Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Validation -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Redis -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot AOP -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MyBatis-Plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MySQL Driver -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>${mysql.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Hutool 工具库 -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringDoc OpenAPI (Swagger) -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Markdown 解析库 -->
|
||||
<dependency>
|
||||
<groupId>org.commonmark</groupId>
|
||||
<artifactId>commonmark</artifactId>
|
||||
<version>${commonmark.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- BCrypt 密码加密 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@ -0,0 +1,24 @@
|
||||
package com.wjbl.worklog;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 工作日志服务平台 - 启动类
|
||||
*
|
||||
* @author WorkLog Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.wjbl.worklog.data.mapper")
|
||||
public class WorklogApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(WorklogApplication.class, args);
|
||||
System.out.println("\n========================================");
|
||||
System.out.println("工作日志服务平台启动成功!");
|
||||
System.out.println("API 文档地址: http://localhost:8080/swagger-ui.html");
|
||||
System.out.println("========================================\n");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.wjbl.worklog.common;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页结果封装
|
||||
*
|
||||
* @author WorkLog Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "分页结果")
|
||||
public class PageResult<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "当前页码")
|
||||
private Long pageNum;
|
||||
|
||||
@Schema(description = "每页大小")
|
||||
private Long pageSize;
|
||||
|
||||
@Schema(description = "总记录数")
|
||||
private Long total;
|
||||
|
||||
@Schema(description = "数据列表")
|
||||
private List<T> list;
|
||||
|
||||
/**
|
||||
* 构建分页结果
|
||||
*/
|
||||
public static <T> PageResult<T> build(Long pageNum, Long pageSize, Long total, List<T> list) {
|
||||
return new PageResult<>(pageNum, pageSize, total, list);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
package com.wjbl.worklog.common;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 统一返回结果封装
|
||||
*
|
||||
* @author WorkLog Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "统一返回结果")
|
||||
public class Result<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "响应码 (200:成功, 400:客户端错误, 500:服务器错误)")
|
||||
private Integer code;
|
||||
|
||||
@Schema(description = "响应消息")
|
||||
private String message;
|
||||
|
||||
@Schema(description = "响应数据")
|
||||
private T data;
|
||||
|
||||
@Schema(description = "是否成功")
|
||||
private Boolean success;
|
||||
|
||||
/**
|
||||
* 成功响应 (无数据)
|
||||
*/
|
||||
public static <T> Result<T> success() {
|
||||
return new Result<>(200, "操作成功", null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应 (带数据)
|
||||
*/
|
||||
public static <T> Result<T> success(T data) {
|
||||
return new Result<>(200, "操作成功", data, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应 (自定义消息和数据)
|
||||
*/
|
||||
public static <T> Result<T> success(String message, T data) {
|
||||
return new Result<>(200, message, data, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应
|
||||
*/
|
||||
public static <T> Result<T> fail(String message) {
|
||||
return new Result<>(400, message, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应 (自定义错误码)
|
||||
*/
|
||||
public static <T> Result<T> fail(Integer code, String message) {
|
||||
return new Result<>(code, message, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务器错误
|
||||
*/
|
||||
public static <T> Result<T> error(String message) {
|
||||
return new Result<>(500, message, null, false);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.wjbl.worklog.common.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*
|
||||
* @author WorkLog Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Getter
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
public BusinessException(String message) {
|
||||
super(message);
|
||||
this.code = 400;
|
||||
}
|
||||
|
||||
public BusinessException(Integer code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public BusinessException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.code = 400;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
package com.wjbl.worklog.common.exception;
|
||||
|
||||
import com.wjbl.worklog.common.Result;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
*
|
||||
* @author WorkLog Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*/
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public Result<?> handleBusinessException(BusinessException e) {
|
||||
log.error("业务异常: {}", e.getMessage());
|
||||
return Result.fail(e.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数校验异常 (Validated)
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public Result<?> handleValidationException(MethodArgumentNotValidException e) {
|
||||
String message = e.getBindingResult().getFieldErrors().stream()
|
||||
.map(FieldError::getDefaultMessage)
|
||||
.collect(Collectors.joining(", "));
|
||||
log.error("参数校验失败: {}", message);
|
||||
return Result.fail(400, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数绑定异常
|
||||
*/
|
||||
@ExceptionHandler(BindException.class)
|
||||
public Result<?> handleBindException(BindException e) {
|
||||
String message = e.getFieldErrors().stream()
|
||||
.map(FieldError::getDefaultMessage)
|
||||
.collect(Collectors.joining(", "));
|
||||
log.error("参数绑定失败: {}", message);
|
||||
return Result.fail(400, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 非法参数异常
|
||||
*/
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public Result<?> handleIllegalArgumentException(IllegalArgumentException e) {
|
||||
log.error("非法参数: {}", e.getMessage());
|
||||
return Result.fail(400, e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统异常
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public Result<?> handleException(Exception e) {
|
||||
log.error("系统异常", e);
|
||||
return Result.error("系统繁忙,请稍后重试");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package com.wjbl.worklog.config;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
/**
|
||||
* API 日志切面
|
||||
* 记录接口请求日志
|
||||
*
|
||||
* @author WorkLog Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Component
|
||||
public class ApiLogAspect {
|
||||
|
||||
/**
|
||||
* 定义切点:拦截所有 Controller
|
||||
*/
|
||||
@Pointcut("execution(* com.wjbl.worklog.controller..*.*(..))")
|
||||
public void apiLog() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 环绕通知:记录请求日志
|
||||
*/
|
||||
@Around("apiLog()")
|
||||
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// 获取请求信息
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (attributes == null) {
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
String method = request.getMethod();
|
||||
String url = request.getRequestURL().toString();
|
||||
String ip = getClientIp(request);
|
||||
Object[] args = joinPoint.getArgs();
|
||||
|
||||
// 记录请求日志
|
||||
log.info("API 请求 - Method: {}, URL: {}, IP: {}, Args: {}",
|
||||
method, url, ip, JSONUtil.toJsonStr(args));
|
||||
|
||||
// 执行方法
|
||||
Object result = joinPoint.proceed();
|
||||
|
||||
// 计算响应时间
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
|
||||
// 记录响应日志
|
||||
log.info("API 响应 - Method: {}, URL: {}, Duration: {}ms",
|
||||
method, url, duration);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端真实IP
|
||||
*/
|
||||
private String getClientIp(HttpServletRequest request) {
|
||||
String ip = request.getHeader("X-Forwarded-For");
|
||||
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
}
|
||||
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.wjbl.worklog.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* MyBatis-Plus 配置
|
||||
*
|
||||
* @author WorkLog Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
/**
|
||||
* 配置 MyBatis-Plus 插件
|
||||
*/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
|
||||
// 分页插件
|
||||
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
|
||||
paginationInterceptor.setMaxLimit(1000L);
|
||||
interceptor.addInnerInterceptor(paginationInterceptor);
|
||||
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package com.wjbl.worklog.config;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
/**
|
||||
* 链路追踪拦截器
|
||||
* 为每个请求生成 traceId 和 spanId,并放入 MDC 上下文
|
||||
*
|
||||
* @author WorkLog Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TraceInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final String TRACE_ID = "traceId";
|
||||
private static final String SPAN_ID = "spanId";
|
||||
private static final String HEADER_TRACE_ID = "X-Trace-Id";
|
||||
private static final String HEADER_SPAN_ID = "X-Span-Id";
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
// 生成链路追踪ID
|
||||
String traceId = IdUtil.fastSimpleUUID();
|
||||
String spanId = IdUtil.fastSimpleUUID();
|
||||
|
||||
// 放入 MDC 上下文
|
||||
MDC.put(TRACE_ID, traceId);
|
||||
MDC.put(SPAN_ID, spanId);
|
||||
|
||||
// 添加到响应头
|
||||
response.setHeader(HEADER_TRACE_ID, traceId);
|
||||
response.setHeader(HEADER_SPAN_ID, spanId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
// 清理 MDC 上下文
|
||||
MDC.clear();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.wjbl.worklog.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Web MVC 配置
|
||||
*
|
||||
* @author WorkLog Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
private final TraceInterceptor traceInterceptor;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册链路追踪拦截器
|
||||
registry.addInterceptor(traceInterceptor)
|
||||
.addPathPatterns("/**");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.wjbl.worklog.controller;
|
||||
|
||||
import com.wjbl.worklog.common.Result;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 健康检查 Controller
|
||||
*
|
||||
* @author WorkLog Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Tag(name = "健康检查", description = "系统健康检查接口")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/health")
|
||||
public class HealthController {
|
||||
|
||||
@Operation(summary = "健康检查")
|
||||
@GetMapping
|
||||
public Result<Map<String, Object>> health() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("status", "UP");
|
||||
data.put("service", "worklog-api");
|
||||
data.put("version", "1.0.0");
|
||||
data.put("timestamp", LocalDateTime.now());
|
||||
return Result.success(data);
|
||||
}
|
||||
}
|
||||
128
worklog-api/src/main/resources/application.yml.example
Normal file
128
worklog-api/src/main/resources/application.yml.example
Normal file
@ -0,0 +1,128 @@
|
||||
spring:
|
||||
application:
|
||||
name: worklog-api
|
||||
jackson:
|
||||
time-zone: GMT+8
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
default-property-inclusion: non_null
|
||||
|
||||
# 数据源配置
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/worklog?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
|
||||
username: worklog
|
||||
password: Wlog@123
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
hikari:
|
||||
minimum-idle: 5
|
||||
maximum-pool-size: 20
|
||||
auto-commit: true
|
||||
idle-timeout: 600000
|
||||
max-lifetime: 1800000
|
||||
connection-timeout: 30000
|
||||
connection-test-query: SELECT 1
|
||||
|
||||
# Redis 配置
|
||||
data:
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password: zjf@123456
|
||||
database: 0
|
||||
timeout: 5000ms
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 8
|
||||
max-wait: -1ms
|
||||
max-idle: 8
|
||||
min-idle: 0
|
||||
|
||||
# Nacos 配置(可选)
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: localhost:8848
|
||||
username: nacos
|
||||
password: nacos
|
||||
namespace: worklog-dev
|
||||
group: DEFAULT_GROUP
|
||||
config:
|
||||
server-addr: localhost:8848
|
||||
username: nacos
|
||||
password: nacos
|
||||
namespace: worklog-dev
|
||||
file-extension: yml
|
||||
group: DEFAULT_GROUP
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
servlet:
|
||||
context-path: /
|
||||
|
||||
# MyBatis-Plus 配置
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: ASSIGN_ID
|
||||
logic-delete-field: deleted
|
||||
logic-delete-value: 1
|
||||
logic-not-delete-value: 0
|
||||
mapper-locations: classpath*:/mapper/**/*.xml
|
||||
type-aliases-package: com.wjbl.worklog.data.entity
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
root: INFO
|
||||
com.wjbl.worklog: DEBUG
|
||||
com.baomidou.mybatisplus: DEBUG
|
||||
com.wjbl.worklog.data.mapper: DEBUG
|
||||
file:
|
||||
path: ./logs
|
||||
pattern:
|
||||
console: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId:-}][%X{spanId:-}] %-5level %logger{50} - %msg%n'
|
||||
file: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId:-}][%X{spanId:-}] %-5level %logger{50} - %msg%n'
|
||||
|
||||
# SpringDoc 配置
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
path: /swagger-ui.html
|
||||
tags-sorter: alpha
|
||||
api-docs:
|
||||
path: /api-docs
|
||||
group-configs:
|
||||
- group: 'default'
|
||||
paths-to-match: '/**'
|
||||
packages-to-scan: com.wjbl.worklog.controller
|
||||
|
||||
# 腾讯云 COS 配置(可选)
|
||||
tencent:
|
||||
cos:
|
||||
enabled: false
|
||||
app-id: 1308258046
|
||||
secret-id: AKIDukKfkY5LK2SbU6QTM7csugCSSDjzyiDS
|
||||
secret-key: 0lHXYIn20jDRP7ZlhNnyub3GEwObZHjw
|
||||
bucket-name: test-1308258046
|
||||
bucket-host: https://test-1308258046.cos.ap-beijing.myqcloud.com
|
||||
region: ap-beijing
|
||||
|
||||
# 应用配置
|
||||
worklog:
|
||||
# Token 配置
|
||||
token:
|
||||
expire-time: 86400 # Token 有效期(秒),默认 24 小时
|
||||
prefix: "auth:token:"
|
||||
|
||||
# 日志内容限制
|
||||
log:
|
||||
max-content-length: 2000 # 最大 2000 汉字
|
||||
|
||||
# 文件上传配置(如果不使用 COS)
|
||||
upload:
|
||||
enabled: true
|
||||
base-path: ./uploads
|
||||
max-file-size: 10MB
|
||||
allowed-extensions: jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx
|
||||
57
worklog-api/src/main/resources/logback-spring.xml
Normal file
57
worklog-api/src/main/resources/logback-spring.xml
Normal file
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<!-- 日志文件存储路径 -->
|
||||
<property name="LOG_PATH" value="logs"/>
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{spanId}] %-5level %logger{50} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 应用日志文件输出 -->
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_PATH}/app.log</file>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{spanId}] %-5level %logger{50} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_PATH}/app-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
|
||||
<!-- SQL日志文件输出 -->
|
||||
<appender name="SQL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_PATH}/sql.log</file>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{spanId}] %-5level %logger{50} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_PATH}/sql-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
|
||||
<!-- MyBatis-Plus SQL日志 -->
|
||||
<logger name="com.wjbl.worklog.data.mapper" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="SQL_FILE"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<!-- 根日志级别 -->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
</root>
|
||||
</configuration>
|
||||
Loading…
x
Reference in New Issue
Block a user