修改主页工作台布局以及数据显示, 改成预报名报表统计页面

This commit is contained in:
mirage 2026-02-26 09:39:22 +08:00
parent 737c7a1ff1
commit c3a8f590e8
18 changed files with 570 additions and 391 deletions

View File

@ -6,8 +6,8 @@ export function getConfig() {
} }
// 工作台主页 // 工作台主页
export function getWorkbench() { export function getWorkbench(params?: Record<string, any>) {
return request.get({ url: '/workbench/index' }) return request.get({ url: '/workbench/index', params })
} }
//字典数据 //字典数据

View File

@ -12,20 +12,10 @@
<el-form-item label="性别" prop="gender"> <el-form-item label="性别" prop="gender">
<el-select v-model="queryParams.gender" class="w-[280px]" clearable> <el-select v-model="queryParams.gender" class="w-[280px]" clearable>
<el-option label="男" value="1" /> <el-option label="男" value="1" />
<el-option label="女" value="2" /> <el-option label="女" value="0" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6">
<el-form-item label="身份证号" prop="idCard">
<el-input class="w-[280px]" v-model="queryParams.idCard" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="毕业学校" prop="previousSchool">
<el-input class="w-[280px]" v-model="queryParams.previousSchool" />
</el-form-item>
</el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="报名专业" prop="majorId"> <el-form-item label="报名专业" prop="majorId">
<el-select <el-select
@ -63,21 +53,11 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="接待老师" prop="receptionTeacherId"> <el-form-item label="创建时间">
<el-select <daterange-picker
v-model="queryParams.receptionTeacherId" v-model:startTime="queryParams.createTimeStart"
class="w-[280px]" v-model:endTime="queryParams.createTimeEnd"
clearable
filterable
placeholder="请选择接待老师"
>
<el-option
v-for="item in teacherOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/> />
</el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
@ -90,7 +70,19 @@
</el-form> </el-form>
</el-card> </el-card>
<el-card class="!border-none mt-4" shadow="never"> <el-card class="!border-none mt-4" shadow="never">
<el-table class="mt-4" size="large" v-loading="pager.loading" :data="pager.lists"> <div class="flex justify-start mb-2">
<el-button type="success" plain @click="handleBatchEnroll" :disabled="!multipleSelection.length">
批量入学
</el-button>
</div>
<el-table
class="mt-4"
size="large"
v-loading="pager.loading"
:data="pager.lists"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="姓名" prop="name" min-width="100" /> <el-table-column label="姓名" prop="name" min-width="100" />
<el-table-column <el-table-column
label="性别" label="性别"
@ -98,71 +90,28 @@
min-width="100" min-width="100"
:formatter="formatGender" :formatter="formatGender"
/> />
<el-table-column <el-table-column label="身份证号" prop="idCard" min-width="200">
label="身份证号"
prop="idCard"
min-width="200"
>
<template #default="{ row }"> <template #default="{ row }">
<span style="white-space: nowrap;">{{ row.idCard }}</span> <span style="white-space: nowrap">{{ row.idCard }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="毕业学校" prop="previousSchool" min-width="150" />
label="毕业学校"
prop="previousSchool"
min-width="150"
/>
<el-table-column
label="身高(cm)"
prop="height"
min-width="100"
/>
<el-table-column
label="体重(kg)"
prop="weight"
min-width="100"
/>
<el-table-column
label="鞋码"
prop="shoeSize"
min-width="100"
/>
<el-table-column
label="中考成绩"
prop="highSchoolScore"
min-width="120"
/>
<el-table-column label="报名专业" prop="majorName" min-width="120" /> <el-table-column label="报名专业" prop="majorName" min-width="120" />
<el-table-column <el-table-column
label="招生老师" label="招生老师"
prop="recruitmentTeacherId" prop="recruitmentTeacherName"
min-width="120" min-width="120"
:formatter="formatRecruitmentTeacher" :formatter="formatRecruitmentTeacher"
/> />
<el-table-column
label="接待老师"
prop="receptionTeacherId"
min-width="120"
:formatter="formatReceptionTeacher"
/>
<el-table-column
label="预报名金额"
prop="preRegistrationAmount"
min-width="120"
/>
<el-table-column label="创建时间" prop="createTime" min-width="180"> <el-table-column label="创建时间" prop="createTime" min-width="180">
<template #default="{ row }"> <template #default="{ row }">
<span style="white-space: nowrap;">{{ row.createTime }}</span> <span style="white-space: nowrap">{{ row.createTime }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="更新时间" prop="updateTime" min-width="180"> <el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }">
<span style="white-space: nowrap;">{{ row.updateTime }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" link @click="handleView(row)">详情</el-button> <el-button type="primary" link @click="handleView(row)">详情</el-button>
<el-button type="success" link @click="handleEnroll(row)">入学</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -174,115 +123,50 @@
</div> </div>
</template> </template>
<script lang="ts" setup name="info"> <script lang="ts" setup name="info">
import { infoLists } from '@/api/info/info' import { infoLists, infoDetail, infoEdit } from '@/api/info/info'
import { majorLists } from '@/api/major' import { majorLists } from '@/api/major'
import { teacherLists } from '@/api/teacher' import { teacherLists } from '@/api/teacher'
import { usePaging } from '@/hooks/usePaging' import { usePaging } from '@/hooks/usePaging'
import feedback from '@/utils/feedback' import feedback from '@/utils/feedback'
import { snakeToCamel } from '@/utils/format'
import request from '@/utils/request'
import EditPopup from '../student/info/edit.vue' import EditPopup from '../student/info/edit.vue'
const editRef = shallowRef<InstanceType<typeof EditPopup>>() const editRef = shallowRef<InstanceType<typeof EditPopup>>()
const showEdit = ref(false) const showEdit = ref(false)
const multipleSelection = ref<any[]>([])
const majorOptions = ref<Array<{ id: number; majorName: string }>>([]) const majorOptions = ref<Array<{ id: number; majorName: string }>>([])
const teacherOptions = ref<Array<{ id: number; name: string }>>([]) const teacherOptions = ref<Array<{ id: number; name: string }>>([])
const queryParams = reactive({ const queryParams = reactive({
id: null,
userId: null,
studentNumber: '',
name: '', name: '',
gender: '', gender: '',
college: '',
majorId: undefined as number | undefined, majorId: undefined as number | undefined,
className: '',
grade: '',
enrollmentYear: '',
expectedGraduationYear: '',
studentStatus: 0, studentStatus: 0,
idCard: '', //
birthday: '', recruitmentTeacherId: '' as unknown as number | undefined,
politicalStatus: '', createTimeStart: '',
nativePlace: '', createTimeEnd: ''
homeAddress: '',
emergencyContact: '',
emergencyPhone: '',
dormitory: '',
counselorId: '',
totalCredits: '',
gpa: '',
scholarshipLevel: '',
academicWarnings: '',
isVerified: '',
verifiedBy: '',
verifiedTime: '',
previousSchool: '',
recruitmentTeacherId: undefined as number | undefined,
receptionTeacherId: undefined as number | undefined
}) })
const formatGender = (row: any, column: any, cellValue: any) => { const formatGender = (row: any, column: any, cellValue: any) => {
const statusMap: Record<string | number, string> = { const statusMap: Record<string | number, string> = {
'1': '男', '1': '男',
'2': '女' '0': '女'
}
return statusMap[String(cellValue)] || cellValue
}
const formatStudentStatus = (row: any, column: any, cellValue: any) => {
const statusMap: Record<string | number, string> = {
'1': '在读',
'2': '休学',
'3': '毕业',
'4': '退学'
}
return statusMap[String(cellValue)] || cellValue
}
const formatPoliticalStatus = (row: any, column: any, cellValue: any) => {
const statusMap: Record<string | number, string> = {
'1': '党员',
'2': '团员',
'3': '群众'
}
return statusMap[String(cellValue)] || cellValue
}
const formatScholarshipLevel = (row: any, column: any, cellValue: any) => {
const statusMap: Record<string | number, string> = {
'0': ' ',
'1': '一等奖',
'2': '二等奖',
'3': '三等奖'
}
return statusMap[String(cellValue)] || cellValue
}
const formatAcademicWarnings = (row: any, column: any, cellValue: any) => {
const statusMap: Record<string | number, string> = {
'0': ' ',
'1': '一级预警',
'2': '二级预警',
'3': '三级预警'
}
return statusMap[String(cellValue)] || cellValue
}
const formatIsVerified = (row: any, column: any, cellValue: any) => {
const statusMap: Record<string | number, string> = {
'0': '未认证',
'1': '已认证'
} }
return statusMap[String(cellValue)] || cellValue return statusMap[String(cellValue)] || cellValue
} }
const formatRecruitmentTeacher = (row: any, column: any, cellValue: any) => { const formatRecruitmentTeacher = (row: any, column: any, cellValue: any) => {
if (!cellValue) return '' // 使
const teacher = teacherOptions.value.find((t) => t.id === cellValue) if (row.recruitmentTeacherName != null && row.recruitmentTeacherName !== '') {
return teacher ? teacher.name : cellValue return row.recruitmentTeacherName
} }
const teacherId = row.recruitmentTeacherId
const formatReceptionTeacher = (row: any, column: any, cellValue: any) => { if (teacherId == null || teacherId === '') return ''
if (!cellValue) return '' const teacher = teacherOptions.value.find(
const teacher = teacherOptions.value.find((t) => t.id === cellValue) (t) => t.id == teacherId || String(t.id) === String(teacherId)
return teacher ? teacher.name : cellValue )
return teacher ? teacher.name : ''
} }
const fetchMajorOptions = async () => { const fetchMajorOptions = async () => {
@ -303,6 +187,10 @@ const fetchTeacherOptions = async () => {
} }
} }
const handleSelectionChange = (val: any[]) => {
multipleSelection.value = val || []
}
const { pager, getLists, resetPage, resetParams } = usePaging({ const { pager, getLists, resetPage, resetParams } = usePaging({
fetchFun: async (params: any) => { fetchFun: async (params: any) => {
// //
@ -314,24 +202,26 @@ const { pager, getLists, resetPage, resetParams } = usePaging({
} }
// undefined null null undefined // undefined null null undefined
if (processedParams.majorId === '' || processedParams.majorId === null || processedParams.majorId === undefined) { if (
processedParams.majorId === '' ||
processedParams.majorId === null ||
processedParams.majorId === undefined
) {
processedParams.majorId = null processedParams.majorId = null
} else if (typeof processedParams.majorId === 'string') { } else if (typeof processedParams.majorId === 'string') {
processedParams.majorId = parseInt(processedParams.majorId) processedParams.majorId = parseInt(processedParams.majorId)
} }
if (processedParams.recruitmentTeacherId === '' || processedParams.recruitmentTeacherId === null || processedParams.recruitmentTeacherId === undefined) { if (
processedParams.recruitmentTeacherId === '' ||
processedParams.recruitmentTeacherId === null ||
processedParams.recruitmentTeacherId === undefined
) {
processedParams.recruitmentTeacherId = null processedParams.recruitmentTeacherId = null
} else if (typeof processedParams.recruitmentTeacherId === 'string') { } else if (typeof processedParams.recruitmentTeacherId === 'string') {
processedParams.recruitmentTeacherId = parseInt(processedParams.recruitmentTeacherId) processedParams.recruitmentTeacherId = parseInt(processedParams.recruitmentTeacherId)
} }
if (processedParams.receptionTeacherId === '' || processedParams.receptionTeacherId === null || processedParams.receptionTeacherId === undefined) {
processedParams.receptionTeacherId = null
} else if (typeof processedParams.receptionTeacherId === 'string') {
processedParams.receptionTeacherId = parseInt(processedParams.receptionTeacherId)
}
return infoLists(processedParams) return infoLists(processedParams)
}, },
params: queryParams params: queryParams
@ -349,5 +239,43 @@ const handleView = async (data: any) => {
editRef.value?.getDetail(data) editRef.value?.getDetail(data)
} }
const handleEnroll = async (row: any) => {
await feedback.confirm('是否确认入学?')
//
const detail = await infoDetail({
id: row.studentId || row.student_id || row.id
})
const data = snakeToCamel(detail)
//
if (!data.studentId) {
data.studentId = row.studentId || row.student_id || row.id
}
data.id = data.studentId
//
data.studentStatus = 2
await infoEdit(data)
feedback.msgSuccess('入学成功')
getLists()
}
const handleBatchEnroll = async () => {
if (!multipleSelection.value.length) return
await feedback.confirm('是否确认批量入学?')
const ids = multipleSelection.value
.map((item) => item.studentId || item.student_id || item.id)
.filter((id) => id != null)
if (!ids.length) {
feedback.msgError('未获取到有效的学生ID')
return
}
// GET /info/batchUpdateStudentStatus?studentIdList=1&studentIdList=2...
const query = ids.map((id) => `studentIdList=${encodeURIComponent(id)}`).join('&')
await request.get({
url: `/info/batchUpdateStudentStatus?${query}`
})
feedback.msgSuccess('批量入学成功')
getLists()
}
getLists() getLists()
</script> </script>

View File

@ -497,7 +497,7 @@
v-model="studentFormData.preRegistrationTime" v-model="studentFormData.preRegistrationTime"
type="datetime" type="datetime"
placeholder="请选择预报名时间" placeholder="请选择预报名时间"
value-format="X" value-format="YYYY-MM-DD HH:mm:ss"
class="w-full" class="w-full"
:disabled="isViewMode" :disabled="isViewMode"
/> />
@ -717,7 +717,7 @@ const studentFormData = reactive({
preRegistrationAmount: '', preRegistrationAmount: '',
recruitmentTeacherId: undefined as number | undefined, recruitmentTeacherId: undefined as number | undefined,
receptionTeacherId: undefined as number | undefined, receptionTeacherId: undefined as number | undefined,
preRegistrationTime: undefined as number | undefined, preRegistrationTime: undefined as string | undefined,
invitationCode: '', invitationCode: '',
isVerified: undefined as number | undefined, isVerified: undefined as number | undefined,
verifiedBy: '', verifiedBy: '',

View File

@ -43,59 +43,83 @@
<div class="flex flex-wrap"> <div class="flex flex-wrap">
<div class="w-1/2 md:w-1/4"> <div class="w-1/2 md:w-1/4">
<div class="leading-10">销售额</div> <div class="leading-10">总招生数量</div>
<div class="text-6xl">{{ workbenchData.today.today_sales }}</div> <div class="text-6xl">{{ workbenchData.today.total_enroll_count || 0 }}</div>
<div class="text-tx-secondary text-xs"> <div class="text-tx-secondary text-xs">
{{ workbenchData.today.total_sales }} 学生状态为"预报名""报名"的学生总数
</div> </div>
</div> </div>
<div class="w-1/2 md:w-1/4"> <div class="w-1/2 md:w-1/4">
<div class="leading-10">成交订单</div> <div class="leading-10">今日招生数量</div>
<div class="text-6xl">{{ workbenchData.today.order_num }}</div> <div class="text-6xl">{{ workbenchData.today.today_enroll_count || 0 }}</div>
<div class="text-tx-secondary text-xs"> <div class="text-tx-secondary text-xs">
{{ workbenchData.today.order_sum }} 今天新增的招生人数
</div> </div>
</div> </div>
<div class="w-1/2 md:w-1/4"> <div class="w-1/2 md:w-1/4">
<div class="leading-10">新增用户</div> <div class="leading-10">本周招生数量</div>
<div class="text-6xl">{{ workbenchData.today.today_new_user }}</div> <div class="text-6xl">{{ workbenchData.today.week_enroll_count || 0 }}</div>
<div class="text-tx-secondary text-xs"> <div class="text-tx-secondary text-xs">
{{ workbenchData.today.total_new_user }} 最近 7 天的招生人数
</div> </div>
</div> </div>
<div class="w-1/2 md:w-1/4"> <div class="w-1/2 md:w-1/4">
<div class="leading-10">新增访问</div> <div class="leading-10">本月招生数</div>
<div class="text-6xl">{{ workbenchData.today.today_visitor }}</div> <div class="text-6xl">{{ workbenchData.today.month_enroll_count || 0 }}</div>
<div class="text-tx-secondary text-xs"> <div class="text-tx-secondary text-xs">
{{ workbenchData.today.total_visitor }} 最近 30 天的招生人数
</div> </div>
</div> </div>
</div> </div>
</el-card> </el-card>
</div> </div>
<div class="function mb-4"> <!-- <div class="function mb-4">-->
<el-card class="flex-1 !border-none" shadow="never"> <!-- <el-card class="flex-1 !border-none" shadow="never">-->
<template #header> <!-- <template #header>-->
<span>常用功能</span> <!-- <span>常用功能</span>-->
</template> <!-- </template>-->
<div class="flex flex-wrap"> <!-- <div class="flex flex-wrap">-->
<div <!-- <div-->
v-for="item in workbenchData.menu" <!-- v-for="item in workbenchData.menu"-->
class="md:w-[12.5%] w-1/4 flex flex-col items-center" <!-- class="md:w-[12.5%] w-1/4 flex flex-col items-center"-->
:key="item" <!-- :key="item"-->
> <!-- >-->
<router-link :to="item.url" class="mb-3 flex flex-col items-center"> <!-- <router-link :to="item.url" class="mb-3 flex flex-col items-center">-->
<image-contain width="40px" height="40px" :src="item?.image" /> <!-- <image-contain width="40px" height="40px" :src="item?.image" />-->
<div class="mt-2">{{ item.name }}</div> <!-- <div class="mt-2">{{ item.name }}</div>-->
</router-link> <!-- </router-link>-->
</div> <!-- </div>-->
</div> <!-- </div>-->
</el-card> <!-- </el-card>-->
</div> <!-- </div>-->
<div class="lg:flex gap-4"> <div class="lg:flex gap-4">
<el-card class="!border-none w-full lg:w-3/3" shadow="never"> <el-card class="!border-none w-full lg:w-3/3" shadow="never">
<template #header> <template #header>
<span>访问量趋势图</span> <div class="flex justify-between items-center">
<span>招生趋势图</span>
<div class="flex items-center space-x-4">
<el-select
v-model="filterForm.teacherId"
class="w-[220px]"
clearable
filterable
placeholder="请选择招生老师"
@change="handleFilterChange"
>
<el-option
v-for="item in teacherOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<el-radio-group v-model="filterForm.rangeType" @change="handleFilterChange">
<el-radio-button label="day"></el-radio-button>
<el-radio-button label="week"></el-radio-button>
<el-radio-button label="month"></el-radio-button>
</el-radio-group>
</div>
</div>
</template> </template>
<div> <div>
<v-charts <v-charts
@ -125,6 +149,8 @@
import vCharts from 'vue-echarts' import vCharts from 'vue-echarts'
import { getWorkbench } from '@/api/app' import { getWorkbench } from '@/api/app'
import { teacherLists } from '@/api/teacher'
// //
const workbenchData: any = reactive({ const workbenchData: any = reactive({
version: { version: {
@ -137,9 +163,9 @@ const workbenchData: any = reactive({
} }
}, },
support: [], support: [],
today: {}, // today: {}, // /
menu: [], // menu: [], //
visitor: [], // 访 visitor: [], //
article: [], // article: [], //
visitorOption: { visitorOption: {
@ -148,151 +174,81 @@ const workbenchData: any = reactive({
data: [] data: []
}, },
yAxis: { yAxis: {
type: 'value' type: 'value',
name: '单位(人)'
},
tooltip: {
trigger: 'axis'
}, },
legend: { legend: {
data: ['访问量']
},
itemStyle: {
//
color: 'red'
},
tooltip: {
trigger: 'axis'
},
series: [
{
name: '访问量',
data: [],
type: 'line',
smooth: true,
lineStyle: {
color: '#4A5DFF',
width: 2
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: '#4A5DFF'
},
{
offset: 1,
color: '#5777ff'
}
]
},
opacity: 0.1
}
}
]
},
saleOption: {
xAxis: {
type: 'category',
data: [] data: []
}, },
yAxis: { series: []
type: 'value',
name: '单位(万)'
},
tooltip: {
trigger: 'axis'
},
series: [
{
data: [],
type: 'bar',
showBackground: true,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)',
borderRadius: [10, 10, 0, 0]
},
barWidth: '40%',
itemStyle: {
borderRadius: [10, 10, 0, 0],
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: '#4A5DFF'
},
{
offset: 1,
color: '#5777ff'
}
]
}
}
}
]
} }
}) })
//
const filterForm = reactive<{
teacherId: number | null
rangeType: 'day' | 'week' | 'month'
}>({
teacherId: null,
rangeType: 'week'
})
//
const teacherOptions = ref<Array<{ id: number; name: string }>>([])
const fetchTeacherOptions = async () => {
try {
const res: any = await teacherLists()
teacherOptions.value = res.lists || []
} catch (e) {
//
console.error('获取招生老师列表失败', e)
}
}
// //
const getData = () => { const getData = () => {
getWorkbench() const params: Record<string, any> = {
rangeType: filterForm.rangeType
}
if (filterForm.teacherId) {
params.teacherId = filterForm.teacherId
}
getWorkbench(params)
.then((res: any) => { .then((res: any) => {
workbenchData.version = res.version workbenchData.version = res.version
workbenchData.today = res.today workbenchData.today = res.today || {}
workbenchData.menu = res.menu workbenchData.menu = res.menu || []
workbenchData.visitor = res.visitor workbenchData.visitor = res.enrollmentTrend || {}
workbenchData.support = res.support workbenchData.support = res.support
// echarts const trend = res.enrollmentTrend || {}
workbenchData.visitorOption.xAxis.data = [] const dates: string[] = Array.isArray(trend.date) ? trend.date : []
workbenchData.visitorOption.series[0].data = [] const list: any[] = Array.isArray(trend.list) ? trend.list : []
workbenchData.saleOption.xAxis.data = []
workbenchData.saleOption.series[0].data = []
// // ECharts
res.visitor.date.reverse().forEach((item: any) => { workbenchData.visitorOption.xAxis.data = []
workbenchData.visitorOption.xAxis.data.push(item) workbenchData.visitorOption.series = []
workbenchData.visitorOption.legend.data = []
// 线
workbenchData.visitorOption.xAxis.data = dates
list.forEach((item: any) => {
const name = item.name || '未知老师'
const data = Array.isArray(item.data) ? item.data : []
workbenchData.visitorOption.legend.data.push(name)
workbenchData.visitorOption.series.push({
name,
type: 'line',
smooth: true,
data
}) })
res.visitor.list[0].data.forEach((item: any) => {
workbenchData.visitorOption.series[0].data.push(item)
})
res.sale.date.reverse().forEach((item: any) => {
workbenchData.saleOption.xAxis.data.push(item)
})
res.sale.list[0].data.forEach((item: any) => {
if (item <= 50) {
item = {
value: item,
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: '#ff8729'
},
{
offset: 1,
color: '#ff8729'
}
]
}
}
}
}
workbenchData.saleOption.series[0].data.push(item)
}) })
}) })
.catch((err: any) => { .catch((err: any) => {
@ -300,7 +256,12 @@ const getData = () => {
}) })
} }
const handleFilterChange = () => {
getData()
}
onMounted(() => { onMounted(() => {
fetchTeacherOptions()
getData() getData()
}) })
</script> </script>

View File

@ -7,6 +7,7 @@ import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -23,8 +24,10 @@ public class IndexController {
@GetMapping("/index") @GetMapping("/index")
@ApiOperation(value="控制台") @ApiOperation(value="控制台")
public AjaxResult<Map<String, Object>> index() { public AjaxResult<Map<String, Object>> index(
Map<String, Object> map = iIndexService.index(); @RequestParam(value = "teacherId", required = false) Integer teacherId,
@RequestParam(value = "rangeType", required = false) String rangeType) {
Map<String, Object> map = iIndexService.index(teacherId, rangeType);
return AjaxResult.success(map); return AjaxResult.success(map);
} }

View File

@ -18,6 +18,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List;
@RestController @RestController
@RequestMapping("adminapi/info") @RequestMapping("adminapi/info")
@ -58,6 +59,14 @@ public class StudentInfoController {
return AjaxResult.success(); return AjaxResult.success();
} }
@Log(title = "学生批量入学")
@GetMapping("/batchUpdateStudentStatus")
@ApiOperation(value = "学生批量入学")
public AjaxResult<Object> batchUpdateStudentStatus(@RequestParam("studentIdList") List<Integer> studentIdList) {
iStudentInfoService.batchUpdateStudentStatus(studentIdList);
return AjaxResult.success();
}
@Log(title = "学生信息删除") @Log(title = "学生信息删除")
@PostMapping("/del") @PostMapping("/del")
@ApiOperation(value = "学生信息删除") @ApiOperation(value = "学生信息删除")

View File

@ -11,9 +11,11 @@ public interface IIndexService {
* 控制台数据 * 控制台数据
* *
* @author fzr * @author fzr
* @param teacherId 招生老师ID可选
* @param rangeType 时间区间类型day / week / month可选默认 week
* @return Map<String, Object> * @return Map<String, Object>
*/ */
Map<String, Object> index(); Map<String, Object> index(Integer teacherId, String rangeType);
/** /**
* 公共配置 * 公共配置

View File

@ -9,6 +9,7 @@ import com.mdd.admin.vo.StudentInfoDetailVo;
import com.mdd.common.core.PageResult; import com.mdd.common.core.PageResult;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.List;
/** /**
* 学生信息服务接口类 * 学生信息服务接口类
@ -58,4 +59,6 @@ public interface IStudentInfoService {
* @param id 主键ID * @param id 主键ID
*/ */
void del(Integer id); void del(Integer id);
void batchUpdateStudentStatus(List<Integer> studentIdList);
} }

View File

@ -4,9 +4,21 @@ import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.mdd.admin.service.IIndexService; import com.mdd.admin.service.IIndexService;
import com.mdd.common.config.GlobalConfig; import com.mdd.common.config.GlobalConfig;
import com.mdd.common.util.*; import com.mdd.common.entity.StudentInfo;
import com.mdd.common.entity.Teacher;
import com.mdd.common.mapper.StudentInfoMapper;
import com.mdd.common.mapper.TeacherMapper;
import com.mdd.common.util.ConfigUtils;
import com.mdd.common.util.ListUtils;
import com.mdd.common.util.TimeUtils;
import com.mdd.common.util.UrlUtils;
import com.mdd.common.util.YmlUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
/** /**
@ -15,14 +27,22 @@ import java.util.*;
@Service @Service
public class IndexServiceImpl implements IIndexService { public class IndexServiceImpl implements IIndexService {
@Resource
private StudentInfoMapper studentInfoMapper;
@Resource
private TeacherMapper teacherMapper;
/** /**
* 控制台数据 * 控制台数据
* *
* @author fzr * @author fzr
* @param teacherId 招生老师ID可选
* @param rangeType 时间区间类型day / week / month可选默认 week
* @return Map<String, Object> * @return Map<String, Object>
*/ */
@Override @Override
public Map<String, Object> index() { public Map<String, Object> index(Integer teacherId, String rangeType) {
Map<String, Object> console = new LinkedHashMap<>(); Map<String, Object> console = new LinkedHashMap<>();
// 版本信息 // 版本信息
@ -37,30 +57,15 @@ public class IndexServiceImpl implements IIndexService {
version.put("channel", channel); version.put("channel", channel);
console.put("version", version); console.put("version", version);
// 今日数据 // 招生统计数据
Map<String, Object> today = new LinkedHashMap<>(); Map<String, Object> enrollmentStats = buildEnrollmentStats();
today.put("time", "2022-08-11 15:08:29"); console.put("today", enrollmentStats);
today.put("today_visitor", 10); // 访问量()
today.put("total_visitor", 100); // 总访问量
today.put("today_sales", 30); // 销售额()
today.put("total_sales", 65); // 总销售额
today.put("order_num", 12); // 订单量()
today.put("order_sum", 255); // 总订单量
today.put("today_new_user", 120); // 新增用户
today.put("total_new_user", 360); // 总访用户
console.put("today", today);
// 访客图表 // 招生趋势数据带筛选条件
Map<String, Object> visitor = new LinkedHashMap<>(); Map<String, Object> enrollmentTrend = buildEnrollmentTrend(teacherId, rangeType);
visitor.put("date", TimeUtils.daysAgoDate(15)); console.put("enrollmentTrend", enrollmentTrend);
visitor.put("list", new JSONArray() {{
add(new JSONObject() {{
put("name", "访客数");
put("data", Arrays.asList(12,13,11,5,8,22,14,9,456,62,78,12,18,22,46));
}});
}});
console.put("visitor", visitor);
// 常用功能菜单
console.put("menu", new JSONArray() {{ console.put("menu", new JSONArray() {{
add(new JSONObject() {{ add(new JSONObject() {{
@ -113,10 +118,190 @@ public class IndexServiceImpl implements IIndexService {
}}); }});
return console; return console;
} }
/**
* 构建招生统计数据
* - 总招生数量
* - 今日招生数量
* - 本周招生数量
* - 本月招生数量
*
* 招生数量定义student_status = 0 1 的学生数量
*/
private Map<String, Object> buildEnrollmentStats() {
Map<String, Object> stats = new LinkedHashMap<>();
// 当前时间
Date now = new Date();
stats.put("time", new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(now));
// 总招生数量所有时间
long totalCount = studentInfoMapper.selectCount(
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<StudentInfo>()
.in("student_status", 0, 1)
.isNull("delete_time")
);
// 今日本周本月时间范围
LocalDate today = LocalDate.now();
Date todayStart = toDate(today.atStartOfDay());
Date tomorrowStart = toDate(today.plusDays(1).atStartOfDay());
LocalDate weekStartDate = today.minusDays(6); // 最近7天
Date weekStart = toDate(weekStartDate.atStartOfDay());
LocalDate monthStartDate = today.minusDays(29); // 最近30天
Date monthStart = toDate(monthStartDate.atStartOfDay());
long todayCount = countEnrollmentInRange(todayStart, tomorrowStart);
long weekCount = countEnrollmentInRange(weekStart, tomorrowStart);
long monthCount = countEnrollmentInRange(monthStart, tomorrowStart);
stats.put("total_enroll_count", totalCount);
stats.put("today_enroll_count", todayCount);
stats.put("week_enroll_count", weekCount);
stats.put("month_enroll_count", monthCount);
return stats;
}
/**
* 计算时间区间内的招生数量 pre_registration_time 筛选
*/
private long countEnrollmentInRange(Date startTime, Date endTime) {
return studentInfoMapper.selectCount(
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<StudentInfo>()
.in("student_status", 0, 1)
.isNull("delete_time")
.isNotNull("pre_registration_time")
.ge("pre_registration_time", startTime)
.lt("pre_registration_time", endTime)
);
}
/**
* 构建招生趋势数据
*
* @param teacherId 招生老师ID为空则统计所有老师
* @param rangeType 时间区间类型day / week / month
*/
private Map<String, Object> buildEnrollmentTrend(Integer teacherId, String rangeType) {
Map<String, Object> result = new LinkedHashMap<>();
if (rangeType == null || rangeType.trim().isEmpty()) {
rangeType = "week";
}
LocalDate today = LocalDate.now();
LocalDate startDate;
switch (rangeType) {
case "day":
startDate = today;
break;
case "month":
startDate = today.minusDays(29);
break;
case "week":
default:
startDate = today.minusDays(6);
break;
}
LocalDate endDate = today;
Date startTime = toDate(startDate.atStartOfDay());
Date endTime = toDate(endDate.plusDays(1).atStartOfDay());
// 生成日期列表
List<String> dateList = new ArrayList<>();
Map<String, Integer> dateIndexMap = new LinkedHashMap<>();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
int idx = 0;
for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) {
String dateStr = d.format(formatter);
dateList.add(dateStr);
dateIndexMap.put(dateStr, idx++);
}
// 查询数据库中的统计数据按日期 + 招生老师分组
List<Map<String, Object>> rows = studentInfoMapper.selectEnrollmentTrend(startTime, endTime, teacherId);
// 收集涉及到的老师ID
Set<Integer> teacherIds = new LinkedHashSet<>();
for (Map<String, Object> row : rows) {
Object tIdObj = row.get("teacherId");
if (tIdObj != null) {
teacherIds.add(((Number) tIdObj).intValue());
}
}
Map<Integer, String> teacherNameMap = new LinkedHashMap<>();
if (!teacherIds.isEmpty()) {
List<Teacher> teachers = teacherMapper.selectBatchIds(teacherIds);
for (Teacher t : teachers) {
teacherNameMap.put(t.getTeacherId(), t.getTeacherName());
}
}
// 构建每个老师的时间序列数据
Map<Integer, List<Long>> teacherSeriesMap = new LinkedHashMap<>();
for (Integer tId : teacherIds) {
List<Long> initList = new ArrayList<>();
for (int i = 0; i < dateList.size(); i++) {
initList.add(0L);
}
teacherSeriesMap.put(tId, initList);
}
for (Map<String, Object> row : rows) {
Object dateObj = row.get("statDate");
Object tIdObj = row.get("teacherId");
Object countObj = row.get("enrollCount");
if (dateObj == null || tIdObj == null || countObj == null) {
continue;
}
String dateStr = String.valueOf(dateObj);
Integer tId = ((Number) tIdObj).intValue();
Integer index = dateIndexMap.get(dateStr);
if (index == null) {
continue;
}
List<Long> dataList = teacherSeriesMap.get(tId);
if (dataList == null) {
dataList = new ArrayList<>();
for (int i = 0; i < dateList.size(); i++) {
dataList.add(0L);
}
teacherSeriesMap.put(tId, dataList);
}
long count = ((Number) countObj).longValue();
dataList.set(index, count);
}
// 组装返回给前端的 list 数据
JSONArray seriesList = new JSONArray();
for (Map.Entry<Integer, List<Long>> entry : teacherSeriesMap.entrySet()) {
Integer tId = entry.getKey();
List<Long> data = entry.getValue();
JSONObject item = new JSONObject();
item.put("teacherId", tId);
item.put("name", teacherNameMap.getOrDefault(tId, "未知老师"));
item.put("data", data);
seriesList.add(item);
}
result.put("date", dateList);
result.put("list", seriesList);
result.put("rangeType", rangeType);
return result;
}
private Date toDate(java.time.LocalDateTime localDateTime) {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
/** /**
* 公共配置 * 公共配置
* *

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mdd.admin.validate.commons.PageValidate; import com.mdd.admin.validate.commons.PageValidate;
import com.mdd.admin.service.IStudentInfoService; import com.mdd.admin.service.IStudentInfoService;
import com.mdd.admin.validate.StudentInfoCreateValidate; import com.mdd.admin.validate.StudentInfoCreateValidate;
@ -12,18 +13,12 @@ import com.mdd.admin.validate.StudentInfoSearchValidate;
import com.mdd.admin.vo.StudentInfoListedVo; import com.mdd.admin.vo.StudentInfoListedVo;
import com.mdd.admin.vo.StudentInfoDetailVo; import com.mdd.admin.vo.StudentInfoDetailVo;
import com.mdd.common.core.PageResult; import com.mdd.common.core.PageResult;
import com.mdd.common.entity.*;
import com.mdd.common.entity.Class; import com.mdd.common.entity.Class;
import com.mdd.common.entity.College;
import com.mdd.common.entity.Major;
import com.mdd.common.entity.StudentInfo;
import com.mdd.common.entity.StudentBaseInfo;
import com.mdd.common.entity.admin.Admin; import com.mdd.common.entity.admin.Admin;
import com.mdd.common.entity.user.User; import com.mdd.common.entity.user.User;
import com.mdd.common.mapper.ClassMapper; import com.mdd.common.exception.OperateException;
import com.mdd.common.mapper.CollegeMapper; import com.mdd.common.mapper.*;
import com.mdd.common.mapper.MajorMapper;
import com.mdd.common.mapper.StudentInfoMapper;
import com.mdd.common.mapper.StudentBaseInfoMapper;
import com.mdd.common.mapper.admin.AdminMapper; import com.mdd.common.mapper.admin.AdminMapper;
import com.mdd.common.mapper.user.UserMapper; import com.mdd.common.mapper.user.UserMapper;
@ -33,6 +28,7 @@ import java.text.SimpleDateFormat;
import com.mdd.common.util.*; import com.mdd.common.util.*;
import io.netty.util.internal.ThreadLocalRandom; import io.netty.util.internal.ThreadLocalRandom;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -47,7 +43,7 @@ import java.util.*;
* @author gyp * @author gyp
*/ */
@Service @Service
public class StudentInfoServiceImpl implements IStudentInfoService { public class StudentInfoServiceImpl extends ServiceImpl<StudentInfoMapper, StudentInfo> implements IStudentInfoService {
@Resource @Resource
StudentInfoMapper studentInfoMapper; StudentInfoMapper studentInfoMapper;
@ -63,6 +59,8 @@ public class StudentInfoServiceImpl implements IStudentInfoService {
private AdminMapper adminMapper; private AdminMapper adminMapper;
@Autowired @Autowired
private UserMapper userMapper; private UserMapper userMapper;
@Autowired
private TeacherMapper teacherMapper;
/** /**
* 学生信息列表 * 学生信息列表
@ -95,6 +93,7 @@ public class StudentInfoServiceImpl implements IStudentInfoService {
"=:verifiedBy@verified_by:int", "=:verifiedBy@verified_by:int",
"=:recruitmentTeacherId@recruitment_teacher_id:int", "=:recruitmentTeacherId@recruitment_teacher_id:int",
"=:receptionTeacherId@reception_teacher_id:int", "=:receptionTeacherId@reception_teacher_id:int",
"datetime:createTimeStart-createTimeEnd@create_time:str",
}); });
IPage<StudentInfo> iPage = studentInfoMapper.selectPage(new Page<>(page, limit), queryWrapper); IPage<StudentInfo> iPage = studentInfoMapper.selectPage(new Page<>(page, limit), queryWrapper);
@ -133,12 +132,14 @@ public class StudentInfoServiceImpl implements IStudentInfoService {
Class clazz = classMapper.selectById(item.getClassId()); Class clazz = classMapper.selectById(item.getClassId());
Admin counselor = adminMapper.selectById(item.getCounselorId()); Admin counselor = adminMapper.selectById(item.getCounselorId());
Admin verifier = adminMapper.selectById(item.getVerifiedBy()); Admin verifier = adminMapper.selectById(item.getVerifiedBy());
Teacher teacher = teacherMapper.selectById(item.getRecruitmentTeacherId());
vo.setCollegeName(college != null ? college.getCollegeName() : ""); vo.setCollegeName(college != null ? college.getCollegeName() : "");
vo.setMajorName(major != null ? major.getMajorName() : ""); vo.setMajorName(major != null ? major.getMajorName() : "");
vo.setClassName(clazz != null ? clazz.getClassName() : ""); vo.setClassName(clazz != null ? clazz.getClassName() : "");
vo.setCounselorName(counselor != null ? counselor.getName() : ""); vo.setCounselorName(counselor != null ? counselor.getName() : "");
vo.setVerifierName(verifier != null ? verifier.getName() : ""); vo.setVerifierName(verifier != null ? verifier.getName() : "");
vo.setRecruitmentTeacherName(teacher != null ? teacher.getTeacherName() : null);
list.add(vo); list.add(vo);
} }
@ -232,6 +233,10 @@ public class StudentInfoServiceImpl implements IStudentInfoService {
studentInfo.setIsVerified(createValidate.getIsVerified() != null ? createValidate.getIsVerified() : 0); studentInfo.setIsVerified(createValidate.getIsVerified() != null ? createValidate.getIsVerified() : 0);
studentInfo.setVerifiedBy(createValidate.getVerifiedBy()); studentInfo.setVerifiedBy(createValidate.getVerifiedBy());
studentInfo.setVerifiedTime(createValidate.getVerifiedTime()); studentInfo.setVerifiedTime(createValidate.getVerifiedTime());
studentInfo.setPreRegistrationTime(createValidate.getPreRegistrationTime());
studentInfo.setRecruitmentTeacherId(createValidate.getRecruitmentTeacherId());
studentInfo.setReceptionTeacherId(createValidate.getReceptionTeacherId());
studentInfo.setInvitationCode(createValidate.getInvitationCode());
studentInfoMapper.insert(studentInfo); studentInfoMapper.insert(studentInfo);
// 创建基本信息 // 创建基本信息
@ -384,6 +389,15 @@ public class StudentInfoServiceImpl implements IStudentInfoService {
studentInfoMapper.delete(new QueryWrapper<StudentInfo>().eq("student_id", id)); studentInfoMapper.delete(new QueryWrapper<StudentInfo>().eq("student_id", id));
} }
@Override
public void batchUpdateStudentStatus(List<Integer> studentIdList) {
if (CollectionUtils.isEmpty(studentIdList)) {
throw new OperateException("批量入学失败, 学生idList丢失");
}
studentInfoMapper.batchUpdateStudentStatus(studentIdList);
}
private <T, M extends BaseMapper<T>> T getRandomEntity(M mapper) { private <T, M extends BaseMapper<T>> T getRandomEntity(M mapper) {
try { try {
QueryWrapper<T> wrapper = new QueryWrapper<>(); QueryWrapper<T> wrapper = new QueryWrapper<>();

View File

@ -1,5 +1,6 @@
package com.mdd.admin.validate; package com.mdd.admin.validate;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@ -75,7 +76,8 @@ public class StudentInfoCreateValidate implements Serializable {
private Integer receptionTeacherId; private Integer receptionTeacherId;
@ApiModelProperty(value = "预报名时间") @ApiModelProperty(value = "预报名时间")
private Long preRegistrationTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private java.util.Date preRegistrationTime;
@ApiModelProperty(value = "邀请码") @ApiModelProperty(value = "邀请码")
private String invitationCode; private String invitationCode;

View File

@ -56,4 +56,10 @@ public class StudentInfoSearchValidate implements Serializable {
@ApiModelProperty(value = "接待老师ID") @ApiModelProperty(value = "接待老师ID")
private Integer receptionTeacherId; private Integer receptionTeacherId;
@ApiModelProperty(value = "创建时间开始")
private String createTimeStart;
@ApiModelProperty(value = "创建时间结束")
private String createTimeEnd;
} }

View File

@ -1,5 +1,6 @@
package com.mdd.admin.validate; package com.mdd.admin.validate;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@ -25,7 +26,7 @@ public class StudentInfoUpdateValidate implements Serializable {
@ApiModelProperty(value = "主键") @ApiModelProperty(value = "主键")
private Long studentId; private Long studentId;
@NotNull(message = "studentNumber参数缺失") // @NotNull(message = "studentNumber参数缺失")
@ApiModelProperty(value = "学号") @ApiModelProperty(value = "学号")
private String studentNumber; private String studentNumber;
@ -41,19 +42,19 @@ public class StudentInfoUpdateValidate implements Serializable {
@ApiModelProperty(value = "班级ID") @ApiModelProperty(value = "班级ID")
private Integer classId; private Integer classId;
@NotNull(message = "grade参数缺失") // @NotNull(message = "grade参数缺失")
@ApiModelProperty(value = "年级") @ApiModelProperty(value = "年级")
private Integer grade; private Integer grade;
@NotNull(message = "enrollmentYear参数缺失") // @NotNull(message = "enrollmentYear参数缺失")
@ApiModelProperty(value = "入学年份") @ApiModelProperty(value = "入学年份")
private Integer enrollmentYear; private Integer enrollmentYear;
@NotNull(message = "expectedGraduationYear参数缺失") // @NotNull(message = "expectedGraduationYear参数缺失")
@ApiModelProperty(value = "预计毕业年份") @ApiModelProperty(value = "预计毕业年份")
private Integer expectedGraduationYear; private Integer expectedGraduationYear;
@NotNull(message = "studentStatus参数缺失") // @NotNull(message = "studentStatus参数缺失")
@ApiModelProperty(value = "学生状态: [0=预报名, 1=报名, 2=在读, 3=休学, 4=毕业, 5=退学]") @ApiModelProperty(value = "学生状态: [0=预报名, 1=报名, 2=在读, 3=休学, 4=毕业, 5=退学]")
private Integer studentStatus; private Integer studentStatus;
@ -85,7 +86,8 @@ public class StudentInfoUpdateValidate implements Serializable {
private Integer receptionTeacherId; private Integer receptionTeacherId;
@ApiModelProperty(value = "预报名时间") @ApiModelProperty(value = "预报名时间")
private Long preRegistrationTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private java.util.Date preRegistrationTime;
@ApiModelProperty(value = "邀请码") @ApiModelProperty(value = "邀请码")
private String invitationCode; private String invitationCode;

View File

@ -1,5 +1,6 @@
package com.mdd.admin.vo; package com.mdd.admin.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@ -69,7 +70,8 @@ public class StudentInfoDetailVo implements Serializable {
private Integer receptionTeacherId; private Integer receptionTeacherId;
@ApiModelProperty(value = "预报名时间") @ApiModelProperty(value = "预报名时间")
private Long preRegistrationTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private java.util.Date preRegistrationTime;
@ApiModelProperty(value = "邀请码") @ApiModelProperty(value = "邀请码")
private String invitationCode; private String invitationCode;

View File

@ -99,6 +99,9 @@ public class StudentInfoListedVo implements Serializable {
@ApiModelProperty(value = "招生老师ID") @ApiModelProperty(value = "招生老师ID")
private Integer recruitmentTeacherId; private Integer recruitmentTeacherId;
@ApiModelProperty(value = "招生老师名字")
private String recruitmentTeacherName;
@ApiModelProperty(value = "接待老师ID") @ApiModelProperty(value = "接待老师ID")
private Integer receptionTeacherId; private Integer receptionTeacherId;

View File

@ -72,7 +72,7 @@ public class StudentInfo implements Serializable {
private Integer receptionTeacherId; private Integer receptionTeacherId;
@ApiModelProperty(value = "预报名时间") @ApiModelProperty(value = "预报名时间")
private Long preRegistrationTime; private Date preRegistrationTime;
@ApiModelProperty(value = "邀请码") @ApiModelProperty(value = "邀请码")
private String invitationCode; private String invitationCode;

View File

@ -3,6 +3,11 @@ package com.mdd.common.mapper;
import com.mdd.common.core.basics.IBaseMapper; import com.mdd.common.core.basics.IBaseMapper;
import com.mdd.common.entity.StudentInfo; import com.mdd.common.entity.StudentInfo;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
import java.util.Map;
/** /**
* 学生信息Mapper * 学生信息Mapper
@ -10,4 +15,18 @@ import org.apache.ibatis.annotations.Mapper;
*/ */
@Mapper @Mapper
public interface StudentInfoMapper extends IBaseMapper<StudentInfo> { public interface StudentInfoMapper extends IBaseMapper<StudentInfo> {
void batchUpdateStudentStatus(@Param("studentIdList") List<Integer> studentIdList);
/**
* 按日期和招生老师统计招生数量student_status = 0 1
*
* @param startTime 开始时间
* @param endTime 结束时间不含
* @param teacherId 招生老师ID可选
* @return 每行包含 statDateyyyy-MM-ddteacherIdenrollCount
*/
List<Map<String, Object>> selectEnrollmentTrend(@Param("startTime") Date startTime,
@Param("endTime") Date endTime,
@Param("teacherId") Integer teacherId);
} }

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mdd.common.mapper.StudentInfoMapper">
<!-- 批量更新学生状态为在读(2) -->
<update id="batchUpdateStudentStatus">
UPDATE la_student_info
SET student_status = 2
WHERE student_id IN
<foreach collection="studentIdList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</update>
<!-- 按日期和招生老师统计招生数量student_status = 0 或 1时间区间按 pre_registration_time -->
<select id="selectEnrollmentTrend" resultType="map">
SELECT
DATE(pre_registration_time) AS statDate,
recruitment_teacher_id AS teacherId,
COUNT(*) AS enrollCount
FROM la_student_info
WHERE delete_time IS NULL
AND student_status IN (0, 1)
AND pre_registration_time IS NOT NULL
<if test="startTime != null">
AND pre_registration_time <![CDATA[>=]]> #{startTime}
</if>
<if test="endTime != null">
AND pre_registration_time <![CDATA[<]]> #{endTime}
</if>
<if test="teacherId != null">
AND recruitment_teacher_id = #{teacherId}
</if>
GROUP BY DATE(pre_registration_time), recruitment_teacher_id
ORDER BY statDate ASC
</select>
</mapper>