Compare commits

..

No commits in common. "fffeaa48a533af6b7cc9799894aa7500bcaaba94" and "46e30c8b06adf47218807d19856dcd59c434f24a" have entirely different histories.

109 changed files with 703 additions and 3016 deletions

View File

@ -1,33 +0,0 @@
# Repository Guidelines
## 项目结构与模块组织
- 后端为 Maven 多模块 Spring Boot 工程。核心服务位于 `fund-*` 模块(如 `fund-sys``fund-cust``fund-proj``fund-gateway`),共享代码在 `fund-common`
- 前端为独立 Vite 项目:管理端在 `fund-admin`,移动端 H5 在 `fund-mobile`
- 运维与打包相关内容在 `scripts/``deploy/``docker/``assembly/`。前端构建先生成各模块 `dist/`,再通过 `scripts/build-frontend.sh` 复制到 `deploy/`;后端构建产物也统一放置在 `deploy/`
## 构建、测试与本地开发命令
- `mvn -q -DskipTests package`: 在仓库根目录构建所有后端模块。
- `mvn test`: 运行后端单元测试(依赖 Spring Boot Test
- `cd fund-admin && npm install && npm run dev`: 本地启动管理端。
- `cd fund-mobile && npm install && npm run dev`: 本地启动移动端。
- `./scripts/build-frontend.sh [admin|mobile]`: 构建单个或全部前端。
- `./scripts/docker-build.sh build-all`: 构建后端服务 Docker 镜像。
- `docker-compose up -d`: 启动 `docker-compose.yml` 中定义的本地依赖/服务。
## 编码风格与命名规范
- Java 采用常规 Spring Boot 风格,包名格式如 `com.fundplatform.{模块}.{层级}`
- 后端类命名按职责清晰命名(如 `CustomerController``CustomerServiceImpl`)。
- 前端 TypeScript/Vue 没有统一的格式化配置,保持与现有代码风格一致。
## 测试指南
- 后端测试放在各模块 `src/test/java`,通过 `mvn test` 运行。
- 前端 `package.json` 未配置测试脚本,若新增测试需同时引入对应测试工具。
## 提交与拉取请求规范
- Git 历史显示轻量 Conventional Commits 风格,建议使用 `feat:``fix:` 等前缀,描述简洁明确。
- PR 需说明范围;有需求单则关联;涉及 UI`fund-admin``fund-mobile`)请附截图。
- 涉及配置或部署脚本的变更(`scripts/``deploy/``docker/`)需显式说明。
## 配置与运行说明
- 服务启动脚本读取 `conf/env.properties``conf/service.properties`(见 `scripts/start.sh`)。
- 根 `pom.xml` 指定 Java 版本为 `21`

165
CLAUDE.md
View File

@ -1,165 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 项目概述
**资金服务平台FundPlatform** — 多租户微服务架构的项目资金管理系统。
- **技术栈**: Java 21 + Spring Boot 3.2.0 + Spring Cloud Alibaba 2023.0.0.0 + MyBatis-Plus 3.5.5 + MySQL 8.0 + Redis
- **前端**: Vue 3 + TypeScript管理后台用 Element Plus移动端用 Vant
## 常用命令
### 后端构建
```bash
# 构建所有模块(跳过测试)
mvn -q -DskipTests package
# 构建单个模块
mvn -q -DskipTests package -pl fund-sys -am
# 运行测试
mvn test
# 打包成 tar.gz 部署包(使用 Assembly
mvn -q -DskipTests package -pl fund-sys -am -P assembly
```
### 前端开发
```bash
# 管理后台
cd fund-admin && npm install && npm run dev
# 移动端
cd fund-mobile && npm install && npm run dev
# 打包前端
./scripts/build-frontend.sh admin # 或 mobile
```
### 本地环境
```bash
# 启动基础设施MySQL、Redis、Nacos、Prometheus、Grafana
docker-compose up -d
# 停止并清理
docker-compose down -v
```
## 模块结构
| 模块 | 端口 | 职责 |
|------|------|------|
| fund-common | — | 公共工具Token、Redis、多租户上下文、统一响应 |
| fund-gateway | 8000 | API网关路由、认证、限流 |
| fund-sys | 8100 | 系统服务:用户、角色、菜单、部门、租户管理 |
| fund-cust | 8200 | 客户管理 |
| fund-proj | 8300 | 项目与合同管理 |
| fund-req | 8400 | 需求工单 |
| fund-exp | 8500 | 支出管理 |
| fund-receipt | 8600 | 收款管理 |
| fund-report | 8700 | 报表统计 |
| fund-file | 8800 | 文件存储腾讯云COS |
## 代码架构
### 分层约定
每个业务服务模块遵循统一分层:
```
com.fundplatform.{module}/
├── controller/ REST API返回 Result<T>
├── service/ 业务逻辑
│ └── impl/
├── data/
│ ├── entity/ MyBatis-Plus 实体(对应数据库表)
│ ├── mapper/ Mapper 接口
│ └── service/ 数据层服务IService
├── dto/ 请求参数对象
├── vo/ 响应视图对象
├── feign/ Feign 客户端(调用其他服务)
├── aop/ 切面(操作日志等)
└── config/ 模块配置
```
### 统一响应
所有接口使用 `fund-common` 中的 `Result<T>``PageResult<T>`
```java
Result.success(data)
Result.success(data, "message")
Result.error("message")
```
### 基础实体
实体类继承 `BaseEntity`(包含 `id``tenantId``createdBy``createdTime``updatedBy``updatedTime``deleted`)。
### 认证机制
- Token 基于 UUID存储在 RedisKey: `auth:token:{token}`有效期24小时
- 密码使用 MD5 加密(`Md5Util`
- 请求头 `Authorization` 携带 Token`X-Tenant-Id` 携带租户ID
## 多租户架构
核心设计:**一库多租户 + VIP专属实例混合模式**
1. **数据隔离**:所有业务表含 `tenant_id` 字段MyBatis-Plus 租户插件自动注入 SQL 条件
2. **上下文传递**`TenantContextHolder` 存储当前租户,通过 HTTP Header 传递Feign 拦截器自动转发
3. **VIP专属实例**Nacos 元数据 `tenant-id` 标记服务实例,`TenantAwareLoadBalancer` 将 VIP 租户路由到专属实例
```yaml
# Nacos 元数据配置(区分共享/专属实例)
spring.cloud.nacos.discovery.metadata:
tenant-id: ${TENANT_ID:} # 空=共享实例,有值=VIP专属
```
## 打包与部署
### 部署包结构
Maven Assembly 打包输出 tar.gz解压后
```
bin/ 启动脚本start.sh
conf/ 配置文件application.yml、bootstrap.yml、logback-spring.xml、env.properties
lib/ 所有 JAR依赖 + 应用本身)
```
> **注意**:根 pom.xml 禁用了 Spring Boot repackage各服务的 fat jar 由 Assembly 统一管理。
### 数据库初始化
SQL 脚本位于 `doc/sql/`,执行顺序:
1. `01_create_user.sql` — 创建 MySQL 用户fundsp / fundSP@123
2. `02_grant_user.sql` — 授权
3. `fund_*_init.sql` — 各模块建表及初始数据
### 环境变量
关键变量在根目录 `.env` 文件中配置MySQL、Redis、Nacos 连接信息、腾讯云COS凭证
## 服务间通信
使用 OpenFeign 声明式调用,`FeignChainInterceptor` 自动传递 `Authorization``X-Tenant-Id``X-Trace-Id` 等 Header。
```java
@FeignClient(name = "fund-sys")
public interface SysUserFeign {
@GetMapping("/sys/user/{id}")
Result<UserDto> getUserById(@PathVariable Long id);
}
```
## 注意事项
- **fund-gateway** 使用 WebFlux不能引入 `spring-boot-starter-web`,须排除 fund-common 中的 web 自动配置
- **租户忽略**:特殊场景(如登录、租户管理)需用 `TenantIgnoreHelper` 标记绕过租户过滤
- **日志**:使用 Logback + Logstash Encoder日志格式为 JSON适配 ELK 收集

View File

@ -1,168 +0,0 @@
# 租户隔离安全漏洞修复记录
**修复日期**: 2026-03-01
**修复分支**: master
**修复范围**: 多租户数据隔离机制
---
## 漏洞一:网关未覆盖客户端伪造的 X-Tenant-Id高危
### 漏洞描述
`TokenAuthFilter` 在 Token 验证通过后,使用 Spring WebFlux 的 `request.mutate().header()` 将 Token 中的 tenantId 写入请求头。然而 WebFlux 的 `header()` 方法是**追加**而非替换语义,若客户端预先在请求中设置了 `X-Tenant-Id`,下游服务从 `getFirst()` 取到的仍然是客户端伪造的值。攻击者可以通过在请求中携带任意 `X-Tenant-Id` 来访问其他租户的数据。
### 攻击场景
```
攻击者发送请求:
Authorization: Bearer <合法Token属于租户A>
X-Tenant-Id: 999 ← 伪造的租户ID
```
原始代码中,下游 `ContextInterceptor` 从请求头取到的 `X-Tenant-Id` 可能是 999导致 `TenantContextHolder` 被设置为 999进而 MyBatis-Plus 租户插件以 tenant_id=999 过滤 SQL越权访问其他租户数据。
### 修复方案
`TokenAuthFilter` 中先移除客户端传来的 `X-Tenant-Id`,再写入 Token 中已认证的值:
```java
// 安全修复:先移除客户端传来的 X-Tenant-Id再写入 Token 中已认证的值
ServerHttpRequest mutatedRequest = request.mutate()
.headers(headers -> headers.remove(TENANT_ID_HEADER)) // 先删除客户端值
.header(USER_ID_HEADER, String.valueOf(tokenInfo.getUserId()))
.header(USERNAME_HEADER, tokenInfo.getUsername())
.header(TENANT_ID_HEADER, String.valueOf(tokenInfo.getTenantId())) // 再写入认证值
.build();
```
### 修复位置
`fund-gateway/src/main/java/com/fundplatform/gateway/filter/TokenAuthFilter.java` 第 86-93 行
---
## 漏洞二IGNORE_TABLES 包含业务敏感表(高危)
### 漏洞描述
`TenantLineHandlerImpl.IGNORE_TABLES` 中包含了 `sys_user``sys_role``sys_dept``sys_config` 等业务表,这些表均有 `tenant_id` 字段属于租户私有数据。将其加入忽略列表后MyBatis-Plus 不会为这些表的查询自动注入 `tenant_id` 过滤条件,导致以下问题:
1. **跨租户用户数据泄漏**:查询 `sys_user` 时返回所有租户的用户数据
2. **跨租户角色泄漏**:查询 `sys_role` 时返回所有租户的角色配置
3. **跨租户部门泄漏**:查询 `sys_dept` 时返回所有租户的部门结构
### 修复方案
`IGNORE_TABLES` 中仅保留真正全平台共享的静态数据表:
```java
// 修复后:仅保留平台级全局共享表
private static final Set<String> IGNORE_TABLES = new HashSet<>(Arrays.asList(
"sys_menu", // 菜单表(系统菜单结构全局共享)
"sys_dict", // 字典表(枚举数据全局共享)
"sys_log", // 日志表(独立存储逻辑)
"gen_table", // 代码生成表
"gen_table_column"
));
// 已移除sys_user、sys_role、sys_dept、sys_config均为租户私有数据
```
### 修复位置
`fund-common/src/main/java/com/fundplatform/common/mybatis/TenantLineHandlerImpl.java` 第 34-46 行
---
## 漏洞三:租户 ID 缺失时 Fallback 到默认值 1中危
### 漏洞描述
`TenantLineHandlerImpl.getTenantId()` 中,当从 `TenantContextHolder` 获取不到租户 ID 时,代码回退到默认值 `1L`
```java
// 漏洞代码
if (tenantId == null) {
tenantId = 1L; // 危险误操作租户1的数据
}
```
这导致在没有认证上下文的场景如定时任务、内部错误等SQL 会意外查询租户1的数据可能造成租户1数据泄漏或误写入。
### 修复方案
当租户上下文缺失时直接抛出异常,中断 SQL 执行:
```java
// 修复后:缺失租户上下文时强制报错
if (tenantId == null) {
throw new IllegalStateException("[Security] 当前请求缺少租户上下文拒绝执行SQL");
}
```
### 修复位置
`fund-common/src/main/java/com/fundplatform/common/mybatis/TenantLineHandlerImpl.java` 第 56-66 行
---
## 漏洞四:登录接口跨租户用户名混乱(中危)
### 漏洞描述
登录接口 `/auth/login` 在 Token 白名单中,`TenantContextHolder` 为空。由于 `sys_user` 原来在 `IGNORE_TABLES` 中(全局查询),登录时仅按用户名查询不区分租户:
```java
// 漏洞代码:不同租户可能有同名用户,查到哪个是不确定的
wrapper.eq(SysUser::getUsername, request.getUsername());
// 未加 tenantId 条件
SysUser user = userDataService.getOne(wrapper);
```
当不同租户有同名用户时,认证结果不确定,存在以下风险:
- 用户 A租户1可能以租户2用户的身份登录
- 登录成功后 Token 中绑定了错误的 tenantId
### 修复方案
1. `LoginRequestDTO` 增加 `tenantId` 字段(必填),前端登录时显式指定租户
2. 登录查询使用 `TenantIgnoreHelper` 跳过自动租户过滤,同时显式加入 `tenant_id` 条件:
```java
// 修复后:使用 TenantIgnoreHelper + 显式 tenantId 条件
SysUser user = TenantIgnoreHelper.ignore(() -> {
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getUsername, request.getUsername());
wrapper.eq(SysUser::getTenantId, request.getTenantId()); // 显式限定租户
wrapper.eq(SysUser::getDeleted, 0);
return userDataService.getOne(wrapper);
});
```
### 修复位置
- `fund-sys/src/main/java/com/fundplatform/sys/dto/LoginRequestDTO.java` — 新增 tenantId 字段
- `fund-sys/src/main/java/com/fundplatform/sys/service/impl/AuthServiceImpl.java` 第 31-44 行
---
## 修复总结
| 编号 | 漏洞类型 | 危险级别 | 修复文件 |
|------|---------|---------|---------|
| 1 | 网关未覆盖客户端伪造的 X-Tenant-Id | 高危 | `fund-gateway/.../TokenAuthFilter.java` |
| 2 | IGNORE_TABLES 包含业务敏感表 | 高危 | `fund-common/.../TenantLineHandlerImpl.java` |
| 3 | 租户 ID 缺失时 fallback 到默认值 | 中危 | `fund-common/.../TenantLineHandlerImpl.java` |
| 4 | 登录查询未限定租户范围 | 中危 | `fund-sys/.../AuthServiceImpl.java`, `LoginRequestDTO.java` |
## 修复后的安全保证
1. **网关层**:经过 Token 验证的请求,`X-Tenant-Id` 由 Token 中的认证值强制覆盖,客户端无法伪造
2. **数据层**业务表sys_user、sys_role、sys_dept、sys_config均受 MyBatis-Plus 租户插件保护,自动注入 `tenant_id` 过滤
3. **兜底保护**:租户上下文为空时 SQL 拒绝执行,不会 fallback 到任何租户
4. **登录安全**:登录时必须指定 tenantId确保同名用户不会跨租户混乱认证
## 注意事项
- 所有需要跨租户操作的合法场景(如超管管理所有租户),必须显式使用 `TenantIgnoreHelper.ignore()` 包装,并在代码中注明安全意图
- 定时任务、事件监听器等异步场景在执行 DB 操作前,必须先设置 `TenantContextHolder`,执行完后在 finally 中清理

View File

@ -1,9 +1,8 @@
-- ============================================= -- =============================================
-- 资金服务平台 - 客户中心数据库初始化脚本 -- 资金服务平台 - 客户中心数据库初始化脚本
-- Database: fund_cust -- Database: fund_cust
-- Version: 2.0 -- Version: 1.0
-- Created: 2026-02-17 -- Created: 2026-02-17
-- Updated: 2026-03-02 (主键类型改为VARCHAR雪花ID)
-- ============================================= -- =============================================
CREATE DATABASE IF NOT EXISTS fund_cust DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS fund_cust DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@ -14,8 +13,8 @@ USE fund_cust;
-- 1. 客户表 (customer) -- 1. 客户表 (customer)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS customer ( CREATE TABLE IF NOT EXISTS customer (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '客户ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
customer_code VARCHAR(64) NOT NULL COMMENT '客户编码', customer_code VARCHAR(64) NOT NULL COMMENT '客户编码',
customer_name VARCHAR(128) NOT NULL COMMENT '客户名称', customer_name VARCHAR(128) NOT NULL COMMENT '客户名称',
contact VARCHAR(64) COMMENT '联系人', contact VARCHAR(64) COMMENT '联系人',
@ -24,9 +23,9 @@ CREATE TABLE IF NOT EXISTS customer (
address VARCHAR(255) COMMENT '地址', address VARCHAR(255) COMMENT '地址',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -40,18 +39,18 @@ CREATE TABLE IF NOT EXISTS customer (
-- 2. 联系人表 (customer_contact) -- 2. 联系人表 (customer_contact)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS customer_contact ( CREATE TABLE IF NOT EXISTS customer_contact (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '联系人ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
customer_id VARCHAR(32) NOT NULL COMMENT '客户ID', customer_id BIGINT NOT NULL COMMENT '客户ID',
contact_name VARCHAR(64) NOT NULL COMMENT '联系人姓名', contact_name VARCHAR(64) NOT NULL COMMENT '联系人姓名',
phone VARCHAR(20) COMMENT '手机号', phone VARCHAR(20) COMMENT '手机号',
email VARCHAR(128) COMMENT '邮箱', email VARCHAR(128) COMMENT '邮箱',
position VARCHAR(64) COMMENT '职位', position VARCHAR(64) COMMENT '职位',
is_primary TINYINT NOT NULL DEFAULT 0 COMMENT '是否主要联系人: 0-否, 1-是', is_primary TINYINT NOT NULL DEFAULT 0 COMMENT '是否主要联系人: 0-否, 1-是',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -63,9 +62,9 @@ CREATE TABLE IF NOT EXISTS customer_contact (
-- 初始化测试数据(租户ID=1) -- 初始化测试数据(租户ID=1)
-- ============================================= -- =============================================
INSERT INTO customer (id, tenant_id, customer_code, customer_name, contact, phone, status, created_by, created_time) INSERT INTO customer (id, tenant_id, customer_code, customer_name, contact, phone, status, created_by, created_time)
VALUES ('1', '1', 'CUST001', '测试客户A', '张三', '13800138001', 1, '1', NOW()) VALUES (1, 1, 'CUST001', '测试客户A', '张三', '13800138001', 1, 1, NOW())
ON DUPLICATE KEY UPDATE customer_code=customer_code; ON DUPLICATE KEY UPDATE customer_code=customer_code;
INSERT INTO customer_contact (tenant_id, customer_id, contact_name, phone, position, is_primary, status, created_by, created_time) INSERT INTO customer_contact (tenant_id, customer_id, contact_name, phone, position, is_primary, status, created_by, created_time)
VALUES ('1', '1', '张三', '13800138001', '总经理', 1, 1, '1', NOW()) VALUES (1, 1, '张三', '13800138001', '总经理', 1, 1, 1, NOW())
ON DUPLICATE KEY UPDATE contact_name=contact_name; ON DUPLICATE KEY UPDATE contact_name=contact_name;

View File

@ -1,9 +1,8 @@
-- ============================================= -- =============================================
-- 资金服务平台 - 支出管理数据库初始化脚本 -- 资金服务平台 - 支出管理数据库初始化脚本
-- Database: fund_exp -- Database: fund_exp
-- Version: 2.0 -- Version: 1.0
-- Created: 2026-02-22 -- Created: 2026-02-22
-- Updated: 2026-03-02 (主键类型改为VARCHAR雪花ID)
-- ============================================= -- =============================================
CREATE DATABASE IF NOT EXISTS fund_exp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS fund_exp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@ -14,19 +13,19 @@ USE fund_exp;
-- 1. 支出类型表 (expense_type) -- 1. 支出类型表 (expense_type)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS expense_type ( CREATE TABLE IF NOT EXISTS expense_type (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '支出类型ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
type_code VARCHAR(64) COMMENT '支出类型编码', type_code VARCHAR(64) COMMENT '支出类型编码',
type_name VARCHAR(128) NOT NULL COMMENT '支出类型名称', type_name VARCHAR(128) NOT NULL COMMENT '支出类型名称',
parent_id VARCHAR(32) NOT NULL DEFAULT '0' COMMENT '父类型ID, 0表示一级类型', parent_id BIGINT NOT NULL DEFAULT 0 COMMENT '父类型ID, 0表示一级类型',
type_level INT NOT NULL DEFAULT 1 COMMENT '类型层级', type_level INT NOT NULL DEFAULT 1 COMMENT '类型层级',
sort_order INT DEFAULT 0 COMMENT '排序号', sort_order INT DEFAULT 0 COMMENT '排序号',
description VARCHAR(500) COMMENT '类型描述', description VARCHAR(500) COMMENT '类型描述',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -40,34 +39,34 @@ CREATE TABLE IF NOT EXISTS expense_type (
-- 2. 支出表 (fund_expense) -- 2. 支出表 (fund_expense)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS fund_expense ( CREATE TABLE IF NOT EXISTS fund_expense (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '支出ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
expense_no VARCHAR(64) NOT NULL COMMENT '支出单号', expense_no VARCHAR(64) NOT NULL COMMENT '支出单号',
title VARCHAR(200) COMMENT '支出标题', title VARCHAR(200) COMMENT '支出标题',
amount DECIMAL(18,2) NOT NULL COMMENT '支出金额', amount DECIMAL(18,2) NOT NULL COMMENT '支出金额',
currency VARCHAR(16) DEFAULT 'CNY' COMMENT '币种', currency VARCHAR(16) DEFAULT 'CNY' COMMENT '币种',
expense_type VARCHAR(32) COMMENT '支出类型ID', expense_type BIGINT COMMENT '支出类型ID',
payee_name VARCHAR(128) COMMENT '收款单位', payee_name VARCHAR(128) COMMENT '收款单位',
payee_bank VARCHAR(128) COMMENT '收款银行', payee_bank VARCHAR(128) COMMENT '收款银行',
payee_account VARCHAR(64) COMMENT '收款账号', payee_account VARCHAR(64) COMMENT '收款账号',
expense_date DATETIME COMMENT '支出日期', expense_date DATETIME COMMENT '支出日期',
purpose VARCHAR(500) COMMENT '用途说明', purpose VARCHAR(500) COMMENT '用途说明',
request_id VARCHAR(32) COMMENT '关联用款申请ID', request_id BIGINT COMMENT '关联用款申请ID',
project_id VARCHAR(32) COMMENT '所属项目ID', project_id BIGINT COMMENT '所属项目ID',
customer_id VARCHAR(32) COMMENT '客户ID', customer_id BIGINT COMMENT '客户ID',
pay_status INT DEFAULT 0 COMMENT '支付状态: 0-待支付, 1-已支付, 2-支付失败', pay_status INT DEFAULT 0 COMMENT '支付状态: 0-待支付, 1-已支付, 2-支付失败',
pay_time DATETIME COMMENT '支付时间', pay_time DATETIME COMMENT '支付时间',
pay_channel VARCHAR(32) COMMENT '支付渠道', pay_channel VARCHAR(32) COMMENT '支付渠道',
pay_voucher VARCHAR(255) COMMENT '支付凭证', pay_voucher VARCHAR(255) COMMENT '支付凭证',
approval_status INT DEFAULT 0 COMMENT '审批状态', approval_status INT DEFAULT 0 COMMENT '审批状态',
approver_id VARCHAR(32) COMMENT '审批人ID', approver_id BIGINT COMMENT '审批人ID',
approval_time DATETIME COMMENT '审批时间', approval_time DATETIME COMMENT '审批时间',
approval_comment VARCHAR(500) COMMENT '审批意见', approval_comment VARCHAR(500) COMMENT '审批意见',
attachments VARCHAR(1000) COMMENT '附件URL', attachments VARCHAR(1000) COMMENT '附件URL',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -86,28 +85,28 @@ CREATE TABLE IF NOT EXISTS fund_expense (
-- 一级支出类型 -- 一级支出类型
INSERT INTO expense_type (id, tenant_id, type_code, type_name, parent_id, type_level, sort_order, status, created_by, created_time) INSERT INTO expense_type (id, tenant_id, type_code, type_name, parent_id, type_level, sort_order, status, created_by, created_time)
VALUES VALUES
('1', '1', 'LABOR', '人力成本', '0', 1, 1, 1, '1', NOW()), (1, 1, 'LABOR', '人力成本', 0, 1, 1, 1, 1, NOW()),
('2', '1', 'OFFICE', '办公费用', '0', 1, 2, 1, '1', NOW()), (2, 1, 'OFFICE', '办公费用', 0, 1, 2, 1, 1, NOW()),
('3', '1', 'TRAVEL', '差旅费用', '0', 1, 3, 1, '1', NOW()), (3, 1, 'TRAVEL', '差旅费用', 0, 1, 3, 1, 1, NOW()),
('4', '1', 'PURCHASE', '采购费用', '0', 1, 4, 1, '1', NOW()), (4, 1, 'PURCHASE', '采购费用', 0, 1, 4, 1, 1, NOW()),
('5', '1', 'OTHER', '其他费用', '0', 1, 5, 1, '1', NOW()) (5, 1, 'OTHER', '其他费用', 0, 1, 5, 1, 1, NOW())
ON DUPLICATE KEY UPDATE type_code=type_code; ON DUPLICATE KEY UPDATE type_code=type_code;
-- 二级支出类型 -- 二级支出类型
INSERT INTO expense_type (id, tenant_id, type_code, type_name, parent_id, type_level, sort_order, status, created_by, created_time) INSERT INTO expense_type (id, tenant_id, type_code, type_name, parent_id, type_level, sort_order, status, created_by, created_time)
VALUES VALUES
('11', '1', 'SALARY', '工资', '1', 2, 1, 1, '1', NOW()), (11, 1, 'SALARY', '工资', 1, 2, 1, 1, 1, NOW()),
('12', '1', 'BONUS', '奖金', '1', 2, 2, 1, '1', NOW()), (12, 1, 'BONUS', '奖金', 1, 2, 2, 1, 1, NOW()),
('13', '1', 'SOCIAL_INSURANCE', '社保', '1', 2, 3, 1, '1', NOW()), (13, 1, 'SOCIAL_INSURANCE', '社保', 1, 2, 3, 1, 1, NOW()),
('21', '1', 'RENT', '房租', '2', 2, 1, 1, '1', NOW()), (21, 1, 'RENT', '房租', 2, 2, 1, 1, 1, NOW()),
('22', '1', 'UTILITIES', '水电费', '2', 2, 2, 1, '1', NOW()), (22, 1, 'UTILITIES', '水电费', 2, 2, 2, 1, 1, NOW()),
('23', '1', 'SUPPLIES', '办公用品', '2', 2, 3, 1, '1', NOW()), (23, 1, 'SUPPLIES', '办公用品', 2, 2, 3, 1, 1, NOW()),
('31', '1', 'TRANSPORT', '交通费', '3', 2, 1, 1, '1', NOW()), (31, 1, 'TRANSPORT', '交通费', 3, 2, 1, 1, 1, NOW()),
('32', '1', 'ACCOMMODATION', '住宿费', '3', 2, 2, 1, '1', NOW()), (32, 1, 'ACCOMMODATION', '住宿费', 3, 2, 2, 1, 1, NOW()),
('33', '1', 'MEALS', '餐饮费', '3', 2, 3, 1, '1', NOW()), (33, 1, 'MEALS', '餐饮费', 3, 2, 3, 1, 1, NOW()),
('41', '1', 'EQUIPMENT', '设备采购', '4', 2, 1, 1, '1', NOW()), (41, 1, 'EQUIPMENT', '设备采购', 4, 2, 1, 1, 1, NOW()),
('42', '1', 'SOFTWARE', '软件采购', '4', 2, 2, 1, '1', NOW()), (42, 1, 'SOFTWARE', '软件采购', 4, 2, 2, 1, 1, NOW()),
('43', '1', 'SERVICE', '服务采购', '4', 2, 3, 1, '1', NOW()) (43, 1, 'SERVICE', '服务采购', 4, 2, 3, 1, 1, NOW())
ON DUPLICATE KEY UPDATE type_code=type_code; ON DUPLICATE KEY UPDATE type_code=type_code;
-- ============================================= -- =============================================

View File

@ -1,9 +1,8 @@
-- ============================================= -- =============================================
-- 资金服务平台 - 文件管理数据库初始化脚本 -- 资金服务平台 - 文件管理数据库初始化脚本
-- Database: fund_file -- Database: fund_file
-- Version: 2.0 -- Version: 1.0
-- Created: 2026-02-22 -- Created: 2026-02-22
-- Updated: 2026-03-02 (主键类型改为VARCHAR雪花ID)
-- ============================================= -- =============================================
CREATE DATABASE IF NOT EXISTS fund_file DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS fund_file DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@ -14,8 +13,8 @@ USE fund_file;
-- 文件记录表 (file_record) -- 文件记录表 (file_record)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS `file_record` ( CREATE TABLE IF NOT EXISTS `file_record` (
`file_id` VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', `file_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '文件ID',
`tenant_id` VARCHAR(32) NOT NULL COMMENT '租户ID', `tenant_id` BIGINT NOT NULL COMMENT '租户ID',
`file_name` VARCHAR(200) NOT NULL COMMENT '原始文件名', `file_name` VARCHAR(200) NOT NULL COMMENT '原始文件名',
`file_path` VARCHAR(500) NOT NULL COMMENT '文件存储路径', `file_path` VARCHAR(500) NOT NULL COMMENT '文件存储路径',
`file_url` VARCHAR(500) COMMENT '文件访问URL', `file_url` VARCHAR(500) COMMENT '文件访问URL',
@ -25,13 +24,13 @@ CREATE TABLE IF NOT EXISTS `file_record` (
`content_type` VARCHAR(100) COMMENT 'MIME类型', `content_type` VARCHAR(100) COMMENT 'MIME类型',
`md5` VARCHAR(32) COMMENT '文件MD5', `md5` VARCHAR(32) COMMENT '文件MD5',
`business_type` VARCHAR(50) COMMENT '业务类型(contract/receipt/expense/other)', `business_type` VARCHAR(50) COMMENT '业务类型(contract/receipt/expense/other)',
`business_id` VARCHAR(32) COMMENT '关联业务ID', `business_id` BIGINT COMMENT '关联业务ID',
`description` VARCHAR(500) COMMENT '文件描述', `description` VARCHAR(500) COMMENT '文件描述',
`download_count` INT NOT NULL DEFAULT 0 COMMENT '下载次数', `download_count` INT NOT NULL DEFAULT 0 COMMENT '下载次数',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态0-禁用1-启用', `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态0-禁用1-启用',
`created_by` VARCHAR(32) COMMENT '上传人ID', `created_by` BIGINT COMMENT '上传人ID',
`created_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `created_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_by` VARCHAR(32) COMMENT '更新人ID', `updated_by` BIGINT COMMENT '更新人ID',
`updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除0-未删除1-已删除', `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除0-未删除1-已删除',
PRIMARY KEY (`file_id`), PRIMARY KEY (`file_id`),

View File

@ -1,9 +1,9 @@
-- ============================================= -- =============================================
-- 资金服务平台 - 项目管理数据库初始化脚本 -- 资金服务平台 - 项目管理数据库初始化脚本
-- Database: fund_proj -- Database: fund_proj
-- Version: 2.0 -- Version: 1.1
-- Created: 2026-02-17 -- Created: 2026-02-17
-- Updated: 2026-03-02 (主键类型改为VARCHAR雪花ID) -- Updated: 2026-02-22 (添加requirement表)
-- ============================================= -- =============================================
CREATE DATABASE IF NOT EXISTS fund_proj DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS fund_proj DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@ -14,11 +14,11 @@ USE fund_proj;
-- 1. 项目表 (project) -- 1. 项目表 (project)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS project ( CREATE TABLE IF NOT EXISTS project (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '项目ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
project_code VARCHAR(64) NOT NULL COMMENT '项目编码', project_code VARCHAR(64) NOT NULL COMMENT '项目编码',
project_name VARCHAR(128) NOT NULL COMMENT '项目名称', project_name VARCHAR(128) NOT NULL COMMENT '项目名称',
customer_id VARCHAR(32) NOT NULL COMMENT '客户ID', customer_id BIGINT NOT NULL COMMENT '客户ID',
project_type VARCHAR(32) NOT NULL COMMENT '项目类型', project_type VARCHAR(32) NOT NULL COMMENT '项目类型',
budget_amount DECIMAL(18,2) COMMENT '预算金额', budget_amount DECIMAL(18,2) COMMENT '预算金额',
start_date DATE COMMENT '开始日期', start_date DATE COMMENT '开始日期',
@ -26,9 +26,9 @@ CREATE TABLE IF NOT EXISTS project (
project_manager VARCHAR(64) COMMENT '项目经理', project_manager VARCHAR(64) COMMENT '项目经理',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-已关闭, 1-进行中, 2-已完成', status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-已关闭, 1-进行中, 2-已完成',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -43,13 +43,13 @@ CREATE TABLE IF NOT EXISTS project (
-- 2. 需求工单表 (requirement) -- 2. 需求工单表 (requirement)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS requirement ( CREATE TABLE IF NOT EXISTS requirement (
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键,需求ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL COMMENT '租户ID',
requirement_code VARCHAR(50) NOT NULL COMMENT '需求编号', requirement_code VARCHAR(50) NOT NULL COMMENT '需求编号',
requirement_name VARCHAR(200) NOT NULL COMMENT '需求名称', requirement_name VARCHAR(200) NOT NULL COMMENT '需求名称',
description TEXT COMMENT '需求描述', description TEXT COMMENT '需求描述',
project_id VARCHAR(32) NOT NULL COMMENT '项目ID', project_id BIGINT NOT NULL COMMENT '项目ID',
customer_id VARCHAR(32) NOT NULL COMMENT '客户ID', customer_id BIGINT NOT NULL COMMENT '客户ID',
priority VARCHAR(20) DEFAULT 'normal' COMMENT '优先级high-高normal-中low-低', priority VARCHAR(20) DEFAULT 'normal' COMMENT '优先级high-高normal-中low-低',
estimated_hours DECIMAL(8,2) DEFAULT 0.00 COMMENT '预估开发工时(小时)', estimated_hours DECIMAL(8,2) DEFAULT 0.00 COMMENT '预估开发工时(小时)',
actual_hours DECIMAL(8,2) DEFAULT 0.00 COMMENT '实际开发工时(小时)', actual_hours DECIMAL(8,2) DEFAULT 0.00 COMMENT '实际开发工时(小时)',
@ -64,9 +64,9 @@ CREATE TABLE IF NOT EXISTS requirement (
progress INT DEFAULT 0 COMMENT '开发进度0-100', progress INT DEFAULT 0 COMMENT '开发进度0-100',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
attachment_url VARCHAR(500) COMMENT '附件URL', attachment_url VARCHAR(500) COMMENT '附件URL',
created_by VARCHAR(32) COMMENT '创建人ID', created_by BIGINT COMMENT '创建人ID',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人ID', updated_by BIGINT COMMENT '更新人ID',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT DEFAULT 0 COMMENT '逻辑删除0-未删除1-已删除', deleted TINYINT DEFAULT 0 COMMENT '逻辑删除0-未删除1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -81,7 +81,7 @@ CREATE TABLE IF NOT EXISTS requirement (
-- 初始化测试数据(租户ID=1) -- 初始化测试数据(租户ID=1)
-- ============================================= -- =============================================
INSERT INTO project (id, tenant_id, project_code, project_name, customer_id, project_type, budget_amount, start_date, status, created_by, created_time) INSERT INTO project (id, tenant_id, project_code, project_name, customer_id, project_type, budget_amount, start_date, status, created_by, created_time)
VALUES ('1', '1', 'PROJ001', '测试项目A', '1', '开发项目', 1000000.00, '2026-01-01', 1, '1', NOW()) VALUES (1, 1, 'PROJ001', '测试项目A', 1, '开发项目', 1000000.00, '2026-01-01', 1, 1, NOW())
ON DUPLICATE KEY UPDATE project_code=project_code; ON DUPLICATE KEY UPDATE project_code=project_code;
-- ============================================= -- =============================================

View File

@ -1,9 +1,8 @@
-- ============================================= -- =============================================
-- 资金服务平台 - 收款管理数据库初始化脚本 -- 资金服务平台 - 收款管理数据库初始化脚本
-- Database: fund_receipt -- Database: fund_receipt
-- Version: 2.0 -- Version: 1.0
-- Created: 2026-02-22 -- Created: 2026-02-22
-- Updated: 2026-03-02 (主键类型改为VARCHAR雪花ID)
-- ============================================= -- =============================================
CREATE DATABASE IF NOT EXISTS fund_receipt DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS fund_receipt DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@ -14,12 +13,12 @@ USE fund_receipt;
-- 1. 应收款表 (receivable) -- 1. 应收款表 (receivable)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS receivable ( CREATE TABLE IF NOT EXISTS receivable (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '应收款ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
receivable_code VARCHAR(64) NOT NULL COMMENT '应收款编号', receivable_code VARCHAR(64) NOT NULL COMMENT '应收款编号',
requirement_id VARCHAR(32) COMMENT '需求ID', requirement_id BIGINT COMMENT '需求ID',
project_id VARCHAR(32) NOT NULL COMMENT '项目ID', project_id BIGINT NOT NULL COMMENT '项目ID',
customer_id VARCHAR(32) NOT NULL COMMENT '客户ID', customer_id BIGINT NOT NULL COMMENT '客户ID',
receivable_amount DECIMAL(18,2) NOT NULL COMMENT '应收款金额', receivable_amount DECIMAL(18,2) NOT NULL COMMENT '应收款金额',
received_amount DECIMAL(18,2) DEFAULT 0.00 COMMENT '已收款金额', received_amount DECIMAL(18,2) DEFAULT 0.00 COMMENT '已收款金额',
unpaid_amount DECIMAL(18,2) DEFAULT 0.00 COMMENT '未收款金额', unpaid_amount DECIMAL(18,2) DEFAULT 0.00 COMMENT '未收款金额',
@ -31,11 +30,11 @@ CREATE TABLE IF NOT EXISTS receivable (
overdue_days INT DEFAULT 0 COMMENT '逾期天数', overdue_days INT DEFAULT 0 COMMENT '逾期天数',
confirm_status INT DEFAULT 0 COMMENT '确认状态: 0-待确认, 1-已确认', confirm_status INT DEFAULT 0 COMMENT '确认状态: 0-待确认, 1-已确认',
confirm_time DATETIME COMMENT '确认时间', confirm_time DATETIME COMMENT '确认时间',
confirm_by VARCHAR(32) COMMENT '确认人ID', confirm_by BIGINT COMMENT '确认人ID',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -52,8 +51,8 @@ CREATE TABLE IF NOT EXISTS receivable (
-- 2. 收款记录表 (fund_receipt) -- 2. 收款记录表 (fund_receipt)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS fund_receipt ( CREATE TABLE IF NOT EXISTS fund_receipt (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '收款记录ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
receipt_no VARCHAR(64) NOT NULL COMMENT '收款单号', receipt_no VARCHAR(64) NOT NULL COMMENT '收款单号',
title VARCHAR(200) COMMENT '收款标题', title VARCHAR(200) COMMENT '收款标题',
amount DECIMAL(18,2) NOT NULL COMMENT '收款金额', amount DECIMAL(18,2) NOT NULL COMMENT '收款金额',
@ -64,21 +63,21 @@ CREATE TABLE IF NOT EXISTS fund_receipt (
payer_account VARCHAR(64) COMMENT '付款账号', payer_account VARCHAR(64) COMMENT '付款账号',
receipt_date DATETIME COMMENT '收款日期', receipt_date DATETIME COMMENT '收款日期',
purpose VARCHAR(500) COMMENT '用途说明', purpose VARCHAR(500) COMMENT '用途说明',
project_id VARCHAR(32) COMMENT '项目ID', project_id BIGINT COMMENT '项目ID',
customer_id VARCHAR(32) COMMENT '客户ID', customer_id BIGINT COMMENT '客户ID',
receivable_id VARCHAR(32) COMMENT '应收款ID', receivable_id BIGINT COMMENT '应收款ID',
receipt_status INT DEFAULT 0 COMMENT '收款状态: 0-待确认, 1-已确认, 2-已核销', receipt_status INT DEFAULT 0 COMMENT '收款状态: 0-待确认, 1-已确认, 2-已核销',
confirm_time DATETIME COMMENT '确认时间', confirm_time DATETIME COMMENT '确认时间',
confirm_by VARCHAR(32) COMMENT '确认人ID', confirm_by BIGINT COMMENT '确认人ID',
write_off_time DATETIME COMMENT '核销时间', write_off_time DATETIME COMMENT '核销时间',
write_off_by VARCHAR(32) COMMENT '核销人ID', write_off_by BIGINT COMMENT '核销人ID',
voucher VARCHAR(255) COMMENT '收款凭证', voucher VARCHAR(255) COMMENT '收款凭证',
invoice_no VARCHAR(64) COMMENT '发票号', invoice_no VARCHAR(64) COMMENT '发票号',
attachments VARCHAR(1000) COMMENT '附件URL', attachments VARCHAR(1000) COMMENT '附件URL',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -94,7 +93,7 @@ CREATE TABLE IF NOT EXISTS fund_receipt (
-- 初始化测试数据(租户ID=1) -- 初始化测试数据(租户ID=1)
-- ============================================= -- =============================================
INSERT INTO receivable (id, tenant_id, receivable_code, project_id, customer_id, receivable_amount, received_amount, unpaid_amount, receivable_date, payment_due_date, status, created_by, created_time) INSERT INTO receivable (id, tenant_id, receivable_code, project_id, customer_id, receivable_amount, received_amount, unpaid_amount, receivable_date, payment_due_date, status, created_by, created_time)
VALUES ('1', '1', 'REC20260101001', '1', '1', 50000.00, 0.00, 50000.00, '2026-01-15', '2026-02-15', 'pending', '1', NOW()) VALUES (1, 1, 'REC20260101001', 1, 1, 50000.00, 0.00, 50000.00, '2026-01-15', '2026-02-15', 'pending', 1, NOW())
ON DUPLICATE KEY UPDATE receivable_code=receivable_code; ON DUPLICATE KEY UPDATE receivable_code=receivable_code;
-- ============================================= -- =============================================

View File

@ -1,9 +1,8 @@
-- ============================================= -- =============================================
-- 资金服务平台 - 用款申请数据库初始化脚本 -- 资金服务平台 - 用款申请数据库初始化脚本
-- Database: fund_req -- Database: fund_req
-- Version: 2.0 -- Version: 1.0
-- Created: 2026-02-17 -- Created: 2026-02-17
-- Updated: 2026-03-02 (主键类型改为VARCHAR雪花ID)
-- ============================================= -- =============================================
CREATE DATABASE IF NOT EXISTS fund_req DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS fund_req DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@ -14,8 +13,8 @@ USE fund_req;
-- 用款申请表 (fund_request) -- 用款申请表 (fund_request)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS fund_request ( CREATE TABLE IF NOT EXISTS fund_request (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '申请ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
request_no VARCHAR(64) NOT NULL COMMENT '申请单号', request_no VARCHAR(64) NOT NULL COMMENT '申请单号',
title VARCHAR(200) COMMENT '申请标题', title VARCHAR(200) COMMENT '申请标题',
amount DECIMAL(18,2) NOT NULL COMMENT '申请金额', amount DECIMAL(18,2) NOT NULL COMMENT '申请金额',
@ -25,20 +24,20 @@ CREATE TABLE IF NOT EXISTS fund_request (
payee_bank VARCHAR(128) COMMENT '收款银行', payee_bank VARCHAR(128) COMMENT '收款银行',
payee_account VARCHAR(64) COMMENT '收款账号', payee_account VARCHAR(64) COMMENT '收款账号',
purpose VARCHAR(500) COMMENT '用途说明', purpose VARCHAR(500) COMMENT '用途说明',
project_id VARCHAR(32) COMMENT '项目ID', project_id BIGINT COMMENT '项目ID',
customer_id VARCHAR(32) COMMENT '客户ID', customer_id BIGINT COMMENT '客户ID',
request_date DATETIME COMMENT '申请日期', request_date DATETIME COMMENT '申请日期',
expected_pay_date DATETIME COMMENT '期望付款日期', expected_pay_date DATETIME COMMENT '期望付款日期',
approval_status INT DEFAULT 0 COMMENT '审批状态: 0-待审批, 1-审批中, 2-审批通过, 3-审批拒绝, 4-已撤回', approval_status INT DEFAULT 0 COMMENT '审批状态: 0-待审批, 1-审批中, 2-审批通过, 3-审批拒绝, 4-已撤回',
current_node INT COMMENT '当前审批节点', current_node INT COMMENT '当前审批节点',
approver_id VARCHAR(32) COMMENT '审批人ID', approver_id BIGINT COMMENT '审批人ID',
approval_time DATETIME COMMENT '审批时间', approval_time DATETIME COMMENT '审批时间',
approval_comment VARCHAR(500) COMMENT '审批意见', approval_comment VARCHAR(500) COMMENT '审批意见',
attachments VARCHAR(1000) COMMENT '附件URL', attachments VARCHAR(1000) COMMENT '附件URL',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -50,7 +49,7 @@ CREATE TABLE IF NOT EXISTS fund_request (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用款申请表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用款申请表';
INSERT INTO fund_request (id, tenant_id, request_no, title, amount, request_type, purpose, project_id, approval_status, created_by, created_time) INSERT INTO fund_request (id, tenant_id, request_no, title, amount, request_type, purpose, project_id, approval_status, created_by, created_time)
VALUES ('1', '1', 'REQ20260101001', '测试用款申请', 50000.00, 2, '测试用款申请', '1', 0, '1', NOW()) VALUES (1, 1, 'REQ20260101001', '测试用款申请', 50000.00, 2, '测试用款申请', 1, 0, 1, NOW())
ON DUPLICATE KEY UPDATE request_no=request_no; ON DUPLICATE KEY UPDATE request_no=request_no;
-- ============================================= -- =============================================

View File

@ -1,10 +1,9 @@
-- ============================================= -- =============================================
-- 资金服务平台 - 系统服务数据库初始化脚本 -- 资金服务平台 - 系统服务数据库初始化脚本
-- Database: fund_sys -- Database: fund_sys
-- Version: 2.0 -- Version: 1.0
-- Author: fundplatform team -- Author: fundplatform team
-- Created: 2026-02-17 -- Created: 2026-02-17
-- Updated: 2026-03-02 (主键类型改为VARCHAR雪花ID)
-- ============================================= -- =============================================
-- 创建数据库 -- 创建数据库
@ -16,20 +15,20 @@ USE fund_sys;
-- 1. 用户表 (sys_user) -- 1. 用户表 (sys_user)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS sys_user ( CREATE TABLE IF NOT EXISTS sys_user (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '用户ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
username VARCHAR(64) NOT NULL COMMENT '用户名', username VARCHAR(64) NOT NULL COMMENT '用户名',
password VARCHAR(128) NOT NULL COMMENT '密码 (MD5)', password VARCHAR(128) NOT NULL COMMENT '密码 (MD5)',
real_name VARCHAR(64) COMMENT '真实姓名', real_name VARCHAR(64) COMMENT '真实姓名',
phone VARCHAR(20) COMMENT '手机号', phone VARCHAR(20) COMMENT '手机号',
email VARCHAR(128) COMMENT '邮箱', email VARCHAR(128) COMMENT '邮箱',
dept_id VARCHAR(32) COMMENT '部门ID', dept_id BIGINT COMMENT '部门ID',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
avatar VARCHAR(255) COMMENT '头像URL', avatar VARCHAR(255) COMMENT '头像URL',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -43,17 +42,17 @@ CREATE TABLE IF NOT EXISTS sys_user (
-- 2. 角色表 (sys_role) -- 2. 角色表 (sys_role)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS sys_role ( CREATE TABLE IF NOT EXISTS sys_role (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '角色ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
role_code VARCHAR(64) NOT NULL COMMENT '角色编码', role_code VARCHAR(64) NOT NULL COMMENT '角色编码',
role_name VARCHAR(128) NOT NULL COMMENT '角色名称', role_name VARCHAR(128) NOT NULL COMMENT '角色名称',
data_scope TINYINT NOT NULL DEFAULT 1 COMMENT '数据权限: 1-全部, 2-本部门及子部门, 3-仅本部门, 4-仅本人', data_scope TINYINT NOT NULL DEFAULT 1 COMMENT '数据权限: 1-全部, 2-本部门及子部门, 3-仅本部门, 4-仅本人',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
sort_order INT DEFAULT 0 COMMENT '排序号', sort_order INT DEFAULT 0 COMMENT '排序号',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -66,11 +65,11 @@ CREATE TABLE IF NOT EXISTS sys_role (
-- 3. 用户角色关联表 (sys_user_role) -- 3. 用户角色关联表 (sys_user_role)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS sys_user_role ( CREATE TABLE IF NOT EXISTS sys_user_role (
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
user_id VARCHAR(32) NOT NULL COMMENT '用户ID', user_id BIGINT NOT NULL COMMENT '用户ID',
role_id VARCHAR(32) NOT NULL COMMENT '角色ID', role_id BIGINT NOT NULL COMMENT '角色ID',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (id), PRIMARY KEY (id),
UNIQUE KEY uk_tenant_user_role (tenant_id, user_id, role_id), UNIQUE KEY uk_tenant_user_role (tenant_id, user_id, role_id),
@ -82,9 +81,9 @@ CREATE TABLE IF NOT EXISTS sys_user_role (
-- 4. 菜单表 (sys_menu) -- 4. 菜单表 (sys_menu)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS sys_menu ( CREATE TABLE IF NOT EXISTS sys_menu (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '菜单ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
parent_id VARCHAR(32) NOT NULL DEFAULT '0' COMMENT '父菜单ID, 0表示根菜单', parent_id BIGINT NOT NULL DEFAULT 0 COMMENT '父菜单ID, 0表示根菜单',
menu_name VARCHAR(128) NOT NULL COMMENT '菜单名称', menu_name VARCHAR(128) NOT NULL COMMENT '菜单名称',
menu_type TINYINT NOT NULL DEFAULT 1 COMMENT '菜单类型: 1-目录, 2-菜单, 3-按钮', menu_type TINYINT NOT NULL DEFAULT 1 COMMENT '菜单类型: 1-目录, 2-菜单, 3-按钮',
menu_path VARCHAR(255) COMMENT '路由路径', menu_path VARCHAR(255) COMMENT '路由路径',
@ -95,9 +94,9 @@ CREATE TABLE IF NOT EXISTS sys_menu (
visible TINYINT NOT NULL DEFAULT 1 COMMENT '是否可见: 0-隐藏, 1-显示', visible TINYINT NOT NULL DEFAULT 1 COMMENT '是否可见: 0-隐藏, 1-显示',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -110,11 +109,11 @@ CREATE TABLE IF NOT EXISTS sys_menu (
-- 5. 角色菜单关联表 (sys_role_menu) -- 5. 角色菜单关联表 (sys_role_menu)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS sys_role_menu ( CREATE TABLE IF NOT EXISTS sys_role_menu (
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
role_id VARCHAR(32) NOT NULL COMMENT '角色ID', role_id BIGINT NOT NULL COMMENT '角色ID',
menu_id VARCHAR(32) NOT NULL COMMENT '菜单ID', menu_id BIGINT NOT NULL COMMENT '菜单ID',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (id), PRIMARY KEY (id),
UNIQUE KEY uk_tenant_role_menu (tenant_id, role_id, menu_id), UNIQUE KEY uk_tenant_role_menu (tenant_id, role_id, menu_id),
@ -126,9 +125,9 @@ CREATE TABLE IF NOT EXISTS sys_role_menu (
-- 6. 部门表 (sys_dept) -- 6. 部门表 (sys_dept)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS sys_dept ( CREATE TABLE IF NOT EXISTS sys_dept (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '部门ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
parent_id VARCHAR(32) NOT NULL DEFAULT '0' COMMENT '父部门ID, 0表示根部门', parent_id BIGINT NOT NULL DEFAULT 0 COMMENT '父部门ID, 0表示根部门',
dept_code VARCHAR(64) NOT NULL COMMENT '部门编码', dept_code VARCHAR(64) NOT NULL COMMENT '部门编码',
dept_name VARCHAR(128) NOT NULL COMMENT '部门名称', dept_name VARCHAR(128) NOT NULL COMMENT '部门名称',
dept_leader VARCHAR(64) COMMENT '部门负责人', dept_leader VARCHAR(64) COMMENT '部门负责人',
@ -137,9 +136,9 @@ CREATE TABLE IF NOT EXISTS sys_dept (
sort_order INT DEFAULT 0 COMMENT '排序号', sort_order INT DEFAULT 0 COMMENT '排序号',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -153,17 +152,17 @@ CREATE TABLE IF NOT EXISTS sys_dept (
-- 7. 数据字典表 (sys_dict) -- 7. 数据字典表 (sys_dict)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS sys_dict ( CREATE TABLE IF NOT EXISTS sys_dict (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '字典ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
dict_type VARCHAR(64) NOT NULL COMMENT '字典类型', dict_type VARCHAR(64) NOT NULL COMMENT '字典类型',
dict_label VARCHAR(128) NOT NULL COMMENT '字典标签', dict_label VARCHAR(128) NOT NULL COMMENT '字典标签',
dict_value VARCHAR(128) NOT NULL COMMENT '字典值', dict_value VARCHAR(128) NOT NULL COMMENT '字典值',
sort_order INT DEFAULT 0 COMMENT '排序号', sort_order INT DEFAULT 0 COMMENT '排序号',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -177,8 +176,8 @@ CREATE TABLE IF NOT EXISTS sys_dict (
-- 8. 系统配置表 (sys_config) -- 8. 系统配置表 (sys_config)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS sys_config ( CREATE TABLE IF NOT EXISTS sys_config (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '配置ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
config_key VARCHAR(128) NOT NULL COMMENT '配置键', config_key VARCHAR(128) NOT NULL COMMENT '配置键',
config_value TEXT COMMENT '配置值', config_value TEXT COMMENT '配置值',
config_type VARCHAR(64) DEFAULT 'string' COMMENT '配置类型: string/number/boolean/json', config_type VARCHAR(64) DEFAULT 'string' COMMENT '配置类型: string/number/boolean/json',
@ -189,9 +188,9 @@ CREATE TABLE IF NOT EXISTS sys_config (
group_name VARCHAR(128) COMMENT '分组名称', group_name VARCHAR(128) COMMENT '分组名称',
sort_order INT DEFAULT 0 COMMENT '排序号', sort_order INT DEFAULT 0 COMMENT '排序号',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
PRIMARY KEY (id), PRIMARY KEY (id),
@ -204,9 +203,9 @@ CREATE TABLE IF NOT EXISTS sys_config (
-- 9. 操作日志表 (sys_log) -- 9. 操作日志表 (sys_log)
-- ============================================= -- =============================================
CREATE TABLE IF NOT EXISTS sys_log ( CREATE TABLE IF NOT EXISTS sys_log (
id VARCHAR(32) NOT NULL COMMENT '主键ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '日志ID',
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID', tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
user_id VARCHAR(32) COMMENT '操作用户ID', user_id BIGINT COMMENT '操作用户ID',
username VARCHAR(64) COMMENT '操作用户名', username VARCHAR(64) COMMENT '操作用户名',
operation VARCHAR(128) COMMENT '操作描述', operation VARCHAR(128) COMMENT '操作描述',
method VARCHAR(255) COMMENT '请求方法', method VARCHAR(255) COMMENT '请求方法',
@ -226,10 +225,12 @@ CREATE TABLE IF NOT EXISTS sys_log (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统操作日志表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统操作日志表';
-- ============================================= -- =============================================
-- 10. 租户表 (sys_tenant) -- 初始化数据
-- ============================================= -- =============================================
-- 创建租户表 (sys_tenant)
CREATE TABLE IF NOT EXISTS sys_tenant ( CREATE TABLE IF NOT EXISTS sys_tenant (
id VARCHAR(32) NOT NULL COMMENT '租户ID雪花算法', id BIGINT NOT NULL AUTO_INCREMENT COMMENT '租户ID',
tenant_code VARCHAR(50) NOT NULL COMMENT '租户编码', tenant_code VARCHAR(50) NOT NULL COMMENT '租户编码',
tenant_name VARCHAR(100) NOT NULL COMMENT '租户名称', tenant_name VARCHAR(100) NOT NULL COMMENT '租户名称',
contact VARCHAR(50) COMMENT '联系人', contact VARCHAR(50) COMMENT '联系人',
@ -241,41 +242,37 @@ CREATE TABLE IF NOT EXISTS sys_tenant (
max_users INT NOT NULL DEFAULT 10 COMMENT '最大用户数', max_users INT NOT NULL DEFAULT 10 COMMENT '最大用户数',
remark VARCHAR(500) COMMENT '备注', remark VARCHAR(500) COMMENT '备注',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
created_by VARCHAR(32) COMMENT '创建人', created_by BIGINT COMMENT '创建人',
created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', created_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_by VARCHAR(32) COMMENT '更新人', updated_by BIGINT COMMENT '更新人',
updated_time DATETIME ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', updated_time DATETIME ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id), PRIMARY KEY (id),
UNIQUE KEY uk_tenant_code (tenant_code, deleted) UNIQUE KEY uk_tenant_code (tenant_code, deleted)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户表';
-- =============================================
-- 初始化数据 (使用雪花ID)
-- =============================================
-- 插入默认租户 -- 插入默认租户
INSERT INTO sys_tenant (id, tenant_code, tenant_name, contact, phone, status, max_users, remark, created_time) INSERT INTO sys_tenant (id, tenant_code, tenant_name, contact, phone, status, max_users, remark, created_time)
VALUES ('1', 'DEFAULT', '默认租户', '管理员', '13800138000', 1, 100, '系统默认租户', NOW()) VALUES (1, 'DEFAULT', '默认租户', '管理员', '13800138000', 1, 100, '系统默认租户', NOW())
ON DUPLICATE KEY UPDATE tenant_code=tenant_code; ON DUPLICATE KEY UPDATE tenant_code=tenant_code;
-- 插入超级管理员用户 (租户ID=1, 密码: admin123, MD5: 0192023a7bbd73250516f069df18b500) -- 插入超级管理员用户 (租户ID=1, 密码: admin123)
INSERT INTO sys_user (id, tenant_id, username, password, real_name, phone, status, created_by, created_time) INSERT INTO sys_user (id, tenant_id, username, password, real_name, phone, status, created_by, created_time)
VALUES ('1', '1', 'admin', '0192023a7bbd73250516f069df18b500', '超级管理员', '13800138000', 1, '1', NOW()) VALUES (1, 1, 'admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5E', '超级管理员', '13800138000', 1, 1, NOW())
ON DUPLICATE KEY UPDATE username=username; ON DUPLICATE KEY UPDATE username=username;
-- 插入超级管理员角色 -- 插入超级管理员角色
INSERT INTO sys_role (id, tenant_id, role_code, role_name, data_scope, status, created_by, created_time) INSERT INTO sys_role (id, tenant_id, role_code, role_name, data_scope, status, created_by, created_time)
VALUES ('1', '1', 'admin', '超级管理员', 1, 1, '1', NOW()) VALUES (1, 1, 'admin', '超级管理员', 1, 1, 1, NOW())
ON DUPLICATE KEY UPDATE role_code=role_code; ON DUPLICATE KEY UPDATE role_code=role_code;
-- 关联超级管理员用户和角色 -- 关联超级管理员用户和角色
INSERT INTO sys_user_role (id, tenant_id, user_id, role_id, created_by, created_time) INSERT INTO sys_user_role (tenant_id, user_id, role_id, created_by, created_time)
VALUES ('1', '1', '1', '1', '1', NOW()) VALUES (1, 1, 1, 1, NOW())
ON DUPLICATE KEY UPDATE user_id=user_id; ON DUPLICATE KEY UPDATE user_id=user_id;
-- 插入根部门 -- 插入根部门
INSERT INTO sys_dept (id, tenant_id, parent_id, dept_code, dept_name, status, created_by, created_time) INSERT INTO sys_dept (id, tenant_id, parent_id, dept_code, dept_name, status, created_by, created_time)
VALUES ('1', '1', '0', 'ROOT', '根部门', 1, '1', NOW()) VALUES (1, 1, 0, 'ROOT', '根部门', 1, 1, NOW())
ON DUPLICATE KEY UPDATE dept_code=dept_code; ON DUPLICATE KEY UPDATE dept_code=dept_code;
-- ============================================= -- =============================================

View File

@ -622,69 +622,6 @@ spring:
/opt/fundplatform/deploy/status.sh /opt/fundplatform/deploy/status.sh
``` ```
### 4.8 服务启动方式说明(重要)
**⚠️ 重要提示:** 为了打包发布的需要,每个服务 JAR 都采用瘦包方式打包(依赖分离),因此**不能**使用 `java -jar fund*.jar` 方式执行。
#### 4.8.1 正确的启动方式
**方式一:使用启动脚本(推荐)**
```bash
# 使用服务自带的启动脚本
cd /opt/fundplatform/deploy/fund-sys
./bin/start.sh
# 或使用一键启动脚本
cd /opt/fundplatform/deploy
./start-all.sh
```
**方式二:使用 Maven Spring Boot 插件**
```bash
# 开发环境下,可以使用 Maven 插件启动
cd fund-sys
mvn spring-boot:run
```
**方式三:手动指定类路径(不推荐)**
```bash
# 如果必须手动启动,需要指定完整的类路径
cd /opt/fundplatform/deploy/fund-sys
java -cp "lib/*:conf/" com.fundplatform.sys.SysApplication
```
#### 4.8.2 错误的启动方式
**❌ 错误示例:**
```bash
# 以下方式是错误的,会导致 ClassNotFoundException
cd /opt/fundplatform/deploy/fund-sys/lib
java -jar fund-sys.jar
# 或在开发环境
cd fund-sys/target
java -jar fund-sys-0.0.1-SNAPSHOT.jar
```
#### 4.8.3 为什么不能使用 java -jar
1. **瘦包打包方式**:项目使用 Maven Assembly Plugin 将依赖 JAR 分离到 `lib/` 目录
2. **Manifest 配置**:主 JAR 包的 MANIFEST.MF 中没有 Class-Path 属性
3. **类加载机制**JVM 无法自动找到 `lib/` 目录下的依赖
#### 4.8.4 调试建议
**开发环境调试:**
- 使用 IDE 直接运行 Application 类
- 或使用 `mvn spring-boot:run` 命令
- 避免使用 `java -jar` 命令
**生产环境调试:**
- 始终使用 `bin/start.sh` 脚本启动
- 查看日志:`tail -f /datacfs/applogs/fund-sys/info.log`
- 检查进程:`ps aux | grep fund-sys`
- 查看端口:`netstat -tlnp | grep 8100`
## 五、部署操作指南 ## 五、部署操作指南
### 5.1 首次部署 ### 5.1 首次部署

View File

@ -161,77 +161,10 @@ npm run dev
# 移动端H5http://localhost:8080 # 移动端H5http://localhost:8080
# 网关地址http://localhost:8000 # 网关地址http://localhost:8000
# Nacos 控制台http://localhost:8048/nacos # Nacos 控制台http://localhost:8048/nacos
# Grafana 监控http://localhost:3000 (Docker 环境) 或 http://localhost:3001 (本地开发) # Grafana 监控http://localhost:3000 (Docker环境) 或 http://localhost:3001 (本地开发)
# Prometheushttp://localhost:9090 # Prometheushttp://localhost:9090
``` ```
### 1.5 服务启动方式说明(重要)
**⚠️ 重要提示:** 为了打包发布的需要,每个服务 JAR 都采用瘦包方式打包(依赖分离),因此**不能**使用 `java -jar` 方式执行。
#### 1.5.1 开发环境启动方式
**✅ 正确的方式:**
1. **使用 Maven 插件(推荐)**
```bash
cd fund-sys
mvn spring-boot:run
```
2. **使用 IDE 直接运行**
- 找到对应的 Application 类(如 `SysApplication.java`
- 右键 -> Run 运行
**❌ 错误的方式:**
```bash
# 以下方式会导致 ClassNotFoundException
cd fund-sys/target
java -jar fund-sys-0.0.1-SNAPSHOT.jar
```
#### 1.5.2 生产环境启动方式
**✅ 正确的方式:**
1. **使用启动脚本(推荐)**
```bash
cd /opt/fundplatform/deploy/fund-sys
./bin/start.sh
```
2. **手动指定类路径**
```bash
cd /opt/fundplatform/deploy/fund-sys
java -cp "lib/*:conf/" com.fundplatform.sys.SysApplication
```
**❌ 错误的方式:**
```bash
# 以下方式会导致 ClassNotFoundException
cd /opt/fundplatform/deploy/fund-sys/lib
java -jar fund-sys.jar
```
#### 1.5.3 为什么不能使用 java -jar
1. **瘦包打包方式**:项目使用 Maven Assembly Plugin 将依赖 JAR 分离到 `lib/` 目录
2. **Manifest 配置**:主 JAR 包的 MANIFEST.MF 中没有 Class-Path 属性
3. **类加载机制**JVM 无法自动找到 `lib/` 目录下的依赖
#### 1.5.4 调试建议
**开发环境:**
- ✅ 使用 IDE 直接运行 Application 类
- ✅ 使用 `mvn spring-boot:run` 命令
- ❌ 避免使用 `java -jar` 命令
**生产环境:**
- ✅ 始终使用 `bin/start.sh` 脚本启动
- ✅ 查看日志:`tail -f /datacfs/applogs/fund-sys/info.log`
- ✅ 检查进程:`ps aux | grep fund-sys`
- ✅ 查看端口:`netstat -tlnp | grep 8100`
--- ---
## 二、Docker Compose 部署 ## 二、Docker Compose 部署

View File

@ -12,7 +12,6 @@
"axios": "^1.13.5", "axios": "^1.13.5",
"echarts": "^6.0.0", "echarts": "^6.0.0",
"element-plus": "^2.13.2", "element-plus": "^2.13.2",
"js-md5": "^0.8.3",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"vue": "^3.5.25", "vue": "^3.5.25",
"vue-router": "^5.0.2" "vue-router": "^5.0.2"
@ -2485,12 +2484,6 @@
"url": "https://github.com/sponsors/mesqueeb" "url": "https://github.com/sponsors/mesqueeb"
} }
}, },
"node_modules/js-md5": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz",
"integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==",
"license": "MIT"
},
"node_modules/jsesc": { "node_modules/jsesc": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",

View File

@ -14,7 +14,6 @@
"axios": "^1.13.5", "axios": "^1.13.5",
"echarts": "^6.0.0", "echarts": "^6.0.0",
"element-plus": "^2.13.2", "element-plus": "^2.13.2",
"js-md5": "^0.8.3",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"vue": "^3.5.25", "vue": "^3.5.25",
"vue-router": "^5.0.2" "vue-router": "^5.0.2"

View File

@ -1,13 +1,8 @@
import { request } from './request' import { request } from './request'
// 登录 // 登录
export function login(data: { username: string; password: string, tenantId?: number }) { export function login(data: { username: string; password: string }) {
// 如果没有传递 tenantId使用默认值 1 return request.post('/auth/login', data)
const requestData = {
...data,
tenantId: data.tenantId || 1
}
return request.post('/auth/login', requestData)
} }
// 登出 // 登出

View File

@ -24,7 +24,7 @@
</div> </div>
<el-table :data="tableData" v-loading="loading" border stripe> <el-table :data="tableData" v-loading="loading" border stripe>
<el-table-column prop="id" label="类型 ID" width="80" /> <el-table-column prop="typeId" label="类型ID" width="80" />
<el-table-column prop="typeCode" label="类型编码" width="140" /> <el-table-column prop="typeCode" label="类型编码" width="140" />
<el-table-column prop="typeName" label="类型名称" min-width="150" /> <el-table-column prop="typeName" label="类型名称" min-width="150" />
<el-table-column prop="parentName" label="上级类型" width="140" /> <el-table-column prop="parentName" label="上级类型" width="140" />
@ -40,7 +40,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="description" label="描述" min-width="200" show-overflow-tooltip /> <el-table-column prop="description" label="描述" min-width="200" show-overflow-tooltip />
<el-table-column prop="createdTime" label="创建时间" width="160" /> <el-table-column prop="createTime" label="创建时间" width="160" />
<el-table-column label="操作" width="180" fixed="right"> <el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button link type="primary" :icon="Edit" @click="handleEdit(row)">编辑</el-button> <el-button link type="primary" :icon="Edit" @click="handleEdit(row)">编辑</el-button>
@ -81,7 +81,7 @@
<el-tree-select <el-tree-select
v-model="form.parentId" v-model="form.parentId"
:data="typeTreeData" :data="typeTreeData"
:props="{ label: 'typeName', value: 'id', children: 'children' }" :props="{ label: 'typeName', value: 'typeId', children: 'children' }"
placeholder="请选择上级类型(不选则为顶级类型)" placeholder="请选择上级类型(不选则为顶级类型)"
check-strictly check-strictly
clearable clearable
@ -137,7 +137,7 @@ const dialogTitle = ref('新增类型')
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()
const form = reactive({ const form = reactive({
id: null as number | null, typeId: null as number | null,
typeCode: '', typeCode: '',
typeName: '', typeName: '',
parentId: null as number | null, parentId: null as number | null,
@ -154,40 +154,11 @@ const rules = reactive<FormRules>({
const fetchData = async () => { const fetchData = async () => {
loading.value = true loading.value = true
try { try {
// ENABLED/DISABLED 1/0 const res: any = await getExpenseTypeList(queryParams)
const apiParams: any = { tableData.value = res.data?.records || []
pageNum: queryParams.pageNum,
pageSize: queryParams.pageSize,
typeName: queryParams.typeName
}
// ENABLED -> 1, DISABLED -> 0
if (queryParams.status === 'ENABLED') {
apiParams.status = 1
} else if (queryParams.status === 'DISABLED') {
apiParams.status = 0
}
console.log('请求参数:', apiParams)
const res: any = await getExpenseTypeList(apiParams)
console.log('API 响应:', res)
console.log('res.data:', res.data)
// records list
const rawData = res.data?.records || res.data?.list || []
console.log('原始数据:', rawData)
tableData.value = rawData.map((item: any) => ({
...item,
status: item.status === 1 ? 'ENABLED' : 'DISABLED'
}))
console.log('处理后的表格数据:', tableData.value)
total.value = res.data?.total || 0 total.value = res.data?.total || 0
console.log('总数:', total.value)
} catch (e) { } catch (e) {
console.error('获取数据失败:', e) console.error(e)
ElMessage.error('获取数据失败')
} finally { } finally {
loading.value = false loading.value = false
} }
@ -228,15 +199,11 @@ const handleEdit = (row: any) => {
const handleStatusChange = async (row: any) => { const handleStatusChange = async (row: any) => {
try { try {
// await updateExpenseType(row.typeId, { status: row.status })
const statusValue = row.status === 'ENABLED' ? 1 : 0
await updateExpenseType(row.id, { status: statusValue })
ElMessage.success('状态更新成功') ElMessage.success('状态更新成功')
} catch (e) { } catch (e) {
console.error(e) console.error(e)
//
row.status = row.status === 'ENABLED' ? 'DISABLED' : 'ENABLED' row.status = row.status === 'ENABLED' ? 'DISABLED' : 'ENABLED'
ElMessage.error('状态更新失败')
} }
} }
@ -247,7 +214,7 @@ const handleDelete = (row: any) => {
type: 'warning' type: 'warning'
}).then(async () => { }).then(async () => {
try { try {
await deleteExpenseType(row.id) await deleteExpenseType(row.typeId)
ElMessage.success('删除成功') ElMessage.success('删除成功')
fetchData() fetchData()
fetchTypeTree() fetchTypeTree()
@ -264,32 +231,18 @@ const handleSubmit = async () => {
submitLoading.value = true submitLoading.value = true
try { try {
// if (form.typeId) {
const submitData: any = { ...form } await updateExpenseType(form.typeId, form)
submitData.status = form.status === 'ENABLED' ? 1 : 0
// parentId null 0
if (submitData.parentId === null || submitData.parentId === undefined) {
submitData.parentId = 0
}
console.log('提交数据:', submitData)
if (form.id) {
console.log('更新类型 ID:', form.id)
await updateExpenseType(form.id, submitData)
ElMessage.success('更新成功') ElMessage.success('更新成功')
} else { } else {
console.log('创建新类型') await createExpenseType(form)
await createExpenseType(submitData)
ElMessage.success('创建成功') ElMessage.success('创建成功')
} }
dialogVisible.value = false dialogVisible.value = false
fetchData() fetchData()
fetchTypeTree() fetchTypeTree()
} catch (e) { } catch (e) {
console.error('提交失败:', e) console.error(e)
ElMessage.error(e instanceof Error ? e.message : '操作失败')
} finally { } finally {
submitLoading.value = false submitLoading.value = false
} }
@ -297,7 +250,7 @@ const handleSubmit = async () => {
} }
const resetForm = () => { const resetForm = () => {
form.id = null form.typeId = null
form.typeCode = '' form.typeCode = ''
form.typeName = '' form.typeName = ''
form.parentId = null form.parentId = null

View File

@ -51,7 +51,6 @@ import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import md5 from 'js-md5'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
@ -71,16 +70,35 @@ const rules: FormRules = {
password: [{ required: true, message: '请输入密码', trigger: 'blur' }] password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
} }
// MD5 使 crypto-js
const md5 = (str: string): string => {
// 使 MD5 crypto-js blueimp-md5
// npm install crypto-js
// import CryptoJS from 'crypto-js'
// return CryptoJS.MD5(str).toString()
// MD5
// 使npm install blueimp-md5 && import md5 from 'blueimp-md5'
let hash = 0
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i)
hash = ((hash << 5) - hash) + char
hash = hash & hash
}
return Math.abs(hash).toString(16).padStart(32, '0')
}
const handleLogin = async () => { const handleLogin = async () => {
if (!formRef.value) return if (!formRef.value) return
try { try {
await formRef.value.validate() await formRef.value.validate()
loading.value = true loading.value = true
// MD5 使 // MD5
const encryptedPassword = md5(form.password) const encryptedPassword = md5(form.password)
console.log('登录 - 原始密码:', form.password, 'MD5 加密后:', encryptedPassword)
await userStore.loginAction(form.username, encryptedPassword) await userStore.loginAction(form.username, encryptedPassword)
// //

View File

@ -127,7 +127,6 @@ import { ElMessage, FormInstance, FormRules, UploadProps } from 'element-plus'
import { UserFilled, OfficeBuilding } from '@element-plus/icons-vue' import { UserFilled, OfficeBuilding } from '@element-plus/icons-vue'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { updateProfile, updatePassword } from '@/api/user' import { updateProfile, updatePassword } from '@/api/user'
import md5 from 'js-md5'
const userStore = useUserStore() const userStore = useUserStore()
const defaultAvatar = 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png' const defaultAvatar = 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png'
@ -232,16 +231,12 @@ const handleUpdateProfile = async () => {
const handleUpdatePassword = async () => { const handleUpdatePassword = async () => {
if (!passwordFormRef.value) return if (!passwordFormRef.value) return
await passwordFormRef.value.validate(async (valid) => { await passwordFormRef.value.validate(async (valid) => {
if (valid) { if (valid) {
saving.value = true saving.value = true
try { try {
await updatePassword({ await updatePassword(passwordForm)
oldPassword: md5(passwordForm.oldPassword),
newPassword: md5(passwordForm.newPassword),
confirmPassword: md5(passwordForm.confirmPassword)
})
ElMessage.success('密码修改成功') ElMessage.success('密码修改成功')
resetPasswordForm() resetPasswordForm()
} catch (error: any) { } catch (error: any) {

View File

@ -78,13 +78,6 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- Hutool (雪花ID生成器) -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.25</version>
</dependency>
<!-- Test --> <!-- Test -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -17,7 +17,7 @@ public class TokenInfo implements Serializable {
/** /**
* 用户ID * 用户ID
*/ */
private String userId; private Long userId;
/** /**
* 用户名 * 用户名
@ -27,7 +27,7 @@ public class TokenInfo implements Serializable {
/** /**
* 租户ID * 租户ID
*/ */
private String tenantId; private Long tenantId;
/** /**
* 登录时间戳 * 登录时间戳
@ -42,7 +42,7 @@ public class TokenInfo implements Serializable {
public TokenInfo() { public TokenInfo() {
} }
public TokenInfo(String userId, String username, String tenantId, Long expireTime) { public TokenInfo(Long userId, String username, Long tenantId, Long expireTime) {
this.userId = userId; this.userId = userId;
this.username = username; this.username = username;
this.tenantId = tenantId; this.tenantId = tenantId;
@ -50,11 +50,11 @@ public class TokenInfo implements Serializable {
this.expireTime = expireTime; this.expireTime = expireTime;
} }
public String getUserId() { public Long getUserId() {
return userId; return userId;
} }
public void setUserId(String userId) { public void setUserId(Long userId) {
this.userId = userId; this.userId = userId;
} }
@ -66,11 +66,11 @@ public class TokenInfo implements Serializable {
this.username = username; this.username = username;
} }
public String getTenantId() { public Long getTenantId() {
return tenantId; return tenantId;
} }
public void setTenantId(String tenantId) { public void setTenantId(Long tenantId) {
this.tenantId = tenantId; this.tenantId = tenantId;
} }

View File

@ -42,7 +42,7 @@ public class TokenService {
* @param tenantId 租户ID * @param tenantId 租户ID
* @return Token字符串 * @return Token字符串
*/ */
public String generateToken(String userId, String username, String tenantId) { public String generateToken(Long userId, String username, Long tenantId) {
return generateToken(userId, username, tenantId, DEFAULT_EXPIRE_SECONDS); return generateToken(userId, username, tenantId, DEFAULT_EXPIRE_SECONDS);
} }
@ -55,7 +55,7 @@ public class TokenService {
* @param expireSeconds 过期时间 * @param expireSeconds 过期时间
* @return Token字符串 * @return Token字符串
*/ */
public String generateToken(String userId, String username, String tenantId, long expireSeconds) { public String generateToken(Long userId, String username, Long tenantId, long expireSeconds) {
// 生成UUID作为Token // 生成UUID作为Token
String token = UUID.randomUUID().toString().replace("-", ""); String token = UUID.randomUUID().toString().replace("-", "");
@ -173,7 +173,7 @@ public class TokenService {
* *
* @param userId 用户ID * @param userId 用户ID
*/ */
public void deleteAllUserTokens(String userId) { public void deleteAllUserTokens(Long userId) {
String userTokensKey = getUserTokensKey(userId); String userTokensKey = getUserTokensKey(userId);
java.util.Map<Object, Object> tokens = redisService.hGetAll(userTokensKey); java.util.Map<Object, Object> tokens = redisService.hGetAll(userTokensKey);
@ -225,7 +225,7 @@ public class TokenService {
/** /**
* 构建用户Token列表Key * 构建用户Token列表Key
*/ */
private String getUserTokensKey(String userId) { private String getUserTokensKey(Long userId) {
return USER_TOKENS_PREFIX + userId; return USER_TOKENS_PREFIX + userId;
} }
} }

View File

@ -5,17 +5,17 @@ package com.fundplatform.common.context;
*/ */
public final class UserContextHolder { public final class UserContextHolder {
private static final ThreadLocal<String> USER_ID_HOLDER = new ThreadLocal<>(); private static final ThreadLocal<Long> USER_ID_HOLDER = new ThreadLocal<>();
private static final ThreadLocal<String> USER_NAME_HOLDER = new ThreadLocal<>(); private static final ThreadLocal<String> USER_NAME_HOLDER = new ThreadLocal<>();
private UserContextHolder() { private UserContextHolder() {
} }
public static void setUserId(String userId) { public static void setUserId(Long userId) {
USER_ID_HOLDER.set(userId); USER_ID_HOLDER.set(userId);
} }
public static String getUserId() { public static Long getUserId() {
return USER_ID_HOLDER.get(); return USER_ID_HOLDER.get();
} }

View File

@ -8,27 +8,25 @@ import java.time.LocalDateTime;
* *
* <p>注意此类不绑定具体 ORM 框架注解 JPAMyBatis-Plus * <p>注意此类不绑定具体 ORM 框架注解 JPAMyBatis-Plus
* 仅作为字段规范的统一来源具体映射由各模块自行扩展</p> * 仅作为字段规范的统一来源具体映射由各模块自行扩展</p>
*
* <p>主键采用字符串类型雪花算法生成解决前端JavaScript大数精度丢失问题</p>
*/ */
public abstract class BaseEntity implements Serializable { public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 主键ID雪花算法生成的19位字符串 */ /** 主键ID */
private String id; private Long id;
/** 租户ID多租户隔离 */ /** 租户ID多租户隔离 */
private String tenantId; private Long tenantId;
/** 创建人 */ /** 创建人 */
private String createdBy; private Long createdBy;
/** 创建时间 */ /** 创建时间 */
private LocalDateTime createdTime; private LocalDateTime createdTime;
/** 更新人 */ /** 更新人 */
private String updatedBy; private Long updatedBy;
/** 更新时间 */ /** 更新时间 */
private LocalDateTime updatedTime; private LocalDateTime updatedTime;
@ -39,27 +37,27 @@ public abstract class BaseEntity implements Serializable {
/** 备注 */ /** 备注 */
private String remark; private String remark;
public String getId() { public Long getId() {
return id; return id;
} }
public void setId(String id) { public void setId(Long id) {
this.id = id; this.id = id;
} }
public String getTenantId() { public Long getTenantId() {
return tenantId; return tenantId;
} }
public void setTenantId(String tenantId) { public void setTenantId(Long tenantId) {
this.tenantId = tenantId; this.tenantId = tenantId;
} }
public String getCreatedBy() { public Long getCreatedBy() {
return createdBy; return createdBy;
} }
public void setCreatedBy(String createdBy) { public void setCreatedBy(Long createdBy) {
this.createdBy = createdBy; this.createdBy = createdBy;
} }
@ -71,11 +69,11 @@ public abstract class BaseEntity implements Serializable {
this.createdTime = createdTime; this.createdTime = createdTime;
} }
public String getUpdatedBy() { public Long getUpdatedBy() {
return updatedBy; return updatedBy;
} }
public void setUpdatedBy(String updatedBy) { public void setUpdatedBy(Long updatedBy) {
this.updatedBy = updatedBy; this.updatedBy = updatedBy;
} }

View File

@ -31,36 +31,34 @@ public class TenantLineHandlerImpl implements TenantLineHandler {
private static final Logger logger = LoggerFactory.getLogger(TenantLineHandlerImpl.class); private static final Logger logger = LoggerFactory.getLogger(TenantLineHandlerImpl.class);
/** /**
* 忽略租户过滤的表仅限平台级公共数据所有租户共享 * 忽略租户过滤的表系统表字典表等公共数据
*
* <p>安全说明仅将真正全平台共享的静态配置表列为忽略表</p>
* <p>sys_user/sys_role/sys_dept 均属于租户私有数据 tenant_id 字段
* 不得加入此列表否则会导致跨租户数据泄漏</p>
*/ */
private static final Set<String> IGNORE_TABLES = new HashSet<>(Arrays.asList( private static final Set<String> IGNORE_TABLES = new HashSet<>(Arrays.asList(
"sys_menu", // 菜单表所有租户共享的系统菜单结构 "sys_user", // 用户表可能跨租户
"sys_dict", // 字典表所有租户共享的枚举数据 "sys_role", // 角色表可能跨租户
"sys_log", // 日志表独立存储由专属逻辑管理 "sys_menu", // 菜单表所有租户共享
"gen_table", // 代码生成表开发工具非业务数据 "sys_dict", // 字典表所有租户共享
"sys_config", // 配置表所有租户共享
"sys_dept", // 部门表可能跨租户
"sys_log", // 日志表独立存储
"gen_table", // 代码生成表
"gen_table_column" // 代码生成字段表 "gen_table_column" // 代码生成字段表
)); ));
/** /**
* 获取租户 ID * 获取租户 ID
* *
* <p> TenantContextHolder 获取当前线程的租户 ID</p> * <p> TenantContextHolder 获取当前线程的租户 ID</p>
* <p>安全修复不再使用 fallback 默认值 1L若租户上下文为空则直接报错
* 防止在缺少认证上下文的情况下误操作租户1的数据</p>
*/ */
@Override @Override
public Expression getTenantId() { public Expression getTenantId() {
Long tenantId = getCurrentTenantId(); Long tenantId = getCurrentTenantId();
if (tenantId == null) { if (tenantId == null) {
// 安全修复租户上下文缺失时必须中断不能 fallback 到任意租户 logger.debug("[MyBatis Tenant] 未获取到租户 ID使用默认值 1");
throw new IllegalStateException("[Security] 当前请求缺少租户上下文拒绝执行SQL请确保请求经过认证过滤器"); tenantId = 1L;
} }
logger.debug("[MyBatis Tenant] 当前租户 ID: {}", tenantId); logger.debug("[MyBatis Tenant] 当前租户 ID: {}", tenantId);
return new LongValue(tenantId); return new LongValue(tenantId);
} }

View File

@ -1,54 +0,0 @@
package com.fundplatform.common.util;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
/**
* 雪花算法ID生成器
* 生成字符串类型的分布式唯一ID
*
* <p>雪花ID特点</p>
* <ul>
* <li>19位数字字符串</li>
* <li>趋势递增有利于数据库索引</li>
* <li>分布式环境下唯一</li>
* <li>解决前端JavaScript大数精度丢失问题</li>
* </ul>
*
* @author fundplatform team
*/
public class SnowflakeIdGenerator {
/**
* 雪花算法实例
* workerId: 工作机器ID (0-31)
* datacenterId: 数据中心ID (0-31)
* 生产环境应根据实际部署情况配置
*/
private static final Snowflake SNOWFLAKE = IdUtil.getSnowflake(1, 1);
/**
* 私有构造函数防止实例化
*/
private SnowflakeIdGenerator() {
}
/**
* 生成下一个字符串类型的雪花ID
*
* @return 19位数字字符串
*/
public static String nextId() {
return String.valueOf(SNOWFLAKE.nextId());
}
/**
* 生成下一个Long类型的雪花ID
* 供MyBatis Plus IdentifierGenerator使用
*
* @return Long类型ID
*/
public static Long nextIdAsLong() {
return SNOWFLAKE.nextId();
}
}

View File

@ -1,76 +0,0 @@
package com.fundplatform.common.context;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* TenantContextHolder 租户上下文持有者单元测试
*/
class TenantContextHolderTest {
@AfterEach
void cleanup() {
TenantContextHolder.clear();
}
@Test
@DisplayName("设置租户ID后可以正确获取")
void setAndGetTenantId() {
TenantContextHolder.setTenantId(100L);
assertEquals(100L, TenantContextHolder.getTenantId());
}
@Test
@DisplayName("未设置时 getTenantId 返回 null")
void getTenantId_null_whenNotSet() {
assertNull(TenantContextHolder.getTenantId());
}
@Test
@DisplayName("clear 后 getTenantId 返回 null")
void clear_removesValue() {
TenantContextHolder.setTenantId(200L);
TenantContextHolder.clear();
assertNull(TenantContextHolder.getTenantId());
}
@Test
@DisplayName("多次设置取最后一次的值")
void setMultipleTimes_lastValueWins() {
TenantContextHolder.setTenantId(10L);
TenantContextHolder.setTenantId(20L);
TenantContextHolder.setTenantId(30L);
assertEquals(30L, TenantContextHolder.getTenantId());
}
@Test
@DisplayName("设置 null 值后 getTenantId 返回 null")
void setNullTenantId() {
TenantContextHolder.setTenantId(100L);
TenantContextHolder.setTenantId(null);
assertNull(TenantContextHolder.getTenantId());
}
@Test
@DisplayName("线程隔离 - 子线程设置的值不影响主线程")
void threadIsolation() throws InterruptedException {
TenantContextHolder.setTenantId(1L);
Thread subThread = new Thread(() -> {
TenantContextHolder.setTenantId(999L);
assertEquals(999L, TenantContextHolder.getTenantId());
});
subThread.start();
subThread.join();
// 主线程的值不受子线程影响
assertEquals(1L, TenantContextHolder.getTenantId());
}
}

View File

@ -1,107 +0,0 @@
package com.fundplatform.common.context;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* UserContextHolder 用户上下文持有者单元测试
*/
class UserContextHolderTest {
@AfterEach
void cleanup() {
UserContextHolder.clear();
}
@Test
@DisplayName("设置用户ID后可以正确获取")
void setAndGetUserId() {
UserContextHolder.setUserId(1L);
assertEquals(1L, UserContextHolder.getUserId());
}
@Test
@DisplayName("设置用户名后可以正确获取")
void setAndGetUserName() {
UserContextHolder.setUserName("admin");
assertEquals("admin", UserContextHolder.getUserName());
}
@Test
@DisplayName("同时设置 userId 和 userName")
void setBoth_userId_and_userName() {
UserContextHolder.setUserId(42L);
UserContextHolder.setUserName("testuser");
assertEquals(42L, UserContextHolder.getUserId());
assertEquals("testuser", UserContextHolder.getUserName());
}
@Test
@DisplayName("未设置时 getUserId 返回 null")
void getUserId_null_whenNotSet() {
assertNull(UserContextHolder.getUserId());
}
@Test
@DisplayName("未设置时 getUserName 返回 null")
void getUserName_null_whenNotSet() {
assertNull(UserContextHolder.getUserName());
}
@Test
@DisplayName("clear 后 userId 和 userName 均为 null")
void clear_removesAllValues() {
UserContextHolder.setUserId(1L);
UserContextHolder.setUserName("admin");
UserContextHolder.clear();
assertNull(UserContextHolder.getUserId());
assertNull(UserContextHolder.getUserName());
}
@Test
@DisplayName("多次设置 userId 取最后一次的值")
void setUserId_multiple_lastWins() {
UserContextHolder.setUserId(1L);
UserContextHolder.setUserId(2L);
UserContextHolder.setUserId(3L);
assertEquals(3L, UserContextHolder.getUserId());
}
@Test
@DisplayName("线程隔离 - 子线程设置的 userId 不影响主线程")
void threadIsolation_userId() throws InterruptedException {
UserContextHolder.setUserId(1L);
Thread subThread = new Thread(() -> {
UserContextHolder.setUserId(999L);
assertEquals(999L, UserContextHolder.getUserId());
});
subThread.start();
subThread.join();
assertEquals(1L, UserContextHolder.getUserId());
}
@Test
@DisplayName("线程隔离 - 子线程设置的 userName 不影响主线程")
void threadIsolation_userName() throws InterruptedException {
UserContextHolder.setUserName("mainthread");
Thread subThread = new Thread(() -> {
UserContextHolder.setUserName("subthread");
assertEquals("subthread", UserContextHolder.getUserName());
});
subThread.start();
subThread.join();
assertEquals("mainthread", UserContextHolder.getUserName());
}
}

View File

@ -1,91 +0,0 @@
package com.fundplatform.common.core;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* PageResult 分页返回结构单元测试
*/
class PageResultTest {
@Test
@DisplayName("无参构造函数 - records 默认为空列表")
void defaultConstructor_emptyRecords() {
PageResult<String> pageResult = new PageResult<>();
assertNotNull(pageResult.getRecords());
assertTrue(pageResult.getRecords().isEmpty());
}
@Test
@DisplayName("全参构造函数 - 正确设置所有字段")
void fullConstructor() {
List<String> records = Arrays.asList("a", "b", "c");
PageResult<String> pageResult = new PageResult<>(1, 10, 3, records);
assertEquals(1, pageResult.getPageNum());
assertEquals(10, pageResult.getPageSize());
assertEquals(3, pageResult.getTotal());
assertEquals(3, pageResult.getRecords().size());
assertEquals("a", pageResult.getRecords().get(0));
}
@Test
@DisplayName("全参构造函数 - records 为 null 时自动转为空列表")
void fullConstructor_nullRecords() {
PageResult<String> pageResult = new PageResult<>(1, 10, 0, null);
assertNotNull(pageResult.getRecords());
assertTrue(pageResult.getRecords().isEmpty());
}
@Test
@DisplayName("setter/getter - 可正常设置和获取所有字段")
void settersAndGetters() {
PageResult<Integer> pageResult = new PageResult<>();
pageResult.setPageNum(2);
pageResult.setPageSize(20);
pageResult.setTotal(100);
pageResult.setRecords(Arrays.asList(1, 2, 3));
assertEquals(2, pageResult.getPageNum());
assertEquals(20, pageResult.getPageSize());
assertEquals(100, pageResult.getTotal());
assertEquals(3, pageResult.getRecords().size());
}
@Test
@DisplayName("total 为 0 时表示无数据")
void total_zero() {
PageResult<Object> pageResult = new PageResult<>(1, 10, 0, Collections.emptyList());
assertEquals(0, pageResult.getTotal());
assertTrue(pageResult.getRecords().isEmpty());
}
@Test
@DisplayName("记录数量与 total 可以不一致(当前页可为最后一页不满页)")
void records_lessThanPageSize() {
List<String> records = List.of("x", "y");
PageResult<String> pageResult = new PageResult<>(5, 10, 42, records);
assertEquals(42, pageResult.getTotal());
assertEquals(2, pageResult.getRecords().size());
}
@Test
@DisplayName("setRecords - 可覆盖原有记录")
void setRecords_override() {
PageResult<String> pageResult = new PageResult<>(1, 10, 2, Arrays.asList("old1", "old2"));
pageResult.setRecords(List.of("new1"));
assertEquals(1, pageResult.getRecords().size());
assertEquals("new1", pageResult.getRecords().get(0));
}
}

View File

@ -1,125 +0,0 @@
package com.fundplatform.common.core;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* Result 统一响应封装单元测试
*/
class ResultTest {
@Test
@DisplayName("success() - 无参成功返回code=200, message=success, data=null")
void success_noArgs() {
Result<Void> result = Result.success();
assertEquals(200, result.getCode());
assertEquals("success", result.getMessage());
assertNull(result.getData());
assertTrue(result.isSuccess());
}
@Test
@DisplayName("success(data) - 携带非String数据的成功返回")
void success_withData() {
Integer data = 42;
Result<Integer> result = Result.success(data);
assertEquals(200, result.getCode());
assertEquals("success", result.getMessage());
assertEquals(42, result.getData());
assertTrue(result.isSuccess());
}
@Test
@DisplayName("success(message) - 仅消息的成功返回data 为 null")
void success_withMessage() {
Result<Void> result = Result.success("操作成功");
assertEquals(200, result.getCode());
assertEquals("操作成功", result.getMessage());
assertNull(result.getData());
assertTrue(result.isSuccess());
}
@Test
@DisplayName("success(data, message) - 携带数据和自定义消息的成功返回")
void success_withDataAndMessage() {
Integer data = 42;
Result<Integer> result = Result.success(data, "查询成功");
assertEquals(200, result.getCode());
assertEquals("查询成功", result.getMessage());
assertEquals(42, result.getData());
assertTrue(result.isSuccess());
}
@Test
@DisplayName("error(message) - 使用默认错误码 500")
void error_withMessage() {
Result<Void> result = Result.error("系统错误");
assertEquals(500, result.getCode());
assertEquals("系统错误", result.getMessage());
assertNull(result.getData());
assertFalse(result.isSuccess());
}
@Test
@DisplayName("error(code, message) - 自定义错误码")
void error_withCodeAndMessage() {
Result<Void> result = Result.error(404, "资源未找到");
assertEquals(404, result.getCode());
assertEquals("资源未找到", result.getMessage());
assertNull(result.getData());
assertFalse(result.isSuccess());
}
@Test
@DisplayName("isSuccess() - code=200 时返回 true")
void isSuccess_true() {
Result<Object> result = new Result<>(200, "ok", null);
assertTrue(result.isSuccess());
}
@Test
@DisplayName("isSuccess() - code!=200 时返回 false")
void isSuccess_false() {
Result<Object> result = new Result<>(500, "error", null);
assertFalse(result.isSuccess());
}
@Test
@DisplayName("无参构造函数 + setter - 可正常设置字段")
void defaultConstructor_withSetters() {
Result<String> result = new Result<>();
result.setCode(200);
result.setMessage("ok");
result.setData("test");
assertEquals(200, result.getCode());
assertEquals("ok", result.getMessage());
assertEquals("test", result.getData());
}
@Test
@DisplayName("success(data, message) 中 data 为 null 也能正常返回")
void success_nullData() {
Result<String> result = Result.success(null, "查询成功");
assertEquals(200, result.getCode());
assertNull(result.getData());
assertEquals("查询成功", result.getMessage());
assertTrue(result.isSuccess());
}
@Test
@DisplayName("常量值SUCCESS=200, ERROR=500")
void constants() {
assertEquals(200, Result.SUCCESS);
assertEquals(500, Result.ERROR);
}
}

View File

@ -61,13 +61,6 @@
<version>7.4</version> <version>7.4</version>
</dependency> </dependency>
<!-- SpringDoc OpenAPI (Swagger UI) -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
<!-- Nacos服务注册发现 --> <!-- Nacos服务注册发现 -->
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>

View File

@ -5,16 +5,12 @@ import com.fundplatform.common.core.Result;
import com.fundplatform.cust.dto.ContactDTO; import com.fundplatform.cust.dto.ContactDTO;
import com.fundplatform.cust.service.ContactService; import com.fundplatform.cust.service.ContactService;
import com.fundplatform.cust.vo.ContactVO; import com.fundplatform.cust.vo.ContactVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
/** /**
* 联系人管理Controller * 联系人管理Controller
*/ */
@Tag(name = "联系人管理", description = "客户联系人的增删改查及主要联系人设置")
@RestController @RestController
@RequestMapping("/api/v1/customer/contact") @RequestMapping("/api/v1/customer/contact")
public class ContactController { public class ContactController {
@ -28,7 +24,6 @@ public class ContactController {
/** /**
* 创建联系人 * 创建联系人
*/ */
@Operation(summary = "创建联系人")
@PostMapping @PostMapping
public Result<Long> create(@Valid @RequestBody ContactDTO dto) { public Result<Long> create(@Valid @RequestBody ContactDTO dto) {
Long id = contactService.createContact(dto); Long id = contactService.createContact(dto);
@ -38,9 +33,8 @@ public class ContactController {
/** /**
* 更新联系人 * 更新联系人
*/ */
@Operation(summary = "更新联系人信息")
@PutMapping("/{id}") @PutMapping("/{id}")
public Result<Boolean> update(@Parameter(description = "联系人ID") @PathVariable Long id, @Valid @RequestBody ContactDTO dto) { public Result<Boolean> update(@PathVariable Long id, @Valid @RequestBody ContactDTO dto) {
boolean result = contactService.updateContact(id, dto); boolean result = contactService.updateContact(id, dto);
return Result.success(result); return Result.success(result);
} }
@ -48,9 +42,8 @@ public class ContactController {
/** /**
* 根据ID查询联系人 * 根据ID查询联系人
*/ */
@Operation(summary = "根据ID查询联系人")
@GetMapping("/{id}") @GetMapping("/{id}")
public Result<ContactVO> getById(@Parameter(description = "联系人ID") @PathVariable Long id) { public Result<ContactVO> getById(@PathVariable Long id) {
ContactVO vo = contactService.getContactById(id); ContactVO vo = contactService.getContactById(id);
return Result.success(vo); return Result.success(vo);
} }
@ -58,12 +51,11 @@ public class ContactController {
/** /**
* 分页查询联系人 * 分页查询联系人
*/ */
@Operation(summary = "分页查询联系人")
@GetMapping("/page") @GetMapping("/page")
public Result<Page<ContactVO>> page( public Result<Page<ContactVO>> page(
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "1") int pageNum,
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize, @RequestParam(defaultValue = "10") int pageSize,
@Parameter(description = "客户ID") @RequestParam(required = false) Long customerId) { @RequestParam(required = false) Long customerId) {
Page<ContactVO> page = contactService.pageContacts(pageNum, pageSize, customerId); Page<ContactVO> page = contactService.pageContacts(pageNum, pageSize, customerId);
return Result.success(page); return Result.success(page);
} }
@ -71,9 +63,8 @@ public class ContactController {
/** /**
* 删除联系人 * 删除联系人
*/ */
@Operation(summary = "删除联系人")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public Result<Boolean> delete(@Parameter(description = "联系人ID") @PathVariable Long id) { public Result<Boolean> delete(@PathVariable Long id) {
boolean result = contactService.deleteContact(id); boolean result = contactService.deleteContact(id);
return Result.success(result); return Result.success(result);
} }
@ -81,11 +72,10 @@ public class ContactController {
/** /**
* 设置主要联系人 * 设置主要联系人
*/ */
@Operation(summary = "设置主要联系人", description = "将指定联系人设为客户的主要联系人")
@PutMapping("/{customerId}/contact/{contactId}/primary") @PutMapping("/{customerId}/contact/{contactId}/primary")
public Result<Boolean> setPrimary( public Result<Boolean> setPrimary(
@Parameter(description = "客户ID") @PathVariable Long customerId, @PathVariable Long customerId,
@Parameter(description = "联系人ID") @PathVariable Long contactId) { @PathVariable Long contactId) {
boolean result = contactService.setPrimaryContact(customerId, contactId); boolean result = contactService.setPrimaryContact(customerId, contactId);
return Result.success(result); return Result.success(result);
} }

View File

@ -7,16 +7,12 @@ import com.fundplatform.cust.dto.CustomerCreateDTO;
import com.fundplatform.cust.dto.CustomerUpdateDTO; import com.fundplatform.cust.dto.CustomerUpdateDTO;
import com.fundplatform.cust.service.CustomerService; import com.fundplatform.cust.service.CustomerService;
import com.fundplatform.cust.vo.CustomerVO; import com.fundplatform.cust.vo.CustomerVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
/** /**
* 客户Controller * 客户Controller
*/ */
@Tag(name = "客户管理", description = "客户的增删改查")
@RestController @RestController
@RequestMapping("/api/v1/customer") @RequestMapping("/api/v1/customer")
public class CustomerController { public class CustomerController {
@ -30,7 +26,6 @@ public class CustomerController {
/** /**
* 创建客户 * 创建客户
*/ */
@Operation(summary = "创建客户")
@PostMapping @PostMapping
public Result<Long> createCustomer(@Valid @RequestBody CustomerCreateDTO dto) { public Result<Long> createCustomer(@Valid @RequestBody CustomerCreateDTO dto) {
Long id = customerService.createCustomer(dto); Long id = customerService.createCustomer(dto);
@ -40,9 +35,8 @@ public class CustomerController {
/** /**
* 更新客户 * 更新客户
*/ */
@Operation(summary = "更新客户信息")
@PutMapping("/{id}") @PutMapping("/{id}")
public Result<Void> updateCustomer(@Parameter(description = "客户ID") @PathVariable Long id, @Valid @RequestBody CustomerUpdateDTO dto) { public Result<Void> updateCustomer(@PathVariable Long id, @Valid @RequestBody CustomerUpdateDTO dto) {
customerService.updateCustomer(id, dto); customerService.updateCustomer(id, dto);
return Result.success(); return Result.success();
} }
@ -50,9 +44,8 @@ public class CustomerController {
/** /**
* 查询客户详情 * 查询客户详情
*/ */
@Operation(summary = "查询客户详情")
@GetMapping("/{id}") @GetMapping("/{id}")
public Result<CustomerVO> getCustomer(@Parameter(description = "客户ID") @PathVariable Long id) { public Result<CustomerVO> getCustomer(@PathVariable Long id) {
CustomerVO vo = customerService.getCustomerById(id); CustomerVO vo = customerService.getCustomerById(id);
return Result.success(vo); return Result.success(vo);
} }
@ -60,12 +53,11 @@ public class CustomerController {
/** /**
* 分页查询客户 * 分页查询客户
*/ */
@Operation(summary = "分页查询客户")
@GetMapping("/page") @GetMapping("/page")
public Result<PageResult<CustomerVO>> pageCustomers( public Result<PageResult<CustomerVO>> pageCustomers(
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "1") int pageNum,
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize, @RequestParam(defaultValue = "10") int pageSize,
@Parameter(description = "关键词(客户名/编码/联系人模糊查询)") @RequestParam(required = false) String keyword) { @RequestParam(required = false) String keyword) {
Page<CustomerVO> page = customerService.pageCustomers(pageNum, pageSize, keyword); Page<CustomerVO> page = customerService.pageCustomers(pageNum, pageSize, keyword);
PageResult<CustomerVO> pageResult = new PageResult<>( PageResult<CustomerVO> pageResult = new PageResult<>(
@ -80,9 +72,8 @@ public class CustomerController {
/** /**
* 删除客户 * 删除客户
*/ */
@Operation(summary = "删除客户(逻辑删除)")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public Result<Void> deleteCustomer(@Parameter(description = "客户ID") @PathVariable Long id) { public Result<Void> deleteCustomer(@PathVariable Long id) {
customerService.deleteCustomer(id); customerService.deleteCustomer(id);
return Result.success(); return Result.success();
} }

View File

@ -14,7 +14,7 @@ public class CustomerContact extends BaseEntity {
/** /**
* 客户ID * 客户ID
*/ */
private String customerId; private Long customerId;
/** /**
* 联系人姓名 * 联系人姓名
@ -51,11 +51,11 @@ public class CustomerContact extends BaseEntity {
*/ */
private String remark; private String remark;
public String getCustomerId() { public Long getCustomerId() {
return customerId; return customerId;
} }
public void setCustomerId(String customerId) { public void setCustomerId(Long customerId) {
this.customerId = customerId; this.customerId = customerId;
} }

View File

@ -1,7 +1,6 @@
package com.fundplatform.exp.controller; package com.fundplatform.exp.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fundplatform.common.core.PageResult;
import com.fundplatform.common.core.Result; import com.fundplatform.common.core.Result;
import com.fundplatform.common.util.ExcelUtil; import com.fundplatform.common.util.ExcelUtil;
import com.fundplatform.exp.dto.ExpenseExcel; import com.fundplatform.exp.dto.ExpenseExcel;
@ -63,21 +62,14 @@ public class FundExpenseController {
* 分页查询支出列表 * 分页查询支出列表
*/ */
@GetMapping("/page") @GetMapping("/page")
public Result<PageResult<FundExpenseVO>> page( public Result<Page<FundExpenseVO>> page(
@RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize, @RequestParam(defaultValue = "10") int pageSize,
@RequestParam(required = false) String title, @RequestParam(required = false) String title,
@RequestParam(required = false) Long expenseType, @RequestParam(required = false) Long expenseType,
@RequestParam(required = false) Integer payStatus, @RequestParam(required = false) Integer payStatus,
@RequestParam(required = false) Integer approvalStatus) { @RequestParam(required = false) Integer approvalStatus) {
Page<FundExpenseVO> page = expenseService.pageExpenses(pageNum, pageSize, title, expenseType, payStatus, approvalStatus); return Result.success(expenseService.pageExpenses(pageNum, pageSize, title, expenseType, payStatus, approvalStatus));
PageResult<FundExpenseVO> pageResult = new PageResult<>(
page.getCurrent(),
page.getSize(),
page.getTotal(),
page.getRecords()
);
return Result.success(pageResult);
} }
/** /**

View File

@ -16,7 +16,7 @@ public class ExpenseType extends BaseEntity {
private String typeName; private String typeName;
/** 父类型ID0表示一级类型 */ /** 父类型ID0表示一级类型 */
private String parentId; private Long parentId;
/** 类型层级 */ /** 类型层级 */
private Integer typeLevel; private Integer typeLevel;
@ -46,11 +46,11 @@ public class ExpenseType extends BaseEntity {
this.typeName = typeName; this.typeName = typeName;
} }
public String getParentId() { public Long getParentId() {
return parentId; return parentId;
} }
public void setParentId(String parentId) { public void setParentId(Long parentId) {
this.parentId = parentId; this.parentId = parentId;
} }

View File

@ -25,7 +25,7 @@ public class FundExpense extends BaseEntity {
private String currency; private String currency;
/** 支出类型(1-日常支出 2-项目支出 3-工资发放 4-其他) */ /** 支出类型(1-日常支出 2-项目支出 3-工资发放 4-其他) */
private String expenseType; private Long expenseType;
/** 收款单位 */ /** 收款单位 */
private String payeeName; private String payeeName;
@ -43,13 +43,13 @@ public class FundExpense extends BaseEntity {
private String purpose; private String purpose;
/** 关联用款申请ID */ /** 关联用款申请ID */
private String requestId; private Long requestId;
/** 项目ID */ /** 项目ID */
private String projectId; private Long projectId;
/** 客户ID */ /** 客户ID */
private String customerId; private Long customerId;
/** 支付状态(0-待支付 1-已支付 2-支付失败) */ /** 支付状态(0-待支付 1-已支付 2-支付失败) */
private Integer payStatus; private Integer payStatus;
@ -67,7 +67,7 @@ public class FundExpense extends BaseEntity {
private Integer approvalStatus; private Integer approvalStatus;
/** 审批人ID */ /** 审批人ID */
private String approverId; private Long approverId;
/** 审批时间 */ /** 审批时间 */
private LocalDateTime approvalTime; private LocalDateTime approvalTime;
@ -110,11 +110,11 @@ public class FundExpense extends BaseEntity {
this.currency = currency; this.currency = currency;
} }
public String getExpenseType() { public Long getExpenseType() {
return expenseType; return expenseType;
} }
public void setExpenseType(String expenseType) { public void setExpenseType(Long expenseType) {
this.expenseType = expenseType; this.expenseType = expenseType;
} }
@ -158,27 +158,27 @@ public class FundExpense extends BaseEntity {
this.purpose = purpose; this.purpose = purpose;
} }
public String getRequestId() { public Long getRequestId() {
return requestId; return requestId;
} }
public void setRequestId(String requestId) { public void setRequestId(Long requestId) {
this.requestId = requestId; this.requestId = requestId;
} }
public String getProjectId() { public Long getProjectId() {
return projectId; return projectId;
} }
public void setProjectId(String projectId) { public void setProjectId(Long projectId) {
this.projectId = projectId; this.projectId = projectId;
} }
public String getCustomerId() { public Long getCustomerId() {
return customerId; return customerId;
} }
public void setCustomerId(String customerId) { public void setCustomerId(Long customerId) {
this.customerId = customerId; this.customerId = customerId;
} }
@ -222,11 +222,11 @@ public class FundExpense extends BaseEntity {
this.approvalStatus = approvalStatus; this.approvalStatus = approvalStatus;
} }
public String getApproverId() { public Long getApproverId() {
return approverId; return approverId;
} }
public void setApproverId(String approverId) { public void setApproverId(Long approverId) {
this.approverId = approverId; this.approverId = approverId;
} }

View File

@ -46,7 +46,7 @@ public class ExpenseTypeServiceImpl implements ExpenseTypeService {
Page<ExpenseType> page = typeDataService.page(new Page<>(pageNum, pageSize), wrapper); Page<ExpenseType> page = typeDataService.page(new Page<>(pageNum, pageSize), wrapper);
Page<ExpenseTypeVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); Page<ExpenseTypeVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
voPage.setRecords(page.getRecords().stream().map(this::convertToVOWithParentName).collect(Collectors.toList())); voPage.setRecords(page.getRecords().stream().map(this::convertToVO).collect(Collectors.toList()));
return voPage; return voPage;
} }
@ -86,10 +86,8 @@ public class ExpenseTypeServiceImpl implements ExpenseTypeService {
type.setId(dto.getId()); type.setId(dto.getId());
type.setTypeCode(dto.getTypeCode()); type.setTypeCode(dto.getTypeCode());
type.setTypeName(dto.getTypeName()); type.setTypeName(dto.getTypeName());
type.setParentId(dto.getParentId() != null ? dto.getParentId() : 0L);
type.setSortOrder(dto.getSortOrder()); type.setSortOrder(dto.getSortOrder());
type.setDescription(dto.getDescription()); type.setDescription(dto.getDescription());
type.setStatus(dto.getStatus());
type.setUpdatedTime(LocalDateTime.now()); type.setUpdatedTime(LocalDateTime.now());
type.setUpdatedBy(UserContextHolder.getUserId()); type.setUpdatedBy(UserContextHolder.getUserId());
@ -197,20 +195,6 @@ public class ExpenseTypeServiceImpl implements ExpenseTypeService {
vo.setCreatedTime(type.getCreatedTime()); vo.setCreatedTime(type.getCreatedTime());
vo.setUpdatedTime(type.getUpdatedTime()); vo.setUpdatedTime(type.getUpdatedTime());
vo.setRemark(type.getRemark()); vo.setRemark(type.getRemark());
// 查询父类型名称
if (type.getParentId() != null && type.getParentId() > 0) {
ExpenseType parentType = typeDataService.getById(type.getParentId());
if (parentType != null) {
vo.setParentName(parentType.getTypeName());
}
}
return vo;
}
private ExpenseTypeVO convertToVOWithParentName(ExpenseType type) {
ExpenseTypeVO vo = convertToVO(type);
return vo; return vo;
} }
} }

View File

@ -1,27 +1,20 @@
package com.fundplatform.exp.vo; package com.fundplatform.exp.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
public class ExpenseTypeVO { public class ExpenseTypeVO {
@JsonSerialize(using = ToStringSerializer.class)
private Long id; private Long id;
private String typeCode; private String typeCode;
private String typeName; private String typeName;
@JsonSerialize(using = ToStringSerializer.class)
private Long parentId; private Long parentId;
private String parentName; private String parentName;
private Integer typeLevel; private Integer typeLevel;
private Integer sortOrder; private Integer sortOrder;
private String description; private String description;
private Integer status; private Integer status;
@JsonSerialize(using = ToStringSerializer.class)
private Long tenantId; private Long tenantId;
@JsonSerialize(using = ToStringSerializer.class)
private Long createdBy; private Long createdBy;
private LocalDateTime createdTime; private LocalDateTime createdTime;
private LocalDateTime updatedTime; private LocalDateTime updatedTime;

View File

@ -1,20 +1,15 @@
package com.fundplatform.exp.vo; package com.fundplatform.exp.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
public class FundExpenseVO { public class FundExpenseVO {
@JsonSerialize(using = ToStringSerializer.class)
private Long id; private Long id;
private String expenseNo; private String expenseNo;
private String title; private String title;
private BigDecimal amount; private BigDecimal amount;
private String currency; private String currency;
@JsonSerialize(using = ToStringSerializer.class)
private Long expenseType; private Long expenseType;
private String expenseTypeName; private String expenseTypeName;
private String payeeName; private String payeeName;
@ -22,11 +17,8 @@ public class FundExpenseVO {
private String payeeAccount; private String payeeAccount;
private LocalDateTime expenseDate; private LocalDateTime expenseDate;
private String purpose; private String purpose;
@JsonSerialize(using = ToStringSerializer.class)
private Long requestId; private Long requestId;
@JsonSerialize(using = ToStringSerializer.class)
private Long projectId; private Long projectId;
@JsonSerialize(using = ToStringSerializer.class)
private Long customerId; private Long customerId;
private Integer payStatus; private Integer payStatus;
private String payStatusName; private String payStatusName;
@ -35,14 +27,11 @@ public class FundExpenseVO {
private String payVoucher; private String payVoucher;
private Integer approvalStatus; private Integer approvalStatus;
private String approvalStatusName; private String approvalStatusName;
@JsonSerialize(using = ToStringSerializer.class)
private Long approverId; private Long approverId;
private LocalDateTime approvalTime; private LocalDateTime approvalTime;
private String approvalComment; private String approvalComment;
private String attachments; private String attachments;
@JsonSerialize(using = ToStringSerializer.class)
private Long tenantId; private Long tenantId;
@JsonSerialize(using = ToStringSerializer.class)
private Long createdBy; private Long createdBy;
private LocalDateTime createdTime; private LocalDateTime createdTime;

View File

@ -1,5 +1,6 @@
package com.fundplatform.file.data.entity; package com.fundplatform.file.data.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
@ -12,10 +13,10 @@ import java.time.LocalDateTime;
@TableName("file_record") @TableName("file_record")
public class FileRecord { public class FileRecord {
@TableId @TableId(type = IdType.AUTO)
private String fileId; private Long fileId;
private String tenantId; private Long tenantId;
private String fileName; private String fileName;
@ -35,7 +36,7 @@ public class FileRecord {
private String businessType; private String businessType;
private String businessId; private Long businessId;
private String description; private String description;
@ -43,11 +44,11 @@ public class FileRecord {
private Integer status; private Integer status;
private String createdBy; private Long createdBy;
private LocalDateTime createdTime; private LocalDateTime createdTime;
private String updatedBy; private Long updatedBy;
private LocalDateTime updatedTime; private LocalDateTime updatedTime;
@ -55,19 +56,19 @@ public class FileRecord {
private Integer deleted; private Integer deleted;
// Getters and Setters // Getters and Setters
public String getFileId() { public Long getFileId() {
return fileId; return fileId;
} }
public void setFileId(String fileId) { public void setFileId(Long fileId) {
this.fileId = fileId; this.fileId = fileId;
} }
public String getTenantId() { public Long getTenantId() {
return tenantId; return tenantId;
} }
public void setTenantId(String tenantId) { public void setTenantId(Long tenantId) {
this.tenantId = tenantId; this.tenantId = tenantId;
} }
@ -143,11 +144,11 @@ public class FileRecord {
this.businessType = businessType; this.businessType = businessType;
} }
public String getBusinessId() { public Long getBusinessId() {
return businessId; return businessId;
} }
public void setBusinessId(String businessId) { public void setBusinessId(Long businessId) {
this.businessId = businessId; this.businessId = businessId;
} }
@ -175,11 +176,11 @@ public class FileRecord {
this.status = status; this.status = status;
} }
public String getCreatedBy() { public Long getCreatedBy() {
return createdBy; return createdBy;
} }
public void setCreatedBy(String createdBy) { public void setCreatedBy(Long createdBy) {
this.createdBy = createdBy; this.createdBy = createdBy;
} }
@ -191,11 +192,11 @@ public class FileRecord {
this.createdTime = createdTime; this.createdTime = createdTime;
} }
public String getUpdatedBy() { public Long getUpdatedBy() {
return updatedBy; return updatedBy;
} }
public void setUpdatedBy(String updatedBy) { public void setUpdatedBy(Long updatedBy) {
this.updatedBy = updatedBy; this.updatedBy = updatedBy;
} }

View File

@ -66,15 +66,13 @@ public class TenantGatewayFilter implements GlobalFilter, Ordered {
return chain.filter(exchange); return chain.filter(exchange);
} }
// 安全修复从请求头中取 X-Tenant-IdTokenAuthFilter 已用 Token 中的值覆盖客户端传入值 // 检查X-Tenant-Id请求头
// 因此此处取到的必为 Token 认证后的租户ID无需再与客户端值单独比对
// 但仍需校验格式合法性防止异常数据流入下游服务
String tenantId = request.getHeaders().getFirst(HEADER_TENANT_ID); String tenantId = request.getHeaders().getFirst(HEADER_TENANT_ID);
if (tenantId == null || tenantId.trim().isEmpty()) { if (tenantId == null || tenantId.trim().isEmpty()) {
logger.warn("[TenantGateway] 缺少X-Tenant-Id请求头路径: {}", path); logger.warn("[TenantGateway] 缺少X-Tenant-Id请求头路径: {}", path);
return missingTenantId(exchange, "缺少X-Tenant-Id请求头"); return missingTenantId(exchange, "缺少X-Tenant-Id请求头");
} }
// 验证租户ID是否为有效数字 // 验证租户ID是否为有效数字
try { try {
Long.parseLong(tenantId); Long.parseLong(tenantId);

View File

@ -83,10 +83,8 @@ public class TokenAuthFilter implements GlobalFilter, Ordered {
return unauthorized(exchange, "Token无效或已过期"); return unauthorized(exchange, "Token无效或已过期");
} }
// 安全修复先移除客户端传来的 X-Tenant-Id再写入 Token 中已认证的值 // 将用户信息写入请求头
// 防止攻击者通过伪造 X-Tenant-Id 请求头绕过租户隔离
ServerHttpRequest mutatedRequest = request.mutate() ServerHttpRequest mutatedRequest = request.mutate()
.headers(headers -> headers.remove(TENANT_ID_HEADER))
.header(USER_ID_HEADER, String.valueOf(tokenInfo.getUserId())) .header(USER_ID_HEADER, String.valueOf(tokenInfo.getUserId()))
.header(USERNAME_HEADER, tokenInfo.getUsername()) .header(USERNAME_HEADER, tokenInfo.getUsername())
.header(TENANT_ID_HEADER, String.valueOf(tokenInfo.getTenantId())) .header(TENANT_ID_HEADER, String.valueOf(tokenInfo.getTenantId()))

View File

@ -24,6 +24,5 @@ declare module 'vue' {
VanPullRefresh: typeof import('vant/es')['PullRefresh'] VanPullRefresh: typeof import('vant/es')['PullRefresh']
VanSearch: typeof import('vant/es')['Search'] VanSearch: typeof import('vant/es')['Search']
VanTag: typeof import('vant/es')['Tag'] VanTag: typeof import('vant/es')['Tag']
VanUploader: typeof import('vant/es')['Uploader']
} }
} }

View File

@ -9,7 +9,6 @@
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"axios": "^1.6.0", "axios": "^1.6.0",
"js-md5": "^0.8.3",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"vant": "^4.9.22", "vant": "^4.9.22",
"vue": "^3.4.0", "vue": "^3.4.0",
@ -1691,12 +1690,6 @@
"node": ">=0.12.0" "node": ">=0.12.0"
} }
}, },
"node_modules/js-md5": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz",
"integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==",
"license": "MIT"
},
"node_modules/local-pkg": { "node_modules/local-pkg": {
"version": "0.4.3", "version": "0.4.3",
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz",

View File

@ -11,7 +11,6 @@
}, },
"dependencies": { "dependencies": {
"axios": "^1.6.0", "axios": "^1.6.0",
"js-md5": "^0.8.3",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"vant": "^4.9.22", "vant": "^4.9.22",
"vue": "^3.4.0", "vue": "^3.4.0",

View File

@ -2,13 +2,8 @@ import request from './request'
// ===================== 用户认证 ===================== // ===================== 用户认证 =====================
export function login(data: { username: string; password: string, tenantId?: number }) { export function login(data: { username: string; password: string }) {
// 如果没有传递 tenantId使用默认值 1 return request.post('/auth/login', data)
const requestData = {
...data,
tenantId: data.tenantId || 1
}
return request.post('/auth/login', requestData)
} }
export function getUserInfo() { export function getUserInfo() {
@ -23,30 +18,6 @@ export function updatePassword(data: { oldPassword: string; newPassword: string;
return request.put('/sys/profile/password', data) return request.put('/sys/profile/password', data)
} }
// ===================== 文件管理 =====================
export function uploadFile(file: File, businessType?: string, businessId?: number, description?: string) {
const formData = new FormData()
formData.append('file', file)
if (businessType) formData.append('businessType', businessType)
if (businessId) formData.append('businessId', String(businessId))
if (description) formData.append('description', description)
return request.post('/file/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
export function getFileList(params?: { pageNum: number; pageSize: number; businessType?: string; businessId?: number; fileType?: string }) {
return request.get('/file/page', { params })
}
export function deleteFile(id: number) {
return request.delete(`/file/${id}`)
}
// ===================== 项目管理 ===================== // ===================== 项目管理 =====================
export function getProjectList(params?: { pageNum: number; pageSize: number; keyword?: string }) { export function getProjectList(params?: { pageNum: number; pageSize: number; keyword?: string }) {

View File

@ -52,7 +52,6 @@ import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { showToast, showSuccessToast } from 'vant' import { showToast, showSuccessToast } from 'vant'
import { login } from '@/api' import { login } from '@/api'
import md5 from 'js-md5'
const router = useRouter() const router = useRouter()
const loading = ref(false) const loading = ref(false)
@ -75,10 +74,7 @@ const handleLogin = async () => {
loading.value = true loading.value = true
try { try {
const res: any = await login({ const res: any = await login(form)
username: form.username,
password: md5(form.password)
})
const data = res.data const data = res.data
localStorage.setItem('token', data.token) localStorage.setItem('token', data.token)
localStorage.setItem('userInfo', JSON.stringify({ localStorage.setItem('userInfo', JSON.stringify({

View File

@ -51,18 +51,6 @@
<label>支出描述</label> <label>支出描述</label>
<textarea v-model="form.description" placeholder="输入描述" rows="3" class="mac-textarea"></textarea> <textarea v-model="form.description" placeholder="输入描述" rows="3" class="mac-textarea"></textarea>
</div> </div>
<div class="form-group">
<label>附件上传图片</label>
<van-uploader
v-model="fileList"
:accept="'image/*'"
:max-count="9"
:after-read="onAfterRead"
:before-delete="onBeforeDelete"
multiple
/>
</div>
</div> </div>
<div class="submit-btn"> <div class="submit-btn">
@ -86,15 +74,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { showToast, showSuccessToast, showFailToast, ImagePreview } from 'vant' import { showToast, showSuccessToast, showFailToast } from 'vant'
import { createExpense, getExpenseTypeTree, uploadFile } from '@/api' import { createExpense, getExpenseTypeTree } from '@/api'
const router = useRouter() const router = useRouter()
const loading = ref(false) const loading = ref(false)
const showTypePicker = ref(false) const showTypePicker = ref(false)
const showDatePicker = ref(false) const showDatePicker = ref(false)
const fileList = ref<any[]>([])
const uploadedAttachments = ref<string[]>([]) // COS
const form = reactive({ const form = reactive({
title: '', title: '',
@ -120,48 +106,6 @@ const onDateConfirm = ({ selectedValues }: any) => {
showDatePicker.value = false showDatePicker.value = false
} }
// - 使 COS
const onAfterRead = async (file: any) => {
if (!file.file) return
try {
//
showToast('上传中...')
// uploadFile API COS
const res: any = await uploadFile(file.file, 'expense', undefined, '支出附件')
//
const filePath = res.data?.filePath || res.data?.url
if (filePath) {
uploadedAttachments.value.push(filePath)
console.log('文件上传成功:', filePath)
showSuccessToast('上传成功')
} else {
showFailToast('上传失败:未获取文件路径')
}
} catch (error: any) {
console.error('上传失败:', error)
showFailToast(error.message || '上传失败')
}
}
const onBeforeDelete = (file: any, detail: any) => {
//
uploadedAttachments.value.splice(detail.index, 1)
return true
}
// URL
const onPreviewImage = (index: number) => {
//
// 使 URL
ImagePreview.show({
images: uploadedAttachments.value,
startPosition: index,
})
}
const handleSubmit = async () => { const handleSubmit = async () => {
if (!form.title) { if (!form.title) {
showFailToast('请输入支出标题') showFailToast('请输入支出标题')
@ -182,22 +126,15 @@ const handleSubmit = async () => {
loading.value = true loading.value = true
try { try {
// LocalDateTime // LocalDateTime
const expenseDateTime = form.expenseDate ? `${form.expenseDate}T12:00:00` : null const expenseDateTime = form.expenseDate ? `${form.expenseDate}T12:00:00` : null
// COS
const attachmentsStr = uploadedAttachments.value.length > 0
? uploadedAttachments.value.join(',')
: null
const requestData = { const requestData = {
title: form.title, title: form.title,
expenseType: form.expenseTypeId, expenseType: form.expenseTypeId,
amount: parseFloat(form.amount), amount: parseFloat(form.amount),
expenseDate: expenseDateTime, expenseDate: expenseDateTime,
purpose: form.description, purpose: form.description,
payeeName: form.payeeName, payeeName: form.payeeName
attachments: attachmentsStr
} }
console.log('提交支出数据:', requestData) console.log('提交支出数据:', requestData)
await createExpense(requestData) await createExpense(requestData)

View File

@ -16,42 +16,29 @@
@load="onLoad" @load="onLoad"
> >
<div class="expense-card" v-for="item in list" :key="item.expenseId"> <div class="expense-card" v-for="item in list" :key="item.expenseId">
<!-- 第一行标题 + 支出时间 --> <div class="expense-header">
<div class="card-row title-row"> <span class="expense-title">{{ item.title }}</span>
<span class="card-title">{{ item.title }}</span> <van-tag :type="getStatusType(item.status)">{{ getStatusText(item.status) }}</van-tag>
<span class="card-time">{{ formatDateTime(item.expenseDate) }}</span>
</div> </div>
<div class="expense-info">
<!-- 第二行支出类型 + 支出金额 --> <div class="info-item">
<div class="card-row info-row"> <van-icon name="apps-o" />
<div class="info-left">
<van-icon name="apps-o" class="row-icon" />
<span class="info-label">支出类型</span>
<span>{{ item.typeName || '-' }}</span> <span>{{ item.typeName || '-' }}</span>
</div> </div>
<span class="card-amount">¥{{ item.amount?.toLocaleString() }}</span> <div class="info-item">
<van-icon name="clock-o" />
<span>{{ item.expenseDate }}</span>
</div>
</div> </div>
<div class="expense-amount">
<!-- 第三行收款单位 --> <div class="amount-item">
<div class="card-row payee-row"> <span class="label">支出金额</span>
<van-icon name="shop-o" class="row-icon" /> <span class="value expense-value">¥{{ item.amount?.toLocaleString() }}</span>
<span class="info-label">收款单位</span> </div>
<span>{{ item.payeeName || '-' }}</span> <div class="amount-item" v-if="item.paidAmount">
</div> <span class="label">已支付</span>
<span class="value">¥{{ item.paidAmount?.toLocaleString() }}</span>
<!-- 第四行支付描述 --> </div>
<div class="card-row desc-row" v-if="item.purpose">
<van-icon name="description-o" class="row-icon" />
<span class="info-label">支付描述</span>
<span class="desc-text">{{ item.purpose }}</span>
</div>
<!-- 第五行查看附件 -->
<div class="card-row attachment-row" v-if="item.attachments">
<van-icon name="photo-o" class="row-icon" />
<span class="attachment-btn" @click="previewAttachments(item)">
查看附件{{ getAttachmentCount(item.attachments) }}
</span>
</div> </div>
</div> </div>
</van-list> </van-list>
@ -67,7 +54,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { getExpenseList } from '@/api' import { getExpenseList } from '@/api'
import { ImagePreview } from 'vant'
const searchText = ref('') const searchText = ref('')
const loading = ref(false) const loading = ref(false)
@ -77,43 +63,6 @@ const list = ref<any[]>([])
const pageNum = ref(1) const pageNum = ref(1)
const pageSize = 10 const pageSize = 10
//
const formatDateTime = (dateTime: string) => {
if (!dateTime) return ''
try {
// LocalDateTime 2024-01-15T10:30:00
const date = new Date(dateTime)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}`
} catch (e) {
return dateTime
}
}
//
const getAttachmentCount = (attachments: string) => {
if (!attachments) return 0
return attachments.split(',').filter(s => s.trim()).length
}
//
const previewAttachments = (item: any) => {
if (!item.attachments) return
// base64 URL
const attachmentList = item.attachments.split(',')
const imageUrls = attachmentList.map((b64: string) => `data:image/jpeg;base64,${b64}`)
ImagePreview.show({
images: imageUrls,
startPosition: 0,
})
}
const getStatusType = (status: string): 'primary' | 'success' | 'warning' | 'danger' | 'default' => { const getStatusType = (status: string): 'primary' | 'success' | 'warning' | 'danger' | 'default' => {
const map: Record<string, 'primary' | 'success' | 'warning' | 'danger' | 'default'> = { const map: Record<string, 'primary' | 'success' | 'warning' | 'danger' | 'default'> = {
'pending': 'warning', 'pending': 'warning',
@ -203,99 +152,59 @@ onMounted(() => {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
} }
.card-row { .expense-header {
display: flex; display: flex;
align-items: center;
margin-bottom: 10px;
}
.card-row:last-child {
margin-bottom: 0;
}
.title-row {
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: center;
margin-bottom: 12px; margin-bottom: 12px;
padding-bottom: 10px;
border-bottom: 1px solid #f0f0f0;
} }
.card-title { .expense-title {
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
color: #333; color: #333;
flex: 1;
margin-right: 12px;
} }
.card-time { .expense-info {
font-size: 12px; display: flex;
color: #999; gap: 16px;
white-space: nowrap; margin-bottom: 12px;
} }
.info-row { .info-item {
justify-content: space-between;
margin-bottom: 10px;
}
.info-left {
display: flex; display: flex;
align-items: center; align-items: center;
flex: 1; gap: 4px;
font-size: 13px; font-size: 13px;
color: #666; color: #666;
} }
.row-icon { .expense-amount {
font-size: 14px; display: flex;
color: #999; gap: 24px;
margin-right: 4px; padding-top: 12px;
}
.info-label {
color: #999;
margin-right: 4px;
}
.card-amount {
font-size: 18px;
font-weight: 600;
color: #FF3B30;
white-space: nowrap;
}
.payee-row,
.desc-row {
font-size: 13px;
color: #666;
margin-bottom: 8px;
}
.desc-text {
flex: 1;
line-height: 1.5;
}
.attachment-row {
padding-top: 10px;
margin-top: 10px;
border-top: 1px solid #f0f0f0; border-top: 1px solid #f0f0f0;
} }
.attachment-btn { .amount-item {
color: #007AFF; display: flex;
font-size: 13px; flex-direction: column;
cursor: pointer;
padding: 4px 8px;
background: rgba(0, 122, 255, 0.08);
border-radius: 4px;
transition: all 0.2s ease;
} }
.attachment-btn:active { .amount-item .label {
background: rgba(0, 122, 255, 0.15); font-size: 12px;
color: #999;
}
.amount-item .value {
font-size: 15px;
font-weight: 600;
color: #333;
margin-top: 4px;
}
.expense-value {
color: #FF3B30 !important;
} }
.add-btn { .add-btn {

View File

@ -60,7 +60,6 @@ import { ref } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { showFailToast, showSuccessToast, showConfirmDialog } from 'vant' import { showFailToast, showSuccessToast, showConfirmDialog } from 'vant'
import { updatePassword } from '@/api' import { updatePassword } from '@/api'
import md5 from 'js-md5'
const router = useRouter() const router = useRouter()
const submitting = ref(false) const submitting = ref(false)
@ -93,11 +92,11 @@ const onSubmit = async () => {
}) })
submitting.value = true submitting.value = true
await updatePassword({ await updatePassword({
oldPassword: md5(form.value.oldPassword), oldPassword: form.value.oldPassword,
newPassword: md5(form.value.newPassword), newPassword: form.value.newPassword,
confirmPassword: md5(form.value.confirmPassword) confirmPassword: form.value.confirmPassword
}) })
showSuccessToast('密码修改成功') showSuccessToast('密码修改成功')

View File

@ -14,7 +14,7 @@ public class Project extends BaseEntity {
private String projectCode; private String projectCode;
private String projectName; private String projectName;
private String customerId; private Long customerId;
private String customerName; private String customerName;
private String projectType; private String projectType;
private BigDecimal budgetAmount; private BigDecimal budgetAmount;
@ -39,11 +39,11 @@ public class Project extends BaseEntity {
this.projectName = projectName; this.projectName = projectName;
} }
public String getCustomerId() { public Long getCustomerId() {
return customerId; return customerId;
} }
public void setCustomerId(String customerId) { public void setCustomerId(Long customerId) {
this.customerId = customerId; this.customerId = customerId;
} }

View File

@ -23,10 +23,10 @@ public class Requirement extends BaseEntity {
private String description; private String description;
/** 项目ID */ /** 项目ID */
private String projectId; private Long projectId;
/** 客户ID */ /** 客户ID */
private String customerId; private Long customerId;
/** 优先级(high-高normal-中low-低) */ /** 优先级(high-高normal-中low-低) */
private String priority; private String priority;
@ -91,19 +91,19 @@ public class Requirement extends BaseEntity {
this.description = description; this.description = description;
} }
public String getProjectId() { public Long getProjectId() {
return projectId; return projectId;
} }
public void setProjectId(String projectId) { public void setProjectId(Long projectId) {
this.projectId = projectId; this.projectId = projectId;
} }
public String getCustomerId() { public Long getCustomerId() {
return customerId; return customerId;
} }
public void setCustomerId(String customerId) { public void setCustomerId(Long customerId) {
this.customerId = customerId; this.customerId = customerId;
} }

View File

@ -43,13 +43,13 @@ public class FundReceipt extends BaseEntity {
private String purpose; private String purpose;
/** 关联项目ID */ /** 关联项目ID */
private String projectId; private Long projectId;
/** 关联客户ID */ /** 关联客户ID */
private String customerId; private Long customerId;
/** 关联应收款ID */ /** 关联应收款ID */
private String receivableId; private Long receivableId;
/** 收款状态(0-待确认 1-已确认 2-已核销) */ /** 收款状态(0-待确认 1-已确认 2-已核销) */
private Integer receiptStatus; private Integer receiptStatus;
@ -58,13 +58,13 @@ public class FundReceipt extends BaseEntity {
private LocalDateTime confirmTime; private LocalDateTime confirmTime;
/** 确认人ID */ /** 确认人ID */
private String confirmBy; private Long confirmBy;
/** 核销时间 */ /** 核销时间 */
private LocalDateTime writeOffTime; private LocalDateTime writeOffTime;
/** 核销人ID */ /** 核销人ID */
private String writeOffBy; private Long writeOffBy;
/** 收款凭证 */ /** 收款凭证 */
private String voucher; private String voucher;
@ -95,22 +95,22 @@ public class FundReceipt extends BaseEntity {
public void setReceiptDate(LocalDateTime receiptDate) { this.receiptDate = receiptDate; } public void setReceiptDate(LocalDateTime receiptDate) { this.receiptDate = receiptDate; }
public String getPurpose() { return purpose; } public String getPurpose() { return purpose; }
public void setPurpose(String purpose) { this.purpose = purpose; } public void setPurpose(String purpose) { this.purpose = purpose; }
public String getProjectId() { return projectId; } public Long getProjectId() { return projectId; }
public void setProjectId(String projectId) { this.projectId = projectId; } public void setProjectId(Long projectId) { this.projectId = projectId; }
public String getCustomerId() { return customerId; } public Long getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; } public void setCustomerId(Long customerId) { this.customerId = customerId; }
public String getReceivableId() { return receivableId; } public Long getReceivableId() { return receivableId; }
public void setReceivableId(String receivableId) { this.receivableId = receivableId; } public void setReceivableId(Long receivableId) { this.receivableId = receivableId; }
public Integer getReceiptStatus() { return receiptStatus; } public Integer getReceiptStatus() { return receiptStatus; }
public void setReceiptStatus(Integer receiptStatus) { this.receiptStatus = receiptStatus; } public void setReceiptStatus(Integer receiptStatus) { this.receiptStatus = receiptStatus; }
public LocalDateTime getConfirmTime() { return confirmTime; } public LocalDateTime getConfirmTime() { return confirmTime; }
public void setConfirmTime(LocalDateTime confirmTime) { this.confirmTime = confirmTime; } public void setConfirmTime(LocalDateTime confirmTime) { this.confirmTime = confirmTime; }
public String getConfirmBy() { return confirmBy; } public Long getConfirmBy() { return confirmBy; }
public void setConfirmBy(String confirmBy) { this.confirmBy = confirmBy; } public void setConfirmBy(Long confirmBy) { this.confirmBy = confirmBy; }
public LocalDateTime getWriteOffTime() { return writeOffTime; } public LocalDateTime getWriteOffTime() { return writeOffTime; }
public void setWriteOffTime(LocalDateTime writeOffTime) { this.writeOffTime = writeOffTime; } public void setWriteOffTime(LocalDateTime writeOffTime) { this.writeOffTime = writeOffTime; }
public String getWriteOffBy() { return writeOffBy; } public Long getWriteOffBy() { return writeOffBy; }
public void setWriteOffBy(String writeOffBy) { this.writeOffBy = writeOffBy; } public void setWriteOffBy(Long writeOffBy) { this.writeOffBy = writeOffBy; }
public String getVoucher() { return voucher; } public String getVoucher() { return voucher; }
public void setVoucher(String voucher) { this.voucher = voucher; } public void setVoucher(String voucher) { this.voucher = voucher; }
public String getInvoiceNo() { return invoiceNo; } public String getInvoiceNo() { return invoiceNo; }

View File

@ -17,13 +17,13 @@ public class Receivable extends BaseEntity {
private String receivableCode; private String receivableCode;
/** 关联需求ID */ /** 关联需求ID */
private String requirementId; private Long requirementId;
/** 关联项目ID */ /** 关联项目ID */
private String projectId; private Long projectId;
/** 关联客户ID */ /** 关联客户ID */
private String customerId; private Long customerId;
/** 应收款金额 */ /** 应收款金额 */
private BigDecimal receivableAmount; private BigDecimal receivableAmount;
@ -59,7 +59,7 @@ public class Receivable extends BaseEntity {
private LocalDateTime confirmTime; private LocalDateTime confirmTime;
/** 确认人ID */ /** 确认人ID */
private String confirmBy; private Long confirmBy;
/** 备注 */ /** 备注 */
private String remark; private String remark;
@ -72,27 +72,27 @@ public class Receivable extends BaseEntity {
this.receivableCode = receivableCode; this.receivableCode = receivableCode;
} }
public String getRequirementId() { public Long getRequirementId() {
return requirementId; return requirementId;
} }
public void setRequirementId(String requirementId) { public void setRequirementId(Long requirementId) {
this.requirementId = requirementId; this.requirementId = requirementId;
} }
public String getProjectId() { public Long getProjectId() {
return projectId; return projectId;
} }
public void setProjectId(String projectId) { public void setProjectId(Long projectId) {
this.projectId = projectId; this.projectId = projectId;
} }
public String getCustomerId() { public Long getCustomerId() {
return customerId; return customerId;
} }
public void setCustomerId(String customerId) { public void setCustomerId(Long customerId) {
this.customerId = customerId; this.customerId = customerId;
} }
@ -184,11 +184,11 @@ public class Receivable extends BaseEntity {
this.confirmTime = confirmTime; this.confirmTime = confirmTime;
} }
public String getConfirmBy() { public Long getConfirmBy() {
return confirmBy; return confirmBy;
} }
public void setConfirmBy(String confirmBy) { public void setConfirmBy(Long confirmBy) {
this.confirmBy = confirmBy; this.confirmBy = confirmBy;
} }

View File

@ -8,7 +8,7 @@ import java.time.LocalDateTime;
public class FundReceiptDTO { public class FundReceiptDTO {
private String id; private Long id;
@NotBlank(message = "收款标题不能为空") @NotBlank(message = "收款标题不能为空")
private String title; private String title;
@ -29,15 +29,15 @@ public class FundReceiptDTO {
private String payerAccount; private String payerAccount;
private LocalDateTime receiptDate; private LocalDateTime receiptDate;
private String purpose; private String purpose;
private String projectId; private Long projectId;
private String customerId; private Long customerId;
private String invoiceNo; private String invoiceNo;
private String voucher; private String voucher;
private String attachments; private String attachments;
private String remark; private String remark;
public String getId() { return id; } public Long getId() { return id; }
public void setId(String id) { this.id = id; } public void setId(Long id) { this.id = id; }
public String getTitle() { return title; } public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; } public void setTitle(String title) { this.title = title; }
public BigDecimal getAmount() { return amount; } public BigDecimal getAmount() { return amount; }
@ -56,10 +56,10 @@ public class FundReceiptDTO {
public void setReceiptDate(LocalDateTime receiptDate) { this.receiptDate = receiptDate; } public void setReceiptDate(LocalDateTime receiptDate) { this.receiptDate = receiptDate; }
public String getPurpose() { return purpose; } public String getPurpose() { return purpose; }
public void setPurpose(String purpose) { this.purpose = purpose; } public void setPurpose(String purpose) { this.purpose = purpose; }
public String getProjectId() { return projectId; } public Long getProjectId() { return projectId; }
public void setProjectId(String projectId) { this.projectId = projectId; } public void setProjectId(Long projectId) { this.projectId = projectId; }
public String getCustomerId() { return customerId; } public Long getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; } public void setCustomerId(Long customerId) { this.customerId = customerId; }
public String getInvoiceNo() { return invoiceNo; } public String getInvoiceNo() { return invoiceNo; }
public void setInvoiceNo(String invoiceNo) { this.invoiceNo = invoiceNo; } public void setInvoiceNo(String invoiceNo) { this.invoiceNo = invoiceNo; }
public String getVoucher() { return voucher; } public String getVoucher() { return voucher; }

View File

@ -12,15 +12,15 @@ import java.time.LocalDate;
*/ */
public class ReceivableDTO { public class ReceivableDTO {
private String id; private Long id;
private String requirementId; private Long requirementId;
@NotNull(message = "项目ID不能为空") @NotNull(message = "项目ID不能为空")
private String projectId; private Long projectId;
@NotNull(message = "客户ID不能为空") @NotNull(message = "客户ID不能为空")
private String customerId; private Long customerId;
@NotNull(message = "应收款金额不能为空") @NotNull(message = "应收款金额不能为空")
@Positive(message = "应收款金额必须大于0") @Positive(message = "应收款金额必须大于0")
@ -37,35 +37,35 @@ public class ReceivableDTO {
private String remark; private String remark;
public String getId() { public Long getId() {
return id; return id;
} }
public void setId(String id) { public void setId(Long id) {
this.id = id; this.id = id;
} }
public String getRequirementId() { public Long getRequirementId() {
return requirementId; return requirementId;
} }
public void setRequirementId(String requirementId) { public void setRequirementId(Long requirementId) {
this.requirementId = requirementId; this.requirementId = requirementId;
} }
public String getProjectId() { public Long getProjectId() {
return projectId; return projectId;
} }
public void setProjectId(String projectId) { public void setProjectId(Long projectId) {
this.projectId = projectId; this.projectId = projectId;
} }
public String getCustomerId() { public Long getCustomerId() {
return customerId; return customerId;
} }
public void setCustomerId(String customerId) { public void setCustomerId(Long customerId) {
this.customerId = customerId; this.customerId = customerId;
} }

View File

@ -5,7 +5,7 @@ import java.time.LocalDateTime;
public class FundReceiptVO { public class FundReceiptVO {
private String id; private Long id;
private String receiptNo; private String receiptNo;
private String title; private String title;
private BigDecimal amount; private BigDecimal amount;
@ -17,24 +17,24 @@ public class FundReceiptVO {
private String payerAccount; private String payerAccount;
private LocalDateTime receiptDate; private LocalDateTime receiptDate;
private String purpose; private String purpose;
private String projectId; private Long projectId;
private String customerId; private Long customerId;
private String receivableId; private Long receivableId;
private Integer receiptStatus; private Integer receiptStatus;
private String receiptStatusName; private String receiptStatusName;
private LocalDateTime confirmTime; private LocalDateTime confirmTime;
private String confirmBy; private Long confirmBy;
private LocalDateTime writeOffTime; private LocalDateTime writeOffTime;
private String writeOffBy; private Long writeOffBy;
private String voucher; private String voucher;
private String invoiceNo; private String invoiceNo;
private String attachments; private String attachments;
private String tenantId; private Long tenantId;
private String createdBy; private Long createdBy;
private LocalDateTime createdTime; private LocalDateTime createdTime;
public String getId() { return id; } public Long getId() { return id; }
public void setId(String id) { this.id = id; } public void setId(Long id) { this.id = id; }
public String getReceiptNo() { return receiptNo; } public String getReceiptNo() { return receiptNo; }
public void setReceiptNo(String receiptNo) { this.receiptNo = receiptNo; } public void setReceiptNo(String receiptNo) { this.receiptNo = receiptNo; }
public String getTitle() { return title; } public String getTitle() { return title; }
@ -57,34 +57,34 @@ public class FundReceiptVO {
public void setReceiptDate(LocalDateTime receiptDate) { this.receiptDate = receiptDate; } public void setReceiptDate(LocalDateTime receiptDate) { this.receiptDate = receiptDate; }
public String getPurpose() { return purpose; } public String getPurpose() { return purpose; }
public void setPurpose(String purpose) { this.purpose = purpose; } public void setPurpose(String purpose) { this.purpose = purpose; }
public String getProjectId() { return projectId; } public Long getProjectId() { return projectId; }
public void setProjectId(String projectId) { this.projectId = projectId; } public void setProjectId(Long projectId) { this.projectId = projectId; }
public String getCustomerId() { return customerId; } public Long getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; } public void setCustomerId(Long customerId) { this.customerId = customerId; }
public String getReceivableId() { return receivableId; } public Long getReceivableId() { return receivableId; }
public void setReceivableId(String receivableId) { this.receivableId = receivableId; } public void setReceivableId(Long receivableId) { this.receivableId = receivableId; }
public Integer getReceiptStatus() { return receiptStatus; } public Integer getReceiptStatus() { return receiptStatus; }
public void setReceiptStatus(Integer receiptStatus) { this.receiptStatus = receiptStatus; } public void setReceiptStatus(Integer receiptStatus) { this.receiptStatus = receiptStatus; }
public String getReceiptStatusName() { return receiptStatusName; } public String getReceiptStatusName() { return receiptStatusName; }
public void setReceiptStatusName(String receiptStatusName) { this.receiptStatusName = receiptStatusName; } public void setReceiptStatusName(String receiptStatusName) { this.receiptStatusName = receiptStatusName; }
public LocalDateTime getConfirmTime() { return confirmTime; } public LocalDateTime getConfirmTime() { return confirmTime; }
public void setConfirmTime(LocalDateTime confirmTime) { this.confirmTime = confirmTime; } public void setConfirmTime(LocalDateTime confirmTime) { this.confirmTime = confirmTime; }
public String getConfirmBy() { return confirmBy; } public Long getConfirmBy() { return confirmBy; }
public void setConfirmBy(String confirmBy) { this.confirmBy = confirmBy; } public void setConfirmBy(Long confirmBy) { this.confirmBy = confirmBy; }
public LocalDateTime getWriteOffTime() { return writeOffTime; } public LocalDateTime getWriteOffTime() { return writeOffTime; }
public void setWriteOffTime(LocalDateTime writeOffTime) { this.writeOffTime = writeOffTime; } public void setWriteOffTime(LocalDateTime writeOffTime) { this.writeOffTime = writeOffTime; }
public String getWriteOffBy() { return writeOffBy; } public Long getWriteOffBy() { return writeOffBy; }
public void setWriteOffBy(String writeOffBy) { this.writeOffBy = writeOffBy; } public void setWriteOffBy(Long writeOffBy) { this.writeOffBy = writeOffBy; }
public String getVoucher() { return voucher; } public String getVoucher() { return voucher; }
public void setVoucher(String voucher) { this.voucher = voucher; } public void setVoucher(String voucher) { this.voucher = voucher; }
public String getInvoiceNo() { return invoiceNo; } public String getInvoiceNo() { return invoiceNo; }
public void setInvoiceNo(String invoiceNo) { this.invoiceNo = invoiceNo; } public void setInvoiceNo(String invoiceNo) { this.invoiceNo = invoiceNo; }
public String getAttachments() { return attachments; } public String getAttachments() { return attachments; }
public void setAttachments(String attachments) { this.attachments = attachments; } public void setAttachments(String attachments) { this.attachments = attachments; }
public String getTenantId() { return tenantId; } public Long getTenantId() { return tenantId; }
public void setTenantId(String tenantId) { this.tenantId = tenantId; } public void setTenantId(Long tenantId) { this.tenantId = tenantId; }
public String getCreatedBy() { return createdBy; } public Long getCreatedBy() { return createdBy; }
public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } public void setCreatedBy(Long createdBy) { this.createdBy = createdBy; }
public LocalDateTime getCreatedTime() { return createdTime; } public LocalDateTime getCreatedTime() { return createdTime; }
public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; } public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; }
} }

View File

@ -40,10 +40,10 @@ public class FundRequest extends BaseEntity {
private String purpose; private String purpose;
/** 项目ID */ /** 项目ID */
private String projectId; private Long projectId;
/** 客户ID */ /** 客户ID */
private String customerId; private Long customerId;
/** 申请日期 */ /** 申请日期 */
private LocalDateTime requestDate; private LocalDateTime requestDate;
@ -58,7 +58,7 @@ public class FundRequest extends BaseEntity {
private Integer currentNode; private Integer currentNode;
/** 审批人ID */ /** 审批人ID */
private String approverId; private Long approverId;
/** 审批时间 */ /** 审批时间 */
private LocalDateTime approvalTime; private LocalDateTime approvalTime;
@ -141,19 +141,19 @@ public class FundRequest extends BaseEntity {
this.purpose = purpose; this.purpose = purpose;
} }
public String getProjectId() { public Long getProjectId() {
return projectId; return projectId;
} }
public void setProjectId(String projectId) { public void setProjectId(Long projectId) {
this.projectId = projectId; this.projectId = projectId;
} }
public String getCustomerId() { public Long getCustomerId() {
return customerId; return customerId;
} }
public void setCustomerId(String customerId) { public void setCustomerId(Long customerId) {
this.customerId = customerId; this.customerId = customerId;
} }
@ -189,11 +189,11 @@ public class FundRequest extends BaseEntity {
this.currentNode = currentNode; this.currentNode = currentNode;
} }
public String getApproverId() { public Long getApproverId() {
return approverId; return approverId;
} }
public void setApproverId(String approverId) { public void setApproverId(Long approverId) {
this.approverId = approverId; this.approverId = approverId;
} }

View File

@ -112,20 +112,6 @@
<artifactId>logstash-logback-encoder</artifactId> <artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version> <version>7.4</version>
</dependency> </dependency>
<!-- SpringDoc OpenAPI (Swagger UI) -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -5,9 +5,6 @@ import com.fundplatform.sys.dto.LoginRequestDTO;
import com.fundplatform.sys.service.AuthService; import com.fundplatform.sys.service.AuthService;
import com.fundplatform.sys.vo.LoginVO; import com.fundplatform.sys.vo.LoginVO;
import com.fundplatform.sys.vo.UserVO; import com.fundplatform.sys.vo.UserVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -19,7 +16,6 @@ import org.springframework.web.bind.annotation.RestController;
/** /**
* 认证Controller * 认证Controller
*/ */
@Tag(name = "认证管理", description = "登录、登出、Token刷新等认证接口")
@RestController @RestController
@RequestMapping("/api/v1/auth") @RequestMapping("/api/v1/auth")
public class AuthController { public class AuthController {
@ -33,7 +29,6 @@ public class AuthController {
/** /**
* 用户登录 * 用户登录
*/ */
@Operation(summary = "用户登录", description = "使用用户名和MD5加密密码登录返回Token及用户信息")
@PostMapping("/login") @PostMapping("/login")
public Result<LoginVO> login(@Valid @RequestBody LoginRequestDTO request) { public Result<LoginVO> login(@Valid @RequestBody LoginRequestDTO request) {
LoginVO vo = authService.login(request); LoginVO vo = authService.login(request);
@ -43,9 +38,8 @@ public class AuthController {
/** /**
* 用户登出 * 用户登出
*/ */
@Operation(summary = "用户登出", description = "销毁当前用户Token使会话失效")
@PostMapping("/logout") @PostMapping("/logout")
public Result<Void> logout(@Parameter(description = "当前登录用户ID") @RequestHeader(value = "X-User-Id", required = false) String userId) { public Result<Void> logout(@RequestHeader(value = "X-User-Id", required = false) Long userId) {
authService.logout(userId); authService.logout(userId);
return Result.success(); return Result.success();
} }
@ -53,9 +47,8 @@ public class AuthController {
/** /**
* 刷新Token * 刷新Token
*/ */
@Operation(summary = "刷新Token", description = "延长当前Token的有效期")
@PostMapping("/refresh") @PostMapping("/refresh")
public Result<LoginVO> refreshToken(@Parameter(description = "当前登录用户ID") @RequestHeader("X-User-Id") String userId) { public Result<LoginVO> refreshToken(@RequestHeader("X-User-Id") Long userId) {
LoginVO vo = authService.refreshToken(userId); LoginVO vo = authService.refreshToken(userId);
return Result.success(vo); return Result.success(vo);
} }
@ -63,9 +56,8 @@ public class AuthController {
/** /**
* 获取当前用户信息 * 获取当前用户信息
*/ */
@Operation(summary = "获取当前用户信息", description = "根据Token中的用户ID获取用户详情")
@GetMapping("/info") @GetMapping("/info")
public Result<UserVO> getUserInfo(@Parameter(description = "当前登录用户ID") @RequestHeader("X-User-Id") String userId) { public Result<UserVO> getUserInfo(@RequestHeader("X-User-Id") Long userId) {
UserVO vo = authService.getUserInfo(userId); UserVO vo = authService.getUserInfo(userId);
return Result.success(vo); return Result.success(vo);
} }

View File

@ -28,8 +28,8 @@ public class ConfigController {
* 创建参数 * 创建参数
*/ */
@PostMapping @PostMapping
public Result<String> create(@Valid @RequestBody ConfigDTO dto) { public Result<Long> create(@Valid @RequestBody ConfigDTO dto) {
String id = configService.createConfig(dto); Long id = configService.createConfig(dto);
return Result.success(id); return Result.success(id);
} }
@ -46,7 +46,7 @@ public class ConfigController {
* 根据ID查询参数 * 根据ID查询参数
*/ */
@GetMapping("/{id}") @GetMapping("/{id}")
public Result<ConfigVO> getById(@PathVariable String id) { public Result<ConfigVO> getById(@PathVariable Long id) {
ConfigVO vo = configService.getConfigById(id); ConfigVO vo = configService.getConfigById(id);
return Result.success(vo); return Result.success(vo);
} }
@ -94,7 +94,7 @@ public class ConfigController {
* 删除参数 * 删除参数
*/ */
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public Result<Boolean> delete(@PathVariable String id) { public Result<Boolean> delete(@PathVariable Long id) {
boolean result = configService.deleteConfig(id); boolean result = configService.deleteConfig(id);
return Result.success(result); return Result.success(result);
} }

View File

@ -4,9 +4,6 @@ import com.fundplatform.common.core.Result;
import com.fundplatform.sys.dto.DeptDTO; import com.fundplatform.sys.dto.DeptDTO;
import com.fundplatform.sys.service.DeptService; import com.fundplatform.sys.service.DeptService;
import com.fundplatform.sys.vo.DeptVO; import com.fundplatform.sys.vo.DeptVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -15,7 +12,6 @@ import java.util.List;
/** /**
* 部门管理Controller * 部门管理Controller
*/ */
@Tag(name = "部门管理", description = "部门的增删改查及树形结构查询")
@RestController @RestController
@RequestMapping("/api/v1/sys/dept") @RequestMapping("/api/v1/sys/dept")
public class DeptController { public class DeptController {
@ -26,51 +22,44 @@ public class DeptController {
this.deptService = deptService; this.deptService = deptService;
} }
@Operation(summary = "创建部门")
@PostMapping @PostMapping
public Result<String> create(@Valid @RequestBody DeptDTO dto) { public Result<Long> create(@Valid @RequestBody DeptDTO dto) {
String deptId = deptService.createDept(dto); Long deptId = deptService.createDept(dto);
return Result.success(deptId); return Result.success(deptId);
} }
@Operation(summary = "更新部门信息")
@PutMapping @PutMapping
public Result<Boolean> update(@Valid @RequestBody DeptDTO dto) { public Result<Boolean> update(@Valid @RequestBody DeptDTO dto) {
boolean result = deptService.updateDept(dto); boolean result = deptService.updateDept(dto);
return Result.success(result); return Result.success(result);
} }
@Operation(summary = "根据ID查询部门")
@GetMapping("/{id}") @GetMapping("/{id}")
public Result<DeptVO> getById(@Parameter(description = "部门ID") @PathVariable String id) { public Result<DeptVO> getById(@PathVariable Long id) {
DeptVO vo = deptService.getDeptById(id); DeptVO vo = deptService.getDeptById(id);
return Result.success(vo); return Result.success(vo);
} }
@Operation(summary = "获取部门树形结构")
@GetMapping("/tree") @GetMapping("/tree")
public Result<List<DeptVO>> getTree() { public Result<List<DeptVO>> getTree() {
List<DeptVO> tree = deptService.getDeptTree(); List<DeptVO> tree = deptService.getDeptTree();
return Result.success(tree); return Result.success(tree);
} }
@Operation(summary = "查询所有部门列表(扁平)")
@GetMapping("/list") @GetMapping("/list")
public Result<List<DeptVO>> listAll() { public Result<List<DeptVO>> listAll() {
List<DeptVO> list = deptService.listAllDepts(); List<DeptVO> list = deptService.listAllDepts();
return Result.success(list); return Result.success(list);
} }
@Operation(summary = "删除部门")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public Result<Boolean> delete(@Parameter(description = "部门ID") @PathVariable String id) { public Result<Boolean> delete(@PathVariable Long id) {
boolean result = deptService.deleteDept(id); boolean result = deptService.deleteDept(id);
return Result.success(result); return Result.success(result);
} }
@Operation(summary = "更新部门状态")
@PutMapping("/{id}/status") @PutMapping("/{id}/status")
public Result<Boolean> updateStatus(@Parameter(description = "部门ID") @PathVariable String id, @Parameter(description = "状态0禁用 1启用") @RequestParam Integer status) { public Result<Boolean> updateStatus(@PathVariable Long id, @RequestParam Integer status) {
boolean result = deptService.updateStatus(id, status); boolean result = deptService.updateStatus(id, status);
return Result.success(result); return Result.success(result);
} }

View File

@ -4,9 +4,6 @@ import com.fundplatform.common.core.Result;
import com.fundplatform.sys.dto.MenuDTO; import com.fundplatform.sys.dto.MenuDTO;
import com.fundplatform.sys.service.MenuService; import com.fundplatform.sys.service.MenuService;
import com.fundplatform.sys.vo.MenuVO; import com.fundplatform.sys.vo.MenuVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -15,7 +12,6 @@ import java.util.List;
/** /**
* 菜单管理Controller * 菜单管理Controller
*/ */
@Tag(name = "菜单管理", description = "菜单和权限的增删改查")
@RestController @RestController
@RequestMapping("/api/v1/sys/menu") @RequestMapping("/api/v1/sys/menu")
public class MenuController { public class MenuController {
@ -26,44 +22,38 @@ public class MenuController {
this.menuService = menuService; this.menuService = menuService;
} }
@Operation(summary = "创建菜单/权限")
@PostMapping @PostMapping
public Result<String> create(@Valid @RequestBody MenuDTO dto) { public Result<Long> create(@Valid @RequestBody MenuDTO dto) {
String menuId = menuService.createMenu(dto); Long menuId = menuService.createMenu(dto);
return Result.success(menuId); return Result.success(menuId);
} }
@Operation(summary = "更新菜单/权限信息")
@PutMapping @PutMapping
public Result<Boolean> update(@Valid @RequestBody MenuDTO dto) { public Result<Boolean> update(@Valid @RequestBody MenuDTO dto) {
boolean result = menuService.updateMenu(dto); boolean result = menuService.updateMenu(dto);
return Result.success(result); return Result.success(result);
} }
@Operation(summary = "根据ID查询菜单")
@GetMapping("/{id}") @GetMapping("/{id}")
public Result<MenuVO> getById(@Parameter(description = "菜单ID") @PathVariable String id) { public Result<MenuVO> getById(@PathVariable Long id) {
MenuVO vo = menuService.getMenuById(id); MenuVO vo = menuService.getMenuById(id);
return Result.success(vo); return Result.success(vo);
} }
@Operation(summary = "获取菜单树(全量)")
@GetMapping("/tree") @GetMapping("/tree")
public Result<List<MenuVO>> getTree() { public Result<List<MenuVO>> getTree() {
List<MenuVO> tree = menuService.getMenuTree(); List<MenuVO> tree = menuService.getMenuTree();
return Result.success(tree); return Result.success(tree);
} }
@Operation(summary = "获取用户菜单树", description = "根据用户ID获取其有权访问的菜单树")
@GetMapping("/user/{userId}") @GetMapping("/user/{userId}")
public Result<List<MenuVO>> getUserTree(@Parameter(description = "用户ID") @PathVariable String userId) { public Result<List<MenuVO>> getUserTree(@PathVariable Long userId) {
List<MenuVO> tree = menuService.getUserMenuTree(userId); List<MenuVO> tree = menuService.getUserMenuTree(userId);
return Result.success(tree); return Result.success(tree);
} }
@Operation(summary = "删除菜单/权限")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public Result<Boolean> delete(@Parameter(description = "菜单ID") @PathVariable String id) { public Result<Boolean> delete(@PathVariable Long id) {
boolean result = menuService.deleteMenu(id); boolean result = menuService.deleteMenu(id);
return Result.success(result); return Result.success(result);
} }
@ -71,9 +61,8 @@ public class MenuController {
/** /**
* 获取用户权限标识列表 * 获取用户权限标识列表
*/ */
@Operation(summary = "获取用户权限标识列表", description = "返回用户拥有的所有权限标识字符串")
@GetMapping("/permissions/{userId}") @GetMapping("/permissions/{userId}")
public Result<List<String>> getUserPermissions(@Parameter(description = "用户ID") @PathVariable String userId) { public Result<List<String>> getUserPermissions(@PathVariable Long userId) {
List<String> permissions = menuService.getUserPermissions(userId); List<String> permissions = menuService.getUserPermissions(userId);
return Result.success(permissions); return Result.success(permissions);
} }

View File

@ -4,15 +4,11 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fundplatform.common.core.Result; import com.fundplatform.common.core.Result;
import com.fundplatform.sys.data.entity.OperationLog; import com.fundplatform.sys.data.entity.OperationLog;
import com.fundplatform.sys.service.OperationLogService; import com.fundplatform.sys.service.OperationLogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
/** /**
* 操作日志Controller * 操作日志Controller
*/ */
@Tag(name = "操作日志", description = "系统操作日志查询与管理")
@RestController @RestController
@RequestMapping("/api/v1/log") @RequestMapping("/api/v1/log")
public class OperationLogController { public class OperationLogController {
@ -26,33 +22,30 @@ public class OperationLogController {
/** /**
* 分页查询操作日志 * 分页查询操作日志
*/ */
@Operation(summary = "分页查询操作日志")
@GetMapping("/page") @GetMapping("/page")
public Result<Page<OperationLog>> page( public Result<Page<OperationLog>> page(
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "1") int pageNum,
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize, @RequestParam(defaultValue = "10") int pageSize,
@Parameter(description = "用户ID") @RequestParam(required = false) Long userId, @RequestParam(required = false) Long userId,
@Parameter(description = "操作类型") @RequestParam(required = false) String operation, @RequestParam(required = false) String operation,
@Parameter(description = "开始时间yyyy-MM-dd HH:mm:ss") @RequestParam(required = false) String startTime, @RequestParam(required = false) String startTime,
@Parameter(description = "结束时间yyyy-MM-dd HH:mm:ss") @RequestParam(required = false) String endTime) { @RequestParam(required = false) String endTime) {
return Result.success(operationLogService.pageLogs(pageNum, pageSize, userId, operation, startTime, endTime)); return Result.success(operationLogService.pageLogs(pageNum, pageSize, userId, operation, startTime, endTime));
} }
/** /**
* 获取日志详情 * 获取日志详情
*/ */
@Operation(summary = "获取操作日志详情")
@GetMapping("/{id}") @GetMapping("/{id}")
public Result<OperationLog> getById(@Parameter(description = "日志ID") @PathVariable Long id) { public Result<OperationLog> getById(@PathVariable Long id) {
return Result.success(operationLogService.getById(id)); return Result.success(operationLogService.getById(id));
} }
/** /**
* 清理历史日志 * 清理历史日志
*/ */
@Operation(summary = "清理历史操作日志", description = "删除N天前的操作日志默认90天")
@DeleteMapping("/clean") @DeleteMapping("/clean")
public Result<Integer> cleanLogs(@Parameter(description = "保留天数默认90天") @RequestParam(defaultValue = "90") int days) { public Result<Integer> cleanLogs(@RequestParam(defaultValue = "90") int days) {
return Result.success(operationLogService.cleanLogs(days)); return Result.success(operationLogService.cleanLogs(days));
} }
} }

View File

@ -5,16 +5,12 @@ import com.fundplatform.sys.dto.PasswordDTO;
import com.fundplatform.sys.dto.ProfileDTO; import com.fundplatform.sys.dto.ProfileDTO;
import com.fundplatform.sys.service.UserService; import com.fundplatform.sys.service.UserService;
import com.fundplatform.sys.vo.UserVO; import com.fundplatform.sys.vo.UserVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
/** /**
* 个人中心Controller * 个人中心Controller
*/ */
@Tag(name = "个人中心", description = "当前登录用户的个人信息查询与修改")
@RestController @RestController
@RequestMapping("/api/v1/sys/profile") @RequestMapping("/api/v1/sys/profile")
public class ProfileController { public class ProfileController {
@ -28,9 +24,8 @@ public class ProfileController {
/** /**
* 获取个人信息 * 获取个人信息
*/ */
@Operation(summary = "获取个人信息")
@GetMapping @GetMapping
public Result<UserVO> getProfile(@Parameter(description = "当前登录用户ID") @RequestHeader("X-User-Id") String userId) { public Result<UserVO> getProfile(@RequestHeader("X-User-Id") Long userId) {
UserVO vo = userService.getUserById(userId); UserVO vo = userService.getUserById(userId);
return Result.success(vo); return Result.success(vo);
} }
@ -38,10 +33,9 @@ public class ProfileController {
/** /**
* 更新个人信息 * 更新个人信息
*/ */
@Operation(summary = "更新个人信息")
@PutMapping @PutMapping
public Result<Boolean> updateProfile( public Result<Boolean> updateProfile(
@Parameter(description = "当前登录用户ID") @RequestHeader("X-User-Id") String userId, @RequestHeader("X-User-Id") Long userId,
@Valid @RequestBody ProfileDTO dto) { @Valid @RequestBody ProfileDTO dto) {
boolean result = userService.updateProfile(userId, dto); boolean result = userService.updateProfile(userId, dto);
return Result.success(result); return Result.success(result);
@ -50,10 +44,9 @@ public class ProfileController {
/** /**
* 修改密码 * 修改密码
*/ */
@Operation(summary = "修改密码", description = "需要提供旧密码和新密码MD5加密后传输")
@PutMapping("/password") @PutMapping("/password")
public Result<Boolean> updatePassword( public Result<Boolean> updatePassword(
@Parameter(description = "当前登录用户ID") @RequestHeader("X-User-Id") String userId, @RequestHeader("X-User-Id") Long userId,
@Valid @RequestBody PasswordDTO dto) { @Valid @RequestBody PasswordDTO dto) {
boolean result = userService.updatePassword(userId, dto); boolean result = userService.updatePassword(userId, dto);
return Result.success(result); return Result.success(result);

View File

@ -5,9 +5,6 @@ import com.fundplatform.common.core.Result;
import com.fundplatform.sys.dto.RoleDTO; import com.fundplatform.sys.dto.RoleDTO;
import com.fundplatform.sys.service.RoleService; import com.fundplatform.sys.service.RoleService;
import com.fundplatform.sys.vo.RoleVO; import com.fundplatform.sys.vo.RoleVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -16,7 +13,6 @@ import java.util.List;
/** /**
* 角色管理Controller * 角色管理Controller
*/ */
@Tag(name = "角色管理", description = "角色的增删改查及菜单权限分配")
@RestController @RestController
@RequestMapping("/api/v1/sys/role") @RequestMapping("/api/v1/sys/role")
public class RoleController { public class RoleController {
@ -27,55 +23,48 @@ public class RoleController {
this.roleService = roleService; this.roleService = roleService;
} }
@Operation(summary = "创建角色")
@PostMapping @PostMapping
public Result<String> create(@Valid @RequestBody RoleDTO dto) { public Result<Long> create(@Valid @RequestBody RoleDTO dto) {
String roleId = roleService.createRole(dto); Long roleId = roleService.createRole(dto);
return Result.success(roleId); return Result.success(roleId);
} }
@Operation(summary = "更新角色信息")
@PutMapping @PutMapping
public Result<Boolean> update(@Valid @RequestBody RoleDTO dto) { public Result<Boolean> update(@Valid @RequestBody RoleDTO dto) {
boolean result = roleService.updateRole(dto); boolean result = roleService.updateRole(dto);
return Result.success(result); return Result.success(result);
} }
@Operation(summary = "根据ID查询角色")
@GetMapping("/{id}") @GetMapping("/{id}")
public Result<RoleVO> getById(@Parameter(description = "角色ID") @PathVariable String id) { public Result<RoleVO> getById(@PathVariable Long id) {
RoleVO vo = roleService.getRoleById(id); RoleVO vo = roleService.getRoleById(id);
return Result.success(vo); return Result.success(vo);
} }
@Operation(summary = "分页查询角色")
@GetMapping("/page") @GetMapping("/page")
public Result<Page<RoleVO>> page( public Result<Page<RoleVO>> page(
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "1") int pageNum,
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize, @RequestParam(defaultValue = "10") int pageSize,
@Parameter(description = "角色名称(模糊查询)") @RequestParam(required = false) String roleName, @RequestParam(required = false) String roleName,
@Parameter(description = "状态0禁用 1启用") @RequestParam(required = false) Integer status) { @RequestParam(required = false) Integer status) {
Page<RoleVO> page = roleService.pageRoles(pageNum, pageSize, roleName, status); Page<RoleVO> page = roleService.pageRoles(pageNum, pageSize, roleName, status);
return Result.success(page); return Result.success(page);
} }
@Operation(summary = "查询所有角色列表")
@GetMapping("/list") @GetMapping("/list")
public Result<List<RoleVO>> listAll() { public Result<List<RoleVO>> listAll() {
List<RoleVO> list = roleService.listAllRoles(); List<RoleVO> list = roleService.listAllRoles();
return Result.success(list); return Result.success(list);
} }
@Operation(summary = "删除角色")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public Result<Boolean> delete(@Parameter(description = "角色ID") @PathVariable String id) { public Result<Boolean> delete(@PathVariable Long id) {
boolean result = roleService.deleteRole(id); boolean result = roleService.deleteRole(id);
return Result.success(result); return Result.success(result);
} }
@Operation(summary = "更新角色状态")
@PutMapping("/{id}/status") @PutMapping("/{id}/status")
public Result<Boolean> updateStatus(@Parameter(description = "角色ID") @PathVariable String id, @Parameter(description = "状态0禁用 1启用") @RequestParam Integer status) { public Result<Boolean> updateStatus(@PathVariable Long id, @RequestParam Integer status) {
boolean result = roleService.updateStatus(id, status); boolean result = roleService.updateStatus(id, status);
return Result.success(result); return Result.success(result);
} }
@ -83,19 +72,17 @@ public class RoleController {
/** /**
* 获取角色菜单ID列表 * 获取角色菜单ID列表
*/ */
@Operation(summary = "获取角色的菜单ID列表")
@GetMapping("/{id}/menus") @GetMapping("/{id}/menus")
public Result<List<String>> getRoleMenus(@Parameter(description = "角色ID") @PathVariable String id) { public Result<List<Long>> getRoleMenus(@PathVariable Long id) {
List<String> menuIds = roleService.getRoleMenus(id); List<Long> menuIds = roleService.getRoleMenus(id);
return Result.success(menuIds); return Result.success(menuIds);
} }
/** /**
* 分配菜单给角色 * 分配菜单给角色
*/ */
@Operation(summary = "为角色分配菜单权限")
@PutMapping("/{id}/menus") @PutMapping("/{id}/menus")
public Result<Boolean> assignMenus(@Parameter(description = "角色ID") @PathVariable String id, @RequestBody List<String> menuIds) { public Result<Boolean> assignMenus(@PathVariable Long id, @RequestBody List<Long> menuIds) {
boolean result = roleService.assignMenus(id, menuIds); boolean result = roleService.assignMenus(id, menuIds);
return Result.success(result); return Result.success(result);
} }

View File

@ -5,16 +5,12 @@ import com.fundplatform.common.core.Result;
import com.fundplatform.sys.dto.TenantDTO; import com.fundplatform.sys.dto.TenantDTO;
import com.fundplatform.sys.service.TenantService; import com.fundplatform.sys.service.TenantService;
import com.fundplatform.sys.vo.TenantVO; import com.fundplatform.sys.vo.TenantVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
/** /**
* 租户管理Controller * 租户管理Controller
*/ */
@Tag(name = "租户管理", description = "平台租户的增删改查及状态管理(超级管理员使用)")
@RestController @RestController
@RequestMapping("/api/v1/sys/tenant") @RequestMapping("/api/v1/sys/tenant")
public class TenantController { public class TenantController {
@ -25,42 +21,36 @@ public class TenantController {
this.tenantService = tenantService; this.tenantService = tenantService;
} }
@Operation(summary = "创建租户")
@PostMapping @PostMapping
public Result<String> create(@Valid @RequestBody TenantDTO dto) { public Result<Long> create(@Valid @RequestBody TenantDTO dto) {
return Result.success(tenantService.createTenant(dto)); return Result.success(tenantService.createTenant(dto));
} }
@Operation(summary = "更新租户信息")
@PutMapping @PutMapping
public Result<Boolean> update(@Valid @RequestBody TenantDTO dto) { public Result<Boolean> update(@Valid @RequestBody TenantDTO dto) {
return Result.success(tenantService.updateTenant(dto)); return Result.success(tenantService.updateTenant(dto));
} }
@Operation(summary = "根据ID查询租户")
@GetMapping("/{id}") @GetMapping("/{id}")
public Result<TenantVO> getById(@Parameter(description = "租户ID") @PathVariable String id) { public Result<TenantVO> getById(@PathVariable Long id) {
return Result.success(tenantService.getTenantById(id)); return Result.success(tenantService.getTenantById(id));
} }
@Operation(summary = "分页查询租户")
@GetMapping("/page") @GetMapping("/page")
public Result<Page<TenantVO>> page( public Result<Page<TenantVO>> page(
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "1") int pageNum,
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize, @RequestParam(defaultValue = "10") int pageSize,
@Parameter(description = "关键词(租户名/编码/联系人模糊查询)") @RequestParam(required = false) String keyword) { @RequestParam(required = false) String keyword) {
return Result.success(tenantService.pageTenants(pageNum, pageSize, keyword)); return Result.success(tenantService.pageTenants(pageNum, pageSize, keyword));
} }
@Operation(summary = "删除租户")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public Result<Boolean> delete(@Parameter(description = "租户ID") @PathVariable String id) { public Result<Boolean> delete(@PathVariable Long id) {
return Result.success(tenantService.deleteTenant(id)); return Result.success(tenantService.deleteTenant(id));
} }
@Operation(summary = "更新租户状态")
@PutMapping("/{id}/status") @PutMapping("/{id}/status")
public Result<Boolean> updateStatus(@Parameter(description = "租户ID") @PathVariable String id, @Parameter(description = "状态0禁用 1启用 2过期") @RequestParam Integer status) { public Result<Boolean> updateStatus(@PathVariable Long id, @RequestParam Integer status) {
return Result.success(tenantService.updateStatus(id, status)); return Result.success(tenantService.updateStatus(id, status));
} }
} }

View File

@ -5,16 +5,12 @@ import com.fundplatform.common.core.Result;
import com.fundplatform.sys.dto.UserDTO; import com.fundplatform.sys.dto.UserDTO;
import com.fundplatform.sys.service.UserService; import com.fundplatform.sys.service.UserService;
import com.fundplatform.sys.vo.UserVO; import com.fundplatform.sys.vo.UserVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
/** /**
* 用户管理Controller * 用户管理Controller
*/ */
@Tag(name = "用户管理", description = "用户的增删改查及状态管理")
@RestController @RestController
@RequestMapping("/api/v1/sys/user") @RequestMapping("/api/v1/sys/user")
public class UserController { public class UserController {
@ -28,17 +24,15 @@ public class UserController {
/** /**
* 创建用户 * 创建用户
*/ */
@Operation(summary = "创建用户")
@PostMapping @PostMapping
public Result<String> create(@Valid @RequestBody UserDTO dto) { public Result<Long> create(@Valid @RequestBody UserDTO dto) {
String userId = userService.createUser(dto); Long userId = userService.createUser(dto);
return Result.success(userId); return Result.success(userId);
} }
/** /**
* 更新用户 * 更新用户
*/ */
@Operation(summary = "更新用户信息")
@PutMapping @PutMapping
public Result<Boolean> update(@Valid @RequestBody UserDTO dto) { public Result<Boolean> update(@Valid @RequestBody UserDTO dto) {
boolean result = userService.updateUser(dto); boolean result = userService.updateUser(dto);
@ -48,9 +42,8 @@ public class UserController {
/** /**
* 根据ID查询用户 * 根据ID查询用户
*/ */
@Operation(summary = "根据ID查询用户")
@GetMapping("/{id}") @GetMapping("/{id}")
public Result<UserVO> getById(@Parameter(description = "用户ID") @PathVariable String id) { public Result<UserVO> getById(@PathVariable Long id) {
UserVO vo = userService.getUserById(id); UserVO vo = userService.getUserById(id);
return Result.success(vo); return Result.success(vo);
} }
@ -58,14 +51,13 @@ public class UserController {
/** /**
* 分页查询用户 * 分页查询用户
*/ */
@Operation(summary = "分页查询用户")
@GetMapping("/page") @GetMapping("/page")
public Result<Page<UserVO>> page( public Result<Page<UserVO>> page(
@Parameter(description = "页码从1开始") @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "1") int pageNum,
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize, @RequestParam(defaultValue = "10") int pageSize,
@Parameter(description = "用户名(模糊查询)") @RequestParam(required = false) String username, @RequestParam(required = false) String username,
@Parameter(description = "状态0禁用 1启用") @RequestParam(required = false) Integer status, @RequestParam(required = false) Integer status,
@Parameter(description = "部门ID") @RequestParam(required = false) String deptId) { @RequestParam(required = false) Long deptId) {
Page<UserVO> page = userService.pageUsers(pageNum, pageSize, username, status, deptId); Page<UserVO> page = userService.pageUsers(pageNum, pageSize, username, status, deptId);
return Result.success(page); return Result.success(page);
} }
@ -73,9 +65,8 @@ public class UserController {
/** /**
* 删除用户 * 删除用户
*/ */
@Operation(summary = "删除用户(逻辑删除)")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public Result<Boolean> delete(@Parameter(description = "用户ID") @PathVariable String id) { public Result<Boolean> delete(@PathVariable Long id) {
boolean result = userService.deleteUser(id); boolean result = userService.deleteUser(id);
return Result.success(result); return Result.success(result);
} }
@ -83,9 +74,8 @@ public class UserController {
/** /**
* 批量删除用户 * 批量删除用户
*/ */
@Operation(summary = "批量删除用户")
@DeleteMapping("/batch") @DeleteMapping("/batch")
public Result<Boolean> batchDelete(@RequestBody String[] ids) { public Result<Boolean> batchDelete(@RequestBody Long[] ids) {
boolean result = userService.batchDeleteUsers(ids); boolean result = userService.batchDeleteUsers(ids);
return Result.success(result); return Result.success(result);
} }
@ -93,9 +83,8 @@ public class UserController {
/** /**
* 重置密码 * 重置密码
*/ */
@Operation(summary = "重置用户密码", description = "将用户密码重置为系统默认密码")
@PutMapping("/{id}/reset-password") @PutMapping("/{id}/reset-password")
public Result<Boolean> resetPassword(@Parameter(description = "用户ID") @PathVariable String id) { public Result<Boolean> resetPassword(@PathVariable Long id) {
boolean result = userService.resetPassword(id); boolean result = userService.resetPassword(id);
return Result.success(result); return Result.success(result);
} }
@ -103,9 +92,8 @@ public class UserController {
/** /**
* 更新用户状态 * 更新用户状态
*/ */
@Operation(summary = "更新用户状态")
@PutMapping("/{id}/status") @PutMapping("/{id}/status")
public Result<Boolean> updateStatus(@Parameter(description = "用户ID") @PathVariable String id, @Parameter(description = "状态0禁用 1启用") @RequestParam Integer status) { public Result<Boolean> updateStatus(@PathVariable Long id, @RequestParam Integer status) {
boolean result = userService.updateStatus(id, status); boolean result = userService.updateStatus(id, status);
return Result.success(result); return Result.success(result);
} }

View File

@ -1,5 +1,6 @@
package com.fundplatform.sys.data.entity; package com.fundplatform.sys.data.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
@ -11,10 +12,10 @@ import java.time.LocalDateTime;
@TableName("sys_operation_log") @TableName("sys_operation_log")
public class OperationLog { public class OperationLog {
@TableId @TableId(type = IdType.AUTO)
private String logId; private Long logId;
private String userId; private Long userId;
private String username; private String username;
@ -36,19 +37,19 @@ public class OperationLog {
private String errorMsg; private String errorMsg;
public String getLogId() { public Long getLogId() {
return logId; return logId;
} }
public void setLogId(String logId) { public void setLogId(Long logId) {
this.logId = logId; this.logId = logId;
} }
public String getUserId() { public Long getUserId() {
return userId; return userId;
} }
public void setUserId(String userId) { public void setUserId(Long userId) {
this.userId = userId; this.userId = userId;
} }

View File

@ -9,7 +9,7 @@ import com.fundplatform.common.core.BaseEntity;
@TableName("sys_dept") @TableName("sys_dept")
public class SysDept extends BaseEntity { public class SysDept extends BaseEntity {
private String parentId; private Long parentId;
private String deptCode; private String deptCode;
private String deptName; private String deptName;
private String deptLeader; private String deptLeader;
@ -18,11 +18,11 @@ public class SysDept extends BaseEntity {
private Integer sortOrder; private Integer sortOrder;
private Integer status; private Integer status;
public String getParentId() { public Long getParentId() {
return parentId; return parentId;
} }
public void setParentId(String parentId) { public void setParentId(Long parentId) {
this.parentId = parentId; this.parentId = parentId;
} }

View File

@ -9,7 +9,7 @@ import com.fundplatform.common.core.BaseEntity;
@TableName("sys_menu") @TableName("sys_menu")
public class SysMenu extends BaseEntity { public class SysMenu extends BaseEntity {
private String parentId; private Long parentId;
private String menuName; private String menuName;
private Integer menuType; private Integer menuType;
private String menuPath; private String menuPath;
@ -20,11 +20,11 @@ public class SysMenu extends BaseEntity {
private Integer visible; private Integer visible;
private Integer status; private Integer status;
public String getParentId() { public Long getParentId() {
return parentId; return parentId;
} }
public void setParentId(String parentId) { public void setParentId(Long parentId) {
this.parentId = parentId; this.parentId = parentId;
} }

View File

@ -14,13 +14,13 @@ public class SysTenant extends BaseEntity {
/** 租户表不需要tenant_id字段 */ /** 租户表不需要tenant_id字段 */
@TableField(exist = false) @TableField(exist = false)
private String tenantId; private Long tenantId;
public String getTenantId() { public Long getTenantId() {
return tenantId; return tenantId;
} }
public void setTenantId(String tenantId) { public void setTenantId(Long tenantId) {
this.tenantId = tenantId; this.tenantId = tenantId;
} }

View File

@ -14,7 +14,7 @@ public class SysUser extends BaseEntity {
private String realName; private String realName;
private String phone; private String phone;
private String email; private String email;
private String deptId; private Long deptId;
private Integer status; private Integer status;
private String avatar; private String avatar;
@ -59,11 +59,11 @@ public class SysUser extends BaseEntity {
this.email = email; this.email = email;
} }
public String getDeptId() { public Long getDeptId() {
return deptId; return deptId;
} }
public void setDeptId(String deptId) { public void setDeptId(Long deptId) {
this.deptId = deptId; this.deptId = deptId;
} }

View File

@ -7,7 +7,7 @@ import jakarta.validation.constraints.NotBlank;
*/ */
public class ConfigDTO { public class ConfigDTO {
private String id; private Long id;
@NotBlank(message = "参数键不能为空") @NotBlank(message = "参数键不能为空")
private String configKey; private String configKey;
@ -28,11 +28,11 @@ public class ConfigDTO {
private Integer sortOrder; private Integer sortOrder;
public String getId() { public Long getId() {
return id; return id;
} }
public void setId(String id) { public void setId(Long id) {
this.id = id; this.id = id;
} }

View File

@ -8,9 +8,9 @@ import jakarta.validation.constraints.Size;
*/ */
public class DeptDTO { public class DeptDTO {
private String id; private Long id;
private String parentId; private Long parentId;
@NotBlank(message = "部门编码不能为空") @NotBlank(message = "部门编码不能为空")
@Size(max = 50, message = "部门编码不能超过50个字符") @Size(max = 50, message = "部门编码不能超过50个字符")
@ -33,19 +33,19 @@ public class DeptDTO {
private String remark; private String remark;
public String getId() { public Long getId() {
return id; return id;
} }
public void setId(String id) { public void setId(Long id) {
this.id = id; this.id = id;
} }
public String getParentId() { public Long getParentId() {
return parentId; return parentId;
} }
public void setParentId(String parentId) { public void setParentId(Long parentId) {
this.parentId = parentId; this.parentId = parentId;
} }

View File

@ -1,7 +1,6 @@
package com.fundplatform.sys.dto; package com.fundplatform.sys.dto;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/** /**
* 登录请求DTO * 登录请求DTO
@ -14,12 +13,6 @@ public class LoginRequestDTO {
@NotBlank(message = "密码不能为空") @NotBlank(message = "密码不能为空")
private String password; private String password;
/**
* 租户ID登录时必须指定以确保用户名在正确租户范围内唯一匹配防止跨租户登录混乱
*/
@NotNull(message = "租户ID不能为空")
private Long tenantId;
public String getUsername() { public String getUsername() {
return username; return username;
} }
@ -35,12 +28,4 @@ public class LoginRequestDTO {
public void setPassword(String password) { public void setPassword(String password) {
this.password = password; this.password = password;
} }
public Long getTenantId() {
return tenantId;
}
public void setTenantId(Long tenantId) {
this.tenantId = tenantId;
}
} }

View File

@ -8,9 +8,9 @@ import jakarta.validation.constraints.Size;
*/ */
public class MenuDTO { public class MenuDTO {
private String id; private Long id;
private String parentId; private Long parentId;
@NotBlank(message = "菜单名称不能为空") @NotBlank(message = "菜单名称不能为空")
@Size(max = 50, message = "菜单名称不能超过50个字符") @Size(max = 50, message = "菜单名称不能超过50个字符")
@ -38,19 +38,19 @@ public class MenuDTO {
private String remark; private String remark;
public String getId() { public Long getId() {
return id; return id;
} }
public void setId(String id) { public void setId(Long id) {
this.id = id; this.id = id;
} }
public String getParentId() { public Long getParentId() {
return parentId; return parentId;
} }
public void setParentId(String parentId) { public void setParentId(Long parentId) {
this.parentId = parentId; this.parentId = parentId;
} }

View File

@ -8,7 +8,7 @@ import jakarta.validation.constraints.Size;
*/ */
public class RoleDTO { public class RoleDTO {
private String id; private Long id;
@NotBlank(message = "角色编码不能为空") @NotBlank(message = "角色编码不能为空")
@Size(max = 50, message = "角色编码不能超过50个字符") @Size(max = 50, message = "角色编码不能超过50个字符")
@ -26,11 +26,11 @@ public class RoleDTO {
private String remark; private String remark;
public String getId() { public Long getId() {
return id; return id;
} }
public void setId(String id) { public void setId(Long id) {
this.id = id; this.id = id;
} }

View File

@ -10,7 +10,7 @@ import java.time.LocalDateTime;
*/ */
public class TenantDTO { public class TenantDTO {
private String id; private Long id;
@NotBlank(message = "租户编码不能为空") @NotBlank(message = "租户编码不能为空")
@Size(max = 50, message = "租户编码长度不能超过50") @Size(max = 50, message = "租户编码长度不能超过50")
@ -41,11 +41,11 @@ public class TenantDTO {
@Size(max = 500, message = "备注长度不能超过500") @Size(max = 500, message = "备注长度不能超过500")
private String remark; private String remark;
public String getId() { public Long getId() {
return id; return id;
} }
public void setId(String id) { public void setId(Long id) {
this.id = id; this.id = id;
} }

View File

@ -9,7 +9,7 @@ import jakarta.validation.constraints.Size;
*/ */
public class UserDTO { public class UserDTO {
private String id; private Long id;
@NotBlank(message = "用户名不能为空") @NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 50, message = "用户名长度必须在3-50个字符之间") @Size(min = 3, max = 50, message = "用户名长度必须在3-50个字符之间")
@ -28,7 +28,7 @@ public class UserDTO {
@Pattern(regexp = "^$|^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", message = "邮箱格式不正确") @Pattern(regexp = "^$|^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", message = "邮箱格式不正确")
private String email; private String email;
private String deptId; private Long deptId;
private Integer status; private Integer status;
@ -36,11 +36,11 @@ public class UserDTO {
private String remark; private String remark;
public String getId() { public Long getId() {
return id; return id;
} }
public void setId(String id) { public void setId(Long id) {
this.id = id; this.id = id;
} }
@ -84,11 +84,11 @@ public class UserDTO {
this.email = email; this.email = email;
} }
public String getDeptId() { public Long getDeptId() {
return deptId; return deptId;
} }
public void setDeptId(String deptId) { public void setDeptId(Long deptId) {
this.deptId = deptId; this.deptId = deptId;
} }

View File

@ -17,15 +17,15 @@ public interface AuthService {
/** /**
* 用户登出 * 用户登出
*/ */
void logout(String userId); void logout(Long userId);
/** /**
* 刷新Token * 刷新Token
*/ */
LoginVO refreshToken(String userId); LoginVO refreshToken(Long userId);
/** /**
* 获取当前用户信息 * 获取当前用户信息
*/ */
UserVO getUserInfo(String userId); UserVO getUserInfo(Long userId);
} }

View File

@ -15,7 +15,7 @@ public interface ConfigService {
/** /**
* 创建参数 * 创建参数
*/ */
String createConfig(ConfigDTO dto); Long createConfig(ConfigDTO dto);
/** /**
* 更新参数 * 更新参数
@ -25,7 +25,7 @@ public interface ConfigService {
/** /**
* 根据ID查询参数 * 根据ID查询参数
*/ */
ConfigVO getConfigById(String id); ConfigVO getConfigById(Long id);
/** /**
* 根据参数键查询值 * 根据参数键查询值
@ -50,7 +50,7 @@ public interface ConfigService {
/** /**
* 删除参数 * 删除参数
*/ */
boolean deleteConfig(String id); boolean deleteConfig(Long id);
/** /**
* 批量更新参数值 * 批量更新参数值

View File

@ -10,17 +10,17 @@ import java.util.List;
*/ */
public interface DeptService { public interface DeptService {
String createDept(DeptDTO dto); Long createDept(DeptDTO dto);
boolean updateDept(DeptDTO dto); boolean updateDept(DeptDTO dto);
DeptVO getDeptById(String id); DeptVO getDeptById(Long id);
List<DeptVO> getDeptTree(); List<DeptVO> getDeptTree();
List<DeptVO> listAllDepts(); List<DeptVO> listAllDepts();
boolean deleteDept(String id); boolean deleteDept(Long id);
boolean updateStatus(String id, Integer status); boolean updateStatus(Long id, Integer status);
} }

View File

@ -13,7 +13,7 @@ public interface MenuService {
/** /**
* 创建菜单 * 创建菜单
*/ */
String createMenu(MenuDTO dto); Long createMenu(MenuDTO dto);
/** /**
* 更新菜单 * 更新菜单
@ -23,7 +23,7 @@ public interface MenuService {
/** /**
* 根据ID查询菜单 * 根据ID查询菜单
*/ */
MenuVO getMenuById(String id); MenuVO getMenuById(Long id);
/** /**
* 查询菜单树 * 查询菜单树
@ -33,15 +33,15 @@ public interface MenuService {
/** /**
* 查询用户菜单树 * 查询用户菜单树
*/ */
List<MenuVO> getUserMenuTree(String userId); List<MenuVO> getUserMenuTree(Long userId);
/** /**
* 删除菜单 * 删除菜单
*/ */
boolean deleteMenu(String id); boolean deleteMenu(Long id);
/** /**
* 获取用户权限标识列表 * 获取用户权限标识列表
*/ */
List<String> getUserPermissions(String userId); List<String> getUserPermissions(Long userId);
} }

View File

@ -14,7 +14,7 @@ public interface RoleService {
/** /**
* 创建角色 * 创建角色
*/ */
String createRole(RoleDTO dto); Long createRole(RoleDTO dto);
/** /**
* 更新角色 * 更新角色
@ -24,7 +24,7 @@ public interface RoleService {
/** /**
* 根据ID查询角色 * 根据ID查询角色
*/ */
RoleVO getRoleById(String id); RoleVO getRoleById(Long id);
/** /**
* 分页查询角色 * 分页查询角色
@ -39,20 +39,20 @@ public interface RoleService {
/** /**
* 删除角色 * 删除角色
*/ */
boolean deleteRole(String id); boolean deleteRole(Long id);
/** /**
* 更新角色状态 * 更新角色状态
*/ */
boolean updateStatus(String id, Integer status); boolean updateStatus(Long id, Integer status);
/** /**
* 获取角色菜单ID列表 * 获取角色菜单ID列表
*/ */
List<String> getRoleMenus(String roleId); List<Long> getRoleMenus(Long roleId);
/** /**
* 分配菜单权限 * 分配菜单权限
*/ */
boolean assignMenus(String roleId, List<String> menuIds); boolean assignMenus(Long roleId, List<Long> menuIds);
} }

View File

@ -9,15 +9,15 @@ import com.fundplatform.sys.vo.TenantVO;
*/ */
public interface TenantService { public interface TenantService {
String createTenant(TenantDTO dto); Long createTenant(TenantDTO dto);
boolean updateTenant(TenantDTO dto); boolean updateTenant(TenantDTO dto);
TenantVO getTenantById(String id); TenantVO getTenantById(Long id);
Page<TenantVO> pageTenants(int pageNum, int pageSize, String keyword); Page<TenantVO> pageTenants(int pageNum, int pageSize, String keyword);
boolean deleteTenant(String id); boolean deleteTenant(Long id);
boolean updateStatus(String id, Integer status); boolean updateStatus(Long id, Integer status);
} }

View File

@ -17,7 +17,7 @@ public interface UserService {
* @param dto 用户DTO * @param dto 用户DTO
* @return 用户ID * @return 用户ID
*/ */
String createUser(UserDTO dto); Long createUser(UserDTO dto);
/** /**
* 更新用户 * 更新用户
@ -33,7 +33,7 @@ public interface UserService {
* @param id 用户ID * @param id 用户ID
* @return 用户VO * @return 用户VO
*/ */
UserVO getUserById(String id); UserVO getUserById(Long id);
/** /**
* 分页查询用户 * 分页查询用户
@ -45,7 +45,7 @@ public interface UserService {
* @param deptId 部门ID * @param deptId 部门ID
* @return 分页数据 * @return 分页数据
*/ */
Page<UserVO> pageUsers(int pageNum, int pageSize, String username, Integer status, String deptId); Page<UserVO> pageUsers(int pageNum, int pageSize, String username, Integer status, Long deptId);
/** /**
* 删除用户 * 删除用户
@ -53,7 +53,7 @@ public interface UserService {
* @param id 用户ID * @param id 用户ID
* @return 是否成功 * @return 是否成功
*/ */
boolean deleteUser(String id); boolean deleteUser(Long id);
/** /**
* 批量删除用户 * 批量删除用户
@ -61,7 +61,7 @@ public interface UserService {
* @param ids 用户ID列表 * @param ids 用户ID列表
* @return 是否成功 * @return 是否成功
*/ */
boolean batchDeleteUsers(String[] ids); boolean batchDeleteUsers(Long[] ids);
/** /**
* 重置密码 * 重置密码
@ -69,7 +69,7 @@ public interface UserService {
* @param id 用户ID * @param id 用户ID
* @return 是否成功 * @return 是否成功
*/ */
boolean resetPassword(String id); boolean resetPassword(Long id);
/** /**
* 更新用户状态 * 更新用户状态
@ -78,7 +78,7 @@ public interface UserService {
* @param status 状态 * @param status 状态
* @return 是否成功 * @return 是否成功
*/ */
boolean updateStatus(String id, Integer status); boolean updateStatus(Long id, Integer status);
/** /**
* 更新个人信息 * 更新个人信息
@ -87,7 +87,7 @@ public interface UserService {
* @param dto 个人信息DTO * @param dto 个人信息DTO
* @return 是否成功 * @return 是否成功
*/ */
boolean updateProfile(String userId, ProfileDTO dto); boolean updateProfile(Long userId, ProfileDTO dto);
/** /**
* 修改密码 * 修改密码
@ -96,5 +96,5 @@ public interface UserService {
* @param dto 密码DTO * @param dto 密码DTO
* @return 是否成功 * @return 是否成功
*/ */
boolean updatePassword(String userId, PasswordDTO dto); boolean updatePassword(Long userId, PasswordDTO dto);
} }

View File

@ -2,7 +2,7 @@ package com.fundplatform.sys.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fundplatform.common.auth.TokenService; import com.fundplatform.common.auth.TokenService;
import com.fundplatform.common.mybatis.TenantIgnoreHelper; import com.fundplatform.common.util.Md5Util;
import com.fundplatform.sys.data.entity.SysUser; import com.fundplatform.sys.data.entity.SysUser;
import com.fundplatform.sys.data.service.SysUserDataService; import com.fundplatform.sys.data.service.SysUserDataService;
import com.fundplatform.sys.dto.LoginRequestDTO; import com.fundplatform.sys.dto.LoginRequestDTO;
@ -29,78 +29,72 @@ public class AuthServiceImpl implements AuthService {
@Override @Override
public LoginVO login(LoginRequestDTO request) { public LoginVO login(LoginRequestDTO request) {
// 安全修复登录时尚未建立租户上下文白名单路径不经过 TokenAuthFilter // 查询用户
// 必须使用 TenantIgnoreHelper 跳过 MyBatis-Plus 自动租户过滤 LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
// 同时显式在查询条件中加入 tenantId确保只在指定租户范围内匹配用户名 wrapper.eq(SysUser::getUsername, request.getUsername());
// 防止不同租户的同名用户发生认证混乱 wrapper.eq(SysUser::getDeleted, 0);
SysUser user = TenantIgnoreHelper.ignore(() -> { SysUser user = userDataService.getOne(wrapper);
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getUsername, request.getUsername());
wrapper.eq(SysUser::getTenantId, request.getTenantId());
wrapper.eq(SysUser::getDeleted, 0);
return userDataService.getOne(wrapper);
});
if (user == null) { if (user == null) {
log.error("登录失败 - 用户不存在username={}, tenantId={}", request.getUsername(), request.getTenantId()); log.error("登录失败 - 用户不存在username={}", request.getUsername());
throw new RuntimeException("用户名或密码错误"); throw new RuntimeException("用户名或密码错误");
} }
// 打印接收到的密码和数据库存储的密码用于调试 // 打印接收到的密码和数据库存储的密码用于调试
log.info("登录验证 - 前端传来的 MD5 密码:{}, 数据库存储的 MD5 密码:{}", request.getPassword(), user.getPassword()); log.info("登录验证 - 前端传来的 MD5 密码:{}, 数据库存储的 MD5 密码:{}", request.getPassword(), user.getPassword());
// 直接比对 MD5 前端已加密数据库也是 MD5无需再次加密 // 直接比对 MD5 前端已加密数据库也是 MD5无需再次加密
if (!request.getPassword().equals(user.getPassword())) { if (!request.getPassword().equals(user.getPassword())) {
log.error("登录失败 - 密码错误username={}", request.getUsername()); log.error("登录失败 - 密码错误username={}", request.getUsername());
throw new RuntimeException("用户名或密码错误"); throw new RuntimeException("用户名或密码错误");
} }
// 检查用户状态 // 检查用户状态
if (user.getStatus() != 1) { if (user.getStatus() != 1) {
throw new RuntimeException("用户已被禁用"); throw new RuntimeException("用户已被禁用");
} }
// 使用 UUID + Redis 生成 Token // 使用 UUID + Redis 生成 Token
String token = tokenService.generateToken(user.getId(), user.getUsername(), user.getTenantId()); String token = tokenService.generateToken(user.getId(), user.getUsername(), user.getTenantId());
log.info("登录成功userId={}, username={}, tenantId={}", user.getId(), user.getUsername(), user.getTenantId()); log.info("登录成功userId={}, username={}, tenantId={}", user.getId(), user.getUsername(), user.getTenantId());
// 返回登录信息 // 返回登录信息
return new LoginVO(user.getId(), user.getUsername(), token, user.getTenantId()); return new LoginVO(user.getId(), user.getUsername(), token, user.getTenantId());
} }
@Override @Override
public void logout(String userId) { public void logout(Long userId) {
// 清除用户所有Token强制登出所有设备 // 清除用户所有Token强制登出所有设备
// 如果只需要登出当前设备需要从前端传递token // 如果只需要登出当前设备需要从前端传递token
tokenService.deleteAllUserTokens(userId); tokenService.deleteAllUserTokens(userId);
} }
@Override @Override
public LoginVO refreshToken(String userId) { public LoginVO refreshToken(Long userId) {
SysUser user = userDataService.getById(userId); SysUser user = userDataService.getById(userId);
if (user == null) { if (user == null) {
throw new RuntimeException("用户不存在"); throw new RuntimeException("用户不存在");
} }
// 检查用户状态 // 检查用户状态
if (user.getStatus() != 1) { if (user.getStatus() != 1) {
throw new RuntimeException("用户已被禁用"); throw new RuntimeException("用户已被禁用");
} }
// 生成新Token // 生成新Token
String token = tokenService.generateToken(user.getId(), user.getUsername(), user.getTenantId()); String token = tokenService.generateToken(user.getId(), user.getUsername(), user.getTenantId());
return new LoginVO(user.getId(), user.getUsername(), token, user.getTenantId()); return new LoginVO(user.getId(), user.getUsername(), token, user.getTenantId());
} }
@Override @Override
public UserVO getUserInfo(String userId) { public UserVO getUserInfo(Long userId) {
SysUser user = userDataService.getById(userId); SysUser user = userDataService.getById(userId);
if (user == null) { if (user == null) {
throw new RuntimeException("用户不存在"); throw new RuntimeException("用户不存在");
} }
UserVO vo = new UserVO(); UserVO vo = new UserVO();
vo.setId(user.getId()); vo.setId(user.getId());
vo.setUsername(user.getUsername()); vo.setUsername(user.getUsername());
@ -113,7 +107,7 @@ public class AuthServiceImpl implements AuthService {
vo.setTenantId(user.getTenantId()); vo.setTenantId(user.getTenantId());
vo.setCreatedTime(user.getCreatedTime()); vo.setCreatedTime(user.getCreatedTime());
vo.setUpdatedTime(user.getUpdatedTime()); vo.setUpdatedTime(user.getUpdatedTime());
return vo; return vo;
} }
} }

View File

@ -33,7 +33,7 @@ public class ConfigServiceImpl implements ConfigService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public String createConfig(ConfigDTO dto) { public Long createConfig(ConfigDTO dto) {
// 检查key是否已存在 // 检查key是否已存在
LambdaQueryWrapper<SysConfig> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SysConfig> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysConfig::getConfigKey, dto.getConfigKey()); wrapper.eq(SysConfig::getConfigKey, dto.getConfigKey());
@ -87,7 +87,7 @@ public class ConfigServiceImpl implements ConfigService {
} }
@Override @Override
public ConfigVO getConfigById(String id) { public ConfigVO getConfigById(Long id) {
SysConfig config = configDataService.getById(id); SysConfig config = configDataService.getById(id);
if (config == null || config.getDeleted() == 1) { if (config == null || config.getDeleted() == 1) {
throw new RuntimeException("参数不存在"); throw new RuntimeException("参数不存在");
@ -157,7 +157,7 @@ public class ConfigServiceImpl implements ConfigService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean deleteConfig(String id) { public boolean deleteConfig(Long id) {
SysConfig existing = configDataService.getById(id); SysConfig existing = configDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) { if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("参数不存在"); throw new RuntimeException("参数不存在");

View File

@ -34,7 +34,7 @@ public class DeptServiceImpl implements DeptService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public String createDept(DeptDTO dto) { public Long createDept(DeptDTO dto) {
LambdaQueryWrapper<SysDept> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SysDept> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysDept::getDeptCode, dto.getDeptCode()); wrapper.eq(SysDept::getDeptCode, dto.getDeptCode());
wrapper.eq(SysDept::getDeleted, 0); wrapper.eq(SysDept::getDeleted, 0);
@ -43,7 +43,7 @@ public class DeptServiceImpl implements DeptService {
} }
SysDept dept = new SysDept(); SysDept dept = new SysDept();
dept.setParentId(dto.getParentId() != null ? dto.getParentId() : "0"); dept.setParentId(dto.getParentId() != null ? dto.getParentId() : 0L);
dept.setDeptCode(dto.getDeptCode()); dept.setDeptCode(dto.getDeptCode());
dept.setDeptName(dto.getDeptName()); dept.setDeptName(dto.getDeptName());
dept.setDeptLeader(dto.getDeptLeader()); dept.setDeptLeader(dto.getDeptLeader());
@ -88,7 +88,7 @@ public class DeptServiceImpl implements DeptService {
} }
@Override @Override
public DeptVO getDeptById(String id) { public DeptVO getDeptById(Long id) {
SysDept dept = deptDataService.getById(id); SysDept dept = deptDataService.getById(id);
if (dept == null || dept.getDeleted() == 1) { if (dept == null || dept.getDeleted() == 1) {
throw new RuntimeException("部门不存在"); throw new RuntimeException("部门不存在");
@ -104,7 +104,7 @@ public class DeptServiceImpl implements DeptService {
wrapper.orderByAsc(SysDept::getSortOrder); wrapper.orderByAsc(SysDept::getSortOrder);
List<SysDept> depts = deptDataService.list(wrapper); List<SysDept> depts = deptDataService.list(wrapper);
return buildDeptTree(depts, "0"); return buildDeptTree(depts, 0L);
} }
@Override @Override
@ -117,7 +117,7 @@ public class DeptServiceImpl implements DeptService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean deleteDept(String id) { public boolean deleteDept(Long id) {
LambdaQueryWrapper<SysDept> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SysDept> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysDept::getParentId, id); wrapper.eq(SysDept::getParentId, id);
wrapper.eq(SysDept::getDeleted, 0); wrapper.eq(SysDept::getDeleted, 0);
@ -136,7 +136,7 @@ public class DeptServiceImpl implements DeptService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean updateStatus(String id, Integer status) { public boolean updateStatus(Long id, Integer status) {
LambdaUpdateWrapper<SysDept> wrapper = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<SysDept> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(SysDept::getId, id); wrapper.eq(SysDept::getId, id);
wrapper.set(SysDept::getStatus, status); wrapper.set(SysDept::getStatus, status);
@ -146,10 +146,10 @@ public class DeptServiceImpl implements DeptService {
return result; return result;
} }
private List<DeptVO> buildDeptTree(List<SysDept> depts, String parentId) { private List<DeptVO> buildDeptTree(List<SysDept> depts, Long parentId) {
List<DeptVO> tree = new ArrayList<>(); List<DeptVO> tree = new ArrayList<>();
Map<String, List<SysDept>> deptMap = depts.stream() Map<Long, List<SysDept>> deptMap = depts.stream()
.collect(Collectors.groupingBy(SysDept::getParentId)); .collect(Collectors.groupingBy(SysDept::getParentId));
List<SysDept> rootDepts = deptMap.getOrDefault(parentId, new ArrayList<>()); List<SysDept> rootDepts = deptMap.getOrDefault(parentId, new ArrayList<>());

View File

@ -34,9 +34,9 @@ public class MenuServiceImpl implements MenuService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public String createMenu(MenuDTO dto) { public Long createMenu(MenuDTO dto) {
SysMenu menu = new SysMenu(); SysMenu menu = new SysMenu();
menu.setParentId(dto.getParentId() != null ? dto.getParentId() : "0"); menu.setParentId(dto.getParentId() != null ? dto.getParentId() : 0L);
menu.setMenuName(dto.getMenuName()); menu.setMenuName(dto.getMenuName());
menu.setMenuType(dto.getMenuType() != null ? dto.getMenuType() : 1); menu.setMenuType(dto.getMenuType() != null ? dto.getMenuType() : 1);
menu.setMenuPath(dto.getMenuPath()); menu.setMenuPath(dto.getMenuPath());
@ -86,7 +86,7 @@ public class MenuServiceImpl implements MenuService {
} }
@Override @Override
public MenuVO getMenuById(String id) { public MenuVO getMenuById(Long id) {
SysMenu menu = menuDataService.getById(id); SysMenu menu = menuDataService.getById(id);
if (menu == null || menu.getDeleted() == 1) { if (menu == null || menu.getDeleted() == 1) {
throw new RuntimeException("菜单不存在"); throw new RuntimeException("菜单不存在");
@ -102,18 +102,18 @@ public class MenuServiceImpl implements MenuService {
wrapper.orderByAsc(SysMenu::getSortOrder); wrapper.orderByAsc(SysMenu::getSortOrder);
List<SysMenu> menus = menuDataService.list(wrapper); List<SysMenu> menus = menuDataService.list(wrapper);
return buildMenuTree(menus, "0"); return buildMenuTree(menus, 0L);
} }
@Override @Override
public List<MenuVO> getUserMenuTree(String userId) { public List<MenuVO> getUserMenuTree(Long userId) {
// TODO: 根据用户角色查询菜单 // TODO: 根据用户角色查询菜单
return getMenuTree(); return getMenuTree();
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean deleteMenu(String id) { public boolean deleteMenu(Long id) {
// 检查是否有子菜单 // 检查是否有子菜单
LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysMenu::getParentId, id); wrapper.eq(SysMenu::getParentId, id);
@ -132,7 +132,7 @@ public class MenuServiceImpl implements MenuService {
} }
@Override @Override
public List<String> getUserPermissions(String userId) { public List<String> getUserPermissions(Long userId) {
// 查询所有启用的菜单/按钮 // 查询所有启用的菜单/按钮
LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysMenu::getDeleted, 0); wrapper.eq(SysMenu::getDeleted, 0);
@ -150,10 +150,10 @@ public class MenuServiceImpl implements MenuService {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private List<MenuVO> buildMenuTree(List<SysMenu> menus, String parentId) { private List<MenuVO> buildMenuTree(List<SysMenu> menus, Long parentId) {
List<MenuVO> tree = new ArrayList<>(); List<MenuVO> tree = new ArrayList<>();
Map<String, List<SysMenu>> menuMap = menus.stream() Map<Long, List<SysMenu>> menuMap = menus.stream()
.collect(Collectors.groupingBy(SysMenu::getParentId)); .collect(Collectors.groupingBy(SysMenu::getParentId));
List<SysMenu> rootMenus = menuMap.getOrDefault(parentId, new ArrayList<>()); List<SysMenu> rootMenus = menuMap.getOrDefault(parentId, new ArrayList<>());

View File

@ -33,7 +33,7 @@ public class RoleServiceImpl implements RoleService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public String createRole(RoleDTO dto) { public Long createRole(RoleDTO dto) {
// 检查角色编码是否存在 // 检查角色编码是否存在
LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysRole::getRoleCode, dto.getRoleCode()); wrapper.eq(SysRole::getRoleCode, dto.getRoleCode());
@ -82,7 +82,7 @@ public class RoleServiceImpl implements RoleService {
} }
@Override @Override
public RoleVO getRoleById(String id) { public RoleVO getRoleById(Long id) {
SysRole role = roleDataService.getById(id); SysRole role = roleDataService.getById(id);
if (role == null || role.getDeleted() == 1) { if (role == null || role.getDeleted() == 1) {
throw new RuntimeException("角色不存在"); throw new RuntimeException("角色不存在");
@ -122,7 +122,7 @@ public class RoleServiceImpl implements RoleService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean deleteRole(String id) { public boolean deleteRole(Long id) {
LambdaUpdateWrapper<SysRole> wrapper = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<SysRole> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(SysRole::getId, id); wrapper.eq(SysRole::getId, id);
wrapper.set(SysRole::getDeleted, 1); wrapper.set(SysRole::getDeleted, 1);
@ -134,7 +134,7 @@ public class RoleServiceImpl implements RoleService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean updateStatus(String id, Integer status) { public boolean updateStatus(Long id, Integer status) {
LambdaUpdateWrapper<SysRole> wrapper = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<SysRole> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(SysRole::getId, id); wrapper.eq(SysRole::getId, id);
wrapper.set(SysRole::getStatus, status); wrapper.set(SysRole::getStatus, status);
@ -145,7 +145,7 @@ public class RoleServiceImpl implements RoleService {
} }
@Override @Override
public List<String> getRoleMenus(String roleId) { public List<Long> getRoleMenus(Long roleId) {
// TODO: 从sys_role_menu表查询角色关联的菜单ID // TODO: 从sys_role_menu表查询角色关联的菜单ID
// 目前返回空列表 // 目前返回空列表
log.info("获取角色菜单: roleId={}", roleId); log.info("获取角色菜单: roleId={}", roleId);
@ -154,7 +154,7 @@ public class RoleServiceImpl implements RoleService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean assignMenus(String roleId, List<String> menuIds) { public boolean assignMenus(Long roleId, List<Long> menuIds) {
// TODO: 实现角色菜单关联 // TODO: 实现角色菜单关联
log.info("分配角色菜单: roleId={}, menuIds={}", roleId, menuIds); log.info("分配角色菜单: roleId={}, menuIds={}", roleId, menuIds);
return true; return true;

View File

@ -30,7 +30,7 @@ public class TenantServiceImpl implements TenantService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public String createTenant(TenantDTO dto) { public Long createTenant(TenantDTO dto) {
// 检查编码是否重复 // 检查编码是否重复
LambdaQueryWrapper<SysTenant> checkWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SysTenant> checkWrapper = new LambdaQueryWrapper<>();
checkWrapper.eq(SysTenant::getTenantCode, dto.getTenantCode()); checkWrapper.eq(SysTenant::getTenantCode, dto.getTenantCode());
@ -101,7 +101,7 @@ public class TenantServiceImpl implements TenantService {
} }
@Override @Override
public TenantVO getTenantById(String id) { public TenantVO getTenantById(Long id) {
SysTenant tenant = tenantDataService.getById(id); SysTenant tenant = tenantDataService.getById(id);
if (tenant == null || tenant.getDeleted() == 1) { if (tenant == null || tenant.getDeleted() == 1) {
throw new RuntimeException("租户不存在"); throw new RuntimeException("租户不存在");
@ -134,7 +134,7 @@ public class TenantServiceImpl implements TenantService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean deleteTenant(String id) { public boolean deleteTenant(Long id) {
SysTenant existing = tenantDataService.getById(id); SysTenant existing = tenantDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) { if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("租户不存在"); throw new RuntimeException("租户不存在");
@ -156,7 +156,7 @@ public class TenantServiceImpl implements TenantService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean updateStatus(String id, Integer status) { public boolean updateStatus(Long id, Integer status) {
SysTenant existing = tenantDataService.getById(id); SysTenant existing = tenantDataService.getById(id);
if (existing == null || existing.getDeleted() == 1) { if (existing == null || existing.getDeleted() == 1) {
throw new RuntimeException("租户不存在"); throw new RuntimeException("租户不存在");

View File

@ -38,7 +38,7 @@ public class UserServiceImpl implements UserService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public String createUser(UserDTO dto) { public Long createUser(UserDTO dto) {
// 检查用户名是否存在 // 检查用户名是否存在
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getUsername, dto.getUsername()); wrapper.eq(SysUser::getUsername, dto.getUsername());
@ -47,10 +47,13 @@ public class UserServiceImpl implements UserService {
throw new RuntimeException("用户名已存在"); throw new RuntimeException("用户名已存在");
} }
// 创建用户密码由前端 MD5 加密后传入直接存储 // 创建用户密码使用 MD5 加密
SysUser user = new SysUser(); SysUser user = new SysUser();
String rawPassword = dto.getPassword();
String md5Password = Md5Util.encrypt(rawPassword);
log.info("创建用户 - 原始密码:{}, MD5 加密后:{}", rawPassword, md5Password);
user.setUsername(dto.getUsername()); user.setUsername(dto.getUsername());
user.setPassword(dto.getPassword()); user.setPassword(md5Password);
user.setRealName(dto.getRealName()); user.setRealName(dto.getRealName());
user.setPhone(dto.getPhone()); user.setPhone(dto.getPhone());
user.setEmail(dto.getEmail()); user.setEmail(dto.getEmail());
@ -97,8 +100,10 @@ public class UserServiceImpl implements UserService {
user.setUsername(dto.getUsername()); user.setUsername(dto.getUsername());
} }
if (StringUtils.hasText(dto.getPassword())) { if (StringUtils.hasText(dto.getPassword())) {
// 密码由前端 MD5 加密后传入直接存储 String rawPassword = dto.getPassword();
user.setPassword(dto.getPassword()); String md5Password = Md5Util.encrypt(rawPassword);
log.info("更新用户密码 - 原始密码:{}, MD5 加密后:{}", rawPassword, md5Password);
user.setPassword(md5Password);
} }
user.setRealName(dto.getRealName()); user.setRealName(dto.getRealName());
user.setPhone(dto.getPhone()); user.setPhone(dto.getPhone());
@ -115,7 +120,7 @@ public class UserServiceImpl implements UserService {
} }
@Override @Override
public UserVO getUserById(String id) { public UserVO getUserById(Long id) {
SysUser user = userDataService.getById(id); SysUser user = userDataService.getById(id);
if (user == null || user.getDeleted() == 1) { if (user == null || user.getDeleted() == 1) {
throw new RuntimeException("用户不存在"); throw new RuntimeException("用户不存在");
@ -124,7 +129,7 @@ public class UserServiceImpl implements UserService {
} }
@Override @Override
public Page<UserVO> pageUsers(int pageNum, int pageSize, String username, Integer status, String deptId) { public Page<UserVO> pageUsers(int pageNum, int pageSize, String username, Integer status, Long deptId) {
Page<SysUser> page = new Page<>(pageNum, pageSize); Page<SysUser> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getDeleted, 0); wrapper.eq(SysUser::getDeleted, 0);
@ -150,7 +155,7 @@ public class UserServiceImpl implements UserService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean deleteUser(String id) { public boolean deleteUser(Long id) {
LambdaUpdateWrapper<SysUser> wrapper = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<SysUser> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(SysUser::getId, id); wrapper.eq(SysUser::getId, id);
wrapper.set(SysUser::getDeleted, 1); wrapper.set(SysUser::getDeleted, 1);
@ -162,7 +167,7 @@ public class UserServiceImpl implements UserService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean batchDeleteUsers(String[] ids) { public boolean batchDeleteUsers(Long[] ids) {
if (ids == null || ids.length == 0) { if (ids == null || ids.length == 0) {
return false; return false;
} }
@ -177,7 +182,7 @@ public class UserServiceImpl implements UserService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean resetPassword(String id) { public boolean resetPassword(Long id) {
String defaultPassword = "123456"; String defaultPassword = "123456";
String md5Password = Md5Util.encrypt(defaultPassword); String md5Password = Md5Util.encrypt(defaultPassword);
log.info("重置用户密码 - userId={}, 原始密码:{}, MD5: {}", id, defaultPassword, md5Password); log.info("重置用户密码 - userId={}, 原始密码:{}, MD5: {}", id, defaultPassword, md5Password);
@ -192,7 +197,7 @@ public class UserServiceImpl implements UserService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean updateStatus(String id, Integer status) { public boolean updateStatus(Long id, Integer status) {
LambdaUpdateWrapper<SysUser> wrapper = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<SysUser> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(SysUser::getId, id); wrapper.eq(SysUser::getId, id);
wrapper.set(SysUser::getStatus, status); wrapper.set(SysUser::getStatus, status);
@ -204,7 +209,7 @@ public class UserServiceImpl implements UserService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean updateProfile(String userId, ProfileDTO dto) { public boolean updateProfile(Long userId, ProfileDTO dto) {
SysUser user = userDataService.getById(userId); SysUser user = userDataService.getById(userId);
if (user == null || user.getDeleted() == 1) { if (user == null || user.getDeleted() == 1) {
throw new RuntimeException("用户不存在"); throw new RuntimeException("用户不存在");
@ -227,7 +232,7 @@ public class UserServiceImpl implements UserService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean updatePassword(String userId, PasswordDTO dto) { public boolean updatePassword(Long userId, PasswordDTO dto) {
// 验证新密码和确认密码一致 // 验证新密码和确认密码一致
if (!dto.getNewPassword().equals(dto.getConfirmPassword())) { if (!dto.getNewPassword().equals(dto.getConfirmPassword())) {
throw new RuntimeException("新密码和确认密码不一致"); throw new RuntimeException("新密码和确认密码不一致");
@ -238,15 +243,16 @@ public class UserServiceImpl implements UserService {
throw new RuntimeException("用户不存在"); throw new RuntimeException("用户不存在");
} }
// 验证旧密码前端已 MD5直接与数据库存储的 MD5 值比对 // 验证旧密码
if (!dto.getOldPassword().equals(user.getPassword())) { if (!Md5Util.matches(dto.getOldPassword(), user.getPassword())) {
log.error("修改密码失败 - 旧密码错误userId={}", userId); log.error("修改密码失败 - 旧密码错误userId={}", userId);
throw new RuntimeException("旧密码错误"); throw new RuntimeException("旧密码错误");
} }
// 更新密码前端已 MD5 加密直接存储 // 更新密码
String md5NewPassword = dto.getNewPassword(); String rawNewPassword = dto.getNewPassword();
log.info("修改用户密码 - userId={}", userId); String md5NewPassword = Md5Util.encrypt(rawNewPassword);
log.info("修改用户密码 - userId={}, 原始新密码:{}, MD5: {}", userId, rawNewPassword, md5NewPassword);
LambdaUpdateWrapper<SysUser> wrapper = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<SysUser> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(SysUser::getId, userId); wrapper.eq(SysUser::getId, userId);
wrapper.set(SysUser::getPassword, md5NewPassword); wrapper.set(SysUser::getPassword, md5NewPassword);

View File

@ -7,7 +7,7 @@ import java.time.LocalDateTime;
*/ */
public class ConfigVO { public class ConfigVO {
private String id; private Long id;
private String configKey; private String configKey;
private String configValue; private String configValue;
private String configType; private String configType;
@ -20,11 +20,11 @@ public class ConfigVO {
private LocalDateTime createdTime; private LocalDateTime createdTime;
private LocalDateTime updatedTime; private LocalDateTime updatedTime;
public String getId() { public Long getId() {
return id; return id;
} }
public void setId(String id) { public void setId(Long id) {
this.id = id; this.id = id;
} }

View File

@ -8,8 +8,8 @@ import java.util.List;
*/ */
public class DeptVO { public class DeptVO {
private String id; private Long id;
private String parentId; private Long parentId;
private String parentName; private String parentName;
private String deptCode; private String deptCode;
private String deptName; private String deptName;
@ -18,26 +18,26 @@ public class DeptVO {
private String email; private String email;
private Integer sortOrder; private Integer sortOrder;
private Integer status; private Integer status;
private String tenantId; private Long tenantId;
private LocalDateTime createdTime; private LocalDateTime createdTime;
private LocalDateTime updatedTime; private LocalDateTime updatedTime;
// 子部门 // 子部门
private List<DeptVO> children; private List<DeptVO> children;
public String getId() { public Long getId() {
return id; return id;
} }
public void setId(String id) { public void setId(Long id) {
this.id = id; this.id = id;
} }
public String getParentId() { public Long getParentId() {
return parentId; return parentId;
} }
public void setParentId(String parentId) { public void setParentId(Long parentId) {
this.parentId = parentId; this.parentId = parentId;
} }
@ -105,11 +105,11 @@ public class DeptVO {
this.status = status; this.status = status;
} }
public String getTenantId() { public Long getTenantId() {
return tenantId; return tenantId;
} }
public void setTenantId(String tenantId) { public void setTenantId(Long tenantId) {
this.tenantId = tenantId; this.tenantId = tenantId;
} }

Some files were not shown because too many files have changed in this diff Show More