Просмотр исходного кода

feat(business): 添加用户申诉管理功能

- 新增用户申诉相关实体类、Mapper、Service、Controller等
- 实现用户申诉的增删查改功能
- 添加用户申诉的导出功能
- 优化分页查询逻辑
fugui001 4 месяцев назад
Родитель
Сommit
015c5f7e47

+ 105 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/controller/UserComplaintsController.java

@@ -0,0 +1,105 @@
+package org.dromara.business.controller;
+
+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.bo.UserComplaintsBo;
+import org.dromara.business.domain.vo.UserComplaintsVo;
+import org.dromara.business.service.IUserComplaintsService;
+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;
+
+/**
+ * 用户申诉
+ *
+ * @author Lion Li
+ * @date 2025-08-21
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/business/complaints")
+public class UserComplaintsController extends BaseController {
+
+    private final IUserComplaintsService userComplaintsService;
+
+    /**
+     * 查询用户申诉列表
+     */
+    @SaCheckPermission("business:complaints:list")
+    @GetMapping("/list")
+    public TableDataInfo<UserComplaintsVo> list(UserComplaintsBo bo, PageQuery pageQuery) {
+        return userComplaintsService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出用户申诉列表
+     */
+    @SaCheckPermission("business:complaints:export")
+    @Log(title = "用户申诉", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(UserComplaintsBo bo, HttpServletResponse response) {
+        List<UserComplaintsVo> list = userComplaintsService.queryList(bo);
+        ExcelUtil.exportExcel(list, "用户申诉", UserComplaintsVo.class, response);
+    }
+
+    /**
+     * 获取用户申诉详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckPermission("business:complaints:query")
+    @GetMapping("/{id}")
+    public R<UserComplaintsVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable Long id) {
+        return R.ok(userComplaintsService.queryById(id));
+    }
+
+    /**
+     * 新增用户申诉
+     */
+    @SaCheckPermission("business:complaints:add")
+    @Log(title = "用户申诉", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody UserComplaintsBo bo) {
+        return toAjax(userComplaintsService.insertByBo(bo));
+    }
+
+    /**
+     * 修改用户申诉
+     */
+    @SaCheckPermission("business:complaints:edit")
+    @Log(title = "用户申诉", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody UserComplaintsBo bo) {
+        return toAjax(userComplaintsService.updateByBo(bo));
+    }
+
+    /**
+     * 删除用户申诉
+     *
+     * @param ids 主键串
+     */
+    @SaCheckPermission("business:complaints:remove")
+    @Log(title = "用户申诉", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable Long[] ids) {
+        return toAjax(userComplaintsService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 72 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/domain/UserComplaints.java

@@ -0,0 +1,72 @@
+package org.dromara.business.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import java.util.Date;
+import java.io.Serial;
+
+/**
+ * 用户申诉对象 user_complaints
+ *
+ * @author Lion Li
+ * @date 2025-08-21
+ */
+@Data
+@TableName("user_complaints")
+public class UserComplaints{
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     *
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 申诉用户
+     */
+    private Long userId;
+
+    /**
+     * 比赛ID
+     */
+    private Long tournamentId;
+
+    /**
+     * 最终名次
+     */
+    private Long finalRank;
+
+    /**
+     * 申述原因
+     */
+    private String complaintReason;
+
+    /**
+     * 创建时间/申述时间
+     */
+    private Date createAt;
+
+    /**
+     * 修改时间/处理时间
+     */
+    private Date updateAt;
+
+    /**
+     * '1申诉中', '2已处理', '3不处理'
+     */
+    private Long status;
+
+    /**
+     * 申诉状态
+     */
+    private String statusText;
+
+
+    private String operateName;
+
+    private String advice;
+
+}

+ 89 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/domain/bo/UserComplaintsBo.java

@@ -0,0 +1,89 @@
+package org.dromara.business.domain.bo;
+
+import org.dromara.business.domain.UserComplaints;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+import java.util.Date;
+
+/**
+ * 用户申诉业务对象 user_complaints
+ *
+ * @author Lion Li
+ * @date 2025-08-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = UserComplaints.class, reverseConvertGenerate = false)
+public class UserComplaintsBo extends BaseEntity {
+
+    /**
+     *
+     */
+    @NotNull(message = "不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 申诉用户
+     */
+    private Long userId;
+
+    /**
+     * 比赛ID
+     */
+    private Long tournamentId;
+
+    /**
+     * 最终名次
+     */
+    private Long finalRank;
+
+    /**
+     * 申述原因
+     */
+    private String complaintReason;
+
+    /**
+     * 创建时间/申述时间
+     */
+    private Date createAt;
+
+    /**
+     * 修改时间/处理时间
+     */
+    private Date updateAt;
+
+    /**
+     * '1申诉中', '2已处理', '3不处理'
+     */
+    private Long status;
+
+    /**
+     * 申诉状态
+     */
+    private String statusText;
+
+
+    private String userIds;
+
+
+
+    /**
+     * 开始时间
+     */
+    private String tournamentBeginTime;
+
+    /**
+     * 结束时间
+     */
+    private String tournamentEndTime;
+
+    /**
+     * 处理建议
+     */
+    private String advice;
+
+}

+ 36 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/domain/enums/ComplaintsStatus.java

@@ -0,0 +1,36 @@
+package org.dromara.business.domain.enums;
+
+public enum ComplaintsStatus {
+
+    ING(1L, "申诉中"),
+    DEEALWITH(2L, "已处理"),
+    NODEALWITH(3L, "不处理");
+
+    private final Long code;
+    private final String description;
+
+    ComplaintsStatus(Long code, String description) {
+        this.code = code;
+        this.description = description;
+    }
+
+    public Long getCode() {
+        return code;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    // 根据code获取对应的枚举值
+    public static ComplaintsStatus fromCode(Long code) {
+        for (ComplaintsStatus status : ComplaintsStatus.values()) {
+            if (status.getCode().equals(code)) {
+                return status;
+            }
+        }
+        throw new IllegalArgumentException("Invalid code: " + code);
+    }
+
+
+}

+ 91 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/domain/vo/UserComplaintsVo.java

@@ -0,0 +1,91 @@
+package org.dromara.business.domain.vo;
+
+import java.util.Date;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.business.domain.UserComplaints;
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 用户申诉视图对象 user_complaints
+ *
+ * @author Lion Li
+ * @date 2025-08-21
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = UserComplaints.class)
+public class UserComplaintsVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     *
+     */
+    @ExcelProperty(value = "")
+    private Long id;
+
+    /**
+     * 申诉用户
+     */
+    @ExcelProperty(value = "申诉用户")
+    private Long userId;
+
+    /**
+     * 比赛ID
+     */
+    @ExcelProperty(value = "比赛ID")
+    private Long tournamentId;
+
+    /**
+     * 最终名次
+     */
+    @ExcelProperty(value = "最终名次")
+    private Long finalRank;
+
+    /**
+     * 申述原因
+     */
+    @ExcelProperty(value = "申述原因")
+    private String complaintReason;
+
+    /**
+     * 创建时间/申述时间
+     */
+    @ExcelProperty(value = "创建时间/申述时间")
+    private Date createAt;
+
+    /**
+     * 修改时间/处理时间
+     */
+    @ExcelProperty(value = "修改时间/处理时间")
+    private Date updateAt;
+
+    /**
+     * '1申诉中', '2已处理', '3不处理'
+     */
+    @ExcelProperty(value = "'1申诉中', '2已处理', '3不处理'")
+    private Long status;
+
+    /**
+     * 申诉状态
+     */
+    @ExcelProperty(value = "申诉状态")
+    private String statusText;
+
+    private String tournamentName;
+
+    private String operateName;
+
+    private String startTime;
+
+    private String phone;
+
+    private String nickName;
+
+}

+ 43 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/mapper/UserComplaintsMapper.java

@@ -0,0 +1,43 @@
+package org.dromara.business.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.business.domain.UserComplaints;
+import org.dromara.business.domain.vo.UserComplaintsVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 用户申诉Mapper接口
+ *
+ * @author Lion Li
+ * @date 2025-08-21
+ */
+@DS("mysql2")
+public interface UserComplaintsMapper extends BaseMapperPlus<UserComplaints, UserComplaintsVo> {
+
+
+    @InterceptorIgnore(tenantLine = "true")
+    Page<UserComplaintsVo> selectUserComplaintsPage(@Param("page") Page<UserComplaints> page, @Param("ew") Wrapper<UserComplaints> wrapper);
+
+    @InterceptorIgnore(tenantLine = "true")
+    UserComplaintsVo selectUserComplaintsPageById(Long id);
+
+    @InterceptorIgnore(tenantLine = "true")
+    List<UserComplaintsVo> selectUserComplaintsList(@Param("ew") Wrapper<UserComplaints> wrapper);
+
+    @InterceptorIgnore(tenantLine = "true")
+    int insertUserComplaints(UserComplaints userComplaints);
+
+    @InterceptorIgnore(tenantLine = "true")
+    int updateUserComplaints(UserComplaints userComplaints);
+
+    @InterceptorIgnore(tenantLine = "true")
+    int deleteUserComplaintsById(@Param("ids") Collection<Long> ids);
+
+}

+ 68 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/service/IUserComplaintsService.java

@@ -0,0 +1,68 @@
+package org.dromara.business.service;
+
+import org.dromara.business.domain.bo.UserComplaintsBo;
+import org.dromara.business.domain.vo.UserComplaintsVo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 用户申诉Service接口
+ *
+ * @author Lion Li
+ * @date 2025-08-21
+ */
+public interface IUserComplaintsService {
+
+    /**
+     * 查询用户申诉
+     *
+     * @param id 主键
+     * @return 用户申诉
+     */
+    UserComplaintsVo queryById(Long id);
+
+    /**
+     * 分页查询用户申诉列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 用户申诉分页列表
+     */
+    TableDataInfo<UserComplaintsVo> queryPageList(UserComplaintsBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的用户申诉列表
+     *
+     * @param bo 查询条件
+     * @return 用户申诉列表
+     */
+    List<UserComplaintsVo> queryList(UserComplaintsBo bo);
+
+    /**
+     * 新增用户申诉
+     *
+     * @param bo 用户申诉
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(UserComplaintsBo bo);
+
+    /**
+     * 修改用户申诉
+     *
+     * @param bo 用户申诉
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(UserComplaintsBo bo);
+
+    /**
+     * 校验并批量删除用户申诉信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 192 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/service/impl/UserComplaintsServiceImpl.java

@@ -0,0 +1,192 @@
+package org.dromara.business.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.dromara.business.domain.UserComplaints;
+import org.dromara.business.domain.bo.UserComplaintsBo;
+import org.dromara.business.domain.enums.ComplaintsStatus;
+import org.dromara.business.domain.vo.UserComplaintsVo;
+import org.dromara.business.mapper.UserComplaintsMapper;
+import org.dromara.business.service.IUserComplaintsService;
+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 lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.springframework.stereotype.Service;
+
+
+import java.util.List;
+import java.util.Collection;
+
+/**
+ * 用户申诉Service业务层处理
+ *
+ * @author Lion Li
+ * @date 2025-08-21
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class UserComplaintsServiceImpl implements IUserComplaintsService {
+
+    private final UserComplaintsMapper baseMapper;
+
+    /**
+     * 查询用户申诉
+     *
+     * @param id 主键
+     * @return 用户申诉
+     */
+    @Override
+    public UserComplaintsVo queryById(Long id){
+        return baseMapper.selectUserComplaintsPageById(id);
+    }
+
+    /**
+     * 分页查询用户申诉列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 用户申诉分页列表
+     */
+    @Override
+    public TableDataInfo<UserComplaintsVo> queryPageList(UserComplaintsBo bo, PageQuery pageQuery) {
+        Wrapper<UserComplaints> lqw = buildQueryWrapper(bo);
+        Page<UserComplaintsVo> result = baseMapper.selectUserComplaintsPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的用户申诉列表
+     *
+     * @param bo 查询条件
+     * @return 用户申诉列表
+     */
+    @Override
+    public List<UserComplaintsVo> queryList(UserComplaintsBo bo) {
+        Wrapper<UserComplaints> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectUserComplaintsList(lqw);
+    }
+
+    private Wrapper<UserComplaints> buildQueryWrapper(UserComplaintsBo bo) {
+        QueryWrapper<UserComplaints> wrapper = new QueryWrapper<>();
+        // 排序:按 a.id 升序
+        wrapper.orderByAsc("a.create_at");
+
+        // ======== 条件拼接 ========
+
+        // user_id
+        if (bo.getUserId() != null) {
+            wrapper.eq("a.user_id", bo.getUserId());
+        }
+
+        // tournament_id
+        if (bo.getTournamentId() != null) {
+            wrapper.eq("a.tournament_id", bo.getTournamentId());
+        }
+
+        // final_rank
+        if (bo.getFinalRank() != null) {
+            wrapper.eq("a.final_rank", bo.getFinalRank());
+        }
+
+        // complaint_reason:模糊匹配
+        if (StringUtils.isNotBlank(bo.getComplaintReason())) {
+            wrapper.like("a.complaint_reason", bo.getComplaintReason());
+        }
+
+        // create_at 时间范围查询(推荐做法)
+        if (bo.getTournamentBeginTime() != null && !bo.getTournamentBeginTime().isEmpty()) {
+            wrapper.ge("a.create_at", bo.getTournamentBeginTime());
+        }
+        if (bo.getTournamentEndTime() != null && !bo.getTournamentEndTime().isEmpty()) {
+            wrapper.le("a.create_at", bo.getTournamentEndTime());
+        }
+
+        // update_at(也可支持范围,这里保留原样)
+        if (bo.getUpdateAt() != null) {
+            wrapper.eq("a.update_at", bo.getUpdateAt());
+        }
+
+        // status
+        if (bo.getStatus() != null) {
+            wrapper.eq("a.status", bo.getStatus());
+        }
+
+        // status_text
+        if (StringUtils.isNotBlank(bo.getStatusText())) {
+            wrapper.eq("a.status_text", bo.getStatusText());
+        }
+
+
+        // ======== 多字段模糊搜索:手机号 或 昵称 ========
+        if (StringUtils.isNotBlank(bo.getUserIds())) {
+            wrapper.and(wrapper1 ->
+                wrapper1.like("b.phone", bo.getUserIds())
+                    .or()
+                    .like("b.nick_name", bo.getUserIds())
+            );
+        }
+
+        return wrapper;
+    }
+
+    /**
+     * 新增用户申诉
+     *
+     * @param bo 用户申诉
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(UserComplaintsBo bo) {
+        UserComplaints add = MapstructUtils.convert(bo, UserComplaints.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insertUserComplaints(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改用户申诉
+     *
+     * @param bo 用户申诉
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(UserComplaintsBo bo) {
+        UserComplaints update = MapstructUtils.convert(bo, UserComplaints.class);
+        validEntityBeforeSave(update);
+        update.setOperateName(StringUtils.isEmpty(LoginHelper.getLoginUser().getUsername())?LoginHelper.getLoginUser().getNickname() : LoginHelper.getLoginUser().getUsername());
+        update.setStatusText(ComplaintsStatus.fromCode(bo.getStatus()).getDescription());
+        update.setAdvice(bo.getAdvice());
+        return baseMapper.updateUserComplaints(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(UserComplaints entity){
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除用户申诉信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteUserComplaintsById(ids) > 0;
+    }
+}

+ 12 - 8
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/utils/PageUtils.java

@@ -13,21 +13,25 @@ public class PageUtils {
     public static <T> TableDataInfo<T> toTablePage(List<T> dataList, int pageNum, int pageSize) {
         // 构造分页对象
         Page<T> page = new Page<>(pageNum, pageSize);
+
         // 总记录数
-        page.setTotal(dataList.size());
+        long total = dataList.size();
+        page.setTotal(total);
 
-        // 计算分页数据
+        // 计算分页范围
         int fromIndex = (pageNum - 1) * pageSize;
-        int toIndex = Math.min(fromIndex + pageSize, dataList.size());
-        List<T> pageList = dataList.subList(fromIndex, toIndex);
 
-        // 设置分页结果
-        page.setRecords(pageList);
+        // 如果起始位置已经超出数据范围,返回空列表
+        if (fromIndex >= total) {
+            page.setRecords(List.of()); // 空列表
+        } else {
+            int toIndex = Math.min(fromIndex + pageSize, (int) total);
+            List<T> pageList = dataList.subList(fromIndex, toIndex);
+            page.setRecords(pageList);
+        }
 
-        // 返回表格数据对象
         return TableDataInfo.build(page);
     }
 
 
-
 }

+ 142 - 0
ruoyi-modules/ruoyi-system/src/main/resources/mapper/business/UserComplaintsMapper.xml

@@ -0,0 +1,142 @@
+<?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.business.mapper.UserComplaintsMapper">
+
+
+    <select id="selectUserComplaintsPage" resultType="org.dromara.business.domain.vo.UserComplaintsVo">
+        SELECT
+            a.id,
+            a.user_id,
+            a.tournament_id,
+            a.final_rank,
+            a.complaint_reason,
+            a.create_at,
+            a.update_at,
+            a.status,
+            a.status_text,
+            b.nick_name,
+            b.phone,
+            c.name  tournamentName,
+            c.start_time,
+            a.advice,
+            a.operate_name
+        FROM
+            user_complaints a left join user b  on a.user_id=b.id
+                              left join  tournaments c  on a.tournament_id=c.id
+            ${ew.customSqlSegment}
+    </select>
+
+
+
+    <select id="selectUserComplaintsPageById" resultType="org.dromara.business.domain.vo.UserComplaintsVo" >
+        SELECT
+            a.id,
+            a.user_id,
+            a.tournament_id,
+            a.final_rank,
+            a.complaint_reason,
+            a.create_at,
+            a.update_at,
+            a.status,
+            a.status_text,
+            b.nick_name,
+            b.phone,
+            c.name  tournamentName,
+            c.start_time,
+            a.advice,
+            a.operate_name
+        FROM
+            user_complaints a left join user b  on a.user_id=b.id
+                              left join  tournaments c  on a.tournament_id=c.id
+        WHERE a.id =  #{id}
+    </select>
+
+
+    <select id="selectUserComplaintsList" resultType="org.dromara.business.domain.vo.UserComplaintsVo">
+        SELECT
+            a.id,
+            a.user_id,
+            a.tournament_id,
+            a.final_rank,
+            a.complaint_reason,
+            a.create_at,
+            a.update_at,
+            a.status,
+            a.status_text,
+            b.nick_name,
+            b.phone,
+            c.name  tournamentName,
+            c.start_time,
+            a.advice,
+            a.operate_name
+        FROM
+            user_complaints a left join user b  on a.user_id=b.id
+                              left join  tournaments c  on a.tournament_id=c.id
+            ${ew.customSqlSegment}
+    </select>
+
+    <insert id="insertUserComplaints"  useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO user_complaints
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="userId != null">user_id,</if>
+            <if test="tournamentId != null">tournament_id,</if>
+            <if test="finalRank != null">final_rank,</if>
+            <if test="complaintReason != null and complaintReason != ''">complaint_reason,</if>
+            <if test="createAt != null">create_at,</if>
+            <if test="status != null">status,</if>
+            <if test="statusText != null and statusText != ''">status_text,</if>
+            <if test="operateName != null and operateName != ''">operate_name,</if>
+            <!-- 如果 create_at 为空,自动设置为当前时间 -->
+            <if test="createAt == null">create_at,</if>
+            <!-- update_at 在插入时也初始化 -->
+            update_at,
+        </trim>
+        <trim prefix="VALUES (" suffix=")" suffixOverrides=",">
+            <if test="userId != null">#{userId},</if>
+            <if test="tournamentId != null">#{tournamentId},</if>
+            <if test="finalRank != null">#{finalRank},</if>
+            <if test="complaintReason != null and complaintReason != ''">#{complaintReason},</if>
+            <if test="createAt != null">#{createAt},</if>
+            <if test="status != null">#{status},</if>
+            <if test="statusText != null and statusText != ''">#{statusText},</if>
+            <if test="operateName != null and operateName != ''">#{operateName},</if>
+            <if test="createAt == null">CURRENT_TIMESTAMP,</if>
+            CURRENT_TIMESTAMP,
+        </trim>
+    </insert>
+
+
+    <update id="updateUserComplaints">
+        UPDATE user_complaints
+        <set>
+            <!-- 只更新非空字段 -->
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="tournamentId != null">tournament_id = #{tournamentId},</if>
+            <if test="finalRank != null">final_rank = #{finalRank},</if>
+            <if test="complaintReason != null and complaintReason != ''">complaint_reason = #{complaintReason},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="statusText != null and statusText != ''">status_text = #{statusText},</if>
+            <if test="advice != null and advice != ''">advice = #{advice},</if>
+            <if test="operateName != null and operateName != ''">operate_name = #{operateName},</if>
+
+           update_at = CURRENT_TIMESTAMP,
+        </set>
+        WHERE id = #{id}
+    </update>
+
+    <delete id="deleteUserComplaintsById">
+        DELETE FROM user_complaints
+        <where>
+            id IN
+            <foreach item="id" collection="ids" open="(" separator="," close=")">
+                <if test="id > 0">
+                    #{id}
+                </if>
+            </foreach>
+        </where>
+    </delete>
+
+
+</mapper>