Merge branch '20260519-3.9.2版本-葛昊天分支'

This commit is contained in:
geht
2026-05-29 10:50:42 +08:00
27 changed files with 2698 additions and 180 deletions

View File

@@ -495,13 +495,23 @@ jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestStd/components/MesXslRubber
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestStd/components/MesXslRubberQuickTestMethodSelectModal.vue
jeecgboot-vue3/src/views/xslmes/mesXslRubberQuickTestStd/components/MesXslRubberQuickTestStdMixerPsSelectModal.vue
-- author:jiangxh---date:20260525--for: 【MES】原材料检验标准密炼PS批准时关联胶料快检实验标准置已批准反审核不回退触发由审核改为批准---
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/common/XslMesBizConstants.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslRubberQuickTestStdService.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslRubberQuickTestStdServiceImpl.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixerPsCompileServiceImpl.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRubberQuickTestStdController.java
-- author:xsl---date:20260528--for: 【IM聊天-OA】IM群聊创建群聊、群列表、群消息收发与未读 ---
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_110__sys_im_group_chat.sql
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/dto/SysImCreateGroupDTO.java
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/entity/SysImConversation.java
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/vo/SysImConversationVO.java
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/mapper/SysImConversationMapper.java
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/mapper/xml/SysImConversationMapper.xml
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/service/ISysImChatService.java
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/service/impl/SysImChatServiceImpl.java
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/controller/SysImChatController.java
jeecgboot-vue3/src/views/system/im/im.api.ts
jeecgboot-vue3/src/views/system/im/ImCreateGroupModal.vue
jeecgboot-vue3/src/views/system/im/ImChat.vue
-- author:jiangxh---date:20260525--for: 【MES】胶料快检记录主子表、质量管理菜单、胶料信息批量检验生成 ---
jeecg-boot/db/mes-xsl-rubber-quick-test-record-menu-permission.sql
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_108__mes_xsl_rubber_quick_test_record.sql

View File

@@ -11,10 +11,13 @@ import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.im.dto.SysImCreateGroupDTO;
import org.jeecg.modules.im.dto.SysImGroupMembersDTO;
import org.jeecg.modules.im.dto.SysImSendMessageDTO;
import org.jeecg.modules.im.service.ISysImChatService;
import org.jeecg.modules.im.vo.SysImContactVO;
import org.jeecg.modules.im.vo.SysImConversationVO;
import org.jeecg.modules.im.vo.SysImGroupDetailVO;
import org.jeecg.modules.im.vo.SysImMessageVO;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysUserService;
@@ -136,4 +139,87 @@ public class SysImChatController {
return Result.OK(imChatService.listDeptMembers(user.getId(), resolveTenantId(user, request), user.getOrgCode(), keyword));
}
//update-end---author:cursor ---date:20260528 for【IM聊天-OA】联系人接口兼容保留同本部门-----------
//update-begin---author:xsl ---date:20260528 for【IM聊天-OA】群聊接口-----------
@Operation(summary = "IM聊天-群聊列表")
@RequiresPermissions("sys:im:chat:list")
@GetMapping("/groups")
public Result<List<SysImConversationVO>> groups(HttpServletRequest request) {
LoginUser user = currentUser();
return Result.OK(imChatService.listGroupConversations(user.getId(), resolveTenantId(user, request)));
}
@Operation(summary = "IM聊天-创建群聊")
@RequiresPermissions("sys:im:chat:group")
@PostMapping("/group/create")
public Result<SysImConversationVO> createGroup(@RequestBody SysImCreateGroupDTO dto, HttpServletRequest request) {
LoginUser user = currentUser();
return Result.OK(imChatService.createGroupConversation(user.getId(), resolveTenantId(user, request), user.getOrgCode(), dto));
}
//update-end---author:xsl ---date:20260528 for【IM聊天-OA】群聊接口-----------
//update-begin---author:cursor ---date:20260529 for【IM聊天-OA】群设置-群管理接口-----------
@Operation(summary = "IM聊天-群聊详情")
@RequiresPermissions("sys:im:chat:list")
@GetMapping("/group/detail")
public Result<SysImGroupDetailVO> groupDetail(@RequestParam(name = "conversationId") String conversationId) {
LoginUser user = currentUser();
return Result.OK(imChatService.getGroupDetail(user.getId(), conversationId));
}
@Operation(summary = "IM聊天-添加群成员")
@RequiresPermissions("sys:im:chat:list")
@PostMapping("/group/addMembers")
public Result<SysImConversationVO> addGroupMembers(@RequestBody SysImGroupMembersDTO dto, HttpServletRequest request) {
LoginUser user = currentUser();
return Result.OK(imChatService.addGroupMembers(user.getId(), resolveTenantId(user, request), user.getOrgCode(), dto));
}
@Operation(summary = "IM聊天-移除群成员")
@RequiresPermissions("sys:im:chat:list")
@PostMapping("/group/removeMember")
public Result<String> removeGroupMember(@RequestParam(name = "conversationId") String conversationId,
@RequestParam(name = "memberUserId") String memberUserId) {
LoginUser user = currentUser();
imChatService.removeGroupMember(user.getId(), conversationId, memberUserId);
return Result.OK("移除成功");
}
@Operation(summary = "IM聊天-修改群名称")
@RequiresPermissions("sys:im:chat:list")
@PostMapping("/group/rename")
public Result<SysImConversationVO> renameGroup(@RequestParam(name = "conversationId") String conversationId,
@RequestParam(name = "groupName") String groupName) {
LoginUser user = currentUser();
return Result.OK(imChatService.renameGroup(user.getId(), conversationId, groupName));
}
@Operation(summary = "IM聊天-转让群主")
@RequiresPermissions("sys:im:chat:list")
@PostMapping("/group/transfer")
public Result<String> transferGroupOwner(@RequestParam(name = "conversationId") String conversationId,
@RequestParam(name = "newOwnerId") String newOwnerId) {
LoginUser user = currentUser();
imChatService.transferGroupOwner(user.getId(), conversationId, newOwnerId);
return Result.OK("转让成功");
}
@Operation(summary = "IM聊天-退出群聊")
@RequiresPermissions("sys:im:chat:list")
@PostMapping("/group/quit")
public Result<String> quitGroup(@RequestParam(name = "conversationId") String conversationId) {
LoginUser user = currentUser();
imChatService.quitGroup(user.getId(), conversationId);
return Result.OK("已退出群聊");
}
@Operation(summary = "IM聊天-解散群聊")
@RequiresPermissions("sys:im:chat:list")
@PostMapping("/group/dismiss")
public Result<String> dismissGroup(@RequestParam(name = "conversationId") String conversationId) {
LoginUser user = currentUser();
imChatService.dismissGroup(user.getId(), conversationId);
return Result.OK("群聊已解散");
}
//update-end---author:cursor ---date:20260529 for【IM聊天-OA】群设置-群管理接口-----------
}

View File

@@ -0,0 +1,20 @@
package org.jeecg.modules.im.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* 创建 IM 群聊
*/
@Data
@Schema(description = "创建IM群聊")
public class SysImCreateGroupDTO {
@Schema(description = "群名称")
private String groupName;
@Schema(description = "群成员用户ID不含本人时可自动加入创建人")
private List<String> memberUserIds;
}

View File

@@ -0,0 +1,22 @@
package org.jeecg.modules.im.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* IM 群聊添加成员
*/
//update-begin---author:cursor ---date:20260529 for【IM聊天-OA】群设置-添加群成员-----------
@Data
@Schema(description = "IM群聊添加成员")
public class SysImGroupMembersDTO {
@Schema(description = "会话ID")
private String conversationId;
@Schema(description = "新增成员用户ID")
private List<String> memberUserIds;
}
//update-end---author:cursor ---date:20260529 for【IM聊天-OA】群设置-添加群成员-----------

View File

@@ -20,10 +20,14 @@ public class SysImConversation implements Serializable {
@TableId(type = IdType.ASSIGN_ID)
private String id;
/** 会话类型 single单聊 */
/** 会话类型 single单聊 group群聊 */
private String convType;
/** 单聊唯一键 */
private String userPairKey;
/** 群名称 */
private String groupName;
/** 群主用户ID */
private String ownerId;
/** 租户ID */
private Integer tenantId;
/** 最后一条消息摘要 */

View File

@@ -16,4 +16,9 @@ public interface SysImConversationMapper extends BaseMapper<SysImConversation> {
* 查询当前用户的会话列表
*/
List<SysImConversationVO> listMyConversations(@Param("userId") String userId, @Param("tenantId") Integer tenantId);
/**
* 查询当前用户的群聊列表
*/
List<SysImConversationVO> listMyGroupConversations(@Param("userId") String userId, @Param("tenantId") Integer tenantId);
}

View File

@@ -23,4 +23,26 @@
ORDER BY c.last_time DESC
</select>
<select id="listMyGroupConversations" resultType="org.jeecg.modules.im.vo.SysImConversationVO">
SELECT
c.id AS conversationId,
c.conv_type AS convType,
c.group_name AS groupName,
c.owner_id AS ownerId,
c.last_content AS lastContent,
c.last_time AS lastTime,
m.unread_count AS unreadCount,
(
SELECT COUNT(1)
FROM sys_im_conversation_member gm
WHERE gm.conversation_id = c.id
) AS memberCount
FROM sys_im_conversation_member m
INNER JOIN sys_im_conversation c ON c.id = m.conversation_id
WHERE m.user_id = #{userId}
AND c.tenant_id = #{tenantId}
AND c.conv_type = 'group'
ORDER BY c.last_time DESC
</select>
</mapper>

View File

@@ -4,12 +4,16 @@ package org.jeecg.modules.im.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.jeecg.modules.im.dto.SysImCreateGroupDTO;
import org.jeecg.modules.im.dto.SysImGroupMembersDTO;
import org.jeecg.modules.im.dto.SysImSendMessageDTO;
import org.jeecg.modules.im.vo.SysImContactVO;
import org.jeecg.modules.im.vo.SysImConversationVO;
import org.jeecg.modules.im.vo.SysImGroupDetailVO;
import org.jeecg.modules.im.vo.SysImMessageVO;
@@ -34,7 +38,9 @@ public interface ISysImChatService {
SysImConversationVO openSingleConversation(String userId, Integer tenantId, String orgCode, String targetUserId);
List<SysImConversationVO> listGroupConversations(String userId, Integer tenantId);
SysImConversationVO createGroupConversation(String userId, Integer tenantId, String orgCode, SysImCreateGroupDTO dto);
IPage<SysImMessageVO> listMessages(String userId, String conversationId, Integer pageNo, Integer pageSize, String startTime, String beforeTime);
@@ -50,5 +56,28 @@ public interface ISysImChatService {
List<SysImContactVO> listDeptMembers(String userId, Integer tenantId, String orgCode, String keyword);
//update-begin---author:cursor ---date:20260529 for【IM聊天-OA】群设置-群管理接口-----------
/** 群聊详情(含成员列表,区分群主) */
SysImGroupDetailVO getGroupDetail(String userId, String conversationId);
/** 添加群成员(所有群成员可操作) */
SysImConversationVO addGroupMembers(String userId, Integer tenantId, String orgCode, SysImGroupMembersDTO dto);
/** 移除群成员(仅群主) */
void removeGroupMember(String userId, String conversationId, String memberUserId);
/** 修改群名称(仅群主) */
SysImConversationVO renameGroup(String userId, String conversationId, String groupName);
/** 转让群主(仅群主) */
void transferGroupOwner(String userId, String conversationId, String newOwnerId);
/** 退出群聊(非群主成员) */
void quitGroup(String userId, String conversationId);
/** 解散群聊(仅群主) */
void dismissGroup(String userId, String conversationId);
//update-end---author:cursor ---date:20260529 for【IM聊天-OA】群设置-群管理接口-----------
}

View File

@@ -9,6 +9,8 @@ import org.jeecg.common.constant.WebsocketConst;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.im.dto.SysImCreateGroupDTO;
import org.jeecg.modules.im.dto.SysImGroupMembersDTO;
import org.jeecg.modules.im.dto.SysImSendMessageDTO;
import org.jeecg.modules.im.entity.SysImConversation;
import org.jeecg.modules.im.entity.SysImConversationMember;
@@ -19,6 +21,8 @@ import org.jeecg.modules.im.mapper.SysImMessageMapper;
import org.jeecg.modules.im.service.ISysImChatService;
import org.jeecg.modules.im.vo.SysImContactVO;
import org.jeecg.modules.im.vo.SysImConversationVO;
import org.jeecg.modules.im.vo.SysImGroupDetailVO;
import org.jeecg.modules.im.vo.SysImGroupMemberVO;
import org.jeecg.modules.im.vo.SysImMessageVO;
import org.jeecg.modules.message.websocket.WebSocket;
import org.jeecg.modules.system.entity.SysDepart;
@@ -41,6 +45,8 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -51,6 +57,7 @@ import java.util.stream.Collectors;
public class SysImChatServiceImpl implements ISysImChatService {
private static final String CONV_TYPE_SINGLE = "single";
private static final String CONV_TYPE_GROUP = "group";
private static final String MSG_TYPE_TEXT = "text";
private static final String MSG_TYPE_IMAGE = "image";
private static final String MSG_TYPE_BIZ_RECORD = "biz_record";
@@ -116,6 +123,271 @@ public class SysImChatServiceImpl implements ISysImChatService {
}
//update-end---author:cursor ---date:20260528 for【IM聊天-OA】打开单聊会话-----------
//update-begin---author:xsl ---date:20260528 for【IM聊天-OA】群聊会话列表与创建-----------
@Override
public List<SysImConversationVO> listGroupConversations(String userId, Integer tenantId) {
if (oConvertUtils.isEmpty(userId) || tenantId == null || tenantId <= 0) {
return Collections.emptyList();
}
return conversationMapper.listMyGroupConversations(userId, tenantId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public SysImConversationVO createGroupConversation(String userId, Integer tenantId, String orgCode, SysImCreateGroupDTO dto) {
if (dto == null || oConvertUtils.isEmpty(dto.getGroupName())) {
throw new JeecgBootException("群名称不能为空");
}
String groupName = dto.getGroupName().trim();
if (groupName.length() > 30) {
throw new JeecgBootException("群名称不能超过30字");
}
List<String> memberIds = normalizeGroupMemberIds(userId, dto.getMemberUserIds());
if (memberIds.size() < 2) {
throw new JeecgBootException("群聊至少需要2名成员");
}
if (memberIds.size() > 50) {
throw new JeecgBootException("群成员不能超过50人");
}
for (String memberId : memberIds) {
if (userId.equals(memberId)) {
continue;
}
validateTenantChat(userId, tenantId, orgCode, memberId);
}
Date now = new Date();
SysImConversation conversation = new SysImConversation();
conversation.setConvType(CONV_TYPE_GROUP);
conversation.setGroupName(groupName);
conversation.setOwnerId(userId);
conversation.setTenantId(tenantId);
conversation.setCreateBy(userId);
conversation.setCreateTime(now);
conversation.setUpdateTime(now);
conversationMapper.insert(conversation);
for (String memberId : memberIds) {
createMember(conversation.getId(), memberId, now);
}
return buildGroupConversationVo(conversation, userId);
}
private List<String> normalizeGroupMemberIds(String creatorId, List<String> memberUserIds) {
Set<String> memberIds = new LinkedHashSet<>();
memberIds.add(creatorId);
if (memberUserIds != null) {
for (String memberId : memberUserIds) {
if (oConvertUtils.isNotEmpty(memberId)) {
memberIds.add(memberId);
}
}
}
return new ArrayList<>(memberIds);
}
private SysImConversationVO buildGroupConversationVo(SysImConversation conversation, String userId) {
SysImConversationMember member = getMember(userId, conversation.getId());
SysImConversationVO vo = new SysImConversationVO();
vo.setConversationId(conversation.getId());
vo.setConvType(CONV_TYPE_GROUP);
vo.setGroupName(conversation.getGroupName());
vo.setOwnerId(conversation.getOwnerId());
vo.setLastContent(conversation.getLastContent());
vo.setLastTime(conversation.getLastTime());
vo.setUnreadCount(member == null ? 0 : member.getUnreadCount());
Long memberCount = memberMapper.selectCount(new LambdaQueryWrapper<SysImConversationMember>()
.eq(SysImConversationMember::getConversationId, conversation.getId()));
vo.setMemberCount(memberCount == null ? 0 : memberCount.intValue());
return vo;
}
//update-end---author:xsl ---date:20260528 for【IM聊天-OA】群聊会话列表与创建-----------
//update-begin---author:cursor ---date:20260529 for【IM聊天-OA】群设置-群管理-----------
private static final int GROUP_MEMBER_MAX = 50;
@Override
public SysImGroupDetailVO getGroupDetail(String userId, String conversationId) {
SysImConversation conversation = assertGroupConversation(userId, conversationId);
List<SysImConversationMember> members = memberMapper.selectList(new LambdaQueryWrapper<SysImConversationMember>()
.eq(SysImConversationMember::getConversationId, conversationId)
.orderByAsc(SysImConversationMember::getCreateTime));
List<String> memberUserIds = members.stream()
.map(SysImConversationMember::getUserId)
.filter(oConvertUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
Map<String, SysUser> userMap = new HashMap<>(memberUserIds.size());
if (!memberUserIds.isEmpty()) {
List<SysUser> users = userMapper.selectBatchIds(memberUserIds);
if (users != null) {
for (SysUser user : users) {
userMap.put(user.getId(), user);
}
}
}
String ownerId = conversation.getOwnerId();
List<SysImGroupMemberVO> memberVoList = new ArrayList<>(members.size());
for (SysImConversationMember member : members) {
SysUser user = userMap.get(member.getUserId());
SysImGroupMemberVO memberVo = new SysImGroupMemberVO();
memberVo.setUserId(member.getUserId());
memberVo.setOwner(member.getUserId() != null && member.getUserId().equals(ownerId));
if (user != null) {
memberVo.setRealname(user.getRealname());
memberVo.setUsername(user.getUsername());
memberVo.setAvatar(user.getAvatar());
}
memberVoList.add(memberVo);
}
// 群主排在最前
memberVoList.sort(Comparator.comparingInt(item -> Boolean.TRUE.equals(item.getOwner()) ? 0 : 1));
SysImGroupDetailVO detail = new SysImGroupDetailVO();
detail.setConversationId(conversation.getId());
detail.setGroupName(conversation.getGroupName());
detail.setOwnerId(ownerId);
detail.setMemberCount(memberVoList.size());
detail.setOwner(userId.equals(ownerId));
detail.setMembers(memberVoList);
return detail;
}
@Override
@Transactional(rollbackFor = Exception.class)
public SysImConversationVO addGroupMembers(String userId, Integer tenantId, String orgCode, SysImGroupMembersDTO dto) {
if (dto == null || oConvertUtils.isEmpty(dto.getConversationId())) {
throw new JeecgBootException("会话不存在");
}
SysImConversation conversation = assertGroupConversation(userId, dto.getConversationId());
if (dto.getMemberUserIds() == null || dto.getMemberUserIds().isEmpty()) {
throw new JeecgBootException("请选择要添加的成员");
}
// 已在群成员
Set<String> existIds = memberMapper.selectList(new LambdaQueryWrapper<SysImConversationMember>()
.eq(SysImConversationMember::getConversationId, conversation.getId()))
.stream().map(SysImConversationMember::getUserId).collect(Collectors.toSet());
// 去重并过滤已存在成员
List<String> toAdd = new ArrayList<>();
Set<String> seen = new LinkedHashSet<>();
for (String memberId : dto.getMemberUserIds()) {
if (oConvertUtils.isEmpty(memberId) || existIds.contains(memberId) || !seen.add(memberId)) {
continue;
}
toAdd.add(memberId);
}
if (toAdd.isEmpty()) {
throw new JeecgBootException("所选成员已在群内");
}
if (existIds.size() + toAdd.size() > GROUP_MEMBER_MAX) {
throw new JeecgBootException("群成员不能超过" + GROUP_MEMBER_MAX + "");
}
// 校验同租户、同部门
for (String memberId : toAdd) {
validateTenantChat(userId, tenantId, orgCode, memberId);
}
Date now = new Date();
for (String memberId : toAdd) {
createMember(conversation.getId(), memberId, now);
}
return buildGroupConversationVo(conversation, userId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removeGroupMember(String userId, String conversationId, String memberUserId) {
SysImConversation conversation = assertGroupConversation(userId, conversationId);
assertGroupOwner(userId, conversation);
if (oConvertUtils.isEmpty(memberUserId)) {
throw new JeecgBootException("请选择要移除的成员");
}
if (memberUserId.equals(conversation.getOwnerId())) {
throw new JeecgBootException("群主不能被移除");
}
memberMapper.delete(new LambdaQueryWrapper<SysImConversationMember>()
.eq(SysImConversationMember::getConversationId, conversationId)
.eq(SysImConversationMember::getUserId, memberUserId));
}
@Override
@Transactional(rollbackFor = Exception.class)
public SysImConversationVO renameGroup(String userId, String conversationId, String groupName) {
SysImConversation conversation = assertGroupConversation(userId, conversationId);
assertGroupOwner(userId, conversation);
if (oConvertUtils.isEmpty(groupName)) {
throw new JeecgBootException("群名称不能为空");
}
String name = groupName.trim();
if (name.length() > 30) {
throw new JeecgBootException("群名称不能超过30字");
}
conversation.setGroupName(name);
conversation.setUpdateBy(userId);
conversation.setUpdateTime(new Date());
conversationMapper.updateById(conversation);
return buildGroupConversationVo(conversation, userId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void transferGroupOwner(String userId, String conversationId, String newOwnerId) {
SysImConversation conversation = assertGroupConversation(userId, conversationId);
assertGroupOwner(userId, conversation);
if (oConvertUtils.isEmpty(newOwnerId)) {
throw new JeecgBootException("请选择新群主");
}
if (newOwnerId.equals(userId)) {
throw new JeecgBootException("不能转让给自己");
}
if (getMember(newOwnerId, conversationId) == null) {
throw new JeecgBootException("新群主必须是群成员");
}
conversation.setOwnerId(newOwnerId);
conversation.setUpdateBy(userId);
conversation.setUpdateTime(new Date());
conversationMapper.updateById(conversation);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void quitGroup(String userId, String conversationId) {
SysImConversation conversation = assertGroupConversation(userId, conversationId);
if (userId.equals(conversation.getOwnerId())) {
throw new JeecgBootException("群主不能退出群聊,请先转让群主或解散群聊");
}
memberMapper.delete(new LambdaQueryWrapper<SysImConversationMember>()
.eq(SysImConversationMember::getConversationId, conversationId)
.eq(SysImConversationMember::getUserId, userId));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void dismissGroup(String userId, String conversationId) {
SysImConversation conversation = assertGroupConversation(userId, conversationId);
assertGroupOwner(userId, conversation);
memberMapper.delete(new LambdaQueryWrapper<SysImConversationMember>()
.eq(SysImConversationMember::getConversationId, conversationId));
conversationMapper.deleteById(conversationId);
}
/** 校验会话存在、为群聊且当前用户是群成员 */
private SysImConversation assertGroupConversation(String userId, String conversationId) {
if (oConvertUtils.isEmpty(conversationId)) {
throw new JeecgBootException("会话不存在");
}
SysImConversation conversation = conversationMapper.selectById(conversationId);
if (conversation == null || !CONV_TYPE_GROUP.equals(conversation.getConvType())) {
throw new JeecgBootException("群聊不存在");
}
assertMember(userId, conversationId);
return conversation;
}
/** 校验当前用户是群主 */
private void assertGroupOwner(String userId, SysImConversation conversation) {
if (!userId.equals(conversation.getOwnerId())) {
throw new JeecgBootException("仅群主可执行该操作");
}
}
//update-end---author:cursor ---date:20260529 for【IM聊天-OA】群设置-群管理-----------
//update-begin---author:cursor ---date:20260528 for【IM聊天-OA】消息分页-----------
@Override
public IPage<SysImMessageVO> listMessages(String userId, String conversationId, Integer pageNo, Integer pageSize, String startTime, String beforeTime) {
@@ -179,7 +451,15 @@ public class SysImChatServiceImpl implements ISysImChatService {
SysImConversation conversation = conversationMapper.selectById(dto.getConversationId());
//update-begin---author:cursor ---date:20260528 for【IM聊天-OA】图片消息会话摘要-----------
conversation.setLastContent(truncate(resolveLastContent(message.getMsgType(), message.getContent()), 200));
String lastContent = resolveLastContent(message.getMsgType(), message.getContent());
if (CONV_TYPE_GROUP.equals(conversation.getConvType())) {
SysUser sender = userMapper.selectById(userId);
String senderName = sender != null ? oConvertUtils.getString(sender.getRealname(), sender.getUsername()) : "";
if (oConvertUtils.isNotEmpty(senderName)) {
lastContent = senderName + ": " + lastContent;
}
}
conversation.setLastContent(truncate(lastContent, 200));
//update-end---author:cursor ---date:20260528 for【IM聊天-OA】图片消息会话摘要-----------
conversation.setLastTime(now);
conversation.setUpdateTime(now);
@@ -188,7 +468,7 @@ public class SysImChatServiceImpl implements ISysImChatService {
memberMapper.incrementUnreadExceptSender(dto.getConversationId(), userId);
SysImMessageVO messageVo = toMessageVo(message, userId);
fillBizRecordReceiverPermission(messageVo, message, userId, new HashMap<>(4));
pushChatMessage(dto.getConversationId(), userId, messageVo);
pushChatMessage(dto.getConversationId(), userId, messageVo, conversation.getConvType());
return messageVo;
}
//update-end---author:cursor ---date:20260528 for【IM聊天-OA】发送消息-----------
@@ -422,6 +702,10 @@ public class SysImChatServiceImpl implements ISysImChatService {
if (!Boolean.TRUE.equals(vo.getMine()) || !MSG_TYPE_BIZ_RECORD.equals(vo.getMsgType())) {
return;
}
SysImConversation conversation = conversationMapper.selectById(message.getConversationId());
if (conversation == null || !CONV_TYPE_SINGLE.equals(conversation.getConvType())) {
return;
}
String pagePath = extractBizRecordPagePath(vo.getContent());
if (oConvertUtils.isEmpty(pagePath)) {
return;
@@ -509,7 +793,7 @@ public class SysImChatServiceImpl implements ISysImChatService {
//update-end---author:xsl ---date:20260528 for【IM聊天-OA】发送方提示接收方无功能权限-----------
//update-end---author:cursor ---date:20260528 for【IM聊天-OA】消息列表批量填充发送人-----------
private void pushChatMessage(String conversationId, String senderId, SysImMessageVO messageVo) {
private void pushChatMessage(String conversationId, String senderId, SysImMessageVO messageVo, String convType) {
List<SysImConversationMember> members = memberMapper.selectList(new LambdaQueryWrapper<SysImConversationMember>()
.eq(SysImConversationMember::getConversationId, conversationId));
for (SysImConversationMember member : members) {
@@ -520,6 +804,9 @@ public class SysImChatServiceImpl implements ISysImChatService {
obj.put(WebsocketConst.MSG_CMD, WebsocketConst.MSG_CHAT);
obj.put(WebsocketConst.MSG_USER_ID, member.getUserId());
obj.put("conversationId", conversationId);
//update-begin---author:xsl ---date:20260528 for【IM聊天-OA】WebSocket推送会话类型区分群聊-----------
obj.put("convType", convType);
//update-end---author:xsl ---date:20260528 for【IM聊天-OA】WebSocket推送会话类型区分群聊-----------
obj.put("messageId", messageVo.getId());
obj.put("senderId", messageVo.getSenderId());
//update-begin---author:cursor ---date:20260528 for【IM聊天-OA】WebSocket推送补全头像字段-----------

View File

@@ -34,4 +34,10 @@ public class SysImConversationVO {
private String targetUsername;
@Schema(description = "对方头像")
private String targetAvatar;
@Schema(description = "群名称")
private String groupName;
@Schema(description = "群成员数")
private Integer memberCount;
@Schema(description = "群主用户ID")
private String ownerId;
}

View File

@@ -0,0 +1,29 @@
package org.jeecg.modules.im.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* IM 群聊详情(群设置)
*/
//update-begin---author:cursor ---date:20260529 for【IM聊天-OA】群设置-群详情-----------
@Data
@Schema(description = "IM群聊详情")
public class SysImGroupDetailVO {
@Schema(description = "会话ID")
private String conversationId;
@Schema(description = "群名称")
private String groupName;
@Schema(description = "群主用户ID")
private String ownerId;
@Schema(description = "群成员数")
private Integer memberCount;
@Schema(description = "当前用户是否群主")
private Boolean owner;
@Schema(description = "群成员列表")
private List<SysImGroupMemberVO> members;
}
//update-end---author:cursor ---date:20260529 for【IM聊天-OA】群设置-群详情-----------

View File

@@ -0,0 +1,25 @@
package org.jeecg.modules.im.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* IM 群成员
*/
//update-begin---author:cursor ---date:20260529 for【IM聊天-OA】群设置-群成员展示-----------
@Data
@Schema(description = "IM群成员")
public class SysImGroupMemberVO {
@Schema(description = "用户ID")
private String userId;
@Schema(description = "姓名")
private String realname;
@Schema(description = "账号")
private String username;
@Schema(description = "头像")
private String avatar;
@Schema(description = "是否群主")
private Boolean owner;
}
//update-end---author:cursor ---date:20260529 for【IM聊天-OA】群设置-群成员展示-----------

View File

@@ -0,0 +1,22 @@
-- IM 群聊扩展会话表字段
SET NAMES utf8mb4;
ALTER TABLE `sys_im_conversation`
ADD COLUMN `group_name` varchar(100) DEFAULT NULL COMMENT '群名称' AFTER `user_pair_key`,
ADD COLUMN `owner_id` varchar(32) DEFAULT NULL COMMENT '群主用户ID' AFTER `group_name`;
ALTER TABLE `sys_im_conversation`
MODIFY COLUMN `conv_type` varchar(10) NOT NULL DEFAULT 'single' COMMENT '会话类型 single单聊 group群聊';
INSERT IGNORE INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
VALUES ('1995000000000000113', '1995000000000000110', '创建群聊', 2, 'sys:im:chat:group', '1', 3.00, 0, 1, 0, '1', 0, 'admin', NOW());
INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`)
SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NULL, NOW(), '127.0.0.1'
FROM `sys_role` r
CROSS JOIN `sys_permission` p
WHERE r.`role_code` = 'admin'
AND p.`id` = '1995000000000000113'
AND NOT EXISTS (
SELECT 1 FROM `sys_role_permission` rp WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
);