diff --git a/bank-backend/FRONTEND_BACKEND_INTEGRATION_PLAN.md b/bank-backend/FRONTEND_BACKEND_INTEGRATION_PLAN.md deleted file mode 100644 index 780d49e..0000000 --- a/bank-backend/FRONTEND_BACKEND_INTEGRATION_PLAN.md +++ /dev/null @@ -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 (系统设置) diff --git a/bank-backend/PORT_UPDATE_SUMMARY.md b/bank-backend/PORT_UPDATE_SUMMARY.md deleted file mode 100644 index d246e95..0000000 --- a/bank-backend/PORT_UPDATE_SUMMARY.md +++ /dev/null @@ -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端口运行,所有相关服务都已正确配置。 diff --git a/bank-backend/README.md b/bank-backend/README.md deleted file mode 100644 index 5cecd8c..0000000 --- a/bank-backend/README.md +++ /dev/null @@ -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 -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 - -## 🙏 致谢 - -感谢所有为这个项目做出贡献的开发者和开源社区。 - ---- - -**注意**: 这是一个演示项目,请勿在生产环境中使用默认的密码和密钥。 \ No newline at end of file diff --git a/bank-backend/SUPERVISION_TASKS_INTEGRATION_SUMMARY.md b/bank-backend/SUPERVISION_TASKS_INTEGRATION_SUMMARY.md deleted file mode 100644 index 8f8ab18..0000000 --- a/bank-backend/SUPERVISION_TASKS_INTEGRATION_SUMMARY.md +++ /dev/null @@ -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服务 -- ✅ 功能丰富的前端界面 -- ✅ 真实的数据存储和查询 -- ✅ 完善的错误处理 -- ✅ 用户友好的交互体验 - -系统现在可以正常使用,支持监管任务的完整生命周期管理。 diff --git a/bank-backend/add-loan-release-test-data.js b/bank-backend/add-loan-release-test-data.js new file mode 100644 index 0000000..6bb498c --- /dev/null +++ b/bank-backend/add-loan-release-test-data.js @@ -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(); diff --git a/bank-backend/check-data.js b/bank-backend/check-data.js deleted file mode 100644 index 674a324..0000000 --- a/bank-backend/check-data.js +++ /dev/null @@ -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(); diff --git a/bank-backend/check-db-data.js b/bank-backend/check-db-data.js deleted file mode 100644 index 35d8e4f..0000000 --- a/bank-backend/check-db-data.js +++ /dev/null @@ -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(); diff --git a/bank-backend/check-loan-products.js b/bank-backend/check-loan-products.js deleted file mode 100644 index b8f722f..0000000 --- a/bank-backend/check-loan-products.js +++ /dev/null @@ -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(); diff --git a/bank-backend/config/swagger.js b/bank-backend/config/swagger.js index 528a4b7..e4fb80d 100644 --- a/bank-backend/config/swagger.js +++ b/bank-backend/config/swagger.js @@ -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: '报表管理相关接口' } ] }, diff --git a/bank-backend/controllers/loanApplicationController.js b/bank-backend/controllers/loanApplicationController.js index 0862680..efa6d80 100644 --- a/bank-backend/controllers/loanApplicationController.js +++ b/bank-backend/controllers/loanApplicationController.js @@ -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 }); } }; diff --git a/bank-backend/controllers/loanContractController.js b/bank-backend/controllers/loanContractController.js index 812817e..20a69dc 100644 --- a/bank-backend/controllers/loanContractController.js +++ b/bank-backend/controllers/loanContractController.js @@ -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 })); diff --git a/bank-backend/controllers/loanProductController.js b/bank-backend/controllers/loanProductController.js index fcb46bf..1d354cf 100644 --- a/bank-backend/controllers/loanProductController.js +++ b/bank-backend/controllers/loanProductController.js @@ -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 } } diff --git a/bank-backend/controllers/loanReleaseController.js b/bank-backend/controllers/loanReleaseController.js new file mode 100644 index 0000000..b7f58ce --- /dev/null +++ b/bank-backend/controllers/loanReleaseController.js @@ -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 +}; diff --git a/bank-backend/create-admin-user.js b/bank-backend/create-admin-user.js deleted file mode 100644 index 727e733..0000000 --- a/bank-backend/create-admin-user.js +++ /dev/null @@ -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) - }) diff --git a/bank-backend/create-audit-records-table.js b/bank-backend/create-audit-records-table.js deleted file mode 100644 index 3eee4c5..0000000 --- a/bank-backend/create-audit-records-table.js +++ /dev/null @@ -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(); diff --git a/bank-backend/debug-accounts.js b/bank-backend/debug-accounts.js deleted file mode 100644 index 0ebde59..0000000 --- a/bank-backend/debug-accounts.js +++ /dev/null @@ -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(); diff --git a/bank-backend/debug-auth-detailed.js b/bank-backend/debug-auth-detailed.js deleted file mode 100644 index 073b363..0000000 --- a/bank-backend/debug-auth-detailed.js +++ /dev/null @@ -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(); diff --git a/bank-backend/debug-login.js b/bank-backend/debug-login.js deleted file mode 100644 index 05597ee..0000000 --- a/bank-backend/debug-login.js +++ /dev/null @@ -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(); diff --git a/bank-backend/debug-this-password.js b/bank-backend/debug-this-password.js deleted file mode 100644 index 1cc99b2..0000000 --- a/bank-backend/debug-this-password.js +++ /dev/null @@ -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(); diff --git a/bank-backend/API_INTERFACE_DOCUMENTATION.md b/bank-backend/docs/API_INTERFACE_DOCUMENTATION.md similarity index 77% rename from bank-backend/API_INTERFACE_DOCUMENTATION.md rename to bank-backend/docs/API_INTERFACE_DOCUMENTATION.md index bd1df48..6795154 100644 --- a/bank-backend/API_INTERFACE_DOCUMENTATION.md +++ b/bank-backend/docs/API_INTERFACE_DOCUMENTATION.md @@ -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 --- -**文档版本**: v1.0.0 -**最后更新**: 2024-01-18 +**文档版本**: v1.1.0 +**最后更新**: 2025-09-26 **维护人员**: 开发团队 diff --git a/bank-backend/docs/API_OVERVIEW.md b/bank-backend/docs/API_OVERVIEW.md new file mode 100644 index 0000000..151f6ff --- /dev/null +++ b/bank-backend/docs/API_OVERVIEW.md @@ -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 +``` + +## 错误码说明 + +| 错误码 | 说明 | +|--------|------| +| 400 | 请求参数错误 | +| 401 | 未授权访问 | +| 403 | 禁止访问 | +| 404 | 资源不存在 | +| 409 | 资源冲突 | +| 422 | 数据验证失败 | +| 500 | 服务器内部错误 | + +## 限流说明 + +- 普通接口: 100次/分钟 +- 登录接口: 10次/分钟 +- 文件上传: 20次/分钟 +- 数据导出: 5次/分钟 + +--- + +**文档版本**: v1.1.0 +**最后更新**: 2025-09-26 +**维护人员**: 开发团队 diff --git a/bank-backend/docs/LOAN_RELEASE_API.md b/bank-backend/docs/LOAN_RELEASE_API.md new file mode 100644 index 0000000..224d2fc --- /dev/null +++ b/bank-backend/docs/LOAN_RELEASE_API.md @@ -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 + + { + "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 + + { + "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 + + { + "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 + + { + "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 + ``` + +- **响应数据**: + ```json + { + "success": true, + "message": "解押申请删除成功" + } + ``` + +## 8. 获取解押统计信息 + +- **接口地址**: `GET /api/loan-releases/stats` +- **功能描述**: 获取解押申请统计信息 +- **认证要求**: 需要JWT Token + +- **请求示例**: + ```bash + GET /api/loan-releases/stats + Authorization: Bearer + ``` + +- **响应数据**: + ```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 +**维护人员**: 开发团队 diff --git a/bank-backend/fix-password-final.js b/bank-backend/fix-password-final.js deleted file mode 100644 index ac96a6c..0000000 --- a/bank-backend/fix-password-final.js +++ /dev/null @@ -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(); diff --git a/bank-backend/fix-supervision-tasks.js b/bank-backend/fix-supervision-tasks.js deleted file mode 100644 index f00f4aa..0000000 --- a/bank-backend/fix-supervision-tasks.js +++ /dev/null @@ -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(); diff --git a/bank-backend/fix-test-data.js b/bank-backend/fix-test-data.js deleted file mode 100644 index d90f63f..0000000 --- a/bank-backend/fix-test-data.js +++ /dev/null @@ -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(); diff --git a/bank-backend/models/LoanRelease.js b/bank-backend/models/LoanRelease.js new file mode 100644 index 0000000..c8e99eb --- /dev/null +++ b/bank-backend/models/LoanRelease.js @@ -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; diff --git a/bank-backend/models/LoanReleaseHistory.js b/bank-backend/models/LoanReleaseHistory.js new file mode 100644 index 0000000..4d48ef7 --- /dev/null +++ b/bank-backend/models/LoanReleaseHistory.js @@ -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; diff --git a/bank-backend/models/User.js b/bank-backend/models/User.js index a7b2c68..6cf3ad8 100644 --- a/bank-backend/models/User.js +++ b/bank-backend/models/User.js @@ -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); } } diff --git a/bank-backend/models/index.js b/bank-backend/models/index.js index c0074f3..6c09c8c 100644 --- a/bank-backend/models/index.js +++ b/bank-backend/models/index.js @@ -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 }; \ No newline at end of file diff --git a/bank-backend/quick-test-projects.js b/bank-backend/quick-test-projects.js deleted file mode 100644 index 450bb84..0000000 --- a/bank-backend/quick-test-projects.js +++ /dev/null @@ -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(); - } -})(); - - diff --git a/bank-backend/routes/loanRelease.js b/bank-backend/routes/loanRelease.js new file mode 100644 index 0000000..c3eb859 --- /dev/null +++ b/bank-backend/routes/loanRelease.js @@ -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; diff --git a/bank-backend/server.js b/bank-backend/server.js index 1754e16..a5f157f 100644 --- a/bank-backend/server.js +++ b/bank-backend/server.js @@ -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')); // 根路径 diff --git a/bank-backend/test-actual-data.js b/bank-backend/test-actual-data.js deleted file mode 100644 index 8fffc87..0000000 --- a/bank-backend/test-actual-data.js +++ /dev/null @@ -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(); diff --git a/bank-backend/test-api-integration.js b/bank-backend/test-api-integration.js deleted file mode 100644 index 2893bc8..0000000 --- a/bank-backend/test-api-integration.js +++ /dev/null @@ -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); diff --git a/bank-backend/test-api.js b/bank-backend/test-api.js deleted file mode 100644 index 30aaac1..0000000 --- a/bank-backend/test-api.js +++ /dev/null @@ -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`); -}); diff --git a/bank-backend/test-auth.js b/bank-backend/test-auth.js deleted file mode 100644 index 6c576cb..0000000 --- a/bank-backend/test-auth.js +++ /dev/null @@ -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(); diff --git a/bank-backend/test-completed-supervisions-api.js b/bank-backend/test-completed-supervisions-api.js deleted file mode 100644 index 26b41cf..0000000 --- a/bank-backend/test-completed-supervisions-api.js +++ /dev/null @@ -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() diff --git a/bank-backend/test-completed-supervisions-simple.js b/bank-backend/test-completed-supervisions-simple.js deleted file mode 100644 index df227e2..0000000 --- a/bank-backend/test-completed-supervisions-simple.js +++ /dev/null @@ -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() diff --git a/bank-backend/test-create-loan-product.js b/bank-backend/test-create-loan-product.js deleted file mode 100644 index 3e69eb0..0000000 --- a/bank-backend/test-create-loan-product.js +++ /dev/null @@ -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(); diff --git a/bank-backend/test-create-project.js b/bank-backend/test-create-project.js deleted file mode 100644 index 5d5b9bc..0000000 --- a/bank-backend/test-create-project.js +++ /dev/null @@ -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(); diff --git a/bank-backend/test-database-storage.js b/bank-backend/test-database-storage.js deleted file mode 100644 index d34a0c2..0000000 --- a/bank-backend/test-database-storage.js +++ /dev/null @@ -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(); diff --git a/bank-backend/test-installation-tasks-api.js b/bank-backend/test-installation-tasks-api.js deleted file mode 100644 index 6161016..0000000 --- a/bank-backend/test-installation-tasks-api.js +++ /dev/null @@ -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() diff --git a/bank-backend/test-installation-tasks-direct.js b/bank-backend/test-installation-tasks-direct.js deleted file mode 100644 index 297293f..0000000 --- a/bank-backend/test-installation-tasks-direct.js +++ /dev/null @@ -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(); - } -})(); diff --git a/bank-backend/test-installation-tasks-simple.js b/bank-backend/test-installation-tasks-simple.js deleted file mode 100644 index 3169592..0000000 --- a/bank-backend/test-installation-tasks-simple.js +++ /dev/null @@ -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() diff --git a/bank-backend/test-loan-applications-api.js b/bank-backend/test-loan-applications-api.js deleted file mode 100644 index af7ffee..0000000 --- a/bank-backend/test-loan-applications-api.js +++ /dev/null @@ -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(); diff --git a/bank-backend/test-loan-products-api.js b/bank-backend/test-loan-products-api.js deleted file mode 100644 index 0bfcf5c..0000000 --- a/bank-backend/test-loan-products-api.js +++ /dev/null @@ -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; diff --git a/bank-backend/test-loan-products-simple.js b/bank-backend/test-loan-products-simple.js deleted file mode 100644 index 7ae3f12..0000000 --- a/bank-backend/test-loan-products-simple.js +++ /dev/null @@ -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(); diff --git a/bank-backend/test-project-direct.js b/bank-backend/test-project-direct.js deleted file mode 100644 index c443537..0000000 --- a/bank-backend/test-project-direct.js +++ /dev/null @@ -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(); diff --git a/bank-backend/test-projects-api.js b/bank-backend/test-projects-api.js deleted file mode 100644 index b03f639..0000000 --- a/bank-backend/test-projects-api.js +++ /dev/null @@ -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); diff --git a/bank-backend/test-projects-simple.js b/bank-backend/test-projects-simple.js deleted file mode 100644 index 9a1c7c5..0000000 --- a/bank-backend/test-projects-simple.js +++ /dev/null @@ -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(); diff --git a/bank-backend/test-simple-update.js b/bank-backend/test-simple-update.js deleted file mode 100644 index 0fe29fd..0000000 --- a/bank-backend/test-simple-update.js +++ /dev/null @@ -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(); diff --git a/bank-backend/test-supervision-tasks-api.js b/bank-backend/test-supervision-tasks-api.js deleted file mode 100644 index 50e023c..0000000 --- a/bank-backend/test-supervision-tasks-api.js +++ /dev/null @@ -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(); diff --git a/bank-backend/test-supervision-tasks-simple.js b/bank-backend/test-supervision-tasks-simple.js deleted file mode 100644 index e22669f..0000000 --- a/bank-backend/test-supervision-tasks-simple.js +++ /dev/null @@ -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(); diff --git a/bank-backend/test-validpassword.js b/bank-backend/test-validpassword.js deleted file mode 100644 index 4c8c34f..0000000 --- a/bank-backend/test-validpassword.js +++ /dev/null @@ -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(); diff --git a/bank-frontend/API_INTEGRATION_COMPLETE.md b/bank-frontend/API_INTEGRATION_COMPLETE.md deleted file mode 100644 index bf0d9e9..0000000 --- a/bank-frontend/API_INTEGRATION_COMPLETE.md +++ /dev/null @@ -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集成已经完成,所有核心功能都能正常工作。系统具有良好的错误处理机制,即使在数据库连接失败的情况下也能提供模拟数据,确保前端功能正常运行。用户现在可以正常使用银行管理系统的所有功能,包括仪表盘数据展示、用户管理、账户管理等。 diff --git a/bank-frontend/src/router/routes.js b/bank-frontend/src/router/routes.js index c9b2c4c..b0d53aa 100644 --- a/bank-frontend/src/router/routes.js +++ b/bank-frontend/src/router/routes.js @@ -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', diff --git a/bank-frontend/src/utils/api.js b/bank-frontend/src/utils/api.js index 0885873..f6b5a80 100644 --- a/bank-frontend/src/utils/api.js +++ b/bank-frontend/src/utils/api.js @@ -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: { /** diff --git a/bank-frontend/src/views/Login.vue b/bank-frontend/src/views/Login.vue index ea007d5..c25cadc 100644 --- a/bank-frontend/src/views/Login.vue +++ b/bank-frontend/src/views/Login.vue @@ -52,7 +52,7 @@ diff --git a/bank-frontend/src/views/loan/LoanRelease.vue b/bank-frontend/src/views/loan/LoanRelease.vue index a4edb24..03d4af2 100644 --- a/bank-frontend/src/views/loan/LoanRelease.vue +++ b/bank-frontend/src/views/loan/LoanRelease.vue @@ -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() }) diff --git a/bank_mini_program/app.json b/bank_mini_program/app.json index 0905321..da30d58 100644 --- a/bank_mini_program/app.json +++ b/bank_mini_program/app.json @@ -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": "我的" } ] diff --git a/bank_mini_program/images/customers-active.png b/bank_mini_program/images/business-active.png similarity index 100% rename from bank_mini_program/images/customers-active.png rename to bank_mini_program/images/business-active.png diff --git a/bank_mini_program/images/customers.png b/bank_mini_program/images/business.png similarity index 100% rename from bank_mini_program/images/customers.png rename to bank_mini_program/images/business.png diff --git a/bank_mini_program/images/dashboard-active.png b/bank_mini_program/images/projects-active.png similarity index 100% rename from bank_mini_program/images/dashboard-active.png rename to bank_mini_program/images/projects-active.png diff --git a/bank_mini_program/images/dashboard.png b/bank_mini_program/images/projects.png similarity index 100% rename from bank_mini_program/images/dashboard.png rename to bank_mini_program/images/projects.png diff --git a/bank_mini_program/images/transactions-active.png b/bank_mini_program/images/warning-active.png similarity index 100% rename from bank_mini_program/images/transactions-active.png rename to bank_mini_program/images/warning-active.png diff --git a/bank_mini_program/images/transactions.png b/bank_mini_program/images/warning.png similarity index 100% rename from bank_mini_program/images/transactions.png rename to bank_mini_program/images/warning.png diff --git a/bank_mini_program/pages/assets/assets.js b/bank_mini_program/pages/assets/assets.js index 5060d41..530c396 100644 --- a/bank_mini_program/pages/assets/assets.js +++ b/bank_mini_program/pages/assets/assets.js @@ -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() { + } -}) +}) \ No newline at end of file diff --git a/bank_mini_program/pages/assets/assets.wxml b/bank_mini_program/pages/assets/assets.wxml index a305d76..faad8c3 100644 --- a/bank_mini_program/pages/assets/assets.wxml +++ b/bank_mini_program/pages/assets/assets.wxml @@ -1,52 +1,2 @@ - - - - - - 🔍 - - - - - - - 资产管理 - - - - 加载中... - - - - 💰 - 暂无资产记录 - - - - - - {{item.bankName}} - {{item.name}} - - {{item.cardNumber}} - - {{item.balance}} - 余额 - - - - - +pages/assets/assets.wxml \ No newline at end of file diff --git a/bank_mini_program/pages/assets/assets.wxss b/bank_mini_program/pages/assets/assets.wxss deleted file mode 100644 index 88a3ad3..0000000 --- a/bank_mini_program/pages/assets/assets.wxss +++ /dev/null @@ -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; -} diff --git a/bank_mini_program/pages/business/business.js b/bank_mini_program/pages/business/business.js new file mode 100644 index 0000000..0113b1c --- /dev/null +++ b/bank_mini_program/pages/business/business.js @@ -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(); + }); + } +}); \ No newline at end of file diff --git a/bank_mini_program/pages/business/business.json b/bank_mini_program/pages/business/business.json new file mode 100644 index 0000000..8835af0 --- /dev/null +++ b/bank_mini_program/pages/business/business.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/bank_mini_program/pages/business/business.wxml b/bank_mini_program/pages/business/business.wxml new file mode 100644 index 0000000..4978f47 --- /dev/null +++ b/bank_mini_program/pages/business/business.wxml @@ -0,0 +1,41 @@ + + + + + + 普惠商品 + > + + + + 业务申请进度 + > + + + + 业务合同 + > + + + + 业务解押 + > + + + \ No newline at end of file diff --git a/bank_mini_program/pages/business/business.wxss b/bank_mini_program/pages/business/business.wxss new file mode 100644 index 0000000..3e5811a --- /dev/null +++ b/bank_mini_program/pages/business/business.wxss @@ -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; +} \ No newline at end of file diff --git a/bank_mini_program/pages/business/loan-applications/loan-applications.js b/bank_mini_program/pages/business/loan-applications/loan-applications.js new file mode 100644 index 0000000..c873e25 --- /dev/null +++ b/bank_mini_program/pages/business/loan-applications/loan-applications.js @@ -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(); + }); + } +}); diff --git a/bank_mini_program/pages/business/loan-applications/loan-applications.wxml b/bank_mini_program/pages/business/loan-applications/loan-applications.wxml new file mode 100644 index 0000000..9942246 --- /dev/null +++ b/bank_mini_program/pages/business/loan-applications/loan-applications.wxml @@ -0,0 +1,101 @@ + + + + + + 🔍 + + + + + + + + + 状态筛选 + {{filterStatus === 'all' ? '全部' : filterStatus}} + > + + + + + + + + + {{item.applicationNumber}} + + {{item.statusText}} + + + + + + 申请人: + {{item.applicantName}} + + + + 申请类型: + {{item.typeText}} + + + + 申请金额: + {{item.amount}}元 + + + + 申请期限: + {{item.term}}个月 + + + + 利率: + {{item.interestRate}}% + + + + 申请时间: + {{item.applicationTime}} + + + + 联系电话: + {{item.phone}} + + + + 申请用途: + {{item.purpose}} + + + + + + + + 加载中... + + + + + 📋 + 暂无申请数据 + + diff --git a/bank_mini_program/pages/business/loan-applications/loan-applications.wxss b/bank_mini_program/pages/business/loan-applications/loan-applications.wxss new file mode 100644 index 0000000..e0627bf --- /dev/null +++ b/bank_mini_program/pages/business/loan-applications/loan-applications.wxss @@ -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; +} diff --git a/bank_mini_program/pages/business/loan-contracts/loan-contracts.js b/bank_mini_program/pages/business/loan-contracts/loan-contracts.js new file mode 100644 index 0000000..7d3a7da --- /dev/null +++ b/bank_mini_program/pages/business/loan-contracts/loan-contracts.js @@ -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(); + }); + } +}); diff --git a/bank_mini_program/pages/business/loan-contracts/loan-contracts.wxml b/bank_mini_program/pages/business/loan-contracts/loan-contracts.wxml new file mode 100644 index 0000000..703d5c3 --- /dev/null +++ b/bank_mini_program/pages/business/loan-contracts/loan-contracts.wxml @@ -0,0 +1,101 @@ + + + + + + 🔍 + + + + + + + + + 状态筛选 + {{filterStatus === 'all' ? '全部' : filterStatus}} + > + + + + + + + + + {{item.contractNumber}} + + {{item.statusText}} + + + + + + 客户姓名: + {{item.customerName}} + + + + 合同类型: + {{item.typeText}} + + + + 合同金额: + {{item.amount}}元 + + + + 合同期限: + {{item.term}}个月 + + + + 利率: + {{item.interestRate}}% + + + + 签订时间: + {{item.signDate}} + + + + 到期时间: + {{item.expiryDate}} + + + + 联系电话: + {{item.phone}} + + + + + + + + 加载中... + + + + + 📄 + 暂无合同数据 + + diff --git a/bank_mini_program/pages/business/loan-contracts/loan-contracts.wxss b/bank_mini_program/pages/business/loan-contracts/loan-contracts.wxss new file mode 100644 index 0000000..d5d6972 --- /dev/null +++ b/bank_mini_program/pages/business/loan-contracts/loan-contracts.wxss @@ -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; +} diff --git a/bank_mini_program/pages/business/loan-products/loan-products.js b/bank_mini_program/pages/business/loan-products/loan-products.js new file mode 100644 index 0000000..0a28828 --- /dev/null +++ b/bank_mini_program/pages/business/loan-products/loan-products.js @@ -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(); + }); + } +}); diff --git a/bank_mini_program/pages/business/loan-products/loan-products.wxml b/bank_mini_program/pages/business/loan-products/loan-products.wxml new file mode 100644 index 0000000..c2e1c1c --- /dev/null +++ b/bank_mini_program/pages/business/loan-products/loan-products.wxml @@ -0,0 +1,90 @@ + + + + + + 🔍 + + + + + + + + + 状态筛选 + {{filterStatus === 'all' ? '全部' : filterStatus}} + > + + + + + + + + + {{item.name}} + + {{item.statusText}} + + + + + + 产品代码: + {{item.code}} + + + + 产品类型: + {{item.typeText}} + + + + 贷款额度: + {{item.minAmount}} - {{item.maxAmount}}元 + + + + 贷款期限: + {{item.minTerm}} - {{item.maxTerm}}个月 + + + + 利率范围: + {{item.interestRate}}% - {{item.maxInterestRate}}% + + + + + {{item.description}} + + + + + + + 加载中... + + + + + 📦 + 暂无商品数据 + + diff --git a/bank_mini_program/pages/business/loan-products/loan-products.wxss b/bank_mini_program/pages/business/loan-products/loan-products.wxss new file mode 100644 index 0000000..39eac67 --- /dev/null +++ b/bank_mini_program/pages/business/loan-products/loan-products.wxss @@ -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; +} diff --git a/bank_mini_program/pages/business/loan-releases/loan-releases.js b/bank_mini_program/pages/business/loan-releases/loan-releases.js new file mode 100644 index 0000000..67b8932 --- /dev/null +++ b/bank_mini_program/pages/business/loan-releases/loan-releases.js @@ -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(); + }); + } +}); diff --git a/bank_mini_program/pages/business/loan-releases/loan-releases.wxml b/bank_mini_program/pages/business/loan-releases/loan-releases.wxml new file mode 100644 index 0000000..3aa872e --- /dev/null +++ b/bank_mini_program/pages/business/loan-releases/loan-releases.wxml @@ -0,0 +1,101 @@ + + + + + + 🔍 + + + + + + + + + 状态筛选 + {{filterStatus === 'all' ? '全部' : filterStatus}} + > + + + + + + + + + {{item.applicationNumber}} + + {{item.statusText}} + + + + + + 客户姓名: + {{item.customerName}} + + + + 产品名称: + {{item.productName}} + + + + 抵押物类型: + {{item.assetType}} + + + + 解押数量: + {{item.releaseQuantity}} + + + + 解押金额: + {{item.releaseAmount}}元 + + + + 申请时间: + {{item.applicationTime}} + + + + 联系电话: + {{item.applicantPhone}} + + + + 处理意见: + {{item.processComment}} + + + + + + + + 加载中... + + + + + 🔓 + 暂无解押申请数据 + + diff --git a/bank_mini_program/pages/business/loan-releases/loan-releases.wxss b/bank_mini_program/pages/business/loan-releases/loan-releases.wxss new file mode 100644 index 0000000..08d109e --- /dev/null +++ b/bank_mini_program/pages/business/loan-releases/loan-releases.wxss @@ -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; +} \ No newline at end of file diff --git a/bank_mini_program/pages/customers/customers.js b/bank_mini_program/pages/customers/customers.js index 48b643f..014d8ec 100644 --- a/bank_mini_program/pages/customers/customers.js +++ b/bank_mini_program/pages/customers/customers.js @@ -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}` - }) } -}) +}) \ No newline at end of file diff --git a/bank_mini_program/pages/customers/customers.wxml b/bank_mini_program/pages/customers/customers.wxml index 0aa6df1..f9a412a 100644 --- a/bank_mini_program/pages/customers/customers.wxml +++ b/bank_mini_program/pages/customers/customers.wxml @@ -1,64 +1,2 @@ - - - - - - 🔍 - - - - - - - 客户管理 - - 新增 - + - - - - - 加载中... - - - - 👥 - 暂无客户记录 - - - - - - - - - - {{item.name}} - - {{item.statusText}} - - - - {{item.phone}} - - 总资产: {{item.totalAssets}} - 信用评分: {{item.creditScore}} - - - - - - +pages/customers/customers.wxml \ No newline at end of file diff --git a/bank_mini_program/pages/customers/customers.wxss b/bank_mini_program/pages/customers/customers.wxss deleted file mode 100644 index a81d15d..0000000 --- a/bank_mini_program/pages/customers/customers.wxss +++ /dev/null @@ -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; -} diff --git a/bank_mini_program/pages/dashboard/dashboard.js b/bank_mini_program/pages/dashboard/dashboard.js index 19e1aa6..7a20886 100644 --- a/bank_mini_program/pages/dashboard/dashboard.js +++ b/bank_mini_program/pages/dashboard/dashboard.js @@ -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() { + } -}) +}) \ No newline at end of file diff --git a/bank_mini_program/pages/dashboard/dashboard.wxml b/bank_mini_program/pages/dashboard/dashboard.wxml index be38b34..57cefcb 100644 --- a/bank_mini_program/pages/dashboard/dashboard.wxml +++ b/bank_mini_program/pages/dashboard/dashboard.wxml @@ -1,66 +1,2 @@ - - - - - {{item.icon}} - - {{item.value}} - {{item.label}} - - {{item.trendText}} - - - - - - - - 收入支出趋势 - - - 📊 - 图表数据加载中... - - - - - - - 快速统计 - - - 💳 - - 1,234 - 今日交易 - - - - 👥 - - 89 - 新增客户 - - - - 💰 - - ¥2.5M - 今日流水 - - - - 📈 - - +15% - 增长率 - - - - - +pages/dashboard/dashboard.wxml \ No newline at end of file diff --git a/bank_mini_program/pages/dashboard/dashboard.wxss b/bank_mini_program/pages/dashboard/dashboard.wxss deleted file mode 100644 index a1fbea0..0000000 --- a/bank_mini_program/pages/dashboard/dashboard.wxss +++ /dev/null @@ -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; -} diff --git a/bank_mini_program/pages/profile/profile.js b/bank_mini_program/pages/profile/profile.js index 8b26f2d..5d22673 100644 --- a/bank_mini_program/pages/profile/profile.js +++ b/bank_mini_program/pages/profile/profile.js @@ -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() { + } -}) +}) \ No newline at end of file diff --git a/bank_mini_program/pages/profile/profile.wxml b/bank_mini_program/pages/profile/profile.wxml index e0ce124..64c021c 100644 --- a/bank_mini_program/pages/profile/profile.wxml +++ b/bank_mini_program/pages/profile/profile.wxml @@ -1,36 +1,2 @@ - - - - - - - - - - - - - {{item.icon}} - {{item.title}} - > - - - - - - - - +pages/profile/profile.wxml \ No newline at end of file diff --git a/bank_mini_program/pages/profile/profile.wxss b/bank_mini_program/pages/profile/profile.wxss deleted file mode 100644 index 85d159d..0000000 --- a/bank_mini_program/pages/profile/profile.wxss +++ /dev/null @@ -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; -} diff --git a/bank_mini_program/pages/projects/projects.js b/bank_mini_program/pages/projects/projects.js new file mode 100644 index 0000000..26102e9 --- /dev/null +++ b/bank_mini_program/pages/projects/projects.js @@ -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('加载更多项目'); + } +}); \ No newline at end of file diff --git a/bank_mini_program/pages/projects/projects.json b/bank_mini_program/pages/projects/projects.json new file mode 100644 index 0000000..8835af0 --- /dev/null +++ b/bank_mini_program/pages/projects/projects.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/bank_mini_program/pages/projects/projects.wxml b/bank_mini_program/pages/projects/projects.wxml new file mode 100644 index 0000000..ec0e5c6 --- /dev/null +++ b/bank_mini_program/pages/projects/projects.wxml @@ -0,0 +1,131 @@ + + + + + + + 项目清单 + + + + + + + 全部 + + + 监管中 + + + 待监管 + + + 已结项 + + + + + + + + {{item.farmName}} + + + + + 法人/负责人: + {{item.loanOfficer || '1'}} + + + + 当前状态: + + {{item.statusText}} + + + + + 监管对象: + {{item.supervisionObject}} + + + + 监管数量: + {{item.supervisionQuantity}} + + + + 监管周期: + {{item.supervisionPeriod}} + + + + 监管金额: + {{item.supervisionAmount}}元 + + + + 起止时间: + {{item.startTime}} 至 {{item.endTime}} + + + + + + 脚环: + {{item.earTag || 0}} + + + 耳标: + {{item.earTag || 0}} + + + 项圈: + {{item.collar || 0}} + + + 主机: + {{item.host || 0}} + + + 饲喂机: + 0 + + + + + + + + + 加载中... + + + + + 📋 + 暂无项目数据 + + \ No newline at end of file diff --git a/bank_mini_program/pages/projects/projects.wxss b/bank_mini_program/pages/projects/projects.wxss new file mode 100644 index 0000000..effcaf5 --- /dev/null +++ b/bank_mini_program/pages/projects/projects.wxss @@ -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; +} \ No newline at end of file diff --git a/bank_mini_program/pages/reports/reports.js b/bank_mini_program/pages/reports/reports.js index b6aa17d..0818fe7 100644 --- a/bank_mini_program/pages/reports/reports.js +++ b/bank_mini_program/pages/reports/reports.js @@ -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() { + } -}) +}) \ No newline at end of file diff --git a/bank_mini_program/pages/reports/reports.wxml b/bank_mini_program/pages/reports/reports.wxml index d1220a3..a643967 100644 --- a/bank_mini_program/pages/reports/reports.wxml +++ b/bank_mini_program/pages/reports/reports.wxml @@ -1,55 +1,2 @@ - - - - - - 🔍 - - - - - - - 报表分析 - - - - 加载中... - - - - 📈 - 暂无报表记录 - - - - - - 📊 - ⚠️ - 📋 - - - {{item.title}} - {{item.createTime}} - - - {{item.statusText}} - - - - - +pages/reports/reports.wxml \ No newline at end of file diff --git a/bank_mini_program/pages/reports/reports.wxss b/bank_mini_program/pages/reports/reports.wxss deleted file mode 100644 index 0b39502..0000000 --- a/bank_mini_program/pages/reports/reports.wxss +++ /dev/null @@ -1,137 +0,0 @@ -/* pages/reports/reports.wxss */ -.reports-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; -} - -.reports-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: 0; -} - -.report-item { - display: flex; - align-items: center; - padding: 30rpx; - border-bottom: 1rpx solid #f0f0f0; - transition: background 0.3s; -} - -.report-item:last-child { - border-bottom: none; -} - -.report-icon { - font-size: 32rpx; - margin-right: 20rpx; - width: 60rpx; - text-align: center; -} - -.report-content { - flex: 1; -} - -.report-title { - font-size: 28rpx; - color: #333; - margin-bottom: 8rpx; -} - -.report-time { - font-size: 20rpx; - color: #999; -} - -.report-status { - font-size: 20rpx; - padding: 8rpx 16rpx; - border-radius: 20rpx; - font-weight: 500; -} - -.report-status.completed { - background: #f6ffed; - color: #52c41a; -} - -.report-status.pending { - background: #fff7e6; - color: #faad14; -} - -.report-status.failed { - background: #fff2f0; - color: #ff4d4f; -} diff --git a/bank_mini_program/pages/risk/risk.js b/bank_mini_program/pages/risk/risk.js index 1f146c1..1ead0b7 100644 --- a/bank_mini_program/pages/risk/risk.js +++ b/bank_mini_program/pages/risk/risk.js @@ -1,51 +1,66 @@ // pages/risk/risk.js -const bankService = require('../../services/bankService.js') - Page({ + + /** + * 页面的初始数据 + */ data: { - searchKeyword: '', - loading: false, - riskData: { - overallRisk: '低', - riskScore: 85, - riskItems: [ - { - id: 1, - title: '信用风险', - level: '低', - score: 90, - description: '客户信用状况良好' - }, - { - id: 2, - title: '市场风险', - level: '中', - score: 75, - description: '市场波动影响较小' - }, - { - id: 3, - title: '操作风险', - level: '低', - score: 88, - description: '操作流程规范' - } - ] - } + }, - onLoad() { - this.loadRiskData() + /** + * 生命周期函数--监听页面加载 + */ + onLoad(options) { + }, - loadRiskData() { - // 模拟数据加载 - console.log('风险数据加载完成') + /** + * 生命周期函数--监听页面初次渲染完成 + */ + onReady() { + }, - onSearchInput(e) { - this.setData({ - searchKeyword: e.detail.value - }) + /** + * 生命周期函数--监听页面显示 + */ + onShow() { + + }, + + /** + * 生命周期函数--监听页面隐藏 + */ + onHide() { + + }, + + /** + * 生命周期函数--监听页面卸载 + */ + onUnload() { + + }, + + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefresh() { + + }, + + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom() { + + }, + + /** + * 用户点击右上角分享 + */ + onShareAppMessage() { + } -}) +}) \ No newline at end of file diff --git a/bank_mini_program/pages/risk/risk.wxml b/bank_mini_program/pages/risk/risk.wxml index b4c5821..ec79e32 100644 --- a/bank_mini_program/pages/risk/risk.wxml +++ b/bank_mini_program/pages/risk/risk.wxml @@ -1,36 +1,2 @@ - - - - - 整体风险等级 - - {{riskData.overallRisk}} - - 风险评分: {{riskData.riskScore}} - - - - - - 风险项目 - - - - {{item.title}} - - {{item.level}} - - - - 评分: {{item.score}} - {{item.description}} - - - - - +pages/risk/risk.wxml \ No newline at end of file diff --git a/bank_mini_program/pages/risk/risk.wxss b/bank_mini_program/pages/risk/risk.wxss deleted file mode 100644 index c7c34f4..0000000 --- a/bank_mini_program/pages/risk/risk.wxss +++ /dev/null @@ -1,127 +0,0 @@ -/* pages/risk/risk.wxss */ -.risk-container { - min-height: 100vh; - background: #f6f6f6; - padding: 20rpx; -} - -.risk-overview { - margin-bottom: 30rpx; -} - -.overview-card { - background: #fff; - border-radius: 16rpx; - padding: 40rpx; - text-align: center; - box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1); -} - -.overview-title { - font-size: 28rpx; - color: #666; - margin-bottom: 20rpx; -} - -.overview-level { - font-size: 48rpx; - font-weight: 700; - margin-bottom: 16rpx; -} - -.overview-level.low { - color: #52c41a; -} - -.overview-level.medium { - color: #faad14; -} - -.overview-level.high { - color: #ff4d4f; -} - -.overview-score { - font-size: 24rpx; - color: #999; -} - -.risk-items { - background: #fff; - border-radius: 16rpx; - padding: 30rpx; - box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1); -} - -.section-title { - font-size: 32rpx; - font-weight: 600; - color: #333; - margin-bottom: 30rpx; -} - -.risk-list { - space-y: 20rpx; -} - -.risk-item { - padding: 30rpx; - background: #f8f9fa; - border-radius: 12rpx; - margin-bottom: 20rpx; -} - -.risk-item:last-child { - margin-bottom: 0; -} - -.risk-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 16rpx; -} - -.risk-title { - font-size: 30rpx; - font-weight: 600; - color: #333; -} - -.risk-level { - font-size: 20rpx; - padding: 8rpx 16rpx; - border-radius: 20rpx; - font-weight: 500; -} - -.risk-level.low { - background: #f6ffed; - color: #52c41a; -} - -.risk-level.medium { - background: #fff7e6; - color: #faad14; -} - -.risk-level.high { - background: #fff2f0; - color: #ff4d4f; -} - -.risk-content { - display: flex; - flex-direction: column; - space-y: 8rpx; -} - -.risk-score { - font-size: 24rpx; - color: #666; -} - -.risk-desc { - font-size: 24rpx; - color: #999; -} diff --git a/bank_mini_program/pages/transactions/transactions.js b/bank_mini_program/pages/transactions/transactions.js index 433ffdf..c0fa186 100644 --- a/bank_mini_program/pages/transactions/transactions.js +++ b/bank_mini_program/pages/transactions/transactions.js @@ -1,53 +1,66 @@ // pages/transactions/transactions.js -const bankService = require('../../services/bankService.js') - Page({ + + /** + * 页面的初始数据 + */ data: { - searchKeyword: '', - loading: false, - transactionsList: [] + }, - onLoad() { - this.loadTransactionsData() + /** + * 生命周期函数--监听页面加载 + */ + onLoad(options) { + }, - loadTransactionsData() { - // 模拟数据 - this.setData({ - transactionsList: [ - { - id: 1, - type: 'income', - title: '工资收入', - amount: '+8,500.00', - time: '2024-01-15 09:00', - status: 'success', - statusText: '成功' - }, - { - id: 2, - type: 'expense', - title: '生活缴费', - amount: '-1,200.00', - time: '2024-01-14 14:30', - status: 'success', - statusText: '成功' - } - ] - }) + /** + * 生命周期函数--监听页面初次渲染完成 + */ + onReady() { + }, - onSearchInput(e) { - this.setData({ - searchKeyword: e.detail.value - }) + /** + * 生命周期函数--监听页面显示 + */ + onShow() { + }, - handleTransactionTap(e) { - const { transactionId } = e.currentTarget.dataset - wx.navigateTo({ - url: `/pages/transactions/detail?id=${transactionId}` - }) + /** + * 生命周期函数--监听页面隐藏 + */ + onHide() { + + }, + + /** + * 生命周期函数--监听页面卸载 + */ + onUnload() { + + }, + + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefresh() { + + }, + + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom() { + + }, + + /** + * 用户点击右上角分享 + */ + onShareAppMessage() { + } -}) +}) \ No newline at end of file diff --git a/bank_mini_program/pages/transactions/transactions.wxml b/bank_mini_program/pages/transactions/transactions.wxml index d6542a9..6932ebe 100644 --- a/bank_mini_program/pages/transactions/transactions.wxml +++ b/bank_mini_program/pages/transactions/transactions.wxml @@ -1,56 +1,2 @@ - - - - - - 🔍 - - - - - - - 交易记录 - - - - 加载中... - - - - 💳 - 暂无交易记录 - - - - - - 💰 - 💸 - 🔄 - - - {{item.title}} - {{item.time}} - - {{item.amount}} - - {{item.statusText}} - - - - - +pages/transactions/transactions.wxml \ No newline at end of file diff --git a/bank_mini_program/pages/transactions/transactions.wxss b/bank_mini_program/pages/transactions/transactions.wxss deleted file mode 100644 index 94eb03b..0000000 --- a/bank_mini_program/pages/transactions/transactions.wxss +++ /dev/null @@ -1,155 +0,0 @@ -/* pages/transactions/transactions.wxss */ -.transactions-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; -} - -.transactions-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: 0; -} - -.transaction-item { - display: flex; - align-items: center; - padding: 30rpx; - border-bottom: 1rpx solid #f0f0f0; - transition: background 0.3s; -} - -.transaction-item:last-child { - border-bottom: none; -} - -.transaction-icon { - font-size: 32rpx; - margin-right: 20rpx; - width: 60rpx; - text-align: center; -} - -.transaction-content { - flex: 1; -} - -.transaction-title { - font-size: 28rpx; - color: #333; - margin-bottom: 8rpx; -} - -.transaction-time { - font-size: 20rpx; - color: #999; -} - -.transaction-amount { - font-size: 28rpx; - font-weight: 600; - margin-right: 16rpx; -} - -.transaction-amount.income { - color: #52c41a; -} - -.transaction-amount.expense { - color: #ff4d4f; -} - -.transaction-amount.transfer { - color: #1890ff; -} - -.transaction-status { - font-size: 20rpx; - padding: 8rpx 16rpx; - border-radius: 20rpx; - font-weight: 500; -} - -.transaction-status.success { - background: #f6ffed; - color: #52c41a; -} - -.transaction-status.pending { - background: #fff7e6; - color: #faad14; -} - -.transaction-status.failed { - background: #fff2f0; - color: #ff4d4f; -} diff --git a/bank_mini_program/pages/warning/warning.js b/bank_mini_program/pages/warning/warning.js new file mode 100644 index 0000000..63da266 --- /dev/null +++ b/bank_mini_program/pages/warning/warning.js @@ -0,0 +1,72 @@ +// pages/warning/warning.js +Page({ + data: { + farms: [ + { + id: '13847540178', + name: '13847540178_养殖场', + warnings: { + notInventoried: 0, + abnormalSteps: 0, + collarCut: 0, + hostDisconnected: 0 + } + }, + { + id: '15848532959', + name: '15848532959_养殖场', + warnings: { + notInventoried: 0, + abnormalSteps: 0, + collarCut: 0, + hostDisconnected: 0 + } + }, + { + id: '15848525265', + name: '15848525265_养殖场', + warnings: { + notInventoried: 0, + abnormalSteps: 0, + collarCut: 0, + hostDisconnected: 0 + } + }, + { + id: '15849585844', + name: '15849585844_养殖场', + warnings: { + notInventoried: 0, + abnormalSteps: 0, + collarCut: 0, + hostDisconnected: 0 + } + } + ] + }, + + onLoad() { + console.log('日检预警页面加载'); + }, + + onShow() { + console.log('日检预警页面显示'); + }, + + // 查看今日预警详情 + viewTodayWarnings() { + wx.showToast({ + title: '今日预警详情', + icon: 'none' + }); + }, + + // 查看养殖场详情 + viewFarmDetail(e) { + const farmId = e.currentTarget.dataset.farmId; + wx.showToast({ + title: `查看养殖场 ${farmId}`, + icon: 'none' + }); + } +}); \ No newline at end of file diff --git a/bank_mini_program/pages/warning/warning.json b/bank_mini_program/pages/warning/warning.json new file mode 100644 index 0000000..8835af0 --- /dev/null +++ b/bank_mini_program/pages/warning/warning.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/bank_mini_program/pages/warning/warning.wxml b/bank_mini_program/pages/warning/warning.wxml new file mode 100644 index 0000000..4e9fc8f --- /dev/null +++ b/bank_mini_program/pages/warning/warning.wxml @@ -0,0 +1,52 @@ + + + + + + + 日常预警 + + + + + + + + 今日预警 + > + + + + + + {{item.name}} + + + {{item.warnings.notInventoried}} + 未盘点 + + + {{item.warnings.abnormalSteps}} + 步数异常 + + + {{item.warnings.collarCut}} + 项圈剪断 + + + {{item.warnings.hostDisconnected}} + 主机断网 + + + + + \ No newline at end of file diff --git a/bank_mini_program/pages/warning/warning.wxss b/bank_mini_program/pages/warning/warning.wxss new file mode 100644 index 0000000..074441e --- /dev/null +++ b/bank_mini_program/pages/warning/warning.wxss @@ -0,0 +1,183 @@ +/* pages/warning/warning.wxss */ +.warning-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: 20rpx; +} + +.icon, .signal, .battery { + font-size: 28rpx; + 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; +} + +/* 提示横幅 */ +.banner { + display: flex; + align-items: center; + margin: 20rpx 30rpx; + padding: 20rpx; + background-color: #fff3cd; + border-radius: 8rpx; + border-left: 6rpx solid #ffc107; +} + +.banner-icon { + font-size: 28rpx; + margin-right: 15rpx; +} + +.banner-text { + font-size: 28rpx; + color: #856404; +} + +/* 今日预警 */ +.today-warnings { + display: flex; + justify-content: space-between; + align-items: center; + padding: 30rpx; + background-color: #fff; + margin: 20rpx 30rpx 0; + border-radius: 8rpx; + box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1); +} + +.section-title { + font-size: 32rpx; + font-weight: 500; + color: #333; +} + +.arrow { + font-size: 32rpx; + color: #999; +} + +/* 养殖场列表 */ +.farm-list { + margin: 20rpx 30rpx; +} + +.farm-item { + background-color: #fff; + margin-bottom: 20rpx; + padding: 30rpx; + border-radius: 8rpx; + box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1); +} + +.farm-name { + font-size: 30rpx; + font-weight: 500; + color: #333; + margin-bottom: 20rpx; +} + +.warnings-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 15rpx; +} + +.warning-item { + display: flex; + flex-direction: column; + align-items: center; + padding: 20rpx 10rpx; + border-radius: 8rpx; + min-height: 120rpx; + justify-content: center; +} + +.warning-item.red { + background-color: #ffebee; + border: 2rpx solid #f44336; +} + +.warning-item.green { + background-color: #e8f5e8; + border: 2rpx solid #4caf50; +} + +.warning-item.blue { + background-color: #e3f2fd; + border: 2rpx solid #2196f3; +} + +.warning-item.orange { + background-color: #fff3e0; + border: 2rpx solid #ff9800; +} + +.warning-count { + font-size: 36rpx; + font-weight: 600; + color: #333; + margin-bottom: 8rpx; +} + +.warning-label { + font-size: 22rpx; + color: #666; + text-align: center; + line-height: 1.2; +} \ No newline at end of file diff --git a/bank_mini_program/services/apiService.js b/bank_mini_program/services/apiService.js new file mode 100644 index 0000000..dee54f3 --- /dev/null +++ b/bank_mini_program/services/apiService.js @@ -0,0 +1,313 @@ +// 银行端小程序API服务层 +const API_BASE_URL = 'https://ad.ningmuyun.com'; + +// 获取存储的token +const getToken = () => { + return wx.getStorageSync('bank_token') || ''; +}; + +// 设置token +const setToken = (token) => { + wx.setStorageSync('bank_token', token); +}; + +// 清除token +const clearToken = () => { + wx.removeStorageSync('bank_token'); + wx.removeStorageSync('bank_user'); +}; + +// 创建请求头 +const createHeaders = (headers = {}) => { + const token = getToken(); + const defaultHeaders = { + 'Content-Type': 'application/json', + }; + + if (token) { + defaultHeaders['Authorization'] = `Bearer ${token}`; + } + + return { ...defaultHeaders, ...headers }; +}; + +// 处理API响应 +const handleResponse = (response) => { + return new Promise((resolve, reject) => { + if (response.statusCode === 401) { + clearToken(); + wx.showToast({ + title: '登录已过期,请重新登录', + icon: 'none' + }); + reject(new Error('认证已过期')); + return; + } + + if (response.statusCode >= 400) { + wx.showToast({ + title: response.data?.message || '请求失败', + icon: 'none' + }); + reject(new Error(response.data?.message || '请求失败')); + return; + } + + if (response.data && !response.data.success) { + wx.showToast({ + title: response.data.message || '操作失败', + icon: 'none' + }); + reject(new Error(response.data.message || '操作失败')); + return; + } + + resolve(response.data); + }); +}; + +// 发起请求 +const request = (options) => { + return new Promise((resolve, reject) => { + wx.request({ + url: `${API_BASE_URL}${options.url}`, + method: options.method || 'GET', + data: options.data || {}, + header: createHeaders(options.headers), + success: (response) => { + handleResponse(response).then(resolve).catch(reject); + }, + fail: (error) => { + wx.showToast({ + title: '网络请求失败', + icon: 'none' + }); + reject(error); + } + }); + }); +}; + +// API服务 +const apiService = { + // 认证相关 + auth: { + // 登录 + login: (username, password) => { + return request({ + url: '/api/auth/login', + method: 'POST', + data: { username, password } + }); + }, + + // 获取当前用户信息 + getCurrentUser: () => { + return request({ + url: '/api/auth/me', + method: 'GET' + }); + }, + + // 登出 + logout: () => { + return request({ + url: '/api/auth/logout', + method: 'POST' + }); + } + }, + + // 项目相关 + projects: { + // 获取项目列表 + getList: (params = {}) => { + return request({ + url: '/api/projects', + method: 'GET', + data: params + }); + }, + + // 获取项目详情 + getById: (id) => { + return request({ + url: `/api/projects/${id}`, + method: 'GET' + }); + }, + + // 创建项目 + create: (data) => { + return request({ + url: '/api/projects', + method: 'POST', + data + }); + }, + + // 更新项目 + update: (id, data) => { + return request({ + url: `/api/projects/${id}`, + method: 'PUT', + data + }); + }, + + // 删除项目 + delete: (id) => { + return request({ + url: `/api/projects/${id}`, + method: 'DELETE' + }); + }, + + // 获取项目统计 + getStats: () => { + return request({ + url: '/api/projects/stats', + method: 'GET' + }); + } + }, + + // 监管任务相关 + supervisionTasks: { + // 获取监管任务列表 + getList: (params = {}) => { + return request({ + url: '/api/supervision-tasks', + method: 'GET', + data: params + }); + }, + + // 获取监管任务详情 + getById: (id) => { + return request({ + url: `/api/supervision-tasks/${id}`, + method: 'GET' + }); + } + }, + + // 贷款商品相关 + loanProducts: { + // 获取贷款商品列表 + getList: (params = {}) => { + return request({ + url: '/api/loan-products', + method: 'GET', + data: params + }); + }, + + // 获取贷款商品详情 + getById: (id) => { + return request({ + url: `/api/loan-products/${id}`, + method: 'GET' + }); + }, + + // 获取贷款商品统计 + getStats: () => { + return request({ + url: '/api/loan-products/stats', + method: 'GET' + }); + } + }, + + // 贷款申请相关 + loanApplications: { + // 获取贷款申请列表 + getList: (params = {}) => { + return request({ + url: '/api/loan-applications', + method: 'GET', + data: params + }); + }, + + // 获取贷款申请详情 + getById: (id) => { + return request({ + url: `/api/loan-applications/${id}`, + method: 'GET' + }); + }, + + // 获取申请统计 + getStats: () => { + return request({ + url: '/api/loan-applications/stats', + method: 'GET' + }); + } + }, + + // 贷款合同相关 + loanContracts: { + // 获取贷款合同列表 + getList: (params = {}) => { + return request({ + url: '/api/loan-contracts', + method: 'GET', + data: params + }); + }, + + // 获取贷款合同详情 + getById: (id) => { + return request({ + url: `/api/loan-contracts/${id}`, + method: 'GET' + }); + }, + + // 获取合同统计 + getStats: () => { + return request({ + url: '/api/loan-contracts/stats', + method: 'GET' + }); + } + }, + + // 贷款解押相关 + loanReleases: { + // 获取贷款解押列表 + getList: (params = {}) => { + return request({ + url: '/api/loan-releases', + method: 'GET', + data: params + }); + }, + + // 获取贷款解押详情 + getById: (id) => { + return request({ + url: `/api/loan-releases/${id}`, + method: 'GET' + }); + }, + + // 获取解押统计 + getStats: () => { + return request({ + url: '/api/loan-releases/stats', + method: 'GET' + }); + } + } +}; + +module.exports = { + apiService, + setToken, + clearToken, + getToken +}; diff --git a/bank_mini_program/src/App.vue b/bank_mini_program/src/App.vue deleted file mode 100644 index 04e432a..0000000 --- a/bank_mini_program/src/App.vue +++ /dev/null @@ -1,118 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/app.json b/bank_mini_program/src/app.json deleted file mode 100644 index 9e73050..0000000 --- a/bank_mini_program/src/app.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "pages": [ - "pages/index/index", - "pages/login/login" - ], - "window": { - "navigationBarTitleText": "银行小程序", - "navigationBarBackgroundColor": "#1976D2", - "navigationBarTextStyle": "white" - }, - "usingComponents": { - "van-button": "@vant/weapp/button/index" - } -} \ No newline at end of file diff --git a/bank_mini_program/src/components/AuthGuard.vue b/bank_mini_program/src/components/AuthGuard.vue deleted file mode 100644 index 10ba1b3..0000000 --- a/bank_mini_program/src/components/AuthGuard.vue +++ /dev/null @@ -1,290 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/components/common/ActionSheet.vue b/bank_mini_program/src/components/common/ActionSheet.vue deleted file mode 100644 index 9747925..0000000 --- a/bank_mini_program/src/components/common/ActionSheet.vue +++ /dev/null @@ -1,508 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/components/common/EmptyState.vue b/bank_mini_program/src/components/common/EmptyState.vue deleted file mode 100644 index 75abaf7..0000000 --- a/bank_mini_program/src/components/common/EmptyState.vue +++ /dev/null @@ -1,492 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/components/common/LoadingSpinner.vue b/bank_mini_program/src/components/common/LoadingSpinner.vue deleted file mode 100644 index 9239e2c..0000000 --- a/bank_mini_program/src/components/common/LoadingSpinner.vue +++ /dev/null @@ -1,381 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/components/common/StatusTag.vue b/bank_mini_program/src/components/common/StatusTag.vue deleted file mode 100644 index cc14f79..0000000 --- a/bank_mini_program/src/components/common/StatusTag.vue +++ /dev/null @@ -1,501 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/components/common/__tests__/StatusTag.spec.ts b/bank_mini_program/src/components/common/__tests__/StatusTag.spec.ts deleted file mode 100644 index 1333e9a..0000000 --- a/bank_mini_program/src/components/common/__tests__/StatusTag.spec.ts +++ /dev/null @@ -1,228 +0,0 @@ -import { describe, it, expect, beforeEach } from '@jest/globals' -import { mountComponent, expectElementExists, expectElementText } from '../../../tests/utils/test-utils' -import StatusTag from '../StatusTag.vue' - -describe('StatusTag.vue', () => { - let wrapper: any - - beforeEach(() => { - wrapper = null - }) - - afterEach(() => { - if (wrapper) { - wrapper.unmount() - } - }) - - it('renders with default props', () => { - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - text: '成功' - } - }) - - expectElementExists(wrapper, '.status-tag') - expectElementText(wrapper, '.status-tag__text', '成功') - expect(wrapper.classes()).toContain('status-tag--success') - }) - - it('renders different status types correctly', () => { - const statusTypes = [ - { status: 'success', class: 'status-tag--success' }, - { status: 'error', class: 'status-tag--error' }, - { status: 'warning', class: 'status-tag--warning' }, - { status: 'info', class: 'status-tag--info' }, - { status: 'pending', class: 'status-tag--pending' } - ] - - statusTypes.forEach(({ status, class: expectedClass }) => { - wrapper = mountComponent(StatusTag, { - props: { - status, - text: '测试' - } - }) - - expect(wrapper.classes()).toContain(expectedClass) - wrapper.unmount() - }) - }) - - it('renders different sizes correctly', () => { - const sizes = ['small', 'medium', 'large'] - - sizes.forEach(size => { - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - text: '测试', - size - } - }) - - expect(wrapper.classes()).toContain(`status-tag--${size}`) - wrapper.unmount() - }) - }) - - it('shows dot when showDot is true', () => { - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - text: '测试', - showDot: true - } - }) - - expectElementExists(wrapper, '.status-tag__dot') - }) - - it('shows icon when icon prop is provided', () => { - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - text: '测试', - icon: 'check' - } - }) - - expectElementExists(wrapper, '.status-tag__icon') - }) - - it('shows right icon when rightIcon prop is provided', () => { - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - text: '测试', - rightIcon: 'arrow-right' - } - }) - - expectElementExists(wrapper, '.status-tag__right-icon') - }) - - it('applies custom color when color prop is provided', () => { - const customColor = '#ff0000' - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - text: '测试', - color: customColor - } - }) - - const element = wrapper.find('.status-tag') - expect(element.attributes('style')).toContain(`--status-color: ${customColor}`) - }) - - it('emits click event when clicked and clickable is true', async () => { - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - text: '测试', - clickable: true - } - }) - - await wrapper.trigger('click') - expect(wrapper.emitted('click')).toBeTruthy() - expect(wrapper.emitted('click')).toHaveLength(1) - }) - - it('does not emit click event when clickable is false', async () => { - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - text: '测试', - clickable: false - } - }) - - await wrapper.trigger('click') - expect(wrapper.emitted('click')).toBeFalsy() - }) - - it('applies disabled class when disabled is true', () => { - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - text: '测试', - disabled: true - } - }) - - expect(wrapper.classes()).toContain('status-tag--disabled') - }) - - it('applies round class when round is true', () => { - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - text: '测试', - round: true - } - }) - - expect(wrapper.classes()).toContain('status-tag--round') - }) - - it('applies plain class when plain is true', () => { - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - text: '测试', - plain: true - } - }) - - expect(wrapper.classes()).toContain('status-tag--plain') - }) - - it('computes status text correctly for predefined statuses', () => { - const statusTextMap = [ - { status: 'active', expectedText: '活跃' }, - { status: 'inactive', expectedText: '非活跃' }, - { status: 'online', expectedText: '在线' }, - { status: 'offline', expectedText: '离线' }, - { status: 'approved', expectedText: '已审批' }, - { status: 'rejected', expectedText: '已拒绝' }, - { status: 'processing', expectedText: '处理中' }, - { status: 'completed', expectedText: '已完成' }, - { status: 'cancelled', expectedText: '已取消' } - ] - - statusTextMap.forEach(({ status, expectedText }) => { - wrapper = mountComponent(StatusTag, { - props: { status } - }) - - expectElementText(wrapper, '.status-tag__text', expectedText) - wrapper.unmount() - }) - }) - - it('uses custom text when provided', () => { - const customText = '自定义状态' - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - text: customText - } - }) - - expectElementText(wrapper, '.status-tag__text', customText) - }) - - it('computes status icon correctly for predefined statuses', () => { - wrapper = mountComponent(StatusTag, { - props: { - status: 'success', - showIcon: true - } - }) - - expectElementExists(wrapper, '.status-tag__icon') - }) -}) \ No newline at end of file diff --git a/bank_mini_program/src/config/api.js b/bank_mini_program/src/config/api.js deleted file mode 100644 index b8bd801..0000000 --- a/bank_mini_program/src/config/api.js +++ /dev/null @@ -1,21 +0,0 @@ -// 银行后端API配置 -export default { - // 基础配置 - BASE_URL: 'http://localhost:5351', // 银行后端本地运行在5351端口 - TIMEOUT: 10000, - - // 账户相关接口 - ACCOUNT: { - CREATE: '/api/accounts', - LIST: '/api/accounts', - DETAIL: '/api/accounts/:id', - DEPOSIT: '/api/accounts/:id/deposit', - WITHDRAW: '/api/accounts/:id/withdraw' - }, - - // 用户相关接口 - USER: { - LOGIN: '/api/auth/login', - INFO: '/api/users/me' - } -} \ No newline at end of file diff --git a/bank_mini_program/src/main.js b/bank_mini_program/src/main.js deleted file mode 100644 index 9a16cc3..0000000 --- a/bank_mini_program/src/main.js +++ /dev/null @@ -1,14 +0,0 @@ -import { createSSRApp } from 'vue' -import { createPinia } from 'pinia' -import App from './App.vue' - -export function createApp() { - const app = createSSRApp(App) - const pinia = createPinia() - - app.use(pinia) - - return { - app - } -} \ No newline at end of file diff --git a/bank_mini_program/src/manifest.json b/bank_mini_program/src/manifest.json deleted file mode 100644 index 83f69a9..0000000 --- a/bank_mini_program/src/manifest.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "银行监管服务小程序", - "appid": "wx1b9c7cd2d0e0bfd3", - "description": "专业的银行监管服务平台,提供信贷管理、风险监控、客户服务等功能", - "versionName": "1.0.0", - "versionCode": "100", - "transformPx": false, - "app-plus": { - "usingComponents": true - }, - "h5": { - "title": "银行监管服务", - "router": { - "mode": "hash", - "base": "/" - } - }, - "mp-weixin": { - "appid": "wx1b9c7cd2d0e0bfd3", - "setting": { - "urlCheck": false, - "es6": true, - "minified": true, - "postcss": true - }, - "usingComponents": true, - "permission": { - "scope.userLocation": { - "desc": "获取位置信息用于抵押物地理位置监控" - }, - "scope.camera": { - "desc": "拍照功能用于现场核查和资产确认" - } - }, - "requiredBackgroundModes": ["location"], - "plugins": { - "WechatSI": { - "version": "0.3.3", - "provider": "wx069ba97219f66d99" - } - } - }, - "mp-alipay": { - "usingComponents": true - }, - "quickapp": {} -} \ No newline at end of file diff --git a/bank_mini_program/src/pages.json b/bank_mini_program/src/pages.json deleted file mode 100644 index 98d94a4..0000000 --- a/bank_mini_program/src/pages.json +++ /dev/null @@ -1,141 +0,0 @@ -{ - "pages": [ - { - "path": "pages/index/index", - "style": { - "navigationBarTitleText": "银行监管", - "enablePullDownRefresh": true, - "backgroundTextStyle": "dark" - } - }, - { - "path": "pages/login/login", - "style": { - "navigationBarTitleText": "登录", - "navigationStyle": "custom" - } - }, - { - "path": "pages/dashboard/dashboard", - "style": { - "navigationBarTitleText": "工作台", - "enablePullDownRefresh": true - } - }, - { - "path": "pages/customers/customers", - "style": { - "navigationBarTitleText": "客户管理", - "enablePullDownRefresh": true - } - }, - { - "path": "pages/customers/customer-detail", - "style": { - "navigationBarTitleText": "客户详情" - } - }, - { - "path": "pages/assets/assets", - "style": { - "navigationBarTitleText": "资产监管", - "enablePullDownRefresh": true - } - }, - { - "path": "pages/assets/asset-detail", - "style": { - "navigationBarTitleText": "资产详情" - } - }, - { - "path": "pages/transactions/transactions", - "style": { - "navigationBarTitleText": "交易管理", - "enablePullDownRefresh": true - } - }, - { - "path": "pages/transactions/transaction-detail", - "style": { - "navigationBarTitleText": "交易详情" - } - }, - { - "path": "pages/risk/risk", - "style": { - "navigationBarTitleText": "风险监控", - "enablePullDownRefresh": true - } - }, - { - "path": "pages/risk/risk-detail", - "style": { - "navigationBarTitleText": "风险详情" - } - }, - { - "path": "pages/profile/profile", - "style": { - "navigationBarTitleText": "个人中心", - "enablePullDownRefresh": true - } - } - ], - "globalStyle": { - "navigationBarTextStyle": "white", - "navigationBarTitleText": "银行监管", - "navigationBarBackgroundColor": "#2c5aa0", - "backgroundColor": "#f5f7fa", - "backgroundTextStyle": "dark", - "app-plus": { - "background": "#f5f7fa" - } - }, - "tabBar": { - "color": "#666666", - "selectedColor": "#2c5aa0", - "backgroundColor": "#ffffff", - "borderStyle": "white", - "height": "50px", - "fontSize": "12px", - "iconWidth": "24px", - "spacing": "3px", - "list": [ - { - "pagePath": "pages/index/index", - "text": "首页", - "iconPath": "static/images/tab-home.png", - "selectedIconPath": "static/images/tab-home-active.png" - }, - { - "pagePath": "pages/dashboard/dashboard", - "text": "工作台", - "iconPath": "static/images/tab-dashboard.png", - "selectedIconPath": "static/images/tab-dashboard-active.png" - }, - { - "pagePath": "pages/customers/customers", - "text": "客户", - "iconPath": "static/images/tab-customers.png", - "selectedIconPath": "static/images/tab-customers-active.png" - }, - { - "pagePath": "pages/risk/risk", - "text": "风控", - "iconPath": "static/images/tab-risk.png", - "selectedIconPath": "static/images/tab-risk-active.png" - }, - { - "pagePath": "pages/profile/profile", - "text": "我的", - "iconPath": "static/images/tab-profile.png", - "selectedIconPath": "static/images/tab-profile-active.png" - } - ] - }, - "condition": { - "current": 0, - "list": [] - } -} \ No newline at end of file diff --git a/bank_mini_program/src/pages/assets/assets.vue b/bank_mini_program/src/pages/assets/assets.vue deleted file mode 100644 index 167793f..0000000 --- a/bank_mini_program/src/pages/assets/assets.vue +++ /dev/null @@ -1,983 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/pages/assets/detail.vue b/bank_mini_program/src/pages/assets/detail.vue deleted file mode 100644 index 6b25868..0000000 --- a/bank_mini_program/src/pages/assets/detail.vue +++ /dev/null @@ -1,1061 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/pages/assets/monitor.vue b/bank_mini_program/src/pages/assets/monitor.vue deleted file mode 100644 index 4730127..0000000 --- a/bank_mini_program/src/pages/assets/monitor.vue +++ /dev/null @@ -1,1268 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/pages/customers/customers.vue b/bank_mini_program/src/pages/customers/customers.vue deleted file mode 100644 index f933d75..0000000 --- a/bank_mini_program/src/pages/customers/customers.vue +++ /dev/null @@ -1,866 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/pages/dashboard/dashboard.vue b/bank_mini_program/src/pages/dashboard/dashboard.vue deleted file mode 100644 index 2936b2a..0000000 --- a/bank_mini_program/src/pages/dashboard/dashboard.vue +++ /dev/null @@ -1,833 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/pages/index/index.vue b/bank_mini_program/src/pages/index/index.vue deleted file mode 100644 index 1de647d..0000000 --- a/bank_mini_program/src/pages/index/index.vue +++ /dev/null @@ -1,392 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/pages/login/login.vue b/bank_mini_program/src/pages/login/login.vue deleted file mode 100644 index 7e45bd4..0000000 --- a/bank_mini_program/src/pages/login/login.vue +++ /dev/null @@ -1,400 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/pages/reports/dashboard.vue b/bank_mini_program/src/pages/reports/dashboard.vue deleted file mode 100644 index 59a292d..0000000 --- a/bank_mini_program/src/pages/reports/dashboard.vue +++ /dev/null @@ -1,1590 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/pages/reports/reports.vue b/bank_mini_program/src/pages/reports/reports.vue deleted file mode 100644 index 081d800..0000000 --- a/bank_mini_program/src/pages/reports/reports.vue +++ /dev/null @@ -1,1285 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/pages/risk/risk.vue b/bank_mini_program/src/pages/risk/risk.vue deleted file mode 100644 index 5c1d01e..0000000 --- a/bank_mini_program/src/pages/risk/risk.vue +++ /dev/null @@ -1,1468 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/pages/transactions/detail.vue b/bank_mini_program/src/pages/transactions/detail.vue deleted file mode 100644 index 2607c98..0000000 --- a/bank_mini_program/src/pages/transactions/detail.vue +++ /dev/null @@ -1,1323 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/pages/transactions/transactions.vue b/bank_mini_program/src/pages/transactions/transactions.vue deleted file mode 100644 index d20f077..0000000 --- a/bank_mini_program/src/pages/transactions/transactions.vue +++ /dev/null @@ -1,1104 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/static/images/default-avatar.png b/bank_mini_program/src/static/images/default-avatar.png deleted file mode 100644 index f9e94ba..0000000 --- a/bank_mini_program/src/static/images/default-avatar.png +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/static/images/default-avatar.svg b/bank_mini_program/src/static/images/default-avatar.svg deleted file mode 100644 index 6c74a06..0000000 --- a/bank_mini_program/src/static/images/default-avatar.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/bank_mini_program/src/static/images/icon-assets.svg b/bank_mini_program/src/static/images/icon-assets.svg deleted file mode 100644 index 1951364..0000000 --- a/bank_mini_program/src/static/images/icon-assets.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/bank_mini_program/src/static/images/icon-customers.svg b/bank_mini_program/src/static/images/icon-customers.svg deleted file mode 100644 index ab52f4d..0000000 --- a/bank_mini_program/src/static/images/icon-customers.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/bank_mini_program/src/static/images/icon-risk.svg b/bank_mini_program/src/static/images/icon-risk.svg deleted file mode 100644 index 12f41ac..0000000 --- a/bank_mini_program/src/static/images/icon-risk.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/bank_mini_program/src/static/images/icon-transactions.svg b/bank_mini_program/src/static/images/icon-transactions.svg deleted file mode 100644 index 9b6724c..0000000 --- a/bank_mini_program/src/static/images/icon-transactions.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/bank_mini_program/src/store/app.js b/bank_mini_program/src/store/app.js deleted file mode 100644 index 37e16ec..0000000 --- a/bank_mini_program/src/store/app.js +++ /dev/null @@ -1,305 +0,0 @@ -import { defineStore } from 'pinia' - -export const useAppStore = defineStore('app', { - state: () => ({ - // 应用配置 - appConfig: { - version: '1.0.0', - name: '银行监管小程序', - theme: 'light' - }, - - // 系统信息 - systemInfo: null, - - // 网络状态 - networkStatus: 'unknown', - - // 加载状态 - loading: false, - - // 全局提示信息 - toast: { - show: false, - message: '', - type: 'info' // info, success, warning, error - }, - - // 页面栈 - pageStack: [], - - // 当前页面 - currentPage: '', - - // 全局数据缓存 - globalCache: new Map() - }), - - getters: { - // 是否为开发环境 - isDevelopment: () => process.env.NODE_ENV === 'development', - - // 获取应用版本 - appVersion: (state) => state.appConfig.version, - - // 获取应用名称 - appName: (state) => state.appConfig.name, - - // 是否在线 - isOnline: (state) => state.networkStatus !== 'none', - - // 获取缓存数据 - getCacheData: (state) => (key) => { - return state.globalCache.get(key) - } - }, - - actions: { - /** - * 初始化应用 - */ - async initApp() { - try { - // 获取系统信息 - await this.getSystemInfo() - - // 监听网络状态 - this.watchNetworkStatus() - - // 初始化应用配置 - await this.loadAppConfig() - - console.log('应用初始化完成') - } catch (error) { - console.error('应用初始化失败:', error) - } - }, - - /** - * 获取系统信息 - */ - async getSystemInfo() { - return new Promise((resolve) => { - uni.getSystemInfo({ - success: (res) => { - this.systemInfo = res - resolve(res) - }, - fail: (error) => { - console.error('获取系统信息失败:', error) - resolve(null) - } - }) - }) - }, - - /** - * 监听网络状态 - */ - watchNetworkStatus() { - // 获取当前网络状态 - uni.getNetworkType({ - success: (res) => { - this.networkStatus = res.networkType - } - }) - - // 监听网络状态变化 - uni.onNetworkStatusChange((res) => { - this.networkStatus = res.networkType - - if (!res.isConnected) { - this.showToast('网络连接已断开', 'warning') - } else { - this.showToast('网络连接已恢复', 'success') - } - }) - }, - - /** - * 加载应用配置 - */ - async loadAppConfig() { - try { - // 从本地存储加载配置 - const localConfig = uni.getStorageSync('appConfig') - if (localConfig) { - this.appConfig = { ...this.appConfig, ...localConfig } - } - - // 这里可以从服务器获取最新配置 - // const serverConfig = await get('/api/app/config') - // if (serverConfig.success) { - // this.appConfig = { ...this.appConfig, ...serverConfig.data } - // uni.setStorageSync('appConfig', this.appConfig) - // } - } catch (error) { - console.error('加载应用配置失败:', error) - } - }, - - /** - * 更新应用配置 - */ - updateAppConfig(config) { - this.appConfig = { ...this.appConfig, ...config } - uni.setStorageSync('appConfig', this.appConfig) - }, - - /** - * 设置加载状态 - */ - setLoading(loading) { - this.loading = loading - - if (loading) { - uni.showLoading({ - title: '加载中...', - mask: true - }) - } else { - uni.hideLoading() - } - }, - - /** - * 显示提示信息 - */ - showToast(message, type = 'info', duration = 2000) { - this.toast = { - show: true, - message, - type - } - - // 使用uni-app的提示 - const iconMap = { - success: 'success', - error: 'error', - warning: 'none', - info: 'none' - } - - uni.showToast({ - title: message, - icon: iconMap[type] || 'none', - duration, - mask: false - }) - - // 自动隐藏 - setTimeout(() => { - this.toast.show = false - }, duration) - }, - - /** - * 隐藏提示信息 - */ - hideToast() { - this.toast.show = false - uni.hideToast() - }, - - /** - * 更新页面栈 - */ - updatePageStack() { - const pages = getCurrentPages() - this.pageStack = pages.map(page => page.route) - this.currentPage = pages[pages.length - 1]?.route || '' - }, - - /** - * 设置缓存数据 - */ - setCacheData(key, data, expireTime = null) { - const cacheItem = { - data, - timestamp: Date.now(), - expireTime - } - this.globalCache.set(key, cacheItem) - }, - - /** - * 获取缓存数据 - */ - getCacheData(key) { - const cacheItem = this.globalCache.get(key) - - if (!cacheItem) { - return null - } - - // 检查是否过期 - if (cacheItem.expireTime && Date.now() > cacheItem.expireTime) { - this.globalCache.delete(key) - return null - } - - return cacheItem.data - }, - - /** - * 清除缓存数据 - */ - clearCacheData(key) { - if (key) { - this.globalCache.delete(key) - } else { - this.globalCache.clear() - } - }, - - /** - * 检查应用更新 - */ - async checkAppUpdate() { - // #ifdef MP-WEIXIN - if (uni.canIUse('getUpdateManager')) { - const updateManager = uni.getUpdateManager() - - updateManager.onCheckForUpdate((res) => { - if (res.hasUpdate) { - this.showToast('发现新版本,正在下载...', 'info') - } - }) - - updateManager.onUpdateReady(() => { - uni.showModal({ - title: '更新提示', - content: '新版本已准备好,是否重启应用?', - success: (res) => { - if (res.confirm) { - updateManager.applyUpdate() - } - } - }) - }) - - updateManager.onUpdateFailed(() => { - this.showToast('新版本下载失败', 'error') - }) - } - // #endif - }, - - /** - * 获取设备信息 - */ - getDeviceInfo() { - return { - platform: this.systemInfo?.platform || 'unknown', - system: this.systemInfo?.system || 'unknown', - version: this.systemInfo?.version || 'unknown', - model: this.systemInfo?.model || 'unknown', - brand: this.systemInfo?.brand || 'unknown', - screenWidth: this.systemInfo?.screenWidth || 0, - screenHeight: this.systemInfo?.screenHeight || 0, - windowWidth: this.systemInfo?.windowWidth || 0, - windowHeight: this.systemInfo?.windowHeight || 0 - } - } - } -}) \ No newline at end of file diff --git a/bank_mini_program/src/store/index.js b/bank_mini_program/src/store/index.js deleted file mode 100644 index b06c05b..0000000 --- a/bank_mini_program/src/store/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import { createPinia } from 'pinia' - -const pinia = createPinia() - -export default pinia - -// 导出所有store -export { useUserStore } from './user' -export { useAppStore } from './app' \ No newline at end of file diff --git a/bank_mini_program/src/store/user.js b/bank_mini_program/src/store/user.js deleted file mode 100644 index 2c05b3e..0000000 --- a/bank_mini_program/src/store/user.js +++ /dev/null @@ -1,217 +0,0 @@ -import { defineStore } from 'pinia' -import request from '@/utils/request' -import api from '@/config/api' - -export const useUserStore = defineStore('user', { - state: () => ({ - // 用户信息 - userInfo: null, - // 登录状态 - isLoggedIn: false, - // 用户权限 - permissions: [], - // 用户角色 - roles: [] - }), - - getters: { - // 获取用户ID - userId: (state) => state.userInfo?.id, - - // 获取用户名 - username: (state) => state.userInfo?.username, - - // 获取真实姓名 - realName: (state) => state.userInfo?.realName, - - // 获取用户角色名称 - roleName: (state) => state.userInfo?.roleName, - - // 检查是否有特定权限 - hasPermission: (state) => (permission) => { - return state.permissions.includes(permission) - }, - - // 检查是否有特定角色 - hasRole: (state) => (role) => { - return state.roles.includes(role) - } - }, - - actions: { - /** - * 用户名密码登录 - */ - async login(loginData) { - try { - const response = await request.post(api.USER.LOGIN, loginData) - - if (response.success) { - // 保存token - uni.setStorageSync('token', response.data.token) - - // 保存用户信息 - this.userInfo = response.data.userInfo - this.isLoggedIn = true - this.permissions = response.data.permissions || [] - this.roles = response.data.roles || [] - - // 持久化用户信息 - uni.setStorageSync('userInfo', this.userInfo) - - return { success: true } - } else { - return { success: false, message: response.message } - } - } catch (error) { - console.error('登录失败:', error) - return { success: false, message: error.message } - } - }, - - /** - * 微信登录 - */ - async wechatLogin(wechatData) { - try { - const response = await request.post(api.USER.WECHAT_LOGIN, wechatData) - - if (response.success) { - // 保存token - uni.setStorageSync('token', response.data.token) - - // 保存用户信息 - this.userInfo = response.data.userInfo - this.isLoggedIn = true - this.permissions = response.data.permissions || [] - this.roles = response.data.roles || [] - - // 持久化用户信息 - uni.setStorageSync('userInfo', this.userInfo) - - return { success: true } - } else { - return { success: false, message: response.message } - } - } catch (error) { - console.error('微信登录失败:', error) - return { success: false, message: error.message } - } - }, - - /** - * 退出登录 - */ - async logout() { - try { - // 调用退出登录接口 - await request.post(api.USER.LOGOUT) - } catch (error) { - console.error('退出登录接口调用失败:', error) - } finally { - // 清除本地数据 - this.clearUserData() - - // 跳转到登录页 - uni.reLaunch({ - url: '/pages/login/login' - }) - } - }, - - /** - * 获取用户信息 - */ - async getUserInfo() { - try { - const response = await request.get(api.USER.INFO) - - if (response.success) { - this.userInfo = response.data - this.permissions = response.data.permissions || [] - this.roles = response.data.roles || [] - - // 更新本地存储 - uni.setStorageSync('userInfo', this.userInfo) - - return { success: true, data: response.data } - } else { - return { success: false, message: response.message } - } - } catch (error) { - console.error('获取用户信息失败:', error) - return { success: false, message: error.message } - } - }, - - /** - * 更新用户信息 - */ - async updateUserInfo(updateData) { - try { - const response = await request.post(api.USER.UPDATE, updateData) - - if (response.success) { - // 更新本地用户信息 - this.userInfo = { ...this.userInfo, ...response.data } - uni.setStorageSync('userInfo', this.userInfo) - - return { success: true } - } else { - return { success: false, message: response.message } - } - } catch (error) { - console.error('更新用户信息失败:', error) - return { success: false, message: error.message } - } - }, - - /** - * 检查登录状态 - */ - checkLoginStatus() { - const token = uni.getStorageSync('token') - const userInfo = uni.getStorageSync('userInfo') - - if (token && userInfo) { - this.userInfo = userInfo - this.isLoggedIn = true - return true - } else { - this.clearUserData() - return false - } - }, - - /** - * 清除用户数据 - */ - clearUserData() { - this.userInfo = null - this.isLoggedIn = false - this.permissions = [] - this.roles = [] - - // 清除本地存储 - uni.removeStorageSync('token') - uni.removeStorageSync('userInfo') - }, - - /** - * 初始化用户状态 - */ - initUserState() { - // 从本地存储恢复用户状态 - const token = uni.getStorageSync('token') - const userInfo = uni.getStorageSync('userInfo') - - if (token && userInfo) { - this.userInfo = userInfo - this.isLoggedIn = true - - // 异步获取最新用户信息 - this.getUserInfo() - } - } - } -}) \ No newline at end of file diff --git a/bank_mini_program/src/styles/base.scss b/bank_mini_program/src/styles/base.scss deleted file mode 100644 index 7c75991..0000000 --- a/bank_mini_program/src/styles/base.scss +++ /dev/null @@ -1,184 +0,0 @@ -@import './variables.scss'; - -/* 重置样式 */ -* { - box-sizing: border-box; -} - -page { - background-color: $bg-color; - font-size: $font-size-base; - line-height: 1.6; - color: $text-color; -} - -/* 布局类 */ -.flex { - display: flex; -} - -.flex-column { - flex-direction: column; -} - -.flex-row { - flex-direction: row; -} - -.justify-center { - justify-content: center; -} - -.justify-between { - justify-content: space-between; -} - -.justify-around { - justify-content: space-around; -} - -.align-center { - align-items: center; -} - -.align-start { - align-items: flex-start; -} - -.align-end { - align-items: flex-end; -} - -.flex-1 { - flex: 1; -} - -/* 文本类 */ -.text-center { - text-align: center; -} - -.text-left { - text-align: left; -} - -.text-right { - text-align: right; -} - -.text-xs { - font-size: $font-size-xs; -} - -.text-sm { - font-size: $font-size-sm; -} - -.text-base { - font-size: $font-size-base; -} - -.text-lg { - font-size: $font-size-lg; -} - -.text-xl { - font-size: $font-size-xl; -} - -.text-xxl { - font-size: $font-size-xxl; -} - -.font-bold { - font-weight: bold; -} - -.font-medium { - font-weight: 500; -} - -.font-normal { - font-weight: normal; -} - -/* 间距类 */ -.m-0 { margin: 0; } -.m-xs { margin: $spacing-xs; } -.m-sm { margin: $spacing-sm; } -.m-base { margin: $spacing-base; } -.m-lg { margin: $spacing-lg; } -.m-xl { margin: $spacing-xl; } -.m-xxl { margin: $spacing-xxl; } - -.mt-0 { margin-top: 0; } -.mt-xs { margin-top: $spacing-xs; } -.mt-sm { margin-top: $spacing-sm; } -.mt-base { margin-top: $spacing-base; } -.mt-lg { margin-top: $spacing-lg; } -.mt-xl { margin-top: $spacing-xl; } -.mt-xxl { margin-top: $spacing-xxl; } - -.mb-0 { margin-bottom: 0; } -.mb-xs { margin-bottom: $spacing-xs; } -.mb-sm { margin-bottom: $spacing-sm; } -.mb-base { margin-bottom: $spacing-base; } -.mb-lg { margin-bottom: $spacing-lg; } -.mb-xl { margin-bottom: $spacing-xl; } -.mb-xxl { margin-bottom: $spacing-xxl; } - -.ml-0 { margin-left: 0; } -.ml-xs { margin-left: $spacing-xs; } -.ml-sm { margin-left: $spacing-sm; } -.ml-base { margin-left: $spacing-base; } -.ml-lg { margin-left: $spacing-lg; } -.ml-xl { margin-left: $spacing-xl; } -.ml-xxl { margin-left: $spacing-xxl; } - -.mr-0 { margin-right: 0; } -.mr-xs { margin-right: $spacing-xs; } -.mr-sm { margin-right: $spacing-sm; } -.mr-base { margin-right: $spacing-base; } -.mr-lg { margin-right: $spacing-lg; } -.mr-xl { margin-right: $spacing-xl; } -.mr-xxl { margin-right: $spacing-xxl; } - -.p-0 { padding: 0; } -.p-xs { padding: $spacing-xs; } -.p-sm { padding: $spacing-sm; } -.p-base { padding: $spacing-base; } -.p-lg { padding: $spacing-lg; } -.p-xl { padding: $spacing-xl; } -.p-xxl { padding: $spacing-xxl; } - -.pt-0 { padding-top: 0; } -.pt-xs { padding-top: $spacing-xs; } -.pt-sm { padding-top: $spacing-sm; } -.pt-base { padding-top: $spacing-base; } -.pt-lg { padding-top: $spacing-lg; } -.pt-xl { padding-top: $spacing-xl; } -.pt-xxl { padding-top: $spacing-xxl; } - -.pb-0 { padding-bottom: 0; } -.pb-xs { padding-bottom: $spacing-xs; } -.pb-sm { padding-bottom: $spacing-sm; } -.pb-base { padding-bottom: $spacing-base; } -.pb-lg { padding-bottom: $spacing-lg; } -.pb-xl { padding-bottom: $spacing-xl; } -.pb-xxl { padding-bottom: $spacing-xxl; } - -.pl-0 { padding-left: 0; } -.pl-xs { padding-left: $spacing-xs; } -.pl-sm { padding-left: $spacing-sm; } -.pl-base { padding-left: $spacing-base; } -.pl-lg { padding-left: $spacing-lg; } -.pl-xl { padding-left: $spacing-xl; } -.pl-xxl { padding-left: $spacing-xxl; } - -.pr-0 { padding-right: 0; } -.pr-xs { padding-right: $spacing-xs; } -.pr-sm { padding-right: $spacing-sm; } -.pr-base { padding-right: $spacing-base; } -.pr-lg { padding-right: $spacing-lg; } -.pr-xl { padding-right: $spacing-xl; } -.pr-xxl { padding-right: $spacing-xxl; } \ No newline at end of file diff --git a/bank_mini_program/src/styles/mixins.scss b/bank_mini_program/src/styles/mixins.scss deleted file mode 100644 index c3b956f..0000000 --- a/bank_mini_program/src/styles/mixins.scss +++ /dev/null @@ -1,544 +0,0 @@ -// 混合器文件 - 提供常用的样式混合器和工具类 -@import './variables.scss'; - -// 文本省略 -@mixin text-ellipsis($lines: 1) { - @if $lines == 1 { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } @else { - display: -webkit-box; - -webkit-line-clamp: $lines; - -webkit-box-orient: vertical; - overflow: hidden; - text-overflow: ellipsis; - } -} - -// 清除浮动 -@mixin clearfix { - &::after { - content: ''; - display: table; - clear: both; - } -} - -// 居中对齐 -@mixin center($type: 'both') { - position: absolute; - - @if $type == 'both' { - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } @else if $type == 'horizontal' { - left: 50%; - transform: translateX(-50%); - } @else if $type == 'vertical' { - top: 50%; - transform: translateY(-50%); - } -} - -// Flex 布局 -@mixin flex($direction: row, $justify: flex-start, $align: stretch, $wrap: nowrap) { - display: flex; - flex-direction: $direction; - justify-content: $justify; - align-items: $align; - flex-wrap: $wrap; -} - -// Flex 居中 -@mixin flex-center { - @include flex(row, center, center); -} - -// 响应式断点 -@mixin respond-to($breakpoint) { - @if $breakpoint == 'mobile' { - @media (max-width: 767px) { - @content; - } - } @else if $breakpoint == 'tablet' { - @media (min-width: 768px) and (max-width: 1023px) { - @content; - } - } @else if $breakpoint == 'desktop' { - @media (min-width: 1024px) { - @content; - } - } @else if $breakpoint == 'large-desktop' { - @media (min-width: 1200px) { - @content; - } - } -} - -// 按钮样式 -@mixin button-style($type: 'primary', $size: 'medium') { - display: inline-flex; - align-items: center; - justify-content: center; - border: none; - border-radius: $border-radius-sm; - font-weight: 500; - text-align: center; - cursor: pointer; - transition: all 0.3s ease; - user-select: none; - - // 尺寸 - @if $size == 'small' { - padding: $spacing-xs $spacing-sm; - font-size: $font-size-sm; - min-height: 32px; - } @else if $size == 'medium' { - padding: $spacing-sm $spacing-md; - font-size: $font-size-md; - min-height: 40px; - } @else if $size == 'large' { - padding: $spacing-md $spacing-lg; - font-size: $font-size-lg; - min-height: 48px; - } - - // 类型 - @if $type == 'primary' { - background: $primary-color; - color: white; - - &:hover { - background: $primary-color-light; - } - - &:active { - background: $primary-color-dark; - } - - &:disabled { - background: $text-color-disabled; - cursor: not-allowed; - } - } @else if $type == 'secondary' { - background: white; - color: $primary-color; - border: 1px solid $primary-color; - - &:hover { - background: rgba($primary-color, 0.05); - } - - &:active { - background: rgba($primary-color, 0.1); - } - - &:disabled { - color: $text-color-disabled; - border-color: $text-color-disabled; - cursor: not-allowed; - } - } @else if $type == 'text' { - background: transparent; - color: $primary-color; - - &:hover { - background: rgba($primary-color, 0.05); - } - - &:active { - background: rgba($primary-color, 0.1); - } - - &:disabled { - color: $text-color-disabled; - cursor: not-allowed; - } - } -} - -// 卡片样式 -@mixin card-style($shadow: true, $border: true, $radius: true) { - background: white; - - @if $border { - border: 1px solid $border-color-light; - } - - @if $radius { - border-radius: $border-radius-md; - } - - @if $shadow { - box-shadow: $shadow-light; - } -} - -// 输入框样式 -@mixin input-style($size: 'medium') { - width: 100%; - border: 1px solid $border-color; - border-radius: $border-radius-sm; - background: white; - color: $text-color-primary; - font-size: $font-size-md; - transition: all 0.3s ease; - - @if $size == 'small' { - padding: $spacing-xs $spacing-sm; - font-size: $font-size-sm; - min-height: 32px; - } @else if $size == 'medium' { - padding: $spacing-sm $spacing-md; - font-size: $font-size-md; - min-height: 40px; - } @else if $size == 'large' { - padding: $spacing-md $spacing-lg; - font-size: $font-size-lg; - min-height: 48px; - } - - &::placeholder { - color: $text-color-placeholder; - } - - &:focus { - border-color: $primary-color; - outline: none; - box-shadow: 0 0 0 2px rgba($primary-color, 0.2); - } - - &:disabled { - background: $bg-color-light; - color: $text-color-disabled; - cursor: not-allowed; - } - - &.error { - border-color: $error-color; - - &:focus { - box-shadow: 0 0 0 2px rgba($error-color, 0.2); - } - } -} - -// 滚动条样式 -@mixin scrollbar($width: 6px, $track-color: transparent, $thumb-color: rgba(0, 0, 0, 0.2)) { - &::-webkit-scrollbar { - width: $width; - height: $width; - } - - &::-webkit-scrollbar-track { - background: $track-color; - border-radius: $width / 2; - } - - &::-webkit-scrollbar-thumb { - background: $thumb-color; - border-radius: $width / 2; - - &:hover { - background: rgba(0, 0, 0, 0.4); - } - } -} - -// 动画 -@mixin fade-in($duration: 0.3s) { - animation: fadeIn $duration ease-in-out; -} - -@mixin fade-out($duration: 0.3s) { - animation: fadeOut $duration ease-in-out; -} - -@mixin slide-up($duration: 0.3s) { - animation: slideUp $duration ease-out; -} - -@mixin slide-down($duration: 0.3s) { - animation: slideDown $duration ease-out; -} - -@mixin bounce-in($duration: 0.5s) { - animation: bounceIn $duration ease-out; -} - -// 关键帧动画 -@keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@keyframes fadeOut { - from { - opacity: 1; - } - to { - opacity: 0; - } -} - -@keyframes slideUp { - from { - transform: translateY(100%); - opacity: 0; - } - to { - transform: translateY(0); - opacity: 1; - } -} - -@keyframes slideDown { - from { - transform: translateY(-100%); - opacity: 0; - } - to { - transform: translateY(0); - opacity: 1; - } -} - -@keyframes bounceIn { - 0% { - transform: scale(0.3); - opacity: 0; - } - 50% { - transform: scale(1.05); - } - 70% { - transform: scale(0.9); - } - 100% { - transform: scale(1); - opacity: 1; - } -} - -@keyframes spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@keyframes pulse { - 0% { - transform: scale(1); - } - 50% { - transform: scale(1.05); - } - 100% { - transform: scale(1); - } -} - -// 阴影 -@mixin shadow($level: 1) { - @if $level == 1 { - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - } @else if $level == 2 { - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); - } @else if $level == 3 { - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2); - } @else if $level == 4 { - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25); - } -} - -// 边框 -@mixin border($position: 'all', $color: $border-color, $width: 1px, $style: solid) { - @if $position == 'all' { - border: $width $style $color; - } @else if $position == 'top' { - border-top: $width $style $color; - } @else if $position == 'right' { - border-right: $width $style $color; - } @else if $position == 'bottom' { - border-bottom: $width $style $color; - } @else if $position == 'left' { - border-left: $width $style $color; - } @else if $position == 'horizontal' { - border-left: $width $style $color; - border-right: $width $style $color; - } @else if $position == 'vertical' { - border-top: $width $style $color; - border-bottom: $width $style $color; - } -} - -// 渐变背景 -@mixin gradient($direction: 'to right', $colors...) { - background: linear-gradient(#{$direction}, $colors); -} - -// 毛玻璃效果 -@mixin glass($blur: 10px, $opacity: 0.8) { - backdrop-filter: blur($blur); - background: rgba(255, 255, 255, $opacity); -} - -// 文本样式 -@mixin text-style($size: 'medium', $weight: normal, $color: $text-color-primary) { - @if $size == 'xs' { - font-size: $font-size-xs; - } @else if $size == 'sm' { - font-size: $font-size-sm; - } @else if $size == 'md' { - font-size: $font-size-md; - } @else if $size == 'lg' { - font-size: $font-size-lg; - } @else if $size == 'xl' { - font-size: $font-size-xl; - } - - font-weight: $weight; - color: $color; - line-height: 1.5; -} - -// 安全区域适配 -@mixin safe-area($position: 'bottom', $property: 'padding') { - @if $position == 'top' { - #{$property}-top: env(safe-area-inset-top); - } @else if $position == 'bottom' { - #{$property}-bottom: env(safe-area-inset-bottom); - } @else if $position == 'left' { - #{$property}-left: env(safe-area-inset-left); - } @else if $position == 'right' { - #{$property}-right: env(safe-area-inset-right); - } -} - -// 1px 边框解决方案 -@mixin hairline($position: 'all', $color: $border-color) { - position: relative; - - &::after { - content: ''; - position: absolute; - pointer-events: none; - - @if $position == 'all' { - top: 0; - left: 0; - right: 0; - bottom: 0; - border: 1px solid $color; - transform-origin: 0 0; - transform: scale(0.5); - } @else if $position == 'top' { - top: 0; - left: 0; - right: 0; - height: 1px; - background: $color; - transform-origin: 0 0; - transform: scaleY(0.5); - } @else if $position == 'bottom' { - bottom: 0; - left: 0; - right: 0; - height: 1px; - background: $color; - transform-origin: 0 100%; - transform: scaleY(0.5); - } @else if $position == 'left' { - top: 0; - left: 0; - bottom: 0; - width: 1px; - background: $color; - transform-origin: 0 0; - transform: scaleX(0.5); - } @else if $position == 'right' { - top: 0; - right: 0; - bottom: 0; - width: 1px; - background: $color; - transform-origin: 100% 0; - transform: scaleX(0.5); - } - } -} - -// 网格布局 -@mixin grid($columns: 2, $gap: $spacing-md) { - display: grid; - grid-template-columns: repeat($columns, 1fr); - gap: $gap; -} - -// 固定宽高比 -@mixin aspect-ratio($ratio: 1) { - position: relative; - - &::before { - content: ''; - display: block; - padding-top: percentage(1 / $ratio); - } - - > * { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - } -} - -// 隐藏元素但保持可访问性 -@mixin visually-hidden { - position: absolute !important; - width: 1px !important; - height: 1px !important; - padding: 0 !important; - margin: -1px !important; - overflow: hidden !important; - clip: rect(0, 0, 0, 0) !important; - white-space: nowrap !important; - border: 0 !important; -} - -// 重置按钮样式 -@mixin reset-button { - background: none; - border: none; - padding: 0; - margin: 0; - font: inherit; - color: inherit; - cursor: pointer; - outline: none; -} - -// 重置列表样式 -@mixin reset-list { - list-style: none; - padding: 0; - margin: 0; -} - -// 图片适配 -@mixin image-fit($fit: cover) { - width: 100%; - height: 100%; - object-fit: $fit; - object-position: center; -} \ No newline at end of file diff --git a/bank_mini_program/src/styles/utilities.scss b/bank_mini_program/src/styles/utilities.scss deleted file mode 100644 index 40dbb13..0000000 --- a/bank_mini_program/src/styles/utilities.scss +++ /dev/null @@ -1,476 +0,0 @@ -// 工具类样式文件 - 提供常用的原子化CSS类 -@import './variables.scss'; -@import './mixins.scss'; - -// 间距工具类 -@each $name, $value in ( - 'xs': $spacing-xs, - 'sm': $spacing-sm, - 'md': $spacing-md, - 'lg': $spacing-lg, - 'xl': $spacing-xl, - 'xxl': $spacing-xxl -) { - // 内边距 - .p-#{$name} { padding: $value !important; } - .pt-#{$name} { padding-top: $value !important; } - .pr-#{$name} { padding-right: $value !important; } - .pb-#{$name} { padding-bottom: $value !important; } - .pl-#{$name} { padding-left: $value !important; } - .px-#{$name} { - padding-left: $value !important; - padding-right: $value !important; - } - .py-#{$name} { - padding-top: $value !important; - padding-bottom: $value !important; - } - - // 外边距 - .m-#{$name} { margin: $value !important; } - .mt-#{$name} { margin-top: $value !important; } - .mr-#{$name} { margin-right: $value !important; } - .mb-#{$name} { margin-bottom: $value !important; } - .ml-#{$name} { margin-left: $value !important; } - .mx-#{$name} { - margin-left: $value !important; - margin-right: $value !important; - } - .my-#{$name} { - margin-top: $value !important; - margin-bottom: $value !important; - } -} - -// 特殊间距 -.p-0 { padding: 0 !important; } -.m-0 { margin: 0 !important; } -.m-auto { margin: auto !important; } -.mx-auto { - margin-left: auto !important; - margin-right: auto !important; -} -.my-auto { - margin-top: auto !important; - margin-bottom: auto !important; -} - -// 文本工具类 -.text-left { text-align: left !important; } -.text-center { text-align: center !important; } -.text-right { text-align: right !important; } -.text-justify { text-align: justify !important; } - -// 文本颜色 -.text-primary { color: $primary-color !important; } -.text-secondary { color: $text-color-secondary !important; } -.text-success { color: $success-color !important; } -.text-warning { color: $warning-color !important; } -.text-error { color: $error-color !important; } -.text-info { color: $info-color !important; } -.text-disabled { color: $text-color-disabled !important; } -.text-white { color: white !important; } -.text-black { color: black !important; } - -// 文本大小 -.text-xs { font-size: $font-size-xs !important; } -.text-sm { font-size: $font-size-sm !important; } -.text-md { font-size: $font-size-md !important; } -.text-lg { font-size: $font-size-lg !important; } -.text-xl { font-size: $font-size-xl !important; } - -// 文本粗细 -.font-thin { font-weight: 100 !important; } -.font-light { font-weight: 300 !important; } -.font-normal { font-weight: 400 !important; } -.font-medium { font-weight: 500 !important; } -.font-semibold { font-weight: 600 !important; } -.font-bold { font-weight: 700 !important; } -.font-extrabold { font-weight: 800 !important; } -.font-black { font-weight: 900 !important; } - -// 文本装饰 -.underline { text-decoration: underline !important; } -.line-through { text-decoration: line-through !important; } -.no-underline { text-decoration: none !important; } - -// 文本省略 -.text-ellipsis { @include text-ellipsis(1); } -.text-ellipsis-2 { @include text-ellipsis(2); } -.text-ellipsis-3 { @include text-ellipsis(3); } - -// 文本换行 -.text-nowrap { white-space: nowrap !important; } -.text-wrap { white-space: normal !important; } -.text-break { word-break: break-all !important; } - -// 背景颜色 -.bg-primary { background-color: $primary-color !important; } -.bg-success { background-color: $success-color !important; } -.bg-warning { background-color: $warning-color !important; } -.bg-error { background-color: $error-color !important; } -.bg-info { background-color: $info-color !important; } -.bg-white { background-color: white !important; } -.bg-light { background-color: $bg-color-light !important; } -.bg-gray { background-color: $bg-color-gray !important; } -.bg-transparent { background-color: transparent !important; } - -// 边框 -.border { border: 1px solid $border-color !important; } -.border-t { border-top: 1px solid $border-color !important; } -.border-r { border-right: 1px solid $border-color !important; } -.border-b { border-bottom: 1px solid $border-color !important; } -.border-l { border-left: 1px solid $border-color !important; } -.border-0 { border: none !important; } - -// 边框颜色 -.border-primary { border-color: $primary-color !important; } -.border-success { border-color: $success-color !important; } -.border-warning { border-color: $warning-color !important; } -.border-error { border-color: $error-color !important; } -.border-info { border-color: $info-color !important; } -.border-light { border-color: $border-color-light !important; } - -// 圆角 -.rounded-none { border-radius: 0 !important; } -.rounded-sm { border-radius: $border-radius-sm !important; } -.rounded { border-radius: $border-radius-md !important; } -.rounded-lg { border-radius: $border-radius-lg !important; } -.rounded-full { border-radius: 50% !important; } - -// 阴影 -.shadow-none { box-shadow: none !important; } -.shadow-sm { @include shadow(1); } -.shadow { @include shadow(2); } -.shadow-lg { @include shadow(3); } -.shadow-xl { @include shadow(4); } - -// 显示/隐藏 -.block { display: block !important; } -.inline { display: inline !important; } -.inline-block { display: inline-block !important; } -.flex { display: flex !important; } -.inline-flex { display: inline-flex !important; } -.grid { display: grid !important; } -.hidden { display: none !important; } - -// Flex 布局 -.flex-row { flex-direction: row !important; } -.flex-col { flex-direction: column !important; } -.flex-wrap { flex-wrap: wrap !important; } -.flex-nowrap { flex-wrap: nowrap !important; } - -// Flex 对齐 -.justify-start { justify-content: flex-start !important; } -.justify-end { justify-content: flex-end !important; } -.justify-center { justify-content: center !important; } -.justify-between { justify-content: space-between !important; } -.justify-around { justify-content: space-around !important; } -.justify-evenly { justify-content: space-evenly !important; } - -.items-start { align-items: flex-start !important; } -.items-end { align-items: flex-end !important; } -.items-center { align-items: center !important; } -.items-baseline { align-items: baseline !important; } -.items-stretch { align-items: stretch !important; } - -// Flex 项目 -.flex-1 { flex: 1 1 0% !important; } -.flex-auto { flex: 1 1 auto !important; } -.flex-initial { flex: 0 1 auto !important; } -.flex-none { flex: none !important; } -.flex-shrink-0 { flex-shrink: 0 !important; } -.flex-grow { flex-grow: 1 !important; } - -// 定位 -.relative { position: relative !important; } -.absolute { position: absolute !important; } -.fixed { position: fixed !important; } -.sticky { position: sticky !important; } -.static { position: static !important; } - -// 定位偏移 -.top-0 { top: 0 !important; } -.right-0 { right: 0 !important; } -.bottom-0 { bottom: 0 !important; } -.left-0 { left: 0 !important; } - -// 宽度 -.w-full { width: 100% !important; } -.w-auto { width: auto !important; } -.w-0 { width: 0 !important; } - -@for $i from 1 through 12 { - .w-#{$i} { width: percentage($i / 12) !important; } -} - -// 高度 -.h-full { height: 100% !important; } -.h-auto { height: auto !important; } -.h-0 { height: 0 !important; } -.h-screen { height: 100vh !important; } - -// 最大/最小宽高 -.max-w-full { max-width: 100% !important; } -.max-h-full { max-height: 100% !important; } -.min-w-0 { min-width: 0 !important; } -.min-h-0 { min-height: 0 !important; } - -// 溢出 -.overflow-auto { overflow: auto !important; } -.overflow-hidden { overflow: hidden !important; } -.overflow-visible { overflow: visible !important; } -.overflow-scroll { overflow: scroll !important; } -.overflow-x-auto { overflow-x: auto !important; } -.overflow-y-auto { overflow-y: auto !important; } -.overflow-x-hidden { overflow-x: hidden !important; } -.overflow-y-hidden { overflow-y: hidden !important; } - -// 透明度 -.opacity-0 { opacity: 0 !important; } -.opacity-25 { opacity: 0.25 !important; } -.opacity-50 { opacity: 0.5 !important; } -.opacity-75 { opacity: 0.75 !important; } -.opacity-100 { opacity: 1 !important; } - -// 层级 -.z-0 { z-index: 0 !important; } -.z-10 { z-index: 10 !important; } -.z-20 { z-index: 20 !important; } -.z-30 { z-index: 30 !important; } -.z-40 { z-index: 40 !important; } -.z-50 { z-index: 50 !important; } -.z-auto { z-index: auto !important; } - -// 光标 -.cursor-auto { cursor: auto !important; } -.cursor-default { cursor: default !important; } -.cursor-pointer { cursor: pointer !important; } -.cursor-wait { cursor: wait !important; } -.cursor-text { cursor: text !important; } -.cursor-move { cursor: move !important; } -.cursor-not-allowed { cursor: not-allowed !important; } - -// 用户选择 -.select-none { user-select: none !important; } -.select-text { user-select: text !important; } -.select-all { user-select: all !important; } -.select-auto { user-select: auto !important; } - -// 指针事件 -.pointer-events-none { pointer-events: none !important; } -.pointer-events-auto { pointer-events: auto !important; } - -// 变换 -.transform { transform: translateZ(0) !important; } -.scale-0 { transform: scale(0) !important; } -.scale-50 { transform: scale(0.5) !important; } -.scale-75 { transform: scale(0.75) !important; } -.scale-90 { transform: scale(0.9) !important; } -.scale-95 { transform: scale(0.95) !important; } -.scale-100 { transform: scale(1) !important; } -.scale-105 { transform: scale(1.05) !important; } -.scale-110 { transform: scale(1.1) !important; } -.scale-125 { transform: scale(1.25) !important; } -.scale-150 { transform: scale(1.5) !important; } - -// 旋转 -.rotate-0 { transform: rotate(0deg) !important; } -.rotate-45 { transform: rotate(45deg) !important; } -.rotate-90 { transform: rotate(90deg) !important; } -.rotate-180 { transform: rotate(180deg) !important; } -.rotate-270 { transform: rotate(270deg) !important; } - -// 过渡 -.transition-none { transition: none !important; } -.transition-all { transition: all 0.3s ease !important; } -.transition-colors { transition: color 0.3s ease, background-color 0.3s ease, border-color 0.3s ease !important; } -.transition-opacity { transition: opacity 0.3s ease !important; } -.transition-shadow { transition: box-shadow 0.3s ease !important; } -.transition-transform { transition: transform 0.3s ease !important; } - -// 动画 -.animate-spin { animation: spin 1s linear infinite !important; } -.animate-ping { animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite !important; } -.animate-pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite !important; } -.animate-bounce { animation: bounce 1s infinite !important; } - -@keyframes ping { - 75%, 100% { - transform: scale(2); - opacity: 0; - } -} - -@keyframes bounce { - 0%, 100% { - transform: translateY(-25%); - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - } - 50% { - transform: none; - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - } -} - -// 滤镜 -.blur-none { filter: blur(0) !important; } -.blur-sm { filter: blur(4px) !important; } -.blur { filter: blur(8px) !important; } -.blur-lg { filter: blur(16px) !important; } -.blur-xl { filter: blur(24px) !important; } - -// 亮度 -.brightness-0 { filter: brightness(0) !important; } -.brightness-50 { filter: brightness(0.5) !important; } -.brightness-75 { filter: brightness(0.75) !important; } -.brightness-90 { filter: brightness(0.9) !important; } -.brightness-95 { filter: brightness(0.95) !important; } -.brightness-100 { filter: brightness(1) !important; } -.brightness-105 { filter: brightness(1.05) !important; } -.brightness-110 { filter: brightness(1.1) !important; } -.brightness-125 { filter: brightness(1.25) !important; } -.brightness-150 { filter: brightness(1.5) !important; } -.brightness-200 { filter: brightness(2) !important; } - -// 对比度 -.contrast-0 { filter: contrast(0) !important; } -.contrast-50 { filter: contrast(0.5) !important; } -.contrast-75 { filter: contrast(0.75) !important; } -.contrast-100 { filter: contrast(1) !important; } -.contrast-125 { filter: contrast(1.25) !important; } -.contrast-150 { filter: contrast(1.5) !important; } -.contrast-200 { filter: contrast(2) !important; } - -// 灰度 -.grayscale-0 { filter: grayscale(0) !important; } -.grayscale { filter: grayscale(100%) !important; } - -// 色相旋转 -.hue-rotate-0 { filter: hue-rotate(0deg) !important; } -.hue-rotate-15 { filter: hue-rotate(15deg) !important; } -.hue-rotate-30 { filter: hue-rotate(30deg) !important; } -.hue-rotate-60 { filter: hue-rotate(60deg) !important; } -.hue-rotate-90 { filter: hue-rotate(90deg) !important; } -.hue-rotate-180 { filter: hue-rotate(180deg) !important; } - -// 饱和度 -.saturate-0 { filter: saturate(0) !important; } -.saturate-50 { filter: saturate(0.5) !important; } -.saturate-100 { filter: saturate(1) !important; } -.saturate-150 { filter: saturate(1.5) !important; } -.saturate-200 { filter: saturate(2) !important; } - -// 深褐色 -.sepia-0 { filter: sepia(0) !important; } -.sepia { filter: sepia(100%) !important; } - -// 反转 -.invert-0 { filter: invert(0) !important; } -.invert { filter: invert(100%) !important; } - -// 投影 -.drop-shadow-sm { filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.05)) !important; } -.drop-shadow { filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.1)) drop-shadow(0 1px 2px rgba(0, 0, 0, 0.06)) !important; } -.drop-shadow-md { filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.07)) drop-shadow(0 2px 4px rgba(0, 0, 0, 0.06)) !important; } -.drop-shadow-lg { filter: drop-shadow(0 10px 15px rgba(0, 0, 0, 0.1)) drop-shadow(0 4px 6px rgba(0, 0, 0, 0.05)) !important; } -.drop-shadow-xl { filter: drop-shadow(0 20px 25px rgba(0, 0, 0, 0.15)) drop-shadow(0 8px 10px rgba(0, 0, 0, 0.04)) !important; } -.drop-shadow-2xl { filter: drop-shadow(0 25px 50px rgba(0, 0, 0, 0.25)) !important; } -.drop-shadow-none { filter: drop-shadow(0 0 #0000) !important; } - -// 响应式工具类 -@include respond-to('mobile') { - .mobile\:hidden { display: none !important; } - .mobile\:block { display: block !important; } - .mobile\:flex { display: flex !important; } - .mobile\:text-center { text-align: center !important; } - .mobile\:text-left { text-align: left !important; } - .mobile\:text-sm { font-size: $font-size-sm !important; } - .mobile\:p-sm { padding: $spacing-sm !important; } - .mobile\:m-sm { margin: $spacing-sm !important; } -} - -@include respond-to('tablet') { - .tablet\:hidden { display: none !important; } - .tablet\:block { display: block !important; } - .tablet\:flex { display: flex !important; } - .tablet\:text-center { text-align: center !important; } - .tablet\:text-left { text-align: left !important; } -} - -@include respond-to('desktop') { - .desktop\:hidden { display: none !important; } - .desktop\:block { display: block !important; } - .desktop\:flex { display: flex !important; } - .desktop\:text-center { text-align: center !important; } - .desktop\:text-left { text-align: left !important; } -} - -// 打印样式 -@media print { - .print\:hidden { display: none !important; } - .print\:block { display: block !important; } - .print\:text-black { color: black !important; } - .print\:bg-white { background-color: white !important; } -} - -// 暗色主题 -@media (prefers-color-scheme: dark) { - .dark\:text-white { color: white !important; } - .dark\:text-gray-300 { color: #d1d5db !important; } - .dark\:bg-gray-800 { background-color: #1f2937 !important; } - .dark\:bg-gray-900 { background-color: #111827 !important; } - .dark\:border-gray-600 { border-color: #4b5563 !important; } -} - -// 高对比度 -@media (prefers-contrast: high) { - .high-contrast\:border-black { border-color: black !important; } - .high-contrast\:text-black { color: black !important; } - .high-contrast\:bg-white { background-color: white !important; } -} - -// 减少动画 -@media (prefers-reduced-motion: reduce) { - .motion-reduce\:animate-none { animation: none !important; } - .motion-reduce\:transition-none { transition: none !important; } -} - -// 自定义工具类 -.clearfix { @include clearfix; } -.center { @include center; } -.center-x { @include center('horizontal'); } -.center-y { @include center('vertical'); } -.flex-center { @include flex-center; } -.visually-hidden { @include visually-hidden; } -.reset-button { @include reset-button; } -.reset-list { @include reset-list; } - -// 滚动条样式 -.scrollbar-thin { @include scrollbar(4px); } -.scrollbar-none { - scrollbar-width: none; - -ms-overflow-style: none; - - &::-webkit-scrollbar { - display: none; - } -} - -// 安全区域 -.safe-top { @include safe-area('top'); } -.safe-bottom { @include safe-area('bottom'); } -.safe-left { @include safe-area('left'); } -.safe-right { @include safe-area('right'); } - -// 1px 边框 -.hairline { @include hairline; } -.hairline-top { @include hairline('top'); } -.hairline-bottom { @include hairline('bottom'); } -.hairline-left { @include hairline('left'); } -.hairline-right { @include hairline('right'); } - -// 宽高比 -.aspect-square { @include aspect-ratio(1); } -.aspect-video { @include aspect-ratio(16/9); } -.aspect-photo { @include aspect-ratio(4/3); } \ No newline at end of file diff --git a/bank_mini_program/src/styles/variables.scss b/bank_mini_program/src/styles/variables.scss deleted file mode 100644 index dfaa593..0000000 --- a/bank_mini_program/src/styles/variables.scss +++ /dev/null @@ -1,54 +0,0 @@ -// 主题色彩 -$primary-color: #2c5aa0; -$primary-light: #4a7bc8; -$primary-dark: #1e3a8a; - -$success-color: #52c41a; -$warning-color: #faad14; -$danger-color: #ff4d4f; -$info-color: #1890ff; - -// 中性色 -$text-color: #333333; -$text-secondary: #666666; -$text-muted: #999999; -$text-light: #cccccc; - -$bg-color: #f5f7fa; -$bg-white: #ffffff; -$bg-gray: #f8f9fa; - -$border-color: #e8e8e8; -$border-light: #f0f0f0; - -// 字体大小 -$font-size-xs: 20rpx; -$font-size-sm: 24rpx; -$font-size-base: 28rpx; -$font-size-lg: 32rpx; -$font-size-xl: 36rpx; -$font-size-xxl: 40rpx; - -// 间距 -$spacing-xs: 8rpx; -$spacing-sm: 12rpx; -$spacing-base: 16rpx; -$spacing-lg: 24rpx; -$spacing-xl: 32rpx; -$spacing-xxl: 48rpx; - -// 圆角 -$border-radius-sm: 4rpx; -$border-radius-base: 8rpx; -$border-radius-lg: 12rpx; -$border-radius-xl: 16rpx; - -// 阴影 -$box-shadow-sm: 0 2rpx 4rpx rgba(0, 0, 0, 0.1); -$box-shadow-base: 0 2rpx 12rpx rgba(0, 0, 0, 0.1); -$box-shadow-lg: 0 4rpx 20rpx rgba(0, 0, 0, 0.15); - -// 动画时间 -$transition-base: 0.3s; -$transition-fast: 0.2s; -$transition-slow: 0.5s; \ No newline at end of file diff --git a/bank_mini_program/src/utils/auth.js b/bank_mini_program/src/utils/auth.js deleted file mode 100644 index bc68432..0000000 --- a/bank_mini_program/src/utils/auth.js +++ /dev/null @@ -1,379 +0,0 @@ -// 银行端小程序认证工具类 - -import { useUserStore } from '@/store' - -/** - * 检查用户是否已登录 - * @returns {boolean} 登录状态 - */ -export const isLoggedIn = () => { - const token = uni.getStorageSync('token') - const userInfo = uni.getStorageSync('userInfo') - return !!(token && userInfo) -} - -/** - * 检查用户是否有特定权限 - * @param {string} permission 权限标识 - * @returns {boolean} 是否有权限 - */ -export const hasPermission = (permission) => { - const userStore = useUserStore() - return userStore.hasPermission(permission) -} - -/** - * 检查用户是否有特定角色 - * @param {string} role 角色标识 - * @returns {boolean} 是否有角色 - */ -export const hasRole = (role) => { - const userStore = useUserStore() - return userStore.hasRole(role) -} - -/** - * 检查用户是否有任一权限 - * @param {Array} permissions 权限数组 - * @returns {boolean} 是否有任一权限 - */ -export const hasAnyPermission = (permissions) => { - if (!Array.isArray(permissions) || permissions.length === 0) { - return true - } - - const userStore = useUserStore() - return permissions.some(permission => userStore.hasPermission(permission)) -} - -/** - * 检查用户是否有所有权限 - * @param {Array} permissions 权限数组 - * @returns {boolean} 是否有所有权限 - */ -export const hasAllPermissions = (permissions) => { - if (!Array.isArray(permissions) || permissions.length === 0) { - return true - } - - const userStore = useUserStore() - return permissions.every(permission => userStore.hasPermission(permission)) -} - -/** - * 检查用户是否有任一角色 - * @param {Array} roles 角色数组 - * @returns {boolean} 是否有任一角色 - */ -export const hasAnyRole = (roles) => { - if (!Array.isArray(roles) || roles.length === 0) { - return true - } - - const userStore = useUserStore() - return roles.some(role => userStore.hasRole(role)) -} - -/** - * 获取当前用户信息 - * @returns {Object|null} 用户信息 - */ -export const getCurrentUser = () => { - const userStore = useUserStore() - return userStore.userInfo -} - -/** - * 获取当前用户ID - * @returns {string|null} 用户ID - */ -export const getCurrentUserId = () => { - const userStore = useUserStore() - return userStore.userId -} - -/** - * 获取当前用户角色 - * @returns {Array} 用户角色数组 - */ -export const getCurrentUserRoles = () => { - const userStore = useUserStore() - return userStore.roles || [] -} - -/** - * 获取当前用户权限 - * @returns {Array} 用户权限数组 - */ -export const getCurrentUserPermissions = () => { - const userStore = useUserStore() - return userStore.permissions || [] -} - -/** - * 跳转到登录页 - */ -export const redirectToLogin = () => { - uni.reLaunch({ - url: '/pages/login/login' - }) -} - -/** - * 权限验证装饰器 - * @param {string|Array} requiredPermissions 必需的权限 - * @param {Function} callback 回调函数 - * @param {Function} onDenied 权限不足时的回调 - */ -export const requirePermission = (requiredPermissions, callback, onDenied) => { - // 检查登录状态 - if (!isLoggedIn()) { - uni.showModal({ - title: '提示', - content: '请先登录', - showCancel: false, - success: () => { - redirectToLogin() - } - }) - return - } - - // 检查权限 - let hasRequiredPermission = false - - if (typeof requiredPermissions === 'string') { - hasRequiredPermission = hasPermission(requiredPermissions) - } else if (Array.isArray(requiredPermissions)) { - hasRequiredPermission = hasAnyPermission(requiredPermissions) - } else { - hasRequiredPermission = true - } - - if (hasRequiredPermission) { - if (typeof callback === 'function') { - callback() - } - } else { - if (typeof onDenied === 'function') { - onDenied() - } else { - uni.showToast({ - title: '权限不足', - icon: 'none', - duration: 2000 - }) - } - } -} - -/** - * 角色验证装饰器 - * @param {string|Array} requiredRoles 必需的角色 - * @param {Function} callback 回调函数 - * @param {Function} onDenied 角色不足时的回调 - */ -export const requireRole = (requiredRoles, callback, onDenied) => { - // 检查登录状态 - if (!isLoggedIn()) { - uni.showModal({ - title: '提示', - content: '请先登录', - showCancel: false, - success: () => { - redirectToLogin() - } - }) - return - } - - // 检查角色 - let hasRequiredRole = false - - if (typeof requiredRoles === 'string') { - hasRequiredRole = hasRole(requiredRoles) - } else if (Array.isArray(requiredRoles)) { - hasRequiredRole = hasAnyRole(requiredRoles) - } else { - hasRequiredRole = true - } - - if (hasRequiredRole) { - if (typeof callback === 'function') { - callback() - } - } else { - if (typeof onDenied === 'function') { - onDenied() - } else { - uni.showToast({ - title: '角色权限不足', - icon: 'none', - duration: 2000 - }) - } - } -} - -/** - * 登录检查装饰器 - * @param {Function} callback 回调函数 - */ -export const requireLogin = (callback) => { - if (isLoggedIn()) { - if (typeof callback === 'function') { - callback() - } - } else { - uni.showModal({ - title: '提示', - content: '请先登录', - showCancel: false, - success: () => { - redirectToLogin() - } - }) - } -} - -/** - * 页面权限检查中间件 - * @param {Object} pageConfig 页面配置 - * @returns {boolean} 是否有权限访问 - */ -export const checkPagePermission = (pageConfig = {}) => { - const { - requireAuth = true, - permissions = [], - roles = [], - onDenied - } = pageConfig - - // 如果不需要认证,直接通过 - if (!requireAuth) { - return true - } - - // 检查登录状态 - if (!isLoggedIn()) { - uni.showModal({ - title: '提示', - content: '请先登录', - showCancel: false, - success: () => { - redirectToLogin() - } - }) - return false - } - - // 检查权限 - if (permissions.length > 0 && !hasAnyPermission(permissions)) { - if (typeof onDenied === 'function') { - onDenied('permission') - } else { - uni.showToast({ - title: '权限不足', - icon: 'none', - duration: 2000 - }) - setTimeout(() => { - uni.navigateBack() - }, 2000) - } - return false - } - - // 检查角色 - if (roles.length > 0 && !hasAnyRole(roles)) { - if (typeof onDenied === 'function') { - onDenied('role') - } else { - uni.showToast({ - title: '角色权限不足', - icon: 'none', - duration: 2000 - }) - setTimeout(() => { - uni.navigateBack() - }, 2000) - } - return false - } - - return true -} - -/** - * 权限常量定义 - */ -export const PERMISSIONS = { - // 客户管理权限 - CUSTOMER_VIEW: 'customer:view', - CUSTOMER_CREATE: 'customer:create', - CUSTOMER_EDIT: 'customer:edit', - CUSTOMER_DELETE: 'customer:delete', - - // 资产管理权限 - ASSET_VIEW: 'asset:view', - ASSET_CREATE: 'asset:create', - ASSET_EDIT: 'asset:edit', - ASSET_DELETE: 'asset:delete', - ASSET_APPROVE: 'asset:approve', - - // 交易管理权限 - TRANSACTION_VIEW: 'transaction:view', - TRANSACTION_CREATE: 'transaction:create', - TRANSACTION_APPROVE: 'transaction:approve', - TRANSACTION_REJECT: 'transaction:reject', - - // 风险管理权限 - RISK_VIEW: 'risk:view', - RISK_ASSESS: 'risk:assess', - RISK_MANAGE: 'risk:manage', - - // 报表权限 - REPORT_VIEW: 'report:view', - REPORT_EXPORT: 'report:export', - - // 系统管理权限 - SYSTEM_CONFIG: 'system:config', - USER_MANAGE: 'user:manage', - ROLE_MANAGE: 'role:manage', - PERMISSION_MANAGE: 'permission:manage' -} - -/** - * 角色常量定义 - */ -export const ROLES = { - SUPER_ADMIN: 'super_admin', // 超级管理员 - ADMIN: 'admin', // 管理员 - SUPERVISOR: 'supervisor', // 主管 - OFFICER: 'officer', // 业务员 - AUDITOR: 'auditor', // 审计员 - VIEWER: 'viewer' // 查看者 -} - -/** - * 默认导出 - */ -export default { - isLoggedIn, - hasPermission, - hasRole, - hasAnyPermission, - hasAllPermissions, - hasAnyRole, - getCurrentUser, - getCurrentUserId, - getCurrentUserRoles, - getCurrentUserPermissions, - redirectToLogin, - requirePermission, - requireRole, - requireLogin, - checkPagePermission, - PERMISSIONS, - ROLES -} \ No newline at end of file diff --git a/bank_mini_program/src/utils/permission.js b/bank_mini_program/src/utils/permission.js deleted file mode 100644 index a972503..0000000 --- a/bank_mini_program/src/utils/permission.js +++ /dev/null @@ -1,485 +0,0 @@ -// 银行端小程序权限管理工具 - -import { PERMISSIONS, ROLES } from './auth' - -/** - * 权限配置映射 - */ -export const PERMISSION_CONFIG = { - // 页面权限配置 - pages: { - '/pages/index/index': { - requireAuth: false, - permissions: [], - roles: [] - }, - '/pages/login/login': { - requireAuth: false, - permissions: [], - roles: [] - }, - '/pages/dashboard/dashboard': { - requireAuth: true, - permissions: [], - roles: [ROLES.OFFICER, ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - '/pages/customers/customers': { - requireAuth: true, - permissions: [PERMISSIONS.CUSTOMER_VIEW], - roles: [ROLES.OFFICER, ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - '/pages/customers/detail': { - requireAuth: true, - permissions: [PERMISSIONS.CUSTOMER_VIEW], - roles: [ROLES.OFFICER, ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - '/pages/assets/assets': { - requireAuth: true, - permissions: [PERMISSIONS.ASSET_VIEW], - roles: [ROLES.OFFICER, ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - '/pages/assets/detail': { - requireAuth: true, - permissions: [PERMISSIONS.ASSET_VIEW], - roles: [ROLES.OFFICER, ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - '/pages/transactions/transactions': { - requireAuth: true, - permissions: [PERMISSIONS.TRANSACTION_VIEW], - roles: [ROLES.OFFICER, ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - '/pages/transactions/detail': { - requireAuth: true, - permissions: [PERMISSIONS.TRANSACTION_VIEW], - roles: [ROLES.OFFICER, ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - '/pages/risk/risk': { - requireAuth: true, - permissions: [PERMISSIONS.RISK_VIEW], - roles: [ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN, ROLES.AUDITOR] - }, - '/pages/profile/profile': { - requireAuth: true, - permissions: [], - roles: [] - } - }, - - // 功能权限配置 - features: { - // 客户管理功能 - 'customer-create': { - permissions: [PERMISSIONS.CUSTOMER_CREATE], - roles: [ROLES.OFFICER, ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - 'customer-edit': { - permissions: [PERMISSIONS.CUSTOMER_EDIT], - roles: [ROLES.OFFICER, ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - 'customer-delete': { - permissions: [PERMISSIONS.CUSTOMER_DELETE], - roles: [ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - - // 资产管理功能 - 'asset-create': { - permissions: [PERMISSIONS.ASSET_CREATE], - roles: [ROLES.OFFICER, ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - 'asset-edit': { - permissions: [PERMISSIONS.ASSET_EDIT], - roles: [ROLES.OFFICER, ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - 'asset-delete': { - permissions: [PERMISSIONS.ASSET_DELETE], - roles: [ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - 'asset-approve': { - permissions: [PERMISSIONS.ASSET_APPROVE], - roles: [ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - - // 交易管理功能 - 'transaction-create': { - permissions: [PERMISSIONS.TRANSACTION_CREATE], - roles: [ROLES.OFFICER, ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - 'transaction-approve': { - permissions: [PERMISSIONS.TRANSACTION_APPROVE], - roles: [ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - 'transaction-reject': { - permissions: [PERMISSIONS.TRANSACTION_REJECT], - roles: [ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - - // 风险管理功能 - 'risk-assess': { - permissions: [PERMISSIONS.RISK_ASSESS], - roles: [ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN, ROLES.AUDITOR] - }, - 'risk-manage': { - permissions: [PERMISSIONS.RISK_MANAGE], - roles: [ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - - // 报表功能 - 'report-view': { - permissions: [PERMISSIONS.REPORT_VIEW], - roles: [ROLES.SUPERVISOR, ROLES.ADMIN, ROLES.SUPER_ADMIN, ROLES.AUDITOR] - }, - 'report-export': { - permissions: [PERMISSIONS.REPORT_EXPORT], - roles: [ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - - // 系统管理功能 - 'system-config': { - permissions: [PERMISSIONS.SYSTEM_CONFIG], - roles: [ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - 'user-manage': { - permissions: [PERMISSIONS.USER_MANAGE], - roles: [ROLES.ADMIN, ROLES.SUPER_ADMIN] - }, - 'role-manage': { - permissions: [PERMISSIONS.ROLE_MANAGE], - roles: [ROLES.SUPER_ADMIN] - } - } -} - -/** - * 角色权限映射 - */ -export const ROLE_PERMISSIONS = { - [ROLES.SUPER_ADMIN]: [ - // 拥有所有权限 - ...Object.values(PERMISSIONS) - ], - - [ROLES.ADMIN]: [ - // 客户管理 - PERMISSIONS.CUSTOMER_VIEW, - PERMISSIONS.CUSTOMER_CREATE, - PERMISSIONS.CUSTOMER_EDIT, - PERMISSIONS.CUSTOMER_DELETE, - - // 资产管理 - PERMISSIONS.ASSET_VIEW, - PERMISSIONS.ASSET_CREATE, - PERMISSIONS.ASSET_EDIT, - PERMISSIONS.ASSET_DELETE, - PERMISSIONS.ASSET_APPROVE, - - // 交易管理 - PERMISSIONS.TRANSACTION_VIEW, - PERMISSIONS.TRANSACTION_CREATE, - PERMISSIONS.TRANSACTION_APPROVE, - PERMISSIONS.TRANSACTION_REJECT, - - // 风险管理 - PERMISSIONS.RISK_VIEW, - PERMISSIONS.RISK_ASSESS, - PERMISSIONS.RISK_MANAGE, - - // 报表 - PERMISSIONS.REPORT_VIEW, - PERMISSIONS.REPORT_EXPORT, - - // 系统管理 - PERMISSIONS.SYSTEM_CONFIG, - PERMISSIONS.USER_MANAGE - ], - - [ROLES.SUPERVISOR]: [ - // 客户管理 - PERMISSIONS.CUSTOMER_VIEW, - PERMISSIONS.CUSTOMER_CREATE, - PERMISSIONS.CUSTOMER_EDIT, - PERMISSIONS.CUSTOMER_DELETE, - - // 资产管理 - PERMISSIONS.ASSET_VIEW, - PERMISSIONS.ASSET_CREATE, - PERMISSIONS.ASSET_EDIT, - PERMISSIONS.ASSET_DELETE, - PERMISSIONS.ASSET_APPROVE, - - // 交易管理 - PERMISSIONS.TRANSACTION_VIEW, - PERMISSIONS.TRANSACTION_CREATE, - PERMISSIONS.TRANSACTION_APPROVE, - PERMISSIONS.TRANSACTION_REJECT, - - // 风险管理 - PERMISSIONS.RISK_VIEW, - PERMISSIONS.RISK_ASSESS, - - // 报表 - PERMISSIONS.REPORT_VIEW - ], - - [ROLES.OFFICER]: [ - // 客户管理 - PERMISSIONS.CUSTOMER_VIEW, - PERMISSIONS.CUSTOMER_CREATE, - PERMISSIONS.CUSTOMER_EDIT, - - // 资产管理 - PERMISSIONS.ASSET_VIEW, - PERMISSIONS.ASSET_CREATE, - PERMISSIONS.ASSET_EDIT, - - // 交易管理 - PERMISSIONS.TRANSACTION_VIEW, - PERMISSIONS.TRANSACTION_CREATE, - - // 风险管理 - PERMISSIONS.RISK_VIEW - ], - - [ROLES.AUDITOR]: [ - // 客户管理 - PERMISSIONS.CUSTOMER_VIEW, - - // 资产管理 - PERMISSIONS.ASSET_VIEW, - - // 交易管理 - PERMISSIONS.TRANSACTION_VIEW, - - // 风险管理 - PERMISSIONS.RISK_VIEW, - PERMISSIONS.RISK_ASSESS, - - // 报表 - PERMISSIONS.REPORT_VIEW - ], - - [ROLES.VIEWER]: [ - // 客户管理 - PERMISSIONS.CUSTOMER_VIEW, - - // 资产管理 - PERMISSIONS.ASSET_VIEW, - - // 交易管理 - PERMISSIONS.TRANSACTION_VIEW, - - // 风险管理 - PERMISSIONS.RISK_VIEW - ] -} - -/** - * 获取页面权限配置 - * @param {string} pagePath 页面路径 - * @returns {Object} 权限配置 - */ -export const getPagePermissionConfig = (pagePath) => { - return PERMISSION_CONFIG.pages[pagePath] || { - requireAuth: true, - permissions: [], - roles: [] - } -} - -/** - * 获取功能权限配置 - * @param {string} featureKey 功能键 - * @returns {Object} 权限配置 - */ -export const getFeaturePermissionConfig = (featureKey) => { - return PERMISSION_CONFIG.features[featureKey] || { - permissions: [], - roles: [] - } -} - -/** - * 根据角色获取权限列表 - * @param {string} role 角色 - * @returns {Array} 权限列表 - */ -export const getPermissionsByRole = (role) => { - return ROLE_PERMISSIONS[role] || [] -} - -/** - * 根据角色列表获取所有权限 - * @param {Array} roles 角色列表 - * @returns {Array} 权限列表 - */ -export const getPermissionsByRoles = (roles) => { - if (!Array.isArray(roles)) { - return [] - } - - const permissions = new Set() - - roles.forEach(role => { - const rolePermissions = getPermissionsByRole(role) - rolePermissions.forEach(permission => { - permissions.add(permission) - }) - }) - - return Array.from(permissions) -} - -/** - * 检查角色是否有特定权限 - * @param {string} role 角色 - * @param {string} permission 权限 - * @returns {boolean} 是否有权限 - */ -export const roleHasPermission = (role, permission) => { - const permissions = getPermissionsByRole(role) - return permissions.includes(permission) -} - -/** - * 权限级别定义 - */ -export const PERMISSION_LEVELS = { - READ: 1, // 只读 - WRITE: 2, // 读写 - DELETE: 3, // 删除 - APPROVE: 4, // 审批 - ADMIN: 5 // 管理 -} - -/** - * 权限分组 - */ -export const PERMISSION_GROUPS = { - CUSTOMER: { - name: '客户管理', - permissions: [ - PERMISSIONS.CUSTOMER_VIEW, - PERMISSIONS.CUSTOMER_CREATE, - PERMISSIONS.CUSTOMER_EDIT, - PERMISSIONS.CUSTOMER_DELETE - ] - }, - - ASSET: { - name: '资产管理', - permissions: [ - PERMISSIONS.ASSET_VIEW, - PERMISSIONS.ASSET_CREATE, - PERMISSIONS.ASSET_EDIT, - PERMISSIONS.ASSET_DELETE, - PERMISSIONS.ASSET_APPROVE - ] - }, - - TRANSACTION: { - name: '交易管理', - permissions: [ - PERMISSIONS.TRANSACTION_VIEW, - PERMISSIONS.TRANSACTION_CREATE, - PERMISSIONS.TRANSACTION_APPROVE, - PERMISSIONS.TRANSACTION_REJECT - ] - }, - - RISK: { - name: '风险管理', - permissions: [ - PERMISSIONS.RISK_VIEW, - PERMISSIONS.RISK_ASSESS, - PERMISSIONS.RISK_MANAGE - ] - }, - - REPORT: { - name: '报表管理', - permissions: [ - PERMISSIONS.REPORT_VIEW, - PERMISSIONS.REPORT_EXPORT - ] - }, - - SYSTEM: { - name: '系统管理', - permissions: [ - PERMISSIONS.SYSTEM_CONFIG, - PERMISSIONS.USER_MANAGE, - PERMISSIONS.ROLE_MANAGE, - PERMISSIONS.PERMISSION_MANAGE - ] - } -} - -/** - * 获取权限显示名称 - * @param {string} permission 权限标识 - * @returns {string} 显示名称 - */ -export const getPermissionDisplayName = (permission) => { - const nameMap = { - [PERMISSIONS.CUSTOMER_VIEW]: '查看客户', - [PERMISSIONS.CUSTOMER_CREATE]: '创建客户', - [PERMISSIONS.CUSTOMER_EDIT]: '编辑客户', - [PERMISSIONS.CUSTOMER_DELETE]: '删除客户', - - [PERMISSIONS.ASSET_VIEW]: '查看资产', - [PERMISSIONS.ASSET_CREATE]: '创建资产', - [PERMISSIONS.ASSET_EDIT]: '编辑资产', - [PERMISSIONS.ASSET_DELETE]: '删除资产', - [PERMISSIONS.ASSET_APPROVE]: '审批资产', - - [PERMISSIONS.TRANSACTION_VIEW]: '查看交易', - [PERMISSIONS.TRANSACTION_CREATE]: '创建交易', - [PERMISSIONS.TRANSACTION_APPROVE]: '审批交易', - [PERMISSIONS.TRANSACTION_REJECT]: '拒绝交易', - - [PERMISSIONS.RISK_VIEW]: '查看风险', - [PERMISSIONS.RISK_ASSESS]: '风险评估', - [PERMISSIONS.RISK_MANAGE]: '风险管理', - - [PERMISSIONS.REPORT_VIEW]: '查看报表', - [PERMISSIONS.REPORT_EXPORT]: '导出报表', - - [PERMISSIONS.SYSTEM_CONFIG]: '系统配置', - [PERMISSIONS.USER_MANAGE]: '用户管理', - [PERMISSIONS.ROLE_MANAGE]: '角色管理', - [PERMISSIONS.PERMISSION_MANAGE]: '权限管理' - } - - return nameMap[permission] || permission -} - -/** - * 获取角色显示名称 - * @param {string} role 角色标识 - * @returns {string} 显示名称 - */ -export const getRoleDisplayName = (role) => { - const nameMap = { - [ROLES.SUPER_ADMIN]: '超级管理员', - [ROLES.ADMIN]: '管理员', - [ROLES.SUPERVISOR]: '主管', - [ROLES.OFFICER]: '业务员', - [ROLES.AUDITOR]: '审计员', - [ROLES.VIEWER]: '查看者' - } - - return nameMap[role] || role -} - -export default { - PERMISSION_CONFIG, - ROLE_PERMISSIONS, - PERMISSION_LEVELS, - PERMISSION_GROUPS, - getPagePermissionConfig, - getFeaturePermissionConfig, - getPermissionsByRole, - getPermissionsByRoles, - roleHasPermission, - getPermissionDisplayName, - getRoleDisplayName -} \ No newline at end of file diff --git a/bank_mini_program/src/utils/request.js b/bank_mini_program/src/utils/request.js deleted file mode 100644 index 70faa71..0000000 --- a/bank_mini_program/src/utils/request.js +++ /dev/null @@ -1,36 +0,0 @@ -import axios from 'axios' -import apiConfig from '../config/api' -import { getToken } from './auth' - -// 创建axios实例 -const service = axios.create({ - baseURL: apiConfig.BASE_URL, - timeout: apiConfig.TIMEOUT -}) - -// 请求拦截器 -service.interceptors.request.use( - config => { - // 添加token - const token = getToken() - if (token) { - config.headers['Authorization'] = `Bearer ${token}` - } - return config - }, - error => { - return Promise.reject(error) - } -) - -// 响应拦截器 -service.interceptors.response.use( - response => { - return response.data - }, - error => { - return Promise.reject(error) - } -) - -export default service \ No newline at end of file diff --git a/bank_mini_program/tests/setup.ts b/bank_mini_program/tests/setup.ts deleted file mode 100644 index cfb2dc4..0000000 --- a/bank_mini_program/tests/setup.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { config } from '@vue/test-utils' -import { createPinia } from 'pinia' - -// 全局测试配置 -config.global.plugins = [createPinia()] - -// Mock uni-app API -const mockUni = { - // 导航相关 - navigateTo: jest.fn(), - navigateBack: jest.fn(), - redirectTo: jest.fn(), - switchTab: jest.fn(), - reLaunch: jest.fn(), - - // 界面相关 - showToast: jest.fn(), - showModal: jest.fn(), - showLoading: jest.fn(), - hideLoading: jest.fn(), - showActionSheet: jest.fn(), - - // 网络请求 - request: jest.fn(), - uploadFile: jest.fn(), - downloadFile: jest.fn(), - - // 数据存储 - setStorage: jest.fn(), - getStorage: jest.fn(), - removeStorage: jest.fn(), - clearStorage: jest.fn(), - setStorageSync: jest.fn(), - getStorageSync: jest.fn(), - removeStorageSync: jest.fn(), - clearStorageSync: jest.fn(), - - // 设备信息 - getSystemInfo: jest.fn(), - getSystemInfoSync: jest.fn(), - - // 位置信息 - getLocation: jest.fn(), - chooseLocation: jest.fn(), - openLocation: jest.fn(), - - // 媒体 - chooseImage: jest.fn(), - previewImage: jest.fn(), - chooseVideo: jest.fn(), - - // 文件 - saveFile: jest.fn(), - getSavedFileList: jest.fn(), - getSavedFileInfo: jest.fn(), - removeSavedFile: jest.fn(), - openDocument: jest.fn(), - - // 其他 - scanCode: jest.fn(), - setClipboardData: jest.fn(), - getClipboardData: jest.fn(), - makePhoneCall: jest.fn(), - - // 页面相关 - onLoad: jest.fn(), - onShow: jest.fn(), - onHide: jest.fn(), - onUnload: jest.fn(), - onPullDownRefresh: jest.fn(), - onReachBottom: jest.fn(), - - // 分享 - onShareAppMessage: jest.fn(), - onShareTimeline: jest.fn(), - - // 支付 - requestPayment: jest.fn(), - - // 登录 - login: jest.fn(), - checkSession: jest.fn(), - getUserInfo: jest.fn(), - getUserProfile: jest.fn(), - - // 授权 - authorize: jest.fn(), - getSetting: jest.fn(), - openSetting: jest.fn() -} - -// 设置全局 uni 对象 -;(global as any).uni = mockUni -;(global as any).wx = mockUni -;(global as any).getCurrentPages = jest.fn(() => []) -;(global as any).getApp = jest.fn(() => ({})) - -// Mock console 方法(可选) -global.console = { - ...console, - // 在测试中静默某些日志 - log: jest.fn(), - debug: jest.fn(), - info: jest.fn(), - warn: jest.fn(), - error: jest.fn() -} - -// 设置默认的 mock 返回值 -mockUni.getSystemInfoSync.mockReturnValue({ - platform: 'devtools', - system: 'iOS 14.0', - version: '8.0.5', - SDKVersion: '2.19.4', - brand: 'iPhone', - model: 'iPhone 12', - pixelRatio: 3, - screenWidth: 375, - screenHeight: 812, - windowWidth: 375, - windowHeight: 812, - statusBarHeight: 44, - safeArea: { - left: 0, - right: 375, - top: 44, - bottom: 778, - width: 375, - height: 734 - } -}) - -mockUni.request.mockImplementation(({ success }) => { - if (success) { - success({ - statusCode: 200, - data: { code: 0, message: 'success', data: {} } - }) - } -}) - -mockUni.showToast.mockImplementation(() => Promise.resolve()) -mockUni.showModal.mockImplementation(() => Promise.resolve({ confirm: true })) -mockUni.showLoading.mockImplementation(() => Promise.resolve()) -mockUni.hideLoading.mockImplementation(() => Promise.resolve()) - -// 导出 mock 对象供测试使用 -export { mockUni } \ No newline at end of file diff --git a/bank_mini_program/tests/utils/test-utils.ts b/bank_mini_program/tests/utils/test-utils.ts deleted file mode 100644 index 2b78ef4..0000000 --- a/bank_mini_program/tests/utils/test-utils.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { mount, VueWrapper } from '@vue/test-utils' -import { createPinia } from 'pinia' -import type { ComponentMountingOptions } from '@vue/test-utils' - -/** - * 创建测试用的 Pinia 实例 - */ -export function createTestPinia() { - return createPinia() -} - -/** - * 挂载组件的辅助函数 - */ -export function mountComponent( - component: T, - options: ComponentMountingOptions = {} -): VueWrapper { - const pinia = createTestPinia() - - return mount(component, { - global: { - plugins: [pinia], - ...options.global - }, - ...options - }) -} - -/** - * 等待 Vue 的下一个 tick - */ -export async function nextTick(): Promise { - return new Promise(resolve => { - setTimeout(resolve, 0) - }) -} - -/** - * 等待指定时间 - */ -export function sleep(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)) -} - -/** - * 模拟 uni-app 的页面跳转 - */ -export function mockNavigation() { - const navigateTo = jest.fn() - const navigateBack = jest.fn() - const redirectTo = jest.fn() - const switchTab = jest.fn() - const reLaunch = jest.fn() - - ;(global as any).uni = { - ...(global as any).uni, - navigateTo, - navigateBack, - redirectTo, - switchTab, - reLaunch - } - - return { - navigateTo, - navigateBack, - redirectTo, - switchTab, - reLaunch - } -} - -/** - * 模拟 uni-app 的网络请求 - */ -export function mockRequest() { - const request = jest.fn() - - ;(global as any).uni = { - ...(global as any).uni, - request - } - - return { request } -} - -/** - * 模拟 uni-app 的存储 - */ -export function mockStorage() { - const storage: Record = {} - - const setStorageSync = jest.fn((key: string, value: any) => { - storage[key] = value - }) - - const getStorageSync = jest.fn((key: string) => { - return storage[key] - }) - - const removeStorageSync = jest.fn((key: string) => { - delete storage[key] - }) - - const clearStorageSync = jest.fn(() => { - Object.keys(storage).forEach(key => { - delete storage[key] - }) - }) - - ;(global as any).uni = { - ...(global as any).uni, - setStorageSync, - getStorageSync, - removeStorageSync, - clearStorageSync - } - - return { - setStorageSync, - getStorageSync, - removeStorageSync, - clearStorageSync, - storage - } -} - -/** - * 模拟 uni-app 的界面反馈 - */ -export function mockUI() { - const showToast = jest.fn() - const showModal = jest.fn() - const showLoading = jest.fn() - const hideLoading = jest.fn() - const showActionSheet = jest.fn() - - ;(global as any).uni = { - ...(global as any).uni, - showToast, - showModal, - showLoading, - hideLoading, - showActionSheet - } - - return { - showToast, - showModal, - showLoading, - hideLoading, - showActionSheet - } -} - -/** - * 创建模拟的响应数据 - */ -export function createMockResponse(data: T, code = 0, message = 'success') { - return { - statusCode: 200, - data: { - code, - message, - data - } - } -} - -/** - * 创建模拟的错误响应 - */ -export function createMockErrorResponse(code = -1, message = 'error') { - return { - statusCode: 500, - data: { - code, - message, - data: null - } - } -} - -/** - * 触发组件事件的辅助函数 - */ -export async function triggerEvent( - wrapper: VueWrapper, - selector: string, - event: string, - payload?: any -) { - const element = wrapper.find(selector) - await element.trigger(event, payload) - await nextTick() -} - -/** - * 等待组件更新完成 - */ -export async function waitForUpdate(wrapper: VueWrapper) { - await wrapper.vm.$nextTick() - await nextTick() -} - -/** - * 检查元素是否存在 - */ -export function expectElementExists(wrapper: VueWrapper, selector: string) { - expect(wrapper.find(selector).exists()).toBe(true) -} - -/** - * 检查元素是否不存在 - */ -export function expectElementNotExists(wrapper: VueWrapper, selector: string) { - expect(wrapper.find(selector).exists()).toBe(false) -} - -/** - * 检查元素文本内容 - */ -export function expectElementText( - wrapper: VueWrapper, - selector: string, - text: string -) { - expect(wrapper.find(selector).text()).toBe(text) -} - -/** - * 检查元素是否包含指定文本 - */ -export function expectElementContainsText( - wrapper: VueWrapper, - selector: string, - text: string -) { - expect(wrapper.find(selector).text()).toContain(text) -} \ No newline at end of file diff --git a/bank_mini_program/启动银行端微信小程序.bat b/bank_mini_program/启动银行端微信小程序.bat deleted file mode 100644 index 21e5db8..0000000 --- a/bank_mini_program/启动银行端微信小程序.bat +++ /dev/null @@ -1,43 +0,0 @@ -@echo off -echo 银行端微信小程序启动指南 -echo ================================ -echo. - -echo 1. 确保已安装微信开发者工具 -echo 2. 确保后端服务已启动 (http://localhost:5352) -echo 3. 在微信开发者工具中导入项目 -echo. - -echo 项目配置信息: -echo - 项目目录: %cd% -echo - AppID: wx1b9c7cd2d0e0bfd3 -echo - 项目名称: 银行端小程序 -echo. - -echo 默认登录账号: -echo - 用户名: admin -echo - 密码: 123456 -echo. - -echo 功能模块: -echo - 首页: 快速访问各功能,查看银行卡和交易 -echo - 数据看板: 银行统计数据和图表 -echo - 客户管理: 客户信息管理 -echo - 交易记录: 交易流水查询 -echo - 资产管理: 银行卡和资产管理 -echo - 风险控制: 风险等级评估 -echo - 报表分析: 财务报表生成 -echo - 个人中心: 用户信息和设置 -echo. - -echo 银行特色功能: -echo - 银行卡渐变设计 -echo - 交易状态标识 -echo - 风险等级评估 -echo - 数据可视化 -echo. - -echo 详细使用说明请查看: 银行端微信小程序使用指南.md -echo. - -pause diff --git a/bank_mini_program/图标说明.md b/bank_mini_program/图标说明.md new file mode 100644 index 0000000..bc8be75 --- /dev/null +++ b/bank_mini_program/图标说明.md @@ -0,0 +1,74 @@ +# 银行端小程序图标说明 + +## 需要添加的图标文件 + +根据新的底部导航栏配置,需要在 `images/` 目录下添加以下图标文件: + +### 1. 日检预警图标 +- `warning.png` - 未选中状态的日检预警图标 +- `warning-active.png` - 选中状态的日检预警图标 + +**建议图标样式:** +- 使用闪电图标 ⚡ 或警告图标 ⚠️ +- 未选中:灰色 (#7A7E83) +- 选中:蓝色 (#1890ff) + +### 2. 项目清单图标 +- `projects.png` - 未选中状态的项目清单图标 +- `projects-active.png` - 选中状态的项目清单图标 + +**建议图标样式:** +- 使用列表图标 📋 或文件夹图标 📁 +- 未选中:灰色 (#7A7E83) +- 选中:蓝色 (#1890ff) + +### 3. 业务管理图标 +- `business.png` - 未选中状态的业务管理图标 +- `business-active.png` - 选中状态的业务管理图标 + +**建议图标样式:** +- 使用钱袋图标 💰 或齿轮图标 ⚙️ +- 未选中:灰色 (#7A7E83) +- 选中:蓝色 (#1890ff) + +### 4. 我的图标(已存在) +- `profile.png` - 未选中状态的我的图标 +- `profile-active.png` - 选中状态的我的图标 + +## 图标规格要求 + +- **尺寸:** 建议 40x40 像素 +- **格式:** PNG 格式,支持透明背景 +- **风格:** 简洁的线性图标,与现有图标风格保持一致 +- **颜色:** + - 未选中状态:#7A7E83(灰色) + - 选中状态:#1890ff(蓝色) + +## 临时解决方案 + +如果暂时没有图标文件,可以: + +1. 使用现有的图标文件作为临时替代 +2. 或者将图标路径设置为空字符串,使用纯文字显示 + +```json +{ + "pagePath": "pages/warning/warning", + "iconPath": "", + "selectedIconPath": "", + "text": "日检预警" +} +``` + +## 图标制作建议 + +可以使用以下工具制作图标: +- Figma +- Sketch +- Adobe Illustrator +- 在线图标生成器 + +或者从以下网站下载免费图标: +- Iconfont +- Feather Icons +- Material Design Icons diff --git a/government-admin/src/components/Layout.vue b/government-admin/src/components/Layout.vue index d888314..d69ce70 100644 --- a/government-admin/src/components/Layout.vue +++ b/government-admin/src/components/Layout.vue @@ -66,11 +66,11 @@ \ No newline at end of file diff --git a/government-admin/src/router/index.js b/government-admin/src/router/index.js index f130190..afb5bb1 100644 --- a/government-admin/src/router/index.js +++ b/government-admin/src/router/index.js @@ -145,35 +145,36 @@ const routes = [ component: () => import('@/views/paperless/epidemic/vaccine-management/VaccineManagement.vue'), meta: { title: '疫苗管理' } }, - { - path: 'paperless/epidemic/epidemic-activity', - name: 'EpidemicActivityManagement', - component: () => import('@/views/paperless/epidemic/epidemic-activity/EpidemicActivityManagement.vue'), - meta: { title: '防疫活动管理' } + { + path: 'paperless/epidemic/epidemic-activity', + name: 'EpidemicActivityManagement', + component: () => import('@/views/paperless/epidemic/epidemic-activity/EpidemicActivityManagement.vue'), + meta: { title: '防疫活动管理' } }, - { // 无纸化检疫主页 + // 无纸化检疫相关路由 + { path: 'paperless/quarantine', name: 'QuarantineHome', component: () => import('@/views/paperless/QuarantineHome.vue'), meta: { title: '无纸化检疫' } }, - { // 建议审批 + { path: 'paperless/quarantine/declaration', name: 'QuarantineDeclaration', component: () => import('@/views/paperless/quarantine/QuarantineDeclaration.vue'), - meta: { title: '建议审批' } + meta: { title: '检疫审批' } }, - { // 检疫证查询 - path: 'paperless/quarantine/record-search', - name: 'QuarantineRecordSearch', - component: () => import('@/views/paperless/quarantine/QuarantineRecordSearch.vue'), + { + path: 'paperless/quarantine/record-query', + name: 'QuarantineRecordQuery', + component: () => import('@/views/paperless/quarantine/QuarantineRecordQuery.vue'), meta: { title: '检疫证查询' } }, - { // 检疫证清单 - path: 'paperless/quarantine/report-export', - name: 'QuarantineReportExport', - component: () => import('@/views/paperless/quarantine/QuarantineReportExport.vue'), - meta: { title: '检疫证清单' } + { + path: 'paperless/quarantine/config', + name: 'QuarantineConfig', + component: () => import('@/views/paperless/quarantine/QuarantineConfig.vue'), + meta: { title: '检疫站清单' } }, { path: 'slaughter', @@ -235,12 +236,18 @@ const routes = [ component: CattleAcademy, meta: { title: '养牛学院' } }, - { + { path: 'notification', name: 'MessageNotification', component: MessageNotification, meta: { title: '消息通知' } }, + { + path: 'device-warning', + name: 'DeviceWarning', + component: () => import('@/views/DeviceWarning.vue'), + meta: { title: '设备预警' } + }, // { // path: 'users', // name: 'UserManagement', @@ -289,12 +296,43 @@ const routes = [ // component: EpidemicManagement, // meta: { title: '疫情管理' } // }, - // { + // { // path: 'visualization', // name: 'VisualAnalysis', // component: VisualAnalysis, // meta: { title: '可视化分析' } // } + // 无纸化防疫相关路由 + { + path: 'paperless/epidemic', + name: 'EpidemicHome', + component: () => import('@/views/paperless/EpidemicHome.vue'), + meta: { title: '防疫首页' } + }, + { + path: 'paperless/epidemic/epidemic-agency', + name: 'EpidemicAgencyManagement', + component: () => import('@/views/paperless/epidemic/epidemic-agency/EpidemicAgency.vue'), + meta: { title: '防疫机构管理' } + }, + { + path: 'paperless/epidemic/epidemic-record', + name: 'EpidemicRecordManagement', + component: () => import('@/views/paperless/epidemic/epidemic-record/EpidemicRecordManagement.vue'), + meta: { title: '防疫记录' } + }, + { + path: 'paperless/epidemic/vaccine-management', + name: 'VaccineManagement', + component: () => import('@/views/paperless/epidemic/vaccine-management/VaccineManagement.vue'), + meta: { title: '疫苗管理' } + }, + { + path: 'paperless/epidemic/epidemic-activity', + name: 'EpidemicActivityManagement', + component: () => import('@/views/paperless/epidemic/epidemic-activity/EpidemicActivityManagement.vue'), + meta: { title: '防疫活动管理' } + } ] } ] diff --git a/government-admin/src/stores/auth.js b/government-admin/src/stores/auth.js index 9f0720b..7d764d8 100644 --- a/government-admin/src/stores/auth.js +++ b/government-admin/src/stores/auth.js @@ -1,4 +1,5 @@ -import { ref } from 'vue' + +import { computed, ref } from 'vue' import { defineStore } from 'pinia' import { message } from 'ant-design-vue' import router from '@/router' @@ -93,6 +94,9 @@ export const useAuthStore = defineStore('auth', () => { const isLoggedIn = () => { return !!token.value } + + // 计算属性,用于在模板中直接使用 + const isAuthenticated = computed(() => isLoggedIn()) return { token, @@ -104,6 +108,7 @@ export const useAuthStore = defineStore('auth', () => { login, logout, hasPermission, - isLoggedIn + isLoggedIn, + isAuthenticated } }) \ No newline at end of file diff --git a/government-admin/src/utils/api.js b/government-admin/src/utils/api.js index 283e6a3..a63f962 100644 --- a/government-admin/src/utils/api.js +++ b/government-admin/src/utils/api.js @@ -10,10 +10,10 @@ const instance = axios.create({ } }) -// 请求拦截器 - 从localStorage中获取token +// 请求拦截器 instance.interceptors.request.use( config => { - // 从localStorage中获取token + // 从localStorage获取token const token = localStorage.getItem('token') // 如果有token,添加到请求头 if (token) { @@ -38,7 +38,7 @@ instance.interceptors.response.use( // 根据不同的状态码处理错误 switch (error.response.status) { case 401: - // 未授权,跳转到登录页面 + // 未授权,清除token并跳转到登录页面 localStorage.removeItem('token') window.location.href = '/login' message.error('登录已过期,请重新登录') @@ -53,243 +53,275 @@ instance.interceptors.response.use( message.error('服务器内部错误') break default: - message.error('请求失败') + message.error(error.response.data.message || '请求失败') } } else if (error.request) { // 请求发出但没有收到响应 - message.error('网络连接失败') + message.error('网络错误,请检查网络连接') } else { // 请求配置出错 - message.error('请求配置出错') + message.error('请求配置错误') } return Promise.reject(error) } ) -// 认证相关的API -const auth = { - // 登录 - login: data => instance.post('/auth/login', data), - // 获取用户信息 - getUserInfo: () => instance.get('/auth/userInfo'), - // 退出登录 - logout: () => instance.post('/auth/logout'), - // 重置密码 - resetPassword: data => instance.post('/auth/resetPassword', data) -} - -// 用户管理相关的API -const user = { - // 获取用户列表 - getList: params => instance.get('/user/list', { params }), - // 新增用户 - create: data => instance.post('/user/create', data), - // 编辑用户 - update: data => instance.post('/user/update', data), - // 删除用户 - delete: id => instance.post(`/user/delete/${id}`), - // 禁用/启用用户 - toggleStatus: data => instance.post('/user/toggleStatus', data), - // 重置密码 - resetPassword: id => instance.post(`/user/resetPassword/${id}`) -} - -// 监管相关的API -const supervision = { - // 获取监管列表 - getList: params => instance.get('/supervision/list', { params }), - // 获取监管详情 - getDetail: id => instance.get(`/supervision/detail/${id}`), - // 新增监管 - create: data => instance.post('/supervision/create', data), - // 更新监管 - update: data => instance.post('/supervision/update', data), - // 删除监管 - delete: id => instance.post(`/supervision/delete/${id}`) -} - -// 审批相关的API -const approval = { - // 获取审批列表 - getList: params => instance.get('/approval/list', { params }), - // 获取审批详情 - getDetail: id => instance.get(`/approval/detail/${id}`), - // 提交审批 - submit: data => instance.post('/approval/submit', data), - // 审批操作 - approve: data => instance.post('/approval/approve', data), - // 驳回操作 - reject: data => instance.post('/approval/reject', data), - // 撤回操作 - withdraw: id => instance.post(`/approval/withdraw/${id}`) -} - -// 疫情监控相关的API -const epidemic = { - // 获取疫情监控列表 - getList: params => instance.get('/epidemic/list', { params }), - // 获取疫情监控详情 - getDetail: id => instance.get(`/epidemic/detail/${id}`), - // 新增疫情监控 - create: data => instance.post('/epidemic/create', data), - // 更新疫情监控 - update: data => instance.post('/epidemic/update', data), - // 删除疫情监控 - delete: id => instance.post(`/epidemic/delete/${id}`), - // 处理疫情 - handle: data => instance.post('/epidemic/handle', data) -} - -// 数据可视化相关的API -const visualization = { - // 获取养殖情况统计 - getBreedingStats: params => instance.get('/visualization/breedingStats', { params }), - // 获取疫情趋势 - getEpidemicTrend: params => instance.get('/visualization/epidemicTrend', { params }), - // 获取监管效果 - getSupervisionEffect: params => instance.get('/visualization/supervisionEffect', { params }) -} - -// 文件管理相关的API -const file = { - // 上传文件 - upload: data => instance.post('/file/upload', data), - // 下载文件 - download: id => instance.get(`/file/download/${id}`), - // 获取文件列表 - getList: params => instance.get('/file/list', { params }), - // 删除文件 - delete: id => instance.post(`/file/delete/${id}`) -} - -// 人员管理相关的API -const staff = { - // 获取人员列表 - getList: params => instance.get('/staff/list', { params }), - // 新增人员 - create: data => instance.post('/staff/create', data), - // 更新人员 - update: data => instance.post('/staff/update', data), - // 删除人员 - delete: id => instance.post(`/staff/delete/${id}`), - // 获取人员详情 - getDetail: id => instance.get(`/staff/detail/${id}`) -} - -// 服务管理相关的API -const service = { - // 获取服务列表 - getList: params => instance.get('/service/list', { params }), - // 新增服务 - create: data => instance.post('/service/create', data), - // 更新服务 - update: data => instance.post('/service/update', data), - // 删除服务 - delete: id => instance.post(`/service/delete/${id}`), - // 获取服务详情 - getDetail: id => instance.get(`/service/detail/${id}`) -} - -// 仓库管理相关的API -const warehouse = { - // 获取仓库列表 - getList: params => instance.get('/warehouse/list', { params }), - // 新增仓库 - create: data => instance.post('/warehouse/create', data), - // 更新仓库 - update: data => instance.post('/warehouse/update', data), - // 删除仓库 - delete: id => instance.post(`/warehouse/delete/${id}`), - // 获取仓库详情 - getDetail: id => instance.get(`/warehouse/detail/${id}`) -} - -// 系统设置相关的API -const system = { - // 获取系统设置 - getSettings: () => instance.get('/system/settings'), - // 更新系统设置 - updateSettings: data => instance.post('/system/updateSettings', data), - // 获取操作日志 - getOperationLogs: params => instance.get('/system/operationLogs', { params }) -} - -// 政府管理相关的API -const government = { - // 行政人员管理 - adminStaff: { - // 获取行政人员列表 - getList: params => instance.get('/government/admin-staff/list', { params }), - // 新增行政人员 - create: data => instance.post('/government/admin-staff/create', data), - // 编辑行政人员 - update: data => instance.post('/government/admin-staff/update', data), - // 删除行政人员 - delete: id => instance.post(`/government/admin-staff/delete/${id}`), +// API接口定义 +const api = { + // 认证相关API + auth: { + // 登录 + login: (data) => instance.post('/auth/login', data), + // 获取用户信息 + getUserInfo: () => instance.get('/auth/userinfo'), + // 退出登录 + logout: () => instance.post('/auth/logout'), // 重置密码 - resetPassword: id => instance.post(`/government/admin-staff/reset-password/${id}`) + resetPassword: (data) => instance.post('/auth/reset-password', data) }, - // 部门管理 - departments: { - // 获取部门列表 - getList: () => instance.get('/government/departments/list'), - // 新增部门 - create: data => instance.post('/government/departments/create', data), - // 编辑部门 - update: data => instance.post('/government/departments/update', data), - // 删除部门 - delete: id => instance.post(`/government/departments/delete/${id}`) + + // 用户管理相关API + user: { + // 获取用户列表 + getList: (params) => instance.get('/users', { params }), + // 获取单个用户信息 + getDetail: (id) => instance.get(`/users/${id}`), + // 创建用户 + create: (data) => instance.post('/users', data), + // 更新用户 + update: (id, data) => instance.put(`/users/${id}`, data), + // 删除用户 + delete: (id) => instance.delete(`/users/${id}`), + // 批量删除用户 + batchDelete: (ids) => instance.post('/users/batch-delete', { ids }), + // 更新用户状态 + updateStatus: (id, status) => instance.put(`/users/${id}/status`, { status }) }, - // 岗位管理 - positions: { - // 获取岗位列表 - getList: () => instance.get('/government/positions/list'), - // 新增岗位 - create: data => instance.post('/government/positions/create', data), - // 编辑岗位 - update: data => instance.post('/government/positions/update', data), - // 删除岗位 - delete: id => instance.post(`/government/positions/delete/${id}`) + + // 监管相关API + supervision: { + // 获取监管统计数据 + getStats: () => instance.get('/supervision/stats'), + // 获取监管任务列表 + getTasks: (params) => instance.get('/supervision/tasks', { params }), + // 获取监管任务详情 + getTaskDetail: (id) => instance.get(`/supervision/tasks/${id}`), + // 创建监管任务 + createTask: (data) => instance.post('/supervision/tasks', data), + // 更新监管任务 + updateTask: (id, data) => instance.put(`/supervision/tasks/${id}`, data), + // 删除监管任务 + deleteTask: (id) => instance.delete(`/supervision/tasks/${id}`) }, - // 养殖户管理 + + // 审批相关API + approval: { + // 获取审批流程列表 + getList: (params) => instance.get('/approval', { params }), + // 创建审批流程 + create: (data) => instance.post('/approval', data), + // 获取审批详情 + getDetail: (id) => instance.get(`/approval/${id}`), + // 更新审批状态 + updateStatus: (id, status) => instance.put(`/approval/${id}/status`, { status }) + }, + + // 疫情监控相关API + epidemic: { + // 获取疫情统计数据 + getStats: () => instance.get('/epidemic/stats'), + // 获取疫苗接种数据 + getVaccinationData: (params) => instance.get('/epidemic/vaccination', { params }), + // 获取检测数据 + getTestData: (params) => instance.get('/epidemic/test', { params }), + // 防疫机构管理 + agencies: { + // 获取防疫机构列表 + getList: (params) => instance.get('/epidemic/agencies', { params }), + // 获取防疫机构详情 + getDetail: (id) => instance.get(`/epidemic/agencies/${id}`), + // 创建防疫机构 + create: (data) => instance.post('/epidemic/agencies', data), + // 更新防疫机构 + update: (id, data) => instance.put(`/epidemic/agencies/${id}`, data), + // 删除防疫机构 + delete: (id) => instance.delete(`/epidemic/agencies/${id}`), + // 切换防疫机构状态 + toggleStatus: (id) => instance.patch(`/epidemic/agencies/${id}/status`) + } + }, + + // 数据可视化相关API + visualization: { + // 获取可视化数据 + getData: (params) => instance.get('/visualization/data', { params }) + }, + + // 文件管理相关API + file: { + // 获取文件列表 + getList: (params) => instance.get('/files', { params }), + // 上传文件 + upload: (file, onUploadProgress) => { + const formData = new FormData() + formData.append('file', file) + return instance.post('/files/upload', formData, { + headers: { + 'Content-Type': 'multipart/form-data' + }, + onUploadProgress + }) + }, + // 下载文件 + download: (id) => instance.get(`/files/${id}/download`, { responseType: 'blob' }), + // 删除文件 + delete: (id) => instance.delete(`/files/${id}`) + }, + + // 人员管理相关API + personnel: { + // 获取人员列表 + getList: (params) => instance.get('/personnel', { params }), + // 创建人员 + create: (data) => instance.post('/personnel', data), + // 更新人员 + update: (id, data) => instance.put(`/personnel/${id}`, data), + // 删除人员 + delete: (id) => instance.delete(`/personnel/${id}`) + }, + + // 服务管理相关API + service: { + // 获取服务列表 + getList: (params) => instance.get('/service', { params }), + // 创建服务 + create: (data) => instance.post('/service', data), + // 更新服务 + update: (id, data) => instance.put(`/service/${id}`, data), + // 删除服务 + delete: (id) => instance.delete(`/service/${id}`) + }, + + // 仓库管理相关API + warehouse: { + // 获取物资列表 + getList: (params) => instance.get('/warehouse', { params }), + // 获取单个物资详情 + getDetail: (id) => instance.get(`/warehouse/${id}`), + // 创建物资 + create: (data) => instance.post('/warehouse', data), + // 更新物资 + update: (id, data) => instance.put(`/warehouse/${id}`, data), + // 删除物资 + delete: (id) => instance.delete(`/warehouse/${id}`), + // 物资入库 + stockIn: (data) => instance.post('/warehouse/in', data), + // 物资出库 + stockOut: (data) => instance.post('/warehouse/out', data), + // 获取库存统计信息 + getStats: () => instance.get('/warehouse/stats') + }, + + // 系统设置相关API + system: { + // 获取系统设置 + getSettings: () => instance.get('/system/settings'), + // 更新系统设置 + updateSettings: (data) => instance.put('/system/settings', data), + // 获取日志列表 + getLogs: (params) => instance.get('/system/logs', { params }) + }, + + // 政府管理相关API + government: { + // 行政人员管理 + adminStaff: { + // 获取行政人员列表 + getList: (params) => instance.get('/personnel', { params }), + // 创建行政人员 + create: (data) => instance.post('/personnel', data), + // 更新行政人员 + update: (id, data) => instance.put(`/personnel/${id}`, data), + // 删除行政人员 + delete: (id) => instance.delete(`/personnel/${id}`), + // 重置行政人员密码 + resetPassword: (id) => instance.post(`/personnel/${id}/reset-password`) + }, + + // 部门管理 + departments: { + // 获取部门列表 + getList: (params) => instance.get('/government/departments', { params }) + }, + + // 岗位管理 + positions: { + // 获取岗位列表 + getList: (params) => instance.get('/government/positions', { params }) + }, + + // 养殖户管理 farmers: { // 获取养殖户列表 - getList: params => instance.get('/government/farmers', { params }), + getList: (params) => instance.get('/government/farmers', { params }), // 新增养殖户 - create: data => instance.post('/government/farmers', data), + create: (data) => instance.post('/government/farmers', data), // 编辑养殖户 update: (id, data) => instance.put(`/government/farmers/${id}`, data), // 删除养殖户 - delete: id => instance.delete(`/government/farmers/${id}`), - // 重置密码 - resetPassword: id => instance.post(`/government/farmers/${id}/reset-password`) + delete: (id) => instance.delete(`/government/farmers/${id}`), + // 重置养殖户密码 + resetPassword: (id) => instance.post(`/government/farmers/${id}/reset-password`) }, - // 养殖类型相关 - farmTypes: { - // 获取养殖类型列表 - getList: () => instance.get('/government/farm-types') - }, - // 养殖种类相关 - animalTypes: { - // 获取养殖种类列表 - getList: () => instance.get('/government/animal-types') + + // 养殖类型相关 + farmTypes: { + // 获取养殖类型列表 + getList: () => instance.get('/government/farm-types') + }, + + // 养殖种类相关 + animalTypes: { + // 获取养殖种类列表 + getList: () => instance.get('/government/animal-types') + }, + + // 智能项圈管理 + collars: { + // 获取智能项圈列表 + getList: (params) => instance.get('/government/collars', { params }), + // 新增智能项圈 + create: (data) => instance.post('/government/collars', data), + // 编辑智能项圈 + update: (id, data) => instance.put(`/government/collars/${id}`, data), + // 删除智能项圈 + delete: (id) => instance.delete(`/government/collars/${id}`) + }, + + // 智能耳标管理 + earmarks: { + // 获取智能耳标列表 + getList: (params) => instance.get('/smart-earmark', { params }), + // 新增智能耳标 + create: (data) => instance.post('/smart-earmark', data), + // 编辑智能耳标 + update: (id, data) => instance.put(`/smart-earmark/${id}`, data), + // 删除智能耳标 + delete: (id) => instance.delete(`/smart-earmark/${id}`) + }, + + // 智能主机管理 + smartHosts: { + // 获取智能主机列表 + getList: (params) => instance.get('/smart-host', { params }), + // 新增智能主机 + create: (data) => instance.post('/smart-host', data), + // 编辑智能主机 + update: (id, data) => instance.put(`/smart-host/${id}`, data), + // 删除智能主机 + delete: (id) => instance.delete(`/smart-host/${id}`) + } } } -// 导出所有API -const api = { - auth, - user, - supervision, - approval, - epidemic, - visualization, - file, - staff, - service, - warehouse, - system, - government -} - export default api \ No newline at end of file diff --git a/government-admin/src/views/DeviceWarning.vue b/government-admin/src/views/DeviceWarning.vue new file mode 100644 index 0000000..177be54 --- /dev/null +++ b/government-admin/src/views/DeviceWarning.vue @@ -0,0 +1,284 @@ + + + + + \ No newline at end of file diff --git a/government-admin/src/views/paperless/epidemic/epidemic-agency/EpidemicAgencyManagement.vue b/government-admin/src/views/paperless/epidemic/epidemic-agency/EpidemicAgencyManagement.vue deleted file mode 100644 index c072492..0000000 --- a/government-admin/src/views/paperless/epidemic/epidemic-agency/EpidemicAgencyManagement.vue +++ /dev/null @@ -1,479 +0,0 @@ - - - - - \ No newline at end of file diff --git a/government-admin/src/views/paperless/quarantine/QuarantineRecordQuery.vue b/government-admin/src/views/paperless/quarantine/QuarantineRecordQuery.vue index 660884a..e8cd9fd 100644 --- a/government-admin/src/views/paperless/quarantine/QuarantineRecordQuery.vue +++ b/government-admin/src/views/paperless/quarantine/QuarantineRecordQuery.vue @@ -31,7 +31,7 @@ v-model:value="dateRange" style="width: 300px;" format="YYYY-MM-DD" - placeholder={['申报日期', '申报日期']} + :placeholder="['申报日期', '申报日期']" /> diff --git a/government-admin/src/views/smart-warehouse/smart-hardware/SmartCollar.vue b/government-admin/src/views/smart-warehouse/smart-hardware/SmartCollar.vue index 5309bb1..d8b14ec 100644 --- a/government-admin/src/views/smart-warehouse/smart-hardware/SmartCollar.vue +++ b/government-admin/src/views/smart-warehouse/smart-hardware/SmartCollar.vue @@ -124,6 +124,8 @@ - - diff --git a/government-mini-program/src/app.scss b/government-mini-program/src/app.scss deleted file mode 100644 index 16ffe5b..0000000 --- a/government-mini-program/src/app.scss +++ /dev/null @@ -1,193 +0,0 @@ -// 全局样式文件 -@import './styles/variables.scss'; -@import './styles/mixins.scss'; - -// 重置样式 -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -html, body { - height: 100%; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif; - background-color: #f6f6f6; -} - -// 通用类 -.container { - padding: 20rpx; -} - -.card { - background: #fff; - border-radius: 16rpx; - padding: 30rpx; - margin-bottom: 20rpx; - box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1); -} - -.title { - font-size: 32rpx; - font-weight: 600; - color: #333; - margin-bottom: 20rpx; -} - -.subtitle { - font-size: 28rpx; - font-weight: 500; - color: #666; - margin-bottom: 16rpx; -} - -.text { - font-size: 26rpx; - color: #999; - line-height: 1.5; -} - -.btn { - display: inline-block; - padding: 20rpx 40rpx; - border-radius: 8rpx; - text-align: center; - font-size: 28rpx; - border: none; - cursor: pointer; - transition: all 0.3s; - - &.primary { - background: #1890ff; - color: #fff; - - &:hover { - background: #40a9ff; - } - } - - &.success { - background: #52c41a; - color: #fff; - - &:hover { - background: #73d13d; - } - } - - &.warning { - background: #faad14; - color: #fff; - - &:hover { - background: #ffc53d; - } - } - - &.danger { - background: #ff4d4f; - color: #fff; - - &:hover { - background: #ff7875; - } - } -} - -.flex { - display: flex; - - &.center { - align-items: center; - justify-content: center; - } - - &.between { - justify-content: space-between; - } - - &.around { - justify-content: space-around; - } - - &.column { - flex-direction: column; - } -} - -.mt-10 { margin-top: 10rpx; } -.mt-20 { margin-top: 20rpx; } -.mt-30 { margin-top: 30rpx; } -.mb-10 { margin-bottom: 10rpx; } -.mb-20 { margin-bottom: 20rpx; } -.mb-30 { margin-bottom: 30rpx; } -.ml-10 { margin-left: 10rpx; } -.ml-20 { margin-left: 20rpx; } -.mr-10 { margin-right: 10rpx; } -.mr-20 { margin-right: 20rpx; } - -.p-10 { padding: 10rpx; } -.p-20 { padding: 20rpx; } -.p-30 { padding: 30rpx; } - -// 加载状态 -.loading { - display: flex; - align-items: center; - justify-content: center; - padding: 40rpx; - color: #999; -} - -// 空状态 -.empty { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 80rpx 40rpx; - color: #999; - - .empty-icon { - font-size: 80rpx; - margin-bottom: 20rpx; - } - - .empty-text { - font-size: 28rpx; - } -} - -// 列表项 -.list-item { - display: flex; - align-items: center; - padding: 30rpx 20rpx; - background: #fff; - border-bottom: 1rpx solid #f0f0f0; - - &:last-child { - border-bottom: none; - } - - .item-content { - flex: 1; - } - - .item-title { - font-size: 30rpx; - color: #333; - margin-bottom: 8rpx; - } - - .item-desc { - font-size: 24rpx; - color: #999; - } - - .item-arrow { - font-size: 24rpx; - color: #ccc; - } -} diff --git a/government-mini-program/src/components/Approval.vue b/government-mini-program/src/components/Approval.vue deleted file mode 100644 index ec16069..0000000 --- a/government-mini-program/src/components/Approval.vue +++ /dev/null @@ -1,510 +0,0 @@ - - - - - diff --git a/government-mini-program/src/components/Dashboard.vue b/government-mini-program/src/components/Dashboard.vue deleted file mode 100644 index d930c9d..0000000 --- a/government-mini-program/src/components/Dashboard.vue +++ /dev/null @@ -1,424 +0,0 @@ - - - - - diff --git a/government-mini-program/src/components/Epidemic.vue b/government-mini-program/src/components/Epidemic.vue deleted file mode 100644 index 4cd1945..0000000 --- a/government-mini-program/src/components/Epidemic.vue +++ /dev/null @@ -1,514 +0,0 @@ - - - - - diff --git a/government-mini-program/src/components/Home.vue b/government-mini-program/src/components/Home.vue deleted file mode 100644 index fabd9ff..0000000 --- a/government-mini-program/src/components/Home.vue +++ /dev/null @@ -1,366 +0,0 @@ - - - - - diff --git a/government-mini-program/src/components/Login.vue b/government-mini-program/src/components/Login.vue deleted file mode 100644 index 5c8b423..0000000 --- a/government-mini-program/src/components/Login.vue +++ /dev/null @@ -1,267 +0,0 @@ - - - - - diff --git a/government-mini-program/src/components/Personnel.vue b/government-mini-program/src/components/Personnel.vue deleted file mode 100644 index 377ea94..0000000 --- a/government-mini-program/src/components/Personnel.vue +++ /dev/null @@ -1,503 +0,0 @@ - - - - - diff --git a/government-mini-program/src/components/Profile.vue b/government-mini-program/src/components/Profile.vue deleted file mode 100644 index f4577ac..0000000 --- a/government-mini-program/src/components/Profile.vue +++ /dev/null @@ -1,407 +0,0 @@ - - - - - diff --git a/government-mini-program/src/components/Service.vue b/government-mini-program/src/components/Service.vue deleted file mode 100644 index 08d6a30..0000000 --- a/government-mini-program/src/components/Service.vue +++ /dev/null @@ -1,477 +0,0 @@ - - - - - diff --git a/government-mini-program/src/components/Supervision.vue b/government-mini-program/src/components/Supervision.vue deleted file mode 100644 index d25699e..0000000 --- a/government-mini-program/src/components/Supervision.vue +++ /dev/null @@ -1,663 +0,0 @@ - - - - - diff --git a/government-mini-program/src/components/Warehouse.vue b/government-mini-program/src/components/Warehouse.vue deleted file mode 100644 index dde4c44..0000000 --- a/government-mini-program/src/components/Warehouse.vue +++ /dev/null @@ -1,494 +0,0 @@ - - - - - diff --git a/government-mini-program/src/main.js b/government-mini-program/src/main.js deleted file mode 100644 index 0494fe0..0000000 --- a/government-mini-program/src/main.js +++ /dev/null @@ -1,59 +0,0 @@ -import Vue from 'vue' -import Vuex from 'vuex' -import App from './App.vue' -import VueCompositionAPI from '@vue/composition-api' -import router from './router' - -// 引入全局样式 -import './app.scss' - -// 安装插件 -Vue.use(VueCompositionAPI) -Vue.use(Vuex) - -// 创建store实例 -const store = new Vuex.Store({ - state: { - user: null, - token: null - }, - mutations: { - SET_USER(state, user) { - state.user = user - }, - SET_TOKEN(state, token) { - state.token = token - }, - CLEAR_AUTH(state) { - state.user = null - state.token = null - } - }, - actions: { - setUser({ commit }, user) { - commit('SET_USER', user) - }, - setToken({ commit }, token) { - commit('SET_TOKEN', token) - }, - clearAuth({ commit }) { - commit('CLEAR_AUTH') - } - }, - getters: { - isAuthenticated: state => !!(state.user && state.token) - } -}) - -// 创建应用实例 -const app = new Vue({ - store, - router, - render: h => h(App) -}) - -// 挂载应用 -app.$mount('#app') - -// 导出应用实例 -export default app diff --git a/government-mini-program/src/pages/approval/approval.vue b/government-mini-program/src/pages/approval/approval.vue deleted file mode 100644 index 5ed85f5..0000000 --- a/government-mini-program/src/pages/approval/approval.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/government-mini-program/src/pages/dashboard/dashboard.vue b/government-mini-program/src/pages/dashboard/dashboard.vue deleted file mode 100644 index 5352448..0000000 --- a/government-mini-program/src/pages/dashboard/dashboard.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/government-mini-program/src/pages/epidemic/epidemic.vue b/government-mini-program/src/pages/epidemic/epidemic.vue deleted file mode 100644 index 2027106..0000000 --- a/government-mini-program/src/pages/epidemic/epidemic.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/government-mini-program/src/pages/index/index.vue b/government-mini-program/src/pages/index/index.vue deleted file mode 100644 index b5ee751..0000000 --- a/government-mini-program/src/pages/index/index.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/government-mini-program/src/pages/login/login.vue b/government-mini-program/src/pages/login/login.vue deleted file mode 100644 index fcb3198..0000000 --- a/government-mini-program/src/pages/login/login.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/government-mini-program/src/pages/personnel/personnel.vue b/government-mini-program/src/pages/personnel/personnel.vue deleted file mode 100644 index f4a596b..0000000 --- a/government-mini-program/src/pages/personnel/personnel.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/government-mini-program/src/pages/profile/profile.vue b/government-mini-program/src/pages/profile/profile.vue deleted file mode 100644 index 60a61cf..0000000 --- a/government-mini-program/src/pages/profile/profile.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/government-mini-program/src/pages/service/service.vue b/government-mini-program/src/pages/service/service.vue deleted file mode 100644 index 1e2c00f..0000000 --- a/government-mini-program/src/pages/service/service.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/government-mini-program/src/pages/supervision/supervision.vue b/government-mini-program/src/pages/supervision/supervision.vue deleted file mode 100644 index dbac2d4..0000000 --- a/government-mini-program/src/pages/supervision/supervision.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/government-mini-program/src/pages/warehouse/warehouse.vue b/government-mini-program/src/pages/warehouse/warehouse.vue deleted file mode 100644 index 3ef9152..0000000 --- a/government-mini-program/src/pages/warehouse/warehouse.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/government-mini-program/src/router/index.js b/government-mini-program/src/router/index.js deleted file mode 100644 index e19aad2..0000000 --- a/government-mini-program/src/router/index.js +++ /dev/null @@ -1,108 +0,0 @@ -import Vue from 'vue' -import VueRouter from 'vue-router' -import Home from '@/components/Home.vue' -import Dashboard from '@/components/Dashboard.vue' -import Supervision from '@/components/Supervision.vue' -import Approval from '@/components/Approval.vue' -import Personnel from '@/components/Personnel.vue' -import Epidemic from '@/components/Epidemic.vue' -import Service from '@/components/Service.vue' -import Warehouse from '@/components/Warehouse.vue' -import Profile from '@/components/Profile.vue' -import Login from '@/components/Login.vue' - -Vue.use(VueRouter) - -const routes = [ - { - path: '/', - name: 'Home', - component: Home - }, - { - path: '/login', - name: 'Login', - component: Login - }, - { - path: '/dashboard', - name: 'Dashboard', - component: Dashboard - }, - { - path: '/supervision', - name: 'Supervision', - component: Supervision - }, - { - path: '/approval', - name: 'Approval', - component: Approval - }, - { - path: '/personnel', - name: 'Personnel', - component: Personnel - }, - { - path: '/epidemic', - name: 'Epidemic', - component: Epidemic - }, - { - path: '/service', - name: 'Service', - component: Service - }, - { - path: '/warehouse', - name: 'Warehouse', - component: Warehouse - }, - { - path: '/profile', - name: 'Profile', - component: Profile - } -] - -const router = new VueRouter({ - mode: 'history', - base: process.env.BASE_URL, - routes -}) - -// 路由守卫 -router.beforeEach((to, from, next) => { - // 导入认证工具 - const auth = require('@/utils/auth').default - - // 检查是否需要登录 - const requiresAuth = to.path !== '/login' - const isLoginPage = to.path === '/login' - const isAuthenticated = auth.isAuthenticated() - - console.log('路由守卫:', { - from: from.path, - to: to.path, - requiresAuth, - isLoginPage, - isAuthenticated - }) - - if (requiresAuth && !isAuthenticated) { - // 需要登录但未登录,跳转到登录页 - console.log('需要登录,跳转到登录页') - next('/login') - } else if (isLoginPage && isAuthenticated) { - // 已登录但访问登录页,跳转到首页 - console.log('已登录,跳转到首页') - next('/') - } else { - // 正常访问 - console.log('正常访问') - next() - } -}) - -export default router diff --git a/government-mini-program/src/services/approvalService.js b/government-mini-program/src/services/approvalService.js deleted file mode 100644 index df60496..0000000 --- a/government-mini-program/src/services/approvalService.js +++ /dev/null @@ -1,44 +0,0 @@ -import { get, post, put, del } from '@/utils/request' - -// 审批管理相关API -export const approvalService = { - // 获取审批列表 - getApprovalList(params = {}) { - return get('/approval/list', params) - }, - - // 获取审批详情 - getApprovalDetail(id) { - return get(`/approval/${id}`) - }, - - // 创建审批 - createApproval(data) { - return post('/approval', data) - }, - - // 更新审批 - updateApproval(id, data) { - return put(`/approval/${id}`, data) - }, - - // 删除审批 - deleteApproval(id) { - return del(`/approval/${id}`) - }, - - // 审批通过 - approve(id, data = {}) { - return post(`/approval/${id}/approve`, data) - }, - - // 审批拒绝 - reject(id, data = {}) { - return post(`/approval/${id}/reject`, data) - }, - - // 获取审批统计 - getStats() { - return get('/approval/stats') - } -} diff --git a/government-mini-program/src/services/authService.js b/government-mini-program/src/services/authService.js deleted file mode 100644 index ad26d89..0000000 --- a/government-mini-program/src/services/authService.js +++ /dev/null @@ -1,22 +0,0 @@ -import { post, get } from '@/utils/request' - -// 认证相关API -export const authService = { - // 用户登录 - login(username, password) { - return post('/auth/login', { - username, - password - }) - }, - - // 获取用户信息 - getUserInfo() { - return get('/auth/userinfo') - }, - - // 登出 - logout() { - return post('/auth/logout') - } -} diff --git a/government-mini-program/src/services/dashboardService.js b/government-mini-program/src/services/dashboardService.js deleted file mode 100644 index e1f2795..0000000 --- a/government-mini-program/src/services/dashboardService.js +++ /dev/null @@ -1,39 +0,0 @@ -import { get } from '@/utils/request' - -// 数据看板相关API -export const dashboardService = { - // 获取统计数据 - getStats() { - return get('/visualization/data') - }, - - // 获取监管统计 - getSupervisionStats() { - return get('/supervision/stats') - }, - - // 获取审批统计 - getApprovalStats() { - return get('/approval/stats') - }, - - // 获取人员统计 - getPersonnelStats() { - return get('/personnel/stats') - }, - - // 获取疫情统计 - getEpidemicStats() { - return get('/epidemic/stats') - }, - - // 获取服务统计 - getServiceStats() { - return get('/service/stats') - }, - - // 获取仓库统计 - getWarehouseStats() { - return get('/warehouse/stats') - } -} diff --git a/government-mini-program/src/services/epidemicService.js b/government-mini-program/src/services/epidemicService.js deleted file mode 100644 index c89d4a8..0000000 --- a/government-mini-program/src/services/epidemicService.js +++ /dev/null @@ -1,34 +0,0 @@ -import { get, post, put, del } from '@/utils/request' - -// 疫情监控相关API -export const epidemicService = { - // 获取疫情列表 - getEpidemicList(params = {}) { - return get('/epidemic/list', params) - }, - - // 获取疫情详情 - getEpidemicDetail(id) { - return get(`/epidemic/${id}`) - }, - - // 创建疫情记录 - createEpidemic(data) { - return post('/epidemic', data) - }, - - // 更新疫情记录 - updateEpidemic(id, data) { - return put(`/epidemic/${id}`, data) - }, - - // 删除疫情记录 - deleteEpidemic(id) { - return del(`/epidemic/${id}`) - }, - - // 获取疫情统计 - getStats() { - return get('/epidemic/stats') - } -} diff --git a/government-mini-program/src/services/personnelService.js b/government-mini-program/src/services/personnelService.js deleted file mode 100644 index 680fdd9..0000000 --- a/government-mini-program/src/services/personnelService.js +++ /dev/null @@ -1,34 +0,0 @@ -import { get, post, put, del } from '@/utils/request' - -// 人员管理相关API -export const personnelService = { - // 获取人员列表 - getPersonnelList(params = {}) { - return get('/personnel/list', params) - }, - - // 获取人员详情 - getPersonnelDetail(id) { - return get(`/personnel/${id}`) - }, - - // 创建人员 - createPersonnel(data) { - return post('/personnel', data) - }, - - // 更新人员 - updatePersonnel(id, data) { - return put(`/personnel/${id}`, data) - }, - - // 删除人员 - deletePersonnel(id) { - return del(`/personnel/${id}`) - }, - - // 获取人员统计 - getStats() { - return get('/personnel/stats') - } -} diff --git a/government-mini-program/src/services/serviceService.js b/government-mini-program/src/services/serviceService.js deleted file mode 100644 index ef477b3..0000000 --- a/government-mini-program/src/services/serviceService.js +++ /dev/null @@ -1,34 +0,0 @@ -import { get, post, put, del } from '@/utils/request' - -// 服务管理相关API -export const serviceService = { - // 获取服务列表 - getServiceList(params = {}) { - return get('/service/list', params) - }, - - // 获取服务详情 - getServiceDetail(id) { - return get(`/service/${id}`) - }, - - // 创建服务 - createService(data) { - return post('/service', data) - }, - - // 更新服务 - updateService(id, data) { - return put(`/service/${id}`, data) - }, - - // 删除服务 - deleteService(id) { - return del(`/service/${id}`) - }, - - // 获取服务统计 - getStats() { - return get('/service/stats') - } -} diff --git a/government-mini-program/src/services/supervisionService.js b/government-mini-program/src/services/supervisionService.js deleted file mode 100644 index 8df8be6..0000000 --- a/government-mini-program/src/services/supervisionService.js +++ /dev/null @@ -1,34 +0,0 @@ -import { get, post, put, del } from '@/utils/request' - -// 监管管理相关API -export const supervisionService = { - // 获取监管列表 - getSupervisionList(params = {}) { - return get('/supervision/list', params) - }, - - // 获取监管详情 - getSupervisionDetail(id) { - return get(`/supervision/${id}`) - }, - - // 创建监管记录 - createSupervision(data) { - return post('/supervision', data) - }, - - // 更新监管记录 - updateSupervision(id, data) { - return put(`/supervision/${id}`, data) - }, - - // 删除监管记录 - deleteSupervision(id) { - return del(`/supervision/${id}`) - }, - - // 获取监管统计 - getStats() { - return get('/supervision/stats') - } -} diff --git a/government-mini-program/src/services/warehouseService.js b/government-mini-program/src/services/warehouseService.js deleted file mode 100644 index df8d501..0000000 --- a/government-mini-program/src/services/warehouseService.js +++ /dev/null @@ -1,34 +0,0 @@ -import { get, post, put, del } from '@/utils/request' - -// 仓库管理相关API -export const warehouseService = { - // 获取仓库列表 - getWarehouseList(params = {}) { - return get('/warehouse/list', params) - }, - - // 获取仓库详情 - getWarehouseDetail(id) { - return get(`/warehouse/${id}`) - }, - - // 创建仓库 - createWarehouse(data) { - return post('/warehouse', data) - }, - - // 更新仓库 - updateWarehouse(id, data) { - return put(`/warehouse/${id}`, data) - }, - - // 删除仓库 - deleteWarehouse(id) { - return del(`/warehouse/${id}`) - }, - - // 获取仓库统计 - getStats() { - return get('/warehouse/stats') - } -} diff --git a/government-mini-program/src/styles/mixins.scss b/government-mini-program/src/styles/mixins.scss deleted file mode 100644 index 13c06fa..0000000 --- a/government-mini-program/src/styles/mixins.scss +++ /dev/null @@ -1,89 +0,0 @@ -// 混入样式 - -// 文本省略 -@mixin text-ellipsis($lines: 1) { - @if $lines == 1 { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } @else { - display: -webkit-box; - -webkit-line-clamp: $lines; - -webkit-box-orient: vertical; - overflow: hidden; - } -} - -// 清除浮动 -@mixin clearfix { - &::after { - content: ''; - display: table; - clear: both; - } -} - -// 居中 -@mixin center($direction: both) { - @if $direction == both { - display: flex; - align-items: center; - justify-content: center; - } @else if $direction == horizontal { - display: flex; - justify-content: center; - } @else if $direction == vertical { - display: flex; - align-items: center; - } -} - -// 按钮样式 -@mixin button($bg-color: $primary-color, $text-color: #fff) { - display: inline-block; - padding: 20rpx 40rpx; - background: $bg-color; - color: $text-color; - border: none; - border-radius: $border-radius-base; - text-align: center; - font-size: $font-size-lg; - cursor: pointer; - transition: all 0.3s; - - &:hover { - opacity: 0.8; - } - - &:active { - transform: scale(0.98); - } -} - -// 卡片样式 -@mixin card { - background: $background-color-white; - border-radius: $border-radius-lg; - padding: $spacing-lg; - margin-bottom: $spacing-base; - box-shadow: $box-shadow-base; -} - -// 输入框样式 -@mixin input { - width: 100%; - padding: 24rpx 20rpx; - border: 1rpx solid $border-color; - border-radius: $border-radius-base; - font-size: $font-size-lg; - background: $background-color-white; - - &:focus { - border-color: $primary-color; - outline: none; - } - - &::placeholder { - color: $text-color-placeholder; - } -} diff --git a/government-mini-program/src/styles/variables.scss b/government-mini-program/src/styles/variables.scss deleted file mode 100644 index 7079e9b..0000000 --- a/government-mini-program/src/styles/variables.scss +++ /dev/null @@ -1,44 +0,0 @@ -// 颜色变量 -$primary-color: #1890ff; -$success-color: #52c41a; -$warning-color: #faad14; -$danger-color: #ff4d4f; -$info-color: #1890ff; - -$text-color: #333; -$text-color-secondary: #666; -$text-color-disabled: #999; -$text-color-placeholder: #ccc; - -$background-color: #f6f6f6; -$background-color-light: #fafafa; -$background-color-white: #fff; - -$border-color: #d9d9d9; -$border-color-light: #f0f0f0; - -// 字体大小 -$font-size-xs: 20rpx; -$font-size-sm: 24rpx; -$font-size-base: 26rpx; -$font-size-lg: 28rpx; -$font-size-xl: 30rpx; -$font-size-xxl: 32rpx; - -// 间距 -$spacing-xs: 8rpx; -$spacing-sm: 16rpx; -$spacing-base: 20rpx; -$spacing-lg: 30rpx; -$spacing-xl: 40rpx; -$spacing-xxl: 60rpx; - -// 圆角 -$border-radius-sm: 4rpx; -$border-radius-base: 8rpx; -$border-radius-lg: 16rpx; - -// 阴影 -$box-shadow-sm: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); -$box-shadow-base: 0 2rpx 12rpx rgba(0, 0, 0, 0.1); -$box-shadow-lg: 0 4rpx 16rpx rgba(0, 0, 0, 0.1); diff --git a/government-mini-program/src/utils/auth.js b/government-mini-program/src/utils/auth.js deleted file mode 100644 index 2235b0c..0000000 --- a/government-mini-program/src/utils/auth.js +++ /dev/null @@ -1,134 +0,0 @@ -// 认证工具类 -class Auth { - constructor() { - this.tokenKey = 'government_token' - this.userKey = 'government_user' - } - - // 设置token - setToken(token) { - try { - // 优先使用localStorage(H5环境) - if (typeof localStorage !== 'undefined') { - localStorage.setItem(this.tokenKey, token) - } else if (typeof uni !== 'undefined') { - // uni-app环境 - uni.setStorageSync(this.tokenKey, token) - } - return true - } catch (error) { - console.error('设置token失败:', error) - return false - } - } - - // 获取token - getToken() { - try { - // 优先使用localStorage(H5环境) - if (typeof localStorage !== 'undefined') { - return localStorage.getItem(this.tokenKey) || '' - } else if (typeof uni !== 'undefined') { - // uni-app环境 - return uni.getStorageSync(this.tokenKey) || '' - } - return '' - } catch (error) { - console.error('获取token失败:', error) - return '' - } - } - - // 清除token - clearToken() { - try { - // 优先使用localStorage(H5环境) - if (typeof localStorage !== 'undefined') { - localStorage.removeItem(this.tokenKey) - localStorage.removeItem(this.userKey) - } else if (typeof uni !== 'undefined') { - // uni-app环境 - uni.removeStorageSync(this.tokenKey) - uni.removeStorageSync(this.userKey) - } - return true - } catch (error) { - console.error('清除token失败:', error) - return false - } - } - - // 设置用户信息 - setUser(user) { - try { - // 优先使用localStorage(H5环境) - if (typeof localStorage !== 'undefined') { - localStorage.setItem(this.userKey, JSON.stringify(user)) - } else if (typeof uni !== 'undefined') { - // uni-app环境 - uni.setStorageSync(this.userKey, user) - } - return true - } catch (error) { - console.error('设置用户信息失败:', error) - return false - } - } - - // 获取用户信息 - getUser() { - try { - // 优先使用localStorage(H5环境) - if (typeof localStorage !== 'undefined') { - const userStr = localStorage.getItem(this.userKey) - return userStr ? JSON.parse(userStr) : null - } else if (typeof uni !== 'undefined') { - // uni-app环境 - return uni.getStorageSync(this.userKey) || null - } - return null - } catch (error) { - console.error('获取用户信息失败:', error) - return null - } - } - - // 检查是否已登录 - isAuthenticated() { - const token = this.getToken() - const user = this.getUser() - return !!(token && user) - } - - // 登出 - logout() { - this.clearToken() - // 跳转到登录页 - if (typeof uni !== 'undefined') { - uni.reLaunch({ - url: '/pages/login/login' - }) - } else { - // H5环境 - window.location.href = '/login' - } - } - - // 检查token是否过期 - isTokenExpired() { - const token = this.getToken() - if (!token) return true - - try { - // 简单的token过期检查(实际项目中应该解析JWT) - const tokenData = JSON.parse(atob(token.split('.')[1])) - const currentTime = Math.floor(Date.now() / 1000) - return tokenData.exp < currentTime - } catch (error) { - console.error('解析token失败:', error) - return true - } - } -} - -export default new Auth() diff --git a/government-mini-program/src/utils/request.js b/government-mini-program/src/utils/request.js deleted file mode 100644 index 92961d7..0000000 --- a/government-mini-program/src/utils/request.js +++ /dev/null @@ -1,222 +0,0 @@ -import auth from './auth' - -// API基础配置 -const BASE_URL = process.env.NODE_ENV === 'development' - ? 'http://localhost:5352/api' - : 'https://your-domain.com/api' - -// 请求拦截器 -const request = (options) => { - return new Promise((resolve, reject) => { - // 添加认证头 - const token = auth.getToken() - if (token) { - options.headers = { - ...options.headers, - 'Authorization': `Bearer ${token}` - } - } - - // 添加基础URL - if (!options.url.startsWith('http')) { - options.url = BASE_URL + options.url - } - - // 添加默认配置 - const config = { - timeout: 10000, - ...options - } - - console.log('发起请求:', config) - - // 检查环境,使用不同的请求方法 - if (typeof uni !== 'undefined') { - // uni-app环境 - uni.request({ - ...config, - success: (res) => { - console.log('请求成功:', res) - - // 检查响应状态 - if (res.statusCode === 200) { - const { code, message, data } = res.data - - if (code === 200) { - resolve(data) - } else if (code === 401) { - // token过期,清除本地存储并跳转登录 - auth.clearToken() - uni.reLaunch({ - url: '/pages/login/login' - }) - reject(new Error(message || '认证失败')) - } else { - reject(new Error(message || '请求失败')) - } - } else { - reject(new Error(`请求失败: ${res.statusCode}`)) - } - }, - fail: (err) => { - console.error('请求失败:', err) - reject(new Error(err.errMsg || '网络请求失败')) - } - }) - } else { - // H5环境,使用fetch - fetch(config.url, { - method: config.method || 'GET', - headers: { - 'Content-Type': 'application/json', - ...config.headers - }, - body: config.data ? JSON.stringify(config.data) : undefined - }) - .then(response => { - console.log('请求成功:', response) - - if (response.ok) { - return response.json() - } else { - throw new Error(`HTTP ${response.status}: ${response.statusText}`) - } - }) - .then(data => { - const { code, message, data: responseData } = data - - if (code === 200) { - resolve(responseData) - } else if (code === 401) { - // token过期,清除本地存储并跳转登录 - auth.clearToken() - window.location.href = '/login' - reject(new Error(message || '认证失败')) - } else { - reject(new Error(message || '请求失败')) - } - }) - .catch(error => { - console.error('请求失败:', error) - reject(new Error(error.message || '网络请求失败')) - }) - } - }) -} - -// GET请求 -export const get = (url, params = {}) => { - const queryString = Object.keys(params) - .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`) - .join('&') - - const fullUrl = queryString ? `${url}?${queryString}` : url - - return request({ - url: fullUrl, - method: 'GET' - }) -} - -// POST请求 -export const post = (url, data = {}) => { - return request({ - url, - method: 'POST', - data, - header: { - 'Content-Type': 'application/json' - } - }) -} - -// PUT请求 -export const put = (url, data = {}) => { - return request({ - url, - method: 'PUT', - data, - header: { - 'Content-Type': 'application/json' - } - }) -} - -// DELETE请求 -export const del = (url) => { - return request({ - url, - method: 'DELETE' - }) -} - -// 文件上传 -export const upload = (url, filePath, formData = {}) => { - return new Promise((resolve, reject) => { - const token = auth.getToken() - - if (typeof uni !== 'undefined') { - // uni-app环境 - uni.uploadFile({ - url: BASE_URL + url, - filePath, - name: 'file', - formData, - header: { - 'Authorization': token ? `Bearer ${token}` : '' - }, - success: (res) => { - try { - const data = JSON.parse(res.data) - if (data.code === 200) { - resolve(data.data) - } else { - reject(new Error(data.message || '上传失败')) - } - } catch (error) { - reject(new Error('解析响应失败')) - } - }, - fail: (err) => { - reject(new Error(err.errMsg || '上传失败')) - } - }) - } else { - // H5环境,使用FormData - const form = new FormData() - form.append('file', filePath) - - // 添加其他表单数据 - Object.keys(formData).forEach(key => { - form.append(key, formData[key]) - }) - - fetch(BASE_URL + url, { - method: 'POST', - headers: { - 'Authorization': token ? `Bearer ${token}` : '' - }, - body: form - }) - .then(response => response.json()) - .then(data => { - if (data.code === 200) { - resolve(data.data) - } else { - reject(new Error(data.message || '上传失败')) - } - }) - .catch(error => { - reject(new Error(error.message || '上传失败')) - }) - } - }) -} - -export default { - get, - post, - put, - delete: del, - upload -} diff --git a/government-mini-program/src/utils/uni-compat.js b/government-mini-program/src/utils/uni-compat.js deleted file mode 100644 index 9dcca15..0000000 --- a/government-mini-program/src/utils/uni-compat.js +++ /dev/null @@ -1,109 +0,0 @@ -// uni-app兼容工具,用于H5环境 -export const uni = { - // 显示提示 - showToast(options) { - if (typeof uni !== 'undefined') { - return uni.showToast(options) - } else { - // H5环境 - alert(options.title || '提示') - } - }, - - // 显示模态框 - showModal(options) { - if (typeof uni !== 'undefined') { - return uni.showModal(options) - } else { - // H5环境 - return new Promise((resolve) => { - const result = confirm(options.content || '确认操作?') - resolve({ confirm: result, cancel: !result }) - }) - } - }, - - // 页面跳转 - navigateTo(options) { - if (typeof uni !== 'undefined') { - return uni.navigateTo(options) - } else { - // H5环境 - window.location.href = options.url - } - }, - - // 重新启动 - reLaunch(options) { - if (typeof uni !== 'undefined') { - return uni.reLaunch(options) - } else { - // H5环境 - window.location.href = options.url - } - }, - - // 选择图片 - chooseImage(options) { - if (typeof uni !== 'undefined') { - return uni.chooseImage(options) - } else { - // H5环境 - const input = document.createElement('input') - input.type = 'file' - input.accept = 'image/*' - input.multiple = options.count > 1 - input.onchange = (e) => { - const files = Array.from(e.target.files) - const tempFilePaths = files.map(file => URL.createObjectURL(file)) - options.success && options.success({ tempFilePaths }) - } - input.click() - } - }, - - // 下拉刷新 - stopPullDownRefresh() { - if (typeof uni !== 'undefined') { - return uni.stopPullDownRefresh() - } - // H5环境不需要处理 - }, - - // 设置存储 - setStorageSync(key, data) { - if (typeof uni !== 'undefined') { - return uni.setStorageSync(key, data) - } else { - // H5环境 - localStorage.setItem(key, JSON.stringify(data)) - } - }, - - // 获取存储 - getStorageSync(key) { - if (typeof uni !== 'undefined') { - return uni.getStorageSync(key) - } else { - // H5环境 - const data = localStorage.getItem(key) - try { - return data ? JSON.parse(data) : '' - } catch { - return data || '' - } - } - }, - - // 移除存储 - removeStorageSync(key) { - if (typeof uni !== 'undefined') { - return uni.removeStorageSync(key) - } else { - // H5环境 - localStorage.removeItem(key) - } - } -} - -export default uni diff --git a/scripts/README.md b/scripts/README.md deleted file mode 100644 index 1ccf1ad..0000000 --- a/scripts/README.md +++ /dev/null @@ -1,167 +0,0 @@ -# 执行脚本目录 - -本目录包含宁夏智慧养殖监管平台的运维脚本和工具脚本。 - -## 📋 脚本索引 - -### 🗄️ 数据库管理 -- [`init-db.js`](init-db.js) - 数据库初始化脚本 -- [`migration-manager.js`](migration-manager.js) - 数据库迁移管理脚本 -- [`seed-manager.js`](seed-manager.js) - 种子数据管理脚本 -- [`database/create_tables.sql`](database/create_tables.sql) - 完整数据库表结构创建脚本 -- [`database/schema.sql`](database/schema.sql) - 基础数据库结构脚本 - -### 🔗 连接测试 -- [`test-connection.js`](test-connection.js) - 数据库连接测试脚本 -- [`test-map-api.js`](test-map-api.js) - 百度地图API连接测试脚本 - -## 🚀 脚本使用方法 - -### 数据库初始化 -```bash -# 初始化数据库结构和基础数据 -node scripts/init-db.js - -# 或者在backend目录下使用npm脚本 -cd backend -npm run init-db -``` - -### 数据库迁移管理 -```bash -# 运行所有未执行的迁移 -node scripts/migration-manager.js up - -# 回滚最后一次迁移 -node scripts/migration-manager.js down - -# 查看迁移状态 -node scripts/migration-manager.js status -``` - -### 种子数据管理 -```bash -# 填充所有种子数据 -node scripts/seed-manager.js all - -# 填充特定种子数据 -node scripts/seed-manager.js users -node scripts/seed-manager.js farms - -# 清空种子数据 -node scripts/seed-manager.js clear -``` - -### 连接测试 -```bash -# 测试数据库连接 -node scripts/test-connection.js - -# 测试百度地图API连接 -node scripts/test-map-api.js -``` - -## ⚙️ 环境要求 - -运行这些脚本需要: - -1. **Node.js 18.0+** - JavaScript运行环境 -2. **MySQL 8.0+** - 数据库服务 -3. **环境变量配置** - 正确配置的.env文件 - -## 🔧 配置说明 - -### 环境变量 -脚本依赖以下环境变量(通常在backend/.env文件中配置): - -```bash -# 数据库配置 -DB_HOST=localhost -DB_PORT=3306 -DB_NAME=nxxmdata -DB_USER=root -DB_PASSWORD=your_password - -# 百度地图API配置 -BAIDU_MAP_API_KEY=your_api_key - -# 其他配置 -NODE_ENV=development -``` - -### 权限要求 -- 数据库用户需要有创建、删除、修改表的权限 -- 百度地图API密钥需要有效且未超过调用限制 - -## 📊 日志和输出 - -脚本运行时会输出详细的日志信息: -- ✅ 成功操作以绿色显示 -- ❌ 错误信息以红色显示 -- ⚠️ 警告信息以黄色显示 -- ℹ️ 信息性消息以蓝色显示 - -## 🛡️ 安全注意事项 - -1. **生产环境**: 在生产环境运行脚本前务必备份数据 -2. **权限控制**: 确保脚本执行用户具有必要的最小权限 -3. **敏感数据**: 不要在脚本中硬编码敏感信息 -4. **审计日志**: 生产环境执行脚本应记录详细的审计日志 - -## 🔄 脚本开发规范 - -如需新增脚本,请遵循以下规范: - -### 文件命名 -- 使用 kebab-case 命名: `example-script.js` -- 功能明确的描述性名称 -- 按功能分类存放 - -### 代码结构 -```javascript -#!/usr/bin/env node - -/** - * 脚本功能说明 - * 用法: node scripts/example-script.js [参数] - */ - -const path = require('path'); -const { logger } = require('../backend/utils/logger'); - -// 主要功能函数 -async function main() { - try { - // 脚本逻辑 - logger.info('脚本执行成功'); - } catch (error) { - logger.error('脚本执行失败:', error); - process.exit(1); - } -} - -// 如果直接运行脚本则执行main函数 -if (require.main === module) { - main(); -} - -module.exports = { main }; -``` - -### 错误处理 -- 使用统一的日志记录 -- 合适的错误码退出 -- 详细的错误信息输出 - -## 📞 支持和帮助 - -如果在使用脚本过程中遇到问题: - -1. 检查环境变量配置是否正确 -2. 确认数据库连接是否正常 -3. 查看脚本输出的错误信息 -4. 参考项目文档或联系技术支持 - ---- - -*最后更新: 2025年1月* \ No newline at end of file diff --git a/scripts/database/create_tables.sql b/scripts/database/create_tables.sql deleted file mode 100644 index 96b2aa4..0000000 --- a/scripts/database/create_tables.sql +++ /dev/null @@ -1,270 +0,0 @@ --- 宁夏智慧养殖监管平台数据库表结构脚本 --- 基于Sequelize模型定义生成 --- 创建时间: 2024年 - --- 设置字符集和排序规则 -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- 创建数据库(如果不存在) --- CREATE DATABASE IF NOT EXISTS nxxmdata CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; --- USE nxxmdata; - --- ============================================ --- 用户相关表 --- ============================================ - --- 用户表 -CREATE TABLE IF NOT EXISTS `users` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户唯一标识', - `username` varchar(50) NOT NULL COMMENT '用户名', - `email` varchar(100) NOT NULL COMMENT '邮箱', - `password` varchar(255) NOT NULL COMMENT '密码(加密后)', - `phone` varchar(20) DEFAULT NULL COMMENT '手机号', - `avatar` varchar(255) DEFAULT NULL COMMENT '头像URL', - `status` enum('active','inactive','banned') DEFAULT 'active' COMMENT '用户状态', - `last_login` datetime DEFAULT NULL COMMENT '最后登录时间', - `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `username` (`username`), - UNIQUE KEY `email` (`email`), - KEY `idx_status` (`status`), - KEY `idx_created_at` (`created_at`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表'; - --- 角色表 -CREATE TABLE IF NOT EXISTS `roles` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色唯一标识', - `name` varchar(50) NOT NULL COMMENT '角色名称', - `description` text DEFAULT NULL COMMENT '角色描述', - `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色表'; - --- 用户角色关联表 -CREATE TABLE IF NOT EXISTS `user_roles` ( - `user_id` int(11) NOT NULL COMMENT '用户ID', - `role_id` int(11) NOT NULL COMMENT '角色ID', - `assigned_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '分配时间', - PRIMARY KEY (`user_id`, `role_id`), - KEY `fk_user_roles_role_id` (`role_id`), - CONSTRAINT `fk_user_roles_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_user_roles_role_id` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户角色关联表'; - --- ============================================ --- 养殖场相关表 --- ============================================ - --- 养殖场表 -CREATE TABLE IF NOT EXISTS `farms` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '养殖场唯一标识', - `name` varchar(100) NOT NULL COMMENT '养殖场名称', - `type` varchar(50) NOT NULL COMMENT '养殖场类型', - `location` json NOT NULL COMMENT '地理位置信息(经纬度等)', - `address` varchar(255) DEFAULT NULL COMMENT '详细地址', - `contact` varchar(50) DEFAULT NULL COMMENT '联系人', - `phone` varchar(20) DEFAULT NULL COMMENT '联系电话', - `status` enum('active','inactive','maintenance') DEFAULT 'active' COMMENT '养殖场状态', - `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `idx_type` (`type`), - KEY `idx_status` (`status`), - KEY `idx_created_at` (`created_at`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='养殖场表'; - --- 动物表 -CREATE TABLE IF NOT EXISTS `animals` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '动物记录唯一标识', - `type` varchar(50) NOT NULL COMMENT '动物类型', - `count` int(11) NOT NULL DEFAULT 0 COMMENT '数量', - `farm_id` int(11) NOT NULL COMMENT '所属养殖场ID', - `health_status` enum('healthy','sick','quarantine') DEFAULT 'healthy' COMMENT '健康状态', - `last_inspection` datetime DEFAULT NULL COMMENT '最后检查时间', - `notes` text DEFAULT NULL COMMENT '备注信息', - `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `fk_animals_farm_id` (`farm_id`), - KEY `idx_type` (`type`), - KEY `idx_health_status` (`health_status`), - CONSTRAINT `fk_animals_farm_id` FOREIGN KEY (`farm_id`) REFERENCES `farms` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='动物表'; - --- 设备表 -CREATE TABLE IF NOT EXISTS `devices` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '设备唯一标识', - `name` varchar(100) NOT NULL COMMENT '设备名称', - `type` varchar(50) NOT NULL COMMENT '设备类型', - `status` enum('online','offline','maintenance') DEFAULT 'offline' COMMENT '设备状态', - `farm_id` int(11) NOT NULL COMMENT '所属养殖场ID', - `last_maintenance` datetime DEFAULT NULL COMMENT '最后维护时间', - `installation_date` datetime DEFAULT NULL COMMENT '安装日期', - `metrics` json DEFAULT NULL COMMENT '设备指标数据', - `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `fk_devices_farm_id` (`farm_id`), - KEY `idx_type` (`type`), - KEY `idx_status` (`status`), - CONSTRAINT `fk_devices_farm_id` FOREIGN KEY (`farm_id`) REFERENCES `farms` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='设备表'; - --- 预警表 -CREATE TABLE IF NOT EXISTS `alerts` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '预警唯一标识', - `type` varchar(50) NOT NULL COMMENT '预警类型', - `level` enum('low','medium','high','critical') DEFAULT 'medium' COMMENT '预警级别', - `message` text NOT NULL COMMENT '预警消息', - `status` enum('active','acknowledged','resolved') DEFAULT 'active' COMMENT '预警状态', - `farm_id` int(11) NOT NULL COMMENT '所属养殖场ID', - `device_id` int(11) DEFAULT NULL COMMENT '关联设备ID', - `resolved_at` datetime DEFAULT NULL COMMENT '解决时间', - `resolved_by` int(11) DEFAULT NULL COMMENT '解决人ID', - `resolution_notes` text DEFAULT NULL COMMENT '解决说明', - `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `fk_alerts_farm_id` (`farm_id`), - KEY `fk_alerts_device_id` (`device_id`), - KEY `fk_alerts_resolved_by` (`resolved_by`), - KEY `idx_type` (`type`), - KEY `idx_level` (`level`), - KEY `idx_status` (`status`), - KEY `idx_created_at` (`created_at`), - CONSTRAINT `fk_alerts_farm_id` FOREIGN KEY (`farm_id`) REFERENCES `farms` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_alerts_device_id` FOREIGN KEY (`device_id`) REFERENCES `devices` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_alerts_resolved_by` FOREIGN KEY (`resolved_by`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='预警表'; - --- ============================================ --- 商品订单相关表 --- ============================================ - --- 产品表 -CREATE TABLE IF NOT EXISTS `products` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '产品唯一标识', - `name` varchar(100) NOT NULL COMMENT '产品名称', - `description` text DEFAULT NULL COMMENT '产品描述', - `price` int(11) NOT NULL COMMENT '产品价格(单位:分)', - `stock` int(11) NOT NULL DEFAULT 0 COMMENT '库存数量', - `image_url` varchar(255) DEFAULT NULL COMMENT '产品图片URL', - `is_active` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否激活', - `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `idx_name` (`name`), - KEY `idx_is_active` (`is_active`), - KEY `idx_created_at` (`created_at`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品表'; - --- 订单表 -CREATE TABLE IF NOT EXISTS `orders` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单唯一标识', - `user_id` int(11) NOT NULL COMMENT '用户ID', - `total_amount` int(11) NOT NULL DEFAULT 0 COMMENT '订单总金额(单位:分)', - `status` enum('pending','processing','shipped','delivered','cancelled') NOT NULL DEFAULT 'pending' COMMENT '订单状态', - `payment_status` enum('unpaid','paid','refunded') NOT NULL DEFAULT 'unpaid' COMMENT '支付状态', - `shipping_address` text DEFAULT NULL COMMENT '收货地址', - `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `fk_orders_user_id` (`user_id`), - KEY `idx_status` (`status`), - KEY `idx_payment_status` (`payment_status`), - KEY `idx_created_at` (`created_at`), - CONSTRAINT `fk_orders_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单表'; - --- 订单项表 -CREATE TABLE IF NOT EXISTS `order_items` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单项唯一标识', - `order_id` int(11) NOT NULL COMMENT '订单ID', - `product_id` int(11) NOT NULL COMMENT '产品ID', - `quantity` int(11) NOT NULL DEFAULT 1 COMMENT '数量', - `price` int(11) NOT NULL COMMENT '单价(单位:分)', - `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `fk_order_items_order_id` (`order_id`), - KEY `fk_order_items_product_id` (`product_id`), - CONSTRAINT `fk_order_items_order_id` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_order_items_product_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单项表'; - --- ============================================ --- 初始化数据 --- ============================================ - --- 插入默认角色 -INSERT IGNORE INTO `roles` (`name`, `description`) VALUES -('admin', '系统管理员'), -('manager', '养殖场管理员'), -('operator', '操作员'), -('viewer', '查看者'); - --- 插入默认管理员用户(密码: admin123,使用bcrypt加密) -INSERT IGNORE INTO `users` (`username`, `email`, `password`, `phone`, `avatar`, `status`) VALUES -('admin', 'admin@nxxmdata.com', '$2b$10$kWV4BQk3P4iSn79kQEEoduByeVo8kv41r7FI04mON1/zcrpF7.kn6', '13800138000', '/uploads/avatars/default.png', 'active'), -('manager', 'manager@nxxmdata.com', '$2b$10$kWV4BQk3P4iSn79kQEEoduByeVo8kv41r7FI04mON1/zcrpF7.kn6', '13800138001', '/uploads/avatars/default.png', 'active'), -('user', 'user@nxxmdata.com', '$2b$10$kWV4BQk3P4iSn79kQEEoduByeVo8kv41r7FI04mON1/zcrpF7.kn6', '13800138002', '/uploads/avatars/default.png', 'active'); - --- 为用户分配角色 -INSERT IGNORE INTO `user_roles` (`user_id`, `role_id`) -SELECT u.id, r.id -FROM `users` u, `roles` r -WHERE (u.username = 'admin' AND r.name = 'admin') - OR (u.username = 'manager' AND r.name = 'manager') - OR (u.username = 'user' AND r.name = 'viewer'); - --- 插入示例养殖场数据 -INSERT IGNORE INTO `farms` (`name`, `type`, `location`, `address`, `contact`, `phone`, `status`) VALUES -('宁夏示范养殖场', '奶牛养殖', '{"latitude": 38.46667, "longitude": 106.26667}', '宁夏回族自治区银川市金凤区', '张经理', '13800138000', 'active'), -('银川现代化养殖基地', '肉牛养殖', '{"latitude": 38.48779, "longitude": 106.22541}', '宁夏回族自治区银川市兴庆区', '李主任', '13800138001', 'active'), -('吴忠生态养殖园', '综合养殖', '{"latitude": 37.98629, "longitude": 106.19273}', '宁夏回族自治区吴忠市利通区', '王场长', '13800138002', 'active'); - --- 插入示例动物数据 -INSERT IGNORE INTO `animals` (`type`, `count`, `farm_id`, `health_status`, `last_inspection`) VALUES -('奶牛', 500, 1, 'healthy', '2025-01-18 10:00:00'), -('肉牛', 300, 2, 'healthy', '2025-01-18 09:30:00'), -('绵羊', 1000, 3, 'healthy', '2025-01-18 11:00:00'); - --- 插入示例设备数据 -INSERT IGNORE INTO `devices` (`name`, `type`, `status`, `farm_id`, `installation_date`, `metrics`) VALUES -('温度传感器001', '温度传感器', 'online', 1, '2024-01-01', '{"temperature": 25.5, "humidity": 60}'), -('湿度传感器001', '湿度传感器', 'online', 1, '2024-01-01', '{"temperature": 25.3, "humidity": 62}'), -('监控摄像头001', '摄像头', 'online', 2, '2024-02-01', '{"status": "normal", "resolution": "1080p"}'); - --- 插入示例产品数据 -INSERT IGNORE INTO `products` (`name`, `description`, `price`, `stock`, `image_url`) VALUES -('优质鲜牛奶', '新鲜优质牛奶,富含营养', 5000, 1000, '/uploads/products/milk.jpg'), -('有机牛肉', '无添加有机牛肉', 12000, 500, '/uploads/products/beef.jpg'), -('纯羊毛制品', '100%纯羊毛制品', 8000, 300, '/uploads/products/wool.jpg'); - --- ============================================ --- 索引优化建议 --- ============================================ - --- 复合索引 -CREATE INDEX `idx_farms_type_status` ON `farms` (`type`, `status`); -CREATE INDEX `idx_animals_farm_type` ON `animals` (`farm_id`, `type`); -CREATE INDEX `idx_devices_farm_status` ON `devices` (`farm_id`, `status`); -CREATE INDEX `idx_alerts_farm_status_level` ON `alerts` (`farm_id`, `status`, `level`); -CREATE INDEX `idx_orders_user_status` ON `orders` (`user_id`, `status`); -CREATE INDEX `idx_order_items_order_product` ON `order_items` (`order_id`, `product_id`); - --- 时间范围查询索引 -CREATE INDEX `idx_alerts_created_status` ON `alerts` (`created_at`, `status`); -CREATE INDEX `idx_orders_created_status` ON `orders` (`created_at`, `status`); - -SET FOREIGN_KEY_CHECKS = 1; - --- 脚本执行完成 --- 注意: --- 1. 请根据实际需求调整字段长度和约束 --- 2. 密码字段需要在应用层进行加密处理 --- 3. JSON字段需要MySQL 5.7+版本支持 --- 4. 建议定期备份数据库 --- 5. 生产环境请根据实际情况调整索引策略 \ No newline at end of file diff --git a/scripts/database/schema.sql b/scripts/database/schema.sql deleted file mode 100644 index a366c08..0000000 --- a/scripts/database/schema.sql +++ /dev/null @@ -1,88 +0,0 @@ --- 创建数据库 -CREATE DATABASE IF NOT EXISTS nxxmdata CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- 使用数据库 -USE nxxmdata; - --- 创建用户表 -CREATE TABLE users ( - id INT PRIMARY KEY AUTO_INCREMENT, - username VARCHAR(50) UNIQUE NOT NULL, - email VARCHAR(100) UNIQUE NOT NULL, - password VARCHAR(255) NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -); - --- 创建角色表 -CREATE TABLE roles ( - id INT PRIMARY KEY AUTO_INCREMENT, - name VARCHAR(50) UNIQUE NOT NULL, - description TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- 创建用户角色关联表 -CREATE TABLE user_roles ( - user_id INT NOT NULL, - role_id INT NOT NULL, - assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (user_id, role_id), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, - FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE -); - --- 创建产品表 -CREATE TABLE products ( - id INT PRIMARY KEY AUTO_INCREMENT, - name VARCHAR(100) NOT NULL, - description TEXT, - price DECIMAL(10,2) NOT NULL, - stock INT DEFAULT 0, - status ENUM('active', 'inactive') DEFAULT 'active', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -); - --- 创建订单表 -CREATE TABLE orders ( - id INT PRIMARY KEY AUTO_INCREMENT, - user_id INT NOT NULL, - total_amount DECIMAL(10,2) NOT NULL, - status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') DEFAULT 'pending', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -); - --- 创建订单项表 -CREATE TABLE order_items ( - id INT PRIMARY KEY AUTO_INCREMENT, - order_id INT NOT NULL, - product_id INT NOT NULL, - quantity INT NOT NULL, - price DECIMAL(10,2) NOT NULL, - FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE, - FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE -); - --- 插入基础角色数据 -INSERT INTO roles (name, description) VALUES -('admin', '系统管理员'), -('user', '普通用户'), -('guest', '访客'); - --- 插入示例用户数据 -INSERT INTO users (username, email, password) VALUES -('admin', 'admin@example.com', '$2b$10$rVHMOB./a2mFmE4EEdI3QO4f./bN3LYb.dpDvtX9gRUM9gNwspj1a'), -('john_doe', 'john@example.com', '$2b$10$rVHMOB./a2mFmE4EEdI3QO4f./bN3LYb.dpDvtX9gRUM9gNwspj1a'); - --- 为示例用户分配角色 -INSERT INTO user_roles (user_id, role_id) VALUES -(1, 1), -- admin用户具有admin角色 -(2, 2); -- john_doe用户具有user角色 - --- 插入示例产品数据 -INSERT INTO products (name, description, price, stock) VALUES -('示例产品1', '这是一个示例产品', 99.99, 100), -('示例产品2', '这是另一个示例产品', 149.99, 50); \ No newline at end of file diff --git a/scripts/init-db.js b/scripts/init-db.js deleted file mode 100644 index 8ee6176..0000000 --- a/scripts/init-db.js +++ /dev/null @@ -1,112 +0,0 @@ -/** - * 数据库初始化脚本 - * @file init-db.js - * @description 初始化数据库结构和基础数据 - */ -const { sequelize, syncModels } = require('../models'); -const { User, Role, UserRole } = require('../models'); -const bcrypt = require('bcrypt'); -const migrationManager = require('./migration-manager'); -const seedManager = require('./seed-manager'); - -async function initDb() { - try { - console.log('开始初始化数据库...'); - - // 测试数据库连接 - await sequelize.authenticate(); - console.log('数据库连接成功'); - - // 创建迁移表 - await migrationManager.createMigrationTable(); - console.log('迁移表创建成功'); - - // 创建种子表 - await seedManager.createSeedTable(); - console.log('种子表创建成功'); - - // 运行待处理的迁移 - await migrationManager.runPendingMigrations(); - console.log('迁移完成'); - - // 运行种子数据 - await seedManager.runAllSeeds(); - console.log('种子数据应用完成'); - - // 同步模型(确保所有模型都已同步到数据库) - await syncModels({ alter: true }); - console.log('模型同步完成'); - - // 检查是否有管理员用户 - const adminUser = await User.findOne({ where: { username: 'admin' } }); - - // 如果有管理员用户,检查密码是否为123456的哈希值 - if (adminUser) { - // 检查密码是否为123456的哈希值 - const isCorrectPassword = await adminUser.validPassword('123456'); - - // 如果密码不是123456的哈希值,则更新密码 - if (!isCorrectPassword) { - adminUser.password = await bcrypt.hash('123456', 10); - await adminUser.save(); - console.log('管理员密码已重置为123456'); - } else { - console.log('管理员密码已是123456'); - } - - // 确保管理员有admin角色 - const adminRole = await Role.findOne({ where: { name: 'admin' } }); - if (adminRole) { - const hasAdminRole = await adminUser.hasRole('admin'); - if (!hasAdminRole) { - await adminUser.assignRole(adminRole.id); - console.log('已为管理员分配admin角色'); - } else { - console.log('管理员已有admin角色'); - } - } - } else { - // 如果没有管理员用户,则创建一个 - const newAdmin = await User.create({ - username: 'admin', - email: 'admin@example.com', - password: await bcrypt.hash('123456', 10) - }); - console.log('管理员用户已创建,用户名: admin,密码: 123456'); - - // 为新管理员分配admin角色 - const adminRole = await Role.findOne({ where: { name: 'admin' } }); - if (adminRole) { - await newAdmin.assignRole(adminRole.id); - console.log('已为新管理员分配admin角色'); - } - } - - console.log('数据库初始化完成'); - - // 关闭数据库连接 - await sequelize.close(); - console.log('数据库连接已关闭'); - - process.exit(0); - } catch (error) { - console.error('数据库初始化失败:', error); - - // 尝试关闭数据库连接 - try { - await sequelize.close(); - console.log('数据库连接已关闭'); - } catch (closeError) { - console.error('关闭数据库连接失败:', closeError); - } - - process.exit(1); - } -} - -// 如果直接运行此脚本,则执行初始化 -if (require.main === module) { - initDb(); -} - -module.exports = initDb; \ No newline at end of file diff --git a/scripts/migration-manager.js b/scripts/migration-manager.js deleted file mode 100644 index ef319d9..0000000 --- a/scripts/migration-manager.js +++ /dev/null @@ -1,315 +0,0 @@ -/** - * 数据库迁移管理器 - * @file migration-manager.js - * @description 管理数据库迁移,支持版本控制和回滚 - */ -const fs = require('fs'); -const path = require('path'); -const { sequelize } = require('../config/database-simple'); -const { QueryTypes } = require('sequelize'); - -// 迁移文件目录 -const MIGRATIONS_DIR = path.join(__dirname, '../migrations'); - -// 确保迁移目录存在 -if (!fs.existsSync(MIGRATIONS_DIR)) { - fs.mkdirSync(MIGRATIONS_DIR, { recursive: true }); -} - -// 创建迁移表(如果不存在) -async function createMigrationTable() { - await sequelize.query(` - CREATE TABLE IF NOT EXISTS migrations ( - id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(255) NOT NULL UNIQUE, - applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - `); -} - -// 获取已应用的迁移 -async function getAppliedMigrations() { - await createMigrationTable(); - - const migrations = await sequelize.query( - 'SELECT name FROM migrations ORDER BY id ASC', - { type: QueryTypes.SELECT } - ); - - return migrations.map(migration => migration.name); -} - -// 获取所有迁移文件 -function getAllMigrations() { - return fs.readdirSync(MIGRATIONS_DIR) - .filter(file => file.endsWith('.js')) - .sort(); // 按文件名排序,通常是时间戳前缀 -} - -// 应用迁移 -async function applyMigration(migrationName) { - const migration = require(path.join(MIGRATIONS_DIR, migrationName)); - - console.log(`正在应用迁移: ${migrationName}`); - - // 开始事务 - const transaction = await sequelize.transaction(); - - try { - // 执行迁移的 up 方法 - await migration.up(sequelize.getQueryInterface(), sequelize); - - // 记录迁移已应用 - await sequelize.query( - 'INSERT INTO migrations (name) VALUES (:name)', - { - replacements: { name: migrationName }, - transaction - } - ); - - // 提交事务 - await transaction.commit(); - console.log(`迁移应用成功: ${migrationName}`); - } catch (error) { - // 回滚事务 - await transaction.rollback(); - console.error(`迁移应用失败: ${migrationName}`, error); - throw error; - } -} - -// 回滚迁移 -async function revertMigration(migrationName) { - const migration = require(path.join(MIGRATIONS_DIR, migrationName)); - - console.log(`正在回滚迁移: ${migrationName}`); - - // 开始事务 - const transaction = await sequelize.transaction(); - - try { - // 执行迁移的 down 方法 - await migration.down(sequelize.getQueryInterface(), sequelize); - - // 删除迁移记录 - await sequelize.query( - 'DELETE FROM migrations WHERE name = :name', - { - replacements: { name: migrationName }, - transaction - } - ); - - // 提交事务 - await transaction.commit(); - console.log(`迁移回滚成功: ${migrationName}`); - } catch (error) { - // 回滚事务 - await transaction.rollback(); - console.error(`迁移回滚失败: ${migrationName}`, error); - throw error; - } -} - -// 运行待处理的迁移 -async function runPendingMigrations() { - const appliedMigrations = await getAppliedMigrations(); - const allMigrations = getAllMigrations(); - - // 找出未应用的迁移 - const pendingMigrations = allMigrations.filter( - migration => !appliedMigrations.includes(migration) - ); - - if (pendingMigrations.length === 0) { - console.log('没有待处理的迁移'); - return; - } - - console.log(`发现 ${pendingMigrations.length} 个待处理的迁移`); - - // 按顺序应用每个待处理的迁移 - for (const migration of pendingMigrations) { - await applyMigration(migration); - } - - console.log('所有待处理的迁移已应用'); -} - -// 回滚最近的迁移 -async function rollbackLastMigration() { - const appliedMigrations = await getAppliedMigrations(); - - if (appliedMigrations.length === 0) { - console.log('没有可回滚的迁移'); - return; - } - - const lastMigration = appliedMigrations[appliedMigrations.length - 1]; - await revertMigration(lastMigration); -} - -// 回滚到特定迁移 -async function rollbackToMigration(targetMigration) { - const appliedMigrations = await getAppliedMigrations(); - - if (appliedMigrations.length === 0) { - console.log('没有可回滚的迁移'); - return; - } - - const targetIndex = appliedMigrations.indexOf(targetMigration); - - if (targetIndex === -1) { - console.error(`目标迁移 ${targetMigration} 未找到或未应用`); - return; - } - - // 从最新的迁移开始,回滚到目标迁移之后的所有迁移 - const migrationsToRollback = appliedMigrations.slice(targetIndex + 1).reverse(); - - for (const migration of migrationsToRollback) { - await revertMigration(migration); - } - - console.log(`已回滚到迁移: ${targetMigration}`); -} - -// 创建新的迁移文件 -function createMigration(name) { - const timestamp = new Date().toISOString().replace(/[-:]/g, '').replace('T', '').split('.')[0]; - const fileName = `${timestamp}_${name}.js`; - const filePath = path.join(MIGRATIONS_DIR, fileName); - - const template = `/** - * 迁移: ${name} - * 创建时间: ${new Date().toISOString()} - */ -'use strict'; - -module.exports = { - up: async (queryInterface, Sequelize) => { - // 在此处添加迁移代码(创建表、添加列等) - // 例如: - // await queryInterface.createTable('users', { - // id: { - // type: Sequelize.INTEGER, - // primaryKey: true, - // autoIncrement: true - // }, - // name: Sequelize.STRING, - // createdAt: { - // type: Sequelize.DATE, - // defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), - // field: 'created_at' - // }, - // updatedAt: { - // type: Sequelize.DATE, - // defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'), - // field: 'updated_at' - // } - // }); - }, - - down: async (queryInterface, Sequelize) => { - // 在此处添加回滚代码(删除表、删除列等) - // 例如: - // await queryInterface.dropTable('users'); - } -}; -`; - - fs.writeFileSync(filePath, template); - console.log(`已创建迁移文件: ${fileName}`); - return fileName; -} - -// 命令行接口 -async function main() { - const args = process.argv.slice(2); - const command = args[0]; - - try { - switch (command) { - case 'create': - if (!args[1]) { - console.error('请提供迁移名称'); - process.exit(1); - } - createMigration(args[1]); - break; - - case 'up': - case 'migrate': - await runPendingMigrations(); - break; - - case 'down': - case 'rollback': - await rollbackLastMigration(); - break; - - case 'to': - if (!args[1]) { - console.error('请提供目标迁移名称'); - process.exit(1); - } - await rollbackToMigration(args[1]); - break; - - case 'status': - const applied = await getAppliedMigrations(); - const all = getAllMigrations(); - - console.log('已应用的迁移:'); - applied.forEach(m => console.log(` - ${m}`)); - - console.log('\n待处理的迁移:'); - all.filter(m => !applied.includes(m)) - .forEach(m => console.log(` - ${m}`)); - break; - - default: - console.log(` -数据库迁移管理器 - -用法: - node migration-manager.js <命令> [参数] - -命令: - create 创建新的迁移文件 - up, migrate 应用所有待处理的迁移 - down, rollback 回滚最近的一次迁移 - to 回滚到指定的迁移(不包括该迁移) - status 显示迁移状态 - `); - } - } catch (error) { - console.error('迁移操作失败:', error); - process.exit(1); - } finally { - // 关闭数据库连接 - await sequelize.close(); - } -} - -// 如果直接运行此脚本,则执行main函数 -if (require.main === module) { - main().catch(err => { - console.error('未处理的错误:', err); - process.exit(1); - }); -} - -module.exports = { - createMigrationTable, - getAppliedMigrations, - getAllMigrations, - applyMigration, - revertMigration, - runPendingMigrations, - rollbackLastMigration, - rollbackToMigration, - createMigration -}; \ No newline at end of file diff --git a/scripts/seed-manager.js b/scripts/seed-manager.js deleted file mode 100644 index a7dcd35..0000000 --- a/scripts/seed-manager.js +++ /dev/null @@ -1,282 +0,0 @@ -/** - * 数据库种子数据管理器 - * @file seed-manager.js - * @description 管理数据库种子数据,用于初始化和测试 - */ -require('dotenv').config(); -const fs = require('fs'); -const path = require('path'); -const { sequelize } = require('../config/database-simple'); -const { QueryTypes } = require('sequelize'); - -// 种子文件目录 -const SEEDS_DIR = path.join(__dirname, '../seeds'); - -// 确保种子目录存在 -if (!fs.existsSync(SEEDS_DIR)) { - fs.mkdirSync(SEEDS_DIR, { recursive: true }); -} - -// 创建种子记录表(如果不存在) -async function createSeedTable() { - await sequelize.query(` - CREATE TABLE IF NOT EXISTS seeds ( - id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(255) NOT NULL UNIQUE, - applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - `); -} - -// 获取已应用的种子 -async function getAppliedSeeds() { - await createSeedTable(); - - const seeds = await sequelize.query( - 'SELECT name FROM seeds ORDER BY id ASC', - { type: QueryTypes.SELECT } - ); - - return seeds.map(seed => seed.name); -} - -// 获取所有种子文件 -function getAllSeeds() { - return fs.readdirSync(SEEDS_DIR) - .filter(file => file.endsWith('.js')) - .sort(); // 按文件名排序 -} - -// 应用种子数据 -async function applySeed(seedName) { - const seed = require(path.join(SEEDS_DIR, seedName)); - - console.log(`正在应用种子数据: ${seedName}`); - - // 开始事务 - const transaction = await sequelize.transaction(); - - try { - // 执行种子的 up 方法 - await seed.up(sequelize.getQueryInterface(), sequelize); - - // 记录种子已应用 - await sequelize.query( - 'INSERT INTO seeds (name) VALUES (:name)', - { - replacements: { name: seedName }, - transaction - } - ); - - // 提交事务 - await transaction.commit(); - console.log(`种子数据应用成功: ${seedName}`); - } catch (error) { - // 回滚事务 - await transaction.rollback(); - console.error(`种子数据应用失败: ${seedName}`, error); - throw error; - } -} - -// 回滚种子数据 -async function revertSeed(seedName) { - const seed = require(path.join(SEEDS_DIR, seedName)); - - console.log(`正在回滚种子数据: ${seedName}`); - - // 开始事务 - const transaction = await sequelize.transaction(); - - try { - // 执行种子的 down 方法 - await seed.down(sequelize.getQueryInterface(), sequelize); - - // 删除种子记录 - await sequelize.query( - 'DELETE FROM seeds WHERE name = :name', - { - replacements: { name: seedName }, - transaction - } - ); - - // 提交事务 - await transaction.commit(); - console.log(`种子数据回滚成功: ${seedName}`); - } catch (error) { - // 回滚事务 - await transaction.rollback(); - console.error(`种子数据回滚失败: ${seedName}`, error); - throw error; - } -} - -// 运行所有种子数据 -async function runAllSeeds() { - const appliedSeeds = await getAppliedSeeds(); - const allSeeds = getAllSeeds(); - - // 找出未应用的种子 - const pendingSeeds = allSeeds.filter( - seed => !appliedSeeds.includes(seed) - ); - - if (pendingSeeds.length === 0) { - console.log('没有待处理的种子数据'); - return; - } - - console.log(`发现 ${pendingSeeds.length} 个待处理的种子数据`); - - // 按顺序应用每个待处理的种子 - for (const seed of pendingSeeds) { - await applySeed(seed); - } - - console.log('所有待处理的种子数据已应用'); -} - -// 回滚所有种子数据 -async function revertAllSeeds() { - const appliedSeeds = await getAppliedSeeds(); - - if (appliedSeeds.length === 0) { - console.log('没有可回滚的种子数据'); - return; - } - - // 从最新的种子开始,回滚所有种子 - const seedsToRevert = [...appliedSeeds].reverse(); - - for (const seed of seedsToRevert) { - await revertSeed(seed); - } - - console.log('所有种子数据已回滚'); -} - -// 创建新的种子文件 -function createSeed(name) { - const timestamp = new Date().toISOString().replace(/[-:]/g, '').replace('T', '').split('.')[0]; - const fileName = `${timestamp}_${name}.js`; - const filePath = path.join(SEEDS_DIR, fileName); - - const template = `/** - * 种子数据: ${name} - * 创建时间: ${new Date().toISOString()} - */ -'use strict'; - -module.exports = { - up: async (queryInterface, Sequelize) => { - // 在此处添加种子数据 - // 例如: - // await queryInterface.bulkInsert('users', [ - // { - // username: 'admin', - // email: 'admin@example.com', - // password: '$2b$10$rVHMOB./a2mFmE4EEdI3QO4f./bN3LYb.dpDvtX9gRUM9gNwspj1a', // 123456 - // created_at: new Date(), - // updated_at: new Date() - // }, - // { - // username: 'user', - // email: 'user@example.com', - // password: '$2b$10$rVHMOB./a2mFmE4EEdI3QO4f./bN3LYb.dpDvtX9gRUM9gNwspj1a', // 123456 - // created_at: new Date(), - // updated_at: new Date() - // } - // ]); - }, - - down: async (queryInterface, Sequelize) => { - // 在此处添加回滚代码 - // 例如: - // await queryInterface.bulkDelete('users', null, {}); - } -}; -`; - - fs.writeFileSync(filePath, template); - console.log(`已创建种子文件: ${fileName}`); - return fileName; -} - -// 命令行接口 -async function main() { - const args = process.argv.slice(2); - const command = args[0]; - - try { - switch (command) { - case 'create': - if (!args[1]) { - console.error('请提供种子名称'); - process.exit(1); - } - createSeed(args[1]); - break; - - case 'run': - await runAllSeeds(); - break; - - case 'revert': - await revertAllSeeds(); - break; - - case 'status': - const applied = await getAppliedSeeds(); - const all = getAllSeeds(); - - console.log('已应用的种子数据:'); - applied.forEach(s => console.log(` - ${s}`)); - - console.log('\n待处理的种子数据:'); - all.filter(s => !applied.includes(s)) - .forEach(s => console.log(` - ${s}`)); - break; - - default: - console.log(` -数据库种子数据管理器 - -用法: - node seed-manager.js <命令> [参数] - -命令: - create 创建新的种子文件 - run 应用所有待处理的种子数据 - revert 回滚所有种子数据 - status 显示种子数据状态 - `); - } - } catch (error) { - console.error('种子数据操作失败:', error); - process.exit(1); - } finally { - // 关闭数据库连接 - await sequelize.close(); - } -} - -// 如果直接运行此脚本,则执行main函数 -if (require.main === module) { - main().catch(err => { - console.error('未处理的错误:', err); - process.exit(1); - }); -} - -module.exports = { - createSeedTable, - getAppliedSeeds, - getAllSeeds, - applySeed, - revertSeed, - runAllSeeds, - revertAllSeeds, - createSeed -}; \ No newline at end of file diff --git a/scripts/simple-db-test.js b/scripts/simple-db-test.js deleted file mode 100644 index bb5b5af..0000000 --- a/scripts/simple-db-test.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * 简单数据库连接测试脚本 - * 测试数据库基本连接功能 - */ -const { sequelize, testConnection } = require('../backend/config/database-simple'); - -async function simpleConnectionTest() { - console.log('🔍 开始测试数据库连接...'); - - try { - // 测试数据库连接 - const isConnected = await testConnection(); - - if (isConnected) { - console.log('✅ 数据库连接测试通过'); - - // 执行简单查询 - const [results] = await sequelize.query('SELECT 1 + 1 AS result'); - console.log('📊 数据库查询测试:', results[0]); - - // 检查数据库版本 - const [version] = await sequelize.query('SELECT VERSION() AS version'); - console.log('🗄️ 数据库版本:', version[0].version); - - console.log('🎉 所有数据库测试通过!'); - } else { - console.log('❌ 数据库连接失败'); - process.exit(1); - } - - } catch (error) { - console.error('❌ 数据库测试出错:', error.message); - process.exit(1); - } finally { - // 关闭连接 - try { - await sequelize.close(); - console.log('🔒 数据库连接已关闭'); - } catch (closeError) { - console.error('关闭连接时出错:', closeError.message); - } - } -} - -// 执行测试 -if (require.main === module) { - simpleConnectionTest(); -} - -module.exports = simpleConnectionTest; \ No newline at end of file diff --git a/scripts/test-connection.js b/scripts/test-connection.js deleted file mode 100644 index 98d0050..0000000 --- a/scripts/test-connection.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * 数据库连接测试脚本 - * @file test-connection.js - * @description 测试数据库连接、连接池状态和查询性能 - */ -const { sequelize } = require('../models'); -const { User } = require('../models'); -const dbPool = require('../config/database-pool'); -const queryOptimizer = require('../config/query-optimizer'); -const dbMonitor = require('../config/db-monitor'); - -async function testConnection() { - try { - console.log('开始测试数据库连接...'); - - // 测试数据库连接 - await sequelize.authenticate(); - console.log('数据库连接成功'); - - // 获取连接池状态 - const poolStatus = await dbPool.getPoolStatus(); - console.log('连接池状态:', JSON.stringify(poolStatus, null, 2)); - - // 获取数据库状态 - const dbStatus = await queryOptimizer.getDatabaseStatus(); - console.log('数据库状态:', JSON.stringify(dbStatus, null, 2)); - - // 查询用户数量 - console.time('用户查询'); - const userCount = await User.count(); - console.timeEnd('用户查询'); - console.log(`当前用户数量: ${userCount}`); - - // 执行查询分析 - const userQuery = User.findAll(); - const explainResult = await queryOptimizer.explainQuery(userQuery); - console.log('查询分析结果:', JSON.stringify(explainResult, null, 2)); - - // 获取表信息 - const tableInfo = await queryOptimizer.getTableInfo('users'); - console.log('用户表信息:', JSON.stringify(tableInfo, null, 2)); - - // 获取索引信息 - const indexInfo = await queryOptimizer.getIndexInfo('users'); - console.log('用户表索引:', JSON.stringify(indexInfo, null, 2)); - - // 监控连接状态 - const connectionStatus = await dbMonitor.checkConnectionStatus(); - console.log('连接监控状态:', JSON.stringify(connectionStatus, null, 2)); - - // 关闭数据库连接 - await sequelize.close(); - console.log('数据库连接已关闭'); - - process.exit(0); - } catch (error) { - console.error('数据库连接测试失败:', error); - - // 尝试关闭数据库连接 - try { - await sequelize.close(); - console.log('数据库连接已关闭'); - } catch (closeError) { - console.error('关闭数据库连接失败:', closeError); - } - - process.exit(1); - } -} - -// 如果直接运行此脚本,则执行测试 -if (require.main === module) { - testConnection(); -} - -module.exports = testConnection; \ No newline at end of file diff --git a/scripts/test-map-api.js b/scripts/test-map-api.js deleted file mode 100644 index a74cccf..0000000 --- a/scripts/test-map-api.js +++ /dev/null @@ -1,139 +0,0 @@ -/** - * 百度地图API测试脚本 - * 用于测试百度地图API服务是否正常工作 - */ - -require('dotenv').config(); -const axios = require('axios'); - -// 百度地图API密钥 -const BAIDU_MAP_AK = process.env.BAIDU_MAP_AK || 'your_baidu_map_ak'; - -// 测试地理编码API -async function testGeocode() { - try { - console.log('测试地理编码API...'); - const address = '宁夏回族自治区银川市兴庆区北京东路'; - - const response = await axios.get('http://api.map.baidu.com/geocoding/v3', { - params: { - address, - output: 'json', - ak: BAIDU_MAP_AK - } - }); - - if (response.data.status === 0) { - console.log('地理编码成功:'); - console.log(`地址: ${address}`); - console.log(`经度: ${response.data.result.location.lng}`); - console.log(`纬度: ${response.data.result.location.lat}`); - return true; - } else { - console.error('地理编码失败:', response.data.message); - return false; - } - } catch (error) { - console.error('地理编码测试错误:', error.message); - return false; - } -} - -// 测试逆地理编码API -async function testReverseGeocode() { - try { - console.log('\n测试逆地理编码API...'); - // 银川市中心坐标 - const lat = 38.4864; - const lng = 106.2324; - - const response = await axios.get('http://api.map.baidu.com/reverse_geocoding/v3', { - params: { - location: `${lat},${lng}`, - output: 'json', - ak: BAIDU_MAP_AK - } - }); - - if (response.data.status === 0) { - console.log('逆地理编码成功:'); - console.log(`经度: ${lng}, 纬度: ${lat}`); - console.log(`地址: ${response.data.result.formatted_address}`); - return true; - } else { - console.error('逆地理编码失败:', response.data.message); - return false; - } - } catch (error) { - console.error('逆地理编码测试错误:', error.message); - return false; - } -} - -// 测试IP定位API -async function testIpLocation() { - try { - console.log('\n测试IP定位API...'); - - const response = await axios.get('http://api.map.baidu.com/location/ip', { - params: { - ak: BAIDU_MAP_AK, - coor: 'bd09ll' - } - }); - - if (response.data.status === 0) { - console.log('IP定位成功:'); - console.log(`地址: ${response.data.content.address}`); - console.log(`经度: ${response.data.content.point.x}`); - console.log(`纬度: ${response.data.content.point.y}`); - return true; - } else { - console.error('IP定位失败:', response.data.message); - return false; - } - } catch (error) { - console.error('IP定位测试错误:', error.message); - return false; - } -} - -// 运行所有测试 -async function runTests() { - console.log('===== 百度地图API测试 ====='); - console.log(`使用的API密钥: ${BAIDU_MAP_AK}`); - - if (BAIDU_MAP_AK === 'your_baidu_map_ak' || BAIDU_MAP_AK === 'your_baidu_map_ak_here') { - console.warn('警告: 您正在使用默认API密钥,请在.env文件中设置有效的BAIDU_MAP_AK'); - } - - console.log('\n开始测试...'); - - const geocodeResult = await testGeocode(); - const reverseGeocodeResult = await testReverseGeocode(); - const ipLocationResult = await testIpLocation(); - - console.log('\n===== 测试结果汇总 ====='); - console.log(`地理编码API: ${geocodeResult ? '✅ 通过' : '❌ 失败'}`); - console.log(`逆地理编码API: ${reverseGeocodeResult ? '✅ 通过' : '❌ 失败'}`); - console.log(`IP定位API: ${ipLocationResult ? '✅ 通过' : '❌ 失败'}`); - - const allPassed = geocodeResult && reverseGeocodeResult && ipLocationResult; - console.log(`\n总体结果: ${allPassed ? '✅ 所有测试通过' : '❌ 部分测试失败'}`); - - if (!allPassed) { - console.log('\n可能的问题:'); - console.log('1. API密钥无效或未正确设置'); - console.log('2. 网络连接问题'); - console.log('3. 百度地图API服务暂时不可用'); - console.log('\n解决方案:'); - console.log('1. 检查.env文件中的BAIDU_MAP_AK设置'); - console.log('2. 确保您的网络可以访问百度地图API'); - console.log('3. 稍后再试'); - } -} - -// 执行测试 -runTests().catch(error => { - console.error('测试执行错误:', error); -}); \ No newline at end of file diff --git a/test/README.md b/test/README.md deleted file mode 100644 index 247ebf3..0000000 --- a/test/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# 测试文件目录 - -本目录包含宁夏智慧养殖监管平台的测试文件和测试用例。 - -## 📋 测试文件索引 - -### 🔧 性能监控测试 -- [`performance-monitor-test.js`](performance-monitor-test.js) - 性能监控系统功能测试 - -## 🧪 测试类型说明 - -### 单元测试 -位于各个模块目录下的 `__tests__` 或 `test` 子目录中,测试单个函数或组件的功能。 - -### 集成测试 -测试多个模块之间的交互和数据流,确保系统各部分能够正确协作。 - -### 端到端测试 -从用户角度测试完整的业务流程,验证系统的整体功能。 - -### 性能测试 -测试系统在各种负载条件下的性能表现,包括响应时间、吞吐量等指标。 - -## 🏃‍♂️ 运行测试 - -### 后端测试 -```bash -cd backend -npm test # 运行所有测试 -npm run test:unit # 运行单元测试 -npm run test:integration # 运行集成测试 -npm run test:performance # 运行性能测试 -``` - -### 前端测试 -```bash -cd admin-system/frontend -npm test # 运行所有测试 -npm run test:unit # 运行单元测试 -npm run test:e2e # 运行端到端测试 -``` - -### 测试覆盖率 -```bash -npm run test:coverage # 生成测试覆盖率报告 -``` - -## 📊 测试报告 - -测试运行后会在以下位置生成报告: -- `coverage/` - 测试覆盖率报告 -- `test-results/` - 测试结果详情 -- `screenshots/` - 端到端测试截图(如有失败) - -## ✅ 测试最佳实践 - -1. **测试命名**: 使用描述性的测试名称,清楚表达测试意图 -2. **测试隔离**: 确保测试之间相互独立,不依赖执行顺序 -3. **数据清理**: 测试后清理测试数据,避免影响其他测试 -4. **模拟外部依赖**: 使用 mock 模拟外部 API 和服务 -5. **测试覆盖率**: 保持较高的代码覆盖率,重点关注核心业务逻辑 - -## 🔧 测试工具 - -### 后端测试工具 -- **Jest**: JavaScript 测试框架 -- **Supertest**: HTTP 接口测试 -- **Sinon**: 测试间谍、存根和模拟 - -### 前端测试工具 -- **Vitest**: Vue 3 推荐的测试框架 -- **Vue Test Utils**: Vue 组件测试工具 -- **Cypress**: 端到端测试框架 - -## 📝 编写新测试 - -1. 在对应模块目录下创建测试文件 -2. 遵循现有的测试文件命名约定 -3. 编写清晰的测试用例 -4. 运行测试确保通过 -5. 检查测试覆盖率 - ---- - -*最后更新: 2025年1月* \ No newline at end of file diff --git a/test/performance-monitor-test.js b/test/performance-monitor-test.js deleted file mode 100644 index 5a7b713..0000000 --- a/test/performance-monitor-test.js +++ /dev/null @@ -1,233 +0,0 @@ -/** - * 性能监控系统测试脚本 - * @file performance-monitor-test.js - * @description 测试性能监控系统的基本功能 - */ -const { performanceMonitor, events: perfEvents } = require('../backend/utils/performance-monitor'); -const logger = require('../backend/utils/logger'); - -// 配置测试日志 -function log(message) { - console.log(`[${new Date().toISOString()}] ${message}`); -} - -// 测试事件监听 -function setupEventListeners() { - perfEvents.on('monitoringStarted', (data) => { - log(`✅ 监控启动事件触发,间隔: ${data.interval}ms`); - }); - - perfEvents.on('monitoringStopped', () => { - log('✅ 监控停止事件触发'); - }); - - perfEvents.on('highCpuUsage', (data) => { - log(`⚠️ 高CPU使用率事件触发: ${data.usage}% (阈值: ${data.threshold}%)`); - }); - - perfEvents.on('highMemoryUsage', (data) => { - log(`⚠️ 高内存使用率事件触发: ${data.usage}% (阈值: ${data.threshold}%)`); - }); - - perfEvents.on('highDiskUsage', (data) => { - log(`⚠️ 高磁盘使用率事件触发: ${data.usage}% (阈值: ${data.threshold}%)`); - }); - - log('事件监听器已设置'); -} - -// 测试系统资源监控 -async function testSystemMonitoring() { - log('\n--- 测试系统资源监控 ---'); - - try { - // 获取系统指标 - const metrics = performanceMonitor.getSystemMetrics(); - log('系统指标:'); - log(`CPU使用率: ${metrics.current.cpu.usage}%`); - log(`内存使用率: ${metrics.current.memory.usage}%`); - log(`磁盘使用率: ${metrics.current.disk.usage}%`); - - // 测试收集系统指标 - const collected = performanceMonitor.collectSystemMetrics(); - log('收集的系统指标:'); - log(`CPU使用率: ${collected.cpuUsage.usage}%`); - log(`内存使用率: ${collected.memoryUsage.usage}%`); - log(`磁盘使用率: ${collected.diskUsage.usage}%`); - - return true; - } catch (error) { - log(`❌ 系统资源监控测试失败: ${error.message}`); - return false; - } -} - -// 测试警报阈值设置 -async function testAlertThresholds() { - log('\n--- 测试警报阈值设置 ---'); - - try { - // 获取当前阈值 - const originalThresholds = performanceMonitor.getAlertThresholds(); - log('当前警报阈值:'); - log(`CPU使用率阈值: ${originalThresholds.system.cpuUsage}%`); - log(`内存使用率阈值: ${originalThresholds.system.memoryUsage}%`); - log(`API响应时间阈值: ${originalThresholds.api.responseTime}ms`); - - // 设置新阈值 - const newThresholds = { - system: { - cpuUsage: 50, - memoryUsage: 60 - }, - api: { - responseTime: 200 - } - }; - - const result = performanceMonitor.setAlertThresholds(newThresholds); - log('设置新阈值结果:'); - log(`成功: ${result.success}`); - log(`CPU使用率新阈值: ${result.thresholds.system.cpuUsage}%`); - log(`内存使用率新阈值: ${result.thresholds.system.memoryUsage}%`); - log(`API响应时间新阈值: ${result.thresholds.api.responseTime}ms`); - - // 恢复原始阈值 - performanceMonitor.setAlertThresholds(originalThresholds); - log('已恢复原始阈值'); - - return true; - } catch (error) { - log(`❌ 警报阈值设置测试失败: ${error.message}`); - return false; - } -} - -// 测试监控启动和停止 -async function testMonitoringControl() { - log('\n--- 测试监控启动和停止 ---'); - - try { - // 获取当前状态 - const originalStatus = performanceMonitor.getMonitoringStatus(); - log(`当前监控状态: ${originalStatus.isMonitoring ? '运行中' : '已停止'}`); - - // 如果已经在运行,先停止 - if (originalStatus.isMonitoring) { - const stopResult = performanceMonitor.stopMonitoring(); - log(`停止监控结果: ${stopResult.success ? '成功' : '失败'} - ${stopResult.message}`); - } - - // 启动监控(使用短间隔进行测试) - const startResult = performanceMonitor.startMonitoring(5000); // 5秒间隔 - log(`启动监控结果: ${startResult.success ? '成功' : '失败'} - ${startResult.message}`); - - // 等待10秒,让监控运行一段时间 - log('等待10秒...'); - await new Promise(resolve => setTimeout(resolve, 10000)); - - // 获取更新后的状态 - const updatedStatus = performanceMonitor.getMonitoringStatus(); - log(`更新后的监控状态: ${updatedStatus.isMonitoring ? '运行中' : '已停止'}`); - log(`监控间隔: ${updatedStatus.interval}ms`); - - // 停止监控 - const finalStopResult = performanceMonitor.stopMonitoring(); - log(`停止监控结果: ${finalStopResult.success ? '成功' : '失败'} - ${finalStopResult.message}`); - - // 恢复原始状态 - if (originalStatus.isMonitoring) { - performanceMonitor.startMonitoring(originalStatus.interval); - log(`已恢复原始监控状态,间隔: ${originalStatus.interval}ms`); - } - - return true; - } catch (error) { - log(`❌ 监控启动和停止测试失败: ${error.message}`); - return false; - } -} - -// 测试API统计 -async function testApiStats() { - log('\n--- 测试API统计 ---'); - - try { - // 模拟API请求 - const req1 = { method: 'GET', path: '/api/test', headers: {}, ip: '127.0.0.1' }; - const res1 = { statusCode: 200, end: function() {}, on: function(event, callback) { callback(); } }; - - const req2 = { method: 'POST', path: '/api/users', headers: {}, ip: '127.0.0.1' }; - const res2 = { statusCode: 201, end: function() {}, on: function(event, callback) { callback(); } }; - - const req3 = { method: 'GET', path: '/api/error', headers: {}, ip: '127.0.0.1' }; - const res3 = { statusCode: 500, end: function() {}, on: function(event, callback) { callback(); } }; - - // 重置API统计 - performanceMonitor.resetApiStats(); - log('API统计已重置'); - - // 记录API请求 - log('记录API请求...'); - performanceMonitor.recordApiRequest(req1, res1, Date.now() - 50); // 50ms响应时间 - performanceMonitor.recordApiRequest(req2, res2, Date.now() - 100); // 100ms响应时间 - performanceMonitor.recordApiRequest(req3, res3, Date.now() - 600); // 600ms响应时间(慢请求) - - // 获取API统计 - const apiStats = performanceMonitor.getApiStats(); - log('API统计:'); - log(`总请求数: ${apiStats.totalRequests}`); - log(`总错误数: ${apiStats.totalErrors}`); - log(`错误率: ${apiStats.errorRate}%`); - log(`平均响应时间: ${apiStats.avgResponseTime}ms`); - log(`每分钟请求数: ${apiStats.requestsPerMinute}`); - - log('端点统计:'); - apiStats.endpoints.forEach(endpoint => { - log(`- ${endpoint.endpoint}: ${endpoint.count}请求, ${endpoint.avgDuration}ms平均响应时间, ${endpoint.errorRate}%错误率`); - }); - - return true; - } catch (error) { - log(`❌ API统计测试失败: ${error.message}`); - return false; - } -} - -// 运行所有测试 -async function runAllTests() { - log('开始性能监控系统测试...'); - - // 设置事件监听器 - setupEventListeners(); - - // 运行测试 - const results = { - systemMonitoring: await testSystemMonitoring(), - alertThresholds: await testAlertThresholds(), - monitoringControl: await testMonitoringControl(), - apiStats: await testApiStats() - }; - - // 输出测试结果摘要 - log('\n--- 测试结果摘要 ---'); - Object.entries(results).forEach(([test, passed]) => { - log(`${test}: ${passed ? '✅ 通过' : '❌ 失败'}`); - }); - - const allPassed = Object.values(results).every(result => result === true); - log(`\n总体结果: ${allPassed ? '✅ 所有测试通过' : '❌ 部分测试失败'}`); - - return allPassed; -} - -// 执行测试 -runAllTests() - .then(passed => { - log(`测试完成,${passed ? '全部通过' : '部分失败'}`); - process.exit(passed ? 0 : 1); - }) - .catch(error => { - log(`测试执行错误: ${error.message}`); - process.exit(1); - }); \ No newline at end of file