From ae33bd4d6a5580d749688a2aa5a2e189c34de1d0 Mon Sep 17 00:00:00 2001 From: zhangjf Date: Tue, 24 Feb 2026 14:47:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9D=E5=A7=8B=E5=8C=96=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=97=A5=E5=BF=97=E6=9C=8D=E5=8A=A1=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 项目文档:PRD、SRS、架构设计文档、前后端详细设计文档、架构设计规范 - 数据库脚本:用户创建和数据库初始化脚本 - 后端框架:Spring Boot 3.2 + MyBatis-Plus 3.5 基础架构 - 公共组件:统一返回结果、分页封装、全局异常处理 - 基础功能:链路追踪、API日志、健康检查接口 - 配置文件:application.yml.example 配置模板 - 开发规范:遵循架构设计规范,data目录存放MyBatis-Plus文件 --- .gitignore | 89 ++ application-dev.yml.example | 125 ++ bootstrap.yml.example | 35 + doc/产品需求文档PRD.md | 157 ++ doc/前端详细设计文档.md | 667 +++++++++ doc/后端模块详细设计文档.md | 413 ++++++ doc/架构设计文档.md | 1131 +++++++++++++++ doc/规范/架构设计规范.md | 1276 +++++++++++++++++ doc/需求规格说明书SRS.md | 113 ++ sql/create_user.sql | 86 ++ sql/init_database.sql | 206 +++ worklog-api/README.md | 162 +++ worklog-api/pom.xml | 129 ++ .../com/wjbl/worklog/WorklogApplication.java | 24 + .../com/wjbl/worklog/common/PageResult.java | 43 + .../java/com/wjbl/worklog/common/Result.java | 77 + .../common/exception/BusinessException.java | 35 + .../exception/GlobalExceptionHandler.java | 73 + .../com/wjbl/worklog/config/ApiLogAspect.java | 82 ++ .../worklog/config/MybatisPlusConfig.java | 32 + .../wjbl/worklog/config/TraceInterceptor.java | 49 + .../com/wjbl/worklog/config/WebMvcConfig.java | 26 + .../worklog/controller/HealthController.java | 35 + .../main/resources/application.yml.example | 128 ++ .../src/main/resources/logback-spring.xml | 57 + 25 files changed, 5250 insertions(+) create mode 100644 .gitignore create mode 100644 application-dev.yml.example create mode 100644 bootstrap.yml.example create mode 100644 doc/产品需求文档PRD.md create mode 100644 doc/前端详细设计文档.md create mode 100644 doc/后端模块详细设计文档.md create mode 100644 doc/架构设计文档.md create mode 100644 doc/规范/架构设计规范.md create mode 100644 doc/需求规格说明书SRS.md create mode 100644 sql/create_user.sql create mode 100644 sql/init_database.sql create mode 100644 worklog-api/README.md create mode 100644 worklog-api/pom.xml create mode 100644 worklog-api/src/main/java/com/wjbl/worklog/WorklogApplication.java create mode 100644 worklog-api/src/main/java/com/wjbl/worklog/common/PageResult.java create mode 100644 worklog-api/src/main/java/com/wjbl/worklog/common/Result.java create mode 100644 worklog-api/src/main/java/com/wjbl/worklog/common/exception/BusinessException.java create mode 100644 worklog-api/src/main/java/com/wjbl/worklog/common/exception/GlobalExceptionHandler.java create mode 100644 worklog-api/src/main/java/com/wjbl/worklog/config/ApiLogAspect.java create mode 100644 worklog-api/src/main/java/com/wjbl/worklog/config/MybatisPlusConfig.java create mode 100644 worklog-api/src/main/java/com/wjbl/worklog/config/TraceInterceptor.java create mode 100644 worklog-api/src/main/java/com/wjbl/worklog/config/WebMvcConfig.java create mode 100644 worklog-api/src/main/java/com/wjbl/worklog/controller/HealthController.java create mode 100644 worklog-api/src/main/resources/application.yml.example create mode 100644 worklog-api/src/main/resources/logback-spring.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5aabc80 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/application-dev.yml.example b/application-dev.yml.example new file mode 100644 index 0000000..1b17f9b --- /dev/null +++ b/application-dev.yml.example @@ -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 diff --git a/bootstrap.yml.example b/bootstrap.yml.example new file mode 100644 index 0000000..717da07 --- /dev/null +++ b/bootstrap.yml.example @@ -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 diff --git a/doc/产品需求文档PRD.md b/doc/产品需求文档PRD.md new file mode 100644 index 0000000..61d0afa --- /dev/null +++ b/doc/产品需求文档PRD.md @@ -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 人员信息管理模块 +**目标:** 维护系统使用者的基础档案与账号状态。 + +| 功能点 | 详细描述 | 字段/规则 | +| :--- | :--- | :--- | +| **人员列表** | 展示所有人员信息,支持搜索与筛选。 | 支持按姓名、职位、状态(启用/禁用)筛选。 | +| **新增人员** | 管理员创建新账号。 | **必填:** 姓名、登陆账号、登陆密码、职位
**选填:** 联系方式、电子邮箱、描述
**默认:** 状态为“启用” | +| **编辑人员** | 修改现有人员信息。 | 管理员可修改所有字段;用户本人仅可修改联系方式、邮箱、描述。**账号与密码由管理员重置。** | +| **删除人员** | 物理或逻辑删除人员。 | 建议采用**逻辑删除**(标记为离职),保留历史日志关联。 | +| **启用/禁用** | 控制账号登陆权限。 | 禁用后,该账号无法登陆,历史数据保留。 | +| **安全要求** | 密码存储与传输。 | 密码需加密存储(如 BCrypt),传输全程 HTTPS。 | + +### 3.2 工作日志信息管理模块 +**目标:** 核心业务模块,记录每日工作内容。 + +| 功能点 | 详细描述 | 字段/规则 | +| :--- | :--- | :--- | +| **日志列表** | 展示日志记录,支持多端查看。 | 按日期倒序排列。支持按日期范围、记录人筛选(管理员)。 | +| **新建日志** | 填写当日工作内容。 | **日志日期:** 默认当天,可补填过去日期。
**记录时间:** 系统自动获取提交时间。
**记录人:** 自动获取当前登陆用户。
**使用模板:** 可选,选择后自动填充内容框架。
**记录内容:** 支持 Markdown 编辑器,**上限 2000 汉字**(含标点)。 | +| **编辑日志** | 修改已提交的日志。 | 仅允许修改**当日**或**未锁定**的日志(具体规则可配)。
内容修改需保留版本记录(可选)。 | +| **删除日志** | 移除错误记录。 | 仅本人可删除本人日志,管理员可删除任意日志。需二次确认。 | +| **查看详情** | 阅读日志具体内容。 | 渲染 Markdown 格式,显示模板名称(如有)。 | +| **数据校验** | 内容长度限制。 | 前端与后端双重校验,超过 2K 汉字禁止提交并提示。 | + +### 3.3 日志模板管理模块 +**目标:** 标准化日志格式,提高填写效率。 + +| 功能点 | 详细描述 | 字段/规则 | +| :--- | :--- | :--- | +| **模板列表** | 展示所有可用模板。 | 显示模板名称、状态、更新时间。 | +| **新增模板** | 创建新的日志格式。 | **模板名称:** 唯一标识(如“研发日报”、“销售周报”)。
**模板内容:** 支持 Markdown 语法的预设文本。
**使用说明:** 指导员工如何填写该模板。 | +| **编辑模板** | 更新模板内容。 | 修改后,新建日志时生效,不影响历史已提交的日志内容。 | +| **删除模板** | 移除废弃模板。 | 若模板已被日志引用,建议仅“禁用”而非物理删除。 | +| **启用/禁用** | 控制模板在前端的可见性。 | 禁用后,员工新建日志时无法选择该模板。 | + +### 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 导入/导出功能,便于初期数据初始化。 | + +--- + +**审批签字:** + +产品经理:________________ 日期:________________ +技术负责人:______________ 日期:________________ +业务方代表:______________ 日期:________________ \ No newline at end of file diff --git a/doc/前端详细设计文档.md b/doc/前端详细设计文档.md new file mode 100644 index 0000000..1eb79aa --- /dev/null +++ b/doc/前端详细设计文档.md @@ -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` 的统一返回结构 + - 鉴权方式: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>('/user/page', { params }), + + // 创建用户 + create: (data: UserCreateDTO) => + request.post('/user', data), + + // 更新用户 + update: (id: string, data: UserUpdateDTO) => + request.put(`/user/${id}`, data), + + // 删除用户 + delete: (id: string) => + request.delete(`/user/${id}`) +} +``` + +4. **组件使用示例**: + +```typescript +// views/user/UserList.vue + +``` + +### 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` 结构 + - 当 `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>('/log/page', { params }), + + // 创建日志 + create: (data: LogCreateDTO) => + request.post('/log', data), + + // 查看日志详情 + detail: (id: string) => + request.get(`/log/${id}`), + + // 更新日志 + update: (id: string, data: LogUpdateDTO) => + request.put(`/log/${id}`, data), + + // 删除日志 + delete: (id: string) => + request.delete(`/log/${id}`) +} +``` + +4. **组件使用示例**: + +```typescript +// views/log/LogList.vue + +``` + +### 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` 结构:`{ code, message, data, success }` +- 分页接口返回 `PageResult` 结构:`{ pageNum, pageSize, total, list }` +- 认证接口约定:登录成功后,后端返回 Token 与用户信息,由前端存储并在后续请求中附加 `Authorization` 头 +- 错误码约定: + - `200`:成功 + - `400`:参数错误(前端需校验并提示) + - `401`:未授权/Token 无效(前端应跳转登录) + - `403`:权限不足(前端提示“无权操作”) + - `500`:服务器内部错误 diff --git a/doc/后端模块详细设计文档.md b/doc/后端模块详细设计文档.md new file mode 100644 index 0000000..17f2271 --- /dev/null +++ b/doc/后端模块详细设计文档.md @@ -0,0 +1,413 @@ +# 工作日志服务平台 - 后端模块详细设计文档 + +**版本号**:V1.0 +**编写日期**:2026-02-24 +**文档状态**:初稿 +**适用范围**:后端核心业务模块详细设计 + +--- + +## 1. 文档说明 + +本文档在总体架构的基础上,对核心业务模块进行更细粒度的设计,包括职责划分、主要流程、类结构与接口协作方式。各模块均遵循《架构设计规范》中关于目录结构、命名规范和分层规则。 + +### 1.1 基础模块说明 + +**当前项目架构**: + +本项目采用单体应用架构,所有模块(Controller、Service、DataService、Mapper等)均在同一个应用 `worklog-api` 中。 + +**基础模块使用规范**: + +按照架构设计规范,如果项目抽取公共能力到独立的 `common` 模块或其他基础模块,必须遵循以下规范: + +1. **强制要求**:基础模块必须采用 Maven/Gradle 项目依赖方式引入 + +```xml + + + com.wjbl.worklog + worklog-common + ${project.version} + +``` + +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` 和 `ServiceImpl` + - 封装用户的 CRUD 与常用查询(如按状态统计)。 + +- **Mapper 层**:`UserMapper` + - 继承 `BaseMapper`,可定义复杂查询 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`,封装模板数据访问逻辑。 + +- **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`:统一接口返回格式,包含:`code`、`message`、`data`、`success`。 +- `PageResult`:分页返回结构,包含:`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 | 从架构设计文档中独立模块详细设计 | ______ | diff --git a/doc/架构设计文档.md b/doc/架构设计文档.md new file mode 100644 index 0000000..81e53f9 --- /dev/null +++ b/doc/架构设计文档.md @@ -0,0 +1,1131 @@ +# 工作日志服务平台 - 架构设计文档 + +**版本号**:V1.0 +**编写日期**:2026-02-24 +**文档状态**:初稿 +**项目代号**:WorkLog Platform + +--- + +## 1. 项目概述 + +### 1.1 项目背景 + +本项目旨在构建一套轻量级、多终端的工作日志服务平台,配合 2026 年公司人员管理数字化升级需求。通过规范员工日常工作记录,沉淀工作成果,并通过模板化管理提高日志填写的效率与质量。 + +### 1.2 设计目标 + +1. **规范化架构**:遵循架构设计规范,确保代码结构清晰、可维护性强 +2. **轻量高效**:聚焦核心功能,系统响应快速,维护成本低 +3. **多端支持**:PC 管理后台 + 移动端 H5,满足不同场景需求 +4. **安全可靠**:保障数据安全,符合 2026 年数据安全合规要求 + +### 1.3 技术选型 + +| 层级 | 技术选型 | 说明 | +|------|----------|------| +| **后端框架** | Spring Boot 3.2 | 主流 Java 开发框架 | +| **数据库** | MySQL 8.0 | 关系型数据库 | +| **缓存** | Redis 7.x | 缓存 Token、提升性能 | +| **ORM框架** | MyBatis-Plus 3.5 | 简化数据访问层开发 | +| **管理后台** | Vue 3 + TypeScript + Element Plus | 现代化前端技术栈 | +| **移动端** | Vue 3 + Vite 5 + Vant 4 | H5 移动端解决方案 | + +--- + +## 2. 系统架构设计 + +### 2.1 整体架构 + +```mermaid +graph TB + subgraph "客户端层" + A1[管理后台 Web] + A2[移动端 H5] + end + + subgraph "应用层" + B1[worklog-api
统一服务] + end + + subgraph "数据层" + C1[(MySQL 8.0)] + C2[(Redis 7.x)] + end + + A1 -->|HTTPS| B1 + A2 -->|HTTPS| B1 + B1 --> C1 + B1 --> C2 +``` + +**说明**: +- **当前架构**:采用单体应用架构,满足轻量化需求和快速上线目标 +- **前后端分离**:管理后台和移动端共用统一 API +- **缓存设计**:Redis 用于 Token 存储和缓存热点数据 +- **架构演进**:当前为单体应用,如未来业务增长需拆分为微服务,将遵循架构设计规范中的OpenFeign服务调用规范 + +### 2.2 应用分层架构 + +```mermaid +graph TB + A[Controller 层] --> B[Service 层] + B --> C[DataService 层] + C --> D[Mapper 层] + D --> E[(数据库)] + + style A fill:#e1f5ff + style B fill:#fff4e1 + style C fill:#e8f5e9 + style D fill:#f3e5f5 +``` + +**职责划分**: +1. **Controller 层**:接收请求,参数校验,调用 Service +2. **Service 层**:业务逻辑处理,事务控制,DTO/VO 转换 +3. **DataService 层**:封装数据访问,提供 CRUD 方法 +4. **Mapper 层**:数据库操作接口 + +--- + +## 3. 模块设计 + +### 3.1 模块划分 + +```mermaid +graph LR + A[工作日志平台] --> B[系统管理模块] + A --> C[日志管理模块] + A --> D[模板管理模块] + + B --> B1[人员管理] + B --> B2[认证授权] + + C --> C1[日志编写] + C --> C2[日志查询] + C --> C3[日志统计] + + D --> D1[模板配置] + D --> D2[模板应用] +``` + +### 3.2 目录结构设计 + +遵循架构设计规范,项目目录结构如下: + +```text +worklog-api/ +├── src/main/java/com/company/worklog/ +│ ├── WorklogApplication.java # 启动类 +│ ├── config/ # 配置类 +│ │ ├── SecurityConfig.java +│ │ ├── RedisConfig.java +│ │ └── MybatisPlusConfig.java +│ ├── controller/ # 控制器层 +│ │ ├── UserController.java +│ │ ├── LogController.java +│ │ └── TemplateController.java +│ ├── service/ # 业务服务层 +│ │ ├── UserService.java +│ │ ├── LogService.java +│ │ ├── TemplateService.java +│ │ └── impl/ +│ │ ├── UserServiceImpl.java +│ │ ├── LogServiceImpl.java +│ │ └── TemplateServiceImpl.java +│ ├── data/ # 数据访问层(遵循架构设计规范) +│ │ ├── entity/ +│ │ │ ├── User.java +│ │ │ ├── WorkLog.java +│ │ │ └── LogTemplate.java +│ │ ├── mapper/ +│ │ │ ├── UserMapper.java +│ │ │ ├── WorkLogMapper.java +│ │ │ └── LogTemplateMapper.java +│ │ └── service/ # DataService层(XxxDataService命名规范) +│ │ ├── UserDataService.java +│ │ ├── WorkLogDataService.java +│ │ ├── LogTemplateDataService.java +│ │ └── impl/ +│ │ ├── UserDataServiceImpl.java +│ │ ├── WorkLogDataServiceImpl.java +│ │ └── LogTemplateDataServiceImpl.java +│ ├── dto/ # 数据传输对象 +│ │ ├── UserCreateDTO.java +│ │ ├── UserUpdateDTO.java +│ │ ├── LogCreateDTO.java +│ │ └── TemplateCreateDTO.java +│ ├── vo/ # 视图对象 +│ │ ├── UserVO.java +│ │ ├── LogVO.java +│ │ └── TemplateVO.java +│ ├── enums/ # 枚举类 +│ │ ├── UserStatus.java +│ │ └── TemplateStatus.java +│ └── common/ # 公共类 +│ ├── Result.java +│ ├── PageResult.java +│ └── exception/ +│ └── BusinessException.java +├── src/main/resources/ +│ ├── application.yml +│ └── logback-spring.xml +└── pom.xml +``` + +**目录设计说明**: + +1. **data 目录**:按照架构设计规范,MyBatis-Plus 相关文件统一存放在 data 目录下 +2. **DataService 命名**:使用 `XxxDataService`/`XxxDataServiceImpl` 命名,避免与业务 Service 冲突 +3. **分层职责**: + - Controller → Service → DataService → Mapper + - 业务 Service 必须通过 DataService 访问数据,禁止直接注入 Mapper + +--- + +## 4. 数据库设计 + +### 4.1 数据库表设计 + +#### 4.1.1 用户表 (sys_user) + +```sql +CREATE TABLE sys_user ( + id VARCHAR(20) NOT NULL PRIMARY KEY COMMENT '用户ID', + username VARCHAR(50) NOT NULL UNIQUE COMMENT '登录账号', + password VARCHAR(100) NOT NULL COMMENT '登录密码(加密)', + 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-已删除)', + INDEX idx_username (username), + INDEX idx_status (status), + INDEX idx_deleted (deleted) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表'; +``` + +#### 4.1.2 日志模板表 (log_template) + +```sql +CREATE TABLE log_template ( + id VARCHAR(20) NOT NULL PRIMARY KEY 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-已删除)', + INDEX idx_status (status), + INDEX idx_deleted (deleted) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='日志模板表'; +``` + +#### 4.1.3 工作日志表 (work_log) + +```sql +CREATE TABLE work_log ( + id VARCHAR(20) NOT NULL PRIMARY KEY 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-已删除)', + INDEX idx_user_id (user_id), + INDEX idx_log_date (log_date), + INDEX idx_deleted (deleted), + FOREIGN KEY (user_id) REFERENCES sys_user(id), + FOREIGN KEY (template_id) REFERENCES log_template(id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工作日志表'; +``` + +### 4.2 数据库设计说明 + +1. **主键策略**:所有表主键使用 `VARCHAR(20)` 存储雪花算法生成的 19 位数字 ID +2. **审计字段**:统一包含 `created_by`、`created_time`、`updated_by`、`updated_time`、`deleted` +3. **逻辑删除**:使用 `deleted` 字段实现软删除,保留历史数据 +4. **索引设计**:为常用查询字段建立索引,提升查询性能 +5. **字符集**:统一使用 `utf8mb4`,支持 emoji 等特殊字符 + +--- + +## 5. API 接口设计 + +### 5.1 接口规范 + +遵循 RESTful 风格,URL 格式:`/api/v1/{资源}/{动作}` + +### 5.2 认证授权接口 + +#### 5.2.1 用户登录 + +```http +POST /api/v1/auth/login +Content-Type: application/json + +{ + "username": "string", + "password": "string" +} + +Response: +{ + "code": 200, + "message": "success", + "data": { + "token": "string", + "userInfo": { + "id": "string", + "username": "string", + "name": "string", + "role": "USER|ADMIN" + } + }, + "success": true +} +``` + +#### 5.2.2 用户登出 + +```http +POST /api/v1/auth/logout +Authorization: Bearer {token} + +Response: +{ + "code": 200, + "message": "success", + "data": null, + "success": true +} +``` + +### 5.3 人员管理接口 + +#### 5.3.1 人员列表(分页) + +```http +GET /api/v1/user/page?pageNum=1&pageSize=10&keyword=张三&status=1 +Authorization: Bearer {token} + +Response: +{ + "code": 200, + "message": "success", + "data": { + "pageNum": 1, + "pageSize": 10, + "total": 100, + "list": [ + { + "id": "string", + "username": "string", + "name": "string", + "phone": "string", + "email": "string", + "position": "string", + "status": 1, + "role": "USER" + } + ] + }, + "success": true +} +``` + +#### 5.3.2 创建人员 + +```http +POST /api/v1/user +Authorization: Bearer {token} +Content-Type: application/json + +{ + "username": "string", // 必填 + "password": "string", // 必填 + "name": "string", // 必填 + "phone": "string", // 选填 + "email": "string", // 选填 + "position": "string", // 选填 + "description": "string", // 选填 + "role": "USER" // 必填 +} + +Response: +{ + "code": 200, + "message": "success", + "data": "1234567890123456789", // 用户ID + "success": true +} +``` + +#### 5.3.3 更新人员信息 + +```http +PUT /api/v1/user/{id} +Authorization: Bearer {token} +Content-Type: application/json + +{ + "name": "string", + "phone": "string", + "email": "string", + "position": "string", + "description": "string" +} + +Response: +{ + "code": 200, + "message": "success", + "data": null, + "success": true +} +``` + +#### 5.3.4 启用/禁用用户 + +```http +PUT /api/v1/user/{id}/status +Authorization: Bearer {token} +Content-Type: application/json + +{ + "status": 1 // 0-禁用,1-启用 +} + +Response: +{ + "code": 200, + "message": "success", + "data": null, + "success": true +} +``` + +#### 5.3.5 删除人员 + +```http +DELETE /api/v1/user/{id} +Authorization: Bearer {token} + +Response: +{ + "code": 200, + "message": "success", + "data": null, + "success": true +} +``` + +### 5.4 模板管理接口 + +#### 5.4.1 模板列表 + +```http +GET /api/v1/template/list?status=1 +Authorization: Bearer {token} + +Response: +{ + "code": 200, + "message": "success", + "data": [ + { + "id": "string", + "templateName": "string", + "templateContent": "string", + "instruction": "string", + "status": 1 + } + ], + "success": true +} +``` + +#### 5.4.2 创建模板 + +```http +POST /api/v1/template +Authorization: Bearer {token} +Content-Type: application/json + +{ + "templateName": "string", // 必填 + "templateContent": "string", // 必填,Markdown格式 + "instruction": "string" // 选填 +} + +Response: +{ + "code": 200, + "message": "success", + "data": "1234567890123456789", + "success": true +} +``` + +### 5.5 日志管理接口 + +#### 5.5.1 日志列表(分页) + +```http +GET /api/v1/log/page?pageNum=1&pageSize=10&startDate=2026-01-01&endDate=2026-01-31 +Authorization: Bearer {token} + +Response: +{ + "code": 200, + "message": "success", + "data": { + "pageNum": 1, + "pageSize": 10, + "total": 50, + "list": [ + { + "id": "string", + "userId": "string", + "userName": "string", + "logDate": "2026-01-15", + "contentPreview": "今日完成...", + "templateName": "研发日报", + "recordTime": "2026-01-15 18:30:00" + } + ] + }, + "success": true +} +``` + +#### 5.5.2 创建日志 + +```http +POST /api/v1/log +Authorization: Bearer {token} +Content-Type: application/json + +{ + "logDate": "2026-01-15", // 必填 + "content": "string", // 必填,最大2000汉字 + "templateId": "string" // 选填 +} + +Response: +{ + "code": 200, + "message": "success", + "data": "1234567890123456789", + "success": true +} +``` + +#### 5.5.3 查看日志详情 + +```http +GET /api/v1/log/{id} +Authorization: Bearer {token} + +Response: +{ + "code": 200, + "message": "success", + "data": { + "id": "string", + "userId": "string", + "userName": "string", + "logDate": "2026-01-15", + "content": "string", // Markdown格式 + "contentHtml": "string", // 渲染后的HTML + "templateId": "string", + "templateName": "string", + "recordTime": "2026-01-15 18:30:00" + }, + "success": true +} +``` + +#### 5.5.4 更新日志 + +```http +PUT /api/v1/log/{id} +Authorization: Bearer {token} +Content-Type: application/json + +{ + "content": "string", + "templateId": "string" +} + +Response: +{ + "code": 200, + "message": "success", + "data": null, + "success": true +} +``` + +#### 5.5.5 删除日志 + +```http +DELETE /api/v1/log/{id} +Authorization: Bearer {token} + +Response: +{ + "code": 200, + "message": "success", + "data": null, + "success": true +} +``` + +--- + +## 6. 安全设计 + +### 6.1 认证机制 + +采用 **UUID Token + Redis** 方案: + +1. **Token 生成**:用户登录成功后,生成 UUID 作为 Token +2. **Token 存储**:Token 存储到 Redis,Key 为 `auth:token:{token}`,Value 为用户信息 +3. **Token 有效期**:24 小时,支持续期 +4. **Token 传递**:请求头 `Authorization: Bearer {token}` + +```java +// Token 存储结构 +Key: auth:token:{token} +Value: { + "userId": "string", + "username": "string", + "role": "USER|ADMIN", + "expireTime": timestamp +} +TTL: 24小时 +``` + +### 6.2 权限控制 + +| 角色 | 权限范围 | +|------|----------| +| **普通用户** | 1. 查看/修改本人信息
2. 创建/查看/编辑/删除本人日志
3. 查看启用的模板 | +| **管理员** | 1. 普通用户所有权限
2. 人员管理(全部权限)
3. 模板管理(全部权限)
4. 查看所有日志 | + +### 6.3 数据安全 + +1. **密码加密**:使用 BCrypt 算法加密存储,加密强度 10 +2. **传输加密**:全站 HTTPS,防止中间人攻击 +3. **SQL 防注入**:使用 MyBatis-Plus 预编译,防止 SQL 注入 +4. **XSS 防护**:Markdown 渲染时进行 HTML 过滤 +5. **敏感信息脱敏**:日志中不记录密码等敏感信息 + +### 6.4 接口鉴权 + +```java +// 白名单路径(无需Token) +/api/v1/auth/login +/api/v1/auth/health + +// 需要认证的路径 +/api/v1/** + +// 需要管理员权限的路径 +/api/v1/user/** +/api/v1/template/** +``` + +--- + +## 7. 日志与追踪设计 + +### 7.1 日志配置 + +按照架构设计规范,日志配置如下: + +```text +logs/ +├── app.log # 应用主日志(所有级别) +├── error.log # ERROR 级别日志 +├── sql.log # MyBatis-Plus SQL 日志 +└── aop.log # API 请求日志 +``` + +### 7.2 链路追踪 + +所有日志必须包含 TraceId 和 SpanId: + +```text +日志格式: +%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId:-}][%X{spanId:-}] %-5level %logger{50} - %msg%n + +日志示例: +2026-02-24 10:15:30.123 [http-nio-8080-exec-1] [a1b2c3d4...][1234abcd] INFO c.c.w.controller.LogController - 创建日志成功 +``` + +### 7.3 HTTP Header 透传 + +| Header | 说明 | +|--------|------| +| `X-Trace-Id` | 全链路追踪 ID | +| `X-Span-Id` | 当前服务 Span | +| `X-User-Id` | 用户 ID | +| `X-Username` | 用户名 | + +--- + +## 8. 性能优化设计 + +### 8.1 缓存策略 + +使用 Redis 缓存以下数据: + +1. **Token 信息**:`auth:token:{token}`,TTL 24小时 +2. **启用的模板列表**:`template:enabled:list`,TTL 30分钟 +3. **用户基本信息**:`user:info:{userId}`,TTL 1小时 + +### 8.2 数据库优化 + +1. **索引优化**:为常用查询字段建立索引 +2. **分页查询**:使用 MyBatis-Plus 分页插件 +3. **字段限制**:日志内容最大 2000 汉字,防止数据膨胀 +4. **连接池配置**:HikariCP,最小连接数 5,最大连接数 20 + +### 8.3 前端优化 + +1. **列表分页**:默认每页 10 条,支持 10/20/50 切换 +2. **日志预览**:列表仅显示前 50 字摘要 +3. **Markdown 渲染**:使用客户端渲染,减轻服务器压力 +4. **图片懒加载**:大列表场景下使用懒加载 + +--- + +## 9. 部署架构 + +### 9.1 服务器配置 + +**最小配置**(支持 50 人同时在线): +- CPU: 2 核 +- 内存: 4GB +- 磁盘: 50GB SSD +- 带宽: 5Mbps + +**推荐配置**(支持 500 人同时在线): +- CPU: 4 核 +- 内存: 8GB +- 磁盘: 100GB SSD +- 带宽: 10Mbps + +### 9.2 部署结构 + +```text +/opt/worklog/ +├── worklog-api/ # 后端服务 +│ ├── bin/ +│ │ ├── start.sh +│ │ ├── stop.sh +│ │ └── restart.sh +│ ├── lib/ +│ │ └── worklog-api.jar +│ └── conf/ +│ ├── application.yml +│ └── logback-spring.xml +├── worklog-admin/ # 管理后台前端 +│ └── dist/ +├── worklog-mobile/ # 移动端前端 +│ └── dist/ +└── logs/ # 日志目录 + ├── app.log + ├── error.log + └── sql.log +``` + +### 9.3 Nginx 配置 + +```nginx +server { + listen 80; + server_name worklog.company.com; + + # 管理后台 + location /admin/ { + alias /opt/worklog/worklog-admin/dist/; + try_files $uri $uri/ /admin/index.html; + } + + # 移动端 + location /mobile/ { + alias /opt/worklog/worklog-mobile/dist/; + try_files $uri $uri/ /mobile/index.html; + } + + # 后端API + location /api/ { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` + +### 9.4 开发环境配置 + +本节提供开发环境的配置信息,用于本地开发和测试。 + +#### 9.4.1 MySQL 配置 + +```yaml +# application-dev.yml +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/worklog?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai&useSSL=false + username: worklog + password: Wlog@123 +``` + +**配置说明**: +- **主机地址**:localhost +- **端口**:3306 +- **数据库名**:worklog +- **数据库用户**:worklog +- **数据库密码**:Wlog@123 +- **认证插件**:caching_sha2_password(MySQL 8.0 默认) +- **安装模式**:service(系统服务方式) + +**数据库用户创建**: + +```sql +-- 创建数据库用户 +CREATE USER 'worklog'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'Wlog@123'; +CREATE USER '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; +``` + +#### 9.4.2 Redis 配置 + +```yaml +# application-dev.yml +spring: + 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 +``` + +**配置说明**: +- **主机地址**:localhost +- **端口**:6379 +- **密码**:zjf@123456 +- **数据库索引**:0(默认使用 DB0) +- **安装模式**:service(系统服务方式) + +#### 9.4.3 Nacos 配置 + +```yaml +# bootstrap.yml +spring: + cloud: + nacos: + discovery: + server-addr: localhost:8848 + username: nacos + password: nacos + namespace: worklog-dev + config: + server-addr: localhost:8848 + username: nacos + password: nacos + namespace: worklog-dev + file-extension: yml + group: DEFAULT_GROUP +``` + +**配置说明**: +- **主机地址**:localhost +- **服务端口**:8848 +- **控制台端口**:8048 +- **用户名**:nacos +- **密码**:nacos +- **安装路径**:/home/along/MyApp/nacos +- **安装模式**:local(本地单机模式) +- **命名空间**:worklog-dev(开发环境隔离) + +**访问地址**: +- Nacos 控制台:http://localhost:8048/nacos + +#### 9.4.4 腾讯云 COS 配置(可选) + +如项目需要使用对象存储服务,可配置腾讯云 COS: + +```yaml +# application-dev.yml +tencent: + cos: + 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 +``` + +**配置说明**: +- **AppId**:1308258046 +- **SecretId**:AKIDukKfkY5LK2SbU6QTM7csugCSSDjzyiDS +- **SecretKey**:0lHXYIn20jDRP7ZlhNnyub3GEwObZHjw +- **存储桶名称**:test-1308258046 +- **访问域名**:https://test-1308258046.cos.ap-beijing.myqcloud.com +- **所属地域**:ap-beijing(北京) + +**用途说明**: +- 用于存储用户上传的附件、图片等文件(V2.0 功能) +- 开发环境使用测试存储桶 +- 生产环境需配置独立的生产存储桶 + +#### 9.4.5 环境配置文件管理 + +开发环境配置文件建议按以下结构组织: + +```text +src/main/resources/ +├── application.yml # 主配置文件 +├── application-dev.yml # 开发环境配置 +├── application-test.yml # 测试环境配置 +├── application-prod.yml # 生产环境配置 +├── bootstrap.yml # 启动配置(Nacos) +└── logback-spring.xml # 日志配置 +``` + +**配置优先级**: +1. bootstrap.yml(最先加载,用于 Nacos 配置中心) +2. application.yml(通用配置) +3. application-{profile}.yml(环境特定配置,覆盖通用配置) + +**激活开发环境**: + +```yaml +# application.yml +spring: + profiles: + active: dev +``` + +或通过启动参数: + +```bash +java -jar worklog-api.jar --spring.profiles.active=dev +``` + +#### 9.4.6 安全注意事项 + +⚠️ **重要提示**: + +1. **密码安全**: + - 开发环境配置文件不应提交到代码仓库 + - 使用 `.gitignore` 忽略 `application-*.yml` 文件 + - 敏感信息建议使用环境变量或配置中心管理 + +2. **配置示例**: + ```gitignore + # .gitignore + application-dev.yml + application-test.yml + application-prod.yml + ``` + +3. **配置模板**: + - 提供 `application-dev.yml.example` 作为模板 + - 开发人员复制并修改为 `application-dev.yml` + - 填入本地实际配置信息 + +--- + +## 10. 数据备份与恢复 + +### 10.1 备份策略 + +1. **增量备份**:每日凌晨 2:00 自动执行 +2. **全量备份**:每周日凌晨 1:00 执行 +3. **备份保留**:增量备份保留 7 天,全量备份保留 4 周 + +### 10.2 备份脚本示例 + +```bash +#!/bin/bash +# 数据库备份脚本 + +BACKUP_DIR="/backup/mysql" +DATE=$(date +%Y%m%d_%H%M%S) +DB_NAME="worklog" + +# 全量备份 +mysqldump -u root -p${MYSQL_PASSWORD} \ + --single-transaction \ + --routines \ + --triggers \ + ${DB_NAME} > ${BACKUP_DIR}/full_${DATE}.sql + +# 压缩 +gzip ${BACKUP_DIR}/full_${DATE}.sql +``` + +--- + +## 11. 监控与运维 + +### 11.1 监控指标 + +| 指标类型 | 监控项 | 阈值 | +|----------|--------|------| +| **系统指标** | CPU 使用率 | < 80% | +| | 内存使用率 | < 80% | +| | 磁盘使用率 | < 85% | +| **应用指标** | 接口响应时间 | < 1000ms | +| | 错误率 | < 1% | +| | QPS | 记录峰值 | +| **数据库指标** | 连接数 | < 80% | +| | 慢查询 | < 10/min | +| | 锁等待 | < 5s | + +### 11.2 告警机制 + +1. **系统告警**:CPU/内存/磁盘超阈值 +2. **应用告警**:接口错误率高、响应慢 +3. **业务告警**:登录失败次数过多 +4. **告警方式**:邮件 + 短信(紧急) + +--- + +## 12. 风险与应对 + +### 12.1 技术风险 + +| 风险点 | 描述 | 应对策略 | +|--------|------|----------| +| **单点故障** | 单体应用,服务器故障导致全站不可用 | 1. 做好数据备份
2. 准备灾备服务器
3. 快速恢复流程 | +| **数据丢失** | 数据库故障或误操作 | 1. 每日自动备份
2. 采用逻辑删除
3. 主从复制(可选) | +| **性能瓶颈** | 并发量超预期 | 1. 增加缓存
2. 数据库读写分离
3. 升级服务器配置 | + +### 12.2 业务风险 + +| 风险点 | 描述 | 应对策略 | +|--------|------|----------| +| **字数限制** | 2000 字可能不够用 | 1. V1.0 严格执行
2. 收集用户反馈
3. V2.0 评估是否支持附件 | +| **移动端体验** | Markdown 编辑体验差 | 1. 提供简化工具栏
2. 支持富文本模式
3. 优化键盘交互 | +| **权限管理** | 角色权限过于简单 | 1. V1.0 满足基本需求
2. V2.0 增加部门权限 | + +--- + +## 13. 开发计划 + +### 13.1 开发阶段 + +| 阶段 | 时间 | 交付物 | 负责人 | +|------|------|--------|--------| +| **阶段一** | 第1月 | 数据库设计、后端框架搭建、认证模块 | 后端 | +| **阶段二** | 第2月 | 人员管理、日志管理、模板管理接口 | 后端 | +| **阶段三** | 第2-3月 | 管理后台前端开发 | 前端 | +| **阶段四** | 第3月 | 移动端 H5 开发 | 前端 | +| **阶段五** | 第3月 | 联调测试、性能优化、安全加固 | 全员 | +| **上线** | 第4月初 | 生产环境部署、用户培训 | 运维 | + +### 13.2 里程碑 + +- **M1(第1月末)**:后端核心接口完成,Postman 测试通过 +- **M2(第2月末)**:管理后台基本功能完成,内部试用 +- **M3(第3月末)**:移动端完成,全功能测试通过 +- **M4(第4月初)**:正式上线,全员推广 + +--- + +## 14. 附录 + +### 15.1 技术栈版本清单 + +| 技术 | 版本 | 说明 | +|------|------|------| +| JDK | 21 | LTS 版本 | +| Spring Boot | 3.2.x | 最新稳定版 | +| MyBatis-Plus | 3.5.x | 数据访问框架 | +| MySQL | 8.0.x | 数据库 | +| Redis | 7.x | 缓存 | +| Vue | 3.x | 前端框架 | +| Element Plus | 2.x | 管理后台 UI | +| Vant | 4.x | 移动端 UI | + +### 15.2 相关文档 + +- [产品需求文档 PRD](./产品需求文档PRD.md) +- [需求规格说明书 SRS](./需求规格说明书SRS.md) +- [架构设计规范](./规范/架构设计规范.md) +- [后端模块详细设计文档](./后端模块详细设计文档.md) +- [前端详细设计文档](./前端详细设计文档.md) + +### 15.3 架构设计规范遵循说明 + +本项目严格遵循《架构设计规范》的各项要求: + +| 规范项 | 遵循说明 | 文档位置 | +|--------|----------|----------| +| **模块目录结构** | data 目录统一存放 MyBatis-Plus 相关文件 | 3.2节 目录结构设计 | +| **DataService 命名** | 使用 XxxDataService/XxxDataServiceImpl 命名规范 | 3.2节、后端模块详细设计 | +| **分层职责** | Controller → Service → DataService → Mapper | 2.2节 应用分层架构 | +| **主键策略** | VARCHAR(20) 存储雪花算法生成的19位ID | 4.1节 数据库表设计 | +| **审计字段** | 统一包含 created_by、created_time、updated_by、updated_time、deleted | 4.1节 数据库表设计 | +| **日志配置** | app.log、sql.log、error.log 扁平目录结构 | 7.1节 日志配置 | +| **链路追踪** | 所有日志包含 traceId 和 spanId | 7.2节 链路追踪 | +| **API 设计** | RESTful 风格,统一 Result 返回格式 | 5节 API 接口设计 | +| **前端 API 管理** | 独立 api 目录,禁止硬编码 URL | 前端详细设计文档 2.1节、3.1节 | +| **密码加密** | BCrypt 算法加密存储 | 6.3节 数据安全 | +| **Token 认证** | UUID Token + Redis,有效期24小时 | 6.1节 认证机制 | +| **架构演进** | 当前单体应用,未来微服务遵循 OpenFeign 规范 | 2.1节 整体架构 | +| **基础模块依赖** | 如抽取 common 模块,必须通过 Maven/Gradle 依赖 | 后端模块详细设计 1.1节 | + +--- + +**文档审批** + +| 角色 | 姓名 | 签字 | 日期 | +|------|------|------|------| +| 架构师 | ______ | ______ | ______ | +| 技术负责人 | ______ | ______ | ______ | +| 产品经理 | ______ | ______ | ______ | + +**变更记录** + +| 版本 | 日期 | 变更内容 | 变更人 | +|------|------|----------|--------| +| V1.0 | 2026-02-24 | 初稿完成 | ______ | diff --git a/doc/规范/架构设计规范.md b/doc/规范/架构设计规范.md new file mode 100644 index 0000000..960772a --- /dev/null +++ b/doc/规范/架构设计规范.md @@ -0,0 +1,1276 @@ +## 架构设计规范 + +> 本文档基于参考文档 `参考Agents.md` 中的内容,抽取并整理出**与具体业务项目无关**的通用架构设计约定,可复用到其它类似的微服务项目中。 + +--- + +### 1. 后端模块结构规范 + +#### 1.1 服务模块目录结构 + +建议单体或微服务项目的后端服务模块遵循统一的目录结构,便于团队协作和代码治理: + +```text +service-xxx/ +├── src/main/java/com/company/project/xxx/ +│ ├── XxxApplication.java # 启动类 +│ ├── config/ # 配置类 +│ ├── controller/ # 控制器层 +│ ├── service/ # 业务服务层 +│ │ └── impl/ # 业务服务实现 +│ ├── data/ # 数据访问层(MyBatis-Plus相关) +│ │ ├── entity/ # 实体类 +│ │ ├── mapper/ # Mapper接口 +│ │ ├── service/ # XxxDataService(数据服务) +│ │ │ └── impl/ # XxxDataServiceImpl(数据服务实现) +│ │ └── xml/ # Mapper XML文件(可选) +│ ├── dto/ # 数据传输对象 +│ ├── vo/ # 视图对象 +│ └── enums/ # 枚举类 +├── src/main/resources/ +│ ├── application.yml # 主配置 +│ ├── application-docker.yml # 容器环境配置(可选) +│ └── logback-spring.xml # 日志配置 +└── pom.xml +``` + +#### 1.2 公共模块结构 + +对于多服务项目,建议抽取公共能力到独立的 `common` 模块,供各业务服务复用: + +```text +common/ +├── src/main/java/com/company/project/common/ +│ ├── auth/ # 认证相关 +│ ├── cache/ # 缓存服务 +│ ├── config/ # 通用配置 +│ ├── context/ # 上下文管理 +│ ├── core/ # 核心类(Result、PageResult 等) +│ ├── exception/ # 异常处理 +│ ├── feign/ # 远程调用拦截器 +│ ├── loadbalancer/ # 负载均衡 +│ ├── mybatis/ # ORM / 数据访问配置 +│ ├── nacos/ # 注册配置(如使用 Nacos) +│ └── web/ # Web 拦截器 +``` + +**基础模块依赖方式**: + +**强制要求**:基础模块(如 `common` 模块)必须采用 Maven/Gradle 项目依赖方式引入,不允许通过拷贝代码或 JAR 包方式使用。 + +```xml + + + com.wjbl.project + common + ${project.version} + +``` + +**优势说明**: +- 统一版本管理,避免基础代码不一致。 +- 便于基础能力的统一升级和维护。 +- 支持 Maven 的传递依赖管理。 +- 确保各业务服务使用的基础模块版本一致。 + +#### 1.3 启动类与组件扫描 + +所有业务服务建议显式配置组件扫描路径,以确保能扫描到业务模块和公共模块: + +```java +@SpringBootApplication(scanBasePackages = { + "com.wjbl.project.xxx", + "com.wjbl.project.common" +}) +@EnableDiscoveryClient +public class XxxApplication { + public static void main(String[] args) { + SpringApplication.run(XxxApplication.class, args); + } +} +``` + +**注意**:基于 WebFlux 的网关类服务不应扫描包含 Servlet 相关依赖的公共模块包,以避免 WebFlux 与 Servlet 依赖冲突。 + +--- + +### 2. 数据库设计规范 + +#### 2.1 审计字段 + +所有业务表建议统一包含以下审计字段,用于记录数据的创建与修改信息: + +```sql +created_by BIGINT NOT NULL COMMENT '创建人ID', +created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', +updated_by BIGINT 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-已删除)' +``` + +#### 2.2 主键策略 + +**强制要求**: + +1. **数据库主键字段类型**: + - 使用 `VARCHAR(20)` 或 `CHAR(19)` 字符串类型存储主键。 + - 存储内容为雪花算法(Snowflake)生成的 19 位数字 ID。 + +2. **实体类主键配置**: + ```java + @TableId(type = IdType.ASSIGN_ID) + private String id; // 注意:使用 String 类型 + ``` + +3. **雪花算法配置**: + - MyBatis-Plus 默认支持雪花算法生成 Long 类型 ID。 + - 通过自定义 IdentifierGenerator 或在插入前转换为字符串存储。 + +**优势说明**: + +- 字符串存储避免前端 JavaScript 处理长整型精度丢失问题(JS Number 最大安全整数为 2^53-1)。 +- 保持雪花算法的分布式唯一性和时间有序性优势。 +- 便于跨语言系统集成(如 Python、Go 等)。 + +#### 2.3 逻辑删除 + +- 统一使用 `deleted` 字段表示逻辑删除状态。 +- 0 = 未删除,1 = 已删除。 +- 在 ORM 框架中统一配置逻辑删除注解(例如:`@TableLogic`)。 + +--- + +### 3. MyBatis-Plus 架构规范 + +#### 3.1 数据访问层目录结构 + +MyBatis-Plus 相关的所有文件统一存放在 `data` 目录下,实现数据访问与业务逻辑的明确分离: + +```text +src/main/java/com/company/project/xxx/data/ +├── entity/ # 实体类(对应数据库表) +│ ├── Customer.java +│ └── Project.java +├── mapper/ # Mapper 接口 +│ ├── CustomerMapper.java +│ └── ProjectMapper.java +├── service/ # 数据服务接口(XxxDataService) +│ ├── CustomerDataService.java +│ └── ProjectDataService.java +└── impl/ # 数据服务实现(XxxDataServiceImpl) + ├── CustomerDataServiceImpl.java + └── ProjectDataServiceImpl.java +``` + +#### 3.2 数据服务命名规范 + +**强制要求**: + +1. **数据服务接口**:命名为 `XxxDataService`,继承 `IService` + ```java + public interface CustomerDataService extends IService { + // 数据访问相关方法 + } + ``` + +2. **数据服务实现**:命名为 `XxxDataServiceImpl`,继承 `ServiceImpl` + ```java + @Service + public class CustomerDataServiceImpl + extends ServiceImpl + implements CustomerDataService { + // 数据访问实现 + } + ``` + +3. **命名原因**: + - 避免与业务 Service(如 `CustomerService`)产生命名冲突。 + - 清晰区分数据访问层(DataService)和业务逻辑层(Service)。 + - 符合单一职责原则,DataService 只负责数据的 CRUD 操作。 + +#### 3.3 业务服务与数据服务的协作规范 + +**强制要求**: + +1. **业务 Service 必须通过 DataService 访问数据**: + ```java + @Service + @RequiredArgsConstructor + public class CustomerServiceImpl implements CustomerService { + + private final CustomerDataService customerDataService; // 注入 DataService + + @Override + public CustomerVO getCustomerById(String id) { + // 通过 DataService 查询数据 + Customer customer = customerDataService.getById(id); + // 业务逻辑处理和转换 + return convertToVO(customer); + } + } + ``` + +2. **禁止业务 Service 直接注入 Mapper**: + ```java + // ❌ 错误:业务层直接使用 Mapper + @Service + public class CustomerServiceImpl implements CustomerService { + @Autowired + private CustomerMapper customerMapper; // 不允许 + } + ``` + +3. **DataService 职责**: + - 封装所有数据库查询和操作。 + - 提供基础的 CRUD 方法(继承自 MyBatis-Plus 的 IService)。 + - 提供复杂查询的封装方法(如多表关联、统计等)。 + +4. **业务 Service 职责**: + - 实现业务逻辑和流程控制。 + - 调用一个或多个 DataService 完成数据操作。 + - 处理 DTO 到实体、实体到 VO 的转换。 + - 处理事务管理(@Transactional)。 + +#### 3.4 分层示例 + +完整的分层调用示例: + +```java +// 1. Controller 层 +@RestController +@RequestMapping("/api/v1/customer") +@RequiredArgsConstructor +public class CustomerController { + private final CustomerService customerService; + + @GetMapping("/{id}") + public Result getCustomer(@PathVariable String id) { + return Result.success(customerService.getCustomerById(id)); + } +} + +// 2. 业务 Service 层 +@Service +@RequiredArgsConstructor +public class CustomerServiceImpl implements CustomerService { + private final CustomerDataService customerDataService; + + @Override + public CustomerVO getCustomerById(String id) { + Customer customer = customerDataService.getById(id); + if (customer == null) { + throw new BusinessException("客户不存在"); + } + return BeanUtil.copyProperties(customer, CustomerVO.class); + } +} + +// 3. 数据 Service 层 +public interface CustomerDataService extends IService { + // 可添加自定义数据访问方法 + List listByStatus(Integer status); +} + +@Service +public class CustomerDataServiceImpl + extends ServiceImpl + implements CustomerDataService { + + @Override + public List listByStatus(Integer status) { + return lambdaQuery() + .eq(Customer::getStatus, status) + .list(); + } +} + +// 4. Mapper 层 +public interface CustomerMapper extends BaseMapper { + // 复杂SQL可在此定义 +} +``` + +--- + +### 4. API 设计规范 + +#### 4.1 服务间调用规范 + +**强制要求**:服务模块(对外提供 API 服务)之间的调用必须采用 OpenFeign 进行,禁止以下方式: +- ❌ 禁止通过 RestTemplate 直接调用其他服务 +- ❌ 禁止通过 HttpClient 等工具类直接调用其他服务 +- ❌ 禁止通过共享数据库方式进行服务间通信 + +**OpenFeign 调用规范**: + +1. **定义 Feign 客户端接口**: + +```java +@FeignClient(name = "service-customer", path = "/cust/api/v1") +public interface CustomerFeignClient { + + @GetMapping("/customer/{id}") + Result getCustomer(@PathVariable("id") String id); + + @PostMapping("/customer") + Result createCustomer(@RequestBody CustomerCreateDTO dto); +} +``` + +2. **服务提供方职责**: + - 确保接口遵循 RESTful 规范 + - 返回统一的 `Result` 格式 + - 接口文档及时更新(推荐使用 Swagger/OpenAPI) + +3. **服务调用方职责**: + - 通过 `@Autowired` 或 `@RequiredArgsConstructor` 注入 Feign 客户端 + - 做好异常处理和降级策略(可选配置 Fallback) + - 避免循环调用和过深的调用链 + +4. **Feign 配置建议**: + +```yaml +feign: + client: + config: + default: + connectTimeout: 5000 # 连接超时 5 秒 + readTimeout: 10000 # 读取超时 10 秒 + compression: + request: + enabled: true + mime-types: text/xml,application/xml,application/json + response: + enabled: true +``` + +5. **示例:服务间调用**: + +```java +@Service +@RequiredArgsConstructor +public class OrderServiceImpl implements OrderService { + + private final CustomerFeignClient customerFeignClient; + + @Override + public OrderVO createOrder(OrderCreateDTO dto) { + // 通过 Feign 调用客户服务获取客户信息 + Result customerResult = customerFeignClient.getCustomer(dto.getCustomerId()); + + if (!customerResult.isSuccess()) { + throw new BusinessException("客户信息获取失败:" + customerResult.getMessage()); + } + + CustomerVO customer = customerResult.getData(); + // 业务逻辑处理... + return null; + } +} +``` + +**优势说明**: +- 统一服务调用方式,便于管理和监控 +- 自动集成负载均衡(结合注册中心) +- 支持熔断降级(结合 Sentinel 或 Hystrix) +- 便于链路追踪(自动传递 TraceId、SpanId 等) +- 接口调用类型安全,编译期检查 + +#### 4.2 URL 设计 + +RESTful 风格接口推荐使用统一的 URL 模式: + +```text +# 基础格式 +/{模块}/api/v1/{资源}/{动作} + +# 示例 +POST /sys/api/v1/auth/login # 登录 +GET /cust/api/v1/customer/{id} # 查询客户 +POST /cust/api/v1/customer # 创建客户 +PUT /cust/api/v1/customer/{id} # 更新客户 +DELETE /cust/api/v1/customer/{id} # 删除客户 +GET /cust/api/v1/customer/page # 分页查询 +``` + +#### 4.3 HTTP 方法语义 + +| 方法 | 用途 | 示例 | +|--------|------|--------------------------| +| GET | 查询 | 查询列表、详情 | +| POST | 创建 | 新增资源 | +| PUT | 更新 | 修改资源信息 | +| DELETE | 删除 | 删除资源 | + +#### 4.4 统一响应格式 + +后端服务建议统一使用泛型包装类 `Result`: + +```json +{ + "code": 200, + "message": "success", + "data": { "...": "..." }, + "success": true +} +``` + +错误响应示例: + +```json +{ + "code": 400, + "message": "参数错误:xxx 不能为空", + "data": null, + "success": false +} +``` + +#### 4.5 分页数据格式 + +```json +{ + "code": 200, + "message": "success", + "data": { + "pageNum": 1, + "pageSize": 10, + "total": 100, + "list": [] + }, + "success": true +} +``` + +#### 4.6 状态码规范 + +| 状态码 | 说明 | +|--------|----------------| +| 200 | 成功 | +| 400 | 参数错误 | +| 401 | 未授权 / Token 无效 | +| 403 | 权限不足 | +| 404 | 资源不存在 | +| 500 | 服务器内部错误 | + +#### 4.7 前后端 API 管理 + +前端项目应采用**独立目录或文件集中管理**与后端交互的 API,避免在页面组件中硬编码 URL: + +```text +src/api/ +├── index.ts # API 定义入口,统一导出 +├── request.ts # Axios 实例配置、拦截器 +└── modules/ # 按业务模块拆分(可选) + ├── auth.ts # 认证相关 API + ├── customer.ts # 客户管理 API + └── expense.ts # 其它业务 API +``` + +**规范要求:** + +- **禁止硬编码**:组件中禁止直接调用 `request.get('/xxx/xxx')`。 +- **统一入口**:所有 API 函数从 `@/api` 统一导出,按模块分组。 +- **路径简化**:API 内部使用简化路径,由 `baseURL` 或拦截器统一添加前缀。 +- **便于维护**:后端接口变更时,只需修改 API 定义位置即可。 + +--- + +### 5. 链路追踪规范 + +#### 5.1 基本概念 + +- **TraceId**:全链路唯一标识,一次请求从入口到各个下游服务共享同一个 TraceId。 +- **SpanId**:链路中单个服务或调用节点的唯一标识,用于区分各个调用段。 + +#### 5.2 日志格式 + +**强制要求**:所有日志必须包含 TraceId 和 SpanId,便于进行日志跟踪和问题排查。 + +推荐在日志格式中统一输出 TraceId 与 SpanId: + +```text +%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId:-}][%X{spanId:-}] %-5level %logger{50} - %msg%n +``` + +日志示例: + +```text +2026-02-20 18:10:25.240 [http-nio-8100-exec-1] [a1b2c3d4e5f6...][1234567890abcdef] INFO c.c.p.controller.CustomerController - 查询客户列表 +``` + +**配置要点**: + +- 使用 `%X{traceId:-}` 和 `%X{spanId:-}` 从 MDC(Mapped Diagnostic Context)中提取追踪信息。 +- `-` 作为默认值,当上下文中不存在追踪信息时显示。 +- 需配合拦截器或过滤器在请求入口将 TraceId 和 SpanId 放入 MDC。 + +#### 5.3 HTTP Header 透传 + +| Header | 说明 | +|------------------|----------------| +| `X-Trace-Id` | 全链路追踪 ID | +| `X-Span-Id` | 当前服务 Span | +| `X-Source-Service` | 来源服务名 | +| `X-Request-Time` | 请求发起时间戳 | +| `X-User-Id` | 用户 ID | +| `X-Username` | 用户名 | + +#### 5.4 响应头返回 + +服务在响应中也应返回追踪信息,便于排查问题: + +```text +X-Trace-Id: a1b2c3d4e5f67890a1b2c3d4e5f67890 +X-Span-Id: 1234567890abcdef +``` + +--- + +### 6. Token 认证规范 + +#### 6.1 Token 机制 + +可采用 **随机 Token + 缓存(如 Redis)** 的会话机制(非 JWT): + +```java +// 生成 Token +String token = tokenService.generateToken(userId, username, tenantId); + +// Token 存储到 Redis +Key: auth:token:{token} +Value: TokenInfo(userId, username, tenantId, expireTime) +TTL: 24 小时(或配置项) +``` + +#### 6.2 请求认证 + +```text +Authorization: Bearer {token} +``` + +#### 6.3 白名单路径 + +可将登录、健康检查等接口配置为无需 Token 验证,例如: + +```text +/sys/api/v1/auth/login +/sys/api/v1/sys/health +``` + +--- + +### 7. 配置与部署规范 + +#### 7.1 配置文件分离架构 + +推荐采用「统一配置 + 服务个性化配置」分离的方式: + +```text +project-root/ +├── scripts/ +│ └── env.properties # 统一配置(所有服务共用) +├── service-a/src/main/resources/ +│ └── service.properties # 个性化配置(单服务独立) +├── service-b/src/main/resources/ +│ └── service.properties +└── ... 其他服务 +``` + +- `env.properties`:环境级通用配置(注册中心、缓存、限流、文件上传、日志等)。 +- 各服务 `service.properties`:服务名、实例名、多租户路由、端口等个性化配置。 + +统一配置示例(节选): + +```properties +# Nacos / 注册中心 +NACOS_SERVER_ADDR=localhost:8848 +NACOS_NAMESPACE=project-namespace +NACOS_GROUP=DEFAULT_GROUP + +# Redis +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD=your-password + +# 网关限流 +GATEWAY_RATE_LIMIT_REPLENISH_RATE=100 +GATEWAY_RATE_LIMIT_BURST_CAPACITY=200 + +# 文件上传 +FILE_UPLOAD_MAX_SIZE=50MB +FILE_UPLOAD_MAX_REQUEST_SIZE=100MB +FILE_STORAGE_PATH=./uploads + +# 日志 +LOG_PATH=/var/logs/apps +LOG_LEVEL_ROOT=INFO +LOG_LEVEL_APP=DEBUG +``` + +个性化配置示例: + +```properties +# 服务名称 +APP_NAME=service-sys + +# 实例名称(多租户场景使用,默认与 APP_NAME 相同) +INSTANCE_NAME=${APP_NAME} + +# 租户标识(多租户场景使用,用于路由) +TENANT_ID= + +# 服务端口(可覆盖 application.yml 中配置) +# SERVER_PORT=8100 +``` + +#### 7.2 配置加载顺序 + +启动脚本中建议按以下顺序加载配置(后加载覆盖先加载): + +```bash +# 1. 加载统一配置 +load_properties "${APP_HOME}/conf/env.properties" + +# 2. 加载个性化配置(覆盖同名参数) +load_properties "${APP_HOME}/conf/service.properties" +``` + +#### 7.3 打包目录结构 + +通过打包插件(如 Maven Assembly)时,推荐输出统一的目录结构: + +```text +service-sys/ # 服务根目录 +├── bin/ # 脚本目录 +│ ├── start.sh # 启动脚本 +│ ├── stop.sh # 停止脚本 +│ ├── restart.sh # 重启脚本 +│ └── status.sh # 状态查看脚本 +├── lib/ # 依赖 JAR 目录 +│ └── service-sys.jar # 服务 JAR 包 +└── conf/ # 配置文件目录 + ├── env.properties # 统一配置 + ├── service.properties # 个性化配置 + ├── application.yml # 主配置 + ├── bootstrap.yml # 启动配置 + └── logback-spring.xml # 日志配置 +``` + +#### 7.4 日志配置集中化 + +**强制要求**: + +1. **日志文件命名**: + - 主日志文件统一命名为 `app.log`。 + - 所有日志文件(包括不同级别)统一输出到 `logs/` 目录下,不再创建子目录。 + +2. **日志目录结构**: + ```text + logs/ + ├── app.log # 应用主日志(所有级别) + ├── error.log # ERROR 级别日志(可选) + ├── sql.log # MyBatis-Plus SQL 日志(强制) + └── aop.log # API 请求日志(可选) + ``` + +日志管理建议统一由 `logback-spring.xml` 等日志框架配置文件控制,从环境变量读取关键参数: + +```xml + + + + + + + + + + + + ${LOG_PATH}/app.log + + ${LOG_PATTERN} + + + ${LOG_PATH}/app-%d{yyyy-MM-dd}.%i.log + 100MB + 30 + + + + + + ${LOG_PATH}/error.log + + ERROR + ACCEPT + DENY + + + ${LOG_PATTERN} + + + ${LOG_PATH}/error-%d{yyyy-MM-dd}.%i.log + 100MB + 30 + + + + + + ${LOG_PATH}/sql.log + + ${LOG_PATTERN} + + + ${LOG_PATH}/sql-%d{yyyy-MM-dd}.%i.log + 100MB + 30 + + + + + + + + + + + + + + +``` + +**SQL 日志配置说明**: + +1. **独立文件**:MyBatis-Plus 的 SQL 日志单独输出到 `sql.log`,便于 SQL 分析和问题排查。 +2. **日志级别**:SQL 日志级别设置为 `DEBUG`,确保能记录完整的 SQL 语句和参数。 +3. **包名配置**: + - `com.baomidou.mybatisplus`:MyBatis-Plus 框架的 SQL 日志 + - `com.wjbl.project.*.data.mapper`:项目 Mapper 的 SQL 日志(需根据实际包名调整) +4. **additivity="false"**:防止 SQL 日志重复输出到主日志文件。 +5. **CONSOLE 输出**:开发环境可以同时输出到控制台,生产环境可移除。 + +与应用配置文件的职责划分建议为:**业务配置写在 application.yml,日志输出规则集中在 logback-spring.xml 管理**。 + +#### 7.5 Redis 与注册中心配置示例 + +Redis 配置(示例): + +```yaml +spring: + data: + redis: + host: localhost + port: 6379 + password: your-password + database: 0 +``` + +注册中心配置(以 Nacos 为例): + +```yaml +spring: + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: project-namespace + group: DEFAULT_GROUP + username: nacos + password: nacos +``` + +--- + +### 8. 多租户架构规范(可选) + +> **说明**:本章节内容适用于需要支持多租户的项目。如果项目不涉及多租户场景,可跳过本章节。 + +#### 8.1 数据库多租户字段 + +支持多租户的业务表必须增加 `tenant_id` 字段: + +```sql +tenant_id VARCHAR(12) NOT NULL COMMENT '租户ID' +``` + +**字段说明**: +- 数据类型:`VARCHAR(12)` 字符串类型 +- 长度限制:12 个字符 +- 非空约束:必填字段 + +#### 8.2 租户 ID 传递 + +多租户系统中,推荐采用以下方式透传租户信息: + +1. **前端请求**:通过 HTTP Header 传递 `X-Tenant-Id`。 +2. **网关层**:网关过滤器提取并透传租户 ID。 +3. **后端服务**:使用 `TenantContextHolder` 或等价机制将租户 ID 存入上下文(如 ThreadLocal)。 +4. **数据访问层**:ORM / MyBatis 拦截器自动注入 `tenant_id` 条件,实现数据隔离。 + +#### 8.3 租户字段自动填充 + +```java +// 实体类字段 - 使用 String 类型 +@TableField(fill = FieldFill.INSERT) +private String tenantId; // 注意:使用 String 类型,长度最大 12 字符 + +// 元数据填充处理 +public class TenantMetaObjectHandler implements MetaObjectHandler { + @Override + public void insertFill(MetaObject metaObject) { + this.strictInsertFill(metaObject, "tenantId", String.class, + TenantContextHolder.getTenantId()); + } +} +``` + +#### 8.4 租户上下文操作 + +```java +// 获取当前租户ID(返回 String 类型) +String tenantId = TenantContextHolder.getTenantId(); + +// 设置租户ID(通常由拦截器自动设置) +TenantContextHolder.setTenantId(tenantId); + +// 清除上下文(请求结束时) +TenantContextHolder.clear(); +``` + +**注意事项**: +- 租户 ID 统一使用 String 类型,最大长度 12 字符。 +- TenantContextHolder 需要对应调整为存储 String 类型。 + +#### 8.5 HTTP Header 透传 + +多租户场景下,需要在链路追踪 Header 基础上增加租户信息: + +| Header | 说明 | +|------------------|----------------| +| `X-Tenant-Id` | 租户 ID | + +#### 8.6 多租户部署结构 + +多实例多租户部署时,可通过目录和配置分离不同实例: + +```text +/opt/project/deploy/ +├── service-sys/ # 共享实例 +│ ├── bin/ +│ ├── lib/ +│ └── conf/ +│ └── service.properties # INSTANCE_NAME=service-sys, TENANT_ID= +├── service-sys-vip001/ # VIP 实例 1 +│ ├── bin/ +│ ├── lib/ +│ └── conf/ +│ └── service.properties # INSTANCE_NAME=service-sys-vip001, TENANT_ID=vip001 +└── service-sys-vip002/ # VIP 实例 2 + └── ... +``` + +**个性化配置示例(多租户场景)**: + +```properties +# 服务名称 +APP_NAME=service-sys + +# 实例名称(多租户场景使用,默认与 APP_NAME 相同) +INSTANCE_NAME=service-sys-vip001 + +# 租户标识(多租户场景使用,用于路由) +TENANT_ID=vip001 + +# 服务端口(可覆盖 application.yml 中配置) +# SERVER_PORT=8100 +``` + +--- + +### 9. 常见架构问题与处理建议 + +#### 9.1 Bean 名称冲突 + +多模块项目中,配置类命名需避免冲突: + +```java +// 不推荐:多个模块都存在同名配置类 +@Configuration +public class RedisConfig { } + +// 推荐:添加模块前缀或语义前缀 +@Configuration +public class GatewayRedisConfig { } // 网关专用配置 +``` + +#### 9.2 WebFlux 与 Servlet 冲突 + +基于 WebFlux 的网关或服务不能使用 Servlet API: + +```java +// 错误:在 WebFlux 项目中使用 Servlet API +public class TokenAuthFilter implements Filter { + public void doFilter(ServletRequest request, ServletResponse response) { } +} + +// 正确:WebFlux 环境下使用 Reactive API +public class TokenAuthFilter implements GlobalFilter { + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { } +} +``` + +#### 9.3 注册中心服务发现失败 + +使用注册中心(如 Nacos)时,应确保依赖与配置完整,例如: + +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + +``` + +并检查: + +- 注册中心地址、命名空间、分组等配置是否正确; +- 服务名是否唯一; +- 客户端网络是否能访问注册中心。 + +--- + +## 十、开发规范 + +### 10.1 命名规范 + +#### 10.1.1 包命名 + +- **全部小写**。 +- 建议格式:`com.{公司或组织}.{项目或域}.{模块}`。 + +示例: + +```text +com.wjbl.project.customer.controller +com.wjbl.project.customer.service +com.wjbl.project.customer.service.impl +com.wjbl.project.customer.data.mapper +com.wjbl.project.customer.data.entity +com.wjbl.project.customer.dto +com.wjbl.project.customer.vo +``` + +#### 10.1.2 类命名 + +| 类型 | 命名规则 | 示例 | +|------------|-----------------------|---------------------------------------| +| 实体类 | 名词,PascalCase | `Customer`, `Project` | +| Controller | 名词 + Controller | `CustomerController` | +| Service 接口 | 名词 + Service | `CustomerService` | +| Service 实现 | 名词 + ServiceImpl | `CustomerServiceImpl` | +| DataService 接口 | 名词 + DataService | `CustomerDataService` | +| DataService 实现 | 名词 + DataServiceImpl | `CustomerDataServiceImpl` | +| Mapper | 名词 + Mapper | `CustomerMapper` | +| DTO | 动作 + 名词 + DTO | `CustomerCreateDTO`, `CustomerUpdateDTO` | +| VO | 名词 + VO | `CustomerVO`, `ProjectVO` | +| 配置类 | 功能 + Config | `RedisConfig`, `MybatisConfig` | + +#### 10.1.3 方法命名 + +| 操作 | Service 方法 | Controller 方法 | +|------------|----------------------|-----------------| +| 创建 | `createXxx()` | `createXxx()` | +| 更新 | `updateXxx()` | `updateXxx()` | +| 删除 | `deleteXxx()` | `deleteXxx()` | +| 查询单个 | `getXxxById()` | `getXxx()` | +| 分页查询 | `pageXxxs()` | `pageXxxs()` | +| 列表查询 | `listXxxs()` | `listXxxs()` | + +#### 10.1.4 数据库命名 + +- **表名**:小写,下划线分隔,如 `customer`, `project_member`。 +- **字段名**:小写,下划线分隔,如 `customer_name`, `created_time`。 +- **索引名**:`idx_表名_字段名`,如 `idx_customer_status`。 +- **主键名**:`表名_id`,如 `customer_id`(数据库中使用字符串类型存储)。 + +--- + +### 10.2 Git 分支与提交规范 + +#### 10.2.1 分支命名 + +推荐的分支模型: + +```text +master # 主分支(稳定版本) +develop # 开发分支(集成测试) +feature/xxx # 功能分支 +bugfix/xxx # Bug 修复分支 +hotfix/xxx # 紧急线上修复分支 +``` + +- 新功能从 `develop` 或 `master` 拉取对应的 `feature/*` 分支。 +- Bug 修复从对应问题所在分支拉取 `bugfix/*` 或 `hotfix/*` 分支。 + +#### 10.2.2 提交信息格式 + +推荐使用简洁且结构化的提交信息格式: + +```text +: + + +``` + +**type 类型约定**: + +| 类型 | 说明 | +|---------|----------------| +| feat | 新功能 | +| fix | Bug 修复 | +| docs | 文档更新 | +| style | 代码格式调整 | +| refactor| 重构 | +| test | 测试相关 | +| chore | 构建/工具相关 | + +**示例 1:功能提交** + +```text +feat: 新增客户管理分页查询接口 + +- 支持按客户名称模糊查询 +- 支持按状态筛选 +- 完成分页参数校验 +``` + +**示例 2:缺陷修复** + +```text +fix: 修复路由配置错误 + +xxx 服务路由路径配置错误,修正为 /cust/** +``` + +**要求**: + +- **subject** 尽量 50 字以内,描述清晰、可读。 +- **body** 用于说明设计思路、变更范围、注意事项等,可选但推荐完整编写。 + +--- + +### 10.3 代码分层与编写规范 + +#### 10.3.1 Controller 层 + +Controller 仅负责: + +- 请求参数接收与基础校验(可结合 `@Valid`)。 +- 调用 Service 层完成业务处理。 +- 将结果封装为统一返回对象输出。 + +示例: + +```java +@RestController +@RequestMapping("/api/v1/customer") +@RequiredArgsConstructor +public class CustomerController { + + private final CustomerService customerService; + + @PostMapping + public Result createCustomer(@Valid @RequestBody CustomerCreateDTO dto) { + String id = customerService.createCustomer(dto); + return Result.success(id); + } + + @GetMapping("/page") + public Result> pageCustomers( + @RequestParam(defaultValue = "1") int pageNum, + @RequestParam(defaultValue = "10") int pageSize, + @RequestParam(required = false) String keyword) { + Page page = customerService.pageCustomers(pageNum, pageSize, keyword); + return Result.success(PageResult.of(page)); + } +} +``` + +#### 10.3.2 Service 层 + +Service 层聚焦业务逻辑,典型步骤: + +1. 参数及前置条件校验。 +2. 业务规则处理。 +3. 调用 DataService 进行数据操作。 +4. 组装并返回结果。 + +示例: + +```java +@Service +@RequiredArgsConstructor +public class CustomerServiceImpl implements CustomerService { + + private final CustomerDataService customerDataService; + + @Override + @Transactional(rollbackFor = Exception.class) + public String createCustomer(CustomerCreateDTO dto) { + // 1. 参数校验 + // 2. 业务逻辑 + // 3. 通过 DataService 进行数据持久化 + // 4. 返回结果 + return null; + } +} +``` + +**重要**:业务 Service 必须通过 DataService 访问数据,禁止直接注入 Mapper。 + +#### 10.3.3 DTO / VO 规范 + +- **DTO(Data Transfer Object)**:用于接收请求参数或封装服务间调用参数,只包含与接口输入相关的字段。 +- **VO(View Object)**:用于接口响应展示,只包含与前端展示相关的字段。 +- 使用校验注解约束 DTO 字段,确保基本数据正确性。 + +示例: + +```java +@Data +public class CustomerCreateDTO { + + @NotBlank(message = "客户名称不能为空") + @Size(max = 200, message = "客户名称最长 200 字符") + private String customerName; + + @NotBlank(message = "联系人不能为空") + private String contact; + + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") + private String phone; +} +``` + +--- + +### 10.4 日志使用规范 + +#### 10.4.1 日志级别 + +| 级别 | 使用场景 | +|-------|--------------------------------------| +| ERROR | 异常、错误、需要立即处理的问题 | +| WARN | 警告、潜在问题 | +| INFO | 关键业务流程、重要操作 | +| DEBUG | 调试信息、详细流程(仅在调试环境启用)| + +**要求**: + +- 线上默认不开启 DEBUG,避免大量无效日志。 +- 禁止在日志中输出敏感信息(密码、密钥、完整身份证号等)。 +- **所有日志必须包含 traceId 和 spanId**,便于链路追踪。 + +#### 10.4.2 API 请求日志 + +推荐通过 AOP 统一记录 API 请求日志,内容包括: + +- 请求路径、HTTP 方法。 +- 请求参数(适当脱敏)。 +- 响应时间。 +- 结果状态(成功/失败)。 + +示例切面: + +```java +@Around("execution(* com.wjbl.project..controller..*(..))") +public Object logApiRequest(ProceedingJoinPoint joinPoint) { + // 记录:请求方法、参数、响应时间、结果状态 + // 日志中必须包含 traceId 和 spanId + return null; +} +``` + +--- + +### 10.5 异常处理规范 + +#### 10.5.1 统一异常处理 + +建议在网关或应用入口层统一捕获并处理异常,输出统一格式的错误响应: + +```java +@RestControllerAdvice +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, "系统异常,请稍后重试"); + } +} +``` + +#### 10.5.2 业务异常定义 + +业务异常类用于表达可预期的业务错误,并携带错误码和错误信息: + +```java +public class BusinessException extends RuntimeException { + private final int code; + + public BusinessException(String message) { + super(message); + this.code = 400; + } + + public BusinessException(int code, String message) { + super(message); + this.code = code; + } +} +``` + +#### 10.5.3 业务代码中使用规范 + +- 对于**参数错误、状态不合法**等可预期问题,直接抛出 `BusinessException`,携带明确的提示信息。 +- 对于**系统级异常**(如网络、IO、第三方服务异常),在适当位置记录详细日志并抛出统一异常,让全局处理器输出通用提示。 + +示例: + +```java +// 参数校验 +if (customerName == null || customerName.isEmpty()) { + throw new BusinessException("客户名称不能为空"); +} + +// 业务校验 +Customer customer = customerDataService.getById(id); +if (customer == null) { + throw new BusinessException(404, "客户不存在"); +} +``` + +--- + +### 10.6 规范落地建议 + +为确保上述规范真正落地,建议在团队内按以下步骤推进: + +1. **纳入项目模板**: + - 将本架构设计规范纳入项目脚手架或模板仓库。 +2. **代码评审检查**: + - 在 Code Review 中重点检查命名、分层、日志、异常处理是否符合规范。 +3. **自动化校验**: + - 结合静态检查工具、提交钩子等,对提交信息格式、基础代码规范进行自动化校验。 +4. **文档持续维护**: + - 根据实际项目经验定期补充常见错误案例和最佳实践,将实践沉淀回本规范文档中。 + diff --git a/doc/需求规格说明书SRS.md b/doc/需求规格说明书SRS.md new file mode 100644 index 0000000..f8980df --- /dev/null +++ b/doc/需求规格说明书SRS.md @@ -0,0 +1,113 @@ +# 工作日志服务平台 - 需求规格说明书 (SRS) + +**版本号**:V1.0 +**日期**:2023-10-27 +**状态**:待开发 + +--- + +## 1. 引言 +### 1.1 项目背景 +配合 2026 年人员管理需求,构建一套简易的工作日志服务平台,用于记录员工日常工作内容,支持模板化管理及人员信息维护。 +### 1.2 建设目标 +实现人员信息数字化管理、工作日志在线填写与查看、日志模板标准化配置。 +### 1.3 范围 +包含人员管理、日志模板管理、工作日志管理三大模块。**不包含项目管理模块**。 + +--- + +## 2. 用户角色 +系统分为两种角色,权限控制如下: + +| 角色 | 说明 | 权限范围 | +| :--- | :--- | :--- | +| **普通员工** | 系统的使用者 | 1. 修改个人密码/信息
2. 撰写、查看、修改、删除**自己的**日志
3. 查看启用的日志模板 | +| **管理员** | 系统的维护者 | 1. 拥有普通员工所有权限
2. **人员管理**:增删改查所有人员、启用/禁用账号
3. **模板管理**:增删改查所有模板、启用/禁用模板
4. **日志管理**:可查看全公司日志(可选) | + +--- + +## 3. 功能需求详解 + +### 3.1 人员管理模块 +| 功能点 | 描述 | 输入项 | 处理逻辑 | 输出/结果 | +| :--- | :--- | :--- | :--- | :--- | +| **账号登录** | 用户进入系统 | 账号、密码 | 验证账号存在且状态为“启用”,密码匹配 | 登录成功跳转首页,失败提示错误 | +| **人员列表** | 查看人员信息 | 无/搜索条件 | 展示人员基本信息列表 | 表格展示姓名、职位、状态等 | +| **新增人员** | 创建新账号 | 姓名、账号、密码、职位等 | 账号唯一性校验,密码加密存储 | 创建成功,状态默认为“启用” | +| **编辑人员** | 修改信息 | 除账号外的所有字段 | 验证权限(仅管理员或本人) | 信息更新 | +| **启用/禁用** | 控制账号状态 | 无 | 切换状态标志位 | 禁用后该账号无法登录 | +| **删除人员** | 移除人员 | 无 | 逻辑删除(建议)或物理删除 | 人员从列表消失 | + +### 3.2 日志模板管理模块 +| 功能点 | 描述 | 输入项 | 处理逻辑 | 输出/结果 | +| :--- | :--- | :--- | :--- | :--- | +| **模板列表** | 查看可用模板 | 无 | 仅展示状态为“启用”的模板给普通用户 | 模板名称、使用说明列表 | +| **新增模板** | 创建新模板 | 名称、内容、说明 | 内容支持 Markdown 格式 | 模板创建成功 | +| **编辑模板** | 修改模板 | 名称、内容、说明 | 验证权限(仅管理员) | 模板信息更新 | +| **启用/禁用** | 控制模板可用性 | 无 | 切换状态标志位 | 禁用后用户写日志时不可选 | +| **删除模板** | 移除模板 | 无 | 验证权限(仅管理员) | 模板从列表消失 | + +### 3.3 工作日志管理模块 +| 功能点 | 描述 | 输入项 | 处理逻辑 | 输出/结果 | +| :--- | :--- | :--- | :--- | :--- | +| **写日志** | 提交日常工作 | 日期、内容、选用模板 | 1. 记录人自动获取当前登录者
2. 内容字数校验
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. **默认值**:新增人员默认状态为“启用”;新增模板默认状态为“启用”。 + +--- +**备注**:本文档为开发基准文档,如有变更需同步更新版本号。 diff --git a/sql/create_user.sql b/sql/create_user.sql new file mode 100644 index 0000000..356ea34 --- /dev/null +++ b/sql/create_user.sql @@ -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 +-- +-- ==================================================== +-- 脚本执行完成 +-- ==================================================== diff --git a/sql/init_database.sql b/sql/init_database.sql new file mode 100644 index 0000000..0b3a653 --- /dev/null +++ b/sql/init_database.sql @@ -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; + +-- ==================================================== +-- 脚本执行完成 +-- ==================================================== diff --git a/worklog-api/README.md b/worklog-api/README.md new file mode 100644 index 0000000..fd00d7a --- /dev/null +++ b/worklog-api/README.md @@ -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 封装响应 + +## 测试账号 + +- **管理员账号**: admin +- **管理员密码**: admin123 + +## 下一步 + +阶段一基础框架已完成,接下来将开发: +1. 认证授权模块 (登录/登出) +2. 人员管理模块 +3. 模板管理模块 +4. 工作日志模块 + +详见开发计划文档。 diff --git a/worklog-api/pom.xml b/worklog-api/pom.xml new file mode 100644 index 0000000..9e72d97 --- /dev/null +++ b/worklog-api/pom.xml @@ -0,0 +1,129 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.2.2 + + + + com.wjbl + worklog-api + 1.0.0 + WorkLog Platform API + 工作日志服务平台后端API + + + 21 + UTF-8 + UTF-8 + 3.5.5 + 5.8.25 + 2.3.0 + 8.0.33 + 0.21.0 + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + + com.mysql + mysql-connector-j + ${mysql.version} + + + + + org.projectlombok + lombok + true + + + + + cn.hutool + hutool-all + ${hutool.version} + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc.version} + + + + + org.commonmark + commonmark + ${commonmark.version} + + + + + org.springframework.security + spring-security-crypto + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + diff --git a/worklog-api/src/main/java/com/wjbl/worklog/WorklogApplication.java b/worklog-api/src/main/java/com/wjbl/worklog/WorklogApplication.java new file mode 100644 index 0000000..760716d --- /dev/null +++ b/worklog-api/src/main/java/com/wjbl/worklog/WorklogApplication.java @@ -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"); + } +} diff --git a/worklog-api/src/main/java/com/wjbl/worklog/common/PageResult.java b/worklog-api/src/main/java/com/wjbl/worklog/common/PageResult.java new file mode 100644 index 0000000..4c52a57 --- /dev/null +++ b/worklog-api/src/main/java/com/wjbl/worklog/common/PageResult.java @@ -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 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 list; + + /** + * 构建分页结果 + */ + public static PageResult build(Long pageNum, Long pageSize, Long total, List list) { + return new PageResult<>(pageNum, pageSize, total, list); + } +} diff --git a/worklog-api/src/main/java/com/wjbl/worklog/common/Result.java b/worklog-api/src/main/java/com/wjbl/worklog/common/Result.java new file mode 100644 index 0000000..13af6df --- /dev/null +++ b/worklog-api/src/main/java/com/wjbl/worklog/common/Result.java @@ -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 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 Result success() { + return new Result<>(200, "操作成功", null, true); + } + + /** + * 成功响应 (带数据) + */ + public static Result success(T data) { + return new Result<>(200, "操作成功", data, true); + } + + /** + * 成功响应 (自定义消息和数据) + */ + public static Result success(String message, T data) { + return new Result<>(200, message, data, true); + } + + /** + * 失败响应 + */ + public static Result fail(String message) { + return new Result<>(400, message, null, false); + } + + /** + * 失败响应 (自定义错误码) + */ + public static Result fail(Integer code, String message) { + return new Result<>(code, message, null, false); + } + + /** + * 服务器错误 + */ + public static Result error(String message) { + return new Result<>(500, message, null, false); + } +} diff --git a/worklog-api/src/main/java/com/wjbl/worklog/common/exception/BusinessException.java b/worklog-api/src/main/java/com/wjbl/worklog/common/exception/BusinessException.java new file mode 100644 index 0000000..500894f --- /dev/null +++ b/worklog-api/src/main/java/com/wjbl/worklog/common/exception/BusinessException.java @@ -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; + } +} diff --git a/worklog-api/src/main/java/com/wjbl/worklog/common/exception/GlobalExceptionHandler.java b/worklog-api/src/main/java/com/wjbl/worklog/common/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..5659145 --- /dev/null +++ b/worklog-api/src/main/java/com/wjbl/worklog/common/exception/GlobalExceptionHandler.java @@ -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("系统繁忙,请稍后重试"); + } +} diff --git a/worklog-api/src/main/java/com/wjbl/worklog/config/ApiLogAspect.java b/worklog-api/src/main/java/com/wjbl/worklog/config/ApiLogAspect.java new file mode 100644 index 0000000..1c7628a --- /dev/null +++ b/worklog-api/src/main/java/com/wjbl/worklog/config/ApiLogAspect.java @@ -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; + } +} diff --git a/worklog-api/src/main/java/com/wjbl/worklog/config/MybatisPlusConfig.java b/worklog-api/src/main/java/com/wjbl/worklog/config/MybatisPlusConfig.java new file mode 100644 index 0000000..174438e --- /dev/null +++ b/worklog-api/src/main/java/com/wjbl/worklog/config/MybatisPlusConfig.java @@ -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; + } +} diff --git a/worklog-api/src/main/java/com/wjbl/worklog/config/TraceInterceptor.java b/worklog-api/src/main/java/com/wjbl/worklog/config/TraceInterceptor.java new file mode 100644 index 0000000..ee1f779 --- /dev/null +++ b/worklog-api/src/main/java/com/wjbl/worklog/config/TraceInterceptor.java @@ -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(); + } +} diff --git a/worklog-api/src/main/java/com/wjbl/worklog/config/WebMvcConfig.java b/worklog-api/src/main/java/com/wjbl/worklog/config/WebMvcConfig.java new file mode 100644 index 0000000..aa9f010 --- /dev/null +++ b/worklog-api/src/main/java/com/wjbl/worklog/config/WebMvcConfig.java @@ -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("/**"); + } +} diff --git a/worklog-api/src/main/java/com/wjbl/worklog/controller/HealthController.java b/worklog-api/src/main/java/com/wjbl/worklog/controller/HealthController.java new file mode 100644 index 0000000..076dbd5 --- /dev/null +++ b/worklog-api/src/main/java/com/wjbl/worklog/controller/HealthController.java @@ -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> health() { + Map 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); + } +} diff --git a/worklog-api/src/main/resources/application.yml.example b/worklog-api/src/main/resources/application.yml.example new file mode 100644 index 0000000..a603747 --- /dev/null +++ b/worklog-api/src/main/resources/application.yml.example @@ -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 diff --git a/worklog-api/src/main/resources/logback-spring.xml b/worklog-api/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..9f3b57a --- /dev/null +++ b/worklog-api/src/main/resources/logback-spring.xml @@ -0,0 +1,57 @@ + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{spanId}] %-5level %logger{50} - %msg%n + UTF-8 + + + + + + ${LOG_PATH}/app.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{spanId}] %-5level %logger{50} - %msg%n + UTF-8 + + + ${LOG_PATH}/app-%d{yyyy-MM-dd}.%i.log + + 100MB + + 30 + + + + + + ${LOG_PATH}/sql.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] [%X{spanId}] %-5level %logger{50} - %msg%n + UTF-8 + + + ${LOG_PATH}/sql-%d{yyyy-MM-dd}.%i.log + + 100MB + + 30 + + + + + + + + + + + + + + +