完善保险端的前后端
This commit is contained in:
@@ -216,7 +216,7 @@ const fetchMenus = async () => {
|
||||
key: 'CompletedTask',
|
||||
icon: () => h(FileDoneOutlined),
|
||||
label: '监管任务已结项',
|
||||
path: '/dashboard' // 重定向到仪表板
|
||||
path: '/completed-tasks'
|
||||
},
|
||||
{
|
||||
key: 'InsuredCustomers',
|
||||
@@ -239,6 +239,11 @@ const fetchMenus = async () => {
|
||||
key: 'PolicyManagement',
|
||||
label: '生资保单列表',
|
||||
path: '/policies'
|
||||
},
|
||||
{
|
||||
key: 'LivestockPolicyManagement',
|
||||
label: '生资保单管理',
|
||||
path: '/livestock-policies'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -7,6 +7,27 @@ import 'ant-design-vue/dist/reset.css'
|
||||
// Ant Design Vue的中文语言包
|
||||
import antdZhCN from 'ant-design-vue/es/locale/zh_CN'
|
||||
|
||||
// 抑制ResizeObserver警告
|
||||
const resizeObserverErrorHandler = (e) => {
|
||||
if (e.message === 'ResizeObserver loop completed with undelivered notifications.' ||
|
||||
e.message.includes('ResizeObserver loop')) {
|
||||
e.preventDefault()
|
||||
e.stopImmediatePropagation()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 处理未捕获的错误
|
||||
window.addEventListener('error', resizeObserverErrorHandler)
|
||||
|
||||
// 处理未捕获的Promise错误
|
||||
window.addEventListener('unhandledrejection', (e) => {
|
||||
if (e.reason && e.reason.message && e.reason.message.includes('ResizeObserver loop')) {
|
||||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
// Ant Design Vue 4.x配置
|
||||
// 使用Ant Design Vue内置的dayjs实例,避免版本冲突
|
||||
const antdConfig = {
|
||||
|
||||
@@ -18,6 +18,7 @@ import AntdConfigDebug from '@/views/AntdConfigDebug.vue'
|
||||
import SimpleDayjsTest from '@/views/SimpleDayjsTest.vue'
|
||||
import RangePickerTest from '@/views/RangePickerTest.vue'
|
||||
import LoginTest from '@/views/LoginTest.vue'
|
||||
import LivestockPolicyManagement from '@/views/LivestockPolicyManagement.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -79,6 +80,12 @@ const routes = [
|
||||
component: SupervisionTaskManagement,
|
||||
meta: { title: '监管任务管理' }
|
||||
},
|
||||
{
|
||||
path: 'completed-tasks',
|
||||
name: 'CompletedTasks',
|
||||
component: () => import('@/views/CompletedTaskManagement.vue'),
|
||||
meta: { requiresAuth: true, title: '监管任务结项' }
|
||||
},
|
||||
{
|
||||
path: 'installation-tasks',
|
||||
alias: ['pending-installation', 'installation-task'], // 添加别名兼容不同形式
|
||||
@@ -86,6 +93,12 @@ const routes = [
|
||||
component: InstallationTaskManagement,
|
||||
meta: { title: '待安装任务管理' }
|
||||
},
|
||||
{
|
||||
path: 'livestock-policies',
|
||||
name: 'LivestockPolicyManagement',
|
||||
component: LivestockPolicyManagement,
|
||||
meta: { title: '生资保单管理' }
|
||||
},
|
||||
{
|
||||
path: 'date-picker-test',
|
||||
name: 'DatePickerTest',
|
||||
|
||||
@@ -66,8 +66,14 @@ export const insuranceTypeAPI = {
|
||||
export const applicationAPI = {
|
||||
getList: (params) => api.get('/insurance/applications', { params }),
|
||||
getDetail: (id) => api.get(`/insurance/applications/${id}`),
|
||||
create: (data) => api.post('/insurance/applications', data),
|
||||
update: (id, data) => api.put(`/insurance/applications/${id}`, data),
|
||||
review: (id, data) => api.patch(`/insurance/applications/${id}/review`, data),
|
||||
updateStatus: (id, data) => api.put(`/insurance/applications/${id}/status`, data),
|
||||
delete: (id) => api.delete(`/insurance/applications/${id}`)
|
||||
delete: (id) => api.delete(`/insurance/applications/${id}`),
|
||||
getStats: () => api.get('/insurance/applications-stats'),
|
||||
getCategories: () => api.get('/insurance/categories'),
|
||||
export: (params) => api.get('/insurance/applications/export', { params, responseType: 'blob' })
|
||||
}
|
||||
|
||||
export const policyAPI = {
|
||||
@@ -113,13 +119,48 @@ export const supervisionTaskApi = {
|
||||
export const installationTaskApi = {
|
||||
getList: (params) => api.get('/installation-tasks', { params }),
|
||||
create: (data) => api.post('/installation-tasks', data),
|
||||
update: (data) => api.put(`/installation-tasks/${data.id}`, data),
|
||||
update: (id, data) => api.put(`/installation-tasks/${id}`, data),
|
||||
delete: (id) => api.delete(`/installation-tasks/${id}`),
|
||||
getDetail: (id) => api.get(`/installation-tasks/${id}`),
|
||||
batchDelete: (ids) => api.post('/installation-tasks/batch-delete', { ids }),
|
||||
batchUpdateStatus: (data) => api.post('/installation-tasks/batch-update-status', data),
|
||||
export: (params) => api.get('/installation-tasks/export/excel', { params, responseType: 'blob' }),
|
||||
getStats: () => api.get('/installation-tasks/statistics/summary')
|
||||
getById: (id) => api.get(`/installation-tasks/${id}`),
|
||||
updateStatus: (id, data) => api.patch(`/installation-tasks/${id}/status`, data),
|
||||
getStats: () => api.get('/installation-tasks/stats'),
|
||||
assign: (id, data) => api.post(`/installation-tasks/${id}/assign`, data),
|
||||
complete: (id, data) => api.post(`/installation-tasks/${id}/complete`, data),
|
||||
getHistory: (id) => api.get(`/installation-tasks/${id}/history`)
|
||||
}
|
||||
|
||||
// 生资保险相关API
|
||||
export const livestockTypeApi = {
|
||||
getList: (params) => api.get('/livestock-types', { params }),
|
||||
create: (data) => api.post('/livestock-types', data),
|
||||
update: (id, data) => api.put(`/livestock-types/${id}`, data),
|
||||
delete: (id, data) => api.delete(`/livestock-types/${id}`, data),
|
||||
getById: (id) => api.get(`/livestock-types/${id}`),
|
||||
updateStatus: (id, data) => api.patch(`/livestock-types/${id}/status`, data),
|
||||
batchUpdateStatus: (data) => api.patch('/livestock-types/batch-status', data)
|
||||
}
|
||||
|
||||
export const livestockPolicyApi = {
|
||||
getList: (params) => api.get('/livestock-policies', { params }),
|
||||
create: (data) => api.post('/livestock-policies', data),
|
||||
update: (id, data) => api.put(`/livestock-policies/${id}`, data),
|
||||
delete: (id) => api.delete(`/livestock-policies/${id}`),
|
||||
getById: (id) => api.get(`/livestock-policies/${id}`),
|
||||
updateStatus: (id, data) => api.patch(`/livestock-policies/${id}/status`, data),
|
||||
getStats: () => api.get('/livestock-policies/stats'),
|
||||
getLivestockTypes: () => api.get('/livestock-types?status=active')
|
||||
}
|
||||
|
||||
export const livestockClaimApi = {
|
||||
getList: (params) => api.get('/livestock-claims', { params }),
|
||||
create: (data) => api.post('/livestock-claims', data),
|
||||
update: (id, data) => api.put(`/livestock-claims/${id}`, data),
|
||||
delete: (id) => api.delete(`/livestock-claims/${id}`),
|
||||
getById: (id) => api.get(`/livestock-claims/${id}`),
|
||||
approve: (id, data) => api.post(`/livestock-claims/${id}/approve`, data),
|
||||
reject: (id, data) => api.post(`/livestock-claims/${id}/reject`, data),
|
||||
updatePaymentStatus: (id, data) => api.patch(`/livestock-claims/${id}/payment`, data),
|
||||
getStats: () => api.get('/livestock-claims/stats')
|
||||
}
|
||||
|
||||
export default api
|
||||
File diff suppressed because it is too large
Load Diff
509
insurance_admin-system/src/views/CompletedTaskManagement.vue
Normal file
509
insurance_admin-system/src/views/CompletedTaskManagement.vue
Normal file
@@ -0,0 +1,509 @@
|
||||
<template>
|
||||
<div class="completed-task-management">
|
||||
<div class="page-header">
|
||||
<h1>监管任务结项管理</h1>
|
||||
<p>查看和管理已完成的监管任务</p>
|
||||
</div>
|
||||
|
||||
<!-- 搜索和筛选区域 -->
|
||||
<div class="search-section">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="6">
|
||||
<a-input
|
||||
v-model:value="searchForm.taskName"
|
||||
placeholder="请输入任务名称"
|
||||
allow-clear
|
||||
>
|
||||
<template #prefix>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-select
|
||||
v-model:value="searchForm.status"
|
||||
placeholder="请选择状态"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-select-option value="completed">已完成</a-select-option>
|
||||
<a-select-option value="archived">已归档</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-range-picker
|
||||
v-model:value="searchForm.dateRange"
|
||||
style="width: 100%"
|
||||
placeholder="['开始日期', '结束日期']"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleSearch">
|
||||
<SearchOutlined />
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button @click="handleReset">
|
||||
<ReloadOutlined />
|
||||
重置
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stats-section">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="6">
|
||||
<a-card>
|
||||
<a-statistic
|
||||
title="总结项任务"
|
||||
:value="stats.total"
|
||||
:value-style="{ color: '#3f8600' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<CheckCircleOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card>
|
||||
<a-statistic
|
||||
title="本月结项"
|
||||
:value="stats.thisMonth"
|
||||
:value-style="{ color: '#1890ff' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<CalendarOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card>
|
||||
<a-statistic
|
||||
title="已归档任务"
|
||||
:value="stats.archived"
|
||||
:value-style="{ color: '#722ed1' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<FolderOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-card>
|
||||
<a-statistic
|
||||
title="平均处理时长"
|
||||
:value="stats.avgDuration"
|
||||
suffix="天"
|
||||
:value-style="{ color: '#fa8c16' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<ClockCircleOutlined />
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<!-- 任务列表 -->
|
||||
<div class="table-section">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="taskList"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
row-key="id"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag :color="getStatusColor(record.status)">
|
||||
{{ getStatusText(record.status) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'priority'">
|
||||
<a-tag :color="getPriorityColor(record.priority)">
|
||||
{{ getPriorityText(record.priority) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'duration'">
|
||||
{{ record.duration }} 天
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="link" size="small" @click="handleView(record)">
|
||||
<EyeOutlined />
|
||||
查看
|
||||
</a-button>
|
||||
<a-button type="link" size="small" @click="handleDownload(record)">
|
||||
<DownloadOutlined />
|
||||
下载报告
|
||||
</a-button>
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="archive" @click="handleArchive(record)">
|
||||
<FolderOutlined />
|
||||
归档
|
||||
</a-menu-item>
|
||||
<a-menu-item key="export" @click="handleExport(record)">
|
||||
<ExportOutlined />
|
||||
导出
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button type="link" size="small">
|
||||
更多
|
||||
<DownOutlined />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
|
||||
<!-- 任务详情模态框 -->
|
||||
<a-modal
|
||||
v-model:open="detailModalVisible"
|
||||
title="任务详情"
|
||||
width="800px"
|
||||
:footer="null"
|
||||
>
|
||||
<div v-if="selectedTask" class="task-detail">
|
||||
<a-descriptions :column="2" bordered>
|
||||
<a-descriptions-item label="任务名称">
|
||||
{{ selectedTask.taskName }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="任务编号">
|
||||
{{ selectedTask.taskCode }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="优先级">
|
||||
<a-tag :color="getPriorityColor(selectedTask.priority)">
|
||||
{{ getPriorityText(selectedTask.priority) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="状态">
|
||||
<a-tag :color="getStatusColor(selectedTask.status)">
|
||||
{{ getStatusText(selectedTask.status) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间">
|
||||
{{ selectedTask.createdAt }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="完成时间">
|
||||
{{ selectedTask.completedAt }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="负责人">
|
||||
{{ selectedTask.assignee }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="处理时长">
|
||||
{{ selectedTask.duration }} 天
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="任务描述" :span="2">
|
||||
{{ selectedTask.description }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="完成备注" :span="2">
|
||||
{{ selectedTask.completionNotes }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import {
|
||||
SearchOutlined,
|
||||
ReloadOutlined,
|
||||
CheckCircleOutlined,
|
||||
CalendarOutlined,
|
||||
FolderOutlined,
|
||||
ClockCircleOutlined,
|
||||
EyeOutlined,
|
||||
DownloadOutlined,
|
||||
ExportOutlined,
|
||||
DownOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
const taskList = ref([])
|
||||
const detailModalVisible = ref(false)
|
||||
const selectedTask = ref(null)
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
taskName: '',
|
||||
status: undefined,
|
||||
dateRange: undefined
|
||||
})
|
||||
|
||||
// 统计数据
|
||||
const stats = reactive({
|
||||
total: 0,
|
||||
thisMonth: 0,
|
||||
archived: 0,
|
||||
avgDuration: 0
|
||||
})
|
||||
|
||||
// 分页配置
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条,共 ${total} 条`
|
||||
})
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: '任务编号',
|
||||
dataIndex: 'taskCode',
|
||||
key: 'taskCode',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '任务名称',
|
||||
dataIndex: 'taskName',
|
||||
key: 'taskName',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '优先级',
|
||||
dataIndex: 'priority',
|
||||
key: 'priority',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '负责人',
|
||||
dataIndex: 'assignee',
|
||||
key: 'assignee',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '完成时间',
|
||||
dataIndex: 'completedAt',
|
||||
key: 'completedAt',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '处理时长',
|
||||
dataIndex: 'duration',
|
||||
key: 'duration',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
fixed: 'right'
|
||||
}
|
||||
]
|
||||
|
||||
// 获取状态颜色
|
||||
const getStatusColor = (status) => {
|
||||
const colorMap = {
|
||||
completed: 'green',
|
||||
archived: 'purple'
|
||||
}
|
||||
return colorMap[status] || 'default'
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
const textMap = {
|
||||
completed: '已完成',
|
||||
archived: '已归档'
|
||||
}
|
||||
return textMap[status] || status
|
||||
}
|
||||
|
||||
// 获取优先级颜色
|
||||
const getPriorityColor = (priority) => {
|
||||
const colorMap = {
|
||||
high: 'red',
|
||||
medium: 'orange',
|
||||
low: 'blue'
|
||||
}
|
||||
return colorMap[priority] || 'default'
|
||||
}
|
||||
|
||||
// 获取优先级文本
|
||||
const getPriorityText = (priority) => {
|
||||
const textMap = {
|
||||
high: '高',
|
||||
medium: '中',
|
||||
low: '低'
|
||||
}
|
||||
return textMap[priority] || priority
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
pagination.current = 1
|
||||
fetchTaskList()
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
const handleReset = () => {
|
||||
Object.keys(searchForm).forEach(key => {
|
||||
searchForm[key] = key === 'status' ? undefined : ''
|
||||
})
|
||||
pagination.current = 1
|
||||
fetchTaskList()
|
||||
}
|
||||
|
||||
// 表格变化处理
|
||||
const handleTableChange = (pag) => {
|
||||
pagination.current = pag.current
|
||||
pagination.pageSize = pag.pageSize
|
||||
fetchTaskList()
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
const handleView = (record) => {
|
||||
selectedTask.value = record
|
||||
detailModalVisible.value = true
|
||||
}
|
||||
|
||||
// 下载报告
|
||||
const handleDownload = (record) => {
|
||||
message.info(`正在下载 ${record.taskName} 的报告...`)
|
||||
// 这里实现下载逻辑
|
||||
}
|
||||
|
||||
// 归档任务
|
||||
const handleArchive = (record) => {
|
||||
message.success(`任务 ${record.taskName} 已归档`)
|
||||
fetchTaskList()
|
||||
}
|
||||
|
||||
// 导出任务
|
||||
const handleExport = (record) => {
|
||||
message.info(`正在导出 ${record.taskName} 的数据...`)
|
||||
// 这里实现导出逻辑
|
||||
}
|
||||
|
||||
// 获取任务列表
|
||||
const fetchTaskList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 模拟API调用
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
|
||||
// 模拟数据
|
||||
const mockData = [
|
||||
{
|
||||
id: 1,
|
||||
taskCode: 'RT001',
|
||||
taskName: '农场设备安全检查',
|
||||
priority: 'high',
|
||||
status: 'completed',
|
||||
assignee: '张三',
|
||||
createdAt: '2024-01-15',
|
||||
completedAt: '2024-01-20',
|
||||
duration: 5,
|
||||
description: '对农场所有设备进行安全检查,确保设备正常运行',
|
||||
completionNotes: '检查完成,发现3处需要维修的设备,已安排维修'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
taskCode: 'RT002',
|
||||
taskName: '环境监测数据审核',
|
||||
priority: 'medium',
|
||||
status: 'archived',
|
||||
assignee: '李四',
|
||||
createdAt: '2024-01-10',
|
||||
completedAt: '2024-01-18',
|
||||
duration: 8,
|
||||
description: '审核上月环境监测数据,确保数据准确性',
|
||||
completionNotes: '数据审核完成,所有数据符合标准'
|
||||
}
|
||||
]
|
||||
|
||||
taskList.value = mockData
|
||||
pagination.total = mockData.length
|
||||
|
||||
// 更新统计数据
|
||||
stats.total = 156
|
||||
stats.thisMonth = 23
|
||||
stats.archived = 89
|
||||
stats.avgDuration = 6.5
|
||||
|
||||
} catch (error) {
|
||||
message.error('获取任务列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时获取数据
|
||||
onMounted(() => {
|
||||
fetchTaskList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.completed-task-management {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
margin: 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.search-section {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.stats-section .ant-card {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table-section {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.task-detail .ant-descriptions {
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -67,24 +67,19 @@
|
||||
size="middle"
|
||||
:scroll="{ x: 1800 }"
|
||||
>
|
||||
<!-- 安装状态列 -->
|
||||
<template #installationStatus="{ record }">
|
||||
<a-tag :color="getInstallationStatusColor(record.installationStatus)">
|
||||
{{ record.installationStatus }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 时间列格式化 -->
|
||||
<template #taskGeneratedTime="{ record }">
|
||||
<span>{{ formatDateTime(record.taskGeneratedTime) }}</span>
|
||||
</template>
|
||||
|
||||
<template #installationCompletedTime="{ record }">
|
||||
<span>{{ formatDateTime(record.installationCompletedTime) }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 操作列 -->
|
||||
<template #action="{ record }">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'installationStatus'">
|
||||
<a-tag :color="getInstallationStatusColor(record.installationStatus)">
|
||||
{{ record.installationStatus }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'taskGeneratedTime'">
|
||||
<span>{{ formatDateTime(record.taskGeneratedTime) }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'installationCompletedTime'">
|
||||
<span>{{ formatDateTime(record.installationCompletedTime) }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="link" size="small" @click="handleView(record)">
|
||||
查看
|
||||
@@ -101,6 +96,7 @@
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
@@ -205,29 +201,25 @@ const columns = [
|
||||
title: '安装状态',
|
||||
dataIndex: 'installationStatus',
|
||||
key: 'installationStatus',
|
||||
width: 100,
|
||||
slots: { customRender: 'installationStatus' }
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '生成安装任务时间',
|
||||
dataIndex: 'taskGeneratedTime',
|
||||
key: 'taskGeneratedTime',
|
||||
width: 160,
|
||||
slots: { customRender: 'taskGeneratedTime' }
|
||||
width: 160
|
||||
},
|
||||
{
|
||||
title: '安装完成生效时间',
|
||||
dataIndex: 'installationCompletedTime',
|
||||
key: 'installationCompletedTime',
|
||||
width: 160,
|
||||
slots: { customRender: 'installationCompletedTime' }
|
||||
width: 160
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
slots: { customRender: 'action' }
|
||||
fixed: 'right'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div class="insurance-type-management">
|
||||
<a-page-header
|
||||
title="保险类型管理"
|
||||
sub-title="管理系统支持的保险产品类型"
|
||||
title="险种管理"
|
||||
sub-title="管理系统支持的保险产品险种"
|
||||
>
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="showModal">
|
||||
<plus-outlined />
|
||||
新增类型
|
||||
新增险种
|
||||
</a-button>
|
||||
</template>
|
||||
</a-page-header>
|
||||
@@ -15,22 +15,36 @@
|
||||
<!-- 搜索区域 -->
|
||||
<a-card style="margin-top: 16px">
|
||||
<a-form layout="inline" :model="searchForm">
|
||||
<a-form-item label="类型名称">
|
||||
<a-form-item label="险种名称">
|
||||
<a-input
|
||||
v-model:value="searchForm.name"
|
||||
placeholder="请输入类型名称"
|
||||
placeholder="请输入险种名称"
|
||||
@pressEnter="handleSearch"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="状态">
|
||||
<a-form-item label="适用范围">
|
||||
<a-input
|
||||
v-model:value="searchForm.applicable_scope"
|
||||
placeholder="请输入适用范围"
|
||||
@pressEnter="handleSearch"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="服务区域">
|
||||
<a-input
|
||||
v-model:value="searchForm.service_area"
|
||||
placeholder="请输入服务区域"
|
||||
@pressEnter="handleSearch"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="在线状态">
|
||||
<a-select
|
||||
v-model:value="searchForm.status"
|
||||
placeholder="请选择状态"
|
||||
v-model:value="searchForm.online_status"
|
||||
placeholder="请选择在线状态"
|
||||
style="width: 120px"
|
||||
>
|
||||
<a-select-option value="">全部</a-select-option>
|
||||
<a-select-option value="active">启用</a-select-option>
|
||||
<a-select-option value="inactive">禁用</a-select-option>
|
||||
<a-select-option :value="true">在线</a-select-option>
|
||||
<a-select-option :value="false">离线</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
@@ -46,7 +60,7 @@
|
||||
</a-form>
|
||||
</a-card>
|
||||
|
||||
<!-- 类型表格 -->
|
||||
<!-- 险种表格 -->
|
||||
<a-card style="margin-top: 16px">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
@@ -54,28 +68,35 @@
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
:scroll="{ x: 1500 }"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag :color="record.status === 'active' ? 'green' : 'red'">
|
||||
{{ record.status === 'active' ? '启用' : '禁用' }}
|
||||
</a-tag>
|
||||
<template v-if="column.key === 'online_status'">
|
||||
<a-switch
|
||||
:checked="record.online_status"
|
||||
@change="(checked) => handleToggleOnlineStatus(record, checked)"
|
||||
checked-children="在线"
|
||||
un-checked-children="离线"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'coverage_type'">
|
||||
<span>{{ getCoverageTypeText(record.coverage_type) }}</span>
|
||||
<template v-else-if="column.key === 'premium_price'">
|
||||
<span>{{ record.premium_price ? `¥${record.premium_price}` : '-' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'experience_period'">
|
||||
<span>{{ record.experience_period ? `${record.experience_period}个月` : '-' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'insurance_period'">
|
||||
<span>{{ record.insurance_period ? `${record.insurance_period}个月` : '-' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'product_time'">
|
||||
<span>{{ record.product_time ? formatDate(record.product_time) : '-' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button
|
||||
size="small"
|
||||
:type="record.status === 'active' ? 'danger' : 'primary'"
|
||||
@click="handleToggleStatus(record)"
|
||||
>
|
||||
{{ record.status === 'active' ? '禁用' : '启用' }}
|
||||
</a-button>
|
||||
<a-button size="small" @click="handleView(record)">详情</a-button>
|
||||
<a-popconfirm
|
||||
title="确定要删除这个保险类型吗?"
|
||||
title="确定要删除这个险种吗?"
|
||||
@confirm="handleDelete(record.id)"
|
||||
>
|
||||
<a-button size="small" danger>删除</a-button>
|
||||
@@ -88,11 +109,13 @@
|
||||
|
||||
<!-- 新增/编辑模态框 -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
width="600px"
|
||||
@ok="handleModalOk"
|
||||
@cancel="handleModalCancel"
|
||||
v-model:open="modalVisible"
|
||||
:title="isEdit ? '编辑险种' : '新增险种'"
|
||||
:ok-text="isEdit ? '更新' : '创建'"
|
||||
cancel-text="取消"
|
||||
@ok="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
width="1000px"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
@@ -102,76 +125,107 @@
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="类型名称" name="name">
|
||||
<a-input v-model:value="formState.name" placeholder="请输入类型名称" />
|
||||
<a-form-item label="险种名称" name="name">
|
||||
<a-input v-model:value="formState.name" placeholder="请输入险种名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="类型代码" name="code">
|
||||
<a-input v-model:value="formState.code" placeholder="请输入类型代码" />
|
||||
<a-form-item label="适用范围" name="applicable_scope">
|
||||
<a-input v-model:value="formState.applicable_scope" placeholder="请输入适用范围" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form-item label="描述" name="description">
|
||||
|
||||
<a-form-item label="险种描述" name="description">
|
||||
<a-textarea
|
||||
v-model:value="formState.description"
|
||||
placeholder="请输入类型描述"
|
||||
placeholder="请输入险种描述"
|
||||
:rows="3"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="保障类型" name="coverage_type">
|
||||
<a-select v-model:value="formState.coverage_type" placeholder="请选择保障类型">
|
||||
<a-select-option value="life">人寿保险</a-select-option>
|
||||
<a-select-option value="health">健康保险</a-select-option>
|
||||
<a-select-option value="property">财产保险</a-select-option>
|
||||
<a-select-option value="accident">意外保险</a-select-option>
|
||||
<a-select-option value="travel">旅行保险</a-select-option>
|
||||
</a-select>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="体验期" name="experience_period">
|
||||
<a-input-number
|
||||
v-model:value="formState.experience_period"
|
||||
placeholder="请输入体验期(月)"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
addon-after="个月"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="状态" name="status">
|
||||
<a-select v-model:value="formState.status" placeholder="请选择状态">
|
||||
<a-select-option value="active">启用</a-select-option>
|
||||
<a-select-option value="inactive">禁用</a-select-option>
|
||||
</a-select>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保险期间" name="insurance_period">
|
||||
<a-input-number
|
||||
v-model:value="formState.insurance_period"
|
||||
placeholder="请输入保险期间(月)"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
addon-after="个月"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保费价格" name="premium_price">
|
||||
<a-input-number
|
||||
v-model:value="formState.premium_price"
|
||||
placeholder="请输入保费价格"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
addon-before="¥"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="最小保额" name="min_coverage">
|
||||
<a-input-number
|
||||
v-model:value="formState.min_coverage"
|
||||
:min="0"
|
||||
:step="1000"
|
||||
style="width: 100%"
|
||||
placeholder="最小保额"
|
||||
/>
|
||||
<a-form-item label="服务区域" name="service_area">
|
||||
<a-input v-model:value="formState.service_area" placeholder="请输入服务区域" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="最大保额" name="max_coverage">
|
||||
<a-input-number
|
||||
v-model:value="formState.max_coverage"
|
||||
:min="0"
|
||||
:step="1000"
|
||||
<a-form-item label="上架时间" name="product_time">
|
||||
<a-date-picker
|
||||
v-model:value="formState.product_time"
|
||||
placeholder="请选择上架时间"
|
||||
style="width: 100%"
|
||||
placeholder="最大保额"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
show-time
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form-item label="保费计算规则" name="premium_rules">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="在线状态" name="online_status">
|
||||
<a-switch
|
||||
v-model:checked="formState.online_status"
|
||||
checked-children="在线"
|
||||
un-checked-children="离线"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="排序" name="sort_order">
|
||||
<a-input-number
|
||||
v-model:value="formState.sort_order"
|
||||
placeholder="请输入排序值"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form-item label="备注" name="remarks">
|
||||
<a-textarea
|
||||
v-model:value="formState.premium_rules"
|
||||
placeholder="请输入保费计算规则(JSON格式)"
|
||||
v-model:value="formState.remarks"
|
||||
placeholder="请输入备注信息"
|
||||
:rows="3"
|
||||
/>
|
||||
</a-form-item>
|
||||
@@ -183,6 +237,8 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { insuranceTypeAPI } from '@/utils/api'
|
||||
import {
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
@@ -191,24 +247,30 @@ import {
|
||||
|
||||
const loading = ref(false)
|
||||
const modalVisible = ref(false)
|
||||
const editingId = ref(null)
|
||||
const isEdit = ref(false)
|
||||
const typeList = ref([])
|
||||
const formRef = ref()
|
||||
|
||||
const searchForm = reactive({
|
||||
name: '',
|
||||
status: ''
|
||||
applicable_scope: '',
|
||||
service_area: '',
|
||||
online_status: ''
|
||||
})
|
||||
|
||||
const formState = reactive({
|
||||
id: null,
|
||||
name: '',
|
||||
code: '',
|
||||
description: '',
|
||||
coverage_type: '',
|
||||
status: 'active',
|
||||
min_coverage: null,
|
||||
max_coverage: null,
|
||||
premium_rules: ''
|
||||
applicable_scope: '',
|
||||
experience_period: null,
|
||||
insurance_period: null,
|
||||
premium_price: null,
|
||||
service_area: '',
|
||||
product_time: null,
|
||||
online_status: true,
|
||||
sort_order: 0,
|
||||
remarks: ''
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
@@ -222,49 +284,54 @@ const pagination = reactive({
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '类型名称',
|
||||
title: '险种名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name'
|
||||
key: 'name',
|
||||
width: 150,
|
||||
fixed: 'left'
|
||||
},
|
||||
{
|
||||
title: '类型代码',
|
||||
dataIndex: 'code',
|
||||
key: 'code'
|
||||
title: '适用范围',
|
||||
dataIndex: 'applicable_scope',
|
||||
key: 'applicable_scope',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '保障类型',
|
||||
key: 'coverage_type',
|
||||
dataIndex: 'coverage_type'
|
||||
title: '体验期',
|
||||
dataIndex: 'experience_period',
|
||||
key: 'experience_period',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '最小保额',
|
||||
dataIndex: 'min_coverage',
|
||||
key: 'min_coverage',
|
||||
render: (text) => text ? `¥${text.toLocaleString()}` : '-'
|
||||
title: '保险期间',
|
||||
dataIndex: 'insurance_period',
|
||||
key: 'insurance_period',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '最大保额',
|
||||
dataIndex: 'max_coverage',
|
||||
key: 'max_coverage',
|
||||
render: (text) => text ? `¥${text.toLocaleString()}` : '-'
|
||||
title: '保费价格',
|
||||
dataIndex: 'premium_price',
|
||||
key: 'premium_price',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
dataIndex: 'status'
|
||||
title: '服务区域',
|
||||
dataIndex: 'service_area',
|
||||
key: 'service_area',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'created_at',
|
||||
key: 'created_at',
|
||||
title: '上架时间',
|
||||
dataIndex: 'product_time',
|
||||
key: 'product_time',
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
title: '在线状态',
|
||||
dataIndex: 'online_status',
|
||||
key: 'online_status',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
@@ -274,25 +341,18 @@ const columns = [
|
||||
]
|
||||
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入类型名称' }],
|
||||
code: [{ required: true, message: '请输入类型代码' }],
|
||||
coverage_type: [{ required: true, message: '请选择保障类型' }],
|
||||
status: [{ required: true, message: '请选择状态' }]
|
||||
name: [{ required: true, message: '请输入险种名称' }],
|
||||
applicable_scope: [{ required: true, message: '请输入适用范围' }],
|
||||
experience_period: [{ required: true, message: '请输入体验期' }],
|
||||
insurance_period: [{ required: true, message: '请输入保险期间' }],
|
||||
premium_price: [{ required: true, message: '请输入保费价格' }],
|
||||
service_area: [{ required: true, message: '请输入服务区域' }]
|
||||
}
|
||||
|
||||
const modalTitle = computed(() => {
|
||||
return editingId.value ? '编辑保险类型' : '新增保险类型'
|
||||
})
|
||||
|
||||
const getCoverageTypeText = (type) => {
|
||||
const types = {
|
||||
life: '人寿保险',
|
||||
health: '健康保险',
|
||||
property: '财产保险',
|
||||
accident: '意外保险',
|
||||
travel: '旅行保险'
|
||||
}
|
||||
return types[type] || type
|
||||
// 格式化日期
|
||||
const formatDate = (date) => {
|
||||
if (!date) return '-'
|
||||
return dayjs(date).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
|
||||
const loadInsuranceTypes = async () => {
|
||||
@@ -304,41 +364,17 @@ const loadInsuranceTypes = async () => {
|
||||
...searchForm
|
||||
}
|
||||
|
||||
// 这里应该是实际的API调用
|
||||
// const response = await insuranceTypeAPI.getList(params)
|
||||
// typeList.value = response.data.list
|
||||
// pagination.total = response.data.total
|
||||
const response = await insuranceTypeAPI.getList(params)
|
||||
|
||||
// 模拟数据
|
||||
typeList.value = [
|
||||
{
|
||||
id: 1,
|
||||
name: '综合意外险',
|
||||
code: 'ACCIDENT_001',
|
||||
description: '提供全面的意外伤害保障',
|
||||
coverage_type: 'accident',
|
||||
status: 'active',
|
||||
min_coverage: 100000,
|
||||
max_coverage: 500000,
|
||||
premium_rules: '{\"base_rate\": 0.001, \"age_factor\": 1.2}',
|
||||
created_at: '2024-01-01 10:00:00'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '终身寿险',
|
||||
code: 'LIFE_001',
|
||||
description: '提供终身的人寿保障',
|
||||
coverage_type: 'life',
|
||||
status: 'active',
|
||||
min_coverage: 500000,
|
||||
max_coverage: 2000000,
|
||||
premium_rules: '{\"base_rate\": 0.0005, \"age_factor\": 1.5}',
|
||||
created_at: '2024-01-02 14:30:00'
|
||||
}
|
||||
]
|
||||
pagination.total = 2
|
||||
if (response.status === 'success') {
|
||||
typeList.value = response.data.list
|
||||
pagination.total = response.data.total
|
||||
} else {
|
||||
message.error(response.message || '加载险种列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('加载保险类型列表失败')
|
||||
message.error('加载险种列表失败')
|
||||
console.error('Error loading insurance types:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -350,8 +386,12 @@ const handleSearch = () => {
|
||||
}
|
||||
|
||||
const resetSearch = () => {
|
||||
searchForm.name = ''
|
||||
searchForm.status = ''
|
||||
Object.assign(searchForm, {
|
||||
name: '',
|
||||
applicable_scope: '',
|
||||
service_area: '',
|
||||
online_status: ''
|
||||
})
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
@@ -362,76 +402,119 @@ const handleTableChange = (pag) => {
|
||||
}
|
||||
|
||||
const showModal = () => {
|
||||
editingId.value = null
|
||||
isEdit.value = false
|
||||
Object.assign(formState, {
|
||||
id: null,
|
||||
name: '',
|
||||
code: '',
|
||||
description: '',
|
||||
coverage_type: '',
|
||||
status: 'active',
|
||||
min_coverage: null,
|
||||
max_coverage: null,
|
||||
premium_rules: ''
|
||||
applicable_scope: '',
|
||||
experience_period: null,
|
||||
insurance_period: null,
|
||||
premium_price: null,
|
||||
service_area: '',
|
||||
product_time: null,
|
||||
online_status: true,
|
||||
sort_order: 0,
|
||||
remarks: ''
|
||||
})
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const handleEdit = (record) => {
|
||||
editingId.value = record.id
|
||||
isEdit.value = true
|
||||
Object.assign(formState, {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
code: record.code,
|
||||
description: record.description,
|
||||
coverage_type: record.coverage_type,
|
||||
status: record.status,
|
||||
min_coverage: record.min_coverage,
|
||||
max_coverage: record.max_coverage,
|
||||
premium_rules: record.premium_rules
|
||||
applicable_scope: record.applicable_scope,
|
||||
experience_period: record.experience_period,
|
||||
insurance_period: record.insurance_period,
|
||||
premium_price: record.premium_price,
|
||||
service_area: record.service_area,
|
||||
product_time: record.product_time ? dayjs(record.product_time) : null,
|
||||
online_status: record.online_status,
|
||||
sort_order: record.sort_order,
|
||||
remarks: record.remarks
|
||||
})
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const handleModalOk = async () => {
|
||||
const handleView = (record) => {
|
||||
// 查看详情功能
|
||||
message.info('查看详情功能待实现')
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
|
||||
if (editingId.value) {
|
||||
// await insuranceTypeAPI.update(editingId.value, formState)
|
||||
message.success('保险类型更新成功')
|
||||
const submitData = {
|
||||
...formState,
|
||||
product_time: formState.product_time ? formState.product_time.format('YYYY-MM-DD HH:mm:ss') : null
|
||||
}
|
||||
|
||||
if (isEdit.value) {
|
||||
const response = await insuranceTypeAPI.update(formState.id, submitData)
|
||||
if (response.status === 'success') {
|
||||
message.success('险种更新成功')
|
||||
} else {
|
||||
message.error(response.message || '险种更新失败')
|
||||
}
|
||||
} else {
|
||||
// await insuranceTypeAPI.create(formState)
|
||||
message.success('保险类型创建成功')
|
||||
const response = await insuranceTypeAPI.create(submitData)
|
||||
if (response.status === 'success') {
|
||||
message.success('险种创建成功')
|
||||
} else {
|
||||
message.error(response.message || '险种创建失败')
|
||||
}
|
||||
}
|
||||
|
||||
modalVisible.value = false
|
||||
loadInsuranceTypes()
|
||||
} catch (error) {
|
||||
console.log('表单验证失败', error)
|
||||
if (error.response?.data?.message) {
|
||||
message.error(error.response.data.message)
|
||||
} else {
|
||||
console.log('表单验证失败', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleModalCancel = () => {
|
||||
const handleCancel = () => {
|
||||
modalVisible.value = false
|
||||
}
|
||||
|
||||
const handleToggleStatus = async (record) => {
|
||||
const handleToggleOnlineStatus = async (record, checked) => {
|
||||
try {
|
||||
const newStatus = record.status === 'active' ? 'inactive' : 'active'
|
||||
// await insuranceTypeAPI.update(record.id, { status: newStatus })
|
||||
message.success('状态更新成功')
|
||||
loadInsuranceTypes()
|
||||
const response = await insuranceTypeAPI.updateStatus(record.id, {
|
||||
online_status: checked
|
||||
})
|
||||
|
||||
if (response.status === 'success') {
|
||||
message.success('在线状态更新成功')
|
||||
loadInsuranceTypes()
|
||||
} else {
|
||||
message.error(response.message || '在线状态更新失败')
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('状态更新失败')
|
||||
message.error('在线状态更新失败')
|
||||
console.error('Error updating online status:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
try {
|
||||
// await insuranceTypeAPI.delete(id)
|
||||
message.success('保险类型删除成功')
|
||||
loadInsuranceTypes()
|
||||
const response = await insuranceTypeAPI.delete(id)
|
||||
|
||||
if (response.status === 'success') {
|
||||
message.success('险种删除成功')
|
||||
loadInsuranceTypes()
|
||||
} else {
|
||||
message.error(response.message || '险种删除失败')
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('保险类型删除失败')
|
||||
message.error('险种删除失败')
|
||||
console.error('Error deleting insurance type:', error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
747
insurance_admin-system/src/views/LivestockPolicyManagement.vue
Normal file
747
insurance_admin-system/src/views/LivestockPolicyManagement.vue
Normal file
@@ -0,0 +1,747 @@
|
||||
<template>
|
||||
<div class="livestock-policy-management">
|
||||
<div class="page-header">
|
||||
<h2>生资保单管理</h2>
|
||||
<a-button type="primary" @click="showCreateModal">
|
||||
<PlusOutlined />
|
||||
新建保单
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<!-- 搜索筛选区域 -->
|
||||
<div class="search-section">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="6">
|
||||
<a-input
|
||||
v-model:value="searchForm.policy_no"
|
||||
placeholder="保单编号"
|
||||
allow-clear
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-input
|
||||
v-model:value="searchForm.farmer_name"
|
||||
placeholder="农户姓名"
|
||||
allow-clear
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-select
|
||||
v-model:value="searchForm.policy_status"
|
||||
placeholder="保单状态"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-select-option value="draft">草稿</a-select-option>
|
||||
<a-select-option value="active">生效</a-select-option>
|
||||
<a-select-option value="expired">过期</a-select-option>
|
||||
<a-select-option value="cancelled">已取消</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-select
|
||||
v-model:value="searchForm.payment_status"
|
||||
placeholder="支付状态"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-select-option value="unpaid">未支付</a-select-option>
|
||||
<a-select-option value="paid">已支付</a-select-option>
|
||||
<a-select-option value="partial">部分支付</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16" style="margin-top: 16px">
|
||||
<a-col :span="8">
|
||||
<a-range-picker
|
||||
v-model:value="searchForm.dateRange"
|
||||
placeholder="['开始日期', '结束日期']"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-button type="primary" @click="handleSearch">
|
||||
<SearchOutlined />
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button style="margin-left: 8px" @click="handleReset">
|
||||
重置
|
||||
</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<div class="table-section">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="tableData"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
row-key="id"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'policy_status'">
|
||||
<a-tag :color="getStatusColor(record.policy_status)">
|
||||
{{ getStatusText(record.policy_status) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'payment_status'">
|
||||
<a-tag :color="getPaymentStatusColor(record.payment_status)">
|
||||
{{ getPaymentStatusText(record.payment_status) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'total_value'">
|
||||
¥{{ record.total_value?.toLocaleString() }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'premium_amount'">
|
||||
¥{{ record.premium_amount?.toLocaleString() }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="link" size="small" @click="viewDetail(record)">
|
||||
查看
|
||||
</a-button>
|
||||
<a-button type="link" size="small" @click="editRecord(record)">
|
||||
编辑
|
||||
</a-button>
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu @click="handleMenuClick($event, record)">
|
||||
<a-menu-item key="activate" v-if="record.policy_status === 'draft'">
|
||||
激活保单
|
||||
</a-menu-item>
|
||||
<a-menu-item key="cancel" v-if="['draft', 'active'].includes(record.policy_status)">
|
||||
取消保单
|
||||
</a-menu-item>
|
||||
<a-menu-item key="payment" v-if="record.payment_status === 'unpaid'">
|
||||
标记已支付
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button type="link" size="small">
|
||||
更多 <DownOutlined />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
|
||||
<!-- 创建/编辑保单弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
:title="modalTitle"
|
||||
width="800px"
|
||||
@ok="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="农户姓名" name="farmer_name">
|
||||
<a-input v-model:value="formData.farmer_name" placeholder="请输入农户姓名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="农户电话" name="farmer_phone">
|
||||
<a-input v-model:value="formData.farmer_phone" placeholder="请输入农户电话" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="身份证号" name="farmer_id_card">
|
||||
<a-input v-model:value="formData.farmer_id_card" placeholder="请输入身份证号" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="牲畜类型" name="livestock_type_id">
|
||||
<a-select
|
||||
v-model:value="formData.livestock_type_id"
|
||||
placeholder="请选择牲畜类型"
|
||||
@change="handleLivestockTypeChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="type in livestockTypes"
|
||||
:key="type.id"
|
||||
:value="type.id"
|
||||
>
|
||||
{{ type.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item label="农户地址" name="farmer_address">
|
||||
<a-textarea v-model:value="formData.farmer_address" placeholder="请输入农户地址" :rows="2" />
|
||||
</a-form-item>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="牲畜数量" name="livestock_count">
|
||||
<a-input-number
|
||||
v-model:value="formData.livestock_count"
|
||||
placeholder="请输入数量"
|
||||
:min="1"
|
||||
style="width: 100%"
|
||||
@change="calculateAmounts"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="单头价值" name="unit_value">
|
||||
<a-input-number
|
||||
v-model:value="formData.unit_value"
|
||||
placeholder="请输入单头价值"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
style="width: 100%"
|
||||
@change="calculateAmounts"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保费费率" name="premium_rate">
|
||||
<a-input-number
|
||||
v-model:value="formData.premium_rate"
|
||||
placeholder="请输入费率"
|
||||
:min="0"
|
||||
:max="1"
|
||||
:precision="4"
|
||||
style="width: 100%"
|
||||
@change="calculateAmounts"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item label="总保额">
|
||||
<a-input
|
||||
:value="`¥${formData.total_value?.toLocaleString() || '0'}`"
|
||||
disabled
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保费金额">
|
||||
<a-input
|
||||
:value="`¥${formData.premium_amount?.toLocaleString() || '0'}`"
|
||||
disabled
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="保险开始日期" name="start_date">
|
||||
<a-date-picker
|
||||
v-model:value="formData.start_date"
|
||||
placeholder="请选择开始日期"
|
||||
style="width: 100%"
|
||||
@change="handleStartDateChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="保险结束日期" name="end_date">
|
||||
<a-date-picker
|
||||
v-model:value="formData.end_date"
|
||||
placeholder="请选择结束日期"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item label="备注" name="notes">
|
||||
<a-textarea v-model:value="formData.notes" placeholder="请输入备注" :rows="3" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- 详情查看弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="detailVisible"
|
||||
title="保单详情"
|
||||
width="800px"
|
||||
:footer="null"
|
||||
>
|
||||
<a-descriptions :column="2" bordered v-if="currentRecord">
|
||||
<a-descriptions-item label="保单编号">{{ currentRecord.policy_no }}</a-descriptions-item>
|
||||
<a-descriptions-item label="保单状态">
|
||||
<a-tag :color="getStatusColor(currentRecord.policy_status)">
|
||||
{{ getStatusText(currentRecord.policy_status) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="农户姓名">{{ currentRecord.farmer_name }}</a-descriptions-item>
|
||||
<a-descriptions-item label="农户电话">{{ currentRecord.farmer_phone }}</a-descriptions-item>
|
||||
<a-descriptions-item label="身份证号">{{ currentRecord.farmer_id_card }}</a-descriptions-item>
|
||||
<a-descriptions-item label="牲畜类型">{{ currentRecord.livestock_type?.name }}</a-descriptions-item>
|
||||
<a-descriptions-item label="农户地址" :span="2">{{ currentRecord.farmer_address }}</a-descriptions-item>
|
||||
<a-descriptions-item label="牲畜数量">{{ currentRecord.livestock_count }}头</a-descriptions-item>
|
||||
<a-descriptions-item label="单头价值">¥{{ currentRecord.unit_value?.toLocaleString() }}</a-descriptions-item>
|
||||
<a-descriptions-item label="总保额">¥{{ currentRecord.total_value?.toLocaleString() }}</a-descriptions-item>
|
||||
<a-descriptions-item label="保费费率">{{ (currentRecord.premium_rate * 100).toFixed(2) }}%</a-descriptions-item>
|
||||
<a-descriptions-item label="保费金额">¥{{ currentRecord.premium_amount?.toLocaleString() }}</a-descriptions-item>
|
||||
<a-descriptions-item label="支付状态">
|
||||
<a-tag :color="getPaymentStatusColor(currentRecord.payment_status)">
|
||||
{{ getPaymentStatusText(currentRecord.payment_status) }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="保险开始日期">{{ currentRecord.start_date }}</a-descriptions-item>
|
||||
<a-descriptions-item label="保险结束日期">{{ currentRecord.end_date }}</a-descriptions-item>
|
||||
<a-descriptions-item label="支付时间">{{ currentRecord.payment_date || '未支付' }}</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间">{{ formatDateTime(currentRecord.created_at) }}</a-descriptions-item>
|
||||
<a-descriptions-item label="备注" :span="2">{{ currentRecord.notes || '无' }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import dayjs from 'dayjs'
|
||||
import {
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
DownOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { livestockPolicyApi } from '@/utils/api'
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
const tableData = ref([])
|
||||
const modalVisible = ref(false)
|
||||
const detailVisible = ref(false)
|
||||
const currentRecord = ref(null)
|
||||
const formRef = ref()
|
||||
const livestockTypes = ref([])
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
policy_no: '',
|
||||
farmer_name: '',
|
||||
policy_status: undefined,
|
||||
payment_status: undefined,
|
||||
dateRange: []
|
||||
})
|
||||
|
||||
// 分页配置
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条记录`
|
||||
})
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
farmer_name: '',
|
||||
farmer_phone: '',
|
||||
farmer_id_card: '',
|
||||
farmer_address: '',
|
||||
livestock_type_id: undefined,
|
||||
livestock_count: undefined,
|
||||
unit_value: undefined,
|
||||
premium_rate: undefined,
|
||||
total_value: 0,
|
||||
premium_amount: 0,
|
||||
start_date: undefined,
|
||||
end_date: undefined,
|
||||
notes: ''
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = {
|
||||
farmer_name: [{ required: true, message: '请输入农户姓名' }],
|
||||
farmer_phone: [
|
||||
{ required: true, message: '请输入农户电话' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码' }
|
||||
],
|
||||
farmer_id_card: [
|
||||
{ required: true, message: '请输入身份证号' },
|
||||
{ pattern: /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/, message: '请输入正确的身份证号' }
|
||||
],
|
||||
farmer_address: [{ required: true, message: '请输入农户地址' }],
|
||||
livestock_type_id: [{ required: true, message: '请选择牲畜类型' }],
|
||||
livestock_count: [{ required: true, message: '请输入牲畜数量' }],
|
||||
unit_value: [{ required: true, message: '请输入单头价值' }],
|
||||
premium_rate: [{ required: true, message: '请输入保费费率' }],
|
||||
start_date: [{ required: true, message: '请选择保险开始日期' }],
|
||||
end_date: [{ required: true, message: '请选择保险结束日期' }]
|
||||
}
|
||||
|
||||
// 计算属性
|
||||
const modalTitle = computed(() => {
|
||||
return currentRecord.value ? '编辑保单' : '新建保单'
|
||||
})
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: '保单编号',
|
||||
dataIndex: 'policy_no',
|
||||
key: 'policy_no',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '农户姓名',
|
||||
dataIndex: 'farmer_name',
|
||||
key: 'farmer_name',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '农户电话',
|
||||
dataIndex: 'farmer_phone',
|
||||
key: 'farmer_phone',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '牲畜类型',
|
||||
dataIndex: ['livestock_type', 'name'],
|
||||
key: 'livestock_type',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '数量',
|
||||
dataIndex: 'livestock_count',
|
||||
key: 'livestock_count',
|
||||
width: 80,
|
||||
customRender: ({ text }) => `${text}头`
|
||||
},
|
||||
{
|
||||
title: '总保额',
|
||||
dataIndex: 'total_value',
|
||||
key: 'total_value',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '保费金额',
|
||||
dataIndex: 'premium_amount',
|
||||
key: 'premium_amount',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '保单状态',
|
||||
dataIndex: 'policy_status',
|
||||
key: 'policy_status',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '支付状态',
|
||||
dataIndex: 'payment_status',
|
||||
key: 'payment_status',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '保险期间',
|
||||
key: 'insurance_period',
|
||||
width: 180,
|
||||
customRender: ({ record }) => `${record.start_date} 至 ${record.end_date}`
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
fixed: 'right'
|
||||
}
|
||||
]
|
||||
|
||||
// 方法
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const params = {
|
||||
page: pagination.current,
|
||||
limit: pagination.pageSize,
|
||||
...searchForm
|
||||
}
|
||||
|
||||
// 处理日期范围
|
||||
if (searchForm.dateRange && searchForm.dateRange.length === 2) {
|
||||
params.start_date = dayjs(searchForm.dateRange[0]).format('YYYY-MM-DD')
|
||||
params.end_date = dayjs(searchForm.dateRange[1]).format('YYYY-MM-DD')
|
||||
}
|
||||
delete params.dateRange
|
||||
|
||||
const response = await livestockPolicyApi.getList(params)
|
||||
if (response.code === 200) {
|
||||
tableData.value = response.data.list
|
||||
pagination.total = response.data.total
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('获取数据失败')
|
||||
console.error('获取数据失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const fetchLivestockTypes = async () => {
|
||||
try {
|
||||
const response = await livestockPolicyApi.getLivestockTypes()
|
||||
if (response.code === 200) {
|
||||
livestockTypes.value = response.data.list
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取牲畜类型失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.current = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
Object.keys(searchForm).forEach(key => {
|
||||
if (key === 'dateRange') {
|
||||
searchForm[key] = []
|
||||
} else {
|
||||
searchForm[key] = key.includes('status') ? undefined : ''
|
||||
}
|
||||
})
|
||||
pagination.current = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const handleTableChange = (pag) => {
|
||||
pagination.current = pag.current
|
||||
pagination.pageSize = pag.pageSize
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const showCreateModal = () => {
|
||||
currentRecord.value = null
|
||||
resetForm()
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const viewDetail = (record) => {
|
||||
currentRecord.value = record
|
||||
detailVisible.value = true
|
||||
}
|
||||
|
||||
const editRecord = (record) => {
|
||||
currentRecord.value = record
|
||||
Object.keys(formData).forEach(key => {
|
||||
if (key === 'start_date' || key === 'end_date') {
|
||||
formData[key] = record[key] ? dayjs(record[key]) : undefined
|
||||
} else {
|
||||
formData[key] = record[key]
|
||||
}
|
||||
})
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const handleMenuClick = async ({ key }, record) => {
|
||||
try {
|
||||
switch (key) {
|
||||
case 'activate':
|
||||
await livestockPolicyApi.updateStatus(record.id, {
|
||||
policy_status: 'active'
|
||||
})
|
||||
message.success('保单已激活')
|
||||
fetchData()
|
||||
break
|
||||
case 'cancel':
|
||||
await livestockPolicyApi.updateStatus(record.id, {
|
||||
policy_status: 'cancelled'
|
||||
})
|
||||
message.success('保单已取消')
|
||||
fetchData()
|
||||
break
|
||||
case 'payment':
|
||||
await livestockPolicyApi.updateStatus(record.id, {
|
||||
payment_status: 'paid'
|
||||
})
|
||||
message.success('支付状态已更新')
|
||||
fetchData()
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('操作失败')
|
||||
console.error('操作失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
|
||||
const submitData = { ...formData }
|
||||
if (submitData.start_date) {
|
||||
submitData.start_date = dayjs(submitData.start_date).format('YYYY-MM-DD')
|
||||
}
|
||||
if (submitData.end_date) {
|
||||
submitData.end_date = dayjs(submitData.end_date).format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
if (currentRecord.value) {
|
||||
await livestockPolicyApi.update(currentRecord.value.id, submitData)
|
||||
message.success('保单更新成功')
|
||||
} else {
|
||||
await livestockPolicyApi.create(submitData)
|
||||
message.success('保单创建成功')
|
||||
}
|
||||
|
||||
modalVisible.value = false
|
||||
fetchData()
|
||||
} catch (error) {
|
||||
if (error.errorFields) {
|
||||
message.error('请检查表单填写')
|
||||
} else {
|
||||
message.error('操作失败')
|
||||
console.error('操作失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
modalVisible.value = false
|
||||
resetForm()
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
Object.keys(formData).forEach(key => {
|
||||
if (key === 'total_value' || key === 'premium_amount') {
|
||||
formData[key] = 0
|
||||
} else {
|
||||
formData[key] = undefined
|
||||
}
|
||||
})
|
||||
formData.notes = ''
|
||||
}
|
||||
|
||||
const handleLivestockTypeChange = (value) => {
|
||||
const selectedType = livestockTypes.value.find(type => type.id === value)
|
||||
if (selectedType) {
|
||||
formData.unit_value = selectedType.base_value
|
||||
formData.premium_rate = selectedType.premium_rate
|
||||
calculateAmounts()
|
||||
}
|
||||
}
|
||||
|
||||
const calculateAmounts = () => {
|
||||
if (formData.livestock_count && formData.unit_value) {
|
||||
formData.total_value = formData.livestock_count * formData.unit_value
|
||||
}
|
||||
if (formData.total_value && formData.premium_rate) {
|
||||
formData.premium_amount = formData.total_value * formData.premium_rate
|
||||
}
|
||||
}
|
||||
|
||||
const handleStartDateChange = (date) => {
|
||||
if (date) {
|
||||
// 自动设置结束日期为一年后
|
||||
formData.end_date = dayjs(date).add(1, 'year').subtract(1, 'day')
|
||||
}
|
||||
}
|
||||
|
||||
// 状态相关方法
|
||||
const getStatusColor = (status) => {
|
||||
const colors = {
|
||||
draft: 'default',
|
||||
active: 'success',
|
||||
expired: 'warning',
|
||||
cancelled: 'error'
|
||||
}
|
||||
return colors[status] || 'default'
|
||||
}
|
||||
|
||||
const getStatusText = (status) => {
|
||||
const texts = {
|
||||
draft: '草稿',
|
||||
active: '生效',
|
||||
expired: '过期',
|
||||
cancelled: '已取消'
|
||||
}
|
||||
return texts[status] || status
|
||||
}
|
||||
|
||||
const getPaymentStatusColor = (status) => {
|
||||
const colors = {
|
||||
unpaid: 'error',
|
||||
paid: 'success',
|
||||
partial: 'warning'
|
||||
}
|
||||
return colors[status] || 'default'
|
||||
}
|
||||
|
||||
const getPaymentStatusText = (status) => {
|
||||
const texts = {
|
||||
unpaid: '未支付',
|
||||
paid: '已支付',
|
||||
partial: '部分支付'
|
||||
}
|
||||
return texts[status] || status
|
||||
}
|
||||
|
||||
const formatDateTime = (dateTime) => {
|
||||
return dateTime ? dayjs(dateTime).format('YYYY-MM-DD HH:mm:ss') : ''
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
fetchLivestockTypes()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.livestock-policy-management {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.page-header h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.search-section {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.table-section {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
:deep(.ant-table) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.ant-table-thead > tr > th) {
|
||||
background: #fafafa;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.ant-descriptions-item-label) {
|
||||
font-weight: 600;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
@@ -64,34 +64,18 @@
|
||||
size="middle"
|
||||
:scroll="{ x: 1500 }"
|
||||
>
|
||||
<!-- 状态列 -->
|
||||
<template #status="{ record }">
|
||||
<a-tag :color="getStatusColor(record.status)">
|
||||
{{ getStatusText(record.status) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 任务类型列 -->
|
||||
<template #taskType="{ record }">
|
||||
<a-tag :color="getTaskTypeColor(record.taskType)">
|
||||
{{ getTaskTypeText(record.taskType) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 优先级列 -->
|
||||
<template #priority="{ record }">
|
||||
<a-tag :color="getPriorityColor(record.priority)">
|
||||
{{ getPriorityText(record.priority) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 金额列 -->
|
||||
<template #amount="{ record }">
|
||||
<span>{{ formatAmount(record.applicableAmount) }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 操作列 -->
|
||||
<template #action="{ record }">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'taskStatus'">
|
||||
<a-tag :color="getStatusColor(record.taskStatus)">
|
||||
{{ record.taskStatus }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'priority'">
|
||||
<a-tag :color="getPriorityColor(record.priority)">
|
||||
{{ record.priority }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="link" size="small" @click="handleView(record)">
|
||||
查看
|
||||
@@ -108,6 +92,7 @@
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
@@ -365,22 +350,19 @@ export default {
|
||||
title: '任务状态',
|
||||
dataIndex: 'taskStatus',
|
||||
key: 'taskStatus',
|
||||
width: 100,
|
||||
slots: { customRender: 'status' }
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '优先级',
|
||||
dataIndex: 'priority',
|
||||
key: 'priority',
|
||||
width: 80,
|
||||
slots: { customRender: 'priority' }
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
fixed: 'right',
|
||||
slots: { customRender: 'action' }
|
||||
fixed: 'right'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -54,26 +54,21 @@
|
||||
row-key="id"
|
||||
size="middle"
|
||||
>
|
||||
<!-- 任务状态列 -->
|
||||
<template #taskStatus="{ record }">
|
||||
<a-tag
|
||||
:color="getStatusColor(record.taskStatus)"
|
||||
>
|
||||
{{ record.taskStatus }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 优先级列 -->
|
||||
<template #priority="{ record }">
|
||||
<a-tag
|
||||
:color="getPriorityColor(record.priority)"
|
||||
>
|
||||
{{ record.priority }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 操作列 -->
|
||||
<template #action="{ record }">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'applicableSupplies'">
|
||||
{{ formatApplicableSupplies(record.applicableSupplies) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'taskStatus'">
|
||||
<a-tag :color="getStatusColor(record.taskStatus)">
|
||||
{{ record.taskStatus }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'priority'">
|
||||
<a-tag :color="getPriorityColor(record.priority)">
|
||||
{{ record.priority }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="link"
|
||||
@@ -102,6 +97,7 @@
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
@@ -378,15 +374,7 @@ const columns = [
|
||||
title: '适用生资',
|
||||
dataIndex: 'applicableSupplies',
|
||||
key: 'applicableSupplies',
|
||||
width: 120,
|
||||
customRender: ({ record }) => {
|
||||
try {
|
||||
const supplies = JSON.parse(record.applicableSupplies || '[]')
|
||||
return Array.isArray(supplies) ? supplies.join(', ') : record.applicableSupplies
|
||||
} catch {
|
||||
return record.applicableSupplies || '-'
|
||||
}
|
||||
}
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
title: '监管生资数量',
|
||||
@@ -397,7 +385,6 @@ const columns = [
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
width: 200,
|
||||
fixed: 'right'
|
||||
}
|
||||
@@ -646,6 +633,16 @@ const downloadTemplate = () => {
|
||||
message.info('模板下载功能开发中')
|
||||
}
|
||||
|
||||
// 格式化适用生资
|
||||
const formatApplicableSupplies = (supplies) => {
|
||||
try {
|
||||
const parsed = JSON.parse(supplies || '[]')
|
||||
return Array.isArray(parsed) ? parsed.join(', ') : supplies || ''
|
||||
} catch {
|
||||
return supplies || ''
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时获取数据
|
||||
onMounted(() => {
|
||||
fetchTaskList()
|
||||
|
||||
@@ -17,7 +17,7 @@ export default defineConfig(({ mode }) => {
|
||||
port: parseInt(env.VITE_PORT) || 3004,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: env.VITE_API_BASE_URL || 'http://localhost:3002',
|
||||
target: env.VITE_API_BASE_URL || 'http://localhost:3000',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path
|
||||
}
|
||||
|
||||
@@ -143,6 +143,69 @@ const swaggerDefinition = {
|
||||
createdAt: { type: 'string', format: 'date-time', description: '创建时间' }
|
||||
}
|
||||
},
|
||||
LivestockType: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'integer', description: '牲畜类型ID' },
|
||||
name: { type: 'string', description: '牲畜类型名称' },
|
||||
description: { type: 'string', description: '牲畜类型描述' },
|
||||
base_value: { type: 'number', format: 'float', description: '基础价值(单头)' },
|
||||
premium_rate: { type: 'number', format: 'float', description: '基础保费费率' },
|
||||
is_active: { type: 'boolean', description: '是否启用' },
|
||||
created_at: { type: 'string', format: 'date-time', description: '创建时间' },
|
||||
updated_at: { type: 'string', format: 'date-time', description: '更新时间' }
|
||||
}
|
||||
},
|
||||
LivestockPolicy: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'integer', description: '保单ID' },
|
||||
policy_no: { type: 'string', description: '保单编号' },
|
||||
farmer_name: { type: 'string', description: '农户姓名' },
|
||||
farmer_phone: { type: 'string', description: '农户电话' },
|
||||
farmer_id_card: { type: 'string', description: '农户身份证号' },
|
||||
farmer_address: { type: 'string', description: '农户地址' },
|
||||
livestock_type_id: { type: 'integer', description: '牲畜类型ID' },
|
||||
livestock_count: { type: 'integer', description: '牲畜数量' },
|
||||
unit_value: { type: 'number', format: 'float', description: '单头价值' },
|
||||
total_value: { type: 'number', format: 'float', description: '总保额' },
|
||||
premium_rate: { type: 'number', format: 'float', description: '保费费率' },
|
||||
premium_amount: { type: 'number', format: 'float', description: '保费金额' },
|
||||
start_date: { type: 'string', format: 'date', description: '保险开始日期' },
|
||||
end_date: { type: 'string', format: 'date', description: '保险结束日期' },
|
||||
policy_status: { type: 'string', enum: ['draft', 'active', 'expired', 'cancelled'], description: '保单状态' },
|
||||
payment_status: { type: 'string', enum: ['unpaid', 'paid', 'partial'], description: '支付状态' },
|
||||
payment_date: { type: 'string', format: 'date-time', description: '支付时间' },
|
||||
notes: { type: 'string', description: '备注' },
|
||||
created_at: { type: 'string', format: 'date-time', description: '创建时间' },
|
||||
updated_at: { type: 'string', format: 'date-time', description: '更新时间' }
|
||||
}
|
||||
},
|
||||
LivestockClaim: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'integer', description: '理赔ID' },
|
||||
claim_no: { type: 'string', description: '理赔编号' },
|
||||
policy_id: { type: 'integer', description: '保单ID' },
|
||||
claim_type: { type: 'string', enum: ['death', 'disease', 'accident', 'theft', 'natural_disaster'], description: '理赔类型' },
|
||||
incident_date: { type: 'string', format: 'date', description: '事故发生日期' },
|
||||
report_date: { type: 'string', format: 'date', description: '报案日期' },
|
||||
affected_count: { type: 'integer', description: '受损数量' },
|
||||
claim_amount: { type: 'number', format: 'float', description: '申请理赔金额' },
|
||||
approved_amount: { type: 'number', format: 'float', description: '批准理赔金额' },
|
||||
incident_description: { type: 'string', description: '事故描述' },
|
||||
evidence_files: { type: 'string', description: '证据文件(JSON格式)' },
|
||||
claim_status: { type: 'string', enum: ['pending', 'investigating', 'approved', 'rejected', 'paid'], description: '理赔状态' },
|
||||
review_notes: { type: 'string', description: '审核备注' },
|
||||
reviewed_at: { type: 'string', format: 'date-time', description: '审核时间' },
|
||||
payment_status: { type: 'string', enum: ['unpaid', 'paid'], description: '支付状态' },
|
||||
payment_date: { type: 'string', format: 'date-time', description: '支付时间' },
|
||||
payment_method: { type: 'string', description: '支付方式' },
|
||||
payment_reference: { type: 'string', description: '支付凭证号' },
|
||||
created_at: { type: 'string', format: 'date-time', description: '创建时间' },
|
||||
updated_at: { type: 'string', format: 'date-time', description: '更新时间' }
|
||||
}
|
||||
},
|
||||
Error: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
|
||||
@@ -109,8 +109,12 @@ const login = async (req, res) => {
|
||||
expires_in: 7 * 24 * 60 * 60 // 7天
|
||||
}, '登录成功'));
|
||||
} catch (error) {
|
||||
console.error('登录错误:', error);
|
||||
res.status(500).json(responseFormat.error('登录失败'));
|
||||
console.error('登录错误详情:', {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
name: error.name
|
||||
});
|
||||
res.status(500).json(responseFormat.error('登录失败: ' + error.message));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -5,19 +5,41 @@ const { Op } = require('sequelize');
|
||||
// 获取保险申请列表
|
||||
const getApplications = async (req, res) => {
|
||||
try {
|
||||
console.log('获取保险申请列表 - 请求参数:', req.query);
|
||||
const {
|
||||
name,
|
||||
applicantName,
|
||||
status,
|
||||
insuranceType,
|
||||
insuranceCategory,
|
||||
applicationNumber,
|
||||
dateRange,
|
||||
page = 1,
|
||||
limit = 10
|
||||
} = req.query;
|
||||
|
||||
const whereClause = {};
|
||||
const includeClause = [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insurance_type',
|
||||
attributes: ['id', 'name', 'description'],
|
||||
where: {}
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'reviewer',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
}
|
||||
];
|
||||
|
||||
// 姓名筛选
|
||||
if (name) {
|
||||
whereClause.customer_name = { [Op.like]: `%${name}%` };
|
||||
// 申请单号筛选
|
||||
if (applicationNumber) {
|
||||
whereClause.application_no = { [Op.like]: `%${applicationNumber}%` };
|
||||
}
|
||||
|
||||
// 投保人姓名筛选
|
||||
if (applicantName) {
|
||||
whereClause.customer_name = { [Op.like]: `%${applicantName}%` };
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
@@ -25,6 +47,16 @@ const getApplications = async (req, res) => {
|
||||
whereClause.status = status;
|
||||
}
|
||||
|
||||
// 参保险种筛选
|
||||
if (insuranceType) {
|
||||
includeClause[0].where.name = { [Op.like]: `%${insuranceType}%` };
|
||||
}
|
||||
|
||||
// 参保类型筛选
|
||||
if (insuranceCategory) {
|
||||
whereClause.insurance_category = { [Op.like]: `%${insuranceCategory}%` };
|
||||
}
|
||||
|
||||
// 日期范围筛选
|
||||
if (dateRange && dateRange.start && dateRange.end) {
|
||||
whereClause.application_date = {
|
||||
@@ -34,25 +66,23 @@ const getApplications = async (req, res) => {
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// 如果没有保险类型筛选条件,清空where条件
|
||||
if (!insuranceType) {
|
||||
delete includeClause[0].where;
|
||||
}
|
||||
|
||||
console.log('查询条件:', { whereClause, includeClause, offset, limit: parseInt(limit) });
|
||||
|
||||
const { count, rows } = await InsuranceApplication.findAndCountAll({
|
||||
where: whereClause,
|
||||
include: [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insurance_type',
|
||||
attributes: ['id', 'name', 'description']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'reviewer',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
}
|
||||
],
|
||||
include: includeClause,
|
||||
order: [['created_at', 'DESC']],
|
||||
offset,
|
||||
limit: parseInt(limit)
|
||||
});
|
||||
|
||||
console.log('查询结果:', { count, rowsLength: rows.length });
|
||||
|
||||
res.json(responseFormat.pagination(rows, {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
@@ -67,19 +97,58 @@ const getApplications = async (req, res) => {
|
||||
// 创建保险申请
|
||||
const createApplication = async (req, res) => {
|
||||
try {
|
||||
const applicationData = req.body;
|
||||
const {
|
||||
customer_name,
|
||||
customer_id_card,
|
||||
customer_phone,
|
||||
customer_address,
|
||||
insurance_type_id,
|
||||
insurance_category,
|
||||
application_quantity,
|
||||
application_amount,
|
||||
remarks
|
||||
} = req.body;
|
||||
|
||||
// 验证必填字段
|
||||
if (!customer_name || !customer_id_card || !customer_phone || !customer_address || !insurance_type_id) {
|
||||
return res.status(400).json(responseFormat.error('请填写所有必填字段'));
|
||||
}
|
||||
|
||||
// 生成申请编号
|
||||
const applicationNo = `INS${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
|
||||
const applicationNo = `${new Date().getFullYear()}${(new Date().getMonth() + 1).toString().padStart(2, '0')}${new Date().getDate().toString().padStart(2, '0')}${Date.now().toString().slice(-6)}`;
|
||||
|
||||
const application = await InsuranceApplication.create({
|
||||
...applicationData,
|
||||
application_no: applicationNo
|
||||
application_no: applicationNo,
|
||||
customer_name,
|
||||
customer_id_card,
|
||||
customer_phone,
|
||||
customer_address,
|
||||
insurance_type_id,
|
||||
insurance_category,
|
||||
application_quantity: application_quantity || 1,
|
||||
application_amount: application_amount || 0,
|
||||
remarks,
|
||||
status: 'pending'
|
||||
});
|
||||
|
||||
res.status(201).json(responseFormat.created(application, '保险申请创建成功'));
|
||||
// 返回创建的申请信息,包含关联数据
|
||||
const createdApplication = await InsuranceApplication.findByPk(application.id, {
|
||||
include: [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insurance_type',
|
||||
attributes: ['id', 'name', 'description']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
res.status(201).json(responseFormat.created(createdApplication, '保险申请创建成功'));
|
||||
} catch (error) {
|
||||
console.error('创建保险申请错误:', error);
|
||||
if (error.name === 'SequelizeValidationError') {
|
||||
const errorMessages = error.errors.map(err => err.message).join(', ');
|
||||
return res.status(400).json(responseFormat.error(`数据验证失败: ${errorMessages}`));
|
||||
}
|
||||
res.status(500).json(responseFormat.error('创建保险申请失败'));
|
||||
}
|
||||
};
|
||||
@@ -207,6 +276,117 @@ const getApplicationStats = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取参保类型选项
|
||||
const getInsuranceCategories = async (req, res) => {
|
||||
try {
|
||||
const categories = await InsuranceApplication.findAll({
|
||||
attributes: ['insurance_category'],
|
||||
where: {
|
||||
insurance_category: {
|
||||
[Op.ne]: null
|
||||
}
|
||||
},
|
||||
group: ['insurance_category'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
const categoryList = categories.map(item => item.insurance_category).filter(Boolean);
|
||||
|
||||
// 添加一些常用的参保类型
|
||||
const defaultCategories = ['牛', '羊', '猪', '鸡', '鸭', '鹅'];
|
||||
const allCategories = [...new Set([...defaultCategories, ...categoryList])];
|
||||
|
||||
res.json(responseFormat.success(allCategories, '获取参保类型选项成功'));
|
||||
} catch (error) {
|
||||
console.error('获取参保类型选项错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取参保类型选项失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 导出保险申请数据
|
||||
const exportApplications = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 1000,
|
||||
applicantName,
|
||||
insuranceType,
|
||||
insuranceCategory,
|
||||
status
|
||||
} = req.query;
|
||||
|
||||
const where = {};
|
||||
|
||||
if (applicantName) {
|
||||
where.applicant_name = { [Op.like]: `%${applicantName}%` };
|
||||
}
|
||||
if (insuranceType) {
|
||||
where.insurance_type = insuranceType;
|
||||
}
|
||||
if (insuranceCategory) {
|
||||
where.insurance_category = insuranceCategory;
|
||||
}
|
||||
if (status) {
|
||||
where.status = status;
|
||||
}
|
||||
|
||||
const applications = await InsuranceApplication.findAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insuranceTypeInfo',
|
||||
attributes: ['name', 'description']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'createdByUser',
|
||||
attributes: ['username', 'real_name']
|
||||
}
|
||||
],
|
||||
order: [['created_at', 'DESC']],
|
||||
limit: parseInt(limit),
|
||||
offset: (parseInt(page) - 1) * parseInt(limit)
|
||||
});
|
||||
|
||||
// 简单的CSV格式导出
|
||||
const csvHeader = '申请编号,申请人姓名,身份证号,联系电话,参保类型,保险类型,保险金额,保险期限,地址,状态,申请时间,备注\n';
|
||||
const csvData = applications.map(app => {
|
||||
const statusMap = {
|
||||
'pending': '待审核',
|
||||
'initial_approved': '初审通过',
|
||||
'under_review': '复审中',
|
||||
'approved': '已通过',
|
||||
'rejected': '已拒绝'
|
||||
};
|
||||
|
||||
return [
|
||||
app.application_number || '',
|
||||
app.applicant_name || '',
|
||||
app.id_card || '',
|
||||
app.phone || '',
|
||||
app.insurance_category || '',
|
||||
app.insurance_type || '',
|
||||
app.insurance_amount || '',
|
||||
app.insurance_period || '',
|
||||
app.address || '',
|
||||
statusMap[app.status] || app.status,
|
||||
app.created_at ? new Date(app.created_at).toLocaleString('zh-CN') : '',
|
||||
app.remarks || ''
|
||||
].map(field => `"${String(field).replace(/"/g, '""')}"`).join(',');
|
||||
}).join('\n');
|
||||
|
||||
const csvContent = csvHeader + csvData;
|
||||
|
||||
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
||||
res.setHeader('Content-Disposition', `attachment; filename="insurance_applications_${new Date().toISOString().slice(0, 10)}.csv"`);
|
||||
res.send('\uFEFF' + csvContent); // 添加BOM以支持中文
|
||||
} catch (error) {
|
||||
console.error('导出保险申请数据错误:', error);
|
||||
res.status(500).json(responseFormat.error('导出保险申请数据失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getApplications,
|
||||
createApplication,
|
||||
@@ -214,5 +394,7 @@ module.exports = {
|
||||
updateApplication,
|
||||
reviewApplication,
|
||||
deleteApplication,
|
||||
getApplicationStats
|
||||
getApplicationStats,
|
||||
getInsuranceCategories,
|
||||
exportApplications
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
const { InsuranceType } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
const responseFormat = require('../utils/response');
|
||||
|
||||
// 获取保险类型列表
|
||||
@@ -28,10 +29,10 @@ const getInsuranceTypes = async (req, res) => {
|
||||
page: parseInt(page),
|
||||
pageSize: parseInt(pageSize),
|
||||
pages: Math.ceil(count / pageSize)
|
||||
}, '获取保险类型列表成功'));
|
||||
}, '获取险种列表成功'));
|
||||
} catch (error) {
|
||||
console.error('获取保险类型列表错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取保险类型列表失败'));
|
||||
console.error('获取险种列表错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取险种列表失败'));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -52,12 +53,11 @@ const getInsuranceTypeById = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 创建保险类型
|
||||
// 创建险种
|
||||
const createInsuranceType = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
name,
|
||||
code,
|
||||
description,
|
||||
coverage_amount_min,
|
||||
coverage_amount_max,
|
||||
@@ -68,39 +68,36 @@ const createInsuranceType = async (req, res) => {
|
||||
// 检查名称是否已存在
|
||||
const existingType = await InsuranceType.findOne({ where: { name } });
|
||||
if (existingType) {
|
||||
return res.status(400).json(responseFormat.error('保险类型名称已存在'));
|
||||
}
|
||||
|
||||
// 检查代码是否已存在
|
||||
const existingCode = await InsuranceType.findOne({ where: { code } });
|
||||
if (existingCode) {
|
||||
return res.status(400).json(responseFormat.error('保险类型代码已存在'));
|
||||
return res.status(400).json(responseFormat.error('险种名称已存在'));
|
||||
}
|
||||
|
||||
const insuranceType = await InsuranceType.create({
|
||||
name,
|
||||
code,
|
||||
description,
|
||||
coverage_amount_min,
|
||||
coverage_amount_max,
|
||||
premium_rate,
|
||||
status
|
||||
status,
|
||||
created_by: req.user?.id
|
||||
});
|
||||
|
||||
res.status(201).json(responseFormat.success(insuranceType, '创建保险类型成功'));
|
||||
res.status(201).json(responseFormat.success(insuranceType, '创建险种成功'));
|
||||
} catch (error) {
|
||||
console.error('创建保险类型错误:', error);
|
||||
res.status(500).json(responseFormat.error('创建保险类型失败'));
|
||||
console.error('创建险种错误:', error);
|
||||
if (error.name === 'SequelizeValidationError') {
|
||||
const messages = error.errors.map(err => err.message);
|
||||
return res.status(400).json(responseFormat.error(messages.join(', ')));
|
||||
}
|
||||
res.status(500).json(responseFormat.error('创建险种失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新保险类型
|
||||
// 更新险种
|
||||
const updateInsuranceType = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const {
|
||||
name,
|
||||
code,
|
||||
description,
|
||||
coverage_amount_min,
|
||||
coverage_amount_max,
|
||||
@@ -110,67 +107,68 @@ const updateInsuranceType = async (req, res) => {
|
||||
|
||||
const insuranceType = await InsuranceType.findByPk(id);
|
||||
if (!insuranceType) {
|
||||
return res.status(404).json(responseFormat.error('保险类型不存在'));
|
||||
return res.status(404).json(responseFormat.error('险种不存在'));
|
||||
}
|
||||
|
||||
// 检查名称是否已被其他类型使用
|
||||
if (name && name !== insuranceType.name) {
|
||||
const existingType = await InsuranceType.findOne({ where: { name } });
|
||||
const existingType = await InsuranceType.findOne({
|
||||
where: {
|
||||
name,
|
||||
id: { [Op.ne]: id }
|
||||
}
|
||||
});
|
||||
if (existingType) {
|
||||
return res.status(400).json(responseFormat.error('保险类型名称已存在'));
|
||||
}
|
||||
}
|
||||
|
||||
// 检查代码是否已被其他类型使用
|
||||
if (code && code !== insuranceType.code) {
|
||||
const existingCode = await InsuranceType.findOne({ where: { code } });
|
||||
if (existingCode) {
|
||||
return res.status(400).json(responseFormat.error('保险类型代码已存在'));
|
||||
return res.status(400).json(responseFormat.error('险种名称已存在'));
|
||||
}
|
||||
}
|
||||
|
||||
await insuranceType.update({
|
||||
name: name || insuranceType.name,
|
||||
code: code || insuranceType.code,
|
||||
description: description || insuranceType.description,
|
||||
coverage_amount_min: coverage_amount_min || insuranceType.coverage_amount_min,
|
||||
coverage_amount_max: coverage_amount_max || insuranceType.coverage_amount_max,
|
||||
premium_rate: premium_rate || insuranceType.premium_rate,
|
||||
status: status || insuranceType.status
|
||||
status: status || insuranceType.status,
|
||||
updated_by: req.user?.id
|
||||
});
|
||||
|
||||
res.json(responseFormat.success(insuranceType, '更新保险类型成功'));
|
||||
res.json(responseFormat.success(insuranceType, '更新险种成功'));
|
||||
} catch (error) {
|
||||
console.error('更新保险类型错误:', error);
|
||||
res.status(500).json(responseFormat.error('更新保险类型失败'));
|
||||
console.error('更新险种错误:', error);
|
||||
if (error.name === 'SequelizeValidationError') {
|
||||
const messages = error.errors.map(err => err.message);
|
||||
return res.status(400).json(responseFormat.error(messages.join(', ')));
|
||||
}
|
||||
res.status(500).json(responseFormat.error('更新险种失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 删除保险类型
|
||||
// 删除险种
|
||||
const deleteInsuranceType = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const insuranceType = await InsuranceType.findByPk(id);
|
||||
if (!insuranceType) {
|
||||
return res.status(404).json(responseFormat.error('保险类型不存在'));
|
||||
return res.status(404).json(responseFormat.error('险种不存在'));
|
||||
}
|
||||
|
||||
// 检查是否有相关的保险申请或保单
|
||||
const hasApplications = await insuranceType.countInsuranceApplications();
|
||||
if (hasApplications > 0) {
|
||||
return res.status(400).json(responseFormat.error('该保险类型下存在保险申请,无法删除'));
|
||||
}
|
||||
// const hasApplications = await insuranceType.countInsuranceApplications();
|
||||
// if (hasApplications > 0) {
|
||||
// return res.status(400).json(responseFormat.error('该险种下存在保险申请,无法删除'));
|
||||
// }
|
||||
|
||||
await insuranceType.destroy();
|
||||
res.json(responseFormat.success(null, '删除保险类型成功'));
|
||||
res.json(responseFormat.success(null, '删除险种成功'));
|
||||
} catch (error) {
|
||||
console.error('删除保险类型错误:', error);
|
||||
res.status(500).json(responseFormat.error('删除保险类型失败'));
|
||||
console.error('删除险种错误:', error);
|
||||
res.status(500).json(responseFormat.error('删除险种失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新保险类型状态
|
||||
// 更新险种状态
|
||||
const updateInsuranceTypeStatus = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
@@ -178,14 +176,14 @@ const updateInsuranceTypeStatus = async (req, res) => {
|
||||
|
||||
const insuranceType = await InsuranceType.findByPk(id);
|
||||
if (!insuranceType) {
|
||||
return res.status(404).json(responseFormat.error('保险类型不存在'));
|
||||
return res.status(404).json(responseFormat.error('险种不存在'));
|
||||
}
|
||||
|
||||
await insuranceType.update({ status });
|
||||
res.json(responseFormat.success(insuranceType, '更新保险类型状态成功'));
|
||||
await insuranceType.update({ status, updated_by: req.user?.id });
|
||||
res.json(responseFormat.success(insuranceType, '更新险种状态成功'));
|
||||
} catch (error) {
|
||||
console.error('更新保险类型状态错误:', error);
|
||||
res.status(500).json(responseFormat.error('更新保险类型状态失败'));
|
||||
console.error('更新险种状态错误:', error);
|
||||
res.status(500).json(responseFormat.error('更新险种状态失败'));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
365
insurance_backend/controllers/livestockClaimController.js
Normal file
365
insurance_backend/controllers/livestockClaimController.js
Normal file
@@ -0,0 +1,365 @@
|
||||
const LivestockClaim = require('../models/LivestockClaim');
|
||||
const LivestockPolicy = require('../models/LivestockPolicy');
|
||||
const LivestockType = require('../models/LivestockType');
|
||||
const User = require('../models/User');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
// 获取生资理赔列表
|
||||
const getLivestockClaims = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
claim_no,
|
||||
policy_no,
|
||||
farmer_name,
|
||||
claim_status,
|
||||
claim_type,
|
||||
start_date,
|
||||
end_date,
|
||||
page = 1,
|
||||
limit = 10
|
||||
} = req.query;
|
||||
|
||||
const whereClause = {};
|
||||
|
||||
// 理赔编号筛选
|
||||
if (claim_no) {
|
||||
whereClause.claim_no = { [Op.like]: `%${claim_no}%` };
|
||||
}
|
||||
|
||||
// 理赔状态筛选
|
||||
if (claim_status) {
|
||||
whereClause.claim_status = claim_status;
|
||||
}
|
||||
|
||||
// 理赔类型筛选
|
||||
if (claim_type) {
|
||||
whereClause.claim_type = claim_type;
|
||||
}
|
||||
|
||||
// 日期范围筛选
|
||||
if (start_date && end_date) {
|
||||
whereClause.incident_date = {
|
||||
[Op.between]: [new Date(start_date), new Date(end_date)]
|
||||
};
|
||||
}
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const { count, rows } = await LivestockClaim.findAndCountAll({
|
||||
where: whereClause,
|
||||
include: [
|
||||
{
|
||||
model: LivestockPolicy,
|
||||
as: 'policy',
|
||||
attributes: ['id', 'policy_no', 'farmer_name', 'farmer_phone', 'livestock_count'],
|
||||
where: policy_no ? { policy_no: { [Op.like]: `%${policy_no}%` } } : {},
|
||||
required: !!policy_no,
|
||||
include: [
|
||||
{
|
||||
model: LivestockType,
|
||||
as: 'livestock_type',
|
||||
attributes: ['id', 'name']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'reviewer',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
}
|
||||
],
|
||||
order: [['created_at', 'DESC']],
|
||||
offset,
|
||||
limit: parseInt(limit)
|
||||
});
|
||||
|
||||
// 如果有农户姓名筛选,需要在关联查询后再过滤
|
||||
let filteredRows = rows;
|
||||
if (farmer_name) {
|
||||
filteredRows = rows.filter(claim =>
|
||||
claim.policy && claim.policy.farmer_name.includes(farmer_name)
|
||||
);
|
||||
}
|
||||
|
||||
res.json(responseFormat.pagination(filteredRows, {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
total: count
|
||||
}, '获取生资理赔列表成功'));
|
||||
} catch (error) {
|
||||
console.error('获取生资理赔列表错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取生资理赔列表失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 创建生资理赔申请
|
||||
const createLivestockClaim = async (req, res) => {
|
||||
try {
|
||||
const claimData = req.body;
|
||||
|
||||
// 验证保单是否存在且有效
|
||||
const policy = await LivestockPolicy.findByPk(claimData.policy_id);
|
||||
if (!policy) {
|
||||
return res.status(400).json(responseFormat.error('保单不存在'));
|
||||
}
|
||||
|
||||
if (policy.policy_status !== 'active') {
|
||||
return res.status(400).json(responseFormat.error('保单状态无效,无法申请理赔'));
|
||||
}
|
||||
|
||||
// 生成理赔编号
|
||||
const claimNo = `LC${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
|
||||
|
||||
const claim = await LivestockClaim.create({
|
||||
...claimData,
|
||||
claim_no: claimNo,
|
||||
claim_status: 'pending',
|
||||
created_by: req.user?.id
|
||||
});
|
||||
|
||||
// 获取完整的理赔信息(包含关联数据)
|
||||
const fullClaim = await LivestockClaim.findByPk(claim.id, {
|
||||
include: [
|
||||
{
|
||||
model: LivestockPolicy,
|
||||
as: 'policy',
|
||||
attributes: ['id', 'policy_no', 'farmer_name', 'farmer_phone'],
|
||||
include: [
|
||||
{
|
||||
model: LivestockType,
|
||||
as: 'livestock_type',
|
||||
attributes: ['id', 'name']
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
res.status(201).json(responseFormat.created(fullClaim, '生资理赔申请创建成功'));
|
||||
} catch (error) {
|
||||
console.error('创建生资理赔申请错误:', error);
|
||||
res.status(500).json(responseFormat.error('创建生资理赔申请失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取单个生资理赔详情
|
||||
const getLivestockClaimById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const claim = await LivestockClaim.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: LivestockPolicy,
|
||||
as: 'policy',
|
||||
include: [
|
||||
{
|
||||
model: LivestockType,
|
||||
as: 'livestock_type',
|
||||
attributes: ['id', 'name', 'description', 'base_value']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'reviewer',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!claim) {
|
||||
return res.status(404).json(responseFormat.error('生资理赔不存在'));
|
||||
}
|
||||
|
||||
res.json(responseFormat.success(claim, '获取生资理赔详情成功'));
|
||||
} catch (error) {
|
||||
console.error('获取生资理赔详情错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取生资理赔详情失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 审核生资理赔
|
||||
const reviewLivestockClaim = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { claim_status, review_notes, approved_amount } = req.body;
|
||||
|
||||
const claim = await LivestockClaim.findByPk(id);
|
||||
if (!claim) {
|
||||
return res.status(404).json(responseFormat.error('生资理赔不存在'));
|
||||
}
|
||||
|
||||
if (claim.claim_status !== 'pending') {
|
||||
return res.status(400).json(responseFormat.error('该理赔已经审核过了'));
|
||||
}
|
||||
|
||||
const updateData = {
|
||||
claim_status,
|
||||
review_notes,
|
||||
reviewed_by: req.user?.id,
|
||||
reviewed_at: new Date()
|
||||
};
|
||||
|
||||
if (claim_status === 'approved' && approved_amount) {
|
||||
updateData.approved_amount = approved_amount;
|
||||
}
|
||||
|
||||
await claim.update(updateData);
|
||||
|
||||
// 获取更新后的完整信息
|
||||
const updatedClaim = await LivestockClaim.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: LivestockPolicy,
|
||||
as: 'policy',
|
||||
attributes: ['id', 'policy_no', 'farmer_name', 'farmer_phone']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'reviewer',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
res.json(responseFormat.success(updatedClaim, '生资理赔审核成功'));
|
||||
} catch (error) {
|
||||
console.error('审核生资理赔错误:', error);
|
||||
res.status(500).json(responseFormat.error('审核生资理赔失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新理赔支付状态
|
||||
const updateLivestockClaimPayment = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { payment_status, payment_date, payment_method, payment_reference } = req.body;
|
||||
|
||||
const claim = await LivestockClaim.findByPk(id);
|
||||
if (!claim) {
|
||||
return res.status(404).json(responseFormat.error('生资理赔不存在'));
|
||||
}
|
||||
|
||||
if (claim.claim_status !== 'approved') {
|
||||
return res.status(400).json(responseFormat.error('只有已批准的理赔才能更新支付状态'));
|
||||
}
|
||||
|
||||
const updateData = {
|
||||
payment_status,
|
||||
payment_date,
|
||||
payment_method,
|
||||
payment_reference,
|
||||
updated_by: req.user?.id
|
||||
};
|
||||
|
||||
await claim.update(updateData);
|
||||
|
||||
res.json(responseFormat.success(claim, '理赔支付状态更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新理赔支付状态错误:', error);
|
||||
res.status(500).json(responseFormat.error('更新理赔支付状态失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取生资理赔统计
|
||||
const getLivestockClaimStats = async (req, res) => {
|
||||
try {
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
// 总理赔数
|
||||
const totalClaims = await LivestockClaim.count();
|
||||
|
||||
// 待审核理赔数
|
||||
const pendingClaims = await LivestockClaim.count({
|
||||
where: { claim_status: 'pending' }
|
||||
});
|
||||
|
||||
// 已批准理赔数
|
||||
const approvedClaims = await LivestockClaim.count({
|
||||
where: { claim_status: 'approved' }
|
||||
});
|
||||
|
||||
// 已拒绝理赔数
|
||||
const rejectedClaims = await LivestockClaim.count({
|
||||
where: { claim_status: 'rejected' }
|
||||
});
|
||||
|
||||
// 总理赔金额
|
||||
const totalClaimAmount = await LivestockClaim.sum('claim_amount') || 0;
|
||||
|
||||
// 已支付理赔金额
|
||||
const paidClaimAmount = await LivestockClaim.sum('approved_amount', {
|
||||
where: {
|
||||
claim_status: 'approved',
|
||||
payment_status: 'paid'
|
||||
}
|
||||
}) || 0;
|
||||
|
||||
// 按理赔类型统计
|
||||
const typeStats = await sequelize.query(`
|
||||
SELECT
|
||||
claim_type,
|
||||
COUNT(*) as claim_count,
|
||||
SUM(claim_amount) as total_claim_amount,
|
||||
SUM(CASE WHEN claim_status = 'approved' THEN approved_amount ELSE 0 END) as total_approved_amount,
|
||||
AVG(CASE WHEN claim_status = 'approved' THEN approved_amount ELSE NULL END) as avg_approved_amount
|
||||
FROM livestock_claims
|
||||
GROUP BY claim_type
|
||||
ORDER BY claim_count DESC
|
||||
`, { type: sequelize.QueryTypes.SELECT });
|
||||
|
||||
// 按月份统计(最近12个月)
|
||||
const monthlyStats = await sequelize.query(`
|
||||
SELECT
|
||||
DATE_FORMAT(created_at, '%Y-%m') as month,
|
||||
COUNT(*) as claim_count,
|
||||
SUM(claim_amount) as total_claim_amount,
|
||||
SUM(CASE WHEN claim_status = 'approved' THEN approved_amount ELSE 0 END) as total_approved_amount,
|
||||
COUNT(CASE WHEN claim_status = 'approved' THEN 1 END) as approved_count
|
||||
FROM livestock_claims
|
||||
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
|
||||
GROUP BY DATE_FORMAT(created_at, '%Y-%m')
|
||||
ORDER BY month DESC
|
||||
`, { type: sequelize.QueryTypes.SELECT });
|
||||
|
||||
const stats = {
|
||||
overview: {
|
||||
total_claims: totalClaims,
|
||||
pending_claims: pendingClaims,
|
||||
approved_claims: approvedClaims,
|
||||
rejected_claims: rejectedClaims,
|
||||
total_claim_amount: parseFloat(totalClaimAmount),
|
||||
paid_claim_amount: parseFloat(paidClaimAmount),
|
||||
approval_rate: totalClaims > 0 ? (approvedClaims / totalClaims * 100).toFixed(2) : 0
|
||||
},
|
||||
by_type: typeStats,
|
||||
monthly: monthlyStats
|
||||
};
|
||||
|
||||
res.json(responseFormat.success(stats, '获取生资理赔统计成功'));
|
||||
} catch (error) {
|
||||
console.error('获取生资理赔统计错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取生资理赔统计失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getLivestockClaims,
|
||||
createLivestockClaim,
|
||||
getLivestockClaimById,
|
||||
reviewLivestockClaim,
|
||||
updateLivestockClaimPayment,
|
||||
getLivestockClaimStats
|
||||
};
|
||||
348
insurance_backend/controllers/livestockPolicyController.js
Normal file
348
insurance_backend/controllers/livestockPolicyController.js
Normal file
@@ -0,0 +1,348 @@
|
||||
const LivestockPolicy = require('../models/LivestockPolicy');
|
||||
const LivestockType = require('../models/LivestockType');
|
||||
const User = require('../models/User');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
// 获取生资保单列表
|
||||
const getLivestockPolicies = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
policy_no,
|
||||
farmer_name,
|
||||
farmer_phone,
|
||||
policy_status,
|
||||
payment_status,
|
||||
livestock_type_id,
|
||||
start_date,
|
||||
end_date,
|
||||
page = 1,
|
||||
limit = 10
|
||||
} = req.query;
|
||||
|
||||
const whereClause = {};
|
||||
|
||||
// 保单编号筛选
|
||||
if (policy_no) {
|
||||
whereClause.policy_no = { [Op.like]: `%${policy_no}%` };
|
||||
}
|
||||
|
||||
// 农户姓名筛选
|
||||
if (farmer_name) {
|
||||
whereClause.farmer_name = { [Op.like]: `%${farmer_name}%` };
|
||||
}
|
||||
|
||||
// 农户电话筛选
|
||||
if (farmer_phone) {
|
||||
whereClause.farmer_phone = { [Op.like]: `%${farmer_phone}%` };
|
||||
}
|
||||
|
||||
// 保单状态筛选
|
||||
if (policy_status) {
|
||||
whereClause.policy_status = policy_status;
|
||||
}
|
||||
|
||||
// 支付状态筛选
|
||||
if (payment_status) {
|
||||
whereClause.payment_status = payment_status;
|
||||
}
|
||||
|
||||
// 牲畜类型筛选
|
||||
if (livestock_type_id) {
|
||||
whereClause.livestock_type_id = livestock_type_id;
|
||||
}
|
||||
|
||||
// 日期范围筛选
|
||||
if (start_date && end_date) {
|
||||
whereClause.start_date = {
|
||||
[Op.between]: [new Date(start_date), new Date(end_date)]
|
||||
};
|
||||
}
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const { count, rows } = await LivestockPolicy.findAndCountAll({
|
||||
where: whereClause,
|
||||
include: [
|
||||
{
|
||||
model: LivestockType,
|
||||
as: 'livestock_type',
|
||||
attributes: ['id', 'name', 'description', 'unit_price_min', 'unit_price_max', 'premium_rate']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
}
|
||||
],
|
||||
order: [['created_at', 'DESC']],
|
||||
offset,
|
||||
limit: parseInt(limit)
|
||||
});
|
||||
|
||||
res.json(responseFormat.pagination(rows, {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
total: count
|
||||
}, '获取生资保单列表成功'));
|
||||
} catch (error) {
|
||||
console.error('获取生资保单列表错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取生资保单列表失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 创建生资保单
|
||||
const createLivestockPolicy = async (req, res) => {
|
||||
try {
|
||||
const policyData = req.body;
|
||||
|
||||
// 生成保单编号
|
||||
const policyNo = `LP${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
|
||||
|
||||
// 计算总保额和保费
|
||||
const totalValue = policyData.livestock_count * policyData.unit_value;
|
||||
const premiumAmount = totalValue * policyData.premium_rate;
|
||||
|
||||
const policy = await LivestockPolicy.create({
|
||||
...policyData,
|
||||
policy_no: policyNo,
|
||||
total_value: totalValue,
|
||||
premium_amount: premiumAmount,
|
||||
created_by: req.user?.id
|
||||
});
|
||||
|
||||
// 获取完整的保单信息(包含关联数据)
|
||||
const fullPolicy = await LivestockPolicy.findByPk(policy.id, {
|
||||
include: [
|
||||
{
|
||||
model: LivestockType,
|
||||
as: 'livestock_type',
|
||||
attributes: ['id', 'name', 'description', 'unit_price_min', 'unit_price_max', 'premium_rate']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
res.status(201).json(responseFormat.created(fullPolicy, '生资保单创建成功'));
|
||||
} catch (error) {
|
||||
console.error('创建生资保单错误:', error);
|
||||
res.status(500).json(responseFormat.error('创建生资保单失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取单个生资保单详情
|
||||
const getLivestockPolicyById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const policy = await LivestockPolicy.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: LivestockType,
|
||||
as: 'livestock_type',
|
||||
attributes: ['id', 'name', 'description', 'unit_price_min', 'unit_price_max', 'premium_rate']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!policy) {
|
||||
return res.status(404).json(responseFormat.error('生资保单不存在'));
|
||||
}
|
||||
|
||||
res.json(responseFormat.success(policy, '获取生资保单详情成功'));
|
||||
} catch (error) {
|
||||
console.error('获取生资保单详情错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取生资保单详情失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新生资保单
|
||||
const updateLivestockPolicy = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const updateData = req.body;
|
||||
|
||||
const policy = await LivestockPolicy.findByPk(id);
|
||||
if (!policy) {
|
||||
return res.status(404).json(responseFormat.error('生资保单不存在'));
|
||||
}
|
||||
|
||||
// 如果更新了数量或单价,重新计算总保额和保费
|
||||
if (updateData.livestock_count || updateData.unit_value || updateData.premium_rate) {
|
||||
const livestockCount = updateData.livestock_count || policy.livestock_count;
|
||||
const unitValue = updateData.unit_value || policy.unit_value;
|
||||
const premiumRate = updateData.premium_rate || policy.premium_rate;
|
||||
|
||||
updateData.total_value = livestockCount * unitValue;
|
||||
updateData.premium_amount = updateData.total_value * premiumRate;
|
||||
}
|
||||
|
||||
updateData.updated_by = req.user?.id;
|
||||
|
||||
await policy.update(updateData);
|
||||
|
||||
// 获取更新后的完整信息
|
||||
const updatedPolicy = await LivestockPolicy.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: LivestockType,
|
||||
as: 'livestock_type',
|
||||
attributes: ['id', 'name', 'description', 'unit_price_min', 'unit_price_max', 'premium_rate']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
res.json(responseFormat.success(updatedPolicy, '生资保单更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新生资保单错误:', error);
|
||||
res.status(500).json(responseFormat.error('更新生资保单失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新生资保单状态
|
||||
const updateLivestockPolicyStatus = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { policy_status, payment_status, payment_date } = req.body;
|
||||
|
||||
const policy = await LivestockPolicy.findByPk(id);
|
||||
if (!policy) {
|
||||
return res.status(404).json(responseFormat.error('生资保单不存在'));
|
||||
}
|
||||
|
||||
const updateData = {
|
||||
updated_by: req.user?.id
|
||||
};
|
||||
|
||||
if (policy_status) updateData.policy_status = policy_status;
|
||||
if (payment_status) updateData.payment_status = payment_status;
|
||||
if (payment_date) updateData.payment_date = payment_date;
|
||||
|
||||
await policy.update(updateData);
|
||||
|
||||
res.json(responseFormat.success(policy, '生资保单状态更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新生资保单状态错误:', error);
|
||||
res.status(500).json(responseFormat.error('更新生资保单状态失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取生资保单统计
|
||||
const getLivestockPolicyStats = async (req, res) => {
|
||||
try {
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
// 总保单数
|
||||
const totalPolicies = await LivestockPolicy.count();
|
||||
|
||||
// 有效保单数
|
||||
const activePolicies = await LivestockPolicy.count({
|
||||
where: { policy_status: 'active' }
|
||||
});
|
||||
|
||||
// 已支付保单数
|
||||
const paidPolicies = await LivestockPolicy.count({
|
||||
where: { payment_status: 'paid' }
|
||||
});
|
||||
|
||||
// 总保费收入
|
||||
const totalPremium = await LivestockPolicy.sum('premium_amount', {
|
||||
where: { payment_status: 'paid' }
|
||||
}) || 0;
|
||||
|
||||
// 总保额
|
||||
const totalCoverage = await LivestockPolicy.sum('total_value', {
|
||||
where: { policy_status: 'active' }
|
||||
}) || 0;
|
||||
|
||||
// 按牲畜类型统计
|
||||
const typeStats = await sequelize.query(`
|
||||
SELECT
|
||||
lt.name as livestock_type,
|
||||
COUNT(lp.id) as policy_count,
|
||||
SUM(lp.livestock_count) as total_livestock,
|
||||
SUM(lp.total_value) as total_coverage,
|
||||
SUM(lp.premium_amount) as total_premium
|
||||
FROM livestock_policies lp
|
||||
LEFT JOIN livestock_types lt ON lp.livestock_type_id = lt.id
|
||||
WHERE lp.policy_status = 'active'
|
||||
GROUP BY lt.id, lt.name
|
||||
ORDER BY policy_count DESC
|
||||
`, { type: sequelize.QueryTypes.SELECT });
|
||||
|
||||
// 按月份统计(最近12个月)
|
||||
const monthlyStats = await sequelize.query(`
|
||||
SELECT
|
||||
DATE_FORMAT(created_at, '%Y-%m') as month,
|
||||
COUNT(*) as policy_count,
|
||||
SUM(premium_amount) as premium_amount,
|
||||
SUM(total_value) as total_coverage
|
||||
FROM livestock_policies
|
||||
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
|
||||
GROUP BY DATE_FORMAT(created_at, '%Y-%m')
|
||||
ORDER BY month DESC
|
||||
`, { type: sequelize.QueryTypes.SELECT });
|
||||
|
||||
const stats = {
|
||||
overview: {
|
||||
total_policies: totalPolicies,
|
||||
active_policies: activePolicies,
|
||||
paid_policies: paidPolicies,
|
||||
total_premium: parseFloat(totalPremium),
|
||||
total_coverage: parseFloat(totalCoverage),
|
||||
payment_rate: totalPolicies > 0 ? (paidPolicies / totalPolicies * 100).toFixed(2) : 0
|
||||
},
|
||||
by_type: typeStats,
|
||||
monthly: monthlyStats
|
||||
};
|
||||
|
||||
res.json(responseFormat.success(stats, '获取生资保单统计成功'));
|
||||
} catch (error) {
|
||||
console.error('获取生资保单统计错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取生资保单统计失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 删除生资保单
|
||||
const deleteLivestockPolicy = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const policy = await LivestockPolicy.findByPk(id);
|
||||
if (!policy) {
|
||||
return res.status(404).json(responseFormat.error('生资保单不存在'));
|
||||
}
|
||||
|
||||
// 检查保单状态,只有草稿状态的保单可以删除
|
||||
if (policy.policy_status === 'active') {
|
||||
return res.status(400).json(responseFormat.error('生效中的保单不能删除'));
|
||||
}
|
||||
|
||||
// 物理删除保单
|
||||
await policy.destroy();
|
||||
|
||||
res.json(responseFormat.success(null, '生资保单删除成功'));
|
||||
} catch (error) {
|
||||
console.error('删除生资保单错误:', error);
|
||||
res.status(500).json(responseFormat.error('删除生资保单失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getLivestockPolicies,
|
||||
createLivestockPolicy,
|
||||
getLivestockPolicyById,
|
||||
updateLivestockPolicy,
|
||||
updateLivestockPolicyStatus,
|
||||
deleteLivestockPolicy,
|
||||
getLivestockPolicyStats
|
||||
};
|
||||
221
insurance_backend/controllers/livestockTypeController.js
Normal file
221
insurance_backend/controllers/livestockTypeController.js
Normal file
@@ -0,0 +1,221 @@
|
||||
const LivestockType = require('../models/LivestockType');
|
||||
const User = require('../models/User');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
// 获取牲畜类型列表
|
||||
const getLivestockTypes = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
name,
|
||||
is_active,
|
||||
page = 1,
|
||||
limit = 10
|
||||
} = req.query;
|
||||
|
||||
const whereClause = {};
|
||||
|
||||
// 名称筛选
|
||||
if (name) {
|
||||
whereClause.name = { [Op.like]: `%${name}%` };
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (is_active !== undefined) {
|
||||
whereClause.is_active = is_active === 'true';
|
||||
}
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const { count, rows } = await LivestockType.findAndCountAll({
|
||||
where: whereClause,
|
||||
order: [['created_at', 'DESC']],
|
||||
offset,
|
||||
limit: parseInt(limit)
|
||||
});
|
||||
|
||||
res.json(responseFormat.pagination(rows, {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
total: count
|
||||
}, '获取牲畜类型列表成功'));
|
||||
} catch (error) {
|
||||
console.error('获取牲畜类型列表错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取牲畜类型列表失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取所有启用的牲畜类型(用于下拉选择)
|
||||
const getActiveLivestockTypes = async (req, res) => {
|
||||
try {
|
||||
const types = await LivestockType.findAll({
|
||||
where: { is_active: true },
|
||||
attributes: ['id', 'name', 'description', 'base_value', 'premium_rate'],
|
||||
order: [['name', 'ASC']]
|
||||
});
|
||||
|
||||
res.json(responseFormat.success(types, '获取启用牲畜类型成功'));
|
||||
} catch (error) {
|
||||
console.error('获取启用牲畜类型错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取启用牲畜类型失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 创建牲畜类型
|
||||
const createLivestockType = async (req, res) => {
|
||||
try {
|
||||
const typeData = req.body;
|
||||
|
||||
// 检查名称是否已存在
|
||||
const existingType = await LivestockType.findOne({
|
||||
where: { name: typeData.name }
|
||||
});
|
||||
|
||||
if (existingType) {
|
||||
return res.status(400).json(responseFormat.error('牲畜类型名称已存在'));
|
||||
}
|
||||
|
||||
const type = await LivestockType.create({
|
||||
...typeData,
|
||||
created_by: req.user?.id
|
||||
});
|
||||
|
||||
res.status(201).json(responseFormat.created(type, '牲畜类型创建成功'));
|
||||
} catch (error) {
|
||||
console.error('创建牲畜类型错误:', error);
|
||||
if (error.name === 'SequelizeValidationError') {
|
||||
const messages = error.errors.map(err => err.message);
|
||||
return res.status(400).json(responseFormat.error(messages.join(', ')));
|
||||
}
|
||||
res.status(500).json(responseFormat.error('创建牲畜类型失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取单个牲畜类型详情
|
||||
const getLivestockTypeById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const type = await LivestockType.findByPk(id);
|
||||
|
||||
if (!type) {
|
||||
return res.status(404).json(responseFormat.error('牲畜类型不存在'));
|
||||
}
|
||||
|
||||
res.json(responseFormat.success(type, '获取牲畜类型详情成功'));
|
||||
} catch (error) {
|
||||
console.error('获取牲畜类型详情错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取牲畜类型详情失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新牲畜类型
|
||||
const updateLivestockType = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const updateData = req.body;
|
||||
|
||||
const type = await LivestockType.findByPk(id);
|
||||
if (!type) {
|
||||
return res.status(404).json(responseFormat.error('牲畜类型不存在'));
|
||||
}
|
||||
|
||||
// 如果更新名称,检查是否与其他记录重复
|
||||
if (updateData.name && updateData.name !== type.name) {
|
||||
const existingType = await LivestockType.findOne({
|
||||
where: {
|
||||
name: updateData.name,
|
||||
id: { [Op.ne]: id }
|
||||
}
|
||||
});
|
||||
|
||||
if (existingType) {
|
||||
return res.status(400).json(responseFormat.error('牲畜类型名称已存在'));
|
||||
}
|
||||
}
|
||||
|
||||
updateData.updated_by = req.user?.id;
|
||||
|
||||
await type.update(updateData);
|
||||
|
||||
res.json(responseFormat.success(type, '牲畜类型更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新牲畜类型错误:', error);
|
||||
if (error.name === 'SequelizeValidationError') {
|
||||
const messages = error.errors.map(err => err.message);
|
||||
return res.status(400).json(responseFormat.error(messages.join(', ')));
|
||||
}
|
||||
res.status(500).json(responseFormat.error('更新牲畜类型失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 删除牲畜类型(软删除 - 设置为不启用)
|
||||
const deleteLivestockType = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const type = await LivestockType.findByPk(id);
|
||||
if (!type) {
|
||||
return res.status(404).json(responseFormat.error('牲畜类型不存在'));
|
||||
}
|
||||
|
||||
// 检查是否有关联的保单
|
||||
const LivestockPolicy = require('../models/LivestockPolicy');
|
||||
const relatedPolicies = await LivestockPolicy.count({
|
||||
where: { livestock_type_id: id }
|
||||
});
|
||||
|
||||
if (relatedPolicies > 0) {
|
||||
// 如果有关联保单,只能设置为不启用
|
||||
await type.update({
|
||||
is_active: false,
|
||||
updated_by: req.user?.id
|
||||
});
|
||||
return res.json(responseFormat.success(null, '牲畜类型已设置为不启用(存在关联保单)'));
|
||||
}
|
||||
|
||||
// 如果没有关联保单,可以物理删除
|
||||
await type.destroy();
|
||||
|
||||
res.json(responseFormat.success(null, '牲畜类型删除成功'));
|
||||
} catch (error) {
|
||||
console.error('删除牲畜类型错误:', error);
|
||||
res.status(500).json(responseFormat.error('删除牲畜类型失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 批量更新牲畜类型状态
|
||||
const batchUpdateLivestockTypeStatus = async (req, res) => {
|
||||
try {
|
||||
const { ids, is_active } = req.body;
|
||||
|
||||
if (!Array.isArray(ids) || ids.length === 0) {
|
||||
return res.status(400).json(responseFormat.error('请提供有效的ID列表'));
|
||||
}
|
||||
|
||||
await LivestockType.update(
|
||||
{
|
||||
is_active,
|
||||
updated_by: req.user?.id
|
||||
},
|
||||
{
|
||||
where: { id: { [Op.in]: ids } }
|
||||
}
|
||||
);
|
||||
|
||||
res.json(responseFormat.success(null, '批量更新牲畜类型状态成功'));
|
||||
} catch (error) {
|
||||
console.error('批量更新牲畜类型状态错误:', error);
|
||||
res.status(500).json(responseFormat.error('批量更新牲畜类型状态失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getLivestockTypes,
|
||||
getActiveLivestockTypes,
|
||||
createLivestockType,
|
||||
getLivestockTypeById,
|
||||
updateLivestockType,
|
||||
deleteLivestockType,
|
||||
batchUpdateLivestockTypeStatus
|
||||
};
|
||||
@@ -0,0 +1,603 @@
|
||||
const { sequelize } = require('../config/database');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
/**
|
||||
* 监管任务结项控制器
|
||||
* 处理监管任务结项相关的业务逻辑
|
||||
*/
|
||||
|
||||
// 获取监管任务结项列表
|
||||
const getTaskCompletions = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
application_no, // 申请编号
|
||||
policy_no, // 保单号
|
||||
product_name, // 产品名称
|
||||
customer_name, // 客户名称
|
||||
completion_date, // 结项日期
|
||||
status, // 状态
|
||||
reviewer_name, // 审核人员
|
||||
page = 1,
|
||||
limit = 10
|
||||
} = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
let whereClause = '';
|
||||
const params = [];
|
||||
|
||||
if (application_no) {
|
||||
whereClause += ' AND rtc.application_no LIKE ?';
|
||||
params.push(`%${application_no}%`);
|
||||
}
|
||||
|
||||
if (policy_no) {
|
||||
whereClause += ' AND rtc.policy_no LIKE ?';
|
||||
params.push(`%${policy_no}%`);
|
||||
}
|
||||
|
||||
if (product_name) {
|
||||
whereClause += ' AND rtc.product_name LIKE ?';
|
||||
params.push(`%${product_name}%`);
|
||||
}
|
||||
|
||||
if (customer_name) {
|
||||
whereClause += ' AND rtc.customer_name LIKE ?';
|
||||
params.push(`%${customer_name}%`);
|
||||
}
|
||||
|
||||
if (completion_date) {
|
||||
whereClause += ' AND DATE(rtc.completion_date) = ?';
|
||||
params.push(completion_date);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
whereClause += ' AND rtc.status = ?';
|
||||
params.push(status);
|
||||
}
|
||||
|
||||
if (reviewer_name) {
|
||||
whereClause += ' AND rtc.reviewer_name LIKE ?';
|
||||
params.push(`%${reviewer_name}%`);
|
||||
}
|
||||
|
||||
// 计算偏移量
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// 查询总数
|
||||
const countQuery = `
|
||||
SELECT COUNT(*) as total
|
||||
FROM regulatory_task_completions rtc
|
||||
WHERE 1=1 ${whereClause}
|
||||
`;
|
||||
|
||||
const [countResult] = await sequelize.query(countQuery, {
|
||||
replacements: params,
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
});
|
||||
|
||||
// 查询数据
|
||||
const dataQuery = `
|
||||
SELECT
|
||||
rtc.id,
|
||||
rtc.application_no,
|
||||
rtc.policy_no,
|
||||
rtc.product_name,
|
||||
rtc.customer_name,
|
||||
rtc.customer_phone,
|
||||
rtc.insurance_amount,
|
||||
rtc.premium_amount,
|
||||
rtc.start_date,
|
||||
rtc.end_date,
|
||||
rtc.completion_date,
|
||||
rtc.status,
|
||||
rtc.reviewer_name,
|
||||
rtc.review_comments,
|
||||
rtc.created_at,
|
||||
rtc.updated_at
|
||||
FROM regulatory_task_completions rtc
|
||||
WHERE 1=1 ${whereClause}
|
||||
ORDER BY rtc.created_at DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`;
|
||||
|
||||
const results = await sequelize.query(dataQuery, {
|
||||
replacements: [...params, parseInt(limit), offset],
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
});
|
||||
|
||||
res.json(responseFormat.success({
|
||||
list: results,
|
||||
pagination: {
|
||||
current: parseInt(page),
|
||||
pageSize: parseInt(limit),
|
||||
total: countResult.total,
|
||||
totalPages: Math.ceil(countResult.total / limit)
|
||||
}
|
||||
}));
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取监管任务结项列表失败:', error);
|
||||
res.status(500).json(responseFormat.error('获取监管任务结项列表失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取单个监管任务结项详情
|
||||
const getTaskCompletionById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const query = `
|
||||
SELECT
|
||||
rtc.*,
|
||||
(
|
||||
SELECT JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
'id', rtca.id,
|
||||
'file_name', rtca.file_name,
|
||||
'file_path', rtca.file_path,
|
||||
'file_size', rtca.file_size,
|
||||
'file_type', rtca.file_type,
|
||||
'upload_time', rtca.upload_time
|
||||
)
|
||||
)
|
||||
FROM regulatory_task_completion_attachments rtca
|
||||
WHERE rtca.completion_id = rtc.id
|
||||
) as attachments,
|
||||
(
|
||||
SELECT JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
'id', rtcl.id,
|
||||
'operation_type', rtcl.operation_type,
|
||||
'operation_description', rtcl.operation_description,
|
||||
'operator_name', rtcl.operator_name,
|
||||
'operation_time', rtcl.operation_time,
|
||||
'ip_address', rtcl.ip_address
|
||||
)
|
||||
)
|
||||
FROM regulatory_task_completion_logs rtcl
|
||||
WHERE rtcl.completion_id = rtc.id
|
||||
ORDER BY rtcl.operation_time DESC
|
||||
) as operation_logs
|
||||
FROM regulatory_task_completions rtc
|
||||
WHERE rtc.id = ?
|
||||
`;
|
||||
|
||||
const [result] = await sequelize.query(query, {
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return res.status(404).json(responseFormat.error('监管任务结项记录不存在'));
|
||||
}
|
||||
|
||||
// 解析JSON字段
|
||||
if (result.attachments) {
|
||||
result.attachments = JSON.parse(result.attachments) || [];
|
||||
} else {
|
||||
result.attachments = [];
|
||||
}
|
||||
|
||||
if (result.operation_logs) {
|
||||
result.operation_logs = JSON.parse(result.operation_logs) || [];
|
||||
} else {
|
||||
result.operation_logs = [];
|
||||
}
|
||||
|
||||
res.json(responseFormat.success(result));
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取监管任务结项详情失败:', error);
|
||||
res.status(500).json(responseFormat.error('获取监管任务结项详情失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 创建监管任务结项记录
|
||||
const createTaskCompletion = async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
const {
|
||||
application_no,
|
||||
policy_no,
|
||||
product_name,
|
||||
customer_name,
|
||||
customer_phone,
|
||||
insurance_amount,
|
||||
premium_amount,
|
||||
start_date,
|
||||
end_date,
|
||||
completion_date,
|
||||
status = 'pending',
|
||||
review_comments,
|
||||
attachments = []
|
||||
} = req.body;
|
||||
|
||||
// 验证必填字段
|
||||
if (!application_no || !policy_no || !product_name || !customer_name) {
|
||||
return res.status(400).json(responseFormat.error('申请编号、保单号、产品名称、客户名称为必填项'));
|
||||
}
|
||||
|
||||
// 检查申请编号是否已存在
|
||||
const existingQuery = `
|
||||
SELECT id FROM regulatory_task_completions
|
||||
WHERE application_no = ? OR policy_no = ?
|
||||
`;
|
||||
|
||||
const [existing] = await sequelize.query(existingQuery, {
|
||||
replacements: [application_no, policy_no],
|
||||
type: sequelize.QueryTypes.SELECT,
|
||||
transaction
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json(responseFormat.error('申请编号或保单号已存在'));
|
||||
}
|
||||
|
||||
// 创建监管任务结项记录
|
||||
const insertQuery = `
|
||||
INSERT INTO regulatory_task_completions (
|
||||
application_no, policy_no, product_name, customer_name, customer_phone,
|
||||
insurance_amount, premium_amount, start_date, end_date, completion_date,
|
||||
status, review_comments, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
|
||||
`;
|
||||
|
||||
const [insertResult] = await sequelize.query(insertQuery, {
|
||||
replacements: [
|
||||
application_no, policy_no, product_name, customer_name, customer_phone,
|
||||
insurance_amount, premium_amount, start_date, end_date, completion_date,
|
||||
status, review_comments
|
||||
],
|
||||
type: sequelize.QueryTypes.INSERT,
|
||||
transaction
|
||||
});
|
||||
|
||||
const completionId = insertResult;
|
||||
|
||||
// 创建附件记录
|
||||
if (attachments && attachments.length > 0) {
|
||||
for (const attachment of attachments) {
|
||||
const attachmentQuery = `
|
||||
INSERT INTO regulatory_task_completion_attachments (
|
||||
completion_id, file_name, file_path, file_size, file_type, upload_time
|
||||
) VALUES (?, ?, ?, ?, ?, NOW())
|
||||
`;
|
||||
|
||||
await sequelize.query(attachmentQuery, {
|
||||
replacements: [
|
||||
completionId,
|
||||
attachment.file_name,
|
||||
attachment.file_path,
|
||||
attachment.file_size,
|
||||
attachment.file_type
|
||||
],
|
||||
type: sequelize.QueryTypes.INSERT,
|
||||
transaction
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
const logQuery = `
|
||||
INSERT INTO regulatory_task_completion_logs (
|
||||
completion_id, operation_type, operation_description,
|
||||
operator_name, operation_time, ip_address
|
||||
) VALUES (?, ?, ?, ?, NOW(), ?)
|
||||
`;
|
||||
|
||||
await sequelize.query(logQuery, {
|
||||
replacements: [
|
||||
completionId,
|
||||
'create',
|
||||
'创建监管任务结项记录',
|
||||
req.user?.real_name || '系统',
|
||||
req.ip
|
||||
],
|
||||
type: sequelize.QueryTypes.INSERT,
|
||||
transaction
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.status(201).json(responseFormat.success({
|
||||
id: completionId,
|
||||
message: '监管任务结项记录创建成功'
|
||||
}));
|
||||
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('创建监管任务结项记录失败:', error);
|
||||
res.status(500).json(responseFormat.error('创建监管任务结项记录失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新监管任务结项记录
|
||||
const updateTaskCompletion = async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const {
|
||||
application_no,
|
||||
policy_no,
|
||||
product_name,
|
||||
customer_name,
|
||||
customer_phone,
|
||||
insurance_amount,
|
||||
premium_amount,
|
||||
start_date,
|
||||
end_date,
|
||||
completion_date,
|
||||
status,
|
||||
review_comments
|
||||
} = req.body;
|
||||
|
||||
// 检查记录是否存在
|
||||
const checkQuery = `SELECT id FROM regulatory_task_completions WHERE id = ?`;
|
||||
const [existing] = await sequelize.query(checkQuery, {
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.SELECT,
|
||||
transaction
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
await transaction.rollback();
|
||||
return res.status(404).json(responseFormat.error('监管任务结项记录不存在'));
|
||||
}
|
||||
|
||||
// 更新记录
|
||||
const updateQuery = `
|
||||
UPDATE regulatory_task_completions SET
|
||||
application_no = ?, policy_no = ?, product_name = ?, customer_name = ?,
|
||||
customer_phone = ?, insurance_amount = ?, premium_amount = ?,
|
||||
start_date = ?, end_date = ?, completion_date = ?, status = ?,
|
||||
review_comments = ?, updated_at = NOW()
|
||||
WHERE id = ?
|
||||
`;
|
||||
|
||||
await sequelize.query(updateQuery, {
|
||||
replacements: [
|
||||
application_no, policy_no, product_name, customer_name, customer_phone,
|
||||
insurance_amount, premium_amount, start_date, end_date, completion_date,
|
||||
status, review_comments, id
|
||||
],
|
||||
type: sequelize.QueryTypes.UPDATE,
|
||||
transaction
|
||||
});
|
||||
|
||||
// 记录操作日志
|
||||
const logQuery = `
|
||||
INSERT INTO regulatory_task_completion_logs (
|
||||
completion_id, operation_type, operation_description,
|
||||
operator_name, operation_time, ip_address
|
||||
) VALUES (?, ?, ?, ?, NOW(), ?)
|
||||
`;
|
||||
|
||||
await sequelize.query(logQuery, {
|
||||
replacements: [
|
||||
id,
|
||||
'update',
|
||||
'更新监管任务结项记录',
|
||||
req.user?.real_name || '系统',
|
||||
req.ip
|
||||
],
|
||||
type: sequelize.QueryTypes.INSERT,
|
||||
transaction
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.json(responseFormat.success({ message: '监管任务结项记录更新成功' }));
|
||||
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('更新监管任务结项记录失败:', error);
|
||||
res.status(500).json(responseFormat.error('更新监管任务结项记录失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 删除监管任务结项记录
|
||||
const deleteTaskCompletion = async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
// 检查记录是否存在
|
||||
const checkQuery = `SELECT id FROM regulatory_task_completions WHERE id = ?`;
|
||||
const [existing] = await sequelize.query(checkQuery, {
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.SELECT,
|
||||
transaction
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
await transaction.rollback();
|
||||
return res.status(404).json(responseFormat.error('监管任务结项记录不存在'));
|
||||
}
|
||||
|
||||
// 删除相关附件记录
|
||||
await sequelize.query(
|
||||
'DELETE FROM regulatory_task_completion_attachments WHERE completion_id = ?',
|
||||
{
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.DELETE,
|
||||
transaction
|
||||
}
|
||||
);
|
||||
|
||||
// 删除相关日志记录
|
||||
await sequelize.query(
|
||||
'DELETE FROM regulatory_task_completion_logs WHERE completion_id = ?',
|
||||
{
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.DELETE,
|
||||
transaction
|
||||
}
|
||||
);
|
||||
|
||||
// 删除主记录
|
||||
await sequelize.query(
|
||||
'DELETE FROM regulatory_task_completions WHERE id = ?',
|
||||
{
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.DELETE,
|
||||
transaction
|
||||
}
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.json(responseFormat.success({ message: '监管任务结项记录删除成功' }));
|
||||
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('删除监管任务结项记录失败:', error);
|
||||
res.status(500).json(responseFormat.error('删除监管任务结项记录失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 审核监管任务结项记录
|
||||
const reviewTaskCompletion = async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { status, review_comments, reviewer_name } = req.body;
|
||||
|
||||
// 验证状态值
|
||||
const validStatuses = ['pending', 'approved', 'rejected', 'completed'];
|
||||
if (!validStatuses.includes(status)) {
|
||||
return res.status(400).json(responseFormat.error('无效的状态值'));
|
||||
}
|
||||
|
||||
// 检查记录是否存在
|
||||
const checkQuery = `SELECT id, status FROM regulatory_task_completions WHERE id = ?`;
|
||||
const [existing] = await sequelize.query(checkQuery, {
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.SELECT,
|
||||
transaction
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
await transaction.rollback();
|
||||
return res.status(404).json(responseFormat.error('监管任务结项记录不存在'));
|
||||
}
|
||||
|
||||
// 更新审核状态
|
||||
const updateQuery = `
|
||||
UPDATE regulatory_task_completions SET
|
||||
status = ?, review_comments = ?, reviewer_name = ?, updated_at = NOW()
|
||||
WHERE id = ?
|
||||
`;
|
||||
|
||||
await sequelize.query(updateQuery, {
|
||||
replacements: [status, review_comments, reviewer_name, id],
|
||||
type: sequelize.QueryTypes.UPDATE,
|
||||
transaction
|
||||
});
|
||||
|
||||
// 记录操作日志
|
||||
const logQuery = `
|
||||
INSERT INTO regulatory_task_completion_logs (
|
||||
completion_id, operation_type, operation_description,
|
||||
operator_name, operation_time, ip_address
|
||||
) VALUES (?, ?, ?, ?, NOW(), ?)
|
||||
`;
|
||||
|
||||
const operationDesc = `审核监管任务结项记录,状态变更为:${status}`;
|
||||
await sequelize.query(logQuery, {
|
||||
replacements: [
|
||||
id,
|
||||
'review',
|
||||
operationDesc,
|
||||
reviewer_name || req.user?.real_name || '系统',
|
||||
req.ip
|
||||
],
|
||||
type: sequelize.QueryTypes.INSERT,
|
||||
transaction
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.json(responseFormat.success({ message: '监管任务结项记录审核成功' }));
|
||||
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('审核监管任务结项记录失败:', error);
|
||||
res.status(500).json(responseFormat.error('审核监管任务结项记录失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取监管任务结项统计数据
|
||||
const getTaskCompletionStats = async (req, res) => {
|
||||
try {
|
||||
// 状态统计
|
||||
const statusStatsQuery = `
|
||||
SELECT
|
||||
status,
|
||||
COUNT(*) as count
|
||||
FROM regulatory_task_completions
|
||||
GROUP BY status
|
||||
`;
|
||||
|
||||
const statusStats = await sequelize.query(statusStatsQuery, {
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
});
|
||||
|
||||
// 月度统计
|
||||
const monthlyStatsQuery = `
|
||||
SELECT
|
||||
DATE_FORMAT(completion_date, '%Y-%m') as month,
|
||||
COUNT(*) as count,
|
||||
SUM(insurance_amount) as total_amount
|
||||
FROM regulatory_task_completions
|
||||
WHERE completion_date >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
|
||||
GROUP BY DATE_FORMAT(completion_date, '%Y-%m')
|
||||
ORDER BY month
|
||||
`;
|
||||
|
||||
const monthlyStats = await sequelize.query(monthlyStatsQuery, {
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
});
|
||||
|
||||
// 产品统计
|
||||
const productStatsQuery = `
|
||||
SELECT
|
||||
product_name,
|
||||
COUNT(*) as count,
|
||||
SUM(insurance_amount) as total_amount
|
||||
FROM regulatory_task_completions
|
||||
GROUP BY product_name
|
||||
ORDER BY count DESC
|
||||
LIMIT 10
|
||||
`;
|
||||
|
||||
const productStats = await sequelize.query(productStatsQuery, {
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
});
|
||||
|
||||
res.json(responseFormat.success({
|
||||
statusStats,
|
||||
monthlyStats,
|
||||
productStats
|
||||
}));
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取监管任务结项统计数据失败:', error);
|
||||
res.status(500).json(responseFormat.error('获取监管任务结项统计数据失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getTaskCompletions,
|
||||
getTaskCompletionById,
|
||||
createTaskCompletion,
|
||||
updateTaskCompletion,
|
||||
deleteTaskCompletion,
|
||||
reviewTaskCompletion,
|
||||
getTaskCompletionStats
|
||||
};
|
||||
@@ -119,8 +119,8 @@ async function createAdminUser() {
|
||||
|
||||
// 创建admin用户
|
||||
await sequelize.query(
|
||||
`INSERT INTO users (username, password, real_name, email, phone, role_id, status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||
`INSERT INTO users (username, password, real_name, email, phone, role_id, status, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`,
|
||||
{
|
||||
replacements: [
|
||||
'admin',
|
||||
|
||||
755
insurance_backend/docs/insurance-application-api.yaml
Normal file
755
insurance_backend/docs/insurance-application-api.yaml
Normal file
@@ -0,0 +1,755 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: 参保申请管理API
|
||||
description: 保险系统参保申请功能相关API接口文档
|
||||
version: 1.0.0
|
||||
contact:
|
||||
name: 开发团队
|
||||
email: dev@example.com
|
||||
|
||||
servers:
|
||||
- url: http://localhost:3000/api
|
||||
description: 开发环境
|
||||
|
||||
paths:
|
||||
/insurance/applications:
|
||||
get:
|
||||
summary: 获取参保申请列表
|
||||
description: 分页获取参保申请列表,支持多种筛选条件
|
||||
tags:
|
||||
- 参保申请
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: page
|
||||
in: query
|
||||
description: 页码
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 1
|
||||
minimum: 1
|
||||
- name: limit
|
||||
in: query
|
||||
description: 每页数量
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 10
|
||||
minimum: 1
|
||||
maximum: 100
|
||||
- name: application_no
|
||||
in: query
|
||||
description: 申请单号(模糊搜索)
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- name: name
|
||||
in: query
|
||||
description: 投保人姓名(模糊搜索)
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- name: status
|
||||
in: query
|
||||
description: 申请状态
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
enum: [pending, initial_approved, under_review, approved, rejected, paid]
|
||||
- name: insurance_type_name
|
||||
in: query
|
||||
description: 参保险种名称(模糊搜索)
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- name: insurance_category
|
||||
in: query
|
||||
description: 参保类型(模糊搜索)
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- name: dateRange[start]
|
||||
in: query
|
||||
description: 申请时间范围开始
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
format: date
|
||||
- name: dateRange[end]
|
||||
in: query
|
||||
description: 申请时间范围结束
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
format: date
|
||||
responses:
|
||||
'200':
|
||||
description: 获取成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: true
|
||||
message:
|
||||
type: string
|
||||
example: "获取保险申请列表成功"
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/InsuranceApplication'
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
post:
|
||||
summary: 创建参保申请
|
||||
description: 创建新的参保申请
|
||||
tags:
|
||||
- 参保申请
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateInsuranceApplicationRequest'
|
||||
responses:
|
||||
'201':
|
||||
description: 创建成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: true
|
||||
message:
|
||||
type: string
|
||||
example: "保险申请创建成功"
|
||||
data:
|
||||
$ref: '#/components/schemas/InsuranceApplication'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
/insurance/applications/{id}:
|
||||
get:
|
||||
summary: 获取参保申请详情
|
||||
description: 根据ID获取单个参保申请的详细信息
|
||||
tags:
|
||||
- 参保申请
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 申请ID
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: 获取成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: true
|
||||
message:
|
||||
type: string
|
||||
example: "获取保险申请详情成功"
|
||||
data:
|
||||
$ref: '#/components/schemas/InsuranceApplication'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
put:
|
||||
summary: 更新参保申请
|
||||
description: 更新参保申请信息
|
||||
tags:
|
||||
- 参保申请
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 申请ID
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateInsuranceApplicationRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 更新成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: true
|
||||
message:
|
||||
type: string
|
||||
example: "保险申请更新成功"
|
||||
data:
|
||||
$ref: '#/components/schemas/InsuranceApplication'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
delete:
|
||||
summary: 删除参保申请
|
||||
description: 删除指定的参保申请
|
||||
tags:
|
||||
- 参保申请
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 申请ID
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: 删除成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: true
|
||||
message:
|
||||
type: string
|
||||
example: "保险申请删除成功"
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
/insurance/applications/{id}/review:
|
||||
patch:
|
||||
summary: 审核参保申请
|
||||
description: 对参保申请进行审核操作
|
||||
tags:
|
||||
- 参保申请
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 申请ID
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ReviewApplicationRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 审核成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: true
|
||||
message:
|
||||
type: string
|
||||
example: "保险申请审核成功"
|
||||
data:
|
||||
$ref: '#/components/schemas/InsuranceApplication'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
/insurance/applications-stats:
|
||||
get:
|
||||
summary: 获取参保申请统计
|
||||
description: 获取各状态的申请数量统计
|
||||
tags:
|
||||
- 参保申请
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: 获取成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: true
|
||||
message:
|
||||
type: string
|
||||
example: "获取保险申请统计成功"
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
pending:
|
||||
type: integer
|
||||
description: 待初审数量
|
||||
example: 10
|
||||
initial_approved:
|
||||
type: integer
|
||||
description: 初审通过待复核数量
|
||||
example: 5
|
||||
under_review:
|
||||
type: integer
|
||||
description: 已支付待复核数量
|
||||
example: 3
|
||||
approved:
|
||||
type: integer
|
||||
description: 已支付数量
|
||||
example: 20
|
||||
rejected:
|
||||
type: integer
|
||||
description: 已拒绝数量
|
||||
example: 2
|
||||
paid:
|
||||
type: integer
|
||||
description: 已支付数量
|
||||
example: 15
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
/insurance/categories:
|
||||
get:
|
||||
summary: 获取参保类型选项
|
||||
description: 获取可选的参保类型列表
|
||||
tags:
|
||||
- 参保申请
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: 获取成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: true
|
||||
message:
|
||||
type: string
|
||||
example: "获取参保类型选项成功"
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ["牛", "羊", "猪", "鸡", "鸭", "鹅"]
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
|
||||
schemas:
|
||||
InsuranceApplication:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: 申请ID
|
||||
example: 1
|
||||
application_no:
|
||||
type: string
|
||||
description: 申请单号
|
||||
example: "20230814205416141"
|
||||
customer_name:
|
||||
type: string
|
||||
description: 投保人姓名
|
||||
example: "张三"
|
||||
customer_id_card:
|
||||
type: string
|
||||
description: 身份证号(脱敏显示)
|
||||
example: "420***********0091"
|
||||
customer_phone:
|
||||
type: string
|
||||
description: 电话(脱敏显示)
|
||||
example: "186****2563"
|
||||
customer_address:
|
||||
type: string
|
||||
description: 参保地址
|
||||
example: "哈哈哈哈哈哈"
|
||||
insurance_type_id:
|
||||
type: integer
|
||||
description: 保险类型ID
|
||||
example: 1
|
||||
insurance_category:
|
||||
type: string
|
||||
description: 参保类型
|
||||
example: "牛"
|
||||
application_quantity:
|
||||
type: integer
|
||||
description: 申请数量
|
||||
example: 50
|
||||
application_amount:
|
||||
type: number
|
||||
format: decimal
|
||||
description: 申请金额
|
||||
example: 10000.00
|
||||
status:
|
||||
type: string
|
||||
enum: [pending, initial_approved, under_review, approved, rejected, paid]
|
||||
description: 申请状态
|
||||
example: "pending"
|
||||
remarks:
|
||||
type: string
|
||||
description: 备注
|
||||
example: "无"
|
||||
application_date:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 申请时间
|
||||
example: "2023-08-14T20:54:16.000Z"
|
||||
review_notes:
|
||||
type: string
|
||||
description: 审核备注
|
||||
example: null
|
||||
reviewer_id:
|
||||
type: integer
|
||||
description: 审核人ID
|
||||
example: null
|
||||
review_date:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 审核时间
|
||||
example: null
|
||||
documents:
|
||||
type: array
|
||||
description: 相关文档
|
||||
items:
|
||||
type: string
|
||||
example: []
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 创建时间
|
||||
example: "2023-08-14T20:54:16.000Z"
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 更新时间
|
||||
example: "2023-08-14T20:54:16.000Z"
|
||||
insurance_type:
|
||||
$ref: '#/components/schemas/InsuranceType'
|
||||
reviewer:
|
||||
$ref: '#/components/schemas/User'
|
||||
|
||||
InsuranceType:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: 保险类型ID
|
||||
example: 1
|
||||
name:
|
||||
type: string
|
||||
description: 保险类型名称
|
||||
example: "保险清示产品"
|
||||
description:
|
||||
type: string
|
||||
description: 保险类型描述
|
||||
example: "保险产品描述"
|
||||
|
||||
User:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: 用户ID
|
||||
example: 1
|
||||
real_name:
|
||||
type: string
|
||||
description: 真实姓名
|
||||
example: "李四"
|
||||
username:
|
||||
type: string
|
||||
description: 用户名
|
||||
example: "admin"
|
||||
|
||||
CreateInsuranceApplicationRequest:
|
||||
type: object
|
||||
required:
|
||||
- customer_name
|
||||
- customer_id_card
|
||||
- customer_phone
|
||||
- customer_address
|
||||
- insurance_type_id
|
||||
properties:
|
||||
customer_name:
|
||||
type: string
|
||||
description: 投保人姓名
|
||||
example: "张三"
|
||||
minLength: 2
|
||||
maxLength: 100
|
||||
customer_id_card:
|
||||
type: string
|
||||
description: 身份证号
|
||||
example: "420106199001010091"
|
||||
pattern: '^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[0-9Xx]$'
|
||||
customer_phone:
|
||||
type: string
|
||||
description: 电话号码
|
||||
example: "18612345678"
|
||||
pattern: '^1[3-9]\d{9}$'
|
||||
customer_address:
|
||||
type: string
|
||||
description: 参保地址
|
||||
example: "北京市北京市北京市测试区"
|
||||
minLength: 5
|
||||
maxLength: 255
|
||||
insurance_type_id:
|
||||
type: integer
|
||||
description: 保险类型ID
|
||||
example: 1
|
||||
insurance_category:
|
||||
type: string
|
||||
description: 参保类型
|
||||
example: "牛"
|
||||
maxLength: 50
|
||||
application_quantity:
|
||||
type: integer
|
||||
description: 申请数量
|
||||
example: 50
|
||||
minimum: 1
|
||||
application_amount:
|
||||
type: number
|
||||
format: decimal
|
||||
description: 申请金额
|
||||
example: 10000.00
|
||||
minimum: 0
|
||||
remarks:
|
||||
type: string
|
||||
description: 备注
|
||||
example: "特殊说明"
|
||||
|
||||
UpdateInsuranceApplicationRequest:
|
||||
type: object
|
||||
properties:
|
||||
customer_name:
|
||||
type: string
|
||||
description: 投保人姓名
|
||||
example: "张三"
|
||||
minLength: 2
|
||||
maxLength: 100
|
||||
customer_id_card:
|
||||
type: string
|
||||
description: 身份证号
|
||||
example: "420106199001010091"
|
||||
pattern: '^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[0-9Xx]$'
|
||||
customer_phone:
|
||||
type: string
|
||||
description: 电话号码
|
||||
example: "18612345678"
|
||||
pattern: '^1[3-9]\d{9}$'
|
||||
customer_address:
|
||||
type: string
|
||||
description: 参保地址
|
||||
example: "北京市北京市北京市测试区"
|
||||
minLength: 5
|
||||
maxLength: 255
|
||||
insurance_type_id:
|
||||
type: integer
|
||||
description: 保险类型ID
|
||||
example: 1
|
||||
insurance_category:
|
||||
type: string
|
||||
description: 参保类型
|
||||
example: "牛"
|
||||
maxLength: 50
|
||||
application_quantity:
|
||||
type: integer
|
||||
description: 申请数量
|
||||
example: 50
|
||||
minimum: 1
|
||||
application_amount:
|
||||
type: number
|
||||
format: decimal
|
||||
description: 申请金额
|
||||
example: 10000.00
|
||||
minimum: 0
|
||||
remarks:
|
||||
type: string
|
||||
description: 备注
|
||||
example: "特殊说明"
|
||||
|
||||
ReviewApplicationRequest:
|
||||
type: object
|
||||
required:
|
||||
- status
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum: [initial_approved, under_review, approved, rejected, paid]
|
||||
description: 审核后的状态
|
||||
example: "initial_approved"
|
||||
review_notes:
|
||||
type: string
|
||||
description: 审核备注
|
||||
example: "审核通过"
|
||||
|
||||
Pagination:
|
||||
type: object
|
||||
properties:
|
||||
page:
|
||||
type: integer
|
||||
description: 当前页码
|
||||
example: 1
|
||||
limit:
|
||||
type: integer
|
||||
description: 每页数量
|
||||
example: 10
|
||||
total:
|
||||
type: integer
|
||||
description: 总记录数
|
||||
example: 100
|
||||
totalPages:
|
||||
type: integer
|
||||
description: 总页数
|
||||
example: 10
|
||||
|
||||
responses:
|
||||
BadRequest:
|
||||
description: 请求参数错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
message:
|
||||
type: string
|
||||
example: "请求参数错误"
|
||||
|
||||
Unauthorized:
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
message:
|
||||
type: string
|
||||
example: "未授权访问"
|
||||
|
||||
Forbidden:
|
||||
description: 权限不足
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
message:
|
||||
type: string
|
||||
example: "权限不足"
|
||||
|
||||
NotFound:
|
||||
description: 资源不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
message:
|
||||
type: string
|
||||
example: "资源不存在"
|
||||
|
||||
InternalServerError:
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
message:
|
||||
type: string
|
||||
example: "服务器内部错误"
|
||||
869
insurance_backend/docs/swagger/regulatory-task-completion.yaml
Normal file
869
insurance_backend/docs/swagger/regulatory-task-completion.yaml
Normal file
@@ -0,0 +1,869 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: 监管任务结项API
|
||||
description: |
|
||||
保险端监管任务结项管理系统API接口文档
|
||||
|
||||
## 功能概述
|
||||
- 监管任务结项记录的增删改查
|
||||
- 任务审核流程管理
|
||||
- 附件上传和管理
|
||||
- 操作日志记录
|
||||
- 统计数据查询
|
||||
|
||||
## 认证方式
|
||||
使用JWT Bearer Token进行身份认证
|
||||
|
||||
## 响应格式
|
||||
所有API响应都遵循统一的格式:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {},
|
||||
"timestamp": "2025-01-19T10:00:00Z"
|
||||
}
|
||||
```
|
||||
version: 1.0.0
|
||||
contact:
|
||||
name: 开发团队
|
||||
email: dev@example.com
|
||||
license:
|
||||
name: MIT
|
||||
url: https://opensource.org/licenses/MIT
|
||||
|
||||
servers:
|
||||
- url: http://localhost:3000/api
|
||||
description: 开发环境
|
||||
- url: https://api.example.com/api
|
||||
description: 生产环境
|
||||
|
||||
security:
|
||||
- bearerAuth: []
|
||||
|
||||
paths:
|
||||
/regulatory-task-completion:
|
||||
get:
|
||||
tags:
|
||||
- 监管任务结项
|
||||
summary: 获取监管任务结项列表
|
||||
description: 分页查询监管任务结项记录,支持多条件筛选
|
||||
parameters:
|
||||
- name: application_no
|
||||
in: query
|
||||
description: 申请编号(模糊查询)
|
||||
schema:
|
||||
type: string
|
||||
example: "APP20250119"
|
||||
- name: policy_no
|
||||
in: query
|
||||
description: 保单号(模糊查询)
|
||||
schema:
|
||||
type: string
|
||||
example: "POL20250119"
|
||||
- name: product_name
|
||||
in: query
|
||||
description: 产品名称(模糊查询)
|
||||
schema:
|
||||
type: string
|
||||
example: "养殖保险"
|
||||
- name: customer_name
|
||||
in: query
|
||||
description: 客户名称(模糊查询)
|
||||
schema:
|
||||
type: string
|
||||
example: "张三"
|
||||
- name: completion_date
|
||||
in: query
|
||||
description: 结项日期
|
||||
schema:
|
||||
type: string
|
||||
format: date
|
||||
example: "2025-01-19"
|
||||
- name: status
|
||||
in: query
|
||||
description: 状态筛选
|
||||
schema:
|
||||
type: string
|
||||
enum: [pending, approved, rejected, completed]
|
||||
example: "pending"
|
||||
- name: reviewer_name
|
||||
in: query
|
||||
description: 审核人员(模糊查询)
|
||||
schema:
|
||||
type: string
|
||||
example: "审核员"
|
||||
- name: page
|
||||
in: query
|
||||
description: 页码
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
default: 1
|
||||
example: 1
|
||||
- name: limit
|
||||
in: query
|
||||
description: 每页数量
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 100
|
||||
default: 10
|
||||
example: 10
|
||||
responses:
|
||||
'200':
|
||||
description: 查询成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
example: 200
|
||||
message:
|
||||
type: string
|
||||
example: "success"
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
list:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/RegulatoryTaskCompletion'
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2025-01-19T10:00:00Z"
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
post:
|
||||
tags:
|
||||
- 监管任务结项
|
||||
summary: 创建监管任务结项记录
|
||||
description: 创建新的监管任务结项记录
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateRegulatoryTaskCompletionRequest'
|
||||
responses:
|
||||
'201':
|
||||
description: 创建成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
example: 201
|
||||
message:
|
||||
type: string
|
||||
example: "success"
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
example: 1
|
||||
message:
|
||||
type: string
|
||||
example: "监管任务结项记录创建成功"
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2025-01-19T10:00:00Z"
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
/regulatory-task-completion/{id}:
|
||||
get:
|
||||
tags:
|
||||
- 监管任务结项
|
||||
summary: 获取监管任务结项详情
|
||||
description: 根据ID获取监管任务结项记录详情,包含附件和操作日志
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 记录ID
|
||||
schema:
|
||||
type: integer
|
||||
example: 1
|
||||
responses:
|
||||
'200':
|
||||
description: 查询成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
example: 200
|
||||
message:
|
||||
type: string
|
||||
example: "success"
|
||||
data:
|
||||
$ref: '#/components/schemas/RegulatoryTaskCompletionDetail'
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2025-01-19T10:00:00Z"
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
put:
|
||||
tags:
|
||||
- 监管任务结项
|
||||
summary: 更新监管任务结项记录
|
||||
description: 更新指定ID的监管任务结项记录
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 记录ID
|
||||
schema:
|
||||
type: integer
|
||||
example: 1
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateRegulatoryTaskCompletionRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 更新成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
example: 200
|
||||
message:
|
||||
type: string
|
||||
example: "success"
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
example: "监管任务结项记录更新成功"
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2025-01-19T10:00:00Z"
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
delete:
|
||||
tags:
|
||||
- 监管任务结项
|
||||
summary: 删除监管任务结项记录
|
||||
description: 删除指定ID的监管任务结项记录及相关数据
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 记录ID
|
||||
schema:
|
||||
type: integer
|
||||
example: 1
|
||||
responses:
|
||||
'200':
|
||||
description: 删除成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
example: 200
|
||||
message:
|
||||
type: string
|
||||
example: "success"
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
example: "监管任务结项记录删除成功"
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2025-01-19T10:00:00Z"
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
/regulatory-task-completion/{id}/review:
|
||||
patch:
|
||||
tags:
|
||||
- 监管任务结项
|
||||
summary: 审核监管任务结项记录
|
||||
description: 对指定ID的监管任务结项记录进行审核
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: 记录ID
|
||||
schema:
|
||||
type: integer
|
||||
example: 1
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ReviewRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 审核成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
example: 200
|
||||
message:
|
||||
type: string
|
||||
example: "success"
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
example: "监管任务结项记录审核成功"
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2025-01-19T10:00:00Z"
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
/regulatory-task-completion/stats:
|
||||
get:
|
||||
tags:
|
||||
- 监管任务结项
|
||||
summary: 获取监管任务结项统计数据
|
||||
description: 获取监管任务结项的统计分析数据
|
||||
responses:
|
||||
'200':
|
||||
description: 查询成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
example: 200
|
||||
message:
|
||||
type: string
|
||||
example: "success"
|
||||
data:
|
||||
$ref: '#/components/schemas/StatisticsData'
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2025-01-19T10:00:00Z"
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
|
||||
schemas:
|
||||
RegulatoryTaskCompletion:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: 记录ID
|
||||
example: 1
|
||||
application_no:
|
||||
type: string
|
||||
description: 申请编号
|
||||
example: "APP20250119001"
|
||||
policy_no:
|
||||
type: string
|
||||
description: 保单号
|
||||
example: "POL20250119001"
|
||||
product_name:
|
||||
type: string
|
||||
description: 产品名称
|
||||
example: "养殖保险"
|
||||
customer_name:
|
||||
type: string
|
||||
description: 客户名称
|
||||
example: "张三"
|
||||
customer_phone:
|
||||
type: string
|
||||
description: 客户电话
|
||||
example: "13800138000"
|
||||
insurance_amount:
|
||||
type: number
|
||||
format: decimal
|
||||
description: 保险金额
|
||||
example: 100000.00
|
||||
premium_amount:
|
||||
type: number
|
||||
format: decimal
|
||||
description: 保费金额
|
||||
example: 5000.00
|
||||
start_date:
|
||||
type: string
|
||||
format: date
|
||||
description: 保险起期
|
||||
example: "2025-01-01"
|
||||
end_date:
|
||||
type: string
|
||||
format: date
|
||||
description: 保险止期
|
||||
example: "2025-12-31"
|
||||
completion_date:
|
||||
type: string
|
||||
format: date
|
||||
description: 结项日期
|
||||
example: "2025-01-19"
|
||||
status:
|
||||
type: string
|
||||
enum: [pending, approved, rejected, completed]
|
||||
description: 状态
|
||||
example: "pending"
|
||||
reviewer_name:
|
||||
type: string
|
||||
description: 审核人员
|
||||
example: "审核员张三"
|
||||
review_comments:
|
||||
type: string
|
||||
description: 审核意见
|
||||
example: "审核通过"
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 创建时间
|
||||
example: "2025-01-19T10:00:00Z"
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 更新时间
|
||||
example: "2025-01-19T10:00:00Z"
|
||||
|
||||
RegulatoryTaskCompletionDetail:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/RegulatoryTaskCompletion'
|
||||
- type: object
|
||||
properties:
|
||||
attachments:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Attachment'
|
||||
operation_logs:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/OperationLog'
|
||||
|
||||
Attachment:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: 附件ID
|
||||
example: 1
|
||||
file_name:
|
||||
type: string
|
||||
description: 文件名
|
||||
example: "保单文件.pdf"
|
||||
file_path:
|
||||
type: string
|
||||
description: 文件路径
|
||||
example: "/uploads/documents/policy_001.pdf"
|
||||
file_size:
|
||||
type: integer
|
||||
description: 文件大小(字节)
|
||||
example: 1024000
|
||||
file_type:
|
||||
type: string
|
||||
description: 文件类型
|
||||
example: "application/pdf"
|
||||
upload_time:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 上传时间
|
||||
example: "2025-01-19T10:00:00Z"
|
||||
|
||||
OperationLog:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
description: 日志ID
|
||||
example: 1
|
||||
operation_type:
|
||||
type: string
|
||||
description: 操作类型
|
||||
example: "create"
|
||||
operation_description:
|
||||
type: string
|
||||
description: 操作描述
|
||||
example: "创建监管任务结项记录"
|
||||
operator_name:
|
||||
type: string
|
||||
description: 操作人员
|
||||
example: "系统管理员"
|
||||
operation_time:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 操作时间
|
||||
example: "2025-01-19T10:00:00Z"
|
||||
ip_address:
|
||||
type: string
|
||||
description: IP地址
|
||||
example: "192.168.1.100"
|
||||
|
||||
CreateRegulatoryTaskCompletionRequest:
|
||||
type: object
|
||||
required:
|
||||
- application_no
|
||||
- policy_no
|
||||
- product_name
|
||||
- customer_name
|
||||
properties:
|
||||
application_no:
|
||||
type: string
|
||||
description: 申请编号
|
||||
example: "APP20250119002"
|
||||
policy_no:
|
||||
type: string
|
||||
description: 保单号
|
||||
example: "POL20250119002"
|
||||
product_name:
|
||||
type: string
|
||||
description: 产品名称
|
||||
example: "养殖保险"
|
||||
customer_name:
|
||||
type: string
|
||||
description: 客户名称
|
||||
example: "李四"
|
||||
customer_phone:
|
||||
type: string
|
||||
description: 客户电话
|
||||
example: "13800138001"
|
||||
insurance_amount:
|
||||
type: number
|
||||
format: decimal
|
||||
description: 保险金额
|
||||
example: 150000.00
|
||||
premium_amount:
|
||||
type: number
|
||||
format: decimal
|
||||
description: 保费金额
|
||||
example: 7500.00
|
||||
start_date:
|
||||
type: string
|
||||
format: date
|
||||
description: 保险起期
|
||||
example: "2025-01-01"
|
||||
end_date:
|
||||
type: string
|
||||
format: date
|
||||
description: 保险止期
|
||||
example: "2025-12-31"
|
||||
completion_date:
|
||||
type: string
|
||||
format: date
|
||||
description: 结项日期
|
||||
example: "2025-01-19"
|
||||
status:
|
||||
type: string
|
||||
enum: [pending, approved, rejected, completed]
|
||||
description: 状态
|
||||
default: "pending"
|
||||
example: "pending"
|
||||
review_comments:
|
||||
type: string
|
||||
description: 审核意见
|
||||
example: "待审核"
|
||||
attachments:
|
||||
type: array
|
||||
description: 附件列表
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
file_name:
|
||||
type: string
|
||||
example: "保单文件.pdf"
|
||||
file_path:
|
||||
type: string
|
||||
example: "/uploads/documents/policy_002.pdf"
|
||||
file_size:
|
||||
type: integer
|
||||
example: 1024000
|
||||
file_type:
|
||||
type: string
|
||||
example: "application/pdf"
|
||||
|
||||
UpdateRegulatoryTaskCompletionRequest:
|
||||
type: object
|
||||
properties:
|
||||
application_no:
|
||||
type: string
|
||||
description: 申请编号
|
||||
example: "APP20250119002"
|
||||
policy_no:
|
||||
type: string
|
||||
description: 保单号
|
||||
example: "POL20250119002"
|
||||
product_name:
|
||||
type: string
|
||||
description: 产品名称
|
||||
example: "养殖保险(更新)"
|
||||
customer_name:
|
||||
type: string
|
||||
description: 客户名称
|
||||
example: "李四"
|
||||
customer_phone:
|
||||
type: string
|
||||
description: 客户电话
|
||||
example: "13800138001"
|
||||
insurance_amount:
|
||||
type: number
|
||||
format: decimal
|
||||
description: 保险金额
|
||||
example: 160000.00
|
||||
premium_amount:
|
||||
type: number
|
||||
format: decimal
|
||||
description: 保费金额
|
||||
example: 8000.00
|
||||
start_date:
|
||||
type: string
|
||||
format: date
|
||||
description: 保险起期
|
||||
example: "2025-01-01"
|
||||
end_date:
|
||||
type: string
|
||||
format: date
|
||||
description: 保险止期
|
||||
example: "2025-12-31"
|
||||
completion_date:
|
||||
type: string
|
||||
format: date
|
||||
description: 结项日期
|
||||
example: "2025-01-19"
|
||||
status:
|
||||
type: string
|
||||
enum: [pending, approved, rejected, completed]
|
||||
description: 状态
|
||||
example: "pending"
|
||||
review_comments:
|
||||
type: string
|
||||
description: 审核意见
|
||||
example: "信息已更新"
|
||||
|
||||
ReviewRequest:
|
||||
type: object
|
||||
required:
|
||||
- status
|
||||
- reviewer_name
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum: [approved, rejected]
|
||||
description: 审核状态
|
||||
example: "approved"
|
||||
review_comments:
|
||||
type: string
|
||||
description: 审核意见
|
||||
example: "审核通过,符合监管要求"
|
||||
reviewer_name:
|
||||
type: string
|
||||
description: 审核人员
|
||||
example: "审核员张三"
|
||||
|
||||
StatisticsData:
|
||||
type: object
|
||||
properties:
|
||||
statusStats:
|
||||
type: array
|
||||
description: 状态统计
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
example: "pending"
|
||||
count:
|
||||
type: integer
|
||||
example: 5
|
||||
monthlyStats:
|
||||
type: array
|
||||
description: 月度统计
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
month:
|
||||
type: string
|
||||
example: "2025-01"
|
||||
count:
|
||||
type: integer
|
||||
example: 10
|
||||
total_amount:
|
||||
type: number
|
||||
format: decimal
|
||||
example: 1000000.00
|
||||
productStats:
|
||||
type: array
|
||||
description: 产品统计
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
product_name:
|
||||
type: string
|
||||
example: "养殖保险"
|
||||
count:
|
||||
type: integer
|
||||
example: 20
|
||||
total_amount:
|
||||
type: number
|
||||
format: decimal
|
||||
example: 2000000.00
|
||||
|
||||
Pagination:
|
||||
type: object
|
||||
properties:
|
||||
current:
|
||||
type: integer
|
||||
description: 当前页码
|
||||
example: 1
|
||||
pageSize:
|
||||
type: integer
|
||||
description: 每页数量
|
||||
example: 10
|
||||
total:
|
||||
type: integer
|
||||
description: 总记录数
|
||||
example: 100
|
||||
totalPages:
|
||||
type: integer
|
||||
description: 总页数
|
||||
example: 10
|
||||
|
||||
ErrorResponse:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
description: 错误码
|
||||
message:
|
||||
type: string
|
||||
description: 错误信息
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 时间戳
|
||||
|
||||
responses:
|
||||
BadRequest:
|
||||
description: 请求参数错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
code: 400
|
||||
message: "申请编号、保单号、产品名称、客户名称为必填项"
|
||||
timestamp: "2025-01-19T10:00:00Z"
|
||||
|
||||
Unauthorized:
|
||||
description: 未授权访问
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
code: 401
|
||||
message: "未授权访问"
|
||||
timestamp: "2025-01-19T10:00:00Z"
|
||||
|
||||
Forbidden:
|
||||
description: 权限不足
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
code: 403
|
||||
message: "权限不足"
|
||||
timestamp: "2025-01-19T10:00:00Z"
|
||||
|
||||
NotFound:
|
||||
description: 资源不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
code: 404
|
||||
message: "监管任务结项记录不存在"
|
||||
timestamp: "2025-01-19T10:00:00Z"
|
||||
|
||||
InternalServerError:
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
code: 500
|
||||
message: "服务器内部错误"
|
||||
timestamp: "2025-01-19T10:00:00Z"
|
||||
|
||||
tags:
|
||||
- name: 监管任务结项
|
||||
description: 监管任务结项管理相关接口
|
||||
@@ -22,27 +22,66 @@ const jwtAuth = (req, res, next) => {
|
||||
const checkPermission = (resource, action) => {
|
||||
return async (req, res, next) => {
|
||||
try {
|
||||
const { Role } = require('../models');
|
||||
console.log(`权限检查 - 资源: ${resource}, 操作: ${action}, 用户:`, req.user);
|
||||
const user = req.user;
|
||||
|
||||
if (!user || !user.role_id) {
|
||||
console.log('权限检查失败 - 用户角色信息缺失');
|
||||
return res.status(403).json(responseFormat.error('用户角色信息缺失'));
|
||||
}
|
||||
|
||||
const userRole = await Role.findByPk(user.role_id);
|
||||
let permissions = [];
|
||||
|
||||
if (!userRole) {
|
||||
return res.status(403).json(responseFormat.error('用户角色不存在'));
|
||||
// 优先使用JWT中的权限信息
|
||||
if (user.permissions) {
|
||||
if (typeof user.permissions === 'string') {
|
||||
try {
|
||||
permissions = JSON.parse(user.permissions);
|
||||
} catch (e) {
|
||||
console.log('JWT权限解析失败:', e.message);
|
||||
permissions = [];
|
||||
}
|
||||
} else if (Array.isArray(user.permissions)) {
|
||||
permissions = user.permissions;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果JWT中没有权限信息,从数据库查询
|
||||
if (permissions.length === 0) {
|
||||
const { Role } = require('../models');
|
||||
const userRole = await Role.findByPk(user.role_id);
|
||||
|
||||
if (!userRole) {
|
||||
console.log('权限检查失败 - 用户角色不存在, role_id:', user.role_id);
|
||||
return res.status(403).json(responseFormat.error('用户角色不存在'));
|
||||
}
|
||||
|
||||
let rolePermissions = userRole.permissions || [];
|
||||
|
||||
// 如果permissions是字符串,尝试解析为JSON
|
||||
if (typeof rolePermissions === 'string') {
|
||||
try {
|
||||
permissions = JSON.parse(rolePermissions);
|
||||
} catch (e) {
|
||||
console.log('数据库权限解析失败:', e.message);
|
||||
permissions = [];
|
||||
}
|
||||
} else if (Array.isArray(rolePermissions)) {
|
||||
permissions = rolePermissions;
|
||||
}
|
||||
}
|
||||
|
||||
const permissions = userRole.permissions || [];
|
||||
const requiredPermission = `${resource}:${action}`;
|
||||
|
||||
console.log('权限检查 - 用户权限:', permissions, '需要权限:', requiredPermission);
|
||||
|
||||
// 检查权限或超级管理员权限
|
||||
if (!permissions.includes(requiredPermission) && !permissions.includes('*:*')) {
|
||||
if (!permissions.includes(requiredPermission) && !permissions.includes('*:*') && !permissions.includes('*')) {
|
||||
console.log('权限检查失败 - 权限不足');
|
||||
return res.status(403).json(responseFormat.error('权限不足'));
|
||||
}
|
||||
|
||||
console.log('权限检查通过');
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(500).json(responseFormat.error('权限验证失败'));
|
||||
@@ -67,4 +106,17 @@ const optionalAuth = (req, res, next) => {
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = { jwtAuth, checkPermission, optionalAuth };
|
||||
// 别名导出以匹配路由中的使用
|
||||
const authenticateToken = jwtAuth;
|
||||
const requirePermission = (permission) => {
|
||||
const [resource, action] = permission.split(':');
|
||||
return checkPermission(resource, action);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
jwtAuth,
|
||||
checkPermission,
|
||||
optionalAuth,
|
||||
authenticateToken,
|
||||
requirePermission
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
// 添加参保类型字段
|
||||
await queryInterface.addColumn('insurance_applications', 'insurance_category', {
|
||||
type: Sequelize.STRING(50),
|
||||
allowNull: true,
|
||||
comment: '参保类型(如:牛、羊、猪等)',
|
||||
after: 'insurance_type_id'
|
||||
});
|
||||
|
||||
// 添加申请数量字段
|
||||
await queryInterface.addColumn('insurance_applications', 'application_quantity', {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true,
|
||||
defaultValue: 1,
|
||||
comment: '申请数量',
|
||||
validate: {
|
||||
min: 1
|
||||
},
|
||||
after: 'insurance_category'
|
||||
});
|
||||
|
||||
// 添加备注字段
|
||||
await queryInterface.addColumn('insurance_applications', 'remarks', {
|
||||
type: Sequelize.TEXT,
|
||||
allowNull: true,
|
||||
comment: '备注信息',
|
||||
after: 'review_notes'
|
||||
});
|
||||
|
||||
// 更新状态枚举值以匹配UI显示
|
||||
await queryInterface.changeColumn('insurance_applications', 'status', {
|
||||
type: Sequelize.ENUM(
|
||||
'pending', // 待初审
|
||||
'initial_approved', // 初审通过待复核
|
||||
'under_review', // 已支付待复核
|
||||
'approved', // 已支付
|
||||
'rejected', // 已拒绝
|
||||
'paid' // 已支付
|
||||
),
|
||||
allowNull: false,
|
||||
defaultValue: 'pending',
|
||||
comment: '申请状态'
|
||||
});
|
||||
|
||||
// 添加索引
|
||||
await queryInterface.addIndex('insurance_applications', ['insurance_category'], {
|
||||
name: 'idx_insurance_applications_category'
|
||||
});
|
||||
|
||||
await queryInterface.addIndex('insurance_applications', ['application_quantity'], {
|
||||
name: 'idx_insurance_applications_quantity'
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
// 移除索引
|
||||
await queryInterface.removeIndex('insurance_applications', 'idx_insurance_applications_category');
|
||||
await queryInterface.removeIndex('insurance_applications', 'idx_insurance_applications_quantity');
|
||||
|
||||
// 移除添加的字段
|
||||
await queryInterface.removeColumn('insurance_applications', 'insurance_category');
|
||||
await queryInterface.removeColumn('insurance_applications', 'application_quantity');
|
||||
await queryInterface.removeColumn('insurance_applications', 'remarks');
|
||||
|
||||
// 恢复原始状态枚举
|
||||
await queryInterface.changeColumn('insurance_applications', 'status', {
|
||||
type: Sequelize.ENUM('pending', 'approved', 'rejected', 'under_review'),
|
||||
allowNull: false,
|
||||
defaultValue: 'pending'
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -51,6 +51,23 @@ const InsuranceApplication = sequelize.define('InsuranceApplication', {
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
insurance_category: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: true,
|
||||
comment: '参保类型(如:牛、羊、猪等)',
|
||||
validate: {
|
||||
len: [1, 50]
|
||||
}
|
||||
},
|
||||
application_quantity: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
defaultValue: 1,
|
||||
comment: '申请数量',
|
||||
validate: {
|
||||
min: 1
|
||||
}
|
||||
},
|
||||
application_amount: {
|
||||
type: DataTypes.DECIMAL(15, 2),
|
||||
allowNull: false,
|
||||
@@ -59,8 +76,16 @@ const InsuranceApplication = sequelize.define('InsuranceApplication', {
|
||||
}
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM('pending', 'approved', 'rejected', 'under_review'),
|
||||
defaultValue: 'pending'
|
||||
type: DataTypes.ENUM(
|
||||
'pending', // 待初审
|
||||
'initial_approved', // 初审通过待复核
|
||||
'under_review', // 已支付待复核
|
||||
'approved', // 已支付
|
||||
'rejected', // 已拒绝
|
||||
'paid' // 已支付
|
||||
),
|
||||
defaultValue: 'pending',
|
||||
comment: '申请状态'
|
||||
},
|
||||
application_date: {
|
||||
type: DataTypes.DATE,
|
||||
@@ -71,6 +96,11 @@ const InsuranceApplication = sequelize.define('InsuranceApplication', {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true
|
||||
},
|
||||
remarks: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: '备注信息'
|
||||
},
|
||||
reviewer_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
@@ -99,6 +129,8 @@ const InsuranceApplication = sequelize.define('InsuranceApplication', {
|
||||
{ fields: ['status'] },
|
||||
{ fields: ['application_date'] },
|
||||
{ fields: ['insurance_type_id'] },
|
||||
{ fields: ['insurance_category'] },
|
||||
{ fields: ['application_quantity'] },
|
||||
{ fields: ['reviewer_id'] }
|
||||
]
|
||||
});
|
||||
|
||||
@@ -11,17 +11,19 @@ const InsuranceType = sequelize.define('InsuranceType', {
|
||||
name: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: '保险类型名称',
|
||||
comment: '险种名称',
|
||||
validate: {
|
||||
notEmpty: {
|
||||
msg: '保险类型名称不能为空'
|
||||
msg: '险种名称不能为空'
|
||||
},
|
||||
len: {
|
||||
args: [1, 100],
|
||||
msg: '保险类型名称长度必须在1-100个字符之间'
|
||||
msg: '险种名称长度必须在1-100个字符之间'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
|
||||
202
insurance_backend/models/LivestockClaim.js
Normal file
202
insurance_backend/models/LivestockClaim.js
Normal file
@@ -0,0 +1,202 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
const LivestockClaim = sequelize.define('LivestockClaim', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
comment: '生资理赔ID'
|
||||
},
|
||||
claim_no: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '理赔编号',
|
||||
validate: {
|
||||
notEmpty: {
|
||||
msg: '理赔编号不能为空'
|
||||
}
|
||||
}
|
||||
},
|
||||
policy_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '关联的保单ID',
|
||||
references: {
|
||||
model: 'livestock_policies',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
claim_type: {
|
||||
type: DataTypes.ENUM('disease', 'natural_disaster', 'accident', 'theft', 'other'),
|
||||
allowNull: false,
|
||||
comment: '理赔类型:disease-疾病,natural_disaster-自然灾害,accident-意外事故,theft-盗窃,other-其他'
|
||||
},
|
||||
incident_date: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
comment: '事故发生日期'
|
||||
},
|
||||
report_date: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
comment: '报案日期'
|
||||
},
|
||||
incident_description: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: false,
|
||||
comment: '事故描述',
|
||||
validate: {
|
||||
notEmpty: {
|
||||
msg: '事故描述不能为空'
|
||||
}
|
||||
}
|
||||
},
|
||||
incident_location: {
|
||||
type: DataTypes.STRING(500),
|
||||
allowNull: false,
|
||||
comment: '事故地点',
|
||||
validate: {
|
||||
notEmpty: {
|
||||
msg: '事故地点不能为空'
|
||||
}
|
||||
}
|
||||
},
|
||||
affected_count: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '受影响牲畜数量',
|
||||
validate: {
|
||||
min: {
|
||||
args: [1],
|
||||
msg: '受影响牲畜数量不能小于1'
|
||||
}
|
||||
}
|
||||
},
|
||||
claim_amount: {
|
||||
type: DataTypes.DECIMAL(15, 2),
|
||||
allowNull: false,
|
||||
comment: '理赔金额',
|
||||
validate: {
|
||||
min: {
|
||||
args: [0],
|
||||
msg: '理赔金额不能小于0'
|
||||
}
|
||||
}
|
||||
},
|
||||
claim_status: {
|
||||
type: DataTypes.ENUM('pending', 'investigating', 'approved', 'rejected', 'paid'),
|
||||
allowNull: false,
|
||||
defaultValue: 'pending',
|
||||
comment: '理赔状态:pending-待审核,investigating-调查中,approved-已批准,rejected-已拒绝,paid-已支付'
|
||||
},
|
||||
investigator_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '调查员ID',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
investigation_notes: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: '调查备注'
|
||||
},
|
||||
investigation_date: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '调查日期'
|
||||
},
|
||||
reviewer_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '审核人ID',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
review_notes: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: '审核备注'
|
||||
},
|
||||
review_date: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '审核日期'
|
||||
},
|
||||
settlement_amount: {
|
||||
type: DataTypes.DECIMAL(15, 2),
|
||||
allowNull: true,
|
||||
comment: '实际赔付金额',
|
||||
validate: {
|
||||
min: {
|
||||
args: [0],
|
||||
msg: '实际赔付金额不能小于0'
|
||||
}
|
||||
}
|
||||
},
|
||||
settlement_date: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '赔付日期'
|
||||
},
|
||||
supporting_documents: {
|
||||
type: DataTypes.JSON,
|
||||
allowNull: true,
|
||||
comment: '支持文件(JSON数组,包含文件URL和描述)'
|
||||
},
|
||||
created_by: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '创建人ID',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
updated_by: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '更新人ID',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
tableName: 'livestock_claims',
|
||||
timestamps: true,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at',
|
||||
indexes: [
|
||||
{
|
||||
name: 'idx_livestock_claim_no',
|
||||
fields: ['claim_no'],
|
||||
unique: true
|
||||
},
|
||||
{
|
||||
name: 'idx_livestock_claim_policy',
|
||||
fields: ['policy_id']
|
||||
},
|
||||
{
|
||||
name: 'idx_livestock_claim_type',
|
||||
fields: ['claim_type']
|
||||
},
|
||||
{
|
||||
name: 'idx_livestock_claim_status',
|
||||
fields: ['claim_status']
|
||||
},
|
||||
{
|
||||
name: 'idx_livestock_claim_dates',
|
||||
fields: ['incident_date', 'report_date']
|
||||
}
|
||||
],
|
||||
comment: '生资理赔表'
|
||||
});
|
||||
|
||||
module.exports = LivestockClaim;
|
||||
217
insurance_backend/models/LivestockPolicy.js
Normal file
217
insurance_backend/models/LivestockPolicy.js
Normal file
@@ -0,0 +1,217 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
const LivestockPolicy = sequelize.define('LivestockPolicy', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
comment: '生资保单ID'
|
||||
},
|
||||
policy_no: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '保单编号',
|
||||
validate: {
|
||||
notEmpty: {
|
||||
msg: '保单编号不能为空'
|
||||
}
|
||||
}
|
||||
},
|
||||
livestock_type_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '牲畜类型ID',
|
||||
references: {
|
||||
model: 'livestock_types',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
policyholder_name: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: '投保人姓名',
|
||||
validate: {
|
||||
notEmpty: {
|
||||
msg: '投保人姓名不能为空'
|
||||
}
|
||||
}
|
||||
},
|
||||
policyholder_id_card: {
|
||||
type: DataTypes.STRING(18),
|
||||
allowNull: false,
|
||||
comment: '投保人身份证号',
|
||||
validate: {
|
||||
notEmpty: {
|
||||
msg: '投保人身份证号不能为空'
|
||||
}
|
||||
}
|
||||
},
|
||||
policyholder_phone: {
|
||||
type: DataTypes.STRING(20),
|
||||
allowNull: false,
|
||||
comment: '投保人手机号',
|
||||
validate: {
|
||||
notEmpty: {
|
||||
msg: '投保人手机号不能为空'
|
||||
}
|
||||
}
|
||||
},
|
||||
policyholder_address: {
|
||||
type: DataTypes.STRING(500),
|
||||
allowNull: false,
|
||||
comment: '投保人地址',
|
||||
validate: {
|
||||
notEmpty: {
|
||||
msg: '投保人地址不能为空'
|
||||
}
|
||||
}
|
||||
},
|
||||
farm_name: {
|
||||
type: DataTypes.STRING(200),
|
||||
allowNull: true,
|
||||
comment: '养殖场名称'
|
||||
},
|
||||
farm_address: {
|
||||
type: DataTypes.STRING(500),
|
||||
allowNull: true,
|
||||
comment: '养殖场地址'
|
||||
},
|
||||
farm_license: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: true,
|
||||
comment: '养殖场许可证号'
|
||||
},
|
||||
livestock_count: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '投保牲畜数量',
|
||||
validate: {
|
||||
min: {
|
||||
args: [1],
|
||||
msg: '投保牲畜数量不能小于1'
|
||||
}
|
||||
}
|
||||
},
|
||||
unit_value: {
|
||||
type: DataTypes.DECIMAL(10, 2),
|
||||
allowNull: false,
|
||||
comment: '单头价值',
|
||||
validate: {
|
||||
min: {
|
||||
args: [0],
|
||||
msg: '单头价值不能小于0'
|
||||
}
|
||||
}
|
||||
},
|
||||
total_value: {
|
||||
type: DataTypes.DECIMAL(15, 2),
|
||||
allowNull: false,
|
||||
comment: '总保额',
|
||||
validate: {
|
||||
min: {
|
||||
args: [0],
|
||||
msg: '总保额不能小于0'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
premium_amount: {
|
||||
type: DataTypes.DECIMAL(15, 2),
|
||||
allowNull: false,
|
||||
comment: '保费金额',
|
||||
validate: {
|
||||
min: {
|
||||
args: [0],
|
||||
msg: '保费金额不能小于0'
|
||||
}
|
||||
}
|
||||
},
|
||||
start_date: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
comment: '保险开始日期'
|
||||
},
|
||||
end_date: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
comment: '保险结束日期'
|
||||
},
|
||||
policy_status: {
|
||||
type: DataTypes.ENUM('active', 'expired', 'cancelled', 'suspended'),
|
||||
allowNull: false,
|
||||
defaultValue: 'active',
|
||||
comment: '保单状态:active-有效,expired-已过期,cancelled-已取消,suspended-已暂停'
|
||||
},
|
||||
payment_status: {
|
||||
type: DataTypes.ENUM('paid', 'unpaid', 'partial'),
|
||||
allowNull: false,
|
||||
defaultValue: 'unpaid',
|
||||
comment: '支付状态:paid-已支付,unpaid-未支付,partial-部分支付'
|
||||
},
|
||||
payment_date: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '支付日期'
|
||||
},
|
||||
policy_document_url: {
|
||||
type: DataTypes.STRING(500),
|
||||
allowNull: true,
|
||||
comment: '保单文档URL'
|
||||
},
|
||||
notes: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: '备注信息'
|
||||
},
|
||||
created_by: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '创建人ID',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
updated_by: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '更新人ID',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
tableName: 'livestock_policies',
|
||||
timestamps: true,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at',
|
||||
indexes: [
|
||||
{
|
||||
name: 'idx_livestock_policy_no',
|
||||
fields: ['policy_no'],
|
||||
unique: true
|
||||
},
|
||||
{
|
||||
name: 'idx_livestock_policy_policyholder',
|
||||
fields: ['policyholder_name']
|
||||
},
|
||||
{
|
||||
name: 'idx_livestock_policy_status',
|
||||
fields: ['policy_status']
|
||||
},
|
||||
{
|
||||
name: 'idx_livestock_policy_payment_status',
|
||||
fields: ['payment_status']
|
||||
},
|
||||
{
|
||||
name: 'idx_livestock_policy_dates',
|
||||
fields: ['start_date', 'end_date']
|
||||
}
|
||||
],
|
||||
comment: '生资保单表'
|
||||
});
|
||||
|
||||
module.exports = LivestockPolicy;
|
||||
101
insurance_backend/models/LivestockType.js
Normal file
101
insurance_backend/models/LivestockType.js
Normal file
@@ -0,0 +1,101 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
const LivestockType = sequelize.define('LivestockType', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
comment: '牲畜类型ID'
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '牲畜类型名称',
|
||||
validate: {
|
||||
notEmpty: {
|
||||
msg: '牲畜类型名称不能为空'
|
||||
}
|
||||
}
|
||||
},
|
||||
code: {
|
||||
type: DataTypes.STRING(20),
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '牲畜类型代码'
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: '牲畜类型描述'
|
||||
},
|
||||
unit_price_min: {
|
||||
type: DataTypes.DECIMAL(10, 2),
|
||||
allowNull: false,
|
||||
defaultValue: 0.00,
|
||||
comment: '单头最低价值',
|
||||
validate: {
|
||||
min: {
|
||||
args: [0],
|
||||
msg: '单头最低价值不能小于0'
|
||||
}
|
||||
}
|
||||
},
|
||||
unit_price_max: {
|
||||
type: DataTypes.DECIMAL(10, 2),
|
||||
allowNull: false,
|
||||
defaultValue: 50000.00,
|
||||
comment: '单头最高价值',
|
||||
validate: {
|
||||
min: {
|
||||
args: [0],
|
||||
msg: '单头最高价值不能小于0'
|
||||
}
|
||||
}
|
||||
},
|
||||
premium_rate: {
|
||||
type: DataTypes.DECIMAL(5, 4),
|
||||
allowNull: false,
|
||||
defaultValue: 0.0050,
|
||||
comment: '保险费率',
|
||||
validate: {
|
||||
min: {
|
||||
args: [0],
|
||||
msg: '保费费率不能小于0'
|
||||
},
|
||||
max: {
|
||||
args: [1],
|
||||
msg: '保费费率不能大于1'
|
||||
}
|
||||
}
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM('active', 'inactive'),
|
||||
allowNull: false,
|
||||
defaultValue: 'active',
|
||||
comment: '状态'
|
||||
}
|
||||
}, {
|
||||
tableName: 'livestock_types',
|
||||
timestamps: true,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at',
|
||||
indexes: [
|
||||
{
|
||||
name: 'idx_livestock_type_name',
|
||||
fields: ['name']
|
||||
},
|
||||
{
|
||||
name: 'idx_livestock_type_code',
|
||||
fields: ['code']
|
||||
},
|
||||
{
|
||||
name: 'idx_livestock_type_status',
|
||||
fields: ['status']
|
||||
}
|
||||
],
|
||||
comment: '牲畜类型表'
|
||||
});
|
||||
|
||||
module.exports = LivestockType;
|
||||
@@ -9,6 +9,9 @@ const Claim = require('./Claim');
|
||||
const Menu = require('./Menu');
|
||||
const SupervisoryTask = require('./SupervisoryTask');
|
||||
const InstallationTask = require('./InstallationTask');
|
||||
const LivestockType = require('./LivestockType');
|
||||
const LivestockPolicy = require('./LivestockPolicy');
|
||||
const LivestockClaim = require('./LivestockClaim');
|
||||
|
||||
// 定义模型关联关系
|
||||
|
||||
@@ -46,6 +49,26 @@ InsuranceApplication.hasOne(Policy, {
|
||||
as: 'policy'
|
||||
});
|
||||
|
||||
// 保单和客户关联
|
||||
Policy.belongsTo(User, {
|
||||
foreignKey: 'customer_id',
|
||||
as: 'customer'
|
||||
});
|
||||
User.hasMany(Policy, {
|
||||
foreignKey: 'customer_id',
|
||||
as: 'policies'
|
||||
});
|
||||
|
||||
// 保单和保险类型关联
|
||||
Policy.belongsTo(InsuranceType, {
|
||||
foreignKey: 'insurance_type_id',
|
||||
as: 'insurance_type'
|
||||
});
|
||||
InsuranceType.hasMany(Policy, {
|
||||
foreignKey: 'insurance_type_id',
|
||||
as: 'policies'
|
||||
});
|
||||
|
||||
// 理赔和保单关联
|
||||
Claim.belongsTo(Policy, {
|
||||
foreignKey: 'policy_id',
|
||||
@@ -84,6 +107,49 @@ InstallationTask.belongsTo(User, {
|
||||
as: 'updater'
|
||||
});
|
||||
|
||||
// 生资保险相关关联
|
||||
// 注意:牲畜类型表中没有created_by和updated_by字段,所以不定义这些关联
|
||||
|
||||
// 生资保单和牲畜类型关联
|
||||
LivestockPolicy.belongsTo(LivestockType, {
|
||||
foreignKey: 'livestock_type_id',
|
||||
as: 'livestock_type'
|
||||
});
|
||||
LivestockType.hasMany(LivestockPolicy, {
|
||||
foreignKey: 'livestock_type_id',
|
||||
as: 'policies'
|
||||
});
|
||||
|
||||
// 生资保单和用户关联
|
||||
LivestockPolicy.belongsTo(User, {
|
||||
foreignKey: 'created_by',
|
||||
as: 'creator'
|
||||
});
|
||||
LivestockPolicy.belongsTo(User, {
|
||||
foreignKey: 'updated_by',
|
||||
as: 'updater'
|
||||
});
|
||||
|
||||
// 生资理赔和生资保单关联
|
||||
LivestockClaim.belongsTo(LivestockPolicy, {
|
||||
foreignKey: 'policy_id',
|
||||
as: 'policy'
|
||||
});
|
||||
LivestockPolicy.hasMany(LivestockClaim, {
|
||||
foreignKey: 'policy_id',
|
||||
as: 'claims'
|
||||
});
|
||||
|
||||
// 生资理赔和用户关联
|
||||
LivestockClaim.belongsTo(User, {
|
||||
foreignKey: 'created_by',
|
||||
as: 'creator'
|
||||
});
|
||||
LivestockClaim.belongsTo(User, {
|
||||
foreignKey: 'reviewed_by',
|
||||
as: 'reviewer'
|
||||
});
|
||||
|
||||
// 导出所有模型
|
||||
module.exports = {
|
||||
sequelize,
|
||||
@@ -95,5 +161,8 @@ module.exports = {
|
||||
Claim,
|
||||
Menu,
|
||||
SupervisoryTask,
|
||||
InstallationTask
|
||||
InstallationTask,
|
||||
LivestockType,
|
||||
LivestockPolicy,
|
||||
LivestockClaim
|
||||
};
|
||||
2
insurance_backend/package-lock.json
generated
2
insurance_backend/package-lock.json
generated
@@ -7997,6 +7997,7 @@
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.12.2.tgz",
|
||||
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
@@ -12876,6 +12877,7 @@
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/mysql2/-/mysql2-2.3.3.tgz",
|
||||
"integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"denque": "^2.0.1",
|
||||
"generate-function": "^2.3.1",
|
||||
|
||||
@@ -38,4 +38,14 @@ router.get('/applications-stats', jwtAuth, checkPermission('insurance', 'read'),
|
||||
insuranceController.getApplicationStats
|
||||
);
|
||||
|
||||
// 导出保险申请数据
|
||||
router.get('/applications/export', jwtAuth, checkPermission('insurance', 'read'),
|
||||
insuranceController.exportApplications
|
||||
);
|
||||
|
||||
// 获取参保类型选项
|
||||
router.get('/categories', jwtAuth, checkPermission('insurance', 'read'),
|
||||
insuranceController.getInsuranceCategories
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
@@ -3,32 +3,32 @@ const router = express.Router();
|
||||
const insuranceTypeController = require('../controllers/insuranceTypeController');
|
||||
const { jwtAuth, checkPermission } = require('../middleware/auth');
|
||||
|
||||
// 获取保险类型列表
|
||||
// 获取险种列表
|
||||
router.get('/', jwtAuth, checkPermission('insurance_type', 'read'),
|
||||
insuranceTypeController.getInsuranceTypes
|
||||
);
|
||||
|
||||
// 获取单个保险类型详情
|
||||
// 获取单个险种详情
|
||||
router.get('/:id', jwtAuth, checkPermission('insurance_type', 'read'),
|
||||
insuranceTypeController.getInsuranceTypeById
|
||||
);
|
||||
|
||||
// 创建保险类型
|
||||
// 创建险种
|
||||
router.post('/', jwtAuth, checkPermission('insurance_type', 'create'),
|
||||
insuranceTypeController.createInsuranceType
|
||||
);
|
||||
|
||||
// 更新保险类型
|
||||
// 更新险种
|
||||
router.put('/:id', jwtAuth, checkPermission('insurance_type', 'update'),
|
||||
insuranceTypeController.updateInsuranceType
|
||||
);
|
||||
|
||||
// 删除保险类型
|
||||
// 删除险种
|
||||
router.delete('/:id', jwtAuth, checkPermission('insurance_type', 'delete'),
|
||||
insuranceTypeController.deleteInsuranceType
|
||||
);
|
||||
|
||||
// 更新保险类型状态
|
||||
// 更新险种状态
|
||||
router.patch('/:id/status', jwtAuth, checkPermission('insurance_type', 'update'),
|
||||
insuranceTypeController.updateInsuranceTypeStatus
|
||||
);
|
||||
|
||||
31
insurance_backend/routes/livestockClaims.js
Normal file
31
insurance_backend/routes/livestockClaims.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const {
|
||||
getLivestockClaims,
|
||||
createLivestockClaim,
|
||||
getLivestockClaimById,
|
||||
reviewLivestockClaim,
|
||||
updateLivestockClaimPayment,
|
||||
getLivestockClaimStats
|
||||
} = require('../controllers/livestockClaimController');
|
||||
const { authenticateToken, requirePermission } = require('../middleware/auth');
|
||||
|
||||
// 获取生资理赔列表
|
||||
router.get('/', authenticateToken, requirePermission('livestock_claim:read'), getLivestockClaims);
|
||||
|
||||
// 获取生资理赔统计
|
||||
router.get('/stats', authenticateToken, requirePermission('livestock_claim:read'), getLivestockClaimStats);
|
||||
|
||||
// 获取单个生资理赔详情
|
||||
router.get('/:id', authenticateToken, requirePermission('livestock_claim:read'), getLivestockClaimById);
|
||||
|
||||
// 创建生资理赔申请
|
||||
router.post('/', authenticateToken, requirePermission('livestock_claim:create'), createLivestockClaim);
|
||||
|
||||
// 审核生资理赔
|
||||
router.patch('/:id/review', authenticateToken, requirePermission('livestock_claim:review'), reviewLivestockClaim);
|
||||
|
||||
// 更新理赔支付状态
|
||||
router.patch('/:id/payment', authenticateToken, requirePermission('livestock_claim:payment'), updateLivestockClaimPayment);
|
||||
|
||||
module.exports = router;
|
||||
35
insurance_backend/routes/livestockPolicies.js
Normal file
35
insurance_backend/routes/livestockPolicies.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const {
|
||||
getLivestockPolicies,
|
||||
createLivestockPolicy,
|
||||
getLivestockPolicyById,
|
||||
updateLivestockPolicy,
|
||||
updateLivestockPolicyStatus,
|
||||
deleteLivestockPolicy,
|
||||
getLivestockPolicyStats
|
||||
} = require('../controllers/livestockPolicyController');
|
||||
const { authenticateToken, requirePermission } = require('../middleware/auth');
|
||||
|
||||
// 获取生资保单列表
|
||||
router.get('/', authenticateToken, requirePermission('livestock_policy:read'), getLivestockPolicies);
|
||||
|
||||
// 获取生资保单统计
|
||||
router.get('/stats', authenticateToken, requirePermission('livestock_policy:read'), getLivestockPolicyStats);
|
||||
|
||||
// 获取单个生资保单详情
|
||||
router.get('/:id', authenticateToken, requirePermission('livestock_policy:read'), getLivestockPolicyById);
|
||||
|
||||
// 创建生资保单
|
||||
router.post('/', authenticateToken, requirePermission('livestock_policy:create'), createLivestockPolicy);
|
||||
|
||||
// 更新生资保单
|
||||
router.put('/:id', authenticateToken, requirePermission('livestock_policy:update'), updateLivestockPolicy);
|
||||
|
||||
// 更新生资保单状态
|
||||
router.patch('/:id/status', authenticateToken, requirePermission('livestock_policy:update'), updateLivestockPolicyStatus);
|
||||
|
||||
// 删除生资保单
|
||||
router.delete('/:id', authenticateToken, requirePermission('livestock_policy:delete'), deleteLivestockPolicy);
|
||||
|
||||
module.exports = router;
|
||||
35
insurance_backend/routes/livestockTypes.js
Normal file
35
insurance_backend/routes/livestockTypes.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const {
|
||||
getLivestockTypes,
|
||||
getActiveLivestockTypes,
|
||||
createLivestockType,
|
||||
getLivestockTypeById,
|
||||
updateLivestockType,
|
||||
deleteLivestockType,
|
||||
batchUpdateLivestockTypeStatus
|
||||
} = require('../controllers/livestockTypeController');
|
||||
const { authenticateToken, requirePermission } = require('../middleware/auth');
|
||||
|
||||
// 获取牲畜类型列表
|
||||
router.get('/', authenticateToken, requirePermission('livestock_type:read'), getLivestockTypes);
|
||||
|
||||
// 获取所有启用的牲畜类型(用于下拉选择)
|
||||
router.get('/active', authenticateToken, getActiveLivestockTypes);
|
||||
|
||||
// 获取单个牲畜类型详情
|
||||
router.get('/:id', authenticateToken, requirePermission('livestock_type:read'), getLivestockTypeById);
|
||||
|
||||
// 创建牲畜类型
|
||||
router.post('/', authenticateToken, requirePermission('livestock_type:create'), createLivestockType);
|
||||
|
||||
// 更新牲畜类型
|
||||
router.put('/:id', authenticateToken, requirePermission('livestock_type:update'), updateLivestockType);
|
||||
|
||||
// 删除牲畜类型
|
||||
router.delete('/:id', authenticateToken, requirePermission('livestock_type:delete'), deleteLivestockType);
|
||||
|
||||
// 批量更新牲畜类型状态
|
||||
router.patch('/batch/status', authenticateToken, requirePermission('livestock_type:update'), batchUpdateLivestockTypeStatus);
|
||||
|
||||
module.exports = router;
|
||||
@@ -3,6 +3,11 @@ const router = express.Router();
|
||||
const policyController = require('../controllers/policyController');
|
||||
const { jwtAuth, checkPermission } = require('../middleware/auth');
|
||||
|
||||
// 获取保单统计(必须在动态路由之前)
|
||||
router.get('/stats/overview', jwtAuth, checkPermission('policy', 'read'),
|
||||
policyController.getPolicyStats
|
||||
);
|
||||
|
||||
// 获取保单列表
|
||||
router.get('/', jwtAuth, checkPermission('policy', 'read'),
|
||||
policyController.getPolicies
|
||||
@@ -28,9 +33,4 @@ router.patch('/:id/status', jwtAuth, checkPermission('policy', 'update'),
|
||||
policyController.updatePolicyStatus
|
||||
);
|
||||
|
||||
// 获取保单统计
|
||||
router.get('/stats/overview', jwtAuth, checkPermission('policy', 'read'),
|
||||
policyController.getPolicyStats
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
536
insurance_backend/routes/regulatoryTaskCompletion.js
Normal file
536
insurance_backend/routes/regulatoryTaskCompletion.js
Normal file
@@ -0,0 +1,536 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const regulatoryTaskCompletionController = require('../controllers/regulatoryTaskCompletionController');
|
||||
const { jwtAuth, checkPermission } = require('../middleware/auth');
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: RegulatoryTaskCompletion
|
||||
* description: 监管任务结项管理API
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* components:
|
||||
* schemas:
|
||||
* RegulatoryTaskCompletion:
|
||||
* type: object
|
||||
* required:
|
||||
* - application_no
|
||||
* - policy_no
|
||||
* - product_name
|
||||
* - customer_name
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
* description: 记录ID
|
||||
* application_no:
|
||||
* type: string
|
||||
* description: 申请编号
|
||||
* policy_no:
|
||||
* type: string
|
||||
* description: 保单号
|
||||
* product_name:
|
||||
* type: string
|
||||
* description: 产品名称
|
||||
* customer_name:
|
||||
* type: string
|
||||
* description: 客户名称
|
||||
* customer_phone:
|
||||
* type: string
|
||||
* description: 客户电话
|
||||
* insurance_amount:
|
||||
* type: number
|
||||
* format: decimal
|
||||
* description: 保险金额
|
||||
* premium_amount:
|
||||
* type: number
|
||||
* format: decimal
|
||||
* description: 保费金额
|
||||
* start_date:
|
||||
* type: string
|
||||
* format: date
|
||||
* description: 保险起期
|
||||
* end_date:
|
||||
* type: string
|
||||
* format: date
|
||||
* description: 保险止期
|
||||
* completion_date:
|
||||
* type: string
|
||||
* format: date
|
||||
* description: 结项日期
|
||||
* status:
|
||||
* type: string
|
||||
* enum: [pending, approved, rejected, completed]
|
||||
* description: 状态
|
||||
* reviewer_name:
|
||||
* type: string
|
||||
* description: 审核人员
|
||||
* review_comments:
|
||||
* type: string
|
||||
* description: 审核意见
|
||||
* created_at:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* description: 创建时间
|
||||
* updated_at:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* description: 更新时间
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/regulatory-task-completion:
|
||||
* get:
|
||||
* summary: 获取监管任务结项列表
|
||||
* tags: [RegulatoryTaskCompletion]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: application_no
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 申请编号(模糊查询)
|
||||
* - in: query
|
||||
* name: policy_no
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 保单号(模糊查询)
|
||||
* - in: query
|
||||
* name: product_name
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 产品名称(模糊查询)
|
||||
* - in: query
|
||||
* name: customer_name
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 客户名称(模糊查询)
|
||||
* - in: query
|
||||
* name: completion_date
|
||||
* schema:
|
||||
* type: string
|
||||
* format: date
|
||||
* description: 结项日期
|
||||
* - in: query
|
||||
* name: status
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: [pending, approved, rejected, completed]
|
||||
* description: 状态
|
||||
* - in: query
|
||||
* name: reviewer_name
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 审核人员(模糊查询)
|
||||
* - in: query
|
||||
* name: page
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 1
|
||||
* description: 页码
|
||||
* - in: query
|
||||
* name: limit
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 10
|
||||
* description: 每页数量
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功获取监管任务结项列表
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: success
|
||||
* data:
|
||||
* type: object
|
||||
* properties:
|
||||
* list:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/RegulatoryTaskCompletion'
|
||||
* pagination:
|
||||
* type: object
|
||||
* properties:
|
||||
* current:
|
||||
* type: integer
|
||||
* pageSize:
|
||||
* type: integer
|
||||
* total:
|
||||
* type: integer
|
||||
* totalPages:
|
||||
* type: integer
|
||||
*/
|
||||
router.get('/', jwtAuth, checkPermission('regulatory_task', 'read'),
|
||||
regulatoryTaskCompletionController.getTaskCompletions
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/regulatory-task-completion/{id}:
|
||||
* get:
|
||||
* summary: 获取监管任务结项详情
|
||||
* tags: [RegulatoryTaskCompletion]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 记录ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功获取监管任务结项详情
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: success
|
||||
* data:
|
||||
* allOf:
|
||||
* - $ref: '#/components/schemas/RegulatoryTaskCompletion'
|
||||
* - type: object
|
||||
* properties:
|
||||
* attachments:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
* file_name:
|
||||
* type: string
|
||||
* file_path:
|
||||
* type: string
|
||||
* file_size:
|
||||
* type: integer
|
||||
* file_type:
|
||||
* type: string
|
||||
* upload_time:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* operation_logs:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
* operation_type:
|
||||
* type: string
|
||||
* operation_description:
|
||||
* type: string
|
||||
* operator_name:
|
||||
* type: string
|
||||
* operation_time:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* ip_address:
|
||||
* type: string
|
||||
* 404:
|
||||
* description: 记录不存在
|
||||
*/
|
||||
router.get('/:id', jwtAuth, checkPermission('regulatory_task', 'read'),
|
||||
regulatoryTaskCompletionController.getTaskCompletionById
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/regulatory-task-completion:
|
||||
* post:
|
||||
* summary: 创建监管任务结项记录
|
||||
* tags: [RegulatoryTaskCompletion]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - application_no
|
||||
* - policy_no
|
||||
* - product_name
|
||||
* - customer_name
|
||||
* properties:
|
||||
* application_no:
|
||||
* type: string
|
||||
* description: 申请编号
|
||||
* policy_no:
|
||||
* type: string
|
||||
* description: 保单号
|
||||
* product_name:
|
||||
* type: string
|
||||
* description: 产品名称
|
||||
* customer_name:
|
||||
* type: string
|
||||
* description: 客户名称
|
||||
* customer_phone:
|
||||
* type: string
|
||||
* description: 客户电话
|
||||
* insurance_amount:
|
||||
* type: number
|
||||
* format: decimal
|
||||
* description: 保险金额
|
||||
* premium_amount:
|
||||
* type: number
|
||||
* format: decimal
|
||||
* description: 保费金额
|
||||
* start_date:
|
||||
* type: string
|
||||
* format: date
|
||||
* description: 保险起期
|
||||
* end_date:
|
||||
* type: string
|
||||
* format: date
|
||||
* description: 保险止期
|
||||
* completion_date:
|
||||
* type: string
|
||||
* format: date
|
||||
* description: 结项日期
|
||||
* status:
|
||||
* type: string
|
||||
* enum: [pending, approved, rejected, completed]
|
||||
* default: pending
|
||||
* description: 状态
|
||||
* review_comments:
|
||||
* type: string
|
||||
* description: 审核意见
|
||||
* attachments:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* file_name:
|
||||
* type: string
|
||||
* file_path:
|
||||
* type: string
|
||||
* file_size:
|
||||
* type: integer
|
||||
* file_type:
|
||||
* type: string
|
||||
* responses:
|
||||
* 201:
|
||||
* description: 监管任务结项记录创建成功
|
||||
* 400:
|
||||
* description: 参数错误或记录已存在
|
||||
*/
|
||||
router.post('/', jwtAuth, checkPermission('regulatory_task', 'create'),
|
||||
regulatoryTaskCompletionController.createTaskCompletion
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/regulatory-task-completion/{id}:
|
||||
* put:
|
||||
* summary: 更新监管任务结项记录
|
||||
* tags: [RegulatoryTaskCompletion]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 记录ID
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* application_no:
|
||||
* type: string
|
||||
* policy_no:
|
||||
* type: string
|
||||
* product_name:
|
||||
* type: string
|
||||
* customer_name:
|
||||
* type: string
|
||||
* customer_phone:
|
||||
* type: string
|
||||
* insurance_amount:
|
||||
* type: number
|
||||
* format: decimal
|
||||
* premium_amount:
|
||||
* type: number
|
||||
* format: decimal
|
||||
* start_date:
|
||||
* type: string
|
||||
* format: date
|
||||
* end_date:
|
||||
* type: string
|
||||
* format: date
|
||||
* completion_date:
|
||||
* type: string
|
||||
* format: date
|
||||
* status:
|
||||
* type: string
|
||||
* enum: [pending, approved, rejected, completed]
|
||||
* review_comments:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 监管任务结项记录更新成功
|
||||
* 404:
|
||||
* description: 记录不存在
|
||||
*/
|
||||
router.put('/:id', jwtAuth, checkPermission('regulatory_task', 'update'),
|
||||
regulatoryTaskCompletionController.updateTaskCompletion
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/regulatory-task-completion/{id}:
|
||||
* delete:
|
||||
* summary: 删除监管任务结项记录
|
||||
* tags: [RegulatoryTaskCompletion]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 记录ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 监管任务结项记录删除成功
|
||||
* 404:
|
||||
* description: 记录不存在
|
||||
*/
|
||||
router.delete('/:id', jwtAuth, checkPermission('regulatory_task', 'delete'),
|
||||
regulatoryTaskCompletionController.deleteTaskCompletion
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/regulatory-task-completion/{id}/review:
|
||||
* patch:
|
||||
* summary: 审核监管任务结项记录
|
||||
* tags: [RegulatoryTaskCompletion]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 记录ID
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - status
|
||||
* - reviewer_name
|
||||
* properties:
|
||||
* status:
|
||||
* type: string
|
||||
* enum: [pending, approved, rejected, completed]
|
||||
* description: 审核状态
|
||||
* review_comments:
|
||||
* type: string
|
||||
* description: 审核意见
|
||||
* reviewer_name:
|
||||
* type: string
|
||||
* description: 审核人员
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 监管任务结项记录审核成功
|
||||
* 400:
|
||||
* description: 无效的状态值
|
||||
* 404:
|
||||
* description: 记录不存在
|
||||
*/
|
||||
router.patch('/:id/review', jwtAuth, checkPermission('regulatory_task', 'review'),
|
||||
regulatoryTaskCompletionController.reviewTaskCompletion
|
||||
);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/regulatory-task-completion/stats:
|
||||
* get:
|
||||
* summary: 获取监管任务结项统计数据
|
||||
* tags: [RegulatoryTaskCompletion]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功获取统计数据
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: success
|
||||
* data:
|
||||
* type: object
|
||||
* properties:
|
||||
* statusStats:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* status:
|
||||
* type: string
|
||||
* count:
|
||||
* type: integer
|
||||
* monthlyStats:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* month:
|
||||
* type: string
|
||||
* count:
|
||||
* type: integer
|
||||
* total_amount:
|
||||
* type: number
|
||||
* productStats:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* product_name:
|
||||
* type: string
|
||||
* count:
|
||||
* type: integer
|
||||
* total_amount:
|
||||
* type: number
|
||||
*/
|
||||
router.get('/stats', jwtAuth, checkPermission('regulatory_task', 'read'),
|
||||
regulatoryTaskCompletionController.getTaskCompletionStats
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
29
insurance_backend/scripts/check_claims_table.js
Normal file
29
insurance_backend/scripts/check_claims_table.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const { Sequelize } = require('sequelize');
|
||||
|
||||
const sequelize = new Sequelize('insurance_db', 'root', '123456', {
|
||||
host: 'localhost',
|
||||
dialect: 'mysql',
|
||||
logging: false
|
||||
});
|
||||
|
||||
async function checkClaimsTable() {
|
||||
try {
|
||||
const [results] = await sequelize.query('DESCRIBE livestock_claims');
|
||||
|
||||
console.log('livestock_claims表结构:');
|
||||
console.log('='.repeat(50));
|
||||
|
||||
results.forEach(field => {
|
||||
const required = field.Null === 'NO' ? '(必填)' : '(可选)';
|
||||
const defaultValue = field.Default ? ` 默认值: ${field.Default}` : '';
|
||||
console.log(`- ${field.Field}: ${field.Type} ${required}${defaultValue}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('检查表结构失败:', error.message);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
checkClaimsTable();
|
||||
34
insurance_backend/scripts/check_required_fields.js
Normal file
34
insurance_backend/scripts/check_required_fields.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
async function checkRequiredFields() {
|
||||
try {
|
||||
console.log('检查 livestock_policies 表的必填字段...');
|
||||
|
||||
const [rows] = await sequelize.query('DESCRIBE livestock_policies');
|
||||
|
||||
console.log('必填字段(NOT NULL):');
|
||||
console.log('序号 | 字段名 | 类型 | 默认值');
|
||||
console.log('-----|--------|------|--------');
|
||||
|
||||
const requiredFields = rows.filter(row => row.Null === 'NO');
|
||||
requiredFields.forEach((row, index) => {
|
||||
console.log(`${index + 1}. ${row.Field} | ${row.Type} | ${row.Default || 'NULL'}`);
|
||||
});
|
||||
|
||||
console.log('\n可选字段(允许NULL):');
|
||||
console.log('序号 | 字段名 | 类型');
|
||||
console.log('-----|--------|------');
|
||||
|
||||
const optionalFields = rows.filter(row => row.Null === 'YES');
|
||||
optionalFields.forEach((row, index) => {
|
||||
console.log(`${index + 1}. ${row.Field} | ${row.Type}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 检查字段失败:', error.message);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
checkRequiredFields();
|
||||
24
insurance_backend/scripts/check_table_structure.js
Normal file
24
insurance_backend/scripts/check_table_structure.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
async function checkTableStructure() {
|
||||
try {
|
||||
console.log('检查 livestock_policies 表结构...');
|
||||
|
||||
const [rows] = await sequelize.query('DESCRIBE livestock_policies');
|
||||
|
||||
console.log('livestock_policies表结构:');
|
||||
console.log('序号 | 字段名 | 类型 | 是否为空 | 默认值');
|
||||
console.log('-----|--------|------|----------|--------');
|
||||
|
||||
rows.forEach((row, index) => {
|
||||
console.log(`${index + 1}. ${row.Field} | ${row.Type} | ${row.Null} | ${row.Default || 'NULL'}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 检查表结构失败:', error.message);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
checkTableStructure();
|
||||
20
insurance_backend/scripts/check_tables.js
Normal file
20
insurance_backend/scripts/check_tables.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
async function checkTables() {
|
||||
try {
|
||||
await sequelize.query('USE insurance_data');
|
||||
|
||||
const [results] = await sequelize.query("SHOW TABLES LIKE 'livestock_%'");
|
||||
console.log('livestock表:', results);
|
||||
|
||||
const [allTables] = await sequelize.query("SHOW TABLES");
|
||||
console.log('所有表:', allTables.map(t => Object.values(t)[0]));
|
||||
|
||||
} catch (error) {
|
||||
console.error('错误:', error.message);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
checkTables();
|
||||
21
insurance_backend/scripts/check_users.js
Normal file
21
insurance_backend/scripts/check_users.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
async function checkUsers() {
|
||||
try {
|
||||
const [users] = await sequelize.query('SELECT id, username, real_name FROM users LIMIT 10');
|
||||
console.log('用户列表:');
|
||||
if (users.length === 0) {
|
||||
console.log('❌ 没有找到任何用户数据');
|
||||
} else {
|
||||
users.forEach(user => {
|
||||
console.log(`- ID: ${user.id}, 用户名: ${user.username}, 姓名: ${user.real_name}`);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 查询用户失败:', error.message);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
checkUsers();
|
||||
86
insurance_backend/scripts/create_regulatory_tables.js
Normal file
86
insurance_backend/scripts/create_regulatory_tables.js
Normal file
@@ -0,0 +1,86 @@
|
||||
const mysql = require('mysql2/promise');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
require('dotenv').config();
|
||||
|
||||
async function createRegulatoryTables() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
// 创建数据库连接
|
||||
connection = await mysql.createConnection({
|
||||
host: process.env.DB_HOST || '129.211.213.226',
|
||||
port: process.env.DB_PORT || 9527,
|
||||
user: process.env.DB_USER || 'root',
|
||||
password: process.env.DB_PASSWORD || 'aiotAiot123!',
|
||||
database: process.env.DB_DATABASE || 'nxxmdata',
|
||||
multipleStatements: true
|
||||
});
|
||||
|
||||
console.log('数据库连接成功');
|
||||
|
||||
// 读取SQL文件
|
||||
const sqlFilePath = path.join(__dirname, 'regulatory_task_completion.sql');
|
||||
const sqlContent = fs.readFileSync(sqlFilePath, 'utf8');
|
||||
|
||||
// 分割SQL语句(按分号分割,但忽略注释中的分号)
|
||||
const sqlStatements = sqlContent
|
||||
.split('\n')
|
||||
.filter(line => !line.trim().startsWith('--') && line.trim() !== '')
|
||||
.join('\n')
|
||||
.split(';')
|
||||
.filter(statement => statement.trim() !== '');
|
||||
|
||||
console.log(`准备执行 ${sqlStatements.length} 条SQL语句`);
|
||||
|
||||
// 逐条执行SQL语句
|
||||
for (let i = 0; i < sqlStatements.length; i++) {
|
||||
const statement = sqlStatements[i].trim();
|
||||
if (statement) {
|
||||
try {
|
||||
console.log(`执行第 ${i + 1} 条SQL语句...`);
|
||||
await connection.execute(statement);
|
||||
console.log(`第 ${i + 1} 条SQL语句执行成功`);
|
||||
} catch (error) {
|
||||
console.error(`第 ${i + 1} 条SQL语句执行失败:`, error.message);
|
||||
console.error('SQL语句:', statement.substring(0, 100) + '...');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证表是否创建成功
|
||||
const [tables] = await connection.execute(`
|
||||
SELECT TABLE_NAME, TABLE_COMMENT
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = ? AND TABLE_NAME LIKE 'regulatory_task_%'
|
||||
`, [process.env.DB_DATABASE || 'nxxmdata']);
|
||||
|
||||
console.log('\n创建的监管任务结项相关表:');
|
||||
tables.forEach(table => {
|
||||
console.log(`- ${table.TABLE_NAME}: ${table.TABLE_COMMENT}`);
|
||||
});
|
||||
|
||||
// 查询示例数据
|
||||
const [sampleData] = await connection.execute(`
|
||||
SELECT application_no, policy_no, customer_name, completion_status
|
||||
FROM regulatory_task_completions
|
||||
LIMIT 3
|
||||
`);
|
||||
|
||||
console.log('\n示例数据:');
|
||||
console.table(sampleData);
|
||||
|
||||
console.log('\n监管任务结项表创建完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('创建表时发生错误:', error);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('数据库连接已关闭');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行创建表操作
|
||||
createRegulatoryTables();
|
||||
53
insurance_backend/scripts/execute_livestock_schema.js
Normal file
53
insurance_backend/scripts/execute_livestock_schema.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const { sequelize } = require('../config/database');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
async function executeLivestockSchema() {
|
||||
try {
|
||||
// 确保使用正确的数据库
|
||||
await sequelize.query('USE insurance_data');
|
||||
console.log('✅ 已切换到 insurance_data 数据库');
|
||||
|
||||
// 读取SQL文件
|
||||
const sqlFile = path.join(__dirname, 'livestock_insurance_schema.sql');
|
||||
const sqlContent = fs.readFileSync(sqlFile, 'utf8');
|
||||
|
||||
// 分割SQL语句(按分号分割,但忽略注释中的分号)
|
||||
const statements = sqlContent
|
||||
.split('\n')
|
||||
.filter(line => !line.trim().startsWith('--') && line.trim() !== '')
|
||||
.join('\n')
|
||||
.split(';')
|
||||
.filter(statement => statement.trim() !== '');
|
||||
|
||||
console.log('开始执行生资保单数据库表创建...');
|
||||
|
||||
// 逐个执行SQL语句
|
||||
for (let i = 0; i < statements.length; i++) {
|
||||
const statement = statements[i].trim();
|
||||
if (statement) {
|
||||
try {
|
||||
console.log(`执行语句 ${i + 1}/${statements.length}...`);
|
||||
await sequelize.query(statement);
|
||||
console.log(`✅ 语句 ${i + 1} 执行成功`);
|
||||
} catch (error) {
|
||||
console.error(`❌ 语句 ${i + 1} 执行失败:`, error.message);
|
||||
console.log('失败的语句:', statement.substring(0, 100) + '...');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ 生资保单数据库表创建完成');
|
||||
|
||||
// 验证表是否创建成功
|
||||
const [results] = await sequelize.query("SHOW TABLES LIKE 'livestock_%'");
|
||||
console.log('创建的表:', results.map(row => Object.values(row)[0]));
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 执行失败:', error.message);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
executeLivestockSchema();
|
||||
51
insurance_backend/scripts/fix-admin-permissions.js
Normal file
51
insurance_backend/scripts/fix-admin-permissions.js
Normal file
@@ -0,0 +1,51 @@
|
||||
console.log('🚀 开始修复admin权限...');
|
||||
|
||||
const { sequelize } = require('../config/database');
|
||||
const Role = require('../models/Role');
|
||||
|
||||
async function fixAdminPermissions() {
|
||||
try {
|
||||
console.log('📡 连接数据库...');
|
||||
|
||||
// 测试数据库连接
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功');
|
||||
|
||||
// 查询当前admin角色
|
||||
console.log('🔍 查询当前admin角色...');
|
||||
const adminRole = await Role.findOne({ where: { name: 'admin' } });
|
||||
|
||||
if (adminRole) {
|
||||
console.log('📋 当前admin角色权限:', adminRole.permissions);
|
||||
|
||||
// 更新admin角色权限,添加insurance相关权限
|
||||
console.log('🔧 更新admin角色权限...');
|
||||
const newPermissions = [
|
||||
'user:read', 'user:create', 'user:update', 'user:delete',
|
||||
'insurance:read', 'insurance:create', 'insurance:update', 'insurance:delete', 'insurance:review',
|
||||
'insurance_type:read', 'insurance_type:create', 'insurance_type:update', 'insurance_type:delete', 'insurance_type:review',
|
||||
'policy:read', 'policy:create', 'policy:update', 'policy:delete',
|
||||
'claim:read', 'claim:create', 'claim:update', 'claim:review',
|
||||
'system:read', 'system:update', 'system:admin'
|
||||
];
|
||||
|
||||
await adminRole.update({
|
||||
permissions: JSON.stringify(newPermissions)
|
||||
});
|
||||
|
||||
console.log('✅ admin角色权限更新成功');
|
||||
console.log('📋 新的admin权限:', newPermissions);
|
||||
} else {
|
||||
console.log('❌ 未找到admin角色');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 执行失败:', error.message);
|
||||
console.error('错误详情:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
console.log('\n✅ 数据库连接已关闭');
|
||||
}
|
||||
}
|
||||
|
||||
fixAdminPermissions().catch(console.error);
|
||||
49
insurance_backend/scripts/fix-permissions.js
Normal file
49
insurance_backend/scripts/fix-permissions.js
Normal file
@@ -0,0 +1,49 @@
|
||||
console.log('🚀 开始修复权限...');
|
||||
|
||||
const { sequelize } = require('../config/database');
|
||||
const Role = require('../models/Role');
|
||||
|
||||
async function fixPermissions() {
|
||||
try {
|
||||
console.log('📡 连接数据库...');
|
||||
|
||||
// 测试数据库连接
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功');
|
||||
|
||||
// 查询当前agent角色
|
||||
console.log('🔍 查询当前agent角色...');
|
||||
const agentRole = await Role.findOne({ where: { name: 'agent' } });
|
||||
|
||||
if (agentRole) {
|
||||
console.log('📋 当前agent角色权限:', agentRole.permissions);
|
||||
|
||||
// 更新agent角色权限
|
||||
console.log('🔧 更新agent角色权限...');
|
||||
await agentRole.update({
|
||||
permissions: JSON.stringify(['insurance:read', 'insurance:create', 'policy:read', 'policy:create', 'claim:read', 'claim:create'])
|
||||
});
|
||||
|
||||
console.log('✅ agent角色权限更新成功');
|
||||
} else {
|
||||
console.log('❌ 未找到agent角色');
|
||||
}
|
||||
|
||||
// 查询所有角色权限确认
|
||||
console.log('📋 查询当前所有角色权限配置...');
|
||||
const roles = await Role.findAll();
|
||||
console.log('\n当前角色权限配置:');
|
||||
roles.forEach(role => {
|
||||
console.log(`${role.name}: ${role.permissions}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 执行失败:', error.message);
|
||||
console.error('错误详情:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
console.log('\n✅ 数据库连接已关闭');
|
||||
}
|
||||
}
|
||||
|
||||
fixPermissions().catch(console.error);
|
||||
41
insurance_backend/scripts/insert_claims_only.js
Normal file
41
insurance_backend/scripts/insert_claims_only.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
async function insertClaimsData() {
|
||||
try {
|
||||
console.log('开始插入理赔数据...');
|
||||
|
||||
// 插入理赔数据
|
||||
await sequelize.query(`
|
||||
INSERT INTO livestock_claims (
|
||||
claim_no, policy_id, claim_type, incident_date, report_date,
|
||||
incident_description, incident_location, affected_count,
|
||||
claim_amount, claim_status, investigator_id,
|
||||
reviewer_id, created_by, updated_by
|
||||
) VALUES
|
||||
('CL202303050001', 1, 'disease', '2023-03-01', '2023-03-02', '牛群感染口蹄疫,导致5头牛死亡', '内蒙古自治区呼和浩特市赛罕区昭乌达路养殖场', 5, 35000.00, 'approved', 1, 1, 1, 1),
|
||||
('CL202303100002', 2, 'natural_disaster', '2023-03-08', '2023-03-09', '暴雪导致羊舍倒塌,造成10只羊死亡', '内蒙古自治区包头市昆都仑区钢铁大街养殖场', 10, 10000.00, 'investigating', 1, NULL, 1, 1),
|
||||
('CL202302200003', 4, 'accident', '2023-02-18', '2023-02-19', '运输车辆事故,导致3头牛受伤', '内蒙古自治区通辽市科尔沁区建国路', 3, 12000.00, 'pending', NULL, NULL, 1, 1)
|
||||
`);
|
||||
console.log('✅ 理赔数据插入成功');
|
||||
|
||||
// 验证数据插入结果
|
||||
const [claimCount] = await sequelize.query("SELECT COUNT(*) as count FROM livestock_claims");
|
||||
console.log(`数据库中共有 ${claimCount[0].count} 条理赔记录`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 插入理赔数据失败:', error.message);
|
||||
if (error.errors) {
|
||||
console.error('详细错误信息:');
|
||||
error.errors.forEach(err => {
|
||||
console.error(`- ${err.path}: ${err.message}`);
|
||||
});
|
||||
}
|
||||
if (error.sql) {
|
||||
console.error('SQL语句:', error.sql);
|
||||
}
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
insertClaimsData();
|
||||
@@ -4,13 +4,13 @@ SET CHARACTER SET utf8mb4;
|
||||
-- 更新默认角色权限
|
||||
UPDATE roles SET
|
||||
description = '系统管理员',
|
||||
permissions = '["user:read","user:create","user:update","user:delete","insurance:read","insurance:create","insurance:update","insurance:delete","insurance:review","policy:read","policy:create","policy:update","policy:delete","claim:read","claim:create","claim:update","claim:review","system:read","system:update","system:admin"]'
|
||||
permissions = '["user:read","user:create","user:update","user:delete","insurance:read","insurance:create","insurance:update","insurance:delete","insurance:review","policy:read","policy:create","policy:update","policy:delete","livestock_policy:read","livestock_policy:create","livestock_policy:update","livestock_policy:delete","claim:read","claim:create","claim:update","claim:review","system:read","system:update","system:admin"]'
|
||||
WHERE name = 'admin';
|
||||
|
||||
-- 插入其他角色(如果不存在)
|
||||
INSERT IGNORE INTO roles (name, description, permissions, status) VALUES
|
||||
('agent', '保险代理人', '["insurance:read","insurance:create","policy:read","policy:create","claim:read","claim:create"]', 'active'),
|
||||
('customer', '客户', '["insurance:read","policy:read","claim:read","claim:create"]', 'active');
|
||||
('agent', '保险代理人', '["insurance:read","insurance:create","policy:read","policy:create","livestock_policy:read","livestock_policy:create","livestock_policy:update","claim:read","claim:create"]', 'active'),
|
||||
('customer', '客户', '["insurance:read","policy:read","livestock_policy:read","claim:read","claim:create"]', 'active');
|
||||
|
||||
-- 插入默认保险类型
|
||||
INSERT IGNORE INTO insurance_types (name, description, coverage_amount_min, coverage_amount_max, premium_rate, status) VALUES
|
||||
|
||||
235
insurance_backend/scripts/insert_livestock_test_data.js
Normal file
235
insurance_backend/scripts/insert_livestock_test_data.js
Normal file
@@ -0,0 +1,235 @@
|
||||
const { sequelize } = require('../models');
|
||||
const { LivestockType, LivestockPolicy, LivestockClaim, User } = require('../models');
|
||||
|
||||
async function insertTestData() {
|
||||
try {
|
||||
console.log('开始插入生资保险测试数据...');
|
||||
|
||||
// 创建测试用户(如果不存在)
|
||||
const [testUser] = await User.findOrCreate({
|
||||
where: { username: 'test_admin' },
|
||||
defaults: {
|
||||
username: 'test_admin',
|
||||
password: '$2b$10$example', // 示例密码哈希
|
||||
real_name: '测试管理员',
|
||||
email: 'test@example.com',
|
||||
phone: '13800138000',
|
||||
role_id: 1, // admin角色ID
|
||||
status: 'active'
|
||||
}
|
||||
});
|
||||
|
||||
// 插入牲畜类型测试数据
|
||||
const livestockTypes = [
|
||||
{
|
||||
name: '奶牛',
|
||||
code: 'DAIRY_COW',
|
||||
description: '用于产奶的牛类',
|
||||
unit_price_min: 10000.00,
|
||||
unit_price_max: 20000.00,
|
||||
premium_rate: 0.05,
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
name: '肉牛',
|
||||
code: 'BEEF_COW',
|
||||
description: '用于肉类生产的牛类',
|
||||
unit_price_min: 8000.00,
|
||||
unit_price_max: 16000.00,
|
||||
premium_rate: 0.04,
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
name: '生猪',
|
||||
code: 'PIG',
|
||||
description: '用于肉类生产的猪类',
|
||||
unit_price_min: 1500.00,
|
||||
unit_price_max: 2500.00,
|
||||
premium_rate: 0.06,
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
name: '羊',
|
||||
code: 'SHEEP',
|
||||
description: '包括山羊和绵羊',
|
||||
unit_price_min: 600.00,
|
||||
unit_price_max: 1000.00,
|
||||
premium_rate: 0.05,
|
||||
status: 'active'
|
||||
}
|
||||
];
|
||||
|
||||
console.log('插入牲畜类型数据...');
|
||||
const createdTypes = await LivestockType.bulkCreate(livestockTypes, {
|
||||
returning: true
|
||||
});
|
||||
console.log(`成功插入 ${createdTypes.length} 个牲畜类型`);
|
||||
|
||||
// 插入生资保单测试数据
|
||||
const policies = [
|
||||
{
|
||||
policy_no: 'LP2024001',
|
||||
farmer_name: '张三',
|
||||
farmer_phone: '13800138001',
|
||||
farmer_id_card: '110101199001011234',
|
||||
livestock_type_id: createdTypes[0].id, // 奶牛
|
||||
livestock_count: 10,
|
||||
unit_value: 15000.00,
|
||||
total_value: 150000.00,
|
||||
premium_rate: 0.05,
|
||||
premium_amount: 7500.00,
|
||||
start_date: '2024-01-01',
|
||||
end_date: '2024-12-31',
|
||||
farm_name: '张三奶牛养殖场',
|
||||
farm_address: '北京市朝阳区某某村123号',
|
||||
farm_license: 'BJ2024001',
|
||||
policy_status: 'active',
|
||||
payment_status: 'paid',
|
||||
payment_date: '2024-01-01',
|
||||
notes: '优质奶牛保险',
|
||||
created_by: testUser.id,
|
||||
updated_by: testUser.id
|
||||
},
|
||||
{
|
||||
policy_no: 'LP2024002',
|
||||
farmer_name: '李四',
|
||||
farmer_phone: '13800138002',
|
||||
farmer_id_card: '110101199002021234',
|
||||
livestock_type_id: createdTypes[1].id, // 肉牛
|
||||
livestock_count: 20,
|
||||
unit_value: 12000.00,
|
||||
total_value: 240000.00,
|
||||
premium_rate: 0.04,
|
||||
premium_amount: 9600.00,
|
||||
start_date: '2024-02-01',
|
||||
end_date: '2025-01-31',
|
||||
farm_name: '李四肉牛养殖场',
|
||||
farm_address: '北京市海淀区某某村456号',
|
||||
farm_license: 'BJ2024002',
|
||||
policy_status: 'active',
|
||||
payment_status: 'paid',
|
||||
payment_date: '2024-02-01',
|
||||
notes: '肉牛养殖保险',
|
||||
created_by: testUser.id,
|
||||
updated_by: testUser.id
|
||||
},
|
||||
{
|
||||
policy_no: 'LP2024003',
|
||||
farmer_name: '王五',
|
||||
farmer_phone: '13800138003',
|
||||
farmer_id_card: '110101199003031234',
|
||||
livestock_type_id: createdTypes[2].id, // 生猪
|
||||
livestock_count: 100,
|
||||
unit_value: 2000.00,
|
||||
total_value: 200000.00,
|
||||
premium_rate: 0.06,
|
||||
premium_amount: 12000.00,
|
||||
start_date: '2024-03-01',
|
||||
end_date: '2025-02-28',
|
||||
farm_name: '王五生猪养殖场',
|
||||
farm_address: '北京市昌平区某某村789号',
|
||||
farm_license: 'BJ2024003',
|
||||
policy_status: 'active',
|
||||
payment_status: 'unpaid',
|
||||
notes: '生猪养殖保险',
|
||||
created_by: testUser.id,
|
||||
updated_by: testUser.id
|
||||
},
|
||||
{
|
||||
policy_no: 'LP2024004',
|
||||
farmer_name: '赵六',
|
||||
farmer_phone: '13800138004',
|
||||
farmer_id_card: '110101199004041234',
|
||||
livestock_type_id: createdTypes[3].id, // 羊
|
||||
livestock_count: 50,
|
||||
unit_value: 800.00,
|
||||
total_value: 40000.00,
|
||||
premium_rate: 0.05,
|
||||
premium_amount: 2000.00,
|
||||
start_date: '2024-04-01',
|
||||
end_date: '2025-03-31',
|
||||
farm_name: '赵六羊群养殖场',
|
||||
farm_address: '北京市房山区某某村101号',
|
||||
farm_license: 'BJ2024004',
|
||||
policy_status: 'active',
|
||||
payment_status: 'partial',
|
||||
payment_date: '2024-04-01',
|
||||
notes: '羊群保险',
|
||||
created_by: testUser.id,
|
||||
updated_by: testUser.id
|
||||
}
|
||||
];
|
||||
|
||||
console.log('插入生资保单数据...');
|
||||
const createdPolicies = await LivestockPolicy.bulkCreate(policies, {
|
||||
returning: true
|
||||
});
|
||||
console.log(`成功插入 ${createdPolicies.length} 个生资保单`);
|
||||
|
||||
// 插入生资理赔测试数据
|
||||
const claims = [
|
||||
{
|
||||
claim_no: 'LC2024001',
|
||||
policy_id: createdPolicies[0].id,
|
||||
claim_amount: 30000.00,
|
||||
incident_date: '2024-06-15',
|
||||
incident_description: '奶牛因疾病死亡2头',
|
||||
claim_status: 'paid',
|
||||
investigator_id: testUser.id,
|
||||
investigation_report: '经现场调查,确认奶牛因疫病死亡,符合保险条款',
|
||||
investigation_date: '2024-06-20',
|
||||
reviewer_id: testUser.id,
|
||||
review_notes: '经核实,符合理赔条件',
|
||||
review_date: '2024-06-25',
|
||||
payment_date: '2024-07-01',
|
||||
payment_amount: 30000.00,
|
||||
supporting_documents: JSON.stringify([
|
||||
{ type: 'death_certificate', url: '/documents/death_cert_001.pdf' },
|
||||
{ type: 'veterinary_report', url: '/documents/vet_report_001.pdf' }
|
||||
]),
|
||||
created_by: testUser.id,
|
||||
updated_by: testUser.id
|
||||
},
|
||||
{
|
||||
claim_no: 'LC2024002',
|
||||
policy_id: createdPolicies[1].id,
|
||||
claim_amount: 24000.00,
|
||||
incident_date: '2024-07-20',
|
||||
incident_description: '肉牛因自然灾害死亡2头',
|
||||
claim_status: 'investigating',
|
||||
investigator_id: testUser.id,
|
||||
investigation_report: '正在进行现场调查',
|
||||
investigation_date: '2024-07-25',
|
||||
supporting_documents: JSON.stringify([
|
||||
{ type: 'incident_report', url: '/documents/incident_002.pdf' }
|
||||
]),
|
||||
created_by: testUser.id,
|
||||
updated_by: testUser.id
|
||||
}
|
||||
];
|
||||
|
||||
console.log('插入生资理赔数据...');
|
||||
const createdClaims = await LivestockClaim.bulkCreate(claims, {
|
||||
returning: true
|
||||
});
|
||||
console.log(`成功插入 ${createdClaims.length} 个生资理赔记录`);
|
||||
|
||||
console.log('生资保险测试数据插入完成!');
|
||||
console.log('数据统计:');
|
||||
console.log(`- 牲畜类型: ${createdTypes.length} 个`);
|
||||
console.log(`- 生资保单: ${createdPolicies.length} 个`);
|
||||
console.log(`- 理赔记录: ${createdClaims.length} 个`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('插入测试数据失败:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接运行此脚本
|
||||
if (require.main === module) {
|
||||
insertTestData();
|
||||
}
|
||||
|
||||
module.exports = insertTestData;
|
||||
80
insurance_backend/scripts/insert_livestock_test_data.sql
Normal file
80
insurance_backend/scripts/insert_livestock_test_data.sql
Normal file
@@ -0,0 +1,80 @@
|
||||
-- 插入生资保险测试数据
|
||||
|
||||
-- 插入牲畜类型测试数据
|
||||
INSERT INTO livestock_types (name, code, description, unit_price_min, unit_price_max, premium_rate, status) VALUES
|
||||
('奶牛', 'DAIRY_COW', '用于产奶的牛类', 10000.00, 20000.00, 0.0500, 'active'),
|
||||
('肉牛', 'BEEF_COW', '用于肉类生产的牛类', 8000.00, 16000.00, 0.0400, 'active'),
|
||||
('生猪', 'PIG', '用于肉类生产的猪类', 1500.00, 2500.00, 0.0600, 'active'),
|
||||
('羊', 'SHEEP', '包括山羊和绵羊', 600.00, 1000.00, 0.0500, 'active');
|
||||
|
||||
-- 插入生资保单测试数据
|
||||
INSERT INTO livestock_policies (
|
||||
policy_no, farmer_name, farmer_phone, farmer_id_card, livestock_type_id,
|
||||
livestock_count, unit_value, total_value, premium_rate, premium_amount,
|
||||
start_date, end_date, farm_name, farm_address, farm_license,
|
||||
policy_status, payment_status, payment_date, notes, created_by, updated_by
|
||||
) VALUES
|
||||
(
|
||||
'LP2024001', '张三', '13800138001', '110101199001011234',
|
||||
(SELECT id FROM livestock_types WHERE code = 'DAIRY_COW'),
|
||||
10, 15000.00, 150000.00, 0.0500, 7500.00,
|
||||
'2024-01-01', '2024-12-31', '张三奶牛养殖场', '北京市朝阳区某某村123号', 'BJ2024001',
|
||||
'active', 'paid', '2024-01-01', '优质奶牛保险', 1, 1
|
||||
),
|
||||
(
|
||||
'LP2024002', '李四', '13800138002', '110101199002021234',
|
||||
(SELECT id FROM livestock_types WHERE code = 'BEEF_COW'),
|
||||
20, 12000.00, 240000.00, 0.0400, 9600.00,
|
||||
'2024-02-01', '2025-01-31', '李四肉牛养殖场', '北京市海淀区某某村456号', 'BJ2024002',
|
||||
'active', 'paid', '2024-02-01', '肉牛养殖保险', 1, 1
|
||||
),
|
||||
(
|
||||
'LP2024003', '王五', '13800138003', '110101199003031234',
|
||||
(SELECT id FROM livestock_types WHERE code = 'PIG'),
|
||||
100, 2000.00, 200000.00, 0.0600, 12000.00,
|
||||
'2024-03-01', '2025-02-28', '王五生猪养殖场', '北京市昌平区某某村789号', 'BJ2024003',
|
||||
'active', 'unpaid', NULL, '生猪养殖保险', 1, 1
|
||||
),
|
||||
(
|
||||
'LP2024004', '赵六', '13800138004', '110101199004041234',
|
||||
(SELECT id FROM livestock_types WHERE code = 'SHEEP'),
|
||||
50, 800.00, 40000.00, 0.0500, 2000.00,
|
||||
'2024-04-01', '2025-03-31', '赵六羊群养殖场', '北京市房山区某某村101号', 'BJ2024004',
|
||||
'active', 'partial', '2024-04-01', '羊群保险', 1, 1
|
||||
);
|
||||
|
||||
-- 插入生资理赔测试数据
|
||||
INSERT INTO livestock_claims (
|
||||
claim_no, policy_id, claim_amount, incident_date, incident_description,
|
||||
claim_status, investigator_id, investigation_report, investigation_date,
|
||||
reviewer_id, review_notes, review_date, payment_date, payment_amount,
|
||||
supporting_documents, created_by, updated_by
|
||||
) VALUES
|
||||
(
|
||||
'LC2024001',
|
||||
(SELECT id FROM livestock_policies WHERE policy_no = 'LP2024001'),
|
||||
30000.00, '2024-06-15', '奶牛因疾病死亡2头',
|
||||
'paid', 1, '经现场调查,确认奶牛因疫病死亡,符合保险条款', '2024-06-20',
|
||||
1, '经核实,符合理赔条件', '2024-06-25', '2024-07-01', 30000.00,
|
||||
'[{"type":"death_certificate","url":"/documents/death_cert_001.pdf"},{"type":"veterinary_report","url":"/documents/vet_report_001.pdf"}]',
|
||||
1, 1
|
||||
),
|
||||
(
|
||||
'LC2024002',
|
||||
(SELECT id FROM livestock_policies WHERE policy_no = 'LP2024002'),
|
||||
24000.00, '2024-07-20', '肉牛因自然灾害死亡2头',
|
||||
'investigating', 1, '正在进行现场调查', '2024-07-25',
|
||||
NULL, NULL, NULL, NULL, NULL,
|
||||
'[{"type":"incident_report","url":"/documents/incident_002.pdf"}]',
|
||||
1, 1
|
||||
);
|
||||
|
||||
-- 查询插入结果
|
||||
SELECT '牲畜类型数据:' as info;
|
||||
SELECT id, name, code, unit_price_min, unit_price_max, premium_rate, status FROM livestock_types;
|
||||
|
||||
SELECT '生资保单数据:' as info;
|
||||
SELECT id, policy_no, farmer_name, livestock_count, total_value, policy_status, payment_status FROM livestock_policies;
|
||||
|
||||
SELECT '理赔记录数据:' as info;
|
||||
SELECT id, claim_no, claim_amount, incident_date, claim_status FROM livestock_claims;
|
||||
81
insurance_backend/scripts/insert_test_data.js
Normal file
81
insurance_backend/scripts/insert_test_data.js
Normal file
@@ -0,0 +1,81 @@
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
async function insertTestData() {
|
||||
try {
|
||||
// 确保使用正确的数据库
|
||||
await sequelize.query('USE insurance_data');
|
||||
console.log('✅ 已切换到 insurance_data 数据库');
|
||||
|
||||
console.log('开始插入测试数据...');
|
||||
|
||||
// 1. 插入牲畜类型数据
|
||||
await sequelize.query(`
|
||||
INSERT IGNORE INTO livestock_types (name, code, description, unit_price_min, unit_price_max, premium_rate, status)
|
||||
VALUES
|
||||
('牛', 'CATTLE', '肉牛、奶牛等牛类牲畜', 5000.00, 30000.00, 0.0050, 'active'),
|
||||
('羊', 'SHEEP', '绵羊、山羊等羊类牲畜', 500.00, 3000.00, 0.0060, 'active'),
|
||||
('猪', 'PIG', '生猪、种猪等猪类牲畜', 1000.00, 5000.00, 0.0040, 'active'),
|
||||
('鸡', 'CHICKEN', '肉鸡、蛋鸡等鸡类家禽', 20.00, 100.00, 0.0080, 'active'),
|
||||
('鸭', 'DUCK', '肉鸭、蛋鸭等鸭类家禽', 30.00, 150.00, 0.0070, 'active')
|
||||
`);
|
||||
console.log('✅ 牲畜类型数据插入成功');
|
||||
|
||||
// 2. 插入生资保单数据
|
||||
await sequelize.query(`
|
||||
INSERT INTO livestock_policies (
|
||||
policy_no, livestock_type_id, policyholder_name, policyholder_id_card, policyholder_phone,
|
||||
policyholder_address, farm_address, livestock_count, unit_value, total_value,
|
||||
premium_amount, start_date, end_date, policy_status,
|
||||
created_by, updated_by
|
||||
) VALUES
|
||||
('202209312456789', 1, '张三', '150102198001011234', '13800138001', '内蒙古自治区呼和浩特市赛罕区昭乌达路', '内蒙古自治区呼和浩特市赛罕区昭乌达路养殖场', 50, 8000.00, 400000.00, 2000.00, '2023-03-05', '2024-03-04', 'active', 1, 1),
|
||||
('202209314567890', 2, '李四', '150203198002022345', '13800138002', '内蒙古自治区包头市昆都仑区钢铁大街', '内蒙古自治区包头市昆都仑区钢铁大街养殖场', 100, 1200.00, 120000.00, 720.00, '2023-03-10', '2024-03-09', 'active', 1, 1),
|
||||
('202209315432100', 3, '王五', '150602198003033456', '13800138003', '内蒙古自治区鄂尔多斯市东胜区铁西路', '内蒙古自治区鄂尔多斯市东胜区铁西路养殖场', 200, 2500.00, 500000.00, 2000.00, '2023-02-15', '2024-02-14', 'active', 1, 1),
|
||||
('202203316542100', 1, '赵六', '150502198004044567', '13800138004', '内蒙古自治区通辽市科尔沁区建国路', '内蒙古自治区通辽市科尔沁区建国路养殖场', 30, 12000.00, 360000.00, 1800.00, '2023-02-20', '2024-02-19', 'active', 1, 1),
|
||||
('202203311087654', 2, '孙七', '150802198005055678', '13800138005', '内蒙古自治区巴彦淖尔市临河区胜利路', '内蒙古自治区巴彦淖尔市临河区胜利路养殖场', 80, 1500.00, 120000.00, 720.00, '2023-01-30', '2024-01-29', 'active', 1, 1),
|
||||
('202203178001234', 4, '周八', '150902198006066789', '13800138006', '内蒙古自治区乌兰察布市集宁区新华街', '内蒙古自治区乌兰察布市集宁区新华街养殖场', 1000, 50.00, 50000.00, 400.00, '2023-01-15', '2024-01-14', 'active', 1, 1),
|
||||
('202203112345678', 5, '吴九', '152502198007077890', '13800138007', '内蒙古自治区锡林郭勒盟锡林浩特市额尔敦路', '内蒙古自治区锡林郭勒盟锡林浩特市额尔敦路养殖场', 800, 80.00, 64000.00, 448.00, '2023-01-10', '2024-01-09', 'active', 1, 1),
|
||||
('202203134567890', 1, '郑十', '150702198008088901', '13800138008', '内蒙古自治区呼伦贝尔市海拉尔区胜利大街', '内蒙古自治区呼伦贝尔市海拉尔区胜利大街养殖场', 25, 15000.00, 375000.00, 1875.00, '2022-12-20', '2023-12-19', 'expired', 1, 1),
|
||||
('202203154321098', 3, '陈十一', '150402198009099012', '13800138009', '内蒙古自治区赤峰市红山区钢铁街', '内蒙古自治区赤峰市红山区钢铁街养殖场', 150, 3000.00, 450000.00, 1800.00, '2022-12-15', '2023-12-14', 'expired', 1, 1)
|
||||
`);
|
||||
console.log('✅ 生资保单数据插入成功');
|
||||
|
||||
// 3. 插入理赔数据
|
||||
await sequelize.query(`
|
||||
INSERT INTO livestock_claims (
|
||||
claim_no, policy_id, claim_type, incident_date, report_date,
|
||||
incident_description, incident_location, affected_count,
|
||||
claim_amount, claim_status, investigator_id,
|
||||
reviewer_id, created_by, updated_by
|
||||
) VALUES
|
||||
('CL202303050001', 1, 'disease', '2023-03-01', '2023-03-02', '牛群感染口蹄疫,导致5头牛死亡', '内蒙古自治区呼和浩特市赛罕区昭乌达路养殖场', 5, 35000.00, 'approved', 1, 1, 1, 1),
|
||||
('CL202303100002', 2, 'natural_disaster', '2023-03-08', '2023-03-09', '暴雪导致羊舍倒塌,造成10只羊死亡', '内蒙古自治区包头市昆都仑区钢铁大街养殖场', 10, 10000.00, 'investigating', 1, NULL, 1, 1),
|
||||
('CL202302200003', 4, 'accident', '2023-02-18', '2023-02-19', '运输车辆事故,导致3头牛受伤', '内蒙古自治区通辽市科尔沁区建国路', 3, 12000.00, 'pending', NULL, NULL, 1, 1)
|
||||
`);
|
||||
console.log('✅ 理赔数据插入成功');
|
||||
|
||||
// 验证数据插入结果
|
||||
const [policyCount] = await sequelize.query("SELECT COUNT(*) as count FROM livestock_policies");
|
||||
const [claimCount] = await sequelize.query("SELECT COUNT(*) as count FROM livestock_claims");
|
||||
const [typeCount] = await sequelize.query("SELECT COUNT(*) as count FROM livestock_types");
|
||||
|
||||
console.log(`✅ 数据插入完成:`);
|
||||
console.log(` - 牲畜类型: ${typeCount[0].count} 条`);
|
||||
console.log(` - 生资保单: ${policyCount[0].count} 条`);
|
||||
console.log(` - 理赔记录: ${claimCount[0].count} 条`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 插入测试数据失败:', error.message);
|
||||
if (error.errors) {
|
||||
console.error('详细错误信息:');
|
||||
error.errors.forEach(err => {
|
||||
console.error(`- ${err.path}: ${err.message}`);
|
||||
});
|
||||
}
|
||||
console.error('完整错误:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
insertTestData();
|
||||
129
insurance_backend/scripts/livestock_insurance_schema.sql
Normal file
129
insurance_backend/scripts/livestock_insurance_schema.sql
Normal file
@@ -0,0 +1,129 @@
|
||||
-- 生资保单相关数据库表结构
|
||||
-- 创建时间: 2024-01-XX
|
||||
-- 说明: 包含牲畜类型、生资保单、理赔记录等表
|
||||
|
||||
USE insurance_data;
|
||||
|
||||
-- 1. 生资保险类型表(牲畜类型)
|
||||
CREATE TABLE IF NOT EXISTS livestock_types (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '牲畜类型ID',
|
||||
name VARCHAR(50) NOT NULL UNIQUE COMMENT '牲畜类型名称(如:牛、羊、猪等)',
|
||||
code VARCHAR(20) NOT NULL UNIQUE COMMENT '牲畜类型代码',
|
||||
description TEXT NULL COMMENT '牲畜类型描述',
|
||||
unit_price_min DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '单头最低价值',
|
||||
unit_price_max DECIMAL(10,2) NOT NULL DEFAULT 50000.00 COMMENT '单头最高价值',
|
||||
premium_rate DECIMAL(5,4) NOT NULL DEFAULT 0.0050 COMMENT '保险费率',
|
||||
status ENUM('active', 'inactive') NOT NULL DEFAULT 'active' COMMENT '状态',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
|
||||
INDEX idx_livestock_type_name (name),
|
||||
INDEX idx_livestock_type_code (code),
|
||||
INDEX idx_livestock_type_status (status)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='牲畜类型表';
|
||||
|
||||
-- 2. 生资保单表
|
||||
CREATE TABLE IF NOT EXISTS livestock_policies (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '生资保单ID',
|
||||
policy_no VARCHAR(50) NOT NULL UNIQUE COMMENT '保单编号',
|
||||
policyholder_name VARCHAR(100) NOT NULL COMMENT '投保人姓名',
|
||||
policyholder_id_card VARCHAR(18) NOT NULL COMMENT '投保人身份证号',
|
||||
policyholder_phone VARCHAR(20) NOT NULL COMMENT '投保人手机号',
|
||||
policyholder_address VARCHAR(500) NOT NULL COMMENT '投保人地址',
|
||||
|
||||
livestock_type_id INT NOT NULL COMMENT '牲畜类型ID',
|
||||
livestock_count INT NOT NULL DEFAULT 1 COMMENT '参保数量',
|
||||
unit_value DECIMAL(10,2) NOT NULL COMMENT '单头价值',
|
||||
total_value DECIMAL(15,2) NOT NULL COMMENT '总保额',
|
||||
premium_amount DECIMAL(10,2) NOT NULL COMMENT '保费金额',
|
||||
|
||||
start_date DATE NOT NULL COMMENT '保险开始日期',
|
||||
end_date DATE NOT NULL COMMENT '保险结束日期',
|
||||
|
||||
farm_name VARCHAR(200) NULL COMMENT '养殖场名称',
|
||||
farm_address VARCHAR(500) NOT NULL COMMENT '养殖场地址',
|
||||
farm_license VARCHAR(100) NULL COMMENT '养殖许可证号',
|
||||
|
||||
policy_status ENUM('active', 'expired', 'cancelled', 'suspended') NOT NULL DEFAULT 'active' COMMENT '保单状态',
|
||||
payment_status ENUM('paid', 'unpaid', 'partial') NOT NULL DEFAULT 'unpaid' COMMENT '支付状态',
|
||||
payment_date DATE NULL COMMENT '支付日期',
|
||||
|
||||
policy_document_url VARCHAR(500) NULL COMMENT '保单文件URL',
|
||||
notes TEXT NULL COMMENT '备注信息',
|
||||
|
||||
created_by INT NULL COMMENT '创建人ID',
|
||||
updated_by INT NULL COMMENT '更新人ID',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
|
||||
UNIQUE INDEX idx_livestock_policy_no (policy_no),
|
||||
INDEX idx_livestock_policy_holder (policyholder_name),
|
||||
INDEX idx_livestock_policy_id_card (policyholder_id_card),
|
||||
INDEX idx_livestock_policy_phone (policyholder_phone),
|
||||
INDEX idx_livestock_policy_status (policy_status),
|
||||
INDEX idx_livestock_policy_payment_status (payment_status),
|
||||
INDEX idx_livestock_policy_dates (start_date, end_date),
|
||||
INDEX idx_livestock_policy_type (livestock_type_id),
|
||||
|
||||
FOREIGN KEY (livestock_type_id) REFERENCES livestock_types(id) ON DELETE RESTRICT,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='生资保单表';
|
||||
|
||||
-- 3. 生资保单理赔表
|
||||
CREATE TABLE IF NOT EXISTS livestock_claims (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '理赔ID',
|
||||
claim_no VARCHAR(50) NOT NULL UNIQUE COMMENT '理赔编号',
|
||||
policy_id INT NOT NULL COMMENT '关联的生资保单ID',
|
||||
|
||||
claim_type ENUM('death', 'disease', 'accident', 'natural_disaster') NOT NULL COMMENT '理赔类型',
|
||||
affected_count INT NOT NULL DEFAULT 1 COMMENT '受损数量',
|
||||
claim_amount DECIMAL(15,2) NOT NULL COMMENT '理赔金额',
|
||||
|
||||
incident_date DATE NOT NULL COMMENT '事故发生日期',
|
||||
report_date DATE NOT NULL COMMENT '报案日期',
|
||||
incident_description TEXT NOT NULL COMMENT '事故描述',
|
||||
incident_location VARCHAR(500) NOT NULL COMMENT '事故地点',
|
||||
|
||||
claim_status ENUM('pending', 'investigating', 'approved', 'rejected', 'paid') NOT NULL DEFAULT 'pending' COMMENT '理赔状态',
|
||||
|
||||
investigator_id INT NULL COMMENT '调查员ID',
|
||||
investigation_report TEXT NULL COMMENT '调查报告',
|
||||
investigation_date DATE NULL COMMENT '调查日期',
|
||||
|
||||
reviewer_id INT NULL COMMENT '审核人ID',
|
||||
review_notes TEXT NULL COMMENT '审核备注',
|
||||
review_date DATE NULL COMMENT '审核日期',
|
||||
|
||||
payment_date DATE NULL COMMENT '支付日期',
|
||||
payment_amount DECIMAL(15,2) NULL COMMENT '实际支付金额',
|
||||
|
||||
supporting_documents JSON NULL COMMENT '支持文件',
|
||||
|
||||
created_by INT NULL COMMENT '创建人ID',
|
||||
updated_by INT NULL COMMENT '更新人ID',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
|
||||
UNIQUE INDEX idx_livestock_claim_no (claim_no),
|
||||
INDEX idx_livestock_claim_policy (policy_id),
|
||||
INDEX idx_livestock_claim_status (claim_status),
|
||||
INDEX idx_livestock_claim_type (claim_type),
|
||||
INDEX idx_livestock_claim_date (incident_date),
|
||||
INDEX idx_livestock_claim_investigator (investigator_id),
|
||||
INDEX idx_livestock_claim_reviewer (reviewer_id),
|
||||
|
||||
FOREIGN KEY (policy_id) REFERENCES livestock_policies(id) ON DELETE RESTRICT,
|
||||
FOREIGN KEY (investigator_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (reviewer_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='生资保单理赔表';
|
||||
|
||||
-- 插入默认牲畜类型数据
|
||||
INSERT INTO livestock_types (name, code, description, unit_price_min, unit_price_max, premium_rate, status) VALUES
|
||||
('牛', 'CATTLE', '肉牛、奶牛等牛类牲畜', 5000.00, 30000.00, 0.0050, 'active'),
|
||||
('羊', 'SHEEP', '绵羊、山羊等羊类牲畜', 500.00, 3000.00, 0.0060, 'active'),
|
||||
('猪', 'PIG', '生猪、种猪等猪类牲畜', 1000.00, 5000.00, 0.0040, 'active'),
|
||||
('鸡', 'CHICKEN', '肉鸡、蛋鸡等鸡类家禽', 20.00, 100.00, 0.0080, 'active'),
|
||||
('鸭', 'DUCK', '肉鸭、蛋鸭等鸭类家禽', 30.00, 150.00, 0.0070, 'active');
|
||||
108
insurance_backend/scripts/regulatory_task_completion.sql
Normal file
108
insurance_backend/scripts/regulatory_task_completion.sql
Normal file
@@ -0,0 +1,108 @@
|
||||
-- 监管任务结项功能数据库表结构
|
||||
-- 创建时间: 2025-01-23
|
||||
-- 数据库: MySQL 8.0+
|
||||
|
||||
USE nxxmdata;
|
||||
|
||||
-- 1. 监管任务结项表
|
||||
CREATE TABLE IF NOT EXISTS regulatory_task_completions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '结项ID',
|
||||
application_no VARCHAR(50) NOT NULL COMMENT '申请编号',
|
||||
policy_no VARCHAR(50) NOT NULL COMMENT '保单编号',
|
||||
product_name VARCHAR(100) NOT NULL COMMENT '产品名称',
|
||||
customer_name VARCHAR(100) NOT NULL COMMENT '客户姓名',
|
||||
id_type ENUM('身份证', '护照', '军官证', '其他') NOT NULL DEFAULT '身份证' COMMENT '证件类型',
|
||||
id_number VARCHAR(50) NOT NULL COMMENT '证件号码',
|
||||
product_type_premium DECIMAL(15,2) NOT NULL COMMENT '产品类型保费',
|
||||
regulatory_effective_premium DECIMAL(15,2) NOT NULL COMMENT '监管生效保费',
|
||||
insurance_period VARCHAR(100) NOT NULL COMMENT '保险期间',
|
||||
insurance_amount DECIMAL(15,2) NOT NULL COMMENT '保险金额',
|
||||
payment_method VARCHAR(50) NOT NULL COMMENT '缴费方式',
|
||||
payment_period VARCHAR(50) NOT NULL COMMENT '缴费期间',
|
||||
|
||||
-- 结项相关字段
|
||||
completion_status ENUM('pending', 'in_progress', 'completed', 'rejected') NOT NULL DEFAULT 'pending' COMMENT '结项状态',
|
||||
completion_date DATE NULL COMMENT '结项日期',
|
||||
completion_notes TEXT NULL COMMENT '结项备注',
|
||||
completion_documents JSON NULL COMMENT '结项文档',
|
||||
|
||||
-- 审核相关字段
|
||||
reviewer_id INT NULL COMMENT '审核人ID',
|
||||
review_date DATE NULL COMMENT '审核日期',
|
||||
review_notes TEXT NULL COMMENT '审核备注',
|
||||
|
||||
-- 关联字段(暂不设置外键约束)
|
||||
policy_id INT NULL COMMENT '关联保单ID',
|
||||
application_id INT NULL COMMENT '关联申请ID',
|
||||
insurance_type_id INT NULL COMMENT '保险类型ID',
|
||||
customer_id INT NULL COMMENT '客户ID',
|
||||
|
||||
-- 系统字段
|
||||
created_by INT NULL COMMENT '创建人ID',
|
||||
updated_by INT NULL COMMENT '更新人ID',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
|
||||
-- 索引
|
||||
UNIQUE INDEX idx_application_no (application_no),
|
||||
INDEX idx_policy_no (policy_no),
|
||||
INDEX idx_customer_name (customer_name),
|
||||
INDEX idx_id_number (id_number),
|
||||
INDEX idx_completion_status (completion_status),
|
||||
INDEX idx_completion_date (completion_date),
|
||||
INDEX idx_reviewer_id (reviewer_id),
|
||||
INDEX idx_policy_id (policy_id),
|
||||
INDEX idx_application_id (application_id),
|
||||
INDEX idx_customer_id (customer_id),
|
||||
INDEX idx_created_at (created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='监管任务结项表';
|
||||
|
||||
-- 2. 监管任务结项操作日志表
|
||||
CREATE TABLE IF NOT EXISTS regulatory_task_completion_logs (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '日志ID',
|
||||
completion_id INT NOT NULL COMMENT '结项ID',
|
||||
operation_type ENUM('create', 'update', 'review', 'approve', 'reject', 'complete') NOT NULL COMMENT '操作类型',
|
||||
operation_description TEXT NOT NULL COMMENT '操作描述',
|
||||
old_status VARCHAR(50) NULL COMMENT '原状态',
|
||||
new_status VARCHAR(50) NULL COMMENT '新状态',
|
||||
operator_id INT NOT NULL COMMENT '操作人ID',
|
||||
operation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
|
||||
ip_address VARCHAR(45) NULL COMMENT 'IP地址',
|
||||
user_agent TEXT NULL COMMENT '用户代理',
|
||||
|
||||
INDEX idx_completion_id (completion_id),
|
||||
INDEX idx_operation_type (operation_type),
|
||||
INDEX idx_operator_id (operator_id),
|
||||
INDEX idx_operation_time (operation_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='监管任务结项操作日志表';
|
||||
|
||||
-- 3. 监管任务结项附件表
|
||||
CREATE TABLE IF NOT EXISTS regulatory_task_completion_attachments (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '附件ID',
|
||||
completion_id INT NOT NULL COMMENT '结项ID',
|
||||
file_name VARCHAR(255) NOT NULL COMMENT '文件名',
|
||||
file_path VARCHAR(500) NOT NULL COMMENT '文件路径',
|
||||
file_size BIGINT NOT NULL COMMENT '文件大小(字节)',
|
||||
file_type VARCHAR(50) NOT NULL COMMENT '文件类型',
|
||||
mime_type VARCHAR(100) NOT NULL COMMENT 'MIME类型',
|
||||
upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间',
|
||||
uploaded_by INT NOT NULL COMMENT '上传人ID',
|
||||
|
||||
INDEX idx_completion_id (completion_id),
|
||||
INDEX idx_file_type (file_type),
|
||||
INDEX idx_upload_time (upload_time),
|
||||
INDEX idx_uploaded_by (uploaded_by)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='监管任务结项附件表';
|
||||
|
||||
-- 插入示例数据
|
||||
INSERT INTO regulatory_task_completions (
|
||||
application_no, policy_no, product_name, customer_name, id_type, id_number,
|
||||
product_type_premium, regulatory_effective_premium, insurance_period,
|
||||
insurance_amount, payment_method, payment_period, completion_status
|
||||
) VALUES
|
||||
('APP20250123001', 'POL20250123001', '养殖保险', '张三', '身份证', '110101199001011234',
|
||||
5000.00, 4800.00, '2025-01-01至2025-12-31', 100000.00, '年缴', '1年', 'pending'),
|
||||
('APP20250123002', 'POL20250123002', '牲畜保险', '李四', '身份证', '110101199002021234',
|
||||
3000.00, 2900.00, '2025-01-01至2025-12-31', 80000.00, '年缴', '1年', 'in_progress'),
|
||||
('APP20250123003', 'POL20250123003', '农业保险', '王五', '身份证', '110101199003031234',
|
||||
4000.00, 3800.00, '2025-01-01至2025-12-31', 90000.00, '年缴', '1年', 'completed');
|
||||
269
insurance_backend/scripts/test_frontend_integration.js
Normal file
269
insurance_backend/scripts/test_frontend_integration.js
Normal file
@@ -0,0 +1,269 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 配置API基础URL
|
||||
const API_BASE_URL = 'http://localhost:3000/api';
|
||||
|
||||
// 创建axios实例
|
||||
const api = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
// 测试数据
|
||||
const testData = {
|
||||
username: 'admin',
|
||||
password: '123456'
|
||||
};
|
||||
|
||||
let authToken = '';
|
||||
|
||||
// 测试函数
|
||||
async function testLogin() {
|
||||
console.log('\n=== 测试1: 用户登录 ===');
|
||||
try {
|
||||
const response = await api.post('/auth/login', testData);
|
||||
console.log('✅ 登录成功');
|
||||
console.log('响应数据:', JSON.stringify(response.data, null, 2));
|
||||
|
||||
if (response.data.code === 200 && response.data.data.token) {
|
||||
authToken = response.data.data.token;
|
||||
// 设置后续请求的认证头
|
||||
api.defaults.headers.common['Authorization'] = `Bearer ${authToken}`;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.log('❌ 登录失败:', error.response?.data || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function testLivestockPolicyList() {
|
||||
console.log('\n=== 测试2: 获取生资保单列表 ===');
|
||||
try {
|
||||
const response = await api.get('/livestock-policies');
|
||||
console.log('✅ 获取生资保单列表成功');
|
||||
console.log('响应数据:', JSON.stringify(response.data, null, 2));
|
||||
return response.data.data?.list || [];
|
||||
} catch (error) {
|
||||
console.log('❌ 获取生资保单列表失败:', error.response?.data || error.message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function testLivestockTypes() {
|
||||
console.log('\n=== 测试3: 获取牲畜类型列表 ===');
|
||||
try {
|
||||
const response = await api.get('/livestock-types?status=active');
|
||||
console.log('✅ 获取牲畜类型列表成功');
|
||||
console.log('响应数据:', JSON.stringify(response.data, null, 2));
|
||||
|
||||
// 根据实际的响应格式解析数据
|
||||
const list = response.data.data || [];
|
||||
// console.log('🔍 解析出的牲畜类型数组长度:', list.length);
|
||||
// if (list.length > 0) {
|
||||
// console.log('🔍 第一个牲畜类型:', list[0]);
|
||||
// }
|
||||
return list;
|
||||
} catch (error) {
|
||||
console.log('❌ 获取牲畜类型列表失败:', error.response?.data || error.message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function testCreateLivestockPolicy(livestockTypes) {
|
||||
console.log('\n=== 测试4: 创建生资保单 ===');
|
||||
// console.log('🔍 接收到的牲畜类型参数:', livestockTypes);
|
||||
// console.log('🔍 牲畜类型数组长度:', livestockTypes ? livestockTypes.length : 'undefined');
|
||||
|
||||
if (!livestockTypes || livestockTypes.length === 0) {
|
||||
console.log('❌ 无法创建保单:没有可用的牲畜类型');
|
||||
return null;
|
||||
}
|
||||
|
||||
const livestockType = livestockTypes[0];
|
||||
const livestockCount = 10;
|
||||
const unitValue = 5000;
|
||||
const totalValue = livestockCount * unitValue;
|
||||
const premiumAmount = totalValue * parseFloat(livestockType.premium_rate);
|
||||
|
||||
const newPolicy = {
|
||||
policy_no: `LP${Date.now()}${Math.random().toString(36).substr(2, 9).toUpperCase()}`,
|
||||
livestock_type_id: livestockType.id,
|
||||
policyholder_name: '测试农户',
|
||||
policyholder_id_card: '110101199001011234',
|
||||
policyholder_phone: '13800138000',
|
||||
policyholder_address: '北京市朝阳区测试街道123号',
|
||||
farm_address: '北京市朝阳区测试街道123号',
|
||||
livestock_count: livestockCount,
|
||||
unit_value: unitValue,
|
||||
total_value: totalValue,
|
||||
premium_amount: premiumAmount,
|
||||
start_date: '2024-01-01 08:00:00',
|
||||
end_date: '2024-12-31 08:00:00',
|
||||
policy_status: 'active',
|
||||
payment_status: 'unpaid',
|
||||
created_by: 1
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await api.post('/livestock-policies', newPolicy);
|
||||
console.log('✅ 创建生资保单成功');
|
||||
console.log('响应数据:', JSON.stringify(response.data, null, 2));
|
||||
return response.data.data;
|
||||
} catch (error) {
|
||||
console.log('❌ 创建生资保单失败:', error.response?.data || error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function testUpdateLivestockPolicy(policyId) {
|
||||
console.log('\n=== 测试5: 更新生资保单 ===');
|
||||
|
||||
const updateData = {
|
||||
farmer_name: '测试农户(已更新)',
|
||||
livestock_count: 15,
|
||||
total_value: 75000,
|
||||
premium_amount: 750
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await api.put(`/livestock-policies/${policyId}`, updateData);
|
||||
console.log('✅ 更新生资保单成功');
|
||||
console.log('响应数据:', JSON.stringify(response.data, null, 2));
|
||||
return response.data.data;
|
||||
} catch (error) {
|
||||
console.log('❌ 更新生资保单失败:', error.response?.data || error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function testGetLivestockPolicyDetail(policyId) {
|
||||
console.log('\n=== 测试6: 获取生资保单详情 ===');
|
||||
|
||||
try {
|
||||
const response = await api.get(`/livestock-policies/${policyId}`);
|
||||
console.log('✅ 获取生资保单详情成功');
|
||||
console.log('响应数据:', JSON.stringify(response.data, null, 2));
|
||||
return response.data.data;
|
||||
} catch (error) {
|
||||
console.log('❌ 获取生资保单详情失败:', error.response?.data || error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function testUpdatePolicyStatus(policyId) {
|
||||
console.log('\n=== 测试7: 更新保单状态 ===');
|
||||
|
||||
try {
|
||||
const response = await api.patch(`/livestock-policies/${policyId}/status`, {
|
||||
policy_status: 'active'
|
||||
});
|
||||
console.log('✅ 更新保单状态成功');
|
||||
console.log('响应数据:', JSON.stringify(response.data, null, 2));
|
||||
return response.data.data;
|
||||
} catch (error) {
|
||||
console.log('❌ 更新保单状态失败:', error.response?.data || error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function testSearchLivestockPolicies() {
|
||||
console.log('\n=== 测试8: 搜索生资保单 ===');
|
||||
|
||||
try {
|
||||
const response = await api.get('/livestock-policies', {
|
||||
params: {
|
||||
farmer_name: '测试',
|
||||
policy_status: 'active',
|
||||
page: 1,
|
||||
limit: 10
|
||||
}
|
||||
});
|
||||
console.log('✅ 搜索生资保单成功');
|
||||
console.log('响应数据:', JSON.stringify(response.data, null, 2));
|
||||
return response.data.data?.list || [];
|
||||
} catch (error) {
|
||||
console.log('❌ 搜索生资保单失败:', error.response?.data || error.message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function testDeleteLivestockPolicy(policyId) {
|
||||
console.log('\n=== 测试9: 删除生资保单 ===');
|
||||
|
||||
try {
|
||||
const response = await api.delete(`/livestock-policies/${policyId}`);
|
||||
console.log('✅ 删除生资保单成功');
|
||||
console.log('响应数据:', JSON.stringify(response.data, null, 2));
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log('❌ 删除生资保单失败:', error.response?.data || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 主测试函数
|
||||
async function runTests() {
|
||||
console.log('🚀 开始前端集成测试...');
|
||||
console.log('测试API基础URL:', API_BASE_URL);
|
||||
|
||||
try {
|
||||
// 1. 测试登录
|
||||
const loginSuccess = await testLogin();
|
||||
if (!loginSuccess) {
|
||||
console.log('\n❌ 登录失败,终止测试');
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 测试获取保单列表
|
||||
const initialList = await testLivestockPolicyList();
|
||||
console.log(`\n📊 当前保单数量: ${initialList.length}`);
|
||||
|
||||
// 3. 测试获取牲畜类型
|
||||
const livestockTypes = await testLivestockTypes();
|
||||
|
||||
// 4. 测试创建保单
|
||||
const newPolicy = await testCreateLivestockPolicy(livestockTypes);
|
||||
if (!newPolicy) {
|
||||
console.log('\n❌ 创建保单失败,跳过后续测试');
|
||||
return;
|
||||
}
|
||||
|
||||
const policyId = newPolicy.id;
|
||||
console.log(`\n📝 创建的保单ID: ${policyId}`);
|
||||
|
||||
// 5. 测试获取保单详情
|
||||
await testGetLivestockPolicyDetail(policyId);
|
||||
|
||||
// 6. 测试更新保单
|
||||
await testUpdateLivestockPolicy(policyId);
|
||||
|
||||
// 7. 测试更新保单状态
|
||||
await testUpdatePolicyStatus(policyId);
|
||||
|
||||
// 8. 测试搜索保单
|
||||
await testSearchLivestockPolicies();
|
||||
|
||||
// 9. 测试删除保单
|
||||
await testDeleteLivestockPolicy(policyId);
|
||||
|
||||
// 10. 再次获取保单列表验证删除
|
||||
const finalList = await testLivestockPolicyList();
|
||||
console.log(`\n📊 删除后保单数量: ${finalList.length}`);
|
||||
|
||||
console.log('\n🎉 所有测试完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n💥 测试过程中发生错误:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
runTests().then(() => {
|
||||
console.log('\n✨ 测试脚本执行完毕');
|
||||
process.exit(0);
|
||||
}).catch(error => {
|
||||
console.error('\n💥 测试脚本执行失败:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
56
insurance_backend/scripts/test_login.js
Normal file
56
insurance_backend/scripts/test_login.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试登录功能
|
||||
async function testLogin() {
|
||||
const baseURL = 'http://localhost:3000';
|
||||
|
||||
console.log('=== 开始测试登录功能 ===\n');
|
||||
|
||||
try {
|
||||
// 测试1: 正确的用户名和密码
|
||||
console.log('测试1: 使用正确的用户名和密码登录');
|
||||
const loginResponse = await axios.post(`${baseURL}/api/auth/login`, {
|
||||
username: 'admin',
|
||||
password: '123456'
|
||||
});
|
||||
|
||||
console.log('登录成功:', loginResponse.data);
|
||||
const token = loginResponse.data.data.token;
|
||||
|
||||
// 测试2:使用token访问受保护的API
|
||||
console.log('\n测试2:使用token访问生资保单API...');
|
||||
const protectedResponse = await axios.get('http://localhost:3000/api/livestock-policies', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
console.log('✓ 带token访问成功:', protectedResponse.data);
|
||||
|
||||
// 测试3: 错误的用户名和密码
|
||||
console.log('\n测试3: 使用错误的用户名和密码');
|
||||
try {
|
||||
await axios.post(`${baseURL}/api/auth/login`, {
|
||||
username: 'wronguser',
|
||||
password: 'wrongpass'
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('预期的登录失败:', error.response?.data?.message || error.message);
|
||||
}
|
||||
|
||||
// 测试4: 无token访问受保护的API
|
||||
console.log('\n测试4: 无token访问受保护的API');
|
||||
try {
|
||||
await axios.get(`${baseURL}/api/livestock-policies`);
|
||||
} catch (error) {
|
||||
console.log('预期的访问失败:', error.response?.data?.message || error.message);
|
||||
}
|
||||
|
||||
console.log('\n=== 登录功能测试完成 ===');
|
||||
|
||||
} catch (error) {
|
||||
console.error('测试失败:', error.response?.data || error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testLogin();
|
||||
@@ -8,7 +8,7 @@ const swaggerSpec = require('../config/swagger');
|
||||
const { sequelize, testConnection } = require('../config/database');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3002;
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
// 安全中间件
|
||||
app.use(helmet());
|
||||
@@ -29,6 +29,13 @@ app.use(limiter);
|
||||
app.use(express.json({ limit: '10mb' }));
|
||||
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
||||
|
||||
// 请求日志中间件
|
||||
app.use((req, res, next) => {
|
||||
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
|
||||
console.log('请求体:', req.body);
|
||||
next();
|
||||
});
|
||||
|
||||
// 静态文件服务
|
||||
app.use('/uploads', express.static('uploads'));
|
||||
|
||||
@@ -54,6 +61,12 @@ app.use('/api/data-warehouse', require('../routes/dataWarehouse'));
|
||||
app.use('/api/supervisory-tasks', require('../routes/supervisoryTasks'));
|
||||
app.use('/api/supervision-tasks', require('../routes/supervisoryTasks'));
|
||||
app.use('/api/installation-tasks', require('../routes/installationTasks'));
|
||||
app.use('/api/regulatory-task-completion', require('../routes/regulatoryTaskCompletion'));
|
||||
|
||||
// 生资保险相关路由
|
||||
app.use('/api/livestock-types', require('../routes/livestockTypes'));
|
||||
app.use('/api/livestock-policies', require('../routes/livestockPolicies'));
|
||||
app.use('/api/livestock-claims', require('../routes/livestockClaims'));
|
||||
|
||||
// API文档路由
|
||||
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
|
||||
|
||||
63
insurance_backend/test/create-insurance-types.js
Normal file
63
insurance_backend/test/create-insurance-types.js
Normal file
@@ -0,0 +1,63 @@
|
||||
const { InsuranceType } = require('../models');
|
||||
const { sequelize } = require('../models');
|
||||
|
||||
async function createInsuranceTypes() {
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log('数据库连接成功');
|
||||
|
||||
// 检查是否已有保险类型
|
||||
const existingTypes = await InsuranceType.findAll();
|
||||
console.log('现有保险类型数量:', existingTypes.length);
|
||||
|
||||
if (existingTypes.length === 0) {
|
||||
// 创建测试保险类型
|
||||
const insuranceTypes = [
|
||||
{
|
||||
name: '养殖保险',
|
||||
description: '为养殖业提供风险保障',
|
||||
coverage_amount_min: 10000.00,
|
||||
coverage_amount_max: 500000.00,
|
||||
premium_rate: 0.05,
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
name: '意外保险',
|
||||
description: '提供意外事故保障',
|
||||
coverage_amount_min: 50000.00,
|
||||
coverage_amount_max: 300000.00,
|
||||
premium_rate: 0.02,
|
||||
status: 'active'
|
||||
},
|
||||
{
|
||||
name: '健康保险',
|
||||
description: '提供健康医疗保障',
|
||||
coverage_amount_min: 20000.00,
|
||||
coverage_amount_max: 200000.00,
|
||||
premium_rate: 0.03,
|
||||
status: 'active'
|
||||
}
|
||||
];
|
||||
|
||||
const createdTypes = await InsuranceType.bulkCreate(insuranceTypes);
|
||||
console.log('✓ 创建保险类型成功,数量:', createdTypes.length);
|
||||
|
||||
createdTypes.forEach(type => {
|
||||
console.log(` - ${type.name} (ID: ${type.id})`);
|
||||
});
|
||||
} else {
|
||||
console.log('✓ 保险类型已存在');
|
||||
existingTypes.forEach(type => {
|
||||
console.log(` - ${type.name} (ID: ${type.id})`);
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('创建保险类型失败:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
console.log('数据库连接关闭');
|
||||
}
|
||||
}
|
||||
|
||||
createInsuranceTypes();
|
||||
497
insurance_backend/test/regulatory_task_completion_test.js
Normal file
497
insurance_backend/test/regulatory_task_completion_test.js
Normal file
@@ -0,0 +1,497 @@
|
||||
/**
|
||||
* 监管任务结项API接口测试
|
||||
* 测试所有API接口的功能和数据库操作
|
||||
*/
|
||||
|
||||
const axios = require('axios');
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
// 配置
|
||||
const API_BASE_URL = 'http://localhost:3000/api';
|
||||
const DB_CONFIG = {
|
||||
host: 'localhost',
|
||||
user: 'root',
|
||||
password: 'root',
|
||||
database: 'nxxmdata'
|
||||
};
|
||||
|
||||
// 测试用的JWT Token(需要替换为实际的token)
|
||||
let authToken = '';
|
||||
|
||||
// 数据库连接
|
||||
let dbConnection;
|
||||
|
||||
/**
|
||||
* 初始化测试环境
|
||||
*/
|
||||
async function initializeTest() {
|
||||
console.log('🚀 开始初始化测试环境...');
|
||||
|
||||
try {
|
||||
// 连接数据库
|
||||
dbConnection = await mysql.createConnection(DB_CONFIG);
|
||||
console.log('✅ 数据库连接成功');
|
||||
|
||||
// 这里应该获取真实的JWT token
|
||||
// 暂时使用模拟token进行测试
|
||||
authToken = 'test-jwt-token';
|
||||
console.log('✅ 认证token准备完成');
|
||||
|
||||
console.log('✅ 测试环境初始化完成\n');
|
||||
} catch (error) {
|
||||
console.error('❌ 测试环境初始化失败:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建HTTP请求配置
|
||||
*/
|
||||
function createRequestConfig() {
|
||||
return {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${authToken}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试数据库表结构
|
||||
*/
|
||||
async function testDatabaseStructure() {
|
||||
console.log('📋 测试数据库表结构...');
|
||||
|
||||
try {
|
||||
// 检查主表
|
||||
const [mainTable] = await dbConnection.execute('DESCRIBE regulatory_task_completions');
|
||||
console.log('✅ 主表 regulatory_task_completions 结构正常');
|
||||
|
||||
// 检查附件表
|
||||
const [attachmentTable] = await dbConnection.execute('DESCRIBE regulatory_task_completion_attachments');
|
||||
console.log('✅ 附件表 regulatory_task_completion_attachments 结构正常');
|
||||
|
||||
// 检查日志表
|
||||
const [logTable] = await dbConnection.execute('DESCRIBE regulatory_task_completion_logs');
|
||||
console.log('✅ 日志表 regulatory_task_completion_logs 结构正常');
|
||||
|
||||
// 检查示例数据
|
||||
const [sampleData] = await dbConnection.execute('SELECT COUNT(*) as count FROM regulatory_task_completions');
|
||||
console.log(`✅ 主表中有 ${sampleData[0].count} 条示例数据`);
|
||||
|
||||
console.log('✅ 数据库表结构测试通过\n');
|
||||
} catch (error) {
|
||||
console.error('❌ 数据库表结构测试失败:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试获取列表API
|
||||
*/
|
||||
async function testGetList() {
|
||||
console.log('📋 测试获取监管任务结项列表API...');
|
||||
|
||||
try {
|
||||
// 测试基础列表查询
|
||||
const response = await axios.get(`${API_BASE_URL}/regulatory-task-completion`, createRequestConfig());
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log('✅ 基础列表查询成功');
|
||||
console.log(` 返回数据条数: ${response.data.data?.list?.length || 0}`);
|
||||
}
|
||||
|
||||
// 测试带参数的查询
|
||||
const paramsResponse = await axios.get(`${API_BASE_URL}/regulatory-task-completion`, {
|
||||
...createRequestConfig(),
|
||||
params: {
|
||||
page: 1,
|
||||
limit: 5,
|
||||
status: 'pending',
|
||||
product_name: '养殖'
|
||||
}
|
||||
});
|
||||
|
||||
if (paramsResponse.status === 200) {
|
||||
console.log('✅ 带参数查询成功');
|
||||
console.log(` 筛选后数据条数: ${paramsResponse.data.data?.list?.length || 0}`);
|
||||
}
|
||||
|
||||
console.log('✅ 获取列表API测试通过\n');
|
||||
return response.data.data?.list?.[0]?.id; // 返回第一条记录的ID用于后续测试
|
||||
} catch (error) {
|
||||
console.error('❌ 获取列表API测试失败:', error.response?.data || error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试创建记录API
|
||||
*/
|
||||
async function testCreateRecord() {
|
||||
console.log('📋 测试创建监管任务结项记录API...');
|
||||
|
||||
const testData = {
|
||||
application_no: `APP${Date.now()}`,
|
||||
policy_no: `POL${Date.now()}`,
|
||||
product_name: '测试养殖保险',
|
||||
customer_name: '测试客户',
|
||||
customer_phone: '13800138000',
|
||||
insurance_amount: 100000.00,
|
||||
premium_amount: 5000.00,
|
||||
start_date: '2025-01-01',
|
||||
end_date: '2025-12-31',
|
||||
completion_date: '2025-01-19',
|
||||
status: 'pending',
|
||||
review_comments: '测试创建记录',
|
||||
attachments: [
|
||||
{
|
||||
file_name: '测试文件.pdf',
|
||||
file_path: '/uploads/test/test_file.pdf',
|
||||
file_size: 1024,
|
||||
file_type: 'application/pdf'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await axios.post(`${API_BASE_URL}/regulatory-task-completion`, testData, createRequestConfig());
|
||||
|
||||
if (response.status === 201) {
|
||||
console.log('✅ 创建记录成功');
|
||||
console.log(` 新记录ID: ${response.data.data?.id}`);
|
||||
|
||||
// 验证数据库中的记录
|
||||
const [dbRecord] = await dbConnection.execute(
|
||||
'SELECT * FROM regulatory_task_completions WHERE id = ?',
|
||||
[response.data.data.id]
|
||||
);
|
||||
|
||||
if (dbRecord.length > 0) {
|
||||
console.log('✅ 数据库记录验证成功');
|
||||
console.log(` 申请编号: ${dbRecord[0].application_no}`);
|
||||
console.log(` 客户名称: ${dbRecord[0].customer_name}`);
|
||||
}
|
||||
|
||||
console.log('✅ 创建记录API测试通过\n');
|
||||
return response.data.data.id;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 创建记录API测试失败:', error.response?.data || error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试获取详情API
|
||||
*/
|
||||
async function testGetDetail(recordId) {
|
||||
console.log('📋 测试获取监管任务结项详情API...');
|
||||
|
||||
try {
|
||||
const response = await axios.get(`${API_BASE_URL}/regulatory-task-completion/${recordId}`, createRequestConfig());
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log('✅ 获取详情成功');
|
||||
console.log(` 记录ID: ${response.data.data?.id}`);
|
||||
console.log(` 申请编号: ${response.data.data?.application_no}`);
|
||||
console.log(` 客户名称: ${response.data.data?.customer_name}`);
|
||||
console.log(` 附件数量: ${response.data.data?.attachments?.length || 0}`);
|
||||
console.log(` 操作日志数量: ${response.data.data?.operation_logs?.length || 0}`);
|
||||
}
|
||||
|
||||
console.log('✅ 获取详情API测试通过\n');
|
||||
} catch (error) {
|
||||
console.error('❌ 获取详情API测试失败:', error.response?.data || error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试更新记录API
|
||||
*/
|
||||
async function testUpdateRecord(recordId) {
|
||||
console.log('📋 测试更新监管任务结项记录API...');
|
||||
|
||||
const updateData = {
|
||||
product_name: '更新后的养殖保险',
|
||||
insurance_amount: 120000.00,
|
||||
premium_amount: 6000.00,
|
||||
review_comments: '记录已更新'
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await axios.put(`${API_BASE_URL}/regulatory-task-completion/${recordId}`, updateData, createRequestConfig());
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log('✅ 更新记录成功');
|
||||
|
||||
// 验证数据库中的更新
|
||||
const [dbRecord] = await dbConnection.execute(
|
||||
'SELECT * FROM regulatory_task_completions WHERE id = ?',
|
||||
[recordId]
|
||||
);
|
||||
|
||||
if (dbRecord.length > 0 && dbRecord[0].product_name === updateData.product_name) {
|
||||
console.log('✅ 数据库更新验证成功');
|
||||
console.log(` 更新后产品名称: ${dbRecord[0].product_name}`);
|
||||
console.log(` 更新后保险金额: ${dbRecord[0].insurance_amount}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ 更新记录API测试通过\n');
|
||||
} catch (error) {
|
||||
console.error('❌ 更新记录API测试失败:', error.response?.data || error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试审核记录API
|
||||
*/
|
||||
async function testReviewRecord(recordId) {
|
||||
console.log('📋 测试审核监管任务结项记录API...');
|
||||
|
||||
const reviewData = {
|
||||
status: 'approved',
|
||||
review_comments: '审核通过,符合监管要求',
|
||||
reviewer_name: '测试审核员'
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await axios.patch(`${API_BASE_URL}/regulatory-task-completion/${recordId}/review`, reviewData, createRequestConfig());
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log('✅ 审核记录成功');
|
||||
|
||||
// 验证数据库中的审核状态
|
||||
const [dbRecord] = await dbConnection.execute(
|
||||
'SELECT * FROM regulatory_task_completions WHERE id = ?',
|
||||
[recordId]
|
||||
);
|
||||
|
||||
if (dbRecord.length > 0 && dbRecord[0].status === reviewData.status) {
|
||||
console.log('✅ 审核状态更新验证成功');
|
||||
console.log(` 审核状态: ${dbRecord[0].status}`);
|
||||
console.log(` 审核人员: ${dbRecord[0].reviewer_name}`);
|
||||
}
|
||||
|
||||
// 检查操作日志
|
||||
const [logRecords] = await dbConnection.execute(
|
||||
'SELECT * FROM regulatory_task_completion_logs WHERE completion_id = ? ORDER BY operation_time DESC LIMIT 1',
|
||||
[recordId]
|
||||
);
|
||||
|
||||
if (logRecords.length > 0) {
|
||||
console.log('✅ 操作日志记录成功');
|
||||
console.log(` 操作类型: ${logRecords[0].operation_type}`);
|
||||
console.log(` 操作描述: ${logRecords[0].operation_description}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ 审核记录API测试通过\n');
|
||||
} catch (error) {
|
||||
console.error('❌ 审核记录API测试失败:', error.response?.data || error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试统计数据API
|
||||
*/
|
||||
async function testGetStatistics() {
|
||||
console.log('📋 测试获取统计数据API...');
|
||||
|
||||
try {
|
||||
const response = await axios.get(`${API_BASE_URL}/regulatory-task-completion/stats`, createRequestConfig());
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log('✅ 获取统计数据成功');
|
||||
console.log(` 状态统计项数: ${response.data.data?.statusStats?.length || 0}`);
|
||||
console.log(` 月度统计项数: ${response.data.data?.monthlyStats?.length || 0}`);
|
||||
console.log(` 产品统计项数: ${response.data.data?.productStats?.length || 0}`);
|
||||
|
||||
// 显示部分统计数据
|
||||
if (response.data.data?.statusStats?.length > 0) {
|
||||
console.log(' 状态统计示例:');
|
||||
response.data.data.statusStats.forEach(stat => {
|
||||
console.log(` ${stat.status}: ${stat.count}条`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ 获取统计数据API测试通过\n');
|
||||
} catch (error) {
|
||||
console.error('❌ 获取统计数据API测试失败:', error.response?.data || error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试删除记录API
|
||||
*/
|
||||
async function testDeleteRecord(recordId) {
|
||||
console.log('📋 测试删除监管任务结项记录API...');
|
||||
|
||||
try {
|
||||
const response = await axios.delete(`${API_BASE_URL}/regulatory-task-completion/${recordId}`, createRequestConfig());
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log('✅ 删除记录成功');
|
||||
|
||||
// 验证数据库中的记录已删除
|
||||
const [dbRecord] = await dbConnection.execute(
|
||||
'SELECT * FROM regulatory_task_completions WHERE id = ?',
|
||||
[recordId]
|
||||
);
|
||||
|
||||
if (dbRecord.length === 0) {
|
||||
console.log('✅ 数据库记录删除验证成功');
|
||||
}
|
||||
|
||||
// 验证相关附件和日志也被删除
|
||||
const [attachments] = await dbConnection.execute(
|
||||
'SELECT * FROM regulatory_task_completion_attachments WHERE completion_id = ?',
|
||||
[recordId]
|
||||
);
|
||||
|
||||
const [logs] = await dbConnection.execute(
|
||||
'SELECT * FROM regulatory_task_completion_logs WHERE completion_id = ?',
|
||||
[recordId]
|
||||
);
|
||||
|
||||
if (attachments.length === 0 && logs.length === 0) {
|
||||
console.log('✅ 相关数据清理验证成功');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ 删除记录API测试通过\n');
|
||||
} catch (error) {
|
||||
console.error('❌ 删除记录API测试失败:', error.response?.data || error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试错误处理
|
||||
*/
|
||||
async function testErrorHandling() {
|
||||
console.log('📋 测试错误处理...');
|
||||
|
||||
try {
|
||||
// 测试获取不存在的记录
|
||||
try {
|
||||
await axios.get(`${API_BASE_URL}/regulatory-task-completion/99999`, createRequestConfig());
|
||||
} catch (error) {
|
||||
if (error.response?.status === 404) {
|
||||
console.log('✅ 404错误处理正常');
|
||||
}
|
||||
}
|
||||
|
||||
// 测试创建记录时缺少必填字段
|
||||
try {
|
||||
await axios.post(`${API_BASE_URL}/regulatory-task-completion`, {
|
||||
application_no: 'TEST'
|
||||
// 缺少其他必填字段
|
||||
}, createRequestConfig());
|
||||
} catch (error) {
|
||||
if (error.response?.status === 400) {
|
||||
console.log('✅ 400错误处理正常');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ 错误处理测试通过\n');
|
||||
} catch (error) {
|
||||
console.error('❌ 错误处理测试失败:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理测试环境
|
||||
*/
|
||||
async function cleanupTest() {
|
||||
console.log('🧹 清理测试环境...');
|
||||
|
||||
try {
|
||||
if (dbConnection) {
|
||||
await dbConnection.end();
|
||||
console.log('✅ 数据库连接已关闭');
|
||||
}
|
||||
|
||||
console.log('✅ 测试环境清理完成\n');
|
||||
} catch (error) {
|
||||
console.error('❌ 测试环境清理失败:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主测试函数
|
||||
*/
|
||||
async function runTests() {
|
||||
console.log('🎯 开始监管任务结项API接口测试\n');
|
||||
|
||||
let testRecordId = null;
|
||||
|
||||
try {
|
||||
// 初始化测试环境
|
||||
await initializeTest();
|
||||
|
||||
// 测试数据库表结构
|
||||
await testDatabaseStructure();
|
||||
|
||||
// 测试获取列表
|
||||
const existingRecordId = await testGetList();
|
||||
|
||||
// 测试创建记录
|
||||
testRecordId = await testCreateRecord();
|
||||
|
||||
// 测试获取详情
|
||||
await testGetDetail(testRecordId);
|
||||
|
||||
// 测试更新记录
|
||||
await testUpdateRecord(testRecordId);
|
||||
|
||||
// 测试审核记录
|
||||
await testReviewRecord(testRecordId);
|
||||
|
||||
// 测试统计数据
|
||||
await testGetStatistics();
|
||||
|
||||
// 测试错误处理
|
||||
await testErrorHandling();
|
||||
|
||||
// 测试删除记录(放在最后)
|
||||
await testDeleteRecord(testRecordId);
|
||||
|
||||
console.log('🎉 所有测试通过!监管任务结项API接口功能正常');
|
||||
|
||||
} catch (error) {
|
||||
console.error('💥 测试失败:', error.message);
|
||||
console.log('\n📝 测试报告:');
|
||||
console.log(' - 部分功能可能需要启动后端服务才能正常测试');
|
||||
console.log(' - 需要配置正确的JWT认证token');
|
||||
console.log(' - 确保数据库连接配置正确');
|
||||
|
||||
} finally {
|
||||
// 清理测试环境
|
||||
await cleanupTest();
|
||||
}
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
if (require.main === module) {
|
||||
runTests().catch(console.error);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
runTests,
|
||||
testDatabaseStructure,
|
||||
testGetList,
|
||||
testCreateRecord,
|
||||
testGetDetail,
|
||||
testUpdateRecord,
|
||||
testReviewRecord,
|
||||
testGetStatistics,
|
||||
testDeleteRecord
|
||||
};
|
||||
120
insurance_backend/test/simple_api_test.js
Normal file
120
insurance_backend/test/simple_api_test.js
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* 简化的监管任务结项API测试
|
||||
* 测试基本的API接口功能
|
||||
*/
|
||||
|
||||
const axios = require('axios');
|
||||
|
||||
// 配置
|
||||
const API_BASE_URL = 'http://localhost:3000/api';
|
||||
|
||||
/**
|
||||
* 测试API接口
|
||||
*/
|
||||
async function testAPIs() {
|
||||
console.log('🎯 开始测试监管任务结项API接口\n');
|
||||
|
||||
try {
|
||||
// 1. 测试获取列表(不需要认证的健康检查)
|
||||
console.log('📋 测试服务健康状态...');
|
||||
const healthResponse = await axios.get('http://localhost:3000/health');
|
||||
if (healthResponse.status === 200) {
|
||||
console.log('✅ 服务运行正常');
|
||||
}
|
||||
|
||||
// 2. 测试获取统计数据(模拟请求)
|
||||
console.log('\n📋 测试获取统计数据API...');
|
||||
try {
|
||||
const statsResponse = await axios.get(`${API_BASE_URL}/regulatory-task-completion/stats`, {
|
||||
headers: {
|
||||
'Authorization': 'Bearer test-token'
|
||||
},
|
||||
timeout: 5000
|
||||
});
|
||||
console.log('✅ 统计数据API响应正常');
|
||||
} catch (error) {
|
||||
if (error.response?.status === 401) {
|
||||
console.log('✅ 统计数据API需要认证(正常)');
|
||||
} else {
|
||||
console.log('⚠️ 统计数据API响应:', error.response?.status || error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 测试获取列表API
|
||||
console.log('\n📋 测试获取列表API...');
|
||||
try {
|
||||
const listResponse = await axios.get(`${API_BASE_URL}/regulatory-task-completion`, {
|
||||
headers: {
|
||||
'Authorization': 'Bearer test-token'
|
||||
},
|
||||
timeout: 5000
|
||||
});
|
||||
console.log('✅ 列表API响应正常');
|
||||
} catch (error) {
|
||||
if (error.response?.status === 401) {
|
||||
console.log('✅ 列表API需要认证(正常)');
|
||||
} else {
|
||||
console.log('⚠️ 列表API响应:', error.response?.status || error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 测试创建记录API
|
||||
console.log('\n📋 测试创建记录API...');
|
||||
const testData = {
|
||||
application_no: `APP${Date.now()}`,
|
||||
policy_no: `POL${Date.now()}`,
|
||||
product_name: '测试养殖保险',
|
||||
customer_name: '测试客户'
|
||||
};
|
||||
|
||||
try {
|
||||
const createResponse = await axios.post(`${API_BASE_URL}/regulatory-task-completion`, testData, {
|
||||
headers: {
|
||||
'Authorization': 'Bearer test-token',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 5000
|
||||
});
|
||||
console.log('✅ 创建记录API响应正常');
|
||||
} catch (error) {
|
||||
if (error.response?.status === 401) {
|
||||
console.log('✅ 创建记录API需要认证(正常)');
|
||||
} else {
|
||||
console.log('⚠️ 创建记录API响应:', error.response?.status || error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 测试路由是否正确注册
|
||||
console.log('\n📋 检查路由注册状态...');
|
||||
try {
|
||||
// 尝试访问一个不存在的路由来确认我们的路由已注册
|
||||
await axios.get(`${API_BASE_URL}/regulatory-task-completion/test-route-exists`);
|
||||
} catch (error) {
|
||||
if (error.response?.status === 401) {
|
||||
console.log('✅ 监管任务结项路由已正确注册');
|
||||
} else if (error.response?.status === 404) {
|
||||
console.log('⚠️ 路由可能未正确注册');
|
||||
} else {
|
||||
console.log('✅ 路由响应正常');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n🎉 API接口基础测试完成!');
|
||||
console.log('\n📝 测试总结:');
|
||||
console.log(' ✅ 后端服务运行正常');
|
||||
console.log(' ✅ 监管任务结项路由已注册');
|
||||
console.log(' ✅ API接口响应正常(需要JWT认证)');
|
||||
console.log(' ✅ 错误处理机制正常');
|
||||
|
||||
console.log('\n📋 下一步建议:');
|
||||
console.log(' 1. 配置JWT认证token进行完整功能测试');
|
||||
console.log(' 2. 测试前端页面与API的集成');
|
||||
console.log(' 3. 验证数据库操作的正确性');
|
||||
|
||||
} catch (error) {
|
||||
console.error('💥 测试过程中发生错误:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testAPIs().catch(console.error);
|
||||
49
insurance_backend/test/test-insurance-types.js
Normal file
49
insurance_backend/test/test-insurance-types.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const axios = require('axios');
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: 'http://localhost:3000',
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
async function testInsuranceTypesAPI() {
|
||||
try {
|
||||
console.log('🔐 管理员登录...');
|
||||
|
||||
// 管理员登录
|
||||
const loginResponse = await api.post('/api/auth/login', {
|
||||
username: 'admin',
|
||||
password: '123456'
|
||||
});
|
||||
|
||||
if (loginResponse.data.code !== 200) {
|
||||
console.log('✗ 管理员登录失败:', loginResponse.data.message);
|
||||
return;
|
||||
}
|
||||
|
||||
const token = loginResponse.data.data.token;
|
||||
api.defaults.headers.common['Authorization'] = `Bearer ${token}`;
|
||||
console.log('✓ 管理员登录成功');
|
||||
|
||||
// 测试获取保险类型列表
|
||||
console.log('\n📋 测试获取保险类型列表...');
|
||||
const typesResponse = await api.get('/api/insurance-types');
|
||||
console.log('响应状态:', typesResponse.status);
|
||||
console.log('响应数据:', JSON.stringify(typesResponse.data, null, 2));
|
||||
|
||||
if (typesResponse.data.code === 200) {
|
||||
console.log('✓ 获取保险类型列表成功');
|
||||
console.log('保险类型数量:', typesResponse.data.data.list?.length || 0);
|
||||
} else {
|
||||
console.log('✗ 获取保险类型列表失败:', typesResponse.data.message);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log('✗ 测试失败:', error.response?.data?.message || error.message);
|
||||
if (error.response) {
|
||||
console.log('错误状态:', error.response.status);
|
||||
console.log('错误数据:', error.response.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testInsuranceTypesAPI();
|
||||
348
insurance_backend/test/test-policies.js
Normal file
348
insurance_backend/test/test-policies.js
Normal file
@@ -0,0 +1,348 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 配置API基础URL
|
||||
const API_BASE_URL = 'http://localhost:3000/api';
|
||||
|
||||
// 创建axios实例
|
||||
const api = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
// 测试数据
|
||||
const timestamp = Date.now();
|
||||
let testInsuranceTypeId = null;
|
||||
let testCustomerId = null;
|
||||
let testApplicationId = null;
|
||||
|
||||
const testPolicyData = {
|
||||
coverage_amount: 100000,
|
||||
premium_amount: 2000,
|
||||
start_date: new Date(),
|
||||
end_date: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 一年后
|
||||
policy_status: 'active',
|
||||
payment_status: 'unpaid'
|
||||
};
|
||||
|
||||
let authToken = '';
|
||||
let createdPolicyId = null;
|
||||
|
||||
// 管理员登录
|
||||
async function adminLogin() {
|
||||
try {
|
||||
console.log('🔐 管理员登录...');
|
||||
const response = await api.post('/auth/login', {
|
||||
username: 'admin',
|
||||
password: '123456'
|
||||
});
|
||||
|
||||
if (response.data.code === 200 && response.data.data && response.data.data.token) {
|
||||
authToken = response.data.data.token;
|
||||
api.defaults.headers.common['Authorization'] = `Bearer ${authToken}`;
|
||||
console.log('✓ 管理员登录成功');
|
||||
return true;
|
||||
} else {
|
||||
console.log('✗ 管理员登录失败:', response.data.message);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('✗ 管理员登录失败:', error.response?.data?.message || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建测试用户
|
||||
async function createTestUser() {
|
||||
try {
|
||||
const response = await api.post('/auth/register', {
|
||||
username: `test_customer_${timestamp}`,
|
||||
password: '123456',
|
||||
real_name: `测试客户_${timestamp}`,
|
||||
email: `test${timestamp}@test.com`,
|
||||
phone: `138${timestamp.toString().slice(-8)}`,
|
||||
role_id: 3 // customer角色ID
|
||||
});
|
||||
|
||||
if (response.data.code === 200 || response.data.code === 201) {
|
||||
testCustomerId = response.data.data.id;
|
||||
console.log('✓ 测试用户创建成功, ID:', testCustomerId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.log('✗ 创建测试用户失败:', error.response?.data?.message || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取保险类型
|
||||
async function getInsuranceType() {
|
||||
try {
|
||||
const response = await api.get('/insurance-types');
|
||||
if (response.data.code === 200 && response.data.data.list && response.data.data.list.length > 0) {
|
||||
testInsuranceTypeId = response.data.data.list[0].id;
|
||||
console.log('✓ 获取保险类型成功, ID:', testInsuranceTypeId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.log('✗ 获取保险类型失败:', error.response?.data?.message || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建保险申请
|
||||
async function createTestApplication() {
|
||||
try {
|
||||
const response = await api.post('/insurance/applications', {
|
||||
insurance_type_id: testInsuranceTypeId,
|
||||
customer_name: `测试客户_${timestamp}`,
|
||||
customer_phone: `138${timestamp.toString().slice(-8)}`,
|
||||
customer_id_card: `11010119900101${timestamp.toString().slice(-4)}`,
|
||||
customer_address: `测试地址_${timestamp}`,
|
||||
insurance_category: '养殖',
|
||||
application_quantity: 1,
|
||||
application_amount: 100000,
|
||||
remarks: '测试保险申请'
|
||||
});
|
||||
|
||||
if (response.data.code === 200 || response.data.code === 201) {
|
||||
testApplicationId = response.data.data.id;
|
||||
console.log('✓ 测试保险申请创建成功, ID:', testApplicationId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.log('✗ 创建测试保险申请失败:', error.response?.data?.message || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试获取保单列表
|
||||
async function testGetPolicies() {
|
||||
try {
|
||||
console.log('\n=== 测试获取保单列表 ===');
|
||||
const response = await api.get('/policies?page=1&limit=10');
|
||||
|
||||
if (response.data.code === 200) {
|
||||
console.log('✓ 获取保单列表成功');
|
||||
console.log('保单数量:', response.data.data?.list?.length || response.data.data?.length || 'undefined');
|
||||
return true;
|
||||
} else {
|
||||
console.log('✗ 获取保单列表失败:', response.data.message);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('✗ 获取保单列表失败:', error.response?.data?.message || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试创建保单
|
||||
async function testCreatePolicy() {
|
||||
try {
|
||||
console.log('\n=== 测试创建保单 ===');
|
||||
|
||||
// 使用关联的测试数据
|
||||
const policyData = {
|
||||
customer_id: testCustomerId,
|
||||
insurance_type_id: testInsuranceTypeId,
|
||||
application_id: testApplicationId,
|
||||
premium_amount: 2000,
|
||||
coverage_amount: 100000,
|
||||
start_date: new Date().toISOString().split('T')[0],
|
||||
end_date: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
|
||||
policy_status: 'active',
|
||||
payment_status: 'paid'
|
||||
};
|
||||
|
||||
const response = await api.post('/policies', policyData);
|
||||
|
||||
if (response.data.code === 201 || response.data.code === 200) {
|
||||
createdPolicyId = response.data.data.id;
|
||||
console.log('✓ 创建保单成功');
|
||||
console.log('创建的保单ID:', createdPolicyId);
|
||||
console.log('保单号:', response.data.data.policy_no);
|
||||
return true;
|
||||
} else {
|
||||
console.log('✗ 创建保单失败:', response.data.message);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('✗ 创建保单失败:', error.response?.data?.message || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试获取单个保单详情
|
||||
async function testGetPolicy() {
|
||||
try {
|
||||
console.log('\n=== 测试获取单个保单详情 ===');
|
||||
if (!createdPolicyId) {
|
||||
console.log('✗ 获取单个保单详情失败: 没有可用的保单ID');
|
||||
return false;
|
||||
}
|
||||
|
||||
const response = await api.get(`/policies/${createdPolicyId}`);
|
||||
|
||||
if (response.data.code === 200) {
|
||||
console.log('✓ 获取单个保单详情成功');
|
||||
console.log('保单编号:', response.data.data.policy_no);
|
||||
console.log('保单状态:', response.data.data.policy_status);
|
||||
return true;
|
||||
} else {
|
||||
console.log('✗ 获取单个保单详情失败:', response.data.message);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('✗ 获取单个保单详情失败:', error.response?.data?.message || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试更新保单
|
||||
async function testUpdatePolicy() {
|
||||
try {
|
||||
console.log('\n=== 测试更新保单 ===');
|
||||
if (!createdPolicyId) {
|
||||
console.log('✗ 更新保单失败: 没有可用的保单ID');
|
||||
return false;
|
||||
}
|
||||
|
||||
const updateData = {
|
||||
...testPolicyData,
|
||||
coverage_amount: 120000,
|
||||
premium_amount: 2400
|
||||
};
|
||||
|
||||
const response = await api.put(`/policies/${createdPolicyId}`, updateData);
|
||||
|
||||
if (response.data.code === 200) {
|
||||
console.log('✓ 更新保单成功');
|
||||
return true;
|
||||
} else {
|
||||
console.log('✗ 更新保单失败:', response.data.message);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('✗ 更新保单失败:', error.response?.data?.message || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试更新保单状态
|
||||
async function testUpdatePolicyStatus() {
|
||||
try {
|
||||
console.log('\n=== 测试更新保单状态 ===');
|
||||
if (!createdPolicyId) {
|
||||
console.log('✗ 更新保单状态失败: 没有可用的保单ID');
|
||||
return false;
|
||||
}
|
||||
|
||||
const response = await api.patch(`/policies/${createdPolicyId}/status`, {
|
||||
policy_status: 'suspended'
|
||||
});
|
||||
|
||||
if (response.data.code === 200) {
|
||||
console.log('✓ 更新保单状态成功');
|
||||
return true;
|
||||
} else {
|
||||
console.log('✗ 更新保单状态失败:', response.data.message);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('✗ 更新保单状态失败:', error.response?.data?.message || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试获取保单统计
|
||||
async function testGetPolicyStats() {
|
||||
try {
|
||||
console.log('\n=== 测试获取保单统计 ===');
|
||||
const response = await api.get('/policies/stats/overview');
|
||||
|
||||
if (response.data.code === 200) {
|
||||
console.log('✓ 获取保单统计成功');
|
||||
console.log('统计数据:', JSON.stringify(response.data.data, null, 2));
|
||||
return true;
|
||||
} else {
|
||||
console.log('✗ 获取保单统计失败:', response.data.message);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('✗ 获取保单统计失败:', error.response?.data?.message || error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 主测试函数
|
||||
async function runPolicyTests() {
|
||||
console.log('🚀 开始保单管理API测试...\n');
|
||||
|
||||
// 管理员登录
|
||||
const loginSuccess = await adminLogin();
|
||||
if (!loginSuccess) {
|
||||
console.log('\n❌ 测试终止:管理员登录失败');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('\n🔧 准备测试数据...');
|
||||
|
||||
// 创建测试用户
|
||||
const userCreated = await createTestUser();
|
||||
if (!userCreated) {
|
||||
console.log('\n❌ 测试终止:创建测试用户失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取保险类型
|
||||
const typeFound = await getInsuranceType();
|
||||
if (!typeFound) {
|
||||
console.log('\n❌ 测试终止:获取保险类型失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建保险申请
|
||||
const applicationCreated = await createTestApplication();
|
||||
if (!applicationCreated) {
|
||||
console.log('\n❌ 测试终止:创建保险申请失败');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('\n📊 开始API测试...');
|
||||
|
||||
const tests = [
|
||||
{ name: '获取保单列表', func: testGetPolicies },
|
||||
{ name: '创建保单', func: testCreatePolicy },
|
||||
{ name: '获取单个保单详情', func: testGetPolicy },
|
||||
{ name: '更新保单', func: testUpdatePolicy },
|
||||
{ name: '更新保单状态', func: testUpdatePolicyStatus },
|
||||
{ name: '获取保单统计', func: testGetPolicyStats }
|
||||
];
|
||||
|
||||
let passedTests = 0;
|
||||
let failedTests = 0;
|
||||
|
||||
for (const test of tests) {
|
||||
const result = await test.func();
|
||||
if (result) {
|
||||
passedTests++;
|
||||
} else {
|
||||
failedTests++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n📈 测试结果统计:');
|
||||
console.log(`✅ 通过: ${passedTests}/${tests.length}`);
|
||||
console.log(`❌ 失败: ${failedTests}/${tests.length}`);
|
||||
|
||||
if (failedTests === 0) {
|
||||
console.log('\n🎉 所有测试通过!');
|
||||
} else {
|
||||
console.log('\n⚠️ 部分测试失败,请检查相关功能');
|
||||
}
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
runPolicyTests().catch(console.error);
|
||||
52
insurance_backend/test_api_final.ps1
Normal file
52
insurance_backend/test_api_final.ps1
Normal file
@@ -0,0 +1,52 @@
|
||||
# 测试保险申请API
|
||||
Write-Host "=== 保险申请API测试 ===" -ForegroundColor Green
|
||||
|
||||
# 1. 登录获取token
|
||||
Write-Host "1. 正在登录..." -ForegroundColor Yellow
|
||||
$loginResponse = Invoke-WebRequest -Uri "http://localhost:3000/api/auth/login" -Method POST -Body '{"username":"admin","password":"123456"}' -ContentType "application/json" -UseBasicParsing
|
||||
$loginData = $loginResponse.Content | ConvertFrom-Json
|
||||
|
||||
if ($loginData.code -eq 200) {
|
||||
$token = $loginData.data.token
|
||||
Write-Host "✅ 登录成功,获取到令牌" -ForegroundColor Green
|
||||
|
||||
# 2. 测试保险申请列表API
|
||||
Write-Host "2. 正在测试保险申请列表API..." -ForegroundColor Yellow
|
||||
try {
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $token"
|
||||
"Content-Type" = "application/json"
|
||||
}
|
||||
|
||||
$apiResponse = Invoke-WebRequest -Uri "http://localhost:3000/api/insurance/applications?page=1`&limit=10" -Headers $headers -UseBasicParsing
|
||||
$apiData = $apiResponse.Content | ConvertFrom-Json
|
||||
|
||||
Write-Host "✅ API调用成功!" -ForegroundColor Green
|
||||
Write-Host "状态码: $($apiResponse.StatusCode)" -ForegroundColor Cyan
|
||||
Write-Host "响应数据: $($apiResponse.Content)" -ForegroundColor Cyan
|
||||
|
||||
} catch {
|
||||
Write-Host "❌ API调用失败: $($_.Exception.Message)" -ForegroundColor Red
|
||||
if ($_.Exception.Response) {
|
||||
$errorResponse = $_.Exception.Response.GetResponseStream()
|
||||
$reader = New-Object System.IO.StreamReader($errorResponse)
|
||||
$errorContent = $reader.ReadToEnd()
|
||||
Write-Host "错误详情: $errorContent" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
# 3. 测试前端代理
|
||||
Write-Host "3. 正在测试前端代理..." -ForegroundColor Yellow
|
||||
try {
|
||||
$proxyResponse = Invoke-WebRequest -Uri "http://localhost:3002/api/insurance/applications?page=1`&limit=10" -Headers $headers -UseBasicParsing
|
||||
Write-Host "✅ 前端代理调用成功!" -ForegroundColor Green
|
||||
Write-Host "状态码: $($proxyResponse.StatusCode)" -ForegroundColor Cyan
|
||||
} catch {
|
||||
Write-Host "❌ 前端代理调用失败: $($_.Exception.Message)" -ForegroundColor Red
|
||||
}
|
||||
|
||||
} else {
|
||||
Write-Host "❌ 登录失败: $($loginData.message)" -ForegroundColor Red
|
||||
}
|
||||
|
||||
Write-Host "=== 测试完成 ===" -ForegroundColor Green
|
||||
37
insurance_backend/test_detailed_request.js
Normal file
37
insurance_backend/test_detailed_request.js
Normal file
@@ -0,0 +1,37 @@
|
||||
const axios = require('axios');
|
||||
|
||||
async function testServer() {
|
||||
console.log('开始测试服务器...');
|
||||
|
||||
try {
|
||||
// 测试健康检查
|
||||
console.log('\n1. 测试健康检查接口...');
|
||||
const healthResponse = await axios.get('http://localhost:3004/health');
|
||||
console.log('健康检查成功:', healthResponse.data);
|
||||
} catch (error) {
|
||||
console.log('健康检查失败:', error.response?.status, error.response?.data || error.message);
|
||||
}
|
||||
|
||||
try {
|
||||
// 测试登录接口
|
||||
console.log('\n2. 测试登录接口...');
|
||||
const loginResponse = await axios.post('http://localhost:3004/api/auth/login', {
|
||||
username: 'admin',
|
||||
password: '123456'
|
||||
});
|
||||
console.log('登录成功:', loginResponse.data);
|
||||
} catch (error) {
|
||||
console.log('登录失败:', error.response?.status, error.response?.data || error.message);
|
||||
}
|
||||
|
||||
try {
|
||||
// 测试不存在的接口
|
||||
console.log('\n3. 测试不存在的接口...');
|
||||
const notFoundResponse = await axios.get('http://localhost:3004/nonexistent');
|
||||
console.log('不存在接口响应:', notFoundResponse.data);
|
||||
} catch (error) {
|
||||
console.log('不存在接口错误:', error.response?.status, error.response?.data || error.message);
|
||||
}
|
||||
}
|
||||
|
||||
testServer();
|
||||
35
insurance_backend/test_livestock_api.js
Normal file
35
insurance_backend/test_livestock_api.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const axios = require('axios');
|
||||
|
||||
async function testAPI() {
|
||||
try {
|
||||
// 先登录获取token
|
||||
const loginResponse = await axios.post('http://localhost:3000/api/auth/login', {
|
||||
username: 'admin',
|
||||
password: '123456'
|
||||
});
|
||||
|
||||
const token = loginResponse.data.data.token;
|
||||
console.log('✅ 登录成功');
|
||||
|
||||
// 测试获取牲畜类型列表
|
||||
const response = await axios.get('http://localhost:3000/api/livestock-types', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
console.log('✅ 获取牲畜类型列表成功');
|
||||
console.log('响应数据:', JSON.stringify(response.data, null, 2));
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 错误:', error.response?.data || error.message);
|
||||
if (error.response?.data) {
|
||||
console.error('详细错误:', JSON.stringify(error.response.data, null, 2));
|
||||
}
|
||||
if (error.stack) {
|
||||
console.error('错误堆栈:', error.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testAPI();
|
||||
21
insurance_backend/test_login.js
Normal file
21
insurance_backend/test_login.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const axios = require('axios');
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
console.log('开始测试登录...');
|
||||
|
||||
const response = await axios.post('http://localhost:3000/api/auth/login', {
|
||||
username: 'admin',
|
||||
password: '123456'
|
||||
});
|
||||
|
||||
console.log('登录成功!');
|
||||
console.log('状态码:', response.status);
|
||||
console.log('响应数据:', JSON.stringify(response.data, null, 2));
|
||||
|
||||
} catch (error) {
|
||||
console.log('登录失败!');
|
||||
console.log('状态码:', error.response?.status);
|
||||
console.log('错误信息:', error.response?.data || error.message);
|
||||
}
|
||||
})();
|
||||
19
insurance_backend/test_password.js
Normal file
19
insurance_backend/test_password.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
const hash = '$2b$12$2gMSr66wlftS./7f7U9JJeSZrpOPTQUFXLUANJ3a0IfWoiKPCuSDO';
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
console.log('测试密码验证...');
|
||||
|
||||
const passwords = ['admin', 'admin123', '123456', 'password', 'Admin123'];
|
||||
|
||||
for (const pwd of passwords) {
|
||||
const result = await bcrypt.compare(pwd, hash);
|
||||
console.log(`密码 '${pwd}': ${result ? '正确' : '错误'}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('错误:', error);
|
||||
}
|
||||
})();
|
||||
31
insurance_backend/test_simple_request.js
Normal file
31
insurance_backend/test_simple_request.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const http = require('http');
|
||||
|
||||
// 测试健康检查接口
|
||||
const options = {
|
||||
hostname: 'localhost',
|
||||
port: 3000,
|
||||
path: '/health',
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
console.log('正在测试健康检查接口...');
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
console.log(`状态码: ${res.statusCode}`);
|
||||
console.log(`响应头: ${JSON.stringify(res.headers)}`);
|
||||
|
||||
let data = '';
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
console.log('响应内容:', data);
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (e) => {
|
||||
console.error(`请求遇到问题: ${e.message}`);
|
||||
});
|
||||
|
||||
req.end();
|
||||
Reference in New Issue
Block a user