修改主页工作台布局以及数据显示, 改成预报名报表统计页面
This commit is contained in:
parent
737c7a1ff1
commit
c3a8f590e8
|
|
@ -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 })
|
||||||
}
|
}
|
||||||
|
|
||||||
//字典数据
|
//字典数据
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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: '',
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 = "学生信息删除")
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 公共配置
|
* 公共配置
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 公共配置
|
* 公共配置
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -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<>();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 每行包含 statDate(yyyy-MM-dd)、teacherId、enrollCount
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> selectEnrollmentTrend(@Param("startTime") Date startTime,
|
||||||
|
@Param("endTime") Date endTime,
|
||||||
|
@Param("teacherId") Integer teacherId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
Loading…
Reference in New Issue