Explorar o código

feat(physical): 添加裁判管理功能模块

- 创建裁判管理实体类 PhysicalJudge,包含姓名、图片URL、工作年限等字段
- 实现裁判管理业务对象 PhysicalJudgeBo 和视图对象 PhysicalJudgeVo
- 开发裁判管理服务接口 IPhysicalJudgeService 及其实现类
- 添加裁判管理控制器 PhysicalJudgeController,提供增删改查和导入导出功能
- 创建裁判编号生成工具类 JudgeCodeGeneratorUtils,生成CP开头的裁判编号
- 实现裁判数据的Excel导入功能,支持批量导入裁判信息
- 配置裁判管理的数据库映射和SQL语句
- 添加裁判数据的分页查询和条件查询功能
fugui001 hai 1 día
pai
achega
1a0abd878f

+ 35 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/utils/JudgeCodeGeneratorUtils.java

@@ -0,0 +1,35 @@
+package org.dromara.business.utils;
+
+import org.springframework.stereotype.Component;
+
+import java.util.Random;
+
+
+/**
+ * 裁判编号生成工具类
+ * 格式:CP + 4位数字(如 CP0001)
+ */
+@Component
+public class JudgeCodeGeneratorUtils {
+
+    private static final String PREFIX = "CP";
+    private static final int DIGITS = 4;
+    private static final int MAX_VALUE = (int) Math.pow(10, DIGITS) - 1; // 9999
+    private static final Random random = new Random(); // 线程安全(Random 是线程安全的)
+
+    /**
+     * 生成随机裁判编号,格式:CP + 4位数字(不足补零)
+     * @return 例如 "CP2847", "CP0034"
+     */
+    public static String generateRandomCode() {
+        int number = random.nextInt(MAX_VALUE + 1); // [0, 9999]
+        return String.format("%s%0" + DIGITS + "d", PREFIX, number);
+    }
+
+    // 可选:带前缀和位数的通用方法
+    public static String generate(String prefix, int digits) {
+        int max = (int) Math.pow(10, digits) - 1;
+        int num = new Random().nextInt(max + 1);
+        return String.format("%s%0" + digits + "d", prefix, num);
+    }
+}

+ 132 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/physical/controller/PhysicalJudgeController.java

@@ -0,0 +1,132 @@
+package org.dromara.physical.controller;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.business.domain.vo.BlindLevelsImportVo;
+import org.dromara.physical.domain.bo.PhysicalBlindStructuresBo;
+import org.dromara.physical.domain.bo.PhysicalJudgeBo;
+import org.dromara.physical.domain.vo.PhysicalJudgeImportVo;
+import org.dromara.physical.domain.vo.PhysicalJudgeVo;
+import org.dromara.physical.service.IPhysicalJudgeService;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 裁判管理
+ *
+ * @author Lion Li
+ * @date 2025-12-24
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/physical/judge")
+public class PhysicalJudgeController extends BaseController {
+
+    private final IPhysicalJudgeService physicalJudgeService;
+
+    /**
+     * 查询裁判管理列表
+     */
+    @SaCheckPermission("physical:judge:list")
+    @GetMapping("/list")
+    public TableDataInfo<PhysicalJudgeVo> list(PhysicalJudgeBo bo, PageQuery pageQuery) {
+        return physicalJudgeService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出裁判管理列表
+     */
+    @SaCheckPermission("physical:judge:export")
+    @Log(title = "裁判管理", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(PhysicalJudgeBo bo, HttpServletResponse response) {
+        List<PhysicalJudgeVo> list = physicalJudgeService.queryList(bo);
+        ExcelUtil.exportExcel(list, "裁判管理", PhysicalJudgeVo.class, response);
+    }
+
+    /**
+     * 获取裁判管理详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckPermission("physical:judge:query")
+    @GetMapping("/{id}")
+    public R<PhysicalJudgeVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable Long id) {
+        return R.ok(physicalJudgeService.queryById(id));
+    }
+
+    /**
+     * 新增裁判管理
+     */
+    @SaCheckPermission("physical:judge:add")
+    @Log(title = "裁判管理", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody PhysicalJudgeBo bo) {
+        return toAjax(physicalJudgeService.insertByBo(bo));
+    }
+
+    /**
+     * 修改裁判管理
+     */
+    @SaCheckPermission("physical:judge:edit")
+    @Log(title = "裁判管理", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody PhysicalJudgeBo bo) {
+        return toAjax(physicalJudgeService.updateByBo(bo));
+    }
+
+    /**
+     * 删除裁判管理
+     *
+     * @param ids 主键串
+     */
+    @SaCheckPermission("physical:judge:remove")
+    @Log(title = "裁判管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable Long[] ids) {
+        return toAjax(physicalJudgeService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+
+    @PostMapping("/importTemplate")
+    public void importTemplate(HttpServletResponse response) {
+        ExcelUtil.exportExcel(new ArrayList<>(), "裁判模版", PhysicalJudgeImportVo.class, response);
+    }
+
+    @Log(title = "【导入裁判】", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping(path = "/uploadJudgeFileImport", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public R<Boolean> add(
+        @RequestParam("file") MultipartFile file
+    ) {
+        // 处理文件上传逻辑
+        if (file == null && file.isEmpty()) {
+            return R.fail();
+        }
+        return physicalJudgeService.exportJudge(file);
+    }
+
+
+}

+ 46 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/physical/domain/PhysicalJudge.java

@@ -0,0 +1,46 @@
+package org.dromara.physical.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 裁判管理对象 physical_judge
+ *
+ * @author Lion Li
+ * @date 2025-12-24
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("physical_judge")
+public class PhysicalJudge extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 裁判姓名
+     */
+    private String name;
+
+    /**
+     * 裁判图片URL,可选
+     */
+    private String peopleIconUrl;
+
+    /**
+     * 工作年限(年)
+     */
+    private Long workYears;
+
+    private String judgeNumber;
+}

+ 47 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/physical/domain/bo/PhysicalJudgeBo.java

@@ -0,0 +1,47 @@
+package org.dromara.physical.domain.bo;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+import org.dromara.physical.domain.PhysicalJudge;
+
+/**
+ * 裁判管理业务对象 physical_judge
+ *
+ * @author Lion Li
+ * @date 2025-12-24
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = PhysicalJudge.class, reverseConvertGenerate = false)
+public class PhysicalJudgeBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    @NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 裁判姓名
+     */
+    @NotBlank(message = "裁判姓名不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String name;
+
+    /**
+     * 裁判图片URL,可选
+     */
+    private String peopleIconUrl;
+
+    /**
+     * 工作年限(年)
+     */
+    private Long workYears;
+
+    private String judgeNumber;
+
+}

+ 37 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/physical/domain/vo/PhysicalJudgeImportVo.java

@@ -0,0 +1,37 @@
+package org.dromara.physical.domain.vo;
+
+import cn.idev.excel.annotation.ExcelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 导入VO
+ *
+ * @author Lion Li
+ */
+
+@Data
+@NoArgsConstructor
+public class PhysicalJudgeImportVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 级别编号,如第1级、第2级
+     */
+    @ExcelProperty(value = "裁判姓名")
+    private String name;
+
+    /**
+     * 工作年限(年)
+     */
+    @ExcelProperty(value = "工作年限")
+    private Long workYears;
+
+
+
+}

+ 53 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/physical/domain/vo/PhysicalJudgeVo.java

@@ -0,0 +1,53 @@
+package org.dromara.physical.domain.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.physical.domain.PhysicalJudge;
+import java.io.Serial;
+import java.io.Serializable;
+
+
+
+/**
+ * 裁判管理视图对象 physical_judge
+ *
+ * @author Lion Li
+ * @date 2025-12-24
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = PhysicalJudge.class)
+public class PhysicalJudgeVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "编号")
+    private Long id;
+
+    /**
+     * 裁判图片URL,可选
+     */
+    @ExcelProperty(value = "裁判图片URL")
+    private String peopleIconUrl;
+
+    @ExcelProperty(value = "裁判编号")
+    private String judgeNumber;
+    /**
+     * 裁判姓名
+     */
+    @ExcelProperty(value = "裁判姓名")
+    private String name;
+    /**
+     * 工作年限(年)
+     */
+    @ExcelProperty(value = "工作年限")
+    private Long workYears;
+
+
+}

+ 44 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/physical/mapper/PhysicalJudgeMapper.java

@@ -0,0 +1,44 @@
+package org.dromara.physical.mapper;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.physical.domain.PhysicalJudge;
+import org.dromara.physical.domain.vo.PhysicalJudgeVo;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 裁判管理Mapper接口
+ *
+ * @author Lion Li
+ * @date 2025-12-24
+ */
+@DS("mysql2")
+public interface PhysicalJudgeMapper extends BaseMapperPlus<PhysicalJudge, PhysicalJudgeVo> {
+
+
+    @InterceptorIgnore(tenantLine = "true")
+    Page<PhysicalJudgeVo> selectPhysicalJudgePage(@Param("page") Page<PhysicalJudge> page, @Param("ew") Wrapper<PhysicalJudge> wrapper);
+
+    @InterceptorIgnore(tenantLine = "true")
+    PhysicalJudgeVo selectPhysicalJudgeById(Long id);
+
+    @InterceptorIgnore(tenantLine = "true")
+    int updatePhysicalJudgeById(PhysicalJudge update);
+
+    @InterceptorIgnore(tenantLine = "true")
+    int insertPhysicalJudge(PhysicalJudge insert);
+
+    @InterceptorIgnore(tenantLine = "true")
+    int deletePhysicalJudge(@Param("ids") Collection<Long> ids);
+
+    @InterceptorIgnore(tenantLine = "true")
+    List<PhysicalJudgeVo> selectPhysicalJudgeList(@Param("ew") Wrapper<PhysicalJudge> wrapper);
+
+
+
+}

+ 80 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/physical/service/IPhysicalJudgeService.java

@@ -0,0 +1,80 @@
+package org.dromara.physical.service;
+
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.physical.domain.bo.PhysicalBlindStructuresBo;
+import org.dromara.physical.domain.bo.PhysicalJudgeBo;
+import org.dromara.physical.domain.vo.PhysicalJudgeVo;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 裁判管理Service接口
+ *
+ * @author Lion Li
+ * @date 2025-12-24
+ */
+public interface IPhysicalJudgeService {
+
+    /**
+     * 查询裁判管理
+     *
+     * @param id 主键
+     * @return 裁判管理
+     */
+    PhysicalJudgeVo queryById(Long id);
+
+    /**
+     * 分页查询裁判管理列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 裁判管理分页列表
+     */
+    TableDataInfo<PhysicalJudgeVo> queryPageList(PhysicalJudgeBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的裁判管理列表
+     *
+     * @param bo 查询条件
+     * @return 裁判管理列表
+     */
+    List<PhysicalJudgeVo> queryList(PhysicalJudgeBo bo);
+
+    /**
+     * 新增裁判管理
+     *
+     * @param bo 裁判管理
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(PhysicalJudgeBo bo);
+
+    /**
+     * 修改裁判管理
+     *
+     * @param bo 裁判管理
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(PhysicalJudgeBo bo);
+
+    /**
+     * 校验并批量删除裁判管理信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 批量导入 裁判管理信息
+     *
+     * @param bo   裁判管理信息
+     * @param file 导入的文件
+     * @return 是否导入成功
+     */
+    R<Boolean> exportJudge(MultipartFile file);
+}

+ 240 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/physical/service/impl/PhysicalJudgeServiceImpl.java

@@ -0,0 +1,240 @@
+package org.dromara.physical.service.impl;
+
+import cn.idev.excel.EasyExcel;
+import cn.idev.excel.context.AnalysisContext;
+import cn.idev.excel.event.AnalysisEventListener;
+import org.apache.commons.collections4.CollectionUtils;
+import org.dromara.business.utils.JudgeCodeGeneratorUtils;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.physical.domain.PhysicalJudge;
+import org.dromara.physical.domain.bo.PhysicalJudgeBo;
+import org.dromara.physical.domain.vo.PhysicalBlindLevelsImportVo;
+import org.dromara.physical.domain.vo.PhysicalJudgeImportVo;
+import org.dromara.physical.domain.vo.PhysicalJudgeVo;
+import org.dromara.physical.mapper.PhysicalJudgeMapper;
+import org.dromara.physical.service.IPhysicalJudgeService;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 裁判管理Service业务层处理
+ *
+ * @author Lion Li
+ * @date 2025-12-24
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class PhysicalJudgeServiceImpl implements IPhysicalJudgeService {
+
+    private final PhysicalJudgeMapper baseMapper;
+
+    /**
+     * 查询裁判管理
+     *
+     * @param id 主键
+     * @return 裁判管理
+     */
+    @Override
+    public PhysicalJudgeVo queryById(Long id){
+        return baseMapper.selectPhysicalJudgeById(id);
+    }
+
+    /**
+     * 分页查询裁判管理列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 裁判管理分页列表
+     */
+    @Override
+    public TableDataInfo<PhysicalJudgeVo> queryPageList(PhysicalJudgeBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<PhysicalJudge> lqw = buildQueryWrapper(bo);
+        Page<PhysicalJudgeVo> result = baseMapper.selectPhysicalJudgePage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的裁判管理列表
+     *
+     * @param bo 查询条件
+     * @return 裁判管理列表
+     */
+    @Override
+    public List<PhysicalJudgeVo> queryList(PhysicalJudgeBo bo) {
+        LambdaQueryWrapper<PhysicalJudge> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectPhysicalJudgeList(lqw);
+    }
+
+    private LambdaQueryWrapper<PhysicalJudge> buildQueryWrapper(PhysicalJudgeBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<PhysicalJudge> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(PhysicalJudge::getId);
+        lqw.like(StringUtils.isNotBlank(bo.getName()), PhysicalJudge::getName, bo.getName());
+        lqw.eq(StringUtils.isNotBlank(bo.getPeopleIconUrl()), PhysicalJudge::getPeopleIconUrl, bo.getPeopleIconUrl());
+        lqw.eq(bo.getWorkYears() != null, PhysicalJudge::getWorkYears, bo.getWorkYears());
+        return lqw;
+    }
+
+    /**
+     * 新增裁判管理
+     *
+     * @param bo 裁判管理
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(PhysicalJudgeBo bo) {
+        PhysicalJudge add = MapstructUtils.convert(bo, PhysicalJudge.class);
+        validEntityBeforeSave(add);
+        assert add != null;
+        add.setJudgeNumber(JudgeCodeGeneratorUtils.generateRandomCode());
+        boolean flag = baseMapper.insertPhysicalJudge(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改裁判管理
+     *
+     * @param bo 裁判管理
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(PhysicalJudgeBo bo) {
+        PhysicalJudge update = MapstructUtils.convert(bo, PhysicalJudge.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updatePhysicalJudgeById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(PhysicalJudge entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除裁判管理信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deletePhysicalJudge(ids) > 0;
+    }
+
+    /**
+     * 导出裁判信息
+     *
+     * @param file 上传的文件
+     * @return 返回结果
+     */
+    @Override
+    public R<Boolean> exportJudge(MultipartFile file) {
+        // 1. 验证文件是否为空
+        if (file == null || file.isEmpty()) {
+            return R.fail("文件不能为空");
+        }
+
+        // 2. 验证文件类型
+        String contentType = file.getContentType();
+        if (!contentType.startsWith("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") &&
+            !contentType.startsWith("application/vnd.ms-excel")) {
+            return R.fail("文件格式不正确,请上传Excel文件");
+        }
+
+        // 3. 解析 Excel 文件
+        List<PhysicalJudgeImportVo> dataList;
+        try (InputStream inputStream = file.getInputStream()) {
+            dataList = parseExcel(inputStream);
+        } catch (IOException e) {
+            log.error("读取文件失败", e);
+            return R.fail("读取文件失败:" + e.getMessage());
+        }
+
+        // 4. 验证解析后的数据
+        if (CollectionUtils.isEmpty(dataList)) {
+            return R.fail("导入数据为空");
+        }
+
+        // 5. 批量插入数据,记录失败的条目
+        List<String> failedNames = new ArrayList<>();
+        int successCount = 0;
+
+        for (PhysicalJudgeImportVo physicalJudgeImportVo : dataList) {
+            // 验证单个数据项
+            if (StringUtils.isBlank(physicalJudgeImportVo.getName())) {
+                failedNames.add("姓名为空的记录");
+                continue;
+            }
+
+            if (physicalJudgeImportVo.getWorkYears() == null || physicalJudgeImportVo.getWorkYears() < 0) {
+                failedNames.add("工作年限无效的记录");
+                continue;
+            }
+
+            // 插入数据
+            PhysicalJudge add = new PhysicalJudge();
+            add.setName(physicalJudgeImportVo.getName());
+            add.setWorkYears(physicalJudgeImportVo.getWorkYears());
+            add.setJudgeNumber(JudgeCodeGeneratorUtils.generateRandomCode());
+
+            try {
+                baseMapper.insertPhysicalJudge(add);
+                successCount++;
+            } catch (Exception e) {
+                log.error("插入裁判信息失败,姓名:{}", physicalJudgeImportVo.getName(), e);
+                failedNames.add("姓名为" + physicalJudgeImportVo.getName() + "的记录");
+            }
+        }
+
+        // 6. 返回结果
+        if (failedNames.isEmpty()) {
+            return R.ok("成功导入" + successCount + "条记录");
+        } else {
+            return R.fail("部分记录导入失败:" + String.join(",", failedNames));
+        }
+    }
+
+    // 解析 Excel 方法
+    private List<PhysicalJudgeImportVo> parseExcel(InputStream inputStream) {
+        List<PhysicalJudgeImportVo> list = new ArrayList<>();
+        EasyExcel.read(inputStream, PhysicalJudgeImportVo.class, new AnalysisEventListener<PhysicalJudgeImportVo>() {
+            @Override
+            public void invoke(PhysicalJudgeImportVo data, AnalysisContext context) {
+                list.add(data);
+            }
+
+            @Override
+            public void doAfterAllAnalysed(AnalysisContext context) {
+                // 解析完成
+            }
+        }).sheet().doRead();
+
+        return list;
+    }
+
+}

+ 90 - 0
ruoyi-modules/ruoyi-system/src/main/resources/mapper/physical/PhysicalJudgeMapper.xml

@@ -0,0 +1,90 @@
+<?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="org.dromara.physical.mapper.PhysicalJudgeMapper">
+
+
+    <!-- 查询所有列 -->
+    <select id="selectPhysicalJudgePage" resultType="org.dromara.physical.domain.vo.PhysicalJudgeVo">
+        SELECT
+            id,
+            name,
+            people_icon_url,
+            work_years,
+            create_time,
+            update_time,
+            judge_number
+        FROM physical_judge ${ew.customSqlSegment}
+    </select>
+
+    <select id="selectPhysicalJudgeList" resultType="org.dromara.physical.domain.vo.PhysicalJudgeVo">
+        SELECT
+            id,
+            name,
+            people_icon_url,
+            work_years,
+            create_time,
+            update_time,
+            judge_number
+        FROM physical_judge ${ew.customSqlSegment}
+    </select>
+
+
+    <select id="selectPhysicalJudgeById" resultType="org.dromara.physical.domain.vo.PhysicalJudgeVo">
+        SELECT
+            id,
+            name,
+            people_icon_url,
+            work_years,
+            create_time,
+            update_time,
+            judge_number
+        FROM physical_judge  WHERE id = #{id}
+    </select>
+
+    <!-- 新增裁判 -->
+    <insert id="insertPhysicalJudge" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO physical_judge (
+        name,
+        <if test="peopleIconUrl != null">people_icon_url,</if>
+        <if test="workYears != null">work_years,</if>
+        <if test="judgeNumber != null">judge_number,</if>
+        create_time,
+        update_time
+        ) VALUES (
+        #{name},
+        <if test="peopleIconUrl != null">#{peopleIconUrl},</if>
+        <if test="workYears != null">#{workYears},</if>
+        <if test="judgeNumber != null">#{judgeNumber},</if>
+        NOW(),
+        NOW()
+        )
+    </insert>
+
+    <!-- 更新裁判信息 -->
+    <update id="updatePhysicalJudgeById">
+        UPDATE physical_judge
+        <set>
+            <if test="name != null and name != ''">name = #{name},</if>
+            <if test="peopleIconUrl != null">people_icon_url = #{peopleIconUrl},</if>
+            <if test="workYears != null">work_years = #{workYears},</if>
+            <if test="judgeNumber != null">judge_number = #{judgeNumber},</if>
+            update_time = NOW()
+        </set>
+        WHERE id = #{id}
+    </update>
+
+    <delete id="deletePhysicalJudge">
+        DELETE FROM physical_judge
+        <where>
+            id IN
+            <foreach item="id" collection="ids" open="(" separator="," close=")">
+                <if test="id > 0">
+                    #{id}
+                </if>
+            </foreach>
+        </where>
+    </delete>
+
+</mapper>