Forráskód Böngészése

管理端红娘审核bug修改和新红娘案例审核功能

caojp 1 hónapja
szülő
commit
410d6bda0a

+ 3 - 0
marriageAdmin-vue/src/config/api.js

@@ -50,6 +50,9 @@ export const API_ENDPOINTS = {
   MATCHMAKER_AUDIT_LIST: '/admin/marr-apply/list',
   MATCHMAKER_AUDIT_APPROVE: '/admin/marr-apply/approve',
   MATCHMAKER_AUDIT_DELETE: '/admin/marr-apply/delete',
+  SUCCESS_CASE_UPLOAD_LIST: '/admin/success-case-upload/list',
+  SUCCESS_CASE_UPLOAD_APPROVE: '/admin/success-case-upload/approve',
+  SUCCESS_CASE_UPLOAD_REJECT: '/admin/success-case-upload/reject',
   
   // 课程管理
   COURSE_LIST: '/api/course/list',

+ 1 - 0
marriageAdmin-vue/src/layouts/MainLayout.vue

@@ -59,6 +59,7 @@
           <el-menu-item index="/matchmaker/audit">红娘审核</el-menu-item>
           <el-menu-item index="/matchmaker/create">添加红娘</el-menu-item>
           <el-menu-item index="/matchmaker/points-product">积分商品</el-menu-item>
+          <el-menu-item index="/matchmaker/case-audit">案例审核</el-menu-item>
         </el-sub-menu>
         
         <el-sub-menu index="/course">

+ 6 - 0
marriageAdmin-vue/src/router/index.js

@@ -115,6 +115,12 @@ const router = createRouter({
               name: 'PointsProduct',
               component: () => import('@/views/points-product/PointsProductList.vue'),
               meta: { title: '积分商品' }
+            },
+            {
+              path: 'case-audit',
+              name: 'CaseAudit',
+              component: () => import('@/views/matchmaker/CaseAudit.vue'),
+              meta: { title: '案例审核' }
             }
           ]
         },

+ 16 - 9
marriageAdmin-vue/src/views/matchmaker/MatchmakerAudit.vue

@@ -155,9 +155,9 @@ const resetFilters = () => {
   loadList()
 }
 
-// 判断是否已审核通过(根据updateMan字段判断
+// 判断是否已审核通过(根据status字段判断,status=0表示审核通过
 const isApproved = (row) => {
-  return !!(row.updateMan || row.update_man)
+  return row.status === 0
 }
 
 // 审核通过
@@ -167,14 +167,20 @@ const handleApprove = async (row) => {
     return
   }
   
+  if (row.status === 0) {
+    ElMessage.warning('该申请已审核通过,无需重复操作')
+    return
+  }
+  
   try {
     await ElMessageBox.confirm(
-      `确定要通过 ${row.name || '该用户'} 的红娘申请吗?通过后该用户将成为红娘。`,
-      '确认审核',
+      `确定要通过 ${row.name || '该用户'} 的红娘申请吗?\n通过后该用户将成为红娘,此操作将更新审核状态。`,
+      '确认审核通过',
       {
-        confirmButtonText: '确定',
+        confirmButtonText: '确定通过',
         cancelButtonText: '取消',
-        type: 'warning'
+        type: 'warning',
+        dangerouslyUseHTMLString: false
       }
     )
     
@@ -205,12 +211,13 @@ const handleApprove = async (row) => {
 const handleDelete = async (row) => {
   try {
     await ElMessageBox.confirm(
-      `确定要删除 ${row.name || '该用户'} 的红娘申请吗?此操作不可恢复。`,
+      `确定要删除 ${row.name || '该用户'} 的红娘申请吗?\n删除后该申请将不再显示在列表中,此操作为逻辑删除。`,
       '确认删除',
       {
-        confirmButtonText: '确定',
+        confirmButtonText: '确定删除',
         cancelButtonText: '取消',
-        type: 'warning'
+        type: 'warning',
+        dangerouslyUseHTMLString: false
       }
     )
     

+ 10 - 0
marriageAdmin-vue/src/views/user/UserList.vue

@@ -111,6 +111,12 @@
           </template>
         </el-table-column>
         <el-table-column prop="createdAt" label="注册时间" width="170" />
+        <el-table-column prop="auditedAt" label="审核时间" width="120" align="center">
+          <template #default="{ row }">
+            <span v-if="row.auditedAt" class="data-highlight">{{ row.auditedAt }}</span>
+            <span v-else style="color: #999;">-</span>
+          </template>
+        </el-table-column>
         <el-table-column prop="lastLoginAt" label="最后登录" width="170">
           <template #default="{ row }">
             {{ row.lastLoginAt || '-' }}
@@ -226,6 +232,10 @@
         </el-descriptions-item>
         <el-descriptions-item label="注册时间" :span="2">{{ currentUser.createdAt }}</el-descriptions-item>
         <el-descriptions-item label="最后更新" :span="2">{{ currentUser.updatedAt }}</el-descriptions-item>
+        <el-descriptions-item label="审核时间" :span="2">
+          <span v-if="currentUser.auditedAt" class="data-highlight">{{ currentUser.auditedAt }}</span>
+          <span v-else>-</span>
+        </el-descriptions-item>
         <el-descriptions-item label="最后登录" :span="2">{{ currentUser.lastLoginAt || '-' }}</el-descriptions-item>
         <el-descriptions-item label="最后活跃" :span="2">{{ currentUser.lastActiveAt || '-' }}</el-descriptions-item>
         <el-descriptions-item label="头像" :span="2">

+ 46 - 2
service/admin/src/main/java/com/zhentao/controller/UserController.java

@@ -106,6 +106,9 @@ public class UserController {
         UserVO vo = new UserVO();
         BeanUtils.copyProperties(user, vo);
         
+        // 计算审核时间(updatedAt - createdAt)
+        calculateAuditedAt(vo, user);
+        
         // 填充VIP信息
         fillVipInfo(vo, user.getUserId());
         
@@ -115,6 +118,45 @@ public class UserController {
         return vo;
     }
     
+    /**
+     * 计算审核时间(updatedAt - createdAt)
+     */
+    private void calculateAuditedAt(UserVO vo, Users user) {
+        if (user.getUpdatedAt() != null && user.getCreatedAt() != null) {
+            Duration duration = Duration.between(user.getCreatedAt(), user.getUpdatedAt());
+            vo.setAuditedAt(formatDuration(duration));
+        } else {
+            vo.setAuditedAt(null);
+        }
+    }
+    
+    /**
+     * 格式化时间差为可读字符串
+     */
+    private String formatDuration(Duration duration) {
+        long totalSeconds = duration.getSeconds();
+        long days = totalSeconds / 86400;
+        long hours = (totalSeconds % 86400) / 3600;
+        long minutes = (totalSeconds % 3600) / 60;
+        long seconds = totalSeconds % 60;
+        
+        StringBuilder sb = new StringBuilder();
+        if (days > 0) {
+            sb.append(days).append("天");
+        }
+        if (hours > 0) {
+            sb.append(hours).append("小时");
+        }
+        if (minutes > 0) {
+            sb.append(minutes).append("分钟");
+        }
+        if (seconds > 0 && days == 0 && hours == 0) {
+            sb.append(seconds).append("秒");
+        }
+        
+        return sb.length() > 0 ? sb.toString() : "0秒";
+    }
+    
     /**
      * 填充VIP信息
      */
@@ -260,13 +302,15 @@ public class UserController {
      * 用户详情
      */
     @GetMapping("/detail/{userId}")
-    public Result<Users> detail(@PathVariable Integer userId) {
+    public Result<UserVO> detail(@PathVariable Integer userId) {
         try {
             Users user = usersMapper.selectById(userId);
             if (user == null) {
                 return Result.error("用户不存在");
             }
-            return Result.success(user);
+            // 转换为UserVO以包含审核时间等计算字段
+            UserVO vo = convertToUserVO(user);
+            return Result.success(vo);
         } catch (Exception e) {
             e.printStackTrace();
             return Result.error("查询失败:" + e.getMessage());

+ 4 - 0
service/admin/src/main/java/com/zhentao/entity/MarrApply.java

@@ -94,6 +94,10 @@ public class MarrApply {
      * 状态0-正常,1-禁止
      */
     private Integer status;
+    /**
+     * 删除状态0-正常,1-禁止   默认为0
+     * */
+    private String idStatus;
 
     @Override
     public boolean equals(Object that) {

+ 5 - 0
service/admin/src/main/java/com/zhentao/entity/Matchmaker.java

@@ -23,6 +23,11 @@ public class Matchmaker implements Serializable {
     @TableId(value = "matchmaker_id", type = IdType.AUTO)
     private Integer matchmakerId;
     
+    /**
+     * 红娘对应的用户ID
+     */
+    private Integer userId;
+    
     private String username;
     private String password;
     private String phone;

+ 186 - 16
service/admin/src/main/java/com/zhentao/service/impl/MarrApplyServiceImpl.java

@@ -17,6 +17,13 @@ import org.springframework.transaction.annotation.Transactional;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.springframework.util.StringUtils;
 import org.springframework.web.client.RestTemplate;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.zhentao.entity.Province;
+import com.zhentao.entity.City;
+import com.zhentao.entity.Area;
+import com.zhentao.mapper.ProvinceMapper;
+import com.zhentao.mapper.CityMapper;
+import com.zhentao.mapper.AreaMapper;
 
 import java.time.LocalDateTime;
 import java.util.Date;
@@ -38,6 +45,15 @@ public class MarrApplyServiceImpl extends ServiceImpl<MarrApplyMapper, MarrApply
     @Autowired
     private MatchmakerMapper matchmakerMapper;
     
+    @Autowired
+    private ProvinceMapper provinceMapper;
+    
+    @Autowired
+    private CityMapper cityMapper;
+    
+    @Autowired
+    private AreaMapper areaMapper;
+    
     private final RestTemplate restTemplate = new RestTemplate();
     
     // websocket 服务的 IM 接口地址
@@ -81,18 +97,46 @@ public class MarrApplyServiceImpl extends ServiceImpl<MarrApplyMapper, MarrApply
         }
         System.out.println("✅ 已更新 users 表,is_matchmaker = 1");
         
-        // 4. 创建 matchmakers 记录
-        Matchmaker matchmaker = new Matchmaker();
+        // 4. 创建或更新 matchmakers 记录
+        // 先检查该用户是否已经存在红娘记录
+        QueryWrapper<Matchmaker> matchmakerWrapper = new QueryWrapper<>();
+        matchmakerWrapper.eq("user_id", apply.getUserId());
+        Matchmaker existingMatchmaker = matchmakerMapper.selectOne(matchmakerWrapper);
         
-        // 从 marr_apply 表获取数据
-        matchmaker.setRealName(apply.getName());           // 真实姓名
+        Matchmaker matchmaker;
+        boolean isUpdate = false;
+        
+        if (existingMatchmaker != null) {
+            // 如果已存在,则更新现有记录
+            matchmaker = existingMatchmaker;
+            isUpdate = true;
+            System.out.println("⚠️ 该用户已存在红娘记录,将更新现有记录,matchmaker_id = " + matchmaker.getMatchmakerId());
+        } else {
+            // 如果不存在,创建新记录
+            matchmaker = new Matchmaker();
+            matchmaker.setCreateTime(LocalDateTime.now());
+        }
+        
+        // 从 marr_apply 表获取数据(按照用户要求映射)
+        matchmaker.setUserId(apply.getUserId());           // 用户ID
+        matchmaker.setRealName(apply.getName());           // 姓名 -> 真实姓名
         matchmaker.setPhone(apply.getPhone());             // 手机号
         matchmaker.setEmail(apply.getEmail());             // 邮箱
         matchmaker.setGender(apply.getGender());           // 性别
-        matchmaker.setProfile(apply.getIntroduction());    // 个人简介
+        matchmaker.setProfile(apply.getExperience());      // 经验 -> 个人简介
+        
+        // 解析地区信息:area 字段包含三级联动,需要解析为 provinceId, cityId, areaId
+        parseAreaToIds(apply.getArea(), matchmaker);
         
         // 从 users 表获取数据
-        matchmaker.setUsername(user.getNickname() != null ? user.getNickname() : user.getPhone()); // 用户名(优先使用昵称,否则使用手机号)
+        String baseUsername = user.getNickname() != null ? user.getNickname() : user.getPhone(); // 用户名(优先使用昵称,否则使用手机号)
+        
+        // 检查用户名是否已存在(如果不存在记录或用户名发生变化)
+        if (!isUpdate || matchmaker.getUsername() == null || !baseUsername.equals(matchmaker.getUsername())) {
+            String finalUsername = generateUniqueUsername(baseUsername, matchmaker.getUserId());
+            matchmaker.setUsername(finalUsername);
+        }
+        
         matchmaker.setPassword(user.getPassword());        // 密码(使用用户的密码)
         matchmaker.setBirthDate(user.getBirthDate());      // 出生日期(更准确)
         
@@ -106,18 +150,28 @@ public class MarrApplyServiceImpl extends ServiceImpl<MarrApplyMapper, MarrApply
         }
         
         // 设置默认值(无法从其他表获取的字段)
-        matchmaker.setMatchmakerType(1);                   // 默认类型:1-普通红娘
-        matchmaker.setLevel(1);                            // 默认等级:1级
-        matchmaker.setSuccessCouples(0);                   // 初始成功撮合数:0
+        if (!isUpdate) {
+            matchmaker.setMatchmakerType(1);                   // 默认类型:1-普通红娘
+            matchmaker.setLevel(1);                            // 默认等级:1级
+            matchmaker.setSuccessCouples(0);                   // 初始成功撮合数:0
+        }
         matchmaker.setStatus(1);                           // 状态:1-正常
-        matchmaker.setCreateTime(LocalDateTime.now());
         matchmaker.setUpdateTime(LocalDateTime.now());
         
-        int insertResult = matchmakerMapper.insert(matchmaker);
-        if (insertResult <= 0) {
-            throw new RuntimeException("创建红娘记录失败");
+        int result;
+        if (isUpdate) {
+            result = matchmakerMapper.updateById(matchmaker);
+            if (result <= 0) {
+                throw new RuntimeException("更新红娘记录失败");
+            }
+            System.out.println("✅ 已更新 matchmakers 记录,matchmaker_id = " + matchmaker.getMatchmakerId());
+        } else {
+            result = matchmakerMapper.insert(matchmaker);
+            if (result <= 0) {
+                throw new RuntimeException("创建红娘记录失败");
+            }
+            System.out.println("✅ 已创建 matchmakers 记录,matchmaker_id = " + matchmaker.getMatchmakerId());
         }
-        System.out.println("✅ 已创建 matchmakers 记录,matchmaker_id = " + matchmaker.getMatchmakerId());
         
         // 5. 导入到腾讯云 IM(使用 m_ + matchmaker_id)
         String imUserId = "m_" + matchmaker.getMatchmakerId();
@@ -141,7 +195,8 @@ public class MarrApplyServiceImpl extends ServiceImpl<MarrApplyMapper, MarrApply
             // 导入失败不影响审核流程,只记录日志
         }
         
-        // 6. 更新申请记录的更新人和更新时间
+        // 6. 更新申请记录的审核状态、更新人和更新时间
+        apply.setStatus(0); // 0-正常(审核通过)
         apply.setUpdateTime(new Date());
         apply.setUpdateMan("系统");
         this.updateById(apply);
@@ -153,7 +208,122 @@ public class MarrApplyServiceImpl extends ServiceImpl<MarrApplyMapper, MarrApply
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean delete(Long applyId) {
-        return this.removeById(applyId);
+        // 逻辑删除:将 idStatus 设置为 "1"(禁止状态)
+        MarrApply marrApply = new MarrApply();
+        marrApply.setApplyId(applyId);
+        marrApply.setIdStatus("1"); // 1-禁止(逻辑删除)
+        return this.updateById(marrApply);
+    }
+    
+    /**
+     * 解析地区字符串,将省市区名称转换为对应的ID
+     * area 格式可能是:"北京市 北京市 朝阳区" 或 "北京市 北京市 朝阳区"(用空格分隔)
+     * 
+     * @param area 地区字符串
+     * @param matchmaker 红娘对象,用于设置 provinceId, cityId, areaId
+     */
+    private void parseAreaToIds(String area, Matchmaker matchmaker) {
+        if (area == null || area.trim().isEmpty()) {
+            System.out.println("⚠️ 地区信息为空,跳过解析");
+            return;
+        }
+        
+        try {
+            // 按空格分割地区字符串
+            String[] areaParts = area.trim().split("\\s+");
+            
+            if (areaParts.length >= 1 && provinceMapper != null) {
+                // 解析省份
+                String provinceName = areaParts[0].trim();
+                QueryWrapper<Province> provinceWrapper = new QueryWrapper<>();
+                provinceWrapper.eq("name", provinceName);
+                Province province = provinceMapper.selectOne(provinceWrapper);
+                if (province != null) {
+                    matchmaker.setProvinceId(province.getId());
+                    System.out.println("✅ 解析省份: " + provinceName + " -> " + province.getId());
+                } else {
+                    System.out.println("⚠️ 未找到省份: " + provinceName);
+                }
+            }
+            
+            if (areaParts.length >= 2 && cityMapper != null && matchmaker.getProvinceId() != null) {
+                // 解析城市
+                String cityName = areaParts[1].trim();
+                QueryWrapper<City> cityWrapper = new QueryWrapper<>();
+                cityWrapper.eq("name", cityName);
+                cityWrapper.eq("province_id", matchmaker.getProvinceId());
+                City city = cityMapper.selectOne(cityWrapper);
+                if (city != null) {
+                    matchmaker.setCityId(city.getId());
+                    System.out.println("✅ 解析城市: " + cityName + " -> " + city.getId());
+                } else {
+                    System.out.println("⚠️ 未找到城市: " + cityName);
+                }
+            }
+            
+            if (areaParts.length >= 3 && areaMapper != null && matchmaker.getCityId() != null) {
+                // 解析区域
+                String areaName = areaParts[2].trim();
+                QueryWrapper<Area> areaWrapper = new QueryWrapper<>();
+                areaWrapper.eq("name", areaName);
+                areaWrapper.eq("city_id", matchmaker.getCityId());
+                Area areaObj = areaMapper.selectOne(areaWrapper);
+                if (areaObj != null) {
+                    matchmaker.setAreaId(areaObj.getId());
+                    System.out.println("✅ 解析区域: " + areaName + " -> " + areaObj.getId());
+                } else {
+                    System.out.println("⚠️ 未找到区域: " + areaName);
+                }
+            }
+            
+            if (areaParts.length < 3) {
+                System.out.println("⚠️ 地区信息不完整,只有 " + areaParts.length + " 部分: " + area);
+            }
+        } catch (Exception e) {
+            System.err.println("❌ 解析地区信息失败: " + e.getMessage());
+            e.printStackTrace();
+            // 解析失败不影响审核流程,只记录日志
+        }
+    }
+    
+    /**
+     * 生成唯一的用户名
+     * 如果用户名已存在,则添加后缀(_1, _2, ...)直到找到不存在的用户名
+     * 
+     * @param baseUsername 基础用户名
+     * @param currentUserId 当前用户ID(如果更新现有记录,排除自己)
+     * @return 唯一的用户名
+     */
+    private String generateUniqueUsername(String baseUsername, Integer currentUserId) {
+        if (baseUsername == null || baseUsername.trim().isEmpty()) {
+            baseUsername = "user_" + System.currentTimeMillis();
+        }
+        
+        String username = baseUsername.trim();
+        int suffix = 0;
+        
+        while (true) {
+            QueryWrapper<Matchmaker> wrapper = new QueryWrapper<>();
+            wrapper.eq("username", username);
+            // 如果当前用户ID不为空,排除自己的记录(用于更新场景)
+            if (currentUserId != null) {
+                wrapper.ne("user_id", currentUserId);
+            }
+            
+            Matchmaker existing = matchmakerMapper.selectOne(wrapper);
+            
+            if (existing == null) {
+                // 用户名不存在,可以使用
+                if (suffix > 0) {
+                    System.out.println("⚠️ 用户名 '" + baseUsername + "' 已存在,使用新用户名: " + username);
+                }
+                return username;
+            }
+            
+            // 用户名已存在,添加后缀
+            suffix++;
+            username = baseUsername + "_" + suffix;
+        }
     }
 }
 

+ 1 - 0
service/admin/src/main/java/com/zhentao/vo/UserVO.java

@@ -47,6 +47,7 @@ public class UserVO implements Serializable {
     
     // 计算字段
     private Integer age;
+    private String auditedAt;  // 审核时间(updatedAt - createdAt,格式化的时间差)
     
     // VIP信息
     private Boolean isVip;  // 是否VIP

+ 3 - 1
service/admin/src/main/resources/com/zhentao/mapper/MarrApplyMapper.xml

@@ -21,12 +21,13 @@
             <result property="updateTime" column="update_time" />
             <result property="updateMan" column="update_man" />
             <result property="status" column="status" />
+            <result property="idStatus" column="id_status" />
     </resultMap>
 
     <sql id="Base_Column_List">
         apply_id,user_id,name,phone,email,age,
         gender,area,experience,server_time,introduction,
-        create_time,create_man,update_time,update_man,status
+        create_time,create_man,update_time,update_man,status,id_status
     </sql>
 
     <select id="selectPageByCondition" resultMap="BaseResultMap">
@@ -34,6 +35,7 @@
         <include refid="Base_Column_List"/>
         FROM marr_apply
         <where>
+            AND (id_status = '0' OR id_status IS NULL)
             <if test="name != null and name != ''">
                 AND name LIKE CONCAT('%', #{name}, '%')
             </if>