Bladeren bron

卡分配记录

jinxia.mo 1 maand geleden
bovenliggende
commit
1996099975

+ 9 - 0
back-ui/src/api/dz/classes.js

@@ -67,3 +67,12 @@ export function delClasses(classId) {
     method: 'delete'
   })
 }
+
+// 校验班级名称在同一学校下是否唯一
+export function checkClassName(schoolId, name, classId) {
+  return request({
+    url: '/dz/classes/checkClassName',
+    method: 'get',
+    params: { schoolId, name, classId }
+  })
+}

+ 39 - 38
back-ui/src/views/dz/campus/index.vue

@@ -35,7 +35,7 @@
                   />
                 </el-select>
               </el-form-item>
-              <el-form-item label="学校类型" prop="examTypes">
+              <!-- <el-form-item label="学校类型" prop="examTypes">
                 <el-select v-model="queryParams.examTypes" placeholder="请选择学校类型" clearable style="width: 170px">
                   <el-option
                           v-for="dict in exam_type"
@@ -44,7 +44,7 @@
                           :value="dict.value"
                   />
                 </el-select>
-              </el-form-item>
+              </el-form-item> -->
               <el-form-item label="省市区" prop="areaIds">
                 <AddressSelect class="w-[198px]" v-model="areaIds" />
               </el-form-item>
@@ -93,17 +93,17 @@
               <!--              <el-table-column label="省" align="center" prop="pro" />-->
               <!--              <el-table-column label="市" align="center" prop="city" />-->
               <!--              <el-table-column label="区" align="center" prop="area" />-->
-              <el-table-column label="所属地区" align="center" prop="proCityAreaName" />
+              <!-- <el-table-column label="所属地区" align="center" prop="proCityAreaName" /> -->
               <el-table-column label="状态" align="center" prop="status">
                 <template #default="scope">
                   <dict-tag :options="bool_values" :value="scope.row.status" />
                 </template>
               </el-table-column>
-              <el-table-column label="学校类型" align="center" prop="examTypes">
+              <!-- <el-table-column label="学校类型" align="center" prop="examTypes">
                 <template #default="scope">
                   <span>{{ formatExamTypes(scope.row.examTypes) }}</span>
                 </template>
-              </el-table-column>
+              </el-table-column> -->
               <el-table-column label="备注" align="center" prop="remark" />
               <el-table-column label="班级管理" align="center" width="120">
                 <template #default="scope">
@@ -149,9 +149,9 @@
         <!-- <el-form-item label="省份" prop="location">
           <el-input v-model="form.location" placeholder="请输入省份" />
         </el-form-item> -->
-        <el-form-item label="省市区" prop="areaIds">
+        <!-- <el-form-item label="省市区" prop="areaIds">
           <AddressSelect v-model="formAreaIds" style="width: 100%" />
-        </el-form-item>
+        </el-form-item> -->
         <!--        <el-form-item label="省" prop="pro">-->
         <!--          <el-input v-model="form.pro" placeholder="请输入省" />-->
         <!--        </el-form-item>-->
@@ -161,7 +161,7 @@
         <!--        <el-form-item label="区" prop="area">-->
         <!--          <el-input v-model="form.area" placeholder="请输入区" />-->
         <!--        </el-form-item>-->
-        <el-form-item label="学校类型" prop="examTypes">
+        <!-- <el-form-item label="学校类型" prop="examTypes">
           <el-select v-model="form.examTypes" placeholder="请选择学校类型" multiple>
             <el-option
                     v-for="dict in exam_type"
@@ -170,7 +170,7 @@
                     :value="dict.value"
             ></el-option>
           </el-select>
-        </el-form-item>
+        </el-form-item> -->
         <el-form-item label="状态" prop="status">
           <el-select v-model="form.status" placeholder="请选择状态">
             <el-option v-for="dict in bool_values" :key="dict.value" :label="dict.label"
@@ -203,7 +203,8 @@ import Classes from '@/views/dz/classes/index.vue';
 
 const { proxy } = getCurrentInstance()
 const appStore = useAppStore()
-const { bool_values, exam_type } = proxy.useDict('bool_values', 'exam_type')
+const { bool_values } = proxy.useDict('bool_values')
+// const { bool_values, exam_type } = proxy.useDict('bool_values', 'exam_type')
 const deptName = ref("")
 const deptOptions = ref(undefined)
 const enabledDeptOptions = ref(undefined)
@@ -242,7 +243,7 @@ const data = reactive({
     city: null,
     area: null,
     status: null,
-    examTypes: null,
+    // examTypes: null,
   },
   rules: {}
 })
@@ -390,7 +391,7 @@ function reset() {
     city: null,
     area: null,
     status: null,
-    examTypes: [],
+    // examTypes: [],
     createTime: null,
     updateTime: null
   }
@@ -446,11 +447,11 @@ function handleUpdate(row) {
   getCampus(_id).then(response => {
     form.value = response.data
     // 将 examTypes 字符串转换为数组(如果是逗号分隔的字符串)
-    if (form.value.examTypes && typeof form.value.examTypes === 'string') {
-      form.value.examTypes = form.value.examTypes.split(',').filter(item => item.trim())
-    } else if (!form.value.examTypes) {
-      form.value.examTypes = []
-    }
+    // if (form.value.examTypes && typeof form.value.examTypes === 'string') {
+    //   form.value.examTypes = form.value.examTypes.split(',').filter(item => item.trim())
+    // } else if (!form.value.examTypes) {
+    //   form.value.examTypes = []
+    // }
     // 将省市区数据转换为 formAreaIds 数组格式,用于 AddressSelect 组件回显
     // 按照顺序构建数组:[省, 市, 区],只包含有值的部分
     const areaArray = []
@@ -481,18 +482,18 @@ function submitForm() {
       };
 
       // 处理 examTypes:如果是数组,转换为逗号分隔的字符串
-      let examTypesValue = form.value.examTypes
-      if (Array.isArray(examTypesValue)) {
-        examTypesValue = examTypesValue.length > 0 ? examTypesValue.join(',') : null
-      } else if (!examTypesValue) {
-        examTypesValue = null
-      }
+      // let examTypesValue = form.value.examTypes
+      // if (Array.isArray(examTypesValue)) {
+      //   examTypesValue = examTypesValue.length > 0 ? examTypesValue.join(',') : null
+      // } else if (!examTypesValue) {
+      //   examTypesValue = null
+      // }
 
       // 合并表单数据和省市区数据
       const submitData = {
         ...form.value,
-        ...areaObj,
-        examTypes: examTypesValue
+        ...areaObj
+        // examTypes: examTypesValue
       };
 
       if (form.value.id != null) {
@@ -533,19 +534,19 @@ function handleExport() {
 }
 
 /** 格式化学校类型显示 */
-function formatExamTypes(examTypes) {
-  if (!examTypes) return ''
-  // 将字符串按逗号分割
-  const types = String(examTypes).split(',').map(item => item.trim()).filter(item => item)
-  if (types.length === 0) return ''
-  // 根据字典查找对应的标签
-  const labels = types.map(type => {
-    const dictItem = exam_type.value.find(item => item.value === type)
-    return dictItem ? dictItem.label : type
-  })
-  // 用英文逗号连接
-  return labels.join(',')
-}
+// function formatExamTypes(examTypes) {
+//   if (!examTypes) return ''
+//   // 将字符串按逗号分割
+//   const types = String(examTypes).split(',').map(item => item.trim()).filter(item => item)
+//   if (types.length === 0) return ''
+//   // 根据字典查找对应的标签
+//   const labels = types.map(type => {
+//     const dictItem = exam_type.value.find(item => item.value === type)
+//     return dictItem ? dictItem.label : type
+//   })
+//   // 用英文逗号连接
+//   return labels.join(',')
+// }
 
 /** 查看班级管理 */
 function handleViewClasses(row) {

+ 93 - 14
back-ui/src/views/dz/classes/index.vue

@@ -196,8 +196,9 @@
 </template>
 
 <script setup name="Classes">
-  import { listClasses, getClasses, delClasses, addClasses, updateClasses } from "@/api/dz/classes"
+  import { listClasses, getClasses, delClasses, addClasses, updateClasses, checkClassName } from "@/api/dz/classes"
   import {listAllSchool, listCampusSchoolList} from "@/api/dz/school"
+  import { debounce } from '@/utils/index';
 
   // 接收外部传入的 schoolId prop
   const props = defineProps({
@@ -220,6 +221,7 @@
   const multiple = ref(true)
   const total = ref(0)
   const title = ref("")
+  const classesRef = ref(null)
 
   const data = reactive({
     form: {},
@@ -233,21 +235,95 @@
       status: null,
       stats: null,
     },
-    rules: {
-      name: [
-        { required: true, message: "名称不能为空", trigger: "blur" }
-      ],
-      schoolId: [
-        { required: true, message: "所在校区不能为空", trigger: "blur" }
-      ],
-      online: [
-        { required: true, message: "是否线上不能为空", trigger: "change" }
-      ],
-    }
+    rules: {}
   })
 
+  // 班级名称校验函数(在 data 定义之后,可以访问 data.form)
+  const validateClassName = (rule, value, callback) => {
+    if (!value) {
+      callback()
+      return
+    }
+    const schoolId = data.form.schoolId
+    if (!schoolId) {
+      callback()
+      return
+    }
+    const classId = data.form.classId || null
+    // 如果名称包含逗号,需要校验每个名称
+    const nameArray = value.split(',').map(n => n.trim()).filter(n => n)
+    if (nameArray.length === 0) {
+      callback()
+      return
+    }
+    // 使用Promise.all并行校验所有名称
+    const checkPromises = nameArray.map(name => 
+      checkClassName(schoolId, name, classId).then(response => ({
+        name,
+        isUnique: response.data
+      }))
+    )
+    Promise.all(checkPromises).then(results => {
+      const duplicateNames = results.filter(r => !r.isUnique).map(r => r.name)
+      if (duplicateNames.length > 0) {
+        callback(new Error('班级名称【' + duplicateNames.join('、') + '】在该学校下已存在,请使用其他名称'))
+      } else {
+        callback()
+      }
+    }).catch(() => {
+      callback()
+    })
+  }
+
+  // 设置 rules(在 validateClassName 定义之后)
+  data.rules = {
+    name: [
+      { required: true, message: "名称不能为空", trigger: "blur" },
+      { validator: validateClassName, trigger: "blur" }
+    ],
+    schoolId: [
+      { required: true, message: "所在校区不能为空", trigger: "blur" }
+    ],
+    online: [
+      { required: true, message: "是否线上不能为空", trigger: "change" }
+    ],
+  }
+
   const { queryParams, form, rules } = toRefs(data)
 
+  // 防抖校验函数
+  const debouncedValidateClassName = debounce((name, schoolId, classId) => {
+    if (!name || !schoolId || !classesRef.value) return
+    // 如果名称包含逗号,需要校验每个名称
+    const nameArray = name.split(',').map(n => n.trim()).filter(n => n)
+    if (nameArray.length === 0) return
+    // 使用Promise.all并行校验所有名称
+    const checkPromises = nameArray.map(n => 
+      checkClassName(schoolId, n, classId).then(response => ({
+        name: n,
+        isUnique: response.data
+      }))
+    )
+    Promise.all(checkPromises).then(results => {
+      const duplicateNames = results.filter(r => !r.isUnique).map(r => r.name)
+      if (duplicateNames.length > 0) {
+        // 名称不唯一,触发校验错误
+        classesRef.value.validateField('name', () => {})
+      } else {
+        // 名称唯一,清除错误
+        classesRef.value.clearValidate('name')
+      }
+    })
+  }, 500)
+
+  // 监听名称和学校ID变化(仅在对话框打开时校验)
+  watch([() => form.value.name, () => form.value.schoolId], ([newName, newSchoolId]) => {
+    if (open.value && newName && newName.trim() && newSchoolId) {
+      const classId = form.value.classId || null
+      debouncedValidateClassName(newName.trim(), newSchoolId, classId)
+    }
+  })
+
   // 计算属性:判断是否有外部传入的 schoolId
   const isExternalSchoolIdSet = computed(() => {
     const schoolId = props.externalSchoolId
@@ -302,7 +378,10 @@
       createTime: null,
       updateTime: null
     }
-    proxy.resetForm("classesRef")
+    if (classesRef.value) {
+      classesRef.value.resetFields()
+      classesRef.value.clearValidate()
+    }
   }
 
   /** 搜索按钮操作 */
@@ -348,7 +427,7 @@
 
   /** 提交按钮 */
   function submitForm() {
-    proxy.$refs["classesRef"].validate(valid => {
+    classesRef.value.validate(valid => {
       if (valid) {
         if (form.value.classId != null) {
           updateClasses(form.value).then(response => {

+ 53 - 12
back-ui/src/views/dz/open/config/form.js

@@ -7,29 +7,70 @@ const info = [
   //   search: true,
   //   placeholder: "请输入申请人",
   // },
+  // {
+  //   label: "卡号",
+  //   name: "cardNo",
+  //   value: "",
+  //   type: "text",
+  //   search: true,
+  //   placeholder: "请输入卡号",
+  // },
+  // {
+  //   label: "申请状态",
+  //   name: "status",
+  //   value: "",
+  //   type: "select",
+  //   search: true,
+  //   placeholder: "请选择申请状态",
+  //   option: [], // 动态设置,使用audit_status字典
+  //   optionLabel: "label",
+  //   optionValue: "value",
+  // },
   {
-    label: "卡号",
-    name: "cardNo",
+    label: "平台机构",
+    name: "deptId",
     value: "",
-    type: "text",
+    type: "select",
     search: true,
-    placeholder: "请输入卡号",
+    placeholder: "请选择平台机构",
+    option: [], // 动态设置
+    optionLabel: "label",
+    optionValue: "id",
+  },
+  {
+    label: "代理商",
+    name: "agentId",
+    value: "",
+    type: "select",
+    search: true,
+    placeholder: "请选择代理商",
+    option: [], // 动态设置
+    optionLabel: "label",
+    optionValue: "value",
+  },
+  {
+    label: "卡类型",
+    name: "cardType",
+    value: "",
+    type: "select",
+    search: true,
+    placeholder: "请选择卡类型",
+    option: [], // 动态设置
+    optionLabel: "label",
+    optionValue: "value",
   },
   {
-    label: "申请状态",
-    name: "status",
+    label: "重新开卡",
+    name: "isReopen",
     value: "",
     type: "select",
     search: true,
-    placeholder: "请选择申请状态",
-    option: [
-      { label: "待审核", value: "0" },
-      { label: "审核通过", value: "1" },
-      { label: "拒绝", value: "2" },
-    ],
+    placeholder: "请选择是否重新开卡",
+    option: [], // 动态设置
     optionLabel: "label",
     optionValue: "value",
   },
+  
 ];
 
 const formInfo = {

+ 50 - 19
back-ui/src/views/dz/open/config/table.js

@@ -7,6 +7,18 @@ const tableConfig = {
       align: "center",
       width: 80,
     },
+    {
+      label: "平台机构",
+      prop: "deptName",
+      align: "center",
+      minWidth: 150,
+    },
+    {
+      label: "学校",
+      prop: "schoolName",
+      align: "center",
+      minWidth: 150,
+    },
     {
       label: "卡号段",
       prop: "cardRange",
@@ -23,26 +35,28 @@ const tableConfig = {
       type: "slot",
       slotName: "quantity",
     },
+    // {
+    //   label: "申请人",
+    //   prop: "applicant",
+    //   align: "center",
+    //   minWidth: 120,
+    // },
     {
-      label: "申请人",
-      prop: "applicant",
-      align: "center",
-      minWidth: 120,
-    },
-    {
-      label: "学校",
-      prop: "schoolName",
+      label: "卡类型",
+      prop: "cardType",
       align: "center",
-      minWidth: 150,
+      width: 100,
+      type: "dict",
+      options: [], // 动态设置
     },
     {
-      label: "申请时间",
-      prop: "createTime",
+      label: "代理商",
+      prop: "agentName",
       align: "center",
-      width: 180,
-      type: "time",
-      format: "{y}-{m}-{d} {h}:{i}:{s}",
+      minWidth: 120,
     },
+    
+    
     {
       label: "申请状态",
       prop: "status",
@@ -51,13 +65,30 @@ const tableConfig = {
       type: "slot",
       slotName: "status",
     },
+    // {
+    //   label: "原因",
+    //   prop: "auditDesc",
+    //   align: "center",
+    //   minWidth: 120,
+    //   type: "slot",
+    //   slotName: "auditDesc",
+    // },
+    
     {
-      label: "原因",
-      prop: "auditDesc",
+      label: "重新开卡",
+      prop: "isReopen",
       align: "center",
-      minWidth: 120,
-      type: "slot",
-      slotName: "auditDesc",
+      width: 100,
+      type: "dict",
+      options: [], // 动态设置
+    },
+    {
+      label: "分配时间",
+      prop: "createTime",
+      align: "center",
+      width: 180,
+      type: "time",
+      format: "{y}-{m}-{d} {h}:{i}:{s}",
     },
   ],
 

+ 104 - 5
back-ui/src/views/dz/open/index.vue

@@ -200,6 +200,8 @@ import tableConfig from "./config/table.js";
 import {ref} from "vue";
 import {getPaperProvinces} from "@/api/dz/papers.js";
 import {getCampusSchoolList} from "@/api/dz/cards.js";
+import { deptTreeSelect } from "@/api/system/user";
+import { listAgent } from "@/api/dz/agent";
 
 const { proxy } = getCurrentInstance();
 
@@ -208,20 +210,48 @@ const open = ref(false);
 const reviewOpen = ref(false);
 const provinceList = ref([]); // 省份列表
 const schoolList = ref([]); // 学校列表
+const deptOptions = ref([]); // 机构列表
+const agentOptions = ref([]); // 代理商列表
 const loading = ref(true);
 const showSearch = ref(true);
 const total = ref(0);
 const title = ref("");
 const selectedRows = ref([]);
-const { card_status, card_distribute_status, card_time_status, bool_values, card_pay_status, audit_status, } = proxy.useDict( "card_status", "card_distribute_status", "card_time_status", "bool_values", "card_pay_status", "audit_status" );
+const { card_status, card_distribute_status, card_time_status, bool_values, card_pay_status, audit_status, card_type } = proxy.useDict( "card_status", "card_distribute_status", "card_time_status", "bool_values", "card_pay_status", "audit_status", "card_type" );
 
 // 搜索配置
 const searchConfig = computed(() => {
-  return formConfig.info.filter((item) => item.search);
+  const config = formConfig.info.filter((item) => item.search);
+  // 动态设置字典选项
+  config.forEach((item) => {
+    if (item.name === 'status') {
+      item.option = audit_status.value || [];
+    } else if (item.name === 'cardType') {
+      item.option = card_type.value || [];
+    } else if (item.name === 'isReopen') {
+      item.option = bool_values.value || [];
+    } else if (item.name === 'deptId') {
+      item.option = deptOptions.value || [];
+    } else if (item.name === 'agentId') {
+      item.option = agentOptions.value || [];
+    }
+  });
+  return config;
 });
 
 // 表格配置
-const tableConfigData = computed(() => tableConfig);
+const tableConfigData = computed(() => {
+  const config = JSON.parse(JSON.stringify(tableConfig));
+  // 动态设置字典选项
+  config.columns.forEach((column) => {
+    if (column.prop === 'cardType') {
+      column.options = card_type.value || [];
+    } else if (column.prop === 'isReopen') {
+      column.options = bool_values.value || [];
+    }
+  });
+  return config;
+});
 
 const data = reactive({
   form: {},
@@ -242,6 +272,10 @@ const data = reactive({
     applicant: null,
     cardNo: null,
     status: null,
+    cardType: null,
+    isReopen: null,
+    deptId: null,
+    agentId: null,
   },
   rules: {
     applicant: [{ required: true, message: "申请人不能为空", trigger: "blur" }],
@@ -299,6 +333,54 @@ async function getCampusListData() {
   }
 }
 
+// 获取机构列表
+async function getDeptList() {
+  try {
+    const response = await deptTreeSelect();
+    if (response.code === 200) {
+      // 将树形结构扁平化
+      const flattenDept = (depts) => {
+        let result = [];
+        depts.forEach(dept => {
+          result.push({
+            id: dept.id,
+            label: dept.label
+          });
+          if (dept.children && dept.children.length > 0) {
+            result = result.concat(flattenDept(dept.children));
+          }
+        });
+        return result;
+      };
+      deptOptions.value = flattenDept(response.data || []);
+    }
+  } catch (error) {
+    console.error("获取机构列表失败:", error);
+  }
+}
+
+// 根据deptId获取代理商列表
+async function getAgentListByDeptId(deptId) {
+  if (!deptId) {
+    agentOptions.value = [];
+    return;
+  }
+  try {
+    const response = await listAgent({ deptId: deptId });
+    if (response.code === 200) {
+      // 将代理商列表转换为选项格式(返回的是扁平数组)
+      const dataList = response.data || [];
+      agentOptions.value = dataList.map(item => ({
+        value: item.agentId,
+        label: item.name
+      }));
+    }
+  } catch (error) {
+    console.error("获取代理商列表失败:", error);
+    agentOptions.value = [];
+  }
+}
+
 // 计算申请数量
 function calculateQuantity(startNo, endNo) {
   if (!startNo || !endNo) return 0;
@@ -377,6 +459,8 @@ function handleQuery() {
 
 /** 重置按钮操作 */
 function resetQuery() {
+  // 清空代理商列表
+  agentOptions.value = [];
   proxy.resetForm("queryRef");
   handleQuery();
 }
@@ -484,8 +568,12 @@ function handleExport() {
     `open_${new Date().getTime()}.xlsx`
   );
 }
-getProvinceList();
-getList();
+// 初始化
+onMounted(() => {
+  getProvinceList();
+  getDeptList();
+  getList();
+});
 
 // 监听省份变化,获取对应学校
 watch(
@@ -497,6 +585,17 @@ watch(
       getCampusListData(newProvince);
     }
 );
+
+// 监听平台机构变化,获取对应代理商
+watch(
+    () => queryParams.value.deptId,
+    (newDeptId) => {
+      // 重置代理商选择
+      queryParams.value.agentId = null;
+      // 获取对应机构的代理商列表
+      getAgentListByDeptId(newDeptId);
+    }
+);
 </script>
 <style>
 .detail {

+ 73 - 1
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzCardsOpenController.java

@@ -1,18 +1,27 @@
 package com.ruoyi.web.controller.dz;
 
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.ValidationException;
 
+import com.ruoyi.common.core.domain.entity.SysDept;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.dz.domain.DzAgent;
+import com.ruoyi.dz.domain.DzSchool;
+import com.ruoyi.dz.mapper.DzAgentMapper;
+import com.ruoyi.dz.mapper.DzSchoolMapper;
+import com.ruoyi.system.mapper.SysDeptMapper;
 import com.ruoyi.dz.service.IDzCardsService;
 import com.ruoyi.enums.CardAudit;
-import com.ruoyi.enums.CardDistributeStatus;
 import com.ruoyi.enums.RequestStatus;
 import com.ruoyi.enums.UserTypeEnum;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -46,6 +55,12 @@ public class DzCardsOpenController extends BaseController
     private IDzCardsOpenService dzCardsOpenService;
     @Autowired
     private IDzCardsService dzCardsService;
+    @Autowired
+    private DzSchoolMapper dzSchoolMapper;
+    @Autowired
+    private DzAgentMapper dzAgentMapper;
+    @Autowired
+    private SysDeptMapper sysDeptMapper;
 
     /**
      * 查询开卡申请列表
@@ -61,6 +76,63 @@ public class DzCardsOpenController extends BaseController
         }
         startPage();
         List<DzCardsOpen> list = dzCardsOpenService.selectDzCardsOpenList(dzCardsOpen);
+        
+        // 提取所有的schoolId、agentId和deptId
+        Set<Long> schoolIds = list.stream()
+                .map(DzCardsOpen::getSchoolId)
+                .filter(id -> id != null)
+                .collect(Collectors.toSet());
+        
+        Set<Long> agentIds = list.stream()
+                .map(DzCardsOpen::getAgentId)
+                .filter(id -> id != null)
+                .collect(Collectors.toSet());
+        
+        Set<Long> deptIds = list.stream()
+                .map(DzCardsOpen::getDeptId)
+                .filter(id -> id != null)
+                .collect(Collectors.toSet());
+        
+        // 批量查询学校
+        Map<Long, String> schoolMap = java.util.Collections.emptyMap();
+        if (CollectionUtils.isNotEmpty(schoolIds)) {
+            List<DzSchool> schoolList = dzSchoolMapper.selectDzSchoolListByIds(new java.util.ArrayList<>(schoolIds));
+            schoolMap = schoolList.stream()
+                    .filter(school -> school.getId() != null && school.getName() != null)
+                    .collect(Collectors.toMap(DzSchool::getId, DzSchool::getName, (existing, replacement) -> existing));
+        }
+        
+        // 批量查询代理商
+        Map<Long, String> agentMap = java.util.Collections.emptyMap();
+        if (CollectionUtils.isNotEmpty(agentIds)) {
+            List<DzAgent> agentList = dzAgentMapper.selectDzAgentByAgentIds(new java.util.ArrayList<>(agentIds));
+            agentMap = agentList.stream()
+                    .filter(agent -> agent.getAgentId() != null && agent.getName() != null)
+                    .collect(Collectors.toMap(DzAgent::getAgentId, DzAgent::getName, (existing, replacement) -> existing));
+        }
+        
+        // 批量查询机构
+        Map<Long, String> deptMap = java.util.Collections.emptyMap();
+        if (CollectionUtils.isNotEmpty(deptIds)) {
+            List<SysDept> deptList = sysDeptMapper.selectDeptByIds(new java.util.ArrayList<>(deptIds));
+            deptMap = deptList.stream()
+                    .filter(dept -> dept.getDeptId() != null && dept.getDeptName() != null)
+                    .collect(Collectors.toMap(SysDept::getDeptId, SysDept::getDeptName, (existing, replacement) -> existing));
+        }
+        
+        // 填充schoolName、agentName和deptName
+        for (DzCardsOpen item : list) {
+            if (item.getSchoolId() != null && schoolMap.containsKey(item.getSchoolId())) {
+                item.setSchoolName(schoolMap.get(item.getSchoolId()));
+            }
+            if (item.getAgentId() != null && agentMap.containsKey(item.getAgentId())) {
+                item.setAgentName(agentMap.get(item.getAgentId()));
+            }
+            if (item.getDeptId() != null && deptMap.containsKey(item.getDeptId())) {
+                item.setDeptName(deptMap.get(item.getDeptId()));
+            }
+        }
+        
         return getDataTable(list);
     }
 

+ 39 - 4
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzClassesController.java

@@ -26,6 +26,7 @@ import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
@@ -130,10 +131,25 @@ public class DzClassesController extends BaseController
         DzSchool dzSchool = dzSchoolMapper.selectDzSchoolById(dzClasses.getSchoolId());
         dzClasses.setDeptId(dzSchool.getDeptId());
         if(StringUtils.isNotEmpty(name)){
-            for (String s : name.split(",")) {
-                dzClasses.setName(s);
-                dzClasses.setIsDefault(BoolValues.no.getValue());
-                dzClassesService.insertDzClasses(dzClasses);
+            // 校验每个班级名称是否重复
+            String[] nameArray = name.split(",");
+            for (String s : nameArray) {
+                String trimmedName = s.trim();
+                if (StringUtils.isNotEmpty(trimmedName)) {
+                    // 校验班级名称是否重复
+                    if (!dzClassesService.checkClassName(dzClasses.getSchoolId(), trimmedName, null)) {
+                        return error("班级名称【" + trimmedName + "】在该学校下已存在,请使用其他名称");
+                    }
+                }
+            }
+            // 校验通过后,创建班级
+            for (String s : nameArray) {
+                String trimmedName = s.trim();
+                if (StringUtils.isNotEmpty(trimmedName)) {
+                    dzClasses.setName(trimmedName);
+                    dzClasses.setIsDefault(BoolValues.no.getValue());
+                    dzClassesService.insertDzClasses(dzClasses);
+                }
             }
             //把创建的默认旧班级删除
             if (null!=dzClasses.getSchoolId()){
@@ -160,6 +176,13 @@ public class DzClassesController extends BaseController
     @PutMapping
     public AjaxResult edit(@RequestBody DzClasses dzClasses)
     {
+        // 校验班级名称是否重复
+        if (StringUtils.isNotEmpty(dzClasses.getName()) && dzClasses.getSchoolId() != null) {
+            String trimmedName = dzClasses.getName().trim();
+            if (!dzClassesService.checkClassName(dzClasses.getSchoolId(), trimmedName, dzClasses.getClassId())) {
+                return error("班级名称【" + trimmedName + "】在该学校下已存在,请使用其他名称");
+            }
+        }
         return toAjax(dzClassesService.updateDzClasses(dzClasses));
     }
 
@@ -173,4 +196,16 @@ public class DzClassesController extends BaseController
     {
         return toAjax(dzClassesService.deleteDzClassesByClassIds(classIds));
     }
+
+    /**
+     * 校验班级名称在同一学校下是否唯一
+     */
+    @GetMapping("/checkClassName")
+    public AjaxResult checkClassName(@RequestParam("schoolId") Long schoolId, 
+                                     @RequestParam("name") String name, 
+                                     @RequestParam(value = "classId", required = false) Long classId)
+    {
+        boolean isUnique = dzClassesService.checkClassName(schoolId, name, classId);
+        return AjaxResult.success(isUnique);
+    }
 }

+ 40 - 0
ie-system/src/main/java/com/ruoyi/dz/domain/DzCardsOpen.java

@@ -45,6 +45,10 @@ public class DzCardsOpen extends BaseEntity
     @Excel(name = "学校")
     private Long schoolId;
 
+    /** 机构ID */
+    @Excel(name = "机构ID")
+    private Long deptId;
+
     /** 发件人 */
     @Excel(name = "发件人")
     private String sender;
@@ -53,6 +57,10 @@ public class DzCardsOpen extends BaseEntity
     @Excel(name = "重新开卡:0否,1是")
     private Integer isReopen;
 
+    /** 卡类型 */
+    @Excel(name = "卡类型")
+    private Integer cardType;
+
     /** 状态(0:无效,1:审核通过,2:拒绝) */
     @Excel(name = "状态(0:无效,1:审核通过,2:审核拒绝)")
     private Integer status;
@@ -66,6 +74,9 @@ public class DzCardsOpen extends BaseEntity
     /** 校区 */
     private String schoolName;
 
+    /** 机构名称 */
+    private String deptName;
+
 
     public String getLocation() {
         return location;
@@ -135,6 +146,16 @@ public class DzCardsOpen extends BaseEntity
         return schoolId;
     }
 
+    public void setDeptId(Long deptId)
+    {
+        this.deptId = deptId;
+    }
+
+    public Long getDeptId()
+    {
+        return deptId;
+    }
+
     public void setSender(String sender) 
     {
         this.sender = sender;
@@ -155,6 +176,16 @@ public class DzCardsOpen extends BaseEntity
         return isReopen;
     }
 
+    public void setCardType(Integer cardType)
+    {
+        this.cardType = cardType;
+    }
+
+    public Integer getCardType()
+    {
+        return cardType;
+    }
+
     public void setStatus(Integer status)
     {
         this.status = status;
@@ -189,6 +220,14 @@ public class DzCardsOpen extends BaseEntity
         this.schoolName = schoolName;
     }
 
+    public String getDeptName() {
+        return deptName;
+    }
+
+    public void setDeptName(String deptName) {
+        this.deptName = deptName;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@@ -201,6 +240,7 @@ public class DzCardsOpen extends BaseEntity
             .append("sender", getSender())
             .append("createTime", getCreateTime())
             .append("isReopen", getIsReopen())
+            .append("cardType", getCardType())
             .append("status", getStatus())
             .toString();
     }

+ 10 - 0
ie-system/src/main/java/com/ruoyi/dz/mapper/DzClassesMapper.java

@@ -71,4 +71,14 @@ public interface DzClassesMapper
      * @return 结果
      */
     public int deleteDzClassesByClassIds(Long[] classIds);
+
+    /**
+     * 校验班级名称在同一学校下是否唯一
+     *
+     * @param schoolId 学校ID
+     * @param name 班级名称
+     * @param classId 班级ID(修改时传入,新增时为null)
+     * @return 结果(0表示唯一,大于0表示重复)
+     */
+    public int checkClassName(@Param("schoolId") Long schoolId, @Param("name") String name, @Param("classId") Long classId);
 }

+ 10 - 0
ie-system/src/main/java/com/ruoyi/dz/service/IDzClassesService.java

@@ -61,4 +61,14 @@ public interface IDzClassesService
      * @return 结果
      */
     public int deleteDzClassesByClassId(Long classId);
+
+    /**
+     * 校验班级名称在同一学校下是否唯一
+     *
+     * @param schoolId 学校ID
+     * @param name 班级名称
+     * @param classId 班级ID(修改时传入,新增时为null)
+     * @return true表示唯一,false表示重复
+     */
+    public boolean checkClassName(Long schoolId, String name, Long classId);
 }

+ 18 - 0
ie-system/src/main/java/com/ruoyi/dz/service/impl/DzClassesServiceImpl.java

@@ -109,4 +109,22 @@ public class DzClassesServiceImpl implements IDzClassesService
     {
         return dzClassesMapper.deleteDzClassesByClassId(classId);
     }
+
+    /**
+     * 校验班级名称在同一学校下是否唯一
+     *
+     * @param schoolId 学校ID
+     * @param name 班级名称
+     * @param classId 班级ID(修改时传入,新增时为null)
+     * @return true表示唯一,false表示重复
+     */
+    @Override
+    public boolean checkClassName(Long schoolId, String name, Long classId)
+    {
+        if (schoolId == null || name == null || name.trim().isEmpty()) {
+            return true;
+        }
+        int count = dzClassesMapper.checkClassName(schoolId, name.trim(), classId);
+        return count == 0;
+    }
 }

+ 11 - 1
ie-system/src/main/resources/mapper/dz/DzCardsOpenMapper.xml

@@ -12,16 +12,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="endDate"    column="end_date"    />
         <result property="location"    column="location"    />
         <result property="schoolId"    column="school_id"    />
+        <result property="deptId"    column="dept_id"    />
         <result property="sender"    column="sender"    />
         <result property="createTime"    column="create_time"    />
         <result property="isReopen"    column="is_reopen"    />
         <result property="status"    column="status"    />
         <result property="auditDesc"    column="audit_desc"    />
+        <result property="cardType"    column="card_type"    />
 
     </resultMap>
 
     <sql id="selectDzCardsOpenVo">
-        select id, agent_id, start_no, end_no, end_date, location, school_id, sender, create_time, is_reopen, status, audit_desc from dz_cards_open
+        select id, agent_id, start_no, end_no, end_date, location, school_id, dept_id, sender, create_time, is_reopen, status, audit_desc, card_type from dz_cards_open
     </sql>
 
     <select id="selectDzCardsOpenList" parameterType="DzCardsOpen" resultMap="DzCardsOpenResult">
@@ -33,9 +35,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="endDate != null "> and end_date = #{endDate}</if>
             <if test="location != null "> and location = #{location}</if>
             <if test="schoolId != null "> and school_id = #{schoolId}</if>
+            <if test="deptId != null "> and dept_id = #{deptId}</if>
             <if test="sender != null  and sender != ''"> and sender = #{sender}</if>
             <if test="isReopen != null "> and is_reopen = #{isReopen}</if>
             <if test="status != null "> and status = #{status}</if>
+            <if test="cardType != null "> and card_type = #{cardType}</if>
         </where>
     </select>
     
@@ -53,10 +57,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="endDate != null">end_date,</if>
             <if test="location != null">location,</if>
             <if test="schoolId != null">school_id,</if>
+            <if test="deptId != null">dept_id,</if>
             <if test="sender != null">sender,</if>
             <if test="createTime != null">create_time,</if>
             <if test="isReopen != null">is_reopen,</if>
             <if test="status != null">status,</if>
+            <if test="cardType != null">card_type,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="agentId != null">#{agentId},</if>
@@ -65,10 +71,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="endDate != null">#{endDate},</if>
             <if test="location != null">#{location},</if>
             <if test="schoolId != null">#{schoolId},</if>
+            <if test="deptId != null">#{deptId},</if>
             <if test="sender != null">#{sender},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="isReopen != null">#{isReopen},</if>
             <if test="status != null">#{status},</if>
+            <if test="cardType != null">#{cardType},</if>
          </trim>
     </insert>
 
@@ -81,10 +89,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="endDate != null">end_date = #{endDate},</if>
             <if test="location != null">location = #{location},</if>
             <if test="schoolId != null">school_id = #{schoolId},</if>
+            <if test="deptId != null">dept_id = #{deptId},</if>
             <if test="sender != null">sender = #{sender},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="isReopen != null">is_reopen = #{isReopen},</if>
             <if test="status != null">status = #{status},</if>
+            <if test="cardType != null">card_type = #{cardType},</if>
             <if test="auditDesc != null ">audit_desc = #{auditDesc}</if>
         </trim>
         where id = #{id}

+ 8 - 0
ie-system/src/main/resources/mapper/dz/DzClassesMapper.xml

@@ -210,4 +210,12 @@
             #{classId}
         </foreach>
     </delete>
+
+    <!-- 校验班级名称在同一学校下是否唯一 -->
+    <select id="checkClassName" resultType="int">
+        select count(1) from dz_classes where school_id = #{schoolId} and name = #{name}
+        <if test="classId != null">
+            and class_id != #{classId}
+        </if>
+    </select>
 </mapper>