diff --git a/bank-backend/SUPERVISION_TASKS_INTEGRATION_SUMMARY.md b/bank-backend/SUPERVISION_TASKS_INTEGRATION_SUMMARY.md new file mode 100644 index 0000000..8f8ab18 --- /dev/null +++ b/bank-backend/SUPERVISION_TASKS_INTEGRATION_SUMMARY.md @@ -0,0 +1,164 @@ +# 监管任务前后端集成完成总结 + +## 🎯 项目概述 +已成功完成银行端监管任务管理系统的前后端集成,实现了完整的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/controllers/projectController.js b/bank-backend/controllers/projectController.js new file mode 100644 index 0000000..e0ea9f4 --- /dev/null +++ b/bank-backend/controllers/projectController.js @@ -0,0 +1,460 @@ +const { Project, User } = require('../models'); +const { Op } = require('sequelize'); + +// 获取项目列表 +const getProjects = async (req, res) => { + try { + const { + page = 1, + limit = 10, + search = '', + status = '', + sortBy = 'createdAt', + sortOrder = 'DESC' + } = req.query; + + const offset = (page - 1) * limit; + + // 构建查询条件 + const where = {}; + + // 搜索条件 + if (search) { + where[Op.or] = [ + { name: { [Op.like]: `%${search}%` } }, + { farmName: { [Op.like]: `%${search}%` } }, + { loanOfficer: { [Op.like]: `%${search}%` } } + ]; + } + + // 状态筛选 + if (status) { + where.status = status; + } + + // 查询项目列表 + const { count, rows: projects } = await Project.findAndCountAll({ + where, + include: [ + { + model: User, + as: 'creator', + attributes: ['id', 'username', 'real_name'] + }, + { + model: User, + as: 'updater', + attributes: ['id', 'username', 'real_name'] + } + ], + order: [[sortBy, sortOrder.toUpperCase()]], + limit: parseInt(limit), + offset: parseInt(offset) + }); + + // 计算分页信息 + const totalPages = Math.ceil(count / limit); + const hasNextPage = page < totalPages; + const hasPrevPage = page > 1; + + res.json({ + success: true, + data: { + projects, + pagination: { + current: parseInt(page), + pageSize: parseInt(limit), + total: count, + totalPages, + hasNextPage, + hasPrevPage + } + }, + message: '获取项目列表成功' + }); + } catch (error) { + console.error('获取项目列表失败:', error); + res.status(500).json({ + success: false, + message: '获取项目列表失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 获取项目详情 +const getProjectById = async (req, res) => { + try { + const { id } = req.params; + + const project = await Project.findByPk(id, { + include: [ + { + model: User, + as: 'creator', + attributes: ['id', 'username', 'name'] + }, + { + model: User, + as: 'updater', + attributes: ['id', 'username', 'name'] + } + ] + }); + + if (!project) { + return res.status(404).json({ + success: false, + message: '项目不存在' + }); + } + + res.json({ + success: true, + data: project, + message: '获取项目详情成功' + }); + } catch (error) { + console.error('获取项目详情失败:', error); + res.status(500).json({ + success: false, + message: '获取项目详情失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 创建项目 +const createProject = async (req, res) => { + try { + const { + name, + status = 'supervision', + farmName, + supervisionObject, + supervisionQuantity = 0, + supervisionPeriod, + supervisionAmount = 0.00, + startTime, + endTime, + earTag = 0, + collar = 0, + host = 0, + loanOfficer, + description + } = req.body; + + // 验证必填字段 + const requiredFields = ['name', 'farmName', 'supervisionObject', 'supervisionPeriod', 'startTime', 'endTime']; + const missingFields = requiredFields.filter(field => !req.body[field]); + + if (missingFields.length > 0) { + return res.status(400).json({ + success: false, + message: `请填写所有必填字段: ${missingFields.join(', ')}` + }); + } + + // 验证字段长度 + if (name.length > 100) { + return res.status(400).json({ + success: false, + message: '项目名称不能超过100个字符' + }); + } + + if (farmName.length > 200) { + return res.status(400).json({ + success: false, + message: '养殖场名称不能超过200个字符' + }); + } + + // 验证数值字段 + if (supervisionQuantity < 0) { + return res.status(400).json({ + success: false, + message: '监管数量不能为负数' + }); + } + + if (supervisionAmount < 0) { + return res.status(400).json({ + success: false, + message: '监管金额不能为负数' + }); + } + + if (earTag < 0 || collar < 0 || host < 0) { + return res.status(400).json({ + success: false, + message: '设备数量不能为负数' + }); + } + + // 验证状态 + if (!['supervision', 'completed'].includes(status)) { + return res.status(400).json({ + success: false, + message: '项目状态只能是 supervision 或 completed' + }); + } + + // 验证日期格式和逻辑 + const startDate = new Date(startTime); + const endDate = new Date(endTime); + + if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { + return res.status(400).json({ + success: false, + message: '日期格式不正确' + }); + } + + if (startDate >= endDate) { + return res.status(400).json({ + success: false, + message: '结束时间必须晚于开始时间' + }); + } + + // 检查项目名称是否已存在 + const existingProject = await Project.findOne({ + where: { name } + }); + + if (existingProject) { + return res.status(400).json({ + success: false, + message: '项目名称已存在,请使用其他名称' + }); + } + + // 创建项目 + const project = await Project.create({ + name, + status, + farmName, + supervisionObject, + supervisionQuantity: parseInt(supervisionQuantity), + supervisionPeriod, + supervisionAmount: parseFloat(supervisionAmount), + startTime, + endTime, + earTag: parseInt(earTag), + collar: parseInt(collar), + host: parseInt(host), + loanOfficer, + description, + createdBy: req.user?.userId || req.user?.id, + updatedBy: req.user?.userId || req.user?.id + }); + + // 获取创建的项目详情(包含关联信息) + const createdProject = await Project.findByPk(project.id, { + include: [ + { + model: User, + as: 'creator', + attributes: ['id', 'username', 'real_name'] + }, + { + model: User, + as: 'updater', + attributes: ['id', 'username', 'real_name'] + } + ] + }); + + res.status(201).json({ + success: true, + data: createdProject, + message: '创建项目成功' + }); + } catch (error) { + console.error('创建项目失败:', error); + + // 处理数据库约束错误 + if (error.name === 'SequelizeValidationError') { + const validationErrors = error.errors.map(err => err.message).join(', '); + return res.status(400).json({ + success: false, + message: `数据验证失败: ${validationErrors}` + }); + } + + if (error.name === 'SequelizeUniqueConstraintError') { + return res.status(400).json({ + success: false, + message: '项目名称已存在,请使用其他名称' + }); + } + + res.status(500).json({ + success: false, + message: '创建项目失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 更新项目 +const updateProject = async (req, res) => { + try { + const { id } = req.params; + const updateData = req.body; + + // 验证日期 + if (updateData.startTime && updateData.endTime) { + if (new Date(updateData.startTime) >= new Date(updateData.endTime)) { + return res.status(400).json({ + success: false, + message: '结束时间必须晚于开始时间' + }); + } + } + + const project = await Project.findByPk(id); + if (!project) { + return res.status(404).json({ + success: false, + message: '项目不存在' + }); + } + + // 更新项目 + await project.update({ + ...updateData, + updatedBy: req.user?.id + }); + + res.json({ + success: true, + data: project, + message: '更新项目成功' + }); + } catch (error) { + console.error('更新项目失败:', error); + res.status(500).json({ + success: false, + message: '更新项目失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 删除项目 +const deleteProject = async (req, res) => { + try { + const { id } = req.params; + + const project = await Project.findByPk(id); + if (!project) { + return res.status(404).json({ + success: false, + message: '项目不存在' + }); + } + + await project.destroy(); + + res.json({ + success: true, + message: '删除项目成功' + }); + } catch (error) { + console.error('删除项目失败:', error); + res.status(500).json({ + success: false, + message: '删除项目失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 获取项目统计 +const getProjectStats = async (req, res) => { + try { + const totalProjects = await Project.count(); + const supervisionProjects = await Project.count({ where: { status: 'supervision' } }); + const completedProjects = await Project.count({ where: { status: 'completed' } }); + + // 计算总监管金额 + const totalAmount = await Project.sum('supervisionAmount'); + + // 计算总监管数量 + const totalQuantity = await Project.sum('supervisionQuantity'); + + res.json({ + success: true, + data: { + total: totalProjects, + supervision: supervisionProjects, + completed: completedProjects, + totalAmount: totalAmount || 0, + totalQuantity: totalQuantity || 0 + }, + message: '获取项目统计成功' + }); + } catch (error) { + console.error('获取项目统计失败:', error); + res.status(500).json({ + success: false, + message: '获取项目统计失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 批量更新项目状态 +const batchUpdateStatus = async (req, res) => { + try { + const { projectIds, status } = req.body; + + if (!projectIds || !Array.isArray(projectIds) || projectIds.length === 0) { + return res.status(400).json({ + success: false, + message: '请选择要更新的项目' + }); + } + + if (!['supervision', 'completed'].includes(status)) { + return res.status(400).json({ + success: false, + message: '无效的项目状态' + }); + } + + await Project.update( + { + status, + updatedBy: req.user?.id + }, + { + where: { id: { [Op.in]: projectIds } } + } + ); + + res.json({ + success: true, + message: `成功更新 ${projectIds.length} 个项目的状态` + }); + } catch (error) { + console.error('批量更新项目状态失败:', error); + res.status(500).json({ + success: false, + message: '批量更新项目状态失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +module.exports = { + getProjects, + getProjectById, + createProject, + updateProject, + deleteProject, + getProjectStats, + batchUpdateStatus +}; diff --git a/bank-backend/controllers/supervisionTaskController.js b/bank-backend/controllers/supervisionTaskController.js new file mode 100644 index 0000000..3e0a255 --- /dev/null +++ b/bank-backend/controllers/supervisionTaskController.js @@ -0,0 +1,573 @@ +/** + * 监管任务控制器 + * @file supervisionTaskController.js + * @description 监管任务相关的API控制器 + */ +const { SupervisionTask, User } = require('../models'); +const { Op } = require('sequelize'); + +// 获取监管任务列表 +const getSupervisionTasks = async (req, res) => { + try { + const { + page = 1, + limit = 10, + search = '', + supervisionStatus = '', + dateRange = '', + sortBy = 'createdAt', + sortOrder = 'DESC' + } = req.query; + + const offset = (page - 1) * limit; + + // 构建查询条件 + const where = {}; + + // 搜索条件 + if (search) { + where[Op.or] = [ + { applicationNumber: { [Op.like]: `%${search}%` } }, + { contractNumber: { [Op.like]: `%${search}%` } }, + { customerName: { [Op.like]: `%${search}%` } }, + { productName: { [Op.like]: `%${search}%` } } + ]; + } + + // 状态筛选 + if (supervisionStatus) { + where.supervisionStatus = supervisionStatus; + } + + // 日期范围筛选 + if (dateRange) { + const [startDate, endDate] = dateRange.split(','); + if (startDate && endDate) { + where.importTime = { + [Op.between]: [new Date(startDate), new Date(endDate)] + }; + } + } + + // 查询监管任务列表 + const { count, rows: tasks } = await SupervisionTask.findAndCountAll({ + where, + include: [ + { + model: User, + as: 'creator', + attributes: ['id', 'username', 'real_name'] + }, + { + model: User, + as: 'updater', + attributes: ['id', 'username', 'real_name'] + } + ], + order: [[sortBy, sortOrder.toUpperCase()]], + limit: parseInt(limit), + offset: parseInt(offset) + }); + + // 计算分页信息 + const totalPages = Math.ceil(count / limit); + const hasNextPage = page < totalPages; + const hasPrevPage = page > 1; + + res.status(200).json({ + success: true, + message: '监管任务列表获取成功', + data: { + tasks, + pagination: { + total: count, + currentPage: parseInt(page), + pageSize: parseInt(limit), + totalPages, + hasNextPage, + hasPrevPage + } + } + }); + } catch (error) { + console.error('获取监管任务列表失败:', error); + res.status(500).json({ + success: false, + message: '获取监管任务列表失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 获取监管任务详情 +const getSupervisionTaskById = async (req, res) => { + try { + const { id } = req.params; + const task = await SupervisionTask.findByPk(id, { + include: [ + { + model: User, + as: 'creator', + attributes: ['id', 'username', 'real_name'] + }, + { + model: User, + as: 'updater', + attributes: ['id', 'username', 'real_name'] + } + ] + }); + + if (!task) { + return res.status(404).json({ + success: false, + message: '监管任务不存在' + }); + } + + res.status(200).json({ + success: true, + message: '监管任务详情获取成功', + data: task + }); + } catch (error) { + console.error('获取监管任务详情失败:', error); + res.status(500).json({ + success: false, + message: '获取监管任务详情失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 创建监管任务 +const createSupervisionTask = async (req, res) => { + try { + const { + applicationNumber, + contractNumber, + productName, + customerName, + idType = 'id_card', + idNumber, + assetType = 'cattle', + assetQuantity = 0, + supervisionStatus = 'pending', + startTime, + endTime, + loanAmount = 0, + interestRate = 0, + loanTerm = 12, + supervisorName, + supervisorPhone, + farmAddress, + remarks + } = req.body; + + // 验证必填字段 + const requiredFields = ['applicationNumber', 'contractNumber', 'productName', 'customerName', 'idNumber', 'startTime', 'endTime']; + const missingFields = requiredFields.filter(field => !req.body[field]); + + if (missingFields.length > 0) { + return res.status(400).json({ + success: false, + message: `请填写所有必填字段: ${missingFields.join(', ')}` + }); + } + + // 验证字段长度 + if (applicationNumber.length > 50) { + return res.status(400).json({ + success: false, + message: '申请单号不能超过50个字符' + }); + } + + if (contractNumber.length > 50) { + return res.status(400).json({ + success: false, + message: '放款合同编号不能超过50个字符' + }); + } + + if (productName.length > 100) { + return res.status(400).json({ + success: false, + message: '产品名称不能超过100个字符' + }); + } + + if (customerName.length > 50) { + return res.status(400).json({ + success: false, + message: '客户姓名不能超过50个字符' + }); + } + + // 验证数值字段 + if (assetQuantity < 0) { + return res.status(400).json({ + success: false, + message: '监管生资数量不能为负数' + }); + } + + if (loanAmount < 0) { + return res.status(400).json({ + success: false, + message: '贷款金额不能为负数' + }); + } + + if (interestRate < 0 || interestRate > 1) { + return res.status(400).json({ + success: false, + message: '利率必须在0-1之间' + }); + } + + if (loanTerm < 0) { + return res.status(400).json({ + success: false, + message: '贷款期限不能为负数' + }); + } + + // 验证状态和类型 + if (!['pending', 'supervising', 'completed', 'suspended'].includes(supervisionStatus)) { + return res.status(400).json({ + success: false, + message: '监管状态无效' + }); + } + + if (!['id_card', 'passport', 'other'].includes(idType)) { + return res.status(400).json({ + success: false, + message: '证件类型无效' + }); + } + + if (!['cattle', 'sheep', 'pig', 'poultry', 'other'].includes(assetType)) { + return res.status(400).json({ + success: false, + message: '养殖生资种类无效' + }); + } + + // 验证日期格式和逻辑 + const startDate = new Date(startTime); + const endDate = new Date(endTime); + + if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { + return res.status(400).json({ + success: false, + message: '日期格式不正确' + }); + } + + if (startDate >= endDate) { + return res.status(400).json({ + success: false, + message: '监管结束时间必须晚于开始时间' + }); + } + + // 检查申请单号和合同编号是否已存在 + const existingTask = await SupervisionTask.findOne({ + where: { + [Op.or]: [ + { applicationNumber }, + { contractNumber } + ] + } + }); + + if (existingTask) { + return res.status(400).json({ + success: false, + message: existingTask.applicationNumber === applicationNumber ? + '申请单号已存在' : '放款合同编号已存在' + }); + } + + // 创建监管任务 + const task = await SupervisionTask.create({ + applicationNumber, + contractNumber, + productName, + customerName, + idType, + idNumber, + assetType, + assetQuantity: parseInt(assetQuantity), + supervisionStatus, + startTime, + endTime, + loanAmount: parseFloat(loanAmount), + interestRate: parseFloat(interestRate), + loanTerm: parseInt(loanTerm), + supervisorName, + supervisorPhone, + farmAddress, + remarks, + createdBy: req.user?.userId || req.user?.id, + updatedBy: req.user?.userId || req.user?.id + }); + + // 获取创建的任务详情(包含关联信息) + const createdTask = await SupervisionTask.findByPk(task.id, { + include: [ + { + model: User, + as: 'creator', + attributes: ['id', 'username', 'real_name'] + }, + { + model: User, + as: 'updater', + attributes: ['id', 'username', 'real_name'] + } + ] + }); + + res.status(201).json({ + success: true, + data: createdTask, + message: '监管任务创建成功' + }); + } catch (error) { + console.error('创建监管任务失败:', error); + + // 处理数据库约束错误 + if (error.name === 'SequelizeValidationError') { + const validationErrors = error.errors.map(err => err.message).join(', '); + return res.status(400).json({ + success: false, + message: `数据验证失败: ${validationErrors}` + }); + } + + if (error.name === 'SequelizeUniqueConstraintError') { + return res.status(400).json({ + success: false, + message: '申请单号或放款合同编号已存在' + }); + } + + res.status(500).json({ + success: false, + message: '创建监管任务失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 更新监管任务 +const updateSupervisionTask = async (req, res) => { + try { + const { id } = req.params; + const updateData = req.body; + + // 验证日期 + if (updateData.startTime && updateData.endTime) { + const startDate = new Date(updateData.startTime); + const endDate = new Date(updateData.endTime); + if (startDate >= endDate) { + return res.status(400).json({ + success: false, + message: '监管结束时间必须晚于开始时间' + }); + } + } + + const task = await SupervisionTask.findByPk(id); + + if (!task) { + return res.status(404).json({ + success: false, + message: '监管任务不存在' + }); + } + + // 如果更新申请单号或合同编号,检查是否重复 + if (updateData.applicationNumber || updateData.contractNumber) { + const whereCondition = { id: { [Op.ne]: id } }; + if (updateData.applicationNumber) { + whereCondition.applicationNumber = updateData.applicationNumber; + } + if (updateData.contractNumber) { + whereCondition.contractNumber = updateData.contractNumber; + } + + const existingTask = await SupervisionTask.findOne({ where: whereCondition }); + if (existingTask) { + return res.status(400).json({ + success: false, + message: '申请单号或放款合同编号已存在' + }); + } + } + + await task.update({ + ...updateData, + updatedBy: req.user?.userId || req.user?.id + }); + + res.status(200).json({ + success: true, + data: task, + message: '监管任务更新成功' + }); + } catch (error) { + console.error('更新监管任务失败:', error); + res.status(500).json({ + success: false, + message: '更新监管任务失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 删除监管任务 +const deleteSupervisionTask = async (req, res) => { + try { + const { id } = req.params; + const task = await SupervisionTask.findByPk(id); + + if (!task) { + return res.status(404).json({ + success: false, + message: '监管任务不存在' + }); + } + + await task.destroy(); + + res.status(200).json({ + success: true, + message: '监管任务删除成功' + }); + } catch (error) { + console.error('删除监管任务失败:', error); + res.status(500).json({ + success: false, + message: '删除监管任务失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 获取监管任务统计 +const getSupervisionTaskStats = async (req, res) => { + try { + const total = await SupervisionTask.count(); + const pending = await SupervisionTask.count({ where: { supervisionStatus: 'pending' } }); + const supervising = await SupervisionTask.count({ where: { supervisionStatus: 'supervising' } }); + const completed = await SupervisionTask.count({ where: { supervisionStatus: 'completed' } }); + const suspended = await SupervisionTask.count({ where: { supervisionStatus: 'suspended' } }); + + res.status(200).json({ + success: true, + message: '监管任务统计获取成功', + data: { + total, + pending, + supervising, + completed, + suspended + } + }); + } catch (error) { + console.error('获取监管任务统计失败:', error); + res.status(500).json({ + success: false, + message: '获取监管任务统计失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 批量更新监管任务状态 +const batchUpdateStatus = async (req, res) => { + try { + const { ids, supervisionStatus } = req.body; + + if (!Array.isArray(ids) || ids.length === 0 || !supervisionStatus) { + return res.status(400).json({ + success: false, + message: '请求参数无效' + }); + } + + if (!['pending', 'supervising', 'completed', 'suspended'].includes(supervisionStatus)) { + return res.status(400).json({ + success: false, + message: '监管状态无效' + }); + } + + const [updatedCount] = await SupervisionTask.update( + { + supervisionStatus, + updatedBy: req.user?.userId || req.user?.id + }, + { where: { id: { [Op.in]: ids } } } + ); + + res.status(200).json({ + success: true, + message: `成功更新 ${updatedCount} 个监管任务状态`, + data: { updatedCount } + }); + } catch (error) { + console.error('批量更新监管任务状态失败:', error); + res.status(500).json({ + success: false, + message: '批量更新监管任务状态失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +// 批量删除监管任务 +const batchDeleteTasks = async (req, res) => { + try { + const { ids } = req.body; + + if (!Array.isArray(ids) || ids.length === 0) { + return res.status(400).json({ + success: false, + message: '请求参数无效' + }); + } + + const deletedCount = await SupervisionTask.destroy({ + where: { id: { [Op.in]: ids } } + }); + + res.status(200).json({ + success: true, + message: `成功删除 ${deletedCount} 个监管任务`, + data: { deletedCount } + }); + } catch (error) { + console.error('批量删除监管任务失败:', error); + res.status(500).json({ + success: false, + message: '批量删除监管任务失败', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}; + +module.exports = { + getSupervisionTasks, + getSupervisionTaskById, + createSupervisionTask, + updateSupervisionTask, + deleteSupervisionTask, + getSupervisionTaskStats, + batchUpdateStatus, + batchDeleteTasks +}; diff --git a/bank-backend/docs/PROJECT_API.md b/bank-backend/docs/PROJECT_API.md new file mode 100644 index 0000000..afac5d0 --- /dev/null +++ b/bank-backend/docs/PROJECT_API.md @@ -0,0 +1,403 @@ +# 项目清单 API 接口文档 + +## 概述 +项目清单管理系统的后端API接口,提供项目的增删改查功能。 + +## 基础信息 +- **基础URL**: `http://localhost:5351/api/projects` +- **认证方式**: Bearer Token +- **内容类型**: `application/json` + +## 接口列表 + +### 1. 创建项目 +**POST** `/api/projects` + +#### 请求头 +``` +Authorization: Bearer +Content-Type: application/json +``` + +#### 请求参数 +| 字段名 | 类型 | 必填 | 说明 | 示例 | +|--------|------|------|------|------| +| name | string | 是 | 项目名称,最大100字符 | "张洪彬养殖项目" | +| farmName | string | 是 | 养殖场名称,最大200字符 | "张洪彬养殖场" | +| supervisionObject | string | 是 | 监管对象 | "牛" | +| supervisionPeriod | string | 是 | 监管周期 | "12个月" | +| startTime | string | 是 | 起始时间,格式:YYYY-MM-DD | "2024-01-01" | +| endTime | string | 是 | 结束时间,格式:YYYY-MM-DD | "2024-12-31" | +| status | string | 否 | 项目状态,默认:supervision | "supervision" 或 "completed" | +| supervisionQuantity | number | 否 | 监管数量,默认:0 | 100 | +| supervisionAmount | number | 否 | 监管金额,默认:0.00 | 500000.00 | +| earTag | number | 否 | 耳标数量,默认:0 | 50 | +| collar | number | 否 | 项圈数量,默认:0 | 30 | +| host | number | 否 | 主机数量,默认:0 | 20 | +| loanOfficer | string | 否 | 贷款专员 | "张专员" | +| description | string | 否 | 项目描述 | "这是一个测试项目" | + +#### 请求示例 +```json +{ + "name": "张洪彬养殖项目", + "farmName": "张洪彬养殖场", + "supervisionObject": "牛", + "supervisionPeriod": "12个月", + "startTime": "2024-01-01", + "endTime": "2024-12-31", + "status": "supervision", + "supervisionQuantity": 100, + "supervisionAmount": 500000.00, + "earTag": 50, + "collar": 30, + "host": 20, + "loanOfficer": "张专员", + "description": "这是一个测试项目" +} +``` + +#### 响应示例 +**成功响应 (201)** +```json +{ + "success": true, + "message": "创建项目成功", + "data": { + "id": 9, + "name": "张洪彬养殖项目", + "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": "这是一个测试项目", + "createdBy": 1, + "updatedBy": 1, + "createdAt": "2024-12-20T10:30:00.000Z", + "updatedAt": "2024-12-20T10:30:00.000Z", + "creator": { + "id": 1, + "username": "admin", + "real_name": "管理员" + }, + "updater": { + "id": 1, + "username": "admin", + "real_name": "管理员" + } + } +} +``` + +**错误响应 (400)** +```json +{ + "success": false, + "message": "请填写所有必填字段: name, farmName" +} +``` + +**错误响应 (400)** +```json +{ + "success": false, + "message": "项目名称已存在,请使用其他名称" +} +``` + +**错误响应 (400)** +```json +{ + "success": false, + "message": "结束时间必须晚于开始时间" +} +``` + +### 2. 获取项目列表 +**GET** `/api/projects` + +#### 查询参数 +| 参数名 | 类型 | 必填 | 说明 | 示例 | +|--------|------|------|------|------| +| page | number | 否 | 页码,默认:1 | 1 | +| limit | number | 否 | 每页数量,默认:10 | 12 | +| search | string | 否 | 搜索关键词 | "张洪彬" | +| status | string | 否 | 状态筛选 | "supervision" 或 "completed" | +| sortBy | string | 否 | 排序字段,默认:createdAt | "name" | +| sortOrder | string | 否 | 排序方向,默认:DESC | "ASC" 或 "DESC" | + +#### 请求示例 +``` +GET /api/projects?page=1&limit=12&search=张洪彬&status=supervision&sortBy=name&sortOrder=ASC +``` + +#### 响应示例 +```json +{ + "success": true, + "message": "获取项目列表成功", + "data": { + "projects": [ + { + "id": 1, + "name": "张洪彬", + "status": "completed", + "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": "项目描述", + "createdAt": "2024-12-20T10:30:00.000Z", + "updatedAt": "2024-12-20T10:30:00.000Z", + "creator": { + "id": 1, + "username": "admin", + "real_name": "管理员" + }, + "updater": { + "id": 1, + "username": "admin", + "real_name": "管理员" + } + } + ], + "pagination": { + "total": 8, + "currentPage": 1, + "pageSize": 12, + "totalPages": 1, + "hasNextPage": false, + "hasPrevPage": false + } + } +} +``` + +### 3. 获取项目详情 +**GET** `/api/projects/:id` + +#### 路径参数 +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| id | number | 是 | 项目ID | + +#### 响应示例 +```json +{ + "success": true, + "message": "获取项目详情成功", + "data": { + "id": 1, + "name": "张洪彬", + "status": "completed", + "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": "项目描述", + "createdAt": "2024-12-20T10:30:00.000Z", + "updatedAt": "2024-12-20T10:30:00.000Z", + "creator": { + "id": 1, + "username": "admin", + "real_name": "管理员" + }, + "updater": { + "id": 1, + "username": "admin", + "real_name": "管理员" + } + } +} +``` + +### 4. 更新项目 +**PUT** `/api/projects/:id` + +#### 请求参数 +与创建项目相同,所有字段都是可选的。 + +#### 响应示例 +```json +{ + "success": true, + "message": "项目更新成功", + "data": { + "id": 1, + "name": "更新后的项目名称", + "status": "completed", + // ... 其他字段 + } +} +``` + +### 5. 删除项目 +**DELETE** `/api/projects/:id` + +#### 响应示例 +```json +{ + "success": true, + "message": "项目删除成功" +} +``` + +### 6. 获取项目统计 +**GET** `/api/projects/stats` + +#### 响应示例 +```json +{ + "success": true, + "message": "项目统计获取成功", + "data": { + "total": 8, + "supervision": 3, + "completed": 5 + } +} +``` + +### 7. 批量更新项目状态 +**PUT** `/api/projects/batch/status` + +#### 请求参数 +```json +{ + "ids": [1, 2, 3], + "status": "completed" +} +``` + +#### 响应示例 +```json +{ + "success": true, + "message": "成功更新 3 个项目状态", + "data": { + "updatedCount": 3 + } +} +``` + +## 错误码说明 + +| 状态码 | 说明 | +|--------|------| +| 200 | 请求成功 | +| 201 | 创建成功 | +| 400 | 请求参数错误 | +| 401 | 未授权,需要登录 | +| 403 | 禁止访问,权限不足 | +| 404 | 资源不存在 | +| 500 | 服务器内部错误 | + +## 数据验证规则 + +### 必填字段验证 +- `name`: 项目名称,不能为空 +- `farmName`: 养殖场名称,不能为空 +- `supervisionObject`: 监管对象,不能为空 +- `supervisionPeriod`: 监管周期,不能为空 +- `startTime`: 起始时间,不能为空 +- `endTime`: 结束时间,不能为空 + +### 字段长度限制 +- `name`: 最大100个字符 +- `farmName`: 最大200个字符 +- `supervisionObject`: 最大50个字符 +- `supervisionPeriod`: 最大50个字符 +- `loanOfficer`: 最大100个字符 + +### 数值字段验证 +- `supervisionQuantity`: 不能为负数 +- `supervisionAmount`: 不能为负数 +- `earTag`: 不能为负数 +- `collar`: 不能为负数 +- `host`: 不能为负数 + +### 日期验证 +- `startTime` 和 `endTime` 必须是有效的日期格式 (YYYY-MM-DD) +- `endTime` 必须晚于 `startTime` + +### 状态验证 +- `status` 只能是 "supervision" 或 "completed" + +## 使用示例 + +### JavaScript (axios) +```javascript +// 创建项目 +const createProject = async (projectData) => { + try { + const response = await axios.post('/api/projects', projectData, { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } + }); + return response.data; + } catch (error) { + console.error('创建项目失败:', error.response.data); + throw error; + } +}; + +// 获取项目列表 +const getProjects = async (params = {}) => { + try { + const response = await axios.get('/api/projects', { + headers: { + 'Authorization': `Bearer ${token}` + }, + params + }); + return response.data; + } catch (error) { + console.error('获取项目列表失败:', error.response.data); + throw error; + } +}; +``` + +### cURL +```bash +# 创建项目 +curl -X POST http://localhost:5351/api/projects \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "测试项目", + "farmName": "测试养殖场", + "supervisionObject": "牛", + "supervisionPeriod": "12个月", + "startTime": "2024-01-01", + "endTime": "2024-12-31" + }' + +# 获取项目列表 +curl -X GET "http://localhost:5351/api/projects?page=1&limit=10" \ + -H "Authorization: Bearer YOUR_TOKEN" +``` diff --git a/bank-backend/docs/SUPERVISION_TASKS_API.md b/bank-backend/docs/SUPERVISION_TASKS_API.md new file mode 100644 index 0000000..68aa301 --- /dev/null +++ b/bank-backend/docs/SUPERVISION_TASKS_API.md @@ -0,0 +1,459 @@ +# 监管任务 API 接口文档 + +## 概述 +监管任务管理系统的后端API接口,提供监管任务的增删改查功能。 + +## 基础信息 +- **基础URL**: `http://localhost:5351/api/supervision-tasks` +- **认证方式**: Bearer Token +- **内容类型**: `application/json` + +## 接口列表 + +### 1. 创建监管任务 +**POST** `/api/supervision-tasks` + +#### 请求头 +``` +Authorization: Bearer +Content-Type: application/json +``` + +#### 请求参数 +| 字段名 | 类型 | 必填 | 说明 | 示例 | +|--------|------|------|------|------| +| applicationNumber | string | 是 | 申请单号,最大50字符,唯一 | "APP001" | +| contractNumber | string | 是 | 放款合同编号,最大50字符,唯一 | "CONTRACT001" | +| productName | string | 是 | 产品名称,最大100字符 | "农业贷款产品A" | +| customerName | string | 是 | 客户姓名,最大50字符 | "张三" | +| idType | string | 是 | 证件类型 | "id_card" (身份证), "passport" (护照), "other" (其他) | +| idNumber | string | 是 | 证件号码,最大50字符 | "110101199001011234" | +| assetType | string | 是 | 养殖生资种类 | "cattle" (牛), "sheep" (羊), "pig" (猪), "poultry" (家禽), "other" (其他) | +| assetQuantity | number | 是 | 监管生资数量,非负数 | 10 | +| startTime | string | 是 | 监管起始时间,格式:YYYY-MM-DD | "2024-01-15" | +| endTime | string | 是 | 监管结束时间,格式:YYYY-MM-DD | "2024-12-15" | +| supervisionStatus | string | 否 | 监管状态,默认:pending | "pending", "supervising", "completed", "suspended" | +| loanAmount | number | 否 | 贷款金额,默认:0 | 500000.00 | +| interestRate | number | 否 | 利率,默认:0,范围:0-1 | 0.0650 | +| loanTerm | number | 否 | 贷款期限(月),默认:12 | 12 | +| supervisorName | string | 否 | 监管员姓名,最大50字符 | "李监管员" | +| supervisorPhone | string | 否 | 监管员电话,最大20字符 | "13800138001" | +| farmAddress | string | 否 | 养殖场地址,最大200字符 | "北京市朝阳区某某养殖场" | +| remarks | string | 否 | 备注 | "重点监管项目,需要定期检查" | + +#### 请求示例 +```json +{ + "applicationNumber": "APP001", + "contractNumber": "CONTRACT001", + "productName": "农业贷款产品A", + "customerName": "张三", + "idType": "id_card", + "idNumber": "110101199001011234", + "assetType": "cattle", + "assetQuantity": 10, + "supervisionStatus": "pending", + "startTime": "2024-01-15", + "endTime": "2024-12-15", + "loanAmount": 500000.00, + "interestRate": 0.0650, + "loanTerm": 12, + "supervisorName": "李监管员", + "supervisorPhone": "13800138001", + "farmAddress": "北京市朝阳区某某养殖场", + "remarks": "重点监管项目,需要定期检查" +} +``` + +#### 响应示例 +**成功响应 (201)** +```json +{ + "success": true, + "message": "监管任务创建成功", + "data": { + "id": 1, + "applicationNumber": "APP001", + "contractNumber": "CONTRACT001", + "productName": "农业贷款产品A", + "customerName": "张三", + "idType": "id_card", + "idNumber": "110101199001011234", + "assetType": "cattle", + "assetQuantity": 10, + "supervisionStatus": "pending", + "importTime": "2024-12-20T10:30:00.000Z", + "startTime": "2024-01-15", + "endTime": "2024-12-15", + "loanAmount": "500000.00", + "interestRate": "0.0650", + "loanTerm": 12, + "supervisorName": "李监管员", + "supervisorPhone": "13800138001", + "farmAddress": "北京市朝阳区某某养殖场", + "remarks": "重点监管项目,需要定期检查", + "createdBy": 1, + "updatedBy": 1, + "createdAt": "2024-12-20T10:30:00.000Z", + "updatedAt": "2024-12-20T10:30:00.000Z", + "creator": { + "id": 1, + "username": "admin", + "real_name": "管理员" + }, + "updater": { + "id": 1, + "username": "admin", + "real_name": "管理员" + } + } +} +``` + +### 2. 获取监管任务列表 +**GET** `/api/supervision-tasks` + +#### 查询参数 +| 参数名 | 类型 | 必填 | 说明 | 示例 | +|--------|------|------|------|------| +| page | number | 否 | 页码,默认:1 | 1 | +| limit | number | 否 | 每页数量,默认:10 | 10 | +| search | string | 否 | 搜索关键词(申请单号、合同编号、客户姓名、产品名称) | "张三" | +| supervisionStatus | string | 否 | 状态筛选 | "pending", "supervising", "completed", "suspended" | +| dateRange | string | 否 | 日期范围筛选,格式:startDate,endDate | "2024-01-01,2024-12-31" | +| sortBy | string | 否 | 排序字段,默认:createdAt | "applicationNumber", "customerName", "supervisionStatus" | +| sortOrder | string | 否 | 排序方向,默认:DESC | "ASC", "DESC" | + +#### 请求示例 +``` +GET /api/supervision-tasks?page=1&limit=10&search=张三&supervisionStatus=supervising&dateRange=2024-01-01,2024-12-31&sortBy=customerName&sortOrder=ASC +``` + +#### 响应示例 +```json +{ + "success": true, + "message": "监管任务列表获取成功", + "data": { + "tasks": [ + { + "id": 1, + "applicationNumber": "APP001", + "contractNumber": "CONTRACT001", + "productName": "农业贷款产品A", + "customerName": "张三", + "idType": "id_card", + "idNumber": "110101199001011234", + "assetType": "cattle", + "assetQuantity": 10, + "supervisionStatus": "supervising", + "importTime": "2024-01-15T10:30:00.000Z", + "startTime": "2024-01-15", + "endTime": "2024-12-15", + "loanAmount": "500000.00", + "interestRate": "0.0650", + "loanTerm": 12, + "supervisorName": "李监管员", + "supervisorPhone": "13800138001", + "farmAddress": "北京市朝阳区某某养殖场", + "remarks": "重点监管项目,需要定期检查", + "createdAt": "2024-12-20T10:30:00.000Z", + "updatedAt": "2024-12-20T10:30:00.000Z", + "creator": { + "id": 1, + "username": "admin", + "real_name": "管理员" + }, + "updater": { + "id": 1, + "username": "admin", + "real_name": "管理员" + } + } + ], + "pagination": { + "total": 8, + "currentPage": 1, + "pageSize": 10, + "totalPages": 1, + "hasNextPage": false, + "hasPrevPage": false + } + } +} +``` + +### 3. 获取监管任务详情 +**GET** `/api/supervision-tasks/:id` + +#### 路径参数 +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| id | number | 是 | 监管任务ID | + +#### 响应示例 +```json +{ + "success": true, + "message": "监管任务详情获取成功", + "data": { + "id": 1, + "applicationNumber": "APP001", + "contractNumber": "CONTRACT001", + "productName": "农业贷款产品A", + "customerName": "张三", + "idType": "id_card", + "idNumber": "110101199001011234", + "assetType": "cattle", + "assetQuantity": 10, + "supervisionStatus": "supervising", + "importTime": "2024-01-15T10:30:00.000Z", + "startTime": "2024-01-15", + "endTime": "2024-12-15", + "loanAmount": "500000.00", + "interestRate": "0.0650", + "loanTerm": 12, + "supervisorName": "李监管员", + "supervisorPhone": "13800138001", + "farmAddress": "北京市朝阳区某某养殖场", + "remarks": "重点监管项目,需要定期检查", + "createdAt": "2024-12-20T10:30:00.000Z", + "updatedAt": "2024-12-20T10:30:00.000Z", + "creator": { + "id": 1, + "username": "admin", + "real_name": "管理员" + }, + "updater": { + "id": 1, + "username": "admin", + "real_name": "管理员" + } + } +} +``` + +### 4. 更新监管任务 +**PUT** `/api/supervision-tasks/:id` + +#### 请求参数 +与创建监管任务相同,所有字段都是可选的。 + +#### 响应示例 +```json +{ + "success": true, + "message": "监管任务更新成功", + "data": { + "id": 1, + "applicationNumber": "APP001", + "contractNumber": "CONTRACT001", + "productName": "农业贷款产品A", + "customerName": "张三", + "supervisionStatus": "supervising", + "remarks": "更新后的备注信息", + "updatedAt": "2024-12-20T11:00:00.000Z" + } +} +``` + +### 5. 删除监管任务 +**DELETE** `/api/supervision-tasks/:id` + +#### 响应示例 +```json +{ + "success": true, + "message": "监管任务删除成功" +} +``` + +### 6. 获取监管任务统计 +**GET** `/api/supervision-tasks/stats` + +#### 响应示例 +```json +{ + "success": true, + "message": "监管任务统计获取成功", + "data": { + "total": 8, + "pending": 2, + "supervising": 3, + "completed": 2, + "suspended": 1 + } +} +``` + +### 7. 批量更新监管任务状态 +**PUT** `/api/supervision-tasks/batch/status` + +#### 请求参数 +```json +{ + "ids": [1, 2, 3], + "supervisionStatus": "completed" +} +``` + +#### 响应示例 +```json +{ + "success": true, + "message": "成功更新 3 个监管任务状态", + "data": { + "updatedCount": 3 + } +} +``` + +### 8. 批量删除监管任务 +**DELETE** `/api/supervision-tasks/batch` + +#### 请求参数 +```json +{ + "ids": [1, 2, 3] +} +``` + +#### 响应示例 +```json +{ + "success": true, + "message": "成功删除 3 个监管任务", + "data": { + "deletedCount": 3 + } +} +``` + +## 错误码说明 + +| 状态码 | 说明 | +|--------|------| +| 200 | 请求成功 | +| 201 | 创建成功 | +| 400 | 请求参数错误 | +| 401 | 未授权,需要登录 | +| 403 | 禁止访问,权限不足 | +| 404 | 资源不存在 | +| 500 | 服务器内部错误 | + +## 数据验证规则 + +### 必填字段验证 +- `applicationNumber`: 申请单号,不能为空,最大50字符,唯一 +- `contractNumber`: 放款合同编号,不能为空,最大50字符,唯一 +- `productName`: 产品名称,不能为空,最大100字符 +- `customerName`: 客户姓名,不能为空,最大50字符 +- `idNumber`: 证件号码,不能为空,最大50字符 +- `startTime`: 监管起始时间,不能为空 +- `endTime`: 监管结束时间,不能为空 + +### 枚举值验证 +- `idType`: 只能是 "id_card", "passport", "other" +- `assetType`: 只能是 "cattle", "sheep", "pig", "poultry", "other" +- `supervisionStatus`: 只能是 "pending", "supervising", "completed", "suspended" + +### 数值字段验证 +- `assetQuantity`: 不能为负数 +- `loanAmount`: 不能为负数 +- `interestRate`: 必须在0-1之间 +- `loanTerm`: 不能为负数 + +### 日期验证 +- `startTime` 和 `endTime` 必须是有效的日期格式 (YYYY-MM-DD) +- `endTime` 必须晚于 `startTime` + +## 使用示例 + +### JavaScript (axios) +```javascript +// 创建监管任务 +const createSupervisionTask = async (taskData) => { + try { + const response = await axios.post('/api/supervision-tasks', taskData, { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } + }); + return response.data; + } catch (error) { + console.error('创建监管任务失败:', error.response.data); + throw error; + } +}; + +// 获取监管任务列表 +const getSupervisionTasks = async (params = {}) => { + try { + const response = await axios.get('/api/supervision-tasks', { + headers: { + 'Authorization': `Bearer ${token}` + }, + params + }); + return response.data; + } catch (error) { + console.error('获取监管任务列表失败:', error.response.data); + throw error; + } +}; +``` + +### cURL +```bash +# 创建监管任务 +curl -X POST http://localhost:5351/api/supervision-tasks \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "applicationNumber": "APP001", + "contractNumber": "CONTRACT001", + "productName": "农业贷款产品A", + "customerName": "张三", + "idType": "id_card", + "idNumber": "110101199001011234", + "assetType": "cattle", + "assetQuantity": 10, + "startTime": "2024-01-15", + "endTime": "2024-12-15" + }' + +# 获取监管任务列表 +curl -X GET "http://localhost:5351/api/supervision-tasks?page=1&limit=10" \ + -H "Authorization: Bearer YOUR_TOKEN" +``` + +## 数据库表结构 + +### supervision_tasks 表 +| 字段名 | 类型 | 说明 | +|--------|------|------| +| id | INTEGER | 主键,自增 | +| applicationNumber | VARCHAR(50) | 申请单号,唯一 | +| contractNumber | VARCHAR(50) | 放款合同编号,唯一 | +| productName | VARCHAR(100) | 产品名称 | +| customerName | VARCHAR(50) | 客户姓名 | +| idType | ENUM | 证件类型 | +| idNumber | VARCHAR(50) | 证件号码 | +| assetType | ENUM | 养殖生资种类 | +| assetQuantity | INTEGER | 监管生资数量 | +| supervisionStatus | ENUM | 监管状态 | +| importTime | DATETIME | 任务导入时间 | +| startTime | DATE | 监管起始时间 | +| endTime | DATE | 监管结束时间 | +| loanAmount | DECIMAL(15,2) | 贷款金额 | +| interestRate | DECIMAL(5,4) | 利率 | +| loanTerm | INTEGER | 贷款期限(月) | +| supervisorName | VARCHAR(50) | 监管员姓名 | +| supervisorPhone | VARCHAR(20) | 监管员电话 | +| farmAddress | VARCHAR(200) | 养殖场地址 | +| remarks | TEXT | 备注 | +| createdBy | INTEGER | 创建人ID | +| updatedBy | INTEGER | 更新人ID | +| createdAt | DATETIME | 创建时间 | +| updatedAt | DATETIME | 更新时间 | diff --git a/bank-backend/migrations/20241220000002-create-projects.js b/bank-backend/migrations/20241220000002-create-projects.js new file mode 100644 index 0000000..c0b4e7e --- /dev/null +++ b/bank-backend/migrations/20241220000002-create-projects.js @@ -0,0 +1,121 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('projects', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false + }, + name: { + type: Sequelize.STRING(100), + allowNull: false, + comment: '项目名称' + }, + status: { + type: Sequelize.ENUM('supervision', 'completed'), + allowNull: false, + defaultValue: 'supervision', + comment: '项目状态:supervision-监管中,completed-已结项' + }, + farmName: { + type: Sequelize.STRING(200), + allowNull: false, + comment: '养殖场名称' + }, + supervisionObject: { + type: Sequelize.STRING(50), + allowNull: false, + comment: '监管对象' + }, + supervisionQuantity: { + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: 0, + comment: '监管数量' + }, + supervisionPeriod: { + type: Sequelize.STRING(50), + allowNull: false, + comment: '监管周期' + }, + supervisionAmount: { + type: Sequelize.DECIMAL(15, 2), + allowNull: false, + defaultValue: 0.00, + comment: '监管金额' + }, + startTime: { + type: Sequelize.DATEONLY, + allowNull: false, + comment: '起始时间' + }, + endTime: { + type: Sequelize.DATEONLY, + allowNull: false, + comment: '结束时间' + }, + earTag: { + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: 0, + comment: '耳标数量' + }, + collar: { + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: 0, + comment: '项圈数量' + }, + host: { + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: 0, + comment: '主机数量' + }, + loanOfficer: { + type: Sequelize.STRING(100), + allowNull: true, + comment: '贷款专员' + }, + description: { + type: Sequelize.TEXT, + allowNull: true, + comment: '项目描述' + }, + createdBy: { + type: Sequelize.INTEGER, + allowNull: true, + comment: '创建人ID' + }, + updatedBy: { + type: Sequelize.INTEGER, + allowNull: true, + comment: '更新人ID' + }, + createdAt: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.NOW + }, + updatedAt: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.NOW + } + }); + + // 添加索引 + await queryInterface.addIndex('projects', ['status']); + await queryInterface.addIndex('projects', ['farmName']); + await queryInterface.addIndex('projects', ['createdBy']); + await queryInterface.addIndex('projects', ['startTime']); + await queryInterface.addIndex('projects', ['endTime']); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('projects'); + } +}; diff --git a/bank-backend/migrations/20241220000003-create-supervision-tasks.js b/bank-backend/migrations/20241220000003-create-supervision-tasks.js new file mode 100644 index 0000000..5389f6a --- /dev/null +++ b/bank-backend/migrations/20241220000003-create-supervision-tasks.js @@ -0,0 +1,152 @@ +'use strict'; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('supervision_tasks', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false + }, + applicationNumber: { + type: Sequelize.STRING(50), + allowNull: false, + unique: true, + comment: '申请单号' + }, + contractNumber: { + type: Sequelize.STRING(50), + allowNull: false, + unique: true, + comment: '放款合同编号' + }, + productName: { + type: Sequelize.STRING(100), + allowNull: false, + comment: '产品名称' + }, + customerName: { + type: Sequelize.STRING(50), + allowNull: false, + comment: '客户姓名' + }, + idType: { + type: Sequelize.ENUM('id_card', 'passport', 'other'), + allowNull: false, + defaultValue: 'id_card', + comment: '证件类型:id_card-身份证,passport-护照,other-其他' + }, + idNumber: { + type: Sequelize.STRING(50), + allowNull: false, + comment: '证件号码' + }, + assetType: { + type: Sequelize.ENUM('cattle', 'sheep', 'pig', 'poultry', 'other'), + allowNull: false, + defaultValue: 'cattle', + comment: '养殖生资种类:cattle-牛,sheep-羊,pig-猪,poultry-家禽,other-其他' + }, + assetQuantity: { + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: 0, + comment: '监管生资数量' + }, + supervisionStatus: { + type: Sequelize.ENUM('pending', 'supervising', 'completed', 'suspended'), + allowNull: false, + defaultValue: 'pending', + comment: '监管状态:pending-待监管,supervising-监管中,completed-已完成,suspended-已暂停' + }, + importTime: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + comment: '任务导入时间' + }, + startTime: { + type: Sequelize.DATEONLY, + allowNull: false, + comment: '监管起始时间' + }, + endTime: { + type: Sequelize.DATEONLY, + allowNull: false, + comment: '监管结束时间' + }, + loanAmount: { + type: Sequelize.DECIMAL(15, 2), + allowNull: true, + defaultValue: 0.00, + comment: '贷款金额' + }, + interestRate: { + type: Sequelize.DECIMAL(5, 4), + allowNull: true, + defaultValue: 0.0000, + comment: '利率' + }, + loanTerm: { + type: Sequelize.INTEGER, + allowNull: true, + defaultValue: 12, + comment: '贷款期限(月)' + }, + supervisorName: { + type: Sequelize.STRING(50), + allowNull: true, + comment: '监管员姓名' + }, + supervisorPhone: { + type: Sequelize.STRING(20), + allowNull: true, + comment: '监管员电话' + }, + farmAddress: { + type: Sequelize.STRING(200), + allowNull: true, + comment: '养殖场地址' + }, + remarks: { + type: Sequelize.TEXT, + allowNull: true, + comment: '备注' + }, + createdBy: { + type: Sequelize.INTEGER, + allowNull: true, + comment: '创建人ID' + }, + updatedBy: { + type: Sequelize.INTEGER, + allowNull: true, + comment: '更新人ID' + }, + createdAt: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') + }, + updatedAt: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP') + } + }); + + // 添加索引 + await queryInterface.addIndex('supervision_tasks', ['applicationNumber']); + await queryInterface.addIndex('supervision_tasks', ['contractNumber']); + await queryInterface.addIndex('supervision_tasks', ['customerName']); + await queryInterface.addIndex('supervision_tasks', ['supervisionStatus']); + await queryInterface.addIndex('supervision_tasks', ['createdBy']); + await queryInterface.addIndex('supervision_tasks', ['startTime']); + await queryInterface.addIndex('supervision_tasks', ['endTime']); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('supervision_tasks'); + } +}; diff --git a/bank-backend/models/Project.js b/bank-backend/models/Project.js new file mode 100644 index 0000000..cc99922 --- /dev/null +++ b/bank-backend/models/Project.js @@ -0,0 +1,119 @@ +const { DataTypes } = require('sequelize'); +const { sequelize } = require('../config/database'); + +const Project = sequelize.define('Project', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true + }, + name: { + type: DataTypes.STRING(100), + allowNull: false, + comment: '项目名称' + }, + status: { + type: DataTypes.ENUM('supervision', 'completed'), + allowNull: false, + defaultValue: 'supervision', + comment: '项目状态:supervision-监管中,completed-已结项' + }, + farmName: { + type: DataTypes.STRING(200), + allowNull: false, + comment: '养殖场名称' + }, + supervisionObject: { + type: DataTypes.STRING(50), + allowNull: false, + comment: '监管对象' + }, + supervisionQuantity: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0, + comment: '监管数量' + }, + supervisionPeriod: { + type: DataTypes.STRING(50), + allowNull: false, + comment: '监管周期' + }, + supervisionAmount: { + type: DataTypes.DECIMAL(15, 2), + allowNull: false, + defaultValue: 0.00, + comment: '监管金额' + }, + startTime: { + type: DataTypes.DATEONLY, + allowNull: false, + comment: '起始时间' + }, + endTime: { + type: DataTypes.DATEONLY, + allowNull: false, + comment: '结束时间' + }, + earTag: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0, + comment: '耳标数量' + }, + collar: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0, + comment: '项圈数量' + }, + host: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0, + comment: '主机数量' + }, + loanOfficer: { + type: DataTypes.STRING(100), + allowNull: true, + comment: '贷款专员' + }, + description: { + type: DataTypes.TEXT, + allowNull: true, + comment: '项目描述' + }, + createdBy: { + type: DataTypes.INTEGER, + allowNull: true, + comment: '创建人ID' + }, + updatedBy: { + type: DataTypes.INTEGER, + allowNull: true, + comment: '更新人ID' + } +}, { + tableName: 'projects', + timestamps: true, + createdAt: 'createdAt', + updatedAt: 'updatedAt', + comment: '项目清单表' +}); + +// 定义关联关系 +Project.associate = (models) => { + // 项目与用户关联(创建人) + Project.belongsTo(models.User, { + foreignKey: 'createdBy', + as: 'creator' + }); + + // 项目与用户关联(更新人) + Project.belongsTo(models.User, { + foreignKey: 'updatedBy', + as: 'updater' + }); +}; + +module.exports = Project; diff --git a/bank-backend/models/SupervisionTask.js b/bank-backend/models/SupervisionTask.js new file mode 100644 index 0000000..de1f898 --- /dev/null +++ b/bank-backend/models/SupervisionTask.js @@ -0,0 +1,166 @@ +/** + * 监管任务模型 + * @file SupervisionTask.js + * @description 监管任务数据模型定义 + */ +const { DataTypes } = require('sequelize'); +const BaseModel = require('./BaseModel'); +const { sequelize } = require('../config/database'); + +class SupervisionTask extends BaseModel {} + +SupervisionTask.init({ + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true + }, + applicationNumber: { + type: DataTypes.STRING(50), + allowNull: false, + unique: true, + comment: '申请单号' + }, + contractNumber: { + type: DataTypes.STRING(50), + allowNull: false, + unique: true, + comment: '放款合同编号' + }, + productName: { + type: DataTypes.STRING(100), + allowNull: false, + comment: '产品名称' + }, + customerName: { + type: DataTypes.STRING(50), + allowNull: false, + comment: '客户姓名' + }, + idType: { + type: DataTypes.ENUM('id_card', 'passport', 'other'), + allowNull: false, + defaultValue: 'id_card', + comment: '证件类型:id_card-身份证,passport-护照,other-其他' + }, + idNumber: { + type: DataTypes.STRING(50), + allowNull: false, + comment: '证件号码' + }, + assetType: { + type: DataTypes.ENUM('cattle', 'sheep', 'pig', 'poultry', 'other'), + allowNull: false, + defaultValue: 'cattle', + comment: '养殖生资种类:cattle-牛,sheep-羊,pig-猪,poultry-家禽,other-其他' + }, + assetQuantity: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0, + comment: '监管生资数量' + }, + supervisionStatus: { + type: DataTypes.ENUM('pending', 'supervising', 'completed', 'suspended'), + allowNull: false, + defaultValue: 'pending', + comment: '监管状态:pending-待监管,supervising-监管中,completed-已完成,suspended-已暂停' + }, + importTime: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + comment: '任务导入时间' + }, + startTime: { + type: DataTypes.DATEONLY, + allowNull: false, + comment: '监管起始时间' + }, + endTime: { + type: DataTypes.DATEONLY, + allowNull: false, + comment: '监管结束时间' + }, + loanAmount: { + type: DataTypes.DECIMAL(15, 2), + allowNull: true, + defaultValue: 0.00, + comment: '贷款金额' + }, + interestRate: { + type: DataTypes.DECIMAL(5, 4), + allowNull: true, + defaultValue: 0.0000, + comment: '利率' + }, + loanTerm: { + type: DataTypes.INTEGER, + allowNull: true, + defaultValue: 12, + comment: '贷款期限(月)' + }, + supervisorName: { + type: DataTypes.STRING(50), + allowNull: true, + comment: '监管员姓名' + }, + supervisorPhone: { + type: DataTypes.STRING(20), + allowNull: true, + comment: '监管员电话' + }, + farmAddress: { + type: DataTypes.STRING(200), + allowNull: true, + comment: '养殖场地址' + }, + remarks: { + type: DataTypes.TEXT, + allowNull: true, + comment: '备注' + }, + createdBy: { + type: DataTypes.INTEGER, + allowNull: true, + comment: '创建人ID' + }, + updatedBy: { + type: DataTypes.INTEGER, + allowNull: true, + comment: '更新人ID' + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW + } +}, { + sequelize, + modelName: 'SupervisionTask', + tableName: 'supervision_tasks', + timestamps: true, + underscored: true, + createdAt: 'createdAt', + updatedAt: 'updatedAt', + comment: '监管任务表' +}); + +// 定义关联关系 +SupervisionTask.associate = (models) => { + SupervisionTask.belongsTo(models.User, { + foreignKey: 'createdBy', + as: 'creator' + }); + SupervisionTask.belongsTo(models.User, { + foreignKey: 'updatedBy', + as: 'updater' + }); +}; + +module.exports = SupervisionTask; diff --git a/bank-backend/models/index.js b/bank-backend/models/index.js index b18cda3..ee624de 100644 --- a/bank-backend/models/index.js +++ b/bank-backend/models/index.js @@ -15,6 +15,8 @@ const Employee = require('./Employee'); const Department = require('./Department'); const Position = require('./Position'); const Report = require('./Report'); +const Project = require('./Project'); +const SupervisionTask = require('./SupervisionTask'); // 定义模型关联关系 @@ -91,6 +93,54 @@ User.hasMany(Report, { as: 'reports' }); +// 项目与用户关联(创建人) +Project.belongsTo(User, { + foreignKey: 'createdBy', + as: 'creator', + targetKey: 'id' +}); + +User.hasMany(Project, { + foreignKey: 'createdBy', + as: 'createdProjects' +}); + +// 项目与用户关联(更新人) +Project.belongsTo(User, { + foreignKey: 'updatedBy', + as: 'updater', + targetKey: 'id' +}); + +User.hasMany(Project, { + foreignKey: 'updatedBy', + as: 'updatedProjects' +}); + +// 监管任务与用户关联(创建人) +SupervisionTask.belongsTo(User, { + foreignKey: 'createdBy', + as: 'creator', + targetKey: 'id' +}); + +User.hasMany(SupervisionTask, { + foreignKey: 'createdBy', + as: 'createdSupervisionTasks' +}); + +// 监管任务与用户关联(更新人) +SupervisionTask.belongsTo(User, { + foreignKey: 'updatedBy', + as: 'updater', + targetKey: 'id' +}); + +User.hasMany(SupervisionTask, { + foreignKey: 'updatedBy', + as: 'updatedSupervisionTasks' +}); + // 导出所有模型和数据库实例 module.exports = { sequelize, @@ -102,5 +152,7 @@ module.exports = { Employee, Department, Position, - Report + Report, + Project, + SupervisionTask }; \ No newline at end of file diff --git a/bank-backend/routes/projects.js b/bank-backend/routes/projects.js new file mode 100644 index 0000000..2492093 --- /dev/null +++ b/bank-backend/routes/projects.js @@ -0,0 +1,37 @@ +const express = require('express'); +const router = express.Router(); +const projectController = require('../controllers/projectController'); +const { authMiddleware } = require('../middleware/auth'); + +// 应用认证中间件到所有路由 +router.use(authMiddleware); + +// 获取项目列表 +// GET /api/projects +router.get('/', projectController.getProjects); + +// 获取项目统计 +// GET /api/projects/stats +router.get('/stats', projectController.getProjectStats); + +// 获取项目详情 +// GET /api/projects/:id +router.get('/:id', projectController.getProjectById); + +// 创建项目 +// POST /api/projects +router.post('/', projectController.createProject); + +// 更新项目 +// PUT /api/projects/:id +router.put('/:id', projectController.updateProject); + +// 删除项目 +// DELETE /api/projects/:id +router.delete('/:id', projectController.deleteProject); + +// 批量更新项目状态 +// PUT /api/projects/batch/status +router.put('/batch/status', projectController.batchUpdateStatus); + +module.exports = router; diff --git a/bank-backend/routes/supervisionTasks.js b/bank-backend/routes/supervisionTasks.js new file mode 100644 index 0000000..a00743a --- /dev/null +++ b/bank-backend/routes/supervisionTasks.js @@ -0,0 +1,41 @@ +const express = require('express'); +const router = express.Router(); +const supervisionTaskController = require('../controllers/supervisionTaskController'); +const { authMiddleware } = require('../middleware/auth'); + +// 应用认证中间件到所有路由 +router.use(authMiddleware); + +// 获取监管任务列表 +// GET /api/supervision-tasks +router.get('/', supervisionTaskController.getSupervisionTasks); + +// 获取监管任务统计 +// GET /api/supervision-tasks/stats +router.get('/stats', supervisionTaskController.getSupervisionTaskStats); + +// 获取监管任务详情 +// GET /api/supervision-tasks/:id +router.get('/:id', supervisionTaskController.getSupervisionTaskById); + +// 创建监管任务 +// POST /api/supervision-tasks +router.post('/', supervisionTaskController.createSupervisionTask); + +// 更新监管任务 +// PUT /api/supervision-tasks/:id +router.put('/:id', supervisionTaskController.updateSupervisionTask); + +// 删除监管任务 +// DELETE /api/supervision-tasks/:id +router.delete('/:id', supervisionTaskController.deleteSupervisionTask); + +// 批量更新监管任务状态 +// PUT /api/supervision-tasks/batch/status +router.put('/batch/status', supervisionTaskController.batchUpdateStatus); + +// 批量删除监管任务 +// DELETE /api/supervision-tasks/batch +router.delete('/batch', supervisionTaskController.batchDeleteTasks); + +module.exports = router; diff --git a/bank-backend/scripts/seed-projects.js b/bank-backend/scripts/seed-projects.js new file mode 100644 index 0000000..1f6af80 --- /dev/null +++ b/bank-backend/scripts/seed-projects.js @@ -0,0 +1,200 @@ +const { Project, User } = require('../models'); +const { sequelize } = require('../config/database'); + +// 项目测试数据 +const projectsData = [ + { + name: '张洪彬', + status: 'completed', + farmName: '大数据中心', + supervisionObject: '牛', + supervisionQuantity: 10, + supervisionPeriod: '23天', + supervisionAmount: 10000.00, + startTime: '2024-02-21', + endTime: '2024-03-15', + earTag: 0, + collar: 0, + host: 0, + loanOfficer: 'mapleaf', + description: '大数据中心养殖项目' + }, + { + name: '田小平', + status: 'completed', + farmName: '139****5685_养殖场', + supervisionObject: '牛', + supervisionQuantity: 0, + supervisionPeriod: '0天', + supervisionAmount: 0.00, + startTime: '2022-12-12', + endTime: '2023-12-12', + earTag: 0, + collar: 0, + host: 0, + loanOfficer: '', + description: '田小平养殖场项目' + }, + { + name: '杜宝民', + status: 'completed', + farmName: '杜宝民养殖场', + supervisionObject: '牛', + supervisionQuantity: 1, + supervisionPeriod: '20天', + supervisionAmount: 1000000.00, + startTime: '2023-05-08', + endTime: '2028-05-08', + earTag: 0, + collar: 0, + host: 0, + loanOfficer: 'mapleaf', + description: '杜宝民养殖场长期项目' + }, + { + name: '满良', + status: 'completed', + farmName: '满良养殖场', + supervisionObject: '牛', + supervisionQuantity: 30, + supervisionPeriod: '365天', + supervisionAmount: 420000.00, + startTime: '2023-01-01', + endTime: '2024-01-01', + earTag: 0, + collar: 0, + host: 0, + loanOfficer: 'mapleaf', + description: '满良养殖场年度项目' + }, + { + name: '敖日布仁琴', + status: 'supervision', + farmName: '敖日布仁琴养殖场', + supervisionObject: '牛', + supervisionQuantity: 38, + supervisionPeriod: '1827天', + supervisionAmount: 530000.00, + startTime: '2019-01-01', + endTime: '2024-01-01', + earTag: 0, + collar: 0, + host: 0, + loanOfficer: 'mapleaf', + description: '敖日布仁琴养殖场长期监管项目' + }, + { + name: '那顺乌日图', + status: 'supervision', + farmName: '那顺乌日图养殖场', + supervisionObject: '牛', + supervisionQuantity: 36, + supervisionPeriod: '1827天', + supervisionAmount: 500000.00, + startTime: '2019-01-01', + endTime: '2024-01-01', + earTag: 0, + collar: 0, + host: 0, + loanOfficer: 'mapleaf', + description: '那顺乌日图养殖场长期监管项目' + }, + { + name: '巴特尔', + status: 'supervision', + farmName: '巴特尔养殖场', + supervisionObject: '牛', + supervisionQuantity: 41, + supervisionPeriod: '1827天', + supervisionAmount: 570000.00, + startTime: '2019-01-01', + endTime: '2024-01-01', + earTag: 0, + collar: 0, + host: 0, + loanOfficer: 'mapleaf', + description: '巴特尔养殖场长期监管项目' + }, + { + name: '王五', + status: 'completed', + farmName: '王五养殖场', + supervisionObject: '牛', + supervisionQuantity: 50, + supervisionPeriod: '365天', + supervisionAmount: 700000.00, + startTime: '2023-01-01', + endTime: '2024-01-01', + earTag: 0, + collar: 0, + host: 0, + loanOfficer: 'mapleaf', + description: '王五养殖场年度项目' + } +]; + +async function seedProjects() { + try { + console.log('开始创建项目测试数据...'); + + // 测试数据库连接 + await sequelize.authenticate(); + console.log('✅ 数据库连接成功'); + + // 查找管理员用户作为创建人 + const adminUser = await User.findOne({ where: { username: 'admin' } }); + const createdBy = adminUser ? adminUser.id : null; + + // 清空现有项目数据 + await Project.destroy({ where: {} }); + console.log('✅ 清空现有项目数据'); + + // 创建项目数据 + for (const projectData of projectsData) { + await Project.create({ + ...projectData, + createdBy: createdBy, + updatedBy: createdBy + }); + } + + console.log(`✅ 成功创建 ${projectsData.length} 个项目`); + + // 验证数据 + const projectCount = await Project.count(); + console.log(`📊 数据库中现有项目数量: ${projectCount}`); + + // 显示统计信息 + const supervisionCount = await Project.count({ where: { status: 'supervision' } }); + const completedCount = await Project.count({ where: { status: 'completed' } }); + const totalAmount = await Project.sum('supervisionAmount'); + const totalQuantity = await Project.sum('supervisionQuantity'); + + console.log('\n📈 项目统计信息:'); + console.log(` 监管中项目: ${supervisionCount}`); + console.log(` 已结项项目: ${completedCount}`); + console.log(` 总监管金额: ${totalAmount?.toFixed(2) || 0} 元`); + console.log(` 总监管数量: ${totalQuantity || 0} 头`); + + console.log('\n🎉 项目数据创建完成!'); + + } catch (error) { + console.error('❌ 创建项目数据失败:', error); + throw error; + } +} + +// 如果直接运行此脚本 +if (require.main === module) { + seedProjects() + .then(() => { + console.log('✅ 脚本执行完成'); + process.exit(0); + }) + .catch((error) => { + console.error('❌ 脚本执行失败:', error); + process.exit(1); + }); +} + +module.exports = seedProjects; diff --git a/bank-backend/scripts/seed-supervision-tasks.js b/bank-backend/scripts/seed-supervision-tasks.js new file mode 100644 index 0000000..5a47fc9 --- /dev/null +++ b/bank-backend/scripts/seed-supervision-tasks.js @@ -0,0 +1,261 @@ +/** + * 监管任务测试数据种子文件 + * @file seed-supervision-tasks.js + * @description 为监管任务表添加测试数据 + */ +const { sequelize, SupervisionTask, User } = require('../models'); + +async function seedSupervisionTasks() { + try { + console.log('🌱 开始添加监管任务测试数据...'); + + // 检查是否已有数据 + const existingCount = await SupervisionTask.count(); + if (existingCount > 0) { + console.log(`⚠️ 监管任务表已有 ${existingCount} 条数据,跳过种子数据添加`); + return; + } + + // 获取管理员用户ID + const adminUser = await User.findOne({ where: { username: 'admin' } }); + if (!adminUser) { + console.log('❌ 未找到管理员用户,请先运行用户种子数据'); + return; + } + + const adminId = adminUser.id; + + // 监管任务测试数据 + const supervisionTasks = [ + { + applicationNumber: 'APP001', + contractNumber: 'CONTRACT001', + productName: '农业贷款产品A', + customerName: '张三', + idType: 'id_card', + idNumber: '110101199001011234', + assetType: 'cattle', + assetQuantity: 10, + supervisionStatus: 'supervising', + importTime: new Date('2024-01-15 10:30:00'), + startTime: '2024-01-15', + endTime: '2024-12-15', + loanAmount: 500000.00, + interestRate: 0.0650, + loanTerm: 12, + supervisorName: '李监管员', + supervisorPhone: '13800138001', + farmAddress: '北京市朝阳区某某养殖场', + remarks: '重点监管项目,需要定期检查', + createdBy: adminId, + updatedBy: adminId + }, + { + applicationNumber: 'APP002', + contractNumber: 'CONTRACT002', + productName: '农业贷款产品B', + customerName: '李四', + idType: 'id_card', + idNumber: '110101199002021234', + assetType: 'sheep', + assetQuantity: 20, + supervisionStatus: 'pending', + importTime: new Date('2024-01-16 14:20:00'), + startTime: '2024-01-16', + endTime: '2024-12-16', + loanAmount: 300000.00, + interestRate: 0.0600, + loanTerm: 12, + supervisorName: '王监管员', + supervisorPhone: '13800138002', + farmAddress: '北京市海淀区某某农场', + remarks: '新申请项目,待开始监管', + createdBy: adminId, + updatedBy: adminId + }, + { + applicationNumber: 'APP003', + contractNumber: 'CONTRACT003', + productName: '农业贷款产品C', + customerName: '王五', + idType: 'id_card', + idNumber: '110101199003031234', + assetType: 'pig', + assetQuantity: 15, + supervisionStatus: 'completed', + importTime: new Date('2024-01-10 09:15:00'), + startTime: '2024-01-10', + endTime: '2024-01-20', + loanAmount: 200000.00, + interestRate: 0.0550, + loanTerm: 6, + supervisorName: '赵监管员', + supervisorPhone: '13800138003', + farmAddress: '北京市丰台区某某猪场', + remarks: '监管任务已完成,客户还款正常', + createdBy: adminId, + updatedBy: adminId + }, + { + applicationNumber: 'APP004', + contractNumber: 'CONTRACT004', + productName: '农业贷款产品D', + customerName: '赵六', + idType: 'id_card', + idNumber: '110101199004041234', + assetType: 'poultry', + assetQuantity: 50, + supervisionStatus: 'supervising', + importTime: new Date('2024-01-20 11:45:00'), + startTime: '2024-01-20', + endTime: '2024-06-20', + loanAmount: 150000.00, + interestRate: 0.0700, + loanTerm: 6, + supervisorName: '孙监管员', + supervisorPhone: '13800138004', + farmAddress: '北京市通州区某某鸡场', + remarks: '家禽养殖项目,需要特别关注防疫情况', + createdBy: adminId, + updatedBy: adminId + }, + { + applicationNumber: 'APP005', + contractNumber: 'CONTRACT005', + productName: '农业贷款产品E', + customerName: '孙七', + idType: 'id_card', + idNumber: '110101199005051234', + assetType: 'cattle', + assetQuantity: 25, + supervisionStatus: 'suspended', + importTime: new Date('2024-01-25 16:30:00'), + startTime: '2024-01-25', + endTime: '2024-12-25', + loanAmount: 800000.00, + interestRate: 0.0625, + loanTerm: 18, + supervisorName: '周监管员', + supervisorPhone: '13800138005', + farmAddress: '北京市昌平区某某牧场', + remarks: '因客户原因暂停监管,等待进一步通知', + createdBy: adminId, + updatedBy: adminId + }, + { + applicationNumber: 'APP006', + contractNumber: 'CONTRACT006', + productName: '农业贷款产品F', + customerName: '周八', + idType: 'passport', + idNumber: 'P123456789', + assetType: 'other', + assetQuantity: 30, + supervisionStatus: 'supervising', + importTime: new Date('2024-02-01 08:20:00'), + startTime: '2024-02-01', + endTime: '2024-08-01', + loanAmount: 400000.00, + interestRate: 0.0680, + loanTerm: 6, + supervisorName: '吴监管员', + supervisorPhone: '13800138006', + farmAddress: '北京市顺义区某某特种养殖场', + remarks: '特种养殖项目,需要专业监管', + createdBy: adminId, + updatedBy: adminId + }, + { + applicationNumber: 'APP007', + contractNumber: 'CONTRACT007', + productName: '农业贷款产品G', + customerName: '吴九', + idType: 'id_card', + idNumber: '110101199007071234', + assetType: 'sheep', + assetQuantity: 40, + supervisionStatus: 'pending', + importTime: new Date('2024-02-05 13:10:00'), + startTime: '2024-02-05', + endTime: '2024-12-05', + loanAmount: 600000.00, + interestRate: 0.0590, + loanTerm: 12, + supervisorName: '郑监管员', + supervisorPhone: '13800138007', + farmAddress: '北京市房山区某某羊场', + remarks: '大规模羊群养殖,需要加强监管', + createdBy: adminId, + updatedBy: adminId + }, + { + applicationNumber: 'APP008', + contractNumber: 'CONTRACT008', + productName: '农业贷款产品H', + customerName: '郑十', + idType: 'id_card', + idNumber: '110101199008081234', + assetType: 'pig', + assetQuantity: 35, + supervisionStatus: 'completed', + importTime: new Date('2024-01-05 15:45:00'), + startTime: '2024-01-05', + endTime: '2024-03-05', + loanAmount: 350000.00, + interestRate: 0.0575, + loanTerm: 3, + supervisorName: '冯监管员', + supervisorPhone: '13800138008', + farmAddress: '北京市大兴区某某养猪场', + remarks: '短期养殖项目,已顺利完成监管', + createdBy: adminId, + updatedBy: adminId + } + ]; + + // 批量创建监管任务 + await SupervisionTask.bulkCreate(supervisionTasks); + + console.log(`✅ 成功添加 ${supervisionTasks.length} 条监管任务测试数据`); + + // 显示统计信息 + const stats = await SupervisionTask.findAll({ + attributes: [ + 'supervisionStatus', + [sequelize.fn('COUNT', sequelize.col('id')), 'count'] + ], + group: ['supervisionStatus'], + raw: true + }); + + console.log('📊 监管任务状态统计:'); + stats.forEach(stat => { + const statusNames = { + 'pending': '待监管', + 'supervising': '监管中', + 'completed': '已完成', + 'suspended': '已暂停' + }; + console.log(` ${statusNames[stat.supervisionStatus] || stat.supervisionStatus}: ${stat.count} 条`); + }); + + } catch (error) { + console.error('❌ 添加监管任务测试数据失败:', error); + throw error; + } +} + +// 如果直接运行此文件 +if (require.main === module) { + seedSupervisionTasks() + .then(() => { + console.log('🎉 监管任务种子数据添加完成'); + process.exit(0); + }) + .catch((error) => { + console.error('💥 监管任务种子数据添加失败:', error); + process.exit(1); + }); +} + +module.exports = seedSupervisionTasks; diff --git a/bank-backend/scripts/setup-projects-simple.js b/bank-backend/scripts/setup-projects-simple.js new file mode 100644 index 0000000..9c7e115 --- /dev/null +++ b/bank-backend/scripts/setup-projects-simple.js @@ -0,0 +1,60 @@ +const { sequelize } = require('../config/database'); +const { Project } = require('../models'); +const seedProjects = require('./seed-projects'); + +async function setupProjectsSimple() { + try { + console.log('🚀 开始设置项目清单功能...\n'); + + // 1. 测试数据库连接 + console.log('1️⃣ 测试数据库连接...'); + await sequelize.authenticate(); + console.log('✅ 数据库连接成功\n'); + + // 2. 同步项目模型(创建表) + console.log('2️⃣ 创建项目表...'); + await Project.sync({ force: false }); // force: false 表示如果表已存在则不删除 + console.log('✅ 项目表创建成功\n'); + + // 3. 创建项目测试数据 + console.log('3️⃣ 创建项目测试数据...'); + await seedProjects(); + console.log('✅ 项目测试数据创建完成\n'); + + // 4. 验证数据 + console.log('4️⃣ 验证项目数据...'); + const projectCount = await Project.count(); + const supervisionCount = await Project.count({ where: { status: 'supervision' } }); + const completedCount = await Project.count({ where: { status: 'completed' } }); + + console.log(` 总项目数: ${projectCount}`); + console.log(` 监管中项目: ${supervisionCount}`); + console.log(` 已结项项目: ${completedCount}`); + console.log('✅ 项目数据验证完成\n'); + + console.log('🎉 项目清单功能设置完成!'); + console.log('📝 接下来可以:'); + console.log(' 1. 启动后端服务器: npm start'); + console.log(' 2. 运行API测试: node test-projects-api.js'); + console.log(' 3. 在前端访问项目清单页面'); + + } catch (error) { + console.error('❌ 设置项目清单功能失败:', error); + throw error; + } +} + +// 如果直接运行此脚本 +if (require.main === module) { + setupProjectsSimple() + .then(() => { + console.log('✅ 脚本执行完成'); + process.exit(0); + }) + .catch((error) => { + console.error('❌ 脚本执行失败:', error); + process.exit(1); + }); +} + +module.exports = setupProjectsSimple; diff --git a/bank-backend/scripts/setup-projects.js b/bank-backend/scripts/setup-projects.js new file mode 100644 index 0000000..78fceaa --- /dev/null +++ b/bank-backend/scripts/setup-projects.js @@ -0,0 +1,79 @@ +const { sequelize } = require('../config/database'); +const seedProjects = require('./seed-projects'); + +async function setupProjects() { + try { + console.log('🚀 开始设置项目清单功能...\n'); + + // 1. 测试数据库连接 + console.log('1️⃣ 测试数据库连接...'); + await sequelize.authenticate(); + console.log('✅ 数据库连接成功\n'); + + // 2. 运行项目表迁移 + console.log('2️⃣ 运行项目表迁移...'); + try { + const { QueryInterface } = require('sequelize'); + const queryInterface = sequelize.getQueryInterface(); + + // 检查表是否已存在 + const tableExists = await queryInterface.showAllTables().then(tables => + tables.includes('projects') + ); + + if (!tableExists) { + // 运行迁移 + const migration = require('../migrations/20241220000002-create-projects'); + await migration.up(queryInterface, sequelize); + console.log('✅ 项目表创建成功\n'); + } else { + console.log('✅ 项目表已存在\n'); + } + } catch (error) { + console.error('❌ 项目表迁移失败:', error.message); + throw error; + } + + // 3. 创建项目测试数据 + console.log('3️⃣ 创建项目测试数据...'); + await seedProjects(); + console.log('✅ 项目测试数据创建完成\n'); + + // 4. 验证数据 + console.log('4️⃣ 验证项目数据...'); + const { Project } = require('../models'); + const projectCount = await Project.count(); + const supervisionCount = await Project.count({ where: { status: 'supervision' } }); + const completedCount = await Project.count({ where: { status: 'completed' } }); + + console.log(` 总项目数: ${projectCount}`); + console.log(` 监管中项目: ${supervisionCount}`); + console.log(` 已结项项目: ${completedCount}`); + console.log('✅ 项目数据验证完成\n'); + + console.log('🎉 项目清单功能设置完成!'); + console.log('📝 接下来可以:'); + console.log(' 1. 启动后端服务器: npm start'); + console.log(' 2. 运行API测试: node test-projects-api.js'); + console.log(' 3. 在前端访问项目清单页面'); + + } catch (error) { + console.error('❌ 设置项目清单功能失败:', error); + throw error; + } +} + +// 如果直接运行此脚本 +if (require.main === module) { + setupProjects() + .then(() => { + console.log('✅ 脚本执行完成'); + process.exit(0); + }) + .catch((error) => { + console.error('❌ 脚本执行失败:', error); + process.exit(1); + }); +} + +module.exports = setupProjects; diff --git a/bank-backend/scripts/setup-supervision-tasks.js b/bank-backend/scripts/setup-supervision-tasks.js new file mode 100644 index 0000000..e2aa9f7 --- /dev/null +++ b/bank-backend/scripts/setup-supervision-tasks.js @@ -0,0 +1,43 @@ +/** + * 监管任务设置脚本 + * @file setup-supervision-tasks.js + * @description 创建监管任务表并添加测试数据 + */ +const { sequelize, SupervisionTask } = require('../models'); +const seedSupervisionTasks = require('./seed-supervision-tasks'); + +async function setupSupervisionTasks() { + try { + console.log('🚀 开始设置监管任务...'); + + // 测试数据库连接 + await sequelize.authenticate(); + console.log('✅ 数据库连接成功'); + + // 同步监管任务模型(创建表) + await sequelize.sync({ force: false }); + console.log('✅ 数据库表同步完成'); + + // 添加测试数据 + await seedSupervisionTasks(); + + console.log('🎉 监管任务设置完成!'); + + } catch (error) { + console.error('❌ 监管任务设置失败:', error); + throw error; + } finally { + await sequelize.close(); + } +} + +// 运行设置 +setupSupervisionTasks() + .then(() => { + console.log('✅ 所有操作完成'); + process.exit(0); + }) + .catch((error) => { + console.error('💥 操作失败:', error); + process.exit(1); + }); diff --git a/bank-backend/server.js b/bank-backend/server.js index 8965c67..b704240 100644 --- a/bank-backend/server.js +++ b/bank-backend/server.js @@ -74,6 +74,8 @@ app.use('/api/transactions', require('./routes/transactions')); app.use('/api/dashboard', require('./routes/dashboard')); app.use('/api/loan-products', require('./routes/loanProducts')); app.use('/api/employees', require('./routes/employees')); +app.use('/api/projects', require('./routes/projects')); +app.use('/api/supervision-tasks', require('./routes/supervisionTasks')); // app.use('/api/reports', require('./routes/reports')); // 根路径 diff --git a/bank-backend/test-create-project.js b/bank-backend/test-create-project.js new file mode 100644 index 0000000..5d5b9bc --- /dev/null +++ b/bank-backend/test-create-project.js @@ -0,0 +1,96 @@ +/** + * 测试创建项目接口 + */ +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-project-direct.js b/bank-backend/test-project-direct.js new file mode 100644 index 0000000..c443537 --- /dev/null +++ b/bank-backend/test-project-direct.js @@ -0,0 +1,34 @@ +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 new file mode 100644 index 0000000..b03f639 --- /dev/null +++ b/bank-backend/test-projects-api.js @@ -0,0 +1,388 @@ +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 new file mode 100644 index 0000000..9a1c7c5 --- /dev/null +++ b/bank-backend/test-projects-simple.js @@ -0,0 +1,58 @@ +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-supervision-tasks-api.js b/bank-backend/test-supervision-tasks-api.js new file mode 100644 index 0000000..50e023c --- /dev/null +++ b/bank-backend/test-supervision-tasks-api.js @@ -0,0 +1,172 @@ +/** + * 监管任务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 new file mode 100644 index 0000000..e22669f --- /dev/null +++ b/bank-backend/test-supervision-tasks-simple.js @@ -0,0 +1,55 @@ +/** + * 简单监管任务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-frontend/src/App.vue b/bank-frontend/src/App.vue index a590ff0..4d17022 100644 --- a/bank-frontend/src/App.vue +++ b/bank-frontend/src/App.vue @@ -9,70 +9,67 @@ - - - -
@@ -156,57 +153,139 @@ onUnmounted(() => { \ No newline at end of file diff --git a/bank-frontend/src/views/DeviceWarning.vue b/bank-frontend/src/views/DeviceWarning.vue new file mode 100644 index 0000000..6e33406 --- /dev/null +++ b/bank-frontend/src/views/DeviceWarning.vue @@ -0,0 +1,421 @@ + + + + + diff --git a/bank-frontend/src/views/EmployeeManagement.vue b/bank-frontend/src/views/EmployeeManagement.vue index ef00611..66a7b38 100644 --- a/bank-frontend/src/views/EmployeeManagement.vue +++ b/bank-frontend/src/views/EmployeeManagement.vue @@ -1,168 +1,64 @@