Compare commits
No commits in common. "fffeaa48a533af6b7cc9799894aa7500bcaaba94" and "46e30c8b06adf47218807d19856dcd59c434f24a" have entirely different histories.
fffeaa48a5
...
46e30c8b06
33
AGENTS.md
33
AGENTS.md
@ -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
165
CLAUDE.md
@ -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,存储在 Redis(Key: `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 收集
|
||||
@ -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 中清理
|
||||
@ -1,9 +1,8 @@
|
||||
-- =============================================
|
||||
-- 资金服务平台 - 客户中心数据库初始化脚本
|
||||
-- Database: fund_cust
|
||||
-- Version: 2.0
|
||||
-- Version: 1.0
|
||||
-- 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;
|
||||
@ -14,8 +13,8 @@ USE fund_cust;
|
||||
-- 1. 客户表 (customer)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS customer (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '客户ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
customer_code VARCHAR(64) NOT NULL COMMENT '客户编码',
|
||||
customer_name VARCHAR(128) NOT NULL COMMENT '客户名称',
|
||||
contact VARCHAR(64) COMMENT '联系人',
|
||||
@ -24,9 +23,9 @@ CREATE TABLE IF NOT EXISTS customer (
|
||||
address VARCHAR(255) COMMENT '地址',
|
||||
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -40,18 +39,18 @@ CREATE TABLE IF NOT EXISTS customer (
|
||||
-- 2. 联系人表 (customer_contact)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS customer_contact (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
customer_id VARCHAR(32) NOT NULL COMMENT '客户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '联系人ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
customer_id BIGINT NOT NULL COMMENT '客户ID',
|
||||
contact_name VARCHAR(64) NOT NULL COMMENT '联系人姓名',
|
||||
phone VARCHAR(20) COMMENT '手机号',
|
||||
email VARCHAR(128) COMMENT '邮箱',
|
||||
position VARCHAR(64) COMMENT '职位',
|
||||
is_primary TINYINT NOT NULL DEFAULT 0 COMMENT '是否主要联系人: 0-否, 1-是',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -63,9 +62,9 @@ CREATE TABLE IF NOT EXISTS customer_contact (
|
||||
-- 初始化测试数据(租户ID=1)
|
||||
-- =============================================
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
-- =============================================
|
||||
-- 资金服务平台 - 支出管理数据库初始化脚本
|
||||
-- Database: fund_exp
|
||||
-- Version: 2.0
|
||||
-- Version: 1.0
|
||||
-- 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;
|
||||
@ -14,19 +13,19 @@ USE fund_exp;
|
||||
-- 1. 支出类型表 (expense_type)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS expense_type (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '支出类型ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
type_code VARCHAR(64) 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 '类型层级',
|
||||
sort_order INT DEFAULT 0 COMMENT '排序号',
|
||||
description VARCHAR(500) COMMENT '类型描述',
|
||||
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -40,34 +39,34 @@ CREATE TABLE IF NOT EXISTS expense_type (
|
||||
-- 2. 支出表 (fund_expense)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS fund_expense (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '支出ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
expense_no VARCHAR(64) NOT NULL COMMENT '支出单号',
|
||||
title VARCHAR(200) COMMENT '支出标题',
|
||||
amount DECIMAL(18,2) NOT NULL COMMENT '支出金额',
|
||||
currency VARCHAR(16) DEFAULT 'CNY' COMMENT '币种',
|
||||
expense_type VARCHAR(32) COMMENT '支出类型ID',
|
||||
expense_type BIGINT COMMENT '支出类型ID',
|
||||
payee_name VARCHAR(128) COMMENT '收款单位',
|
||||
payee_bank VARCHAR(128) COMMENT '收款银行',
|
||||
payee_account VARCHAR(64) COMMENT '收款账号',
|
||||
expense_date DATETIME COMMENT '支出日期',
|
||||
purpose VARCHAR(500) COMMENT '用途说明',
|
||||
request_id VARCHAR(32) COMMENT '关联用款申请ID',
|
||||
project_id VARCHAR(32) COMMENT '所属项目ID',
|
||||
customer_id VARCHAR(32) COMMENT '客户ID',
|
||||
request_id BIGINT COMMENT '关联用款申请ID',
|
||||
project_id BIGINT COMMENT '所属项目ID',
|
||||
customer_id BIGINT COMMENT '客户ID',
|
||||
pay_status INT DEFAULT 0 COMMENT '支付状态: 0-待支付, 1-已支付, 2-支付失败',
|
||||
pay_time DATETIME COMMENT '支付时间',
|
||||
pay_channel VARCHAR(32) COMMENT '支付渠道',
|
||||
pay_voucher VARCHAR(255) COMMENT '支付凭证',
|
||||
approval_status INT DEFAULT 0 COMMENT '审批状态',
|
||||
approver_id VARCHAR(32) COMMENT '审批人ID',
|
||||
approver_id BIGINT COMMENT '审批人ID',
|
||||
approval_time DATETIME COMMENT '审批时间',
|
||||
approval_comment VARCHAR(500) COMMENT '审批意见',
|
||||
attachments VARCHAR(1000) COMMENT '附件URL',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
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)
|
||||
VALUES
|
||||
('1', '1', 'LABOR', '人力成本', '0', 1, 1, 1, '1', NOW()),
|
||||
('2', '1', 'OFFICE', '办公费用', '0', 1, 2, 1, '1', NOW()),
|
||||
('3', '1', 'TRAVEL', '差旅费用', '0', 1, 3, 1, '1', NOW()),
|
||||
('4', '1', 'PURCHASE', '采购费用', '0', 1, 4, 1, '1', NOW()),
|
||||
('5', '1', 'OTHER', '其他费用', '0', 1, 5, 1, '1', NOW())
|
||||
(1, 1, 'LABOR', '人力成本', 0, 1, 1, 1, 1, NOW()),
|
||||
(2, 1, 'OFFICE', '办公费用', 0, 1, 2, 1, 1, NOW()),
|
||||
(3, 1, 'TRAVEL', '差旅费用', 0, 1, 3, 1, 1, NOW()),
|
||||
(4, 1, 'PURCHASE', '采购费用', 0, 1, 4, 1, 1, NOW()),
|
||||
(5, 1, 'OTHER', '其他费用', 0, 1, 5, 1, 1, NOW())
|
||||
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)
|
||||
VALUES
|
||||
('11', '1', 'SALARY', '工资', '1', 2, 1, 1, '1', NOW()),
|
||||
('12', '1', 'BONUS', '奖金', '1', 2, 2, 1, '1', NOW()),
|
||||
('13', '1', 'SOCIAL_INSURANCE', '社保', '1', 2, 3, 1, '1', NOW()),
|
||||
('21', '1', 'RENT', '房租', '2', 2, 1, 1, '1', NOW()),
|
||||
('22', '1', 'UTILITIES', '水电费', '2', 2, 2, 1, '1', NOW()),
|
||||
('23', '1', 'SUPPLIES', '办公用品', '2', 2, 3, 1, '1', NOW()),
|
||||
('31', '1', 'TRANSPORT', '交通费', '3', 2, 1, 1, '1', NOW()),
|
||||
('32', '1', 'ACCOMMODATION', '住宿费', '3', 2, 2, 1, '1', NOW()),
|
||||
('33', '1', 'MEALS', '餐饮费', '3', 2, 3, 1, '1', NOW()),
|
||||
('41', '1', 'EQUIPMENT', '设备采购', '4', 2, 1, 1, '1', NOW()),
|
||||
('42', '1', 'SOFTWARE', '软件采购', '4', 2, 2, 1, '1', NOW()),
|
||||
('43', '1', 'SERVICE', '服务采购', '4', 2, 3, 1, '1', NOW())
|
||||
(11, 1, 'SALARY', '工资', 1, 2, 1, 1, 1, NOW()),
|
||||
(12, 1, 'BONUS', '奖金', 1, 2, 2, 1, 1, NOW()),
|
||||
(13, 1, 'SOCIAL_INSURANCE', '社保', 1, 2, 3, 1, 1, NOW()),
|
||||
(21, 1, 'RENT', '房租', 2, 2, 1, 1, 1, NOW()),
|
||||
(22, 1, 'UTILITIES', '水电费', 2, 2, 2, 1, 1, NOW()),
|
||||
(23, 1, 'SUPPLIES', '办公用品', 2, 2, 3, 1, 1, NOW()),
|
||||
(31, 1, 'TRANSPORT', '交通费', 3, 2, 1, 1, 1, NOW()),
|
||||
(32, 1, 'ACCOMMODATION', '住宿费', 3, 2, 2, 1, 1, NOW()),
|
||||
(33, 1, 'MEALS', '餐饮费', 3, 2, 3, 1, 1, NOW()),
|
||||
(41, 1, 'EQUIPMENT', '设备采购', 4, 2, 1, 1, 1, NOW()),
|
||||
(42, 1, 'SOFTWARE', '软件采购', 4, 2, 2, 1, 1, NOW()),
|
||||
(43, 1, 'SERVICE', '服务采购', 4, 2, 3, 1, 1, NOW())
|
||||
ON DUPLICATE KEY UPDATE type_code=type_code;
|
||||
|
||||
-- =============================================
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
-- =============================================
|
||||
-- 资金服务平台 - 文件管理数据库初始化脚本
|
||||
-- Database: fund_file
|
||||
-- Version: 2.0
|
||||
-- Version: 1.0
|
||||
-- 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;
|
||||
@ -14,8 +13,8 @@ USE fund_file;
|
||||
-- 文件记录表 (file_record)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS `file_record` (
|
||||
`file_id` VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
`tenant_id` VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
`file_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '文件ID',
|
||||
`tenant_id` BIGINT NOT NULL COMMENT '租户ID',
|
||||
`file_name` VARCHAR(200) NOT NULL COMMENT '原始文件名',
|
||||
`file_path` VARCHAR(500) NOT NULL COMMENT '文件存储路径',
|
||||
`file_url` VARCHAR(500) COMMENT '文件访问URL',
|
||||
@ -25,13 +24,13 @@ CREATE TABLE IF NOT EXISTS `file_record` (
|
||||
`content_type` VARCHAR(100) COMMENT 'MIME类型',
|
||||
`md5` VARCHAR(32) COMMENT '文件MD5',
|
||||
`business_type` VARCHAR(50) COMMENT '业务类型(contract/receipt/expense/other)',
|
||||
`business_id` VARCHAR(32) COMMENT '关联业务ID',
|
||||
`business_id` BIGINT COMMENT '关联业务ID',
|
||||
`description` VARCHAR(500) COMMENT '文件描述',
|
||||
`download_count` INT NOT NULL DEFAULT 0 COMMENT '下载次数',
|
||||
`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 '创建时间',
|
||||
`updated_by` VARCHAR(32) COMMENT '更新人ID',
|
||||
`updated_by` BIGINT COMMENT '更新人ID',
|
||||
`updated_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0-未删除,1-已删除',
|
||||
PRIMARY KEY (`file_id`),
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
-- =============================================
|
||||
-- 资金服务平台 - 项目管理数据库初始化脚本
|
||||
-- Database: fund_proj
|
||||
-- Version: 2.0
|
||||
-- Version: 1.1
|
||||
-- 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;
|
||||
@ -14,11 +14,11 @@ USE fund_proj;
|
||||
-- 1. 项目表 (project)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS project (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '项目ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
project_code VARCHAR(64) 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 '项目类型',
|
||||
budget_amount DECIMAL(18,2) COMMENT '预算金额',
|
||||
start_date DATE COMMENT '开始日期',
|
||||
@ -26,9 +26,9 @@ CREATE TABLE IF NOT EXISTS project (
|
||||
project_manager VARCHAR(64) COMMENT '项目经理',
|
||||
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-已关闭, 1-进行中, 2-已完成',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -43,13 +43,13 @@ CREATE TABLE IF NOT EXISTS project (
|
||||
-- 2. 需求工单表 (requirement)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS requirement (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键,需求ID',
|
||||
tenant_id BIGINT NOT NULL COMMENT '租户ID',
|
||||
requirement_code VARCHAR(50) NOT NULL COMMENT '需求编号',
|
||||
requirement_name VARCHAR(200) NOT NULL COMMENT '需求名称',
|
||||
description TEXT COMMENT '需求描述',
|
||||
project_id VARCHAR(32) NOT NULL COMMENT '项目ID',
|
||||
customer_id VARCHAR(32) NOT NULL COMMENT '客户ID',
|
||||
project_id BIGINT NOT NULL COMMENT '项目ID',
|
||||
customer_id BIGINT NOT NULL COMMENT '客户ID',
|
||||
priority VARCHAR(20) DEFAULT 'normal' COMMENT '优先级:high-高,normal-中,low-低',
|
||||
estimated_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)',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
attachment_url VARCHAR(500) COMMENT '附件URL',
|
||||
created_by VARCHAR(32) COMMENT '创建人ID',
|
||||
created_by BIGINT COMMENT '创建人ID',
|
||||
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 '更新时间',
|
||||
deleted TINYINT DEFAULT 0 COMMENT '逻辑删除:0-未删除,1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -81,7 +81,7 @@ CREATE TABLE IF NOT EXISTS requirement (
|
||||
-- 初始化测试数据(租户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)
|
||||
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;
|
||||
|
||||
-- =============================================
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
-- =============================================
|
||||
-- 资金服务平台 - 收款管理数据库初始化脚本
|
||||
-- Database: fund_receipt
|
||||
-- Version: 2.0
|
||||
-- Version: 1.0
|
||||
-- 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;
|
||||
@ -14,12 +13,12 @@ USE fund_receipt;
|
||||
-- 1. 应收款表 (receivable)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS receivable (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '应收款ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
receivable_code VARCHAR(64) NOT NULL COMMENT '应收款编号',
|
||||
requirement_id VARCHAR(32) COMMENT '需求ID',
|
||||
project_id VARCHAR(32) NOT NULL COMMENT '项目ID',
|
||||
customer_id VARCHAR(32) NOT NULL COMMENT '客户ID',
|
||||
requirement_id BIGINT COMMENT '需求ID',
|
||||
project_id BIGINT NOT NULL COMMENT '项目ID',
|
||||
customer_id BIGINT NOT NULL COMMENT '客户ID',
|
||||
receivable_amount DECIMAL(18,2) NOT NULL COMMENT '应收款金额',
|
||||
received_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 '逾期天数',
|
||||
confirm_status INT DEFAULT 0 COMMENT '确认状态: 0-待确认, 1-已确认',
|
||||
confirm_time DATETIME COMMENT '确认时间',
|
||||
confirm_by VARCHAR(32) COMMENT '确认人ID',
|
||||
confirm_by BIGINT COMMENT '确认人ID',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -52,8 +51,8 @@ CREATE TABLE IF NOT EXISTS receivable (
|
||||
-- 2. 收款记录表 (fund_receipt)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS fund_receipt (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '收款记录ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
receipt_no VARCHAR(64) NOT NULL COMMENT '收款单号',
|
||||
title VARCHAR(200) COMMENT '收款标题',
|
||||
amount DECIMAL(18,2) NOT NULL COMMENT '收款金额',
|
||||
@ -64,21 +63,21 @@ CREATE TABLE IF NOT EXISTS fund_receipt (
|
||||
payer_account VARCHAR(64) COMMENT '付款账号',
|
||||
receipt_date DATETIME COMMENT '收款日期',
|
||||
purpose VARCHAR(500) COMMENT '用途说明',
|
||||
project_id VARCHAR(32) COMMENT '项目ID',
|
||||
customer_id VARCHAR(32) COMMENT '客户ID',
|
||||
receivable_id VARCHAR(32) COMMENT '应收款ID',
|
||||
project_id BIGINT COMMENT '项目ID',
|
||||
customer_id BIGINT COMMENT '客户ID',
|
||||
receivable_id BIGINT COMMENT '应收款ID',
|
||||
receipt_status INT DEFAULT 0 COMMENT '收款状态: 0-待确认, 1-已确认, 2-已核销',
|
||||
confirm_time DATETIME COMMENT '确认时间',
|
||||
confirm_by VARCHAR(32) COMMENT '确认人ID',
|
||||
confirm_by BIGINT COMMENT '确认人ID',
|
||||
write_off_time DATETIME COMMENT '核销时间',
|
||||
write_off_by VARCHAR(32) COMMENT '核销人ID',
|
||||
write_off_by BIGINT COMMENT '核销人ID',
|
||||
voucher VARCHAR(255) COMMENT '收款凭证',
|
||||
invoice_no VARCHAR(64) COMMENT '发票号',
|
||||
attachments VARCHAR(1000) COMMENT '附件URL',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -94,7 +93,7 @@ CREATE TABLE IF NOT EXISTS fund_receipt (
|
||||
-- 初始化测试数据(租户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)
|
||||
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;
|
||||
|
||||
-- =============================================
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
-- =============================================
|
||||
-- 资金服务平台 - 用款申请数据库初始化脚本
|
||||
-- Database: fund_req
|
||||
-- Version: 2.0
|
||||
-- Version: 1.0
|
||||
-- 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;
|
||||
@ -14,8 +13,8 @@ USE fund_req;
|
||||
-- 用款申请表 (fund_request)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS fund_request (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '申请ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
request_no VARCHAR(64) NOT NULL COMMENT '申请单号',
|
||||
title VARCHAR(200) 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_account VARCHAR(64) COMMENT '收款账号',
|
||||
purpose VARCHAR(500) COMMENT '用途说明',
|
||||
project_id VARCHAR(32) COMMENT '项目ID',
|
||||
customer_id VARCHAR(32) COMMENT '客户ID',
|
||||
project_id BIGINT COMMENT '项目ID',
|
||||
customer_id BIGINT COMMENT '客户ID',
|
||||
request_date DATETIME COMMENT '申请日期',
|
||||
expected_pay_date DATETIME COMMENT '期望付款日期',
|
||||
approval_status INT DEFAULT 0 COMMENT '审批状态: 0-待审批, 1-审批中, 2-审批通过, 3-审批拒绝, 4-已撤回',
|
||||
current_node INT COMMENT '当前审批节点',
|
||||
approver_id VARCHAR(32) COMMENT '审批人ID',
|
||||
approver_id BIGINT COMMENT '审批人ID',
|
||||
approval_time DATETIME COMMENT '审批时间',
|
||||
approval_comment VARCHAR(500) COMMENT '审批意见',
|
||||
attachments VARCHAR(1000) COMMENT '附件URL',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -50,7 +49,7 @@ CREATE TABLE IF NOT EXISTS fund_request (
|
||||
) 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)
|
||||
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;
|
||||
|
||||
-- =============================================
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
-- =============================================
|
||||
-- 资金服务平台 - 系统服务数据库初始化脚本
|
||||
-- Database: fund_sys
|
||||
-- Version: 2.0
|
||||
-- Version: 1.0
|
||||
-- Author: fundplatform team
|
||||
-- Created: 2026-02-17
|
||||
-- Updated: 2026-03-02 (主键类型改为VARCHAR雪花ID)
|
||||
-- =============================================
|
||||
|
||||
-- 创建数据库
|
||||
@ -16,20 +15,20 @@ USE fund_sys;
|
||||
-- 1. 用户表 (sys_user)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS sys_user (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '用户ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
username VARCHAR(64) NOT NULL COMMENT '用户名',
|
||||
password VARCHAR(128) NOT NULL COMMENT '密码 (MD5)',
|
||||
real_name VARCHAR(64) COMMENT '真实姓名',
|
||||
phone VARCHAR(20) 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-启用',
|
||||
avatar VARCHAR(255) COMMENT '头像URL',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -43,17 +42,17 @@ CREATE TABLE IF NOT EXISTS sys_user (
|
||||
-- 2. 角色表 (sys_role)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS sys_role (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '角色ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
role_code VARCHAR(64) NOT NULL COMMENT '角色编码',
|
||||
role_name VARCHAR(128) NOT NULL COMMENT '角色名称',
|
||||
data_scope TINYINT NOT NULL DEFAULT 1 COMMENT '数据权限: 1-全部, 2-本部门及子部门, 3-仅本部门, 4-仅本人',
|
||||
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
|
||||
sort_order INT DEFAULT 0 COMMENT '排序号',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -66,11 +65,11 @@ CREATE TABLE IF NOT EXISTS sys_role (
|
||||
-- 3. 用户角色关联表 (sys_user_role)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS sys_user_role (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
user_id VARCHAR(32) NOT NULL COMMENT '用户ID',
|
||||
role_id VARCHAR(32) NOT NULL COMMENT '角色ID',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
user_id BIGINT NOT NULL COMMENT '用户ID',
|
||||
role_id BIGINT NOT NULL COMMENT '角色ID',
|
||||
created_by BIGINT COMMENT '创建人',
|
||||
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (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)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS sys_menu (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
parent_id VARCHAR(32) NOT NULL DEFAULT '0' COMMENT '父菜单ID, 0表示根菜单',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '菜单ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
parent_id BIGINT NOT NULL DEFAULT 0 COMMENT '父菜单ID, 0表示根菜单',
|
||||
menu_name VARCHAR(128) NOT NULL COMMENT '菜单名称',
|
||||
menu_type TINYINT NOT NULL DEFAULT 1 COMMENT '菜单类型: 1-目录, 2-菜单, 3-按钮',
|
||||
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-显示',
|
||||
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -110,11 +109,11 @@ CREATE TABLE IF NOT EXISTS sys_menu (
|
||||
-- 5. 角色菜单关联表 (sys_role_menu)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS sys_role_menu (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
role_id VARCHAR(32) NOT NULL COMMENT '角色ID',
|
||||
menu_id VARCHAR(32) NOT NULL COMMENT '菜单ID',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
role_id BIGINT NOT NULL COMMENT '角色ID',
|
||||
menu_id BIGINT NOT NULL COMMENT '菜单ID',
|
||||
created_by BIGINT COMMENT '创建人',
|
||||
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (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)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS sys_dept (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
parent_id VARCHAR(32) NOT NULL DEFAULT '0' COMMENT '父部门ID, 0表示根部门',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '部门ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
parent_id BIGINT NOT NULL DEFAULT 0 COMMENT '父部门ID, 0表示根部门',
|
||||
dept_code VARCHAR(64) NOT NULL COMMENT '部门编码',
|
||||
dept_name VARCHAR(128) NOT NULL COMMENT '部门名称',
|
||||
dept_leader VARCHAR(64) COMMENT '部门负责人',
|
||||
@ -137,9 +136,9 @@ CREATE TABLE IF NOT EXISTS sys_dept (
|
||||
sort_order INT DEFAULT 0 COMMENT '排序号',
|
||||
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -153,17 +152,17 @@ CREATE TABLE IF NOT EXISTS sys_dept (
|
||||
-- 7. 数据字典表 (sys_dict)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS sys_dict (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '字典ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
dict_type VARCHAR(64) NOT NULL COMMENT '字典类型',
|
||||
dict_label VARCHAR(128) NOT NULL COMMENT '字典标签',
|
||||
dict_value VARCHAR(128) NOT NULL COMMENT '字典值',
|
||||
sort_order INT DEFAULT 0 COMMENT '排序号',
|
||||
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -177,8 +176,8 @@ CREATE TABLE IF NOT EXISTS sys_dict (
|
||||
-- 8. 系统配置表 (sys_config)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS sys_config (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '配置ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
config_key VARCHAR(128) NOT NULL COMMENT '配置键',
|
||||
config_value TEXT COMMENT '配置值',
|
||||
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 '分组名称',
|
||||
sort_order INT DEFAULT 0 COMMENT '排序号',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_by BIGINT 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 '更新时间',
|
||||
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除',
|
||||
PRIMARY KEY (id),
|
||||
@ -204,9 +203,9 @@ CREATE TABLE IF NOT EXISTS sys_config (
|
||||
-- 9. 操作日志表 (sys_log)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS sys_log (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID(雪花算法)',
|
||||
tenant_id VARCHAR(32) NOT NULL COMMENT '租户ID',
|
||||
user_id VARCHAR(32) COMMENT '操作用户ID',
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '日志ID',
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
|
||||
user_id BIGINT COMMENT '操作用户ID',
|
||||
username VARCHAR(64) COMMENT '操作用户名',
|
||||
operation VARCHAR(128) 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='系统操作日志表';
|
||||
|
||||
-- =============================================
|
||||
-- 10. 租户表 (sys_tenant)
|
||||
-- 初始化数据
|
||||
-- =============================================
|
||||
|
||||
-- 创建租户表 (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_name VARCHAR(100) NOT NULL COMMENT '租户名称',
|
||||
contact VARCHAR(50) COMMENT '联系人',
|
||||
@ -241,41 +242,37 @@ CREATE TABLE IF NOT EXISTS sys_tenant (
|
||||
max_users INT NOT NULL DEFAULT 10 COMMENT '最大用户数',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
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 '创建时间',
|
||||
updated_by VARCHAR(32) COMMENT '更新人',
|
||||
updated_by BIGINT COMMENT '更新人',
|
||||
updated_time DATETIME ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_tenant_code (tenant_code, deleted)
|
||||
) 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)
|
||||
VALUES ('1', 'DEFAULT', '默认租户', '管理员', '13800138000', 1, 100, '系统默认租户', NOW())
|
||||
VALUES (1, 'DEFAULT', '默认租户', '管理员', '13800138000', 1, 100, '系统默认租户', NOW())
|
||||
ON DUPLICATE KEY UPDATE tenant_code=tenant_code;
|
||||
|
||||
-- 插入超级管理员用户 (租户ID=1, 密码: admin123, MD5: 0192023a7bbd73250516f069df18b500)
|
||||
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())
|
||||
-- 插入超级管理员用户 (租户ID=1, 密码: admin123)
|
||||
INSERT INTO sys_user (id, tenant_id, username, password, real_name, phone, status, created_by, created_time)
|
||||
VALUES (1, 1, 'admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5E', '超级管理员', '13800138000', 1, 1, NOW())
|
||||
ON DUPLICATE KEY UPDATE username=username;
|
||||
|
||||
-- 插入超级管理员角色
|
||||
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;
|
||||
|
||||
-- 关联超级管理员用户和角色
|
||||
INSERT INTO sys_user_role (id, tenant_id, user_id, role_id, created_by, created_time)
|
||||
VALUES ('1', '1', '1', '1', '1', NOW())
|
||||
INSERT INTO sys_user_role (tenant_id, user_id, role_id, created_by, created_time)
|
||||
VALUES (1, 1, 1, 1, NOW())
|
||||
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)
|
||||
VALUES ('1', '1', '0', 'ROOT', '根部门', 1, '1', NOW())
|
||||
VALUES (1, 1, 0, 'ROOT', '根部门', 1, 1, NOW())
|
||||
ON DUPLICATE KEY UPDATE dept_code=dept_code;
|
||||
|
||||
-- =============================================
|
||||
|
||||
@ -622,69 +622,6 @@ spring:
|
||||
/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 首次部署
|
||||
|
||||
@ -161,77 +161,10 @@ npm run dev
|
||||
# 移动端H5:http://localhost:8080
|
||||
# 网关地址:http://localhost:8000
|
||||
# Nacos 控制台:http://localhost:8048/nacos
|
||||
# Grafana 监控:http://localhost:3000 (Docker 环境) 或 http://localhost:3001 (本地开发)
|
||||
# Grafana 监控:http://localhost:3000 (Docker环境) 或 http://localhost:3001 (本地开发)
|
||||
# Prometheus:http://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 部署
|
||||
|
||||
7
fund-admin/package-lock.json
generated
7
fund-admin/package-lock.json
generated
@ -12,7 +12,6 @@
|
||||
"axios": "^1.13.5",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.13.2",
|
||||
"js-md5": "^0.8.3",
|
||||
"pinia": "^3.0.4",
|
||||
"vue": "^3.5.25",
|
||||
"vue-router": "^5.0.2"
|
||||
@ -2485,12 +2484,6 @@
|
||||
"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": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
"axios": "^1.13.5",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.13.2",
|
||||
"js-md5": "^0.8.3",
|
||||
"pinia": "^3.0.4",
|
||||
"vue": "^3.5.25",
|
||||
"vue-router": "^5.0.2"
|
||||
|
||||
@ -1,13 +1,8 @@
|
||||
import { request } from './request'
|
||||
|
||||
// 登录
|
||||
export function login(data: { username: string; password: string, tenantId?: number }) {
|
||||
// 如果没有传递 tenantId,使用默认值 1
|
||||
const requestData = {
|
||||
...data,
|
||||
tenantId: data.tenantId || 1
|
||||
}
|
||||
return request.post('/auth/login', requestData)
|
||||
export function login(data: { username: string; password: string }) {
|
||||
return request.post('/auth/login', data)
|
||||
}
|
||||
|
||||
// 登出
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
</div>
|
||||
|
||||
<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="typeName" label="类型名称" min-width="150" />
|
||||
<el-table-column prop="parentName" label="上级类型" width="140" />
|
||||
@ -40,7 +40,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<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">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" :icon="Edit" @click="handleEdit(row)">编辑</el-button>
|
||||
@ -81,7 +81,7 @@
|
||||
<el-tree-select
|
||||
v-model="form.parentId"
|
||||
:data="typeTreeData"
|
||||
:props="{ label: 'typeName', value: 'id', children: 'children' }"
|
||||
:props="{ label: 'typeName', value: 'typeId', children: 'children' }"
|
||||
placeholder="请选择上级类型(不选则为顶级类型)"
|
||||
check-strictly
|
||||
clearable
|
||||
@ -137,7 +137,7 @@ const dialogTitle = ref('新增类型')
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const form = reactive({
|
||||
id: null as number | null,
|
||||
typeId: null as number | null,
|
||||
typeCode: '',
|
||||
typeName: '',
|
||||
parentId: null as number | null,
|
||||
@ -154,40 +154,11 @@ const rules = reactive<FormRules>({
|
||||
const fetchData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 转换查询参数:将前端的 ENABLED/DISABLED 转换为后端的 1/0
|
||||
const apiParams: any = {
|
||||
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)
|
||||
|
||||
const res: any = await getExpenseTypeList(queryParams)
|
||||
tableData.value = res.data?.records || []
|
||||
total.value = res.data?.total || 0
|
||||
console.log('总数:', total.value)
|
||||
} catch (e) {
|
||||
console.error('获取数据失败:', e)
|
||||
ElMessage.error('获取数据失败')
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@ -228,15 +199,11 @@ const handleEdit = (row: any) => {
|
||||
|
||||
const handleStatusChange = async (row: any) => {
|
||||
try {
|
||||
// 将前端的状态字符串转换为后端期望的整数
|
||||
const statusValue = row.status === 'ENABLED' ? 1 : 0
|
||||
await updateExpenseType(row.id, { status: statusValue })
|
||||
await updateExpenseType(row.typeId, { status: row.status })
|
||||
ElMessage.success('状态更新成功')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
// 恢复原状态
|
||||
row.status = row.status === 'ENABLED' ? 'DISABLED' : 'ENABLED'
|
||||
ElMessage.error('状态更新失败')
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,7 +214,7 @@ const handleDelete = (row: any) => {
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
await deleteExpenseType(row.id)
|
||||
await deleteExpenseType(row.typeId)
|
||||
ElMessage.success('删除成功')
|
||||
fetchData()
|
||||
fetchTypeTree()
|
||||
@ -264,32 +231,18 @@ const handleSubmit = async () => {
|
||||
|
||||
submitLoading.value = true
|
||||
try {
|
||||
// 准备提交数据:将表单中的状态字符串转换为后端期望的整数
|
||||
const submitData: any = { ...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)
|
||||
if (form.typeId) {
|
||||
await updateExpenseType(form.typeId, form)
|
||||
ElMessage.success('更新成功')
|
||||
} else {
|
||||
console.log('创建新类型')
|
||||
await createExpenseType(submitData)
|
||||
await createExpenseType(form)
|
||||
ElMessage.success('创建成功')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
fetchData()
|
||||
fetchTypeTree()
|
||||
} catch (e) {
|
||||
console.error('提交失败:', e)
|
||||
ElMessage.error(e instanceof Error ? e.message : '操作失败')
|
||||
console.error(e)
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
@ -297,7 +250,7 @@ const handleSubmit = async () => {
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
form.id = null
|
||||
form.typeId = null
|
||||
form.typeCode = ''
|
||||
form.typeName = ''
|
||||
form.parentId = null
|
||||
|
||||
@ -51,7 +51,6 @@ import { useRouter, useRoute } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import md5 from 'js-md5'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@ -71,16 +70,35 @@ const rules: FormRules = {
|
||||
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 () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
loading.value = true
|
||||
|
||||
// 密码先进行 MD5 加密(使用默认导出)
|
||||
|
||||
// 密码先进行 MD5 加密
|
||||
const encryptedPassword = md5(form.password)
|
||||
|
||||
console.log('登录 - 原始密码:', form.password, 'MD5 加密后:', encryptedPassword)
|
||||
|
||||
await userStore.loginAction(form.username, encryptedPassword)
|
||||
|
||||
// 获取用户信息
|
||||
|
||||
@ -127,7 +127,6 @@ import { ElMessage, FormInstance, FormRules, UploadProps } from 'element-plus'
|
||||
import { UserFilled, OfficeBuilding } from '@element-plus/icons-vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { updateProfile, updatePassword } from '@/api/user'
|
||||
import md5 from 'js-md5'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const defaultAvatar = 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png'
|
||||
@ -232,16 +231,12 @@ const handleUpdateProfile = async () => {
|
||||
|
||||
const handleUpdatePassword = async () => {
|
||||
if (!passwordFormRef.value) return
|
||||
|
||||
|
||||
await passwordFormRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
saving.value = true
|
||||
try {
|
||||
await updatePassword({
|
||||
oldPassword: md5(passwordForm.oldPassword),
|
||||
newPassword: md5(passwordForm.newPassword),
|
||||
confirmPassword: md5(passwordForm.confirmPassword)
|
||||
})
|
||||
await updatePassword(passwordForm)
|
||||
ElMessage.success('密码修改成功')
|
||||
resetPasswordForm()
|
||||
} catch (error: any) {
|
||||
|
||||
@ -78,13 +78,6 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Hutool (雪花ID生成器) -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>5.8.25</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@ -17,7 +17,7 @@ public class TokenInfo implements Serializable {
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
@ -27,7 +27,7 @@ public class TokenInfo implements Serializable {
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private String tenantId;
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 登录时间戳
|
||||
@ -42,7 +42,7 @@ public class TokenInfo implements Serializable {
|
||||
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.username = username;
|
||||
this.tenantId = tenantId;
|
||||
@ -50,11 +50,11 @@ public class TokenInfo implements Serializable {
|
||||
this.expireTime = expireTime;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
@ -66,11 +66,11 @@ public class TokenInfo implements Serializable {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getTenantId() {
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(String tenantId) {
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ public class TokenService {
|
||||
* @param tenantId 租户ID
|
||||
* @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);
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ public class TokenService {
|
||||
* @param expireSeconds 过期时间(秒)
|
||||
* @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
|
||||
String token = UUID.randomUUID().toString().replace("-", "");
|
||||
|
||||
@ -173,7 +173,7 @@ public class TokenService {
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
public void deleteAllUserTokens(String userId) {
|
||||
public void deleteAllUserTokens(Long userId) {
|
||||
String userTokensKey = getUserTokensKey(userId);
|
||||
java.util.Map<Object, Object> tokens = redisService.hGetAll(userTokensKey);
|
||||
|
||||
@ -225,7 +225,7 @@ public class TokenService {
|
||||
/**
|
||||
* 构建用户Token列表Key
|
||||
*/
|
||||
private String getUserTokensKey(String userId) {
|
||||
private String getUserTokensKey(Long userId) {
|
||||
return USER_TOKENS_PREFIX + userId;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,17 +5,17 @@ package com.fundplatform.common.context;
|
||||
*/
|
||||
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 UserContextHolder() {
|
||||
}
|
||||
|
||||
public static void setUserId(String userId) {
|
||||
public static void setUserId(Long userId) {
|
||||
USER_ID_HOLDER.set(userId);
|
||||
}
|
||||
|
||||
public static String getUserId() {
|
||||
public static Long getUserId() {
|
||||
return USER_ID_HOLDER.get();
|
||||
}
|
||||
|
||||
|
||||
@ -8,27 +8,25 @@ import java.time.LocalDateTime;
|
||||
*
|
||||
* <p>注意:此类不绑定具体 ORM 框架注解(如 JPA、MyBatis-Plus),
|
||||
* 仅作为字段规范的统一来源,具体映射由各模块自行扩展。</p>
|
||||
*
|
||||
* <p>主键采用字符串类型(雪花算法生成),解决前端JavaScript大数精度丢失问题。</p>
|
||||
*/
|
||||
public abstract class BaseEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键ID(雪花算法生成的19位字符串) */
|
||||
private String id;
|
||||
/** 主键ID */
|
||||
private Long id;
|
||||
|
||||
/** 租户ID(多租户隔离) */
|
||||
private String tenantId;
|
||||
private Long tenantId;
|
||||
|
||||
/** 创建人 */
|
||||
private String createdBy;
|
||||
private Long createdBy;
|
||||
|
||||
/** 创建时间 */
|
||||
private LocalDateTime createdTime;
|
||||
|
||||
/** 更新人 */
|
||||
private String updatedBy;
|
||||
private Long updatedBy;
|
||||
|
||||
/** 更新时间 */
|
||||
private LocalDateTime updatedTime;
|
||||
@ -39,27 +37,27 @@ public abstract class BaseEntity implements Serializable {
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
public String getId() {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTenantId() {
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(String tenantId) {
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
public String getCreatedBy() {
|
||||
public Long getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(String createdBy) {
|
||||
public void setCreatedBy(Long createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
@ -71,11 +69,11 @@ public abstract class BaseEntity implements Serializable {
|
||||
this.createdTime = createdTime;
|
||||
}
|
||||
|
||||
public String getUpdatedBy() {
|
||||
public Long getUpdatedBy() {
|
||||
return updatedBy;
|
||||
}
|
||||
|
||||
public void setUpdatedBy(String updatedBy) {
|
||||
public void setUpdatedBy(Long updatedBy) {
|
||||
this.updatedBy = updatedBy;
|
||||
}
|
||||
|
||||
|
||||
@ -31,36 +31,34 @@ public class TenantLineHandlerImpl implements TenantLineHandler {
|
||||
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(
|
||||
"sys_menu", // 菜单表(所有租户共享的系统菜单结构)
|
||||
"sys_dict", // 字典表(所有租户共享的枚举数据)
|
||||
"sys_log", // 日志表(独立存储,由专属逻辑管理)
|
||||
"gen_table", // 代码生成表(开发工具,非业务数据)
|
||||
"sys_user", // 用户表(可能跨租户)
|
||||
"sys_role", // 角色表(可能跨租户)
|
||||
"sys_menu", // 菜单表(所有租户共享)
|
||||
"sys_dict", // 字典表(所有租户共享)
|
||||
"sys_config", // 配置表(所有租户共享)
|
||||
"sys_dept", // 部门表(可能跨租户)
|
||||
"sys_log", // 日志表(独立存储)
|
||||
"gen_table", // 代码生成表
|
||||
"gen_table_column" // 代码生成字段表
|
||||
));
|
||||
|
||||
/**
|
||||
* 获取租户 ID 值
|
||||
*
|
||||
* <p>从 TenantContextHolder 获取当前线程的租户 ID。</p>
|
||||
* <p>安全修复:不再使用 fallback 默认值 1L,若租户上下文为空则直接报错,
|
||||
* 防止在缺少认证上下文的情况下误操作租户1的数据。</p>
|
||||
*
|
||||
* <p>从 TenantContextHolder 获取当前线程的租户 ID</p>
|
||||
*/
|
||||
@Override
|
||||
public Expression getTenantId() {
|
||||
Long tenantId = getCurrentTenantId();
|
||||
|
||||
|
||||
if (tenantId == null) {
|
||||
// 安全修复:租户上下文缺失时必须中断,不能 fallback 到任意租户
|
||||
throw new IllegalStateException("[Security] 当前请求缺少租户上下文,拒绝执行SQL,请确保请求经过认证过滤器");
|
||||
logger.debug("[MyBatis Tenant] 未获取到租户 ID,使用默认值 1");
|
||||
tenantId = 1L;
|
||||
}
|
||||
|
||||
|
||||
logger.debug("[MyBatis Tenant] 当前租户 ID: {}", tenantId);
|
||||
return new LongValue(tenantId);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -61,13 +61,6 @@
|
||||
<version>7.4</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringDoc OpenAPI (Swagger UI) -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Nacos服务注册发现 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
|
||||
@ -5,16 +5,12 @@ import com.fundplatform.common.core.Result;
|
||||
import com.fundplatform.cust.dto.ContactDTO;
|
||||
import com.fundplatform.cust.service.ContactService;
|
||||
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 org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 联系人管理Controller
|
||||
*/
|
||||
@Tag(name = "联系人管理", description = "客户联系人的增删改查及主要联系人设置")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/customer/contact")
|
||||
public class ContactController {
|
||||
@ -28,7 +24,6 @@ public class ContactController {
|
||||
/**
|
||||
* 创建联系人
|
||||
*/
|
||||
@Operation(summary = "创建联系人")
|
||||
@PostMapping
|
||||
public Result<Long> create(@Valid @RequestBody ContactDTO dto) {
|
||||
Long id = contactService.createContact(dto);
|
||||
@ -38,9 +33,8 @@ public class ContactController {
|
||||
/**
|
||||
* 更新联系人
|
||||
*/
|
||||
@Operation(summary = "更新联系人信息")
|
||||
@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);
|
||||
return Result.success(result);
|
||||
}
|
||||
@ -48,9 +42,8 @@ public class ContactController {
|
||||
/**
|
||||
* 根据ID查询联系人
|
||||
*/
|
||||
@Operation(summary = "根据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);
|
||||
return Result.success(vo);
|
||||
}
|
||||
@ -58,12 +51,11 @@ public class ContactController {
|
||||
/**
|
||||
* 分页查询联系人
|
||||
*/
|
||||
@Operation(summary = "分页查询联系人")
|
||||
@GetMapping("/page")
|
||||
public Result<Page<ContactVO>> page(
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int pageNum,
|
||||
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize,
|
||||
@Parameter(description = "客户ID") @RequestParam(required = false) Long customerId) {
|
||||
@RequestParam(defaultValue = "1") int pageNum,
|
||||
@RequestParam(defaultValue = "10") int pageSize,
|
||||
@RequestParam(required = false) Long customerId) {
|
||||
Page<ContactVO> page = contactService.pageContacts(pageNum, pageSize, customerId);
|
||||
return Result.success(page);
|
||||
}
|
||||
@ -71,9 +63,8 @@ public class ContactController {
|
||||
/**
|
||||
* 删除联系人
|
||||
*/
|
||||
@Operation(summary = "删除联系人")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> delete(@Parameter(description = "联系人ID") @PathVariable Long id) {
|
||||
public Result<Boolean> delete(@PathVariable Long id) {
|
||||
boolean result = contactService.deleteContact(id);
|
||||
return Result.success(result);
|
||||
}
|
||||
@ -81,11 +72,10 @@ public class ContactController {
|
||||
/**
|
||||
* 设置主要联系人
|
||||
*/
|
||||
@Operation(summary = "设置主要联系人", description = "将指定联系人设为客户的主要联系人")
|
||||
@PutMapping("/{customerId}/contact/{contactId}/primary")
|
||||
public Result<Boolean> setPrimary(
|
||||
@Parameter(description = "客户ID") @PathVariable Long customerId,
|
||||
@Parameter(description = "联系人ID") @PathVariable Long contactId) {
|
||||
@PathVariable Long customerId,
|
||||
@PathVariable Long contactId) {
|
||||
boolean result = contactService.setPrimaryContact(customerId, contactId);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@ -7,16 +7,12 @@ import com.fundplatform.cust.dto.CustomerCreateDTO;
|
||||
import com.fundplatform.cust.dto.CustomerUpdateDTO;
|
||||
import com.fundplatform.cust.service.CustomerService;
|
||||
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 org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 客户Controller
|
||||
*/
|
||||
@Tag(name = "客户管理", description = "客户的增删改查")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/customer")
|
||||
public class CustomerController {
|
||||
@ -30,7 +26,6 @@ public class CustomerController {
|
||||
/**
|
||||
* 创建客户
|
||||
*/
|
||||
@Operation(summary = "创建客户")
|
||||
@PostMapping
|
||||
public Result<Long> createCustomer(@Valid @RequestBody CustomerCreateDTO dto) {
|
||||
Long id = customerService.createCustomer(dto);
|
||||
@ -40,9 +35,8 @@ public class CustomerController {
|
||||
/**
|
||||
* 更新客户
|
||||
*/
|
||||
@Operation(summary = "更新客户信息")
|
||||
@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);
|
||||
return Result.success();
|
||||
}
|
||||
@ -50,9 +44,8 @@ public class CustomerController {
|
||||
/**
|
||||
* 查询客户详情
|
||||
*/
|
||||
@Operation(summary = "查询客户详情")
|
||||
@GetMapping("/{id}")
|
||||
public Result<CustomerVO> getCustomer(@Parameter(description = "客户ID") @PathVariable Long id) {
|
||||
public Result<CustomerVO> getCustomer(@PathVariable Long id) {
|
||||
CustomerVO vo = customerService.getCustomerById(id);
|
||||
return Result.success(vo);
|
||||
}
|
||||
@ -60,12 +53,11 @@ public class CustomerController {
|
||||
/**
|
||||
* 分页查询客户
|
||||
*/
|
||||
@Operation(summary = "分页查询客户")
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<CustomerVO>> pageCustomers(
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int pageNum,
|
||||
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize,
|
||||
@Parameter(description = "关键词(客户名/编码/联系人模糊查询)") @RequestParam(required = false) String keyword) {
|
||||
@RequestParam(defaultValue = "1") int pageNum,
|
||||
@RequestParam(defaultValue = "10") int pageSize,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
|
||||
Page<CustomerVO> page = customerService.pageCustomers(pageNum, pageSize, keyword);
|
||||
PageResult<CustomerVO> pageResult = new PageResult<>(
|
||||
@ -80,9 +72,8 @@ public class CustomerController {
|
||||
/**
|
||||
* 删除客户
|
||||
*/
|
||||
@Operation(summary = "删除客户(逻辑删除)")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> deleteCustomer(@Parameter(description = "客户ID") @PathVariable Long id) {
|
||||
public Result<Void> deleteCustomer(@PathVariable Long id) {
|
||||
customerService.deleteCustomer(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ public class CustomerContact extends BaseEntity {
|
||||
/**
|
||||
* 客户ID
|
||||
*/
|
||||
private String customerId;
|
||||
private Long customerId;
|
||||
|
||||
/**
|
||||
* 联系人姓名
|
||||
@ -51,11 +51,11 @@ public class CustomerContact extends BaseEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
public String getCustomerId() {
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(String customerId) {
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.fundplatform.exp.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fundplatform.common.core.PageResult;
|
||||
import com.fundplatform.common.core.Result;
|
||||
import com.fundplatform.common.util.ExcelUtil;
|
||||
import com.fundplatform.exp.dto.ExpenseExcel;
|
||||
@ -63,21 +62,14 @@ public class FundExpenseController {
|
||||
* 分页查询支出列表
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<FundExpenseVO>> page(
|
||||
public Result<Page<FundExpenseVO>> page(
|
||||
@RequestParam(defaultValue = "1") int pageNum,
|
||||
@RequestParam(defaultValue = "10") int pageSize,
|
||||
@RequestParam(required = false) String title,
|
||||
@RequestParam(required = false) Long expenseType,
|
||||
@RequestParam(required = false) Integer payStatus,
|
||||
@RequestParam(required = false) Integer approvalStatus) {
|
||||
Page<FundExpenseVO> page = 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);
|
||||
return Result.success(expenseService.pageExpenses(pageNum, pageSize, title, expenseType, payStatus, approvalStatus));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -16,7 +16,7 @@ public class ExpenseType extends BaseEntity {
|
||||
private String typeName;
|
||||
|
||||
/** 父类型ID(0表示一级类型) */
|
||||
private String parentId;
|
||||
private Long parentId;
|
||||
|
||||
/** 类型层级 */
|
||||
private Integer typeLevel;
|
||||
@ -46,11 +46,11 @@ public class ExpenseType extends BaseEntity {
|
||||
this.typeName = typeName;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ public class FundExpense extends BaseEntity {
|
||||
private String currency;
|
||||
|
||||
/** 支出类型(1-日常支出 2-项目支出 3-工资发放 4-其他) */
|
||||
private String expenseType;
|
||||
private Long expenseType;
|
||||
|
||||
/** 收款单位 */
|
||||
private String payeeName;
|
||||
@ -43,13 +43,13 @@ public class FundExpense extends BaseEntity {
|
||||
private String purpose;
|
||||
|
||||
/** 关联用款申请ID */
|
||||
private String requestId;
|
||||
private Long requestId;
|
||||
|
||||
/** 项目ID */
|
||||
private String projectId;
|
||||
private Long projectId;
|
||||
|
||||
/** 客户ID */
|
||||
private String customerId;
|
||||
private Long customerId;
|
||||
|
||||
/** 支付状态(0-待支付 1-已支付 2-支付失败) */
|
||||
private Integer payStatus;
|
||||
@ -67,7 +67,7 @@ public class FundExpense extends BaseEntity {
|
||||
private Integer approvalStatus;
|
||||
|
||||
/** 审批人ID */
|
||||
private String approverId;
|
||||
private Long approverId;
|
||||
|
||||
/** 审批时间 */
|
||||
private LocalDateTime approvalTime;
|
||||
@ -110,11 +110,11 @@ public class FundExpense extends BaseEntity {
|
||||
this.currency = currency;
|
||||
}
|
||||
|
||||
public String getExpenseType() {
|
||||
public Long getExpenseType() {
|
||||
return expenseType;
|
||||
}
|
||||
|
||||
public void setExpenseType(String expenseType) {
|
||||
public void setExpenseType(Long expenseType) {
|
||||
this.expenseType = expenseType;
|
||||
}
|
||||
|
||||
@ -158,27 +158,27 @@ public class FundExpense extends BaseEntity {
|
||||
this.purpose = purpose;
|
||||
}
|
||||
|
||||
public String getRequestId() {
|
||||
public Long getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
public void setRequestId(String requestId) {
|
||||
public void setRequestId(Long requestId) {
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
public String getProjectId() {
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(String projectId) {
|
||||
public void setProjectId(Long projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getCustomerId() {
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(String customerId) {
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
@ -222,11 +222,11 @@ public class FundExpense extends BaseEntity {
|
||||
this.approvalStatus = approvalStatus;
|
||||
}
|
||||
|
||||
public String getApproverId() {
|
||||
public Long getApproverId() {
|
||||
return approverId;
|
||||
}
|
||||
|
||||
public void setApproverId(String approverId) {
|
||||
public void setApproverId(Long approverId) {
|
||||
this.approverId = approverId;
|
||||
}
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@ public class ExpenseTypeServiceImpl implements ExpenseTypeService {
|
||||
|
||||
Page<ExpenseType> page = typeDataService.page(new Page<>(pageNum, pageSize), wrapper);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -86,10 +86,8 @@ public class ExpenseTypeServiceImpl implements ExpenseTypeService {
|
||||
type.setId(dto.getId());
|
||||
type.setTypeCode(dto.getTypeCode());
|
||||
type.setTypeName(dto.getTypeName());
|
||||
type.setParentId(dto.getParentId() != null ? dto.getParentId() : 0L);
|
||||
type.setSortOrder(dto.getSortOrder());
|
||||
type.setDescription(dto.getDescription());
|
||||
type.setStatus(dto.getStatus());
|
||||
type.setUpdatedTime(LocalDateTime.now());
|
||||
type.setUpdatedBy(UserContextHolder.getUserId());
|
||||
|
||||
@ -197,20 +195,6 @@ public class ExpenseTypeServiceImpl implements ExpenseTypeService {
|
||||
vo.setCreatedTime(type.getCreatedTime());
|
||||
vo.setUpdatedTime(type.getUpdatedTime());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,27 +1,20 @@
|
||||
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.util.List;
|
||||
|
||||
public class ExpenseTypeVO {
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
private String typeCode;
|
||||
private String typeName;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long parentId;
|
||||
private String parentName;
|
||||
private Integer typeLevel;
|
||||
private Integer sortOrder;
|
||||
private String description;
|
||||
private Integer status;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long tenantId;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long createdBy;
|
||||
private LocalDateTime createdTime;
|
||||
private LocalDateTime updatedTime;
|
||||
|
||||
@ -1,20 +1,15 @@
|
||||
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.time.LocalDateTime;
|
||||
|
||||
public class FundExpenseVO {
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
private String expenseNo;
|
||||
private String title;
|
||||
private BigDecimal amount;
|
||||
private String currency;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long expenseType;
|
||||
private String expenseTypeName;
|
||||
private String payeeName;
|
||||
@ -22,11 +17,8 @@ public class FundExpenseVO {
|
||||
private String payeeAccount;
|
||||
private LocalDateTime expenseDate;
|
||||
private String purpose;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long requestId;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long projectId;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long customerId;
|
||||
private Integer payStatus;
|
||||
private String payStatusName;
|
||||
@ -35,14 +27,11 @@ public class FundExpenseVO {
|
||||
private String payVoucher;
|
||||
private Integer approvalStatus;
|
||||
private String approvalStatusName;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long approverId;
|
||||
private LocalDateTime approvalTime;
|
||||
private String approvalComment;
|
||||
private String attachments;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long tenantId;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long createdBy;
|
||||
private LocalDateTime createdTime;
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.fundplatform.file.data.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@ -12,10 +13,10 @@ import java.time.LocalDateTime;
|
||||
@TableName("file_record")
|
||||
public class FileRecord {
|
||||
|
||||
@TableId
|
||||
private String fileId;
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long fileId;
|
||||
|
||||
private String tenantId;
|
||||
private Long tenantId;
|
||||
|
||||
private String fileName;
|
||||
|
||||
@ -35,7 +36,7 @@ public class FileRecord {
|
||||
|
||||
private String businessType;
|
||||
|
||||
private String businessId;
|
||||
private Long businessId;
|
||||
|
||||
private String description;
|
||||
|
||||
@ -43,11 +44,11 @@ public class FileRecord {
|
||||
|
||||
private Integer status;
|
||||
|
||||
private String createdBy;
|
||||
private Long createdBy;
|
||||
|
||||
private LocalDateTime createdTime;
|
||||
|
||||
private String updatedBy;
|
||||
private Long updatedBy;
|
||||
|
||||
private LocalDateTime updatedTime;
|
||||
|
||||
@ -55,19 +56,19 @@ public class FileRecord {
|
||||
private Integer deleted;
|
||||
|
||||
// Getters and Setters
|
||||
public String getFileId() {
|
||||
public Long getFileId() {
|
||||
return fileId;
|
||||
}
|
||||
|
||||
public void setFileId(String fileId) {
|
||||
public void setFileId(Long fileId) {
|
||||
this.fileId = fileId;
|
||||
}
|
||||
|
||||
public String getTenantId() {
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(String tenantId) {
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
@ -143,11 +144,11 @@ public class FileRecord {
|
||||
this.businessType = businessType;
|
||||
}
|
||||
|
||||
public String getBusinessId() {
|
||||
public Long getBusinessId() {
|
||||
return businessId;
|
||||
}
|
||||
|
||||
public void setBusinessId(String businessId) {
|
||||
public void setBusinessId(Long businessId) {
|
||||
this.businessId = businessId;
|
||||
}
|
||||
|
||||
@ -175,11 +176,11 @@ public class FileRecord {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getCreatedBy() {
|
||||
public Long getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(String createdBy) {
|
||||
public void setCreatedBy(Long createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
@ -191,11 +192,11 @@ public class FileRecord {
|
||||
this.createdTime = createdTime;
|
||||
}
|
||||
|
||||
public String getUpdatedBy() {
|
||||
public Long getUpdatedBy() {
|
||||
return updatedBy;
|
||||
}
|
||||
|
||||
public void setUpdatedBy(String updatedBy) {
|
||||
public void setUpdatedBy(Long updatedBy) {
|
||||
this.updatedBy = updatedBy;
|
||||
}
|
||||
|
||||
|
||||
@ -66,15 +66,13 @@ public class TenantGatewayFilter implements GlobalFilter, Ordered {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// 安全修复:从请求头中取 X-Tenant-Id(TokenAuthFilter 已用 Token 中的值覆盖客户端传入值)
|
||||
// 因此此处取到的必为 Token 认证后的租户ID,无需再与客户端值单独比对。
|
||||
// 但仍需校验格式合法性,防止异常数据流入下游服务。
|
||||
// 检查X-Tenant-Id请求头
|
||||
String tenantId = request.getHeaders().getFirst(HEADER_TENANT_ID);
|
||||
if (tenantId == null || tenantId.trim().isEmpty()) {
|
||||
logger.warn("[TenantGateway] 缺少X-Tenant-Id请求头,路径: {}", path);
|
||||
return missingTenantId(exchange, "缺少X-Tenant-Id请求头");
|
||||
}
|
||||
|
||||
|
||||
// 验证租户ID是否为有效数字
|
||||
try {
|
||||
Long.parseLong(tenantId);
|
||||
|
||||
@ -83,10 +83,8 @@ public class TokenAuthFilter implements GlobalFilter, Ordered {
|
||||
return unauthorized(exchange, "Token无效或已过期");
|
||||
}
|
||||
|
||||
// 安全修复:先移除客户端传来的 X-Tenant-Id,再写入 Token 中已认证的值,
|
||||
// 防止攻击者通过伪造 X-Tenant-Id 请求头绕过租户隔离。
|
||||
// 将用户信息写入请求头
|
||||
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()))
|
||||
|
||||
1
fund-mobile/components.d.ts
vendored
1
fund-mobile/components.d.ts
vendored
@ -24,6 +24,5 @@ declare module 'vue' {
|
||||
VanPullRefresh: typeof import('vant/es')['PullRefresh']
|
||||
VanSearch: typeof import('vant/es')['Search']
|
||||
VanTag: typeof import('vant/es')['Tag']
|
||||
VanUploader: typeof import('vant/es')['Uploader']
|
||||
}
|
||||
}
|
||||
|
||||
7
fund-mobile/package-lock.json
generated
7
fund-mobile/package-lock.json
generated
@ -9,7 +9,6 @@
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
"js-md5": "^0.8.3",
|
||||
"pinia": "^2.1.7",
|
||||
"vant": "^4.9.22",
|
||||
"vue": "^3.4.0",
|
||||
@ -1691,12 +1690,6 @@
|
||||
"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": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz",
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
"js-md5": "^0.8.3",
|
||||
"pinia": "^2.1.7",
|
||||
"vant": "^4.9.22",
|
||||
"vue": "^3.4.0",
|
||||
|
||||
@ -2,13 +2,8 @@ import request from './request'
|
||||
|
||||
// ===================== 用户认证 =====================
|
||||
|
||||
export function login(data: { username: string; password: string, tenantId?: number }) {
|
||||
// 如果没有传递 tenantId,使用默认值 1
|
||||
const requestData = {
|
||||
...data,
|
||||
tenantId: data.tenantId || 1
|
||||
}
|
||||
return request.post('/auth/login', requestData)
|
||||
export function login(data: { username: string; password: string }) {
|
||||
return request.post('/auth/login', data)
|
||||
}
|
||||
|
||||
export function getUserInfo() {
|
||||
@ -23,30 +18,6 @@ export function updatePassword(data: { oldPassword: string; newPassword: string;
|
||||
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 }) {
|
||||
|
||||
@ -52,7 +52,6 @@ import { ref, reactive } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showToast, showSuccessToast } from 'vant'
|
||||
import { login } from '@/api'
|
||||
import md5 from 'js-md5'
|
||||
|
||||
const router = useRouter()
|
||||
const loading = ref(false)
|
||||
@ -75,10 +74,7 @@ const handleLogin = async () => {
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const res: any = await login({
|
||||
username: form.username,
|
||||
password: md5(form.password)
|
||||
})
|
||||
const res: any = await login(form)
|
||||
const data = res.data
|
||||
localStorage.setItem('token', data.token)
|
||||
localStorage.setItem('userInfo', JSON.stringify({
|
||||
|
||||
@ -51,18 +51,6 @@
|
||||
<label>支出描述</label>
|
||||
<textarea v-model="form.description" placeholder="输入描述" rows="3" class="mac-textarea"></textarea>
|
||||
</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 class="submit-btn">
|
||||
@ -86,15 +74,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showToast, showSuccessToast, showFailToast, ImagePreview } from 'vant'
|
||||
import { createExpense, getExpenseTypeTree, uploadFile } from '@/api'
|
||||
import { showToast, showSuccessToast, showFailToast } from 'vant'
|
||||
import { createExpense, getExpenseTypeTree } from '@/api'
|
||||
|
||||
const router = useRouter()
|
||||
const loading = ref(false)
|
||||
const showTypePicker = ref(false)
|
||||
const showDatePicker = ref(false)
|
||||
const fileList = ref<any[]>([])
|
||||
const uploadedAttachments = ref<string[]>([]) // 存储 COS 返回的文件路径
|
||||
|
||||
const form = reactive({
|
||||
title: '',
|
||||
@ -120,48 +106,6 @@ const onDateConfirm = ({ selectedValues }: any) => {
|
||||
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 () => {
|
||||
if (!form.title) {
|
||||
showFailToast('请输入支出标题')
|
||||
@ -182,22 +126,15 @@ const handleSubmit = async () => {
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
// 转换日期格式为 LocalDateTime 格式
|
||||
// 转换日期格式为LocalDateTime格式
|
||||
const expenseDateTime = form.expenseDate ? `${form.expenseDate}T12:00:00` : null
|
||||
|
||||
// 处理附件:将 COS 文件路径数组转为逗号分隔的字符串
|
||||
const attachmentsStr = uploadedAttachments.value.length > 0
|
||||
? uploadedAttachments.value.join(',')
|
||||
: null
|
||||
|
||||
const requestData = {
|
||||
title: form.title,
|
||||
expenseType: form.expenseTypeId,
|
||||
amount: parseFloat(form.amount),
|
||||
expenseDate: expenseDateTime,
|
||||
purpose: form.description,
|
||||
payeeName: form.payeeName,
|
||||
attachments: attachmentsStr
|
||||
payeeName: form.payeeName
|
||||
}
|
||||
console.log('提交支出数据:', requestData)
|
||||
await createExpense(requestData)
|
||||
|
||||
@ -16,42 +16,29 @@
|
||||
@load="onLoad"
|
||||
>
|
||||
<div class="expense-card" v-for="item in list" :key="item.expenseId">
|
||||
<!-- 第一行:标题 + 支出时间 -->
|
||||
<div class="card-row title-row">
|
||||
<span class="card-title">{{ item.title }}</span>
|
||||
<span class="card-time">{{ formatDateTime(item.expenseDate) }}</span>
|
||||
<div class="expense-header">
|
||||
<span class="expense-title">{{ item.title }}</span>
|
||||
<van-tag :type="getStatusType(item.status)">{{ getStatusText(item.status) }}</van-tag>
|
||||
</div>
|
||||
|
||||
<!-- 第二行:支出类型 + 支出金额 -->
|
||||
<div class="card-row info-row">
|
||||
<div class="info-left">
|
||||
<van-icon name="apps-o" class="row-icon" />
|
||||
<span class="info-label">支出类型:</span>
|
||||
<div class="expense-info">
|
||||
<div class="info-item">
|
||||
<van-icon name="apps-o" />
|
||||
<span>{{ item.typeName || '-' }}</span>
|
||||
</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 class="card-row payee-row">
|
||||
<van-icon name="shop-o" class="row-icon" />
|
||||
<span class="info-label">收款单位:</span>
|
||||
<span>{{ item.payeeName || '-' }}</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 class="expense-amount">
|
||||
<div class="amount-item">
|
||||
<span class="label">支出金额</span>
|
||||
<span class="value expense-value">¥{{ item.amount?.toLocaleString() }}</span>
|
||||
</div>
|
||||
<div class="amount-item" v-if="item.paidAmount">
|
||||
<span class="label">已支付</span>
|
||||
<span class="value">¥{{ item.paidAmount?.toLocaleString() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-list>
|
||||
@ -67,7 +54,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getExpenseList } from '@/api'
|
||||
import { ImagePreview } from 'vant'
|
||||
|
||||
const searchText = ref('')
|
||||
const loading = ref(false)
|
||||
@ -77,43 +63,6 @@ const list = ref<any[]>([])
|
||||
const pageNum = ref(1)
|
||||
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 map: Record<string, 'primary' | 'success' | 'warning' | 'danger' | 'default'> = {
|
||||
'pending': 'warning',
|
||||
@ -203,99 +152,59 @@ onMounted(() => {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.card-row {
|
||||
.expense-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.card-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.title-row {
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
.expense-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.card-time {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
white-space: nowrap;
|
||||
.expense-info {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.info-left {
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
gap: 4px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.row-icon {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.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;
|
||||
.expense-amount {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.attachment-btn {
|
||||
color: #007AFF;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
padding: 4px 8px;
|
||||
background: rgba(0, 122, 255, 0.08);
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
.amount-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.attachment-btn:active {
|
||||
background: rgba(0, 122, 255, 0.15);
|
||||
.amount-item .label {
|
||||
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 {
|
||||
|
||||
@ -60,7 +60,6 @@ import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showFailToast, showSuccessToast, showConfirmDialog } from 'vant'
|
||||
import { updatePassword } from '@/api'
|
||||
import md5 from 'js-md5'
|
||||
|
||||
const router = useRouter()
|
||||
const submitting = ref(false)
|
||||
@ -93,11 +92,11 @@ const onSubmit = async () => {
|
||||
})
|
||||
|
||||
submitting.value = true
|
||||
|
||||
|
||||
await updatePassword({
|
||||
oldPassword: md5(form.value.oldPassword),
|
||||
newPassword: md5(form.value.newPassword),
|
||||
confirmPassword: md5(form.value.confirmPassword)
|
||||
oldPassword: form.value.oldPassword,
|
||||
newPassword: form.value.newPassword,
|
||||
confirmPassword: form.value.confirmPassword
|
||||
})
|
||||
|
||||
showSuccessToast('密码修改成功')
|
||||
|
||||
@ -14,7 +14,7 @@ public class Project extends BaseEntity {
|
||||
|
||||
private String projectCode;
|
||||
private String projectName;
|
||||
private String customerId;
|
||||
private Long customerId;
|
||||
private String customerName;
|
||||
private String projectType;
|
||||
private BigDecimal budgetAmount;
|
||||
@ -39,11 +39,11 @@ public class Project extends BaseEntity {
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
public String getCustomerId() {
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(String customerId) {
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
|
||||
@ -23,10 +23,10 @@ public class Requirement extends BaseEntity {
|
||||
private String description;
|
||||
|
||||
/** 项目ID */
|
||||
private String projectId;
|
||||
private Long projectId;
|
||||
|
||||
/** 客户ID */
|
||||
private String customerId;
|
||||
private Long customerId;
|
||||
|
||||
/** 优先级(high-高,normal-中,low-低) */
|
||||
private String priority;
|
||||
@ -91,19 +91,19 @@ public class Requirement extends BaseEntity {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getProjectId() {
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(String projectId) {
|
||||
public void setProjectId(Long projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getCustomerId() {
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(String customerId) {
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
|
||||
@ -43,13 +43,13 @@ public class FundReceipt extends BaseEntity {
|
||||
private String purpose;
|
||||
|
||||
/** 关联项目ID */
|
||||
private String projectId;
|
||||
private Long projectId;
|
||||
|
||||
/** 关联客户ID */
|
||||
private String customerId;
|
||||
private Long customerId;
|
||||
|
||||
/** 关联应收款ID */
|
||||
private String receivableId;
|
||||
private Long receivableId;
|
||||
|
||||
/** 收款状态(0-待确认 1-已确认 2-已核销) */
|
||||
private Integer receiptStatus;
|
||||
@ -58,13 +58,13 @@ public class FundReceipt extends BaseEntity {
|
||||
private LocalDateTime confirmTime;
|
||||
|
||||
/** 确认人ID */
|
||||
private String confirmBy;
|
||||
private Long confirmBy;
|
||||
|
||||
/** 核销时间 */
|
||||
private LocalDateTime writeOffTime;
|
||||
|
||||
/** 核销人ID */
|
||||
private String writeOffBy;
|
||||
private Long writeOffBy;
|
||||
|
||||
/** 收款凭证 */
|
||||
private String voucher;
|
||||
@ -95,22 +95,22 @@ public class FundReceipt extends BaseEntity {
|
||||
public void setReceiptDate(LocalDateTime receiptDate) { this.receiptDate = receiptDate; }
|
||||
public String getPurpose() { return purpose; }
|
||||
public void setPurpose(String purpose) { this.purpose = purpose; }
|
||||
public String getProjectId() { return projectId; }
|
||||
public void setProjectId(String projectId) { this.projectId = projectId; }
|
||||
public String getCustomerId() { return customerId; }
|
||||
public void setCustomerId(String customerId) { this.customerId = customerId; }
|
||||
public String getReceivableId() { return receivableId; }
|
||||
public void setReceivableId(String receivableId) { this.receivableId = receivableId; }
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
public Long getCustomerId() { return customerId; }
|
||||
public void setCustomerId(Long customerId) { this.customerId = customerId; }
|
||||
public Long getReceivableId() { return receivableId; }
|
||||
public void setReceivableId(Long receivableId) { this.receivableId = receivableId; }
|
||||
public Integer getReceiptStatus() { return receiptStatus; }
|
||||
public void setReceiptStatus(Integer receiptStatus) { this.receiptStatus = receiptStatus; }
|
||||
public LocalDateTime getConfirmTime() { return confirmTime; }
|
||||
public void setConfirmTime(LocalDateTime confirmTime) { this.confirmTime = confirmTime; }
|
||||
public String getConfirmBy() { return confirmBy; }
|
||||
public void setConfirmBy(String confirmBy) { this.confirmBy = confirmBy; }
|
||||
public Long getConfirmBy() { return confirmBy; }
|
||||
public void setConfirmBy(Long confirmBy) { this.confirmBy = confirmBy; }
|
||||
public LocalDateTime getWriteOffTime() { return writeOffTime; }
|
||||
public void setWriteOffTime(LocalDateTime writeOffTime) { this.writeOffTime = writeOffTime; }
|
||||
public String getWriteOffBy() { return writeOffBy; }
|
||||
public void setWriteOffBy(String writeOffBy) { this.writeOffBy = writeOffBy; }
|
||||
public Long getWriteOffBy() { return writeOffBy; }
|
||||
public void setWriteOffBy(Long writeOffBy) { this.writeOffBy = writeOffBy; }
|
||||
public String getVoucher() { return voucher; }
|
||||
public void setVoucher(String voucher) { this.voucher = voucher; }
|
||||
public String getInvoiceNo() { return invoiceNo; }
|
||||
|
||||
@ -17,13 +17,13 @@ public class Receivable extends BaseEntity {
|
||||
private String receivableCode;
|
||||
|
||||
/** 关联需求ID */
|
||||
private String requirementId;
|
||||
private Long requirementId;
|
||||
|
||||
/** 关联项目ID */
|
||||
private String projectId;
|
||||
private Long projectId;
|
||||
|
||||
/** 关联客户ID */
|
||||
private String customerId;
|
||||
private Long customerId;
|
||||
|
||||
/** 应收款金额 */
|
||||
private BigDecimal receivableAmount;
|
||||
@ -59,7 +59,7 @@ public class Receivable extends BaseEntity {
|
||||
private LocalDateTime confirmTime;
|
||||
|
||||
/** 确认人ID */
|
||||
private String confirmBy;
|
||||
private Long confirmBy;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
@ -72,27 +72,27 @@ public class Receivable extends BaseEntity {
|
||||
this.receivableCode = receivableCode;
|
||||
}
|
||||
|
||||
public String getRequirementId() {
|
||||
public Long getRequirementId() {
|
||||
return requirementId;
|
||||
}
|
||||
|
||||
public void setRequirementId(String requirementId) {
|
||||
public void setRequirementId(Long requirementId) {
|
||||
this.requirementId = requirementId;
|
||||
}
|
||||
|
||||
public String getProjectId() {
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(String projectId) {
|
||||
public void setProjectId(Long projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getCustomerId() {
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(String customerId) {
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
@ -184,11 +184,11 @@ public class Receivable extends BaseEntity {
|
||||
this.confirmTime = confirmTime;
|
||||
}
|
||||
|
||||
public String getConfirmBy() {
|
||||
public Long getConfirmBy() {
|
||||
return confirmBy;
|
||||
}
|
||||
|
||||
public void setConfirmBy(String confirmBy) {
|
||||
public void setConfirmBy(Long confirmBy) {
|
||||
this.confirmBy = confirmBy;
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import java.time.LocalDateTime;
|
||||
|
||||
public class FundReceiptDTO {
|
||||
|
||||
private String id;
|
||||
private Long id;
|
||||
|
||||
@NotBlank(message = "收款标题不能为空")
|
||||
private String title;
|
||||
@ -29,15 +29,15 @@ public class FundReceiptDTO {
|
||||
private String payerAccount;
|
||||
private LocalDateTime receiptDate;
|
||||
private String purpose;
|
||||
private String projectId;
|
||||
private String customerId;
|
||||
private Long projectId;
|
||||
private Long customerId;
|
||||
private String invoiceNo;
|
||||
private String voucher;
|
||||
private String attachments;
|
||||
private String remark;
|
||||
|
||||
public String getId() { return id; }
|
||||
public void setId(String id) { this.id = id; }
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
public BigDecimal getAmount() { return amount; }
|
||||
@ -56,10 +56,10 @@ public class FundReceiptDTO {
|
||||
public void setReceiptDate(LocalDateTime receiptDate) { this.receiptDate = receiptDate; }
|
||||
public String getPurpose() { return purpose; }
|
||||
public void setPurpose(String purpose) { this.purpose = purpose; }
|
||||
public String getProjectId() { return projectId; }
|
||||
public void setProjectId(String projectId) { this.projectId = projectId; }
|
||||
public String getCustomerId() { return customerId; }
|
||||
public void setCustomerId(String customerId) { this.customerId = customerId; }
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
public Long getCustomerId() { return customerId; }
|
||||
public void setCustomerId(Long customerId) { this.customerId = customerId; }
|
||||
public String getInvoiceNo() { return invoiceNo; }
|
||||
public void setInvoiceNo(String invoiceNo) { this.invoiceNo = invoiceNo; }
|
||||
public String getVoucher() { return voucher; }
|
||||
|
||||
@ -12,15 +12,15 @@ import java.time.LocalDate;
|
||||
*/
|
||||
public class ReceivableDTO {
|
||||
|
||||
private String id;
|
||||
private Long id;
|
||||
|
||||
private String requirementId;
|
||||
private Long requirementId;
|
||||
|
||||
@NotNull(message = "项目ID不能为空")
|
||||
private String projectId;
|
||||
private Long projectId;
|
||||
|
||||
@NotNull(message = "客户ID不能为空")
|
||||
private String customerId;
|
||||
private Long customerId;
|
||||
|
||||
@NotNull(message = "应收款金额不能为空")
|
||||
@Positive(message = "应收款金额必须大于0")
|
||||
@ -37,35 +37,35 @@ public class ReceivableDTO {
|
||||
|
||||
private String remark;
|
||||
|
||||
public String getId() {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getRequirementId() {
|
||||
public Long getRequirementId() {
|
||||
return requirementId;
|
||||
}
|
||||
|
||||
public void setRequirementId(String requirementId) {
|
||||
public void setRequirementId(Long requirementId) {
|
||||
this.requirementId = requirementId;
|
||||
}
|
||||
|
||||
public String getProjectId() {
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(String projectId) {
|
||||
public void setProjectId(Long projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getCustomerId() {
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(String customerId) {
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import java.time.LocalDateTime;
|
||||
|
||||
public class FundReceiptVO {
|
||||
|
||||
private String id;
|
||||
private Long id;
|
||||
private String receiptNo;
|
||||
private String title;
|
||||
private BigDecimal amount;
|
||||
@ -17,24 +17,24 @@ public class FundReceiptVO {
|
||||
private String payerAccount;
|
||||
private LocalDateTime receiptDate;
|
||||
private String purpose;
|
||||
private String projectId;
|
||||
private String customerId;
|
||||
private String receivableId;
|
||||
private Long projectId;
|
||||
private Long customerId;
|
||||
private Long receivableId;
|
||||
private Integer receiptStatus;
|
||||
private String receiptStatusName;
|
||||
private LocalDateTime confirmTime;
|
||||
private String confirmBy;
|
||||
private Long confirmBy;
|
||||
private LocalDateTime writeOffTime;
|
||||
private String writeOffBy;
|
||||
private Long writeOffBy;
|
||||
private String voucher;
|
||||
private String invoiceNo;
|
||||
private String attachments;
|
||||
private String tenantId;
|
||||
private String createdBy;
|
||||
private Long tenantId;
|
||||
private Long createdBy;
|
||||
private LocalDateTime createdTime;
|
||||
|
||||
public String getId() { return id; }
|
||||
public void setId(String id) { this.id = id; }
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getReceiptNo() { return receiptNo; }
|
||||
public void setReceiptNo(String receiptNo) { this.receiptNo = receiptNo; }
|
||||
public String getTitle() { return title; }
|
||||
@ -57,34 +57,34 @@ public class FundReceiptVO {
|
||||
public void setReceiptDate(LocalDateTime receiptDate) { this.receiptDate = receiptDate; }
|
||||
public String getPurpose() { return purpose; }
|
||||
public void setPurpose(String purpose) { this.purpose = purpose; }
|
||||
public String getProjectId() { return projectId; }
|
||||
public void setProjectId(String projectId) { this.projectId = projectId; }
|
||||
public String getCustomerId() { return customerId; }
|
||||
public void setCustomerId(String customerId) { this.customerId = customerId; }
|
||||
public String getReceivableId() { return receivableId; }
|
||||
public void setReceivableId(String receivableId) { this.receivableId = receivableId; }
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
public Long getCustomerId() { return customerId; }
|
||||
public void setCustomerId(Long customerId) { this.customerId = customerId; }
|
||||
public Long getReceivableId() { return receivableId; }
|
||||
public void setReceivableId(Long receivableId) { this.receivableId = receivableId; }
|
||||
public Integer getReceiptStatus() { return receiptStatus; }
|
||||
public void setReceiptStatus(Integer receiptStatus) { this.receiptStatus = receiptStatus; }
|
||||
public String getReceiptStatusName() { return receiptStatusName; }
|
||||
public void setReceiptStatusName(String receiptStatusName) { this.receiptStatusName = receiptStatusName; }
|
||||
public LocalDateTime getConfirmTime() { return confirmTime; }
|
||||
public void setConfirmTime(LocalDateTime confirmTime) { this.confirmTime = confirmTime; }
|
||||
public String getConfirmBy() { return confirmBy; }
|
||||
public void setConfirmBy(String confirmBy) { this.confirmBy = confirmBy; }
|
||||
public Long getConfirmBy() { return confirmBy; }
|
||||
public void setConfirmBy(Long confirmBy) { this.confirmBy = confirmBy; }
|
||||
public LocalDateTime getWriteOffTime() { return writeOffTime; }
|
||||
public void setWriteOffTime(LocalDateTime writeOffTime) { this.writeOffTime = writeOffTime; }
|
||||
public String getWriteOffBy() { return writeOffBy; }
|
||||
public void setWriteOffBy(String writeOffBy) { this.writeOffBy = writeOffBy; }
|
||||
public Long getWriteOffBy() { return writeOffBy; }
|
||||
public void setWriteOffBy(Long writeOffBy) { this.writeOffBy = writeOffBy; }
|
||||
public String getVoucher() { return voucher; }
|
||||
public void setVoucher(String voucher) { this.voucher = voucher; }
|
||||
public String getInvoiceNo() { return invoiceNo; }
|
||||
public void setInvoiceNo(String invoiceNo) { this.invoiceNo = invoiceNo; }
|
||||
public String getAttachments() { return attachments; }
|
||||
public void setAttachments(String attachments) { this.attachments = attachments; }
|
||||
public String getTenantId() { return tenantId; }
|
||||
public void setTenantId(String tenantId) { this.tenantId = tenantId; }
|
||||
public String getCreatedBy() { return createdBy; }
|
||||
public void setCreatedBy(String createdBy) { this.createdBy = createdBy; }
|
||||
public Long getTenantId() { return tenantId; }
|
||||
public void setTenantId(Long tenantId) { this.tenantId = tenantId; }
|
||||
public Long getCreatedBy() { return createdBy; }
|
||||
public void setCreatedBy(Long createdBy) { this.createdBy = createdBy; }
|
||||
public LocalDateTime getCreatedTime() { return createdTime; }
|
||||
public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; }
|
||||
}
|
||||
|
||||
@ -40,10 +40,10 @@ public class FundRequest extends BaseEntity {
|
||||
private String purpose;
|
||||
|
||||
/** 项目ID */
|
||||
private String projectId;
|
||||
private Long projectId;
|
||||
|
||||
/** 客户ID */
|
||||
private String customerId;
|
||||
private Long customerId;
|
||||
|
||||
/** 申请日期 */
|
||||
private LocalDateTime requestDate;
|
||||
@ -58,7 +58,7 @@ public class FundRequest extends BaseEntity {
|
||||
private Integer currentNode;
|
||||
|
||||
/** 审批人ID */
|
||||
private String approverId;
|
||||
private Long approverId;
|
||||
|
||||
/** 审批时间 */
|
||||
private LocalDateTime approvalTime;
|
||||
@ -141,19 +141,19 @@ public class FundRequest extends BaseEntity {
|
||||
this.purpose = purpose;
|
||||
}
|
||||
|
||||
public String getProjectId() {
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(String projectId) {
|
||||
public void setProjectId(Long projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getCustomerId() {
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(String customerId) {
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
@ -189,11 +189,11 @@ public class FundRequest extends BaseEntity {
|
||||
this.currentNode = currentNode;
|
||||
}
|
||||
|
||||
public String getApproverId() {
|
||||
public Long getApproverId() {
|
||||
return approverId;
|
||||
}
|
||||
|
||||
public void setApproverId(String approverId) {
|
||||
public void setApproverId(Long approverId) {
|
||||
this.approverId = approverId;
|
||||
}
|
||||
|
||||
|
||||
@ -112,20 +112,6 @@
|
||||
<artifactId>logstash-logback-encoder</artifactId>
|
||||
<version>7.4</version>
|
||||
</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>
|
||||
|
||||
<build>
|
||||
|
||||
@ -5,9 +5,6 @@ import com.fundplatform.sys.dto.LoginRequestDTO;
|
||||
import com.fundplatform.sys.service.AuthService;
|
||||
import com.fundplatform.sys.vo.LoginVO;
|
||||
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 org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -19,7 +16,6 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
/**
|
||||
* 认证Controller
|
||||
*/
|
||||
@Tag(name = "认证管理", description = "登录、登出、Token刷新等认证接口")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/auth")
|
||||
public class AuthController {
|
||||
@ -33,7 +29,6 @@ public class AuthController {
|
||||
/**
|
||||
* 用户登录
|
||||
*/
|
||||
@Operation(summary = "用户登录", description = "使用用户名和MD5加密密码登录,返回Token及用户信息")
|
||||
@PostMapping("/login")
|
||||
public Result<LoginVO> login(@Valid @RequestBody LoginRequestDTO request) {
|
||||
LoginVO vo = authService.login(request);
|
||||
@ -43,9 +38,8 @@ public class AuthController {
|
||||
/**
|
||||
* 用户登出
|
||||
*/
|
||||
@Operation(summary = "用户登出", description = "销毁当前用户Token,使会话失效")
|
||||
@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);
|
||||
return Result.success();
|
||||
}
|
||||
@ -53,9 +47,8 @@ public class AuthController {
|
||||
/**
|
||||
* 刷新Token
|
||||
*/
|
||||
@Operation(summary = "刷新Token", description = "延长当前Token的有效期")
|
||||
@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);
|
||||
return Result.success(vo);
|
||||
}
|
||||
@ -63,9 +56,8 @@ public class AuthController {
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
@Operation(summary = "获取当前用户信息", description = "根据Token中的用户ID获取用户详情")
|
||||
@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);
|
||||
return Result.success(vo);
|
||||
}
|
||||
|
||||
@ -28,8 +28,8 @@ public class ConfigController {
|
||||
* 创建参数
|
||||
*/
|
||||
@PostMapping
|
||||
public Result<String> create(@Valid @RequestBody ConfigDTO dto) {
|
||||
String id = configService.createConfig(dto);
|
||||
public Result<Long> create(@Valid @RequestBody ConfigDTO dto) {
|
||||
Long id = configService.createConfig(dto);
|
||||
return Result.success(id);
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ public class ConfigController {
|
||||
* 根据ID查询参数
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<ConfigVO> getById(@PathVariable String id) {
|
||||
public Result<ConfigVO> getById(@PathVariable Long id) {
|
||||
ConfigVO vo = configService.getConfigById(id);
|
||||
return Result.success(vo);
|
||||
}
|
||||
@ -94,7 +94,7 @@ public class ConfigController {
|
||||
* 删除参数
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> delete(@PathVariable String id) {
|
||||
public Result<Boolean> delete(@PathVariable Long id) {
|
||||
boolean result = configService.deleteConfig(id);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@ -4,9 +4,6 @@ import com.fundplatform.common.core.Result;
|
||||
import com.fundplatform.sys.dto.DeptDTO;
|
||||
import com.fundplatform.sys.service.DeptService;
|
||||
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 org.springframework.web.bind.annotation.*;
|
||||
|
||||
@ -15,7 +12,6 @@ import java.util.List;
|
||||
/**
|
||||
* 部门管理Controller
|
||||
*/
|
||||
@Tag(name = "部门管理", description = "部门的增删改查及树形结构查询")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/sys/dept")
|
||||
public class DeptController {
|
||||
@ -26,51 +22,44 @@ public class DeptController {
|
||||
this.deptService = deptService;
|
||||
}
|
||||
|
||||
@Operation(summary = "创建部门")
|
||||
@PostMapping
|
||||
public Result<String> create(@Valid @RequestBody DeptDTO dto) {
|
||||
String deptId = deptService.createDept(dto);
|
||||
public Result<Long> create(@Valid @RequestBody DeptDTO dto) {
|
||||
Long deptId = deptService.createDept(dto);
|
||||
return Result.success(deptId);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新部门信息")
|
||||
@PutMapping
|
||||
public Result<Boolean> update(@Valid @RequestBody DeptDTO dto) {
|
||||
boolean result = deptService.updateDept(dto);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据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);
|
||||
return Result.success(vo);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取部门树形结构")
|
||||
@GetMapping("/tree")
|
||||
public Result<List<DeptVO>> getTree() {
|
||||
List<DeptVO> tree = deptService.getDeptTree();
|
||||
return Result.success(tree);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询所有部门列表(扁平)")
|
||||
@GetMapping("/list")
|
||||
public Result<List<DeptVO>> listAll() {
|
||||
List<DeptVO> list = deptService.listAllDepts();
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除部门")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> delete(@Parameter(description = "部门ID") @PathVariable String id) {
|
||||
public Result<Boolean> delete(@PathVariable Long id) {
|
||||
boolean result = deptService.deleteDept(id);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新部门状态")
|
||||
@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);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@ -4,9 +4,6 @@ import com.fundplatform.common.core.Result;
|
||||
import com.fundplatform.sys.dto.MenuDTO;
|
||||
import com.fundplatform.sys.service.MenuService;
|
||||
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 org.springframework.web.bind.annotation.*;
|
||||
|
||||
@ -15,7 +12,6 @@ import java.util.List;
|
||||
/**
|
||||
* 菜单管理Controller
|
||||
*/
|
||||
@Tag(name = "菜单管理", description = "菜单和权限的增删改查")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/sys/menu")
|
||||
public class MenuController {
|
||||
@ -26,44 +22,38 @@ public class MenuController {
|
||||
this.menuService = menuService;
|
||||
}
|
||||
|
||||
@Operation(summary = "创建菜单/权限")
|
||||
@PostMapping
|
||||
public Result<String> create(@Valid @RequestBody MenuDTO dto) {
|
||||
String menuId = menuService.createMenu(dto);
|
||||
public Result<Long> create(@Valid @RequestBody MenuDTO dto) {
|
||||
Long menuId = menuService.createMenu(dto);
|
||||
return Result.success(menuId);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新菜单/权限信息")
|
||||
@PutMapping
|
||||
public Result<Boolean> update(@Valid @RequestBody MenuDTO dto) {
|
||||
boolean result = menuService.updateMenu(dto);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据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);
|
||||
return Result.success(vo);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取菜单树(全量)")
|
||||
@GetMapping("/tree")
|
||||
public Result<List<MenuVO>> getTree() {
|
||||
List<MenuVO> tree = menuService.getMenuTree();
|
||||
return Result.success(tree);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取用户菜单树", description = "根据用户ID获取其有权访问的菜单树")
|
||||
@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);
|
||||
return Result.success(tree);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除菜单/权限")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> delete(@Parameter(description = "菜单ID") @PathVariable String id) {
|
||||
public Result<Boolean> delete(@PathVariable Long id) {
|
||||
boolean result = menuService.deleteMenu(id);
|
||||
return Result.success(result);
|
||||
}
|
||||
@ -71,9 +61,8 @@ public class MenuController {
|
||||
/**
|
||||
* 获取用户权限标识列表
|
||||
*/
|
||||
@Operation(summary = "获取用户权限标识列表", description = "返回用户拥有的所有权限标识字符串")
|
||||
@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);
|
||||
return Result.success(permissions);
|
||||
}
|
||||
|
||||
@ -4,15 +4,11 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fundplatform.common.core.Result;
|
||||
import com.fundplatform.sys.data.entity.OperationLog;
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* 操作日志Controller
|
||||
*/
|
||||
@Tag(name = "操作日志", description = "系统操作日志查询与管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/log")
|
||||
public class OperationLogController {
|
||||
@ -26,33 +22,30 @@ public class OperationLogController {
|
||||
/**
|
||||
* 分页查询操作日志
|
||||
*/
|
||||
@Operation(summary = "分页查询操作日志")
|
||||
@GetMapping("/page")
|
||||
public Result<Page<OperationLog>> page(
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int pageNum,
|
||||
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize,
|
||||
@Parameter(description = "用户ID") @RequestParam(required = false) Long userId,
|
||||
@Parameter(description = "操作类型") @RequestParam(required = false) String operation,
|
||||
@Parameter(description = "开始时间(yyyy-MM-dd HH:mm:ss)") @RequestParam(required = false) String startTime,
|
||||
@Parameter(description = "结束时间(yyyy-MM-dd HH:mm:ss)") @RequestParam(required = false) String endTime) {
|
||||
@RequestParam(defaultValue = "1") int pageNum,
|
||||
@RequestParam(defaultValue = "10") int pageSize,
|
||||
@RequestParam(required = false) Long userId,
|
||||
@RequestParam(required = false) String operation,
|
||||
@RequestParam(required = false) String startTime,
|
||||
@RequestParam(required = false) String endTime) {
|
||||
return Result.success(operationLogService.pageLogs(pageNum, pageSize, userId, operation, startTime, endTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日志详情
|
||||
*/
|
||||
@Operation(summary = "获取操作日志详情")
|
||||
@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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理历史日志
|
||||
*/
|
||||
@Operation(summary = "清理历史操作日志", description = "删除N天前的操作日志,默认90天")
|
||||
@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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,16 +5,12 @@ import com.fundplatform.sys.dto.PasswordDTO;
|
||||
import com.fundplatform.sys.dto.ProfileDTO;
|
||||
import com.fundplatform.sys.service.UserService;
|
||||
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 org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 个人中心Controller
|
||||
*/
|
||||
@Tag(name = "个人中心", description = "当前登录用户的个人信息查询与修改")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/sys/profile")
|
||||
public class ProfileController {
|
||||
@ -28,9 +24,8 @@ public class ProfileController {
|
||||
/**
|
||||
* 获取个人信息
|
||||
*/
|
||||
@Operation(summary = "获取个人信息")
|
||||
@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);
|
||||
return Result.success(vo);
|
||||
}
|
||||
@ -38,10 +33,9 @@ public class ProfileController {
|
||||
/**
|
||||
* 更新个人信息
|
||||
*/
|
||||
@Operation(summary = "更新个人信息")
|
||||
@PutMapping
|
||||
public Result<Boolean> updateProfile(
|
||||
@Parameter(description = "当前登录用户ID") @RequestHeader("X-User-Id") String userId,
|
||||
@RequestHeader("X-User-Id") Long userId,
|
||||
@Valid @RequestBody ProfileDTO dto) {
|
||||
boolean result = userService.updateProfile(userId, dto);
|
||||
return Result.success(result);
|
||||
@ -50,10 +44,9 @@ public class ProfileController {
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
@Operation(summary = "修改密码", description = "需要提供旧密码和新密码(MD5加密后传输)")
|
||||
@PutMapping("/password")
|
||||
public Result<Boolean> updatePassword(
|
||||
@Parameter(description = "当前登录用户ID") @RequestHeader("X-User-Id") String userId,
|
||||
@RequestHeader("X-User-Id") Long userId,
|
||||
@Valid @RequestBody PasswordDTO dto) {
|
||||
boolean result = userService.updatePassword(userId, dto);
|
||||
return Result.success(result);
|
||||
|
||||
@ -5,9 +5,6 @@ import com.fundplatform.common.core.Result;
|
||||
import com.fundplatform.sys.dto.RoleDTO;
|
||||
import com.fundplatform.sys.service.RoleService;
|
||||
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 org.springframework.web.bind.annotation.*;
|
||||
|
||||
@ -16,7 +13,6 @@ import java.util.List;
|
||||
/**
|
||||
* 角色管理Controller
|
||||
*/
|
||||
@Tag(name = "角色管理", description = "角色的增删改查及菜单权限分配")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/sys/role")
|
||||
public class RoleController {
|
||||
@ -27,55 +23,48 @@ public class RoleController {
|
||||
this.roleService = roleService;
|
||||
}
|
||||
|
||||
@Operation(summary = "创建角色")
|
||||
@PostMapping
|
||||
public Result<String> create(@Valid @RequestBody RoleDTO dto) {
|
||||
String roleId = roleService.createRole(dto);
|
||||
public Result<Long> create(@Valid @RequestBody RoleDTO dto) {
|
||||
Long roleId = roleService.createRole(dto);
|
||||
return Result.success(roleId);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新角色信息")
|
||||
@PutMapping
|
||||
public Result<Boolean> update(@Valid @RequestBody RoleDTO dto) {
|
||||
boolean result = roleService.updateRole(dto);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据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);
|
||||
return Result.success(vo);
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询角色")
|
||||
@GetMapping("/page")
|
||||
public Result<Page<RoleVO>> page(
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int pageNum,
|
||||
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize,
|
||||
@Parameter(description = "角色名称(模糊查询)") @RequestParam(required = false) String roleName,
|
||||
@Parameter(description = "状态:0禁用 1启用") @RequestParam(required = false) Integer status) {
|
||||
@RequestParam(defaultValue = "1") int pageNum,
|
||||
@RequestParam(defaultValue = "10") int pageSize,
|
||||
@RequestParam(required = false) String roleName,
|
||||
@RequestParam(required = false) Integer status) {
|
||||
Page<RoleVO> page = roleService.pageRoles(pageNum, pageSize, roleName, status);
|
||||
return Result.success(page);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询所有角色列表")
|
||||
@GetMapping("/list")
|
||||
public Result<List<RoleVO>> listAll() {
|
||||
List<RoleVO> list = roleService.listAllRoles();
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除角色")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> delete(@Parameter(description = "角色ID") @PathVariable String id) {
|
||||
public Result<Boolean> delete(@PathVariable Long id) {
|
||||
boolean result = roleService.deleteRole(id);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新角色状态")
|
||||
@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);
|
||||
return Result.success(result);
|
||||
}
|
||||
@ -83,19 +72,17 @@ public class RoleController {
|
||||
/**
|
||||
* 获取角色菜单ID列表
|
||||
*/
|
||||
@Operation(summary = "获取角色的菜单ID列表")
|
||||
@GetMapping("/{id}/menus")
|
||||
public Result<List<String>> getRoleMenus(@Parameter(description = "角色ID") @PathVariable String id) {
|
||||
List<String> menuIds = roleService.getRoleMenus(id);
|
||||
public Result<List<Long>> getRoleMenus(@PathVariable Long id) {
|
||||
List<Long> menuIds = roleService.getRoleMenus(id);
|
||||
return Result.success(menuIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配菜单给角色
|
||||
*/
|
||||
@Operation(summary = "为角色分配菜单权限")
|
||||
@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);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@ -5,16 +5,12 @@ import com.fundplatform.common.core.Result;
|
||||
import com.fundplatform.sys.dto.TenantDTO;
|
||||
import com.fundplatform.sys.service.TenantService;
|
||||
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 org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 租户管理Controller
|
||||
*/
|
||||
@Tag(name = "租户管理", description = "平台租户的增删改查及状态管理(超级管理员使用)")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/sys/tenant")
|
||||
public class TenantController {
|
||||
@ -25,42 +21,36 @@ public class TenantController {
|
||||
this.tenantService = tenantService;
|
||||
}
|
||||
|
||||
@Operation(summary = "创建租户")
|
||||
@PostMapping
|
||||
public Result<String> create(@Valid @RequestBody TenantDTO dto) {
|
||||
public Result<Long> create(@Valid @RequestBody TenantDTO dto) {
|
||||
return Result.success(tenantService.createTenant(dto));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新租户信息")
|
||||
@PutMapping
|
||||
public Result<Boolean> update(@Valid @RequestBody TenantDTO dto) {
|
||||
return Result.success(tenantService.updateTenant(dto));
|
||||
}
|
||||
|
||||
@Operation(summary = "根据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));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询租户")
|
||||
@GetMapping("/page")
|
||||
public Result<Page<TenantVO>> page(
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int pageNum,
|
||||
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize,
|
||||
@Parameter(description = "关键词(租户名/编码/联系人模糊查询)") @RequestParam(required = false) String keyword) {
|
||||
@RequestParam(defaultValue = "1") int pageNum,
|
||||
@RequestParam(defaultValue = "10") int pageSize,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
return Result.success(tenantService.pageTenants(pageNum, pageSize, keyword));
|
||||
}
|
||||
|
||||
@Operation(summary = "删除租户")
|
||||
@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));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新租户状态")
|
||||
@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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,16 +5,12 @@ import com.fundplatform.common.core.Result;
|
||||
import com.fundplatform.sys.dto.UserDTO;
|
||||
import com.fundplatform.sys.service.UserService;
|
||||
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 org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 用户管理Controller
|
||||
*/
|
||||
@Tag(name = "用户管理", description = "用户的增删改查及状态管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/sys/user")
|
||||
public class UserController {
|
||||
@ -28,17 +24,15 @@ public class UserController {
|
||||
/**
|
||||
* 创建用户
|
||||
*/
|
||||
@Operation(summary = "创建用户")
|
||||
@PostMapping
|
||||
public Result<String> create(@Valid @RequestBody UserDTO dto) {
|
||||
String userId = userService.createUser(dto);
|
||||
public Result<Long> create(@Valid @RequestBody UserDTO dto) {
|
||||
Long userId = userService.createUser(dto);
|
||||
return Result.success(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户
|
||||
*/
|
||||
@Operation(summary = "更新用户信息")
|
||||
@PutMapping
|
||||
public Result<Boolean> update(@Valid @RequestBody UserDTO dto) {
|
||||
boolean result = userService.updateUser(dto);
|
||||
@ -48,9 +42,8 @@ public class UserController {
|
||||
/**
|
||||
* 根据ID查询用户
|
||||
*/
|
||||
@Operation(summary = "根据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);
|
||||
return Result.success(vo);
|
||||
}
|
||||
@ -58,14 +51,13 @@ public class UserController {
|
||||
/**
|
||||
* 分页查询用户
|
||||
*/
|
||||
@Operation(summary = "分页查询用户")
|
||||
@GetMapping("/page")
|
||||
public Result<Page<UserVO>> page(
|
||||
@Parameter(description = "页码,从1开始") @RequestParam(defaultValue = "1") int pageNum,
|
||||
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int pageSize,
|
||||
@Parameter(description = "用户名(模糊查询)") @RequestParam(required = false) String username,
|
||||
@Parameter(description = "状态:0禁用 1启用") @RequestParam(required = false) Integer status,
|
||||
@Parameter(description = "部门ID") @RequestParam(required = false) String deptId) {
|
||||
@RequestParam(defaultValue = "1") int pageNum,
|
||||
@RequestParam(defaultValue = "10") int pageSize,
|
||||
@RequestParam(required = false) String username,
|
||||
@RequestParam(required = false) Integer status,
|
||||
@RequestParam(required = false) Long deptId) {
|
||||
Page<UserVO> page = userService.pageUsers(pageNum, pageSize, username, status, deptId);
|
||||
return Result.success(page);
|
||||
}
|
||||
@ -73,9 +65,8 @@ public class UserController {
|
||||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
@Operation(summary = "删除用户(逻辑删除)")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> delete(@Parameter(description = "用户ID") @PathVariable String id) {
|
||||
public Result<Boolean> delete(@PathVariable Long id) {
|
||||
boolean result = userService.deleteUser(id);
|
||||
return Result.success(result);
|
||||
}
|
||||
@ -83,9 +74,8 @@ public class UserController {
|
||||
/**
|
||||
* 批量删除用户
|
||||
*/
|
||||
@Operation(summary = "批量删除用户")
|
||||
@DeleteMapping("/batch")
|
||||
public Result<Boolean> batchDelete(@RequestBody String[] ids) {
|
||||
public Result<Boolean> batchDelete(@RequestBody Long[] ids) {
|
||||
boolean result = userService.batchDeleteUsers(ids);
|
||||
return Result.success(result);
|
||||
}
|
||||
@ -93,9 +83,8 @@ public class UserController {
|
||||
/**
|
||||
* 重置密码
|
||||
*/
|
||||
@Operation(summary = "重置用户密码", description = "将用户密码重置为系统默认密码")
|
||||
@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);
|
||||
return Result.success(result);
|
||||
}
|
||||
@ -103,9 +92,8 @@ public class UserController {
|
||||
/**
|
||||
* 更新用户状态
|
||||
*/
|
||||
@Operation(summary = "更新用户状态")
|
||||
@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);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.fundplatform.sys.data.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
@ -11,10 +12,10 @@ import java.time.LocalDateTime;
|
||||
@TableName("sys_operation_log")
|
||||
public class OperationLog {
|
||||
|
||||
@TableId
|
||||
private String logId;
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long logId;
|
||||
|
||||
private String userId;
|
||||
private Long userId;
|
||||
|
||||
private String username;
|
||||
|
||||
@ -36,19 +37,19 @@ public class OperationLog {
|
||||
|
||||
private String errorMsg;
|
||||
|
||||
public String getLogId() {
|
||||
public Long getLogId() {
|
||||
return logId;
|
||||
}
|
||||
|
||||
public void setLogId(String logId) {
|
||||
public void setLogId(Long logId) {
|
||||
this.logId = logId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import com.fundplatform.common.core.BaseEntity;
|
||||
@TableName("sys_dept")
|
||||
public class SysDept extends BaseEntity {
|
||||
|
||||
private String parentId;
|
||||
private Long parentId;
|
||||
private String deptCode;
|
||||
private String deptName;
|
||||
private String deptLeader;
|
||||
@ -18,11 +18,11 @@ public class SysDept extends BaseEntity {
|
||||
private Integer sortOrder;
|
||||
private Integer status;
|
||||
|
||||
public String getParentId() {
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import com.fundplatform.common.core.BaseEntity;
|
||||
@TableName("sys_menu")
|
||||
public class SysMenu extends BaseEntity {
|
||||
|
||||
private String parentId;
|
||||
private Long parentId;
|
||||
private String menuName;
|
||||
private Integer menuType;
|
||||
private String menuPath;
|
||||
@ -20,11 +20,11 @@ public class SysMenu extends BaseEntity {
|
||||
private Integer visible;
|
||||
private Integer status;
|
||||
|
||||
public String getParentId() {
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
|
||||
@ -14,13 +14,13 @@ public class SysTenant extends BaseEntity {
|
||||
|
||||
/** 租户表不需要tenant_id字段 */
|
||||
@TableField(exist = false)
|
||||
private String tenantId;
|
||||
private Long tenantId;
|
||||
|
||||
public String getTenantId() {
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(String tenantId) {
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ public class SysUser extends BaseEntity {
|
||||
private String realName;
|
||||
private String phone;
|
||||
private String email;
|
||||
private String deptId;
|
||||
private Long deptId;
|
||||
private Integer status;
|
||||
private String avatar;
|
||||
|
||||
@ -59,11 +59,11 @@ public class SysUser extends BaseEntity {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getDeptId() {
|
||||
public Long getDeptId() {
|
||||
return deptId;
|
||||
}
|
||||
|
||||
public void setDeptId(String deptId) {
|
||||
public void setDeptId(Long deptId) {
|
||||
this.deptId = deptId;
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import jakarta.validation.constraints.NotBlank;
|
||||
*/
|
||||
public class ConfigDTO {
|
||||
|
||||
private String id;
|
||||
private Long id;
|
||||
|
||||
@NotBlank(message = "参数键不能为空")
|
||||
private String configKey;
|
||||
@ -28,11 +28,11 @@ public class ConfigDTO {
|
||||
|
||||
private Integer sortOrder;
|
||||
|
||||
public String getId() {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
@ -8,9 +8,9 @@ import jakarta.validation.constraints.Size;
|
||||
*/
|
||||
public class DeptDTO {
|
||||
|
||||
private String id;
|
||||
private Long id;
|
||||
|
||||
private String parentId;
|
||||
private Long parentId;
|
||||
|
||||
@NotBlank(message = "部门编码不能为空")
|
||||
@Size(max = 50, message = "部门编码不能超过50个字符")
|
||||
@ -33,19 +33,19 @@ public class DeptDTO {
|
||||
|
||||
private String remark;
|
||||
|
||||
public String getId() {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.fundplatform.sys.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 登录请求DTO
|
||||
@ -14,12 +13,6 @@ public class LoginRequestDTO {
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 租户ID,登录时必须指定,以确保用户名在正确租户范围内唯一匹配,防止跨租户登录混乱。
|
||||
*/
|
||||
@NotNull(message = "租户ID不能为空")
|
||||
private Long tenantId;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
@ -35,12 +28,4 @@ public class LoginRequestDTO {
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,9 +8,9 @@ import jakarta.validation.constraints.Size;
|
||||
*/
|
||||
public class MenuDTO {
|
||||
|
||||
private String id;
|
||||
private Long id;
|
||||
|
||||
private String parentId;
|
||||
private Long parentId;
|
||||
|
||||
@NotBlank(message = "菜单名称不能为空")
|
||||
@Size(max = 50, message = "菜单名称不能超过50个字符")
|
||||
@ -38,19 +38,19 @@ public class MenuDTO {
|
||||
|
||||
private String remark;
|
||||
|
||||
public String getId() {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import jakarta.validation.constraints.Size;
|
||||
*/
|
||||
public class RoleDTO {
|
||||
|
||||
private String id;
|
||||
private Long id;
|
||||
|
||||
@NotBlank(message = "角色编码不能为空")
|
||||
@Size(max = 50, message = "角色编码不能超过50个字符")
|
||||
@ -26,11 +26,11 @@ public class RoleDTO {
|
||||
|
||||
private String remark;
|
||||
|
||||
public String getId() {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ import java.time.LocalDateTime;
|
||||
*/
|
||||
public class TenantDTO {
|
||||
|
||||
private String id;
|
||||
private Long id;
|
||||
|
||||
@NotBlank(message = "租户编码不能为空")
|
||||
@Size(max = 50, message = "租户编码长度不能超过50")
|
||||
@ -41,11 +41,11 @@ public class TenantDTO {
|
||||
@Size(max = 500, message = "备注长度不能超过500")
|
||||
private String remark;
|
||||
|
||||
public String getId() {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import jakarta.validation.constraints.Size;
|
||||
*/
|
||||
public class UserDTO {
|
||||
|
||||
private String id;
|
||||
private Long id;
|
||||
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@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 = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
private String deptId;
|
||||
private Long deptId;
|
||||
|
||||
private Integer status;
|
||||
|
||||
@ -36,11 +36,11 @@ public class UserDTO {
|
||||
|
||||
private String remark;
|
||||
|
||||
public String getId() {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ -84,11 +84,11 @@ public class UserDTO {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getDeptId() {
|
||||
public Long getDeptId() {
|
||||
return deptId;
|
||||
}
|
||||
|
||||
public void setDeptId(String deptId) {
|
||||
public void setDeptId(Long deptId) {
|
||||
this.deptId = deptId;
|
||||
}
|
||||
|
||||
|
||||
@ -17,15 +17,15 @@ public interface AuthService {
|
||||
/**
|
||||
* 用户登出
|
||||
*/
|
||||
void logout(String userId);
|
||||
void logout(Long userId);
|
||||
|
||||
/**
|
||||
* 刷新Token
|
||||
*/
|
||||
LoginVO refreshToken(String userId);
|
||||
LoginVO refreshToken(Long userId);
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
UserVO getUserInfo(String userId);
|
||||
UserVO getUserInfo(Long userId);
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ public interface ConfigService {
|
||||
/**
|
||||
* 创建参数
|
||||
*/
|
||||
String createConfig(ConfigDTO dto);
|
||||
Long createConfig(ConfigDTO dto);
|
||||
|
||||
/**
|
||||
* 更新参数
|
||||
@ -25,7 +25,7 @@ public interface ConfigService {
|
||||
/**
|
||||
* 根据ID查询参数
|
||||
*/
|
||||
ConfigVO getConfigById(String id);
|
||||
ConfigVO getConfigById(Long id);
|
||||
|
||||
/**
|
||||
* 根据参数键查询值
|
||||
@ -50,7 +50,7 @@ public interface ConfigService {
|
||||
/**
|
||||
* 删除参数
|
||||
*/
|
||||
boolean deleteConfig(String id);
|
||||
boolean deleteConfig(Long id);
|
||||
|
||||
/**
|
||||
* 批量更新参数值
|
||||
|
||||
@ -10,17 +10,17 @@ import java.util.List;
|
||||
*/
|
||||
public interface DeptService {
|
||||
|
||||
String createDept(DeptDTO dto);
|
||||
Long createDept(DeptDTO dto);
|
||||
|
||||
boolean updateDept(DeptDTO dto);
|
||||
|
||||
DeptVO getDeptById(String id);
|
||||
DeptVO getDeptById(Long id);
|
||||
|
||||
List<DeptVO> getDeptTree();
|
||||
|
||||
List<DeptVO> listAllDepts();
|
||||
|
||||
boolean deleteDept(String id);
|
||||
boolean deleteDept(Long id);
|
||||
|
||||
boolean updateStatus(String id, Integer status);
|
||||
boolean updateStatus(Long id, Integer status);
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ public interface MenuService {
|
||||
/**
|
||||
* 创建菜单
|
||||
*/
|
||||
String createMenu(MenuDTO dto);
|
||||
Long createMenu(MenuDTO dto);
|
||||
|
||||
/**
|
||||
* 更新菜单
|
||||
@ -23,7 +23,7 @@ public interface MenuService {
|
||||
/**
|
||||
* 根据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);
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ public interface RoleService {
|
||||
/**
|
||||
* 创建角色
|
||||
*/
|
||||
String createRole(RoleDTO dto);
|
||||
Long createRole(RoleDTO dto);
|
||||
|
||||
/**
|
||||
* 更新角色
|
||||
@ -24,7 +24,7 @@ public interface RoleService {
|
||||
/**
|
||||
* 根据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列表
|
||||
*/
|
||||
List<String> getRoleMenus(String roleId);
|
||||
List<Long> getRoleMenus(Long roleId);
|
||||
|
||||
/**
|
||||
* 分配菜单权限
|
||||
*/
|
||||
boolean assignMenus(String roleId, List<String> menuIds);
|
||||
boolean assignMenus(Long roleId, List<Long> menuIds);
|
||||
}
|
||||
|
||||
@ -9,15 +9,15 @@ import com.fundplatform.sys.vo.TenantVO;
|
||||
*/
|
||||
public interface TenantService {
|
||||
|
||||
String createTenant(TenantDTO dto);
|
||||
Long createTenant(TenantDTO dto);
|
||||
|
||||
boolean updateTenant(TenantDTO dto);
|
||||
|
||||
TenantVO getTenantById(String id);
|
||||
TenantVO getTenantById(Long id);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ public interface UserService {
|
||||
* @param dto 用户DTO
|
||||
* @return 用户ID
|
||||
*/
|
||||
String createUser(UserDTO dto);
|
||||
Long createUser(UserDTO dto);
|
||||
|
||||
/**
|
||||
* 更新用户
|
||||
@ -33,7 +33,7 @@ public interface UserService {
|
||||
* @param id 用户ID
|
||||
* @return 用户VO
|
||||
*/
|
||||
UserVO getUserById(String id);
|
||||
UserVO getUserById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询用户
|
||||
@ -45,7 +45,7 @@ public interface UserService {
|
||||
* @param deptId 部门ID
|
||||
* @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
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean deleteUser(String id);
|
||||
boolean deleteUser(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除用户
|
||||
@ -61,7 +61,7 @@ public interface UserService {
|
||||
* @param ids 用户ID列表
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean batchDeleteUsers(String[] ids);
|
||||
boolean batchDeleteUsers(Long[] ids);
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
@ -69,7 +69,7 @@ public interface UserService {
|
||||
* @param id 用户ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean resetPassword(String id);
|
||||
boolean resetPassword(Long id);
|
||||
|
||||
/**
|
||||
* 更新用户状态
|
||||
@ -78,7 +78,7 @@ public interface UserService {
|
||||
* @param status 状态
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean updateStatus(String id, Integer status);
|
||||
boolean updateStatus(Long id, Integer status);
|
||||
|
||||
/**
|
||||
* 更新个人信息
|
||||
@ -87,7 +87,7 @@ public interface UserService {
|
||||
* @param dto 个人信息DTO
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean updateProfile(String userId, ProfileDTO dto);
|
||||
boolean updateProfile(Long userId, ProfileDTO dto);
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
@ -96,5 +96,5 @@ public interface UserService {
|
||||
* @param dto 密码DTO
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean updatePassword(String userId, PasswordDTO dto);
|
||||
boolean updatePassword(Long userId, PasswordDTO dto);
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ package com.fundplatform.sys.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
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.service.SysUserDataService;
|
||||
import com.fundplatform.sys.dto.LoginRequestDTO;
|
||||
@ -29,78 +29,72 @@ public class AuthServiceImpl implements AuthService {
|
||||
|
||||
@Override
|
||||
public LoginVO login(LoginRequestDTO request) {
|
||||
// 安全修复:登录时尚未建立租户上下文(白名单路径不经过 TokenAuthFilter),
|
||||
// 必须使用 TenantIgnoreHelper 跳过 MyBatis-Plus 自动租户过滤,
|
||||
// 同时显式在查询条件中加入 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);
|
||||
});
|
||||
|
||||
// 查询用户
|
||||
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysUser::getUsername, request.getUsername());
|
||||
wrapper.eq(SysUser::getDeleted, 0);
|
||||
SysUser user = userDataService.getOne(wrapper);
|
||||
|
||||
if (user == null) {
|
||||
log.error("登录失败 - 用户不存在:username={}, tenantId={}", request.getUsername(), request.getTenantId());
|
||||
log.error("登录失败 - 用户不存在:username={}", request.getUsername());
|
||||
throw new RuntimeException("用户名或密码错误");
|
||||
}
|
||||
|
||||
|
||||
// 打印接收到的密码和数据库存储的密码(用于调试)
|
||||
log.info("登录验证 - 前端传来的 MD5 密码:{}, 数据库存储的 MD5 密码:{}", request.getPassword(), user.getPassword());
|
||||
|
||||
|
||||
// 直接比对 MD5 值(前端已加密,数据库也是 MD5,无需再次加密)
|
||||
if (!request.getPassword().equals(user.getPassword())) {
|
||||
log.error("登录失败 - 密码错误:username={}", request.getUsername());
|
||||
throw new RuntimeException("用户名或密码错误");
|
||||
}
|
||||
|
||||
|
||||
// 检查用户状态
|
||||
if (user.getStatus() != 1) {
|
||||
throw new RuntimeException("用户已被禁用");
|
||||
}
|
||||
|
||||
|
||||
// 使用 UUID + Redis 生成 Token
|
||||
String token = tokenService.generateToken(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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout(String userId) {
|
||||
public void logout(Long userId) {
|
||||
// 清除用户所有Token(强制登出所有设备)
|
||||
// 如果只需要登出当前设备,需要从前端传递token
|
||||
tokenService.deleteAllUserTokens(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginVO refreshToken(String userId) {
|
||||
public LoginVO refreshToken(Long userId) {
|
||||
SysUser user = userDataService.getById(userId);
|
||||
if (user == null) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
|
||||
|
||||
// 检查用户状态
|
||||
if (user.getStatus() != 1) {
|
||||
throw new RuntimeException("用户已被禁用");
|
||||
}
|
||||
|
||||
|
||||
// 生成新Token
|
||||
String token = tokenService.generateToken(user.getId(), user.getUsername(), user.getTenantId());
|
||||
|
||||
|
||||
return new LoginVO(user.getId(), user.getUsername(), token, user.getTenantId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserVO getUserInfo(String userId) {
|
||||
public UserVO getUserInfo(Long userId) {
|
||||
SysUser user = userDataService.getById(userId);
|
||||
if (user == null) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
|
||||
|
||||
UserVO vo = new UserVO();
|
||||
vo.setId(user.getId());
|
||||
vo.setUsername(user.getUsername());
|
||||
@ -113,7 +107,7 @@ public class AuthServiceImpl implements AuthService {
|
||||
vo.setTenantId(user.getTenantId());
|
||||
vo.setCreatedTime(user.getCreatedTime());
|
||||
vo.setUpdatedTime(user.getUpdatedTime());
|
||||
|
||||
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String createConfig(ConfigDTO dto) {
|
||||
public Long createConfig(ConfigDTO dto) {
|
||||
// 检查key是否已存在
|
||||
LambdaQueryWrapper<SysConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysConfig::getConfigKey, dto.getConfigKey());
|
||||
@ -87,7 +87,7 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigVO getConfigById(String id) {
|
||||
public ConfigVO getConfigById(Long id) {
|
||||
SysConfig config = configDataService.getById(id);
|
||||
if (config == null || config.getDeleted() == 1) {
|
||||
throw new RuntimeException("参数不存在");
|
||||
@ -157,7 +157,7 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean deleteConfig(String id) {
|
||||
public boolean deleteConfig(Long id) {
|
||||
SysConfig existing = configDataService.getById(id);
|
||||
if (existing == null || existing.getDeleted() == 1) {
|
||||
throw new RuntimeException("参数不存在");
|
||||
|
||||
@ -34,7 +34,7 @@ public class DeptServiceImpl implements DeptService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String createDept(DeptDTO dto) {
|
||||
public Long createDept(DeptDTO dto) {
|
||||
LambdaQueryWrapper<SysDept> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysDept::getDeptCode, dto.getDeptCode());
|
||||
wrapper.eq(SysDept::getDeleted, 0);
|
||||
@ -43,7 +43,7 @@ public class DeptServiceImpl implements DeptService {
|
||||
}
|
||||
|
||||
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.setDeptName(dto.getDeptName());
|
||||
dept.setDeptLeader(dto.getDeptLeader());
|
||||
@ -88,7 +88,7 @@ public class DeptServiceImpl implements DeptService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeptVO getDeptById(String id) {
|
||||
public DeptVO getDeptById(Long id) {
|
||||
SysDept dept = deptDataService.getById(id);
|
||||
if (dept == null || dept.getDeleted() == 1) {
|
||||
throw new RuntimeException("部门不存在");
|
||||
@ -104,7 +104,7 @@ public class DeptServiceImpl implements DeptService {
|
||||
wrapper.orderByAsc(SysDept::getSortOrder);
|
||||
|
||||
List<SysDept> depts = deptDataService.list(wrapper);
|
||||
return buildDeptTree(depts, "0");
|
||||
return buildDeptTree(depts, 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -117,7 +117,7 @@ public class DeptServiceImpl implements DeptService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean deleteDept(String id) {
|
||||
public boolean deleteDept(Long id) {
|
||||
LambdaQueryWrapper<SysDept> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysDept::getParentId, id);
|
||||
wrapper.eq(SysDept::getDeleted, 0);
|
||||
@ -136,7 +136,7 @@ public class DeptServiceImpl implements DeptService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateStatus(String id, Integer status) {
|
||||
public boolean updateStatus(Long id, Integer status) {
|
||||
LambdaUpdateWrapper<SysDept> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(SysDept::getId, id);
|
||||
wrapper.set(SysDept::getStatus, status);
|
||||
@ -146,10 +146,10 @@ public class DeptServiceImpl implements DeptService {
|
||||
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<>();
|
||||
|
||||
Map<String, List<SysDept>> deptMap = depts.stream()
|
||||
Map<Long, List<SysDept>> deptMap = depts.stream()
|
||||
.collect(Collectors.groupingBy(SysDept::getParentId));
|
||||
|
||||
List<SysDept> rootDepts = deptMap.getOrDefault(parentId, new ArrayList<>());
|
||||
|
||||
@ -34,9 +34,9 @@ public class MenuServiceImpl implements MenuService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String createMenu(MenuDTO dto) {
|
||||
public Long createMenu(MenuDTO dto) {
|
||||
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.setMenuType(dto.getMenuType() != null ? dto.getMenuType() : 1);
|
||||
menu.setMenuPath(dto.getMenuPath());
|
||||
@ -86,7 +86,7 @@ public class MenuServiceImpl implements MenuService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuVO getMenuById(String id) {
|
||||
public MenuVO getMenuById(Long id) {
|
||||
SysMenu menu = menuDataService.getById(id);
|
||||
if (menu == null || menu.getDeleted() == 1) {
|
||||
throw new RuntimeException("菜单不存在");
|
||||
@ -102,18 +102,18 @@ public class MenuServiceImpl implements MenuService {
|
||||
wrapper.orderByAsc(SysMenu::getSortOrder);
|
||||
|
||||
List<SysMenu> menus = menuDataService.list(wrapper);
|
||||
return buildMenuTree(menus, "0");
|
||||
return buildMenuTree(menus, 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MenuVO> getUserMenuTree(String userId) {
|
||||
public List<MenuVO> getUserMenuTree(Long userId) {
|
||||
// TODO: 根据用户角色查询菜单
|
||||
return getMenuTree();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean deleteMenu(String id) {
|
||||
public boolean deleteMenu(Long id) {
|
||||
// 检查是否有子菜单
|
||||
LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysMenu::getParentId, id);
|
||||
@ -132,7 +132,7 @@ public class MenuServiceImpl implements MenuService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUserPermissions(String userId) {
|
||||
public List<String> getUserPermissions(Long userId) {
|
||||
// 查询所有启用的菜单/按钮
|
||||
LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysMenu::getDeleted, 0);
|
||||
@ -150,10 +150,10 @@ public class MenuServiceImpl implements MenuService {
|
||||
.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<>();
|
||||
|
||||
Map<String, List<SysMenu>> menuMap = menus.stream()
|
||||
Map<Long, List<SysMenu>> menuMap = menus.stream()
|
||||
.collect(Collectors.groupingBy(SysMenu::getParentId));
|
||||
|
||||
List<SysMenu> rootMenus = menuMap.getOrDefault(parentId, new ArrayList<>());
|
||||
|
||||
@ -33,7 +33,7 @@ public class RoleServiceImpl implements RoleService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String createRole(RoleDTO dto) {
|
||||
public Long createRole(RoleDTO dto) {
|
||||
// 检查角色编码是否存在
|
||||
LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysRole::getRoleCode, dto.getRoleCode());
|
||||
@ -82,7 +82,7 @@ public class RoleServiceImpl implements RoleService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleVO getRoleById(String id) {
|
||||
public RoleVO getRoleById(Long id) {
|
||||
SysRole role = roleDataService.getById(id);
|
||||
if (role == null || role.getDeleted() == 1) {
|
||||
throw new RuntimeException("角色不存在");
|
||||
@ -122,7 +122,7 @@ public class RoleServiceImpl implements RoleService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean deleteRole(String id) {
|
||||
public boolean deleteRole(Long id) {
|
||||
LambdaUpdateWrapper<SysRole> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(SysRole::getId, id);
|
||||
wrapper.set(SysRole::getDeleted, 1);
|
||||
@ -134,7 +134,7 @@ public class RoleServiceImpl implements RoleService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateStatus(String id, Integer status) {
|
||||
public boolean updateStatus(Long id, Integer status) {
|
||||
LambdaUpdateWrapper<SysRole> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(SysRole::getId, id);
|
||||
wrapper.set(SysRole::getStatus, status);
|
||||
@ -145,7 +145,7 @@ public class RoleServiceImpl implements RoleService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRoleMenus(String roleId) {
|
||||
public List<Long> getRoleMenus(Long roleId) {
|
||||
// TODO: 从sys_role_menu表查询角色关联的菜单ID
|
||||
// 目前返回空列表
|
||||
log.info("获取角色菜单: roleId={}", roleId);
|
||||
@ -154,7 +154,7 @@ public class RoleServiceImpl implements RoleService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean assignMenus(String roleId, List<String> menuIds) {
|
||||
public boolean assignMenus(Long roleId, List<Long> menuIds) {
|
||||
// TODO: 实现角色菜单关联
|
||||
log.info("分配角色菜单: roleId={}, menuIds={}", roleId, menuIds);
|
||||
return true;
|
||||
|
||||
@ -30,7 +30,7 @@ public class TenantServiceImpl implements TenantService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String createTenant(TenantDTO dto) {
|
||||
public Long createTenant(TenantDTO dto) {
|
||||
// 检查编码是否重复
|
||||
LambdaQueryWrapper<SysTenant> checkWrapper = new LambdaQueryWrapper<>();
|
||||
checkWrapper.eq(SysTenant::getTenantCode, dto.getTenantCode());
|
||||
@ -101,7 +101,7 @@ public class TenantServiceImpl implements TenantService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TenantVO getTenantById(String id) {
|
||||
public TenantVO getTenantById(Long id) {
|
||||
SysTenant tenant = tenantDataService.getById(id);
|
||||
if (tenant == null || tenant.getDeleted() == 1) {
|
||||
throw new RuntimeException("租户不存在");
|
||||
@ -134,7 +134,7 @@ public class TenantServiceImpl implements TenantService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean deleteTenant(String id) {
|
||||
public boolean deleteTenant(Long id) {
|
||||
SysTenant existing = tenantDataService.getById(id);
|
||||
if (existing == null || existing.getDeleted() == 1) {
|
||||
throw new RuntimeException("租户不存在");
|
||||
@ -156,7 +156,7 @@ public class TenantServiceImpl implements TenantService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateStatus(String id, Integer status) {
|
||||
public boolean updateStatus(Long id, Integer status) {
|
||||
SysTenant existing = tenantDataService.getById(id);
|
||||
if (existing == null || existing.getDeleted() == 1) {
|
||||
throw new RuntimeException("租户不存在");
|
||||
|
||||
@ -38,7 +38,7 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String createUser(UserDTO dto) {
|
||||
public Long createUser(UserDTO dto) {
|
||||
// 检查用户名是否存在
|
||||
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysUser::getUsername, dto.getUsername());
|
||||
@ -47,10 +47,13 @@ public class UserServiceImpl implements UserService {
|
||||
throw new RuntimeException("用户名已存在");
|
||||
}
|
||||
|
||||
// 创建用户,密码由前端 MD5 加密后传入,直接存储
|
||||
// 创建用户,密码使用 MD5 加密
|
||||
SysUser user = new SysUser();
|
||||
String rawPassword = dto.getPassword();
|
||||
String md5Password = Md5Util.encrypt(rawPassword);
|
||||
log.info("创建用户 - 原始密码:{}, MD5 加密后:{}", rawPassword, md5Password);
|
||||
user.setUsername(dto.getUsername());
|
||||
user.setPassword(dto.getPassword());
|
||||
user.setPassword(md5Password);
|
||||
user.setRealName(dto.getRealName());
|
||||
user.setPhone(dto.getPhone());
|
||||
user.setEmail(dto.getEmail());
|
||||
@ -97,8 +100,10 @@ public class UserServiceImpl implements UserService {
|
||||
user.setUsername(dto.getUsername());
|
||||
}
|
||||
if (StringUtils.hasText(dto.getPassword())) {
|
||||
// 密码由前端 MD5 加密后传入,直接存储
|
||||
user.setPassword(dto.getPassword());
|
||||
String rawPassword = dto.getPassword();
|
||||
String md5Password = Md5Util.encrypt(rawPassword);
|
||||
log.info("更新用户密码 - 原始密码:{}, MD5 加密后:{}", rawPassword, md5Password);
|
||||
user.setPassword(md5Password);
|
||||
}
|
||||
user.setRealName(dto.getRealName());
|
||||
user.setPhone(dto.getPhone());
|
||||
@ -115,7 +120,7 @@ public class UserServiceImpl implements UserService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserVO getUserById(String id) {
|
||||
public UserVO getUserById(Long id) {
|
||||
SysUser user = userDataService.getById(id);
|
||||
if (user == null || user.getDeleted() == 1) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
@ -124,7 +129,7 @@ public class UserServiceImpl implements UserService {
|
||||
}
|
||||
|
||||
@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);
|
||||
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysUser::getDeleted, 0);
|
||||
@ -150,7 +155,7 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean deleteUser(String id) {
|
||||
public boolean deleteUser(Long id) {
|
||||
LambdaUpdateWrapper<SysUser> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(SysUser::getId, id);
|
||||
wrapper.set(SysUser::getDeleted, 1);
|
||||
@ -162,7 +167,7 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean batchDeleteUsers(String[] ids) {
|
||||
public boolean batchDeleteUsers(Long[] ids) {
|
||||
if (ids == null || ids.length == 0) {
|
||||
return false;
|
||||
}
|
||||
@ -177,7 +182,7 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean resetPassword(String id) {
|
||||
public boolean resetPassword(Long id) {
|
||||
String defaultPassword = "123456";
|
||||
String md5Password = Md5Util.encrypt(defaultPassword);
|
||||
log.info("重置用户密码 - userId={}, 原始密码:{}, MD5: {}", id, defaultPassword, md5Password);
|
||||
@ -192,7 +197,7 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateStatus(String id, Integer status) {
|
||||
public boolean updateStatus(Long id, Integer status) {
|
||||
LambdaUpdateWrapper<SysUser> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(SysUser::getId, id);
|
||||
wrapper.set(SysUser::getStatus, status);
|
||||
@ -204,7 +209,7 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateProfile(String userId, ProfileDTO dto) {
|
||||
public boolean updateProfile(Long userId, ProfileDTO dto) {
|
||||
SysUser user = userDataService.getById(userId);
|
||||
if (user == null || user.getDeleted() == 1) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
@ -227,7 +232,7 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updatePassword(String userId, PasswordDTO dto) {
|
||||
public boolean updatePassword(Long userId, PasswordDTO dto) {
|
||||
// 验证新密码和确认密码一致
|
||||
if (!dto.getNewPassword().equals(dto.getConfirmPassword())) {
|
||||
throw new RuntimeException("新密码和确认密码不一致");
|
||||
@ -238,15 +243,16 @@ public class UserServiceImpl implements UserService {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
|
||||
// 验证旧密码(前端已 MD5,直接与数据库存储的 MD5 值比对)
|
||||
if (!dto.getOldPassword().equals(user.getPassword())) {
|
||||
// 验证旧密码
|
||||
if (!Md5Util.matches(dto.getOldPassword(), user.getPassword())) {
|
||||
log.error("修改密码失败 - 旧密码错误:userId={}", userId);
|
||||
throw new RuntimeException("旧密码错误");
|
||||
}
|
||||
|
||||
// 更新密码(前端已 MD5 加密,直接存储)
|
||||
String md5NewPassword = dto.getNewPassword();
|
||||
log.info("修改用户密码 - userId={}", userId);
|
||||
|
||||
// 更新密码
|
||||
String rawNewPassword = dto.getNewPassword();
|
||||
String md5NewPassword = Md5Util.encrypt(rawNewPassword);
|
||||
log.info("修改用户密码 - userId={}, 原始新密码:{}, MD5: {}", userId, rawNewPassword, md5NewPassword);
|
||||
LambdaUpdateWrapper<SysUser> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(SysUser::getId, userId);
|
||||
wrapper.set(SysUser::getPassword, md5NewPassword);
|
||||
|
||||
@ -7,7 +7,7 @@ import java.time.LocalDateTime;
|
||||
*/
|
||||
public class ConfigVO {
|
||||
|
||||
private String id;
|
||||
private Long id;
|
||||
private String configKey;
|
||||
private String configValue;
|
||||
private String configType;
|
||||
@ -20,11 +20,11 @@ public class ConfigVO {
|
||||
private LocalDateTime createdTime;
|
||||
private LocalDateTime updatedTime;
|
||||
|
||||
public String getId() {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
@ -8,8 +8,8 @@ import java.util.List;
|
||||
*/
|
||||
public class DeptVO {
|
||||
|
||||
private String id;
|
||||
private String parentId;
|
||||
private Long id;
|
||||
private Long parentId;
|
||||
private String parentName;
|
||||
private String deptCode;
|
||||
private String deptName;
|
||||
@ -18,26 +18,26 @@ public class DeptVO {
|
||||
private String email;
|
||||
private Integer sortOrder;
|
||||
private Integer status;
|
||||
private String tenantId;
|
||||
private Long tenantId;
|
||||
private LocalDateTime createdTime;
|
||||
private LocalDateTime updatedTime;
|
||||
|
||||
// 子部门
|
||||
private List<DeptVO> children;
|
||||
|
||||
public String getId() {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
@ -105,11 +105,11 @@ public class DeptVO {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getTenantId() {
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(String tenantId) {
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user