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