修改政府端前端,银行端小程序和后端接口

This commit is contained in:
2025-09-26 17:52:50 +08:00
parent 852adbcfff
commit 00dfa83fd1
237 changed files with 9172 additions and 33500 deletions

View File

@@ -1,186 +0,0 @@
# 前后端集成实现计划
## 前端页面分析
根据前端页面分析需要实现以下模块的后端API接口
### 1. 用户管理模块 (Users.vue)
**功能需求**:
- 用户列表查询(分页、搜索、筛选)
- 用户创建、编辑、删除
- 用户状态管理(启用/禁用)
- 用户角色管理
- 密码重置
**后端API接口**:
- `GET /api/users` - 获取用户列表
- `POST /api/users` - 创建用户
- `GET /api/users/:id` - 获取用户详情
- `PUT /api/users/:id` - 更新用户
- `DELETE /api/users/:id` - 删除用户
- `PUT /api/users/:id/status` - 更新用户状态
- `POST /api/users/:id/reset-password` - 重置密码
### 2. 账户管理模块 (Accounts.vue)
**功能需求**:
- 账户列表查询(分页、搜索、筛选)
- 账户创建、编辑、删除
- 账户状态管理
- 账户类型管理
- 余额管理
**后端API接口**:
- `GET /api/accounts` - 获取账户列表
- `POST /api/accounts` - 创建账户
- `GET /api/accounts/:id` - 获取账户详情
- `PUT /api/accounts/:id` - 更新账户
- `DELETE /api/accounts/:id` - 删除账户
- `PUT /api/accounts/:id/status` - 更新账户状态
- `POST /api/accounts/:id/deposit` - 存款
- `POST /api/accounts/:id/withdraw` - 取款
### 3. 交易管理模块 (Transactions.vue)
**功能需求**:
- 交易记录查询(分页、搜索、筛选)
- 交易详情查看
- 交易统计
- 交易撤销
**后端API接口**:
- `GET /api/transactions` - 获取交易列表
- `GET /api/transactions/:id` - 获取交易详情
- `POST /api/transactions` - 创建交易
- `PUT /api/transactions/:id/cancel` - 撤销交易
- `GET /api/transactions/statistics` - 获取交易统计
### 4. 贷款管理模块
#### 4.1 贷款商品 (LoanProducts.vue)
**功能需求**:
- 贷款产品列表查询
- 产品创建、编辑、删除
- 产品状态管理
- 产品配置管理
**后端API接口**:
- `GET /api/loan-products` - 获取贷款产品列表
- `POST /api/loan-products` - 创建贷款产品
- `GET /api/loan-products/:id` - 获取产品详情
- `PUT /api/loan-products/:id` - 更新产品
- `DELETE /api/loan-products/:id` - 删除产品
- `PUT /api/loan-products/:id/status` - 更新产品状态
#### 4.2 贷款申请 (LoanApplications.vue)
**功能需求**:
- 贷款申请列表查询
- 申请详情查看
- 申请审批流程
- 申请状态管理
**后端API接口**:
- `GET /api/loan-applications` - 获取申请列表
- `POST /api/loan-applications` - 提交申请
- `GET /api/loan-applications/:id` - 获取申请详情
- `PUT /api/loan-applications/:id` - 更新申请
- `PUT /api/loan-applications/:id/approve` - 审批申请
- `PUT /api/loan-applications/:id/reject` - 拒绝申请
#### 4.3 贷款合同 (LoanContracts.vue)
**功能需求**:
- 合同列表查询
- 合同详情查看
- 合同生成和管理
- 合同状态管理
**后端API接口**:
- `GET /api/loan-contracts` - 获取合同列表
- `POST /api/loan-contracts` - 创建合同
- `GET /api/loan-contracts/:id` - 获取合同详情
- `PUT /api/loan-contracts/:id` - 更新合同
- `PUT /api/loan-contracts/:id/status` - 更新合同状态
#### 4.4 贷款解押 (LoanRelease.vue)
**功能需求**:
- 解押申请列表
- 解押申请处理
- 解押状态管理
**后端API接口**:
- `GET /api/loan-releases` - 获取解押申请列表
- `POST /api/loan-releases` - 提交解押申请
- `GET /api/loan-releases/:id` - 获取申请详情
- `PUT /api/loan-releases/:id` - 更新申请
- `PUT /api/loan-releases/:id/approve` - 审批解押
### 5. 报表统计模块 (Reports.vue)
**功能需求**:
- 交易报表生成
- 用户报表生成
- 账户报表生成
- 报表导出功能
**后端API接口**:
- `GET /api/reports/transactions` - 获取交易报表
- `GET /api/reports/users` - 获取用户报表
- `GET /api/reports/accounts` - 获取账户报表
- `GET /api/reports/export/:type` - 导出报表
### 6. 员工管理模块 (EmployeeManagement.vue)
**功能需求**:
- 员工列表管理
- 员工信息维护
- 员工统计
**后端API接口**:
- `GET /api/employees` - 获取员工列表
- `POST /api/employees` - 创建员工
- `GET /api/employees/:id` - 获取员工详情
- `PUT /api/employees/:id` - 更新员工
- `DELETE /api/employees/:id` - 删除员工
- `GET /api/employees/statistics` - 获取员工统计
### 7. 系统管理模块
#### 7.1 系统设置 (Settings.vue)
**功能需求**:
- 系统参数配置
- 系统日志查看
- 系统备份恢复
**后端API接口**:
- `GET /api/settings` - 获取系统设置
- `PUT /api/settings` - 更新系统设置
- `GET /api/logs` - 获取系统日志
- `POST /api/backup` - 创建备份
- `POST /api/restore` - 恢复备份
#### 7.2 个人中心 (Profile.vue)
**功能需求**:
- 个人信息查看和修改
- 密码修改
- 登录日志
**后端API接口**:
- `GET /api/profile` - 获取个人信息
- `PUT /api/profile` - 更新个人信息
- `PUT /api/profile/password` - 修改密码
- `GET /api/profile/login-logs` - 获取登录日志
## 实现步骤
1. **完善现有API接口** - 用户管理、账户管理、交易管理
2. **实现贷款管理API** - 贷款产品、申请、合同、解押
3. **实现报表统计API** - 各种报表生成和导出
4. **实现员工管理API** - 员工信息管理
5. **实现系统管理API** - 设置、日志、备份
6. **更新前端API调用** - 确保前端正确调用所有接口
7. **测试和验证** - 端到端功能测试
## 数据库模型扩展
需要创建以下新的数据模型:
- LoanProduct (贷款产品)
- LoanApplication (贷款申请)
- LoanContract (贷款合同)
- LoanRelease (贷款解押)
- Employee (员工)
- SystemLog (系统日志)
- SystemSetting (系统设置)

View File

@@ -1,119 +0,0 @@
# 银行后端端口号修改总结
## 修改概述
将银行后端系统的端口号从5350修改为5351确保所有相关配置文件都已同步更新。
## 修改的文件
### 1. 后端配置文件
#### ✅ `bank-backend/server.js`
- **修改内容**: 端口号配置已经是5351
- **代码位置**: 第30行 `const PORT = process.env.PORT || 5351;`
- **状态**: 无需修改,已正确配置
#### ✅ `bank-backend/env.example`
- **修改内容**: 环境变量示例文件中的端口号已经是5351
- **代码位置**: 第2行 `PORT=5351`
- **状态**: 无需修改,已正确配置
### 2. 前端配置文件
#### ✅ `bank-frontend/src/config/env.js`
- **修改内容**: API基础URL配置已经是5351
- **代码位置**: 第15行和第21行 `baseUrl: getEnvVar('VITE_API_BASE_URL', 'http://localhost:5351')`
- **状态**: 无需修改,已正确配置
#### ✅ `bank-frontend/env.example`
- **修改内容**: 更新环境变量示例文件中的API基础URL
- **修改前**: `VITE_API_BASE_URL=http://localhost:5350`
- **修改后**: `VITE_API_BASE_URL=http://localhost:5351`
- **状态**: ✅ 已修改
#### ✅ `bank-frontend/vite.config.js`
- **修改内容**: 更新Vite代理配置中的默认目标端口
- **修改前**: `target: env.VITE_API_BASE_URL || 'http://localhost:5350'`
- **修改后**: `target: env.VITE_API_BASE_URL || 'http://localhost:5351'`
- **状态**: ✅ 已修改
#### ✅ `bank-frontend/README.md`
- **修改内容**: 更新文档中的API基础URL示例
- **修改前**: `VITE_API_BASE_URL=http://localhost:5350`
- **修改后**: `VITE_API_BASE_URL=http://localhost:5351`
- **状态**: ✅ 已修改
### 3. 小程序配置文件
#### ✅ `bank_mini_program/src/config/api.js`
- **修改内容**: 更新银行小程序的API基础URL
- **修改前**: `BASE_URL: 'http://localhost:5350'`
- **修改后**: `BASE_URL: 'http://localhost:5351'`
- **状态**: ✅ 已修改
## 端口号配置总结
### 后端服务
- **服务器端口**: 5351
- **配置文件**: `bank-backend/server.js`
- **环境变量**: `PORT=5351`
### 前端服务
- **前端端口**: 5300 (保持不变)
- **API代理目标**: http://localhost:5351
- **环境变量**: `VITE_API_BASE_URL=http://localhost:5351`
### 小程序服务
- **API基础URL**: http://localhost:5351
- **配置文件**: `bank_mini_program/src/config/api.js`
## 验证方法
### 1. 启动后端服务
```bash
cd bank-backend
npm start
```
预期输出:
```
🚀 银行管理后台服务器启动成功
📡 服务地址: http://localhost:5351
📚 API文档: http://localhost:5351/api-docs
🏥 健康检查: http://localhost:5351/health
```
### 2. 启动前端服务
```bash
cd bank-frontend
npm run dev
```
预期输出:
```
Local: http://localhost:5300/
Network: http://192.168.x.x:5300/
```
### 3. 测试API连接
访问以下URL验证服务是否正常运行
- 后端健康检查: http://localhost:5351/health
- 后端API文档: http://localhost:5351/api-docs
- 前端应用: http://localhost:5300
## 注意事项
1. **环境变量**: 如果使用了自定义的 `.env` 文件,请确保其中的 `VITE_API_BASE_URL` 设置为 `http://localhost:5351`
2. **代理配置**: 前端开发服务器会自动将 `/api` 请求代理到后端5351端口
3. **生产环境**: 生产环境部署时需要相应更新生产环境的API基础URL配置
4. **数据库连接**: 端口号修改不影响数据库连接,数据库配置保持不变
## 修改完成状态
**所有相关配置文件已成功更新为5351端口**
**前后端服务配置已同步**
**小程序API配置已更新**
**文档已更新**
现在银行后端系统将在5351端口运行所有相关服务都已正确配置。

View File

@@ -1,283 +0,0 @@
# 银行管理后台系统
一个基于 Node.js 和 Express 的现代化银行管理后台系统,提供完整的用户管理、账户管理、交易管理等功能。
## 🚀 功能特性
### 核心功能
- **用户管理**: 用户注册、登录、权限管理
- **账户管理**: 账户创建、状态管理、余额查询
- **交易管理**: 存款、取款、转账、交易记录查询
- **权限控制**: 基于角色的访问控制(RBAC)
- **安全防护**: JWT认证、密码加密、请求限流
### 技术特性
- **RESTful API**: 标准化的API设计
- **数据库ORM**: Sequelize ORM支持
- **API文档**: Swagger自动生成文档
- **日志系统**: Winston日志管理
- **安全中间件**: 多层安全防护
- **错误处理**: 完善的错误处理机制
## 🛠 技术栈
- **运行环境**: Node.js 16+
- **Web框架**: Express.js 4.18+
- **数据库**: MySQL 8.0+
- **ORM**: Sequelize 6.35+
- **认证**: JWT (jsonwebtoken)
- **密码加密**: bcryptjs
- **API文档**: Swagger
- **日志**: Winston
- **安全**: Helmet, CORS, Rate Limiting
## 📦 安装部署
### 环境要求
- Node.js 16.0+
- MySQL 8.0+
- npm 8.0+
### 安装步骤
1. **克隆项目**
```bash
git clone <repository-url>
cd bank-backend
```
2. **安装依赖**
```bash
npm install
```
3. **环境配置**
```bash
cp env.example .env
# 编辑 .env 文件,配置数据库连接等信息
```
4. **数据库初始化**
手动或脚本方式,避免自动建表:
```powershell
# PowerShell推荐自动生成管理员bcrypt哈希
cd scripts
./setup-bank-db.ps1 -AdminPlain 'Admin123456'
```
或在数据库手工执行:
```sql
-- 1) 执行建表
-- scripts/create-bank-schema.sql
-- 2) 执行测试数据(将 REPLACE_ADMIN_BCRYPT 替换为真实 bcrypt 哈希)
-- scripts/seed-bank-demo.sql
```
5. **启动服务**
```bash
# 开发环境
npm run dev
# 生产环境
npm start
```
## ⚙️ 环境配置
创建 `.env` 文件并配置以下环境变量:
```env
# 服务器配置
PORT=5351
NODE_ENV=development
# 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_NAME=bank_management
DB_USER=root
DB_PASSWORD=your_password
DB_DIALECT=mysql
# JWT配置
JWT_SECRET=your_jwt_secret_key_here
JWT_EXPIRES_IN=24h
# 安全配置
BCRYPT_ROUNDS=10
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100
# 银行系统配置
BANK_CODE=001
BANK_NAME=示例银行
CURRENCY=CNY
TIMEZONE=Asia/Shanghai
```
## 📚 API文档
启动服务后访问以下地址查看API文档
- 开发环境: http://localhost:5351/api-docs
- 生产环境: https://your-domain.com/api-docs
## 🗄️ 数据库设计
### 主要数据表
#### 用户表 (users)
- 用户基本信息
- 身份认证信息
- 角色关联
#### 角色表 (roles)
- 角色定义
- 权限级别
- 系统角色标识
#### 账户表 (accounts)
- 账户基本信息
- 余额管理
- 账户状态
#### 交易记录表 (transactions)
- 交易详情
- 余额变化
- 交易状态
## 🔐 安全特性
### 认证与授权
- JWT令牌认证
- 基于角色的权限控制
- 会话超时管理
- 登录失败锁定
### 数据安全
- 密码bcrypt加密
- SQL注入防护
- XSS攻击防护
- 请求频率限制
### 传输安全
- HTTPS支持
- CORS配置
- 安全头部设置
- 输入数据验证
## 📊 系统监控
### 健康检查
```bash
curl http://localhost:5351/health
```
### 日志查看
```bash
# 查看错误日志
tail -f logs/error.log
# 查看所有日志
tail -f logs/combined.log
```
## 🧪 测试
```bash
# 运行测试
npm test
# 测试覆盖率
npm run test:coverage
# 监听模式
npm run test:watch
```
## 📝 开发指南
### 项目结构
```
bank-backend/
├── config/ # 配置文件
├── controllers/ # 控制器
├── models/ # 数据模型
├── routes/ # 路由定义
├── middleware/ # 中间件
├── utils/ # 工具类
├── services/ # 业务服务
├── migrations/ # 数据库迁移
├── seeds/ # 种子数据
├── logs/ # 日志文件
├── uploads/ # 上传文件
└── scripts/ # 脚本文件
```
### 代码规范
- 使用ESLint进行代码检查
- 遵循RESTful API设计规范
- 统一的错误处理格式
- 完整的API文档注释
### 开发命令
```bash
# 代码检查
npm run lint
# 代码修复
npm run lint:fix
# 数据库连接测试
npm run test-connection
# 清理临时文件
npm run clean
```
## 🚀 部署指南
### Docker部署
```bash
# 构建镜像
docker build -t bank-backend .
# 运行容器
docker run -p 5351:5351 bank-backend
```
### PM2部署
```bash
# 安装PM2
npm install -g pm2
# 启动应用
pm2 start server.js --name bank-backend
# 查看状态
pm2 status
```
## 🤝 贡献指南
1. Fork 项目
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 打开 Pull Request
## 📄 许可证
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
## 📞 联系方式
- 项目维护者: 银行开发团队
- 邮箱: dev@bank.com
- 项目地址: https://github.com/bank-management/bank-backend
## 🙏 致谢
感谢所有为这个项目做出贡献的开发者和开源社区。
---
**注意**: 这是一个演示项目,请勿在生产环境中使用默认的密码和密钥。

View File

@@ -1,164 +0,0 @@
# 监管任务前后端集成完成总结
## 🎯 项目概述
已成功完成银行端监管任务管理系统的前后端集成实现了完整的CRUD功能使用真实的后端API接口替代了前端模拟数据。
## ✅ 已完成的功能
### 后端功能
1. **数据库模型** (`models/SupervisionTask.js`)
- 完整的监管任务数据模型
- 支持所有必要字段:申请单号、合同编号、客户信息、监管信息等
- 与用户模型的关联关系
2. **数据库迁移** (`migrations/20241220000003-create-supervision-tasks.js`)
- 创建监管任务表结构
- 添加必要的索引优化查询性能
3. **API控制器** (`controllers/supervisionTaskController.js`)
- 获取监管任务列表(支持分页、搜索、筛选)
- 获取监管任务详情
- 创建监管任务(完整的数据验证)
- 更新监管任务
- 删除监管任务
- 获取监管任务统计
- 批量更新状态
- 批量删除
4. **API路由** (`routes/supervisionTasks.js`)
- 完整的RESTful API路由配置
- 统一的认证中间件保护
5. **测试数据** (`scripts/seed-supervision-tasks.js`)
- 8条完整的测试数据
- 涵盖不同状态和类型的监管任务
### 前端功能
1. **API集成** (`src/utils/api.js`)
- 完整的监管任务API方法
- 统一的错误处理和认证
2. **页面改造** (`src/views/SupervisionTasks.vue`)
- 移除模拟数据使用真实API
- 实现完整的CRUD操作
- 添加搜索和筛选功能
- 分页功能
- 错误处理和用户反馈
## 📊 数据字段说明
### 核心字段
- **申请单号** (`applicationNumber`): 唯一标识最大50字符
- **放款合同编号** (`contractNumber`): 唯一标识最大50字符
- **产品名称** (`productName`): 最大100字符
- **客户姓名** (`customerName`): 最大50字符
- **证件类型** (`idType`): 枚举值(身份证、护照、其他)
- **证件号码** (`idNumber`): 最大50字符
- **养殖生资种类** (`assetType`): 枚举值(牛、羊、猪、家禽、其他)
- **监管生资数量** (`assetQuantity`): 非负整数
- **监管状态** (`supervisionStatus`): 枚举值(待监管、监管中、已完成、已暂停)
### 时间字段
- **任务导入时间** (`importTime`): 自动生成
- **监管起始时间** (`startTime`): 必填
- **监管结束时间** (`endTime`): 必填
### 扩展字段
- **贷款金额** (`loanAmount`): 精确到分
- **利率** (`interestRate`): 精确到万分位
- **贷款期限** (`loanTerm`): 月数
- **监管员姓名** (`supervisorName`): 最大50字符
- **监管员电话** (`supervisorPhone`): 最大20字符
- **养殖场地址** (`farmAddress`): 最大200字符
- **备注** (`remarks`): 文本字段
## 🔧 API接口列表
### 基础CRUD
- `GET /api/supervision-tasks` - 获取监管任务列表
- `GET /api/supervision-tasks/:id` - 获取监管任务详情
- `POST /api/supervision-tasks` - 创建监管任务
- `PUT /api/supervision-tasks/:id` - 更新监管任务
- `DELETE /api/supervision-tasks/:id` - 删除监管任务
### 统计和批量操作
- `GET /api/supervision-tasks/stats` - 获取监管任务统计
- `PUT /api/supervision-tasks/batch/status` - 批量更新状态
- `DELETE /api/supervision-tasks/batch` - 批量删除
### 查询参数
- `page`: 页码默认1
- `limit`: 每页数量默认10
- `search`: 搜索关键词(申请单号、合同编号、客户姓名、产品名称)
- `supervisionStatus`: 状态筛选
- `dateRange`: 日期范围筛选
- `sortBy`: 排序字段
- `sortOrder`: 排序方向
## 🚀 使用方法
### 1. 启动后端服务
```bash
cd bank-backend
npm start
```
### 2. 启动前端服务
```bash
cd bank-frontend
npm run dev
```
### 3. 访问页面
- 监管任务页面: `http://localhost:5300/supervision-tasks`
- API测试页面: `http://localhost:5300/test-supervision-tasks.html`
### 4. 功能测试
1. 登录系统admin/Admin123456
2. 查看监管任务列表
3. 使用搜索和筛选功能
4. 创建新的监管任务
5. 编辑和删除任务
## 📝 数据验证规则
### 必填字段
- 申请单号、放款合同编号、产品名称、客户姓名、证件号码、监管起始时间、监管结束时间
### 唯一性约束
- 申请单号、放款合同编号必须唯一
### 数据格式
- 日期格式YYYY-MM-DD
- 金额格式:精确到分
- 利率格式0-1之间的小数
### 业务规则
- 监管结束时间必须晚于开始时间
- 监管生资数量不能为负数
- 贷款金额不能为负数
## 🔍 测试数据
系统已预置8条测试数据涵盖
- 不同监管状态(待监管、监管中、已完成、已暂停)
- 不同养殖生资种类(牛、羊、猪、家禽、其他)
- 不同证件类型(身份证、护照)
- 不同贷款金额和期限
## 📚 相关文档
- [监管任务API文档](docs/SUPERVISION_TASKS_API.md)
- [项目API文档](docs/PROJECT_API.md)
- [前端API集成指南](API_INTEGRATION_COMPLETE.md)
## 🎉 总结
监管任务管理系统已完全集成,提供了:
- ✅ 完整的后端API服务
- ✅ 功能丰富的前端界面
- ✅ 真实的数据存储和查询
- ✅ 完善的错误处理
- ✅ 用户友好的交互体验
系统现在可以正常使用,支持监管任务的完整生命周期管理。

View File

@@ -0,0 +1,210 @@
/**
* 添加贷款解押测试数据
* @file add-loan-release-test-data.js
* @description 为贷款解押功能添加测试数据
*/
const { LoanRelease, LoanReleaseHistory, User } = require('./models');
async function addTestData() {
try {
console.log('🔄 开始添加贷款解押测试数据...\n');
// 获取管理员用户
const adminUser = await User.findOne({ where: { username: 'admin' } });
if (!adminUser) {
console.log('❌ 未找到管理员用户,请先创建用户');
return;
}
// 清空现有数据
await LoanReleaseHistory.destroy({ where: {} });
await LoanRelease.destroy({ where: {} });
console.log('🗑️ 清空现有测试数据');
// 创建解押申请测试数据
const releases = [
{
application_number: '20240227145555918',
contract_id: null,
customer_name: '刘超',
customer_phone: '13800138000',
customer_id_card: '511123199001013017',
farmer_name: '刘超',
product_name: '中国工商银行扎旗支行"畜禽活体抵押"',
collateral_type: 'livestock',
collateral_description: '优质肉牛',
collateral_value: 150000.00,
loan_amount: 100000.00,
release_amount: 10000.00,
release_quantity: '10头',
application_reason: '需要资金周转,申请部分解押',
status: 'released',
application_date: new Date('2024-02-27 14:55:55'),
process_date: new Date('2024-02-27 15:30:00'),
complete_date: new Date('2024-02-27 16:00:00'),
processor_id: adminUser.id,
processor_name: adminUser.real_name || adminUser.username,
process_comment: '审核通过,同意解押',
remark: '客户信用良好'
},
{
application_number: '20240226113416302',
contract_id: null,
customer_name: '李四',
customer_phone: '13900139000',
customer_id_card: '511123199002020002',
farmer_name: '李四',
product_name: '惠农贷',
collateral_type: 'livestock',
collateral_description: '优质奶牛',
collateral_value: 200000.00,
loan_amount: 150000.00,
release_amount: 0.00,
release_quantity: '10头',
application_reason: '申请全部解押',
status: 'released',
application_date: new Date('2024-02-26 11:34:16'),
process_date: new Date('2024-02-26 12:00:00'),
complete_date: new Date('2024-02-26 12:30:00'),
processor_id: adminUser.id,
processor_name: adminUser.real_name || adminUser.username,
process_comment: '审核通过,同意解押',
remark: '客户按时还款'
},
{
application_number: '20240223140542290',
contract_id: null,
customer_name: '张洪彬',
customer_phone: '13700137000',
customer_id_card: '511123199003030003',
farmer_name: '张洪彬',
product_name: '惠农贷',
collateral_type: 'livestock',
collateral_description: '优质肉牛',
collateral_value: 500000.00,
loan_amount: 300000.00,
release_amount: 1000000.00,
release_quantity: '10头',
application_reason: '扩大养殖规模,申请解押',
status: 'released',
application_date: new Date('2024-02-23 14:05:42'),
process_date: new Date('2024-02-23 15:00:00'),
complete_date: new Date('2024-02-23 15:30:00'),
processor_id: adminUser.id,
processor_name: adminUser.real_name || adminUser.username,
process_comment: '审核通过,同意解押',
remark: '客户经营状况良好'
},
{
application_number: '20231131890123456',
contract_id: null,
customer_name: '田小平',
customer_phone: '13600136000',
customer_id_card: '150123199001013140',
farmer_name: '田小平',
product_name: '中国工商银行扎旗支行"畜禽活体抵押"',
collateral_type: 'livestock',
collateral_description: '优质肉牛',
collateral_value: 600000.00,
loan_amount: 400000.00,
release_amount: 420000.00,
release_quantity: '30头',
application_reason: '需要资金购买饲料',
status: 'released',
application_date: new Date('2023-11-31 08:30:12'),
process_date: new Date('2023-11-31 10:00:00'),
complete_date: new Date('2023-11-31 10:30:00'),
processor_id: adminUser.id,
processor_name: adminUser.real_name || adminUser.username,
process_comment: '审核通过,同意解押',
remark: '客户信用记录良好'
},
{
application_number: '20231131789012345',
contract_id: null,
customer_name: '杜宝民',
customer_phone: '13500135000',
customer_id_card: '150123199002020723',
farmer_name: '杜宝民',
product_name: '中国农业银行扎旗支行"畜禽活体抵押"',
collateral_type: 'livestock',
collateral_description: '优质奶牛',
collateral_value: 700000.00,
loan_amount: 500000.00,
release_amount: 420000.00,
release_quantity: '30头',
application_reason: '设备更新需要资金',
status: 'released',
application_date: new Date('2023-11-31 07:30:01'),
process_date: new Date('2023-11-31 09:00:00'),
complete_date: new Date('2023-11-31 09:30:00'),
processor_id: adminUser.id,
processor_name: adminUser.real_name || adminUser.username,
process_comment: '审核通过,同意解押',
remark: '客户还款能力强'
},
{
application_number: '20231131901234567',
contract_id: null,
customer_name: '满良',
customer_phone: '13400134000',
customer_id_card: '150123199003030514',
farmer_name: '满良',
product_name: '中国农业银行扎旗支行"畜禽活体抵押"',
collateral_type: 'livestock',
collateral_description: '优质肉牛',
collateral_value: 800000.00,
loan_amount: 600000.00,
release_amount: 530000.00,
release_quantity: '38头',
application_reason: '扩大养殖规模',
status: 'released',
application_date: new Date('2023-11-31 09:01:23'),
process_date: new Date('2023-11-31 11:00:00'),
complete_date: new Date('2023-11-31 11:30:00'),
processor_id: adminUser.id,
processor_name: adminUser.real_name || adminUser.username,
process_comment: '审核通过,同意解押',
remark: '客户经营稳定'
}
];
// 批量创建解押申请
const createdReleases = await LoanRelease.bulkCreate(releases);
console.log(`✅ 创建了 ${createdReleases.length} 条解押申请记录`);
// 为每个解押申请创建历史记录
for (let i = 0; i < createdReleases.length; i++) {
const release = createdReleases[i];
// 提交申请历史
await LoanReleaseHistory.create({
release_id: release.id,
action: 'apply',
operator: release.customer_name,
comment: '提交解押申请'
});
// 处理完成历史
await LoanReleaseHistory.create({
release_id: release.id,
action: 'complete',
operator: release.processor_name,
operator_id: release.processor_id,
comment: '解押手续办理完成',
previous_status: 'pending',
new_status: 'released'
});
}
console.log(`✅ 创建了 ${createdReleases.length * 2} 条历史记录`);
console.log('\n🎉 贷款解押测试数据添加完成!');
} catch (error) {
console.error('❌ 添加测试数据失败:', error.message);
console.error('错误详情:', error);
}
}
addTestData();

View File

@@ -1,32 +0,0 @@
const { sequelize } = require('./config/database');
async function checkData() {
try {
console.log('检查各表数据统计...\n');
const tables = [
'supervision_tasks',
'projects',
'installation_tasks',
'completed_supervisions',
'loan_applications',
'loan_contracts'
];
for (const table of tables) {
const result = await sequelize.query(
`SELECT COUNT(*) as count FROM ${table}`,
{ type: sequelize.QueryTypes.SELECT }
);
console.log(`${table}: ${result[0].count} 条记录`);
}
console.log('\n检查完成');
} catch (error) {
console.error('错误:', error.message);
} finally {
await sequelize.close();
}
}
checkData();

View File

@@ -1,31 +0,0 @@
const { LoanProduct } = require('./models');
async function checkData() {
try {
console.log('检查数据库中的贷款商品数据...');
const count = await LoanProduct.count();
console.log(`数据库中共有 ${count} 条贷款商品数据`);
if (count > 0) {
const products = await LoanProduct.findAll({
limit: 3,
attributes: ['id', 'productName', 'loanAmount', 'loanTerm', 'interestRate', 'onSaleStatus']
});
console.log('前3条数据:');
products.forEach((product, index) => {
console.log(`${index + 1}. ID: ${product.id}, 名称: ${product.productName}, 额度: ${product.loanAmount}`);
});
} else {
console.log('数据库中没有贷款商品数据,需要添加测试数据');
}
} catch (error) {
console.error('检查数据失败:', error);
} finally {
process.exit(0);
}
}
checkData();

View File

@@ -1,36 +0,0 @@
const { LoanProduct } = require('./models');
async function checkLoanProducts() {
try {
console.log('查询数据库中的贷款商品数据...');
const products = await LoanProduct.findAll({
attributes: ['id', 'productName', 'loanAmount', 'loanTerm', 'interestRate', 'serviceArea', 'servicePhone', 'totalCustomers', 'supervisionCustomers', 'completedCustomers', 'onSaleStatus', 'createdAt']
});
console.log(`找到 ${products.length} 条贷款商品数据:`);
products.forEach((product, index) => {
console.log(`\n--- 产品 ${index + 1} ---`);
console.log(`ID: ${product.id}`);
console.log(`产品名称: ${product.productName}`);
console.log(`贷款额度: ${product.loanAmount}`);
console.log(`贷款周期: ${product.loanTerm}`);
console.log(`贷款利率: ${product.interestRate}`);
console.log(`服务区域: ${product.serviceArea}`);
console.log(`服务电话: ${product.servicePhone}`);
console.log(`总客户数: ${product.totalCustomers}`);
console.log(`监管中客户: ${product.supervisionCustomers}`);
console.log(`已结项客户: ${product.completedCustomers}`);
console.log(`在售状态: ${product.onSaleStatus}`);
console.log(`创建时间: ${product.createdAt}`);
});
} catch (error) {
console.error('查询失败:', error);
} finally {
process.exit(0);
}
}
checkLoanProducts();

View File

@@ -10,8 +10,8 @@ const options = {
openapi: '3.0.0',
info: {
title: '银行管理后台API',
version: '1.0.0',
description: '银行管理后台系统API文档',
version: '1.1.0',
description: '银行管理后台系统API文档,包含贷款解押模块',
contact: {
name: '银行开发团队',
email: 'dev@bank.com'
@@ -104,6 +104,196 @@ const options = {
example: 10
}
}
},
LoanRelease: {
type: 'object',
properties: {
id: {
type: 'integer',
example: 1
},
applicationNumber: {
type: 'string',
example: '20240227145555918'
},
productName: {
type: 'string',
example: '中国工商银行扎旗支行"畜禽活体抵押"'
},
farmerName: {
type: 'string',
example: '刘超'
},
applicantName: {
type: 'string',
example: '刘超'
},
applicantIdNumber: {
type: 'string',
example: '511123199001013017'
},
applicantPhone: {
type: 'string',
example: '13800138000'
},
assetType: {
type: 'string',
example: '牛'
},
releaseQuantity: {
type: 'string',
example: '10头'
},
releaseAmount: {
type: 'number',
example: 10000
},
status: {
type: 'string',
enum: ['pending', 'processing', 'approved', 'rejected', 'completed', 'released'],
example: 'released'
},
applicationTime: {
type: 'string',
format: 'date-time',
example: '2024-02-27T06:55:55.000Z'
},
processTime: {
type: 'string',
format: 'date-time',
example: '2024-02-27T07:30:00.000Z'
},
completeTime: {
type: 'string',
format: 'date-time',
example: '2024-02-27T08:00:00.000Z'
},
processorName: {
type: 'string',
example: '系统管理员'
},
processComment: {
type: 'string',
example: '审核通过,同意解押'
},
rejectionReason: {
type: 'string',
example: null
},
remark: {
type: 'string',
example: '客户信用良好'
}
}
},
LoanReleaseHistory: {
type: 'object',
properties: {
id: {
type: 'integer',
example: 1
},
action: {
type: 'string',
enum: ['apply', 'process', 'approve', 'reject', 'complete', 'release'],
example: 'apply'
},
operator: {
type: 'string',
example: '刘超'
},
time: {
type: 'string',
format: 'date-time',
example: '2025-09-26T03:38:51.000Z'
},
comment: {
type: 'string',
example: '提交解押申请'
}
}
},
LoanReleaseCreateRequest: {
type: 'object',
required: ['customer_name', 'customer_phone', 'customer_id_card', 'collateral_type', 'release_amount'],
properties: {
contract_id: {
type: 'integer',
example: 1
},
customer_name: {
type: 'string',
example: '测试用户'
},
customer_phone: {
type: 'string',
example: '13800138000'
},
customer_id_card: {
type: 'string',
example: '511123199001010001'
},
farmer_name: {
type: 'string',
example: '测试用户'
},
product_name: {
type: 'string',
example: '测试产品'
},
collateral_type: {
type: 'string',
enum: ['house', 'car', 'land', 'equipment', 'livestock'],
example: 'livestock'
},
collateral_description: {
type: 'string',
example: '测试抵押物'
},
collateral_value: {
type: 'number',
example: 100000
},
loan_amount: {
type: 'number',
example: 80000
},
release_amount: {
type: 'number',
example: 50000
},
release_quantity: {
type: 'string',
example: '5头'
},
application_reason: {
type: 'string',
example: '测试解押申请'
},
remark: {
type: 'string',
example: '测试备注'
}
}
},
LoanReleaseProcessRequest: {
type: 'object',
required: ['action', 'comment'],
properties: {
action: {
type: 'string',
enum: ['approve', 'reject'],
example: 'approve'
},
comment: {
type: 'string',
example: '审核通过'
},
remark: {
type: 'string',
example: '测试处理'
}
}
}
},
responses: {
@@ -191,6 +381,14 @@ const options = {
}
],
tags: [
{
name: 'Authentication',
description: '认证相关接口'
},
{
name: 'Dashboard',
description: '仪表盘相关接口'
},
{
name: 'Users',
description: '用户管理相关接口'
@@ -202,6 +400,46 @@ const options = {
{
name: 'Transactions',
description: '交易管理相关接口'
},
{
name: 'Loan Products',
description: '贷款产品管理相关接口'
},
{
name: 'Loan Applications',
description: '贷款申请管理相关接口'
},
{
name: 'Loan Contracts',
description: '贷款合同管理相关接口'
},
{
name: 'Loan Release',
description: '贷款解押管理相关接口'
},
{
name: 'Employees',
description: '员工管理相关接口'
},
{
name: 'Projects',
description: '项目管理相关接口'
},
{
name: 'Supervision Tasks',
description: '监管任务管理相关接口'
},
{
name: 'Installation Tasks',
description: '安装任务管理相关接口'
},
{
name: 'Completed Supervisions',
description: '完成监管管理相关接口'
},
{
name: 'Reports',
description: '报表管理相关接口'
}
]
},

View File

@@ -38,6 +38,9 @@ const getApplications = async (req, res) => {
} else if (searchField === 'applicationNumber') {
// 数据库中实际没有applicationNumber字段使用id作为替代
where.id = { [Op.like]: `%${searchValue}%` };
} else if (searchField === 'productName') {
// 产品名称搜索暂时不处理,因为数据库中没有这个字段
// 可以在前端过滤
}
}
@@ -70,17 +73,27 @@ const getApplications = async (req, res) => {
limit
});
// 格式化数据
// 格式化数据 - 匹配前端期望的字段
const applications = rows.map(app => ({
id: app.id,
customer_name: app.customer_name,
customer_phone: app.customer_phone,
customer_id_card: app.customer_id_card,
loan_amount: parseFloat(app.loan_amount),
loan_term: app.loan_term,
interest_rate: parseFloat(app.interest_rate),
application_date: app.application_date,
applicationNumber: `APP-${String(app.id).padStart(6, '0')}`, // 生成申请单号
productName: '养殖贷款', // 默认产品名称
farmerName: app.customer_name || '未知养殖户',
borrowerName: app.customer_name || '未知借款人',
borrowerIdNumber: app.customer_id_card || '',
assetType: '养殖设备', // 默认生资种类
applicationQuantity: '1', // 默认申请数量
amount: parseFloat(app.loan_amount),
status: app.status,
type: 'personal', // 默认类型
term: app.loan_term,
interestRate: parseFloat(app.interest_rate),
phone: app.customer_phone || '',
purpose: '养殖经营', // 默认用途
remark: '', // 默认备注
applicationTime: app.application_date,
approvedTime: null,
rejectedTime: null,
created_at: app.created_at,
updated_at: app.updated_at
}));
@@ -221,7 +234,7 @@ const auditApplication = async (req, res) => {
const { id } = req.params;
const { action, comment } = req.body;
const userId = req.user?.id;
const userId = req.user?.userId;
// 获取申请信息
const application = await LoanApplication.findByPk(id);
@@ -246,13 +259,15 @@ const auditApplication = async (req, res) => {
// 根据审核动作更新状态
if (action === 'approve') {
newStatus = 'approved';
application.approvedTime = new Date();
application.approvedBy = userId;
// 注意LoanApplication模型中没有这些字段暂时注释掉
// application.approvedTime = new Date();
// application.approvedBy = userId;
} else if (action === 'reject') {
newStatus = 'rejected';
application.rejectedTime = new Date();
application.rejectedBy = userId;
application.rejectionReason = comment;
// 注意LoanApplication模型中没有这些字段暂时注释掉
// application.rejectedTime = new Date();
// application.rejectedBy = userId;
// application.rejectionReason = comment;
}
// 更新申请状态
@@ -283,9 +298,12 @@ const auditApplication = async (req, res) => {
});
} catch (error) {
console.error('审核贷款申请失败:', error);
console.error('错误详情:', error.message);
console.error('错误堆栈:', error.stack);
res.status(500).json({
success: false,
message: '审核贷款申请失败'
message: '审核贷款申请失败',
error: error.message
});
}
};

View File

@@ -40,6 +40,13 @@ const getContracts = async (req, res) => {
} else if (searchField === 'applicationNumber') {
// 数据库中实际没有applicationNumber字段使用id作为替代
where.id = { [Op.like]: `%${searchValue}%` };
} else if (searchField === 'borrowerName') {
where.customer_name = { [Op.like]: `%${searchValue}%` };
} else if (searchField === 'farmerName') {
where.customer_name = { [Op.like]: `%${searchValue}%` };
} else if (searchField === 'productName') {
// 产品名称搜索暂时不处理,因为数据库中没有这个字段
// 可以在前端过滤
}
}
@@ -72,18 +79,32 @@ const getContracts = async (req, res) => {
limit
});
// 格式化数据
// 格式化数据 - 匹配前端期望的字段
const contracts = rows.map(contract => ({
id: contract.id,
contract_number: contract.contract_number,
customer_name: contract.customer_name,
customer_phone: contract.customer_phone,
customer_id_card: contract.customer_id_card,
loan_amount: parseFloat(contract.loan_amount),
loan_term: contract.loan_term,
interest_rate: parseFloat(contract.interest_rate),
contract_date: contract.contract_date,
contractNumber: contract.contract_number,
applicationNumber: `APP-${String(contract.id).padStart(6, '0')}`, // 生成申请单号
productName: '养殖贷款', // 默认产品名称
farmerName: contract.customer_name || '未知养殖户',
borrowerName: contract.customer_name || '未知借款人',
borrowerIdNumber: contract.customer_id_card || '',
assetType: '养殖设备', // 默认生资种类
applicationQuantity: '1', // 默认申请数量
amount: parseFloat(contract.loan_amount),
paidAmount: 0, // 默认已还款金额
status: contract.status,
type: 'livestock_collateral', // 默认类型
term: contract.loan_term,
interestRate: parseFloat(contract.interest_rate),
phone: contract.customer_phone || '',
purpose: '养殖经营', // 默认用途
remark: '', // 默认备注
contractTime: contract.contract_date,
disbursementTime: null,
maturityTime: null,
completedTime: null,
remainingAmount: parseFloat(contract.loan_amount), // 剩余金额等于贷款金额
repaymentProgress: 0, // 默认还款进度
created_at: contract.created_at,
updated_at: contract.updated_at
}));

View File

@@ -201,7 +201,7 @@ const createLoanProduct = async (req, res) => {
}
// 检查用户信息
if (!req.user || !req.user.id) {
if (!req.user || !req.user.userId) {
return res.status(401).json({
success: false,
message: '用户信息无效'
@@ -222,8 +222,8 @@ const createLoanProduct = async (req, res) => {
riskLevel,
minLoanAmount: minLoanAmount ? parseFloat(minLoanAmount) : null,
maxLoanAmount: maxLoanAmount ? parseFloat(maxLoanAmount) : null,
createdBy: req.user.id,
updatedBy: req.user.id
createdBy: req.user.userId,
updatedBy: req.user.userId
});
// 获取创建后的完整信息
@@ -313,7 +313,7 @@ const updateLoanProduct = async (req, res) => {
}
});
product.updatedBy = req.user.id;
product.updatedBy = req.user.userId;
await product.save();
// 获取更新后的完整信息
@@ -440,7 +440,7 @@ const batchUpdateStatus = async (req, res) => {
await LoanProduct.update(
{
onSaleStatus,
updatedBy: req.user.id
updatedBy: req.user.userId
},
{
where: { id: { [Op.in]: ids } }

View File

@@ -0,0 +1,545 @@
/**
* 贷款解押控制器
* @file loanReleaseController.js
* @description 银行系统贷款解押相关接口控制器
*/
const { LoanRelease, LoanReleaseHistory, User } = require('../models');
const { Op } = require('sequelize');
/**
* 获取解押申请列表
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const getReleases = async (req, res) => {
try {
const {
page = 1,
pageSize = 10,
searchField = 'application_number',
searchValue = '',
status = '',
sortField = 'created_at',
sortOrder = 'DESC'
} = req.query;
// 构建查询条件
const whereCondition = {};
// 搜索条件
if (searchValue) {
switch (searchField) {
case 'application_number':
whereCondition.application_number = {
[Op.like]: `%${searchValue}%`
};
break;
case 'customer_name':
whereCondition[Op.or] = [
{ customer_name: { [Op.like]: `%${searchValue}%` } },
{ farmer_name: { [Op.like]: `%${searchValue}%` } }
];
break;
case 'product_name':
whereCondition.product_name = {
[Op.like]: `%${searchValue}%`
};
break;
}
}
// 状态筛选
if (status) {
whereCondition.status = status;
}
// 分页参数
const offset = (page - 1) * pageSize;
const limit = parseInt(pageSize);
// 查询数据
const { count, rows } = await LoanRelease.findAndCountAll({
where: whereCondition,
order: [[sortField, sortOrder.toUpperCase()]],
offset,
limit,
include: [
{
model: User,
as: 'processor',
attributes: ['id', 'username', 'real_name']
}
]
});
// 格式化返回数据
const releases = rows.map(release => ({
id: release.id,
applicationNumber: release.application_number,
productName: release.product_name || '养殖贷款',
farmerName: release.farmer_name || release.customer_name,
applicantName: release.customer_name,
applicantIdNumber: release.customer_id_card,
applicantPhone: release.customer_phone,
assetType: release.collateral_type === 'livestock' ? '牛' : '其他',
releaseQuantity: release.release_quantity || '1头',
releaseAmount: parseFloat(release.release_amount),
status: release.status,
applicationTime: release.application_date,
processTime: release.process_date,
completeTime: release.complete_date,
processorName: release.processor?.real_name || release.processor_name,
processComment: release.process_comment,
rejectionReason: release.rejection_reason,
remark: release.remark,
created_at: release.created_at,
updated_at: release.updated_at
}));
res.json({
success: true,
data: {
releases,
pagination: {
current: parseInt(page),
pageSize: limit,
total: count,
totalPages: Math.ceil(count / limit)
}
}
});
} catch (error) {
console.error('获取解押申请列表失败:', error);
res.status(500).json({
success: false,
message: '获取解押申请列表失败'
});
}
};
/**
* 获取解押申请详情
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const getReleaseDetail = async (req, res) => {
try {
const { id } = req.params;
const release = await LoanRelease.findByPk(id, {
include: [
{
model: User,
as: 'processor',
attributes: ['id', 'username', 'real_name']
}
]
});
if (!release) {
return res.status(404).json({
success: false,
message: '解押申请不存在'
});
}
// 获取历史记录
const history = await LoanReleaseHistory.findAll({
where: { release_id: id },
order: [['operation_time', 'ASC']]
});
const releaseDetail = {
id: release.id,
releaseNumber: release.application_number,
customerName: release.customer_name,
contractNumber: `CONTRACT-${String(release.contract_id || release.id).padStart(6, '0')}`,
status: release.status,
collateralType: release.collateral_type,
collateralDescription: release.collateral_description,
loanAmount: parseFloat(release.loan_amount || 0),
collateralValue: parseFloat(release.collateral_value || 0),
applicationTime: release.application_date,
processTime: release.process_date,
completeTime: release.complete_date,
phone: release.customer_phone,
idCard: release.customer_id_card,
reason: release.application_reason,
remark: release.remark,
history: history.map(h => ({
id: h.id,
action: h.action,
operator: h.operator,
time: h.operation_time,
comment: h.comment
}))
};
res.json({
success: true,
data: releaseDetail
});
} catch (error) {
console.error('获取解押申请详情失败:', error);
res.status(500).json({
success: false,
message: '获取解押申请详情失败'
});
}
};
/**
* 创建解押申请
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const createRelease = async (req, res) => {
try {
const {
contract_id,
customer_name,
customer_phone,
customer_id_card,
farmer_name,
product_name,
collateral_type,
collateral_description,
collateral_value,
loan_amount,
release_amount,
release_quantity,
application_reason,
remark
} = req.body;
// 生成申请单号
const applicationNumber = `REL-${Date.now()}`;
const release = await LoanRelease.create({
application_number: applicationNumber,
contract_id,
customer_name,
customer_phone,
customer_id_card,
farmer_name,
product_name,
collateral_type,
collateral_description,
collateral_value,
loan_amount,
release_amount,
release_quantity,
application_reason,
remark,
status: 'pending'
});
// 创建历史记录
await LoanReleaseHistory.create({
release_id: release.id,
action: 'apply',
operator: customer_name,
comment: '提交解押申请'
});
res.status(201).json({
success: true,
message: '解押申请创建成功',
data: {
id: release.id,
applicationNumber: release.application_number
}
});
} catch (error) {
console.error('创建解押申请失败:', error);
res.status(500).json({
success: false,
message: '创建解押申请失败'
});
}
};
/**
* 更新解押申请
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const updateRelease = async (req, res) => {
try {
const { id } = req.params;
const updateData = req.body;
const release = await LoanRelease.findByPk(id);
if (!release) {
return res.status(404).json({
success: false,
message: '解押申请不存在'
});
}
// 更新数据
await release.update(updateData);
res.json({
success: true,
message: '解押申请更新成功'
});
} catch (error) {
console.error('更新解押申请失败:', error);
res.status(500).json({
success: false,
message: '更新解押申请失败'
});
}
};
/**
* 处理解押申请
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const processRelease = async (req, res) => {
try {
const { id } = req.params;
const { action, comment, remark } = req.body;
if (!req.user || !req.user.userId) {
return res.status(401).json({
success: false,
message: '用户信息无效'
});
}
const release = await LoanRelease.findByPk(id);
if (!release) {
return res.status(404).json({
success: false,
message: '解押申请不存在'
});
}
if (release.status !== 'pending') {
return res.status(400).json({
success: false,
message: '该申请已完成处理,无法重复操作'
});
}
const previousStatus = release.status;
let newStatus = release.status;
// 根据处理动作更新状态
if (action === 'approve') {
newStatus = 'approved';
} else if (action === 'reject') {
newStatus = 'rejected';
}
// 更新申请状态
release.status = newStatus;
release.process_date = new Date();
release.processor_id = req.user.userId;
release.processor_name = req.user.username;
release.process_comment = comment;
release.remark = remark;
if (action === 'reject') {
release.rejection_reason = comment;
}
await release.save();
// 创建历史记录
await LoanReleaseHistory.create({
release_id: release.id,
action: action,
operator: req.user.username,
operator_id: req.user.userId,
comment: comment,
previous_status: previousStatus,
new_status: newStatus
});
res.json({
success: true,
message: `解押申请已${action === 'approve' ? '通过' : '拒绝'}`,
data: {
id: release.id,
status: newStatus,
action,
comment
}
});
} catch (error) {
console.error('处理解押申请失败:', error);
res.status(500).json({
success: false,
message: '处理解押申请失败'
});
}
};
/**
* 完成解押
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const completeRelease = async (req, res) => {
try {
const { id } = req.params;
const { comment } = req.body;
if (!req.user || !req.user.userId) {
return res.status(401).json({
success: false,
message: '用户信息无效'
});
}
const release = await LoanRelease.findByPk(id);
if (!release) {
return res.status(404).json({
success: false,
message: '解押申请不存在'
});
}
if (release.status !== 'approved') {
return res.status(400).json({
success: false,
message: '只有已通过的解押申请才能完成解押'
});
}
const previousStatus = release.status;
// 更新状态为已完成
release.status = 'completed';
release.complete_date = new Date();
await release.save();
// 创建历史记录
await LoanReleaseHistory.create({
release_id: release.id,
action: 'complete',
operator: req.user.username,
operator_id: req.user.userId,
comment: comment || '解押手续办理完成',
previous_status: previousStatus,
new_status: 'completed'
});
res.json({
success: true,
message: '解押完成',
data: {
id: release.id,
status: 'completed'
}
});
} catch (error) {
console.error('完成解押失败:', error);
res.status(500).json({
success: false,
message: '完成解押失败'
});
}
};
/**
* 删除解押申请
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const deleteRelease = async (req, res) => {
try {
const { id } = req.params;
const release = await LoanRelease.findByPk(id);
if (!release) {
return res.status(404).json({
success: false,
message: '解押申请不存在'
});
}
// 只有待处理状态的申请才能删除
if (release.status !== 'pending') {
return res.status(400).json({
success: false,
message: '只有待处理状态的申请才能删除'
});
}
await release.destroy();
res.json({
success: true,
message: '解押申请删除成功'
});
} catch (error) {
console.error('删除解押申请失败:', error);
res.status(500).json({
success: false,
message: '删除解押申请失败'
});
}
};
/**
* 获取解押统计信息
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const getReleaseStats = async (req, res) => {
try {
const stats = await LoanRelease.findAll({
attributes: [
'status',
[require('sequelize').fn('COUNT', '*'), 'count']
],
group: ['status']
});
const totalCount = await LoanRelease.count();
const pendingCount = await LoanRelease.count({ where: { status: 'pending' } });
const processingCount = await LoanRelease.count({ where: { status: 'processing' } });
const completedCount = await LoanRelease.count({ where: { status: 'completed' } });
res.json({
success: true,
data: {
total: totalCount,
pending: pendingCount,
processing: processingCount,
completed: completedCount,
statusStats: stats
}
});
} catch (error) {
console.error('获取解押统计信息失败:', error);
res.status(500).json({
success: false,
message: '获取解押统计信息失败'
});
}
};
module.exports = {
getReleases,
getReleaseDetail,
createRelease,
updateRelease,
processRelease,
completeRelease,
deleteRelease,
getReleaseStats
};

View File

@@ -1,56 +0,0 @@
const { User } = require('./models')
const bcrypt = require('bcryptjs')
async function createAdminUser() {
try {
console.log('开始创建管理员用户...')
// 检查是否已存在管理员用户
const existingAdmin = await User.findOne({
where: { username: 'admin' }
})
if (existingAdmin) {
console.log('管理员用户已存在,更新密码...')
const hashedPassword = await bcrypt.hash('admin123', 10)
await existingAdmin.update({
password: hashedPassword,
status: 'active'
})
console.log('✅ 管理员用户密码已更新')
} else {
console.log('创建新的管理员用户...')
const hashedPassword = await bcrypt.hash('admin123', 10)
await User.create({
username: 'admin',
password: hashedPassword,
real_name: '系统管理员',
email: 'admin@bank.com',
phone: '13800138000',
id_card: '110101199001010001',
status: 'active',
role_id: 1
})
console.log('✅ 管理员用户创建成功')
}
console.log('管理员用户信息:')
console.log('用户名: admin')
console.log('密码: admin123')
console.log('状态: active')
} catch (error) {
console.error('创建管理员用户失败:', error)
}
}
createAdminUser()
.then(() => {
console.log('管理员用户设置完成')
process.exit(0)
})
.catch((error) => {
console.error('脚本执行失败:', error)
process.exit(1)
})

View File

@@ -1,43 +0,0 @@
const { sequelize } = require('./config/database');
async function createAuditRecordsTable() {
try {
console.log('创建bank_audit_records表...');
await sequelize.query(`
CREATE TABLE IF NOT EXISTS bank_audit_records (
id INT(11) NOT NULL AUTO_INCREMENT,
applicationId INT(11) NOT NULL,
action VARCHAR(50) NOT NULL,
auditor VARCHAR(100) NOT NULL,
auditorId INT(11) NOT NULL,
comment TEXT,
auditTime DATETIME NOT NULL,
previousStatus VARCHAR(50),
newStatus VARCHAR(50),
createdAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_application_id (applicationId),
KEY idx_auditor_id (auditorId),
KEY idx_audit_time (auditTime)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`, { type: sequelize.QueryTypes.RAW });
console.log('✅ bank_audit_records表创建成功');
// 标记迁移为完成
await sequelize.query(`
INSERT IGNORE INTO SequelizeMeta (name) VALUES ('20241220000008-create-audit-records.js')
`, { type: sequelize.QueryTypes.RAW });
console.log('✅ 迁移记录已标记为完成');
} catch (error) {
console.error('❌ 创建表失败:', error.message);
} finally {
await sequelize.close();
}
}
createAuditRecordsTable();

View File

@@ -1,70 +0,0 @@
const http = require('http');
async function testAccountsAPI() {
try {
// 先登录获取token
const loginResult = await makeRequest({
hostname: 'localhost',
port: 5351,
path: '/api/auth/login',
method: 'POST',
headers: { 'Content-Type': 'application/json' }
}, {
username: 'admin',
password: 'Admin123456'
});
if (!loginResult.data.success) {
console.log('登录失败:', loginResult.data.message);
return;
}
const token = loginResult.data.data.token;
console.log('登录成功token:', token.substring(0, 20) + '...');
// 测试账户API
const accountsResult = await makeRequest({
hostname: 'localhost',
port: 5351,
path: '/api/accounts?page=1&pageSize=10',
method: 'GET',
headers: { 'Authorization': `Bearer ${token}` }
});
console.log('账户API响应状态:', accountsResult.status);
console.log('账户API响应数据:', JSON.stringify(accountsResult.data, null, 2));
} catch (error) {
console.error('测试失败:', error);
}
}
function makeRequest(options, data = null) {
return new Promise((resolve, reject) => {
const req = http.request(options, (res) => {
let body = '';
res.on('data', (chunk) => {
body += chunk;
});
res.on('end', () => {
try {
const result = JSON.parse(body);
resolve({ status: res.statusCode, data: result });
} catch (error) {
resolve({ status: res.statusCode, data: body });
}
});
});
req.on('error', (error) => {
reject(error);
});
if (data) {
req.write(JSON.stringify(data));
}
req.end();
});
}
testAccountsAPI();

View File

@@ -1,131 +0,0 @@
const { User, Role } = require('./models');
const bcrypt = require('bcryptjs');
async function debugAuthDetailed() {
try {
console.log('=== 详细调试认证逻辑 ===\n');
// 1. 检查数据库连接
console.log('1. 检查数据库连接...');
const user = await User.findOne({ where: { username: 'admin' } });
if (!user) {
console.log('❌ 未找到admin用户');
return;
}
console.log('✅ 数据库连接正常找到admin用户\n');
// 2. 检查用户基本信息
console.log('2. 检查用户基本信息...');
console.log('用户名:', user.username);
console.log('状态:', user.status);
console.log('登录尝试次数:', user.login_attempts);
console.log('锁定时间:', user.locked_until);
console.log('密码哈希:', user.password);
console.log('');
// 3. 检查用户角色关联
console.log('3. 检查用户角色关联...');
const userWithRole = await User.findOne({
where: { username: 'admin' },
include: [{
model: Role,
as: 'role'
}]
});
if (userWithRole) {
console.log('✅ 用户角色关联正常');
console.log('角色:', userWithRole.role ? userWithRole.role.name : '无角色');
} else {
console.log('❌ 用户角色关联失败');
}
console.log('');
// 4. 测试密码验证
console.log('4. 测试密码验证...');
const testPassword = 'Admin123456';
console.log('测试密码:', testPassword);
// 直接使用bcrypt比较
const directTest = await bcrypt.compare(testPassword, user.password);
console.log('直接bcrypt验证:', directTest);
// 使用模型方法验证
const modelTest = await user.validPassword(testPassword);
console.log('模型验证:', modelTest);
if (!directTest) {
console.log('❌ 密码不匹配,重新生成密码...');
const newHash = await bcrypt.hash(testPassword, 10);
console.log('新哈希:', newHash);
await user.update({
password: newHash,
status: 'active',
login_attempts: 0,
locked_until: null
});
console.log('✅ 密码已更新');
// 重新加载用户数据
await user.reload();
// 再次验证
const finalTest = await bcrypt.compare(testPassword, user.password);
console.log('最终验证:', finalTest);
if (finalTest) {
console.log('🎉 密码修复成功!');
} else {
console.log('❌ 密码修复失败');
}
} else {
console.log('✅ 密码验证成功');
}
console.log('');
// 5. 模拟完整的登录流程
console.log('5. 模拟完整的登录流程...');
const loginUser = await User.findOne({
where: { username: 'admin' },
include: [{
model: Role,
as: 'role'
}]
});
if (loginUser) {
console.log('✅ 用户查找成功');
console.log('用户状态:', loginUser.status);
if (loginUser.status !== 'active') {
console.log('❌ 用户状态不是active:', loginUser.status);
} else {
console.log('✅ 用户状态正常');
}
const passwordValid = await loginUser.validPassword(testPassword);
console.log('密码验证结果:', passwordValid);
if (passwordValid) {
console.log('🎉 完整登录流程验证成功!');
console.log('用户名: admin');
console.log('密码: Admin123456');
console.log('状态: active');
} else {
console.log('❌ 密码验证失败');
}
} else {
console.log('❌ 用户查找失败');
}
} catch (error) {
console.error('调试失败:', error.message);
console.error('错误堆栈:', error.stack);
}
process.exit(0);
}
debugAuthDetailed();

View File

@@ -1,73 +0,0 @@
const { User } = require('./models');
const bcrypt = require('bcryptjs');
async function debugLogin() {
try {
console.log('=== 调试登录问题 ===');
// 查找用户
const user = await User.findOne({ where: { username: 'admin' } });
if (!user) {
console.log('❌ 未找到admin用户');
return;
}
console.log('✅ 找到admin用户');
console.log('用户名:', user.username);
console.log('状态:', user.status);
console.log('登录尝试次数:', user.login_attempts);
console.log('锁定时间:', user.locked_until);
console.log('密码哈希:', user.password);
// 测试密码验证
const testPassword = 'Admin123456';
console.log('\n=== 测试密码验证 ===');
console.log('测试密码:', testPassword);
// 直接使用bcrypt比较
const directTest = await bcrypt.compare(testPassword, user.password);
console.log('直接bcrypt验证:', directTest);
// 使用模型方法验证
const modelTest = await user.validPassword(testPassword);
console.log('模型验证:', modelTest);
if (directTest && modelTest) {
console.log('✅ 密码验证成功!');
} else {
console.log('❌ 密码验证失败');
// 重新生成密码
console.log('\n=== 重新生成密码 ===');
const newHash = await bcrypt.hash(testPassword, 10);
console.log('新哈希:', newHash);
await user.update({
password: newHash,
status: 'active',
login_attempts: 0,
locked_until: null
});
console.log('✅ 密码已更新');
// 再次验证
const finalTest = await bcrypt.compare(testPassword, newHash);
console.log('最终验证:', finalTest);
if (finalTest) {
console.log('🎉 密码修复成功!');
console.log('用户名: admin');
console.log('密码: Admin123456');
}
}
} catch (error) {
console.error('调试失败:', error.message);
console.error('错误堆栈:', error.stack);
}
process.exit(0);
}
debugLogin();

View File

@@ -1,83 +0,0 @@
const { User } = require('./models');
const bcrypt = require('bcryptjs');
async function debugThisPassword() {
try {
console.log('=== 调试this.password问题 ===\n');
// 1. 获取用户
const user = await User.findOne({ where: { username: 'admin' } });
if (!user) {
console.log('❌ 未找到admin用户');
return;
}
console.log('1. 用户基本信息:');
console.log('用户名:', user.username);
console.log('this.password类型:', typeof user.password);
console.log('this.password值:', user.password);
console.log('this.password长度:', user.password ? user.password.length : 0);
console.log('');
// 2. 测试密码
const testPassword = 'Admin123456';
console.log('2. 测试密码:', testPassword);
console.log('');
// 3. 直接使用bcrypt比较
console.log('3. 直接使用bcrypt比较:');
const directTest = await bcrypt.compare(testPassword, user.password);
console.log('直接bcrypt验证结果:', directTest);
console.log('');
// 4. 检查this.password是否为空或undefined
console.log('4. 检查this.password状态:');
console.log('this.password === null:', user.password === null);
console.log('this.password === undefined:', user.password === undefined);
console.log('this.password === "":', user.password === "");
console.log('this.password存在:', !!user.password);
console.log('');
// 5. 如果this.password有问题重新设置
if (!user.password || user.password === '') {
console.log('5. this.password有问题重新设置...');
const newHash = await bcrypt.hash(testPassword, 10);
console.log('新生成的哈希:', newHash);
await user.update({ password: newHash });
await user.reload();
console.log('更新后的this.password:', user.password);
console.log('更新后的this.password类型:', typeof user.password);
console.log('');
}
// 6. 再次测试
console.log('6. 再次测试密码验证:');
const finalTest = await user.validPassword(testPassword);
console.log('最终验证结果:', finalTest);
// 7. 手动测试bcrypt
console.log('7. 手动测试bcrypt:');
const manualTest = await bcrypt.compare(testPassword, user.password);
console.log('手动bcrypt验证结果:', manualTest);
if (finalTest && manualTest) {
console.log('🎉 密码验证成功!');
} else {
console.log('❌ 密码验证失败');
console.log('可能的原因:');
console.log('- this.password为空或undefined');
console.log('- 数据库更新失败');
console.log('- Sequelize模型问题');
}
} catch (error) {
console.error('调试失败:', error.message);
console.error('错误堆栈:', error.stack);
}
process.exit(0);
}
debugThisPassword();

View File

@@ -797,17 +797,248 @@
- **接口地址**: `POST /api/employees/export`
- **功能描述**: 导出员工数据
## 13. 个人中心模块 (Profile)
## 13. 贷款解押模块 (Loan Release)
### 13.1 获取个人信息
### 13.1 获取解押申请列表
- **接口地址**: `GET /api/loan-releases`
- **功能描述**: 获取贷款解押申请列表,支持分页和搜索
- **请求参数**:
- `page` (integer, optional): 页码默认1
- `pageSize` (integer, optional): 每页数量默认10
- `searchField` (string, optional): 搜索字段 (application_number, customer_name, product_name)
- `searchValue` (string, optional): 搜索值
- `status` (string, optional): 解押状态筛选
- `sortField` (string, optional): 排序字段
- `sortOrder` (string, optional): 排序方向
- **响应数据**:
```json
{
"success": true,
"data": {
"releases": [
{
"id": 1,
"applicationNumber": "20240227145555918",
"productName": "中国工商银行扎旗支行\"畜禽活体抵押\"",
"farmerName": "刘超",
"applicantName": "刘超",
"applicantIdNumber": "511123199001013017",
"applicantPhone": "13800138000",
"assetType": "牛",
"releaseQuantity": "10头",
"releaseAmount": 10000,
"status": "released",
"applicationTime": "2024-02-27T06:55:55.000Z",
"processTime": "2024-02-27T07:30:00.000Z",
"completeTime": "2024-02-27T08:00:00.000Z",
"processorName": "系统管理员",
"processComment": "审核通过,同意解押",
"rejectionReason": null,
"remark": "客户信用良好",
"created_at": "2025-09-26T03:38:50.000Z",
"updated_at": "2025-09-26T03:38:50.000Z"
}
],
"pagination": {
"current": 1,
"pageSize": 10,
"total": 6,
"totalPages": 1
}
}
}
```
### 13.2 获取解押申请详情
- **接口地址**: `GET /api/loan-releases/:id`
- **功能描述**: 获取指定解押申请的详细信息
- **路径参数**:
- `id` (integer, required): 解押申请ID
- **响应数据**:
```json
{
"success": true,
"data": {
"id": 1,
"releaseNumber": "20240227145555918",
"customerName": "刘超",
"contractNumber": "CONTRACT-000007",
"status": "released",
"collateralType": "livestock",
"collateralDescription": "优质肉牛",
"loanAmount": 100000,
"collateralValue": 150000,
"applicationTime": "2024-02-27T06:55:55.000Z",
"processTime": "2024-02-27T07:30:00.000Z",
"completeTime": "2024-02-27T08:00:00.000Z",
"phone": "13800138000",
"idCard": "511123199001013017",
"reason": "需要资金周转,申请部分解押",
"remark": "客户信用良好",
"history": [
{
"id": 1,
"action": "apply",
"operator": "刘超",
"time": "2025-09-26T03:38:51.000Z",
"comment": "提交解押申请"
},
{
"id": 2,
"action": "complete",
"operator": "系统管理员",
"time": "2025-09-26T03:38:51.000Z",
"comment": "解押手续办理完成"
}
]
}
}
```
### 13.3 创建解押申请
- **接口地址**: `POST /api/loan-releases`
- **功能描述**: 创建新的解押申请
- **请求参数**:
```json
{
"contract_id": 1,
"customer_name": "测试用户",
"customer_phone": "13800138000",
"customer_id_card": "511123199001010001",
"farmer_name": "测试用户",
"product_name": "测试产品",
"collateral_type": "livestock",
"collateral_description": "测试抵押物",
"collateral_value": 100000,
"loan_amount": 80000,
"release_amount": 50000,
"release_quantity": "5头",
"application_reason": "测试解押申请",
"remark": "测试备注"
}
```
- **响应数据**:
```json
{
"success": true,
"message": "解押申请创建成功",
"data": {
"id": 13,
"applicationNumber": "REL-1758868323652"
}
}
```
### 13.4 更新解押申请
- **接口地址**: `PUT /api/loan-releases/:id`
- **功能描述**: 更新指定解押申请信息
- **路径参数**:
- `id` (integer, required): 解押申请ID
- **请求参数**: 同创建解押申请参数
- **响应数据**:
```json
{
"success": true,
"message": "解押申请更新成功"
}
```
### 13.5 处理解押申请
- **接口地址**: `POST /api/loan-releases/:id/process`
- **功能描述**: 处理解押申请(通过/拒绝)
- **路径参数**:
- `id` (integer, required): 解押申请ID
- **请求参数**:
```json
{
"action": "approve",
"comment": "审核通过",
"remark": "测试处理"
}
```
- **响应数据**:
```json
{
"success": true,
"message": "解押申请已通过",
"data": {
"id": 13,
"status": "approved",
"action": "approve",
"comment": "审核通过"
}
}
```
### 13.6 完成解押
- **接口地址**: `POST /api/loan-releases/:id/complete`
- **功能描述**: 完成解押手续
- **路径参数**:
- `id` (integer, required): 解押申请ID
- **请求参数**:
```json
{
"comment": "解押手续办理完成"
}
```
- **响应数据**:
```json
{
"success": true,
"message": "解押完成",
"data": {
"id": 13,
"status": "completed"
}
}
```
### 13.7 删除解押申请
- **接口地址**: `DELETE /api/loan-releases/:id`
- **功能描述**: 删除指定解押申请(仅限待处理状态)
- **路径参数**:
- `id` (integer, required): 解押申请ID
- **响应数据**:
```json
{
"success": true,
"message": "解押申请删除成功"
}
```
### 13.8 获取解押统计信息
- **接口地址**: `GET /api/loan-releases/stats`
- **功能描述**: 获取解押申请统计信息
- **响应数据**:
```json
{
"success": true,
"data": {
"total": 6,
"pending": 0,
"processing": 0,
"completed": 6,
"statusStats": [
{
"status": "released",
"count": 6
}
]
}
}
```
## 14. 个人中心模块 (Profile)
### 14.1 获取个人信息
- **接口地址**: `GET /api/profile`
- **功能描述**: 获取当前用户个人信息
### 13.2 更新个人信息
### 14.2 更新个人信息
- **接口地址**: `PUT /api/profile`
- **功能描述**: 更新个人信息
### 13.3 修改密码
### 14.3 修改密码
- **接口地址**: `POST /api/profile/change-password`
- **功能描述**: 修改用户密码
- **请求参数**:
@@ -818,48 +1049,48 @@
}
```
### 13.4 更新头像
### 14.4 更新头像
- **接口地址**: `POST /api/profile/avatar`
- **功能描述**: 更新用户头像
- **请求类型**: `multipart/form-data`
### 13.5 获取通知列表
### 14.5 获取通知列表
- **接口地址**: `GET /api/profile/notifications`
- **功能描述**: 获取用户通知列表
### 13.6 标记通知已读
### 14.6 标记通知已读
- **接口地址**: `PUT /api/profile/notifications/:id/read`
- **功能描述**: 标记通知为已读
## 14. 系统设置模块 (Settings)
## 15. 系统设置模块 (Settings)
### 14.1 获取系统设置
### 15.1 获取系统设置
- **接口地址**: `GET /api/settings`
- **功能描述**: 获取系统配置设置
### 14.2 更新基本设置
### 15.2 更新基本设置
- **接口地址**: `PUT /api/settings/basic`
- **功能描述**: 更新系统基本设置
### 14.3 更新安全设置
### 15.3 更新安全设置
- **接口地址**: `PUT /api/settings/security`
- **功能描述**: 更新安全相关设置
### 14.4 获取系统日志
### 15.4 获取系统日志
- **接口地址**: `GET /api/settings/logs`
- **功能描述**: 获取系统操作日志
### 14.5 备份系统数据
### 15.5 备份系统数据
- **接口地址**: `POST /api/settings/backup`
- **功能描述**: 备份系统数据
### 14.6 恢复系统数据
### 15.6 恢复系统数据
- **接口地址**: `POST /api/settings/restore`
- **功能描述**: 恢复系统数据
## 15. 文件上传模块 (File Upload)
## 16. 文件上传模块 (File Upload)
### 15.1 上传文件
### 16.1 上传文件
- **接口地址**: `POST /api/upload`
- **功能描述**: 上传文件
- **请求类型**: `multipart/form-data`
@@ -867,13 +1098,13 @@
- `file`: 文件
- `type`: 文件类型 (avatar, document, image)
### 15.2 删除文件
### 16.2 删除文件
- **接口地址**: `DELETE /api/upload/:id`
- **功能描述**: 删除已上传的文件
## 16. 健康检查模块 (Health Check)
## 17. 健康检查模块 (Health Check)
### 16.1 系统健康检查
### 17.1 系统健康检查
- **接口地址**: `GET /api/health`
- **功能描述**: 检查系统健康状态
- **响应数据**:
@@ -956,7 +1187,7 @@ Authorization: Bearer <your-jwt-token>
---
**文档版本**: v1.0.0
**最后更新**: 2024-01-18
**文档版本**: v1.1.0
**最后更新**: 2025-09-26
**维护人员**: 开发团队

View File

@@ -0,0 +1,272 @@
# 银行管理系统API接口总览
## 概述
本文档提供了银行管理系统所有API接口的快速参考包括接口地址、请求方法、功能描述等信息。
## 基础信息
- **基础URL**: `http://localhost:5351`
- **API版本**: v1.0.0
- **认证方式**: JWT Token
- **数据格式**: JSON
## 接口列表
### 1. 认证模块 (Authentication)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/auth/login` | POST | 用户登录 | 否 |
| `/api/auth/logout` | POST | 用户登出 | 是 |
| `/api/auth/refresh` | POST | 刷新令牌 | 是 |
| `/api/auth/me` | GET | 获取当前用户信息 | 是 |
| `/api/auth/change-password` | POST | 修改密码 | 是 |
### 2. 仪表盘模块 (Dashboard)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/dashboard` | GET | 获取仪表盘统计数据 | 是 |
| `/api/dashboard/charts` | GET | 获取图表数据 | 是 |
| `/api/dashboard/recent-transactions` | GET | 获取最近交易记录 | 是 |
### 3. 用户管理模块 (Users)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/users` | GET | 获取用户列表 | 是 |
| `/api/users/:id` | GET | 获取用户详情 | 是 |
| `/api/users` | POST | 创建用户 | 是 |
| `/api/users/:id` | PUT | 更新用户 | 是 |
| `/api/users/:id` | DELETE | 删除用户 | 是 |
| `/api/users/stats` | GET | 获取用户统计 | 是 |
### 4. 账户管理模块 (Accounts)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/accounts` | GET | 获取账户列表 | 是 |
| `/api/accounts/:id` | GET | 获取账户详情 | 是 |
| `/api/accounts` | POST | 创建账户 | 是 |
| `/api/accounts/:id` | PUT | 更新账户 | 是 |
| `/api/accounts/:id` | DELETE | 删除账户 | 是 |
| `/api/accounts/:id/deposit` | POST | 存款 | 是 |
| `/api/accounts/:id/withdraw` | POST | 取款 | 是 |
### 5. 交易记录模块 (Transactions)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/transactions` | GET | 获取交易记录列表 | 是 |
| `/api/transactions/:id` | GET | 获取交易详情 | 是 |
| `/api/transactions` | POST | 创建交易记录 | 是 |
| `/api/transactions/stats` | GET | 获取交易统计 | 是 |
### 6. 贷款产品模块 (Loan Products)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/loan-products` | GET | 获取贷款产品列表 | 是 |
| `/api/loan-products/:id` | GET | 获取产品详情 | 是 |
| `/api/loan-products` | POST | 创建产品 | 是 |
| `/api/loan-products/:id` | PUT | 更新产品 | 是 |
| `/api/loan-products/:id` | DELETE | 删除产品 | 是 |
| `/api/loan-products/stats` | GET | 获取产品统计 | 是 |
| `/api/loan-products/batch/status` | PUT | 批量更新状态 | 是 |
| `/api/loan-products/batch/delete` | DELETE | 批量删除 | 是 |
### 7. 贷款申请模块 (Loan Applications)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/loan-applications` | GET | 获取申请列表 | 是 |
| `/api/loan-applications/:id` | GET | 获取申请详情 | 是 |
| `/api/loan-applications/:id/audit` | POST | 审核申请 | 是 |
| `/api/loan-applications/stats` | GET | 获取申请统计 | 是 |
| `/api/loan-applications/batch/status` | PUT | 批量更新状态 | 是 |
### 8. 贷款合同模块 (Loan Contracts)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/loan-contracts` | GET | 获取合同列表 | 是 |
| `/api/loan-contracts/:id` | GET | 获取合同详情 | 是 |
| `/api/loan-contracts` | POST | 创建合同 | 是 |
| `/api/loan-contracts/:id` | PUT | 更新合同 | 是 |
| `/api/loan-contracts/:id` | DELETE | 删除合同 | 是 |
| `/api/loan-contracts/stats` | GET | 获取合同统计 | 是 |
| `/api/loan-contracts/batch/status` | PUT | 批量更新状态 | 是 |
### 9. 贷款解押模块 (Loan Release) - 新增
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/loan-releases` | GET | 获取解押申请列表 | 是 |
| `/api/loan-releases/:id` | GET | 获取解押申请详情 | 是 |
| `/api/loan-releases` | POST | 创建解押申请 | 是 |
| `/api/loan-releases/:id` | PUT | 更新解押申请 | 是 |
| `/api/loan-releases/:id/process` | POST | 处理解押申请 | 是 |
| `/api/loan-releases/:id/complete` | POST | 完成解押 | 是 |
| `/api/loan-releases/:id` | DELETE | 删除解押申请 | 是 |
| `/api/loan-releases/stats` | GET | 获取解押统计 | 是 |
### 10. 员工管理模块 (Employees)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/employees` | GET | 获取员工列表 | 是 |
| `/api/employees/:id` | GET | 获取员工详情 | 是 |
| `/api/employees` | POST | 创建员工 | 是 |
| `/api/employees/:id` | PUT | 更新员工 | 是 |
| `/api/employees/:id` | DELETE | 删除员工 | 是 |
| `/api/employees/stats` | GET | 获取员工统计 | 是 |
| `/api/employees/export` | POST | 导出员工数据 | 是 |
### 11. 项目管理模块 (Projects)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/projects` | GET | 获取项目列表 | 是 |
| `/api/projects/:id` | GET | 获取项目详情 | 是 |
| `/api/projects` | POST | 创建项目 | 是 |
| `/api/projects/:id` | PUT | 更新项目 | 是 |
| `/api/projects/:id` | DELETE | 删除项目 | 是 |
| `/api/projects/stats` | GET | 获取项目统计 | 是 |
### 12. 监管任务模块 (Supervision Tasks)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/supervision-tasks` | GET | 获取监管任务列表 | 是 |
| `/api/supervision-tasks/:id` | GET | 获取任务详情 | 是 |
| `/api/supervision-tasks` | POST | 创建监管任务 | 是 |
| `/api/supervision-tasks/:id` | PUT | 更新任务 | 是 |
| `/api/supervision-tasks/:id` | DELETE | 删除任务 | 是 |
| `/api/supervision-tasks/stats` | GET | 获取任务统计 | 是 |
### 13. 安装任务模块 (Installation Tasks)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/installation-tasks` | GET | 获取安装任务列表 | 是 |
| `/api/installation-tasks/:id` | GET | 获取任务详情 | 是 |
| `/api/installation-tasks` | POST | 创建安装任务 | 是 |
| `/api/installation-tasks/:id` | PUT | 更新任务 | 是 |
| `/api/installation-tasks/:id` | DELETE | 删除任务 | 是 |
| `/api/installation-tasks/stats` | GET | 获取任务统计 | 是 |
### 14. 完成监管模块 (Completed Supervisions)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/completed-supervisions` | GET | 获取完成监管列表 | 是 |
| `/api/completed-supervisions/:id` | GET | 获取完成监管详情 | 是 |
| `/api/completed-supervisions` | POST | 创建完成监管记录 | 是 |
| `/api/completed-supervisions/:id` | PUT | 更新完成监管记录 | 是 |
| `/api/completed-supervisions/:id` | DELETE | 删除完成监管记录 | 是 |
| `/api/completed-supervisions/stats` | GET | 获取完成监管统计 | 是 |
| `/api/completed-supervisions/batch/delete` | DELETE | 批量删除 | 是 |
### 15. 报表模块 (Reports)
| 接口地址 | 请求方法 | 功能描述 | 认证要求 |
|---------|---------|---------|---------|
| `/api/reports` | GET | 获取报表列表 | 是 |
| `/api/reports/:id` | GET | 获取报表详情 | 是 |
| `/api/reports` | POST | 创建报表 | 是 |
| `/api/reports/:id` | PUT | 更新报表 | 是 |
| `/api/reports/:id` | DELETE | 删除报表 | 是 |
| `/api/reports/export` | POST | 导出报表 | 是 |
## 通用参数说明
### 分页参数
- `page`: 页码从1开始默认1
- `pageSize`: 每页数量默认10最大100
### 搜索参数
- `searchField`: 搜索字段
- `searchValue`: 搜索值
- `startDate`: 开始日期 (YYYY-MM-DD)
- `endDate`: 结束日期 (YYYY-MM-DD)
### 排序参数
- `sortField`: 排序字段
- `sortOrder`: 排序方向 (asc/desc)
### 筛选参数
- `status`: 状态筛选
- `type`: 类型筛选
- `category`: 分类筛选
## 响应格式
### 成功响应
```json
{
"success": true,
"message": "操作成功",
"data": {},
"timestamp": "2025-09-26T10:30:00.000Z"
}
```
### 错误响应
```json
{
"success": false,
"message": "错误信息",
"error": "ERROR_CODE",
"timestamp": "2025-09-26T10:30:00.000Z"
}
```
### 分页响应
```json
{
"success": true,
"data": {
"items": [],
"pagination": {
"current": 1,
"pageSize": 10,
"total": 100,
"totalPages": 10
}
}
}
```
## 认证说明
所有需要认证的接口都需要在请求头中携带JWT令牌
```bash
Authorization: Bearer <your-jwt-token>
```
## 错误码说明
| 错误码 | 说明 |
|--------|------|
| 400 | 请求参数错误 |
| 401 | 未授权访问 |
| 403 | 禁止访问 |
| 404 | 资源不存在 |
| 409 | 资源冲突 |
| 422 | 数据验证失败 |
| 500 | 服务器内部错误 |
## 限流说明
- 普通接口: 100次/分钟
- 登录接口: 10次/分钟
- 文件上传: 20次/分钟
- 数据导出: 5次/分钟
---
**文档版本**: v1.1.0
**最后更新**: 2025-09-26
**维护人员**: 开发团队

View File

@@ -0,0 +1,476 @@
# 贷款解押模块API接口文档
## 概述
本文档详细描述了银行管理系统贷款解押模块的所有API接口包括接口地址、请求方法、参数说明、响应格式等信息。
## 基础信息
- **基础URL**: `http://localhost:5351`
- **模块路径**: `/api/loan-releases`
- **认证方式**: JWT Token
- **数据格式**: JSON
- **字符编码**: UTF-8
## 通用响应格式
### 成功响应
```json
{
"success": true,
"message": "操作成功",
"data": {},
"timestamp": "2025-09-26T10:30:00.000Z"
}
```
### 错误响应
```json
{
"success": false,
"message": "错误信息",
"error": "ERROR_CODE",
"timestamp": "2025-09-26T10:30:00.000Z"
}
```
## 1. 获取解押申请列表
- **接口地址**: `GET /api/loan-releases`
- **功能描述**: 获取贷款解押申请列表,支持分页和搜索
- **认证要求**: 需要JWT Token
- **请求参数**:
- `page` (integer, optional): 页码默认1
- `pageSize` (integer, optional): 每页数量默认10最大100
- `searchField` (string, optional): 搜索字段,可选值:
- `application_number`: 申请单号
- `customer_name`: 客户姓名
- `product_name`: 产品名称
- `searchValue` (string, optional): 搜索值
- `status` (string, optional): 解押状态筛选,可选值:
- `pending`: 待处理
- `processing`: 处理中
- `approved`: 已通过
- `rejected`: 已拒绝
- `completed`: 已完成
- `released`: 已解押
- `sortField` (string, optional): 排序字段默认created_at
- `sortOrder` (string, optional): 排序方向可选值asc, desc默认desc
- **请求示例**:
```bash
GET /api/loan-releases?page=1&pageSize=10&searchField=application_number&searchValue=20240227&status=pending
```
- **响应数据**:
```json
{
"success": true,
"data": {
"releases": [
{
"id": 1,
"applicationNumber": "20240227145555918",
"productName": "中国工商银行扎旗支行\"畜禽活体抵押\"",
"farmerName": "刘超",
"applicantName": "刘超",
"applicantIdNumber": "511123199001013017",
"applicantPhone": "13800138000",
"assetType": "牛",
"releaseQuantity": "10头",
"releaseAmount": 10000,
"status": "released",
"applicationTime": "2024-02-27T06:55:55.000Z",
"processTime": "2024-02-27T07:30:00.000Z",
"completeTime": "2024-02-27T08:00:00.000Z",
"processorName": "系统管理员",
"processComment": "审核通过,同意解押",
"rejectionReason": null,
"remark": "客户信用良好",
"created_at": "2025-09-26T03:38:50.000Z",
"updated_at": "2025-09-26T03:38:50.000Z"
}
],
"pagination": {
"current": 1,
"pageSize": 10,
"total": 6,
"totalPages": 1
}
}
}
```
## 2. 获取解押申请详情
- **接口地址**: `GET /api/loan-releases/:id`
- **功能描述**: 获取指定解押申请的详细信息
- **认证要求**: 需要JWT Token
- **路径参数**:
- `id` (integer, required): 解押申请ID
- **请求示例**:
```bash
GET /api/loan-releases/1
```
- **响应数据**:
```json
{
"success": true,
"data": {
"id": 1,
"releaseNumber": "20240227145555918",
"customerName": "刘超",
"contractNumber": "CONTRACT-000007",
"status": "released",
"collateralType": "livestock",
"collateralDescription": "优质肉牛",
"loanAmount": 100000,
"collateralValue": 150000,
"applicationTime": "2024-02-27T06:55:55.000Z",
"processTime": "2024-02-27T07:30:00.000Z",
"completeTime": "2024-02-27T08:00:00.000Z",
"phone": "13800138000",
"idCard": "511123199001013017",
"reason": "需要资金周转,申请部分解押",
"remark": "客户信用良好",
"history": [
{
"id": 1,
"action": "apply",
"operator": "刘超",
"time": "2025-09-26T03:38:51.000Z",
"comment": "提交解押申请"
},
{
"id": 2,
"action": "complete",
"operator": "系统管理员",
"time": "2025-09-26T03:38:51.000Z",
"comment": "解押手续办理完成"
}
]
}
}
```
## 3. 创建解押申请
- **接口地址**: `POST /api/loan-releases`
- **功能描述**: 创建新的解押申请
- **认证要求**: 需要JWT Token
- **请求参数**:
```json
{
"contract_id": 1,
"customer_name": "测试用户",
"customer_phone": "13800138000",
"customer_id_card": "511123199001010001",
"farmer_name": "测试用户",
"product_name": "测试产品",
"collateral_type": "livestock",
"collateral_description": "测试抵押物",
"collateral_value": 100000,
"loan_amount": 80000,
"release_amount": 50000,
"release_quantity": "5头",
"application_reason": "测试解押申请",
"remark": "测试备注"
}
```
- **字段说明**:
- `contract_id` (integer, optional): 关联合同ID
- `customer_name` (string, required): 客户姓名
- `customer_phone` (string, required): 客户电话
- `customer_id_card` (string, required): 客户身份证号
- `farmer_name` (string, optional): 养殖户姓名
- `product_name` (string, optional): 贷款产品名称
- `collateral_type` (string, required): 抵押物类型,可选值:
- `house`: 房产
- `car`: 车辆
- `land`: 土地
- `equipment`: 设备
- `livestock`: 牲畜
- `collateral_description` (string, optional): 抵押物描述
- `collateral_value` (number, optional): 抵押物价值
- `loan_amount` (number, optional): 贷款金额
- `release_amount` (number, required): 解押金额
- `release_quantity` (string, optional): 解押数量
- `application_reason` (string, optional): 申请原因
- `remark` (string, optional): 备注
- **请求示例**:
```bash
POST /api/loan-releases
Content-Type: application/json
Authorization: Bearer <your-jwt-token>
{
"customer_name": "测试用户",
"customer_phone": "13800138000",
"customer_id_card": "511123199001010001",
"collateral_type": "livestock",
"release_amount": 50000,
"release_quantity": "5头",
"application_reason": "测试解押申请"
}
```
- **响应数据**:
```json
{
"success": true,
"message": "解押申请创建成功",
"data": {
"id": 13,
"applicationNumber": "REL-1758868323652"
}
}
```
## 4. 更新解押申请
- **接口地址**: `PUT /api/loan-releases/:id`
- **功能描述**: 更新指定解押申请信息
- **认证要求**: 需要JWT Token
- **路径参数**:
- `id` (integer, required): 解押申请ID
- **请求参数**: 同创建解押申请参数
- **限制条件**: 只有待处理状态的申请才能更新
- **请求示例**:
```bash
PUT /api/loan-releases/13
Content-Type: application/json
Authorization: Bearer <your-jwt-token>
{
"release_amount": 60000,
"application_reason": "更新后的申请原因"
}
```
- **响应数据**:
```json
{
"success": true,
"message": "解押申请更新成功"
}
```
## 5. 处理解押申请
- **接口地址**: `POST /api/loan-releases/:id/process`
- **功能描述**: 处理解押申请(通过/拒绝)
- **认证要求**: 需要JWT Token
- **路径参数**:
- `id` (integer, required): 解押申请ID
- **请求参数**:
```json
{
"action": "approve",
"comment": "审核通过",
"remark": "测试处理"
}
```
- **字段说明**:
- `action` (string, required): 处理动作,可选值:
- `approve`: 通过
- `reject`: 拒绝
- `comment` (string, required): 处理意见
- `remark` (string, optional): 备注
- **请求示例**:
```bash
POST /api/loan-releases/13/process
Content-Type: application/json
Authorization: Bearer <your-jwt-token>
{
"action": "approve",
"comment": "审核通过,同意解押",
"remark": "客户信用良好"
}
```
- **响应数据**:
```json
{
"success": true,
"message": "解押申请已通过",
"data": {
"id": 13,
"status": "approved",
"action": "approve",
"comment": "审核通过"
}
}
```
## 6. 完成解押
- **接口地址**: `POST /api/loan-releases/:id/complete`
- **功能描述**: 完成解押手续
- **认证要求**: 需要JWT Token
- **路径参数**:
- `id` (integer, required): 解押申请ID
- **请求参数**:
```json
{
"comment": "解押手续办理完成"
}
```
- **字段说明**:
- `comment` (string, required): 完成说明
- **请求示例**:
```bash
POST /api/loan-releases/13/complete
Content-Type: application/json
Authorization: Bearer <your-jwt-token>
{
"comment": "解押手续办理完成,抵押物已释放"
}
```
- **响应数据**:
```json
{
"success": true,
"message": "解押完成",
"data": {
"id": 13,
"status": "completed"
}
}
```
## 7. 删除解押申请
- **接口地址**: `DELETE /api/loan-releases/:id`
- **功能描述**: 删除指定解押申请(仅限待处理状态)
- **认证要求**: 需要JWT Token
- **路径参数**:
- `id` (integer, required): 解押申请ID
- **限制条件**: 只有待处理状态的申请才能删除
- **请求示例**:
```bash
DELETE /api/loan-releases/13
Authorization: Bearer <your-jwt-token>
```
- **响应数据**:
```json
{
"success": true,
"message": "解押申请删除成功"
}
```
## 8. 获取解押统计信息
- **接口地址**: `GET /api/loan-releases/stats`
- **功能描述**: 获取解押申请统计信息
- **认证要求**: 需要JWT Token
- **请求示例**:
```bash
GET /api/loan-releases/stats
Authorization: Bearer <your-jwt-token>
```
- **响应数据**:
```json
{
"success": true,
"data": {
"total": 6,
"pending": 0,
"processing": 0,
"approved": 0,
"rejected": 0,
"completed": 0,
"released": 6,
"statusStats": [
{
"status": "released",
"count": 6
}
],
"amountStats": {
"totalReleaseAmount": 1500000,
"averageReleaseAmount": 250000
}
}
}
```
## 状态说明
### 解押申请状态流转
1. **pending** (待处理) - 初始状态,申请已提交
2. **processing** (处理中) - 正在审核中
3. **approved** (已通过) - 审核通过
4. **rejected** (已拒绝) - 审核拒绝
5. **completed** (已完成) - 解押手续完成
6. **released** (已解押) - 抵押物已释放
### 状态流转规则
- `pending` → `processing` (开始处理)
- `processing` → `approved` (审核通过)
- `processing` → `rejected` (审核拒绝)
- `approved` → `completed` (完成解押)
- `completed` → `released` (释放抵押物)
## 错误码说明
| 错误码 | 说明 |
|--------|------|
| 400 | 请求参数错误 |
| 401 | 未授权访问 |
| 403 | 禁止访问 |
| 404 | 解押申请不存在 |
| 409 | 状态不允许此操作 |
| 422 | 数据验证失败 |
| 500 | 服务器内部错误 |
## 数据验证规则
### 必填字段验证
- `customer_name`: 客户姓名不能为空
- `customer_phone`: 客户电话不能为空,格式验证
- `customer_id_card`: 客户身份证号不能为空,格式验证
- `collateral_type`: 抵押物类型不能为空
- `release_amount`: 解押金额不能为空必须大于0
### 格式验证
- `customer_phone`: 手机号格式验证
- `customer_id_card`: 身份证号格式验证
- `release_amount`: 金额格式验证保留2位小数
### 业务规则验证
- 解押金额不能超过贷款金额
- 只有待处理状态的申请才能更新或删除
- 只有已通过状态的申请才能完成解押
## 权限说明
- **查看权限**: 所有认证用户
- **创建权限**: 所有认证用户
- **更新权限**: 申请创建者或管理员
- **处理权限**: 贷款专员或管理员
- **删除权限**: 申请创建者或管理员
---
**文档版本**: v1.0.0
**最后更新**: 2025-09-26
**维护人员**: 开发团队

View File

@@ -1,82 +0,0 @@
const { User } = require('./models');
const { sequelize } = require('./config/database');
const bcrypt = require('bcryptjs');
async function fixPasswordFinal() {
try {
console.log('=== 最终修复密码问题 ===\n');
const testPassword = 'Admin123456';
console.log('目标密码:', testPassword);
// 1. 生成新的密码哈希
const newHash = await bcrypt.hash(testPassword, 10);
console.log('1. 新生成的哈希:', newHash);
console.log('哈希长度:', newHash.length);
// 2. 验证新生成的哈希
const hashValid = await bcrypt.compare(testPassword, newHash);
console.log('2. 新哈希验证:', hashValid);
if (!hashValid) {
console.log('❌ 生成的哈希无效,退出');
return;
}
// 3. 直接使用SQL更新密码
console.log('3. 直接使用SQL更新密码...');
const [affectedRows] = await sequelize.query(
'UPDATE bank_users SET password = ?, status = ?, login_attempts = 0, locked_until = NULL WHERE username = ?',
{
replacements: [newHash, 'active', 'admin'],
type: sequelize.QueryTypes.UPDATE
}
);
console.log('SQL更新影响行数:', affectedRows);
// 4. 验证数据库更新
console.log('4. 验证数据库更新...');
const [results] = await sequelize.query(
'SELECT username, password, status, login_attempts FROM bank_users WHERE username = ?',
{
replacements: ['admin'],
type: sequelize.QueryTypes.SELECT
}
);
if (results) {
console.log('数据库中的数据:');
console.log('用户名:', results.username);
console.log('状态:', results.status);
console.log('登录尝试次数:', results.login_attempts);
console.log('密码哈希:', results.password);
console.log('哈希长度:', results.password.length);
// 5. 验证更新后的密码
const dbValid = await bcrypt.compare(testPassword, results.password);
console.log('5. 数据库密码验证:', dbValid);
if (dbValid) {
console.log('🎉 密码修复成功!');
console.log('\n=== 登录信息 ===');
console.log('用户名: admin');
console.log('密码: Admin123456');
console.log('状态: active');
console.log('现在可以尝试登录了!');
} else {
console.log('❌ 密码验证仍然失败');
}
} else {
console.log('❌ 查询数据库失败');
}
} catch (error) {
console.error('修复失败:', error.message);
console.error('错误堆栈:', error.stack);
}
process.exit(0);
}
fixPasswordFinal();

View File

@@ -1,52 +0,0 @@
const { sequelize } = require('./config/database');
async function fixSupervisionTasks() {
try {
console.log('开始修复supervision_tasks表...');
// 检查并添加created_by字段
const createdByExists = await sequelize.query(
'SHOW COLUMNS FROM supervision_tasks LIKE "created_by"',
{ type: sequelize.QueryTypes.SELECT }
);
if (createdByExists.length === 0) {
await sequelize.query(
'ALTER TABLE supervision_tasks ADD COLUMN created_by INT(11) AFTER remarks',
{ type: sequelize.QueryTypes.RAW }
);
console.log('✅ 添加created_by字段成功');
} else {
console.log('✅ created_by字段已存在');
}
// 检查并添加updated_by字段
const updatedByExists = await sequelize.query(
'SHOW COLUMNS FROM supervision_tasks LIKE "updated_by"',
{ type: sequelize.QueryTypes.SELECT }
);
if (updatedByExists.length === 0) {
await sequelize.query(
'ALTER TABLE supervision_tasks ADD COLUMN updated_by INT(11) AFTER created_by',
{ type: sequelize.QueryTypes.RAW }
);
console.log('✅ 添加updated_by字段成功');
} else {
console.log('✅ updated_by字段已存在');
}
// 显示表结构
const columns = await sequelize.query('DESCRIBE supervision_tasks', { type: sequelize.QueryTypes.SELECT });
console.log('\nsupervision_tasks表结构:');
columns.forEach(col => console.log('-', col.Field, col.Type));
console.log('\n✅ supervision_tasks表修复完成');
} catch (error) {
console.error('❌ 修复失败:', error.message);
} finally {
await sequelize.close();
}
}
fixSupervisionTasks();

View File

@@ -1,75 +0,0 @@
const { sequelize } = require('./config/database');
async function fixTestData() {
try {
console.log('开始修复测试数据...');
// 添加installation_tasks测试数据
await sequelize.query(`
INSERT INTO installation_tasks (
applicationNumber, contractNumber, productName, customerName, idType, idNumber,
assetType, equipmentToInstall, installationStatus, taskGenerationTime, completionTime,
installationNotes, installerName, installerPhone, installationAddress,
createdBy, updatedBy, createdAt, updatedAt
) VALUES
('APP001', 'CON001', '养殖设备', '张三', 'ID_CARD', '110101199001010001',
'cattle', '耳标设备', 'pending', NOW(), NULL,
'测试安装任务', '安装员1', '13900139000', '北京市朝阳区农场',
2, 2, NOW(), NOW()),
('APP002', 'CON002', '种植设备', '李四', 'ID_CARD', '110101199002020002',
'sheep', '项圈设备', 'completed', NOW(), NOW(),
'测试安装任务2', '安装员2', '14000140000', '北京市海淀区农场',
2, 2, NOW(), NOW())
`, { type: sequelize.QueryTypes.RAW });
console.log('✅ 添加installation_tasks测试数据成功');
// 添加completed_supervisions测试数据
await sequelize.query(`
INSERT INTO completed_supervisions (
applicationNumber, contractNumber, productName, customerName, idType, idNumber,
assetType, assetQuantity, totalRepaymentPeriods, settlementStatus, settlementDate,
importTime, settlementAmount, remainingAmount, settlementNotes,
createdBy, updatedBy, createdAt, updatedAt
) VALUES
('APP001', 'CON001', '养殖贷款', '张三', 'ID_CARD', '110101199001010001',
'cattle', 10, 12, 'partial', '2024-01-15',
NOW(), 30000.00, 20000.00, '已结清部分',
2, 2, NOW(), NOW()),
('APP002', 'CON002', '种植贷款', '李四', 'ID_CARD', '110101199002020002',
'sheep', 20, 10, 'settled', '2024-01-20',
NOW(), 30000.00, 0.00, '已完全结清',
2, 2, NOW(), NOW())
`, { type: sequelize.QueryTypes.RAW });
console.log('✅ 添加completed_supervisions测试数据成功');
// 添加loan_applications测试数据
await sequelize.query(`
INSERT INTO loan_applications (
customer_name, customer_phone, customer_id_card, loan_amount, loan_term,
interest_rate, application_date, status
) VALUES
('张三', '13800138000', '110101199001010001', 50000.00, 12, 0.05, '2024-01-01', 'pending'),
('李四', '13900139000', '110101199002020002', 30000.00, 10, 0.06, '2024-01-02', 'approved')
`, { type: sequelize.QueryTypes.RAW });
console.log('✅ 添加loan_applications测试数据成功');
// 添加loan_contracts测试数据
await sequelize.query(`
INSERT INTO loan_contracts (
contract_number, customer_name, customer_phone, customer_id_card, loan_amount,
loan_term, interest_rate, contract_date, status
) VALUES
('CON001', '张三', '13800138000', '110101199001010001', 50000.00, 12, 0.05, '2024-01-01', 'active'),
('CON002', '李四', '13900139000', '110101199002020002', 30000.00, 10, 0.06, '2024-01-02', 'completed')
`, { type: sequelize.QueryTypes.RAW });
console.log('✅ 添加loan_contracts测试数据成功');
console.log('\n✅ 所有测试数据修复完成!');
} catch (error) {
console.error('❌ 修复测试数据失败:', error.message);
} finally {
await sequelize.close();
}
}
fixTestData();

View File

@@ -0,0 +1,201 @@
/**
* 贷款解押模型
* @file LoanRelease.js
* @description 银行系统贷款解押数据模型
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
class LoanRelease extends BaseModel {
/**
* 获取解押状态文本
* @returns {String} 状态文本
*/
getStatusText() {
const statusMap = {
pending: '待处理',
processing: '处理中',
approved: '已通过',
rejected: '已拒绝',
completed: '已完成',
released: '已解押'
};
return statusMap[this.status] || this.status;
}
/**
* 获取解押状态颜色
* @returns {String} 颜色
*/
getStatusColor() {
const colorMap = {
pending: 'orange',
processing: 'blue',
approved: 'green',
rejected: 'red',
completed: 'green',
released: 'default'
};
return colorMap[this.status] || 'default';
}
/**
* 格式化解押金额
* @returns {String} 格式化后的金额
*/
getFormattedAmount() {
return `${this.release_amount.toFixed(2)}`;
}
/**
* 获取抵押物类型文本
* @returns {String} 类型文本
*/
getCollateralTypeText() {
const typeMap = {
house: '房产',
car: '车辆',
land: '土地',
equipment: '设备',
livestock: '牲畜'
};
return typeMap[this.collateral_type] || this.collateral_type;
}
}
// 初始化LoanRelease模型
LoanRelease.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
application_number: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
comment: '解押申请单号'
},
contract_id: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '关联合同ID'
},
customer_name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '客户姓名'
},
customer_phone: {
type: DataTypes.STRING(20),
allowNull: false,
comment: '客户电话'
},
customer_id_card: {
type: DataTypes.STRING(18),
allowNull: false,
comment: '客户身份证号'
},
farmer_name: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '养殖户姓名'
},
product_name: {
type: DataTypes.STRING(200),
allowNull: true,
comment: '贷款产品名称'
},
collateral_type: {
type: DataTypes.ENUM('house', 'car', 'land', 'equipment', 'livestock'),
allowNull: false,
comment: '抵押物类型'
},
collateral_description: {
type: DataTypes.TEXT,
allowNull: true,
comment: '抵押物描述'
},
collateral_value: {
type: DataTypes.DECIMAL(15, 2),
allowNull: true,
comment: '抵押物价值'
},
loan_amount: {
type: DataTypes.DECIMAL(15, 2),
allowNull: true,
comment: '贷款金额'
},
release_amount: {
type: DataTypes.DECIMAL(15, 2),
allowNull: false,
comment: '解押金额'
},
release_quantity: {
type: DataTypes.STRING(50),
allowNull: true,
comment: '解押数量'
},
application_reason: {
type: DataTypes.TEXT,
allowNull: true,
comment: '申请原因'
},
status: {
type: DataTypes.ENUM('pending', 'processing', 'approved', 'rejected', 'completed', 'released'),
allowNull: false,
defaultValue: 'pending',
comment: '解押状态'
},
application_date: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '申请日期'
},
process_date: {
type: DataTypes.DATE,
allowNull: true,
comment: '处理日期'
},
complete_date: {
type: DataTypes.DATE,
allowNull: true,
comment: '完成日期'
},
processor_id: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '处理人ID'
},
processor_name: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '处理人姓名'
},
process_comment: {
type: DataTypes.TEXT,
allowNull: true,
comment: '处理意见'
},
rejection_reason: {
type: DataTypes.TEXT,
allowNull: true,
comment: '拒绝原因'
},
remark: {
type: DataTypes.TEXT,
allowNull: true,
comment: '备注'
}
}, {
sequelize: require('../config/database').sequelize,
modelName: 'LoanRelease',
tableName: 'bank_loan_releases',
timestamps: true,
underscored: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
module.exports = LoanRelease;

View File

@@ -0,0 +1,108 @@
/**
* 贷款解押历史记录模型
* @file LoanReleaseHistory.js
* @description 银行系统贷款解押历史记录数据模型
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
class LoanReleaseHistory extends BaseModel {
/**
* 获取操作动作文本
* @returns {String} 动作文本
*/
getActionText() {
const actionMap = {
apply: '提交申请',
process: '开始处理',
approve: '审核通过',
reject: '审核拒绝',
complete: '处理完成',
release: '解押完成'
};
return actionMap[this.action] || this.action;
}
/**
* 获取操作动作颜色
* @returns {String} 颜色
*/
getActionColor() {
const colorMap = {
apply: 'blue',
process: 'orange',
approve: 'green',
reject: 'red',
complete: 'green',
release: 'default'
};
return colorMap[this.action] || 'default';
}
}
// 初始化LoanReleaseHistory模型
LoanReleaseHistory.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
release_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '解押申请ID'
},
action: {
type: DataTypes.ENUM(
'apply',
'process',
'approve',
'reject',
'complete',
'release'
),
allowNull: false,
comment: '操作动作'
},
operator: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '操作人'
},
operator_id: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '操作人ID'
},
comment: {
type: DataTypes.TEXT,
allowNull: true,
comment: '操作备注'
},
previous_status: {
type: DataTypes.STRING(50),
allowNull: true,
comment: '操作前状态'
},
new_status: {
type: DataTypes.STRING(50),
allowNull: true,
comment: '操作后状态'
},
operation_time: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '操作时间'
}
}, {
sequelize: require('../config/database').sequelize,
modelName: 'LoanReleaseHistory',
tableName: 'bank_loan_release_histories',
timestamps: true,
underscored: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
module.exports = LoanReleaseHistory;

View File

@@ -16,6 +16,9 @@ class User extends BaseModel {
*/
async validPassword(password) {
try {
if (!password || !this.password) {
return false;
}
const bcrypt = require('bcryptjs');
return await bcrypt.compare(password, this.password);
} catch (error) {
@@ -178,12 +181,12 @@ User.init({
updatedAt: 'updated_at',
hooks: {
beforeCreate: async (user) => {
if (user.password) {
if (user.password && !user.password.startsWith('$2a$')) {
user.password = await bcrypt.hash(user.password, 10);
}
},
beforeUpdate: async (user) => {
if (user.changed('password')) {
if (user.changed('password') && user.password && !user.password.startsWith('$2a$')) {
user.password = await bcrypt.hash(user.password, 10);
}
}

View File

@@ -22,6 +22,8 @@ const CompletedSupervision = require('./CompletedSupervision');
const LoanApplication = require('./LoanApplication');
const AuditRecord = require('./AuditRecord');
const LoanContract = require('./LoanContract');
const LoanRelease = require('./LoanRelease');
const LoanReleaseHistory = require('./LoanReleaseHistory');
// 定义模型关联关系
@@ -226,6 +228,54 @@ User.hasMany(AuditRecord, {
// 贷款合同暂时不关联用户表,因为当前表结构中没有外键字段
// 如果需要关联,需要先添加相应的外键字段到数据库表中
// 贷款解押与用户关联(处理人)
LoanRelease.belongsTo(User, {
foreignKey: 'processor_id',
as: 'processor',
targetKey: 'id'
});
User.hasMany(LoanRelease, {
foreignKey: 'processor_id',
as: 'processedReleases'
});
// 贷款解押与贷款合同关联
LoanRelease.belongsTo(LoanContract, {
foreignKey: 'contract_id',
as: 'contract',
targetKey: 'id'
});
LoanContract.hasMany(LoanRelease, {
foreignKey: 'contract_id',
as: 'releases'
});
// 贷款解押历史记录与解押申请关联
LoanReleaseHistory.belongsTo(LoanRelease, {
foreignKey: 'release_id',
as: 'release',
targetKey: 'id'
});
LoanRelease.hasMany(LoanReleaseHistory, {
foreignKey: 'release_id',
as: 'histories'
});
// 贷款解押历史记录与用户关联(操作人)
LoanReleaseHistory.belongsTo(User, {
foreignKey: 'operator_id',
as: 'operatorUser',
targetKey: 'id'
});
User.hasMany(LoanReleaseHistory, {
foreignKey: 'operator_id',
as: 'releaseHistories'
});
// 导出所有模型和数据库实例
module.exports = {
sequelize,
@@ -244,5 +294,7 @@ module.exports = {
CompletedSupervision,
LoanApplication,
AuditRecord,
LoanContract
LoanContract,
LoanRelease,
LoanReleaseHistory
};

View File

@@ -1,26 +0,0 @@
const { sequelize, Project, User } = require('./models');
(async () => {
try {
console.log('DB should be ningxia_bank test:');
const [dbRow] = await sequelize.query('SELECT DATABASE() AS db');
console.log('DB:', dbRow[0].db);
console.log('Testing Project.findAndCountAll with includes...');
const result = await Project.findAndCountAll({
include: [
{ model: User, as: 'creator', attributes: ['id', 'username'] },
{ model: User, as: 'updater', attributes: ['id', 'username'] }
],
limit: 5
});
console.log('count:', result.count);
console.log('rows length:', result.rows.length);
} catch (err) {
console.error('ERROR:', err.message);
} finally {
await sequelize.close();
}
})();

View File

@@ -0,0 +1,417 @@
/**
* 贷款解押路由
* @file loanRelease.js
* @description 银行系统贷款解押相关路由
*/
const express = require('express');
const router = express.Router();
const { authMiddleware } = require('../middleware/auth');
const {
getReleases,
getReleaseDetail,
createRelease,
updateRelease,
processRelease,
completeRelease,
deleteRelease,
getReleaseStats
} = require('../controllers/loanReleaseController');
// 所有路由都需要认证
router.use(authMiddleware);
/**
* @swagger
* /api/loan-releases:
* get:
* summary: 获取解押申请列表
* tags: [Loan Release]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* default: 1
* description: 页码
* - in: query
* name: pageSize
* schema:
* type: integer
* default: 10
* description: 每页数量
* - in: query
* name: searchField
* schema:
* type: string
* enum: [application_number, customer_name, product_name]
* default: application_number
* description: 搜索字段
* - in: query
* name: searchValue
* schema:
* type: string
* description: 搜索值
* - in: query
* name: status
* schema:
* type: string
* enum: [pending, processing, approved, rejected, completed, released]
* description: 解押状态筛选
* - in: query
* name: sortField
* schema:
* type: string
* default: created_at
* description: 排序字段
* - in: query
* name: sortOrder
* schema:
* type: string
* enum: [asc, desc]
* default: desc
* description: 排序方向
* responses:
* 200:
* description: 获取成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* type: object
* properties:
* releases:
* type: array
* items:
* $ref: '#/components/schemas/LoanRelease'
* pagination:
* $ref: '#/components/schemas/Pagination'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 500:
* $ref: '#/components/responses/InternalServerError'
*/
router.get('/', getReleases);
/**
* @swagger
* /api/loan-releases/stats:
* get:
* summary: 获取解押统计信息
* tags: [Loan Release]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 获取成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* type: object
* properties:
* total:
* type: integer
* example: 6
* pending:
* type: integer
* example: 0
* processing:
* type: integer
* example: 0
* approved:
* type: integer
* example: 0
* rejected:
* type: integer
* example: 0
* completed:
* type: integer
* example: 0
* released:
* type: integer
* example: 6
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 500:
* $ref: '#/components/responses/InternalServerError'
*/
router.get('/stats', getReleaseStats);
/**
* @swagger
* /api/loan-releases/{id}:
* get:
* summary: 获取解押申请详情
* tags: [Loan Release]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 解押申请ID
* responses:
* 200:
* description: 获取成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* allOf:
* - $ref: '#/components/schemas/LoanRelease'
* - type: object
* properties:
* history:
* type: array
* items:
* $ref: '#/components/schemas/LoanReleaseHistory'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
* 500:
* $ref: '#/components/responses/InternalServerError'
*/
router.get('/:id', getReleaseDetail);
/**
* @swagger
* /api/loan-releases:
* post:
* summary: 创建解押申请
* tags: [Loan Release]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LoanReleaseCreateRequest'
* responses:
* 201:
* description: 创建成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* message:
* type: string
* data:
* type: object
* properties:
* id:
* type: integer
* applicationNumber:
* type: string
* 400:
* $ref: '#/components/responses/ValidationError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 500:
* $ref: '#/components/responses/InternalServerError'
*/
router.post('/', createRelease);
/**
* @swagger
* /api/loan-releases/{id}:
* put:
* summary: 更新解押申请
* tags: [Loan Release]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 解押申请ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LoanReleaseCreateRequest'
* responses:
* 200:
* description: 更新成功
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Success'
* 400:
* $ref: '#/components/responses/ValidationError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
* 500:
* $ref: '#/components/responses/InternalServerError'
*/
router.put('/:id', updateRelease);
/**
* @swagger
* /api/loan-releases/{id}/process:
* post:
* summary: 处理解押申请(通过/拒绝)
* tags: [Loan Release]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 解押申请ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LoanReleaseProcessRequest'
* responses:
* 200:
* description: 处理成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* message:
* type: string
* data:
* type: object
* properties:
* id:
* type: integer
* status:
* type: string
* action:
* type: string
* comment:
* type: string
* 400:
* $ref: '#/components/responses/ValidationError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
* 500:
* $ref: '#/components/responses/InternalServerError'
*/
router.post('/:id/process', processRelease);
/**
* @swagger
* /api/loan-releases/{id}/complete:
* post:
* summary: 完成解押
* tags: [Loan Release]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 解押申请ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - comment
* properties:
* comment:
* type: string
* example: 解押手续办理完成
* responses:
* 200:
* description: 完成成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* message:
* type: string
* data:
* type: object
* properties:
* id:
* type: integer
* status:
* type: string
* 400:
* $ref: '#/components/responses/ValidationError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
* 500:
* $ref: '#/components/responses/InternalServerError'
*/
router.post('/:id/complete', completeRelease);
/**
* @swagger
* /api/loan-releases/{id}:
* delete:
* summary: 删除解押申请
* tags: [Loan Release]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 解押申请ID
* responses:
* 200:
* description: 删除成功
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Success'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
* 500:
* $ref: '#/components/responses/InternalServerError'
*/
router.delete('/:id', deleteRelease);
module.exports = router;

View File

@@ -80,6 +80,7 @@ app.use('/api/installation-tasks', require('./routes/installationTasks'));
app.use('/api/completed-supervisions', require('./routes/completedSupervisions'));
app.use('/api/loan-applications', require('./routes/loanApplications'));
app.use('/api/loan-contracts', require('./routes/loanContracts'));
app.use('/api/loan-releases', require('./routes/loanRelease'));
// app.use('/api/reports', require('./routes/reports'));
// 根路径

View File

@@ -1,94 +0,0 @@
const { User } = require('./models');
const { sequelize } = require('./config/database');
const bcrypt = require('bcryptjs');
async function testActualData() {
try {
console.log('=== 测试实际数据库数据 ===\n');
// 1. 直接查询数据库
console.log('1. 直接查询数据库...');
const [results] = await sequelize.query(
'SELECT id, username, password, status FROM bank_users WHERE username = ?',
{
replacements: ['admin'],
type: sequelize.QueryTypes.SELECT
}
);
if (!results) {
console.log('❌ 数据库中未找到admin用户');
console.log('查询结果:', results);
return;
}
const dbUser = results;
console.log('数据库中的用户数据:');
console.log('查询结果:', results);
console.log('结果长度:', results.length);
if (dbUser) {
console.log('ID:', dbUser.id);
console.log('用户名:', dbUser.username);
console.log('状态:', dbUser.status);
console.log('密码哈希:', dbUser.password);
console.log('密码哈希长度:', dbUser.password ? dbUser.password.length : 0);
}
console.log('');
// 2. 使用Sequelize查询
console.log('2. 使用Sequelize查询...');
const sequelizeUser = await User.findOne({ where: { username: 'admin' } });
if (sequelizeUser) {
console.log('Sequelize查询到的用户数据:');
console.log('ID:', sequelizeUser.id);
console.log('用户名:', sequelizeUser.username);
console.log('状态:', sequelizeUser.status);
console.log('密码哈希:', sequelizeUser.password);
console.log('密码哈希长度:', sequelizeUser.password ? sequelizeUser.password.length : 0);
console.log('');
// 3. 比较两种查询结果
console.log('3. 比较两种查询结果:');
console.log('密码哈希是否相同:', dbUser.password === sequelizeUser.password);
console.log('');
// 4. 测试密码验证
const testPassword = 'Admin123456';
console.log('4. 测试密码验证:');
console.log('测试密码:', testPassword);
// 使用数据库查询的密码哈希
const dbTest = await bcrypt.compare(testPassword, dbUser.password);
console.log('数据库密码验证结果:', dbTest);
// 使用Sequelize查询的密码哈希
const sequelizeTest = await bcrypt.compare(testPassword, sequelizeUser.password);
console.log('Sequelize密码验证结果:', sequelizeTest);
// 使用User模型的validPassword方法
const modelTest = await sequelizeUser.validPassword(testPassword);
console.log('User模型验证结果:', modelTest);
console.log('');
if (dbTest && sequelizeTest && modelTest) {
console.log('🎉 所有验证都成功!');
} else {
console.log('❌ 验证失败');
console.log('可能原因:');
if (!dbTest) console.log('- 数据库中的密码哈希有问题');
if (!sequelizeTest) console.log('- Sequelize查询的密码哈希有问题');
if (!modelTest) console.log('- User模型的validPassword方法有问题');
}
} else {
console.log('❌ Sequelize查询失败');
}
} catch (error) {
console.error('测试失败:', error.message);
console.error('错误堆栈:', error.stack);
}
process.exit(0);
}
testActualData();

View File

@@ -1,291 +0,0 @@
const http = require('http');
// 测试配置
const BASE_URL = 'http://localhost:5351';
let authToken = '';
// 辅助函数发送HTTP请求
function makeRequest(options, data = null) {
return new Promise((resolve, reject) => {
const req = http.request(options, (res) => {
let body = '';
res.on('data', (chunk) => {
body += chunk;
});
res.on('end', () => {
try {
const result = JSON.parse(body);
resolve({ status: res.statusCode, data: result });
} catch (error) {
resolve({ status: res.statusCode, data: body });
}
});
});
req.on('error', (error) => {
reject(error);
});
if (data) {
req.write(JSON.stringify(data));
}
req.end();
});
}
// 测试登录
async function testLogin() {
console.log('🔐 测试登录...');
try {
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/auth/login',
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
const result = await makeRequest(options, {
username: 'admin',
password: 'Admin123456'
});
if (result.status === 200 && result.data.success) {
authToken = result.data.data.token;
console.log('✅ 登录成功');
return true;
} else {
console.log('❌ 登录失败:', result.data.message);
return false;
}
} catch (error) {
console.log('❌ 登录请求失败:', error.message);
return false;
}
}
// 测试仪表盘统计
async function testDashboardStats() {
console.log('📊 测试仪表盘统计...');
try {
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/dashboard',
method: 'GET',
headers: {
'Authorization': `Bearer ${authToken}`
}
};
const result = await makeRequest(options);
if (result.status === 200 && result.data.success) {
console.log('✅ 仪表盘统计获取成功');
console.log(' - 总用户数:', result.data.data.overview?.totalUsers || 0);
console.log(' - 总账户数:', result.data.data.overview?.totalAccounts || 0);
console.log(' - 今日交易数:', result.data.data.today?.transactionCount || 0);
return true;
} else {
console.log('❌ 仪表盘统计获取失败:', result.data.message);
return false;
}
} catch (error) {
console.log('❌ 仪表盘统计请求失败:', error.message);
return false;
}
}
// 测试用户列表
async function testUsersList() {
console.log('👥 测试用户列表...');
try {
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/users?page=1&pageSize=10',
method: 'GET',
headers: {
'Authorization': `Bearer ${authToken}`
}
};
const result = await makeRequest(options);
if (result.status === 200 && result.data.success) {
console.log('✅ 用户列表获取成功');
console.log(' - 用户数量:', result.data.data.users?.length || 0);
console.log(' - 分页信息:', result.data.data.pagination);
return true;
} else {
console.log('❌ 用户列表获取失败:', result.data.message);
return false;
}
} catch (error) {
console.log('❌ 用户列表请求失败:', error.message);
return false;
}
}
// 测试账户列表
async function testAccountsList() {
console.log('🏦 测试账户列表...');
try {
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/accounts?page=1&pageSize=10',
method: 'GET',
headers: {
'Authorization': `Bearer ${authToken}`
}
};
const result = await makeRequest(options);
if (result.status === 200 && result.data.success) {
console.log('✅ 账户列表获取成功');
console.log(' - 账户数量:', result.data.data.accounts?.length || 0);
console.log(' - 分页信息:', result.data.data.pagination);
return true;
} else {
console.log('❌ 账户列表获取失败:', result.data.message);
return false;
}
} catch (error) {
console.log('❌ 账户列表请求失败:', error.message);
return false;
}
}
// 测试交易记录列表
async function testTransactionsList() {
console.log('💳 测试交易记录列表...');
try {
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/transactions?page=1&pageSize=10',
method: 'GET',
headers: {
'Authorization': `Bearer ${authToken}`
}
};
const result = await makeRequest(options);
if (result.status === 200 && result.data.success) {
console.log('✅ 交易记录列表获取成功');
console.log(' - 交易数量:', result.data.data.transactions?.length || 0);
console.log(' - 分页信息:', result.data.data.pagination);
return true;
} else {
console.log('❌ 交易记录列表获取失败:', result.data.message);
return false;
}
} catch (error) {
console.log('❌ 交易记录列表请求失败:', error.message);
return false;
}
}
// 测试员工列表
async function testEmployeesList() {
console.log('👨‍💼 测试员工列表...');
try {
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/employees?page=1&pageSize=10',
method: 'GET',
headers: {
'Authorization': `Bearer ${authToken}`
}
};
const result = await makeRequest(options);
if (result.status === 200 && result.data.success) {
console.log('✅ 员工列表获取成功');
console.log(' - 员工数量:', result.data.data.employees?.length || 0);
console.log(' - 分页信息:', result.data.data.pagination);
return true;
} else {
console.log('❌ 员工列表获取失败:', result.data.message);
return false;
}
} catch (error) {
console.log('❌ 员工列表请求失败:', error.message);
return false;
}
}
// 测试贷款产品列表
async function testLoanProductsList() {
console.log('💰 测试贷款产品列表...');
try {
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/loan-products?page=1&pageSize=10',
method: 'GET',
headers: {
'Authorization': `Bearer ${authToken}`
}
};
const result = await makeRequest(options);
if (result.status === 200 && result.data.success) {
console.log('✅ 贷款产品列表获取成功');
console.log(' - 产品数量:', result.data.data.products?.length || 0);
console.log(' - 分页信息:', result.data.data.pagination);
return true;
} else {
console.log('❌ 贷款产品列表获取失败:', result.data.message);
return false;
}
} catch (error) {
console.log('❌ 贷款产品列表请求失败:', error.message);
return false;
}
}
// 主测试函数
async function runTests() {
console.log('🚀 开始API集成测试...\n');
const tests = [
{ name: '登录', fn: testLogin },
{ name: '仪表盘统计', fn: testDashboardStats },
{ name: '用户列表', fn: testUsersList },
{ name: '账户列表', fn: testAccountsList },
{ name: '交易记录列表', fn: testTransactionsList },
{ name: '员工列表', fn: testEmployeesList },
{ name: '贷款产品列表', fn: testLoanProductsList }
];
let passed = 0;
let total = tests.length;
for (const test of tests) {
try {
const success = await test.fn();
if (success) {
passed++;
}
} catch (error) {
console.log(`${test.name}测试异常:`, error.message);
}
console.log(''); // 空行分隔
}
console.log('📋 测试结果汇总:');
console.log(`✅ 通过: ${passed}/${total}`);
console.log(`❌ 失败: ${total - passed}/${total}`);
if (passed === total) {
console.log('🎉 所有测试通过API集成正常');
} else {
console.log('⚠️ 部分测试失败请检查相关API接口');
}
}
// 运行测试
runTests().catch(console.error);

View File

@@ -1,129 +0,0 @@
const express = require('express');
const cors = require('cors');
const app = express();
// 中间件
app.use(cors());
app.use(express.json());
// 模拟认证中间件
const mockAuthMiddleware = (req, res, next) => {
req.user = { id: 1, username: 'test' };
next();
};
// 模拟仪表盘控制器
const mockDashboardController = {
getStats: (req, res) => {
res.json({
success: true,
message: '获取统计数据成功(模拟数据)',
data: {
overview: {
totalUsers: 1250,
totalAccounts: 3420,
totalTransactions: 15680,
totalBalance: 12500000.50,
activeUsers: 1180,
activeAccounts: 3200
},
today: {
transactionCount: 156,
transactionAmount: 125000.00
},
accountTypes: [
{ type: 'savings', count: 2100, totalBalance: 8500000.00 },
{ type: 'checking', count: 800, totalBalance: 3200000.00 },
{ type: 'credit', count: 400, totalBalance: 500000.00 },
{ type: 'loan', count: 120, totalBalance: 300000.00 }
],
trends: [
{ date: '2024-01-15', count: 45, totalAmount: 12500.00 },
{ date: '2024-01-16', count: 52, totalAmount: 15200.00 },
{ date: '2024-01-17', count: 38, totalAmount: 9800.00 },
{ date: '2024-01-18', count: 61, totalAmount: 18500.00 },
{ date: '2024-01-19', count: 48, totalAmount: 13200.00 },
{ date: '2024-01-20', count: 55, totalAmount: 16800.00 },
{ date: '2024-01-21', count: 42, totalAmount: 11200.00 }
]
}
});
},
getRecentTransactions: (req, res) => {
const { limit = 10 } = req.query;
const mockTransactions = [
{
id: 1,
type: 'deposit',
amount: 1000.00,
description: '存款',
status: 'completed',
created_at: new Date(),
account: {
account_number: '1234567890',
user: {
username: 'user1',
real_name: '张三'
}
}
},
{
id: 2,
type: 'withdrawal',
amount: 500.00,
description: '取款',
status: 'completed',
created_at: new Date(Date.now() - 3600000),
account: {
account_number: '1234567891',
user: {
username: 'user2',
real_name: '李四'
}
}
},
{
id: 3,
type: 'transfer',
amount: 200.00,
description: '转账',
status: 'completed',
created_at: new Date(Date.now() - 7200000),
account: {
account_number: '1234567892',
user: {
username: 'user3',
real_name: '王五'
}
}
}
].slice(0, parseInt(limit));
res.json({
success: true,
message: '获取最近交易记录成功(模拟数据)',
data: mockTransactions
});
}
};
// 路由
app.get('/health', (req, res) => {
res.json({ message: '银行后端服务运行正常', timestamp: new Date().toISOString() });
});
app.get('/api/dashboard', mockAuthMiddleware, mockDashboardController.getStats);
app.get('/api/dashboard/recent-transactions', mockAuthMiddleware, mockDashboardController.getRecentTransactions);
// 启动服务器
const PORT = 5351;
app.listen(PORT, () => {
console.log(`🚀 银行后端模拟服务已启动`);
console.log(`📡 服务地址: http://localhost:${PORT}`);
console.log(`🏥 健康检查: http://localhost:${PORT}/health`);
console.log(`📊 仪表盘API: http://localhost:${PORT}/api/dashboard`);
console.log(`💳 最近交易API: http://localhost:${PORT}/api/dashboard/recent-transactions`);
});

View File

@@ -1,80 +0,0 @@
const { User, Role } = require('./models');
const bcrypt = require('bcryptjs');
async function testAuth() {
try {
console.log('=== 测试认证逻辑 ===');
// 查找用户(包含角色)
const user = await User.findOne({
where: { username: 'admin' },
include: [{
model: Role,
as: 'role'
}]
});
if (!user) {
console.log('❌ 未找到admin用户');
return;
}
console.log('✅ 找到admin用户');
console.log('用户名:', user.username);
console.log('状态:', user.status);
console.log('角色:', user.role ? user.role.name : '无角色');
console.log('密码哈希:', user.password);
// 测试密码验证
const testPassword = 'Admin123456';
console.log('\n=== 测试密码验证 ===');
console.log('测试密码:', testPassword);
// 直接使用bcrypt比较
const directTest = await bcrypt.compare(testPassword, user.password);
console.log('直接bcrypt验证:', directTest);
// 使用模型方法验证
const modelTest = await user.validPassword(testPassword);
console.log('模型验证:', modelTest);
if (!modelTest) {
console.log('\n=== 重新生成密码 ===');
const newHash = await bcrypt.hash(testPassword, 10);
console.log('新哈希:', newHash);
await user.update({
password: newHash,
status: 'active',
login_attempts: 0,
locked_until: null
});
console.log('✅ 密码已更新');
// 重新加载用户数据
await user.reload();
// 再次验证
const finalTest = await user.validPassword(testPassword);
console.log('最终验证:', finalTest);
if (finalTest) {
console.log('🎉 密码修复成功!');
console.log('用户名: admin');
console.log('密码: Admin123456');
console.log('状态: active');
}
} else {
console.log('✅ 密码验证成功!');
}
} catch (error) {
console.error('测试失败:', error.message);
console.error('错误堆栈:', error.stack);
}
process.exit(0);
}
testAuth();

View File

@@ -1,121 +0,0 @@
const axios = require('axios')
const BASE_URL = 'http://localhost:5351'
async function testCompletedSupervisionsAPI() {
try {
console.log('开始测试监管任务已结项API...')
// 1. 登录获取token
console.log('\n1. 用户登录...')
const loginResponse = await axios.post(`${BASE_URL}/api/auth/login`, {
username: 'admin',
password: 'admin123'
})
if (!loginResponse.data.success) {
throw new Error('登录失败: ' + loginResponse.data.message)
}
const token = loginResponse.data.data.token
console.log('✅ 登录成功')
const headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
// 2. 获取监管任务已结项列表
console.log('\n2. 获取监管任务已结项列表...')
const listResponse = await axios.get(`${BASE_URL}/api/completed-supervisions`, {
headers,
params: {
page: 1,
limit: 10
}
})
console.log('监管任务已结项列表响应:', JSON.stringify(listResponse.data, null, 2))
// 3. 获取监管任务已结项统计
console.log('\n3. 获取监管任务已结项统计...')
const statsResponse = await axios.get(`${BASE_URL}/api/completed-supervisions/stats`, {
headers
})
console.log('监管任务已结项统计响应:', JSON.stringify(statsResponse.data, null, 2))
// 4. 创建新的监管任务已结项
console.log('\n4. 创建新的监管任务已结项...')
const newTask = {
applicationNumber: 'APP2024999',
contractNumber: 'LOAN2024999',
productName: '测试养殖贷',
customerName: '测试用户',
idType: 'ID_CARD',
idNumber: '440999999999999999',
assetType: '测试动物',
assetQuantity: 100,
totalRepaymentPeriods: 12,
settlementStatus: 'unsettled',
settlementNotes: '这是一个测试任务'
}
const createResponse = await axios.post(`${BASE_URL}/api/completed-supervisions`, newTask, {
headers
})
console.log('创建监管任务已结项响应:', JSON.stringify(createResponse.data, null, 2))
const createdTaskId = createResponse.data.data.id
// 5. 获取单个监管任务已结项详情
console.log('\n5. 获取监管任务已结项详情...')
const detailResponse = await axios.get(`${BASE_URL}/api/completed-supervisions/${createdTaskId}`, {
headers
})
console.log('监管任务已结项详情响应:', JSON.stringify(detailResponse.data, null, 2))
// 6. 更新监管任务已结项
console.log('\n6. 更新监管任务已结项...')
const updateData = {
settlementStatus: 'settled',
settlementDate: '2024-12-20',
settlementNotes: '更新后的备注信息'
}
const updateResponse = await axios.put(`${BASE_URL}/api/completed-supervisions/${createdTaskId}`, updateData, {
headers
})
console.log('更新监管任务已结项响应:', JSON.stringify(updateResponse.data, null, 2))
// 7. 批量更新状态
console.log('\n7. 批量更新状态...')
const batchUpdateResponse = await axios.put(`${BASE_URL}/api/completed-supervisions/batch/status`, {
ids: [createdTaskId],
settlementStatus: 'partial'
}, {
headers
})
console.log('批量更新状态响应:', JSON.stringify(batchUpdateResponse.data, null, 2))
// 8. 删除监管任务已结项
console.log('\n8. 删除监管任务已结项...')
const deleteResponse = await axios.delete(`${BASE_URL}/api/completed-supervisions/${createdTaskId}`, {
headers
})
console.log('删除监管任务已结项响应:', JSON.stringify(deleteResponse.data, null, 2))
console.log('\n✅ 所有监管任务已结项API测试完成')
} catch (error) {
console.error('❌ 测试失败:', error.response?.data || error.message)
}
}
// 运行测试
testCompletedSupervisionsAPI()

View File

@@ -1,30 +0,0 @@
const axios = require('axios')
const BASE_URL = 'http://localhost:5351'
async function testCompletedSupervisionsSimple() {
try {
console.log('测试监管任务已结项API连接...')
const response = await axios.get(`${BASE_URL}/api/completed-supervisions`, {
timeout: 5000
})
console.log('✅ 监管任务已结项API连接成功')
console.log('响应状态:', response.status)
console.log('响应数据:', JSON.stringify(response.data, null, 2))
} catch (error) {
if (error.response) {
console.log('API响应错误:')
console.log('状态码:', error.response.status)
console.log('错误信息:', error.response.data)
} else if (error.request) {
console.log('❌ 无法连接到服务器,请确保后端服务正在运行')
} else {
console.log('❌ 请求配置错误:', error.message)
}
}
}
testCompletedSupervisionsSimple()

View File

@@ -1,42 +0,0 @@
/**
* 测试创建贷款商品
*/
const { LoanProduct } = require('./models');
async function testCreateLoanProduct() {
try {
console.log('🧪 测试创建贷款商品...');
const productData = {
productName: '测试贷款产品',
loanAmount: '50000~5000000',
loanTerm: 12,
interestRate: 5.5,
serviceArea: '北京市',
servicePhone: '13800138000',
productDescription: '这是一个测试贷款产品',
applicationRequirements: '需要提供身份证和收入证明',
requiredDocuments: '身份证、收入证明、银行流水',
approvalProcess: '提交申请->审核->放款',
riskLevel: 'LOW',
minLoanAmount: 50000,
maxLoanAmount: 5000000,
createdBy: 2,
updatedBy: 2
};
console.log('创建数据:', productData);
const product = await LoanProduct.create(productData);
console.log('✅ 创建成功:', product.toJSON());
} catch (error) {
console.error('❌ 创建失败:', error.message);
console.error('详细错误:', error);
}
process.exit(0);
}
testCreateLoanProduct();

View File

@@ -1,96 +0,0 @@
/**
* 测试创建项目接口
*/
const axios = require('axios');
const API_BASE_URL = 'http://localhost:5351';
async function testCreateProject() {
try {
console.log('🚀 开始测试创建项目接口...\n');
// 1. 先登录获取token
console.log('1. 登录获取认证token...');
const loginResponse = await axios.post(`${API_BASE_URL}/api/auth/login`, {
username: 'admin',
password: 'Admin123456'
});
if (!loginResponse.data.success) {
throw new Error('登录失败: ' + loginResponse.data.message);
}
const token = loginResponse.data.data.token;
console.log('✅ 登录成功获取到token\n');
// 2. 测试创建项目
console.log('2. 测试创建新项目...');
const newProject = {
name: '测试项目_' + new Date().getTime(),
status: 'supervision',
farmName: '测试养殖场',
supervisionObject: '牛',
supervisionQuantity: 100,
supervisionPeriod: '12个月',
supervisionAmount: 500000.00,
startTime: '2024-01-01',
endTime: '2024-12-31',
earTag: 50,
collar: 30,
host: 20,
loanOfficer: '张专员',
description: '这是一个测试项目,用于验证创建接口功能'
};
const createResponse = await axios.post(`${API_BASE_URL}/api/projects`, newProject, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (createResponse.data.success) {
console.log('✅ 项目创建成功!');
console.log('📋 创建的项目信息:');
console.log(` - 项目ID: ${createResponse.data.data.id}`);
console.log(` - 项目名称: ${createResponse.data.data.name}`);
console.log(` - 养殖场: ${createResponse.data.data.farmName}`);
console.log(` - 监管对象: ${createResponse.data.data.supervisionObject}`);
console.log(` - 监管数量: ${createResponse.data.data.supervisionQuantity}`);
console.log(` - 监管金额: ${createResponse.data.data.supervisionAmount}`);
console.log(` - 贷款专员: ${createResponse.data.data.loanOfficer}`);
console.log(` - 创建时间: ${createResponse.data.data.createdAt}`);
} else {
console.log('❌ 项目创建失败:', createResponse.data.message);
}
// 3. 验证项目是否在列表中
console.log('\n3. 验证项目是否在列表中...');
const listResponse = await axios.get(`${API_BASE_URL}/api/projects`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (listResponse.data.success) {
const projects = listResponse.data.data.projects;
const createdProject = projects.find(p => p.name === newProject.name);
if (createdProject) {
console.log('✅ 项目已成功添加到列表中');
console.log(`📊 当前总项目数: ${projects.length}`);
} else {
console.log('❌ 项目未在列表中找到');
}
}
} catch (error) {
console.error('❌ 测试失败:', error.message);
if (error.response) {
console.error('响应数据:', error.response.data);
}
}
}
// 运行测试
testCreateProject();

View File

@@ -1,80 +0,0 @@
const { User } = require('./models');
const bcrypt = require('bcryptjs');
async function testDatabaseStorage() {
try {
console.log('=== 测试数据库存储问题 ===\n');
// 1. 生成一个新的密码哈希
const testPassword = 'Admin123456';
const newHash = await bcrypt.hash(testPassword, 10);
console.log('1. 生成新的密码哈希:');
console.log('原始密码:', testPassword);
console.log('生成的哈希:', newHash);
console.log('哈希长度:', newHash.length);
console.log('');
// 2. 验证新生成的哈希
console.log('2. 验证新生成的哈希:');
const isValid = await bcrypt.compare(testPassword, newHash);
console.log('新哈希验证结果:', isValid);
console.log('');
// 3. 更新数据库
console.log('3. 更新数据库:');
const user = await User.findOne({ where: { username: 'admin' } });
if (!user) {
console.log('❌ 未找到admin用户');
return;
}
await user.update({ password: newHash });
console.log('✅ 数据库已更新');
console.log('');
// 4. 重新查询数据库
console.log('4. 重新查询数据库:');
const updatedUser = await User.findOne({ where: { username: 'admin' } });
if (updatedUser) {
console.log('查询到的密码哈希:', updatedUser.password);
console.log('查询到的哈希长度:', updatedUser.password.length);
console.log('哈希是否匹配:', updatedUser.password === newHash);
console.log('');
// 5. 测试查询到的哈希
console.log('5. 测试查询到的哈希:');
const queryTest = await bcrypt.compare(testPassword, updatedUser.password);
console.log('查询到的哈希验证结果:', queryTest);
console.log('');
// 6. 测试User模型的validPassword方法
console.log('6. 测试User模型的validPassword方法:');
const modelTest = await updatedUser.validPassword(testPassword);
console.log('模型验证结果:', modelTest);
console.log('');
if (queryTest && modelTest) {
console.log('🎉 数据库存储和验证都正常!');
} else if (queryTest && !modelTest) {
console.log('❌ 数据库存储正常但User模型验证失败');
console.log('可能原因: User模型的validPassword方法有问题');
} else if (!queryTest && modelTest) {
console.log('❌ 数据库存储有问题但User模型验证成功');
console.log('可能原因: 数据库存储时数据被截断或损坏');
} else {
console.log('❌ 数据库存储和User模型验证都失败');
console.log('可能原因: 数据库字段长度不够或编码问题');
}
} else {
console.log('❌ 重新查询用户失败');
}
} catch (error) {
console.error('测试失败:', error.message);
console.error('错误堆栈:', error.stack);
}
process.exit(0);
}
testDatabaseStorage();

View File

@@ -1,121 +0,0 @@
const axios = require('axios')
const BASE_URL = 'http://localhost:5351'
async function testInstallationTasksAPI() {
try {
console.log('开始测试待安装任务API...')
// 1. 登录获取token
console.log('\n1. 用户登录...')
const loginResponse = await axios.post(`${BASE_URL}/api/auth/login`, {
username: 'admin',
password: 'admin123'
})
if (!loginResponse.data.success) {
throw new Error('登录失败: ' + loginResponse.data.message)
}
const token = loginResponse.data.data.token
console.log('✅ 登录成功')
const headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
// 2. 获取待安装任务列表
console.log('\n2. 获取待安装任务列表...')
const listResponse = await axios.get(`${BASE_URL}/api/installation-tasks`, {
headers,
params: {
page: 1,
limit: 10
}
})
console.log('待安装任务列表响应:', JSON.stringify(listResponse.data, null, 2))
// 3. 获取待安装任务统计
console.log('\n3. 获取待安装任务统计...')
const statsResponse = await axios.get(`${BASE_URL}/api/installation-tasks/stats`, {
headers
})
console.log('待安装任务统计响应:', JSON.stringify(statsResponse.data, null, 2))
// 4. 创建新的待安装任务
console.log('\n4. 创建新的待安装任务...')
const newTask = {
applicationNumber: 'APP2024999',
contractNumber: 'LOAN2024999',
productName: '测试养殖贷',
customerName: '测试用户',
idType: 'ID_CARD',
idNumber: '440999999999999999',
assetType: '测试动物',
equipmentToInstall: '测试设备',
installationNotes: '这是一个测试任务',
installerName: '测试安装员',
installerPhone: '13800138999',
installationAddress: '测试地址'
}
const createResponse = await axios.post(`${BASE_URL}/api/installation-tasks`, newTask, {
headers
})
console.log('创建待安装任务响应:', JSON.stringify(createResponse.data, null, 2))
const createdTaskId = createResponse.data.data.id
// 5. 获取单个待安装任务详情
console.log('\n5. 获取待安装任务详情...')
const detailResponse = await axios.get(`${BASE_URL}/api/installation-tasks/${createdTaskId}`, {
headers
})
console.log('待安装任务详情响应:', JSON.stringify(detailResponse.data, null, 2))
// 6. 更新待安装任务
console.log('\n6. 更新待安装任务...')
const updateData = {
installationStatus: 'in-progress',
installationNotes: '更新后的备注信息'
}
const updateResponse = await axios.put(`${BASE_URL}/api/installation-tasks/${createdTaskId}`, updateData, {
headers
})
console.log('更新待安装任务响应:', JSON.stringify(updateResponse.data, null, 2))
// 7. 批量更新状态
console.log('\n7. 批量更新状态...')
const batchUpdateResponse = await axios.put(`${BASE_URL}/api/installation-tasks/batch/status`, {
ids: [createdTaskId],
installationStatus: 'completed'
}, {
headers
})
console.log('批量更新状态响应:', JSON.stringify(batchUpdateResponse.data, null, 2))
// 8. 删除待安装任务
console.log('\n8. 删除待安装任务...')
const deleteResponse = await axios.delete(`${BASE_URL}/api/installation-tasks/${createdTaskId}`, {
headers
})
console.log('删除待安装任务响应:', JSON.stringify(deleteResponse.data, null, 2))
console.log('\n✅ 所有待安装任务API测试完成')
} catch (error) {
console.error('❌ 测试失败:', error.response?.data || error.message)
}
}
// 运行测试
testInstallationTasksAPI()

View File

@@ -1,38 +0,0 @@
const { sequelize, InstallationTask, User } = require('./models');
(async () => {
try {
console.log('测试InstallationTask模型...');
// 测试基本查询
console.log('1. 测试基本count查询...');
const count = await InstallationTask.count();
console.log('InstallationTask count:', count);
// 测试findAndCountAll查询
console.log('2. 测试findAndCountAll查询...');
const result = await InstallationTask.findAndCountAll({
limit: 5
});
console.log('findAndCountAll result count:', result.count);
console.log('findAndCountAll rows length:', result.rows.length);
// 测试带include的查询
console.log('3. 测试带include的查询...');
const resultWithInclude = await InstallationTask.findAndCountAll({
include: [
{ model: User, as: 'creator', attributes: ['id', 'username'] },
{ model: User, as: 'updater', attributes: ['id', 'username'] }
],
limit: 5
});
console.log('include查询 count:', resultWithInclude.count);
console.log('include查询 rows length:', resultWithInclude.rows.length);
} catch (err) {
console.error('错误:', err.message);
console.error('SQL:', err.sql);
} finally {
await sequelize.close();
}
})();

View File

@@ -1,30 +0,0 @@
const axios = require('axios')
const BASE_URL = 'http://localhost:5351'
async function testInstallationTasksSimple() {
try {
console.log('测试待安装任务API连接...')
const response = await axios.get(`${BASE_URL}/api/installation-tasks`, {
timeout: 5000
})
console.log('✅ 待安装任务API连接成功')
console.log('响应状态:', response.status)
console.log('响应数据:', JSON.stringify(response.data, null, 2))
} catch (error) {
if (error.response) {
console.log('API响应错误:')
console.log('状态码:', error.response.status)
console.log('错误信息:', error.response.data)
} else if (error.request) {
console.log('❌ 无法连接到服务器,请确保后端服务正在运行')
} else {
console.log('❌ 请求配置错误:', error.message)
}
}
}
testInstallationTasksSimple()

View File

@@ -1,117 +0,0 @@
/**
* 贷款申请API测试
* @file test-loan-applications-api.js
*/
const axios = require('axios');
async function testLoanApplicationsAPI() {
try {
console.log('🔍 测试贷款申请API...');
// 1. 登录获取token
console.log('\n1. 登录测试...');
const loginResponse = await axios.post('http://localhost:5351/api/auth/login', {
username: 'admin',
password: 'Admin123456'
});
if (!loginResponse.data.success) {
throw new Error('登录失败: ' + loginResponse.data.message);
}
const token = loginResponse.data.data.token;
console.log('✅ 登录成功');
// 设置授权头
const authHeaders = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
};
// 2. 获取贷款申请列表
console.log('\n2. 获取申请列表...');
const listResponse = await axios.get('http://localhost:5351/api/loan-applications', {
headers: authHeaders
});
if (!listResponse.data.success) {
throw new Error('获取列表失败: ' + listResponse.data.message);
}
console.log('✅ 获取申请列表成功');
console.log(`📊 申请数量: ${listResponse.data.data.applications.length}`);
console.log(`📊 总数: ${listResponse.data.data.pagination.total}`);
if (listResponse.data.data.applications.length > 0) {
const firstApp = listResponse.data.data.applications[0];
console.log(`📋 第一个申请: ${firstApp.applicationNumber} - ${firstApp.productName} - ${firstApp.status}`);
// 3. 获取申请详情
console.log('\n3. 获取申请详情...');
const detailResponse = await axios.get(`http://localhost:5351/api/loan-applications/${firstApp.id}`, {
headers: authHeaders
});
if (!detailResponse.data.success) {
throw new Error('获取详情失败: ' + detailResponse.data.message);
}
console.log('✅ 获取申请详情成功');
console.log(`📋 申请详情: ${detailResponse.data.data.applicationNumber}`);
console.log(`📋 审核记录数: ${detailResponse.data.data.auditRecords.length}`);
// 4. 测试审核功能(仅对待审核的申请)
if (firstApp.status === 'pending_review') {
console.log('\n4. 测试审核功能...');
const auditResponse = await axios.post(`http://localhost:5351/api/loan-applications/${firstApp.id}/audit`, {
action: 'approve',
comment: 'API测试审核通过'
}, {
headers: authHeaders
});
if (!auditResponse.data.success) {
throw new Error('审核失败: ' + auditResponse.data.message);
}
console.log('✅ 审核功能测试成功');
console.log(`📋 审核结果: ${auditResponse.data.message}`);
}
}
// 5. 获取统计信息
console.log('\n5. 获取统计信息...');
const statsResponse = await axios.get('http://localhost:5351/api/loan-applications/stats', {
headers: authHeaders
});
if (!statsResponse.data.success) {
throw new Error('获取统计失败: ' + statsResponse.data.message);
}
console.log('✅ 获取统计信息成功');
console.log(`📊 总申请数: ${statsResponse.data.data.total.applications}`);
console.log(`📊 总金额: ${statsResponse.data.data.total.amount.toFixed(2)}`);
console.log('📊 按状态统计:');
Object.entries(statsResponse.data.data.byStatus.counts).forEach(([status, count]) => {
if (count > 0) {
console.log(` - ${status}: ${count}个申请`);
}
});
console.log('\n🎉 所有API测试完成');
} catch (error) {
console.error('\n❌ API测试失败:', error.message);
if (error.response) {
console.error('响应状态:', error.response.status);
console.error('响应数据:', error.response.data);
} else if (error.code) {
console.error('错误代码:', error.code);
}
console.error('完整错误:', error);
}
}
// 运行测试
testLoanApplicationsAPI();

View File

@@ -1,125 +0,0 @@
const axios = require('axios');
const BASE_URL = 'http://localhost:5351';
// 测试贷款商品API
async function testLoanProductsAPI() {
try {
console.log('开始测试贷款商品API...');
// 1. 登录获取token
console.log('\n1. 用户登录...');
const loginResponse = await axios.post(`${BASE_URL}/api/auth/login`, {
username: 'admin',
password: 'admin123'
});
if (!loginResponse.data.success) {
throw new Error(`登录失败: ${loginResponse.data.message}`);
}
const token = loginResponse.data.data.token;
console.log('✅ 登录成功');
// 设置请求头
const headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
};
// 2. 获取贷款商品列表
console.log('\n2. 获取贷款商品列表...');
const listResponse = await axios.get(`${BASE_URL}/api/loan-products`, { headers });
console.log('✅ 获取贷款商品列表成功');
console.log(` 总数: ${listResponse.data.data.pagination.total}`);
console.log(` 当前页: ${listResponse.data.data.pagination.current}`);
console.log(` 每页数量: ${listResponse.data.data.pagination.pageSize}`);
// 3. 获取贷款商品统计信息
console.log('\n3. 获取贷款商品统计信息...');
const statsResponse = await axios.get(`${BASE_URL}/api/loan-products/stats`, { headers });
console.log('✅ 获取统计信息成功');
console.log(` 总产品数: ${statsResponse.data.data.totalProducts}`);
console.log(` 在售产品: ${statsResponse.data.data.onSaleProducts}`);
console.log(` 停售产品: ${statsResponse.data.data.offSaleProducts}`);
// 4. 创建新的贷款商品
console.log('\n4. 创建新的贷款商品...');
const newProduct = {
productName: '测试贷款产品',
loanAmount: '100000~500000元',
loanTerm: 12,
interestRate: 5.5,
serviceArea: '测试区域',
servicePhone: '13800138000',
productDescription: '这是一个测试贷款产品',
applicationRequirements: '测试申请条件',
requiredDocuments: '测试所需材料',
approvalProcess: '测试审批流程',
riskLevel: 'MEDIUM',
minLoanAmount: 100000,
maxLoanAmount: 500000
};
const createResponse = await axios.post(`${BASE_URL}/api/loan-products`, newProduct, { headers });
console.log('✅ 创建贷款商品成功');
console.log(` 产品ID: ${createResponse.data.data.id}`);
console.log(` 产品名称: ${createResponse.data.data.productName}`);
const productId = createResponse.data.data.id;
// 5. 根据ID获取贷款商品详情
console.log('\n5. 获取贷款商品详情...');
const detailResponse = await axios.get(`${BASE_URL}/api/loan-products/${productId}`, { headers });
console.log('✅ 获取贷款商品详情成功');
console.log(` 产品名称: ${detailResponse.data.data.productName}`);
console.log(` 贷款利率: ${detailResponse.data.data.interestRate}%`);
// 6. 更新贷款商品
console.log('\n6. 更新贷款商品...');
const updateData = {
productName: '更新后的测试贷款产品',
interestRate: 6.0,
productDescription: '这是更新后的测试贷款产品描述'
};
const updateResponse = await axios.put(`${BASE_URL}/api/loan-products/${productId}`, updateData, { headers });
console.log('✅ 更新贷款商品成功');
console.log(` 更新后产品名称: ${updateResponse.data.data.productName}`);
console.log(` 更新后利率: ${updateResponse.data.data.interestRate}%`);
// 7. 批量更新在售状态
console.log('\n7. 批量更新在售状态...');
const batchUpdateResponse = await axios.put(`${BASE_URL}/api/loan-products/batch/status`, {
ids: [productId],
onSaleStatus: false
}, { headers });
console.log('✅ 批量更新状态成功');
// 8. 删除贷款商品
console.log('\n8. 删除贷款商品...');
const deleteResponse = await axios.delete(`${BASE_URL}/api/loan-products/${productId}`, { headers });
console.log('✅ 删除贷款商品成功');
console.log('\n🎉 所有贷款商品API测试通过');
} catch (error) {
console.error('❌ 测试失败:', error.response?.data || error.message);
throw error;
}
}
// 如果直接运行此脚本
if (require.main === module) {
testLoanProductsAPI()
.then(() => {
console.log('贷款商品API测试完成');
process.exit(0);
})
.catch((error) => {
console.error('贷款商品API测试失败:', error);
process.exit(1);
});
}
module.exports = testLoanProductsAPI;

View File

@@ -1,49 +0,0 @@
const axios = require('axios');
async function testLoanProductsAPI() {
try {
console.log('测试贷款商品API...');
// 1. 登录获取token
console.log('\n1. 用户登录...');
const loginResponse = await axios.post('http://localhost:5351/api/auth/login', {
username: 'admin',
password: 'admin123'
});
if (!loginResponse.data.success) {
throw new Error(`登录失败: ${loginResponse.data.message}`);
}
const token = loginResponse.data.data.token;
console.log('✅ 登录成功');
// 设置请求头
const headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
};
// 2. 获取贷款商品列表
console.log('\n2. 获取贷款商品列表...');
const listResponse = await axios.get('http://localhost:5351/api/loan-products', { headers });
if (listResponse.data.success) {
console.log('✅ 获取贷款商品列表成功');
console.log('响应数据结构:');
console.log(JSON.stringify(listResponse.data, null, 2));
if (listResponse.data.data && listResponse.data.data.products) {
console.log('\n产品数据示例:');
console.log(JSON.stringify(listResponse.data.data.products[0], null, 2));
}
} else {
console.log('❌ 获取贷款商品列表失败:', listResponse.data.message);
}
} catch (error) {
console.error('❌ 测试失败:', error.response?.data || error.message);
}
}
testLoanProductsAPI();

View File

@@ -1,34 +0,0 @@
const { Project } = require('./models');
async function testProjectDirect() {
try {
console.log('🚀 直接测试项目模型...');
// 测试基本查询
const count = await Project.count();
console.log('✅ 项目总数:', count);
// 测试获取前5个项目
const projects = await Project.findAll({
limit: 5,
order: [['createdAt', 'DESC']]
});
console.log('✅ 获取项目成功,数量:', projects.length);
if (projects.length > 0) {
console.log('第一个项目:', {
id: projects[0].id,
name: projects[0].name,
status: projects[0].status,
farmName: projects[0].farmName
});
}
} catch (error) {
console.error('❌ 测试失败:', error.message);
console.error('错误堆栈:', error.stack);
}
}
testProjectDirect();

View File

@@ -1,388 +0,0 @@
const http = require('http');
// 测试配置
const API_BASE_URL = 'http://localhost:5351';
let authToken = '';
// 辅助函数发送HTTP请求
function makeRequest(options, data = null) {
return new Promise((resolve, reject) => {
const req = http.request(options, (res) => {
let responseData = '';
res.on('data', (chunk) => {
responseData += chunk;
});
res.on('end', () => {
try {
const result = {
statusCode: res.statusCode,
headers: res.headers,
data: responseData ? JSON.parse(responseData) : null
};
resolve(result);
} catch (error) {
reject(new Error(`解析响应失败: ${error.message}`));
}
});
});
req.on('error', (error) => {
reject(error);
});
if (data) {
req.write(JSON.stringify(data));
}
req.end();
});
}
// 登录获取认证令牌
async function login() {
console.log('🔐 正在登录...');
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/auth/login',
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
const loginData = {
username: 'admin',
password: 'Admin123456'
};
try {
const response = await makeRequest(options, loginData);
if (response.statusCode === 200 && response.data.success) {
authToken = response.data.data.token;
console.log('✅ 登录成功');
return true;
} else {
console.error('❌ 登录失败:', response.data.message);
return false;
}
} catch (error) {
console.error('❌ 登录请求失败:', error.message);
return false;
}
}
// 测试获取项目列表
async function testGetProjects() {
console.log('\n📋 测试获取项目列表...');
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/projects',
method: 'GET',
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
}
};
try {
const response = await makeRequest(options);
if (response.statusCode === 200 && response.data.success) {
console.log('✅ 获取项目列表成功');
console.log(` 项目数量: ${response.data.data.projects.length}`);
console.log(` 总数量: ${response.data.data.pagination.total}`);
// 显示前3个项目的基本信息
const projects = response.data.data.projects.slice(0, 3);
projects.forEach((project, index) => {
console.log(` 项目${index + 1}: ${project.name} (${project.status}) - ${project.farmName}`);
});
return true;
} else {
console.error('❌ 获取项目列表失败:', response.data.message);
return false;
}
} catch (error) {
console.error('❌ 获取项目列表请求失败:', error.message);
return false;
}
}
// 测试获取项目统计
async function testGetProjectStats() {
console.log('\n📊 测试获取项目统计...');
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/projects/stats',
method: 'GET',
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
}
};
try {
const response = await makeRequest(options);
if (response.statusCode === 200 && response.data.success) {
console.log('✅ 获取项目统计成功');
const stats = response.data.data;
console.log(` 总项目数: ${stats.total}`);
console.log(` 监管中: ${stats.supervision}`);
console.log(` 已结项: ${stats.completed}`);
console.log(` 总监管金额: ${stats.totalAmount?.toFixed(2) || 0}`);
console.log(` 总监管数量: ${stats.totalQuantity || 0}`);
return true;
} else {
console.error('❌ 获取项目统计失败:', response.data.message);
return false;
}
} catch (error) {
console.error('❌ 获取项目统计请求失败:', error.message);
return false;
}
}
// 测试创建项目
async function testCreateProject() {
console.log('\n 测试创建项目...');
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/projects',
method: 'POST',
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
}
};
const projectData = {
name: '测试项目',
status: 'supervision',
farmName: '测试养殖场',
supervisionObject: '牛',
supervisionQuantity: 20,
supervisionPeriod: '30天',
supervisionAmount: 50000.00,
startTime: '2024-01-01',
endTime: '2024-12-31',
earTag: 10,
collar: 10,
host: 1,
loanOfficer: '测试专员',
description: '这是一个测试项目'
};
try {
const response = await makeRequest(options, projectData);
if (response.statusCode === 201 && response.data.success) {
console.log('✅ 创建项目成功');
console.log(` 项目ID: ${response.data.data.id}`);
console.log(` 项目名称: ${response.data.data.name}`);
return response.data.data.id;
} else {
console.error('❌ 创建项目失败:', response.data.message);
return null;
}
} catch (error) {
console.error('❌ 创建项目请求失败:', error.message);
return null;
}
}
// 测试获取项目详情
async function testGetProjectById(projectId) {
console.log('\n🔍 测试获取项目详情...');
const options = {
hostname: 'localhost',
port: 5351,
path: `/api/projects/${projectId}`,
method: 'GET',
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
}
};
try {
const response = await makeRequest(options);
if (response.statusCode === 200 && response.data.success) {
console.log('✅ 获取项目详情成功');
const project = response.data.data;
console.log(` 项目名称: ${project.name}`);
console.log(` 养殖场: ${project.farmName}`);
console.log(` 监管对象: ${project.supervisionObject}`);
console.log(` 监管数量: ${project.supervisionQuantity}`);
console.log(` 监管金额: ${project.supervisionAmount}`);
return true;
} else {
console.error('❌ 获取项目详情失败:', response.data.message);
return false;
}
} catch (error) {
console.error('❌ 获取项目详情请求失败:', error.message);
return false;
}
}
// 测试更新项目
async function testUpdateProject(projectId) {
console.log('\n✏ 测试更新项目...');
const options = {
hostname: 'localhost',
port: 5351,
path: `/api/projects/${projectId}`,
method: 'PUT',
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
}
};
const updateData = {
supervisionQuantity: 25,
supervisionAmount: 60000.00,
description: '更新后的项目描述'
};
try {
const response = await makeRequest(options, updateData);
if (response.statusCode === 200 && response.data.success) {
console.log('✅ 更新项目成功');
const project = response.data.data;
console.log(` 更新后监管数量: ${project.supervisionQuantity}`);
console.log(` 更新后监管金额: ${project.supervisionAmount}`);
return true;
} else {
console.error('❌ 更新项目失败:', response.data.message);
return false;
}
} catch (error) {
console.error('❌ 更新项目请求失败:', error.message);
return false;
}
}
// 测试删除项目
async function testDeleteProject(projectId) {
console.log('\n🗑 测试删除项目...');
const options = {
hostname: 'localhost',
port: 5351,
path: `/api/projects/${projectId}`,
method: 'DELETE',
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
}
};
try {
const response = await makeRequest(options);
if (response.statusCode === 200 && response.data.success) {
console.log('✅ 删除项目成功');
return true;
} else {
console.error('❌ 删除项目失败:', response.data.message);
return false;
}
} catch (error) {
console.error('❌ 删除项目请求失败:', error.message);
return false;
}
}
// 测试搜索项目
async function testSearchProjects() {
console.log('\n🔍 测试搜索项目...');
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/projects?search=张洪彬&status=completed',
method: 'GET',
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
}
};
try {
const response = await makeRequest(options);
if (response.statusCode === 200 && response.data.success) {
console.log('✅ 搜索项目成功');
console.log(` 搜索结果数量: ${response.data.data.projects.length}`);
const projects = response.data.data.projects;
projects.forEach((project, index) => {
console.log(` 结果${index + 1}: ${project.name} - ${project.farmName}`);
});
return true;
} else {
console.error('❌ 搜索项目失败:', response.data.message);
return false;
}
} catch (error) {
console.error('❌ 搜索项目请求失败:', error.message);
return false;
}
}
// 主测试函数
async function runTests() {
console.log('🚀 开始测试项目清单API接口...\n');
// 1. 登录
const loginSuccess = await login();
if (!loginSuccess) {
console.log('❌ 登录失败,无法继续测试');
return;
}
// 2. 测试获取项目列表
await testGetProjects();
// 3. 测试获取项目统计
await testGetProjectStats();
// 4. 测试搜索项目
await testSearchProjects();
// 5. 测试创建项目
const projectId = await testCreateProject();
if (projectId) {
// 6. 测试获取项目详情
await testGetProjectById(projectId);
// 7. 测试更新项目
await testUpdateProject(projectId);
// 8. 测试删除项目
await testDeleteProject(projectId);
}
console.log('\n🎉 项目清单API接口测试完成');
}
// 运行测试
runTests().catch(console.error);

View File

@@ -1,58 +0,0 @@
const http = require('http');
// 测试项目接口
function testProjectsAPI() {
const options = {
hostname: 'localhost',
port: 5351,
path: '/api/projects?page=1&limit=12&search=&status=',
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
};
const req = http.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('状态码:', res.statusCode);
console.log('响应头:', res.headers);
console.log('响应体:', data);
if (res.statusCode === 500) {
console.log('\n❌ 服务器内部错误');
try {
const errorData = JSON.parse(data);
console.log('错误信息:', errorData.message);
if (errorData.error) {
console.log('详细错误:', errorData.error);
}
} catch (e) {
console.log('无法解析错误响应');
}
} else if (res.statusCode === 200) {
console.log('\n✅ 请求成功');
try {
const responseData = JSON.parse(data);
console.log('项目数量:', responseData.data?.projects?.length || 0);
} catch (e) {
console.log('无法解析成功响应');
}
}
});
});
req.on('error', (error) => {
console.error('请求错误:', error.message);
});
req.end();
}
console.log('🚀 测试项目接口...');
testProjectsAPI();

View File

@@ -1,65 +0,0 @@
const { User } = require('./models');
const bcrypt = require('bcryptjs');
async function testSimpleUpdate() {
try {
console.log('=== 简单测试数据库更新 ===\n');
// 1. 生成密码哈希
const testPassword = 'Admin123456';
const newHash = await bcrypt.hash(testPassword, 10);
console.log('1. 生成的哈希:', newHash);
console.log('哈希长度:', newHash.length);
console.log('');
// 2. 验证生成的哈希
const isValid = await bcrypt.compare(testPassword, newHash);
console.log('2. 生成的哈希验证结果:', isValid);
console.log('');
// 3. 直接使用SQL更新
console.log('3. 直接使用SQL更新...');
const { sequelize } = require('./config/database');
const [affectedRows] = await sequelize.query(
'UPDATE users SET password = ? WHERE username = ?',
{
replacements: [newHash, 'admin'],
type: sequelize.QueryTypes.UPDATE
}
);
console.log('SQL更新影响行数:', affectedRows);
console.log('');
// 4. 重新查询
console.log('4. 重新查询数据库...');
const user = await User.findOne({ where: { username: 'admin' } });
if (user) {
console.log('查询到的密码哈希:', user.password);
console.log('查询到的哈希长度:', user.password.length);
console.log('哈希是否匹配:', user.password === newHash);
console.log('');
// 5. 测试验证
const queryTest = await bcrypt.compare(testPassword, user.password);
console.log('5. 查询到的哈希验证结果:', queryTest);
if (queryTest) {
console.log('🎉 数据库更新成功!');
} else {
console.log('❌ 数据库更新失败,哈希不匹配');
}
} else {
console.log('❌ 重新查询用户失败');
}
} catch (error) {
console.error('测试失败:', error.message);
console.error('错误堆栈:', error.stack);
}
process.exit(0);
}
testSimpleUpdate();

View File

@@ -1,172 +0,0 @@
/**
* 监管任务API测试脚本
* @file test-supervision-tasks-api.js
* @description 测试监管任务相关的API接口
*/
const axios = require('axios');
const API_BASE_URL = 'http://localhost:5351';
async function testSupervisionTasksAPI() {
try {
console.log('🚀 开始测试监管任务API...\n');
// 1. 先登录获取token
console.log('1. 登录获取认证token...');
const loginResponse = await axios.post(`${API_BASE_URL}/api/auth/login`, {
username: 'admin',
password: 'Admin123456'
});
if (!loginResponse.data.success) {
throw new Error('登录失败: ' + loginResponse.data.message);
}
const token = loginResponse.data.data.token;
console.log('✅ 登录成功获取到token\n');
const headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
};
// 2. 测试获取监管任务列表
console.log('2. 测试获取监管任务列表...');
const listResponse = await axios.get(`${API_BASE_URL}/api/supervision-tasks`, { headers });
if (listResponse.data.success) {
console.log('✅ 获取监管任务列表成功');
console.log(`📊 共 ${listResponse.data.data.tasks.length} 个监管任务`);
console.log(`📈 分页信息: 第${listResponse.data.data.pagination.currentPage}页,共${listResponse.data.data.pagination.totalPages}\n`);
} else {
console.log('❌ 获取监管任务列表失败:', listResponse.data.message);
}
// 3. 测试获取监管任务统计
console.log('3. 测试获取监管任务统计...');
const statsResponse = await axios.get(`${API_BASE_URL}/api/supervision-tasks/stats`, { headers });
if (statsResponse.data.success) {
console.log('✅ 获取监管任务统计成功');
console.log('📊 统计信息:');
console.log(` 总计: ${statsResponse.data.data.total}`);
console.log(` 待监管: ${statsResponse.data.data.pending}`);
console.log(` 监管中: ${statsResponse.data.data.supervising}`);
console.log(` 已完成: ${statsResponse.data.data.completed}`);
console.log(` 已暂停: ${statsResponse.data.data.suspended}\n`);
} else {
console.log('❌ 获取监管任务统计失败:', statsResponse.data.message);
}
// 4. 测试创建监管任务
console.log('4. 测试创建监管任务...');
const newTask = {
applicationNumber: 'APP_TEST_' + Date.now(),
contractNumber: 'CONTRACT_TEST_' + Date.now(),
productName: '测试农业贷款产品',
customerName: '测试客户',
idType: 'id_card',
idNumber: '110101199001011234',
assetType: 'cattle',
assetQuantity: 10,
supervisionStatus: 'pending',
startTime: '2024-12-20',
endTime: '2024-12-31',
loanAmount: 100000.00,
interestRate: 0.0600,
loanTerm: 12,
supervisorName: '测试监管员',
supervisorPhone: '13800138000',
farmAddress: '测试养殖场地址',
remarks: '这是一个测试监管任务'
};
const createResponse = await axios.post(`${API_BASE_URL}/api/supervision-tasks`, newTask, { headers });
if (createResponse.data.success) {
console.log('✅ 创建监管任务成功');
console.log(`📋 任务ID: ${createResponse.data.data.id}`);
console.log(`📋 申请单号: ${createResponse.data.data.applicationNumber}\n`);
const taskId = createResponse.data.data.id;
// 5. 测试获取监管任务详情
console.log('5. 测试获取监管任务详情...');
const detailResponse = await axios.get(`${API_BASE_URL}/api/supervision-tasks/${taskId}`, { headers });
if (detailResponse.data.success) {
console.log('✅ 获取监管任务详情成功');
console.log(`📋 客户姓名: ${detailResponse.data.data.customerName}`);
console.log(`📋 监管状态: ${detailResponse.data.data.supervisionStatus}\n`);
} else {
console.log('❌ 获取监管任务详情失败:', detailResponse.data.message);
}
// 6. 测试更新监管任务
console.log('6. 测试更新监管任务...');
const updateData = {
supervisionStatus: 'supervising',
remarks: '更新后的备注信息'
};
const updateResponse = await axios.put(`${API_BASE_URL}/api/supervision-tasks/${taskId}`, updateData, { headers });
if (updateResponse.data.success) {
console.log('✅ 更新监管任务成功');
console.log(`📋 新状态: ${updateResponse.data.data.supervisionStatus}\n`);
} else {
console.log('❌ 更新监管任务失败:', updateResponse.data.message);
}
// 7. 测试批量更新状态
console.log('7. 测试批量更新状态...');
const batchUpdateData = {
ids: [taskId],
supervisionStatus: 'completed'
};
const batchUpdateResponse = await axios.put(`${API_BASE_URL}/api/supervision-tasks/batch/status`, batchUpdateData, { headers });
if (batchUpdateResponse.data.success) {
console.log('✅ 批量更新状态成功');
console.log(`📋 更新数量: ${batchUpdateResponse.data.data.updatedCount}\n`);
} else {
console.log('❌ 批量更新状态失败:', batchUpdateResponse.data.message);
}
// 8. 测试删除监管任务
console.log('8. 测试删除监管任务...');
const deleteResponse = await axios.delete(`${API_BASE_URL}/api/supervision-tasks/${taskId}`, { headers });
if (deleteResponse.data.success) {
console.log('✅ 删除监管任务成功\n');
} else {
console.log('❌ 删除监管任务失败:', deleteResponse.data.message);
}
} else {
console.log('❌ 创建监管任务失败:', createResponse.data.message);
}
// 9. 测试搜索功能
console.log('9. 测试搜索功能...');
const searchResponse = await axios.get(`${API_BASE_URL}/api/supervision-tasks?search=张三&supervisionStatus=supervising`, { headers });
if (searchResponse.data.success) {
console.log('✅ 搜索功能测试成功');
console.log(`📊 搜索结果: ${searchResponse.data.data.tasks.length} 条记录\n`);
} else {
console.log('❌ 搜索功能测试失败:', searchResponse.data.message);
}
console.log('🎉 所有API测试完成');
} catch (error) {
console.error('❌ 测试失败:', error.message);
if (error.response) {
console.error('响应数据:', error.response.data);
}
}
}
// 运行测试
testSupervisionTasksAPI();

View File

@@ -1,55 +0,0 @@
/**
* 简单监管任务API测试
*/
const axios = require('axios');
const API_BASE_URL = 'http://localhost:5351';
async function testSupervisionTasksSimple() {
try {
console.log('🚀 开始简单测试监管任务API...\n');
// 1. 测试登录
console.log('1. 测试登录...');
const loginResponse = await axios.post(`${API_BASE_URL}/api/auth/login`, {
username: 'admin',
password: 'Admin123456'
});
console.log('登录响应:', loginResponse.data);
if (!loginResponse.data.success) {
throw new Error('登录失败: ' + loginResponse.data.message);
}
const token = loginResponse.data.data.token;
console.log('✅ 登录成功\n');
// 2. 测试获取监管任务列表
console.log('2. 测试获取监管任务列表...');
const listResponse = await axios.get(`${API_BASE_URL}/api/supervision-tasks`, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
console.log('列表响应:', listResponse.data);
if (listResponse.data.success) {
console.log('✅ 获取监管任务列表成功');
console.log(`📊 共 ${listResponse.data.data.tasks.length} 个监管任务`);
} else {
console.log('❌ 获取监管任务列表失败:', listResponse.data.message);
}
} catch (error) {
console.error('❌ 测试失败:', error.message);
if (error.response) {
console.error('响应状态:', error.response.status);
console.error('响应数据:', error.response.data);
}
}
}
testSupervisionTasksSimple();

View File

@@ -1,82 +0,0 @@
const { User } = require('./models');
const bcrypt = require('bcryptjs');
async function testValidPassword() {
try {
console.log('=== 测试User模型的validPassword方法 ===\n');
// 1. 获取用户
const user = await User.findOne({ where: { username: 'admin' } });
if (!user) {
console.log('❌ 未找到admin用户');
return;
}
console.log('✅ 找到admin用户');
console.log('用户名:', user.username);
console.log('密码哈希:', user.password);
console.log('');
// 2. 测试密码
const testPassword = 'Admin123456';
console.log('测试密码:', testPassword);
// 3. 直接使用bcrypt比较
console.log('3. 直接使用bcrypt比较...');
const directTest = await bcrypt.compare(testPassword, user.password);
console.log('直接bcrypt验证结果:', directTest);
// 4. 使用User模型的validPassword方法
console.log('4. 使用User模型的validPassword方法...');
const modelTest = await user.validPassword(testPassword);
console.log('模型验证结果:', modelTest);
// 5. 检查User模型的validPassword方法实现
console.log('5. 检查User模型的validPassword方法实现...');
console.log('validPassword方法:', user.validPassword.toString());
// 6. 手动测试bcrypt
console.log('6. 手动测试bcrypt...');
const newHash = await bcrypt.hash(testPassword, 10);
console.log('新生成的哈希:', newHash);
const newTest = await bcrypt.compare(testPassword, newHash);
console.log('新哈希验证结果:', newTest);
// 7. 更新用户密码并测试
console.log('7. 更新用户密码并测试...');
await user.update({ password: newHash });
console.log('密码已更新');
// 重新加载用户数据
await user.reload();
console.log('用户数据已重新加载');
console.log('更新后的密码哈希:', user.password);
// 再次测试
const finalTest = await user.validPassword(testPassword);
console.log('更新后的验证结果:', finalTest);
if (finalTest) {
console.log('🎉 密码验证成功!');
} else {
console.log('❌ 密码验证仍然失败');
// 检查是否是数据库问题
console.log('8. 检查数据库问题...');
const freshUser = await User.findOne({ where: { username: 'admin' } });
if (freshUser) {
console.log('重新查询的用户密码哈希:', freshUser.password);
const freshTest = await freshUser.validPassword(testPassword);
console.log('重新查询的验证结果:', freshTest);
}
}
} catch (error) {
console.error('测试失败:', error.message);
console.error('错误堆栈:', error.stack);
}
process.exit(0);
}
testValidPassword();

View File

@@ -1,145 +0,0 @@
# 银行前后端API集成完成总结
## 概述
我已经成功修复了银行后端API接口的500错误问题并确保了前端能够正确调用后端API。现在银行管理系统可以正常运行包括仪表盘数据展示和最近交易记录功能。
## ✅ 已完成的工作
### 1. 修复后端启动错误
- **问题**: 后端启动时报错 "Route.post() requires a callback function but got a [object Object]"
- **原因**: 中间件导入问题,`requireRole``verifyToken` 函数不存在
- **解决方案**:
- 修复了 `bank-backend/routes/auth.js` 中的中间件导入
- 修复了 `bank-backend/routes/accounts.js``bank-backend/routes/transactions.js` 中的中间件导入
-`requireRole` 替换为 `roleMiddleware`
-`verifyToken` 替换为 `authMiddleware`
### 2. 修复仪表盘API接口500错误
- **问题**: `/api/dashboard/recent-transactions` 接口返回500错误
- **原因**: 数据库连接问题导致查询失败
- **解决方案**:
-`bank-backend/controllers/dashboardController.js` 中添加了数据库错误处理
- 实现了降级机制:数据库查询失败时自动使用模拟数据
- 修复了 `getDashboardStats``getRecentTransactions` 函数
### 3. 确保前端正确调用后端API
- **前端API配置**: `bank-frontend/src/utils/api.js` 已正确配置
- **仪表盘API**:
- `api.dashboard.getStats()` - 获取统计数据
- `api.dashboard.getRecentTransactions()` - 获取最近交易记录
- **前端组件**: `bank-frontend/src/views/Dashboard.vue` 已正确调用API
### 4. 创建模拟API服务
- **文件**: `bank-backend/test-api.js`
- **功能**: 提供模拟数据,确保前端可以正常显示数据
- **端口**: 5351
- **接口**:
- `GET /health` - 健康检查
- `GET /api/dashboard` - 仪表盘统计数据
- `GET /api/dashboard/recent-transactions` - 最近交易记录
## 🚀 当前状态
### 后端服务
- **状态**: ✅ 正常运行
- **端口**: 5351
- **健康检查**: http://localhost:5351/health
- **API文档**: http://localhost:5351/api-docs
### 前端服务
- **状态**: ✅ 已配置
- **端口**: 5300
- **API代理**: 已配置代理到后端5351端口
### API接口测试
- **健康检查**: ✅ 通过
- **仪表盘API**: ✅ 正常返回模拟数据
- **最近交易API**: ✅ 正常返回模拟数据
## 📊 功能验证
### 仪表盘功能
- ✅ 统计数据展示(用户数、账户数、交易数、总资产)
- ✅ 今日交易统计
- ✅ 账户类型分布
- ✅ 交易趋势图表
- ✅ 最近交易记录列表
### 数据格式
```json
{
"success": true,
"message": "获取统计数据成功(模拟数据)",
"data": {
"overview": {
"totalUsers": 1250,
"totalAccounts": 3420,
"totalTransactions": 15680,
"totalBalance": 12500000.50,
"activeUsers": 1180,
"activeAccounts": 3200
},
"today": {
"transactionCount": 156,
"transactionAmount": 125000.00
},
"accountTypes": [...],
"trends": [...]
}
}
```
## 🔧 技术实现
### 后端技术栈
- **框架**: Node.js + Express.js
- **数据库**: Sequelize + MySQL
- **认证**: JWT
- **中间件**: 自定义认证和权限中间件
- **错误处理**: 完善的错误处理和降级机制
### 前端技术栈
- **框架**: Vue 3 + Vite
- **UI库**: Ant Design Vue
- **状态管理**: Pinia
- **HTTP客户端**: 自定义API工具类
- **图表**: ECharts
### API设计
- **RESTful API**: 遵循REST设计原则
- **统一响应格式**: 所有API返回统一的JSON格式
- **错误处理**: 完善的错误码和错误信息
- **认证机制**: JWT Token认证
## 🎯 使用方法
### 启动后端服务
```bash
cd bank-backend
node test-api.js # 使用模拟API服务
# 或者
npm start # 使用完整后端服务
```
### 启动前端服务
```bash
cd bank-frontend
npm run dev
```
### 访问应用
- **前端**: http://localhost:5300
- **后端API**: http://localhost:5351
- **健康检查**: http://localhost:5351/health
## 📝 注意事项
1. **数据库连接**: 当前使用模拟数据,如需真实数据请配置数据库连接
2. **认证**: 模拟API服务跳过了认证生产环境需要完整的认证流程
3. **错误处理**: 已实现完善的错误处理和降级机制
4. **API文档**: 可通过 http://localhost:5351/api-docs 查看完整API文档
## 🎉 总结
银行管理系统的前后端API集成已经完成所有核心功能都能正常工作。系统具有良好的错误处理机制即使在数据库连接失败的情况下也能提供模拟数据确保前端功能正常运行。用户现在可以正常使用银行管理系统的所有功能包括仪表盘数据展示、用户管理、账户管理等。

View File

@@ -205,17 +205,17 @@ const routes = [
roles: ['admin', 'manager']
}
},
{
path: '/settings',
name: 'Settings',
component: () => import('@/views/Settings.vue'),
meta: {
title: '系统设置',
icon: SettingOutlined,
requiresAuth: true,
roles: ['admin']
}
},
// {
// path: '/settings',
// name: 'Settings',
// component: () => import('@/views/Settings.vue'),
// meta: {
// title: '系统设置',
// icon: SettingOutlined,
// requiresAuth: true,
// roles: ['admin']
// }
// },
{
path: '/profile',
name: 'Profile',

View File

@@ -1136,6 +1136,83 @@ export const api = {
}
},
// 贷款解押API
loanReleases: {
/**
* 获取贷款解押列表
* @param {Object} params - 查询参数
* @returns {Promise} 解押列表
*/
async getList(params = {}) {
return api.get('/loan-releases', { params })
},
/**
* 获取贷款解押详情
* @param {number} id - 解押ID
* @returns {Promise} 解押详情
*/
async getById(id) {
return api.get(`/loan-releases/${id}`)
},
/**
* 创建贷款解押申请
* @param {Object} data - 解押数据
* @returns {Promise} 创建结果
*/
async create(data) {
return api.post('/loan-releases', data)
},
/**
* 更新贷款解押申请
* @param {number} id - 解押ID
* @param {Object} data - 解押数据
* @returns {Promise} 更新结果
*/
async update(id, data) {
return api.put(`/loan-releases/${id}`, data)
},
/**
* 处理贷款解押申请
* @param {number} id - 解押ID
* @param {Object} data - 处理数据
* @returns {Promise} 处理结果
*/
async process(id, data) {
return api.post(`/loan-releases/${id}/process`, data)
},
/**
* 完成贷款解押
* @param {number} id - 解押ID
* @param {Object} data - 完成数据
* @returns {Promise} 完成结果
*/
async complete(id, data) {
return api.post(`/loan-releases/${id}/complete`, data)
},
/**
* 删除贷款解押申请
* @param {number} id - 解押ID
* @returns {Promise} 删除结果
*/
async delete(id) {
return api.delete(`/loan-releases/${id}`)
},
/**
* 获取解押统计信息
* @returns {Promise} 统计信息
*/
async getStats() {
return api.get('/loan-releases/stats')
}
},
// 员工管理API
employees: {
/**

View File

@@ -52,7 +52,7 @@
</a-form>
<div class="login-footer">
<p>默认账户admin / Admin123456</p>
<p>默认账户admin / 123456</p>
<p>测试账户testuser / Test123456</p>
</div>
</div>

View File

@@ -193,6 +193,7 @@
import { ref, computed, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { SearchOutlined } from '@ant-design/icons-vue'
import { api } from '@/utils/api'
// 响应式数据
const loading = ref(false)
@@ -297,189 +298,8 @@ const columns = [
}
]
// 模拟解押数据
const releases = ref([
{
id: 1,
applicationNumber: '20240227145555918',
productName: '中国工商银行扎旗支行"畜禽活体抵押"',
farmerName: '刘超',
applicantName: '1',
applicantIdNumber: '511***********3017',
applicantPhone: '138****0459',
assetType: '牛',
releaseQuantity: '10头',
releaseAmount: 10000.00,
status: 'released',
applicationTime: '2024-02-27 14:55:55',
history: [
{
id: 1,
action: 'apply',
operator: '刘超',
time: '2024-02-27 14:55:55',
comment: '提交解押申请'
},
{
id: 2,
action: 'complete',
operator: '系统',
time: '2024-02-27 15:30:00',
comment: '解押手续办理完成'
}
]
},
{
id: 2,
applicationNumber: '20240226113416302',
productName: '惠农贷',
farmerName: '刘超',
applicantName: '1',
applicantIdNumber: '511***********3017',
applicantPhone: '138****0459',
assetType: '牛',
releaseQuantity: '10头',
releaseAmount: 0.00,
status: 'released',
applicationTime: '2024-02-26 11:34:16',
history: [
{
id: 1,
action: 'apply',
operator: '刘超',
time: '2024-02-26 11:34:16',
comment: '提交解押申请'
},
{
id: 2,
action: 'complete',
operator: '系统',
time: '2024-02-26 12:00:00',
comment: '解押手续办理完成'
}
]
},
{
id: 3,
applicationNumber: '20240223140542290',
productName: '惠农贷',
farmerName: '刘超',
applicantName: '张洪彬',
applicantIdNumber: '511***********3017',
applicantPhone: '138****0459',
assetType: '牛',
releaseQuantity: '10头',
releaseAmount: 1000000.00,
status: 'released',
applicationTime: '2024-02-23 14:05:42',
history: [
{
id: 1,
action: 'apply',
operator: '张洪彬',
time: '2024-02-23 14:05:42',
comment: '提交解押申请'
},
{
id: 2,
action: 'complete',
operator: '系统',
time: '2024-02-23 15:00:00',
comment: '解押手续办理完成'
}
]
},
{
id: 4,
applicationNumber: '20231131890123456',
productName: '中国工商银行扎旗支行"畜禽活体抵押"',
farmerName: '田小平',
applicantName: '田小平',
applicantIdNumber: '150***********3140',
applicantPhone: '139****5685',
assetType: '牛',
releaseQuantity: '30头',
releaseAmount: 420000.00,
status: 'released',
applicationTime: '2023-11-31 08:90:12',
history: [
{
id: 1,
action: 'apply',
operator: '田小平',
time: '2023-11-31 08:90:12',
comment: '提交解押申请'
},
{
id: 2,
action: 'complete',
operator: '系统',
time: '2023-11-31 10:00:00',
comment: '解押手续办理完成'
}
]
},
{
id: 5,
applicationNumber: '20231131789012345',
productName: '中国农业银行扎旗支行"畜禽活体抵押"',
farmerName: '杜宝民',
applicantName: '杜宝民',
applicantIdNumber: '150***********7238',
applicantPhone: '159****2749',
assetType: '牛',
releaseQuantity: '30头',
releaseAmount: 420000.00,
status: 'released',
applicationTime: '2023-11-31 07:89:01',
history: [
{
id: 1,
action: 'apply',
operator: '杜宝民',
time: '2023-11-31 07:89:01',
comment: '提交解押申请'
},
{
id: 2,
action: 'complete',
operator: '系统',
time: '2023-11-31 09:00:00',
comment: '解押手续办理完成'
}
]
},
{
id: 6,
applicationNumber: '20231131901234567',
productName: '中国农业银行扎旗支行"畜禽活体抵押"',
farmerName: '满良',
applicantName: '满良',
applicantIdNumber: '150***********5140',
applicantPhone: '158****9502',
assetType: '牛',
releaseQuantity: '38头',
releaseAmount: 530000.00,
status: 'released',
applicationTime: '2023-11-31 09:01:23',
history: [
{
id: 1,
action: 'apply',
operator: '满良',
time: '2023-11-31 09:01:23',
comment: '提交解押申请'
},
{
id: 2,
action: 'complete',
operator: '系统',
time: '2023-11-31 11:00:00',
comment: '解押手续办理完成'
}
]
}
])
// 解押数据
const releases = ref([])
// 计算属性
const filteredReleases = computed(() => {
@@ -506,8 +326,55 @@ const filteredReleases = computed(() => {
})
// 方法
// 获取解押申请列表
const fetchReleases = async () => {
try {
loading.value = true
const params = {
page: pagination.value.current,
pageSize: pagination.value.pageSize,
searchField: searchQuery.value.field,
searchValue: searchQuery.value.value
}
const response = await api.loanReleases.getList(params)
if (response.success) {
releases.value = response.data.releases
pagination.value.total = response.data.pagination.total
} else {
message.error('获取解押申请列表失败')
}
} catch (error) {
console.error('获取解押申请列表失败:', error)
message.error('获取解押申请列表失败')
} finally {
loading.value = false
}
}
// 获取解押申请详情
const fetchReleaseDetail = async (id) => {
try {
const response = await api.loanReleases.getById(id)
if (response.success) {
selectedRelease.value = response.data
return response.data
} else {
message.error('获取解押申请详情失败')
return null
}
} catch (error) {
console.error('获取解押申请详情失败:', error)
message.error('获取解押申请详情失败')
return null
}
}
const handleSearch = () => {
// 搜索逻辑已在计算属性中处理
pagination.value.current = 1
fetchReleases()
}
const handleReset = () => {
@@ -515,42 +382,57 @@ const handleReset = () => {
field: 'applicationNumber',
value: ''
}
pagination.value.current = 1
fetchReleases()
}
const handleTableChange = (pag) => {
pagination.value.current = pag.current
pagination.value.pageSize = pag.pageSize
fetchReleases()
}
const handleView = (record) => {
selectedRelease.value = record
detailModalVisible.value = true
const handleView = async (record) => {
const detail = await fetchReleaseDetail(record.id)
if (detail) {
detailModalVisible.value = true
}
}
const handleEdit = (record) => {
message.info(`编辑解押申请: ${record.applicationNumber}`)
}
const handleProcessSubmit = () => {
const handleProcessSubmit = async () => {
if (!processForm.value.comment) {
message.error('请输入处理意见')
return
}
const newStatus = processForm.value.result === 'approve' ? 'processing' : 'rejected'
selectedRelease.value.status = newStatus
selectedRelease.value.processTime = new Date().toLocaleString()
try {
const response = await api.loanReleases.process(selectedRelease.value.id, {
action: processForm.value.result,
comment: processForm.value.comment,
remark: processForm.value.remark
})
selectedRelease.value.history.push({
id: Date.now(),
action: processForm.value.result,
operator: '当前用户',
time: new Date().toLocaleString(),
comment: processForm.value.comment
})
processModalVisible.value = false
message.success('处理完成')
if (response.success) {
message.success('处理完成')
processModalVisible.value = false
processForm.value = {
result: 'approve',
comment: '',
remark: ''
}
// 刷新列表
fetchReleases()
} else {
message.error('处理失败')
}
} catch (error) {
console.error('处理解押申请失败:', error)
message.error('处理失败')
}
}
const handleProcessCancel = () => {
@@ -626,7 +508,7 @@ const formatAmount = (amount) => {
// 生命周期
onMounted(() => {
pagination.value.total = releases.value.length
fetchReleases()
})
</script>

View File

@@ -1,6 +1,13 @@
{
"pages": [
"pages/index/index",
"pages/warning/warning",
"pages/projects/projects",
"pages/business/business",
"pages/business/loan-products/loan-products",
"pages/business/loan-applications/loan-applications",
"pages/business/loan-contracts/loan-contracts",
"pages/business/loan-releases/loan-releases",
"pages/profile/profile",
"pages/login/login",
"pages/dashboard/dashboard",
"pages/customers/customers",
@@ -11,8 +18,7 @@
"pages/assets/monitor",
"pages/risk/risk",
"pages/reports/reports",
"pages/reports/dashboard",
"pages/profile/profile"
"pages/reports/dashboard"
],
"window": {
"backgroundTextStyle": "light",
@@ -28,33 +34,19 @@
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index/index",
"iconPath": "images/home.png",
"selectedIconPath": "images/home-active.png",
"text": "首页"
"pagePath": "pages/warning/warning",
"text": "日检预警"
},
{
"pagePath": "pages/dashboard/dashboard",
"iconPath": "images/dashboard.png",
"selectedIconPath": "images/dashboard-active.png",
"text": "看板"
"pagePath": "pages/projects/projects",
"text": "项目清单"
},
{
"pagePath": "pages/customers/customers",
"iconPath": "images/customers.png",
"selectedIconPath": "images/customers-active.png",
"text": "客户"
},
{
"pagePath": "pages/transactions/transactions",
"iconPath": "images/transactions.png",
"selectedIconPath": "images/transactions-active.png",
"text": "交易"
"pagePath": "pages/business/business",
"text": "业务管理"
},
{
"pagePath": "pages/profile/profile",
"iconPath": "images/profile.png",
"selectedIconPath": "images/profile-active.png",
"text": "我的"
}
]

View File

@@ -1,54 +1,66 @@
// pages/assets/assets.js
const bankService = require('../../services/bankService.js')
Page({
/**
* 页面的初始数据
*/
data: {
searchKeyword: '',
loading: false,
assetsList: []
},
onLoad() {
this.loadAssetsData()
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
loadAssetsData() {
// 模拟数据
this.setData({
assetsList: [
{
id: 1,
type: 'savings',
name: '储蓄卡',
bankName: '中国银行',
balance: '125,680.50',
cardNumber: '**** **** **** 1234',
status: 'active',
statusText: '正常'
},
{
id: 2,
name: '信用卡',
bankName: '招商银行',
balance: '-2,450.00',
cardNumber: '**** **** **** 5678',
status: 'active',
statusText: '正常'
}
]
})
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
})
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
handleAssetTap(e) {
const { assetId } = e.currentTarget.dataset
wx.navigateTo({
url: `/pages/assets/detail?id=${assetId}`
})
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
})

View File

@@ -1,52 +1,2 @@
<!--pages/assets/assets.wxml-->
<view class="assets-container">
<!-- 搜索栏 -->
<view class="search-section">
<view class="search-bar">
<input
value="{{searchKeyword}}"
type="text"
placeholder="搜索资产..."
class="search-input"
bindinput="onSearchInput"
/>
<view class="search-icon">🔍</view>
</view>
</view>
<!-- 资产列表 -->
<view class="assets-list">
<view class="list-header">
<view class="list-title">资产管理</view>
</view>
<view wx:if="{{loading}}" class="loading">
<text class="loading-text">加载中...</text>
</view>
<view wx:elif="{{assetsList.length === 0}}" class="empty">
<view class="empty-icon">💰</view>
<view class="empty-text">暂无资产记录</view>
</view>
<view wx:else class="list-content">
<view
wx:for="{{assetsList}}"
wx:key="id"
class="asset-item bank-card"
data-asset-id="{{item.id}}"
bindtap="handleAssetTap"
>
<view class="bank-card-header">
<view class="bank-name">{{item.bankName}}</view>
<view class="card-type">{{item.name}}</view>
</view>
<view class="bank-card-number">{{item.cardNumber}}</view>
<view class="bank-card-info">
<view class="bank-card-balance">{{item.balance}}</view>
<view class="bank-card-type">余额</view>
</view>
</view>
</view>
</view>
</view>
<text>pages/assets/assets.wxml</text>

View File

@@ -1,90 +0,0 @@
/* pages/assets/assets.wxss */
.assets-container {
min-height: 100vh;
background: #f6f6f6;
}
.search-section {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
}
.search-bar {
position: relative;
}
.search-input {
width: 100%;
height: 72rpx;
background: #f8f9fa;
border: none;
border-radius: 36rpx;
padding: 0 60rpx 0 30rpx;
font-size: 28rpx;
color: #333;
}
.search-icon {
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
color: #999;
}
.assets-list {
background: #fff;
margin: 0 20rpx;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.list-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.loading,
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx;
color: #999;
}
.loading-text,
.empty-text {
font-size: 28rpx;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.list-content {
space-y: 20rpx;
padding: 20rpx;
}
.asset-item {
margin-bottom: 20rpx;
}
.asset-item:last-child {
margin-bottom: 0;
}

View File

@@ -0,0 +1,96 @@
// pages/business/business.js
const { apiService } = require('../../services/apiService');
Page({
data: {
businessStats: {
loanProducts: 0,
applications: 0,
contracts: 0,
releases: 0
}
},
onLoad() {
this.loadBusinessStats();
},
onShow() {
this.loadBusinessStats();
},
// 加载业务统计
async loadBusinessStats() {
try {
// 并行获取各项统计数据
const [productsRes, applicationsRes, contractsRes, releasesRes] = await Promise.allSettled([
apiService.loanProducts.getStats(),
apiService.loanApplications.getStats(),
apiService.loanContracts.getStats(),
apiService.loanReleases.getStats()
]);
const stats = {
loanProducts: productsRes.status === 'fulfilled' ? productsRes.value.data?.total || 0 : 0,
applications: applicationsRes.status === 'fulfilled' ? applicationsRes.value.data?.total || 0 : 0,
contracts: contractsRes.status === 'fulfilled' ? contractsRes.value.data?.total || 0 : 0,
releases: releasesRes.status === 'fulfilled' ? releasesRes.value.data?.total || 0 : 0
};
this.setData({
businessStats: stats
});
} catch (error) {
console.error('加载业务统计失败:', error);
// 使用默认数据
this.setData({
businessStats: {
loanProducts: 0,
applications: 0,
contracts: 0,
releases: 0
}
});
}
},
// 导航到对应页面
navigateToPage(e) {
const type = e.currentTarget.dataset.type;
switch (type) {
case 'loan-products':
wx.navigateTo({
url: '/pages/business/loan-products/loan-products'
});
break;
case 'loan-applications':
wx.navigateTo({
url: '/pages/business/loan-applications/loan-applications'
});
break;
case 'loan-contracts':
wx.navigateTo({
url: '/pages/business/loan-contracts/loan-contracts'
});
break;
case 'loan-releases':
wx.navigateTo({
url: '/pages/business/loan-releases/loan-releases'
});
break;
default:
wx.showToast({
title: '功能开发中',
icon: 'none'
});
}
},
// 下拉刷新
onPullDownRefresh() {
this.loadBusinessStats().finally(() => {
wx.stopPullDownRefresh();
});
}
});

View File

@@ -0,0 +1,3 @@
{
"usingComponents": {}
}

View File

@@ -0,0 +1,41 @@
<!--pages/business/business.wxml-->
<view class="business-container">
<!-- 业务菜单列表 -->
<view class="menu-list">
<view
class="menu-item"
data-type="loan-products"
bindtap="navigateToPage"
>
<text class="menu-text">普惠商品</text>
<text class="menu-arrow">></text>
</view>
<view
class="menu-item"
data-type="loan-applications"
bindtap="navigateToPage"
>
<text class="menu-text">业务申请进度</text>
<text class="menu-arrow">></text>
</view>
<view
class="menu-item"
data-type="loan-contracts"
bindtap="navigateToPage"
>
<text class="menu-text">业务合同</text>
<text class="menu-arrow">></text>
</view>
<view
class="menu-item"
data-type="loan-releases"
bindtap="navigateToPage"
>
<text class="menu-text">业务解押</text>
<text class="menu-arrow">></text>
</view>
</view>
</view>

View File

@@ -0,0 +1,42 @@
/* pages/business/business.wxss */
.business-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 100rpx;
}
/* 业务菜单列表 */
.menu-list {
background-color: #fff;
margin-top: 20rpx;
}
.menu-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
background-color: #fff;
transition: background-color 0.3s ease;
}
.menu-item:last-child {
border-bottom: none;
}
.menu-item:active {
background-color: #f8f8f8;
}
.menu-text {
font-size: 32rpx;
color: #333;
font-weight: 400;
}
.menu-arrow {
font-size: 28rpx;
color: #999;
font-weight: 300;
}

View File

@@ -0,0 +1,154 @@
// pages/business/loan-applications/loan-applications.js
const { apiService } = require('../../../services/apiService');
Page({
data: {
applications: [],
loading: false,
searchKeyword: '',
filterStatus: 'all',
typeMap: {
'personal': '个人贷款',
'mortgage': '住房贷款',
'business': '企业贷款',
'agricultural': '农业贷款'
},
statusMap: {
'pending': '待审核',
'processing': '审核中',
'approved': '已通过',
'rejected': '已拒绝'
}
},
onLoad() {
this.loadApplications();
},
onShow() {
this.loadApplications();
},
// 加载申请数据
async loadApplications() {
this.setData({ loading: true });
try {
const params = {
page: 1,
limit: 20,
search: this.data.searchKeyword,
status: this.data.filterStatus === 'all' ? '' : this.data.filterStatus
};
const response = await apiService.loanApplications.getList(params);
if (response.success) {
const applications = response.data.applications.map(application => ({
...application,
typeText: this.data.typeMap[application.type] || application.type,
statusText: this.data.statusMap[application.status] || application.status,
applicationTime: this.formatDate(application.applicationTime)
}));
this.setData({
applications,
loading: false
});
} else {
throw new Error(response.message || '获取申请列表失败');
}
} catch (error) {
console.error('加载申请失败:', error);
// 使用模拟数据作为降级处理
const mockApplications = [
{
id: 1,
applicationNumber: 'APP-202401180001',
applicantName: '张三',
type: 'personal',
typeText: '个人贷款',
status: 'pending',
statusText: '待审核',
amount: 200000,
term: 24,
interestRate: 6.5,
applicationTime: '2024-01-18 09:30',
phone: '13800138000',
purpose: '个人消费'
},
{
id: 2,
applicationNumber: 'APP-202401180002',
applicantName: '李四',
type: 'mortgage',
typeText: '住房贷款',
status: 'approved',
statusText: '已通过',
amount: 500000,
term: 240,
interestRate: 4.5,
applicationTime: '2024-01-18 10:15',
phone: '13800138001',
purpose: '购买住房'
}
];
this.setData({
applications: mockApplications,
loading: false
});
wx.showToast({
title: '使用模拟数据',
icon: 'none'
});
}
},
// 格式化日期
formatDate(dateString) {
if (!dateString) return '';
const date = new Date(dateString);
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
},
// 搜索输入
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
});
this.loadApplications();
},
// 筛选状态
onFilterChange(e) {
const status = e.detail.value;
this.setData({
filterStatus: status
});
this.loadApplications();
},
// 查看申请详情
viewApplicationDetail(e) {
const applicationId = e.currentTarget.dataset.applicationId;
wx.navigateTo({
url: `/pages/business/loan-applications/detail?applicationId=${applicationId}`
});
},
// 下拉刷新
onPullDownRefresh() {
this.loadApplications().finally(() => {
wx.stopPullDownRefresh();
});
}
});

View File

@@ -0,0 +1,101 @@
<!--pages/business/loan-applications/loan-applications.wxml-->
<view class="loan-applications-container">
<!-- 搜索栏 -->
<view class="search-bar">
<view class="search-input">
<text class="search-icon">🔍</text>
<input
placeholder="搜索申请人姓名或申请单号"
value="{{searchKeyword}}"
bindinput="onSearchInput"
/>
</view>
</view>
<!-- 筛选栏 -->
<view class="filter-bar">
<picker
bindchange="onFilterChange"
value="{{filterStatus}}"
range="{{['全部', '待审核', '审核中', '已通过', '已拒绝']}}"
range-key=""
>
<view class="filter-item">
<text>状态筛选</text>
<text class="filter-value">{{filterStatus === 'all' ? '全部' : filterStatus}}</text>
<text class="arrow">></text>
</view>
</picker>
</view>
<!-- 申请列表 -->
<view class="application-list">
<view
class="application-item"
wx:for="{{applications}}"
wx:key="id"
data-application-id="{{item.id}}"
bindtap="viewApplicationDetail"
>
<view class="application-header">
<text class="application-number">{{item.applicationNumber}}</text>
<view class="application-status {{item.status}}">
<text>{{item.statusText}}</text>
</view>
</view>
<view class="application-info">
<view class="info-row">
<text class="info-label">申请人:</text>
<text class="info-value">{{item.applicantName}}</text>
</view>
<view class="info-row">
<text class="info-label">申请类型:</text>
<text class="info-value">{{item.typeText}}</text>
</view>
<view class="info-row">
<text class="info-label">申请金额:</text>
<text class="info-value">{{item.amount}}元</text>
</view>
<view class="info-row">
<text class="info-label">申请期限:</text>
<text class="info-value">{{item.term}}个月</text>
</view>
<view class="info-row">
<text class="info-label">利率:</text>
<text class="info-value">{{item.interestRate}}%</text>
</view>
<view class="info-row">
<text class="info-label">申请时间:</text>
<text class="info-value">{{item.applicationTime}}</text>
</view>
<view class="info-row">
<text class="info-label">联系电话:</text>
<text class="info-value">{{item.phone}}</text>
</view>
<view class="info-row">
<text class="info-label">申请用途:</text>
<text class="info-value">{{item.purpose}}</text>
</view>
</view>
</view>
</view>
<!-- 加载状态 -->
<view class="loading" wx:if="{{loading}}">
<text>加载中...</text>
</view>
<!-- 空状态 -->
<view class="empty-state" wx:if="{{!loading && applications.length === 0}}">
<text class="empty-icon">📋</text>
<text class="empty-text">暂无申请数据</text>
</view>
</view>

View File

@@ -0,0 +1,157 @@
/* pages/business/loan-applications/loan-applications.wxss */
.loan-applications-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 100rpx;
}
/* 搜索栏 */
.search-bar {
padding: 20rpx 30rpx;
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.search-input {
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 25rpx;
padding: 0 20rpx;
height: 70rpx;
}
.search-icon {
font-size: 28rpx;
color: #999;
margin-right: 15rpx;
}
.search-input input {
flex: 1;
font-size: 28rpx;
color: #333;
}
/* 筛选栏 */
.filter-bar {
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.filter-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
font-size: 28rpx;
color: #333;
}
.filter-value {
color: #1890ff;
}
.arrow {
font-size: 24rpx;
color: #999;
}
/* 申请列表 */
.application-list {
padding: 20rpx 30rpx;
}
.application-item {
background-color: #fff;
margin-bottom: 20rpx;
padding: 30rpx;
border-radius: 8rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
}
.application-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.application-number {
font-size: 28rpx;
font-weight: 500;
color: #333;
flex: 1;
}
.application-status {
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
color: #fff;
}
.application-status.pending {
background-color: #faad14;
}
.application-status.processing {
background-color: #1890ff;
}
.application-status.approved {
background-color: #52c41a;
}
.application-status.rejected {
background-color: #f5222d;
}
.application-info {
display: flex;
flex-direction: column;
gap: 10rpx;
}
.info-row {
display: flex;
align-items: center;
font-size: 26rpx;
}
.info-label {
color: #666;
min-width: 140rpx;
}
.info-value {
color: #333;
flex: 1;
}
/* 加载状态 */
.loading {
text-align: center;
padding: 60rpx 0;
color: #999;
font-size: 28rpx;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}

View File

@@ -0,0 +1,148 @@
// pages/business/loan-contracts/loan-contracts.js
const { apiService } = require('../../../services/apiService');
Page({
data: {
contracts: [],
loading: false,
searchKeyword: '',
filterStatus: 'all',
typeMap: {
'personal': '个人贷款',
'mortgage': '住房贷款',
'business': '企业贷款',
'agricultural': '农业贷款'
},
statusMap: {
'active': '生效中',
'expired': '已到期',
'terminated': '已终止'
}
},
onLoad() {
this.loadContracts();
},
onShow() {
this.loadContracts();
},
// 加载合同数据
async loadContracts() {
this.setData({ loading: true });
try {
const params = {
page: 1,
limit: 20,
search: this.data.searchKeyword,
status: this.data.filterStatus === 'all' ? '' : this.data.filterStatus
};
const response = await apiService.loanContracts.getList(params);
if (response.success) {
const contracts = response.data.contracts.map(contract => ({
...contract,
typeText: this.data.typeMap[contract.type] || contract.type,
statusText: this.data.statusMap[contract.status] || contract.status,
signDate: this.formatDate(contract.signDate),
expiryDate: this.formatDate(contract.expiryDate)
}));
this.setData({
contracts,
loading: false
});
} else {
throw new Error(response.message || '获取合同列表失败');
}
} catch (error) {
console.error('加载合同失败:', error);
// 使用模拟数据作为降级处理
const mockContracts = [
{
id: 1,
contractNumber: 'CONTRACT-202401180001',
customerName: '张三',
type: 'personal',
typeText: '个人贷款',
status: 'active',
statusText: '生效中',
amount: 200000,
term: 24,
interestRate: 6.5,
signDate: '2024-01-18',
expiryDate: '2026-01-18',
phone: '13800138000'
},
{
id: 2,
contractNumber: 'CONTRACT-202401180002',
customerName: '李四',
type: 'mortgage',
typeText: '住房贷款',
status: 'active',
statusText: '生效中',
amount: 500000,
term: 240,
interestRate: 4.5,
signDate: '2024-01-18',
expiryDate: '2044-01-18',
phone: '13800138001'
}
];
this.setData({
contracts: mockContracts,
loading: false
});
wx.showToast({
title: '使用模拟数据',
icon: 'none'
});
}
},
// 格式化日期
formatDate(dateString) {
if (!dateString) return '';
const date = new Date(dateString);
return date.toLocaleDateString('zh-CN');
},
// 搜索输入
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
});
this.loadContracts();
},
// 筛选状态
onFilterChange(e) {
const status = e.detail.value;
this.setData({
filterStatus: status
});
this.loadContracts();
},
// 查看合同详情
viewContractDetail(e) {
const contractId = e.currentTarget.dataset.contractId;
wx.navigateTo({
url: `/pages/business/loan-contracts/detail?contractId=${contractId}`
});
},
// 下拉刷新
onPullDownRefresh() {
this.loadContracts().finally(() => {
wx.stopPullDownRefresh();
});
}
});

View File

@@ -0,0 +1,101 @@
<!--pages/business/loan-contracts/loan-contracts.wxml-->
<view class="loan-contracts-container">
<!-- 搜索栏 -->
<view class="search-bar">
<view class="search-input">
<text class="search-icon">🔍</text>
<input
placeholder="搜索合同编号或客户姓名"
value="{{searchKeyword}}"
bindinput="onSearchInput"
/>
</view>
</view>
<!-- 筛选栏 -->
<view class="filter-bar">
<picker
bindchange="onFilterChange"
value="{{filterStatus}}"
range="{{['全部', '生效中', '已到期', '已终止']}}"
range-key=""
>
<view class="filter-item">
<text>状态筛选</text>
<text class="filter-value">{{filterStatus === 'all' ? '全部' : filterStatus}}</text>
<text class="arrow">></text>
</view>
</picker>
</view>
<!-- 合同列表 -->
<view class="contract-list">
<view
class="contract-item"
wx:for="{{contracts}}"
wx:key="id"
data-contract-id="{{item.id}}"
bindtap="viewContractDetail"
>
<view class="contract-header">
<text class="contract-number">{{item.contractNumber}}</text>
<view class="contract-status {{item.status}}">
<text>{{item.statusText}}</text>
</view>
</view>
<view class="contract-info">
<view class="info-row">
<text class="info-label">客户姓名:</text>
<text class="info-value">{{item.customerName}}</text>
</view>
<view class="info-row">
<text class="info-label">合同类型:</text>
<text class="info-value">{{item.typeText}}</text>
</view>
<view class="info-row">
<text class="info-label">合同金额:</text>
<text class="info-value">{{item.amount}}元</text>
</view>
<view class="info-row">
<text class="info-label">合同期限:</text>
<text class="info-value">{{item.term}}个月</text>
</view>
<view class="info-row">
<text class="info-label">利率:</text>
<text class="info-value">{{item.interestRate}}%</text>
</view>
<view class="info-row">
<text class="info-label">签订时间:</text>
<text class="info-value">{{item.signDate}}</text>
</view>
<view class="info-row">
<text class="info-label">到期时间:</text>
<text class="info-value">{{item.expiryDate}}</text>
</view>
<view class="info-row">
<text class="info-label">联系电话:</text>
<text class="info-value">{{item.phone}}</text>
</view>
</view>
</view>
</view>
<!-- 加载状态 -->
<view class="loading" wx:if="{{loading}}">
<text>加载中...</text>
</view>
<!-- 空状态 -->
<view class="empty-state" wx:if="{{!loading && contracts.length === 0}}">
<text class="empty-icon">📄</text>
<text class="empty-text">暂无合同数据</text>
</view>
</view>

View File

@@ -0,0 +1,153 @@
/* pages/business/loan-contracts/loan-contracts.wxss */
.loan-contracts-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 100rpx;
}
/* 搜索栏 */
.search-bar {
padding: 20rpx 30rpx;
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.search-input {
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 25rpx;
padding: 0 20rpx;
height: 70rpx;
}
.search-icon {
font-size: 28rpx;
color: #999;
margin-right: 15rpx;
}
.search-input input {
flex: 1;
font-size: 28rpx;
color: #333;
}
/* 筛选栏 */
.filter-bar {
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.filter-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
font-size: 28rpx;
color: #333;
}
.filter-value {
color: #1890ff;
}
.arrow {
font-size: 24rpx;
color: #999;
}
/* 合同列表 */
.contract-list {
padding: 20rpx 30rpx;
}
.contract-item {
background-color: #fff;
margin-bottom: 20rpx;
padding: 30rpx;
border-radius: 8rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
}
.contract-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.contract-number {
font-size: 28rpx;
font-weight: 500;
color: #333;
flex: 1;
}
.contract-status {
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
color: #fff;
}
.contract-status.active {
background-color: #52c41a;
}
.contract-status.expired {
background-color: #faad14;
}
.contract-status.terminated {
background-color: #f5222d;
}
.contract-info {
display: flex;
flex-direction: column;
gap: 10rpx;
}
.info-row {
display: flex;
align-items: center;
font-size: 26rpx;
}
.info-label {
color: #666;
min-width: 140rpx;
}
.info-value {
color: #333;
flex: 1;
}
/* 加载状态 */
.loading {
text-align: center;
padding: 60rpx 0;
color: #999;
font-size: 28rpx;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}

View File

@@ -0,0 +1,136 @@
// pages/business/loan-products/loan-products.js
const { apiService } = require('../../../services/apiService');
Page({
data: {
products: [],
loading: false,
searchKeyword: '',
filterStatus: 'all',
typeMap: {
'personal': '个人贷款',
'mortgage': '住房贷款',
'business': '企业贷款',
'agricultural': '农业贷款'
}
},
onLoad() {
this.loadProducts();
},
onShow() {
this.loadProducts();
},
// 加载商品数据
async loadProducts() {
this.setData({ loading: true });
try {
const params = {
page: 1,
limit: 20,
search: this.data.searchKeyword,
status: this.data.filterStatus === 'all' ? '' : this.data.filterStatus
};
const response = await apiService.loanProducts.getList(params);
if (response.success) {
const products = response.data.products.map(product => ({
...product,
statusText: product.status === 'active' ? '在售' : '停售',
typeText: this.data.typeMap[product.type] || product.type
}));
this.setData({
products,
loading: false
});
} else {
throw new Error(response.message || '获取商品列表失败');
}
} catch (error) {
console.error('加载商品失败:', error);
// 使用模拟数据作为降级处理
const mockProducts = [
{
id: 1,
name: '个人住房贷款',
code: 'LOAN-001',
type: 'mortgage',
typeText: '住房贷款',
status: 'active',
statusText: '在售',
minAmount: 100000,
maxAmount: 5000000,
minTerm: 12,
maxTerm: 360,
interestRate: 4.5,
maxInterestRate: 6.5,
description: '专为个人购房提供的住房抵押贷款产品'
},
{
id: 2,
name: '个人消费贷款',
code: 'LOAN-002',
type: 'personal',
typeText: '个人贷款',
status: 'active',
statusText: '在售',
minAmount: 10000,
maxAmount: 500000,
minTerm: 6,
maxTerm: 60,
interestRate: 6.8,
maxInterestRate: 12.5,
description: '用于个人消费支出的信用贷款产品'
}
];
this.setData({
products: mockProducts,
loading: false
});
wx.showToast({
title: '使用模拟数据',
icon: 'none'
});
}
},
// 搜索输入
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
});
this.loadProducts();
},
// 筛选状态
onFilterChange(e) {
const status = e.detail.value;
this.setData({
filterStatus: status
});
this.loadProducts();
},
// 查看商品详情
viewProductDetail(e) {
const productId = e.currentTarget.dataset.productId;
wx.navigateTo({
url: `/pages/business/loan-products/detail?productId=${productId}`
});
},
// 下拉刷新
onPullDownRefresh() {
this.loadProducts().finally(() => {
wx.stopPullDownRefresh();
});
}
});

View File

@@ -0,0 +1,90 @@
<!--pages/business/loan-products/loan-products.wxml-->
<view class="loan-products-container">
<!-- 搜索栏 -->
<view class="search-bar">
<view class="search-input">
<text class="search-icon">🔍</text>
<input
placeholder="搜索商品名称"
value="{{searchKeyword}}"
bindinput="onSearchInput"
/>
</view>
</view>
<!-- 筛选栏 -->
<view class="filter-bar">
<picker
bindchange="onFilterChange"
value="{{filterStatus}}"
range="{{['全部', '在售', '停售']}}"
range-key=""
>
<view class="filter-item">
<text>状态筛选</text>
<text class="filter-value">{{filterStatus === 'all' ? '全部' : filterStatus}}</text>
<text class="arrow">></text>
</view>
</picker>
</view>
<!-- 商品列表 -->
<view class="product-list">
<view
class="product-item"
wx:for="{{products}}"
wx:key="id"
data-product-id="{{item.id}}"
bindtap="viewProductDetail"
>
<view class="product-header">
<text class="product-name">{{item.name}}</text>
<view class="product-status {{item.status}}">
<text>{{item.statusText}}</text>
</view>
</view>
<view class="product-info">
<view class="info-row">
<text class="info-label">产品代码:</text>
<text class="info-value">{{item.code}}</text>
</view>
<view class="info-row">
<text class="info-label">产品类型:</text>
<text class="info-value">{{item.typeText}}</text>
</view>
<view class="info-row">
<text class="info-label">贷款额度:</text>
<text class="info-value">{{item.minAmount}} - {{item.maxAmount}}元</text>
</view>
<view class="info-row">
<text class="info-label">贷款期限:</text>
<text class="info-value">{{item.minTerm}} - {{item.maxTerm}}个月</text>
</view>
<view class="info-row">
<text class="info-label">利率范围:</text>
<text class="info-value">{{item.interestRate}}% - {{item.maxInterestRate}}%</text>
</view>
</view>
<view class="product-desc">
<text class="desc-text">{{item.description}}</text>
</view>
</view>
</view>
<!-- 加载状态 -->
<view class="loading" wx:if="{{loading}}">
<text>加载中...</text>
</view>
<!-- 空状态 -->
<view class="empty-state" wx:if="{{!loading && products.length === 0}}">
<text class="empty-icon">📦</text>
<text class="empty-text">暂无商品数据</text>
</view>
</view>

View File

@@ -0,0 +1,159 @@
/* pages/business/loan-products/loan-products.wxss */
.loan-products-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 100rpx;
}
/* 搜索栏 */
.search-bar {
padding: 20rpx 30rpx;
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.search-input {
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 25rpx;
padding: 0 20rpx;
height: 70rpx;
}
.search-icon {
font-size: 28rpx;
color: #999;
margin-right: 15rpx;
}
.search-input input {
flex: 1;
font-size: 28rpx;
color: #333;
}
/* 筛选栏 */
.filter-bar {
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.filter-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
font-size: 28rpx;
color: #333;
}
.filter-value {
color: #1890ff;
}
.arrow {
font-size: 24rpx;
color: #999;
}
/* 商品列表 */
.product-list {
padding: 20rpx 30rpx;
}
.product-item {
background-color: #fff;
margin-bottom: 20rpx;
padding: 30rpx;
border-radius: 8rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
}
.product-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.product-name {
font-size: 32rpx;
font-weight: 500;
color: #333;
flex: 1;
}
.product-status {
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
color: #fff;
}
.product-status.active {
background-color: #52c41a;
}
.product-status.inactive {
background-color: #faad14;
}
.product-info {
margin-bottom: 20rpx;
}
.info-row {
display: flex;
align-items: center;
margin-bottom: 10rpx;
font-size: 26rpx;
}
.info-label {
color: #666;
min-width: 140rpx;
}
.info-value {
color: #333;
flex: 1;
}
.product-desc {
padding-top: 20rpx;
border-top: 1rpx solid #f0f0f0;
}
.desc-text {
font-size: 24rpx;
color: #666;
line-height: 1.4;
}
/* 加载状态 */
.loading {
text-align: center;
padding: 60rpx 0;
color: #999;
font-size: 28rpx;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}

View File

@@ -0,0 +1,147 @@
// pages/business/loan-releases/loan-releases.js
const { apiService } = require('../../../services/apiService');
Page({
data: {
releases: [],
loading: false,
searchKeyword: '',
filterStatus: 'all',
statusMap: {
'pending': '待处理',
'processing': '处理中',
'approved': '已通过',
'rejected': '已拒绝',
'completed': '已完成',
'released': '已解押'
}
},
onLoad() {
this.loadReleases();
},
onShow() {
this.loadReleases();
},
// 加载解押申请数据
async loadReleases() {
this.setData({ loading: true });
try {
const params = {
page: 1,
limit: 20,
searchValue: this.data.searchKeyword,
status: this.data.filterStatus === 'all' ? '' : this.data.filterStatus
};
const response = await apiService.loanReleases.getList(params);
if (response.success) {
const releases = response.data.releases.map(release => ({
...release,
statusText: this.data.statusMap[release.status] || release.status,
applicationTime: this.formatDate(release.applicationTime)
}));
this.setData({
releases,
loading: false
});
} else {
throw new Error(response.message || '获取解押申请列表失败');
}
} catch (error) {
console.error('加载解押申请失败:', error);
// 使用模拟数据作为降级处理
const mockReleases = [
{
id: 1,
applicationNumber: '20240227145555918',
customerName: '刘超',
productName: '中国工商银行扎旗支行"畜禽活体抵押"',
assetType: '牛',
releaseQuantity: '10头',
releaseAmount: 10000,
status: 'released',
statusText: '已解押',
applicationTime: '2024-02-27 06:55',
applicantPhone: '13800138000',
processComment: '审核通过,同意解押'
},
{
id: 2,
applicationNumber: '20240227145555919',
customerName: '张三',
productName: '个人住房贷款',
assetType: '房产',
releaseQuantity: '1套',
releaseAmount: 50000,
status: 'pending',
statusText: '待处理',
applicationTime: '2024-02-27 10:30',
applicantPhone: '13800138001',
processComment: ''
}
];
this.setData({
releases: mockReleases,
loading: false
});
wx.showToast({
title: '使用模拟数据',
icon: 'none'
});
}
},
// 格式化日期
formatDate(dateString) {
if (!dateString) return '';
const date = new Date(dateString);
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
},
// 搜索输入
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
});
this.loadReleases();
},
// 筛选状态
onFilterChange(e) {
const status = e.detail.value;
this.setData({
filterStatus: status
});
this.loadReleases();
},
// 查看解押申请详情
viewReleaseDetail(e) {
const releaseId = e.currentTarget.dataset.releaseId;
wx.navigateTo({
url: `/pages/business/loan-releases/detail?releaseId=${releaseId}`
});
},
// 下拉刷新
onPullDownRefresh() {
this.loadReleases().finally(() => {
wx.stopPullDownRefresh();
});
}
});

View File

@@ -0,0 +1,101 @@
<!--pages/business/loan-releases/loan-releases.wxml-->
<view class="loan-releases-container">
<!-- 搜索栏 -->
<view class="search-bar">
<view class="search-input">
<text class="search-icon">🔍</text>
<input
placeholder="搜索申请单号或客户姓名"
value="{{searchKeyword}}"
bindinput="onSearchInput"
/>
</view>
</view>
<!-- 筛选栏 -->
<view class="filter-bar">
<picker
bindchange="onFilterChange"
value="{{filterStatus}}"
range="{{['全部', '待处理', '处理中', '已通过', '已拒绝', '已完成', '已解押']}}"
range-key=""
>
<view class="filter-item">
<text>状态筛选</text>
<text class="filter-value">{{filterStatus === 'all' ? '全部' : filterStatus}}</text>
<text class="arrow">></text>
</view>
</picker>
</view>
<!-- 解押申请列表 -->
<view class="release-list">
<view
class="release-item"
wx:for="{{releases}}"
wx:key="id"
data-release-id="{{item.id}}"
bindtap="viewReleaseDetail"
>
<view class="release-header">
<text class="release-number">{{item.applicationNumber}}</text>
<view class="release-status {{item.status}}">
<text>{{item.statusText}}</text>
</view>
</view>
<view class="release-info">
<view class="info-row">
<text class="info-label">客户姓名:</text>
<text class="info-value">{{item.customerName}}</text>
</view>
<view class="info-row">
<text class="info-label">产品名称:</text>
<text class="info-value">{{item.productName}}</text>
</view>
<view class="info-row">
<text class="info-label">抵押物类型:</text>
<text class="info-value">{{item.assetType}}</text>
</view>
<view class="info-row">
<text class="info-label">解押数量:</text>
<text class="info-value">{{item.releaseQuantity}}</text>
</view>
<view class="info-row">
<text class="info-label">解押金额:</text>
<text class="info-value">{{item.releaseAmount}}元</text>
</view>
<view class="info-row">
<text class="info-label">申请时间:</text>
<text class="info-value">{{item.applicationTime}}</text>
</view>
<view class="info-row">
<text class="info-label">联系电话:</text>
<text class="info-value">{{item.applicantPhone}}</text>
</view>
<view class="info-row" wx:if="{{item.processComment}}">
<text class="info-label">处理意见:</text>
<text class="info-value">{{item.processComment}}</text>
</view>
</view>
</view>
</view>
<!-- 加载状态 -->
<view class="loading" wx:if="{{loading}}">
<text>加载中...</text>
</view>
<!-- 空状态 -->
<view class="empty-state" wx:if="{{!loading && releases.length === 0}}">
<text class="empty-icon">🔓</text>
<text class="empty-text">暂无解押申请数据</text>
</view>
</view>

View File

@@ -0,0 +1,165 @@
/* pages/business/loan-releases/loan-releases.wxss */
.loan-releases-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 100rpx;
}
/* 搜索栏 */
.search-bar {
padding: 20rpx 30rpx;
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.search-input {
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 25rpx;
padding: 0 20rpx;
height: 70rpx;
}
.search-icon {
font-size: 28rpx;
color: #999;
margin-right: 15rpx;
}
.search-input input {
flex: 1;
font-size: 28rpx;
color: #333;
}
/* 筛选栏 */
.filter-bar {
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.filter-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
font-size: 28rpx;
color: #333;
}
.filter-value {
color: #1890ff;
}
.arrow {
font-size: 24rpx;
color: #999;
}
/* 解押申请列表 */
.release-list {
padding: 20rpx 30rpx;
}
.release-item {
background-color: #fff;
margin-bottom: 20rpx;
padding: 30rpx;
border-radius: 8rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
}
.release-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.release-number {
font-size: 28rpx;
font-weight: 500;
color: #333;
flex: 1;
}
.release-status {
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
color: #fff;
}
.release-status.pending {
background-color: #faad14;
}
.release-status.processing {
background-color: #1890ff;
}
.release-status.approved {
background-color: #52c41a;
}
.release-status.rejected {
background-color: #f5222d;
}
.release-status.completed {
background-color: #13c2c2;
}
.release-status.released {
background-color: #722ed1;
}
.release-info {
display: flex;
flex-direction: column;
gap: 10rpx;
}
.info-row {
display: flex;
align-items: center;
font-size: 26rpx;
}
.info-label {
color: #666;
min-width: 140rpx;
}
.info-value {
color: #333;
flex: 1;
}
/* 加载状态 */
.loading {
text-align: center;
padding: 60rpx 0;
color: #999;
font-size: 28rpx;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}

View File

@@ -1,69 +1,66 @@
// pages/customers/customers.js
const bankService = require('../../services/bankService.js')
Page({
/**
* 页面的初始数据
*/
data: {
searchKeyword: '',
loading: false,
customersList: []
},
onLoad() {
this.loadCustomersData()
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
this.loadCustomersData()
setTimeout(() => {
wx.stopPullDownRefresh()
}, 1000)
},
loadCustomersData() {
// 模拟数据
this.setData({
customersList: [
{
id: 1,
name: '张三',
phone: '13800138000',
email: 'zhangsan@example.com',
creditScore: 850,
totalAssets: '¥125,680.50',
status: 'active',
statusText: '活跃'
},
{
id: 2,
name: '李四',
phone: '13800138001',
email: 'lisi@example.com',
creditScore: 720,
totalAssets: '¥89,450.00',
status: 'active',
statusText: '活跃'
}
]
})
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
})
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
handleAdd() {
wx.showToast({
title: '新增功能待实现',
icon: 'none'
})
},
handleCustomerTap(e) {
const { customerId } = e.currentTarget.dataset
wx.navigateTo({
url: `/pages/customers/detail?id=${customerId}`
})
}
})
})

View File

@@ -1,64 +1,2 @@
<!--pages/customers/customers.wxml-->
<view class="customers-container">
<!-- 搜索栏 -->
<view class="search-section">
<view class="search-bar">
<input
value="{{searchKeyword}}"
type="text"
placeholder="搜索客户..."
class="search-input"
bindinput="onSearchInput"
/>
<view class="search-icon">🔍</view>
</view>
</view>
<!-- 客户列表 -->
<view class="customers-list">
<view class="list-header">
<view class="list-title">客户管理</view>
<view class="add-btn" bindtap="handleAdd">
<text class="add-text">新增</text>
<view class="add-icon">+</view>
</view>
</view>
<view wx:if="{{loading}}" class="loading">
<text class="loading-text">加载中...</text>
</view>
<view wx:elif="{{customersList.length === 0}}" class="empty">
<view class="empty-icon">👥</view>
<view class="empty-text">暂无客户记录</view>
</view>
<view wx:else class="list-content">
<view
wx:for="{{customersList}}"
wx:key="id"
class="customer-item"
data-customer-id="{{item.id}}"
bindtap="handleCustomerTap"
>
<view class="item-avatar">
<image src="/images/avatar.png" class="avatar-img" />
</view>
<view class="item-content">
<view class="item-header">
<view class="item-name">{{item.name}}</view>
<view class="item-status {{item.status}}">
{{item.statusText}}
</view>
</view>
<view class="item-info">
<view class="item-phone">{{item.phone}}</view>
<view class="item-email">{{item.email}}</view>
<view class="item-assets">总资产: {{item.totalAssets}}</view>
<view class="item-credit">信用评分: {{item.creditScore}}</view>
</view>
</view>
</view>
</view>
</view>
</view>
<text>pages/customers/customers.wxml</text>

View File

@@ -1,166 +0,0 @@
/* pages/customers/customers.wxss */
.customers-container {
min-height: 100vh;
background: #f6f6f6;
}
.search-section {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
}
.search-bar {
position: relative;
}
.search-input {
width: 100%;
height: 72rpx;
background: #f8f9fa;
border: none;
border-radius: 36rpx;
padding: 0 60rpx 0 30rpx;
font-size: 28rpx;
color: #333;
}
.search-icon {
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
color: #999;
}
.customers-list {
background: #fff;
margin: 0 20rpx;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.list-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.add-btn {
display: flex;
align-items: center;
padding: 16rpx 24rpx;
background: #1890ff;
border-radius: 24rpx;
color: #fff;
}
.add-text {
font-size: 24rpx;
margin-right: 8rpx;
}
.add-icon {
font-size: 24rpx;
font-weight: 600;
}
.loading,
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx;
color: #999;
}
.loading-text,
.empty-text {
font-size: 28rpx;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.list-content {
space-y: 0;
}
.customer-item {
display: flex;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
transition: background 0.3s;
}
.customer-item:last-child {
border-bottom: none;
}
.item-avatar {
margin-right: 20rpx;
}
.avatar-img {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
background: #f0f0f0;
}
.item-content {
flex: 1;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
}
.item-name {
font-size: 30rpx;
font-weight: 600;
color: #333;
}
.item-status {
font-size: 20rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-weight: 500;
}
.item-status.active {
background: #f6ffed;
color: #52c41a;
}
.item-info {
display: flex;
flex-direction: column;
space-y: 8rpx;
}
.item-phone,
.item-email,
.item-assets,
.item-credit {
font-size: 24rpx;
color: #666;
}

View File

@@ -1,87 +1,66 @@
// pages/dashboard/dashboard.js
const bankService = require('../../services/bankService.js')
Page({
/**
* 页面的初始数据
*/
data: {
overviewCards: [
{
key: 'totalAssets',
icon: '💰',
label: '总资产',
value: '¥1,234,567.89',
trend: 'up',
trendText: '+2.5%',
type: 'primary'
},
{
key: 'monthlyIncome',
icon: '📈',
label: '月收入',
value: '¥85,000.00',
trend: 'up',
trendText: '+5.2%',
type: 'success'
},
{
key: 'activeCustomers',
icon: '👥',
label: '活跃客户',
value: '1,234',
trend: 'up',
trendText: '+12',
type: 'warning'
},
{
key: 'riskLevel',
icon: '⚠️',
label: '风险等级',
value: '低',
trend: 'down',
trendText: '-0.5%',
type: 'danger'
}
],
chartData: {
labels: ['1月', '2月', '3月', '4月', '5月', '6月'],
income: [65000, 72000, 68000, 75000, 82000, 85000],
expense: [45000, 48000, 52000, 49000, 55000, 58000]
}
},
onLoad() {
this.loadDashboardData()
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
this.loadDashboardData()
setTimeout(() => {
wx.stopPullDownRefresh()
}, 1000)
},
async loadDashboardData() {
try {
const data = await bankService.getDashboardData()
if (data) {
this.updateOverviewCards(data)
}
} catch (error) {
console.error('加载仪表板数据失败:', error)
wx.showToast({
title: '加载数据失败',
icon: 'error'
})
}
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
updateOverviewCards(data) {
const overviewCards = this.data.overviewCards.map(card => {
const newValue = data[card.key] || card.value
return {
...card,
value: newValue
}
})
this.setData({ overviewCards })
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
})

View File

@@ -1,66 +1,2 @@
<!--pages/dashboard/dashboard.wxml-->
<view class="dashboard-container">
<!-- 数据概览卡片 -->
<view class="overview-cards">
<view
wx:for="{{overviewCards}}"
wx:key="key"
class="overview-card {{item.type}}"
>
<view class="card-icon">{{item.icon}}</view>
<view class="card-content">
<view class="card-value">{{item.value}}</view>
<view class="card-label">{{item.label}}</view>
<view class="card-trend {{item.trend}}">
{{item.trendText}}
</view>
</view>
</view>
</view>
<!-- 图表区域 -->
<view class="charts-section">
<view class="section-title">收入支出趋势</view>
<view class="chart-container">
<view class="chart-placeholder">
<view class="chart-icon">📊</view>
<view class="chart-text">图表数据加载中...</view>
</view>
</view>
</view>
<!-- 快速统计 -->
<view class="quick-stats">
<view class="section-title">快速统计</view>
<view class="stats-grid">
<view class="stat-item">
<view class="stat-icon">💳</view>
<view class="stat-info">
<view class="stat-value">1,234</view>
<view class="stat-label">今日交易</view>
</view>
</view>
<view class="stat-item">
<view class="stat-icon">👥</view>
<view class="stat-info">
<view class="stat-value">89</view>
<view class="stat-label">新增客户</view>
</view>
</view>
<view class="stat-item">
<view class="stat-icon">💰</view>
<view class="stat-info">
<view class="stat-value">¥2.5M</view>
<view class="stat-label">今日流水</view>
</view>
</view>
<view class="stat-item">
<view class="stat-icon">📈</view>
<view class="stat-info">
<view class="stat-value">+15%</view>
<view class="stat-label">增长率</view>
</view>
</view>
</view>
</view>
</view>
<text>pages/dashboard/dashboard.wxml</text>

View File

@@ -1,147 +0,0 @@
/* pages/dashboard/dashboard.wxss */
.dashboard-container {
min-height: 100vh;
background: #f6f6f6;
padding: 20rpx;
}
.overview-cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
margin-bottom: 30rpx;
}
.overview-card {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
display: flex;
align-items: center;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.overview-card.primary {
border-left: 8rpx solid #1890ff;
}
.overview-card.success {
border-left: 8rpx solid #52c41a;
}
.overview-card.warning {
border-left: 8rpx solid #faad14;
}
.overview-card.danger {
border-left: 8rpx solid #ff4d4f;
}
.card-icon {
font-size: 48rpx;
margin-right: 20rpx;
}
.card-content {
flex: 1;
}
.card-value {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 8rpx;
}
.card-label {
font-size: 24rpx;
color: #666;
margin-bottom: 8rpx;
}
.card-trend {
font-size: 20rpx;
font-weight: 500;
}
.card-trend.up {
color: #52c41a;
}
.card-trend.down {
color: #ff4d4f;
}
.charts-section,
.quick-stats {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 30rpx;
}
.chart-container {
height: 400rpx;
background: #f8f9fa;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
}
.chart-placeholder {
text-align: center;
}
.chart-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.chart-text {
font-size: 28rpx;
color: #999;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
}
.stat-item {
display: flex;
align-items: center;
padding: 20rpx;
background: #f8f9fa;
border-radius: 12rpx;
}
.stat-icon {
font-size: 32rpx;
margin-right: 16rpx;
}
.stat-info {
flex: 1;
}
.stat-value {
font-size: 28rpx;
font-weight: 600;
color: #333;
margin-bottom: 4rpx;
}
.stat-label {
font-size: 22rpx;
color: #666;
}

View File

@@ -1,88 +1,66 @@
// pages/profile/profile.js
const auth = require('../../utils/auth.js')
Page({
/**
* 页面的初始数据
*/
data: {
userInfo: {},
menuItems: [
{
key: 'settings',
title: '设置',
icon: '⚙️',
path: ''
},
{
key: 'about',
title: '关于',
icon: '',
path: ''
},
{
key: 'help',
title: '帮助',
icon: '❓',
path: ''
},
{
key: 'feedback',
title: '反馈',
icon: '💬',
path: ''
}
]
},
onLoad() {
this.loadUserInfo()
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
loadUserInfo() {
const userInfo = auth.getUser()
this.setData({
userInfo: userInfo || {}
})
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
handleMenuTap(e) {
const { key } = e.currentTarget.dataset
switch (key) {
case 'settings':
wx.showToast({
title: '设置功能待实现',
icon: 'none'
})
break
case 'about':
wx.showToast({
title: '关于功能待实现',
icon: 'none'
})
break
case 'help':
wx.showToast({
title: '帮助功能待实现',
icon: 'none'
})
break
case 'feedback':
wx.showToast({
title: '反馈功能待实现',
icon: 'none'
})
break
}
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
handleLogout() {
wx.showModal({
title: '确认退出',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
auth.logout()
}
}
})
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
})

View File

@@ -1,36 +1,2 @@
<!--pages/profile/profile.wxml-->
<view class="profile-container">
<!-- 用户信息区域 -->
<view class="user-section">
<view class="user-avatar">
<image src="/images/avatar.png" class="avatar-img" />
</view>
<view class="user-info">
<view class="username">{{userInfo.name || '银行管理员'}}</view>
<view class="user-role">{{userInfo.role || '客户经理'}}</view>
<view class="user-phone">{{userInfo.phone || '13800138000'}}</view>
</view>
</view>
<!-- 功能菜单 -->
<view class="menu-section">
<view
wx:for="{{menuItems}}"
wx:key="key"
class="menu-item"
data-key="{{item.key}}"
bindtap="handleMenuTap"
>
<view class="menu-icon">{{item.icon}}</view>
<view class="menu-title">{{item.title}}</view>
<view class="menu-arrow">></view>
</view>
</view>
<!-- 退出登录 -->
<view class="logout-section">
<button class="logout-btn" bindtap="handleLogout">
退出登录
</button>
</view>
</view>
<text>pages/profile/profile.wxml</text>

View File

@@ -1,103 +0,0 @@
/* pages/profile/profile.wxss */
.profile-container {
min-height: 100vh;
background: #f6f6f6;
}
.user-section {
background: #fff;
padding: 60rpx 30rpx;
margin-bottom: 20rpx;
display: flex;
align-items: center;
}
.user-avatar {
margin-right: 30rpx;
}
.avatar-img {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
background: #f0f0f0;
}
.user-info {
flex: 1;
}
.username {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-bottom: 12rpx;
}
.user-role {
font-size: 28rpx;
color: #666;
margin-bottom: 8rpx;
}
.user-phone {
font-size: 24rpx;
color: #999;
}
.menu-section {
background: #fff;
margin-bottom: 20rpx;
}
.menu-item {
display: flex;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
transition: background 0.3s;
}
.menu-item:last-child {
border-bottom: none;
}
.menu-item:active {
background: #f8f9fa;
}
.menu-icon {
font-size: 32rpx;
margin-right: 24rpx;
width: 40rpx;
text-align: center;
}
.menu-title {
flex: 1;
font-size: 30rpx;
color: #333;
}
.menu-arrow {
font-size: 24rpx;
color: #ccc;
}
.logout-section {
padding: 30rpx;
}
.logout-btn {
width: 100%;
height: 88rpx;
background: #ff4d4f;
color: #fff;
border: none;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
}

View File

@@ -0,0 +1,133 @@
// pages/projects/projects.js
const { apiService } = require('../../services/apiService');
Page({
data: {
projects: [],
activeTab: 'all',
loading: false,
statusMap: {
'supervision': '监管中',
'completed': '已结项',
'pending': '待监管'
}
},
onLoad() {
this.loadProjects();
},
onShow() {
this.loadProjects();
},
// 加载项目数据
async loadProjects() {
this.setData({ loading: true });
try {
const params = {
page: 1,
limit: 20,
status: this.data.activeTab === 'all' ? '' : this.data.activeTab
};
const response = await apiService.projects.getList(params);
if (response.success) {
const projects = response.data.projects.map(project => ({
...project,
statusText: this.data.statusMap[project.status] || project.status,
showDeviceInfo: project.status === 'completed' // 只有已结项的项目显示设备信息
}));
this.setData({
projects,
loading: false
});
} else {
throw new Error(response.message || '获取项目列表失败');
}
} catch (error) {
console.error('加载项目失败:', error);
// 使用模拟数据作为降级处理
const mockProjects = [
{
id: 1,
farmName: '大数据中心',
loanOfficer: '1',
status: 'completed',
statusText: '已结项',
supervisionObject: '牛',
supervisionQuantity: 10,
supervisionPeriod: '23天',
supervisionAmount: '10000.00',
startTime: '2024-02-21',
endTime: '2024-03-15',
earTag: 0,
collar: 0,
host: 0,
showDeviceInfo: true
},
{
id: 2,
farmName: '大数据中心',
loanOfficer: '1',
status: 'completed',
statusText: '已结项',
supervisionObject: '牛',
supervisionQuantity: 0,
supervisionPeriod: '0天',
supervisionAmount: '0.00',
startTime: '2024-02-26',
endTime: '2024-02-27',
earTag: 0,
collar: 0,
host: 0,
showDeviceInfo: false
}
];
this.setData({
projects: mockProjects,
loading: false
});
wx.showToast({
title: '使用模拟数据',
icon: 'none'
});
}
},
// 切换标签
onTabChange(e) {
const tab = e.currentTarget.dataset.tab;
this.setData({
activeTab: tab
});
this.loadProjects();
},
// 查看项目详情
viewProjectDetail(e) {
const projectId = e.currentTarget.dataset.projectId;
wx.navigateTo({
url: `/pages/projects/detail?projectId=${projectId}`
});
},
// 下拉刷新
onPullDownRefresh() {
this.loadProjects().finally(() => {
wx.stopPullDownRefresh();
});
},
// 上拉加载更多
onReachBottom() {
// 这里可以实现分页加载
console.log('加载更多项目');
}
});

View File

@@ -0,0 +1,3 @@
{
"usingComponents": {}
}

View File

@@ -0,0 +1,131 @@
<!--pages/projects/projects.wxml-->
<view class="projects-container">
<!-- 标题栏 -->
<view class="header">
<text class="title">项目清单</text>
</view>
<!-- 筛选标签 -->
<view class="filter-tabs">
<view
class="tab-item {{activeTab === 'all' ? 'active' : ''}}"
data-tab="all"
bindtap="onTabChange"
>
<text>全部</text>
</view>
<view
class="tab-item {{activeTab === 'supervision' ? 'active' : ''}}"
data-tab="supervision"
bindtap="onTabChange"
>
<text>监管中</text>
</view>
<view
class="tab-item {{activeTab === 'pending' ? 'active' : ''}}"
data-tab="pending"
bindtap="onTabChange"
>
<text>待监管</text>
</view>
<view
class="tab-item {{activeTab === 'completed' ? 'active' : ''}}"
data-tab="completed"
bindtap="onTabChange"
>
<text>已结项</text>
</view>
</view>
<!-- 项目列表 -->
<view class="project-list">
<view
class="project-item"
wx:for="{{projects}}"
wx:key="id"
data-project-id="{{item.id}}"
bindtap="viewProjectDetail"
>
<!-- 项目标题 -->
<view class="project-title">{{item.farmName}}</view>
<!-- 项目信息 -->
<view class="project-info">
<view class="info-row">
<text class="info-label">法人/负责人:</text>
<text class="info-value">{{item.loanOfficer || '1'}}</text>
</view>
<view class="info-row">
<text class="info-label">当前状态:</text>
<view class="status-tag {{item.status}}">
<text>{{item.statusText}}</text>
</view>
</view>
<view class="info-row">
<text class="info-label">监管对象:</text>
<text class="info-value">{{item.supervisionObject}}</text>
</view>
<view class="info-row">
<text class="info-label">监管数量:</text>
<text class="info-value">{{item.supervisionQuantity}}</text>
</view>
<view class="info-row">
<text class="info-label">监管周期:</text>
<text class="info-value">{{item.supervisionPeriod}}</text>
</view>
<view class="info-row">
<text class="info-label">监管金额:</text>
<text class="info-value">{{item.supervisionAmount}}元</text>
</view>
<view class="info-row">
<text class="info-label">起止时间:</text>
<text class="info-value">{{item.startTime}} 至 {{item.endTime}}</text>
</view>
<!-- 设备信息 -->
<view class="device-info" wx:if="{{item.showDeviceInfo}}">
<view class="device-row">
<text class="device-label">脚环:</text>
<text class="device-value">{{item.earTag || 0}}</text>
</view>
<view class="device-row">
<text class="device-label">耳标:</text>
<text class="device-value">{{item.earTag || 0}}</text>
</view>
<view class="device-row">
<text class="device-label">项圈:</text>
<text class="device-value">{{item.collar || 0}}</text>
</view>
<view class="device-row">
<text class="device-label">主机:</text>
<text class="device-value">{{item.host || 0}}</text>
</view>
<view class="device-row">
<text class="device-label">饲喂机:</text>
<text class="device-value">0</text>
</view>
</view>
</view>
</view>
</view>
<!-- 加载状态 -->
<view class="loading" wx:if="{{loading}}">
<text>加载中...</text>
</view>
<!-- 空状态 -->
<view class="empty-state" wx:if="{{!loading && projects.length === 0}}">
<text class="empty-icon">📋</text>
<text class="empty-text">暂无项目数据</text>
</view>
</view>

View File

@@ -0,0 +1,218 @@
/* pages/projects/projects.wxss */
.projects-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 100rpx;
}
/* 状态栏 */
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.time {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.status-icons {
display: flex;
align-items: center;
gap: 15rpx;
}
.icon, .signal, .wifi, .network, .signal-bars, .battery {
font-size: 24rpx;
color: #666;
}
/* 标题栏 */
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
background-color: #fff;
border-bottom: 1rpx solid #eee;
}
.title {
font-size: 36rpx;
font-weight: 600;
color: #333;
flex: 1;
text-align: center;
}
.header-icons {
display: flex;
align-items: center;
gap: 20rpx;
}
.header-icon {
font-size: 32rpx;
color: #666;
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
border-radius: 30rpx;
}
/* 筛选标签 */
.filter-tabs {
display: flex;
background-color: #fff;
border-bottom: 1rpx solid #eee;
padding: 0 30rpx;
}
.tab-item {
flex: 1;
text-align: center;
padding: 30rpx 0;
position: relative;
font-size: 28rpx;
color: #666;
}
.tab-item.active {
color: #1890ff;
font-weight: 500;
}
.tab-item.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 4rpx;
background-color: #1890ff;
border-radius: 2rpx;
}
/* 项目列表 */
.project-list {
padding: 20rpx 30rpx;
}
.project-item {
background-color: #fff;
margin-bottom: 20rpx;
padding: 30rpx;
border-radius: 8rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
}
.project-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
}
.project-info {
display: flex;
flex-direction: column;
gap: 15rpx;
}
.info-row {
display: flex;
align-items: center;
font-size: 26rpx;
}
.info-label {
color: #666;
min-width: 160rpx;
}
.info-value {
color: #333;
flex: 1;
}
.status-tag {
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
color: #fff;
}
.status-tag.supervision {
background-color: #1890ff;
}
.status-tag.completed {
background-color: #52c41a;
}
.status-tag.pending {
background-color: #faad14;
}
/* 设备信息 */
.device-info {
margin-top: 20rpx;
padding-top: 20rpx;
border-top: 1rpx solid #f0f0f0;
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
.device-row {
display: flex;
align-items: center;
font-size: 24rpx;
min-width: 120rpx;
}
.device-label {
color: #999;
margin-right: 8rpx;
}
.device-value {
color: #333;
font-weight: 500;
}
/* 加载状态 */
.loading {
text-align: center;
padding: 60rpx 0;
color: #999;
font-size: 28rpx;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}

View File

@@ -1,51 +1,66 @@
// pages/reports/reports.js
const bankService = require('../../services/bankService.js')
Page({
/**
* 页面的初始数据
*/
data: {
searchKeyword: '',
loading: false,
reportsList: []
},
onLoad() {
this.loadReportsData()
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
loadReportsData() {
// 模拟数据
this.setData({
reportsList: [
{
id: 1,
title: '月度财务报表',
type: 'monthly',
createTime: '2024-01-15 10:00',
status: 'completed',
statusText: '已完成'
},
{
id: 2,
title: '风险分析报告',
type: 'risk',
createTime: '2024-01-14 15:30',
status: 'pending',
statusText: '生成中'
}
]
})
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
})
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
handleReportTap(e) {
const { reportId } = e.currentTarget.dataset
wx.navigateTo({
url: `/pages/reports/detail?id=${reportId}`
})
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
})

View File

@@ -1,55 +1,2 @@
<!--pages/reports/reports.wxml-->
<view class="reports-container">
<!-- 搜索栏 -->
<view class="search-section">
<view class="search-bar">
<input
value="{{searchKeyword}}"
type="text"
placeholder="搜索报表..."
class="search-input"
bindinput="onSearchInput"
/>
<view class="search-icon">🔍</view>
</view>
</view>
<!-- 报表列表 -->
<view class="reports-list">
<view class="list-header">
<view class="list-title">报表分析</view>
</view>
<view wx:if="{{loading}}" class="loading">
<text class="loading-text">加载中...</text>
</view>
<view wx:elif="{{reportsList.length === 0}}" class="empty">
<view class="empty-icon">📈</view>
<view class="empty-text">暂无报表记录</view>
</view>
<view wx:else class="list-content">
<view
wx:for="{{reportsList}}"
wx:key="id"
class="report-item"
data-report-id="{{item.id}}"
bindtap="handleReportTap"
>
<view class="report-icon">
<text wx:if="{{item.type === 'monthly'}}">📊</text>
<text wx:elif="{{item.type === 'risk'}}">⚠️</text>
<text wx:else>📋</text>
</view>
<view class="report-content">
<view class="report-title">{{item.title}}</view>
<view class="report-time">{{item.createTime}}</view>
</view>
<view class="report-status {{item.status}}">
{{item.statusText}}
</view>
</view>
</view>
</view>
</view>
<text>pages/reports/reports.wxml</text>

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